From 1192c4575ea7afdf38e3fa0df4587a08620af11e Mon Sep 17 00:00:00 2001 From: root Date: Tue, 21 Jul 2020 10:00:46 +0200 Subject: SZS v2.18a --- project/Makefile | 1094 + project/Makefile.user | 1 + project/bin/wbmgt | Bin 0 -> 570760 bytes project/bin/wctct | Bin 0 -> 1312344 bytes project/bin/wimgt | Bin 0 -> 763704 bytes project/bin/wkclt | Bin 0 -> 779096 bytes project/bin/wkmpt | Bin 0 -> 2078120 bytes project/bin/wlect | Bin 0 -> 1035920 bytes project/bin/wmdlt | Bin 0 -> 665776 bytes project/bin/wpatt | Bin 0 -> 565272 bytes project/bin/wstrt | Bin 0 -> 613712 bytes project/bin/wszst | Bin 0 -> 3548584 bytes project/bin/wtest | Bin 0 -> 1163136 bytes project/bin/x86_64/wbmgt | Bin 0 -> 570760 bytes project/bin/x86_64/wctct | Bin 0 -> 1312344 bytes project/bin/x86_64/wimgt | Bin 0 -> 763704 bytes project/bin/x86_64/wkclt | Bin 0 -> 779096 bytes project/bin/x86_64/wkmpt | Bin 0 -> 2078120 bytes project/bin/x86_64/wlect | Bin 0 -> 1035920 bytes project/bin/x86_64/wmdlt | Bin 0 -> 665776 bytes project/bin/x86_64/wpatt | Bin 0 -> 565272 bytes project/bin/x86_64/wstrt | Bin 0 -> 613712 bytes project/bin/x86_64/wszst | Bin 0 -> 3548584 bytes project/bin/x86_64/wtest | Bin 0 -> 1163136 bytes project/dclib/Makefile.inc | 67 + project/dclib/dclib-basics.c | 8065 +++++++ project/dclib/dclib-basics.h | 5252 +++++ project/dclib/dclib-color.c | 4351 ++++ project/dclib/dclib-color.h | 828 + project/dclib/dclib-debug.c | 1891 ++ project/dclib/dclib-debug.h | 714 + project/dclib/dclib-file.c | 5360 +++++ project/dclib/dclib-file.h | 1278 + project/dclib/dclib-gen-ui.h | 162 + project/dclib/dclib-gen-ui.inc | 1303 ++ project/dclib/dclib-mysql.c | 1560 ++ project/dclib/dclib-mysql.h | 544 + project/dclib/dclib-network-linux.c | 461 + project/dclib/dclib-network-linux.h | 171 + project/dclib/dclib-network.c | 4546 ++++ project/dclib/dclib-network.h | 1486 ++ project/dclib/dclib-numeric.c | 6102 +++++ project/dclib/dclib-numeric.h | 839 + project/dclib/dclib-option.c | 1012 + project/dclib/dclib-option.h | 383 + project/dclib/dclib-regex.c | 465 + project/dclib/dclib-regex.h | 131 + project/dclib/dclib-shift-jis.c | 1408 ++ project/dclib/dclib-system.h | 101 + project/dclib/dclib-tables.c | 417 + project/dclib/dclib-types.h | 403 + project/dclib/dclib-ui.c | 950 + project/dclib/dclib-ui.h | 215 + project/dclib/dclib-utf8.c | 1910 ++ project/dclib/dclib-utf8.h | 130 + project/dclib/dclib-vector.c | 2582 +++ project/dclib/dclib-xdump.c | 2214 ++ project/dclib/dclib-xdump.h | 348 + project/dclib/lib-bmg.c | 6346 +++++ project/dclib/lib-bmg.h | 1125 + project/dclib/lib-dol.c | 1208 + project/dclib/lib-dol.h | 517 + project/dclib/lib-mkw.c | 1107 + project/dclib/lib-mkw.h | 308 + project/gen-template.sh | 56 + project/gen-text-file.sh | 77 + project/gpl-2.0.txt | 339 + project/makefiles-local/Makefile.local | 17 + project/makefiles-local/Makefile.local.i386 | 10 + project/makefiles-local/Makefile.local.mac | 96 + project/makefiles-local/Makefile.local.x86_64 | 10 + project/revision.sh | 1 + project/scripts/create-brres-lists.sh | 256 + project/scripts/distrib.list | 3 + project/scripts/kcl/lower-walls.txt | 41 + project/scripts/kcl/minimap.txt | 17 + project/scripts/kcl/remove-face-down-roads.txt | 20 + project/scripts/minimap.sh | 174 + project/scripts/patch-all-messages.sh | 32 + project/scripts/test-wszst.sh | 99 + project/scripts/test-wu8.sh | 96 + project/setup.sh | 196 + project/setup/INSTALL.txt | 50 + project/setup/cygwin-copy.sh | 71 + project/setup/install.sh | 92 + project/setup/sendto-install.bat | 2 + project/setup/sendto-install.sh | 80 + project/setup/sendto-uninstall.bat | 2 + project/setup/sendto-uninstall.sh | 83 + project/setup/szs.def | 47 + project/setup/version.h | 59 + project/setup/windows-install.exe | Bin 0 -> 425984 bytes project/setup/windows-install.sh | 128 + project/setup/windows-sendto/adjust-minimap.bat | 3 + project/setup/windows-sendto/clean-KCL-of-SZS.bat | 3 + project/setup/windows-sendto/convert-to-SZS.bat | 3 + project/setup/windows-sendto/convert-to-WBZ.bat | 3 + .../windows-sendto/create-autoadd-library.bat | 3 + project/setup/windows-sendto/normalize-SZS.bat | 3 + project/setup/windows-uninstall.bat | 2 + project/setup/windows-uninstall.sh | 103 + project/src/analyze.c | 1361 ++ project/src/crypt.h | 45 + project/src/crypto/md32_common.h | 408 + project/src/crypto/sha1-586-cygwin.s | 1441 ++ project/src/crypto/sha1-586-elf.s | 1442 ++ project/src/crypto/sha1-x86_64.s | 1282 ++ project/src/crypto/sha1_one.c | 74 + project/src/crypto/sha1dgst.c | 64 + project/src/crypto/sha_locl.h | 435 + project/src/crypto/ssl-asm.S | 39 + project/src/crypto/ssl-asm.h | 56 + project/src/crypto/wiimm-sha.h | 204 + project/src/db-ctcode.c | 2758 +++ project/src/db-ctcode.h | 64 + project/src/db-dol.c | 545 + project/src/db-dol.h | 69 + project/src/db-file.c | 12064 ++++++++++ project/src/db-file.h | 212 + project/src/db-kcl.c | 881 + project/src/db-kcl.h | 310 + project/src/db-mkw.c | 917 + project/src/db-mkw.h | 127 + project/src/db-object.c | 3108 +++ project/src/db-object.h | 186 + project/src/file-type.c | 1224 + project/src/file-type.h | 236 + project/src/getopt.c | 511 + project/src/lib-breff.c | 451 + project/src/lib-breff.h | 168 + project/src/lib-brres.c | 2125 ++ project/src/lib-brres.h | 505 + project/src/lib-bzip2.c | 932 + project/src/lib-bzip2.h | 406 + project/src/lib-checksum.c | 982 + project/src/lib-checksum.h | 196 + project/src/lib-common.c | 1434 ++ project/src/lib-common.h | 849 + project/src/lib-ctcode.c | 4248 ++++ project/src/lib-ctcode.h | 543 + project/src/lib-ctdata.c | 960 + project/src/lib-file.c | 3435 +++ project/src/lib-geohit.c | 766 + project/src/lib-image.h | 1077 + project/src/lib-image1.c | 4089 ++++ project/src/lib-image2.c | 2992 +++ project/src/lib-image3.c | 2219 ++ project/src/lib-itemslot.c | 790 + project/src/lib-kcl-image.c | 634 + project/src/lib-kcl.c | 8955 +++++++ project/src/lib-kcl.h | 1221 + project/src/lib-kmp-diff.c | 2788 +++ project/src/lib-kmp-draw.c | 2493 ++ project/src/lib-kmp-pflags.c | 1965 ++ project/src/lib-kmp-text.c | 7192 ++++++ project/src/lib-kmp.c | 7899 +++++++ project/src/lib-kmp.h | 2846 +++ project/src/lib-lecode.c | 4574 ++++ project/src/lib-lecode.h | 896 + project/src/lib-mdl.c | 4556 ++++ project/src/lib-mdl.h | 824 + project/src/lib-minigame.c | 923 + project/src/lib-numeric.c | 2050 ++ project/src/lib-numeric.h | 2337 ++ project/src/lib-object.c | 336 + project/src/lib-object.h | 186 + project/src/lib-objflow.c | 641 + project/src/lib-pack.c | 255 + project/src/lib-pack.h | 104 + project/src/lib-parser-func.c | 5597 +++++ project/src/lib-parser.c | 7617 ++++++ project/src/lib-pat.c | 2166 ++ project/src/lib-pat.h | 441 + project/src/lib-rarc.c | 271 + project/src/lib-rarc.h | 139 + project/src/lib-rkc.c | 276 + project/src/lib-rkc.h | 117 + project/src/lib-rkg.c | 153 + project/src/lib-rkg.h | 120 + project/src/lib-staticr.c | 6886 ++++++ project/src/lib-staticr.h | 745 + project/src/lib-std.c | 6604 ++++++ project/src/lib-std.h | 2234 ++ project/src/lib-szs-create.c | 4744 ++++ project/src/lib-szs.c | 4567 ++++ project/src/lib-szs.h | 1258 + project/src/lib-transform.c | 1391 ++ project/src/lib-xbmg.c | 1511 ++ project/src/lib-xbmg.h | 148 + project/src/libbz2/LICENSE | 42 + project/src/libbz2/blocksort.c | 1094 + project/src/libbz2/bzlib.c | 1572 ++ project/src/libbz2/bzlib.h | 282 + project/src/libbz2/bzlib_private.h | 515 + project/src/libbz2/compress.c | 672 + project/src/libbz2/crctable.c | 104 + project/src/libbz2/decompress.c | 646 + project/src/libbz2/huffman.c | 205 + project/src/libbz2/randtable.c | 84 + project/src/system.h | 88 + project/src/types.h | 62 + project/src/ui/gen-ui.c | 185 + project/src/ui/tab-wbmgt.inc | 605 + project/src/ui/tab-wctct.inc | 780 + project/src/ui/tab-wimgt.inc | 497 + project/src/ui/tab-wkclt.inc | 793 + project/src/ui/tab-wkmpt.inc | 932 + project/src/ui/tab-wlect.inc | 837 + project/src/ui/tab-wmdlt.inc | 600 + project/src/ui/tab-wpatt.inc | 441 + project/src/ui/tab-wstrt.inc | 1038 + project/src/ui/tab-wszst.inc | 3238 +++ project/src/ui/ui-wbmgt.c | 1695 ++ project/src/ui/ui-wbmgt.h | 417 + project/src/ui/ui-wctct.c | 1952 ++ project/src/ui/ui-wctct.h | 505 + project/src/ui/ui-wimgt.c | 1400 ++ project/src/ui/ui-wimgt.h | 348 + project/src/ui/ui-wkclt.c | 2482 ++ project/src/ui/ui-wkclt.h | 481 + project/src/ui/ui-wkmpt.c | 2661 +++ project/src/ui/ui-wkmpt.h | 536 + project/src/ui/ui-wlect.c | 1865 ++ project/src/ui/ui-wlect.h | 451 + project/src/ui/ui-wmdlt.c | 1756 ++ project/src/ui/ui-wmdlt.h | 417 + project/src/ui/ui-wpatt.c | 1228 + project/src/ui/ui-wpatt.h | 324 + project/src/ui/ui-wstrt.c | 2207 ++ project/src/ui/ui-wstrt.h | 560 + project/src/ui/ui-wszst.c | 7805 +++++++ project/src/ui/ui-wszst.h | 1226 + project/src/ui/ui.def | 23034 +++++++++++++++++++ project/src/ui/ui.h | 50 + project/src/wbmgt.c | 984 + project/src/wctct.c | 992 + project/src/wimgt.c | 785 + project/src/wkclt.c | 1356 ++ project/src/wkmpt.c | 1835 ++ project/src/wlect.c | 1299 ++ project/src/wmdlt.c | 757 + project/src/wpatt.c | 547 + project/src/wrapper-wbmgt.c | 2 + project/src/wrapper-wctct.c | 2 + project/src/wrapper-wimgt.c | 2 + project/src/wrapper-wkclt.c | 2 + project/src/wrapper-wkmpt.c | 2 + project/src/wrapper-wlect.c | 2 + project/src/wrapper-wmdlt.c | 2 + project/src/wrapper-wpatt.c | 2 + project/src/wrapper-wstrt.c | 2 + project/src/wstrt.c | 1485 ++ project/src/wszst.c | 5896 +++++ project/src/wtest.c | 3935 ++++ project/support/KCLFlags.xml | 39 + project/support/KMPItems.xml | 319 + project/support/kcl-types.txt | 1721 ++ project/system.c | 45 + project/templates/DOCUMENTATION.txt | 35 + project/templates/HISTORY-v0.txt | 784 + project/templates/HISTORY-v1.txt | 1288 ++ project/templates/HISTORY.txt | 396 + project/templates/MILESTONES.txt | 153 + project/templates/forums/mariokartwii.txt | 25 + project/templates/module/features-de.forum | 6 + project/templates/module/features-en.forum | 6 + project/templates/module/features.txt | 13 + project/templates/module/head.txt | 21 + project/templates/module/last-history.txt | 33 + .../text-files/common.inc/explain-objmgr-cr.inc | 33 + project/text-files/ctcode.inc/ctcode-head-cr.inc | 27 + .../ctcode.inc/ctcode-list-syntax-brief-cr.inc | 20 + .../ctcode.inc/ctcode-list-syntax-full-cr.inc | 141 + project/text-files/ctcode.inc/ctcode-setup-cr.inc | 33 + project/text-files/distrib.inc/distrib-head-cr.inc | 144 + .../text-files/distrib.inc/distrib-param-cr.inc | 6 + .../text-files/distrib.inc/distrib-tracks-cr.inc | 9 + .../text-files/geohit.inc/geohit-item-head-cr.inc | 1 + .../text-files/geohit.inc/geohit-item-info-cr.inc | 36 + .../geohit.inc/geohit-item-objects-cr.inc | 9 + .../text-files/geohit.inc/geohit-item-setup-cr.inc | 12 + .../text-files/geohit.inc/geohit-kart-head-cr.inc | 1 + .../text-files/geohit.inc/geohit-kart-info-cr.inc | 36 + .../geohit.inc/geohit-kart-objects-cr.inc | 9 + .../text-files/geohit.inc/geohit-kart-setup-cr.inc | 12 + .../text-files/itemslot.inc/itemslot-head-cr.inc | 1 + .../text-files/itemslot.inc/itemslot-info-cr.inc | 24 + .../text-files/itemslot.inc/itemslot-param-cr.inc | 6 + .../text-files/itemslot.inc/itemslot-setup-cr.inc | 12 + .../text-files/itemslot.inc/itemslot-table-cr.inc | 3 + project/text-files/kcl.inc/kcl-dump-head-cr.inc | 40 + project/text-files/kcl.inc/kcl-dump-octlist-cr.inc | 2 + project/text-files/kcl.inc/kcl-dump-octree-cr.inc | 6 + project/text-files/kcl.inc/kcl-dump-tridata-cr.inc | 15 + .../text-files/kcl.inc/kcl-flag-template-cr.inc | 48 + project/text-files/kcl.inc/kcl-head-cr.inc | 18 + project/text-files/kmp.inc/kmp-area-cr.inc | 16 + project/text-files/kmp.inc/kmp-came-cr.inc | 19 + project/text-files/kmp.inc/kmp-ckph-cr.inc | 7 + project/text-files/kmp.inc/kmp-ckpt-cr.inc | 32 + project/text-files/kmp.inc/kmp-cnpt-cr.inc | 7 + project/text-files/kmp.inc/kmp-defaults-cr.inc | 8 + project/text-files/kmp.inc/kmp-enph-cr.inc | 7 + project/text-files/kmp.inc/kmp-enpt-cr.inc | 7 + project/text-files/kmp.inc/kmp-enpt-ropt-cr.inc | 42 + project/text-files/kmp.inc/kmp-gobj-cr.inc | 49 + project/text-files/kmp.inc/kmp-gobj-tabhead-cr.inc | 5 + project/text-files/kmp.inc/kmp-head-cr.inc | 28 + project/text-files/kmp.inc/kmp-itph-cr.inc | 7 + project/text-files/kmp.inc/kmp-itpt-cr.inc | 7 + project/text-files/kmp.inc/kmp-itpt-ropt-cr.inc | 4 + project/text-files/kmp.inc/kmp-jgpt-auto-cr.inc | 26 + project/text-files/kmp.inc/kmp-jgpt-cr.inc | 7 + project/text-files/kmp.inc/kmp-ktpt-cr.inc | 23 + project/text-files/kmp.inc/kmp-macros-cr.inc | 60 + project/text-files/kmp.inc/kmp-mspt-cr.inc | 7 + project/text-files/kmp.inc/kmp-param1-cr.inc | 54 + project/text-files/kmp.inc/kmp-param2-cr.inc | 28 + project/text-files/kmp.inc/kmp-param3-cr.inc | 6 + project/text-files/kmp.inc/kmp-ph-cr.inc | 15 + project/text-files/kmp.inc/kmp-poti-cr.inc | 42 + project/text-files/kmp.inc/kmp-pt-head-cr.inc | 6 + project/text-files/kmp.inc/kmp-pt-info-cr.inc | 32 + project/text-files/kmp.inc/kmp-pt-mask-cr.inc | 6 + project/text-files/kmp.inc/kmp-pt2-cr.inc | 25 + project/text-files/kmp.inc/kmp-pt2-head-cr.inc | 6 + project/text-files/kmp.inc/kmp-setup-cr.inc | 43 + project/text-files/kmp.inc/kmp-stgi-cr.inc | 20 + project/text-files/lex.inc/lex-elem-cann-cr.inc | 15 + project/text-files/lex.inc/lex-elem-hipt-cr.inc | 35 + project/text-files/lex.inc/lex-elem-set1-cr.inc | 20 + project/text-files/lex.inc/lex-elem-test-cr.inc | 43 + project/text-files/lex.inc/lex-head-cr.inc | 12 + project/text-files/lex.inc/lex-hexdump-cr.inc | 16 + project/text-files/lex.inc/lex-setup-cr.inc | 17 + project/text-files/logo.inc | 14 + project/text-files/lpar.inc/lpar-head-cr.inc | 13 + project/text-files/lpar.inc/lpar-sect-chat-cr.inc | 56 + .../lpar.inc/lpar-sect-chat-example-cr.inc | 4 + project/text-files/lpar.inc/lpar-sect-param-cr.inc | 60 + project/text-files/mdl.inc/mdl-file-head-cr.inc | 26 + project/text-files/mdl.inc/mdl-header-cr.inc | 8 + project/text-files/mdl.inc/mdl-hexdump-cr.inc | 4 + project/text-files/mdl.inc/mdl-s0-cr.inc | 19 + project/text-files/mdl.inc/mdl-s0-tabhead-cr.inc | 2 + project/text-files/mdl.inc/mdl-setup-cr.inc | 21 + .../text-files/minigame.inc/minigame-head-cr.inc | 1 + .../text-files/minigame.inc/minigame-info-cr.inc | 24 + .../text-files/minigame.inc/minigame-param-cr.inc | 2 + .../text-files/minigame.inc/minigame-setup-cr.inc | 12 + .../text-files/minigame.inc/minigame-table-cr.inc | 3 + .../minigame.inc/minigame-table-head-cr.inc | 3 + project/text-files/obj-mtl-bz2.inc | 1343 ++ project/text-files/objflow.inc/objflow-head-cr.inc | 1 + project/text-files/objflow.inc/objflow-info-cr.inc | 43 + .../text-files/objflow.inc/objflow-objects-cr.inc | 7 + .../text-files/objflow.inc/objflow-setup-cr.inc | 12 + project/text-files/pat.inc/pat-file-head-cr.inc | 16 + project/text-files/pat.inc/pat-header-cr.inc | 8 + project/text-files/pat.inc/pat-s0_base-cr.inc | 8 + project/text-files/pat.inc/pat-s0_list-cr.inc | 7 + project/text-files/pat.inc/pat-setup-cr.inc | 21 + project/text-files/pat.inc/pat-simple-cr.inc | 7 + project/text-files/pat.inc/pat-strings-cr.inc | 8 + project/text-files/setup.inc/setup-create-cr.inc | 24 + project/text-files/setup.inc/setup-encode-cr.inc | 26 + project/text-files/setup.inc/setup-exclude-cr.inc | 36 + project/text-files/setup.inc/setup-header-cr.inc | 15 + project/text-files/setup.inc/setup-include-cr.inc | 26 + project/text-files/setup.inc/setup-order-cr.inc | 14 + project/text-files/ui-head.inc | 41 + 371 files changed, 328455 insertions(+) create mode 100644 project/Makefile create mode 100644 project/Makefile.user create mode 100755 project/bin/wbmgt create mode 100755 project/bin/wctct create mode 100755 project/bin/wimgt create mode 100755 project/bin/wkclt create mode 100755 project/bin/wkmpt create mode 100755 project/bin/wlect create mode 100755 project/bin/wmdlt create mode 100755 project/bin/wpatt create mode 100755 project/bin/wstrt create mode 100755 project/bin/wszst create mode 100755 project/bin/wtest create mode 100755 project/bin/x86_64/wbmgt create mode 100755 project/bin/x86_64/wctct create mode 100755 project/bin/x86_64/wimgt create mode 100755 project/bin/x86_64/wkclt create mode 100755 project/bin/x86_64/wkmpt create mode 100755 project/bin/x86_64/wlect create mode 100755 project/bin/x86_64/wmdlt create mode 100755 project/bin/x86_64/wpatt create mode 100755 project/bin/x86_64/wstrt create mode 100755 project/bin/x86_64/wszst create mode 100755 project/bin/x86_64/wtest create mode 100644 project/dclib/Makefile.inc create mode 100644 project/dclib/dclib-basics.c create mode 100644 project/dclib/dclib-basics.h create mode 100644 project/dclib/dclib-color.c create mode 100644 project/dclib/dclib-color.h create mode 100644 project/dclib/dclib-debug.c create mode 100644 project/dclib/dclib-debug.h create mode 100644 project/dclib/dclib-file.c create mode 100644 project/dclib/dclib-file.h create mode 100644 project/dclib/dclib-gen-ui.h create mode 100644 project/dclib/dclib-gen-ui.inc create mode 100644 project/dclib/dclib-mysql.c create mode 100644 project/dclib/dclib-mysql.h create mode 100644 project/dclib/dclib-network-linux.c create mode 100644 project/dclib/dclib-network-linux.h create mode 100644 project/dclib/dclib-network.c create mode 100644 project/dclib/dclib-network.h create mode 100644 project/dclib/dclib-numeric.c create mode 100644 project/dclib/dclib-numeric.h create mode 100644 project/dclib/dclib-option.c create mode 100644 project/dclib/dclib-option.h create mode 100644 project/dclib/dclib-regex.c create mode 100644 project/dclib/dclib-regex.h create mode 100644 project/dclib/dclib-shift-jis.c create mode 100644 project/dclib/dclib-system.h create mode 100644 project/dclib/dclib-tables.c create mode 100644 project/dclib/dclib-types.h create mode 100644 project/dclib/dclib-ui.c create mode 100644 project/dclib/dclib-ui.h create mode 100644 project/dclib/dclib-utf8.c create mode 100644 project/dclib/dclib-utf8.h create mode 100644 project/dclib/dclib-vector.c create mode 100644 project/dclib/dclib-xdump.c create mode 100644 project/dclib/dclib-xdump.h create mode 100755 project/dclib/lib-bmg.c create mode 100755 project/dclib/lib-bmg.h create mode 100644 project/dclib/lib-dol.c create mode 100644 project/dclib/lib-dol.h create mode 100644 project/dclib/lib-mkw.c create mode 100644 project/dclib/lib-mkw.h create mode 100755 project/gen-template.sh create mode 100755 project/gen-text-file.sh create mode 100644 project/gpl-2.0.txt create mode 100644 project/makefiles-local/Makefile.local create mode 100644 project/makefiles-local/Makefile.local.i386 create mode 100644 project/makefiles-local/Makefile.local.mac create mode 100644 project/makefiles-local/Makefile.local.x86_64 create mode 100644 project/revision.sh create mode 100755 project/scripts/create-brres-lists.sh create mode 100644 project/scripts/distrib.list create mode 100644 project/scripts/kcl/lower-walls.txt create mode 100644 project/scripts/kcl/minimap.txt create mode 100644 project/scripts/kcl/remove-face-down-roads.txt create mode 100644 project/scripts/minimap.sh create mode 100644 project/scripts/patch-all-messages.sh create mode 100755 project/scripts/test-wszst.sh create mode 100644 project/scripts/test-wu8.sh create mode 100644 project/setup.sh create mode 100644 project/setup/INSTALL.txt create mode 100755 project/setup/cygwin-copy.sh create mode 100755 project/setup/install.sh create mode 100644 project/setup/sendto-install.bat create mode 100644 project/setup/sendto-install.sh create mode 100644 project/setup/sendto-uninstall.bat create mode 100644 project/setup/sendto-uninstall.sh create mode 100644 project/setup/szs.def create mode 100644 project/setup/version.h create mode 100644 project/setup/windows-install.exe create mode 100755 project/setup/windows-install.sh create mode 100644 project/setup/windows-sendto/adjust-minimap.bat create mode 100644 project/setup/windows-sendto/clean-KCL-of-SZS.bat create mode 100644 project/setup/windows-sendto/convert-to-SZS.bat create mode 100644 project/setup/windows-sendto/convert-to-WBZ.bat create mode 100644 project/setup/windows-sendto/create-autoadd-library.bat create mode 100644 project/setup/windows-sendto/normalize-SZS.bat create mode 100644 project/setup/windows-uninstall.bat create mode 100644 project/setup/windows-uninstall.sh create mode 100644 project/src/analyze.c create mode 100644 project/src/crypt.h create mode 100644 project/src/crypto/md32_common.h create mode 100644 project/src/crypto/sha1-586-cygwin.s create mode 100644 project/src/crypto/sha1-586-elf.s create mode 100644 project/src/crypto/sha1-x86_64.s create mode 100644 project/src/crypto/sha1_one.c create mode 100644 project/src/crypto/sha1dgst.c create mode 100644 project/src/crypto/sha_locl.h create mode 100644 project/src/crypto/ssl-asm.S create mode 100644 project/src/crypto/ssl-asm.h create mode 100644 project/src/crypto/wiimm-sha.h create mode 100644 project/src/db-ctcode.c create mode 100644 project/src/db-ctcode.h create mode 100644 project/src/db-dol.c create mode 100644 project/src/db-dol.h create mode 100644 project/src/db-file.c create mode 100644 project/src/db-file.h create mode 100644 project/src/db-kcl.c create mode 100644 project/src/db-kcl.h create mode 100644 project/src/db-mkw.c create mode 100644 project/src/db-mkw.h create mode 100644 project/src/db-object.c create mode 100644 project/src/db-object.h create mode 100644 project/src/file-type.c create mode 100644 project/src/file-type.h create mode 100644 project/src/getopt.c create mode 100644 project/src/lib-breff.c create mode 100644 project/src/lib-breff.h create mode 100644 project/src/lib-brres.c create mode 100644 project/src/lib-brres.h create mode 100644 project/src/lib-bzip2.c create mode 100644 project/src/lib-bzip2.h create mode 100755 project/src/lib-checksum.c create mode 100755 project/src/lib-checksum.h create mode 100644 project/src/lib-common.c create mode 100644 project/src/lib-common.h create mode 100644 project/src/lib-ctcode.c create mode 100644 project/src/lib-ctcode.h create mode 100644 project/src/lib-ctdata.c create mode 100644 project/src/lib-file.c create mode 100644 project/src/lib-geohit.c create mode 100644 project/src/lib-image.h create mode 100644 project/src/lib-image1.c create mode 100644 project/src/lib-image2.c create mode 100644 project/src/lib-image3.c create mode 100755 project/src/lib-itemslot.c create mode 100644 project/src/lib-kcl-image.c create mode 100644 project/src/lib-kcl.c create mode 100644 project/src/lib-kcl.h create mode 100755 project/src/lib-kmp-diff.c create mode 100644 project/src/lib-kmp-draw.c create mode 100644 project/src/lib-kmp-pflags.c create mode 100644 project/src/lib-kmp-text.c create mode 100644 project/src/lib-kmp.c create mode 100644 project/src/lib-kmp.h create mode 100644 project/src/lib-lecode.c create mode 100644 project/src/lib-lecode.h create mode 100644 project/src/lib-mdl.c create mode 100644 project/src/lib-mdl.h create mode 100755 project/src/lib-minigame.c create mode 100644 project/src/lib-numeric.c create mode 100644 project/src/lib-numeric.h create mode 100644 project/src/lib-object.c create mode 100644 project/src/lib-object.h create mode 100644 project/src/lib-objflow.c create mode 100644 project/src/lib-pack.c create mode 100644 project/src/lib-pack.h create mode 100644 project/src/lib-parser-func.c create mode 100644 project/src/lib-parser.c create mode 100644 project/src/lib-pat.c create mode 100644 project/src/lib-pat.h create mode 100644 project/src/lib-rarc.c create mode 100644 project/src/lib-rarc.h create mode 100644 project/src/lib-rkc.c create mode 100644 project/src/lib-rkc.h create mode 100644 project/src/lib-rkg.c create mode 100644 project/src/lib-rkg.h create mode 100644 project/src/lib-staticr.c create mode 100644 project/src/lib-staticr.h create mode 100644 project/src/lib-std.c create mode 100644 project/src/lib-std.h create mode 100644 project/src/lib-szs-create.c create mode 100644 project/src/lib-szs.c create mode 100644 project/src/lib-szs.h create mode 100644 project/src/lib-transform.c create mode 100644 project/src/lib-xbmg.c create mode 100644 project/src/lib-xbmg.h create mode 100644 project/src/libbz2/LICENSE create mode 100644 project/src/libbz2/blocksort.c create mode 100644 project/src/libbz2/bzlib.c create mode 100644 project/src/libbz2/bzlib.h create mode 100644 project/src/libbz2/bzlib_private.h create mode 100644 project/src/libbz2/compress.c create mode 100644 project/src/libbz2/crctable.c create mode 100644 project/src/libbz2/decompress.c create mode 100644 project/src/libbz2/huffman.c create mode 100644 project/src/libbz2/randtable.c create mode 100644 project/src/system.h create mode 100644 project/src/types.h create mode 100644 project/src/ui/gen-ui.c create mode 100644 project/src/ui/tab-wbmgt.inc create mode 100644 project/src/ui/tab-wctct.inc create mode 100644 project/src/ui/tab-wimgt.inc create mode 100644 project/src/ui/tab-wkclt.inc create mode 100644 project/src/ui/tab-wkmpt.inc create mode 100644 project/src/ui/tab-wlect.inc create mode 100644 project/src/ui/tab-wmdlt.inc create mode 100644 project/src/ui/tab-wpatt.inc create mode 100644 project/src/ui/tab-wstrt.inc create mode 100644 project/src/ui/tab-wszst.inc create mode 100644 project/src/ui/ui-wbmgt.c create mode 100644 project/src/ui/ui-wbmgt.h create mode 100644 project/src/ui/ui-wctct.c create mode 100644 project/src/ui/ui-wctct.h create mode 100644 project/src/ui/ui-wimgt.c create mode 100644 project/src/ui/ui-wimgt.h create mode 100644 project/src/ui/ui-wkclt.c create mode 100644 project/src/ui/ui-wkclt.h create mode 100644 project/src/ui/ui-wkmpt.c create mode 100644 project/src/ui/ui-wkmpt.h create mode 100644 project/src/ui/ui-wlect.c create mode 100644 project/src/ui/ui-wlect.h create mode 100644 project/src/ui/ui-wmdlt.c create mode 100644 project/src/ui/ui-wmdlt.h create mode 100644 project/src/ui/ui-wpatt.c create mode 100644 project/src/ui/ui-wpatt.h create mode 100644 project/src/ui/ui-wstrt.c create mode 100644 project/src/ui/ui-wstrt.h create mode 100644 project/src/ui/ui-wszst.c create mode 100644 project/src/ui/ui-wszst.h create mode 100644 project/src/ui/ui.def create mode 100644 project/src/ui/ui.h create mode 100644 project/src/wbmgt.c create mode 100644 project/src/wctct.c create mode 100644 project/src/wimgt.c create mode 100644 project/src/wkclt.c create mode 100644 project/src/wkmpt.c create mode 100644 project/src/wlect.c create mode 100644 project/src/wmdlt.c create mode 100644 project/src/wpatt.c create mode 100644 project/src/wrapper-wbmgt.c create mode 100644 project/src/wrapper-wctct.c create mode 100644 project/src/wrapper-wimgt.c create mode 100644 project/src/wrapper-wkclt.c create mode 100644 project/src/wrapper-wkmpt.c create mode 100644 project/src/wrapper-wlect.c create mode 100644 project/src/wrapper-wmdlt.c create mode 100644 project/src/wrapper-wpatt.c create mode 100644 project/src/wrapper-wstrt.c create mode 100644 project/src/wstrt.c create mode 100644 project/src/wszst.c create mode 100644 project/src/wtest.c create mode 100644 project/support/KCLFlags.xml create mode 100644 project/support/KMPItems.xml create mode 100644 project/support/kcl-types.txt create mode 100644 project/system.c create mode 100644 project/templates/DOCUMENTATION.txt create mode 100644 project/templates/HISTORY-v0.txt create mode 100644 project/templates/HISTORY-v1.txt create mode 100644 project/templates/HISTORY.txt create mode 100644 project/templates/MILESTONES.txt create mode 100644 project/templates/forums/mariokartwii.txt create mode 100644 project/templates/module/features-de.forum create mode 100644 project/templates/module/features-en.forum create mode 100644 project/templates/module/features.txt create mode 100644 project/templates/module/head.txt create mode 100644 project/templates/module/last-history.txt create mode 100644 project/text-files/common.inc/explain-objmgr-cr.inc create mode 100644 project/text-files/ctcode.inc/ctcode-head-cr.inc create mode 100644 project/text-files/ctcode.inc/ctcode-list-syntax-brief-cr.inc create mode 100644 project/text-files/ctcode.inc/ctcode-list-syntax-full-cr.inc create mode 100644 project/text-files/ctcode.inc/ctcode-setup-cr.inc create mode 100755 project/text-files/distrib.inc/distrib-head-cr.inc create mode 100644 project/text-files/distrib.inc/distrib-param-cr.inc create mode 100644 project/text-files/distrib.inc/distrib-tracks-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-item-head-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-item-info-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-item-objects-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-item-setup-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-kart-head-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-kart-info-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-kart-objects-cr.inc create mode 100644 project/text-files/geohit.inc/geohit-kart-setup-cr.inc create mode 100755 project/text-files/itemslot.inc/itemslot-head-cr.inc create mode 100755 project/text-files/itemslot.inc/itemslot-info-cr.inc create mode 100755 project/text-files/itemslot.inc/itemslot-param-cr.inc create mode 100755 project/text-files/itemslot.inc/itemslot-setup-cr.inc create mode 100755 project/text-files/itemslot.inc/itemslot-table-cr.inc create mode 100644 project/text-files/kcl.inc/kcl-dump-head-cr.inc create mode 100644 project/text-files/kcl.inc/kcl-dump-octlist-cr.inc create mode 100644 project/text-files/kcl.inc/kcl-dump-octree-cr.inc create mode 100644 project/text-files/kcl.inc/kcl-dump-tridata-cr.inc create mode 100644 project/text-files/kcl.inc/kcl-flag-template-cr.inc create mode 100644 project/text-files/kcl.inc/kcl-head-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-area-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-came-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-ckph-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-ckpt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-cnpt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-defaults-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-enph-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-enpt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-enpt-ropt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-gobj-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-gobj-tabhead-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-head-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-itph-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-itpt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-itpt-ropt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-jgpt-auto-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-jgpt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-ktpt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-macros-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-mspt-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-param1-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-param2-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-param3-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-ph-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-poti-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-pt-head-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-pt-info-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-pt-mask-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-pt2-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-pt2-head-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-setup-cr.inc create mode 100644 project/text-files/kmp.inc/kmp-stgi-cr.inc create mode 100644 project/text-files/lex.inc/lex-elem-cann-cr.inc create mode 100755 project/text-files/lex.inc/lex-elem-hipt-cr.inc create mode 100644 project/text-files/lex.inc/lex-elem-set1-cr.inc create mode 100644 project/text-files/lex.inc/lex-elem-test-cr.inc create mode 100755 project/text-files/lex.inc/lex-head-cr.inc create mode 100755 project/text-files/lex.inc/lex-hexdump-cr.inc create mode 100755 project/text-files/lex.inc/lex-setup-cr.inc create mode 100644 project/text-files/logo.inc create mode 100644 project/text-files/lpar.inc/lpar-head-cr.inc create mode 100644 project/text-files/lpar.inc/lpar-sect-chat-cr.inc create mode 100644 project/text-files/lpar.inc/lpar-sect-chat-example-cr.inc create mode 100644 project/text-files/lpar.inc/lpar-sect-param-cr.inc create mode 100644 project/text-files/mdl.inc/mdl-file-head-cr.inc create mode 100644 project/text-files/mdl.inc/mdl-header-cr.inc create mode 100755 project/text-files/mdl.inc/mdl-hexdump-cr.inc create mode 100644 project/text-files/mdl.inc/mdl-s0-cr.inc create mode 100644 project/text-files/mdl.inc/mdl-s0-tabhead-cr.inc create mode 100644 project/text-files/mdl.inc/mdl-setup-cr.inc create mode 100755 project/text-files/minigame.inc/minigame-head-cr.inc create mode 100755 project/text-files/minigame.inc/minigame-info-cr.inc create mode 100755 project/text-files/minigame.inc/minigame-param-cr.inc create mode 100755 project/text-files/minigame.inc/minigame-setup-cr.inc create mode 100755 project/text-files/minigame.inc/minigame-table-cr.inc create mode 100755 project/text-files/minigame.inc/minigame-table-head-cr.inc create mode 100644 project/text-files/obj-mtl-bz2.inc create mode 100644 project/text-files/objflow.inc/objflow-head-cr.inc create mode 100644 project/text-files/objflow.inc/objflow-info-cr.inc create mode 100644 project/text-files/objflow.inc/objflow-objects-cr.inc create mode 100644 project/text-files/objflow.inc/objflow-setup-cr.inc create mode 100644 project/text-files/pat.inc/pat-file-head-cr.inc create mode 100644 project/text-files/pat.inc/pat-header-cr.inc create mode 100644 project/text-files/pat.inc/pat-s0_base-cr.inc create mode 100644 project/text-files/pat.inc/pat-s0_list-cr.inc create mode 100644 project/text-files/pat.inc/pat-setup-cr.inc create mode 100644 project/text-files/pat.inc/pat-simple-cr.inc create mode 100644 project/text-files/pat.inc/pat-strings-cr.inc create mode 100755 project/text-files/setup.inc/setup-create-cr.inc create mode 100755 project/text-files/setup.inc/setup-encode-cr.inc create mode 100755 project/text-files/setup.inc/setup-exclude-cr.inc create mode 100755 project/text-files/setup.inc/setup-header-cr.inc create mode 100755 project/text-files/setup.inc/setup-include-cr.inc create mode 100644 project/text-files/setup.inc/setup-order-cr.inc create mode 100644 project/text-files/ui-head.inc diff --git a/project/Makefile b/project/Makefile new file mode 100644 index 0000000..d36fb5e --- /dev/null +++ b/project/Makefile @@ -0,0 +1,1094 @@ + +##################################################################### +## _______ _______ _______ ## +## | ___ |____ | ___ | ## +## | | |_| / /| | |_| ## +## | |_____ / / | |_____ ## +## |_____ | / / |_____ | ## +## _ | | / / _ | | ## +## | |___| |/ /____| |___| | ## +## |_______|_______|_______| ## +## ## +## Wiimms SZS Tools ## +## https://szs.wiimm.de/ ## +## ## +##################################################################### +## ## +## This file is part of the SZS project. ## +## Visit https://szs.wiimm.de/ for project details and sources. ## +## ## +## Copyright (c) 2011-2020 by Dirk Clemens ## +## ## +##################################################################### + +#---------------------------------------------------------------- +# make manual: http://www.gnu.org/software/make/manual/make.html +#---------------------------------------------------------------- + +#------------------------------------------------------------------------------- +# global settings + +MYMAKE := $(lastword $(MAKEFILE_LIST)) + +#SHELL = /bin/bash +SHELL = /usr/bin/env bash + +AUTHOR = Dirk Clemens +TOOLSET_SHORT = SZS +TOOLSET_LONG = Wiimms SZS Toolset + +WSZST_SHORT = wszst +WSZST_LONG = Wiimms SZS Tool +WBMGT_SHORT = wbmgt +WBMGT_LONG = Wiimms BMG Tool +WCTCT_SHORT = wctct +WCTCT_LONG = Wiimms CT-CODE Tool +WIMGT_SHORT = wimgt +WIMGT_LONG = Wiimms Image Tool +WKCLT_SHORT = wkclt +WKCLT_LONG = Wiimms KCL Tool +WKMPT_SHORT = wkmpt +WKMPT_LONG = Wiimms KMP Tool +WLECT_SHORT = wlect +WLECT_LONG = Wiimms LE-CODE Tool +WMDLT_SHORT = wmdlt +WMDLT_LONG = Wiimms MDL Tool +WPATT_SHORT = wpatt +WPATT_LONG = Wiimms PAT Tool +WSTRT_SHORT = wstrt +WSTRT_LONG = Wiimms StaticR Tool + +VERSION_NUM = 2.18a +BETA_VERSION = 0 + # 0:off -1:"beta" >0:"beta#" + +URI_HOME = https://szs.wiimm.de/ +URI_DOWNLOAD = https://szs.wiimm.de/download +URI_FILE = https://szs.wiimm.de/file +URI_SOURCE = https://download.wiimm.de/source/wiimms-szs-tools/ + +ifeq ($(BETA_VERSION),0) + URI_REPOS = http://opensvn.wiimm.de/wii/trunk/wiimms-szs-tools/ + URI_VIEWVC = https://szs.wiimm.de/r/viewvc +else + URI_REPOS = http://opensvn.wiimm.de/wii/branches/public/wiimms-szs-tools/ + URI_VIEWVC = https://szs.wiimm.de/r/viewvc-beta +endif + +URI_DOWNLOAD_I386 = $(URI_DOWNLOAD)/$(DISTRIB_I386) +URI_DOWNLOAD_X86_64 = $(URI_DOWNLOAD)/$(DISTRIB_X86_64) +URI_DOWNLOAD_MAC = $(URI_DOWNLOAD)/$(DISTRIB_MAC) +URI_DOWNLOAD_CYGWIN = $(URI_DOWNLOAD)/$(DISTRIB_CYGWIN) + +DOWNLOAD_DIR = /n/www/szs.wiimm.de/download +EXPORT_DIR = /cygdrive/n/wit/wiimms-szs-tools/bin + +# format for logging messages, $1=job, $2=object, $3=more info +LOGFORMAT := \33[36;1m*** %7s %-17s %s\33[0m\n +LOGFORMAT_CC := \33[33;1m*** %7s %-17s %s\33[0m\n +LOGFORMAT_TOOL := \33[35;1m*** %7s %-17s %s\33[0m\n +LOGFORMAT_INST := \33[32;1m*** %7s %-17s %s\33[0m\n + +#------------------------------------------------------------------------------- +# version+beta settings + +ifeq ($(BETA_VERSION),0) + BETA_SUFFIX := +else ifeq ($(BETA_VERSION),-1) + BETA_SUFFIX := .beta +else + BETA_SUFFIX := .beta$(BETA_VERSION) +endif + +VERSION := $(VERSION_NUM)$(BETA_SUFFIX) +VERSION_INT := $(shell sed 's/\.//g; s/[^0-9]/ /g; s/ .*//' <<< "$(VERSION_NUM)" ) + +DUMMY := $(shell $(SHELL) ./setup.sh $(VERSION) $(VERSION_NUM) $(VERSION_INT) ) +include Makefile.setup + +#------------------------------------------------------------------------------ +# include source library definitions + +DCLIB_MYSQL := 0 +include ./dclib/Makefile.inc + +#------------------------------------------------------------------------------- +# compiler settings + +PRE ?= +CC = $(PRE)gcc +CPP = $(PRE)g++ +STRIP = $(PRE)strip + +#------------------------------------------------------------------------------- +# files + +DIR_LIST = + +RM_FILES = *.{a,o,d,tmp,bak,exe} edit-list/*.log +RM_FILES += */*.{tmp,bak} */*/*.{tmp,bak} */*/*/*.{tmp,bak} +RM_FILES2 = templates.sed + +MODE_FILE = ./_mode.flag +MODE = $(shell test -s $(MODE_FILE) && cat $(MODE_FILE)) +RM_FILES += $(MODE_FILE) + +UI = ./src/ui + +#------------------------------------------------------------------------------- +# base flags + +DEFINES1 += -DLARGE_FILES -D_FILE_OFFSET_BITS=64 +DEFINES1 += -DWSZST # compile szs tools (switch for shared sources) +DEFINES1 += -DDEBUG_ASSERT # enable ASSERTions in release version too +DEFINES1 += -DEXTENDED_ERRORS=1 # enable extended error messages (function,line,file) +DEFINES1 += -DUIOPT_INDEX_SIZE=300 # define max number of options (default 0x100) + +CFLAGS += -fomit-frame-pointer -fno-strict-aliasing -funroll-loops +CFLAGS += -Wall -Wno-parentheses -Wno-unused-function +CFLAGS += -Wno-deprecated-declarations +CFLAGS += -O3 -Isrc -I$(UI) -I. + +DEPFLAGS += -MMD + +#--- system depedent flags & libs + +ifeq ($(SYSTEM),mac) + CFLAGS += -I/usr/local/include +endif + +ifeq ($(SYSTEM_LINUX),1) + CFLAGS += -ffunction-sections -fdata-sections + LDFLAGS += -Xlinker --gc-sections + #LDFLAGS += -Xlinker --print-gc-sections + LIBS += -ltinfo + LDFLAGS += -static-libgcc +endif + +ifeq ($(SYSTEM),cygwin) + LDFLAGS += -static-libgcc +endif + +#ifeq ($(HAVE_PCRE),1) +#ifeq ($(STATIC),1) +# LIBS += $(PCRE_A) +#else +# LIBS += -lpcre +#endif +# DEFINES1 += -DHAVE_PCRE=1 +#endif + +#------------------------------------------------------------------------------- +# tools + +MAIN_TOOLS := wszst wbmgt wctct wimgt wkclt wkmpt wlect wmdlt wpatt wstrt +TEST_TOOLS := wtest +EXTRA_TOOLS := +HELPER_TOOLS := gen-ui + +WRAPPER_TOOLS := $(filter-out wszst, $(MAIN_TOOLS)) + +ALL_TOOLS := $(sort $(MAIN_TOOLS) $(TEST_TOOLS)) +ALL_TOOLS_X := $(sort $(MAIN_TOOLS) $(TEST_TOOLS) $(EXTRA_TOOLS)) + +RM_FILES += $(ALL_TOOLS_X) $(HELPER_TOOLS) gen-ui + +#------------------------------------------------------------------------------- +# tool dependent options and objects + +ifeq ($(STATIC),1) + OPT_STATIC := -static -DDCLIB_STATIC=1 +else + OPT_STATIC := +endif + +#--------------- + +TOPT_wszst := $(OPT_STATIC) +TOPT_wbmgt := $(OPT_STATIC) +TOPT_wctct := $(OPT_STATIC) +TOPT_wimgt := $(OPT_STATIC) +TOPT_wkclt := $(OPT_STATIC) +TOPT_wkmpt := $(OPT_STATIC) +TOPT_wlect := $(OPT_STATIC) +TOPT_wmdlt := $(OPT_STATIC) +TOPT_wpatt := $(OPT_STATIC) +TOPT_wstrt := $(OPT_STATIC) + +#--------------- + +WRAPPER_OBJ = $(patsubst %,wrapper-%.o,$(WRAPPER_TOOLS)) + +XOBJ_IMAGE = lib-image1.o lib-image2.o lib-image3.o +XOBJ_PARSER = lib-parser.o lib-parser-func.o +XOBJ_OBJECT = lib-object.o db-object.o db-file.o lib-bmg.o +XOBJ_KCL = lib-kcl.o db-kcl.o +XOBJ_KMP = lib-kmp.o lib-kmp-text.o lib-kmp-pflags.o lib-kmp-diff.o lib-kmp-draw.o +XOBJ_COMMON = lib-common.o lib-geohit.o lib-itemslot.o lib-minigame.o lib-objflow.o + +TOBJ_gen-ui = $(XOBJ_OBJECT) + +TOBJ_wszst = analyze.o lib-szs-create.o lib-xbmg.o \ + lib-mdl.o lib-pat.o lib-transform.o \ + lib-ctcode.o lib-lecode.o \ + lib-staticr.o db-dol.o db-ctcode.o getopt.o \ + $(XOBJ_KMP) $(XOBJ_COMMON) $(XOBJ_KCL) lib-kcl-image.o lib-rkg.o \ + $(XOBJ_OBJECT) $(XOBJ_PARSER) $(XOBJ_IMAGE) \ + $(WRAPPER_OBJ) +TOBJ_wbmgt = lib-xbmg.o $(XOBJ_OBJECT) +TOBJ_wctct = lib-ctcode.o lib-lecode.o lib-xbmg.o db-ctcode.o \ + $(XOBJ_PARSER) $(XOBJ_IMAGE) $(XOBJ_OBJECT) +TOBJ_wimgt = $(XOBJ_IMAGE) $(XOBJ_OBJECT) +TOBJ_wkclt = lib-transform.o $(XOBJ_KCL) $(XOBJ_PARSER) $(XOBJ_OBJECT) +TOBJ_wkmpt = $(XOBJ_KMP) lib-transform.o \ + $(XOBJ_KCL) lib-kcl-image.o \ + $(XOBJ_IMAGE) $(XOBJ_PARSER) $(XOBJ_OBJECT) +TOBJ_wlect = lib-lecode.o lib-ctcode.o lib-xbmg.o db-ctcode.o \ + $(XOBJ_PARSER) $(XOBJ_IMAGE) $(XOBJ_OBJECT) +TOBJ_wmdlt = lib-mdl.o lib-transform.o $(XOBJ_PARSER) $(XOBJ_OBJECT) +TOBJ_wpatt = lib-pat.o $(XOBJ_PARSER) $(XOBJ_OBJECT) +TOBJ_wstrt = lib-staticr.o db-dol.o db-ctcode.o $(XOBJ_OBJECT) +TOBJ_wtest = $(XOBJ_KMP) $(XOBJ_KCL) $(XOBJ_OBJECT) $(XOBJ_PARSER) $(XOBJ_IMAGE) \ + lib-mdl.o lib-transform.o + +#------------------------------------------------------------------------------- +# sub libs + +# libbz2 +LIBBZ2_SRC = $(shell echo src/libbz2/*.c) +LIBBZ2_OBJ = $(patsubst %.c,%.o,$(LIBBZ2_SRC)) + +ifneq ($(SYSTEM),mac) + BZIP2_FLAGS = -Wno-maybe-uninitialized +endif + +# lib summary +LIB_LIST += libbz2 +LIB_OBJECTS += $(LIBBZ2_OBJ) +RM_FILES += $(foreach l,$(LIB_LIST),src/$(l)/*.{d,o}) + +#------------------------------------------------------------------------------- +# object files + +TOBJ_ALL = $(sort $(TOBJ_wszst) $(TOBJ_wbmgt) $(TOBJ_wctct) \ + $(TOBJ_wimgt) $(TOBJ_wkclt) $(TOBJ_wkmpt) \ + $(TOBJ_wlect) $(TOBJ_wmdlt) $(TOBJ_wpatt) \ + $(TOBJ_wstrt) $(TOBJ_wtest) $(TOBJ_szs) ) +TOBJ_NO_WRAPPER = $(filter-out $(WRAPPER_OBJ), $(TOBJ_ALL) ) + +# objects of tools +MAIN_TOOLS_OBJ = $(patsubst %,%.o,$(MAIN_TOOLS) $(EXTRA_TOOLS)) +OTHER_TOOLS_OBJ = $(patsubst %,%.o,$(TEST_TOOLS) $(HELPER_TOOLS)) + +# dclib +DCLIB_DIR := ./dclib +DIR_LIST += $(DCLIB_DIR) +VPATH += $(DCLIB_DIR) +CFLAGS += -I$(DCLIB_DIR) + +# other objects +SZS_O := lib-std.o lib-file.o lib-numeric.o \ + lib-szs.o lib-checksum.o \ + lib-rarc.o lib-pack.o lib-rkc.o lib-brres.o lib-breff.o \ + file-type.o db-mkw.o lib-mkw.o lib-dol.o \ + sha1dgst.o sha1_one.o lib-bzip2.o \ + $(DCLIB_O) + +# object groups +UI_OBJECTS = $(sort $(MAIN_TOOLS_OBJ)) +C_OBJECTS = $(sort $(OTHER_TOOLS_OBJ) $(SZS_O) $(TOBJ_NO_WRAPPER)) +ASM_OBJECTS = ssl-asm.o + +# all objects + sources +ALL_OBJECTS = $(sort $(SZS_O) $(ASM_OBJECTS) $(LIB_OBJECTS)) +ALL_SOURCES = $(patsubst %.o,%.c,$(UI_OBJECTS) $(C_OBJECTS) \ + $(WRAPPER_OBJ) $(ASM_OBJECTS)) + +#------------------------------------------------------------------------------- +# source files + +UI_FILES = ui.def +UI_FILES += $(patsubst %,ui-%.c,$(MAIN_TOOLS) $(EXTRA_TOOLS)) +UI_FILES += $(patsubst %,ui-%.h,$(MAIN_TOOLS) $(EXTRA_TOOLS)) + +UI_TABS = $(patsubst %,tab-%.inc,$(MAIN_TOOLS) $(EXTRA_TOOLS)) + +SETUP_DIR = ./setup +SETUP_INFO = INSTALL.txt +SETUP_FILES = version.h install.sh cygwin-copy.sh szs.def \ + $(SETUP_INFO) $(CYGWIN_SCRIPTS) +DIR_LIST += $(SETUP_DIR) +RM_FILES2 += $(SETUP_FILES) + +TEXT_DIR = ./text-files +TEXT_FILES = $(shell cd $(TEXT_DIR) && echo *.inc) +DIR_LIST += $(TEXT_DIR) +RM_FILES2 += $(TEXT_FILES) + +#------------------------------------------------------------------------------- +# lib export + +LIBEXPORT = ./_lib-export + +LIBEX_FILES = src/lib-*.[ch] +LIBEX_FILES += src/system.h src/types.h src/debug.h + +#------------------------------------------------------------------------------- + +INSTALL_SCRIPTS = install.sh +RM_FILES += $(INSTALL_SCRIPTS) +TEMPLATES = ./templates +MODULES = $(TEMPLATES)/module +GEN_TEMPLATE = ./gen-template.sh +GEN_TEXT_FILE = ./gen-text-file.sh +DIR_LIST += $(TEMPLATES) $(MODULES) + +VPATH += src src/crypto $(UI) +DIR_LIST += src src/crypto $(UI) + + +#--- external flags & clean + +DEFINES = $(strip $(DEFINES1) $(MODE) $(XDEF)) +CFLAGS += $(XFLAGS) +CFLAGS := $(strip $(CFLAGS)) +LDFLAGS := $(strip $(LDFLAGS)) +LIBS += $(LIBPNG) -lm -lncurses $(XLIBS) + +#------------------------------------------------------------------------------- + +DISTRIB_RM = ./szs-v$(VERSION)-r +DISTRIB_BASE = szs-v$(VERSION)-r$(REVISION_NEXT) +DISTRIB_PATH = ./$(DISTRIB_BASE)-$(SYSTEM) +DISTRIB_I386 = $(DISTRIB_BASE)-i386.tar.gz +DISTRIB_X86_64 = $(DISTRIB_BASE)-x86_64.tar.gz +DISTRIB_MAC = $(DISTRIB_BASE)-mac.tar.gz +DISTRIB_CYGWIN = $(DISTRIB_BASE)-cygwin.zip +DISTRIB_FILES = gpl-2.0.txt $(INSTALL_SCRIPTS) $(SETUP_INFO) + +DOC_FILES = doc/*.txt +IGNORE_DOC_FILES= HISTORY-v*.txt + +SUPPORT_DIR = ./support +SUPPORT_FILES = $(SUPPORT_DIR)/*.txt $(SUPPORT_DIR)/*.xml +DIR_LIST += $(SUPPORT_DIR) + +BIN_FILES = $(MAIN_TOOLS) $(EXTRA_TOOLS) +SHARE_FILES = + +CYGWIN_DIR = /usr/bin +CYGWIN_TOOLS = bash chmod cp env grep mkdir realpath regtool stat tr +CYGWIN_SCRIPTS = windows-install.sh windows-uninstall.sh windows-uninstall.bat \ + sendto-install.sh sendto-uninstall.sh +CYGWIN_INSTALL = $(SETUP_DIR)/windows-install.exe \ + $(SETUP_DIR)/sendto-install.bat \ + $(SETUP_DIR)/sendto-uninstall.bat +CYGWIN_UNINSTALL= $(SETUP_DIR)/windows-uninstall.bat + +WIN_INSTALL_PATH= Wiimm/SZS + +DIR_LIST_BIN = $(SCRIPTS) bin +DIR_LIST += $(DIR_LIST_BIN) +DIR_LIST += share makefiles-local edit-list + +# +############################################################################### +# default rule + +default_rule: all + @echo "HINT: try 'make help'" + +# include this behind the default rule +-include $(ALL_SOURCES:.c=.d) +-include $(TEXT_FILES:.inc=.inc.d) + +# +############################################################################### +# local definitions + +-include makefiles-local/Makefile.local.$(SYSTEM) +-include Makefile.user + +ifeq ($(WIIMM).$(NOWIIMM).$(HAVE_WORK),1..1) + DIR_LIST += ./work +endif + +ifeq ($(WIIMM).$(NOWIIMM).$(HAVE_WORK_SRC),1..1) + DEFINES1 += -DHAVE_WIIMM_EXT=1 + CFLAGS += -I./work/src + VPATH += ./work/src + DIR_LIST += ./work/src + TOBJ_wszst += lib-vehicle.o db-vehicle.o wcommand.o + #LIBS += -lncurses +endif + +# +############################################################################### +# general rules +# -Xlinker -Map=$@.map + +$(ALL_TOOLS_X): %: %.o $(ALL_OBJECTS) $(TOBJ_ALL) Makefile | $(HELPER_TOOLS) $(UI_OBJECTS) + @printf "$(LOGFORMAT_TOOL)" tool "$@" "$(MODE) $(TOPT_$@)" + @$(CC) $(CFLAGS) $(DEFINES) $(LDFLAGS) $@.o \ + $(ALL_OBJECTS) $(TOBJ_$@) $(LIBS) $(TOPT_$@) -o $@ + @if test -f $@.exe; then $(STRIP) $@.exe; else $(STRIP) $@; fi + + @mkdir -p bin/$(SYSTEM) bin/$(SYSTEM)/debug + @if test -s $(MODE_FILE) && grep -Fq -e -DDEBUG $(MODE_FILE); \ + then cp -p $@ bin/$(SYSTEM)/debug/; \ + else cp -p $@ bin/; cp -p $@ bin/$(SYSTEM)/; fi + +#-------------------------- + +$(HELPER_TOOLS): %: %.o $(ALL_OBJECTS) $(TOBJ_NO_WRAPPER) $(UI_TABS) Makefile + @printf "$(LOGFORMAT_TOOL)" helper "$@ $(TOBJ_$@)" "$(MODE)" + @$(CC) $(CFLAGS) $(DEFINES) $(LDFLAGS) $@.o \ + $(ALL_OBJECTS) $(TOBJ_$@) $(LIBS) -o $@ + +#-------------------------- + +$(UI_OBJECTS): %.o: %.c ui-%.c ui-%.h version.h Makefile + @printf "$(LOGFORMAT_CC)" +object "$@" "$(MODE)" + @$(CC) $(CFLAGS) $(DEPFLAGS) $(DEFINES) -c $< -o $@ + +#-------------------------- + +$(C_OBJECTS): %.o: %.c version.h Makefile | $(TEXT_FILES) + @printf "$(LOGFORMAT_CC)" object "$@" "$(MODE)" + @$(CC) $(CFLAGS) $(DEPFLAGS) $(DEFINES) -c $< -o $@ + +#-------------------------- + +$(WRAPPER_OBJ): %.o: %.c version.h Makefile | $(TEXT_FILES) gen-ui + @printf "$(LOGFORMAT_CC)" object "$@" "$(MODE)" + @$(CC) $(CFLAGS) $(DEPFLAGS) $(DEFINES) -c $< -o $@ + +#-------------------------- + +$(ASM_OBJECTS): %.o: %.S Makefile + @printf "$(LOGFORMAT_CC)" asm "$@" "$(MODE)" + @$(CC) $(CFLAGS) $(DEPFLAGS) $(DEFINES) -c $< -o $@ + +#-------------------------- + +$(SETUP_FILES): templates.sed $(SETUP_DIR)/$@ + @printf "$(LOGFORMAT)" create "$@" "" + @chmod 775 $(GEN_TEMPLATE) + @$(GEN_TEMPLATE) $@ + +.PHONY : setup-files +setup-files: templates.sed version.h install.sh + +#-------------------------- + +$(TEXT_FILES): $(GEN_TEXT_FILE) + @printf "$(LOGFORMAT)" text "$@" "" + @chmod 775 $(GEN_TEXT_FILE) + @$(GEN_TEXT_FILE) $(TEXT_DIR) $@ + +#-------------------------- + +$(UI_FILES): gen-ui.c ui.h $(UI_TABS) | gen-ui + @printf "$(LOGFORMAT)" run gen-ui "" + @./gen-ui + +.PHONY : run-ui +run-ui : gen-ui.c ui.h $(UI_TABS) $(UI_FILES) | gen-ui +# @printf "$(LOGFORMAT)" run gen-ui +# @./gen-ui + +# +############################################################################### +# lib specific rules + +$(LIBBZ2_OBJ): %.o: %.c Makefile + @printf "$(LOGFORMAT_CC)" object "$(subst src/libbz2/,,$@)" "$(MODE) [libbz2]" + @$(CC) $(CFLAGS) $(DEPFLAGS) $(BZIP2_FLAGS) $(DEFINES) -c $< -o $@ + +# +############################################################################### +# specific rules in alphabetic order + +.PHONY : all +all: setup-files $(TEXT_FILES) $(HELPER_TOOLS) $(ALL_TOOLS) $(INSTALL_SCRIPTS) $(SETUP_INFO) + +.PHONY : all+ +all+: clean+ all distrib + +# +#-------------------------- + +.PHONY : ch+ +ch+: chmod chown chgrp + +# +#-------------------------- + +.PHONY : chmod +chmod: + @printf "$(LOGFORMAT)" chmod 775/664 "" + @for d in . $(DIR_LIST); do test -d "$$d" && chmod ug+rw "$$d"/*; done || true + @for d in . $(DIR_LIST); do test -d "$$d" && chmod -f a+x "$$d"/*.sh; done || true + @for d in $(DIR_LIST); do test -d "$$d" && chmod 775 "$$d"; done || true + @for t in $(ALL_TOOLS_X); do test -f "$$t" && chmod 775 "$$t"; done || true + @chmod 664 src/*.[hc] src/*/*.[hc] dclib/*.[hc] + +# +#-------------------------- + +.PHONY : chown +chown: + @printf "$(LOGFORMAT)" chown "-R $$( stat -c%u . 2>/dev/null || stat -f%u . ) ." "" + @chown "$$( stat -c%u . 2>/dev/null || stat -f%u . )" * + @for d in $(DIR_LIST); do test -d "$$d" && \ + chown -R "$$( stat -c%u . 2>/dev/null || stat -f%u . )" "$$d"; done || true + +# +#-------------------------- + +.PHONY : chgrp +chgrp: + @printf "$(LOGFORMAT)" chgrp "-R $$( stat -c%g . 2>/dev/null || stat -f%g . ) ." "" + @chgrp "$$( stat -c%u . 2>/dev/null || stat -f%u . )" * + @for d in $(DIR_LIST); do test -d "$$d" && \ + chgrp -R "$$( stat -c%g . 2>/dev/null || stat -f%g . )" "$$d"; done || true + +# +#-------------------------- + +.PHONY : clean +clean: + @printf "$(LOGFORMAT)" rm "output files + distrib" "" + -@rm -fr _start-pos.tmp + @find -type d -name test.tmp -exec rm -rf {} + + @rm -f $(RM_FILES) + @rm -fr $(DISTRIB_RM)* $(LIBEXPORT) + @cd dclib && rm -f $(DCLIB_PRIVATE) + +.phony : do_clean+ +do_clean+: clean + @printf "$(LOGFORMAT)" rm "test files + template output" "" + -@rm -f $(RM_FILES2) + -@rm -fr doc + +.PHONY : c+ clean+ +c+ clean+: do_clean+ + +.PHONY : c++ clean++ +c++ clean++: do_clean+ st + +# +#-------------------------- + +.PHONY: dclib +dclib: + @echo "DCLIB_MYSQL=$(DCLIB_MYSQL)" + @echo "DCLIB_FLAGS=$(DCLIB_FLAGS)" + @echo "DCLIB_O=$(DCLIB_O)" + +# +#-------------------------- + +.PHONY : debug +debug: + @printf "$(LOGFORMAT)" enable debug "-> define -DDEBUG" + @rm -f *.o $(ALL_TOOLS_X) + @echo "-DDEBUG" >>$(MODE_FILE) + @sort $(MODE_FILE) | uniq > $(MODE_FILE).tmp +# 2 steps to bypass a cygwin mv failure + @cp $(MODE_FILE).tmp $(MODE_FILE) + @rm -f $(MODE_FILE).tmp + +# +#-------------------------- + +.PHONY : distrib2 +distrib2: +ifeq ($(SYSTEM_LINUX),1) + + @printf "\n---------- DISTRIB LINUX/I386 ----------\n\n" + @for t in $(ALL_TOOLS_X); do rm -f bin/$$t; done + @$(MAKE) --no-print-directory clean+ + @M32=1 $(MAKE) --no-print-directory distrib + @mv "$(DISTRIB_I386)" "save-$(DISTRIB_I386)" + + @printf "\n---------- DISTRIB LINUX/X86_64 ----------\n\n" + @for t in $(ALL_TOOLS_X); do rm -f bin/$$t; done + @$(MAKE) --no-print-directory clean+ + @$(MAKE) --no-print-directory distrib + @mv "save-$(DISTRIB_I386)" "$(DISTRIB_I386)" + +else + @$(MAKE) --no-print-directory clean+ distrib +endif + +#----- + +.PHONY : distrib +distrib: +ifeq ($(SYSTEM),mac) + @NOWIIMM=1 $(MAKE) --no-print-directory mac-distrib +else ifeq ($(SYSTEM),cygwin) + NOWIIMM=1 $(MAKE) --no-print-directory all doc cygwin-copy.sh \ + $(CYGWIN_SCRIPTS) gen-distrib info +else + NOWIIMM=1 STATIC=1 $(MAKE) --no-print-directory all doc gen-distrib info +endif + +#----- + +.PHONY : gen-distrib +gen-distrib: + @printf "$(LOGFORMAT)" create "$(DISTRIB_PATH)" "" + +ifeq ($(SYSTEM),cygwin) + + @rm -rf $(DISTRIB_PATH)/* 2>/dev/null || true + @rm -rf $(DISTRIB_PATH) 2>/dev/null || true + @mkdir -p $(DISTRIB_PATH)/bin/windows-sendto + @mkdir -p $(DISTRIB_PATH)/scripts + @mkdir -p $(DISTRIB_PATH)/doc $(DISTRIB_PATH)/support + @./cygwin-copy.sh + @printf '@cmd\r\n' >$(DISTRIB_PATH)/bin/szs-console.bat + @cp -p gpl-2.0.txt $(DISTRIB_PATH) + @ln -f $(MAIN_TOOLS) $(CYGWIN_SCRIPTS) $(CYGWIN_INSTALL) $(DISTRIB_PATH)/bin + @ln -f $(SETUP_DIR)/windows-sendto/*.bat $(DISTRIB_PATH)/bin/windows-sendto + @ln -f $(CYGWIN_INSTALL) $(CYGWIN_UNINSTALL) $(SETUP_INFO) $(DISTRIB_PATH) + @cp -p $(DOC_FILES) $(DISTRIB_PATH)/doc + @cp -p $(SUPPORT_FILES) $(DISTRIB_PATH)/support + @rm -f $(DISTRIB_PATH)/doc/$(IGNORE_DOC_FILES) + @cp -rp $$( cat ./scripts/distrib.list ) $(DISTRIB_PATH)/scripts/ + @rm -rf $(DISTRIB_PATH)/scripts/*/.svn + + @zip -roq $(DISTRIB_PATH).zip $(DISTRIB_PATH) + @chmod 664 $(DISTRIB_PATH).zip + +else + + @rm -rf $(DISTRIB_PATH) + @mkdir -p $(DISTRIB_PATH)/bin $(DISTRIB_PATH)/scripts + @mkdir -p $(DISTRIB_PATH)/doc $(DISTRIB_PATH)/support + + @cp -p $(DISTRIB_FILES) $(DISTRIB_PATH) + @ln -f $(MAIN_TOOLS) $(DISTRIB_PATH)/bin + @for t in $(EXTRA_TOOLS); do [[ -f bin/$(SYSTEM)/$$t ]] \ + && ln -f bin/$(SYSTEM)/$$t $(DISTRIB_PATH)/bin; done || true + @cp -p $(DOC_FILES) $(DISTRIB_PATH)/doc + @cp -p $(SUPPORT_FILES) $(DISTRIB_PATH)/support + @rm -f $(DISTRIB_PATH)/doc/$(IGNORE_DOC_FILES) + @cp -rp $$( cat ./scripts/distrib.list ) $(DISTRIB_PATH)/scripts/ + @rm -rf $(DISTRIB_PATH)/scripts/*/.svn + + @chmod -R 664 $(DISTRIB_PATH) + @chmod a+x $(DISTRIB_PATH)/*.sh $(DISTRIB_PATH)/bin*/* + -@ chmod a+x $(DISTRIB_PATH)/scripts/*.sh 2>/dev/null + @chmod -R a+X $(DISTRIB_PATH) + + @tar -czf $(DISTRIB_PATH).tar.gz $(DISTRIB_PATH) + @chmod 664 $(DISTRIB_PATH).tar.gz + +endif + +#----- + +.PHONY : copy-distrib +copy-distrib: distrib2 + @printf "$(LOGFORMAT)" copy "$(DISTRIB_BASE).*" "-> $(DOWNLOAD_DIR)" + @[[ -s "$(DISTRIB_I386)" ]] \ + && cp --preserve=time "$(DISTRIB_I386)" "$(DOWNLOAD_DIR)" \ + || true + @[[ -s "$(DISTRIB_X86_64)" ]] \ + && cp --preserve=time "$(DISTRIB_X86_64)" "$(DOWNLOAD_DIR)" \ + || true + @[[ -s "$(DISTRIB_CYGWIN)" ]] \ + && cp --preserve=time "$(DISTRIB_CYGWIN)" "$(DOWNLOAD_DIR)" \ + || true + +# +#-------------------------- + +.PHONY : doc +doc: $(MAIN_TOOLS) templates.sed gen-doc + @mkdir -p ./doc + +.PHONY : gen-doc +gen-doc: + @printf "$(LOGFORMAT)" create documentation "" + @chmod ug+x $(GEN_TEMPLATE) + @$(GEN_TEMPLATE) + +.PHONY : doc+ +doc+: doc info + +# +#-------------------------- + +.PHONY : flags +flags: + @echo "" + @echo "DEFINES: $(DEFINES)" + @echo "" + @echo "CFLAGS: $(CFLAGS)" + @echo "" + @echo "LDFLAGS: $(LDFLAGS)" + @echo "" + @echo "LIBS: $(LIBS)" + @echo "" + @echo "C_OBJECTS: $(C_OBJECTS)" + @echo "" + @echo "UI_FILES: $(UI_FILES)" + @echo "" + @echo "ALL_TOOLS: $(ALL_TOOLS)" + @echo "" + @echo "WRAPPER_TOOLS: $(WRAPPER_TOOLS)" + @echo "" + @echo "ALL_TOOLS_X: $(ALL_TOOLS_X)" + @echo "" + +# +#-------------------------- + +.PHONY : info +info: szs.def wszst wkclt wkmpt + @printf "$(LOGFORMAT)" create info.def "" + @mkdir -p doc + @./wszst _export makedoc > doc/info.def + @./wkclt _export makedoc functions >> doc/info.def + @./wkmpt _export makedoc functions >> doc/info.def + +# +#-------------------------- + +.PHONY : install +install: all +ifeq ($(SYSTEM),cygwin) + @[[ -d $(DISTRIB_PATH)/bin ]] || make distrib + @chmod a+x "$(DISTRIB_PATH)/windows-install.exe" + @( cd "$(DISTRIB_PATH)" && ./windows-install.exe ) +else + @chmod a+x install.sh + @./install.sh --make +endif + +.PHONY : install+ +install+: clean+ all + @chmod a+x install.sh + @./install.sh --make + +#-------------------------- + +.PHONY : install2 +install2: +ifeq ($(SYSTEM_LINUX),1) + + @printf "\n---------- INSTALL LINUX/I386 ----------\n\n" + @for t in $(ALL_TOOLS_X); do rm -f bin/$$t; done + @$(MAKE) --no-print-directory clean+ + @M32=1 $(MAKE) -j --no-print-directory install + +ifeq ($(HAVE_INSTBIN_32),1) + @printf "$(LOGFORMAT)" copy "$(INSTBIN)/* to $(INSTBIN_32)" + @for f in $(BIN_FILES); do [[ -f $(INSTBIN)/$$f ]] \ + && cp -p $(INSTBIN)/$$f $(INSTBIN_32); done; true +endif + + @printf "\n---------- INSTALL LINUX/X86_64 ----------\n\n" + @for t in $(ALL_TOOLS_X); do rm -f bin/$$t; done + @$(MAKE) --no-print-directory clean+ + @$(MAKE) -j --no-print-directory install doc info + +ifeq ($(HAVE_INSTBIN_64),1) + @printf "$(LOGFORMAT)" copy "$(INSTBIN)/* to $(INSTBIN_64)" + @for f in $(BIN_FILES); do [[ -f $(INSTBIN)/$$f ]] \ + && cp -p $(INSTBIN)/$$f $(INSTBIN_64); done; true +endif + +else + @$(MAKE) --no-print-directory clean+ install +endif + +# +#-------------------------- + +.PHONY : export32 +export32: + @printf "\n---------- BUILDING EXPORT (I386) ----------\n\n" + @rm -rf ./bin/export-i386 + @mkdir -p ./bin/export-i386 + @M32=1 NOWIIMM=1 STATIC=1 $(MAKE) --no-print-directory clean+ all + @for f in $(BIN_FILES); do [[ -f ./$$f ]] \ + && cp -p ./$$f ./bin/export-i386/; done; true + +.PHONY : export64 +export64: + @printf "\n---------- BUILDING EXPORT (X86_64) ----------\n\n" + @rm -rf ./bin/export-x86_64 + @mkdir -p ./bin/export-x86_64 + @NOWIIMM=1 STATIC=1 $(MAKE) --no-print-directory clean+ all + @for f in $(BIN_FILES); do [[ -f ./$$f ]] \ + && cp -p ./$$f ./bin/export-x86_64/; done; true + +.PHONY : export +export: +ifeq ($(SYSTEM_LINUX),1) + + $(MAKE) --no-print-directory export32 export64 + +else ifeq ($(SYSTEM),cygwin) + + @printf "\n---------- BUILDING EXPORT (CYGWIN) ----------\n\n" + @rm -rf ./bin/export-cygwin + @mkdir -p ./bin/export-cygwin + @NOWIIMM=1 $(MAKE) --no-print-directory clean+ all + @for f in $(BIN_FILES); do [[ -f ./$$f ]] \ + && cp -p ./$$f ./bin/export-cygwin/; done; true + @[[ -d "$(EXPORT_DIR)" ]] && cp -r ./bin/export-cygwin "$(EXPORT_DIR)/" + +else ifeq ($(SYSTEM),mac) + + @printf "\n---------- BUILDING EXPORT (MAC) ----------\n\n" + @rm -rf ./bin/export-mac + @mkdir -p ./bin/export-mac + @NOWIIMM=1 $(MAKE) --no-print-directory mac-distrib + @for f in $(BIN_FILES); do [[ -f ./$$f ]] \ + && cp -p ./$$f ./bin/export-mac/; done; true + @[[ -d "$(EXPORT_DIR)" ]] && cp -r ./bin/export-mac "$(EXPORT_DIR)/" + +else + + @printf "\n---------- BUILDING EXPORT ----------\n\n" + @rm -rf ./bin/export + @mkdir -p ./bin/export + @NOWIIMM=1 STATIC=1 $(MAKE) --no-print-directory clean+ all + @for f in $(BIN_FILES); do [[ -f ./$$f ]] \ + && cp -p ./$$f ./bin/export/; done; true + +endif + +.PHONY : export+ +export+: export + @printf "\n---------- BUILDING EXPORT ----------\n\n" + @printf "$(LOGFORMAT)" copy "export-bin to DISTRIB & LESERATTE" + @cp -p bin/export-x86_64/wszst /n/www/download.wiimm.de/florian/bin/wszst + @cp -p bin/export-x86_64/wszst /n/mkw-fun/distrib/bin/linux64/ + @cp -p bin/export-i386/wszst /n/mkw-fun/distrib/bin/linux32/ + @cp -p bin/export-cygwin/wszst.exe /n/mkw-fun/distrib/bin/cygwin/ || true + +# +#-------------------------- + +.PHONY : libexport +libexport: + @printf "$(LOGFORMAT)" export "-> $(LIBEXPORT)" + @rm -rf $(LIBEXPORT) + @mkdir -p $(LIBEXPORT) + @cp -p $(LIBEX_FILES) $(LIBEXPORT) + +# +#-------------------------- + +.PHONY : new +new: + @printf "$(LOGFORMAT)" enable new "-> define -DNEW_FEATURES" + @rm -f *.o $(ALL_TOOLS_X) + @echo "-DNEW_FEATURES" >>$(MODE_FILE) + @sort $(MODE_FILE) | uniq > $(MODE_FILE).tmp +# 2 steps to bypass a cygwin mv failure + @cp $(MODE_FILE).tmp $(MODE_FILE) + @rm -f $(MODE_FILE).tmp + +# +#-------------------------- + +.PHONY : predef +predef: + @gcc -E -dM none.c | sort + +# +#-------------------------- + +templates.sed: Makefile + @printf "$(LOGFORMAT)" create templates.sed "" + @echo -e '' \ + '/^~/ d;\n' \ + 's|@.@@@|$(VERSION_NUM)|g;\n' \ + 's|@@@@-@@-@@|$(DATE)|g;\n' \ + 's|@@:@@:@@|$(TIME)|g;\n' \ + 's|@@AUTHOR@@|$(AUTHOR)|g;\n' \ + 's|@@TOOLSET-SHORT@@|$(TOOLSET_SHORT)|g;\n' \ + 's|@@TOOLSET-LONG@@|$(TOOLSET_LONG)|g;\n' \ + 's|@@WSZST-SHORT@@|$(WSZST_SHORT)|g;\n' \ + 's|@@WSZST-LONG@@|$(WSZST_LONG)|g;\n' \ + 's|@@WBMGT-SHORT@@|$(WBMGT_SHORT)|g;\n' \ + 's|@@WBMGT-LONG@@|$(WBMGT_LONG)|g;\n' \ + 's|@@WCTCT-SHORT@@|$(WCTCT_SHORT)|g;\n' \ + 's|@@WCTCT-LONG@@|$(WCTCT_LONG)|g;\n' \ + 's|@@WIMGT-SHORT@@|$(WIMGT_SHORT)|g;\n' \ + 's|@@WIMGT-LONG@@|$(WIMGT_LONG)|g;\n' \ + 's|@@WKCLT-SHORT@@|$(WKCLT_SHORT)|g;\n' \ + 's|@@WKCLT-LONG@@|$(WKCLT_LONG)|g;\n' \ + 's|@@WKMPT-SHORT@@|$(WKMPT_SHORT)|g;\n' \ + 's|@@WKMPT-LONG@@|$(WKMPT_LONG)|g;\n' \ + 's|@@WLECT-SHORT@@|$(WLECT_SHORT)|g;\n' \ + 's|@@WLECT-LONG@@|$(WLECT_LONG)|g;\n' \ + 's|@@WMDLT-SHORT@@|$(WMDLT_SHORT)|g;\n' \ + 's|@@WMDLT-LONG@@|$(WMDLT_LONG)|g;\n' \ + 's|@@WPATT-SHORT@@|$(WPATT_SHORT)|g;\n' \ + 's|@@WPATT-LONG@@|$(WPATT_LONG)|g;\n' \ + 's|@@WSTRT-SHORT@@|$(WSTRT_SHORT)|g;\n' \ + 's|@@WSTRT-LONG@@|$(WSTRT_LONG)|g;\n' \ + 's|@@VERSION@@|$(VERSION)|g;\n' \ + 's|@@VERSION-NUM@@|$(VERSION_NUM)|g;\n' \ + 's|@@VERSION-INT@@|$(VERSION_INT)|g;\n' \ + 's|@@BETA-VERSION@@|$(BETA_VERSION)|g;\n' \ + 's|@@BETA-SUFFIX@@|$(BETA_SUFFIX)|g;\n' \ + 's|@@REV@@|$(REVISION)|g;\n' \ + 's|@@REV-NUM@@|$(REVISION_NUM)|g;\n' \ + 's|@@REV-NEXT@@|$(REVISION_NEXT)|g;\n' \ + 's|@@BINTIME@@|$(BINTIME)|g;\n' \ + 's|@@DATE@@|$(DATE)|g;\n' \ + 's|@@TIME@@|$(TIME)|g;\n' \ + 's|@@YEAR@@|$(YEAR)|g;\n' \ + 's|@@INSTALL-PATH@@|$(INSTALL_PATH)|g;\n' \ + 's|@@BIN-FILES@@|$(BIN_FILES)|g;\n' \ + 's|@@SHARE-FILES@@|$(SHARE_FILES)|g;\n' \ + 's|@@CYGWIN-DIR@@|$(CYGWIN_DIR)|g;\n' \ + 's|@@CYGWIN-TOOLS@@|$(CYGWIN_TOOLS)|g;\n' \ + 's|@@WIN-INSTALL-PATH@@|$(WIN_INSTALL_PATH)|g;\n' \ + 's|@@DISTRIB-PATH@@|$(DISTRIB_PATH)|g;\n' \ + 's|@@DISTRIB-I386@@|$(DISTRIB_I386)|g;\n' \ + 's|@@DISTRIB-X86_64@@|$(DISTRIB_X86_64)|g;\n' \ + 's|@@DISTRIB-MAC@@|$(DISTRIB_MAC)|g;\n' \ + 's|@@DISTRIB-CYGWIN@@|$(DISTRIB_CYGWIN)|g;\n' \ + 's|@@URI-REPOS@@|$(URI_REPOS)|g;\n' \ + 's|@@URI-VIEWVC@@|$(URI_VIEWVC)|g;\n' \ + 's|@@URI-HOME@@|$(URI_HOME)|g;\n' \ + 's|@@URI-SOURCE@@|$(URI_SOURCE)|g;\n' \ + 's|@@URI-DOWNLOAD@@|$(URI_DOWNLOAD)|g;\n' \ + 's|@@URI-DOWNLOAD-I386@@|$(URI_DOWNLOAD_I386)|g;\n' \ + 's|@@URI-DOWNLOAD-X86_64@@|$(URI_DOWNLOAD_X86_64)|g;\n' \ + 's|@@URI-DOWNLOAD-MAC@@|$(URI_DOWNLOAD_MAC)|g;\n' \ + 's|@@URI-DOWNLOAD-CYGWIN@@|$(URI_DOWNLOAD_CYGWIN)|g;\n' \ + 's|@@HAVE-XSRC@@|$(HAVE_XSRC)|g;\n' \ + >templates.sed + +# +#-------------------------- + +.PHONY : st +st: + @printf "$(LOGFORMAT)" svn status + @[[ -h xsrc ]] && rm xsrc || true + @test -d .svn && svn status | sort -k2 || true + @printf "* N=" + @svn diff | grep ^+ | wc -l + +# +#-------------------------- + +.PHONY : test +test: + @printf "$(LOGFORMAT)" enable test "-> define -DTEST" + @rm -f *.o $(ALL_TOOLS_X) + @echo "-DTEST" >>$(MODE_FILE) + @sort $(MODE_FILE) | uniq > $(MODE_FILE).tmp +# 2 steps to bypass a cygwin mv failure + @cp $(MODE_FILE).tmp $(MODE_FILE) + @rm -f $(MODE_FILE).tmp + +# +#-------------------------- + +.PHONY : test-trace +test-trace: + @printf "$(LOGFORMAT)" enable testtrace "-> define -DTESTTRACE" + @rm -f *.o $(ALL_TOOLS_X) + @echo "-DTESTTRACE" >>$(MODE_FILE) + @sort $(MODE_FILE) | uniq > $(MODE_FILE).tmp +# 2 steps to bypass a cygwin mv failure + @cp $(MODE_FILE).tmp $(MODE_FILE) + @rm -f $(MODE_FILE).tmp + +# +#-------------------------- + +.PHONY : tools +tools: $(ALL_TOOLS) + +# +#-------------------------- + +.PHONY : wait +wait: + @printf "$(LOGFORMAT)" enable wait "-> define -DWAIT_ENABLED" + @rm -f *.o $(ALL_TOOLS_X) + @echo "-DWAIT_ENABLED" >>$(MODE_FILE) + @sort $(MODE_FILE) | uniq > $(MODE_FILE).tmp +# 2 steps to bypass a cygwin mv failure + @cp $(MODE_FILE).tmp $(MODE_FILE) + @rm -f $(MODE_FILE).tmp + +# +#-------------------------- + +.PHONY : wrapper +wrapper: + @printf "$(LOGFORMAT)" create "wrapper scripts" + @mkdir -p ./bin/wrapper + @for f in $(WRAPPER_TOOLS); do \ + printf '#!/usr/bin/sh\nwszst %s "$$@"\n' "$$f" >./bin/wrapper/$$f; done + @chmod 775 ./bin/wrapper/* + +# +############################################################################### +# help rule + +.PHONY : help +help: + @echo "" + @echo "$(DATE) $(TIME) - $(VERSION) - svn r$(REVISION):$(REVISION_NEXT)" + @echo "" + @echo " make := make all" + @echo " make all make all tools and install scripts" + @echo " make all+ := make clean+ all distrib" + @echo " make _tool_ compile only the named '_tool_' (wszst,...)" + @echo " make tools make all tools" + @echo " make libexport create directory '$(LIBEXPORT)' with export files." + @echo "" + @echo " make clean remove all output files" + @echo " make clean+ make clean & rm test_files & rm template_output" + @echo "" + @echo " make debug enable '-DDEBUG'" + @echo " make test enable '-DTEST'" + @echo " make new enable '-DNEW_FEATURES'" + @echo " make wait enable '-DWAIT'" + @echo " make testtrace enable '-DTESTTRACE'" + @echo " make flags print DEFINES, CFLAGS and LDFLAGS" + @echo "" + @echo " make doc generate doc files from their templates" + @echo " make distrib make all & build $(DISTRIB_PATH)" + @echo " make distrib2 make distrib (i386 + X86_64 for linux)" + @echo " make install make all & copy tools to $(INSTALL_PATH)" + @echo " make install+ := make clean+ install" + @echo " make install2 make install+ (i386 + X86_64 for linux)" + @echo "" + @echo " make chmod change mode 775/644 for known dirs and files" + @echo " make chown change owner of all dirs+files to owner of ." + @echo " make chgrp change group of all dirs+files to group of ." + @echo " make ch+ := make chmod chown chgrp" + @echo "" + @echo " make help print this help" + @echo "" + @echo "-------------------------------------------------------------------------------" + @awk -F: '/^.PHONY/ { gsub(/[[:blank:]]*/,"",$$2); print $$2}' $(MYMAKE) \ + | sort | pr -5T -w80 + @echo "-------------------------------------------------------------------------------" + +# @echo "------" +# @echo $(TOBJ_ALL) +# @echo "------" +# @echo $(C_OBJECTS) +# @echo "------" +# @echo $(SZS_O) +# @echo "------" + diff --git a/project/Makefile.user b/project/Makefile.user new file mode 100644 index 0000000..a5badf2 --- /dev/null +++ b/project/Makefile.user @@ -0,0 +1 @@ +-include work/Makefile.user diff --git a/project/bin/wbmgt b/project/bin/wbmgt new file mode 100755 index 0000000..597c89f Binary files /dev/null and b/project/bin/wbmgt differ diff --git a/project/bin/wctct b/project/bin/wctct new file mode 100755 index 0000000..1ae5da7 Binary files /dev/null and b/project/bin/wctct differ diff --git a/project/bin/wimgt b/project/bin/wimgt new file mode 100755 index 0000000..18245de Binary files /dev/null and b/project/bin/wimgt differ diff --git a/project/bin/wkclt b/project/bin/wkclt new file mode 100755 index 0000000..0f3b4eb Binary files /dev/null and b/project/bin/wkclt differ diff --git a/project/bin/wkmpt b/project/bin/wkmpt new file mode 100755 index 0000000..6f27232 Binary files /dev/null and b/project/bin/wkmpt differ diff --git a/project/bin/wlect b/project/bin/wlect new file mode 100755 index 0000000..4afe203 Binary files /dev/null and b/project/bin/wlect differ diff --git a/project/bin/wmdlt b/project/bin/wmdlt new file mode 100755 index 0000000..80e7405 Binary files /dev/null and b/project/bin/wmdlt differ diff --git a/project/bin/wpatt b/project/bin/wpatt new file mode 100755 index 0000000..f47aff4 Binary files /dev/null and b/project/bin/wpatt differ diff --git a/project/bin/wstrt b/project/bin/wstrt new file mode 100755 index 0000000..943141b Binary files /dev/null and b/project/bin/wstrt differ diff --git a/project/bin/wszst b/project/bin/wszst new file mode 100755 index 0000000..5e29585 Binary files /dev/null and b/project/bin/wszst differ diff --git a/project/bin/wtest b/project/bin/wtest new file mode 100755 index 0000000..9bdc8c7 Binary files /dev/null and b/project/bin/wtest differ diff --git a/project/bin/x86_64/wbmgt b/project/bin/x86_64/wbmgt new file mode 100755 index 0000000..597c89f Binary files /dev/null and b/project/bin/x86_64/wbmgt differ diff --git a/project/bin/x86_64/wctct b/project/bin/x86_64/wctct new file mode 100755 index 0000000..1ae5da7 Binary files /dev/null and b/project/bin/x86_64/wctct differ diff --git a/project/bin/x86_64/wimgt b/project/bin/x86_64/wimgt new file mode 100755 index 0000000..18245de Binary files /dev/null and b/project/bin/x86_64/wimgt differ diff --git a/project/bin/x86_64/wkclt b/project/bin/x86_64/wkclt new file mode 100755 index 0000000..0f3b4eb Binary files /dev/null and b/project/bin/x86_64/wkclt differ diff --git a/project/bin/x86_64/wkmpt b/project/bin/x86_64/wkmpt new file mode 100755 index 0000000..6f27232 Binary files /dev/null and b/project/bin/x86_64/wkmpt differ diff --git a/project/bin/x86_64/wlect b/project/bin/x86_64/wlect new file mode 100755 index 0000000..4afe203 Binary files /dev/null and b/project/bin/x86_64/wlect differ diff --git a/project/bin/x86_64/wmdlt b/project/bin/x86_64/wmdlt new file mode 100755 index 0000000..80e7405 Binary files /dev/null and b/project/bin/x86_64/wmdlt differ diff --git a/project/bin/x86_64/wpatt b/project/bin/x86_64/wpatt new file mode 100755 index 0000000..f47aff4 Binary files /dev/null and b/project/bin/x86_64/wpatt differ diff --git a/project/bin/x86_64/wstrt b/project/bin/x86_64/wstrt new file mode 100755 index 0000000..943141b Binary files /dev/null and b/project/bin/x86_64/wstrt differ diff --git a/project/bin/x86_64/wszst b/project/bin/x86_64/wszst new file mode 100755 index 0000000..5e29585 Binary files /dev/null and b/project/bin/x86_64/wszst differ diff --git a/project/bin/x86_64/wtest b/project/bin/x86_64/wtest new file mode 100755 index 0000000..9bdc8c7 Binary files /dev/null and b/project/bin/x86_64/wtest differ diff --git a/project/dclib/Makefile.inc b/project/dclib/Makefile.inc new file mode 100644 index 0000000..6ab04d8 --- /dev/null +++ b/project/dclib/Makefile.inc @@ -0,0 +1,67 @@ + +#------------------------------------------------------------------------------ +# standard object files + +DCLIB_O = dclib-basics.o dclib-color.o dclib-numeric.o \ + dclib-file.o dclib-option.o dclib-regex.o \ + dclib-utf8.o dclib-shift-jis.o \ + dclib-vector.o dclib-tables.o dclib-ui.o \ + dclib-xdump.o dclib-debug.o +DCLIB_O1 := lib-dol.o lib-bmg.o lib-mkw.o +DCLIB_O2 := lib-wiimmfi.o lib-gamespy.o lib-natneg.o + +DCLIB_PRIVATE := dclib-terminal* dclib-command* lib-wiimmfi* lib-gamespy* lib-natneg* + +#------------------------------------------------------------------------------ +# if DCLIB_MYSQL not set: enable it if file 'mysql.h' is found + +ifeq ($(origin DCLIB_MYSQL), undefined) + DCLIB_MYSQL := $(shell [[ -f /usr/include/mysql/mysql.h ]]; echo $$((!$$?))) +endif + +#------------------------------------------------------------------------------ +# if DCLIB_TERMINAL not set: disable it + +ifeq ($(origin DCLIB_TERMINAL), undefined) + DCLIB_TERMINAL := 0 +endif + +#------------------------------------------------------------------------------ +# if DCLIB_NETWORK not set: enable it if DCLIB_MYSQL is also set + +ifeq ($(origin DCLIB_NETWORK), undefined) + DCLIB_NETWORK := $(DCLIB_MYSQL) +endif + +#------------------------------------------------------------------------------ +# some more setting + +ifeq ($(origin DCLIB_HAVE_USABLE_SIZE), undefined) + DCLIB_HAVE_USABLE_SIZE := $(shell grep -sqw malloc_usable_size /usr/include/malloc.h; echo $$((!$$?))) +endif + +#------------------------------------------------------------------------------ +# add libraries and guardian macros + +ifeq ($(DCLIB_NETWORK),1) + DCLIB_O += dclib-network.o + DCLIB_FLAGS += -DDCLIB_NETWORK=1 +else + DCLIB_MYSQL := 0 +endif + +ifeq ($(DCLIB_MYSQL),1) + DCLIB_O += dclib-mysql.o + DCLIB_FLAGS += -DDCLIB_MYSQL=1 +endif + +ifeq ($(DCLIB_TERMINAL),1) + DCLIB_O += dclib-terminal.o dclib-command.o + DCLIB_FLAGS += -DDCLIB_TERMINAL=1 +endif + +ifeq ($(DCLIB_HAVE_USABLE_SIZE),1) + DCLIB_FLAGS += -DDCLIB_HAVE_USABLE_SIZE=1 +endif + +#------------------------------------------------------------------------------ diff --git a/project/dclib/dclib-basics.c b/project/dclib/dclib-basics.c new file mode 100644 index 0000000..74676d0 --- /dev/null +++ b/project/dclib/dclib-basics.c @@ -0,0 +1,8065 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#ifndef WIN_SZS_LIB + // needed for GetTermWidthFD() + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dclib-basics.h" +#include "dclib-debug.h" +#include "dclib-file.h" +#include "dclib-utf8.h" +#include "dclib-network.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// some basic string functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const char EmptyQuote[] = "\"\""; // Ignored by FreeString() +const char EmptyString[4] = "\0\0\0"; // Ignored by FreeString() +const char MinusString[4] = "-\0\0"; // Ignored by FreeString() + +const char LoDigits[0x41] = + "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ".+"; + +const char HiDigits[0x41] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + ".+"; + +const char Tabs20[21] = // 20 * TABS + NULL + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + +const char LF20[21] = // 20 * LF + NULL + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + +const char Space200[201] = // 200 * ' ' + NULL + " " + " " + " " + " "; + +const char Minus300[301] = // 300 * '-' + NULL + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------"; + +const char Underline200[201] = // 200 * '_' + NULL + "__________________________________________________" + "__________________________________________________" + "__________________________________________________" + "__________________________________________________"; + +const char Hash200[201] = // 200 * '#' + NULL + "##################################################" + "##################################################" + "##################################################" + "##################################################"; + +const char Slash200[201] = // 200 * '/' + NULL + "//////////////////////////////////////////////////" + "//////////////////////////////////////////////////" + "//////////////////////////////////////////////////" + "//////////////////////////////////////////////////"; + +// UTF-8 => 3 bytes per char +const char ThinLine300_3[901] = // 300 * '─' (U+2500) + NULL + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────"; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintSettingsDCLIB ( FILE *f, int indent ) +{ + DASSERT(f); + indent = NormalizeIndent(indent); + MARK_USED(indent); // for the case, that all other conditions fail! + + #ifdef DCLIB_NETWORK + fprintf(f,"%*s" "DCLIB_NETWORK\n",indent,""); + #endif + + #ifdef DCLIB_TERMINAL + fprintf(f,"%*s" "DCLIB_TERMINAL\n",indent,""); + #endif + + #ifdef DCLIB_MYSQL + fprintf(f,"%*s" "DCLIB_MYSQL\n",indent,""); + #endif + + #ifdef DCLIB_HAVE_USABLE_SIZE + fprintf(f,"%*s" "DCLIB_HAVE_USABLE_SIZE\n",indent,""); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupMultiProcessing() +{ + multi_processing = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupProgname ( int argc, char ** argv, ccp tname, ccp tvers, ccp ttitle ) +{ + if (tname) + toolname = tname; + if (tvers) + toolversion = tvers; + if (ttitle) + tooltitle = ttitle; + + if (!progpath) + { + char path[PATH_MAX]; + int res = GetProgramPath( path, sizeof(path), true, argc > 0 ? argv[0] : 0 ); + if ( res > 0 ) + progpath = STRDUP(path); + } + + if ( argc > 0 && *argv && **argv ) + { + ccp slash = strrchr(*argv,'/'); + progname = slash ? slash + 1 : *argv; + } + else + progname = toolname && *toolname ? toolname : "?"; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp ProgramPath() +{ + static bool done = false; + if ( !progpath && !done ) + { + done = true; + + char path[PATH_MAX]; + int res = GetProgramPath( path, sizeof(path), true, 0 ); + if ( res > 0 ) + progpath = STRDUP(path); + } + return progpath; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp ProgramDirectory() +{ + if (!progdir) + { + ccp path = ProgramPath(); + if (path) + { + ccp end = strrchr(path,'/'); + const uint len = end ? end - path : strlen(path); + progdir = MEMDUP(path,len); + } + } + return progdir; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __CYGWIN__ + +ccp ProgramPathNoExt() +{ + static ccp res = 0; + if (!res) + { + res = ProgramPath(); + if (res) + { + ccp slash = strrchr(res,'/'); + ccp ext = strrchr( slash ? slash + 1 : res, '.' ); + if (ext) + res = MEMDUP(res,ext-res); + } + } + return res; +} + +#endif // __CYGWIN__ + +/////////////////////////////////////////////////////////////////////////////// + +int GetProgramPath +( + // returns strlen(buf) on success, -1 on failure + + char *buf, // buffer for the result; empty string on failure. + uint buf_size, // size of buffer + bool check_proc, // true: read files of /proc/... to find path + ccp argv0 // not NULL: analyse this argument (argv[0]) +) +{ + char path[PATH_MAX], path2[PATH_MAX]; + + + //--- read files of /proc/... + + if (check_proc) + { + static ccp tab[] = + { + "/proc/self/exe", + "/proc/curproc/file", + "/proc/self/path/a.out", + 0 + }; + + ccp *ptr; + for ( ptr = tab; *ptr; ptr++ ) + if ( realpath(*ptr,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + + + //--- analyze argv0 + + if (argv0) + { + if ( *argv0 == '/' ) + { + // absolute path + if ( realpath(argv0,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + else if (strchr(argv0,'/')) + { + // relative path + if ( getcwd(path2,sizeof(path2)) > 0 ) + { + ccp p2 = PathCatPP(path2,sizeof(path2),path,argv0); + if ( realpath(p2,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + } + else + { + // simple name => search in path environment + uint arglen = strlen(argv0); + if ( arglen < sizeof(path2) - 2 ) + { + ccp dest_end = path2 + sizeof(path2) - 1 - arglen; + ccp env = getenv("PATH"); + while ( env && *env ) + { + while ( *env == ':' ) + env++; + char *dest = path2; + while ( *env && *env != ':' && dest < dest_end ) + *dest++ = *env++; + *dest++ = '/'; + memcpy(dest,argv0,arglen+1); + if ( realpath(path2,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + } + } + } + + + //--- failed + + *buf = 0; + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeString ( ccp str ) +{ + noTRACE("FreeString(%p) EmptyString=%p MinusString=%p\n", + str, EmptyString, MinusString ); + if ( str != EmptyString && str != EmptyQuote && str != MinusString ) + FREE((char*)str); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * StringCopyE ( char * buf, ccp buf_end, ccp src ) +{ + // RESULT: end of copied string pointing to NULL + // 'src' may be a NULL pointer. + + if ( buf >= buf_end ) + return (char*)buf_end - 1; + + DASSERT(buf); + DASSERT(buf= 0 && buf + max_copy < buf_end ) + buf_end = buf + max_copy; + + if (src) + while( buf < buf_end && *src ) + *buf++ = *src++; + + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +char * StringCopySM ( char * buf, size_t buf_size, ccp src, size_t max_copy ) +{ + return StringCopyEM(buf,buf+buf_size,src,max_copy); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * StringCat2E ( char * buf, ccp buf_end, ccp src1, ccp src2 ) +{ + // RESULT: end of copied string pointing to NULL + // 'src*' may be a NULL pointer. + + ASSERT(buf); + ASSERT(buf= buf_end ) + return (char*)buf_end - 1; + + DASSERT(buf); + DASSERT(buf= buf_end ) + return (char*)buf_end - 1; + + DASSERT(buf); + DASSERT(buf buf ); + if ( ptr[-1] != '/' ) + *ptr++ = '/'; + if (path2) + { + while ( *path2 == '/' ) + path2++; + StringCopyE(ptr,buf+bufsize,path2); + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatPP ( char * buf, size_t bufsize, ccp path1, ccp path2 ) +{ + // concatenate path + path; returns path1 | path2 | buf + + if ( !path1 || !*path1 ) + return path2 ? path2 : ""; + + if ( !path2 || !*path2 ) + return path1; + + char * ptr = StringCopyS(buf,bufsize-1,path1); + DASSERT( ptr > buf ); + if ( ptr[-1] != '/' ) + *ptr++ = '/'; + while ( *path2 == '/' ) + path2++; + StringCopyE(ptr,buf+bufsize,path2); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocPP ( ccp path1, ccp path2 ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatPP(buf,sizeof(buf),path1,path2)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufPPE ( char * buf, size_t bufsize, ccp path1, ccp path2, ccp ext ) +{ + // concatenate path + path; returns (by definition) path1 | path2 | buf + + DASSERT(buf); + DASSERT(bufsize); + + char *ptr = path1 ? StringCopyS(buf,bufsize-1,path1) : buf; + *ptr = 0; + + if (path2) + { + if ( ptr > buf && ptr[-1] != '/' ) + *ptr++ = '/'; + while ( *path2 == '/' ) + path2++; + ptr = StringCopyE(ptr,buf+bufsize,path2); + } + + if (ext) + StringCopyE(ptr,buf+bufsize,ext); + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PathCatPPE() is inline + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocPPE ( ccp path1, ccp path2, ccp ext ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatPPE(buf,sizeof(buf),path1,path2,ext)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufBP ( char *buf, size_t bufsize, ccp base, ccp path ) +{ + // PathCatBP() is special: path can be part of buf + + if (!path) + path = ""; + if ( *path == '/' || base && !*base ) + { + // already absolute | no base to add + StringCopyS(buf,bufsize,path); + return buf; + } + + + //--- now copy base or cwd + + while ( path[0] == '.' && path[1] == '/' ) + path += 2; + + char buf2[PATH_MAX]; + StringCopyS(buf2,sizeof(buf2),path); + + char *dest; + if (base) + dest = StringCopyS(buf,bufsize,base); + else + { + char *cwd = getcwd(buf,bufsize); + if (cwd) + dest = buf + strlen(buf); + else + { + // on error: return fallback + dest = StringCopyS(buf,bufsize,"./"); + } + } + StringCat2E(dest,buf+bufsize,"/",buf2); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatBP ( char *buf, size_t bufsize, ccp base, ccp path ) +{ + // PathCatBP() is special: path can be part of buf + + if ( path && *path == '/' || base && !*base ) + return path; // already absolute | no base to add + + return PathCatBufBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocBP ( ccp base, ccp path ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatBP(buf,sizeof(buf),base,path)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufBPP ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2 ) +{ + ccp path = PathCatPP(buf,bufsize,path1,path2); + return PathCatBufBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatBPP ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2 ) +{ + ccp path = PathCatPP(buf,bufsize,path1,path2); + return PathCatBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocBPP ( ccp base, ccp path1, ccp path2 ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatBPP(buf,sizeof(buf),base,path1,path2)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufBPPE ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2, ccp ext ) +{ + ccp path = PathCatPPE(buf,bufsize,path1,path2,ext); + return PathCatBufBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatBPPE ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2, ccp ext ) +{ + ccp path = PathCatPPE(buf,bufsize,path1,path2,ext); + return PathCatBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocBPPE ( ccp base, ccp path1, ccp path2, ccp ext ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatBPPE(buf,sizeof(buf),base,path1,path2,ext)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCombine ( char *buf, uint buf_size, ccp path, ccp base_path ) +{ + DASSERT(buf); + DASSERT( buf_size > 10 ); + + if (!path) + path = ""; + + char *dest = buf; + if ( base_path && *base_path && *path != '/' ) + { + StringCopyS(buf,buf_size,base_path); + char *p = strrchr(buf,'/'); + if (p) + dest = p+1; + } + + StringCopyE(dest,buf+buf_size,path); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PathCombineFast ( char *buf, uint buf_size, ccp path, ccp base_path ) +{ + DASSERT(buf); + DASSERT( buf_size > 10 ); + + if (!path) + path = ""; + + if ( !base_path || !*base_path || *path == '/' ) + return (char*)path; + + StringCopyS(buf,buf_size,base_path); + char *p = strrchr(buf,'/'); + if (!p) + return (char*)path; + + StringCopyE(p+1,buf+buf_size,path); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * NewFileExtE ( char * buf, ccp buf_end, ccp path, ccp ext ) +{ + DASSERT( buf); + DASSERT( buf_end); + DASSERT( buf_end > buf ); + + char * end = path && path != buf + ? StringCopyE(buf,buf_end-1,path) + : buf + strlen(buf); + DASSERT( end < buf_end ); + + char * ptr = end; + while ( ptr > buf && *--ptr != '/' ) + if ( *ptr == '.' ) + { + end = ptr; + break; + } + + if ( ext && *ext ) + { + if ( *ext != '.' ) + *end++ = '.'; + end = StringCopyE(end,buf_end,ext); + } + + *end = 0; + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * NewFileExtS ( char * buf, size_t bufsize, ccp path, ccp ext ) +{ + return NewFileExtE(buf,buf+bufsize,path,ext); +} + +/////////////////////////////////////////////////////////////////////////////// + +int StrPathCmp ( ccp path1, ccp path2 ) +{ + if ( path1[0] == '.' && path1[1] == '/' ) + path1 += 2; + if ( path2[0] == '.' && path2[1] == '/' ) + path2 += 2; + return strcmp(path1,path2); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int PathCmp +( + ccp path1, // NULL or first path + ccp path2, // NULL or second path + uint mode // bit field: + // 1: skip (multiple) leading './' + // 2: compare case insensitive + // 4: sort with respect for unsigned decimal numbers +) +{ + if (!path2) + return path1 != 0; + if (!path1) + return -1; + + noPRINT("PathCmp( | %s | %s | %u )\n",path1,path2,mode); + + //--- skip './' + + if ( mode & 1 ) + { + while ( path1[0] == '.' && path1[1] == '/' ) + path1 += 2; + while ( path2[0] == '.' && path2[1] == '/' ) + path2 += 2; + } + + + //--- eliminate equal characters + + while ( *path1 == *path2 ) + { + if (!*path1) + return 0; + path1++; + path2++; + } + + + //--- start the case+path test + + ccp p1 = path1; + ccp p2 = path2; + + const uint ignore_case = mode & 2; + + while ( *p1 || *p2 ) + { + int ch1, ch2; + if (ignore_case) + { + ch1 = (uchar)tolower((int)*p1++); + ch2 = (uchar)tolower((int)*p2++); + } + else + { + ch1 = (uchar)*p1++; + ch2 = (uchar)*p2++; + } + int stat = ch1 - ch2; + noPRINT("%02x,%02x,%c %02x,%02x,%c -> %2d\n", + p1[-1], ch1, ch1, p2[-1], ch2, ch2, stat ); + + if (stat) + { + if ( mode & 4 ) + { + p1--; + while ( *p1 == '0' ) + p1++; + ccp q1 = p1; + while ( *q1 >= '0' && *q1 <= '9' ) + q1++; + const int len1 = q1 - p1; + + if (len1) + { + p2--; + while ( *p2 == '0' ) + p2++; + ccp q2 = p2; + while ( *q2 >= '0' && *q2 <= '9' ) + q2++; + const int len2 = q2 - p2; + + if (len2) + { + int stat2 = len1 - len2; + if (!stat2) + stat2 = memcmp(p1,p2,len1); + if (stat2) + return stat2; + } + } + } + return ch1 == '/' ? -1 : ch2 == '/' ? 1 : stat; + } + + if ( ch1 == '/' ) + break; + } + + return strcmp(path1,path2); +} + +/////////////////////////////////////////////////////////////////////////////// + +int StrNumCmp ( ccp a, ccp b ) +{ + if (!b) + return a != 0; + if (!a) + return -1; + + for(;;) + { + const int va = *a++ & 0xff; + const int vb = *b++ & 0xff; + const int delta = va - vb; + + if (delta) + { + if ( va < '0' || va > '9' || vb < '0' || vb > '9' ) + return delta; // no number + + //--- both are numbers + + a--; + while ( *a == '0' ) + a++; + ccp start_a = a; + while ( *a >= '0' && *a <= '9' ) + a++; + + b--; + while ( *b == '0' ) + b++; + ccp start_b = b; + while ( *b >= '0' && *b <= '9' ) + b++; + + // compare num length + int num_delta = ( a - start_a ) - ( b - start_b ); + if (num_delta) + return num_delta; + + // compare numeric values + while ( start_a < a ) + { + num_delta = *start_a++ - *start_b++; + if (num_delta) + return num_delta; + } + + // number identical, but not string + return delta; + } + else if (!va) + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountEqual ( cvp m1, cvp m2, uint size ) +{ + const u8 *p1 = m1; + const u8 *p2 = m2; + while ( size-- ) + if ( *p1++ != *p2++ ) + return p1 - (u8*)m1 - 1; + return p1 - (u8*)m1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ReplaceToBuf +( + // returns -1 on error (buf to small) or the length of the resulting string. + // In the later case, a 0 byte is appended, but not counting for the length. + + char *buf, // pointer to buf + uint buf_size, // size of 'buf' + + ccp src1, // NULL or source, maybe part of 'buf' + int src1_len, // length of 'src1', if -1: determine by strlen() + int pos, // robust replace position, <0: relative to end + int rm_len, // robust num of chars to remove, <0: rm before 'pos' + + ccp src2, // NULL or source to insert, must not be part of 'buf' + int src2_len // lengh of 'src2', if -1: determine by strlen() +) +{ + DASSERT(buf); + + //--- normalize source #2 + + if (!src2) + src2_len = 0; + else if ( src2_len < 0 ) + src2_len = strlen(src2); + + + //--- if source #1 available + + if (src1) + { + //--- check src1 length + + if ( src1_len < 0 ) + src1_len = strlen(src1); + + if (!src1_len) + goto no_src1; + + + //--- normalize 'pos' + + if ( pos < 0 ) + { + pos += src1_len; + if ( pos < 0 ) + { + if ( rm_len > 0 ) + { + rm_len += pos; + if ( rm_len < 0 ) + rm_len = 0; + } + pos = 0; + } + } + else if ( pos > src1_len ) + pos = src1_len; + + + //--- normalize 'rm_len' + + if ( rm_len < 0 ) + { + rm_len = -rm_len; + if ( rm_len < pos ) + pos -= rm_len; + else + { + rm_len = pos; + pos = 0; + } + } + else + { + const int max = src1_len - pos; + if ( rm_len > max ) + rm_len = max; + } + + const int res_len = src1_len + src2_len - rm_len; + if ( res_len >= buf_size ) + return -1; + + + //--- copy src1 + + if ( src1 > buf ) + { + memmove( buf, src1, pos ); + const int pos2 = pos + rm_len; + memmove( buf+pos+src2_len, src1+pos2, src1_len-pos2 ); + } + else + { + const int pos2 = pos + rm_len; + memmove( buf+pos+src2_len, src1+pos2, src1_len-pos2 ); + memmove( buf, src1, pos ); + } + + + //--- copy src2 & terminate + + memcpy( buf+pos, src2, src2_len ); + buf[res_len] = 0; + return res_len; + } + + no_src1: + if ( src2_len < buf_size ) + { + memcpy(buf,src2,src2_len); + buf[src2_len] = 0; + return src2_len; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int NormalizeIndent ( int indent ) +{ + return indent < 0 ? 0 : indent < 50 ? indent : 50; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControls ( ccp src ) +{ + // skips all character 1..SPACE + // 'src' can be NULL + + if (src) + while ( *src > 0 && *src <= ' ' ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControlsE ( ccp src, ccp end ) +{ + // skips all character 0..SPACE + // if end==NULL: Use SkipControls() + // 'src' can be NULL + + if (!end) + return SkipControls(src); + + if (src) + while ( src < end && (uchar)*src <= ' ' ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControls1 ( ccp src, char ch1 ) +{ + // skips all character 1..SPACE and 'ch1' + // 'src' can be NULL + + if (src) + while ( *src > 0 && *src <= ' ' || *src == ch1 ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControlsE1 ( ccp src, ccp end, char ch1 ) +{ + // skips all character 0..SPACE and 'ch1' + // if end==NULL: Use SkipControls1() + // 'src' can be NULL + + if (!end) + return SkipControls1(src,ch1); + + if (src) + while ( src < end && ( (uchar)*src <= ' ' || *src == ch1 )) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControls2 ( ccp src, char ch1, char ch2 ) +{ + // skips all character 1..SPACE and 'ch1' and 'ch2' + // 'src' can be NULL + + if (src) + while ( *src > 0 && *src <= ' ' || *src == ch1 || *src == ch2 ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControlsE2 ( ccp src, ccp end, char ch1, char ch2 ) +{ + // skips all character 0..SPACE and 'ch1' and 'ch2' + // if end==NULL: Use SkipControls2() + // 'src' can be NULL + + if (!end) + return SkipControls2(src,ch1,ch2); + + if (src) + while ( src < end && ( (uchar)*src <= ' ' || *src == ch1 || *src == ch2 )) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// return the number of chars until first EOL ( \0 | \r | \n ); +// pointer are NULL save + +uint LineLen ( ccp ptr ) +{ + if (!ptr) + return 0; + + ccp p = ptr; + while ( *p && *p != '\n' && *p != '\r' ) + p++; + return p - ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint LineLenE ( ccp ptr, ccp end ) +{ + if (!ptr) + return 0; + if (!end) + return LineLen(ptr); + + ccp p = ptr; + while ( p < end && *p && *p != '\n' && *p != '\r' ) + p++; + return p - ptr; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint TrimBlanks +( + char *str, // pointer to string, modified + ccp end // NULL or end of string +) +{ + char *src = str; + char *dest = str; + + bool space = false; + while ( end ? src < end : *src ) + { + DASSERT( dest <= src ); + uchar ch = *src++; + if ( ch > ' ' ) + { + if (space) + { + space = false; + if ( dest > str ) + *dest++ = ' '; + } + *dest++ = ch; + } + else if ( ch == ' ' ) + space = true; + } + + if ( !end || dest < end ) + *dest = 0; + + return dest - str; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanName +( + // return a pointer to the first unused character + char *buf, // destination buffer, terminated with NULL + uint buf_size, // size of 'buf', must be >0 + ccp str, // source string + ccp end, // NULL or end of 'str' + bool allow_signs // true: allow '-' and '+' +) +{ + DASSERT(buf); + DASSERT(buf_size); + if (!str) + { + *buf = 0; + return 0; + } + + //--- skip leading blanks + + if (end) + while ( str < end && (uchar)*str <= ' ' ) + str++; + else + while ( str < end && *str && (uchar)*str <= ' ' ) + str++; + + char *dest = buf, *dest_end = buf + buf_size - 1; + while ( end ? str < end : *str ) + { + const int ch = *str; + if ( isalnum(ch) || ch == '_' || allow_signs && ( ch =='-' || ch == '+' ) ) + { + str++; + if ( dest < dest_end ) + *dest++ = toupper(ch); + } + else + break; + } + *dest = 0; + + if (end) + while ( str < end && (uchar)*str <= ' ' ) + str++; + else + while ( str < end && *str && (uchar)*str <= ' ' ) + str++; + + return (char*)str; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintVersion +( + char * buf, // result buffer + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 version // version number to print +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 20 ); + + version = htonl(version); // we use big endian here + const u8 * v = (const u8 *)&version; + + if (v[2]) + { + if ( !v[3] || v[3] == 0xff ) + snprintf(buf,buf_size,"%u.%02x.%02x",v[0],v[1],v[2]); + else + snprintf(buf,buf_size,"%u.%02x.%02x.beta%u",v[0],v[1],v[2],v[3]); + } + else + { + if ( !v[3] || v[3] == 0xff ) + snprintf(buf,buf_size,"%u.%02x",v[0],v[1]); + else + snprintf(buf,buf_size,"%u.%02x.beta%u",v[0],v[1],v[3]); + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintID +( + const void * id, // ID to convert in printable format + size_t id_len, // len of 'id' + void * buf // Pointer to buffer, size >= id_len + 1 + // If NULL, a local circulary static buffer is used +) +{ + if (!buf) + buf = GetCircBuf( id_len + 1); + + ccp src = id; + char * dest = buf; + while ( id_len-- > 0 ) + { + const char ch = *src++; + *dest++ = ch >= ' ' && ch < 0x7f ? ch : '.'; + } + *dest = 0; + + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// circ buf /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static char circ_buf[CIRC_BUF_SIZE]; +static char *circ_ptr = circ_buf; + +/////////////////////////////////////////////////////////////////////////////// + +char * GetCircBuf +( + // never returns NULL, but always ALIGN(4) + + u32 buf_size // wanted buffer size, caller must add 1 for NULL-term + // if buf_size > CIRC_BUF_MAX_ALLOC: + // ==> ERROR0(ERR_OUT_OF_MEMORY) +) +{ + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + buf_size = buf_size + 3 & ~3; + if ( buf_size > CIRC_BUF_MAX_ALLOC ) + { + ERROR0(ERR_OUT_OF_MEMORY, + "Circulary buffer too small: needed=%u, half-size=%zu\n", + buf_size, sizeof(circ_buf)/2 ); + ASSERT(0); + exit(ERR_OUT_OF_MEMORY); + } + + if ( circ_buf + sizeof(circ_buf) - circ_ptr < buf_size ) + circ_ptr = circ_buf; + + char *result = circ_ptr; + circ_ptr = result + buf_size; + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + noPRINT("CIRC-BUF: %3u -> %3zu / %3zu / %3zu\n", + buf_size, result-circ_buf, circ_ptr-circ_buf, sizeof(circ_buf) ); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * CopyCircBuf +( + // never returns NULL, but always ALIGN(4) + + cvp data, // source to copy + u32 data_size // see GetCircBuf() +) +{ + char *dest = GetCircBuf(data_size); + memcpy(dest,data,data_size); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * CopyCircBuf0 +( + // never returns NULL, but always ALIGN(4) + // an additional char is alloced and set to NULL + + cvp data, // source to copy + u32 data_size // see GetCircBuf() +) +{ + char *dest = GetCircBuf(data_size+1); + memcpy(dest,data,data_size); + dest[data_size] = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintCircBuf +( + ccp format, // format string for vsnprintf() + ... // arguments for 'format' +) +{ + char buf[CIRC_BUF_MAX_ALLOC-1]; + va_list arg; + va_start(arg,format); + const int len = vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + + return len > 0 ? CopyCircBuf0(buf,len) : (char*)EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ReleaseCircBuf +( + ccp end_buf, // pointer to end of previous alloced buffer + uint release_size // number of bytes to give back from end +) +{ + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + if ( end_buf == circ_ptr + && release_size <= CIRC_BUF_MAX_ALLOC + && circ_ptr >= circ_buf + release_size ) + { + circ_ptr -= release_size; + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct FastBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +FastBuf_t * InitializeFastBuf ( cvp mem, uint size ) +{ + DASSERT(mem); + DASSERT( size >= sizeof(FastBuf_t) ); + + FastBuf_t *fb = (FastBuf_t*)mem; + + int buf_size = (int)size - offsetof(FastBuf_t,fast_buf); + if ( buf_size < sizeof(fb->fast_buf) ) + { + PrintError(__FUNCTION__,__FILE__,__LINE__,0,ERR_OUT_OF_MEMORY, + "Out of memory, FastBuf_t is at least %zd bytes to short.", + sizeof(fb->fast_buf) - size ); + ASSERT(0); + return 0; + } + + fb->fast_buf_size = buf_size; + fb->buf = fb->ptr = fb->fast_buf; + fb->end = fb->buf + buf_size - 1; + DASSERT( fb->buf < fb->end ); + return fb; +} + +//----------------------------------------------------------------------------- + +FastBuf_t * InitializeFastBufAlloc ( FastBuf_t *fb, uint size ) +{ + DASSERT(fb); + InitializeFastBuf(fb,sizeof(*fb)); + + const uint new_size = GetGoodAllocSize(size+10); + fb->buf = MALLOC(new_size); + fb->ptr = fb->buf; + fb->end = fb->buf + new_size - 1; + return fb; +} + +/////////////////////////////////////////////////////////////////////////////// + +#define RESET_FASTBUF(fb) fb->buf = fb->ptr = fb->fast_buf; \ + fb->end = fb->buf + fb->fast_buf_size - 1; + +void ResetFastBuf ( FastBuf_t *fb ) +{ + DASSERT(fb); + if ( fb->buf != fb->fast_buf ) + FREE(fb->buf); + + RESET_FASTBUF(fb) +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFastBufStatus ( const FastBuf_t *fb ) +{ + char info[100]; + int len = snprintf(info,sizeof(info),"%s=%u/%u", + fb->buf == fb->fast_buf ? "FastBuf" : "Alloc", + (uint)(fb->ptr - fb->buf), (uint)(fb->end - fb->buf) ); + return CopyCircBuf(info,len+1); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * MoveFromFastBufString ( FastBuf_t *fb ) +{ + DASSERT(fb); + *fb->ptr = 0; + + char *res; + if ( fb->buf == fb->fast_buf ) + { + res = MEMDUP(fb->buf,fb->ptr-fb->buf); + fb->ptr = fb->buf; + } + else + { + res = fb->buf; + RESET_FASTBUF(fb) + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t MoveFromFastBufMem ( FastBuf_t *fb ) +{ + DASSERT(fb); + *fb->ptr = 0; + + mem_t res; + res.len = fb->ptr - fb->buf; + + if ( fb->buf == fb->fast_buf ) + { + res.ptr = MEMDUP(fb->buf,res.len); + fb->ptr = fb->buf; + } + else + { + res.ptr = fb->buf; + RESET_FASTBUF(fb) + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * GetSpaceFastBuf ( FastBuf_t *fb, uint size ) +{ + DASSERT(fb); + if ( size > fb->end - fb->ptr ) + { + const uint used = fb->ptr - fb->buf; + const uint new_size = GetGoodAllocSize( used + used/4 + size + 1000 ); + if ( fb->buf == fb->fast_buf ) + { + fb->buf = MALLOC(new_size); + memcpy(fb->buf,fb->fast_buf,used); + } + else + fb->buf = REALLOC(fb->buf,new_size); + fb->ptr = fb->buf + used; + fb->end = fb->buf + new_size - 1; + } + + char *res = fb->ptr; + fb->ptr += size; + return res; +} + +//----------------------------------------------------------------------------- + +int ReserveSpaceFastBuf ( FastBuf_t *fb, uint size ) +{ + DASSERT(fb); + if ( size > 0 ) + fb->ptr = GetSpaceFastBuf(fb,size); + return fb->end - fb->ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertFastBuf ( FastBuf_t *fb, int index, cvp source, int size ) +{ + DASSERT(fb); + DASSERT(source||!size); + + if ( size < 0 ) + size = strlen(source); + + const uint len = fb->ptr - fb->buf; + index = CheckIndex1(len,index); + noPRINT("index=%d/%d/%zd, size=%d\n",index,len,fb->end-fb->buf,size); + + char *ptr = GetSpaceFastBuf(fb,size); + char *dest = fb->buf + index; + DASSERT( dest >= fb->buf ); + DASSERT( dest + size + len <= fb->end ); + + memmove(dest+size,dest,ptr-dest); + memcpy(dest,source,size); + return index; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * AppendFastBuf ( FastBuf_t *fb, cvp source, int size ) +{ + DASSERT(fb); + DASSERT(source||!size); + + if ( size < 0 ) + size = strlen(source); + + char *dest = GetSpaceFastBuf(fb,size); + memcpy(dest,source,size); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendUTF8CharFastBuf ( FastBuf_t *fb, u32 code ) +{ + char buf[8]; + const uint len = PrintUTF8Char(buf,code) - buf; + + char *dest = GetSpaceFastBuf(fb,len); + memcpy(dest,buf,len); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendBE16FastBuf ( FastBuf_t *fb, u16 num ) +{ + DASSERT(fb); + + char *dest = GetSpaceFastBuf(fb,sizeof(num)); + write_be16(dest,num); +} + +//----------------------------------------------------------------------------- + +void AppendBE32FastBuf ( FastBuf_t *fb, u32 num ) +{ + DASSERT(fb); + + char *dest = GetSpaceFastBuf(fb,sizeof(num)); + write_be32(dest,num); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendInt64FastBuf ( FastBuf_t *fb, u64 val, IntMode_t mode ) +{ + DASSERT(fb); + + switch(mode) + { + case IMD_UNSET: + AppendFastBuf(fb,&val,sizeof(val)); + break; + + case IMD_BE1: + case IMD_LE1: AppendCharFastBuf(fb,val); break; + + case IMD_BE2: write_be16(GetSpaceFastBuf(fb,2),val); break; + case IMD_BE3: write_be24(GetSpaceFastBuf(fb,3),val); break; + case IMD_BE4: write_be32(GetSpaceFastBuf(fb,4),val); break; + case IMD_BE5: write_be40(GetSpaceFastBuf(fb,5),val); break; + case IMD_BE6: write_be48(GetSpaceFastBuf(fb,6),val); break; + case IMD_BE7: write_be56(GetSpaceFastBuf(fb,7),val); break; + case IMD_BE0: + case IMD_BE8: write_be64(GetSpaceFastBuf(fb,8),val); break; + + case IMD_LE2: write_le16(GetSpaceFastBuf(fb,2),val); break; + case IMD_LE3: write_le24(GetSpaceFastBuf(fb,3),val); break; + case IMD_LE4: write_le32(GetSpaceFastBuf(fb,4),val); break; + case IMD_LE5: write_le40(GetSpaceFastBuf(fb,5),val); break; + case IMD_LE6: write_le48(GetSpaceFastBuf(fb,6),val); break; + case IMD_LE7: write_le56(GetSpaceFastBuf(fb,7),val); break; + case IMD_LE0: + case IMD_LE8: write_le64(GetSpaceFastBuf(fb,8),val); break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +char * WriteFastBuf ( FastBuf_t *fb, uint offset, cvp source, int size ) +{ + DASSERT(fb); + + uint oldlen = fb->ptr - fb->buf; + uint minlen = offset + size; + if ( minlen > oldlen ) + { + char *ptr = GetSpaceFastBuf(fb,minlen-oldlen); + memset(ptr,0,minlen-oldlen); + } + DASSERT( fb->ptr - fb->buf >= minlen ); + char *dest = fb->buf + offset; + if (source) + memcpy( dest, source, size ); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AssignFastBuf ( FastBuf_t *fb, cvp source, int size ) +{ + DASSERT(fb); + + if ( size < 0 ) + size = strlen(source); + + if ( source == fb->buf ) + { + const uint used = fb->ptr - fb->buf; + if ( size < used ) + fb->ptr = fb->buf + size; + else + size = used; + } + else + { + fb->ptr = fb->buf; + if (source) + AppendFastBuf(fb,source,size); + *fb->ptr = 0; + } + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropFastBuf ( FastBuf_t *fb, int index, int size ) +{ + DASSERT(fb); + if (size) + { + const uint len = fb->ptr - fb->buf; + index = CheckIndex1(len,index); + if ( size < 0 ) + { + size = -size; + if ( size > index ) + size = index; + index -= size; + } + int index2 = CheckIndex1(len,index+size); + size = index2 - index; + if (size) + { + memmove(fb->buf+index,fb->buf+index2,len-index2+1); + fb->ptr -= size; + } + } + + return size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CopyFastBuf ( FastBuf_t *dest, const FastBuf_t *src ) +{ + if ( dest && dest != src ) + { + if (!src) + ClearFastBuf(dest); + else + AssignFastBuf( dest, src->buf, src->ptr - src->buf ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveFastBuf ( FastBuf_t *dest, FastBuf_t *src ) +{ + if ( dest && src && dest != src ) + { + if ( src->buf != src->fast_buf ) + { + // src is allocated + if ( dest->buf != dest->fast_buf ) + FREE(dest->buf); + dest->buf = src->buf; + dest->ptr = src->ptr; + dest->end = src->end; + InitializeFastBuf( src, offsetof(FastBuf_t,fast_buf) + src->fast_buf_size ); + } + else + AssignFastBuf( dest, src->buf, src->ptr - src->buf ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alloc info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool alloc_info_calced = false; +uint alloc_info_overhead = 8; // default values +uint alloc_info_block_size = 16; +uint alloc_info_add1 = 7; +uint alloc_info_add2 = 8; +uint alloc_info_mask = ~15; + +/////////////////////////////////////////////////////////////////////////////// + +uint SetupAllocInfo() +{ + if (!alloc_info_calced) + { + alloc_info_calced = true; + + enum { MAX = 200 }; + + char *alloc[MAX+1]; + memset(alloc,0,sizeof(alloc)); + + uint i, mask_found = ~0; + for ( i = 1; i <= MAX; i++ ) + { + alloc[i] = (char*)MALLOC(i); + noPRINT("%4x: %p [%zx]\n",i,alloc[i],alloc[i]-alloc[ i>0 ? i-1 : 0 ] ); + if ( i > 1 ) + { + int diff = abs ( alloc[i-1] - alloc[i] ); + mask_found &= diff - 1; + } + } + const int lo0 = FindLowest0Bit(&mask_found,1); + const int hi1 = FindHighest1Bit(&mask_found,1); + PRINT("mask: %08x, lo0=%d, hi1=%d\n",mask_found,lo0,hi1); + + if ( hi1 + 1 == lo0 ) + { + // -> maske besteht aus hi1 '1' gefolgt von ausnahmslos '0' + + alloc_info_mask = ~mask_found; + alloc_info_block_size = mask_found + 1; + PRINT("alloc_info_block_size = %d, alloc_info_mask = 0x%x\n", + alloc_info_block_size, alloc_info_mask ); + + int found = 0x100; + for ( i = 2; i <= MAX; i++ ) + { + const int diff = abs ( alloc[i-1] - alloc[i] ) - i; + if ( diff >= 0 && found > diff ) + found = diff; + } + + if ( found < MAX ) + { + alloc_info_overhead = found; + found %= alloc_info_block_size; + alloc_info_add1 = found - 1; + alloc_info_add2 = alloc_info_block_size - found; + PRINT("alloc_info_overhead = %d, alloc_info_add = %d, %d\n", + alloc_info_overhead, alloc_info_add1, alloc_info_add2 ); + } + } + + for ( i = 1; i <= MAX; i++ ) + FREE(alloc[i]); + } + + return alloc_info_block_size; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SplitArg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetSplitArg ( SplitArg_t *sa ) +{ + DASSERT(sa); + FREE(sa->argv); + FREE(sa->temp); + InitializeSplitArg(sa); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanSplitArg +( + // returns arg->argc; + + SplitArg_t *arg, // pointer to object + bool init_arg, // true: call InitializeSplitArg() + ccp src, // NULL or source string + ccp src_end, // end of 'src'; if NULL: calculated by strlen(src) + char *work_buf // pointer to a buffer for strings, may be 'src' + // size must be: length of 'src' + 1 + // if NULL, a temporary buffer is alloced. +) +{ + DASSERT(arg); + if (init_arg) + InitializeSplitArg(arg); + else + ResetSplitArg(arg); + + if (!src) + src = EmptyString; + if (!src_end) + src_end = src + strlen(src); + + #ifdef TEST0 + enum { N_PREDEF = 5 }; + #else + enum { N_PREDEF = 250 }; + #endif + char *argv_predef[N_PREDEF+1], + **argv_buf = argv_predef, + **argv_dest = argv_predef, + **argv_end = argv_predef + N_PREDEF; + *argv_dest++ = progname ? (char*)progname : ""; + uint argv_size = 0; + + if (!work_buf) + { + arg->temp_size = src_end - src + 1; + arg->temp = work_buf = MALLOC(arg->temp_size); + } + char *dest = work_buf; + + + //--- main loop + + for(;;) + { + while ( src < src_end + && ( !*src || *src == ' ' || *src == '\t' || *src == '\r' || *src == '\n' )) + { + src++; + } + if ( src == src_end ) + break; + + if ( argv_dest == argv_end ) + { + PRINT("NEED MORE: %zu/%zu size=%u: |%s|\n", + argv_dest-argv_buf, argv_end-argv_buf, argv_size, src ); + const uint idx = argv_dest - argv_buf; + if (argv_size) + { + argv_size += N_PREDEF; + argv_buf = REALLOC(argv_buf,(argv_size+1)*sizeof(*argv_buf)); + } + else + { + argv_size = 2*N_PREDEF; + char **temp = MALLOC((argv_size+1)*sizeof(*argv_buf)); + memcpy(temp,argv_buf,idx*sizeof(*argv_buf)); + argv_buf = temp; + } + argv_dest = argv_buf + idx; + argv_end = argv_buf + argv_size; + } + + *argv_dest++ = dest; + while ( src < src_end ) + { + const char ch = *src; + if ( !ch || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' ) + break; + src++; + if ( ch == '\\' && src < src_end ) + *dest++ = *src++; + else if ( ch == '\'' ) + { + while ( src < src_end && *src != '\'' ) + *dest++ = *src++; + if ( src < src_end ) + src++; + } + else if ( ch == '"' ) + { + while ( src < src_end && *src != '"' ) + { + char ch = *src++; + if ( ch == '\\' ) + { + uint code; + src = ScanEscape(&code,src,src_end); + ch = code; + } + *dest++ = ch; + } + if ( src < src_end ) + src++; + } + else + *dest++ = ch; + } + *dest++ = 0; + } + + *argv_dest = 0; + arg->argc = argv_dest - argv_buf; + if ( argv_size ) + { + DASSERT( argv_buf != argv_predef ); + arg->argv_size = argv_size; + arg->argv = argv_buf; + } + else + { + DASSERT( argv_buf == argv_predef ); + arg->argv_size = arg->argc; + arg->argv = MEMDUP(argv_buf,(arg->argv_size+1)*sizeof(*argv_dest)); + } + + return arg->argc; +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mem_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +mem_t EmptyMem = {EmptyString,0}; +mem_t NullMem = {0,0}; + +/////////////////////////////////////////////////////////////////////////////// + +mem_t MidMem ( const mem_t src, int begin, int count ) +{ + begin = CheckIndex1(src.len,begin); + if ( count < 0 ) + { + count = -count; + begin -= count; + if ( begin < 0 ) + { + count += begin; + begin = 0; + } + } + + mem_t res; + res.ptr = src.ptr + begin; + const int max = src.len - begin; + res.len = count < max ? count : max; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t BeforeMem ( const mem_t src, ccp ref ) +{ + mem_t res; + res.ptr = src.ptr; + res.len = ref < src.ptr ? 0 + : ref <= src.ptr + src.len ? ref - src.ptr + : src.len; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t BehindMem ( const mem_t src, ccp ref ) +{ + mem_t res; + if ( ref <= src.ptr ) + res = src; + else if ( ref <= src.ptr + src.len ) + { + res.ptr = ref; + res.len = src.ptr + src.len - ref; + } + else + { + res.ptr = src.ptr + src.len; + res.len = 0; + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CmpMem ( const mem_t s1, const mem_t s2 ) +{ + if ( s1.len < s2.len ) + { + const int stat = memcmp(s1.ptr,s2.ptr,s1.len); + return stat ? stat : -1; + } + + const int stat = memcmp(s1.ptr,s2.ptr,s2.len); + return stat ? stat : s1.len > s2.len; +} + +/////////////////////////////////////////////////////////////////////////////// + +int StrCmpMem ( const mem_t mem, ccp str ) +{ + const uint slen = str ? strlen(str) : 0; + + if ( mem.len < slen ) + { + const int stat = memcmp(mem.ptr,str,mem.len); + return stat ? stat : -1; + } + + const int stat = memcmp(mem.ptr,str,slen); + return stat ? stat : mem.len > slen; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool LeftStrCmpMemEQ ( const mem_t mem, ccp str ) +{ + const uint slen = str ? strlen(str) : 0; + return mem.len <= slen && !memcmp(mem.ptr,str,mem.len); +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t MemCat2A ( const mem_t m1, const mem_t m2 ) +{ + const uint len1 = m1.len >= 0 ? m1.len : strlen(m1.ptr); + const uint len2 = m2.len >= 0 ? m2.len : strlen(m2.ptr); + + char *dest; + mem_t res; + res.len = len1 + len2; + res.ptr = dest = MALLOC(res.len+1); + dest[res.len] = 0; + + if (len1) + memcpy(dest,m1.ptr,len1); + if (len2) + memcpy(dest+len1,m2.ptr,len2); + + return res; +} + +//----------------------------------------------------------------------------- + +mem_t MemCat3A ( const mem_t m1, const mem_t m2, const mem_t m3 ) +{ + const uint len1 = m1.len >= 0 ? m1.len : strlen(m1.ptr); + const uint len2 = m2.len >= 0 ? m2.len : strlen(m2.ptr); + const uint len3 = m3.len >= 0 ? m3.len : strlen(m3.ptr); + + char *dest; + mem_t res; + res.len = len1 + len2 + len3; + res.ptr = dest = MALLOC(res.len+1); + dest[res.len] = 0; + + if (len1) + memcpy(dest,m1.ptr,len1); + if (len2) + memcpy(dest+len1,m2.ptr,len2); + if (len3) + memcpy(dest+len1+len2,m3.ptr,len3); + + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mem_list_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMemList ( mem_list_t *ml ) +{ + DASSERT(ml); + memset(ml,0,sizeof(*ml)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMemList ( mem_list_t *ml ) +{ + DASSERT(ml); + FREE(ml->list); + FREE(ml->buf); + memset(ml,0,sizeof(*ml)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveMemList ( mem_list_t *dest, mem_list_t *src ) +{ + if ( dest != src ) + { + if (!dest) + ResetMemList(src); + else + { + ResetMemList(dest); + if (src) + { + memcpy(dest,src,sizeof(*dest)); + memset(src,0,sizeof(*src)); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrowBufMemList ( mem_list_t *ml, uint need_size ) +{ + if ( ml->buf_size < need_size ) + { + PRINT("MEM-LIST: MALLOC/BUF(%u)\n",need_size); + char *new_buf = MALLOC(need_size); + + //--- copy existing strings + + char *bufptr = new_buf; + mem_t *dest = ml->list; + uint i; + for ( i = 0; i < ml->used; i++, dest++ ) + if ( dest->len ) + { + memcpy(bufptr,dest->ptr,dest->len); + dest->ptr = bufptr; + bufptr += dest->len; + *bufptr++ = 0; + DASSERT( bufptr <= new_buf + need_size ); + noPRINT("OLD: %p,%u\n",dest->ptr,dest->len); + } + FREE(ml->buf); + ml->buf = new_buf; + ml->buf_used = bufptr - new_buf; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrepareMemList ( mem_list_t *ml, uint n_elem, uint buf_size ) +{ + DASSERT(ml); + if ( ml->size < n_elem ) + { + ml->size = n_elem; + PRINT("MEM-LIST: REALLOC/LIST(%u)\n",ml->size); + ml->list = REALLOC(ml->list,sizeof(*ml->list)*ml->size); + } + + GrowBufMemList(ml,buf_size); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InsertMemListN +( + mem_list_t *ml, // valid destination mem_list + int pos, // insert position => CheckIndex1() + const mem_t *src, // source list + uint src_len, // number of elements in source list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(ml); + DASSERT( src || !src_len ); + + //--- count needed size and elements + + const mem_t *sptr, *src_end = src + src_len; + + uint need_size = 0, add_elem = 0; + for ( sptr = src; sptr < src_end; sptr++ ) + { + if ( sptr->len ) + { + need_size += sptr->len + 1; // always terminate with NULL + add_elem++; + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr || src_ignore == 3 ) + add_elem++; + } + + if (!add_elem) + return; + + + //--- string pool management + + if (!ml->used) + ml->buf_used = 0; + + char *new_buf, *bufptr; + if ( ml->buf_used + need_size <= ml->buf_size ) + { + new_buf = 0; + bufptr = ml->buf + ml->buf_used; + ml->buf_used += need_size; + } + else + { + need_size += ml->buf_used + 20; + need_size += need_size/10; + + // don't use GrowBufMemList() here, because the old buffer is needed + // BUT: char old_buf = ml->buf; ml->buf = 0; GrowBufMemList(ml,need_size) + + PRINT("MEM-LIST: MALLOC/BUF(%u)\n",need_size); + bufptr = new_buf = MALLOC(need_size); + + //--- copy existing strings + + mem_t *dest = ml->list; + uint i; + for ( i = 0; i < ml->used; i++, dest++ ) + if ( dest->len ) + { + memcpy(bufptr,dest->ptr,dest->len); + dest->ptr = bufptr; + bufptr += dest->len; + *bufptr++ = 0; + DASSERT( bufptr <= new_buf+need_size ); + noPRINT("OLD: %p,%u\n",dest->ptr,dest->len); + } + } + + + //--- realloc list if necessary + + const uint need_elem = ml->used + add_elem; + if ( need_elem > ml->size ) + { + ml->size = need_elem + 5 + need_elem/10; + PRINT("MEM-LIST: REALLOC/LIST(%u)\n",ml->size); + ml->list = REALLOC(ml->list,sizeof(*ml->list)*ml->size); + } + + pos = CheckIndex1(ml->used,pos); + mem_t *dest = ml->list + pos; + if ( pos < ml->used ) + memmove(dest+add_elem,dest,sizeof(*dest)*(ml->used-pos)); + ml->used += add_elem; + + + //--- iterate source list + + for ( sptr = src; sptr < src_end; sptr++ ) + { + if (sptr->len) + { + dest->ptr = bufptr; + memcpy(bufptr,sptr->ptr,sptr->len); + dest->len = sptr->len; + bufptr += sptr->len; + *bufptr++ = 0; + DASSERT( bufptr <= ( new_buf ? new_buf+need_size : ml->buf + ml->buf_size ) ); + noPRINT("NEW: %p,%u\n",dest->ptr,dest->len); + dest++; + DASSERT( dest <= ml->list + ml->used ); + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr ) + { + dest->ptr = sptr->ptr ? EmptyString : NULL; + dest->len = 0; + dest++; + DASSERT( dest <= ml->list + ml->used ); + } + else if ( src_ignore == 3 ) + { + dest->ptr = EmptyString; + dest->len = 0; + dest++; + DASSERT( dest <= ml->list + ml->used ); + } + } + DASSERT_MSG( dest == ml->list + pos + add_elem, + "DEST= %zu != %u+%u == %u", + dest-ml->list, pos, add_elem, pos+add_elem ); + + + //--- clean + + if (new_buf) + { + DASSERT(ml->buf != new_buf); + FREE(ml->buf); + ml->buf = new_buf; + ml->buf_size = need_size; + } + ml->buf_used = bufptr - ml->buf; + DASSERT( ml->buf_used <= ml->buf_size ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CatMemListN +( + mem_list_t *dest, // valid destination mem_list + const mem_list_t **src_list, // list with mem lists, element may be NULL + uint n_src_list, // number of elements in 'src' + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(dest); + DASSERT( src_list || !n_src_list ); + + + //--- count needed size and elements + + uint need_size = 0, need_elem = 0; + bool dest_is_source = false; + + const mem_list_t **list_ptr, **src_list_end = src_list + n_src_list; + for ( list_ptr = src_list; list_ptr < src_list_end; list_ptr++ ) + { + if (!*list_ptr) + continue; + + if ( *list_ptr == dest ) + dest_is_source = true; + + const mem_t *sptr = (*list_ptr)->list, *send = sptr + (*list_ptr)->used; + for ( ; sptr < send; sptr++ ) + { + if ( sptr->len ) + { + need_size += sptr->len + 1; // always terminate with NULL + need_elem++; + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr || src_ignore == 3 ) + need_elem++; + } + } + + PRINT("CatMemList(N=%u) n_elem=%u, size=%u, dest_is_source=%d\n", + n_src_list,need_elem,need_size,dest_is_source); + if (!need_elem) + { + dest->used = 0; + return; + } + + + //--- setup dest buffers + + char *new_buf = dest_is_source || need_size > dest->buf_size + ? MALLOC(need_size) : dest->buf; + mem_t *new_list = dest_is_source || need_elem > dest->size + ? MALLOC(sizeof(*new_list)*need_elem) : dest->list; + + + + //--- assign values + + char *bufptr = new_buf; + mem_t *listptr = new_list; + + for ( list_ptr = src_list; list_ptr < src_list_end; list_ptr++ ) + { + if (!*list_ptr) + continue; + + const mem_t *sptr = (*list_ptr)->list, *send = sptr + (*list_ptr)->used; + for ( ; sptr < send; sptr++ ) + { + if (sptr->len) + { + listptr->ptr = bufptr; + memcpy(bufptr,sptr->ptr,sptr->len); + listptr->len = sptr->len; + bufptr += sptr->len; + *bufptr++ = 0; + DASSERT( bufptr <= new_buf + need_size ); + noPRINT("NEW: %p,%u\n",dest->ptr,dest->len); + listptr++; + DASSERT( listptr <= new_list + need_elem ); + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr ) + { + listptr->ptr = sptr->ptr ? EmptyString : NULL; + listptr->len = 0; + listptr++; + DASSERT( listptr <= new_list + need_elem ); + } + else if ( src_ignore == 3 ) + { + listptr->ptr = EmptyString; + listptr->len = 0; + listptr++; + DASSERT( listptr <= new_list + need_elem ); + } + } + } + DASSERT( bufptr == new_buf + need_size ); + DASSERT( listptr == new_list + need_elem ); + + + //--- terminate + + dest->buf_used = need_size; + if ( new_buf != dest->buf ) + { + FREE(dest->buf); + dest->buf = new_buf; + dest->buf_size = need_size; + } + + dest->used = need_elem; + if ( new_list != dest->list ) + { + FREE(dest->list); + dest->list = new_list; + dest->size = need_elem; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint LeftMemList ( mem_list_t *ml, int count ) +{ + DASSERT(ml); + return ml->used = CheckIndex1(ml->used,count); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RightMemList ( mem_list_t *ml, int count ) +{ + DASSERT(ml); + + const uint begin = CheckIndex1End(ml->used,-count); + if (begin) + { + ml->used -= begin; + memmove( ml->list, ml->list + begin, ml->used * sizeof(*ml->list) ); + } + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint MidMemList ( mem_list_t *ml, int begin, int count ) +{ + DASSERT(ml); + + begin = CheckIndex1(ml->used,begin); + if ( count < 0 ) + { + count = -count; + begin -= count; + if ( begin < 0 ) + { + count += begin; + begin = 0; + } + } + + const uint max = ml->used - begin; + if ( count > max ) + count = max; + memmove( ml->list, ml->list + begin, count * sizeof(*ml->list) ); + return ml->used = count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ExtractMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + ml->used = CheckIndex2(ml->used,&begin,&end); + memmove( ml->list, ml->list + begin, ml->used * sizeof(*ml->list) ); + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ExtractEndMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + ml->used = CheckIndex2End(ml->used,&begin,&end); + memmove( ml->list, ml->list + begin, ml->used * sizeof(*ml->list) ); + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + const uint sub = CheckIndex2(ml->used,&begin,&end); + memmove( ml->list + begin, ml->list + end, ( ml->used - end ) * sizeof(*ml->list) ); + return ml->used -= sub; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveEndMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + const uint sub = CheckIndex2End(ml->used,&begin,&end); + memmove( ml->list + begin, ml->list + end, ( ml->used - end ) * sizeof(*ml->list) ); + return ml->used -= sub; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MemPool_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetMemPool ( MemPool_t *mp ) +{ + DASSERT(mp); + MemPoolChunk_t *ptr = mp->chunk; + while (ptr) + { + MemPoolChunk_t *next = ptr->next; + FREE(ptr); + ptr = next; + } + InitializeMemPool(mp,mp->chunk_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +void * MallocMemPool ( MemPool_t *mp, uint size ) +{ + DASSERT(mp); + if (!size) + return (char*)EmptyString; + + if ( size > mp->space ) + { + uint data_size = size > mp->chunk_size ? size : mp->chunk_size; + if ( data_size < 0x400 ) + data_size = 0x400; + MemPoolChunk_t *chunk = MALLOC(sizeof(MemPoolChunk_t)+data_size); + DASSERT(chunk); + chunk->next = mp->chunk; + mp->chunk = chunk; + mp->space = data_size; + } + + DASSERT(mp->chunk); + DASSERT(mp->space >= size ); + + mp->space -= size; + return mp->chunk->data + mp->space; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * CallocMemPool ( MemPool_t *mp, uint size ) +{ + DASSERT(mp); + void *res = MallocMemPool(mp,size); + DASSERT(res); + memset(res,0,size); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * MallocMemPoolA ( MemPool_t *mp, uint size, uint align ) +{ + DASSERT(mp); + char *res = MallocMemPool(mp,size); + DASSERT(res); + const uint fix = mp->space - mp->space / align * align; + res -= fix; + mp->space -= fix; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * CallocMemPoolA ( MemPool_t *mp, uint size, uint align ) +{ + DASSERT(mp); + void *res = MallocMemPoolA(mp,size,align); + DASSERT(res); + memset(res,0,size); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * MemDupMemPool ( MemPool_t *mp, cvp source, uint size ) +{ + DASSERT(mp); + DASSERT(source||!size); + + char *res = MallocMemPool(mp,size+1); + memcpy(res,source,size); + res[size] = 0; + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CircBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[CircBuf_t]] + +void InitializeCircBuf ( CircBuf_t *cb, uint size ) +{ + DASSERT(cb); + memset(cb,0,sizeof(*cb)); + cb->size = size; //data is alloced on first insertion +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetCircBuf ( CircBuf_t *cb ) +{ + DASSERT(cb); + FREE(cb->buf); + cb->buf = 0; + cb->used = cb->pos = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint WriteCircBuf ( CircBuf_t *cb, cvp data, uint size ) +{ + DASSERT(cb); + DASSERT(data||!size); + + if ( !size || !cb->size ) + return 0; + + if (!cb->buf) + { + cb->buf = MALLOC(cb->size); + DASSERT(cb->buf); + cb->used = cb->pos = 0; + } + + ccp d = data; + if ( size >= cb->size ) + { + cb->used = cb->size; + cb->pos = 0; + memcpy( cb->buf, d + (size-cb->size), cb->size ); + //fprintf(stderr,"INS*: %d+%d = %d / %d\n",cb->pos,cb->used,cb->pos+cb->used,cb->size); + return cb->size; + } + + const int ins_count = size; + int ins_pos = cb->pos + cb->used; + if ( ins_pos >= cb->size ) + ins_pos -= cb->size; + + if ( ins_pos + size > cb->size ) + { + const uint copy_len = cb->size - ins_pos; + //fprintf(stderr,"COPY %u..%u\n",ins_pos,ins_pos+copy_len); + memcpy( cb->buf + ins_pos, d, copy_len ); + size -= copy_len; + d += copy_len; + ins_pos = 0; + } + + DASSERT( size > 0 ); + DASSERT( ins_pos <= cb->size ); + DASSERT( ins_pos + size <= cb->size ); + //fprintf(stderr,"COPY %u..%u\n",ins_pos,ins_pos+size); + memcpy( cb->buf + ins_pos, d, size ); + + cb->used += ins_count; + if ( cb->used > cb->size ) + { + cb->pos += cb->used - cb->size; + if ( cb->pos >= cb->size ) + cb->pos -= cb->size; + cb->used = cb->size; + } + + //fprintf(stderr,"INS: %d+%d = %d / %d\n",cb->pos,cb->used,cb->pos+cb->used,cb->size); + return ins_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PeakCircBuf ( CircBuf_t *cb, char *buf, uint buf_size ) +{ + DASSERT(cb); + DASSERT(buf||!buf_size); + + if ( !cb->size || !buf_size ) + return 0; + + if ( buf_size > cb->used ) + buf_size = cb->used; + + uint count = cb->size - cb->pos; + if ( count > buf_size ) + count -= buf_size; + + memcpy( buf, cb->buf + cb->pos, count ); + if ( count < buf_size ) + { + const uint copy_len = buf_size - count; + memcpy( buf+count, cb->buf, copy_len ); + count += copy_len; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ReadCircBuf ( CircBuf_t *cb, char *buf, uint buf_size ) +{ + DASSERT(cb); + const uint count = PeakCircBuf(cb,buf,buf_size); + return DropCircBuf(cb,count); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropCircBuf ( CircBuf_t *cb, uint size ) +{ + DASSERT(cb); + + if ( size >= cb->used ) + { + const uint res = cb->used; + cb->pos = cb->used = 0; + return res; + } + + cb->used -= size; + cb->pos += size; + if ( cb->pos >= cb->size ) + cb->pos -= cb->size; + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearCircBuf ( CircBuf_t *cb ) +{ + DASSERT(cb); + cb->used = cb->pos = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PurgeCircBuf ( CircBuf_t *cb ) +{ + DASSERT(cb); + + if ( cb->used && cb->pos ) + { + char *temp = MALLOC(cb->size); + PeakCircBuf(cb,temp,cb->used); + FREE(cb->buf); + cb->buf = temp; + } + cb->pos = 0; + return cb->used; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum CopyMode_t interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void * CopyData +( + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode, // copy mode + bool *res_alloced // not NULL: + // store true, if data must be freed + // otherwise store false +) +{ + DASSERT( data || !size ); + if ( !data || !size ) + { + if (res_alloced) + *res_alloced = false; + return (void*)EmptyString; + } + + if (res_alloced) + *res_alloced = mode != CPM_LINK; + + return mode == CPM_MOVE || mode == CPM_LINK + ? (void*)data + : MEMDUP(data,size); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeData +( + const void *data, // data to free + CopyMode_t mode // copy mode +) +{ + if ( mode != CPM_LINK && data != EmptyString ) + FREE((void*)data); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ContainerData_t, Container_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +Container_t * CreateContainer +( + // returns 'c' or the alloced container + // 'c' is always initialized + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +) +{ + if (!c) + c = MALLOC(sizeof(*c)); + memset(c,0,sizeof(*c)); + c->protect_level = protect; + + ContainerData_t *cdata = CALLOC(sizeof(*cdata),1); + c->cdata = cdata; + + cdata->data = CopyData(data,size,mode,&cdata->data_alloced); + cdata->size = size; + cdata->ref_count = 1; + if ( protect > 0 ) + cdata->protect_count++; + return c; +} + +/////////////////////////////////////////////////////////////////////////////// + +Container_t * InheritContainer +( + // returns 'c' or the alloced container + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + const Container_t *parent, // NULL or valid parent container + const void *data, // data to copy/move/link + uint size // size of 'data' +) +{ + if (!c) + c = CALLOC(sizeof(*c),1); + + if ( InContainerS(parent,data,size) || !InContainerS(c,data,size) ) + { + UnlinkContainerData(c); + UseContainerData(c,protect,parent); + } + return c; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool AssignContainer +( + // return TRUE on new ContainerData_t + + Container_t *c, // valid container; if NULL: only FREE(data) + int protect, // new protection level + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +) +{ + if (c) + { + SetProtectContainer(c,protect); + + if (!InContainerS(c,data,size)) + { + UnlinkContainerData(c); + ContainerData_t *cdata = CALLOC(sizeof(*cdata),1); + c->cdata = cdata; + cdata->data = CopyData(data,size,mode,&cdata->data_alloced); + cdata->size = size; + cdata->ref_count = 1; + if ( protect > 0 ) + cdata->protect_count++; + return true; + } + } + + if ( mode == CPM_MOVE ) + FREE((void*)data); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetContainer +( + Container_t *c // container to reset => no data +) +{ + if (c) + { + UnlinkContainerData(c); + memset(c,0,sizeof(*c)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void DeleteContainer +( + Container_t *c // container to reset and to free => no data +) +{ + if (c) + { + UnlinkContainerData(c); + FREE(c); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UnlinkContainerData +( + Container_t *c // container to reset => no data +) +{ + if ( c && c->cdata ) + { + ContainerData_t *cdata = c->cdata; + if ( c->protect_level > 0 ) + cdata->protect_count--; + c->cdata = 0; + if ( !--cdata->ref_count ) + { + if (cdata->data_alloced) + FREE((void*)cdata->data); + FREE(cdata); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * LinkContainerData +( + // increment 'ref_count' and return NULL or current container-data + // => use CatchContainerData() to complete operation + + const Container_t *c // NULL or valid container +) +{ + if ( !c || !c->cdata ) + return 0; + + c->cdata->ref_count++; + return c->cdata; +} + +//----------------------------------------------------------------------------- + +ContainerData_t * MoveContainerData +( + // return NULL or unlinked current container-data + // => use CatchContainerData() to complete operation + Container_t *c // NULL or valid container +) +{ + if ( !c || !c->cdata ) + return 0; + + ContainerData_t *cdata = c->cdata; + cdata->ref_count++; + UnlinkContainerData(c); + return cdata; +} + +//----------------------------------------------------------------------------- + +Container_t * CatchContainerData +( + // returns 'c' or the alloced container + // 'c' is always initialized + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + ContainerData_t *cdata // if not NULL: catch this +) +{ + if (!c) + c = MALLOC(sizeof(*c)); + memset(c,0,sizeof(*c)); + c->protect_level = protect; + + if (cdata) + { + c->cdata = cdata; + if ( protect > 0 ) + cdata->protect_count++; + } + return c; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * DisjoinContainerData +( + // Disjoin data container date without freeing it. Call either + // JoinContainerData() or FreeContainerData() to finish operation. + // Reference counters are not modified. + // Return the data container or NULL + + Container_t *c // NULL or valid container +) +{ + ContainerData_t *res = 0; + if ( c && c->cdata ) + { + const int old_protect = SetProtectContainer(c,0); + res = c->cdata; + c->cdata = 0; + SetProtectContainer(c,old_protect); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void JoinContainerData +( + // Join a data container, that was diosjoined by DisjoinContainerData() + // Reference counters are not modified. + + Container_t *c, // container to reset => no data + ContainerData_t *cdata // NULL or container-data to join +) +{ + if (c) + { + UnlinkContainerData(c); + DASSERT(!c->cdata); + if (cdata) + { + const int old_protect = SetProtectContainer(c,0); + c->cdata = cdata; + SetProtectContainer(c,old_protect); + } + } + else + FreeContainerData(cdata); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeContainerData +( + // Decrement the reference counter of an already disjoined data container + // and free it if unused. + + ContainerData_t *cdata // NULL or container-data to free +) +{ + if ( cdata && !--cdata->ref_count ) + { + if (cdata->data_alloced) + FREE((void*)cdata->data); + FREE(cdata); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ModifyAllContainer +( + // prepare modification of container-data, create a copy if necessary + // return true, if a new container-data is used + + Container_t *c // NULL or valid container +) +{ + if ( c && c->cdata && !ModificationAllowed(c) ) + { + ContainerData_t *oldcd = c->cdata; + ContainerData_t *newcd = CALLOC(sizeof(*newcd),1); + newcd->data = MEMDUP(oldcd->data,oldcd->size); + newcd->size = oldcd->size; + newcd->data_alloced = true; + newcd->ref_count = 1; + if ( c->protect_level > 0 ) + newcd->protect_count++; + UnlinkContainerData(c); + c->cdata = newcd; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool ModifyContainer +( + // prepare modification of container-data, create an extract if necessary + // return true, if a new container-data is used + + Container_t *c, // NULL or valid container + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +) +{ + if ( c && c->cdata && ( !ModificationAllowed(c) || !InContainerS(c,data,size) )) + { + ContainerData_t *newcd = CALLOC(sizeof(*newcd),1); + newcd->data = CopyData(data,size,mode,&newcd->data_alloced); + newcd->size = size; + newcd->ref_count = 1; + if ( c->protect_level > 0 ) + newcd->protect_count++; + UnlinkContainerData(c); + c->cdata = newcd; + return true; + } + + if ( mode == CPM_MOVE ) + FREE((void*)data); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +int SetProtectContainer +( + // returns 'c' new protection level + Container_t *c, // valid container, alloc one if NULL + int new_protect // new protection value +) +{ + DASSERT(c); + if (c->cdata) + { + if ( new_protect > 0 ) + { + if ( c->protect_level <= 0 ) + c->cdata->protect_count++; + } + else + { + if ( c->protect_level > 0 ) + c->cdata->protect_count--; + } + } + return c->protect_level = new_protect; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint DumpInfoContainer +( + // return the number of printed lines + + FILE *f, // valid output file + const ColorSet_t *colset, // NULL or color set + int indent, // indent the output + ccp prefix, // not NULL: use it as prefix behind indention + const Container_t *c, // dump infos for this container, NULL allowed + uint hexdump_len // max number of bytes used for a hexdump +) +{ + DASSERT(f); + if (!c) + return 0; + + indent = NormalizeIndent(indent); + if (!colset) + colset = GetColorSet0(); + + fprintf(f,"%*s%s%s%smod=%d, PL=%d, ", + indent,"", + colset->caption, prefix ? prefix : "", + colset->status, ModificationAllowed(c), c->protect_level ); + uint line_count = DumpInfoContainerData(f,0,0,0,c->cdata,hexdump_len,indent+2); + if (!line_count) + { + fputs("",f); + line_count++; + } + fprintf(f,"%s\n",colset->reset); + return line_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DumpInfoContainerData +( + // return the number of printed lines + + FILE *f, // valid output file + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + ccp prefix, // not NULL: use it as prefix behind indention + const + ContainerData_t *cdata, // dump infos for this container-data, NULL allowed + uint hexdump_len, // max number of bytes used for a hexdump + int hexdump_indent // indent of hexdump +) +{ + DASSERT(f); + if (!cdata) + return 0; + + indent = NormalizeIndent(indent); + if (!colset) + colset = GetColorSet0(); + + fprintf(f,"%*s%s%s%sPC=%d, RC=%d, alloc=%d, size=%u=0x%x, addr=%p\n", + indent,"", + colset->caption, prefix ? prefix : "", colset->status, + cdata->protect_count, cdata->ref_count, cdata->data_alloced, + cdata->size, cdata->size, cdata->data ); + + if ( hexdump_len > 0 && cdata->data && cdata->size ) + { + if ( hexdump_len > cdata->size ) + hexdump_len = cdata->size; + return 1 + HexDump16(f,hexdump_indent,0,cdata->data,hexdump_len); + } + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// split strings /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByCharMem +( + // 'mem_list' will be padded with {EmptyString,0} + + mem_t *mem_list, // array of mem_t + uint n_mem, // number of elements of 'mem_list' + mem_t source, // source string + char sep // separator character +) +{ + DASSERT(mem_list); + DASSERT(n_mem>1); + + ccp src = source.ptr; + ccp end = src + source.len; + + mem_t *mem = mem_list; + mem_t *last = mem + n_mem - 1; + + while ( mem < last ) + { + mem->ptr = src; + while ( src < end && *src != sep ) + src++; + + mem->len = src - mem->ptr; + mem++; + + if ( ++src > end ) + break; + } + + if ( src <= end ) + { + mem->ptr = src; + mem->len = end - src; + mem++; + } + const uint result = mem - mem_list; + + while ( mem <= last ) + { + mem->ptr = EmptyString; + mem->len = 0; + mem++; + } + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByTextMem +( + // 'mem_list' will be padded with {EmptyString,0} + + mem_t *mem_list, // array of mem_t + uint n_mem, // number of elements of 'mem_list' + mem_t source, // source string + mem_t sep // separator text +) +{ + DASSERT(mem_list); + DASSERT(n_mem>0); + + ccp src = source.ptr; + ccp end = src + source.len; + + mem_t *mem = mem_list; + mem_t *last = mem + n_mem - 1; + + if ( sep.len > 0 && sep.len <= source.len ) + { + ccp cmp_max = end - sep.len; + while ( mem < last ) + { + mem->ptr = src; + while ( src <= cmp_max && memcmp(src,sep.ptr,sep.len) ) + src++; + + if ( src > cmp_max ) + { + mem->len = end - mem->ptr; + mem++; + src = end + 1; + break; + } + + mem->len = src - mem->ptr; + mem++; + src += sep.len; + } + } + + if ( src <= end ) + { + mem->ptr = src; + mem->len = end - src; + mem++; + } + const uint result = mem - mem_list; + + while ( mem <= last ) + { + mem->ptr = EmptyString; + mem->len = 0; + mem++; + } + + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByCharMemList +( + mem_list_t *ml, // valid destination mem_list + bool init_ml, // true: initialize 'ml' first + mem_t source, // source string + char sep, // separator character + int max_fields // >0: max number of result fields +) +{ + DASSERT(ml); + if (init_ml) + InitializeMemList(ml); + ml->used = 0; + + if ( max_fields <= 0 ) + max_fields = INT_MAX; + GrowBufMemList(ml,source.len+1); + + ccp src = source.ptr; + ccp end = src + source.len; + + while ( max_fields-- > 1 ) + { + mem_t mem; + mem.ptr = src; + while ( src < end && *src != sep ) + src++; + + mem.len = src - mem.ptr; + AppendMemListN(ml,&mem,1,0); + if ( ++src > end ) + break; + } + + if ( src <= end ) + { + mem_t mem = { src, end - src }; + AppendMemListN(ml,&mem,1,0); + } + + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByTextMemList +( + mem_list_t *ml, // valid destination mem_list + bool init_ml, // true: initialize 'ml' first + mem_t source, // source string + mem_t sep, // separator text + int max_fields // >0: max number of result fields +) +{ + DASSERT(ml); + if (init_ml) + InitializeMemList(ml); + ml->used = 0; + + if ( max_fields <= 0 ) + max_fields = INT_MAX; + GrowBufMemList(ml,source.len+1); + + ccp src = source.ptr; + ccp end = src + source.len; + + if ( sep.len > 0 && sep.len <= source.len ) + { + ccp cmp_max = end - sep.len; + while ( max_fields-- > 1 ) + { + mem_t mem; + mem.ptr = src; + while ( src <= cmp_max && memcmp(src,sep.ptr,sep.len) ) + src++; + + if ( src > cmp_max ) + { + mem.len = end - mem.ptr; + AppendMemListN(ml,&mem,1,0); + src = end + 1; + break; + } + + mem.len = src - mem.ptr; + AppendMemListN(ml,&mem,1,0); + src += sep.len; + } + } + + if ( src <= end ) + { + mem_t mem = { src, end - src }; + AppendMemListN(ml,&mem,1,0); + } + + return ml->used; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatchPattern() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp AnalyseBrackets +( + ccp pattern, + ccp * p_start, + bool * p_negate, + int * p_multiple +) +{ + ASSERT(pattern); + + bool negate = false; + if ( *pattern == '^' ) + { + pattern++; + negate = true; + } + if (p_negate) + *p_negate = negate; + + int multiple = 0; + if ( *pattern == '+' ) + { + pattern++; + multiple = 1; + } + else if ( *pattern == '*' ) + { + pattern++; + multiple = 2; + } + if (p_multiple) + *p_multiple = multiple; + + if (p_start) + *p_start = pattern; + + if (*pattern) // ']' allowed in first position + pattern++; + while ( *pattern && *pattern++ != ']' ) // find end + ; + + return pattern; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool MatchBracktes +( + char ch, + ccp pattern, + bool negate +) +{ + if (!ch) + return false; + + bool ok = false; + ccp p = pattern; + for (; !ok && *p && ( p == pattern || *p != ']' ); p++ ) + { + if ( *p == '-' ) + { + if ( ch <= *++p && ch >= p[-2] ) + { + if (negate) + return false; + ok = true; + } + } + else + { + if ( *p == '\\' ) + p++; + + if ( *p == ch ) + { + if (negate) + return false; + ok = true; + } + } + } + return ok || negate; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool MatchPatternHelper +( + ccp pattern, + ccp text, + bool skip_end, + int alt_depth, + char path_sep // path separator character, standard is '/' +) +{ + ASSERT(pattern); + ASSERT(text); + noTRACE(" - %d,%d |%s|%s|\n",skip_end,alt_depth,pattern,text); + + char ch; + while ( ( ch = *pattern++ ) != 0 ) + { + switch (ch) + { + case '*': + if ( *pattern == '*' ) + { + pattern++; + if (*pattern) + while (!MatchPatternHelper(pattern,text,skip_end,alt_depth,path_sep)) + if (!*text++) + return false; + } + else + { + while (!MatchPatternHelper(pattern,text,skip_end,alt_depth,path_sep)) + if ( *text == path_sep || !*text++ ) + return false; + } + return true; + + case '#': + if ( *text < '0' || *text > '9' ) + return false; + while ( *text >= '0' && *text <= '9' ) + if (MatchPatternHelper(pattern,++text,skip_end,alt_depth,path_sep)) + return true; + return false; + + case ' ': + if ( *text < 1 || * text > ' ' ) + return false; + text++; + break; + + case '?': + if ( !*text || *text == path_sep ) + return false; + text++; + break; + + case '[': + { + ccp start; + bool negate; + int multiple; + TRACELINE; + pattern = AnalyseBrackets(pattern,&start,&negate,&multiple); + TRACELINE; + + if ( multiple < 2 && !MatchBracktes(*text++,start,negate) ) + return false; + + if (multiple) + { + while (!MatchPatternHelper(pattern,text,skip_end,alt_depth,path_sep)) + if (!MatchBracktes(*text++,start,negate)) + return false; + return true; + } + } + break; + + case '{': + for(;;) + { + if (MatchPatternHelper(pattern,text,skip_end,alt_depth+1,path_sep)) + return true; + // skip until next ',' || '}' + int skip_depth = 1; + while ( skip_depth > 0 ) + { + ch = *pattern++; + switch(ch) + { + case 0: + return false; + + case '\\': + if (!*pattern) + return false; + pattern++; + break; + + case '{': + skip_depth++; + break; + + case ',': + if ( skip_depth == 1 ) + skip_depth--; + break; + + case '}': + if (!--skip_depth) + return false; + break; + + case '[': // [2do] forgotten marker? + pattern = AnalyseBrackets(pattern,0,0,0); + break; + } + } + } + + case ',': + if (alt_depth) + { + alt_depth--; + int skip_depth = 1; + while ( skip_depth > 0 ) + { + ch = *pattern++; + switch(ch) + { + case 0: + return false; + + case '\\': + if (!*pattern) + return false; + pattern++; + break; + + case '{': + skip_depth++; + break; + + case '}': + skip_depth--; + break; + + case '[': // [2do] forgotten marker? + pattern = AnalyseBrackets(pattern,0,0,0); + break; + } + } + } + else if ( *text++ != ch ) + return false; + break; + + case '}': + if ( !alt_depth && *text++ != ch ) + return false; + break; + + case '$': + if ( !*pattern && !*text ) + return true; + if ( *text++ != ch ) + return false; + break; + + case '\\': + ch = *pattern++; + // fall through + + default: + if ( *text++ != ch ) + return false; + break; + } + } + return skip_end || *text == 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MatchPatternFull +( + ccp pattern, // pattern text + ccp text // raw text +) +{ + TRACE("MatchPattern(|%s|%s|)\n",pattern,text); + if ( !pattern || !*pattern ) + return true; + + if (!text) + text = ""; + + if ( *pattern == '^' ) + pattern++; + + return MatchPatternHelper(pattern,text,false,0,'/'); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MatchPattern +( + ccp pattern, // pattern text + ccp text, // raw text + char path_sep // path separator character, standard is '/' +) +{ + TRACE("MatchPattern(|%s|%s|%c|)\n",pattern,text,path_sep); + if ( !pattern || !*pattern ) + return true; + + if (!text) + text = ""; + + const size_t plen = strlen(pattern); + ccp last = pattern + plen - 1; + char last_ch = *last; + int count = 0; + while ( last > pattern && *--last == '\\' ) + count++; + if ( count & 1 ) + last_ch = 0; // no special char! + + if ( *pattern == path_sep || *pattern == '^' ) + { + pattern++; + return MatchPatternHelper(pattern,text,last_ch!='$',0,path_sep); + } + + while (*text) + if (MatchPatternHelper(pattern,text++,last_ch!='$',0,path_sep)) + return true; + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp MatchStringField ( const StringField_t * sf, ccp key ) +{ + DASSERT(sf); + DASSERT(key); + + ccp *ptr = sf->field, *end; + for ( end = ptr + sf->used; ptr < end; ptr++ ) + if (MatchPattern(*ptr,key,'/')) + return *ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * MatchParamField ( const ParamField_t * pf, ccp key ) +{ + DASSERT(pf); + DASSERT(key); + + ParamFieldItem_t *ptr = pf->field, *end; + for ( end = ptr + pf->used; ptr < end; ptr++ ) + if (MatchPattern(ptr->key,key,'/')) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * MatchRuleLine +( + // returns a pointer to the first non scanned char + + int *status, // not NULL: return match status here + // -2: no prefix found (no ruile found) + // -1: empty line (no ruile found) + // 0: rule found, but don't match + // 1: rule found and match + ccp src, // source line, scanned until CONTROL + char rule_prefix, // NULL or a rule-beginning-char' + ccp path, // path to verify + char path_sep // path separator character, standard is '/' +) +{ + int local_status; + if (!status) + status = &local_status; + + while ( *src > 0 && *src <= ' ' && *src != '\n' ) + src++; + + if (rule_prefix) + { + if ( *src != rule_prefix ) + { + *status = -2; + return (char*)src; + } + src++; + while ( *src > 0 && *src <= ' ' && *src != '\n' ) + src++; + } + + ccp start = src; + while ( (uchar)*src >= ' ' ) + src++; + + char buf[1000]; + uint len = src - start; + if (!len) + { + *status = -1; + return (char*)src; + } + + if ( len > sizeof(buf)-1 ) + len = sizeof(buf)-1; + memcpy(buf,start,len); + buf[len] = 0; + + *status = MatchPattern(buf,path,path_sep); + return (char*)src; +} + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan keywords /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t *GetKewordById +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id // id to search +) +{ + if (key_tab) + { + const KeywordTab_t *key; + for ( key = key_tab; *key->name1; key++ ) + if ( key->id == id ) + return key; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t *GetKewordByIdAndOpt +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id, // id to search + s64 opt // opt to search +) +{ + if (key_tab) + { + const KeywordTab_t *key; + for ( key = key_tab; *key->name1; key++ ) + if ( key->id == id && key->opt == opt ) + return key; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t * ScanKeyword +( + int * res_abbrev, // NULL or pointer to result 'abbrev_count' + ccp arg, // argument to scan + const KeywordTab_t * key_tab // valid pointer to command table +) +{ + DASSERT(arg); + char key_buf[KEYWORD_NAME_MAX]; + + char *dest = key_buf; + char *end = key_buf + sizeof(key_buf) - 1; + while ( *arg && *arg != ',' && *arg != ';' && dest < end ) + *dest++ = toupper((int)*arg++); + *dest = 0; + const int key_len = dest - key_buf; + + int favor_count = 0, abbrev_count = 0; + const KeywordTab_t *ct, *key_ct = 0; + const KeywordTab_t *favor_ct = 0, *favor_prev = 0, *abbrev_ct = 0, *abbrev_prev = 0; + for ( ct = key_tab; ct->name1; ct++ ) + { + if ( !strcmp(ct->name1,key_buf) ) + { + key_ct = ct; + break; + } + + bool name2_abbrev = false; + if (ct->name2) + { + if (!strcmp(ct->name2,key_buf)) + { + key_ct = ct; + break; + } + name2_abbrev = !memcmp(ct->name2,key_buf,key_len); + } + + if ( name2_abbrev && !memcmp(ct->name1,key_buf,strlen(ct->name1)) ) + { + if (!favor_ct) + { + favor_prev = favor_ct = ct; + favor_count++; + } + else if ( favor_prev->id != ct->id || favor_prev->opt != ct->opt ) + { + favor_prev = ct; + favor_count++; + } + } + + if ( *key_buf == '_' ) // no abbreviations for commands beginning with '_' + continue; + + if ( name2_abbrev || !memcmp(ct->name1,key_buf,key_len) ) + { + if (!abbrev_ct) + { + abbrev_prev = abbrev_ct = ct; + abbrev_count++; + } + else if ( abbrev_prev->id != ct->id || abbrev_prev->opt != ct->opt ) + { + abbrev_prev = ct; + abbrev_count++; + } + } + } + + if (key_ct) + abbrev_count = 0; + else if ( favor_count == 1 ) + { + key_ct = favor_ct; + abbrev_count = 0; + } + else if ( abbrev_count == 1 ) + key_ct = abbrev_ct; + else if (!abbrev_count) + abbrev_count = -1; + + if (res_abbrev) + *res_abbrev = abbrev_count; + + return key_ct; +} + +/////////////////////////////////////////////////////////////////////////////// + +s64 ScanKeywordListEx +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab, // valid pointer to command table + KeywordCallbackFunc func, // NULL or calculation function + bool allow_prefix, // allow '-' | '+' | '=' as prefix + u32 max_number, // allow numbers < 'max_number' (0=disabled) + s64 result, // start value for result + + uint err_mode, // bit field: + // 1: continue on error + ccp err_msg, // not NULL: print a warning message: + // ": Unknown keyword: " + enumError err_code, // use 'err_code' for the 'err_msg' + uint *err_count // not NULL: store errors here +) +{ + ASSERT(arg); + + char key_buf[KEYWORD_NAME_MAX]; + char *end = key_buf + sizeof(key_buf) - 1; + uint err_cnt = 0; + + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == ',' || *arg == '.' ) + arg++; + + if ( !*arg || *arg == '#' ) + break; + + char *dest = key_buf; + while ( *arg > ' ' && *arg != ',' && *arg != '.' && *arg != '#' + && ( *arg != '+' || dest == key_buf ) && dest < end ) + *dest++ = *arg++; + *dest = 0; + char prefix = 0; + int abbrev_count; + const KeywordTab_t * cptr = ScanKeyword(&abbrev_count,key_buf,key_tab); + if ( !cptr && allow_prefix && key_buf[1] + && ( *key_buf == '+' || *key_buf == '-' + || *key_buf == '/' || *key_buf == '=' )) + { + prefix = *key_buf == '/' ? '-' : *key_buf; + cptr = ScanKeyword(&abbrev_count,key_buf+1,key_tab); + } + + KeywordTab_t ct_num = { 0, key_buf, 0, 0 }; + if ( max_number && abbrev_count ) + { + char * start = key_buf + (prefix!=0); + ulong num = str2l(start,&dest,10); + if ( num < max_number && dest > start && !*dest ) + { + ct_num.id = num; + cptr = &ct_num; + } + } + + noPRINT("cptr=%p [%s], opt=%llx, prefix=%02x\n", + cptr, cptr ? cptr->name1 : "", cptr ? cptr->opt : 0, prefix ); + if ( !cptr || cptr->opt && prefix && prefix != '+' ) + { + if (err_msg) + { + if ( abbrev_count > 1 ) + ERROR0(err_code,"%s: Ambiguous keyword: %s\n",err_msg,key_buf); + else + ERROR0(err_code,"%s: Unknown keyword: %s\n",err_msg,key_buf); + } + err_cnt++; + if ( err_mode & 1 ) + continue; + result = M1(result); + break; + } + + if (func) + { + const s64 res = func(0,key_buf,key_tab,cptr,prefix,result); + if ( res == M1(res) ) + { + if ( !(err_mode & 1) ) + { + result = res; + break; + } + } + else + result = res; + } + else + { + switch (prefix) + { + case 0: + case '+': result = result & ~cptr->opt | cptr->id; break; + case '-': result &= ~cptr->id; break; + case '=': result = cptr->id; break; + } + } + } + + if (err_count) + *err_count = err_cnt; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanKeywordListFunc +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab, // valid pointer to command table + KeywordCallbackFunc func, // calculation function + void * param, // used define parameter for 'func' + bool allow_prefix // allow '-' | '+' | '=' as prefix +) +{ + ASSERT(arg); + ASSERT(func); + + char key_buf[KEYWORD_NAME_MAX]; + char *end = key_buf + sizeof(key_buf) - 1; + + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == ',' || *arg == '.' ) + arg++; + + if (!*arg) + return ERR_OK; + + char *dest = key_buf; + while ( *arg > ' ' && *arg != ',' && *arg != '.' + && ( *arg != '+' || dest == key_buf ) && dest < end ) + *dest++ = *arg++; + *dest = 0; + char prefix = 0; + int abbrev_count; + const KeywordTab_t * cptr = ScanKeyword(&abbrev_count,key_buf,key_tab); + if ( !cptr && allow_prefix && key_buf[1] + && ( *key_buf == '+' || *key_buf == '-' || *key_buf == '/' || *key_buf == '=' )) + { + prefix = *key_buf == '/' ? '-' : *key_buf; + cptr = ScanKeyword(&abbrev_count,key_buf+1,key_tab); + } + + const enumError err = func(param,key_buf,key_tab,cptr,prefix,0); + if (err) + return err; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static s64 ScanKeywordListMaskHelper +( + void * param, // NULL or user defined parameter + ccp name, // normalized name of option + const KeywordTab_t * key_tab, // valid pointer to command table + const KeywordTab_t * key, // valid pointer to found command + char prefix, // 0 | '-' | '+' | '=' + s64 result // current value of result +) +{ + return key->opt + ? result & ~key->opt | key->id + : key->id; +} + +//----------------------------------------------------------------------------- + +s64 ScanKeywordListMask +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab // valid pointer to command table +) +{ + return ScanKeywordList(arg,key_tab,ScanKeywordListMaskHelper,false,0,0,0,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CollectAmbiguousKeywords +( + char *buf, // destination buffer, 200 bytes are good + uint buf_size, // size of buffer + const KeywordTab_t * key_tab, // NULL or pointer to command table + ccp key_arg // analyzed command +) +{ + DASSERT(buf); + DASSERT(buf_size>10); + DASSERT(key_tab); + DASSERT(key_arg); + + int n = 0; + char *dest = buf, *buf_end = buf + buf_size - 2; + const int arg_len = strlen(key_arg); + int last_id = -1; + + const KeywordTab_t *ct; + for ( ct = key_tab; ct->name1 && dest < buf_end; ct++ ) + { + if ( ct->id != last_id ) + { + ccp ok = 0; + if (!strncasecmp(key_arg,ct->name1,arg_len)) + ok = ct->name1; + else if ( ct->name2 && !strncasecmp(key_arg,ct->name2,arg_len)) + ok = ct->name2; + if (ok) + { + if (!n++) + { + *dest++ = ' '; + *dest++ = '['; + } + else if ( n > 5 ) + { + dest = StringCopyE(dest,buf_end,",..."); + break; + } + else + *dest++ = ','; + dest = StringCopyE(dest,buf_end,ok); + last_id = ct->id; + } + } + } + if ( dest > buf+1 ) + *dest++ = ']'; + else + dest = buf; + *dest = 0; + return dest - buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintKeywordError +( + const KeywordTab_t * key_tab, // NULL or pointer to command table + ccp key_arg, // analyzed command + int key_stat, // status of ScanKeyword() + ccp prefix, // NULL or first prefix for messages + ccp object // NULL or object for error messages + // default= 'command' +) +{ + DASSERT(key_arg); + + if ( !object || !*object ) + object = "command"; + + if ( key_stat <= 0 ) + return ERROR0(ERR_SYNTAX,"%sUnknown %s: %s\n", + prefix ? prefix : "", object, key_arg ); + + char buf[1000], *dest = buf; + if (key_tab) + { + int n = 0; + ccp buf_end = buf + sizeof(buf) - 2; + const int arg_len = strlen(key_arg); + const KeywordTab_t *ct; + int last_id = -1; + + for ( ct = key_tab; ct->name1 && dest < buf_end; ct++ ) + { + if ( ct->id != last_id ) + { + ccp ok = 0; + if (!strncasecmp(key_arg,ct->name1,arg_len)) + ok = ct->name1; + else if ( ct->name2 && !strncasecmp(key_arg,ct->name2,arg_len)) + ok = ct->name2; + if (ok) + { + if (!n++) + { + *dest++ = ' '; + *dest++ = '['; + } + else if ( n > 5 ) + { + dest = StringCopyE(dest,buf_end,",..."); + break; + } + else + *dest++ = ','; + dest = StringCopyE(dest,buf_end,ok); + last_id = ct->id; + } + } + } + if ( dest > buf+1 ) + *dest++ = ']'; + else + dest = buf; + } + *dest = 0; + + return ERROR0(ERR_SYNTAX,"%s%c%s abbreviation is ambiguous: %s%s\n", + prefix ? prefix : "", + toupper((int)*object), object+1, key_arg, buf ); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintKeywordList +( + // returns a pointer to the result buffer + + char *buf, // result buffer + // If NULL, a local circulary static buffer + // with max CIRC_BUF_MAX_ALLOC bytes is used + uint buf_size, // size of 'buf', at least 10 bytes if buf is set + uint *ret_length, // not NULL: store result length here + + const KeywordTab_t *tab, // related keyword table + u64 mode, // mode to print + u64 default_mode, // NULL or default mode + u64 hide_mode // bit field to hide parameters +) +{ + DASSERT( !buf || buf_size >= 10 ); + char temp[CIRC_BUF_MAX_ALLOC]; + if (!buf) + { + buf = temp; + buf_size = sizeof(temp); + } + + char *dest = buf; + char *end = buf + buf_size - 1; + + mode = mode | hide_mode; + u64 mode1 = mode; + + for ( ; tab->name1 && dest < end; tab++ ) + { + if ( !tab->id || tab->opt & hide_mode ) + continue; + + if ( tab->opt ? (mode & tab->opt) == tab->id : mode & tab->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,tab->name1); + mode &= ~(tab->id|tab->opt); + } + } + + if ( default_mode && mode1 == (default_mode|hide_mode) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + const uint len = dest - buf; + if (ret_length) + *ret_length = len; + + if ( buf == temp ) + { + buf = GetCircBuf(len+1); + memcpy(buf,temp,len+1); + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanKeywordOffOn +( + // returns 0 for '0|OFF'; 1 for '1|ON; -1 for empty; -2 on error + + ccp arg, // argument to scan + uint max_num, // >0: additionally accept+return number <= max_num + ccp object // NULL (silent) or object for error messages +) +{ + if (!arg||!*arg) + return -1; + + static const KeywordTab_t keytab[] = + { + { 0, "OFF", "0", 0 }, + { 1, "ON", "1", 0 }, + {0,0,0,0} + }; + + int status; + const KeywordTab_t *key = ScanKeyword(&status,arg,keytab); + if (key) + return key->id; + + if ( max_num > 0 ) + { + char *end; + const uint num = str2ul(arg,&end,10); + if ( !*end && num <= max_num ) + return num; + } + + if (object) + PrintKeywordError(keytab,arg,status,0,object); + + return -2; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OFF/AUTO/ON/FORCE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t KeyTab_OFF_AUTO_ON[] = +{ + { OFFON_OFF, "OFF", "-1", 0 }, + { OFFON_OFF, "DISABLED", 0, 0 }, + { OFFON_AUTO, "AUTO", "0", 0 }, + { OFFON_ON, "ON", "1", 0 }, + { OFFON_ON, "ENABLED", 0, 0 }, + { OFFON_FORCE, "FORCE", "2", 0 }, + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +int ScanKeywordOffAutoOn +( + // returns one of OFFON_* + + ccp arg, // argument to scan + int on_empty, // return this value on empty + uint max_num, // >0: additionally accept+return number <= max_num + ccp object // NULL (silent) or object for error messages +) +{ + if ( !arg || !*arg ) + return on_empty; + + int status; + const KeywordTab_t *key = ScanKeyword(&status,arg,KeyTab_OFF_AUTO_ON); + if (key) + return key->id; + + if ( max_num > 0 ) + { + char *end; + const int num = str2l(arg,&end,10); + if ( !*end && num >= OFFON_OFF && num <= max_num ) + return num; + } + + if (object) + PrintKeywordError(KeyTab_OFF_AUTO_ON,arg,status,0,object); + + return OFFON_ERROR; +} + +//----------------------------------------------------------------------------- + +ccp GetKeywordOffAutoOn ( OffOn_t value ) +{ + const KeywordTab_t *key = GetKewordById(KeyTab_OFF_AUTO_ON,value); + return key ? key->name1 : "?"; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan command lists /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ExecCommandOfList ( CommandList_t *cli ) +{ + DASSERT(cli); + if ( cli->n_bin ) + { + printf("%3u. %s [n_bin=%u] = |%.*s|\n", + cli->cmd_count, cli->cmd, cli->n_bin, cli->param_len, cli->param ); + uint i; + for ( i = 0; i < cli->n_bin; i++ ) + { + printf(" > binary #%u, len=%u\n",i,cli->bin[0].len); + HexDump16( stdout,5,0, cli->bin[i].ptr, + cli->bin[i].len < 0x20 ? cli->bin[i].len : 0x20 ); + } + } + else + printf("%3u. %s = |%.*s|\n", + cli->cmd_count, cli->cmd, cli->param_len, cli->param ); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanCommandList +( + // returns -1 on 'cli->fail_count'; otherwise 'cli->cmd_count' + + CommandList_t *cli, // valid command list + CommandListFunc func // NULL or function for each command +) +{ + DASSERT(cli); + + static uint log_pid = 0; + static uint log_seq = 0; + static FILE *log = 0; + + if ( cli->log_fname && cli->log_level && !log_pid ) + { + log_pid = getpid(); + log = fopen(cli->log_fname,"ab"); + if (log) + { + fcntl(fileno(log),F_SETFD,FD_CLOEXEC); + fprintf(log,"\n#\f\n#OPEN %s, pid=%d\n\n", + PrintTimeByFormat("%F %T %z",time(0)), log_pid ); + } + } + uint log_level = log ? cli->log_level : 0; + + cli->read_cmd_len = 0; + const char sep = cli->command_sep ? cli->command_sep : ';'; + ccp cmd = cli->command, end = cmd + cli->command_len; + if ( log_level > 0 ) + { + fprintf(log,"\n++ %u.%u len=%u\n", + log_pid, ++log_seq, cli->command_len ); + fflush(log); + } + + for(;;) + { + cli->n_bin = 0; + memset(cli->bin,0,sizeof(cli->bin)); + + //--- skip blanks and terminators => find begin of record + + while ( cmd < end && ( (uchar)*cmd <= ' ' || *cmd == sep )) + cmd++; + cli->read_cmd_len = cmd - cli->command; // definitley scanned! + + if ( cmd >= end ) + break; + + cli->record = (u8*)cmd; + + + //--- find end of command+param + + ccp endcmd = memchr(cmd,sep,end-cmd); + if (!endcmd) + { + if (!cli->is_terminated) + goto abort; + endcmd = end; + } + + + //--- scan and copy command + + ccp param = cmd; + while ( param < endcmd && ( isalnum(*param) || *param == '-' || *param == '_' ) ) + param++; + cli->cmd_len = param - cmd; + if ( cli->cmd_len > sizeof(cli->cmd)-1 ) + cli->cmd_len = sizeof(cli->cmd)-1; + + if ( log_level > 1 ) + { + fprintf(log,">%.*s\n",cli->cmd_len,cmd); + fflush(log); + } + + if (cli->change_case) + { + char *dest = cli->cmd; + uint count = cli->cmd_len; + if ( cli->change_case == 1 ) + while ( count-- > 0 ) + *dest++ = tolower((int)*cmd++); + else + while ( count-- > 0 ) + *dest++ = toupper((int)*cmd++); + *dest = 0; + } + else + { + memcpy(cli->cmd,cmd,cli->cmd_len); + cli->cmd[cli->cmd_len] = 0; + } + + + //--- scan binary + + while ( cli->n_bin < COMMAND_LIST_N_BIN ) + { + while ( param < endcmd && ( *param == ' ' || *param == '\t' )) + param++; + + if ( param == endcmd || *param != '\1' ) + break; // no binary! + + //-- From now on for binary examintation, only 'end' is relevant, + //-- because binary data may contain the separator character. + + if ( param+3 > end ) + { + if ( log_level > 1 ) + { + fprintf(log,"bin incomplete\n"); + fflush(log); + } + + if (cli->is_terminated) + { + // now we have a conflict: + fprintf(stderr, + "#WARN: Conflict in ExecCommandOfList():" + " Stream is terminated, but binary not: %s\n", + cli->user_ts ? cli->user_ts->info : "" ); + + if (log) + { + fprintf(log,"#WARN: Conflict: Stream is terminated, but binary not!\n"); + fflush(log); + } + } + goto abort; // incomplete + } + + const uint bin_len = be16(++param); + param += 2; + if ( log_level > 2 ) + { + fprintf(log,"bin %u,%u..%u/%u%s\n", + bin_len, + (uint)(param - cli->command), + (uint)(param - cli->command + bin_len), + cli->command_len, + param + bin_len > end ? " incomplete" : "" ); + fflush(log); + } + if ( param + bin_len > end ) + goto abort; // incomplete + + cli->bin[cli->n_bin].ptr = param; + cli->bin[cli->n_bin].len = bin_len; + param += bin_len; + cli->n_bin++; + + //-- rescan 'endcmd' after binary block + + endcmd = memchr(param,sep,end-param); + if (!endcmd) + { + if (!cli->is_terminated) + goto abort; // incomplete + endcmd = end; + } + } + + + //--- calc record length + + cli->input_len = (u8*)endcmd - cli->record; + if ( endcmd < end ) + cli->input_len++; + + + //--- scan param + + while ( param < endcmd && (uchar)*param <= ' ' ) + param++; + if ( param < endcmd && *param == '=' ) + { + param++; + while ( param < endcmd && (uchar)*param <= ' ' ) + param++; + } + + ccp endparam = endcmd-1; + while ( endparam > param && (uchar)*endparam <= ' ' ) + endparam--; + endparam++; + + if ( log_level > 2 && endparam > param ) + { + const uint plen = endparam - param, max = 25; + if ( plen <= max ) + fprintf(log,"par %.*s\n",plen,param); + else + fprintf(log,"par %.*s…+%u\n",max,param,plen-max); + fflush(log); + } + + if (cli->term_param) + { + if ( cli->term_param > 1 ) + { + ((char*)endparam)[0] = sep; + endparam++; + } + else + ((char*)endparam)[0] = 0; + } + + cli->param = param; + cli->param_len = endparam - param; + cli->record_len = endparam - (ccp)cli->record; + cli->read_cmd_len = endcmd - (ccp)cli->command; + cmd = endcmd; + + + //--- execute command + + if ( log_level > 3 ) + { + fprintf(log,"func\n"); + fflush(log); + const int stat = func(cli); + fprintf(log,"s=%d\n",stat); + fflush(log); + if ( stat < 0 ) + break; + } + else if ( func && func(cli) < 0 ) + break; + + cli->cmd_count++; + } + + abort:; + if ( log_level > 1 || log && cli->read_cmd_len > cli->command_len ) + { + fprintf(log,"-- %u,%u/%u%s\n", + cli->cmd_count, + cli->read_cmd_len, cli->command_len, + cli->read_cmd_len > cli->command_len ? " #FAIL!" : "" ); + fflush(log); + } + + // should never happen + if ( cli->read_cmd_len > cli->command_len ) + cli->read_cmd_len = cli->command_len; + + return cli->fail_count ? -1 : cli->cmd_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global commands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError Command_ARGTEST ( int argc, char ** argv ) +{ + printf("ARGUMENT TEST: %u arguments:\n",argc); + + int idx; + for ( idx = 0; idx < argc; idx++ ) + printf("%4u.: |%s|\n",idx,argv[idx]); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError Command_COLORS +( + int level, // only used, if mode==NULL + // < 0: status message (ignore mode) + // >= 1: include names + // >= 2: include alt names + // >= 3: include color names incl bold + // >= 4: include background color names + uint mode, // output mode => see PrintColorSetHelper() + uint format // output format => see PrintColorSetEx() +) +{ + if (!colout) + SetupStdMsg(); + + if ( level < 0 ) + { + printf("%s--color=%d [%s], colorize=%d [%s]\n" + "term=%s\n" + "stdout: tty=%d, mode=%d [%s], have-color=%d, n-colors=%u%s\n", + colout->status, + opt_colorize, GetColorModeName(opt_colorize,"?"), + colorize_stdout, GetColorModeName(colorize_stdout,"?"), + getenv("TERM"), isatty(fileno(stdout)), + colout->col_mode, GetColorModeName(colout->col_mode,"?"), + colout->colorize, colout->n_colors, colout->reset ); + return ERR_OK; + } + + if ( !mode && level > 0 ) + switch (level) + { + case 1: mode = 0x08; break; + case 2: mode = 0x18; break; + case 3: mode = 0x1b; break; + default: mode = 0x1f; break; + } + + if (!format) + { + const ColorMode_t col_mode = colout ? colout->col_mode : COLMD_AUTO; + PrintTextModes( stdout, 4, col_mode ); + PrintColorModes( stdout, 4, col_mode, GCM_ALT ); + #ifdef TEST + PrintColorModes( stdout, 4, col_mode, GCM_ALT|GCM_SHORT ); + #endif + } + if ( mode || format ) + PrintColorSetEx(stdout,4,colout,mode,format); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global commands: TESTCOLORS /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[color_info_t]] + +typedef struct color_info_t +{ + bool valid; // >0: data below is valid + + u8 m_index; // index of 0..255 for "\e[*m" + + s8 m_red; // -1:invalid, 0..5: red value of 'm_index' + s8 m_green; // -1:invalid, 0..5: green value of 'm_index' + s8 m_blue; // -1:invalid, 0..5: blue value of 'm_index' + s8 m_gray; // -1:invalid, 0..23: gray value of 'm_index' + + u32 ref_color; // reference colors, based on 'm_index'; + u32 src_color; // scanned color +} +color_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static bool AssignColorInfo ( color_info_t *ci, u32 rgb ) +{ + DASSERT(ci); + memset(ci,0,sizeof(*ci)); + + ci->src_color = rgb & 0xffffff; + ci->m_index = ConvertColorRGBToM256(ci->src_color); + ci->ref_color = ConvertColorM256ToRGB(ci->m_index); + + const uint r = ci->ref_color >> 16 & 0xff; + const uint g = ci->ref_color >> 8 & 0xff; + const uint b = ci->ref_color & 0xff; + + ci->m_red = SingleColorToM6(r); + ci->m_green = SingleColorToM6(g); + ci->m_blue = SingleColorToM6(b); + + ci->m_gray = ( r + g + b + 21 ) / 30; + if ( ci->m_gray > 23 ) + ci->m_gray = 23; + else if ( ci->m_gray > 0 ) + ci->m_gray--; + + return ci->valid = 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ScanColorInfo ( color_info_t *ci, ccp arg ) +{ + DASSERT(ci); + memset(ci,0,sizeof(*ci)); + if ( arg && *arg ) + { + arg = SkipControls(arg); + + ulong num, r, g, b; + if ( *arg >= 'g' && *arg <= 'z' ) + { + const char mode = *arg; + arg = SkipControls(arg+1); + + num = str2ul(arg,0,10); + switch (mode) + { + case 'm': + return AssignColorInfo(ci,ConvertColorM256ToRGB(num)); + + case 'g': + return AssignColorInfo(ci,ConvertColorM256ToRGB(232 + num % 24)); + + case 'c': + r = num / 100 % 10; + g = num / 10 % 10; + b = num % 10; + goto rgb5; + } + } + else + { + char *end; + num = str2ul(arg,&end,16); + + ci->valid = true; + if ( end - arg <= 3 ) + { + r = num >> 8 & 15; + g = num >> 4 & 15; + b = num & 15; + + rgb5: + if ( r > 5 ) r = 5; + if ( g > 5 ) g = 5; + if ( b > 5 ) b = 5; + return AssignColorInfo(ci,ConvertColorM256ToRGB(16 + 36*r + 6*g + b)); + } + return AssignColorInfo(ci,num); + } + } + + return ci->valid; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void PrintColorInfo ( FILE *f, color_info_t *ci ) +{ + DASSERT(f); + DASSERT(ci); + + if (ci->valid) + fprintf(f," %06x -> %06x : %3u : %u,%u,%u : %2u" + " \e[48;5;16;38;5;%um test \e[0m" + " \e[48;5;244;38;5;%um test \e[0m" + " \e[48;5;231;38;5;%um test \e[0m " + " \e[38;5;16;48;5;%um test \e[0m" + " \e[38;5;244;48;5;%um test \e[0m" + " \e[38;5;231;48;5;%um test \e[0m\n", + ci->src_color, ci->ref_color, + ci->m_index, ci->m_red, ci->m_green, ci->m_blue, ci->m_gray, + ci->m_index, ci->m_index, ci->m_index, + ci->m_index, ci->m_index, ci->m_index ); + else + fputs(" --\n",f); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError Command_TESTCOLORS ( int argc, char ** argv ) +{ + #if HAVE_PRINT + GetColorByName(colout,"blue"); + #endif + + printf("TEST COLORS: %u arguments:\n",argc); + + bool sep = true; + int idx; + for ( idx = 0; idx < argc; idx++ ) + { + ccp minus = strchr(argv[idx],'-'); + if ( minus || sep ) + { + putchar('\n'); + sep = false; + if ( strlen(argv[idx]) == 1 ) + continue; + } + + color_info_t ci1; + ScanColorInfo(&ci1,argv[idx]); + PrintColorInfo(stdout,&ci1); + + if (!minus) + continue; + + color_info_t ci2; + ScanColorInfo(&ci2,minus+1); + if ( ci2.ref_color == ci1.ref_color ) + continue; + + int r1 = ci1.ref_color >> 16 & 0xff; + int g1 = ci1.ref_color >> 8 & 0xff; + int b1 = ci1.ref_color & 0xff; + int r2 = ci2.ref_color >> 16 & 0xff; + int g2 = ci2.ref_color >> 8 & 0xff; + int b2 = ci2.ref_color & 0xff; + + int n_steps = abs( r2 - r1 ); + int diff = abs( g2 - g1 ); + if ( n_steps < diff ) + n_steps = diff; + diff = abs( b2 - b1 ); + if ( n_steps < diff ) + n_steps = diff; + + u8 prev_m = ci1.m_index; + u32 prev_rgb = ci1.ref_color; + int i; + for ( i = 1; i <= n_steps; i++ ) + { + uint r = ( r2 - r1 ) * i / n_steps + r1; + uint g = ( g2 - g1 ) * i / n_steps + g1; + uint b = ( b2 - b1 ) * i / n_steps + b1; + u8 new_m = ConvertColorRGB3ToM256(r,g,b); + if ( prev_m != new_m ) + { + prev_m = new_m; + color_info_t ci3; + AssignColorInfo(&ci3,r<<16|g<<8|b); + if ( prev_rgb != ci3.ref_color ) + { + prev_rgb = ci3.src_color = ci3.ref_color; + PrintColorInfo(stdout,&ci3); + } + } + } + sep = true; + } + putchar('\n'); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// https://en.wikipedia.org/wiki/ANSI_escape_code +// https://jonasjacek.github.io/colors/ +// +// 0..15: 000000 800000 008000 808000 000080 800080 008080 c0c0c0 +// 808080 ff0000 00ff00 ffff00 0000ff ff00ff 00ffff ffffff +// => real colors varies on implementation +// +// 6x6x6: 0 95 135 175 215 255 == 00 5f 87 af d7 ff +// +// gray: 8 18 28 ... 238 == 08 12 1c 26 30 3a ... e4 ee f8 +// +/////////////////////////////////////////////////////////////////////////////// +// +// mkw-ana testcol 000000-ffffff +// mkw-ana testcol 500-550 550-050 050-055 055-005 005-505 505-500 +// +/////////////////////////////////////////////////////////////////////////////// + +u32 ColorTab_M0_M15[16] = +{ + 0x000000, 0x800000, 0x008000, 0x808000, + 0x000080, 0x800080, 0x008080, 0xc0c0c0, + 0x808080, 0xff0000, 0x00ff00, 0xffff00, + 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, +}; + +/////////////////////////////////////////////////////////////////////////////// + +u8 ConvertColorRGB3ToM256 ( u8 r, u8 g, u8 b ) +{ + // 0..5 + const uint r6 = SingleColorToM6(r); + const uint g6 = SingleColorToM6(g); + const uint b6 = SingleColorToM6(b); + + u8 m256 = 36*r6 + 6*g6 + b6 + 16; + uint delta = abs ( ( r6 ? 55 + 40 * r6 : 0 ) - r ) + + abs ( ( g6 ? 55 + 40 * g6 : 0 ) - g ) + + abs ( ( b6 ? 55 + 40 * b6 : 0 ) - b ); + + //printf("%02x>%u %02x>%u %02x>%u : m=%u delta=%d\n",r,r6,g,g6,b,b6,m256,delta); + + uint m, gray, prev = 0x1000000; + for ( m = 232, gray = 8; m < 256; m++, gray += 10 ) + { + const uint d = abs( gray - r ) + abs( gray - g ) + abs( gray - b ); + if ( d > prev ) + break; + if ( d <= delta ) + { + delta = d; + m256 = m; + } + prev = d; + } + + return m256; +} + +/////////////////////////////////////////////////////////////////////////////// + +u8 ConvertColorRGBToM256 ( u32 rgb ) +{ + return ConvertColorRGB3ToM256( rgb >> 16, rgb >> 8, rgb ); +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 ConvertColorM256ToRGB ( u8 m256 ) +{ + if ( m256 < 16 ) + return ColorTab_M0_M15[m256]; + + if ( m256 < 232 ) + { + m256 -= 16; + const uint r = m256 / 36; + const uint g = m256 / 6 % 6; + const uint b = m256 % 6; + + u32 col = 0; + if (r) col |= 0x370000 + 0x280000 * r; + if (g) col |= 0x003700 + 0x002800 * g; + if (b) col |= 0x000037 + 0x000028 * b; + return col; + } + + m256 -= 232; + return 0x080808 + m256 * 0x0a0a0a; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dynamic data support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetDD ( DynData_t *dd ) +{ + if (dd) + { + FREE(dd->data); + dd->data = 0; + dd->len = dd->size = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InsertDD ( DynData_t *dd, const void *data, uint data_size ) +{ + DASSERT(dd); + DASSERT( !dd->data == !dd->size ); + DASSERT( dd->len <= dd->size ); + + if ( !data || !data_size ) + dd->len = 0; + else + { + if ( data_size > dd->size ) + { + FREE(dd->data); + dd->data = MALLOC(data_size); + dd->size = data_size; + } + + memcpy(dd->data,data,data_size); + dd->len = data_size; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetDDL ( DynDataList_t *ddl ) +{ + if (ddl) + { + DynData_t *dd = ddl->list; + DynData_t *end = dd + ddl->used; + for ( ; dd < end; dd++ ) + FREE(dd->data); + + FREE(ddl->list); + ddl->list = 0; + ddl->used = ddl->size = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +DynData_t *InsertDDL +( + DynDataList_t *ddl, // valid dynamic data list + uint index, // element index to change + const void *data, // NULL or data to assign + uint data_size // size of data +) +{ + DASSERT(ddl); + DASSERT( !ddl->list == !ddl->size ); + DASSERT( ddl->used <= ddl->size ); + + if ( index >= ddl->size ) + { + const uint new_size = 11 * index / 10 + 10; + ddl->list = REALLOC( ddl->list, sizeof(*ddl->list) * new_size ); + memset( ddl->list + ddl->size, 0, (new_size - ddl->size) * sizeof(*ddl->list) ); + ddl->size = new_size; + } + + if ( ddl->used <= index ) + { + uint idx; + for ( idx = ddl->used; idx < index; idx++ ) + ddl->list[idx].len = 0; + ddl->used = index+1; + } + + DynData_t *dd = ddl->list + index; + InsertDD(dd,data,data_size); + return dd; +} + +/////////////////////////////////////////////////////////////////////////////// + +DynData_t *GetDDL +( + DynDataList_t *ddl, // valid dynamic data list + uint index, // element index to get + uint create // create an empty element if not exist +) +{ + DASSERT(ddl); + DASSERT( !ddl->list == !ddl->size ); + DASSERT( ddl->used <= ddl->size ); + + return index < ddl->used + ? ddl->list + ddl->used + : create + ? InsertDDL(ddl, index >= create ? index : create-1, 0,0) + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// StringField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetStringField ( StringField_t * sf ) +{ + DASSERT(sf); + if (sf) + { + if ( sf->used > 0 ) + { + DASSERT(sf->field); + ccp *ptr = sf->field, *end; + for ( end = ptr + sf->used; ptr < end; ptr++ ) + FreeString(*ptr); + } + FREE(sf->field); + + int (*func_cmp)( ccp s1, ccp s2 ) = sf->func_cmp; + InitializeStringField(sf); + sf->func_cmp = func_cmp; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveStringField ( StringField_t * dest, StringField_t * src ) +{ + DASSERT(src); + DASSERT(dest); + if ( src != dest ) + { + ResetStringField(dest); + dest->field = src->field; + dest->used = src->used; + dest->size = src->size; + dest->func_cmp = src->func_cmp; + + InitializeStringField(src); + src->func_cmp = dest->func_cmp; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindStringFieldIndex ( const StringField_t * sf, ccp key, int not_found_value ) +{ + bool found; + const int idx = FindStringFieldHelper(sf,&found,key); + return found ? idx : not_found_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp FindStringField ( const StringField_t * sf, ccp key ) +{ + bool found; + const int idx = FindStringFieldHelper(sf,&found,key); + return found ? sf->field[idx] : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp * InsertStringFieldHelper ( StringField_t * sf, int idx ) +{ + DASSERT(sf); + DASSERT( sf->used <= sf->size ); + noPRINT("+SF: %u/%u/%u\n",idx,sf->used,sf->size); + if ( sf->used == sf->size ) + { + sf->size += 0x100; + sf->field = REALLOC(sf->field,sf->size*sizeof(*sf->field)); + } + DASSERT( idx <= sf->used ); + ccp * dest = sf->field + idx; + memmove(dest+1,dest,(sf->used-idx)*sizeof(*dest)); + sf->used++; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool InsertStringField ( StringField_t * sf, ccp key, bool move_key ) +{ + if (!key) + return 0; + + bool found; + int idx = FindStringFieldHelper(sf,&found,key); + if (found) + { + if (move_key) + FreeString(key); + } + else + { + ccp * dest = InsertStringFieldHelper(sf,idx); + *dest = move_key ? key : STRDUP(key); + } + + return !found; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveStringField ( StringField_t * sf, ccp key ) +{ + bool found; + uint idx = FindStringFieldHelper(sf,&found,key); + if (found) + { + sf->used--; + ASSERT( idx <= sf->used ); + ccp * dest = sf->field + idx; + FreeString(*dest); + memmove(dest,dest+1,(sf->used-idx)*sizeof(*dest)); + } + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveStringFieldByIndex ( StringField_t * sf, int index, int n ) +{ + DASSERT(sf); + + if ( index < 0 ) + index += sf->used; + if ( n < 0 ) + { + index += n; + n -= n; + } + + if ( index < 0 ) + index = 0; + int last = index + n; + if ( last > sf->used ) + last = sf->used; + if ( index >= last ) + return 0; + + ccp * src = sf->field + index; + ccp * end = sf->field + last; + while ( src < end ) + FreeString(*src++); + + memmove( sf->field + index, src, ( sf->used - last) * sizeof(*sf->field) ); + const uint rm = last - index; + sf->used -= rm; + return rm; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendStringField ( StringField_t * sf, ccp key, bool move_key ) +{ + DASSERT(sf); + if (key) + { + DASSERT( sf->used <= sf->size ); + noPRINT(">SF: %u/%u\n",sf->used,sf->size); + if ( sf->used == sf->size ) + { + sf->size += 0x100; + sf->field = REALLOC(sf->field,sf->size*sizeof(*sf->field)); + } + TRACE("AppendStringField(%s,%d) %d/%d\n",key,move_key,sf->used,sf->size); + ccp *dest = sf->field + sf->used++; + *dest = move_key ? key : STRDUP(key); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SortStringField ( StringField_t * sf ) +{ + DASSERT(sf); + if ( sf->used > 1 ) + { + StringField_t temp; + InitializeStringField(&temp); + temp.func_cmp = sf->func_cmp; + uint i; + for ( i = 0; i < sf->used; i++ ) + InsertStringField(&temp,sf->field[i],true); + sf->used = 0; + MoveStringField(sf,&temp); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindStringFieldHelper ( const StringField_t * sf, bool * p_found, ccp key ) +{ + DASSERT(sf); + + int (*cmp)( ccp s1, ccp s2 ) = sf->func_cmp ? sf->func_cmp : strcmp; + + int beg = 0; + if ( sf && key ) + { + int end = sf->used - 1; + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + const int stat = cmp(key,sf->field[idx]); + if ( stat < 0 ) + end = idx - 1; + else if ( stat > 0 ) + beg = idx + 1; + else + { + noTRACE("FindStringFieldHelper(%s) FOUND=%d/%d/%d\n", + key, idx, sf->used, sf->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + noTRACE("FindStringFieldHelper(%s) failed=%d/%d/%d\n", + key, beg, sf->used, sf->size ); + + if (p_found) + *p_found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadStringField +( + StringField_t * sf, // string field + bool init_sf, // true: initialize 'sf' first + bool sort, // true: sort 'sf' + ccp filename, // filename of source file + bool silent // true: don't print open/read errors +) +{ + ASSERT(sf); + DASSERT(filename); + DASSERT(*filename); + + TRACE("LoadStringField(%p,%d,%d,%s,%d)\n",sf,init_sf,sort,filename,silent); + + if (init_sf) + InitializeStringField(sf); + + FILE * f = fopen(filename,"rb"); + if (!f) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",filename); + return ERR_CANT_OPEN; + } + + char iobuf[10000]; + while (fgets(iobuf,sizeof(iobuf)-1,f)) + { + char *ptr = iobuf; + while (*ptr) + ptr++; + if ( ptr > iobuf && ptr[-1] == '\n' ) + { + ptr--; + if ( ptr > iobuf && ptr[-1] == '\r' ) + ptr--; + } + + if ( ptr > iobuf ) + { + *ptr++ = 0; + const size_t len = ptr-iobuf; + ptr = MALLOC(len); + memcpy(ptr,iobuf,len); + if (sort) + InsertStringField(sf,ptr,true); + else + AppendStringField(sf,ptr,true); + } + } + + fclose(f); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveStringField +( + StringField_t * sf, // valid string field + ccp filename, // filename of dest file + bool rm_if_empty // true: rm dest file if 'sf' is empty +) +{ + ASSERT(sf); + ASSERT(filename); + ASSERT(*filename); + + TRACE("SaveStringField(%p,%s,%d)\n",sf,filename,rm_if_empty); + + if ( !sf->used && rm_if_empty ) + { + unlink(filename); + return ERR_OK; + } + + FILE * f = fopen(filename,"wb"); + if (!f) + return ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",filename); + + enumError err = WriteStringField(f,filename,sf,0,0); + fclose(f); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError WriteStringField +( + FILE * f, // open destination file + ccp filename, // NULL or filename (needed on write error) + StringField_t * sf, // valid string field + ccp prefix, // not NULL: insert prefix before each line + ccp eol // end of line text (if NULL: use LF) +) +{ + if (!prefix) + prefix = ""; + if (!eol) + eol = "\n"; + + ccp *ptr = sf->field, *end; + for ( end = ptr + sf->used; ptr < end; ptr++ ) + if ( fprintf(f,"%s%s%s",prefix,*ptr,eol) < 0 ) + return ERROR1(ERR_WRITE_FAILED, + "Error while writing string list: %s\n", + filename ? filename : "?" ); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateStringField +( + FILE *f, // output file + ccp name_prefix, // NULL or prefix of name, up to 40 chars + uint tab_pos, // tab pos of '=' + const StringField_t *sf, // valid string field + EncodeMode_t emode // encoding mode +) +{ + DASSERT(f); + DASSERT(sf); + + if (!name_prefix) + name_prefix = EmptyString; + + int base = 8*tab_pos + 7 - strlen(name_prefix); + fprintf(f,"%sn%.*s= %u\n", name_prefix, (base-1)/8, Tabs20, sf->used ); + + char buf[5000], name[10]; + + uint i; + for ( i = 0; i < sf->used; i++ ) + { + const uint len = snprintf(name,sizeof(name),"%u",i); + mem_t mem = EncodeByModeMem(buf,sizeof(buf),sf->field[i],-1,emode); + fprintf(f, "%s%s%.*s= %s\n", + name_prefix, name, (base-len)/8, Tabs20, mem.ptr ); + if ( mem.ptr != buf ) + FreeMem(&mem); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateStringField +( + RestoreState_t *rs, // info data, may be modified + ccp name_prefix, // NULL or prefix of name, up to 40 chars + StringField_t *sf, // string field + bool init_sf, // true: initialize 'sf', otherwise reset + EncodeMode_t emode // encoding mode +) +{ + DASSERT(rs); + DASSERT(sf); + + if (init_sf) + InitializeStringField(sf); + else + ResetStringField(sf); + + if (!name_prefix) + name_prefix = EmptyString; + + char name[50]; + snprintf(name,sizeof(name),"%sn",name_prefix); + int n = GetParamFieldINT(rs,name,0); + PRINT("RestoreStateStringField(), N=%d\n",n); + if ( n > 0 ) + { + sf->size = n; + sf->field = MALLOC(sf->size*sizeof(*sf->field)); + + uint i; + for ( i = 0; i < n; i++ ) + { + snprintf(name,sizeof(name),"%s%u",name_prefix,i); + ParamFieldItem_t *pfi = GetParamField(rs,name); + if (pfi) + { + mem_t mem = DecodeByModeMem(0,0,pfi->data,-1,emode); + AppendStringField(sf,mem.ptr,true); + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ParamField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeParamField ( ParamField_t * pf ) +{ + DASSERT(pf); + memset(pf,0,sizeof(*pf)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetParamField ( ParamField_t * pf ) +{ + DASSERT(pf); + if (pf) + { + if ( pf->used > 0 ) + { + DASSERT(pf->field); + ParamFieldItem_t *ptr = pf->field, *end; + for ( end = ptr + pf->used; ptr < end; ptr++ ) + { + FreeString(ptr->key); + if (pf->free_data) + FREE(ptr->data); + } + } + FREE(pf->field); + + const bool free_data = pf->free_data; + int (*func_cmp)( ccp s1, ccp s2 ) = pf->func_cmp; + InitializeParamField(pf); + pf->free_data = free_data; + pf->func_cmp = func_cmp; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveParamField ( ParamField_t * dest, ParamField_t * src ) +{ + DASSERT(src); + DASSERT(dest); + if ( src != dest ) + { + ResetParamField(dest); + dest->field = src->field; + dest->used = src->used; + dest->size = src->size; + dest->free_data = src->free_data; + InitializeParamField(src); + src->free_data = dest->free_data; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindParamFieldIndex ( const ParamField_t * pf, ccp key, int not_found_value ) +{ + bool found; + const int idx = FindParamFieldHelper(pf,&found,key); + return found ? idx : not_found_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * FindParamField ( const ParamField_t * pf, ccp key ) +{ + bool found; + const int idx = FindParamFieldHelper(pf,&found,key); + return found ? pf->field + idx : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * FindParamFieldByNum ( const ParamField_t * pf, uint num ) +{ + DASSERT(pf); + noTRACE("FindParamFieldByNum(%x)\n",num); + + ParamFieldItem_t *ptr = pf->field, *end; + for ( end = ptr + pf->used; ptr < end; ptr++ ) + if ( ptr->num == num ) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ParamFieldItem_t * InsertParamFieldHelper ( ParamField_t * pf, int idx ) +{ + DASSERT(pf); + DASSERT( pf->used <= pf->size ); + noPRINT("+SF: %u/%u/%u\n",idx,pf->used,pf->size); + if ( pf->used == pf->size ) + { + pf->size += 0x100; + pf->field = REALLOC(pf->field,pf->size*sizeof(*pf->field)); + } + DASSERT( idx <= pf->used ); + ParamFieldItem_t * dest = pf->field + idx; + memmove(dest+1,dest,(pf->used-idx)*sizeof(*dest)); + pf->used++; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * FindInsertParamField + ( ParamField_t * pf, ccp key, bool move_key, bool *old_found ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + ParamFieldItem_t *item; + if (found) + { + if (move_key) + FreeString(key); + item = pf->field + idx; + } + else + { + item = InsertParamFieldHelper(pf,idx); + item->key = move_key ? key : STRDUP(key); + item->num = 0; + item->data = 0; + } + + if (old_found) + *old_found = found; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * CountParamField ( ParamField_t * pf, ccp key, bool move_key ) +{ + ParamFieldItem_t *item = FindInsertParamField(pf,key,move_key,0); + if (item) + item->num++; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool InsertParamField + ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + if (found) + { + if (move_key) + FreeString(key); + if (pf->free_data) + FREE((void*)data); + } + else + { + ParamFieldItem_t * dest = InsertParamFieldHelper(pf,idx); + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = (void*)data; + } + + return !found; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * InsertParamFieldEx + ( ParamField_t * pf, ccp key, bool move_key, uint num, bool *p_found ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + if (p_found) + *p_found = found; + + if (found) + { + if (move_key) + FreeString(key); + return pf->field + idx; + } + + ParamFieldItem_t * dest = InsertParamFieldHelper(pf,idx); + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ReplaceParamField + ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + if (found) + { + if (move_key) + FreeString(key); + + ParamFieldItem_t * dest = pf->field + idx; + dest->num = num; + FREE(dest->data); + dest->data = (void*)data; + } + else + { + ParamFieldItem_t * dest = InsertParamFieldHelper(pf,idx); + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = (void*)data; + } + + return !found; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveParamField ( ParamField_t * pf, ccp key ) +{ + bool found; + uint idx = FindParamFieldHelper(pf,&found,key); + if (found) + { + pf->used--; + ASSERT( idx <= pf->used ); + ParamFieldItem_t * dest = pf->field + idx; + FreeString(dest->key); + if (pf->free_data) + FREE(dest->data); + memmove(dest,dest+1,(pf->used-idx)*sizeof(*dest)); + } + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * AppendParamField + ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ) +{ + DASSERT(pf); + if (key) + { + DASSERT( pf->used <= pf->size ); + noPRINT(">SF: %u/%u\n",pf->used,pf->size); + if ( pf->used == pf->size ) + { + pf->size += 0x100; + pf->field = REALLOC(pf->field,pf->size*sizeof(*pf->field)); + } + TRACE("AppendParamField(%s,%d) %d/%d\n",key,move_key,pf->used,pf->size); + ParamFieldItem_t * dest = pf->field + pf->used++; + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = (void*)data; + return dest; + } + + if (pf->free_data) + FREE((void*)data); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindParamFieldHelper ( const ParamField_t * pf, bool * p_found, ccp key ) +{ + DASSERT(pf); + + int (*cmp)( ccp s1, ccp s2 ) = pf->func_cmp ? pf->func_cmp : strcmp; + + int beg = 0; + if ( pf && key ) + { + int end = pf->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + int stat = cmp(key,pf->field[idx].key); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + { + noTRACE("FindParamFieldHelper(%s) FOUND=%d/%d/%d\n", + key, idx, pf->used, pf->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + noTRACE("FindParamFieldHelper(%s) failed=%d/%d/%d\n", + key, beg, pf->used, pf->size ); + + if (p_found) + *p_found = false; + return beg; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Memory Maps /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMemMap ( MemMap_t * mm ) +{ + DASSERT(mm); + memset(mm,0,sizeof(*mm)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMemMap ( MemMap_t * mm ) +{ + DASSERT(mm); + + uint i; + if (mm->field) + { + for ( i = 0; i < mm->used; i++ ) + FREE(mm->field[i]); + FREE(mm->field); + } + memset(mm,0,sizeof(*mm)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyMemMap +( + MemMap_t * mm1, // merged mem map, not NULL, cleared + const MemMap_t * mm2, // NULL or second mem map + bool use_tie // TRUE: use InsertMemMapTie() +) +{ + DASSERT(mm1); + ResetMemMap(mm1); + MergeMemMap(mm1,mm2,use_tie); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MergeMemMap +( + MemMap_t * mm1, // merged mem map, not NULL, not cleared + const MemMap_t * mm2, // NULL or second mem map + bool use_tie // TRUE: use InsertMemMapTie() +) +{ + DASSERT(mm1); + if (mm2) + { + uint i; + for ( i = 0; i < mm2->used; i++ ) + { + const MemMapItem_t *src = mm2->field[i]; + if (src) + { + MemMapItem_t *it = use_tie + ? InsertMemMapTie(mm1,src->off,src->size) + : InsertMemMap(mm1,src->off,src->size); + if (it) + StringCopyS(it->info,sizeof(it->info),src->info); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +MemMapItem_t * FindMemMap ( MemMap_t * mm, off_t off, off_t size ) +{ + DASSERT(mm); + + off_t off_end = off + size; + int beg = 0; + int end = mm->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + MemMapItem_t * mi = mm->field[idx]; + if ( off_end <= mi->off ) + end = idx - 1 ; + else if ( off >= mi->off + mi->size ) + beg = idx + 1; + else + return mi; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertMemMapIndex +( + // returns the index of the new item + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +) +{ + DASSERT(mm); + uint idx = FindMemMapHelper(mm,off,size); + + DASSERT( mm->used <= mm->size ); + if ( mm->used == mm->size ) + { + mm->size += 64; + mm->field = REALLOC(mm->field,mm->size*sizeof(*mm->field)); + } + + DASSERT( idx <= mm->used ); + MemMapItem_t ** dest = mm->field + idx; + memmove(dest+1,dest,(mm->used-idx)*sizeof(*dest)); + mm->used++; + + MemMapItem_t * mi = MALLOC(sizeof(MemMapItem_t)); + mi->off = off; + mi->size = size; + mi->overlap = 0; + *dest = mi; + return idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemMapItem_t * InsertMemMap +( + // returns a pointer to a new item (never NULL) + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +) +{ + const uint idx = InsertMemMapIndex(mm,off,size); + // a C sequence point is important here + return mm->field[idx]; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool TieMemMap +( + // returns true if element are tied togehther + + MemMap_t * mm, // mem map pointer + uint idx, // tie element 'idx' and 'idx+1' + bool force // always tie and not only if overlapped +) +{ + DASSERT(mm); + DASSERT( idx+1 < mm->used ); + + MemMapItem_t * i1 = mm->field[idx]; + MemMapItem_t * i2 = mm->field[idx+1]; + if ( force || i1->off + i1->size >= i2->off ) + { + const off_t new_size = i2->off + i2->size - i1->off; + if ( i1->size < new_size ) + i1->size = new_size; + FREE(i2); + idx++; + mm->used--; + memmove( mm->field + idx, + mm->field + idx + 1, + ( mm->used - idx ) * sizeof(MemMapItem_t*) ); + + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemMapItem_t * InsertMemMapTie +( + // returns a pointer to a new or existing item (never NULL) + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +) +{ + uint idx = InsertMemMapIndex(mm,off,size); + + if ( idx > 0 && TieMemMap(mm,idx-1,false) ) + idx--; + + while ( idx + 1 < mm->used && TieMemMap(mm,idx,false) ) + ; + + return mm->field[idx]; +} + +/////////////////////////////////////////////////////////////////////////////// + +void InsertMemMapWrapper +( + void * param, // user defined parameter + u64 offset, // offset of object + u64 size, // size of object + ccp info // info about object +) +{ + noTRACE("InsertMemMapWrapper(%p,%llx,%llx,%s)\n",param,offset,size,info); + DASSERT(param); + MemMapItem_t * mi = InsertMemMap(param,offset,size); + StringCopyS(mi->info,sizeof(mi->info),info); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindMemMapHelper ( MemMap_t * mm, off_t off, off_t size ) +{ + DASSERT(mm); + + int beg = 0; + int end = mm->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + MemMapItem_t * mi = mm->field[idx]; + if ( off < mi->off ) + end = idx - 1 ; + else if ( off > mi->off ) + beg = idx + 1; + else if ( size < mi->size ) + end = idx - 1 ; + else if ( size > mi->size ) + beg = idx + 1; + else + { + TRACE("FindMemMapHelper(%llx,%llx) FOUND=%d/%d/%d\n", + (u64)off, (u64)size, idx, mm->used, mm->size ); + return idx; + } + } + + TRACE("FindStringFieldHelper(%llx,%llx) failed=%d/%d/%d\n", + (u64)off, (u64)size, beg, mm->used, mm->size ); + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CalcOverlapMemMap ( MemMap_t * mm ) +{ + DASSERT(mm); + + uint i, count = 0; + MemMapItem_t * prev = 0; + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t * ptr = mm->field[i]; + ptr->overlap = 0; + if ( prev && ptr->off < prev->off + prev->size ) + { + ptr ->overlap |= 1; + prev->overlap |= 2; + count++; + } + prev = ptr; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 FindFreeSpaceMemMap +( + // return address or NULL on not-found + + const MemMap_t * mm, + u64 addr_beg, // first possible address + u64 addr_end, // last possible address + u64 size, // minimal size + u64 align, // aligning + u64 *space // not NULL: store available space here +) +{ + DASSERT(mm); + + if (!size) + goto term; + + if (!align) + align = 1; + addr_beg = ALIGN64(addr_beg,align); + + uint i; + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t *mi = mm->field[i]; + const u64 end = ALIGN64( mi->off + mi->size, align ); + if ( mi->off > addr_beg ) + { + u64 found_space = ALIGNOFF64(mi->off,align) - addr_beg; + if ( size <= found_space ) + { + if (space) + *space = found_space; + return addr_beg; + } + } + if ( addr_beg < end ) + addr_beg = end; + } + + u64 found_space = ALIGNOFF64(addr_end,align) - addr_beg; + if ( size <= found_space ) + { + if (space) + *space = found_space; + return addr_beg; + } + + term: + if (space) + *space = 0; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintMemMap ( MemMap_t * mm, FILE * f, int indent, ccp info_head ) +{ + DASSERT(mm); + if ( !f || !mm->used ) + return; + + CalcOverlapMemMap(mm); + indent = NormalizeIndent(indent); + + static char ovl[][3] = { " ", "!.", ".!", "!!" }; + + if (!info_head) + info_head = "info"; + int i, max_ilen = strlen(info_head); + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t * ptr = mm->field[i]; + ptr->info[sizeof(ptr->info)-1] = 0; + const int ilen = strlen(ptr->info); + if ( max_ilen < ilen ) + max_ilen = ilen; + } + + fprintf(f,"%*s unused : off(beg) .. off(end) : size : %s\n%*s%.*s\n", + indent, "", info_head, + indent, "", max_ilen+52, Minus300 ); + + off_t max_end = mm->begin; + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t * ptr = mm->field[i]; + if ( !i && max_end > ptr->off ) + max_end = ptr->off; + const off_t end = ptr->off + ptr->size; + if ( ptr->off > max_end ) + fprintf(f,"%*s%s%10llx :%10llx ..%10llx :%10llx : %s\n", + indent, "", ovl[ptr->overlap&3], (u64)( ptr->off - max_end ), + (u64)ptr->off, (u64)end, (u64)ptr->size, ptr->info ); + else + fprintf(f,"%*s%s :%10llx ..%10llx :%10llx : %s\n", + indent, "", ovl[ptr->overlap&3], + (u64)ptr->off, (u64)end, (u64)ptr->size, ptr->info ); + if ( max_end < end ) + max_end = end; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Memory Map: Scan addresses /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanAddress +( + // returns first not scanned char of 'arg' + + ScanAddr_t * result, // result, initialized by ScanAddress() + ccp arg, // format: ADDR | ADDR:END | ADDR#SIZE + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +) +{ + DASSERT(result); + memset(result,0,sizeof(*result)); + if (!arg) + return 0; + + ccp end = ScanUINT(&result->addr,arg,default_base); + if ( end == arg ) + return (char*)arg; + arg = end; + while ( *arg == ' ' || *arg == '\t' ) + arg++; + result->size = 1; + result->stat = 1; + + if ( *arg == ':' ) + { + uint addr2; + end = ScanUINT(&addr2,++arg,default_base); + if ( end == arg ) + return(char*) arg-1; + + result->size = addr2 > result->addr ? addr2 - result->addr : 0; + result->stat = 2; + } + else if ( *arg == '#' ) + { + end = ScanUINT(&result->size,++arg,default_base); + result->stat = 2; + } + + return (char*)end; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertAddressMemMap +( + // returns the number of added addresses + + MemMap_t * mm, // mem map pointer + bool use_tie, // TRUE: use InsertMemMapTie() + ccp arg, // comma separated list of addresses + // formats: ADDR | ADDR:END | ADDR#SIZE + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +) +{ + DASSERT(mm); + if (!arg) + return 0; + + uint count = 0; + while (*arg) + { + ScanAddr_t addr; + arg = ScanAddress(&addr,arg,default_base); + if (addr.stat) + { + if (use_tie) + InsertMemMapTie(mm,addr.addr,addr.size); + else + InsertMemMap(mm,addr.addr,addr.size); + count++; + } + + while ( *arg && *arg != ' ' && *arg != '\t' && *arg != ',' ) + arg++; + while ( *arg == ' ' || *arg == '\t' || *arg == ',' ) + arg++; + } + + return count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DataBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeDataBuf + ( DataBuf_t *db, uint buf_size, DataBufFill_t func, void *user_data ) +{ + DASSERT(db); + memset(db,0,sizeof(*db)); + + if (buf_size) + { + db->size = buf_size; + db->buf = MALLOC(buf_size); + db->buf_end = db->buf + buf_size; + db->data = db->data_end = db->buf; + } + + db->fill_func = func; + db->user_data = user_data; + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetDataBuf ( DataBuf_t *db ) +{ + if (db) + { + DASSERT(!DumpDataBuf(stderr,0,db,true)); + FREE(db->buf); + memset(db,0,sizeof(*db)); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResizeDataBuf ( DataBuf_t *db, uint new_buf_size ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + DASSERT(new_buf_size>0); + + if (!db->buf) + return; + + PackDataBuf(db); + db->buf = REALLOC(db->buf,new_buf_size); + db->buf_end = db->buf + new_buf_size; + db->size = new_buf_size; + if ( db->used > new_buf_size ) + db->used = new_buf_size; + db->data = db->buf; + db->data_end = db->data + db->used; + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearDataBuf ( DataBuf_t *db, uint new_buf_size_if_not_null ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + db->offset = 0; + db->data = db->data_end = db->buf; + db->used = 0; + db->offset = 0; + if (new_buf_size_if_not_null) + ResizeDataBuf(db,new_buf_size_if_not_null); + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void EmptyDataBuf ( DataBuf_t *db ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + db->data = db->data_end = db->buf; + db->used = 0; + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PackDataBuf ( DataBuf_t *db ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( !db->used || !db->buf ) + { + db->data = db->data_end = db->buf; + } + else if ( db->data > db->buf ) + { + if ( db->data < db->data_end ) + { + // easy case: FREE1 DATA FREE2 + + memmove(db->buf,db->data,db->used); + } + else + { + // case: DATA2 FREE DATA1 + + uint d1len = db->buf_end - db->data; + uint d2len = db->data_end - db->buf; + DASSERT( d1len + d2len == db->used ); + + #ifdef TEST0 + u8 temp[10]; + #else + u8 temp[0x8000]; + #endif + if ( d2len <= sizeof(temp) && d2len < d1len ) + { + memcpy(temp,db->buf,d2len); + memmove(db->buf,db->data,d1len); + memcpy(db->buf+d1len,temp,d2len); + } + else if ( d1len <= sizeof(temp) ) + { + memcpy(temp,db->data,d1len); + memmove(db->buf+d1len,db->buf,d2len); + memcpy(db->buf,temp,d1len); + } + else + { + noPRINT("PACK %u+%u/%u, temp=%zu\n",d2len,d1len,db->size,sizeof(temp)); + // use a dynamic buffer => [2do]] optimize later + u8 *temp = MEMDUP(db->buf,d2len); + memmove(db->buf,db->data,d1len); + memcpy(db->buf+d1len,temp,d2len); + FREE(temp); + } + } + db->data = db->buf; + db->data_end = db->data + db->used; + } + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// debugging + +bool IsValidDataBuf ( DataBuf_t *db ) +{ + if ( !db || db->used > db->size ) + return false; + + if (db->buf) + { + if ( db->buf_end != db->buf + db->size + || db->data < db->buf || db->data >= db->buf_end ) + return false; + + u8 *end = db->data + db->used; + if ( end > db->buf_end ) + end -= db->size; + return end == db->data_end; + } + + return !db->size && !db->buf_end && !db->data && !db->data_end; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DumpDataBuf ( FILE *f, uint indent, DataBuf_t *db, bool if_invalid ) +{ + DASSERT(f); + DASSERT(db); + if ( if_invalid && IsValidDataBuf(db) ) + return false; + + fflush(stdout); + fflush(stderr); + indent = NormalizeIndent(indent); + if (!db) + { + fprintf(f,"%*sdata_buf_t: NULL\n",indent,""); + return true; + } + + if (!db->buf) + { + fprintf(f,"%*sdata_buf_t buf: Invalid buf\n",indent,""); + return true; + } + + fprintf(f,"%*sdata_buf_t %p .. %d, data %d .. %d, used %u/%u, off=%llu\n", + indent,"", db->buf, (int)(db->buf_end - db->buf), + (int)(db->data - db->buf), (int)(db->data_end - db->buf), + db->used, db->size, db->offset ); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FillPatternDataBuf ( DataBuf_t *db ) +{ + // fill the buffer with a test pattern: + // '-' for unused bytes + // ">0123456789<" in a longer or shorter form for used bytes + + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + memset(db->buf,'-',db->size); + + if ( db->used <= 1 ) + { + if ( db->used == 1 ) + *db->data = '+'; + return; + } + + u8 *ptr = db->data; + u8 *end = ptr + db->used - 1; + if ( end >= db->buf_end ) + end -= db->size; + *ptr++ = '>'; + *end = '<'; + uint i = 0, len = end - ptr; + if ( len > db->size ) + len += db->size; + for(;;) + { + if ( ptr == db->buf_end ) + ptr = db->buf; + if ( ptr == end ) + break; + *ptr++ = 'a' + i/len; + i += 26; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint InsertDataBuf +( + // Insert at top of buffer. + // Return the number of inserted bytes + + DataBuf_t *db, // valid pointer to data buffer + const void *data, // data to insert + uint data_size, // size of 'data' + bool all_or_none // true: insert complete data or nothing +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + uint space = db->size - db->used; + if ( data_size > space ) + { + if (all_or_none) + return 0; + data_size = space; + } + + if (!db->used) + db->data = db->data_end = db->buf + data_size; + db->used += data_size; + + uint copy_len = db->data - db->buf; + if ( copy_len > data_size ) + copy_len = data_size; + memcpy(db->data-copy_len,data,copy_len); + db->data -= copy_len; + if ( data_size > copy_len ) + { + const uint copy_len2 = data_size - copy_len; + memcpy(db->buf_end-copy_len2,(u8*)data+copy_len,copy_len2); + db->data = db->buf_end - copy_len2; + } + + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return data_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AppendDataBuf +( + // Append data at end of buffer + // Return the number of inserted bytes + + DataBuf_t *db, // valid pointer to data buffer + const void *data, // data to insert + uint data_size, // size of 'data' + bool all_or_none // true: insert complete data or nothing +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + uint space = db->size - db->used; + noPRINT("APPEND %u/%u [%u/%u]\n",data_size,space,db->used,db->size); + if ( data_size > space ) + { + if (all_or_none) + return 0; + data_size = space; + } + + if (!db->used) + db->data = db->data_end = db->buf; + db->used += data_size; + + uint copy_len = db->buf_end - db->data_end; + if ( copy_len > data_size ) + copy_len = data_size; + memcpy(db->data_end,data,copy_len); + db->data_end += copy_len; + if ( data_size > copy_len ) + { + const uint copy_len2 = data_size - copy_len; + memcpy(db->buf,(u8*)data+copy_len,copy_len2); + db->data_end = db->buf + copy_len2; + } + + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return data_size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void * GetPtrDataBuf +( + // Return a pointer to the data and make sure, + // that the data is not wrapped. Return NULL if failed. + + DataBuf_t *db, // valid pointer to data buffer + uint data_size, // number of needed bytes + uint align, // >1: Data alignment is also needed. + // Only 2,4,8,16,... are allowed, but not verified. + // Begin of buffer is always well aligned. + bool drop // true: On success, drop data from buffer + // In real, the data is available and only + // the pointers and counters are updated. +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if (!data_size) + return db->data; + if ( data_size > db->used ) + { + if ( data_size <= db->size && db->fill_func ) + db->fill_func(db,data_size-db->used,0); + if ( data_size > db->used ) + return 0; + } + + if ( db->data + data_size > db->buf_end ) + PackDataBuf(db); + else if ( align > 1 ) + { + const uint offset = db->data - db->buf; + if ( offset & (align-1) ) + PackDataBuf(db); + } + + u8 *res = db->data; + if (drop) + { + db->data += data_size; + db->used -= data_size; + DASSERT(!DumpDataBuf(stderr,0,db,true)); + } + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CopyDataBuf +( + // Copy data from buffer, but don't remove it + // Return the number of copied bytes + + DataBuf_t *db, // valid pointer to data buffer + void *dest, // destination buffer wit at least 'max_size' space + uint min_read, // return with 0 read bytes if less bytes available + // if 'min_read' > 'size', NULL is returned + uint max_read // try to read upto 'max_read' bytes +) +{ + DASSERT(db); + DASSERT(dest); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( min_read > db->used ) + { + if ( min_read <= db->size && db->fill_func ) + db->fill_func(db,min_read-db->used,0); + if ( min_read > db->used ) + return 0; + } + + if ( max_read > db->used ) + max_read = db->used; + uint copy_len1 = db->buf_end - db->data; + if ( copy_len1 >= max_read ) + memcpy(dest,db->data,max_read); + else + { + memcpy(dest,db->data,copy_len1); + memcpy((u8*)dest+copy_len1,db->buf,max_read-copy_len1); + } + + return max_read; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetDataBuf +( + // Copy data from buffer and remove it + // Return the number of copied bytes + + DataBuf_t *db, // valid pointer to data buffer + void *dest, // destination buffer wit at least 'max_size' space + uint min_read, // return with 0 read bytes if less bytes available + uint max_read // try to read upto 'max_read' bytes +) +{ + DASSERT(db); + DASSERT(dest); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( min_read > db->used ) + { + if ( min_read <= db->size && db->fill_func ) + db->fill_func(db,min_read-db->used,0); + if ( min_read > db->used ) + return 0; + } + + if ( max_read > db->used ) + max_read = db->used; + uint copy_len1 = db->buf_end - db->data; + if ( copy_len1 >= max_read ) + memcpy(dest,db->data,max_read); + else + { + memcpy(dest,db->data,copy_len1); + memcpy((u8*)dest+copy_len1,db->buf,max_read-copy_len1); + } + + db->used -= max_read; + if (db->used) + { + db->data += max_read; + if ( db->data >= db->buf_end ) + db->data -= db->size; + } + else + db->data = db->data_end = db->buf; + + db->offset += max_read; + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return max_read; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SkipDataBuf +( + // Skip 'skip_size' from input stream. Read more data if needed. + // Return the number of really skiped bytes + // If fill_func) + { + const uint max_fill = skip_size < db->size ? skip_size : db->size; + db->fill_func(db,max_fill,0); + } + + if (!db->used) + break; + } + + db->offset += skipped; + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return skipped; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropDataBuf +( + // Remove upto 'drop_size' bytes from buffer. + // Return the number of dropped bytes. + + DataBuf_t *db, // valid pointer to data buffer + uint drop_size // number of bytes to drop +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( drop_size >= db->used ) + { + drop_size = db->used; + db->used = 0; + db->data = db->data_end = db->buf; + } + else + { + db->data += drop_size; + if ( db->data >= db->buf_end ) + db->data -= db->size; + db->used -= drop_size; + } + db->offset += drop_size; + + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return drop_size; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ResizeElement_t, ResizeHelper_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeResize +( + ResizeHelper_t *r, // data structure + uint s_size, // size of source + uint d_size // size of dest +) +{ + DASSERT(r); + memset(r,0,sizeof(*r)); + + r->src_size = s_size; + r->dest_size= d_size; + if ( !s_size || !d_size ) + return; + + const uint divisor = gcd(s_size,d_size); + s_size /= divisor; + d_size /= divisor; + + r->sum_factor = s_size; + r->half_factor = r->sum_factor/2; + + r->idx_inc = s_size / d_size; + r->frac_inc = s_size % d_size; + r->frac_next = d_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool CalcResize ( ResizeHelper_t *r ) +{ + DASSERT(r); + + ResizeElement_t *re = r->elem; + re[0].n_elem = re[1].n_elem = re[2].n_elem = 0; + DASSERT(!re[3].n_elem); + + if ( r->src_idx >= r->src_size || r->dest_idx >= r->dest_size ) + return false; + + uint sum = r->sum_factor; + if (r->src_frac) + { + uint max = r->frac_next - r->src_frac; + if ( max > sum ) + max = sum; + re->n_elem = 1; + re->factor = max; + re++; + sum -= max; + } + + if ( sum >= r->frac_next ) + { + const uint n = sum / r->frac_next; + re->n_elem = n; + re->factor = r->frac_next; + re++; + sum -= n * r->frac_next; + } + + if ( sum > 0 ) + { + re->n_elem = 1; + re->factor = sum; + re++; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FirstResize ( ResizeHelper_t *r ) +{ + DASSERT(r); + + r->dest_idx = r->src_idx = r->src_frac = 0; + memset(r->elem,0,sizeof(r->elem)); + return CalcResize(r); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NextResize ( ResizeHelper_t *r ) +{ + DASSERT(r); + + r->dest_idx++; + r->src_idx += r->idx_inc; + r->src_frac += r->frac_inc; + if ( r->src_frac >= r->frac_next ) + { + r->src_frac -= r->frac_next; + r->src_idx++; + } + + return CalcResize(r); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GrowBuffer_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const uint grow_buffer_size_factor = 0x100; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeGrowBuffer ( GrowBuffer_t *gb, uint max_buf_size ) +{ + DASSERT(gb); + memset(gb,0,sizeof(*gb)); + gb->grow_size = grow_buffer_size_factor; + gb->max_size = ( max_buf_size + grow_buffer_size_factor - 1 ) + / grow_buffer_size_factor * grow_buffer_size_factor; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetGrowBuffer ( GrowBuffer_t *gb ) +{ + DASSERT(gb); + FREE(gb->buf); + const uint grow_size = gb->grow_size; + InitializeGrowBuffer(gb,gb->max_size); + gb->grow_size = grow_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetSpaceGrowBuffer +( + // returns the maximal possible space + const GrowBuffer_t *gb // valid grow-buffer object +) +{ + DASSERT(gb); + const uint max = gb->max_size > gb->size ? gb->max_size : gb->size; + return max - gb->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrepareGrowBuffer +( + // returns the number of available space, smaller than or equal 'size' + + GrowBuffer_t *gb, // valid grow-buffer object + uint size, // needed size + bool force // always alloc enough space and return 'size' +) +{ + #if HAVE_DASSERT + DASSERT(gb); + if (gb->buf) + { + DASSERT( gb->used <= gb->size ); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + } + else + { + DASSERT(!gb->ptr); + DASSERT(!gb->size); + DASSERT(!gb->used); + } + #endif + + if (!gb->buf) + { + //--- initial setup of buffer + + if ( gb->grow_size < grow_buffer_size_factor ) + gb->grow_size = grow_buffer_size_factor; + else + gb->grow_size = gb->grow_size + / grow_buffer_size_factor * grow_buffer_size_factor; + gb->size = ( size / gb->grow_size + 1 ) * gb->grow_size; + + if ( gb->max_size < grow_buffer_size_factor ) + gb->max_size = grow_buffer_size_factor; + else + gb->max_size = gb->max_size + / grow_buffer_size_factor * grow_buffer_size_factor; + if ( gb->size > gb->max_size ) + gb->size = gb->max_size; + + if ( gb->size < size && force ) + gb->size = ( size + gb->grow_size ) + / gb->grow_size * gb->grow_size - 1; + + gb->buf = gb->ptr = MALLOC(gb->size+1); // 1 extra byte for NULL-TERM + gb->used = 0; + } + else + { + if (!gb->grow_size) + gb->grow_size = grow_buffer_size_factor; + if (!gb->used) + gb->ptr = gb->buf; + } + DASSERT(gb->grow_size); + + uint avail = gb->size - gb->used; + if ( size > avail && ( force || gb->size < gb->max_size ) ) + { + //--- grow buffer + + gb->size = ( size + gb->used + gb->grow_size ) + / gb->grow_size * gb->grow_size - 1; + DASSERT( gb->used <= gb->size ); + u8 *newbuf = MALLOC(gb->size+1); // 1 extra byte for NULL-TERM + memcpy(newbuf,gb->ptr,gb->used); + FREE(gb->buf); + gb->buf = gb->ptr = newbuf; + } + + uint space = ( gb->buf + gb->size ) - ( gb->ptr + gb->used ); + if ( space < size && gb->buf != gb->ptr ) + { + memmove(gb->buf,gb->ptr,gb->used); + gb->ptr = gb->buf; + space = gb->size - gb->used; + } + + DASSERT( gb->used <= gb->size ); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + + return size < space ? size : space; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + const void *data, // data to insert + uint size // size of 'data' +) +{ + DASSERT(gb); + size = PrepareGrowBuffer(gb,size,false); + DASSERT( gb->ptr + gb->used + size <= gb->buf + gb->size ); + memcpy( gb->ptr + gb->used, data, size ); + gb->used += size; + gb->ptr[gb->used] = 0; + if ( gb->max_used < gb->used ) + gb->max_used = gb->used; + + DASSERT( gb->used <= gb->size ); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +char *tiparm ( ccp str, ... ); + +uint InsertCapNGrowBuffer +( + // insert 'cap_1' or 'cap_n' depending of 'n' and the existence of 'cap_*' + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + int n, // repeat number; if <1: insert nothing + mem_t cap_1, // default for N=1 + mem_t cap_n // default for N>1 +) +{ + DASSERT(gb); + + if ( n <= 0 ) + return 0; + + if ( n == 1 && cap_1.len ) + return InsertMemGrowBuffer(gb,cap_1); + + #if DCLIB_TERMINAL + if (cap_n.len) + { + ccp str = tiparm(cap_n.ptr,n); + if (str) + return InsertGrowBuffer(gb,str,strlen(str)); + } + #endif + + if (!cap_1.len) + return 0; + + const uint total = cap_1.len * n; + PrepareGrowBuffer(gb,total,true); + u8 *ptr = gb->ptr + gb->used; + while ( n-- > 0 ) + { + memcpy(ptr,cap_1.ptr,cap_1.len); + ptr += cap_1.len; + } + gb->used += total; + gb->ptr[gb->used] = 0; + if ( gb->max_used < gb->used ) + gb->max_used = gb->used; + return total; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint WriteCapN +( + // print 'cap_1' or 'cap_n' depending of 'n' and the existence of 'cap_*' + // returns the number of printed bytes + + FILE *f, // valid FILE + int n, // repeat number; if <1: insert nothing + mem_t cap_1, // default for N=1 + mem_t cap_n // default for N>1 +) +{ + DASSERT(f); + + if ( n <= 0 ) + return 0; + + if ( n == 1 && cap_1.len ) + return fwrite(cap_1.ptr,cap_1.len,1,f); + + #if DCLIB_TERMINAL + if (cap_n.len) + { + ccp str = tiparm(cap_n.ptr,n); + if (str) + return fwrite(str,strlen(str),1,f); + } + #endif + + if (!cap_1.len) + return 0; + + const uint total = cap_1.len * n; + while ( n-- > 0 ) + fwrite(cap_1.ptr,cap_1.len,1,f); + return total; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertCatchedGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + SavedStdFiles_t *ssf // saved output and catched data +) +{ + DASSERT(gb); + DASSERT(ssf); + + TermCatchStdFiles(ssf); + uint stat = 0; + if (ssf->data) + { + PrepareGrowBuffer(gb,ssf->size,true); + stat = InsertGrowBuffer(gb,ssf->data,ssf->size); + XFREE(ssf->data); + ssf->data = 0; + ssf->size = 0; + } + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint PrintArgGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + ccp format, // format string for vfprintf() + va_list arg // parameters for 'format' +) +{ + DASSERT(gb); + DASSERT(format); + + char buf[10000]; + + va_list arg2; + va_copy(arg2,arg); + int stat = vsnprintf(buf,sizeof(buf),format,arg2); + va_end(arg2); + + if ( stat < sizeof(buf) ) + return InsertGrowBuffer(gb,buf,stat); + + + //--- buffer too small, use dynamic memory + + noPRINT("PrintArgGrowBuffer() -> MALLOC(%u)\n",stat+1); + + char *dest = MALLOC(stat+1); + stat = vsnprintf(dest,stat+1,format,arg); + + const uint res = InsertGrowBuffer(gb,dest,stat); + FREE(dest); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + ccp format, // format string for vfprintf() + ... // arguments for 'vsnprintf(format,...)' +) +{ + DASSERT(gb); + DASSERT(format); + + va_list arg; + va_start(arg,format); + enumError err = PrintArgGrowBuffer(gb,format,arg); + va_end(arg); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ConvertToCrLfGrowBuffer +( + // returns the number of inserted CR bytes + + GrowBuffer_t *gb, // valid grow-buffer object + uint begin // index to begin with conversion +) +{ + DASSERT(gb); + uint count = 0; + + if ( begin < gb->used ) + { + //--- count LF without CR + + u8 *ptr = gb->ptr + begin; + u8 *end = gb->ptr + gb->used; + for ( ; ptr < end; ptr++ ) + { + if ( *ptr == '\r' ) + ptr++; + else if ( *ptr == '\n' ) + count++; + } + + if ( count > 0 ) + { + PrepareGrowBuffer(gb,count,true); + u8 *src = gb->ptr + gb->used; + u8 *dest = src + count; + u8 *beg = gb->ptr + begin; + while ( src > beg ) + { + const char ch = *--src; + *--dest = ch; + if ( ch == '\n' && src[-1] != '\r' ) + *--dest = '\r'; + } + DASSERT( src == dest ); + gb->used += count; + gb->ptr[gb->used] = 0; + if ( gb->max_used < gb->used ) + gb->max_used = gb->used; + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ClearGrowBuffer ( GrowBuffer_t *gb ) +{ + DASSERT(gb); + gb->ptr = gb->buf; + if (gb->ptr) + *gb->ptr = 0; + gb->used = 0; + return gb->size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PurgeGrowBuffer ( GrowBuffer_t *gb ) +{ + if (!gb->used) + return ClearGrowBuffer(gb); + + if ( gb->ptr > gb->buf ) + { + memmove(gb->buf,gb->ptr,gb->used); + gb->ptr = gb->buf; + } + return gb->size - gb->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropGrowBuffer +( + // returns the number of dropped bytes + + GrowBuffer_t *gb, // valid grow-buffer object + uint size // number of bytes to drop +) +{ + #if HAVE_DASSERT + DASSERT(gb); + if (gb->buf) + { + DASSERT_MSG( gb->used < gb->size, "%d/%d\n",gb->used,gb->size); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + } + else + { + DASSERT(!gb->ptr); + DASSERT(!gb->size); + DASSERT(!gb->used); + } + #endif + + if (!gb->buf) + return 0; + + if ( size >= gb->used ) + { + size = gb->used; + gb->used = 0; + gb->ptr = gb->buf; + *gb->ptr = 0; + return size; + } + + gb->ptr += size; + gb->used -= size; + return size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void LogGrowBuffer +( + FILE *f, // output file + int indent, // indention + const GrowBuffer_t *gb, // valid grow-buffer + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(format,...)' +) +{ + DASSERT(f); + DASSERT(gb); + indent = NormalizeIndent(indent); + + fprintf(f,"%*sTB: size=%s/%s/%s idx=%s grow=%s/%s disabled=%d", + indent,"", + PrintSize1024(0,0,gb->used,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintSize1024(0,0,gb->max_used,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintSize1024(0,0,gb->size,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintNumberU6(0,0,gb->ptr-gb->buf,true), + PrintSize1024(0,0,gb->grow_size,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintSize1024(0,0,gb->max_size,DC_SFORM_TINY|DC_SFORM_ALIGN), + gb->disabled ); + + if (format) + { + fputs(" : ",f); + va_list arg; + va_start(arg,format); + vfprintf(f,format,arg); + va_end(arg); + } + + fputc('\n',f); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifndef __APPLE__ + +FILE * OpenFileGrowBuffer +( + // returns a FILE opened by fmemopen() => call CloseFileGrowBuffer() + + GrowBuffer_t *gb, // valid grow-buffer object + uint size // needed size +) +{ + DASSERT(gb); + uint space = PurgeGrowBuffer(gb); + if ( space < size ) + space = PrepareGrowBuffer(gb,size,true); + return fmemopen( gb->ptr + gb->used, space, "w" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int CloseFileGrowBuffer +( + // returns the number of wirtten bytes, or -1 on error + GrowBuffer_t *gb, // valid grow-buffer object + FILE *f // FILE returned by OpenFileGrowBuffer() +) +{ + DASSERT(gb); + if (!f) + return -1; + + const int stat = ftell(f); + if ( stat >= 0 && gb->ptr + gb->used + stat <= gb->buf + gb->size ) + gb->used += stat; + + fclose(f); + return stat; +} + +#endif // !__APPLE__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GROW_BUFFER_CHUNK_SIZE 3*1024 // multiple of 3 because of BASE64 (3->4) + +void SaveCurrentStateGrowBuffer +( + FILE *f, // output file + ccp name_prefix, // NULL or prefix of name + uint tab_pos, // tab pos of '=' + const GrowBuffer_t *gb // valid grow-buffer +) +{ + DASSERT(f); + DASSERT(gb); + + if (!name_prefix) + name_prefix = EmptyString; + + const int base = 8*tab_pos + 7 - strlen(name_prefix); + + fprintf(f, + "%s" "disabled%.*s= %u\n" + "%s" "used%.*s= %u\n" + "%s" "size%.*s= %u\n" + "%s" "grow-size%.*s= %u\n" + "%s" "max-size%.*s= %u\n" + ,name_prefix, (base-8)/8, Tabs20, gb->disabled + ,name_prefix, (base-4)/8, Tabs20, gb->used + ,name_prefix, (base-4)/8, Tabs20, gb->size + ,name_prefix, (base-9)/8, Tabs20, gb->grow_size + ,name_prefix, (base-8)/8, Tabs20, gb->max_size + ); + + char buf[4*GROW_BUFFER_CHUNK_SIZE/3+10], name[20]; + uint start,i; + + for ( start = i = 0; start < gb->used; i++ ) + { + uint size = gb->used - start; + if ( size > GROW_BUFFER_CHUNK_SIZE ) + size = GROW_BUFFER_CHUNK_SIZE; + + const uint len = snprintf(name,sizeof(name),"data-%u",i); + EncodeBase64(buf,sizeof(buf),(ccp)gb->ptr+start,size,0,true,0,0); + fprintf(f, "%s%s%.*s= %s\n", + name_prefix, name, (base-len)/8, Tabs20, buf ); + + start += size; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateGrowBuffer +( + GrowBuffer_t *gb, // valid grow-buffer + ccp name_prefix, // NULL or prefix of name + RestoreState_t *rs // info data, can be modified (cleaned after call) +) +{ + DASSERT(gb); + DASSERT(rs); + + ResetGrowBuffer(gb); + + if (!name_prefix) + name_prefix = EmptyString; + + char name[200]; // name buf + snprintf(name,sizeof(name),"%sused",name_prefix); + int size = GetParamFieldUINT(rs,name,0); + snprintf(name,sizeof(name),"%sdisabled",name_prefix); + gb->disabled = GetParamFieldINT(rs,name,gb->disabled) > 0; + + if ( size > 0 ) + { + char buf[2*GROW_BUFFER_CHUNK_SIZE]; + + PrepareGrowBuffer(gb,size,true); + uint index = 0; + while ( size > 0 ) + { + snprintf(name,sizeof(name),"%sdata-%u",name_prefix,index++); + int res = GetParamFieldBUF(buf,sizeof(buf),rs,name,ENCODE_BASE64URL,0); + if ( res < 0 ) + break; + + InsertGrowBuffer(gb,buf,res); + size -= res; + } + + #ifdef TEST0 + if ( gb->used > 10000 ) + { + fprintf(stderr,"--- ResetGrowBuffer() size = %d\n",gb->used); + fwrite(gb->ptr,1,gb->used,stderr); + fprintf(stderr,"--- ResetGrowBuffer() size = %d\n",gb->used); + } + #endif + } + + + //--- known as 'unused' + + if ( rs->log_mode & RSL_UNUSED_NAMES ) + { + static ccp name_tab[] = + { + "size", + "grow-size", + "max-size", + 0 + }; + + ccp *np; + for ( np = name_tab; *np; np++ ) + { + StringCat2S(name,sizeof(name),name_prefix,*np); + GetParamField(rs,name); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int opt_new = 0; + +/////////////////////////////////////////////////////////////////////////////// + +uint CreateUniqueId ( int range ) +{ + static uint unique_id = 1; + const uint ret = unique_id; + if ( range > 0 ) + unique_id += range; + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sha1Hex2Bin ( sha1_hash_t bin, ccp src, ccp end ) +{ + DASSERT(bin); + DASSERT(src); + + uint i; + for ( i = 0; i < 20; i++ ) + bin[i] = ScanDigits(&src,end,16,2,0); +} + +//----------------------------------------------------------------------------- + +void Sha1Bin2Hex ( sha1_hex_t hex, cvp bin ) +{ + DASSERT(hex); + DASSERT(bin); + + snprintf(hex,sizeof(sha1_hex_t),"%08x%08x%08x%08x%08x", + be32(bin), be32(bin+4), be32(bin+8), be32(bin+12), be32(bin+16) ); +} + +//----------------------------------------------------------------------------- + +ccp GetSha1Hex ( cvp bin ) +{ + DASSERT(bin); + + char *hex = GetCircBuf(sizeof(sha1_hex_t)); + Sha1Bin2Hex(hex,bin); + return hex; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-basics.h b/project/dclib/dclib-basics.h new file mode 100644 index 0000000..7c6e7a2 --- /dev/null +++ b/project/dclib/dclib-basics.h @@ -0,0 +1,5252 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_BASICS_H +#define DCLIB_BASICS_H 1 + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN_DCLIB + #include +#endif + +#include "dclib-types.h" +#include "dclib-debug.h" +#include "dclib-color.h" +#include "dclib-numeric.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define SIZEOFMEMBER(type,a) sizeof(((type*)0)->a) + +#define SIZEOFRANGE(type,a,b) \ + ( offsetof(type,b) + sizeof(((type*)0)->b) - offsetof(type,a) ) + +#define PTR_DISTANCE(a,b) ( (u8*)(a) - (u8*)(b) ) +#define PTR_DISTANCE_INT(a,b) ( (int)( (u8*)(a) - (u8*)(b) )) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// bits /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const uchar TableBitCount[0x100]; +extern const char TableLowest0Bit[0x100]; +extern const char TableLowest1Bit[0x100]; +extern const char TableHighest0Bit[0x100]; +extern const char TableHighest1Bit[0x100]; + +uint Count1Bits ( cvp addr, uint size ); +static inline uint Count0Bits ( cvp addr, uint size ) + { return CHAR_BIT * size - Count1Bits(addr,size); } + +#define Count1BitsObj(obj) Count1Bits(&(obj),sizeof(obj)) +#define Count0BitsObj(obj) Count1Bits(&(obj),sizeof(obj)) + +static inline uint Count1Bits8 ( u8 data ) +{ + return TableBitCount[data]; +} + +static inline uint Count1Bits16 ( u16 data ) +{ + const u8 * d = (u8*)&data; + return TableBitCount[d[0]] + + TableBitCount[d[1]]; +} + +static inline uint Count1Bits32 ( u32 data ) +{ + // __builtin_popcount(unsigned int) is slower than table based summary + + const u8 * d = (u8*)&data; + return TableBitCount[d[0]] + + TableBitCount[d[1]] + + TableBitCount[d[2]] + + TableBitCount[d[3]]; +} + +static inline uint Count1Bits64 ( u64 data ) +{ + #if __GNUC__ > 3 + return __builtin_popcountll(data); + #else + const u8 * d = (u8*)&data; + return TableBitCount[d[0]] + + TableBitCount[d[1]] + + TableBitCount[d[2]] + + TableBitCount[d[3]] + + TableBitCount[d[4]] + + TableBitCount[d[5]] + + TableBitCount[d[6]] + + TableBitCount[d[7]]; + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindLowest0BitBE ( cvp addr, uint size ); +int FindLowest1BitBE ( cvp addr, uint size ); +int FindHighest0BitBE ( cvp addr, uint size ); +int FindHighest1BitBE ( cvp addr, uint size ); + +int FindLowest0BitLE ( cvp addr, uint size ); +int FindLowest1BitLE ( cvp addr, uint size ); +int FindHighest0BitLE ( cvp addr, uint size ); +int FindHighest1BitLE ( cvp addr, uint size ); + +#if __BYTE_ORDER == 1234 + #define FindLowest0Bit FindLowest0BitLE + #define FindLowest1Bit FindLowest1BitLE + #define FindHighest0Bit FindHighest0BitLE + #define FindHighest1Bit FindHighest1BitLE +#else + #define FindLowest0Bit FindLowest0BitBE + #define FindLowest1Bit FindLowest1BitBE + #define FindHighest0Bit FindHighest0BitBE + #define FindHighest1Bit FindHighest1BitBE +#endif + +u32 GetAlign32 ( u32 data ); +u64 GetAlign64 ( u64 data ); + +/////////////////////////////////////////////////////////////////////////////// + +static inline void SetBit ( void *addr, uint bit_num ) + { ((u8*)addr)[ bit_num >> 3 ] |= 1 << ( bit_num & 7 ); } + +static inline void ClearBit ( void *addr, uint bit_num ) + { ((u8*)addr)[ bit_num >> 3 ] &= ~(1 << ( bit_num & 7 )); } + +static inline void InvertBit ( void *addr, uint bit_num ) + { ((u8*)addr)[ bit_num >> 3 ] ^= 1 << ( bit_num & 7 ); } + +static inline bool TestBit ( const void *addr, uint bit_num ) + { return ( ((u8*)addr)[ bit_num >> 3 ] & 1 << ( bit_num & 7 ) ) != 0; } + +// change a single bit and return the previous state +bool TestSetBit ( void *addr, uint bit_num ); +bool TestClearBit ( void *addr, uint bit_num ); +bool TestInvertBit ( void *addr, uint bit_num ); + +// change bits inclusive 'beg' to exclusive 'end' if beg_bitnum> 32) ); + ((u32*)&result)[1] = htonl( (u32)data ); + return result; +} + +static inline u64 ntoh64 ( be64_t data ) +{ + return (u64)ntohl(((u32*)&data)[0]) << 32 | ntohl(((u32*)&data)[1]); +} + +/////////////////////////////////////////////////////////////////////////////// + +// convert a number in host format into big endian data +void write_be16 ( void * be_data_ptr, u16 data ); +void write_be24 ( void * be_data_ptr, u32 data ); +void write_be32 ( void * be_data_ptr, u32 data ); +void write_be40 ( void * be_data_ptr, u64 data ); +void write_be48 ( void * be_data_ptr, u64 data ); +void write_be56 ( void * be_data_ptr, u64 data ); +void write_be64 ( void * be_data_ptr, u64 data ); +void write_bef4 ( void * be_data_ptr, float data ); +void write_bef8 ( void * be_data_ptr, double data ); + +// convert a number in host format into little endian data +void write_le16 ( void * le_data_ptr, u16 data ); +void write_le24 ( void * le_data_ptr, u32 data ); +void write_le32 ( void * le_data_ptr, u32 data ); +void write_le40 ( void * le_data_ptr, u64 data ); +void write_le48 ( void * le_data_ptr, u64 data ); +void write_le56 ( void * le_data_ptr, u64 data ); +void write_le64 ( void * le_data_ptr, u64 data ); +void write_lef4 ( void * le_data_ptr, float data ); +void write_lef8 ( void * le_data_ptr, double data ); + +// convert lists +void be16n ( u16 * dest, const u16 * src, int n ); +void be32n ( u32 * dest, const u32 * src, int n ); +void bef4n ( float32 * dest, const float32 * src, int n ); +void le16n ( u16 * dest, const u16 * src, int n ); +void lef4n ( float32 * dest, const float32 * src, int n ); + +void write_be16n ( u16 * dest, const u16 * src, int n ); +void write_be32n ( u32 * dest, const u32 * src, int n ); +void write_bef4n ( float32 * dest, const float32 * src, int n ); +void write_le16n ( u16 * dest, const u16 * src, int n ); +void write_lef4n ( float32 * dest, const float32 * src, int n ); + +//----------------------------------------------------------------------------- +// [[endian_func_t]] + +typedef struct endian_func_t +{ + //--- info section + + u8 bom[2]; // btye order mark: BE=0xfe,0xff, LE=0xff,0xfe + bool is_be; // true: is big endian + bool is_le; // true: is little endian + dcEndian_t endian; // DC_BIG_ENDIAN or DC_LITTLE_ENDIAN + + //--- read functions + + u16 (*rd16) ( const void * data_ptr ); + u32 (*rd24) ( const void * data_ptr ); + u32 (*rd32) ( const void * data_ptr ); + u64 (*rd40) ( const void * data_ptr ); + u64 (*rd48) ( const void * data_ptr ); + u64 (*rd56) ( const void * data_ptr ); + u64 (*rd64) ( const void * data_ptr ); + float (*rdf4) ( const void * data_ptr ); + double (*rdf8) ( const void * data_ptr ); + + //--- write functions + + void (*wr16) ( void * data_ptr, u16 data ); + void (*wr24) ( void * data_ptr, u32 data ); + void (*wr32) ( void * data_ptr, u32 data ); + void (*wr40) ( void * data_ptr, u64 data ); + void (*wr48) ( void * data_ptr, u64 data ); + void (*wr56) ( void * data_ptr, u64 data ); + void (*wr64) ( void * data_ptr, u64 data ); + void (*wrf4) ( void * data_ptr, float data ); + void (*wrf8) ( void * data_ptr, double data ); + +} endian_func_t; + +extern const endian_func_t be_func; +extern const endian_func_t le_func; + +const endian_func_t * GetEndianFunc ( const void * byte_order_mark ); +uint GetTextBOMLen ( const void * data, uint data_size ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mem_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mem_t]] + +typedef struct mem_t +{ + ccp ptr; // NULL or a pointer to the data + int len; // length in bytes of the data +} +mem_t; + +extern mem_t EmptyMem; // mem_t(EmptyString,0) +extern mem_t NullMem; // mem_t(0,0) + +//----------------------------------------------------------------------------- + +static inline mem_t MemByChar ( char ch ) +{ + mem_t mem = { &ch, 1 }; + return mem; +} + +static inline mem_t MemByString0 ( ccp str ) +{ + if (!str) + return NullMem; + + mem_t mem = { str, strlen(str) }; + return mem; +} + +static inline mem_t MemByString ( ccp str ) +{ + mem_t mem = { str, strlen(str) }; + return mem; +} + +static inline mem_t MemByStringS ( ccp str, int len ) +{ + mem_t mem = { str, len < 0 ? strlen(str) : len }; + return mem; +} + +static inline mem_t MemByStringE ( ccp str, ccp end ) +{ + mem_t mem = { str, end ? end-str : strlen(str) }; + return mem; +} + +static inline mem_t MemByS ( ccp str, int len ) +{ + mem_t mem = { str, len }; + return mem; +} + +static inline mem_t MemByE ( ccp str, ccp end ) +{ + mem_t mem = { str, end-str }; + return mem; +} + +//----------------------------------------------------------------------------- + +// Index helpers +// For the 'End' variants, index 0 is end_of_source + +int CheckIndex1 ( int max, int index ); +int CheckIndex1End ( int max, int index ); +int CheckIndex2 ( int max, int * p_begin, int * p_end ); +int CheckIndex2End ( int max, int * p_begin, int * p_end ); + +//----------------------------------------------------------------------------- + +// All functions are robust. +// A negative index is relative to the end of source. +// For RightMem() and ExtractEndMem(), index 0 means: end of source. +// count<0 means: copy left from 'begin' (only MidMem()) or skip bytes. + +mem_t MidMem ( const mem_t src, int begin, int count ); + +static inline mem_t LeftMem ( const mem_t src, int count ) +{ + mem_t res; + res.ptr = src.ptr; + res.len = CheckIndex1(src.len,count); + return res; +} + +static inline mem_t RightMem ( const mem_t src, int count ) +{ + mem_t res; + res.len = src.len - CheckIndex1End(src.len,-count); + res.ptr = src.ptr + src.len - res.len; + return res; +} + +static inline mem_t ExtractMem ( const mem_t src, int begin, int end ) +{ + mem_t res; + res.len = CheckIndex2(src.len,&begin,&end); + res.ptr = src.ptr + begin; + return res; +} + +static inline mem_t ExtractEndMem ( const mem_t src, int begin, int end ) +{ + mem_t res; + res.len = CheckIndex2End(src.len,&begin,&end); + res.ptr = src.ptr + begin; + return res; +} + +mem_t BeforeMem ( const mem_t src, ccp ref ); +mem_t BehindMem ( const mem_t src, ccp ref ); + +//----------------------------------------------------------------------------- + +// Alloc dest buffer and terminate with 0. +// If m*.len < 0, then use strlen(). + +mem_t MemCat2A ( const mem_t m1, const mem_t m2 ); +mem_t MemCat3A ( const mem_t m1, const mem_t m2, const mem_t m3 ); + +//----------------------------------------------------------------------------- + +int CmpMem ( const mem_t s1, const mem_t s2 ); +int StrCmpMem ( const mem_t mem, ccp str ); +bool LeftStrCmpMemEQ ( const mem_t mem, ccp str ); + +// free a string if str is not NULL|EmptyString|MinusString +void FreeString ( ccp str ); + +static inline void FreeMem ( mem_t *mem ) +{ + DASSERT(mem); + FreeString(mem->ptr); + mem->ptr = 0; + mem->len = 0; +} + +static inline void AssignDupMem ( mem_t *dest, mem_t src ) +{ + DASSERT(dest); + FreeString(dest->ptr); + dest->ptr = MEMDUP(src.ptr,src.len); + dest->len = src.len; +} + +static inline mem_t DupMem ( const mem_t src ) +{ + mem_t res = { MEMDUP(src.ptr,src.len), src.len }; + return res; +} + +static inline char * DupStrMem ( const mem_t src ) +{ + return MEMDUP(src.ptr,src.len); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mem_list_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mem_list_t]] + +typedef struct mem_list_t +{ + mem_t *list; // List of mem_t objects. + // Elements are always terminated with NULL. + // Content is stored in 'buf' or points to EmptyString. + + uint used; // Number of active 'list' elements + uint size; // Number of alloced 'list' elements + + char *buf; // Alloced temp buffer + uint buf_used; // Usage of 'buf' + uint buf_size; // Size of 'buf' +} +mem_list_t; + +//----------------------------------------------------------------------------- + +void InitializeMemList ( mem_list_t *ml ); +void ResetMemList ( mem_list_t *ml ); +void MoveMemList ( mem_list_t *dest, mem_list_t *src ); + +void GrowBufMemList ( mem_list_t *ml, uint need_size ); +void PrepareMemList ( mem_list_t *ml, uint n_elem, uint buf_size ); + +//----------------------------------------------------------------------------- + +void InsertMemListN +( + mem_list_t *ml, // valid destination mem_list + int pos, // insert position => CheckIndex1() + const mem_t *src, // source list + uint src_len, // number of elements in source list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +); + +//----------------------------------------------------------------------------- + +static inline void InsertMemList2 +( + mem_list_t *ml, // valid destination mem_list + int pos, // insert position => CheckIndex1() + const mem_list_t *src, // source mem_list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(src); + InsertMemListN(ml,pos,src->list,src->used,src_ignore); +} + +//----------------------------------------------------------------------------- + +static inline void AppendMemListN +( + mem_list_t *ml, // valid destination mem_list + const mem_t *src, // source list + uint src_len, // number of elements in source list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(ml); + InsertMemListN(ml,ml->used,src,src_len,src_ignore); +} + +//----------------------------------------------------------------------------- + +static inline void AppendMemList2 +( + mem_list_t *ml, // valid destination mem_list + const mem_list_t *src, // source mem_list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(ml); + DASSERT(src); + InsertMemListN(ml,ml->used,src->list,src->used,src_ignore); +} + +//----------------------------------------------------------------------------- + +static inline void AssignMemListN +( + mem_list_t *ml, // valid destination mem_list + const mem_t *src, // source list + uint src_len, // number of elements in source list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(ml); + ml->used = 0; + InsertMemListN(ml,0,src,src_len,src_ignore); +} + +//----------------------------------------------------------------------------- + +static inline void AssignMemList2 +( + mem_list_t *ml, // valid destination mem_list + const mem_list_t *src, // source mem_list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(ml); + DASSERT(src); + ml->used = 0; + InsertMemListN(ml,0,src->list,src->used,src_ignore); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CatMemListN +( + mem_list_t *dest, // valid destination mem_list + const mem_list_t **src_list, // list with mem lists, element may be NULL + uint n_src_list, // number of elements in 'src' + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +); + +//----------------------------------------------------------------------------- + +static inline void CatMemList2 +( + mem_list_t *dest, // valid destination mem_list + const mem_list_t *src1, // source mem_list + const mem_list_t *src2, // source mem_list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + const mem_list_t *ml[2] = { src1, src2 }; + CatMemListN(dest,ml,2,src_ignore); +} + +//----------------------------------------------------------------------------- + +static inline void CatMemList3 +( + mem_list_t *dest, // valid destination mem_list + const mem_list_t *src1, // source mem_list + const mem_list_t *src2, // source mem_list + const mem_list_t *src3, // source mem_list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + const mem_list_t *ml[3] = { src1, src2, src3 }; + CatMemListN(dest,ml,3,src_ignore); +} + +//----------------------------------------------------------------------------- + +// All functions are robust and modify the current list. +// A negative index is relative to the end of source. +// For RightMemList() and *EndMemList(), index 0 means: end of source. +// count<0 means: copy left from 'begin' (only MidMemList()) or skip bytes. +// All functions return the number of elements aufer operation. + +uint LeftMemList ( mem_list_t *ml, int count ); +uint RightMemList ( mem_list_t *ml, int count ); +uint MidMemList ( mem_list_t *ml, int begin, int count ); +uint ExtractMemList ( mem_list_t *ml, int begin, int end ); +uint ExtractEndMemList ( mem_list_t *ml, int begin, int end ); +uint RemoveMemList ( mem_list_t *ml, int begin, int end ); +uint RemoveEndMemList ( mem_list_t *ml, int begin, int end ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MemPool_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MemPoolChunk_t]] + +typedef struct MemPoolChunk_t +{ + struct MemPoolChunk_t *next; + char data[]; +} +MemPoolChunk_t; + +//----------------------------------------------------------------------------- +// [[MemPool_t]] + +typedef struct MemPool_t +{ + MemPoolChunk_t *chunk; // pointer to first (=current) chunk + uint space; // space left in current chunk + uint chunk_size; // wanted chunk size +} +MemPool_t; + +//----------------------------------------------------------------------------- + +static inline void InitializeMemPool ( MemPool_t *mp, uint chunk_size ) + { DASSERT(mp); memset(mp,0,sizeof(*mp)); mp->chunk_size = chunk_size; } + +void ResetMemPool ( MemPool_t *mp ); +void * MallocMemPool ( MemPool_t *mp, uint size ); +void * CallocMemPool ( MemPool_t *mp, uint size ); +void * MallocMemPoolA ( MemPool_t *mp, uint size, uint align ); +void * CallocMemPoolA ( MemPool_t *mp, uint size, uint align ); +void * MemDupMemPool ( MemPool_t *mp, cvp source, uint size ); + +static inline void * StrDupMemPool ( MemPool_t *mp, ccp source ) + { DASSERT(mp); return source ? MemDupMemPool(mp,source,strlen(source)) : 0; } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CircBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[CircBuf_t]] + +typedef struct CircBuf_t +{ + char *buf; // NULL or alloced buffer + uint pos; // position of first valid byte + uint used; // used bytes in buffer + uint size; // size of 'buf' +} +CircBuf_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeCircBuf ( CircBuf_t *cb, uint size ); +void ResetCircBuf ( CircBuf_t *cb ); +uint WriteCircBuf ( CircBuf_t *cb, cvp data, uint size ); +uint PeakCircBuf ( CircBuf_t *cb, char *buf, uint buf_size ); +uint ReadCircBuf ( CircBuf_t *cb, char *buf, uint buf_size ); +uint DropCircBuf ( CircBuf_t *cb, uint size ); +void ClearCircBuf ( CircBuf_t *cb ); +uint PurgeCircBuf ( CircBuf_t *cb ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CopyMode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum CopyMode_t +{ + CPM_COPY, // make a copy of the data, alloced + CPM_MOVE, // move the alloced data + CPM_LINK, // link the data + +} CopyMode_t; + +/////////////////////////////////////////////////////////////////////////////// + +void * CopyData +( + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode, // copy mode + bool *res_alloced // not NULL: + // store true, if data must be freed + // otherwise store false +); + +/////////////////////////////////////////////////////////////////////////////// + +void FreeData +( + const void *data, // data to free + CopyMode_t mode // copy mode +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// split strings /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByCharMem +( + // 'mem_list' will be padded with {EmptyString,0} + + mem_t *mem_list, // array of mem_t + uint n_mem, // number of elements of 'mem_list' + mem_t source, // source string + char sep // separator character +); + +//----------------------------------------------------------------------------- + +uint SplitByTextMem +( + // 'mem_list' will be padded with {EmptyString,0} + + mem_t *mem_list, // array of mem_t + uint n_mem, // number of elements of 'mem_list' + mem_t source, // source string + mem_t sep // separator text +); + +//----------------------------------------------------------------------------- + +uint SplitByCharMemList +( + mem_list_t *ml, // valid destination mem_list + bool init_ml, // true: initialize 'ml' first + mem_t source, // source string + char sep, // separator character + int max_fields // >0: max number of result fields +); + +//----------------------------------------------------------------------------- + +uint SplitByTextMemList +( + mem_list_t *ml, // valid destination mem_list + bool init_ml, // true: initialize 'ml' first + mem_t source, // source string + mem_t sep, // separator text + int max_fields // >0: max number of result fields +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alloc info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern bool alloc_info_calced; +extern uint alloc_info_overhead; +extern uint alloc_info_block_size; +extern uint alloc_info_add1; +extern uint alloc_info_add2; +extern uint alloc_info_mask; + +/////////////////////////////////////////////////////////////////////////////// + +uint SetupAllocInfo(void); + +static inline uint GetGoodAllocSize ( uint need ) +{ + return ( need + alloc_info_add1 & alloc_info_mask ) + + alloc_info_add2; +} + +static inline uint GetGoodAllocSize2 ( uint n_elem, uint itemsize ) +{ + return (( n_elem*itemsize + alloc_info_add1 & alloc_info_mask ) + + alloc_info_add2 ) / itemsize; +} + +static inline uint GetGoodAllocSizeA ( uint need, uint align ) +{ + need = ( need + align - 1) / align * align; + return (( need + alloc_info_add1 & alloc_info_mask ) + + alloc_info_add2 ) / align * align; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// some basic string functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const char EmptyQuote[]; // »""« Ignored by FreeString() +extern const char EmptyString[4]; // »« + 4*NULL, Ignored by FreeString() +extern const char MinusString[4]; // »-« + 3*NULL, Ignored by FreeString() + +extern const char LoDigits[0x41]; // 0-9 a-z A-Z .+ NULL +extern const char HiDigits[0x41]; // 0-9 A-Z a-z .+ NULL + +extern const char Tabs20[21]; // 20 * TAB + NULL +extern const char LF20[21]; // 20 * LF + NULL +extern const char Space200[201]; // 200 * ' ' + NULL +extern const char Minus300[301]; // 300 * '-' + NULL +extern const char Underline200[201]; // 200 * '_' + NULL +extern const char Hash200[201]; // 200 * '#' + NULL +extern const char Slash200[201]; // 200 * '/' + NULL + +// UTF-8 => 3 bytes per char +extern const char ThinLine300_3[901]; // 300 * '─' (U+2500) + NULL + +//----------------------------------------------------------------------------- + +void SetupMultiProcessing(void); +void PrintSettingsDCLIB ( FILE *f, int indent ); +void SetupProgname ( int argc, char ** argv, + ccp tool_name, ccp tool_vers, ccp tool_title ); + +// return NULL or 'progpath', calc GetProgramPath() once if needed +ccp ProgramPath(void); + +// return NULL or 'progdir', calc ProgramPath() once if needed +ccp ProgramDirectory(void); + +#ifdef __CYGWIN__ + ccp ProgramPathNoExt(void); +#else + static inline ccp ProgramPathNoExt(void) { return ProgramPath(); } +#endif + +int GetProgramPath +( + // returns strlen(buf) on success, -1 on failure + + char *buf, // buffer for the result; empty string on failure. + uint buf_size, // size of buffer + bool check_proc, // true: read files of /proc/... to find path + ccp argv0 // not NULL: analyse this argument (argv[0]) +); + +//----- + +// StringCopy(), StringCopyE(), StringCat*() +// RESULT: end of copied string pointing to a NULL character. +// 'src*' may be a NULL pointer. + +char * StringCopyS ( char * buf, size_t bufsize, ccp src ); +char * StringCopySM ( char * buf, size_t bufsize, ccp src, size_t max_copy ); +char * StringCat2S ( char * buf, size_t bufsize, ccp src1, ccp src2 ); +char * StringCat3S ( char * buf, size_t bufsize, ccp src1, ccp src2, ccp src3 ); + +char * StringCopyE ( char * buf, ccp buf_end, ccp src ); +char * StringCopyEM ( char * buf, ccp buf_end, ccp src, size_t max_copy ); +char * StringCat2E ( char * buf, ccp buf_end, ccp src1, ccp src2 ); +char * StringCat3E ( char * buf, ccp buf_end, ccp src1, ccp src2, ccp src3 ); + +// alloc space and return +char * StringCat2A ( ccp src1, ccp src2 ); +char * StringCat3A ( ccp src1, ccp src2, ccp src3 ); + +static inline char * StringCopySMem ( char * buf, size_t bufsize, mem_t mem ) + { return StringCopySM(buf,bufsize,mem.ptr,mem.len); } +static inline char * StringCopyEMem ( char * buf, ccp buf_end, mem_t mem ) + { return StringCopyEM(buf,buf_end,mem.ptr,mem.len); } + +char * StringLowerS ( char * buf, size_t bufsize, ccp src ); +char * StringLowerE ( char * buf, ccp buf_end, ccp src ); +char * StringUpperS ( char * buf, size_t bufsize, ccp src ); +char * StringUpperE ( char * buf, ccp buf_end, ccp src ); + +// special handling for unsigned decimals +int StrNumCmp ( ccp a, ccp b ); + +// count the number of equal bytes and return a value of 0 to SIZE +uint CountEqual ( cvp m1, cvp m2, uint size ); + +// Concatenate path + path, return pointer to buf +// Return pointer to buf or path1|path2|buf or alloced string +char *PathCatBufPP ( char *buf, size_t bufsize, ccp path1, ccp path2 ); +char *PathCatBufPPE ( char *buf, size_t bufsize, ccp path1, ccp path2, ccp ext ); +ccp PathCatPP ( char *buf, size_t bufsize, ccp path1, ccp path2 ); +//ccp PathCatPPE ( char *buf, size_t bufsize, ccp path1, ccp path2, ccp ext ); +ccp PathAllocPP ( ccp path1, ccp path2 ); +ccp PathAllocPPE ( ccp path1, ccp path2, ccp ext ); + +// inline wrapper +static inline ccp PathCatPPE ( char *buf, size_t bufsize, ccp path1, ccp path2, ccp ext ) + { return PathCatBufPPE(buf,bufsize,path1,path2,ext); } + +// Same as PathCatPP*(), but use 'base' as prefix for relative pathes. +// If 'base' is NULL (but not empty), use getcwd() instead. +// PathCatBufBP() and PathCatBP() are special: path can be part of buf +char *PathCatBufBP ( char *buf, size_t bufsize, ccp base, ccp path ); +char *PathCatBufBPP ( char *buf, size_t bufsize, ccp base, ccp path1, ccp path2 ); +char *PathCatBufBPPE ( char *buf, size_t bufsize, ccp base, ccp path1, ccp path2, ccp ext ); +ccp PathCatBP ( char *buf, size_t bufsize, ccp base, ccp path ); +ccp PathCatBPP ( char *buf, size_t bufsize, ccp base, ccp path1, ccp path2 ); +ccp PathCatBPPE ( char *buf, size_t bufsize, ccp base, ccp path1, ccp path2, ccp ext ); +ccp PathAllocBP ( ccp base, ccp path ); +ccp PathAllocBPP ( ccp base, ccp path1, ccp path2 ); +ccp PathAllocBPPE ( ccp base, ccp path1, ccp path2, ccp ext ); + +char * PathCombine ( char *temp_buf, uint buf_size, ccp path, ccp base_path ); +char * PathCombineFast ( char *dest_buf, uint buf_size, ccp path, ccp base_path ); + +// Remove last extension and add a new one. +// If ext==NULL or emtpy: remove only +// 'ext' may start with '.'. If not, a additional '.' is included +// If 'path' is NULL or path==buf: Inplace job using 'buf' as source. +char * NewFileExtE ( char * buf, ccp buf_end, ccp path, ccp ext ); +char * NewFileExtS ( char * buf, size_t bufsize, ccp path, ccp ext ); + +// eliminate leading './' before comparing +int StrPathCmp ( ccp path1, ccp path2 ); + +int PathCmp +( + ccp path1, // NULL or first path + ccp path2, // NULL or second path + uint mode // bit field: + // 1: skip (multiple) leading './' + // 2: compare case insensitive + // 4: sort with respect for unsigned decimal numbers +); + +int ReplaceToBuf +( + // returns -1 on error (buf to small) or the length of the resulting string. + // In the later case, a 0 byte is appended, but not counting for the length. + + char *buf, // pointer to buf + uint buf_size, // size of 'buf' + + ccp src1, // NULL or source, maybe part of 'buf' + int src1_len, // length of 'src1', if -1: determine by strlen() + int pos, // robust replace position, <0: relative to end + int rm_len, // robust num of chars to remove, <0: rm before 'pos' + + ccp src2, // NULL or source to insert, must not be part of 'buf' + int src2_len // lengh of 'src2', if -1: determine by strlen() +); + +int NormalizeIndent ( int indent ); + +//----------------------------------------------------- + +// skips all character 1..SPACE; 'src' can be NULL +char * SkipControls ( ccp src ); + +// skips all character 0..SPACE; if end==NULL: Use SkipControls(); 'src' can be NULL +char * SkipControlsE ( ccp src, ccp end ); + +// same as above, but skip also 'ch1'; 'src' can be NULL +char * SkipControls1 ( ccp src, char ch1 ); +char * SkipControlsE1 ( ccp src, ccp end, char ch1 ); + +// same as above, but skip also 'ch1' and 'ch2'; 'src' can be NULL +char * SkipControls2 ( ccp src, char ch1, char ch2 ); +char * SkipControlsE2 ( ccp src, ccp end, char ch1, char ch2 ); + +//----------------------------------------------------- + +// return the number of chars until first EOL ( \0 | \r | \n ); +// pointer are NULL save +uint LineLen ( ccp ptr ); +uint LineLenE ( ccp ptr, ccp end ); + +//----------------------------------------------------- + +uint TrimBlanks +( + char *str, // pointer to string, modified + ccp end // NULL or end of string +); + +char * ScanName +( + // return a pointer to the first unused character + char *buf, // destination buffer, terminated with NULL + uint buf_size, // size of 'buf', must be >0 + ccp str, // source string + ccp end, // NULL or end of 'str' + bool allow_signs // true: allow '-' and '+' +); + +//----------------------------------------------------- +// Format of version number: AABBCCDD = A.BB | A.BB.CC +// If D != 0x00 && D != 0xff => append: 'beta' D +//----------------------------------------------------- + +char * PrintVersion +( + char * buf, // result buffer + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 version // version number to print +); + +char * PrintID +( + const void * id, // ID to convert in printable format + size_t id_len, // len of 'id' + void * buf // Pointer to buffer, size >= id_len + 1 + // If NULL, a local circulary static buffer is used +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// circ buf /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CIRC_BUF_MAX_ALLOC 0x0400 // request limit +#define CIRC_BUF_SIZE 0x4000 // internal buffer size + +char * GetCircBuf +( + // never returns NULL, but always ALIGN(4) + + u32 buf_size // wanted buffer size, caller must add 1 for NULL-term + // if buf_size > CIRC_BUF_MAX_ALLOC: + // ==> ERROR0(ERR_OUT_OF_MEMORY) +); + +char * CopyCircBuf +( + // never returns NULL, but always ALIGN(4) + + cvp data, // source to copy + u32 data_size // see GetCircBuf() +); + +char * CopyCircBuf0 +( + // never returns NULL, but always ALIGN(4) + // an additional char is alloced and set to NULL + + cvp data, // source to copy + u32 data_size // see GetCircBuf() +); + +char * PrintCircBuf +( + ccp format, // format string for vsnprintf() + ... // arguments for 'format' +) +__attribute__ ((__format__(__printf__,1,2))); + +void ReleaseCircBuf +( + ccp end_buf, // pointer to end of previous alloced buffer + uint release_size // number of bytes to give back from end +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct IntMode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[IntMode_t]] + +typedef enum IntMode_t +{ + IMD_UNSET = 0, // default, always 0 + + //--- big endian modes + + IMD_BE0, // default big endian + IMD_BE1, + IMD_BE2, + IMD_BE3, + IMD_BE4, + IMD_BE5, + IMD_BE6, + IMD_BE7, + IMD_BE8, + + //--- little endian modes + + IMD_LE0, // default little endian + IMD_LE1, + IMD_LE2, + IMD_LE3, + IMD_LE4, + IMD_LE5, + IMD_LE6, + IMD_LE7, + IMD_LE8, + + //--- local endian modes + + #ifdef LITTLE_ENDIAN + + IMD_0 = IMD_LE0, + IMD_1 = IMD_LE1, + IMD_2 = IMD_LE2, + IMD_3 = IMD_LE3, + IMD_4 = IMD_LE4, + IMD_5 = IMD_LE5, + IMD_6 = IMD_LE6, + IMD_7 = IMD_LE7, + IMD_8 = IMD_LE8, + + #else + + IMD_0 = IMD_BE0, + IMD_1 = IMD_BE1, + IMD_2 = IMD_BE2, + IMD_3 = IMD_BE3, + IMD_4 = IMD_BE4, + IMD_5 = IMD_BE5, + IMD_6 = IMD_BE6, + IMD_7 = IMD_BE7, + IMD_8 = IMD_BE8, + + #endif +} +__attribute__ ((packed)) IntMode_t; + +//----------------------------------------------------------------------------- + +ccp GetIntModeName ( IntMode_t mode ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct FastBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[FastBuf_t]] + +typedef struct FastBuf_t +{ + char *buf; // pointer to buffer + char *ptr; // pointer to first unused char (always space for 0-term) + char *end; // pointer to end of buffer := buf+size-1 + + uint fast_buf_size; // size of 'fast_buf+space' + char fast_buf[4]; // first use buffer with minimal space + char space[]; // additional space +} +FastBuf_t; + +/////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------ +// +// 4 examples for initialization: +// +// FastBuf_t fb1; +// InitializeFastBufAlloc(&fb1,1000); +// printf("fb1: %s: %s\n", GetFastBufStatus(&fb1), fb1.buf ); +// +// FastBuf_t fb2; +// InitializeFastBuf(&fb2,sizeof(fb2)); +// printf("fb2: %s: %s\n", GetFastBufStatus(&fb2), fb2.buf ); +// +// struct { FastBuf_t b; char space[2000]; } fb3; +// InitializeFastBuf(&fb3,sizeof(fb3)); +// printf("fb3: %s: %s\n", GetFastBufStatus(&fb.b), fb3.b.buf ); +// +// char fb4x[2000]; +// FastBuf_t *fb4 = InitializeFastBuf(fb4x,sizeof(fb4x)); +// printf("fb4: %s: %s\n", GetFastBufStatus(fb4), fb4->buf ); +// +//------------------------------------------------------------------ + +FastBuf_t * InitializeFastBuf ( cvp mem, uint size ); +FastBuf_t * InitializeFastBufAlloc ( FastBuf_t *fb, uint size ); + +void ResetFastBuf ( FastBuf_t *fb ); +static inline void ClearFastBuf ( FastBuf_t *fb ) + { DASSERT(fb); fb->ptr = fb->buf; } + +ccp GetFastBufStatus ( const FastBuf_t *fb ); +int ReserveSpaceFastBuf ( FastBuf_t *fb, uint size ); +char * GetSpaceFastBuf ( FastBuf_t *fb, uint size ); + +uint InsertFastBuf ( FastBuf_t *fb, int index, cvp source, int size ); +char * AppendFastBuf ( FastBuf_t *fb, cvp source, int size ); +char * WriteFastBuf ( FastBuf_t *fb, uint offset, cvp source, int size ); +uint AssignFastBuf ( FastBuf_t *fb, cvp source, int size ); +uint DropFastBuf ( FastBuf_t *fb, int index, int size ); + +void CopyFastBuf ( FastBuf_t *dest, const FastBuf_t *src ); +void MoveFastBuf ( FastBuf_t *dest, FastBuf_t *src ); + +static inline void AppendMemFastBuf ( FastBuf_t *fb, const mem_t mem ) + { AppendFastBuf(fb,mem.ptr,mem.len); } + +static inline void AppendCharFastBuf ( FastBuf_t *fb, char ch ) +{ + DASSERT(fb); + GetSpaceFastBuf(fb,1)[0] = ch; +} + +void AppendUTF8CharFastBuf ( FastBuf_t *fb, u32 code ); +void AppendBE16FastBuf ( FastBuf_t *fb, u16 num ); +void AppendBE32FastBuf ( FastBuf_t *fb, u32 num ); +void AppendInt64FastBuf ( FastBuf_t *fb, u64 val, IntMode_t mode ); + +static inline void AppendU16FastBuf ( FastBuf_t *fb, u16 num ) + { AppendFastBuf(fb,&num,sizeof(num)); } +static inline void AppendU32FastBuf ( FastBuf_t *fb, u32 num ) + { AppendFastBuf(fb,&num,sizeof(num)); } +static inline void AppendU64FastBuf ( FastBuf_t *fb, u64 num ) + { AppendFastBuf(fb,&num,sizeof(num)); } + +static inline int GetFastBufLen ( const FastBuf_t *fb ) + { DASSERT(fb); return fb->ptr - fb->buf; } + +//----------------------------------------------------------------------------- +// The resulting strings of GetFastBufString() and GetFastBufMem0() are always +// terminated by an additional NULL byte. +// The resulting strings of GetFastBufMem() is not terminated by a NULL byte. +//----------------------------------------------------------------------------- + +static inline char * GetFastBufString ( const FastBuf_t *fb ) +{ + DASSERT(fb); + *fb->ptr = 0; + return fb->buf; +} + +static inline mem_t GetFastBufMem0 ( const FastBuf_t *fb ) +{ + DASSERT(fb); + *fb->ptr = 0; + mem_t mem = { fb->buf, fb->ptr - fb->buf }; + return mem; +} + +static inline mem_t GetFastBufMem ( const FastBuf_t *fb ) +{ + DASSERT(fb); + mem_t mem = { fb->buf, fb->ptr - fb->buf }; + return mem; +} + +//----------------------------------------------------------------------------- +// The allocated result is always terminated by an additional NULL byte. +// FastBuf_t itself is reset, similar to ResetFastBuf() + +char * MoveFromFastBufString ( FastBuf_t *fb ); +mem_t MoveFromFastBufMem ( FastBuf_t *fb ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatchPattern() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define PATTERN_WILDCARDS "*# ?[{" + +bool MatchPatternFull +( + ccp pattern, // pattern text + ccp text // raw text +); + +bool MatchPattern +( + ccp pattern, // pattern text + ccp text, // raw text + char path_sep // path separator character, standard is '/' +); + +char * MatchRuleLine +( + // returns a pointer to the first non scanned char + + int *status, // not NULL: return match status here + // -2: no prefix found (no ruile found) + // -1: empty line (no ruile found) + // 0: rule found, but don't match + // 1: rule found and match + ccp src, // source line, scanned untilCONTROL + char rule_prefix, // NULL or a rule-beginning-char' + ccp path, // path to verify + char path_sep // path separator character, standard is '/' +); + +/////////////////////////////////////////////////////////////////////////////// + +static inline int GetHexDigit ( char ch ) +{ + return ch >= '0' && ch <= '9' ? ch-'0' + : ch >= 'a' && ch <= 'f' ? ch-'a'+10 + : ch >= 'A' && ch <= 'F' ? ch-'A'+10 + : -1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SplitArg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[SplitArg_t]] + +typedef struct SplitArg_t +{ + uint argc; // number of arguments in 'argv' + char **argv; // array with 'argc' arguments + a NULL term, alloced + uint argv_size; // number of alloced pointers for 'argv' without NULL term + + char *temp; // NULL or temporary buffer + uint temp_size; // size of 'temp' +} +SplitArg_t; + +static inline void InitializeSplitArg ( SplitArg_t *sa ) + { DASSERT(sa); memset(sa,0,sizeof(*sa)); } + +void ResetSplitArg ( SplitArg_t *sa ); + +int ScanSplitArg +( + // returns arg->argc; + + SplitArg_t *arg, // pointer to object + bool init_arg, // true: call InitializeSplitArg() + ccp src, // NULL or source string + ccp src_end, // end of 'src'; if NULL: calculated by strlen(src) + char *work_buf // pointer to a buffer for strings, may be 'src' + // size must be: length of 'src' + 1 + // if NULL, a temporary buffer is alloced. +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ContainerData_t, Container_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// OVERVIEW +//----------------------------------------------------------------------------- +// Container_t * CreateContainer ( Container_t, protect, data,size, CopyMode_t ) +// Container_t * InheritContainer ( Container_t cur, protect, Container_t parent, data,size ) +// bool AssignContainer ( Container_t, protect, data,size, CopyMode_t ) +// void ResetContainer ( Container_t ) +// void DeleteContainer ( Container_t ) +// void UnlinkContainerData ( Container_t ) +// ContainerData_t * LinkContainerData ( Container_t ) +// ContainerData_t * MoveContainerData ( Container_t ) +// Container_t * CatchContainerData ( Container_t, int protect, ContainerData_t ) +// Container_t * UseContainerData ( Container_t, int protect, Container_t ) +//------------ +// ContainerData_t DisjoinContainerData ( Container_t ) +// void JoinContainerData ( Container_t, ContainerData_t ) +// void FreeContainerData ( ContainerData_t ) +//------------ +// static inline bool ModificationAllowed ( const Container_t ) +// void ModifyAllContainer ( Container_t ) +// void ModifyContainer ( Container_t, data, size, CopyMode_t ) +// int SetProtectContainer ( Container_t, int new_protect ) +// static inline int AddProtectContainer ( Container_t, int add_protect ) +// static inline bool IsValidContainer ( const Container_t * c ) +// static inline bool InContainerP ( const Container_t, cvp ptr ) +// static inline bool InContainerS ( const Container_t, cvp ptr, uint size ) +// static inline bool InContainerE ( const Container_t, cvp ptr, cvp end ) +// uint DumpInfoContainer ( f,colset,indent,prefix, Container_t, hexdump_len ) +// uint DumpInfoContainerData ( f,col,indent,prefix, CData_t, hex_len, hex_indent ) +//----------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////// +// [[ContainerData_t]] + +typedef struct ContainerData_t +{ + u8 *data; // data + uint size; // size of 'data' + int ref_count; // reference counter + int protect_count; // >0: don't modify data + bool data_alloced; // true: free data + +} ContainerData_t; + +//----------------------------------------------------------------------------- +// [[Container_t]] + +typedef struct Container_t +{ + ContainerData_t *cdata; // NULL or pointer to ContainerData + int protect_level; // >0: don't modify data + +} Container_t; + +/////////////////////////////////////////////////////////////////////////////// + +Container_t * CreateContainer +( + // returns 'c' or the alloced container + // 'c' is always initialized + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +); + +//----------------------------------------------------------------------------- + +Container_t * InheritContainer +( + // returns 'c' or the alloced container + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + const Container_t *parent, // NULL or valid parent container + const void *data, // data to copy/move/link + uint size // size of 'data' +); + +//----------------------------------------------------------------------------- + +bool AssignContainer +( + // return TRUE on new ContainerData_t + + Container_t *c, // valid container; if NULL: only FREE(data) + int protect, // new protection level + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +); + +//----------------------------------------------------------------------------- + +void ResetContainer +( + Container_t *c // container to reset => no data +); + +//----------------------------------------------------------------------------- + +void DeleteContainer +( + Container_t *c // container to reset and to free => no data +); + +//----------------------------------------------------------------------------- + +void UnlinkContainerData +( + Container_t *c // container to reset => no data +); + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * LinkContainerData +( + // increment 'ref_count' and return NULL or current ContainerData + // => use CatchContainerData() to complete operation + + const Container_t *c // NULL or valid container +); + +//----------------------------------------------------------------------------- + +ContainerData_t * MoveContainerData +( + // return NULL or unlinked current ContainerData + // => use CatchContainerData() to complete operation + Container_t *c // NULL or valid container +); + +//----------------------------------------------------------------------------- + +Container_t * CatchContainerData +( + // returns 'c' or the alloced container + // 'c' is always initialized + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + ContainerData_t *cdata // if not NULL: catch this +); + +//----------------------------------------------------------------------------- + +static inline Container_t * UseContainerData +( + // returns 'c' or the alloced container + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + const Container_t *src // if not NULL: catch this +) +{ + return CatchContainerData(c,protect,LinkContainerData(src)); +} + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * DisjoinContainerData +( + // Disjoin data container date without freeing it. Call either + // JoinContainerData() or FreeContainerData() to finish operation. + // Reference counters are not modified. + // Return the data container or NULL + + Container_t *c // NULL or valid container +); + +//----------------------------------------------------------------------------- + +void JoinContainerData +( + // Join a data container, that was diojoined by DisjoinContainerData() + // Reference counters are not modified. + + Container_t *c, // if NULL: FreeContainerData() + ContainerData_t *cdata // NULL or container-data to join +); + +//----------------------------------------------------------------------------- + +void FreeContainerData +( + // Decrement the reference counter of an already disjoined data container + // and free it if unused. + + ContainerData_t *cdata // NULL or container-data to free +); + +/////////////////////////////////////////////////////////////////////////////// + +static inline bool ModificationAllowed ( const Container_t *c ) + { return c && c->cdata + && ( c->cdata->ref_count <= 1 + || c->cdata->protect_count <= ( c->protect_level > 0 ) ); + } + +//----------------------------------------------------------------------------- + +bool ModifyAllContainer +( + // prepare modification of container-data, create a copy if necessary + // return true, if a new container-data is used + + Container_t *c // NULL or valid container +); + +//----------------------------------------------------------------------------- + +bool ModifyContainer +( + // prepare modification of container-data, create an extract if necessary + // return true, if a new container-data is used + + Container_t *c, // NULL or valid container + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +); + +//----------------------------------------------------------------------------- + +int SetProtectContainer +( + // returns 'c' new protection level + Container_t *c, // valid container, alloc one if NULL + int new_protect // new protection value +); + +//----------------------------------------------------------------------------- + +static inline int AddProtectContainer +( + // returns 'c' new protection level + Container_t *c, // valid container, alloc one if NULL + int add_protect // new protection value +) +{ + DASSERT(c); + return SetProtectContainer(c,c->protect_level+add_protect); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline bool IsValidContainer ( const Container_t * c ) + { return c && c->cdata && c->cdata->data; } + +//----------------------------------------------------------------------------- + +static inline bool InContainerP ( const Container_t * c, cvp ptr ) + { return c && c->cdata && ptr + && (u8*)ptr >= c->cdata->data + && (u8*)ptr <= c->cdata->data + c->cdata->size; } + +//----------------------------------------------------------------------------- + +static inline bool InContainerS ( const Container_t * c, cvp ptr, uint size ) + { return c && c->cdata && ptr + && (u8*)ptr >= c->cdata->data + && (u8*)ptr + size <= c->cdata->data + c->cdata->size; } + +//----------------------------------------------------------------------------- + +static inline bool InContainerE ( const Container_t * c, cvp ptr, cvp end ) + { return c && c->cdata + && ptr && (u8*)ptr >= c->cdata->data + && end && (u8*)end <= c->cdata->data + c->cdata->size; } + +/////////////////////////////////////////////////////////////////////////////// + +struct ColorSet_t; +uint DumpInfoContainer +( + // return the number of printed lines + + FILE *f, // valid output file + const ColorSet_t *colset, // NULL or color set + int indent, // indent the output + ccp prefix, // not NULL: use it as prefix behind indention + const Container_t *c, // dump infos for this container, NULL allowed + uint hexdump_len // max number of bytes used for a hexdump +); + +//----------------------------------------------------------------------------- + +uint DumpInfoContainerData +( + // return the number of printed lines + + FILE *f, // valid output file + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + ccp prefix, // not NULL: use it as prefix behind indention + const + ContainerData_t *cdata, // dump infos for this container-data, NULL allowed + uint hexdump_len, // max number of bytes used for a hexdump + int hexdump_indent // indent of hexdump +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print lines with autobreak /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PutLines +( + FILE * f, // valid output stream + int indent, // indent of output + int fw, // field width of output + int first_line, // length without prefix of already printed first line + ccp prefix, // NULL or prefix for each line + ccp text, // text to print + ccp eol // End Of Line test. If NULL -> LF +); + +void PrintArgLines +( + FILE * f, // valid output stream + int indent, // indent of output + int fw, // field width of output + int first_line, // length without prefix of already printed first line + ccp prefix, // NULL or prefix for each line + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +); + +void PrintLines +( + FILE * f, // valid output stream + int indent, // indent of output + int fw, // field width of output + int first_line, // length without prefix of already printed first line + ccp prefix, // NULL or prefix for each line + ccp format, // format string for vsnprintf() + ... // arguments for 'vsnprintf(format,...)' +) +__attribute__ ((__format__(__printf__,6,7))); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// Rules for P*ColoredLines(): +// +// |+ : Define a TAB pos (not is defined at start) +// |- : Remove a TAB pos +// |[pos,...] : Comma separated list: Clear all tabs and define new TABs +// POS : Define TAB add POS +// POS*DELTA : Define TAB add POS and then at every DELTA +// \t : Jump to TAB stop +// +// | or |> : Define an indention for continuation lines. +// Use the single char only, if a letter, digit, underline, SPACE, +// CTRL or '{' follows. All ather chars are reserved for extensions. +// \r : Force a new continuation line. +// \n : Terminate current line. +// \r\n : Same as '\n'. +// +// {name} : Change active color to NAME +// {name|text} : Use color NAME only for TEXT, then restore active color. +// {} : Reset and deactivate all colors. +// +// || : Print Single '|' +// {{ : Print Single '{' +// +/////////////////////////////////////////////////////////////////////////////// + +uint PutColoredLines +( + // returns the number of written lines + + FILE * f, // valid output stream + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + int fw, // field width; indent+prefix+eol don't count + ccp prefix, // NULL or prefix for each line + ccp eol, // End Of Line test. If NULL -> LF + ccp text // text to print +); + +uint PrintArgColoredLines +( + // returns the number of written lines + + FILE * f, // valid output stream + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + int fw, // field width; indent+prefix+eol don't count + ccp prefix, // NULL or prefix for each line + ccp eol, // End Of Line test. If NULL -> LF + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +); + +uint PrintColoredLines +( + // returns the number of written lines + + FILE * f, // valid output stream + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + int fw, // field width; indent+prefix+eol don't count + ccp prefix, // NULL or prefix for each line + ccp eol, // End Of Line test. If NULL -> LF + ccp format, // format string for vsnprintf() + ... // arguments for 'vsnprintf(format,...)' +) +__attribute__ ((__format__(__printf__,7,8))); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// encoding/decoding /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const u32 TableCRC32[0x100]; +u32 CalcCRC32 ( u32 crc, cvp buf, uint size ); + +/////////////////////////////////////////////////////////////////////////////// + +extern const u16 TableCP1252_80[0x20]; + +/////////////////////////////////////////////////////////////////////////////// +// [[CharMode_t]] + +typedef enum CharMode_t // select encodig/decoding method +{ + CHMD_UTF8 = 0x01, // UTF8 support enabled (compatible with legacy bool) + CHMD_ESC = 0x02, // escape ESC by \e + + CHMD__ALL = CHMD_UTF8 | CHMD_ESC, +} +__attribute__ ((packed)) CharMode_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[EncodeMode_t]] + +typedef enum EncodeMode_t // select encodig/decoding method +{ + ENCODE_OFF, // no encoding (always 0) + ENCODE_STRING, // ScanEscapedString(ANSI), byte mode + ENCODE_UTF8, // ScanEscapedString(UTF8), force UTF8 on decoding + ENCODE_BASE64, // Base64 + ENCODE_BASE64URL, // Base64.URL (=) / decoder detects Standard + URL + STAR + ENCODE_BASE64STAR, // Base64.URL (*) / decoder detects Standard + URL + STAR + ENCODE_BASE64XML, // Base64 with XML name tokens / decoder detects Standard + XML (name+id) + ENCODE_JSON, // JSON string encoding + + ENCODE__N // number of encoding modes +} +EncodeMode_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[DecodeType_t]] + +enum DecodeType_t // for decoding tables +{ + DECODE_NULL = -1, + DECODE_CONTROL = -2, + DECODE_LINE = -3, + DECODE_SPACE = -4, + DECODE_SEPARATE = -5, + DECODE_FILLER = -6, + DECODE_OTHER = -7 +}; + +extern const char TableNumbers[256]; + +extern char TableDecode64[256]; // Standard coding +extern char TableDecode64url[256]; // Standard + URL + STAR coding +extern char TableDecode64xml[256]; // Standard + XML (name tokens + identifiers) + +extern const char TableEncode64[64+1]; // last char is filler +extern const char TableEncode64url[64+1]; // URL coding, used by Nintendo +extern const char TableEncode64star[64+1]; // Like URL, with '*' instead of '=' as filler +extern const char TableEncode64xml[64+1]; // XML name tokens + +// for tests: encode the 48 bytes to get the full BASE64 alphabet +extern const u8 TableAlphabet64[48]; + +// Default tables for DecodeBase64() and EncodeBase64(), if no table is defined. +// They are initialized with TableDecode64[] and TableEncode64[]. +extern ccp TableDecode64default, TableEncode64default; + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintEscapedString +( + // returns 'buf' + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 10 + ccp source, // NULL string to print + int len, // length of string. if -1, str is null terminated + CharMode_t char_mode, // modes, bit field (CHMD_*) + char quote, // NULL or quotation char, that must be quoted + uint *dest_len // not NULL: Store length of result here +); + +/////////////////////////////////////////////////////////////////////////////// + +uint ScanEscapedString +( + // returns the number of valid bytes in 'buf' (NULL term not counted) + + char *buf, // valid destination buffer, maybe source + uint buf_size, // size of 'buf' + ccp source, // string to scan + int len, // length of string. if -1, str is null terminated + bool utf8, // true: source and output is UTF-8 + int quote, // 0:none, -1:auto, >0: quotation char + uint *scanned_len // not NULL: Store number of scanned 'source' bytes here +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u64 DecodeU64 +( + ccp source, // string to decode + int len, // length of string. if -1, str is null terminated + uint base, // number of possible digits (2..36): + uint *scanned_len // not NULL: Store number of scanned 'str' bytes here +); + +/////////////////////////////////////////////////////////////////////////////// + +ccp EncodeU64 +( + char * buf, // result buffer (size depends on base) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 num, // number to encode + uint base // number of possible digits (2..64): +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint DecodeBase64 +( + // returns the number of valid bytes in 'buf' + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 3 + ccp source, // NULL or string to decode + int len, // length of string. if -1, str is null terminated + const char decode64[256], // decoding table; if NULL: use TableDecode64default + bool allow_white_spaces, // true: skip white spaces + uint *scanned_len // not NULL: Store number of scanned 'str' bytes here +); + +//----------------------------------------------------------------------------- + +mem_t DecodeBase64Circ +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + ccp source, // NULL or string to decode + int len, // length of string. if -1, str is null terminated + const char decode64[256] // decoding table; if NULL: use TableDecode64default +); + +/////////////////////////////////////////////////////////////////////////////// + +uint EncodeBase64 +( + // returns the number of scanned bytes of 'source' + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 4 + const void *source, // NULL or data to encode + int source_len, // length of 'source'; if <0: use strlen(source) + const char encode64[64+1], // encoding table; if NULL: use TableEncode64default + bool use_filler, // use filler for aligned output + ccp next_line, // not NULL: use this string as new line sep + uint next_line_trigger // >0: use 'next_line' every # input bytes +); + +//----------------------------------------------------------------------------- + +mem_t EncodeBase64Circ +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + const void *source, // NULL or data to encode + int source_len, // length of 'source'; if <0: use strlen(source) + const char encode64[64+1] // encoding table; if NULL: use TableEncode64default +); + +/////////////////////////////////////////////////////////////////////////////// + +static inline uint EncodeBase64Len ( uint src_len ) + { return 4 * (src_len+2) / 3; } + +static inline uint EncodeBase64FillLen ( uint src_len ) + { return (src_len+2) / 3 * 4; } + +static inline uint DecodeBase64Len ( uint src_len ) + { return 3 * src_len / 4; } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static inline uint DecodeJSON +( + // returns the number of valid bytes in 'buf' + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 3 + ccp source, // NULL or string to decode + int source_len, // length of 'source'. If -1, str is NULL terminated + uint *scanned_len // not NULL: Store number of scanned 'str' bytes here +) +{ + return ScanEscapedString(buf,buf_size,source,source_len,true,0,scanned_len); +} + +//----------------------------------------------------------------------------- + +mem_t DecodeJSONCirc +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + ccp source, // NULL or string to decode + int source_len // length of 'source'. If -1, str is NULL terminated +); + +/////////////////////////////////////////////////////////////////////////////// + +uint EncodeJSON +( + // returns the number of valid bytes in 'buf'. Result is NULL-terminated. + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >2 and 2 bytes longer than needed + const void *source, // NULL or data to encode + int source_len // length of 'source'; if <0: use strlen(source) +); + +//----------------------------------------------------------------------------- + +mem_t EncodeJSONCirc +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + const void *source, // NULL or data to encode + int source_len // length of 'source'; if <0: use strlen(source) +); + +//----------------------------------------------------------------------------- + +mem_t QuoteJSONCirc +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + const void *source, // NULL or data to encode + int source_len, // length of 'source'; if <0: use strlen(source) + int null_if // return 'null' without quotes ... + // <=0: never + // >=1: if source == NULL + // >=2: if source_len == 0 +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint DecodeByMode +( + // returns the number of valid bytes in 'buf'. Result is NULL-terminated. + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 3 + ccp source, // string to decode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +); + +/////////////////////////////////////////////////////////////////////////////// + +uint EncodeByMode +( + // returns the number of valid bytes in 'buf'. Result is NULL-terminated. + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 4 + ccp source, // string to encode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +); + +/////////////////////////////////////////////////////////////////////////////// + +mem_t DecodeByModeMem +( + // Returns the decoded 'source'. Result is NULL-terminated. + // It points either to 'buf' or is alloced (on buf==NULL or to less space) + // If alloced (mem.ptr!=buf) => call FreeMem(&mem) + + char *buf, // NULL or destination buffer + uint buf_size, // size of 'buf' + ccp source, // string to decode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +); + +/////////////////////////////////////////////////////////////////////////////// + +mem_t EncodeByModeMem +( + // Returns the encoded 'source'. Result is NULL-terminated. + // It points either to 'buf' or is alloced (on buf==NULL or to less space) + // If alloced (mem.ptr!=buf) => call FreeMem(&mem) + + char *buf, // NULL or destination buffer + uint buf_size, // size of 'buf' + ccp source, // string to encode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +); + +/////////////////////////////////////////////////////////////////////////////// + +#define SJIS_TAB_MAPPING_BEG 0xeea0 // first code used for table mapping +#define SJIS_TAB_MAPPING_END 0xeecf // last code used for table mapping +#define SJIS_INVALID_CODE 0xffff // invalid code +#define SJIS_CACHE_SIZE 0xffff // number of elements in cache + +static inline bool IsValidShiftJIS ( uint code ) + { return code < SJIS_TAB_MAPPING_BEG + || code > SJIS_TAB_MAPPING_END && code < SJIS_INVALID_CODE; } + +int ScanShiftJISChar ( cucp * str ); +int ScanShiftJISCharE ( cucp * str, cvp end ); + +void SetupGetShiftJISCache(void); +int GetShiftJISChar ( u32 code ); + +ccp GetShiftJISStatistics(void); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan keywords /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define KEYWORD_NAME_MAX 100 + +typedef s64 (*KeywordCallbackFunc) +( + void *param, // NULL or user defined parameter + ccp name, // normalized name of option + const KeywordTab_t *key_tab, // valid pointer to command table + const KeywordTab_t *key, // valid pointer to found command + char prefix, // 0 | '-' | '+' | '=' + s64 result // current value of result +); + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t *GetKewordById +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id // id to search +); + +//----------------------------------------------------------------------------- + +const KeywordTab_t *GetKewordByIdAndOpt +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id, // id to search + s64 opt // opt to search +); + +//----------------------------------------------------------------------------- + +static inline ccp GetKewordNameById +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id, // id to search + ccp res_not_found // return this if not found +) +{ + const KeywordTab_t *key = GetKewordById(key_tab,id); + return key ? key->name1 : res_not_found; +} + +//----------------------------------------------------------------------------- + +static inline ccp GetKewordNameByIdAndOpt +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id, // id to search + s64 opt, // opt to search + ccp res_not_found // return this if not found +) +{ + const KeywordTab_t *key = GetKewordByIdAndOpt(key_tab,id,opt); + return key ? key->name1 : res_not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t * ScanKeyword +( + int *res_abbrev, // NULL or pointer to result 'abbrev_count' + ccp arg, // argument to scan + const KeywordTab_t *key_tab // valid pointer to command table +); + +//----------------------------------------------------------------------------- + +s64 ScanKeywordListEx +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab, // valid pointer to command table + KeywordCallbackFunc func, // NULL or calculation function + bool allow_prefix, // allow '-' | '+' | '=' as prefix + u32 max_number, // allow numbers < 'max_number' (0=disabled) + s64 result, // start value for result + + uint err_mode, // bit field: + // 1: continue on error + ccp err_msg, // not NULL: print a warning message: + // ": Unknown keyword: " + enumError err_code, // use 'err_code' for the 'err_msg' + uint *err_count // not NULL: store errors here +); + +static inline s64 ScanKeywordList +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab, // valid pointer to command table + KeywordCallbackFunc func, // NULL or calculation function + bool allow_prefix, // allow '-' | '+' | '=' as prefix + u32 max_number, // allow numbers < 'max_number' (0=disabled) + s64 result, // start value for result + + ccp err_msg, // not NULL: print a warning message: + // ": Unknown keyword: " + enumError err_code // use 'err_code' for the 'err_msg' +) +{ + return ScanKeywordListEx( arg, key_tab, func, allow_prefix, + max_number, result, 0, err_msg, err_code, 0 ); +} + +//----------------------------------------------------------------------------- + +enumError ScanKeywordListFunc +( + ccp arg, // argument to scan + const KeywordTab_t *key_tab, // valid pointer to command table + KeywordCallbackFunc func, // calculation function + void *param, // used define parameter for 'func' + bool allow_prefix // allow '-' | '+' | '=' as prefix +); + +//----------------------------------------------------------------------------- + +s64 ScanKeywordListMask +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab // valid pointer to command table +); + +//----------------------------------------------------------------------------- + +enumError PrintKeywordError +( + const KeywordTab_t * key_tab, // NULL or pointer to command table + ccp key_arg, // analyzed command + int key_stat, // status of ScanKeyword() + ccp prefix, // NULL or prefix for messages + ccp object // NULL or object for error messages + // default= 'command' +); + +uint CollectAmbiguousKeywords +( + char *buf, // destination buffer, 200 bytes are good + uint buf_size, // size of buffer + const KeywordTab_t * key_tab, // NULL or pointer to command table + ccp key_arg // analyzed command +); + +//----------------------------------------------------------------------------- + +char * PrintKeywordList +( + // returns a pointer to the result buffer + + char *buf, // result buffer + // If NULL, a local circulary static buffer + // with max CIRC_BUF_MAX_ALLOC bytes is used + uint buf_size, // size of 'buf', at least 10 bytes if buf is set + uint *ret_length, // not NULL: store result length here + + const KeywordTab_t *tab, // related keyword table + u64 mode, // mode to print + u64 default_mode, // NULL or default mode + u64 hide_mode // bit field to hide parameters +); + +//----------------------------------------------------------------------------- + +int ScanKeywordOffOn +( + // returns 0 for '0|OFF'; 1 for '1|ON; -1 for empty; -2 on error + + ccp arg, // argument to scan + uint max_num, // >0: additionally accept+return number <= max_num + ccp object // NULL (silent) or object for error messages +); + +//----------------------------------------------------------------------------- + +enumError Command_ARGTEST ( int argc, char ** argv ); +enumError Command_TESTCOLORS ( int argc, char ** argv ); + +enumError Command_COLORS +( + int level, // only used, if mode==NULL + // < 0: status message (ignore mode) + // >= 1: include names + // >= 2: include alt names + // >= 3: include color names incl bold + // >= 4: include background color names + uint mode, // output mode => see PrintColorSetHelper() + uint format // output format => see PrintColorSetEx() +); + + +/////////////////////////////////////////////////////////////////////////////// +/////////////// OFF/AUTO/ON/FORCE /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[OffOn_t]] + +typedef enum OffOn_t +{ + OFFON_ERROR = -99, + OFFON_OFF = -1, + OFFON_AUTO = 0, + OFFON_ON = 1, + OFFON_FORCE = 2, +} +OffOn_t; + +extern const KeywordTab_t KeyTab_OFF_AUTO_ON[]; + +int ScanKeywordOffAutoOn +( + // returns one of OFFON_* + + ccp arg, // argument to scan + int on_empty, // return this value on empty + uint max_num, // >0: additionally accept+return number <= max_num + ccp object // NULL (silent) or object for error messages +); + +ccp GetKeywordOffAutoOn ( OffOn_t value ); + +// // +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan command lists /////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// Scan a semicolon separated command list with binary support of format: +// COMMAND := NAME [BINLIST] CONTROL* ['='] CONTROL* [PARAM] CONTROL* ';' +// BLANK := TAB(9) or SPACE(32) +// CONTROL := NUL..SPACE (0..32) +// NAME := 1..99 alphanumeric chars incl. minus and underline +// BINLIST := BINARY [BINLIST] +// BINARY := BLANK* '\1' be16:SIZE DATA +// PARAM := all except ';' +// +// CONTROLS are possible before and behind the '=' and at the end of param. +// Only BLANK* are allowed between command NAME and BINARY block. +// BINARY data starts with \1 and is stored as '->bin' and '->bin_len' and +// inludes SIZE and DATA. The BINARY.SIZE includes itself and is a 16-bit +// big-endian number. In '->bin[]', member 'be16:SIZE' is excluded, +// but '->bin[].ptr - 2' points to be16:SIZE. +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[CommandList_t]] docu -> see above + +struct TCPStream_t; +#define COMMAND_LIST_N_BIN 5 + +typedef struct CommandList_t +{ + //--- input params + + ccp command; // command text + uint command_len; // total length of 'command' + char command_sep; // not NULL: alternative command separator + u8 change_case; // >0: convert command to lower(1) or upper(2) case + bool term_param; // 1: write a NULL at end-of-param => modify 'command' + // 2: write a ';' at end-of-param => modify 'command' + // in this case, ';' is counted to 'record_len' + bool is_terminated; // true: last command is terminated + // even without ';' + + //--- user parameters, pass-through + + struct TCPStream_t + *user_ts; // NULL or a stream for replies + void *user_ptr; // any pointer + int user_int; // any number or id + + ccp log_fname; // not NULL: open log file + uint log_level; // 0:off, 1:stat+err, 2:+cmd, 3:+bin+param, 4:+func + + //--- analysed data, only valid at callback + + char cmd[100]; // scanned command name + uint cmd_len; // length of scanned command name + + uint n_bin; // number of binary blocks + mem_t bin[COMMAND_LIST_N_BIN]; + // binary data detected, if bin[].ptr not null + + ccp param; // pointer to text parameter, not NULL terminated + uint param_len; // length of text parameter + const u8 *record; // complete record from NAME to END_RECORD_CHAR + uint record_len; // length of trimmed record + uint input_len; // length of complete record at input time + + //--- statistics + + uint read_cmd_len; // number of used bytes of 'command' + uint fail_count; // number of failed commands + uint cmd_count; // number of commands +} +CommandList_t; + +// return -1 to abort scanning +typedef int (*CommandListFunc) ( CommandList_t *cli ); + +//----------------------------------------------------------------------------- + +static inline void InitializeCommandList ( CommandList_t *cli ) + { DASSERT(cli); memset(cli,0,sizeof(*cli)); } + +static inline void ClearCommandList ( CommandList_t *cli ) + { DASSERT(cli); } + +static inline void ResetCommandList ( CommandList_t *cli ) + { DASSERT(cli); memset(cli,0,sizeof(*cli)); } + +// a test function for ScanCommandList() +int ExecCommandOfList ( CommandList_t *cli ); + +int ScanCommandList +( + // scan a semicolon separated command list. See above for format + // returns -1 on 'cli->fail_count'; otherwise 'cli->cmd_count' + + CommandList_t *cli, // valid command list + CommandListFunc func // NULL or function for each command +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// numeric functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern int urandom_available; // <0:not, =0:not tested, >0:ok +extern bool use_urandom_for_myrandom; + +uint ReadFromUrandom ( void *dest, uint size ); + +/////////////////////////////////////////////////////////////////////////////// + +u32 MyRandom ( u32 max ); +u64 MySeed ( u64 base ); +u64 MySeedByTime(void); + +void MyRandomFill ( void * buf, size_t size ); + +/////////////////////////////////////////////////////////////////////////////// + +void CreateUUID ( uuid_buf_t dest ); +uint CreateTextUUID ( char *buf, uint bufsize ); +uint PrintUUID ( char *buf, uint bufsize, uuid_buf_t uuid ); +char * ScanUUID ( uuid_buf_t uuid, ccp source ); + +/////////////////////////////////////////////////////////////////////////////// + +uint gcd ( uint n1, uint n2 ); // greatest common divisor, german: ggt +u32 gcd32 ( u32 n1, u32 n2 ); +u64 gcd64 ( u64 n1, u64 n2 ); + +uint lcm ( uint n1, uint n2 ); // lowest common multiple, german: kgv +u32 lcm32 ( u32 n1, u32 n2 ); +u64 lcm64 ( u64 n1, u64 n2 ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan number /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum DigitTable_t +{ + NUMBER_NULL = -1, // NULL + NUMBER_CONTROL = -2, // < 0x20, but not SPACE or LINE + NUMBER_LINE = -3, // LF, CR + NUMBER_SPACE = -4, // SPACE, TAB, VT + NUMBER_TIE = -5, // ':' '-' '.' + NUMBER_SEPARATE = -6, // ',' ';' + NUMBER_OTHER = -7 // all other +}; + +extern const char DigitTable[256]; + +//----------------------------------------------------------------------------- + +u64 ScanDigits +( + // same as ScanNumber(), but other interface + // returns the scanned number + + ccp *source, // pointer to source string, modified + ccp end_source, // NULL or end of 'source' + uint intbase, // integer base, 2..36 + int maxchar, // max number of digits to read + uint *count // not NULL: store number of scanned digits here +); + +//----------------------------------------------------------------------------- + +char * ScanNumber +( + // same as ScanDigits(), but other interface + // returns a pointer to the first not used character + + uint *dest_num, // store result here, never NULL + ccp src, // pointer to source string, modified + ccp src_end, // NULL or end of 'src' + uint intbase, // integer base, 2..36 + int maxchar // max number of digits to read +); + +//----------------------------------------------------------------------------- + +char * ScanEscape +( + // returns a pointer to the first not used character + + uint *dest_code, // store result here, never NULL + ccp src, // pointer to source string (behind escape char) + ccp src_end // NULL or end of 'src' +); + +//----------------------------------------------------------------------------- + +uint ScanHexString +( + // return the number of written bytes + + void *buf, // write scanned data here + uint buf_size, // size of buf + ccp *source, // pointer to source string, modified + ccp end_source, // NULL or end of 'source' + bool allow_tie // allow chars ' .:-' and TAB,VT as byte separator +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanS32 +( + // return 'source' on error + + s32 *res_num, // not NULL: store result (only on success) + ccp source, // NULL or source text + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +); + +static inline char * ScanU32 ( u32 *res_num, ccp source, uint default_base ) + { return ScanS32((s32*)res_num,source,default_base); } + +//----------------------------------------------------------------------------- + +char * ScanS64 +( + // return 'source' on error + + s64 *res_num, // not NULL: store result (only on success) + ccp source, // NULL or source text + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +); + +static inline char * ScanU64 ( u64 *res_num, ccp source, uint default_base ) + { return ScanS64((s64*)res_num,source,default_base); } + +//----------------------------------------------------------------------------- + +#if __WORDSIZE >= 64 + + static inline char * ScanINT ( int *res_num, ccp source, uint default_base ) + { return ScanS32((s32*)res_num,source,default_base); } + static inline char * ScanUINT ( uint *res_num, ccp source, uint default_base ) + { return ScanS32((s32*)res_num,source,default_base); } + +#else + + static inline char * ScanINT ( int *res_num, ccp source, uint default_base ) + { return ScanS64((s64*)res_num,source,default_base); } + static inline char * ScanUINT ( uint *res_num, ccp source, uint default_base ) + { return ScanS64((s64*)res_num,source,default_base); } + +#endif + +//----------------------------------------------------------------------------- +// strto*() replacements: Use Scan*() with better base support, NULL allowed as source + +long int str2l ( const char *nptr, char **endptr, int base ); +long long int str2ll ( const char *nptr, char **endptr, int base ); +unsigned long int str2ul ( const char *nptr, char **endptr, int base ); +unsigned long long int str2ull ( const char *nptr, char **endptr, int base ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print numbers and size /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sizeform_mode_t]] + +typedef enum sizeform_mode_t +{ + DC_SFORM_ALIGN = 0x01, // aligned output + DC_SFORM_NARROW = 0x02, // suppress space between number and unit + DC_SFORM_UNIT1 = 0x04, // limit unit to 1 character (space for bytes) + DC_SFORM_INC = 0x08, // increment to next factor if no fraction is lost + DC_SFORM_PLUS = 0x10, // on signed output: print PLUS sign for values >0 + DC_SFORM_DASH = 0x20, // on NULL: print only a dash (minus sign) + + DC_SFORM__MASK = 0x3f, // mask of above + DC_SFORM__AUTO = 0x40, // hint for some functions to decide by themself + + DC_SFORM_TINY = DC_SFORM_NARROW | DC_SFORM_UNIT1, +} +sizeform_mode_t; + +/////////////////////////////////////////////////////////////////////////////// + +float RoundF2bytes ( float f ); +float RoundF3bytes ( float f ); +double RoundD6bytes ( double d ); +double RoundD7bytes ( double d ); + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberU4 ( char *buf, size_t buf_size, u64 num, sizeform_mode_t mode ); +char * PrintNumberU5 ( char *buf, size_t buf_size, u64 num, sizeform_mode_t mode ); +char * PrintNumberU6 ( char *buf, size_t buf_size, u64 num, sizeform_mode_t mode ); +char * PrintNumberU7 ( char *buf, size_t buf_size, u64 num, sizeform_mode_t mode ); + +char * PrintNumberS5 ( char *buf, size_t buf_size, s64 num, sizeform_mode_t mode ); +char * PrintNumberS6 ( char *buf, size_t buf_size, s64 num, sizeform_mode_t mode ); +char * PrintNumberS7 ( char *buf, size_t buf_size, s64 num, sizeform_mode_t mode ); + +char * PrintNumberD5 ( char *buf, size_t buf_size, double num, sizeform_mode_t mode ); +char * PrintNumberD6 ( char *buf, size_t buf_size, double num, sizeform_mode_t mode ); +char * PrintNumberD7 ( char *buf, size_t buf_size, double num, sizeform_mode_t mode ); + +// Parameters: +// char *buf, // result buffer +// // NULL: use a local circulary static buffer +// size_t buf_size, // size of 'buf', ignored if buf==NULL +// * num, // number to print +// sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH[not *D*()] + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[size_mode_t]] + +typedef enum size_mode_t +{ + //----- modes => used as index for dc_size_tab_1000[] & dc_size_tab_1024[] + + DC_SIZE_DEFAULT = 0, // special default value, fall back to AUTO + DC_SIZE_AUTO, // select unit automatically + + DC_SIZE_BYTES, // force output in bytes + DC_SIZE_K, // force output in KB or KiB (kilo,kibi) + DC_SIZE_M, // force output in MB or MiB (mega,mebi) + DC_SIZE_G, // force output in GB or GiB (giga,gibi) + DC_SIZE_T, // force output in TB or TiB (tera,tebi) + DC_SIZE_P, // force output in PB or PiB (peta,pebi) + DC_SIZE_E, // force output in EB or EiB (exa, exbi) + // zetta/zebi & yotta/yobi not supported because >2^64 + + DC_SIZE_N_MODES, // number of modes + + //----- flags + + DC_SIZE_F_1000 = 0x010, // force output in SI units (kB=1000, MB=1000000,...) + DC_SIZE_F_1024 = 0x020, // force output in IEC units (KiB=1024, MiB=1024*1024,...) + DC_SIZE_F_AUTO_UNIT = 0x040, // suppress output of unit for non AUTO mode + DC_SIZE_F_NO_UNIT = 0x080, // suppress allways output of unit + + //----- masks + + DC_SIZE_M_MODE = 0x00f, // mask for modes + DC_SIZE_M_BASE = 0x030, // mask for base + DC_SIZE_M_ALL = 0x0ff, // all relevant bits +} +size_mode_t; + +//----------------------------------------------------------------------------- + +extern ccp dc_size_tab_1000[DC_SIZE_N_MODES+1]; +extern ccp dc_size_tab_1024[DC_SIZE_N_MODES+1]; + +//----------------------------------------------------------------------------- + +ccp GetSizeUnit // get a unit for column headers +( + size_mode_t mode, // print mode + ccp if_invalid // output for invalid modes +); + +//----------------------------------------------------------------------------- + +int GetSizeFW // get a good value field width +( + size_mode_t mode, // print mode + int min_fw // minimum fw => return max(calc_fw,min_fw); + // this value is also returned for invalid modes +); + +//----------------------------------------------------------------------------- + +char * PrintSize +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 size, // size to print + sizeform_mode_t sform_mode, // output format, bit field + size_mode_t mode // print mode +); + +//----------------------------------------------------------------------------- + +char * PrintSize1000 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 size, // size to print + sizeform_mode_t sform_mode // output format, bit field +); + +//----------------------------------------------------------------------------- + +char * PrintSize1024 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 size, // size to print + sizeform_mode_t sform_mode // output format, bit field +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan size /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern u64 (*ScanSizeFactorHook) +( + char ch_factor, // char to analyze + int force_base // if 1000|1024: force multiple of this +); + +u64 ScanSizeFactor +( + char ch_factor, // char to analyze + int force_base // if 1000|1024: force multiple of this +); + +char * ScanSizeTerm +( + double *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + int force_base // if 1000|1024: force multiple of this +); + +char * ScanSize +( + double *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base // if 1000|1024: force multiple of this +); + +char * ScanSizeU32 +( + u32 *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base // if 1000|1024: force multiple of this +); + +char * ScanSizeU64 +( + u64 *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base // if 1000|1024: force multiple of this +); + +//----------------------------------------------------------------------------- +// strto*() like wrappers + +u32 GetSizeU32 ( ccp src, char **end, int force_base ); +u64 GetSizeU64 ( ccp src, char **end, int force_base ); +double GetSizeD ( ccp src, char **end, int force_base ); + +//----------------------------------------------------------------------------- + +char * ScanSizeRange +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + double *num1, // not NULL: store 'from' result + double *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + double max_value // >0: max value for open ranges +); + +char * ScanSizeRangeU32 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + u32 *num1, // not NULL: store 'from' result + u32 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u32 max_value // >0: max value for open ranges +); + +char * ScanSizeRangeU64 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + u64 *num1, // not NULL: store 'from' result + u64 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u64 max_value // >0: max value for open ranges +); + +//----------------------------------------------------------------------------- + +char * ScanSizeRangeList +( + uint *n_range, // not NULL: store number of scanned ranges + double *num, // array with '2*max_range' elements + // unused elements are filled with 0.0 + uint max_range, // max number of allowed ranges + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + double max_value // >0: max value for open ranges +); + +char * ScanSizeRangeListU32 +( + uint *n_range, // not NULL: store number of scanned ranges + u32 *num, // array with '2*max_range' elements + // unused elements are filled with 0 + uint max_range, // max number of allowed ranges + + ccp source, // source text + u32 default_factor, // use this factor if number hasn't one + u32 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u32 max_value // >0: max value for open ranges +); + +char * ScanSizeRangeListU64 +( + uint *n_range, // not NULL: store number of scanned ranges + u64 *num, // array with '2*max_range' elements + // unused elements are filled with 0 + uint max_range, // max number of allowed ranges + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u64 max_value // >0: max value for open ranges +); + +//----------------------------------------------------------------------------- + +enumError ScanSizeOpt +( + double *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + + ccp opt_name, // NULL or name of option for error messages + u64 min, // >0: minimum allowed value + u64 max, // >0: maximum allowed value + bool print_err // true: print error messages +); + +enumError ScanSizeOptU64 +( + u64 *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + int force_base, // if 1000|1024: force multiple of this + + ccp opt_name, // NULL or name of option for error messages + u64 min, // >0: minimum allowed value + u64 max, // >0: maximum allowed value + u32 multiple, // >0: result must be multiple + u32 pow2, // >0: result must power of '1<0: minimum allowed value + u64 max, // >0: maximum allowed value + u32 multiple, // >0: result must be multiple + u32 pow2, // >0: result must power of '1<0.0 ? (u64)trunc( 1e3*d+0.5 ) : 0; } +static inline u64 double2usec ( double d ) { return d>0.0 ? (u64)trunc( 1e6*d+0.5 ) : 0; } + +static inline double msec2double ( u64 num ) { return num * 1e-3; } +static inline double usec2double ( u64 num ) { return num * 1e-6; } + +//--- another epoch: Monday, 2001-01-01 + +#define EPOCH_2001_SEC 0x3a4fc880 +#define EPOCH_2001_MSEC (EPOCH_2001_SEC*MSEC_PER_SEC) +#define EPOCH_2001_USEC (EPOCH_2001_SEC*USEC_PER_SEC) + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time // seconds since epoch -> time() +); + +ccp PrintTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time // seconds since epoch -> time() +); + +//----------------------------------------------------------------------------- + +ccp PrintUsecByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time, // seconds since epoch -> time() + uint usec // micro second of second +); + +ccp PrintUsecByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time, // seconds since epoch -> time() + uint usec // micro second of second +); + +//----------------------------------------------------------------------------- + + +ccp PrintTimevalByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + const struct timeval *tv // time to print, if NULL use gettimeofday() +); + + +ccp PrintTimevalByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + const struct timeval *tv // time to print, if NULL use gettimeofday() +); + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimeSec +( + char * buf, // result buffer (>19 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_sec_t sec // seconds (=time) to print +); + +//----------------------------------------------------------------------------- + +ccp PrintTimeMSec +( + char * buf, // result buffer (>23 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_msec_t msec, // milliseconds to print + uint fraction // number of digits (0-3) to print as fraction +); + +//----------------------------------------------------------------------------- + +ccp PrintTimeUSec +( + char * buf, // result buffer (>26 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_usec_t usec, // microseconds to print + uint fraction // number of digits (0-6) to print as fraction +); + +//----------------------------------------------------------------------------- + +ccp PrintTimerMSec +( + char * buf, // result buffer (>12 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 msec, // milliseconds to print + uint fraction // number of digits (0-3) to print as fraction +); + +ccp PrintTimerUSec +( + char * buf, // result buffer (>16 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_usec_t usec, // microseconds to print + uint fraction // number of digits (0-3) to print as fraction +); + +//----------------------------------------------------------------------------- + +ccp PrintTimer3 // helper function +( + char * buf, // result buffer (>3 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 sec, // seconds to print + int usec, // 0...999999: usec fraction, otherwise suppress ms/us output + bool aligned // true: print aligned 3 character output +); + +static inline ccp PrintTimerSec3 +( + char * buf, // result buffer (>3 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_sec_t sec, // seconds to print + bool aligned // true: print aligned 3 character output +) +{ + return PrintTimer3(buf,buf_size,sec,-1,aligned); +} + +static inline ccp PrintTimerMSec3 +( + char * buf, // result buffer (>3 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_msec_t msec, // microseconds to print + bool aligned // true: print aligned 3 character output +) +{ + return PrintTimer3(buf,buf_size,msec/1000,msec%1000*1000,aligned); +} + +static inline ccp PrintTimerUSec3 +( + char * buf, // result buffer (>3 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_usec_t usec, // microseconds to print + bool aligned // true: print aligned 3 character output +) +{ + return PrintTimer3(buf,buf_size,usec/1000000,usec%1000000,aligned); +} + +//----------------------------------------------------------------------------- + +ccp PrintTimer4 // helper function +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 sec, // seconds to print + int usec, // 0...999999: usec fraction, otherwise suppress ms/us output + bool aligned // true: print aligned 4 character output +); + +static inline ccp PrintTimerSec4 +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_sec_t sec, // seconds to print + bool aligned // true: print aligned 4 character output +) +{ + return PrintTimer4(buf,buf_size,sec,-1,aligned); +} + +static inline ccp PrintTimerMSec4 +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_msec_t msec, // microseconds to print + bool aligned // true: print aligned 4 character output +) +{ + return PrintTimer4(buf,buf_size,msec/1000,msec%1000*1000,aligned); +} + +static inline ccp PrintTimerUSec4 +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_usec_t usec, // microseconds to print + bool aligned // true: print aligned 4 character output +) +{ + return PrintTimer4(buf,buf_size,usec/1000000,usec%1000000,aligned); +} + +//----------------------------------------------------------------------------- + +ccp PrintTimer6 // helper function +( + char * buf, // result buffer (>6 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 sec, // seconds to print + int usec, // 0...999999: usec fraction, + // otherwise suppress ms/us output + bool aligned // true: print aligned 6 character output +); + +static inline ccp PrintTimerSec6 +( + char * buf, // result buffer (>6 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_sec_t sec, // seconds to print + bool aligned // true: print aligned 6 character output +) +{ + return PrintTimer6(buf,buf_size,sec,-1,aligned); +} + +static inline ccp PrintTimerMSec6 +( + char * buf, // result buffer (>6 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_msec_t msec, // microseconds to print + bool aligned // true: print aligned 6 character output +) +{ + return PrintTimer6(buf,buf_size,msec/1000,msec%1000*1000,aligned); +} + +static inline ccp PrintTimerUSec6 +( + char * buf, // result buffer (>6 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_usec_t usec, // microseconds to print + bool aligned // true: print aligned 6 character output +) +{ + return PrintTimer6(buf,buf_size,usec/1000000,usec%1000000,aligned); +} + +//----------------------------------------------------------------------------- + +ccp PrintTimerUSec4s +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_usec_t usec, // microseconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +); + +static inline ccp PrintTimerMSec4s +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_msec_t msec, // milliseconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +) +{ + return PrintTimerUSec4s(buf,buf_size,1000*msec,mode); +} + +static inline ccp PrintTimerSec4s +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + int sec, // seconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +) +{ + return PrintTimerUSec4s(buf,buf_size,1000000ll*sec,mode); +} + +//----------------------------------------------------------------------------- + +ccp PrintTimerUSec7s +( + char * buf, // result buffer (>7 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_usec_t usec, // microseconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +); + +static inline ccp PrintTimerMSec7s +( + char * buf, // result buffer (>7 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_msec_t msec, // milliseconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +) +{ + return PrintTimerUSec7s(buf,buf_size,1000*msec,mode); +} + +static inline ccp PrintTimerSec7s +( + char * buf, // result buffer (>7 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + int sec, // seconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +) +{ + return PrintTimerUSec7s(buf,buf_size,1000000ll*sec,mode); +} + +//----------------------------------------------------------------------------- + +typedef struct xtime_t +{ + time_t time; + suseconds_t usec; + u64 total_usec; + u64 rel_usec; + +} xtime_t; + +u64 GetXTime ( xtime_t * xtime ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan date & time /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ScanInterval*() is old ==> try ScanDuration() + +char * ScanInterval +( + time_t *res_time, // not NULL: store seconds + u32 *res_usec, // not NULL: store micro seconds of second + ccp source // source text +); + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanInterval64 +( + u64 *res_usec, // not NULL: store total micro seconds + ccp source // source text +); + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanDateTime +( + time_t *res_time, // not NULL: store seconds + u32 *res_usec, // not NULL: store micro seconds of second + ccp source, // source text + bool allow_delta // true: allow +|- interval +); + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanDateTime64 +( + u64 *res_usec, // not NULL: store total micro seconds + ccp source, // source text + bool allow_delta // true: allow +|- interval +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Since 2001 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// Values for day and week are exact. +// Values for month, quarter and year are based on 365.2425 days/year. + +//----------------------------------------------------------------------- +// name full/13 short/11 alt/10 tiny/8 +//----------------------------------------------------------------------- +// hour YYYY-MM-DD HH YY-MM-DD HH DD. HH:xx MM-DD HH +// day YYYY-MM-DD << << YY-MM-DD +// week YYYYwWW << << << +// month YYYY-MM << << << +// quarter YYYYqQ << << << +// year YYYY << << << +//----------------------------------------------------------------------- + +enum +{ + S2001_FW_FULL = 13, ///< Standard-Text mit vollem Jahr (maximal 13 Zeichen). + S2001_FW_SHORT = 11, ///< Verkürzte Stundenangabe (maximal 11 Zeichen). + S2001_FW_ALT = 10, ///< Alternative Stundenangabe mit »:xx« (maximal 10 Zeichen). + S2001_FW_TINY = 8, ///< Verkürzte Stunden- und Wochenangabe (maximal 8 Zeichen). + + S2001_DUR_HOUR = 3600, ///< Dauer einer Stunde in Sekunden. + S2001_DUR_DAY = 86400, ///< Dauer eines Tages in Sekunden. + S2001_DUR_WEEK = 604800, ///< Dauer einer Woche in Sekunden. + S2001_DUR_MONTH = 2629746, ///< Dauer eines Monats in Sekunden (365.2425/12 Tage). + S2001_DUR_QUARTER = 7889238, ///< Dauer eines Quartals in Sekunden (365.2425/4 Tage). + S2001_DUR_YEAR = 31556952, ///< Dauer eines Jahres in Sekunden (365.2425 Tage). +}; + +/////////////////////////////////////////////////////////////////////////// +// hour support + +static inline int time2hour ( s64 time ) + { return time / 3600 - 271752; } + +static inline double time2hourF ( s64 time ) + { return time / 3600.0 - 271752; } + +static inline s64 hour2time ( int hour ) + { return ( hour + 271752 ) * 3600; } + +static inline int hour2duration ( int hour ) + { return S2001_DUR_HOUR; } + +ccp hour2text +( + int hour, // hour to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use '-' + ccp sep_dt // NULL or separator of between date and time + // if NULL: use ' ' +); + +/////////////////////////////////////////////////////////////////////////// +// day support + +static inline int time2day ( s64 time ) + { return time / 86400 - 11323; } + +static inline double time2dayF ( s64 time ) + { return time / 86400.0 - 11323; } + +static inline s64 day2time ( int day ) + { return ( day + 11323 ) * 86400; } + +static inline int day2duration ( int day ) + { return S2001_DUR_DAY; } + +ccp day2text +( + int day, // day to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use '-' + ccp sep_dt // ignored +); + +/////////////////////////////////////////////////////////////////////////// +// week support + +static inline int time2week ( s64 time ) + { return ( time - 978307200 ) / 604800; } + +static inline double time2weekF ( s64 time ) + { return ( time - 978307200 ) / 604800.0; } + +static inline s64 week2time ( int week ) + { return week * 604800 + 978307200; } + +static inline int week2duration ( int week ) + { return S2001_DUR_WEEK; } + +ccp week2text +( + int week, // week to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use 'w' + ccp sep_dt // ignored +); + +/////////////////////////////////////////////////////////////////////////// +// month support + +int time2month ( time_t tim ); +time_t month2time ( int month ); + +static inline int month2duration ( int month ) + { return month2time(month+1) - month2time(month); } + +ccp month2text +( + int month, // month to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use '-' + ccp sep_dt // ignored +); + +/////////////////////////////////////////////////////////////////////////// +// quarter support + +int time2quarter ( time_t tim ); +time_t quarter2time ( int quarter ); + +static inline int quarter2duration ( int quarter ) + { return quarter2time(quarter+1) - quarter2time(quarter); } + +ccp quarter2text +( + int quarter, // quarter to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use 'q' + ccp sep_dt // ignored +); + +/////////////////////////////////////////////////////////////////////////// +// year support + +int time2year ( time_t tim ); +time_t year2time ( int year ); + +static inline int year2duration ( int year ) + { return year2time(year+1) - year2time(year); } + +static inline ccp year2text +( + int year, // year to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // ignored + ccp sep_dt // ignored +) +{ + return PrintCircBuf("%04d",year); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sockaddr support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sockaddr_t]] [[sockaddr_in_t]] [[sockaddr_un_t]] [[sockaddr_info_t]] + +typedef struct sockaddr sockaddr_t; +typedef struct sockaddr_in sockaddr_in_t; +typedef struct sockaddr_un sockaddr_un_t; + +typedef struct sockaddr_info_t +{ + sockaddr_t *sa; // pointer to sockaddr or sockaddr_* + uint size; // size of '*sa' +} +sockaddr_info_t; + +//----------------------------------------------------------------------------- + +// [[doxygen]] +static inline sockaddr_info_t CreateSockaddrInfo ( cvp sa, uint size ) +{ + sockaddr_info_t i = + { + .sa = (sockaddr_t*)sa, + .size = size + }; + return i; +} + +//----------------------------------------------------------------------------- + +// [[doxygen]] +static inline void SetupSockaddrInfo ( sockaddr_info_t *sai, cvp sa, uint size ) +{ + DASSERT(sai); + sai->sa = (sockaddr_t*)sa; + sai->size = size ? size : sizeof(sockaddr_in_t); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrintIP4*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define FW_IPV4_A 12 // 255.16777215 +#define FW_IPV4_B 13 // 255.255.65535 +#define FW_IPV4_C 15 // 255.255.255.255 + +#define FW_IPV4_PORT 5 // 65535 + +#define FW_IPV4_A_PORT (FW_IPV4_A+1+FW_IPV4_PORT) +#define FW_IPV4_B_PORT (FW_IPV4_B+1+FW_IPV4_PORT) +#define FW_IPV4_C_PORT (FW_IPV4_C+1+FW_IPV4_PORT) + +//----------------------------------------------------------------------------- + +char * PrintIP4A +( + // print in A-notation + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +); + +char * PrintIP4B +( + // print in A-notation + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +); + +//----------------------------------------------------------------------------- +// print in C-notation (usual notation) + +char * PrintIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +); + +char * PrintLeftIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +); + +char * PrintRightIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port, // 0..0xffff: add ':port' + uint port_mode // 0: 'ip:port', 1: 'ip :port', 2: 'ip : port' +); + +char * PrintAlignedIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +); + +//----------------------------------------------------------------------------- +struct sockaddr_in; + +static inline char * PrintIP4sa +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + + const struct sockaddr_in *sa // socket address +) +{ + DASSERT(sa); + return PrintIP4(buf,buf_size, + ntohl(sa->sin_addr.s_addr), ntohs(sa->sin_port) ); +} + +static inline char * PrintRightIP4sa +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + + struct sockaddr_in *sa // socket address +) +{ + DASSERT(sa); + return PrintRightIP4(buf,buf_size, + ntohl(sa->sin_addr.s_addr), ntohs(sa->sin_port), 0 ); +} + +static inline char * PrintAlignedIP4sa +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + + struct sockaddr_in *sa // socket address +) +{ + DASSERT(sa); + return PrintAlignedIP4(buf,buf_size, + ntohl(sa->sin_addr.s_addr), ntohs(sa->sin_port) ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dynamic data support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[DynData_t]] + +typedef struct DynData_t +{ + u8 *data; // pointer to data, alloced + uint len; // length of used data + uint size; // size of allocated data +} +DynData_t; + +//----------------------------------------------------------------------------- + +void ResetDD ( DynData_t *dd ); +void InsertDD ( DynData_t *dd, const void *data, uint data_size ); + +/////////////////////////////////////////////////////////////////////////////// +// [[DynDataList_t]] + +typedef struct DynDataList_t +{ + DynData_t *list; // pointer to the data list + uint used; // number of used data elements + uint size; // number of allocated pointer in 'list' +} +DynDataList_t; + +//----------------------------------------------------------------------------- + +void ResetDDL ( DynDataList_t *ddl ); + +DynData_t *InsertDDL +( + DynDataList_t *ddl, // valid dynamic data list + uint index, // element index to change + const void *data, // NULL or data to assign + uint data_size // size of data +); + +DynData_t *GetDDL +( + DynDataList_t *ddl, // valid dynamic data list + uint index, // element index to get + uint create // >0: create an empty element if not exist + // >index: create at least # elements +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct StringField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[StringField_t]] + +typedef struct StringField_t +{ + ccp * field; // pointer to the string field + uint used; // number of used titles in the title field + uint size; // number of allocated pointer in 'field' + int (*func_cmp)( ccp s1, ccp s2 ); // compare function, default is strcmp() + +} StringField_t; + +//----------------------------------------------------------------------------- + +static inline void InitializeStringField ( StringField_t * sf ) + { DASSERT(sf); memset(sf,0,sizeof(*sf)); } + +void ResetStringField ( StringField_t * sf ); +void MoveStringField ( StringField_t * dest, StringField_t * src ); + +int FindStringFieldIndex ( const StringField_t * sf, ccp key, int not_found_value ); +ccp FindStringField ( const StringField_t * sf, ccp key ); + +// return: true if item inserted/deleted +bool InsertStringField ( StringField_t * sf, ccp key, bool move_key ); +bool RemoveStringField ( StringField_t * sf, ccp key ); +uint RemoveStringFieldByIndex ( StringField_t * sf, int index, int n ); + +// helper function +ccp * InsertStringFieldHelper ( StringField_t * sf, int idx ); + +// append at the end an do not sort +void AppendStringField ( StringField_t * sf, ccp key, bool move_key ); + +// re-sort field using sf->func_cmp() +void SortStringField ( StringField_t * sf ); + +// return the index of the (next) item +uint FindStringFieldHelper ( const StringField_t * sf, bool * found, ccp key ); + +// find first pattern that matches +ccp MatchStringField ( const StringField_t * sf, ccp key ); + +//----------------------------------------------------------------------------- +// file support + +enumError LoadStringField +( + StringField_t * sf, // string field + bool init_sf, // true: initialize 'sf' first + bool sort, // true: sort 'sf' + ccp filename, // filename of source file + bool silent // true: don't print open/read errors +); + +enumError SaveStringField +( + StringField_t * sf, // valid string field + ccp filename, // filename of dest file + bool rm_if_empty // true: rm dest file if 'sf' is empty +); + +enumError WriteStringField +( + FILE * f, // open destination file + ccp filename, // NULL or filename (needed on write error) + StringField_t * sf, // valid string field + ccp prefix, // not NULL: insert prefix before each line + ccp eol // end of line text (if NULL: use LF) +); + +//----------------------------------------------------------------------------- +// save-state support + +void SaveCurrentStateStringField +( + FILE *f, // output file + ccp name_prefix, // NULL or prefix of name, up to 40 chars + uint tab_pos, // tab pos of '=' + const StringField_t *sf, // valid string field + EncodeMode_t emode // encoding mode +); + +struct RestoreState_t; +void RestoreStateStringField +( + struct RestoreState_t *rs, // info data, may be modified + ccp name_prefix, // NULL or prefix of name, up to 40 chars + StringField_t *sf, // string field + bool init_sf, // true: initialize 'sf', otherwise reset + EncodeMode_t emode // encoding mode +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct ParamField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ParamFieldItem_t]] + +typedef struct ParamFieldItem_t +{ + ccp key; // string as main key + uint num; // user defined numeric parameter + void *data; // user defined pointer + +} ParamFieldItem_t; + +//----------------------------------------------------------------------------- +// [[ParamField_t]] + +typedef struct ParamField_t +{ + ParamFieldItem_t * field; // pointer to the string field + uint used; // number of used titles in the title field + uint size; // number of allocated pointer in 'field' + bool free_data; // true: unused data will be free'd + // initialized with 'false' + int (*func_cmp)( ccp s1, ccp s2 ); // compare function, default is strcmp() +} ParamField_t; + +//----------------------------------------------------------------------------- + +void InitializeParamField ( ParamField_t * pf ); +void ResetParamField ( ParamField_t * pf ); +void MoveParamField ( ParamField_t * dest, ParamField_t * src ); + +int FindParamFieldIndex ( const ParamField_t * pf, ccp key, int not_found_value ); +ParamFieldItem_t * FindParamField ( const ParamField_t * pf, ccp key ); +ParamFieldItem_t * FindParamFieldByNum ( const ParamField_t * pf, uint num ); +ParamFieldItem_t * FindInsertParamField + ( ParamField_t * pf, ccp key, bool move_key, bool *old_found ); +ParamFieldItem_t * CountParamField ( ParamField_t * pf, ccp key, bool move_key ); + +// return: true if item inserted/deleted +bool InsertParamField ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ); +bool ReplaceParamField( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ); +bool RemoveParamField ( ParamField_t * pf, ccp key ); + +ParamFieldItem_t * InsertParamFieldEx + ( ParamField_t * pf, ccp key, bool move_key, uint num, bool *found ); + +// append at the end an do not sort +ParamFieldItem_t * AppendParamField + ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ); + +// return the index of the (next) item +uint FindParamFieldHelper ( const ParamField_t * pf, bool * found, ccp key ); + +// find first pattern that matches +ParamFieldItem_t * MatchParamField ( const ParamField_t * pf, ccp key ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Memory Map /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MemMapItem_t]] + +typedef struct MemMapItem_t +{ + u64 off; // offset + u64 size; // size + u8 overlap; // system info: item overlaps other items + u8 index; // user defined index + char info[62]; // user defined info text + +} MemMapItem_t; + +//----------------------------------------------------------------------------- +// [[MemMap_t]] + +typedef struct MemMap_t +{ + MemMapItem_t ** field; // pointer to the item field + uint used; // number of used titles in the item field + uint size; // number of allocated pointer in 'field' + u64 begin; // first address + +} MemMap_t; + +//----------------------------------------------------------------------------- +// Memory maps allow duplicate entries. +// The off+size pair is used as key. +// The entries are sorted by off and size (small values first). +//----------------------------------------------------------------------------- + +void InitializeMemMap ( MemMap_t * mm ); +void ResetMemMap ( MemMap_t * mm ); + +void CopyMemMap +( + MemMap_t * mm1, // merged mem map, not NULL, cleared + const MemMap_t * mm2, // NULL or second mem map + bool use_tie // TRUE: use InsertMemMapTie() +); + +void MergeMemMap +( + MemMap_t * mm1, // merged mem map, not NULL, not cleared + const MemMap_t * mm2, // NULL or second mem map + bool use_tie // TRUE: use InsertMemMapTie() +); + +MemMapItem_t * FindMemMap +( + // returns NULL or the pointer to the one! matched key (={off,size}) + + MemMap_t * mm, + off_t off, + off_t size +); + +uint InsertMemMapIndex +( + // returns the index of the new item + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +); + +MemMapItem_t * InsertMemMap +( + // returns a pointer to a new item (never NULL) + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +); + +MemMapItem_t * InsertMemMapTie +( + // returns a pointer to a new or existing item (never NULL) + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +); + +void InsertMemMapWrapper +( + void * param, // user defined parameter + u64 offset, // offset of object + u64 size, // size of object + ccp info // info about object +); + +// Remove all entires with key. Return number of delete entries +//uint RemoveMemMap ( MemMap_t * mm, off_t off, off_t size ); + +// Return the index of the found or next item +uint FindMemMapHelper ( MemMap_t * mm, off_t off, off_t size ); + +// Calculate overlaps. Return number of items with overlap. +uint CalcOverlapMemMap ( MemMap_t * mm ); + +u64 FindFreeSpaceMemMap +( + // return address or NULL on not-found + + const MemMap_t * mm, + u64 addr_beg, // first possible address + u64 addr_end, // last possible address + u64 size, // minimal size + u64 align, // aligning + u64 *space // not NULL: store available space here +); + +// Print out memory map +void PrintMemMap ( MemMap_t * mm, FILE * f, int indent, ccp info_head ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Memory Map: Scan addresses /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ScanAddr_t]] + +typedef struct ScanAddr_t +{ + uint stat; // 0:invalid, 1:single address, 2:range + uint addr; // address + uint size; // size inf bytes + +} ScanAddr_t; + +//----------------------------------------------------------------------------- + +char * ScanAddress +( + // returns first not scanned char of 'arg' + + ScanAddr_t * result, // result, initialized by ScanAddress() + ccp arg, // format: ADDR | ADDR:END | ADDR#SIZE + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +); + +//----------------------------------------------------------------------------- + +uint InsertAddressMemMap +( + // returns the number of added addresses + + MemMap_t * mm, // mem map pointer + bool use_tie, // TRUE: use InsertMemMapTie() + ccp arg, // comma separated list of addresses + // formats: ADDR | ADDR:END | ADDR#SIZE + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DataBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct DataBuf_t; + +typedef enumError (*DataBufFill_t) +( + // function to fill the buffer + + struct DataBuf_t* db, // valid pointer to related DataBuf_t to fill + uint need, // number of bytes needed => wait for more data + int mode // user mode, null on auto fill +); + +//----------------------------------------------------------------------------- +// [[DataBuf_t]] + +typedef struct DataBuf_t +{ + uint size; // size of buffer + uint used; // number of used bytes + u64 offset; // current file offset, related to 'data' + + u8 *buf; // pointer to buffer, cyclic usage + u8 *buf_end; // pointer to buffer end := buf + size + + u8 *data; // pointer to data + u8 *data_end; // pointer to end of data + + //--- user data + + DataBufFill_t fill_func; // function to fill buffer + void *user_data; // NULL or pointer to user data for 'fill_func' +} +DataBuf_t; + +/////////////////////////////////////////////////////////////////////////////// +// management + +void InitializeDataBuf + ( DataBuf_t *db, uint buf_size, DataBufFill_t func, void *user_data ); +void ResetDataBuf ( DataBuf_t *db ); +void ResizeDataBuf ( DataBuf_t *db, uint new_buf_size ); +void ClearDataBuf ( DataBuf_t *db, uint new_buf_size_if_not_null ); +void EmptyDataBuf ( DataBuf_t *db ); +void PackDataBuf ( DataBuf_t *db ); + +/////////////////////////////////////////////////////////////////////////////// +// debugging + +bool IsValidDataBuf ( DataBuf_t *db ); +bool DumpDataBuf ( FILE *f, uint indent, DataBuf_t *db, bool if_invalid ); + +// fill the buffer with a test pattern: +// '-' for unused bytes +// ">0123456789<" in a longer or shorter form for used bytes +void FillPatternDataBuf ( DataBuf_t *db ); + +/////////////////////////////////////////////////////////////////////////////// +// insert and remove data + +static inline enumError FillDataBuf( DataBuf_t *db, uint need, int mode ) +{ + return db && db->fill_func ? db->fill_func(db,need,mode) : ERR_WARNING; +} + +uint InsertDataBuf +( + // Insert at top of buffer. + // Return the number of inserted bytes + + DataBuf_t *db, // valid pointer to data buffer + const void *data, // data to insert + uint data_size, // size of 'data' + bool all_or_none // true: insert complete data or nothing +); + +uint AppendDataBuf +( + // Append data at end of buffer + // Return the number of inserted bytes + + DataBuf_t *db, // valid pointer to data buffer + const void *data, // data to insert + uint data_size, // size of 'data' + bool all_or_none // true: insert complete data or nothing +); + +/////////////////////////////////////////////////////////////////////////////// + +void * GetPtrDataBuf +( + // Return a pointer to the data and make sure, + // that the data is not wrapped. Return NULL if failed. + + DataBuf_t *db, // valid pointer to data buffer + uint data_size, // number of needed bytes + uint align, // >1: Data alignment is also needed. + // Only 2,4,8,16,... are allowed, but not verified. + // Begin of buffer is always well aligned. + bool drop // true: On success, drop data from buffer + // In real, the data is available and only + // the pointers and counters are updated. +); + +uint CopyDataBuf +( + // Copy data from buffer, but don't remove it + // Return the number of copied bytes + + DataBuf_t *db, // valid pointer to data buffer + void *dest, // destination buffer wit at least 'max_size' space + uint min_read, // return with 0 read bytes if less bytes available + // if 'min_read' > 'size', NULL is returned + uint max_read // try to read upto 'max_read' bytes +); + +uint GetDataBuf +( + // Copy data from buffer and remove it + // Return the number of copied bytes + + DataBuf_t *db, // valid pointer to data buffer + void *dest, // destination buffer wit at least 'max_size' space + uint min_read, // return with 0 read bytes if less bytes available + uint max_read // try to read upto 'max_read' bytes +); + +uint SkipDataBuf +( + // Skip 'skip_size' from input stream. Read more data if needed. + // Return the number of really skiped bytes + // If =frac_next': increment 'src_idx' + + + //--- iteration data + + uint dest_idx; // dest index: (0..dest_size( + uint src_idx; // src index: (0..src_size( + uint src_frac; // (0..frac_next( + + ResizeElement_t // list terminates with n_elem=0 + elem[4]; // ==> max 3 elements are used + +} +ResizeHelper_t; + +//----------------------------------------------------------------------------- + +void InitializeResize +( + ResizeHelper_t *r, // data structure + uint s_size, // size of source + uint d_size // size of dest +); + +bool FirstResize ( ResizeHelper_t *r ); +bool NextResize ( ResizeHelper_t *r ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// restore configuration /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[RestoreStateLog_t]] + +typedef enum RestoreStateLog_t +{ + RSL_WARNINGS = 0x01, // general warnings + RSL_MISSED_NAMES = 0x02, // hints about missed names + RSL_UNUSED_NAMES = 0x04, // hints about unused names + + RSL_ALL = 0x07 // all flags +} +RestoreStateLog_t; + +//----------------------------------------------------------------------------- +// [[RestoreState_t]] + +typedef struct RestoreState_t +{ + ccp sect; // section name + ccp path; // section sub path + int index; // index or -1 + ParamField_t param; // name=value parameters + cvp user_param; // pointer provided as parameter by user + cvp user_info; // pointer to deliver insfos between stages + + RestoreStateLog_t log_mode; // log mode + FILE *log_file; // log file + +} +RestoreState_t; + +//----------------------------------------------------------------------------- + +typedef void (*RestoreStateFunc) +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +); + +//----------------------------------------------------------------------------- + +typedef struct RestoreStateTab_t +{ + ccp name; // name of section + RestoreStateFunc func; // function to call + cvp user_table; // any user provided pointer +} +RestoreStateTab_t; + +/////////////////////////////////////////////////////////////////////////////// + +enumError RestoreState +( + const RestoreStateTab_t + *table, // section table, terminated by name==0 + void *data, // file data, modified, terminated by NULL or LF + uint size, // size of file data + RestoreStateLog_t log_mode, // print warnings (bit field) + FILE *log_file, // error file; if NULL use stderr + cvp user_param // pointer provided by user +); + +/////////////////////////////////////////////////////////////////////////////// + +// print into cyclic buffer +ccp PrintRestoreStateSection ( const RestoreState_t *rs ); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * GetParamField +( + const RestoreState_t *rs, // valid restore-state structure + ccp name // name of member +); + +//----------------------------------------------------------------------------- + +int GetParamFieldINT +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + int not_found // return this value if not found +); + +//----------------------------------------------------------------------------- + +uint GetParamFieldUINT +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + uint not_found // return this value if not found +); + +//----------------------------------------------------------------------------- + +u64 GetParamFieldS64 +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + s64 not_found // return this value if not found +); + +//----------------------------------------------------------------------------- + +u64 GetParamFieldU64 +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + u64 not_found // return this value if not found +); + +//----------------------------------------------------------------------------- + +float GetParamFieldFLOAT +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + float not_found // return this value if not found +); + +//----------------------------------------------------------------------------- + +double GetParamFieldDBL +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + double not_found // return this value if not found +); + +//----------------------------------------------------------------------------- + +long double GetParamFieldLDBL +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + long double not_found // return this value if not found +); + +//----------------------------------------------------------------------------- + +int GetParamFieldBUF +( + // returns >=0: length of read data; -1:nothing done (not_found==NULL) + + char *buf, // buffer to store result + uint buf_size, // size of buffer + + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + EncodeMode_t decode, // decoding mode, fall back to OFF + // supported: STRING, UTF8, BASE64, BASE64X + ccp not_found // not NULL: store this value if not found +); + +//----------------------------------------------------------------------------- + +mem_t GetParamFieldMEM +( + // Returns the decoded 'source'. Result is NULL-terminated. + // It points either to 'buf' or is alloced (on buf==NULL or to less space) + // If alloced (mem.ptr!=buf) => call FreeMem(&mem) + + char *buf, // buffer to store result + uint buf_size, // size of buffer + + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + EncodeMode_t decode, // decoding mode, fall back to OFF + // supported: STRING, UTF8, BASE64, BASE64X + mem_t not_found // not NULL: return this value +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// save + restore config by table /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[SaveRestoreType_t]] + +typedef enum SaveRestoreType_t +{ + SRT__TERM, // list terminator + + SRT_BOOL, // type bool + SRT_UINT, // unsigned int of any size (%llu) + SRT_HEX, // unsigned int of any size as hex (%#llx) + SRT_INT, // signed int of any size (%lld) + SRT_FLOAT, // float (%.8g) or double (%.16g) or long double (%.20g) + SRT_XFLOAT, // float or double or long double as hex-float (%a/%La) + + SRT_STRING_SIZE, // string with max size, var is char[] + SRT_STRING_ALLOC, // alloced string, var is ccp or char* + SRT_MEM, // alloced string, var is mem_t + + SRT__IS_LIST, //----- from here: print lists; also used as separator + + SRT_STRING_FIELD, // var is StringField_t + SRT_PARAM_FIELD, // var is ParamField_t +} +SaveRestoreType_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[SaveRestoreTab_t]] + +typedef struct SaveRestoreTab_t +{ + uint offset; // offset of variable + uint size; // sizeof( var or string ) + ccp name; // name in configuration file + u16 n_elem; // >0: is array with N elements + u8 type; // SaveRestoreType_t + u8 emode; // EncodeMode_t +} +__attribute__ ((packed)) SaveRestoreTab_t; + +//----------------------------------------------------------------------------- + +// At the very beginning of the table, define 'SRT_NAME' by 2 lines: +// #undef SRT_NAME +// #define SRT_NAME name_of_the_struct + +#define DEF_SRT_VAR(v,ne,n,t,e) {offsetof(SRT_NAME,v),sizeof(((SRT_NAME*)0)->v),n,ne,t,e} + +#define DEF_SRT_BOOL(v,n) DEF_SRT_VAR(v,1,n,SRT_BOOL,0) +#define DEF_SRT_UINT(v,n) DEF_SRT_VAR(v,1,n,SRT_UINT,0) +#define DEF_SRT_HEX(v,n) DEF_SRT_VAR(v,1,n,SRT_HEX,0) +#define DEF_SRT_INT(v,n) DEF_SRT_VAR(v,1,n,SRT_INT,0) +#define DEF_SRT_FLOAT(v,n) DEF_SRT_VAR(v,1,n,SRT_FLOAT,0) +#define DEF_SRT_XFLOAT(v,n) DEF_SRT_VAR(v,1,n,SRT_XFLOAT,0) + +#define DEF_SRT_STR_SIZE(v,n,e) DEF_SRT_VAR(v,1,n,SRT_STRING_SIZE,e) +#define DEF_SRT_STR_ALLOC(v,n,e) DEF_SRT_VAR(v,1,n,SRT_STRING_ALLOC,e) +#define DEF_SRT_MEM(v,n,e) DEF_SRT_VAR(v,1,n,SRT_MEM,e) +#define DEF_SRT_STRING_FIELD(v,n,e) DEF_SRT_VAR(v,1,n,SRT_STRING_FIELD,e) +#define DEF_SRT_PARAM_FIELD(v,n,e) DEF_SRT_VAR(v,1,n,SRT_PARAM_FIELD,e) + +#define DEF_SRT_BOOL_N(v,ne,n) DEF_SRT_VAR(v,ne,n,SRT_BOOL,0) +#define DEF_SRT_UINT_N(v,ne,n) DEF_SRT_VAR(v,ne,n,SRT_UINT,0) +#define DEF_SRT_HEX_N(v,ne,n) DEF_SRT_VAR(v,ne,n,SRT_HEX,0) +#define DEF_SRT_INT_N(v,ne,n) DEF_SRT_VAR(v,ne,n,SRT_INT,0) +#define DEF_SRT_FLOAT_N(v,ne,n) DEF_SRT_VAR(v,ne,n,SRT_FLOAT,0) +#define DEF_SRT_XFLOAT_N(v,ne,n) DEF_SRT_VAR(v,ne,n,SRT_XFLOAT,0) + +#define DEF_SRT_ARRAY(v,n,t,e) \ + {offsetof(SRT_NAME,v), sizeof(((SRT_NAME*)0)->v[0]), n, \ + sizeof(((SRT_NAME*)0)->v)/sizeof(*((SRT_NAME*)0)->v), t, e } + +#define DEF_SRT_BOOL_A(v,n) DEF_SRT_ARRAY(v,n,SRT_BOOL,0) +#define DEF_SRT_UINT_A(v,n) DEF_SRT_ARRAY(v,n,SRT_UINT,0) +#define DEF_SRT_HEX_A(v,n) DEF_SRT_ARRAY(v,n,SRT_HEX,0) +#define DEF_SRT_INT_A(v,n) DEF_SRT_ARRAY(v,n,SRT_INT,0) +#define DEF_SRT_FLOAT_A(v,n) DEF_SRT_ARRAY(v,n,SRT_FLOAT,0) +#define DEF_SRT_XFLOAT_A(v,n) DEF_SRT_ARRAY(v,n,SRT_XFLOAT,0) + +#define DEF_SRT_SEPARATOR() {0,0,0,0,SRT__IS_LIST,0} +#define DEF_SRT_COMMENT(c) {0,0,c,0,SRT__IS_LIST,0} +#define DEF_SRT_TERM() {0,0,0,0,SRT__TERM,0} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpStateTable +( + FILE *f, // valid output file + int indent, + const SaveRestoreTab_t + *srt // list of variables +); + +void SaveCurrentStateByTable +( + FILE *f, // valid output file + cvp data0, // valid pointer to source struct + const SaveRestoreTab_t + *srt, // list of variables + ccp prefix, // NULL or prefix for names + uint fw_name // field width of name, 0=AUTO + // tabs are used for multiple of 8 and for AUTO +); + +void RestoreStateByTable +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + void *data0, // valid pointer to destination struct + const SaveRestoreTab_t + *srt, // list of variables + ccp prefix // NULL or prefix for names +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GrowBuffer_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[GrowBuffer_t]] + +typedef struct GrowBuffer_t +{ + // allways: gb->ptr[gb->used] == 0 + + u8 *buf; // NULL or data buffer + uint size; // size of 'buf' + uint grow_size; // >0: auto grow buffer by multiple of this + uint max_size; // >0: max size for auto grow + + u8 *ptr; // pointer to first valid byte + uint used; // number of valid bytes behind 'ptr' + uint max_used; // max 'used' value + + uint disabled; // disable rading from TCP socket +} +GrowBuffer_t; + +/////////////////////////////////////////////////////////////////////////////// + +void LogGrowBuffer +( + FILE *f, // output file + int indent, // indention + const GrowBuffer_t *gb, // valid grow-buffer + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(format,...)' +) +__attribute__ ((__format__(__printf__,4,5))); + +//----------------------------------------------------------------------------- + +void InitializeGrowBuffer ( GrowBuffer_t *gb, uint max_buf_size ); +void ResetGrowBuffer ( GrowBuffer_t *gb ); +uint ClearGrowBuffer ( GrowBuffer_t *gb ); // returns available space +uint PurgeGrowBuffer ( GrowBuffer_t *gb ); // returns available space + +//----------------------------------------------------------------------------- + +uint GetSpaceGrowBuffer +( + // returns the maximum possible space + const GrowBuffer_t *gb // valid grow-buffer object +); + +//----------------------------------------------------------------------------- + +uint PrepareGrowBuffer +( + // returns the number of available space, smaller than or equal 'size' + + GrowBuffer_t *gb, // valid grow-buffer object + uint size, // needed size + bool force // always alloc enough space and return 'size' +); + +uint InsertGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + const void *data, // data to insert + uint size // size of 'data' +); + +static inline uint InsertCharGrowBuffer +( + // returns the number of inserted bytes (0 on fail, 1 else) + + GrowBuffer_t *gb, // valid grow-buffer object + char ch // any character including NULL +) +{ + return InsertGrowBuffer(gb,&ch,1); +} + +static inline uint InsertMemGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + mem_t mem // any data +) +{ + return InsertGrowBuffer(gb,mem.ptr,mem.len); +} + +uint InsertCapNGrowBuffer +( + // insert 'cap_1' or 'cap_n' depending of 'n' and the existence of 'cap_*' + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + int n, // repeat number; if <1: insert nothing + mem_t cap_1, // default for N=1 + mem_t cap_n // default for N>1 +); + +uint WriteCapN +( + // print 'cap_1' or 'cap_n' depending of 'n' and the existence of 'cap_*' + // returns the number of printed bytes + + FILE *f, // valid FILE + int n, // repeat number; if <1: insert nothing + mem_t cap_1, // default for N=1 + mem_t cap_n // default for N>1 +); + +uint InsertCatchedGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + SavedStdFiles_t *ssf // saved output and catched data +); + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintArgGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + ccp format, // format string for vfprintf() + va_list arg // arguments for 'vsnprintf(format,...)' +); + +uint PrintGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + ccp format, // format string for vfprintf() + ... // arguments for 'vsnprintf(format,...)' +) +__attribute__ ((__format__(__printf__,2,3))); + +//----------------------------------------------------------------------------- + +uint ConvertToCrLfGrowBuffer +( + // returns the number of inserted CR bytes + + GrowBuffer_t *gb, // valid grow-buffer object + uint begin // index to begin with conversion +); + +//----------------------------------------------------------------------------- + +uint DropGrowBuffer +( + // returns the number of dropped bytes + + GrowBuffer_t *gb, // valid grow-buffer object + uint size // number of bytes to drop +); + +//----------------------------------------------------------------------------- +#ifndef __APPLE__ + +FILE * OpenFileGrowBuffer +( + // returns a FILE opened by fmemopen() => call CloseFileGrowBuffer() + + GrowBuffer_t *gb, // valid grow-buffer object + uint size // needed size +); + +int CloseFileGrowBuffer +( + // returns the number of wirtten bytes, or -1 on error + GrowBuffer_t *gb, // valid grow-buffer object + FILE *f // FILE returned by OpenFileGrowBuffer() +); + +#endif // !__APPLE__ +//----------------------------------------------------------------------------- + +void SaveCurrentStateGrowBuffer +( + FILE *f, // output file + ccp name_prefix, // NULL or prefix of name + uint tab_pos, // tab pos of '=' + const GrowBuffer_t *gb // valid grow-buffer +); + +void RestoreStateGrowBuffer +( + GrowBuffer_t *gb, // valid grow-buffer + ccp name_prefix, // NULL or prefix of name + RestoreState_t *rs // info data, can be modified (cleaned after call) +); + +//----------------------------------------------------------------------------- + +int WriteDirectGrowBuffer +( + // write() data direct without blocking and without calling any notifier. + // If output buf is not empty or send failed, append the data to 'gb'. + // Returns the number of bytes added to the grow buffer or -1 on error. + // The data is send+stored completely (returns 'size') or not at all + // (returns '0'). -1 is returned if the socket is invalid. + + GrowBuffer_t *gb, // grow buffer to cache data + int fd, // destination file + bool flush_output, // true: try to flush 'gb' before + cvp data, // data to send + uint size, // size of 'data', If NULL && flush: flush only + uint *send_count // not NULL: add num of sent bytes +); + +//----------------------------------------------------------------------------- + +int SendDirectGrowBuffer +( + // send() data direct without blocking and without calling any notifier. + // If output buf is not empty or send failed, append the data to 'gb'. + // Returns the number of bytes added to the grow buffer or -1 on error. + // The data is send+stored completely (returns 'size') or not at all + // (returns '0'). -1 is returned if the socket is invalid. + + GrowBuffer_t *gb, // grow buffer to cache data + int sock, // destination socket + bool flush_output, // true: try to flush 'gb' before + cvp data, // data to send + uint size, // size of 'data', If NULL && flush: flush only + uint *send_count // not NULL: add num of sent bytes +); + +//----------------------------------------------------------------------------- + +static inline int FlushGrowBuffer +( + // Try to flush the bufer using SendDirectGrowBuffer() withour new data. + + GrowBuffer_t *gb, // grow buffer to cache data + int sock, // destination socket + uint *send_count // not NULL: add num of sent bytes +) +{ + return SendDirectGrowBuffer(gb,sock,true,0,0,send_count); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CONVERT_TO_STRING1(a) #a +#define CONVERT_TO_STRING(a) CONVERT_TO_STRING1(a) + +extern int opt_new; // default 0 + +float double2float ( double d ); // reduce precision + +uint CreateUniqueId ( int range ); + +void Sha1Hex2Bin ( sha1_hash_t bin, ccp src, ccp end ); +void Sha1Bin2Hex ( sha1_hex_t hex, cvp bin ); +ccp GetSha1Hex ( cvp bin ); + +/////////////////////////////////////////////////////////////////////////////// + +static uint snprintfS ( char *buf, size_t size, ccp format, ... ) + __attribute__ ((__format__(__printf__,3,4))); + +static inline uint snprintfS ( char *buf, size_t size, ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + const int res = vsnprintf(buf,size,format,arg); + va_end(arg); + + return res < 0 ? 0 : res < size ? res : size - 1; +} + +static char * snprintfE ( char *buf, char *end, ccp format, ... ) + __attribute__ ((__format__(__printf__,3,4))); + +static inline char * snprintfE ( char *buf, char *end, ccp format, ... ) +{ + const int size = end - buf; + DASSERT( size > 0 ); + + va_list arg; + va_start(arg,format); + const int res = vsnprintf(buf,size,format,arg); + va_end(arg); + + return buf + ( res < 0 ? 0 : res < size ? res : size - 1 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_BASICS_H diff --git a/project/dclib/dclib-color.c b/project/dclib/dclib-color.c new file mode 100644 index 0000000..341c915 --- /dev/null +++ b/project/dclib/dclib-color.c @@ -0,0 +1,4351 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +//#include +//#include + +#include "dclib-color.h" +#include "dclib-basics.h" +#include "dclib-utf8.h" + +#ifdef DCLIB_TERMINAL + #include "dclib-terminal.h" +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color modes /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t color_mode_keywords[] = +{ + { COLMD_OFF, "OFF", "NONE", 0 }, + { COLMD_OFF, "NO-COLORS", "NOCOLORS", 0 }, + { COLMD_OFF, "0-COLORS", "0COLORS", 0 }, + { COLMD_AUTO, "AUTO", 0, 0 }, + { COLMD_ON, "ON", 0, 0 }, + { COLMD_8_COLORS, "8-COLORS", "8COLORS", 0 }, + { COLMD_256_COLORS, "256-COLORS", "256COLORS", 0 }, + {0,0,0,0} +}; + +ColorMode_t opt_colorize = COLMD_AUTO; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptColorize ( ColorMode_t *opt, ccp arg, ccp err_prefix ) +{ + if (!opt) + opt = &opt_colorize; + + if (!arg) + { + if ( *opt < COLMD_ON ) + *opt = COLMD_ON; + return 0; + } + + int abbrev; + const KeywordTab_t *key = ScanKeyword(&abbrev,arg,color_mode_keywords); + if (!key) + return PrintKeywordError( color_mode_keywords, arg, abbrev, + err_prefix ? err_prefix : "Option --color: ", + "keyword"); + + *opt = key->id; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetColorModeName ( ColorMode_t col_mode, ccp res_not_found ) +{ + return GetKewordNameById(color_mode_keywords,col_mode,res_not_found); +} + +/////////////////////////////////////////////////////////////////////////////// + +ColorMode_t NormalizeColorMode ( ColorMode_t col_mode, int n_colors ) +{ + // returns COLMD_OFF or COLMD_8_COLORS or COLMD_256_COLORS + + switch (col_mode) + { + case COLMD_OFF: + case COLMD_8_COLORS: + case COLMD_256_COLORS: + return col_mode; + + case COLMD_ON: + return n_colors < 256 ? COLMD_8_COLORS : COLMD_256_COLORS; + + default: + //case COLMD_AUTO: + return n_colors < 8 ? COLMD_OFF + : n_colors < 256 ? COLMD_8_COLORS : COLMD_256_COLORS; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ColorMode_t NormalizeColorModeByTermName +( + ColorMode_t col_mode, // predefind mode + ccp term_name // find "256color" in terminal name. + // if NULL: use getenv("TERM") + // assume 8 or 256 colors! +) +{ + if (!term_name) + term_name = getenv("TERM"); + const int n_colors = term_name && strstr(term_name,"256color") ? 256 : 8; + return NormalizeColorMode(col_mode,n_colors); +} + +/////////////////////////////////////////////////////////////////////////////// + +ColorMode_t GetNextColorMode ( ColorMode_t col_mode ) +{ + switch(col_mode) + { + case COLMD_8_COLORS: return COLMD_256_COLORS; + case COLMD_256_COLORS: return COLMD_OFF; + default: return COLMD_8_COLORS; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ColorMode_t GetPrevColorMode ( ColorMode_t col_mode ) +{ + switch(col_mode) + { + case COLMD_8_COLORS: return COLMD_OFF; + case COLMD_256_COLORS: return COLMD_8_COLORS; + default: return COLMD_256_COLORS; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print lines with autobreak /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PutLines +( + FILE * f, // valid output stream + int indent, // indent of output + int fw, // field width of output + int first_line, // length without prefix of already printed first line + ccp prefix, // NULL or prefix for each line + ccp text, // text to print + ccp eol // End Of Line test. If NULL -> LF +) +{ + DASSERT(f); + DASSERT( indent >= 0 ); + + if (!prefix) + prefix = ""; + if (!eol) + eol = "\n"; + TRACE("PutLines(,%d,%d,%d,%.10s,%.20s)\n",indent,fw,first_line,prefix,text); + fw -= strlen(prefix); + if ( fw < 10 ) + fw = 10; + + ccp prefix1 = ""; + int indent1, fw1; + if ( indent > first_line ) + { + indent1 = indent - first_line; + fw1 = fw - indent; + } + else + { + indent1 = 0; + fw1 = fw - first_line; + } + + fw -= indent; + if ( fw < 20 ) + fw = 20; + + if ( fw1 < 20 ) + { + fputs(eol,f); + indent1 = indent; + fw1 = fw; + prefix1 = prefix; + } + + while ( *text ) + { + // skip blank and control + if ( *text == '\n' ) + { + // don't skip spaces behind a LF ==> needed for tables + while ( *text > 0 && *text < ' ' ) + text++; + } + else + { + // but ignore spaces on an automatic line break + while ( *text > 0 && *text <= ' ' ) + text++; + } + + // setup + ccp start = text, last_blank = text; + ccp max = text + fw1; + + while ( text < max && *text && *text != '\n' ) + { + if ( *text > 0 && *text <= ' ' ) + last_blank = text; + text++; + } + + // set back to last blank + if ( last_blank > start && (u8)*text > ' ' ) + text = last_blank; + + // print out + if ( *text || text > start ) + fprintf(f,"%s%*s%.*s%s", + prefix1, indent1, "", + (int)(text-start), start, eol ); + + // use standard values for next lines + indent1 = indent; + fw1 = fw; + prefix1 = prefix; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintArgLines +( + FILE * f, // valid output stream + int indent, // indent of output + int fw, // field width of output + int first_line, // length without prefix of already printed first line + ccp prefix, // NULL or prefix for each line + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +) +{ + DASSERT(f); + DASSERT(format); + + char buf[10000]; + + va_list arg2; + va_copy(arg2,arg); + int stat = vsnprintf(buf,sizeof(buf),format,arg2); + va_end(arg2); + + if ( stat < sizeof(buf) ) + { + PutLines(f,indent,fw,first_line,prefix,buf,0); + return; + } + + + //--- buffer too small, use dynamic memory + + noPRINT("PrintArgGrowBuffer() -> MALLOC(%u)\n",stat+1); + + char *temp = MALLOC(stat+1); + vsnprintf(temp,stat+1,format,arg); + va_end(arg); + PutLines(f,indent,fw,first_line,prefix,temp,0); + FREE(temp); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintLines +( + FILE * f, // valid output stream + int indent, // indent of output + int fw, // field width of output + int first_line, // length without prefix of already printed first line + ccp prefix, // NULL or prefix for each line + ccp format, // format string for vsnprintf() + ... // arguments for 'vsnprintf(format,...)' +) +{ + DASSERT(f); + DASSERT(format); + + va_list arg; + va_start(arg,format); + PrintArgLines(f,indent,fw,first_line,prefix,format,arg); + va_end(arg); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print colored lines with autobreak /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint PutColoredLines +( + // returns the number of written lines + + FILE * f, // valid output stream + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + int fw, // field width; indent+prefix+eol don't count + ccp prefix, // NULL or prefix for each line + ccp eol, // End Of Line test. If NULL -> LF + ccp text // text to print +) +{ + DASSERT(f); + DASSERT(text); + + + //--- normalize params + + indent = NormalizeIndent(indent); + + if ( fw < 1 || fw > 1000 ) + fw = 1000; + else if ( fw < 20 ) + fw = 20; + + if (!eol) + eol = "\n"; + + if (!prefix) + prefix = ""; + indent += strlen(prefix); + + if (!colset) + colset = GetFileColorSet(f); + + + //--- setup tabs + + u8 is_tab[200]; + memset(is_tab,0,sizeof(is_tab)); + + + //--- prepare output buffer + + char buf[10000]; + char *buf_end = buf + 3*sizeof(buf)/4; + int cont_indent = -1; + uint n_lines = 0, n_space = 0; + uint column = 0; + //ccp col_begin = ""; + + enum { GB_NONE, GB_SIGN, GB_COMMA, GB_SPACE } gb_mode = GB_NONE; + + struct gb_save_t + { + ccp text; // text position before code assignment + char *dest; // destination + ccp col_active; // NULL or active color by last "{name}" + ccp col_embed; // NULL or active color "{name|text}" + }; + struct gb_save_t cur = { text, buf, 0, 0 }; + struct gb_save_t save = cur; // only valid if gb_mode != GB_NONE + + + //--- main loop + + for(;;) + { + #ifdef TEST + if ( n_lines > 1000 ) + return n_lines; + #endif + + cur.text = text; + u32 code = ScanUTF8AnsiChar(&text); + switch(code) + { + case 0: + if (!column) + return n_lines; + // fall through + + case '\r': + if ( *text == '\n' ) + code = *text++; + // fall through + + case '\n': + cur.text = text; + new_line: + *cur.dest = 0; + fprintf(f,"%-*s%s%s%s", indent,prefix, buf, + cur.col_active || cur.col_embed ? colset->reset : "", eol ); + cur.dest = buf; + text = cur.text; + n_lines++; + column = 0; + n_space = 0; + gb_mode = GB_NONE; + + if ( code == '\n' ) + cont_indent = -1; + else if (!code) + return n_lines; + else if ( cont_indent > 0 ) + n_space = cont_indent; + + if ( cur.col_embed ) + cur.dest = StringCopyE(cur.dest,buf+sizeof(buf),cur.col_embed); + else if ( cur.col_active ) + cur.dest = StringCopyE(cur.dest,buf+sizeof(buf),cur.col_active); + break; + + case '{': + if ( *text == '{' ) + { + text++; + goto is_char; + } + else if ( *text == '}' ) + { + cur.dest = StringCopyE(cur.dest,buf+sizeof(buf),colset->reset); + cur.col_active = cur.col_embed = 0; + text++; + } + else + { + ccp name = text; + while ( *text && *text != '|' && *text != '}' ) + text++; + if (!*text) + break; + + ccp col = ""; + char namebuf[20]; + const uint nlen = text - name; + if ( nlen < sizeof(namebuf) ) + { + memcpy(namebuf,name,nlen); + namebuf[nlen] = 0; + col = GetColorByName(colset,namebuf); + } + + if (*col) + { + cur.dest = StringCopyE(cur.dest,buf+sizeof(buf),col); + if ( *text == '}' ) + cur.col_active = col; + else + cur.col_embed = col; + } + else if ( *text == '|' ) + cur.col_embed = ""; // to detect the closing '}' + text++; + } + break; + + case '}': + if (!cur.col_embed) + goto is_char; + cur.col_embed = 0; + cur.dest = StringCopyE(cur.dest,buf+sizeof(buf), + cur.col_active ? cur.col_active : colset->reset ); + break; + + case '\t': + case ' ': + if ( !n_space++ && column ) + { + gb_mode = column < fw/2 ? GB_COMMA : GB_SPACE; + save = cur; + } + if ( code == '\t' ) + { + uint pos; + for ( pos = column + n_space; pos < sizeof(is_tab); pos++ ) + if (is_tab[pos]) + { + n_space = pos - column; + break; + } + } + break; + + case '|': + if ( *text == '|' ) + { + text++; + goto is_char; + } + else if ( *text == '+' ) + { + text++; + uint pos = column + n_space; + if ( pos < sizeof(is_tab) ) + is_tab[pos] = 1; + } + else if ( *text == '-' ) + { + uint pos = column + n_space; + if ( pos < sizeof(is_tab) ) + is_tab[pos] = 0; + } + else if ( *text == '[' ) + { + text++; + memset(is_tab,0,sizeof(is_tab)); + for(;;) + { + while ( *text == ',' ) + text++; + + char *end; + uint num = str2ul(text,&end,10); + if ( text == end ) + break; + text = end; + if ( num < sizeof(is_tab) ) + is_tab[num] = 1; + + if ( *text == '*' ) + { + text++; + uint delta = str2ul(text,&end,10); + if ( text == end ) + break; + if ( delta > 0 ) + for ( ; num < sizeof(is_tab); num += delta ) + is_tab[num] = 1; + } + } + if ( *text == ']' ) + text++; + //HexDump16(stderr,0,0,is_tab,sizeof(is_tab)); + } + else + { + if ( *text == '>' ) + text++; + cont_indent = column + n_space; + if ( cont_indent > fw/2 ) + cont_indent = fw/2; + } + break; + + case '+': + case '-': + case '=': + case '/': + if ( gb_mode <= GB_SIGN ) + { + gb_mode = GB_SIGN; + save = cur; + } + // fall through + + default: + is_char: + column += n_space; + if ( column >= fw ) + { + // auto break line + if ( gb_mode > GB_NONE ) + cur = save; + while ( *cur.text == ' ' ) + cur.text++; + goto new_line; + } + + while ( n_space > 0 ) + *cur.dest++ = ' ', n_space--; + cur.dest = PrintUTF8Char(cur.dest,code); + if ( cur.dest > buf_end ) + goto new_line; // should never happen! + column++; + + if ( gb_mode <= GB_COMMA && ( code == ',' || code == ';' )) + { + gb_mode = GB_COMMA; + save = cur; + save.text++; + } + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintArgColoredLines +( + // returns the number of written lines + + FILE * f, // valid output stream + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + int fw, // field width; indent+prefix+eol don't count + ccp prefix, // NULL or prefix for each line + ccp eol, // End Of Line test. If NULL -> LF + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +) +{ + DASSERT(f); + DASSERT(format); + + char buf[10000]; + + va_list arg2; + va_copy(arg2,arg); + int stat = vsnprintf(buf,sizeof(buf),format,arg2); + va_end(arg2); + + if ( stat < sizeof(buf) ) + return PutColoredLines(f,colset,indent,fw,prefix,eol,buf); + + + //--- buffer too small, use dynamic memory + + noPRINT("PrintArgGrowBuffer() -> MALLOC(%u)\n",stat+1); + + char *temp = MALLOC(stat+1); + stat = vsnprintf(temp,stat+1,format,arg); + const uint res = PutColoredLines(f,colset,indent,fw,prefix,eol,temp); + FREE(temp); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintColoredLines +( + // returns the number of written lines + + FILE * f, // valid output stream + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + int fw, // field width; indent+prefix+eol don't count + ccp prefix, // NULL or prefix for each line + ccp eol, // End Of Line test. If NULL -> LF + ccp format, // format string for vsnprintf() + ... // arguments for 'vsnprintf(format,...)' +) +{ + DASSERT(f); + DASSERT(format); + + va_list arg; + va_start(arg,format); + const uint res + = PrintArgColoredLines(f,colset,indent,fw,prefix,eol,format,arg); + va_end(arg); + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// terminal /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// Sequence counter for output to stdout (and stderr). +// Functions shall increment it on output, so that other functions +// know about a destroyed screen. + +uint stdout_seq_count = 0; + +/////////////////////////////////////////////////////////////////////////////// + +uint opt_width = 0; +uint opt_height = 0; + +int ScanOptWidth ( ccp arg ) +{ + return ERR_OK != ScanSizeOptU32( + &opt_width, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "width", // ccp opt_name + 40, // u64 min + 10000, // u64 max + 1, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ); +} + +int ScanOptHeight ( ccp arg ) +{ + return ERR_OK != ScanSizeOptU32( + &opt_height, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "height", // ccp opt_name + 40, // u64 min + 10000, // u64 max + 1, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +term_size_t GetTermSize ( int default_width, int default_height ) +{ + TRACE("GetTermWidth(%d,%d) opt=%d,%d\n", + default_width, default_height, opt_width, opt_height ); + + term_size_t ts; + if ( opt_width > 0 && opt_height > 0 ) + { + ts.width = opt_width; + ts.height = opt_height; + } + else + { + ts = GetTermSizeFD(STDOUT_FILENO,default_width,default_height); + if ( opt_width > 0 ) + ts.width = opt_width; + if ( opt_height > 0 ) + ts.height = opt_height; + } + + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// + +term_size_t GetTermSizeFD ( int fd, int default_width, int default_height ) +{ + PRINT("GetTermSizeFD(%d,%d,%d)\n",fd,default_width,default_height); + + #ifdef TIOCGSIZE + TRACE(" - have TIOCGSIZE\n"); + #endif + + #ifdef TIOCGWINSZ + TRACE(" - have TIOCGWINSZ\n"); + #endif + + #ifndef WIN_SZS_LIB + if (isatty(fd)) + { + TRACE(" - isatty\n"); + + #ifdef TIOCGSIZE + { + struct ttysize ts; + if ( !ioctl(fd,TIOCGSIZE,&ts)) + { + PRINT(" - TIOCGSIZE = %d*%d\n",ts.ts_cols,ts.ts_lines); + if ( ts.ts_cols > 0 && ts.ts_lines > 0 ) + { + term_size_t tes = {ts.ts_cols,ts.ts_lines}; + return tes; + } + } + } + #endif + + #ifdef TIOCGWINSZ + { + struct winsize ws; + if ( !ioctl(fd,TIOCGWINSZ,&ws)) + { + PRINT(" - TIOCGWINSZ = %d*%d\n",ws.ws_col,ws.ws_row); + if ( ws.ws_col > 0 && ws.ws_row > 0 ) + { + term_size_t ts = {ws.ws_col,ws.ws_row}; + return ts; + } + } + } + #endif + } + #endif + + term_size_t ts = {default_width,default_height}; + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int GetTermWidth ( int default_value, int min_value ) +{ + TRACE("GetTermWidth(%d,%d) opt=%d\n",default_value,min_value,opt_width); + + int term_width = opt_width > 0 + ? opt_width + : GetTermWidthFD(STDOUT_FILENO,-1,min_value); + + return term_width > 0 ? term_width : default_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetTermWidthFD ( int fd, int default_value, int min_value ) +{ + TRACE("GetTermWidthFD(%d,%d,%d)\n",fd,default_value,min_value); + + term_size_t ts = GetTermSizeFD(fd,default_value,25); + return ts.width > min_value ? ts.width : min_value; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int GetTermHeight ( int default_value, int min_value ) +{ + TRACE("GetTermHeight(%d,%d) opt=%d\n",default_value,min_value,opt_height); + + int term_height = opt_height > 0 + ? opt_height + : GetTermHeightFD(STDOUT_FILENO,-1,min_value); + + return term_height > 0 ? term_height : default_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetTermHeightFD ( int fd, int default_value, int min_value ) +{ + TRACE("GetTermHeightFD(%d,%d,%d)\n",fd,default_value,min_value); + + term_size_t ts = GetTermSizeFD(fd,default_value,25); + return ts.height > min_value ? ts.height : min_value; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +term_size_t auto_term_size = {80,25}; +uint auto_term_resized = 0; // incremented for each resize, init by 0 +uint auto_term_size_dirty = 0; // >0: SIGWINCH received, auto_term_size is dirty + +//----------------------------------------------------------------------------- + +bool GetAutoTermSize() +{ + // return true if changed + + auto_term_size_dirty = 0; + int fd = isatty(1) ? 1 : isatty(0) ? 0 : -1; + if ( fd >= 0 ) + { + term_size_t size + = GetTermSizeFD(fd,auto_term_size.width,auto_term_size.height); + if (memcmp(&auto_term_size,&size,sizeof(size))) + { + auto_term_size = size; + if (!++auto_term_resized) + auto_term_resized++; + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- + +static void Signal_SIGWINCH ( int signal ) +{ + if (!++auto_term_size_dirty) + auto_term_size_dirty++; +} + +void EnableAutoTermSize() +{ + signal(SIGWINCH,Signal_SIGWINCH); + GetAutoTermSize(); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp TermCursorUp = TERM_CURSOR_UP; // move cursor up +ccp TermCursorDown = TERM_CURSOR_DOWN; // move cursor down +ccp TermCursorRight = TERM_CURSOR_RIGHT; // move cursor right +ccp TermCursorLeft = TERM_CURSOR_LEFT; // move cursor left + +ccp TermCursorUpN = TERM_CURSOR_UP_N; // move cursor N* up (format 1 '%d') +ccp TermCursorDownN = TERM_CURSOR_DOWN_N; // move cursor N* down (format 1 '%d') +ccp TermCursorRightN = TERM_CURSOR_RIGHT_N; // move cursor N* right (format 1 '%d') +ccp TermCursorLeftN = TERM_CURSOR_LEFT_N; // move cursor N* left (format 1 '%d') + +ccp TermClearEOL = TERM_CLEAR_EOL; // clear from cursor to end of line +ccp TermClearBOL = TERM_CLEAR_BOL; // clear from beginning of line to cursor +ccp TermClearLine = TERM_CLEAR_LINE; // clear entire line + +ccp TermClearEOD = TERM_CLEAR_EOD; // clear from cursor to end of display +ccp TermClearBOD = TERM_CLEAR_BOD; // clear from beginning of display to cursor +ccp TermClearDisplay = TERM_CLEAR_DISPLAY; // clear Entire display + +ccp TermSetWrap = TERM_SET_WRAP; // enabled line wrapping +ccp TermResetWrap = TERM_RESET_WRAP; // disable line wrapping + +ccp TermTextModeBeg = TERM_TEXT_MODE_BEG; // begin of text mode sequence +ccp TermTextModeEnd = TERM_TEXT_MODE_END; // end of text mode sequence +ccp TermTextModeReset = TERM_TEXT_MODE_RESET; // reset text mode + +static uint grayscale_tab[4] = {0,8,7,15}; + +#if SUPPORT_36_COLORS + + const char ColorIndexName[TCI__N][16] = + { + "black", + "dark_gray_1", + "dark_gray_2", + "dark_gray_3", + "light_gray_1", + "light_gray_2", + "light_gray_3", + "white", + + "red", + "red_orange", + "orange", + "orange_yellow", + "yellow", + "yellow_green", + "green", + "green_cyan", + "cyan", + "cyan_blue", + "blue", + "blue_magenta", + "magenta", + "magenta_red", + + "b_red", + "b_red_orange", + "b_orange", + "b_orange_yellow", + "b_yellow", + "b_yellow_green", + "b_green", + "b_green_cyan", + "b_cyan", + "b_cyan_blue", + "b_blue", + "b_blue_magenta", + "b_magenta", + "b_magenta_red", + }; +#else // !SUPPORT_36_COLORS + + const char ColorIndexName[TCI__N][11] = + { + "black", + "dark gray", + "light gray", + "white", + + "red", + "orange", + "yellow", + "green", + "cyan", + "blue", + "magenta", + + "b_red", + "b_orange", + "b_yellow", + "b_green", + "b_cyan", + "b_blue", + "b_magenta", + }; +#endif // !SUPPORT_36_COLORS + +const int Color18Index[18+1] = // 18 elements + -1 as terminator +{ + TCI_BLACK, // gray scale + TCI_DARKGRAY, + TCI_LIGHTGRAY, + TCI_WHITE, + + TCI_RED, // normal colors in rainbow order + TCI_ORANGE, + TCI_YELLOW, + TCI_GREEN, + TCI_CYAN, + TCI_BLUE, + TCI_MAGENTA, + + TCI_B_RED, // bold colors in rainbow order + TCI_B_ORANGE, + TCI_B_YELLOW, + TCI_B_GREEN, + TCI_B_CYAN, + TCI_B_BLUE, + TCI_B_MAGENTA, + + -1 +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static char * GetColorCircBuf ( uint len ) +{ + static char circ_buf[1024]; + static char *circ_ptr = circ_buf; + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + if ( len > sizeof(circ_buf)/8 ) + return GetCircBuf(len); + + if ( circ_buf + sizeof(circ_buf) - circ_ptr < len ) + circ_ptr = circ_buf; + + char *buf = circ_ptr; + circ_ptr += len; + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + return buf; +} + +//----------------------------------------------------------------------------- + +ccp GetTextMode +( + // returns a pointer to an internal circulary buffer (not GetCircBuf()) + ColorMode_t col_mode, // if < COLMD_ON: return an empty string + TermTextMode_t mode // mode for text generation +) +{ + if ( col_mode < COLMD_ON ) + return EmptyString; + + if ( mode == TTM_RESET ) + return TermTextModeReset; + + uint len = strlen(TermTextModeBeg) + strlen(TermTextModeEnd) + 2; + + if ( mode & TTM_F_COLOR ) + len += 3; + else if ( mode & TTM_F_GRAYSCALE ) + len += 8; + + if ( mode & TTM_F_BG_COLOR ) + len += 3; + else if ( mode & TTM_F_BG_GRAYSCALE ) + len += 8; + + if ( mode & TTM_RESET ) + len += 2; + if ( mode & (TTM_BOLD|TTM_NO_BOLD) ) + len += 3; + if ( mode & (TTM_UL|TTM_NO_UL) ) + len += 3; + + //--- use a private circulary buffer (long term valid) + + char *buf = GetColorCircBuf(len); + + //--- compose string + + uint sep = 0; + char *end = buf + len; + char *dest = StringCopyS(buf,len,TermTextModeBeg); + + if ( mode & TTM_RESET ) + { + *dest++ = '0'; + sep++; + } + + if ( mode & TTM_BOLD ) + { + if (sep++) + *dest++ = ';'; + *dest++ = '1'; + } + else if ( mode & TTM_NO_BOLD ) + { + if (sep++) + *dest++ = ';'; + *dest++ = '2'; + *dest++ = '2'; + } + + if ( mode & TTM_UL ) + { + if (sep++) + *dest++ = ';'; + *dest++ = '4'; + } + else if ( mode & TTM_NO_UL ) + { + if (sep++) + *dest++ = ';'; + *dest++ = '2'; + *dest++ = '4'; + } + + if ( mode & TTM_F_COLOR ) + { + if (sep++) + *dest++ = ';'; + dest = snprintfE(dest,end,"%u",(mode&TTM_M_COLOR)+30); + } + else if ( mode & TTM_F_GRAYSCALE ) + { + if (sep++) + *dest++ = ';'; + dest = snprintfE(dest,end,"38;5;%u", + grayscale_tab[ mode & TTM_M_GRAYSCALE ]); + } + + if ( mode & TTM_F_BG_COLOR ) + { + if (sep++) + *dest++ = ';'; + dest = snprintfE(dest,end,"%u",((mode&TTM_M_BG_COLOR)>>TTM_SHIFT_BG)+40); + } + else if ( mode & TTM_F_BG_GRAYSCALE ) + { + if (sep++) + *dest++ = ';'; + dest = snprintfE(dest,end,"48;5;%u", + grayscale_tab[ (mode & TTM_M_BG_GRAYSCALE) >> TTM_SHIFT_BG ]); + } + + StringCopyE(dest,end,TermTextModeEnd); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SUPPORT_36_COLORS + + ccp GetColorMode // 36 colors version + ( + // returns a pointer to an internal circulary buffer (not GetCircBuf()) + + ColorMode_t col_mode, // if < COLMD_ON: return an empty string + TermColorIndex_t font_index, // index of font color + TermColorIndex_t bg_index, // index of background color + GetColorOption_t option // execution options + ) + { + col_mode = NormalizeColorMode(col_mode,8); + if ( col_mode < COLMD_ON ) + return EmptyString; + + + //--- recommended combinations + + if ( option & GCM_ALT + && (uint)font_index < TCI__IGNORE + && (uint)bg_index < TCI__IGNORE ) + { + static const u8 tab[TCI__N_BG][TCI__N_FONT] = + { + //--- TCI_GRAY0 = TCI_BLACK + { + TCI_GRAY2, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY2, // TCI_GRAY1 + TCI_GRAY3, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_B_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GRAY1 + { + TCI_GRAY3, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY3, // TCI_GRAY1 + TCI_GRAY4, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY5, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_B_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GRAY2 = TCI_DARKGRAY + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY0, // TCI_GRAY1 + TCI_GRAY0, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_B_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GRAY3 + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY0, // TCI_GRAY1 + TCI_GRAY1, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY1, // TCI_GRAY3 + TCI_GRAY5, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_B_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GRAY4 = TCI_LIGHTGRAY + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY1, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY2, // TCI_GRAY3 + TCI_GRAY2, // TCI_GRAY4 + TCI_GRAY6, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GRAY5 = TCI_LIGHTGRAY + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY2, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY3, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY7, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GRAY6 + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY2, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY3, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY4, // TCI_GRAY6 + TCI_GRAY4, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_ORANGE_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GRAY7 = TCI_WHITE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY4, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY5, // TCI_GRAY6 + TCI_GRAY5, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_ORANGE_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_RED + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY1, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_ORANGE, // TCI_RED + TCI_B_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_B_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA_RED + }, + + //--- TCI_RED_ORANGE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY2, // TCI_GRAY3 + TCI_GRAY5, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED, // TCI_RED_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_ORANGE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED, // TCI_RED_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_ORANGE_YELLOW + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY6, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_YELLOW + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY2, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY3, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_YELLOW_GREEN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY6, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GREEN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_GREEN_CYAN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY6, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_CYAN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY6, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_CYAN_BLUE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY1, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY2, // TCI_GRAY3 + TCI_GRAY2, // TCI_GRAY4 + TCI_GRAY6, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_BLUE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY0, // TCI_GRAY1 + TCI_GRAY3, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_B_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_BLUE_MAGENTA + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY0, // TCI_GRAY1 + TCI_GRAY3, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_B_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_MAGENTA + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY1, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED_ORANGE, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_MAGENTA_RED + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY1, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_RED_ORANGE, // TCI_RED + TCI_B_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_B_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_RED + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY1, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY4, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_B_ORANGE, // TCI_RED + TCI_B_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA + TCI_B_MAGENTA, // TCI_MAGENTA_RED + }, + + //--- TCI_B_RED_ORANGE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY2, // TCI_GRAY3 + TCI_GRAY5, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED, // TCI_RED_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_ORANGE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY3, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_ORANGE_YELLOW + { + + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY4, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_YELLOW + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY4, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY5, // TCI_GRAY6 + TCI_GRAY5, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_YELLOW_GREEN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY4, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY5, // TCI_GRAY6 + TCI_GRAY5, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_GREEN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY4, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY5, // TCI_GRAY6 + TCI_GRAY5, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_GREEN_CYAN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY4, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY5, // TCI_GRAY6 + TCI_GRAY5, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_CYAN + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY4, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY5, // TCI_GRAY6 + TCI_GRAY5, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_YELLOW, // TCI_YELLOW + TCI_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_GREEN, // TCI_GREEN + TCI_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_CYAN_BLUE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_BLUE + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_BLUE_MAGENTA + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_B_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_MAGENTA + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED_ORANGE, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + + //--- TCI_B_MAGENTA_RED + { + TCI_GRAY0, // TCI_GRAY0 = TCI_BLACK + TCI_GRAY1, // TCI_GRAY1 + TCI_GRAY2, // TCI_GRAY2 = TCI_DARKGRAY + TCI_GRAY3, // TCI_GRAY3 + TCI_GRAY4, // TCI_GRAY4 + TCI_GRAY5, // TCI_GRAY5 = TCI_LIGHTGRAY + TCI_GRAY6, // TCI_GRAY6 + TCI_GRAY7, // TCI_GRAY7 = TCI_WHITE + + TCI_RED, // TCI_RED + TCI_RED, // TCI_RED_ORANGE + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_ORANGE_YELLOW, // TCI_ORANGE_YELLOW + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_YELLOW_GREEN, // TCI_YELLOW_GREEN + TCI_B_GREEN, // TCI_GREEN + TCI_B_GREEN_CYAN, // TCI_GREEN_CYAN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN_BLUE, // TCI_CYAN_BLUE + TCI_BLUE, // TCI_BLUE + TCI_BLUE_MAGENTA, // TCI_BLUE_MAGENTA + TCI_MAGENTA, // TCI_MAGENTA + TCI_MAGENTA_RED, // TCI_MAGENTA_RED + }, + }; + + const uint font = font_index < TCI_B_RED ? font_index + : font_index - (TCI_B_RED-TCI_RED); + font_index = tab[bg_index][font]; + } + + + //--- color index + + static const u8 color_index_8[] = + { + 0, // TCI_GRAY0 = TCI_BLACK + 0, // TCI_GRAY1 + 8, // TCI_GRAY2 = TCI_DARKGRAY + 8, // TCI_GRAY3 + 7, // TCI_GRAY4 + 7, // TCI_GRAY5 = TCI_LIGHTGRAY + 15, // TCI_GRAY6 + 15, // TCI_GRAY7 = TCI_WHITE + + 1, // TCI_RED + 1, // TCI_RED_ORANGE + 1, // TCI_ORANGE + 1, // TCI_ORANGE_YELLOW + 3, // TCI_YELLOW + 3, // TCI_YELLOW_GREEN + 2, // TCI_GREEN + 2, // TCI_GREEN_CYAN + 6, // TCI_CYAN + 6, // TCI_CYAN_BLUE + 4, // TCI_BLUE + 4, // TCI_BLUE_MAGENTA + 5, // TCI_MAGENTA + 5, // TCI_MAGENTA_RED + + 9, // TCI_B_RED + 9, // TCI_B_RED_ORANGE + 9, // TCI_B_ORANGE + 9, // TCI_B_ORANGE_YELLOW + 11, // TCI_B_YELLOW + 11, // TCI_B_YELLOW_GREEN + 10, // TCI_B_GREEN + 10, // TCI_B_GREEN_CYAN + 14, // TCI_B_CYAN + 14, // TCI_B_CYAN_BLUE + 12, // TCI_B_BLUE + 12, // TCI_B_BLUE_MAGENTA + 13, // TCI_B_MAGENTA + 13, // TCI_B_MAGENTA_RED + }; + + static const u8 color_index_256[] = + { + 232, // TCI_GRAY0 = TCI_BLACK + 236, // TCI_GRAY1 + 240, // TCI_GRAY2 = TCI_DARKGRAY + 243, // TCI_GRAY3 + 246, // TCI_GRAY4 + 249, // TCI_GRAY5 = TCI_LIGHTGRAY + 252, // TCI_GRAY6 + 255, // TCI_GRAY7 = TCI_WHITE + + 160, // TCI_RED + 166, // TCI_RED_ORANGE + 172, // TCI_ORANGE + 136, // TCI_ORANGE_YELLOW + 142, // TCI_YELLOW + 106, // TCI_YELLOW_GREEN + 34, // TCI_GREEN + 36, // TCI_GREEN_CYAN + 37, // TCI_CYAN + 32, // TCI_CYAN_BLUE + 21, // TCI_BLUE + 92, // TCI_BLUE_MAGENTA + 164, // TCI_MAGENTA + 162, // TCI_MAGENTA_RED + + 196, // TCI_B_RED + 202, // TCI_B_RED_ORANGE + 208, // TCI_B_ORANGE + 214, // TCI_B_ORANGE_YELLOW + 226, // TCI_B_YELLOW + 154, // TCI_B_YELLOW_GREEN + 46, // TCI_B_GREEN + 49, // TCI_B_GREEN_CYAN + 51, // TCI_B_CYAN + 75, // TCI_B_CYAN_BLUE + 105, // TCI_B_BLUE + 177, // TCI_B_BLUE_MAGENTA + 207, // TCI_B_MAGENTA + 204, // TCI_B_MAGENTA_RED + }; + + const u8 *color_index; + uint x_bg; + if ( col_mode == COLMD_256_COLORS ) + { + color_index = color_index_256; + x_bg = 8; + } + else + { + color_index = color_index_8; + x_bg = 16; + option |= GCM_SHORT; + } + + + //--- output + + char buf[100], *end = buf + sizeof(buf); + char *dest = StringCopyE(buf,end,TermTextModeBeg); + + const uint bg = (uint)bg_index < TCI__IGNORE ? color_index[bg_index] : ~0; + + ccp sep = ""; + if ( (uint)font_index < TCI__IGNORE ) + { + uint idx = color_index[font_index]; + if ( idx < 16 && idx == (bg&7) && option & GCM_ALT ) + idx = idx ^ 8; + + if ( !(option & GCM_SHORT) || idx >= 16 ) + dest = snprintfE(dest,end,"38;5;%u",idx); + else if ( idx < 8 ) + dest = snprintfE(dest,end,"0;%u",30+idx); + else + dest = snprintfE(dest,end,"1;%u",22+idx); + sep = ";"; + } + + if ( (uint)bg_index < TCI__IGNORE ) + { + if ( !(option & GCM_SHORT) || bg >= x_bg ) + dest = snprintfE(dest,end,"%s48;5;%u",sep,bg); + else + dest = snprintfE(dest,end,"%s%u",sep,40+(bg&7)); + } + + dest = StringCopyE(dest,end,TermTextModeEnd); + const int len = dest + 1 - buf; + char *res = GetColorCircBuf(len); + memcpy(res,buf,len); + return res; + } + +#else // !SUPPORT_36_COLORS + + ccp GetColorMode // 18 colors version + ( + // returns a pointer to an internal circulary buffer (not GetCircBuf()) + + ColorMode_t col_mode, // if < COLMD_ON: return an empty string + TermColorIndex_t font_index, // index of font color + TermColorIndex_t bg_index, // index of background color + GetColorOption_t option // execution options + ) + { + col_mode = NormalizeColorMode(col_mode,8); + if ( col_mode < COLMD_ON ) + return EmptyString; + + + //--- recommended combinations + + if ( option & GCM_ALT + && (uint)font_index < TCI__IGNORE + && (uint)bg_index < TCI__IGNORE ) + { + + static const u8 tab[TCI__N_BG][TCI__N_FONT] = + { + //--- TCI_BLACK + { + TCI_DARKGRAY, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_B_RED, // TCI_RED + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_B_BLUE, // TCI_BLUE + TCI_B_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_DARKGRAY + { + TCI_BLACK, // TCI_BLACK + TCI_LIGHTGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_B_RED, // TCI_RED + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_B_BLUE, // TCI_BLUE + TCI_B_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_LIGHTGRAY + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_DARKGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_GREEN, // TCI_GREEN + TCI_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_WHITE + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_LIGHTGRAY, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_ORANGE, // TCI_YELLOW + TCI_GREEN, // TCI_GREEN + TCI_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_RED + { + TCI_BLACK, // TCI_BLACK + TCI_LIGHTGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_B_RED, // TCI_RED + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_B_BLUE, // TCI_BLUE + TCI_B_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_ORANGE + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_WHITE, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_YELLOW + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_WHITE, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_GREEN + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_CYAN + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_BLUE + { + TCI_BLACK, // TCI_BLACK + TCI_LIGHTGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN, // TCI_BLUE + TCI_B_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_MAGENTA + { + TCI_BLACK, // TCI_BLACK + TCI_LIGHTGRAY, // TCI_DARKGRAY + TCI_LIGHTGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN, // TCI_BLUE + TCI_B_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_B_RED + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_WHITE, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_B_ORANGE + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_WHITE, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_B_YELLOW + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_DARKGRAY, // TCI_LIGHTGRAY + TCI_DARKGRAY, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_YELLOW, // TCI_YELLOW + TCI_GREEN, // TCI_GREEN + TCI_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_B_GREEN + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_DARKGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_GREEN, // TCI_GREEN + TCI_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_B_CYAN + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_DARKGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_ORANGE, // TCI_ORANGE + TCI_YELLOW, // TCI_YELLOW + TCI_GREEN, // TCI_GREEN + TCI_CYAN, // TCI_CYAN + TCI_BLUE, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_B_BLUE + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_DARKGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_B_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + + //--- TCI_B_MAGENTA + { + TCI_BLACK, // TCI_BLACK + TCI_DARKGRAY, // TCI_DARKGRAY + TCI_DARKGRAY, // TCI_LIGHTGRAY + TCI_WHITE, // TCI_WHITE + TCI_RED, // TCI_RED + TCI_B_ORANGE, // TCI_ORANGE + TCI_B_YELLOW, // TCI_YELLOW + TCI_GREEN, // TCI_GREEN + TCI_B_CYAN, // TCI_CYAN + TCI_CYAN, // TCI_BLUE + TCI_MAGENTA, // TCI_MAGENTA + }, + }; + + const uint font = font_index < TCI_B_RED ? font_index + : font_index - (TCI_B_RED-TCI_RED); + font_index = tab[bg_index][font]; + } + + + //--- color index + + static const u8 color_index_8[] = + { + 0, // TCI_BLACK + 8, // TCI_DARKGRAY + 7, // TCI_LIGHTGRAY + 15, // TCI_WHITE + + 1, // TCI_RED + 1, // TCI_ORANGE + 3, // TCI_YELLOW + 2, // TCI_GREEN + 6, // TCI_CYAN + 4, // TCI_BLUE + 5, // TCI_MAGENTA + + 9, // TCI_B_RED + 9, // TCI_B_ORANGE + 11, // TCI_B_YELLOW + 10, // TCI_B_GREEN + 14, // TCI_B_CYAN + 12, // TCI_B_BLUE + 13, // TCI_B_MAGENTA + }; + + static const u8 color_index_256[] = + { + 0, // TCI_BLACK + 8, // TCI_DARKGRAY + 7, // TCI_LIGHTGRAY + 15, // TCI_WHITE + + 160, // TCI_RED + 172, // TCI_ORANGE + 142, // TCI_YELLOW + 34, // TCI_GREEN + 37, // TCI_CYAN + 21, // TCI_BLUE + 164, // TCI_MAGENTA + + 196, // TCI_B_RED + 208, // TCI_B_ORANGE + 226, // TCI_B_YELLOW + 46, // TCI_B_GREEN + 51, // TCI_B_CYAN + 105, // TCI_B_BLUE + 207, // TCI_B_MAGENTA + }; + + const u8 *color_index; + uint x_bg; + if ( col_mode == COLMD_256_COLORS ) + { + color_index = color_index_256; + x_bg = 8; + } + else + { + color_index = color_index_8; + x_bg = 16; + option |= GCM_SHORT; + } + + + //--- output + + char buf[100], *end = buf + sizeof(buf); + char *dest = StringCopyE(buf,end,TermTextModeBeg); + + ccp sep = ""; + if ( (uint)font_index < TCI__IGNORE ) + { + const uint idx = color_index[font_index]; + if ( !(option & GCM_SHORT) || idx >= 16 ) + dest = snprintfE(dest,end,"38;5;%u",idx); + else if ( idx < 8 ) + dest = snprintfE(dest,end,"0;%u",30+idx); + else + dest = snprintfE(dest,end,"1;%u",22+idx); + sep = ";"; + } + + if ( (uint)bg_index < TCI__IGNORE ) + { + const uint idx = color_index[bg_index]; + if ( !(option & GCM_SHORT) || idx >= x_bg ) + dest = snprintfE(dest,end,"%s48;5;%u",sep,idx); + else + dest = snprintfE(dest,end,"%s%u",sep,40+(idx&7)); + } + + dest = StringCopyE(dest,end,TermTextModeEnd); + const int len = dest + 1 - buf; + char *res = GetColorCircBuf(len); + memcpy(res,buf,len); + return res; + } + +#endif // !SUPPORT_36_COLORS + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// colors /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const ColorSet_t * GetColorSet0() +{ + static ColorSet_t col = {0}; + if (!col.col_mode) + { + col.col_mode = COLMD_OFF; + col.colorize = false; + col.n_colors = 0; + + col.space = EmptyString; + col.tab = EmptyString; + col.lf = EmptyString; + + ccp *ptr = &col.reset; + ccp *end = (ccp*)((ccp)&col + sizeof(col)); + while ( ptr < end ) + *ptr++ = EmptyString; + } + return &col; +} + +/////////////////////////////////////////////////////////////////////////////// + +const ColorSet_t * GetColorSet8() +{ + static ColorSet_t col = {0}; + if (!col.col_mode) + { + col.col_mode = COLMD_8_COLORS; + col.colorize = true; + col.n_colors = 8; + + col.space = Space200; + col.tab = Tabs20; + col.lf = LF20; + + col.reset = TermTextModeReset; + col.setup = STRDUP(GetTextMode(1,TTM_COL_SETUP)); + col.run = STRDUP(GetTextMode(1,TTM_COL_RUN)); + col.abort = STRDUP(GetTextMode(1,TTM_COL_ABORT)); + col.finish = STRDUP(GetTextMode(1,TTM_COL_FINISH)); + col.script = STRDUP(GetTextMode(1,TTM_COL_SCRIPT)); + col.open = STRDUP(GetTextMode(1,TTM_COL_OPEN)); + col.close = STRDUP(GetTextMode(1,TTM_COL_CLOSE)); + col.file = STRDUP(GetTextMode(1,TTM_COL_FILE)); + col.job = STRDUP(GetTextMode(1,TTM_COL_JOB)); + col.info = STRDUP(GetTextMode(1,TTM_COL_INFO)); + col.hint = STRDUP(GetTextMode(1,TTM_COL_HINT)); + col.warn = STRDUP(GetTextMode(1,TTM_COL_WARN)); + col.debug = STRDUP(GetTextMode(1,TTM_COL_DEBUG)); + col.log = STRDUP(GetTextMode(1,TTM_COL_LOG)); + col.name = STRDUP(GetTextMode(1,TTM_COL_NAME)); + col.value = STRDUP(GetTextMode(1,TTM_COL_VALUE)); + col.success = STRDUP(GetTextMode(1,TTM_COL_SUCCESS)); + col.error = STRDUP(GetTextMode(1,TTM_COL_ERROR)); + col.fail = STRDUP(GetTextMode(1,TTM_COL_FAIL)); + col.fail2 = STRDUP(GetTextMode(1,TTM_COL_FAIL2)); + col.fatal = STRDUP(GetTextMode(1,TTM_COL_FATAL)); + col.mark = STRDUP(GetTextMode(1,TTM_COL_MARK)); + col.bad = STRDUP(GetTextMode(1,TTM_COL_BAD)); + col.select = STRDUP(GetTextMode(1,TTM_COL_SELECT)); + col.differ = STRDUP(GetTextMode(1,TTM_COL_DIFFER)); + col.stat_line = STRDUP(GetTextMode(1,TTM_COL_STAT_LINE)); + col.warn_line = STRDUP(GetTextMode(1,TTM_COL_WARN_LINE)); + col.proc_line = STRDUP(GetTextMode(1,TTM_COL_PROC_LINE)); + col.cite = STRDUP(GetTextMode(1,TTM_COL_CITE)); + col.status = STRDUP(GetTextMode(1,TTM_COL_STATUS)); + col.highlight = STRDUP(GetTextMode(1,TTM_COL_HIGHLIGHT)); + col.hide = STRDUP(GetTextMode(1,TTM_COL_HIDE)); + col.heading = STRDUP(GetTextMode(1,TTM_COL_HEADING)); + col.caption = STRDUP(GetTextMode(1,TTM_COL_CAPTION)); + col.section = STRDUP(GetTextMode(1,TTM_COL_SECTION)); + col.syntax = STRDUP(GetTextMode(1,TTM_COL_SYNTAX)); + col.cmd = STRDUP(GetTextMode(1,TTM_COL_CMD)); + col.option = STRDUP(GetTextMode(1,TTM_COL_OPTION)); + col.param = STRDUP(GetTextMode(1,TTM_COL_PARAM)); + col.on = STRDUP(GetTextMode(1,TTM_COL_ON)); + col.off = STRDUP(GetTextMode(1,TTM_COL_OFF)); + + col.black = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_BLACK)); + col.b_black = STRDUP(GetTextMode(1,TTM_BOLD|TTM_BLACK)); + col.white = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_WHITE)); + col.b_white = STRDUP(GetTextMode(1,TTM_BOLD|TTM_WHITE)); + + col.red = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_RED)); + col.red_orange = col.red; + col.orange = col.red; + col.yellow = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_YELLOW)); + col.orange_yellow = col.yellow; + col.yellow_green = col.yellow; + col.green = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_GREEN)); + col.green_cyan = col.green; + col.cyan = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_CYAN)); + col.cyan_blue = col.cyan; + col.blue = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_BLUE)); + col.blue_magenta = col.blue; + col.magenta = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_MAGENTA)); + col.magenta_red = col.magenta; + + col.b_red = STRDUP(GetTextMode(1,TTM_BOLD|TTM_RED)); + col.b_red_orange = col.b_red; + col.b_orange = col.b_red; + col.b_yellow = STRDUP(GetTextMode(1,TTM_BOLD|TTM_YELLOW)); + col.b_orange_yellow = col.b_yellow; + col.b_yellow_green = col.b_yellow; + col.b_green = STRDUP(GetTextMode(1,TTM_BOLD|TTM_GREEN)); + col.b_green_cyan = col.b_green; + col.b_cyan = STRDUP(GetTextMode(1,TTM_BOLD|TTM_CYAN)); + col.b_cyan_blue = col.b_cyan; + col.b_blue = STRDUP(GetTextMode(1,TTM_BOLD|TTM_BLUE)); + col.b_blue_magenta = col.b_blue; + col.b_magenta = STRDUP(GetTextMode(1,TTM_BOLD|TTM_MAGENTA)); + col.b_magenta_red = col.b_magenta; + + TermColorIndex_t font, bg; + for ( font = 0; font < TCI__N_FONT; font++ ) + for ( bg = 0; bg < TCI__N_BG; bg++ ) + col.matrix[font][bg] + = STRDUP(GetColorMode(COLMD_8_COLORS,font,bg,GCM_ALT|GCM_SHORT)); + } + + return &col; +} + +/////////////////////////////////////////////////////////////////////////////// + +const ColorSet_t * GetColorSet256() +{ + static ColorSet_t col = {0}; + if (!col.col_mode) + { + //--- default settings (copy of 8-COLORS) + + memcpy(&col,GetColorSet8(),sizeof(col)); + col.col_mode = COLMD_256_COLORS; + col.n_colors = 256; + + + //--- special settings + + col.setup = "\e[38;5;86;40m"; + col.run = "\e[38;5;229;40m"; + col.abort = "\e[38;5;198;40m"; + col.finish = "\e[38;5;154;40m"; + col.script = "\e[0;30;48;5;219m"; + col.open = "\e[38;5;225;40m"; + col.close = "\e[38;5;219;40m"; + col.heading = "\e[38;5;111;40m"; + col.info = "\e[38;5;49;40m"; + col.hint = "\e[38;5;220;40m"; + col.warn = "\e[38;5;208;40m"; + col.debug = "\e[38;5;210;40m"; + col.error = "\e[38;5;255;48;5;130m"; + col.fail = "\e[38;5;251;48;5;88m"; + col.fail2 = "\e[38;5;255;48;5;88m"; + col.fatal = "\e[38;5;255;48;5;129m"; + col.differ = "\e[38;5;226;48;5;19m"; + col.stat_line = "\e[38;5;231;48;5;19m"; + col.warn_line = "\e[38;5;215;48;5;19m"; + col.hide = "\e[38;5;245;40m"; + + + //--- special settings: color names + + // dark: + // ./mkw-ana testcol 400-420 420-330 330-030 030-033 033-005 005-404 404-400 + // ./mkw-ana testcol 400 410 420 320 330 230 030 032 033 024 005 204 404 402 400 + + col.red = "\e[38;5;160m"; + col.red_orange = "\e[38;5;166m"; + col.orange = "\e[38;5;172m"; + col.orange_yellow = "\e[38;5;136m"; + col.yellow = "\e[38;5;142m"; + col.yellow_green = "\e[38;5;106m"; + col.green = "\e[38;5;34m"; + col.green_cyan = "\e[38;5;36m"; + col.cyan = "\e[38;5;37m"; + col.cyan_blue = "\e[38;5;32m"; + col.blue = "\e[38;5;21m"; + col.blue_magenta = "\e[38;5;92m"; + col.magenta = "\e[38;5;164m"; + col.magenta_red = "\e[38;5;162m"; + + // bold: + // ./mkw-ana testcol 500-520 520-550 550-050 050-055 055-225 225-515 515-500 + // ./mkw-ana testcol 500 510 520 530 550 350 050 053 055 135 225 425 515 512 500 + + col.b_red = "\e[38;5;196m"; + col.b_red_orange = "\e[38;5;202m"; + col.b_orange = "\e[38;5;208m"; + col.b_orange_yellow = "\e[38;5;214m"; + col.b_yellow = "\e[38;5;226m"; + col.b_yellow_green = "\e[38;5;154m"; + col.b_green = "\e[38;5;46m"; + col.b_green_cyan = "\e[38;5;49m"; + col.b_cyan = "\e[38;5;51m"; + col.b_cyan_blue = "\e[38;5;75m"; + col.b_blue = "\e[38;5;105m"; + col.b_blue_magenta = "\e[38;5;177m"; + col.b_magenta = "\e[38;5;207m"; + col.b_magenta_red = "\e[38;5;204m"; + + TermColorIndex_t font, bg; + for ( font = 0; font < TCI__N_FONT; font++ ) + for ( bg = 0; bg < TCI__N_BG; bg++ ) + col.matrix[font][bg] + = STRDUP(GetColorMode(COLMD_256_COLORS,font,bg,GCM_ALT|GCM_SHORT)); + } + + return &col; + +//---------- old definitions ---------- +// +// col.differ = STRDUP(GetColorMode(COLMD_256_COLORS, +// TCI_YELLOW, TCI_BLUE, GCM_ALT|GCM_SHORT )); +// col.stat_line = STRDUP(GetColorMode(COLMD_256_COLORS, +// TCI_WHITE, TCI_BLUE, GCM_ALT|GCM_SHORT )); +// col.warn_line = STRDUP(GetColorMode(COLMD_256_COLORS, +// TCI_ORANGE, TCI_BLUE, GCM_ALT|GCM_SHORT )); +// +// col.orange = STRDUP(GetColorMode(COLMD_256_COLORS, +// TCI_ORANGE, TCI__IGNORE, GCM_ALT|GCM_SHORT )); +// col.b_orange = STRDUP(GetColorMode(COLMD_256_COLORS, +// TCI_B_ORANGE, TCI__IGNORE, GCM_ALT|GCM_SHORT )); +// +//------------------------------------- +} + +/////////////////////////////////////////////////////////////////////////////// + +int setupterm(char *term, int fildes, int *errret); +int tigetnum(char *capname); + +const ColorSet_t * GetColorSetAuto ( bool force_on ) +{ + static ColorMode_t auto_mode = COLMD_AUTO; + if ( opt_colorize != COLMD_AUTO ) + { + if ( opt_colorize == COLMD_ON ) + force_on = true; + else + auto_mode = opt_colorize; + } + + if ( auto_mode == COLMD_AUTO ) + { + int error; + setupterm(getenv("TERM"),1,&error); + const int ncol = tigetnum("colors"); + + auto_mode = ncol >= 256 ? COLMD_256_COLORS + : ncol >= 8 ? COLMD_8_COLORS + : COLMD_OFF; + if ( auto_mode == COLMD_OFF && !strcmp(getenv("TERM"),"cygwin") ) + auto_mode = COLMD_8_COLORS; + + #ifdef TEST + fprintf(stderr,">>> GetColorSetAuto(%d) => \"%s\" n=%d => %d [%s]\n", + force_on, getenv("TERM"), ncol, + auto_mode, GetColorModeName(auto_mode,0) ); + #endif + } + + switch(auto_mode) + { + case COLMD_8_COLORS: + return GetColorSet8(); + + case COLMD_256_COLORS: + return GetColorSet256(); + + default: + return force_on ? GetColorSet8() : GetColorSet0(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +const ColorSet_t * GetColorSet ( ColorMode_t col_mode ) +{ + switch(col_mode) + { + case COLMD_AUTO: + return GetColorSetAuto(false); + + case COLMD_ON: + return GetColorSetAuto(true); + + case COLMD_8_COLORS: + return GetColorSet8(); + + case COLMD_256_COLORS: + return GetColorSet256(); + + default: + return GetColorSet0(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +const ColorSet_t * GetFileColorSet ( FILE *f ) +{ + return GetColorSet(GetFileColorized(f)); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SetupColorSet ( ColorSet_t *cs, FILE *f ) +{ + DASSERT(cs); + memcpy(cs,GetColorSet(GetFileColorized(f)),sizeof(*cs)); + return cs->colorize; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetColorByName ( const ColorSet_t *colset, ccp name ) +{ + struct cdef_t + { + u16 delta; + char name[16]; + }; + + #undef DEF1 + #undef DEF2 + #define DEF1(n) { offsetof(ColorSet_t,n), #n }, + #define DEF2(t,n) { offsetof(ColorSet_t,n), #t }, + + static const struct cdef_t tab[] = + { + DEF1(abort) + DEF1(b_black) + DEF1(b_blue) + DEF1(b_blue_magenta) + DEF1(b_cyan) + DEF1(b_cyan_blue) + DEF1(b_green) + DEF1(b_green_cyan) + DEF1(b_magenta) + DEF1(b_magenta_red) + DEF1(b_orange) + DEF1(b_orange_yellow) + DEF1(b_red) + DEF1(b_red_orange) + DEF1(b_white) + DEF1(b_yellow) + DEF1(b_yellow_green) + DEF1(bad) + DEF1(black) + DEF1(blue) + DEF1(blue_magenta) + DEF1(caption) + DEF1(cite) + DEF1(close) + DEF1(cmd) + DEF1(cyan) + DEF1(cyan_blue) + DEF1(debug) + DEF1(differ) + DEF1(error) + DEF1(fail) + DEF1(fail2) + DEF1(fatal) + DEF1(file) + DEF1(finish) + DEF1(green) + DEF1(green_cyan) + DEF2(head,heading) + DEF1(heading) + DEF1(hide) + DEF1(highlight) + DEF1(hint) + DEF2(hl,highlight) + DEF1(info) + DEF1(job) + DEF1(log) + DEF1(magenta) + DEF1(magenta_red) + DEF1(mark) + DEF1(name) + DEF1(off) + DEF1(on) + DEF1(open) + DEF2(opt,option) + DEF1(option) + DEF1(orange) + DEF1(orange_yellow) + DEF2(par,param) + DEF1(param) + DEF1(proc_line) + DEF1(red) + DEF1(red_orange) + DEF1(reset) + DEF1(run) + DEF1(script) + DEF1(section) + DEF1(select) + DEF1(setup) + DEF1(stat_line) + DEF1(status) + DEF1(success) + DEF1(syntax) + DEF2(val,value) + DEF1(value) + DEF1(warn) + DEF1(warn_line) + DEF1(white) + DEF1(yellow) + DEF1(yellow_green) + }; + + #if HAVE_PRINT + static bool done = false; + if (!done) + { + done = true; + uint i, max_len = 0, err_count = 0; + const struct cdef_t *cd = tab; + for ( i = 0; i < sizeof(tab)/sizeof(*tab); i++, cd++ ) + { + if ( max_len < strlen(cd->name) ) + max_len = strlen(cd->name); + + if ( i > 0 && strcmp(cd[-1].name,cd->name) >= 0 ) + { + err_count++; + PRINT("WRONG COL ORDER: %s > %s\n", cd[-1].name, cd->name); + } + } + + DASSERT_MSG( max_len < sizeof(cd->name), + "%s(): max name len = %d, possible = %zd\n", + __FUNCTION__, max_len, sizeof(cd->name)-1 ); + DASSERT_MSG( !err_count, + "%s(): color order is %d* wrong!\n", + __FUNCTION__, err_count ); + } + #endif + + //--- search the name + + if ( colset && name ) + { + int beg = 0; + int end = sizeof(tab)/sizeof(*tab) - 1; + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + const int stat = strcmp(name,tab[idx].name); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + return *(ccp*)((u8*)colset+tab[idx].delta); + } + } + return EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintColorSetHelper +( + FILE *f, // valid output file + int indent, // indention of output + const ColorSet_t *cs, // valid color set; if NULL: use std colors + PrintColorFunc func, // output function, never NULL + uint mode // output mode (bit field, NULL=default): + // 1: print normal colors (e.g. RED) + // 2: print bold colors (e.g. B_RED) + // 4: print background (e.g. BLUE_RED) + // 8: print color names (e.g. HIGHLIGHT) + // 16: include alternative names too (e.g. HL) +) +{ + DASSERT(f); + DASSERT(func); + + if (!cs) + cs = GetColorSet(COLMD_ON); + indent = NormalizeIndent(indent) + 10; // +10 is max name width + if (!mode) + mode = 15; + + #undef JOB1 + #undef JOB2 + #define JOB1(m,n) func(f,indent,cs,m,#n,cs->n) + #define JOB2(m,t,n) func(f,indent,cs,m,#t,cs->n) + + if ( mode & 1 ) + { + func(f,indent,cs,1,0,0); + JOB1(1,black); + JOB1(1,red); + JOB1(1,red_orange); + JOB1(1,orange); + JOB1(1,orange_yellow); + JOB1(1,yellow); + JOB1(1,yellow_green); + JOB1(1,green); + JOB1(1,green_cyan); + JOB1(1,cyan); + JOB1(1,cyan_blue); + JOB1(1,blue); + JOB1(1,blue_magenta); + JOB1(1,magenta); + JOB1(1,magenta_red); + JOB1(1,white); + } + + if ( mode & 2 ) + { + func(f,indent,cs,2,0,0); + JOB1(2,b_black); + JOB1(2,b_red); + JOB1(2,b_red_orange); + JOB1(2,b_orange); + JOB1(2,b_orange_yellow); + JOB1(2,b_yellow); + JOB1(2,b_yellow_green); + JOB1(2,b_green); + JOB1(2,b_green_cyan); + JOB1(2,b_cyan); + JOB1(2,b_cyan_blue); + JOB1(2,b_blue); + JOB1(2,b_blue_magenta); + JOB1(2,b_magenta); + JOB1(2,b_magenta_red); + JOB1(2,b_white); + } + + if ( mode & 8 ) + { + const bool alt = ( mode & 16 ) != 0; + func(f,indent,cs,8,0,0); + + JOB1(4,setup); + JOB1(4,run); + JOB1(4,abort); + JOB1(4,finish); + JOB1(4,script); + JOB1(4,open); + JOB1(4,close); + JOB1(4,file); + JOB1(4,job); + + JOB1(4,info); + JOB1(4,hint); + JOB1(4,warn); + JOB1(4,debug); + JOB1(4,log); + + JOB1(4,name); + JOB1(4,value); if (alt) JOB2(12,val,value); + JOB1(4,success); + JOB1(4,error); + JOB1(4,fail); + JOB1(4,fail2); + JOB1(4,fatal); + JOB1(4,mark); + JOB1(4,bad); + + JOB1(4,select); + JOB1(4,differ); + JOB1(4,stat_line); + JOB1(4,warn_line); + JOB1(4,proc_line); + JOB1(4,cite); + JOB1(4,status); + JOB1(4,highlight); if (alt) JOB2(12,hl,highlight); + JOB1(4,hide); + + JOB1(4,heading); if (alt) JOB2(12,head,heading); + JOB1(4,caption); + JOB1(4,section); + JOB1(4,syntax); + JOB1(4,cmd); + JOB1(4,option); if (alt) JOB2(12,opt,option); + JOB1(4,param); if (alt) JOB2(12,par,param); + + JOB1(4,off); + JOB1(4,on); + } + + #undef JOB1 + #undef JOB2 +} + +/////////////////////////////////////////////////////////////////////////////// + +static void PrintColorSetCOL +( + // On start of group: col_name := NULL, col_string := NULL + + FILE *f, // valid output file + int indent, // normalized indention of output + const ColorSet_t *cs, // valid color set, never NULL + uint mode, // output mode of PrintColorSetHelper() + ccp col_name, // name of color + ccp col_string // escape string for the color +) +{ + if (!col_name) + fputc('\n',f); + else + { + DASSERT(col_string); + #ifdef TEST + fprintf(f,"%*s %s %s %s | %s %s %s\n", \ + indent, col_name, col_string, col_name, cs->reset, + GetColorByName(cs,col_name), col_name, cs->reset ); + #else + fprintf(f,"%*s %s %s %s\n", + indent, col_name, col_string, col_name, cs->reset ); + #endif + } +} + +//----------------------------------------------------------------------------- + +static void PrintColorSetSH +( + // On start of group: col_name := NULL, col_string := NULL + + FILE *f, // valid output file + int indent, // normalized indention of output + const ColorSet_t *cs, // valid color set, never NULL + uint mode, // output mode of PrintColorSetHelper() + ccp col_name, // name of color + ccp col_string // escape string for the color +) +{ + if (col_name) + { + DASSERT(col_string); + + char name[50], buf[50]; + char *dest = name; + while ( *col_name && dest < name + sizeof(name) - 1 ) + *dest++ = toupper((int)*col_name++); + *dest = 0; + + PrintEscapedString(buf,sizeof(buf),col_string,-1,CHMD_ESC,0,0); + if (memcmp(buf,"\\x1B",4)) + fprintf(f,"COL_%s=$'%s'\n",name,buf); + else + fprintf(f,"COL_%s='\\033%s'\n",name,buf+4); + } +} + +//----------------------------------------------------------------------------- + +static void PrintColorSetPHP +( + // On start of group: col_name := NULL, col_string := NULL + + FILE *f, // valid output file + int indent, // normalized indention of output + const ColorSet_t *cs, // valid color set, never NULL + uint mode, // output mode of PrintColorSetHelper() + ccp col_name, // name of color + ccp col_string // escape string for the color +) +{ + if (col_name) + { + DASSERT(col_string); + + char buf[50]; + PrintEscapedString(buf,sizeof(buf),col_string,-1,CHMD_ESC,0,0); + if (memcmp(buf,"\\x1B",4)) + fprintf(f,"$COL->%-15s = \"%s\";\n",col_name,buf); + else + fprintf(f,"$COL->%-15s = \"\\033%s\";\n",col_name,buf+4); + } +} + +//----------------------------------------------------------------------------- + +void PrintColorSet +( + FILE *f, // valid output file + int indent, // indention of output + const ColorSet_t *cs // valid color set; if NULL: use std colors +) +{ + DASSERT(f); + PrintColorSetHelper(f,indent,cs,PrintColorSetCOL,4); + fputc('\n',f); +} + +//----------------------------------------------------------------------------- + +void PrintColorSetEx +( + FILE *f, // valid output file + int indent, // indention of output + const ColorSet_t *cs, // valid color set; if NULL: use std colors + uint mode, // output mode => see PrintColorSetHelper() + uint format // output format + // 0: colored list + // 1: shell definitions + // 2: php class +) +{ + DASSERT(f); + char buf[50]; + + switch(format) + { + case 1: + if (!cs) + cs = GetColorSet(COLMD_ON); + PrintColorSetHelper(f,indent,cs,PrintColorSetSH,mode); + PrintEscapedString(buf,sizeof(buf),cs->reset,-1,CHMD_ESC,0,0); + if (memcmp(buf,"\\x1B",4)) + fprintf(f,"COL0=$'%s'\n",buf); + else + fprintf(f,"COL0=$'\\033%s'\n",buf+4); + break; + + case 2: + if (!cs) + cs = GetColorSet(COLMD_ON); + PrintColorSetHelper(f,indent,cs,PrintColorSetPHP,mode); + break; + + default: + PrintColorSetHelper(f,indent,cs,PrintColorSetCOL,mode); + fputc('\n',f); + break; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ViewColor*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupColorView ( ColorView_t *cv ) +{ + DASSERT(cv); + + if (!cv->f) + cv->f = stdout; + cv->indent = NormalizeIndent(cv->indent); + +#ifdef DCLIB_TERMINAL + cv->col_mode = cv->colset + ? cv->colset->col_mode + : NormalizeColorMode(cv->col_mode, + cv->term ? cv->term->colors : 8 ); +#else + cv->col_mode = cv->colset + ? cv->colset->col_mode + : NormalizeColorMode(cv->col_mode,8); +#endif + if (!cv->colset) + cv->colset = GetColorSet(cv->col_mode); + + switch(cv->order) + { + case 'r': case 'R': cv->order = 0; break; + case 'g': case 'G': cv->order = 1; break; + case 'b': case 'B': cv->order = 2; break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupColorViewMode ( ColorView_t *cv, ColorMode_t col_mode ) +{ + DASSERT(cv); + cv->col_mode = col_mode; + cv->colset = 0; + SetupColorView(cv); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp col_names[] = +{ + "black:", + "red:", + "green:", + "yellow:", + "blue:", + "magenta:", + "cyan:", + "white:", + 0 +}; + +//----------------------------------------------------------------------------- + +void ViewColorsAttrib8 ( ColorView_t *cv ) +{ + DASSERT(cv); + SetupColorView(cv); + DASSERT(cv->colset); + + #ifdef TEST + fprintf(cv->f,"%*s%s() %s, %u colors, mode %d, stdcol %d, order %d\n", + cv->indent,"", __FUNCTION__, + GetColorModeName(cv->colset->col_mode,0), + cv->colset->n_colors, cv->mode, cv->std_col, cv->order ); + #endif + + //-------------------------------------------------- + + fprintf(cv->f, + "%s\n%*sStandard font colors with attributes (ul=underline) for mode %s:\n\n", + cv->colset->reset, cv->indent, "", + GetColorModeName(cv->col_mode,0) ); + + uint fg = 0, bg = 0; + switch( (int)cv->std_col ) + { + case TCI_BLACK: + case TCI_DARKGRAY: fg = TTM_WHITE|TTM_BOLD; bg = TTM_BG_BLACK; break; + case TCI_LIGHTGRAY: + case TCI_WHITE: fg = TTM_BLACK; bg = TTM_BG_WHITE; break; + case TCI_RED: + case TCI_B_RED: + case TCI_ORANGE: + case TCI_B_ORANGE: fg = TTM_WHITE|TTM_BOLD; bg = TTM_BG_RED; break; + case TCI_YELLOW: + case TCI_B_YELLOW: fg = TTM_BLACK; bg = TTM_BG_YELLOW; break; + case TCI_GREEN: + case TCI_B_GREEN: fg = TTM_WHITE|TTM_BOLD; bg = TTM_BG_GREEN; break; + case TCI_CYAN: + case TCI_B_CYAN: fg = TTM_BLACK; bg = TTM_BG_CYAN; break; + case TCI_BLUE: + case TCI_B_BLUE: fg = TTM_WHITE|TTM_BOLD; bg = TTM_BG_BLUE; break; + case TCI_MAGENTA: + case TCI_B_MAGENTA: fg = TTM_WHITE|TTM_BOLD; bg = TTM_BG_MAGENTA; break; + } + + ccp *cname; + TermTextMode_t mode = TTM_BLACK; + for ( cname = col_names; *cname; cname++, mode += 0x01 ) + { + fprintf(cv->f,"%*s%s %u %-9s %snormal %sbold %sbold+ul %sul%s reset %s\n", + cv->indent, "", + GetTextMode(cv->col_mode,fg|bg), + (int)(cname - col_names) + 30, + *cname, + GetTextMode(cv->col_mode,bg|mode), + GetTextMode(cv->col_mode,bg|TTM_BOLD), + GetTextMode(cv->col_mode,bg|TTM_UL), + GetTextMode(cv->col_mode,bg|TTM_NO_BOLD), + GetTextMode(cv->col_mode,bg|TTM_RESET), + cv->colset->reset ); + } + + //-------------------------------------------------- + + fprintf(cv->f, + "\n%*sStandard background colors with attributes (ul=underline) for mode %s:\n\n", + cv->indent, "", GetColorModeName(cv->col_mode,0) ); + + mode = TTM_BG_BLACK; + for ( cname = col_names; *cname; cname++, mode += 0x10 ) + { + fprintf(cv->f,"%*s %u %-9s %s white %s bold %sul%s black %sbold %sul%s %s\n", + cv->indent, "", + (int)(cname - col_names) + 40, + *cname, + GetTextMode(cv->col_mode,mode|TTM_WHITE), + GetTextMode(cv->col_mode,TTM_BOLD), + GetTextMode(cv->col_mode,TTM_UL|TTM_NO_BOLD), + GetTextMode(cv->col_mode,mode|TTM_BLACK|TTM_RESET), + GetTextMode(cv->col_mode,TTM_BOLD), + GetTextMode(cv->col_mode,TTM_UL|TTM_NO_BOLD), + GetTextMode(cv->col_mode,TTM_NO_UL), + GetTextMode(cv->col_mode,TTM_RESET) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ViewColorsCombi8 ( ColorView_t *cv ) +{ + DASSERT(cv); + SetupColorView(cv); + DASSERT(cv->colset); + + #ifdef TEST + fprintf(cv->f,"%*s%s() %s, %u colors, mode %d, stdcol %d, order %d\n", + cv->indent,"", __FUNCTION__, + GetColorModeName(cv->colset->col_mode,0), + cv->colset->n_colors, cv->mode, cv->std_col, cv->order ); + #endif + + fprintf(cv->f, + "%s\n%*sFont and background color combinations (no=normal, bo=bold) for mode %s:\n\n", + cv->colset->reset, cv->indent, "", + GetColorModeName(cv->col_mode,0) ); + + uint mode = TTM_BG_BLACK; + ccp *cname; + for ( cname = col_names; *cname; cname++, mode += 0x10 ) + { + fprintf(cv->f,"%*s %u %-9s ", + cv->indent, "", + (int)(cname - col_names) + 40, + *cname ); + + TermTextMode_t mode2; + for ( mode2 = TTM_BLACK; mode2 <= TTM_WHITE; mode2++ ) + fprintf(cv->f,"%s no %sbo ", + GetTextMode(cv->col_mode,mode|mode2|TTM_NO_BOLD), + GetTextMode(cv->col_mode,TTM_BOLD) ); + fprintf(cv->f," %s\n",cv->colset->reset ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ViewColors18 ( ColorView_t *cv ) +{ + DASSERT(cv); + SetupColorView(cv); + DASSERT(cv->colset); + + #ifdef TEST + fprintf(cv->f,"%*s%s() %s, %u colors, mode %d, stdcol %d, order %d\n", + cv->indent,"", __FUNCTION__, + GetColorModeName(cv->colset->col_mode,0), + cv->colset->n_colors, cv->mode, cv->std_col, cv->order ); + #endif + + GetColorOption_t col_option = cv->col_option & ~GCM_ALT; + + fprintf(cv->f, + "%s\n%*s18x18 color table ('0'=std, '1'=optimized%s) for mode %s:\n\n", + cv->colset->reset, cv->indent, "", + col_option & GCM_SHORT ? "; allow short codes" : "", + GetColorModeName(cv->col_mode,0) ); + + const int *pbg; + for ( pbg = Color18Index; *pbg >= 0; pbg++ ) + { + uint bg = *pbg; + if ( bg == TCI_RED || bg == TCI_B_RED ) + fputc('\n',cv->f); + fprintf(cv->f,"%*s %-12s ", + cv->indent, "", ColorIndexName[bg] ); + + const int *pfont; + for ( pfont = Color18Index; *pfont >= 0; pfont++ ) + { + uint font = *pfont; + if ( font == TCI_RED || font == TCI_B_RED ) + fprintf(cv->f," %s ",TERM_TEXT_MODE_RESET); + fprintf(cv->f,"%s 0%s1", + GetColorMode(cv->col_mode,font,bg,col_option), + GetColorMode(cv->col_mode,font,bg,col_option|GCM_ALT) ); + } + fprintf(cv->f," %s\n",TERM_TEXT_MODE_RESET); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ViewColorsDC ( ColorView_t *cv, bool optimized ) +{ + DASSERT(cv); + SetupColorView(cv); + DASSERT(cv->colset); + + #ifdef TEST + fprintf(cv->f,"%*s%s() %s, %u colors, mode %d, stdcol %d, order %d\n", + cv->indent,"", __FUNCTION__, + GetColorModeName(cv->colset->col_mode,0), + cv->colset->n_colors, cv->mode, cv->std_col, cv->order ); + #endif + + GetColorOption_t col_option = cv->col_option & ~GCM_ALT; + if (optimized) + col_option |= GCM_ALT; + + fprintf(cv->f, + "%s\n%*s%ux%u color table (%s%s) for mode %s:\n\n", + cv->colset->reset, cv->indent, "", + TCI__N, TCI__N, + optimized ? "optimized" : "standard", + col_option & GCM_SHORT ? "; allow short codes" : "", + GetColorModeName(cv->col_mode,0) ); + + uint bg; + for ( bg = 0; bg < TCI__N; bg++ ) + { + if ( bg == TCI_RED || bg == TCI_B_RED ) + fputc('\n',cv->f); + fprintf(cv->f,"%*s %-15s ", + cv->indent, "", ColorIndexName[bg] ); + + ccp fill = " "; + uint font; + for ( font = 0; font < TCI__N; font++ ) + { + if ( font == TCI_RED || font == TCI_B_RED ) + { + fprintf(cv->f," %s ",TERM_TEXT_MODE_RESET); + fill = " "; + } + fprintf(cv->f,"%s%sx", + GetColorMode(cv->col_mode,font,bg,col_option), fill ); + fill = ""; + } + fprintf(cv->f," %s\n",TERM_TEXT_MODE_RESET); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ViewColors256_helper +( + ColorView_t *cv, // basic data + uint beg, // first color printed + uint end, // first color not printed + uint step // increment +) +{ + if ( cv->colset->n_colors < 8 ) + { + uint i; + for ( i = beg; i < end; i += step ) + fprintf(cv->f," %03u ",i); + } + #if 0 // [[2do]] + else if ( cv->colset->n_colors < 256 ) + { + } + #endif + else + { + const int mode = cv->mode & 1 ? 48 : 38; + + uint i; + for ( i = beg; i < end; i += step ) + fprintf(cv->f,"\e[%u;5;%um %03u ",mode,i,i); + fputs("\e[0m",cv->f); + } +} + +//----------------------------------------------------------------------------- + +void ViewColors256 ( ColorView_t *cv ) +{ + DASSERT(cv); + SetupColorView(cv); + DASSERT(cv->colset); + + #ifdef TEST + fprintf(cv->f,"%*s%s() %s, %u colors, mode %d, stdcol %d, order %d\n", + cv->indent,"", __FUNCTION__, + GetColorModeName(cv->colset->col_mode,0), + cv->colset->n_colors, cv->mode, cv->std_col, cv->order ); + #endif + + fprintf(cv->f, + "%s\n%*s256 colors = 16 std colors + 6x6x6 colors + 24 gray for mode %s:\n", + cv->colset->reset, cv->indent, "", + GetColorModeName(cv->col_mode,0) ); + + int step, inc_horz, inc_vert; + switch (cv->order) + { + case 1: + step = 1; + inc_horz = 6; + inc_vert = 36; + break; + + case 2: + step = 6; + inc_horz = 1; + inc_vert = 36; + break; + + default: + step = 1; + inc_horz = 36; + inc_vert = 6; + break; + } + + //--- 16 standard colors in 2 rows + + fprintf(cv->f,"\n%*s ",cv->indent,""); + ViewColors256_helper(cv,0,8,1); + fprintf(cv->f,"\n%*s ",cv->indent,""); + ViewColors256_helper(cv,8,16,1); + fputs("\e[0m\n\n",cv->f); + + uint bl, num = 16; + + for ( bl = 0; bl < 3; bl++ ) + { + uint i, n = num; + for ( i = 0; i < 6; i++ ) + { + fprintf(cv->f,"%*s ",cv->indent,""); + ViewColors256_helper(cv,n,n+6*step,step); + fputs(" ",cv->f); + + ViewColors256_helper(cv,n+inc_horz,n+inc_horz+6*step,step); + fputc('\n',cv->f); + n += inc_vert; + } + + fputc('\n',cv->f); + num += 2*inc_horz; + } + + fprintf(cv->f,"%*s ",cv->indent,""); + ViewColors256_helper(cv,232,232+12,1); + fprintf(cv->f,"\n%*s ",cv->indent,""); + ViewColors256_helper(cv,232+12,232+24,1); + fputs("\e[0m\n",cv->f); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ViewColorsPredef_helper +( + ColorView_t *cv, // basic data + ccp col_name, // name of color + ccp col_string // escape string for the color +) +{ + DASSERT(cv); + DASSERT(col_string); + + if ( col_string && *col_string ) + fprintf(cv->f,"%*s %s %-15s %s \\e%s\n", + cv->indent, col_name, col_string, col_name, + cv->colset->reset, col_string+1 ); + else + fprintf(cv->f,"%*s %s\n", + cv->indent, col_name, col_name ); +} + +//----------------------------------------------------------------------------- + +void ViewColorsPredef ( ColorView_t *cv, uint mode ) +{ + DASSERT(cv); + SetupColorView(cv); + DASSERT(cv->colset); + + #ifdef TEST + fprintf(cv->f,"%*s%s() %s, %u colors, mode %d, stdcol %d, order %d\n", + cv->indent,"", __FUNCTION__, + GetColorModeName(cv->colset->col_mode,0), + cv->colset->n_colors, cv->mode, cv->std_col, cv->order ); + #endif + + fprintf(cv->f, + "%s\n%*sPredefined%s color names for mode %s:\n\n", + cv->colset->reset, cv->indent, "", + ( mode & 3 ) == 1 ? " semantic" : "", + GetColorModeName(cv->col_mode,0) ); + + cv->indent += 14; + + #undef JOB1 + #undef JOB2 + #define JOB1(n) ViewColorsPredef_helper(cv,#n,cv->colset->n) + #define JOB2(n,c) ViewColorsPredef_helper(cv,#n,cv->colset->c) + + if ( mode & 1 ) + { + const bool alt = cv->mode & 1; + + JOB1(setup); + JOB1(run); + JOB1(abort); + JOB1(finish); + JOB1(script); + JOB1(open); + JOB1(close); + JOB1(file); + JOB1(job); + + JOB1(info); + JOB1(hint); + JOB1(warn); + JOB1(debug); + JOB1(log); + + JOB1(name); + JOB1(value); if (alt) JOB2(val,value); + JOB1(success); + JOB1(error); + JOB1(fail); + JOB1(fail2); + JOB1(fatal); + JOB1(mark); + JOB1(bad); + + JOB1(select); + JOB1(differ); + JOB1(stat_line); + JOB1(warn_line); + JOB1(proc_line); + JOB1(cite); + JOB1(status); + JOB1(highlight); if (alt) JOB2(hl,highlight); + JOB1(hide); + + JOB1(heading); + JOB1(caption); + JOB1(section); + JOB1(syntax); + JOB1(cmd); + JOB1(option); if (alt) JOB2(opt,option); + JOB1(param); if (alt) JOB2(par,param); + + JOB1(off); + JOB1(on); + } + + if ( mode & 2 ) + { + if ( mode & 1 ) + fputc('\n',cv->f); + JOB1(black); + JOB1(b_black); + JOB1(white); + JOB1(b_white); + + if ( !(mode & 1) ) + fputc('\n',cv->f); + JOB1(red); + JOB1(red_orange); + JOB1(orange); + JOB1(orange_yellow); + JOB1(yellow); + JOB1(yellow_green); + JOB1(green); + JOB1(green_cyan); + JOB1(cyan); + JOB1(cyan_blue); + JOB1(blue); + JOB1(blue_magenta); + JOB1(magenta); + JOB1(magenta_red); + JOB1(red); + + if ( !(mode & 1) ) + fputc('\n',cv->f); + JOB1(b_red); + JOB1(b_red_orange); + JOB1(b_orange); + JOB1(b_orange_yellow); + JOB1(b_yellow); + JOB1(b_yellow_green); + JOB1(b_green); + JOB1(b_green_cyan); + JOB1(b_cyan); + JOB1(b_cyan_blue); + JOB1(b_blue); + JOB1(b_blue_magenta); + JOB1(b_magenta); + JOB1(b_magenta_red); + JOB1(b_red); + } + + #undef JOB1 + #undef JOB2 + cv->indent -= 14; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// old color views (wrapper) /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintTextModes +( + FILE *f, // valid output file + int indent, // indention of output + ColorMode_t col_mode // color mode +) +{ + ColorView_t cv; + InitializeColorView(&cv); + cv.f = f; + cv.indent = indent; + cv.col_mode = col_mode; + ViewColorsAttrib8(&cv); + ViewColorsCombi8(&cv); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintColorModes +( + FILE *f, // valid output file + int indent, // indention of output + ColorMode_t col_mode, // color mode -> NormalizeColorMode() + GetColorOption_t option // execution options (GCM_ALT ignored) +) +{ + ColorView_t cv; + InitializeColorView(&cv); + cv.f = f; + cv.indent = indent; + cv.col_mode = col_mode; + cv.col_option = option; + ViewColors18(&cv); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// stdout, stderr, stdlog and colors /////////////// +/////////////////////////////////////////////////////////////////////////////// + +FILE *stdlog = 0; // NULL or logfile +bool close_stdlog = 0; // true: close file behing 'stdlog' + +FILE *stdmsg = 0; // 'stdlog' or 'stdout' or 'stderr' or NULL +FILE *stdwrn = 0; // used by PrintError(), initialized with 'stderr' + +ColorMode_t colorize_stdout = COLMD_AUTO; +ColorMode_t colorize_stderr = COLMD_AUTO; +ColorMode_t colorize_stdlog = COLMD_AUTO; +ColorMode_t colorize_stdmsg = COLMD_AUTO; +ColorMode_t colorize_stdwrn = COLMD_AUTO; + +const ColorSet_t *colout = 0; // color set for 'stdout' +const ColorSet_t *colerr = 0; // color set for 'stderr' +const ColorSet_t *collog = 0; // color set for 'stdlog' +const ColorSet_t *colmsg = 0; // color set for 'stdmsg' +const ColorSet_t *colwrn = 0; // color set for 'stdwrn' + +/////////////////////////////////////////////////////////////////////////////// + +ColorMode_t GetFileColorized ( FILE *f ) +{ + if (!stdmsg) + SetupStdMsg(); + + return !f ? COLMD_OFF + : f == stdlog && colorize_stdlog != COLMD_AUTO ? colorize_stdlog + : f == stdout && colorize_stdout != COLMD_AUTO ? colorize_stdout + : f == stderr && colorize_stderr != COLMD_AUTO ? colorize_stderr + : f == stdmsg && colorize_stdmsg != COLMD_AUTO ? colorize_stdmsg + : f == stdwrn && colorize_stdwrn != COLMD_AUTO ? colorize_stdwrn + : opt_colorize >= COLMD_ON ? opt_colorize + : opt_colorize == COLMD_AUTO && isatty(fileno(f)) + ? COLMD_AUTO + : COLMD_OFF; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupStdMsg() +{ + //--- setup + + static bool active = false; // avoid loops + if (!active) + { + active = true; + + stdwrn = stderr; + colwrn = colerr; + colorize_stdwrn = colorize_stderr; + + if (!colout) + { + colorize_stdout = GetFileColorized(stdout); + colout = GetColorSet(colorize_stdout); + } + + if (!colerr) + { + colorize_stderr = GetFileColorized(stderr); + colerr = GetColorSet(colorize_stderr); + } + + if (!collog) + { + colorize_stdlog = GetFileColorized(stdlog); + collog = GetColorSet(colorize_stdlog); + } + + active = false; + } + + //--- assign to msg + + if (stdlog) + { + stdmsg = stdlog; + colmsg = collog; + colorize_stdmsg = colorize_stdlog; + } + else if (stdout) + { + stdmsg = stdout; + colmsg = colout; + colorize_stdmsg = colorize_stdout; + } + else if (stderr) + { + stdmsg = stderr; + colmsg = colerr; + colorize_stdmsg = colorize_stderr; + } + else + { + stdmsg = 0; + colmsg = GetColorSet0(); + colorize_stdmsg = 0; + } + + noPRINT("SetupStdMsg() : out=%d[%zd], err=%d[%zd], log=%d[%zd], msg=%d[%zd]\n", + colorize_stdout, colout ? strlen(colout->reset) : -1, + colorize_stderr, colerr ? strlen(colerr->reset) : -1, + colorize_stdlog, collog ? strlen(collog->reset) : -1, + colorize_stdmsg, colmsg ? strlen(colmsg->reset) : -1 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenStdLog +( + ccp fname, // If NULL or empty: don't open and use 'fallback' + // If first char == '+': open file in append mode + FILE *fallback // Fall back to this file, if no file name given + // If NULL: just clode 'stdlog'. +) +{ + CloseStdLog(); + + bool append = fname && *fname == '+'; + if (append) + fname++; + + if ( !fname || !*fname ) + { + if (fallback) + { + stdlog = fallback; + colorize_stdlog = GetFileColorized(stdlog); + PRINT("COLORIZE/LOG/FALLBACK: opt=%d, out=%d, err=%d, log=%d\n", + opt_colorize, colorize_stdout, colorize_stderr, colorize_stdlog ); + } + } + else + { + if (!strcmp(fname,"-")) + { + stdlog = stdout; + colorize_stdlog = colorize_stdout; + } + else + { + stdlog = fopen( fname, append ? "ab" : "wb" ); + if (!stdlog) + return ERROR1(ERR_CANT_CREATE, + "Can't open log file (mode=%s): %s\n", + append ? "append" : "create", fname ); + + close_stdlog = true; + colorize_stdlog = GetFileColorized(stdlog); + } + + PRINT("COLORIZE/LOG: opt=%d, out=%d, err=%d, log=%d\n", + opt_colorize, colorize_stdout, colorize_stderr, colorize_stdlog ); + } + + collog = GetColorSet(colorize_stdlog); + SetupStdMsg(); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseStdLog() +{ + if (stdlog) + { + if (close_stdlog) + fclose(stdlog); + else + fflush(stdlog); + stdlog = 0; + } + + close_stdlog = false; + colorize_stdlog = 0; + collog = GetColorSet0(); + + SetupStdMsg(); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveStdFiles ( SavedStdFiles_t *ssf ) +{ + DASSERT(ssf); + + ssf->std_out = stdout; + ssf->std_err = stderr; + ssf->std_log = stdlog; + ssf->std_msg = stdmsg; + ssf->std_wrn = stdwrn; + + ssf->col_out = colout; + ssf->col_err = colerr; + ssf->col_log = collog; + ssf->col_msg = colmsg; + ssf->col_wrn = colwrn; + + ssf->stdout_seq_count = stdout_seq_count; + + ssf->f = 0; + ssf->data = 0; + ssf->size = 0; + ssf->marker = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStdFiles ( SavedStdFiles_t *ssf ) +{ + DASSERT(ssf); + + stdout = ssf->std_out; + stderr = ssf->std_err; + stdlog = ssf->std_log; + stdmsg = ssf->std_msg; + stdwrn = ssf->std_wrn; + + colout = ssf->col_out; + colerr = ssf->col_err; + collog = ssf->col_log; + colmsg = ssf->col_msg; + colwrn = ssf->col_wrn; + + stdout_seq_count = ssf->stdout_seq_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +void RedirectStdFiles +( + SavedStdFiles_t *ssf, // not NULL: save old output here + FILE *f, // use this as out,err,log and msg, never NULL + const ColorSet_t *colset,// new colset; if NULL: use GetFileColorSet(f) + bool err_too // true: redirect stderr too +) +{ + DASSERT(f); + if (ssf) + SaveStdFiles(ssf); + + stdout = stdwrn = stdmsg = f; + colout = colwrn = colmsg = colset ? colset : GetFileColorSet(f); + + // special handling for stdlog + if (stdlog) + { + stdlog = stdout; + collog = colout; + } + + if (err_too) + { + stderr = stdout; + colerr = colout; + } +} + +/////////////////////////////////////////////////////////////////////////////// +#ifndef __APPLE__ + +enumError CatchStdFiles +( + SavedStdFiles_t *ssf, // save old output and data here, never NULL + const ColorSet_t *colset // new colset; if NULL: no color support +) +{ + DASSERT(ssf); + SaveStdFiles(ssf); + FILE *f = open_memstream(&ssf->data,&ssf->size); + if (!f) + return ERR_CANT_CREATE; + + ssf->f = f; + RedirectStdFiles(0,f,colset,false); + return ERR_OK; +} + +#endif // !__APPLE__ +/////////////////////////////////////////////////////////////////////////////// + +void TermCatchStdFiles ( SavedStdFiles_t *ssf ) +{ + DASSERT(ssf); + RestoreStdFiles(ssf); + if (ssf->f) + { + fclose(ssf->f); + ssf->f = 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-color.h b/project/dclib/dclib-color.h new file mode 100644 index 0000000..6591f2e --- /dev/null +++ b/project/dclib/dclib-color.h @@ -0,0 +1,828 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_COLOR_H +#define DCLIB_COLOR_H 1 + +#define _GNU_SOURCE 1 +#include + +#include "dclib-types.h" +#include "dclib-debug.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color modes /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SUPPORT_36_COLORS + #if NEW_FEATURES + #define SUPPORT_36_COLORS 1 + #else + #define SUPPORT_36_COLORS 0 + #endif +#endif + +//----------------------------------------------------------------------------- +// [[ColorMode_t]] + +typedef enum ColorMode_t +{ + COLMD_OFF = -1, // don't support colors + COLMD_AUTO = 0, // auto detect + COLMD_ON, // on, auto select mode + COLMD_8_COLORS, // force 8 color mode + COLMD_256_COLORS, // force 256 color mode +} +ColorMode_t; + +//----------------------------------------------------------------------------- + +struct KeywordTab_t; +extern const struct KeywordTab_t color_mode_keywords[]; +extern ColorMode_t opt_colorize; // <0:no color, 0:auto, >0:force color + +int ScanOptColorize ( ColorMode_t *opt, ccp arg, ccp err_prefix ); +ccp GetColorModeName ( ColorMode_t col_mode, ccp res_not_found ); + +//----------------------------------------------------------------------------- +// returns COLMD_OFF | COLMD_8_COLORS | COLMD_256_COLORS + +ColorMode_t NormalizeColorMode ( ColorMode_t col_mode, int n_colors ); + +ColorMode_t NormalizeColorModeByTermName +( + ColorMode_t col_mode, // predefind mode + ccp term_name // find "256color" in terminal name. + // if NULL: use getenv("TERM") + // assume 8 or 256 colors! +); + +struct TerminalInfo_t; +ColorMode_t NormalizeColorModeByTerm +( + ColorMode_t col_mode, // predefind mode + const struct + TerminalInfo_t *term // if NULL: use getenv("TERM") to find terminal +); + +// iteration +ColorMode_t GetNextColorMode ( ColorMode_t col_mode ); +ColorMode_t GetPrevColorMode ( ColorMode_t col_mode ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// terminal & colors /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern int termios_valid; // valid if >0 + +void ResetTermios(void); +bool EnableSingleCharInput(void); + +/////////////////////////////////////////////////////////////////////////////// + +// Sequence counter for output to stdout (and stderr). +// Functions shall increment it on output. so that other functions +// know about an destroid screen. + +extern uint stdout_seq_count; + +//----------------------------------------------------------------------------- +// [[term_size_t]] + +typedef struct term_size_t +{ + uint width; + uint height; +} +term_size_t; + +extern uint opt_width, opt_height; +int ScanOptWidth ( ccp arg ); +int ScanOptHeight ( ccp arg ); + +term_size_t GetTermSize ( int default_width, int default_height ); +term_size_t GetTermSizeFD ( int fd, int default_width, int default_height ); + +int GetTermWidth ( int default_value, int min_value ); +int GetTermWidthFD ( int fd, int default_value, int min_value ); + +int GetTermHeight ( int default_value, int min_value ); +int GetTermHeightFD ( int fd, int default_value, int min_value ); + +//----------------------------------- + +extern term_size_t auto_term_size; // default = {80,25}; +extern uint auto_term_resized; // incremented for each resize, init by 0 +extern uint auto_term_size_dirty; // >0: SIGWINCH received, auto_term_size is dirty + +bool GetAutoTermSize(void); // return true if changed +void EnableAutoTermSize(void); + +//----------------------------------------------------------------------------- + +//----- some basic terminal codes + +#define TERM_CURSOR_UP "\e[A" // move cursor up +#define TERM_CURSOR_DOWN "\e[B" // move cursor down +#define TERM_CURSOR_RIGHT "\e[C" // move cursor right +#define TERM_CURSOR_LEFT "\e[D" // move cursor left + +#define TERM_CURSOR_UP_N "\e[%dA" // move cursor N* up +#define TERM_CURSOR_DOWN_N "\e[%dB" // move cursor N* down +#define TERM_CURSOR_RIGHT_N "\e[%dC" // move cursor N* right +#define TERM_CURSOR_LEFT_N "\e[%dD" // move cursor N* left + +#define TERM_CLEAR_EOL "\e[K" // clear from cursor to end of line +#define TERM_CLEAR_BOL "\e[1K" // clear from beginning of line to cursor +#define TERM_CLEAR_LINE "\e[2K" // clear entire line + +#define TERM_CLEAR_EOD "\e[J" // clear from cursor to end of display +#define TERM_CLEAR_BOD "\e[1J" // clear from beginning of display to cursor +#define TERM_CLEAR_DISPLAY "\e[2J" // clear Entire display + +#define TERM_SET_WRAP "\e[?7h" // enabled line wrapping +#define TERM_RESET_WRAP "\e[?7l" // disable line wrapping + +#define TERM_TEXT_MODE_BEG "\e[" // begin of text mode sequence +#define TERM_TEXT_MODE_END "m" // end of text mode sequence +#define TERM_TEXT_MODE_RESET "\e[m" // reset text mode + +// pointers are initialized with codes above + +extern ccp TermCursorUp; // move cursor up +extern ccp TermCursorDown; // move cursor down +extern ccp TermCursorRight; // move cursor right +extern ccp TermCursorLeft; // move cursor left + +extern ccp TermCursorUpN; // move cursor N* up (format string with 1 '%d') +extern ccp TermCursorDownN; // move cursor N* down (format string with 1 '%d') +extern ccp TermCursorRightN; // move cursor N* right (format string with 1 '%d') +extern ccp TermCursorLeftN; // move cursor N* left (format string with 1 '%d') + +extern ccp TermClearEOL; // clear from cursor to end of line +extern ccp TermClearBOL; // clear from beginning of line to cursor +extern ccp TermClearLine; // clear entire line + +extern ccp TermClearEOD; // clear from cursor to end of display +extern ccp TermClearBOD; // clear from beginning of display to cursor +extern ccp TermClearDisplay; // clear entire display + +extern ccp TermSetWrap; // enabled line wrapping +extern ccp TermResetWrap; // disable line wrapping + +extern ccp TermTextModeBeg; // begin of text mode sequence +extern ccp TermTextModeEnd; // end of text mode sequence +extern ccp TermTextModeReset; // reset text mode + +//----------------------------------------------------------------------------- +// [[TermTextMode_t]] + +typedef enum TermTextMode_t +{ + //--- standard fg colors: \e[30..37m + + TTM_BLACK = 0x08, + TTM_RED = 0x09, + TTM_GREEN = 0x0a, + TTM_YELLOW = 0x0b, + TTM_BLUE = 0x0c, + TTM_MAGENTA = 0x0d, + TTM_CYAN = 0x0e, + TTM_WHITE = 0x0f, + + + //--- standard bg colors: \e[40..47m + + TTM_BG_BLACK = 0x80, + TTM_BG_RED = 0x90, + TTM_BG_GREEN = 0xa0, + TTM_BG_YELLOW = 0xb0, + TTM_BG_BLUE = 0xc0, + TTM_BG_MAGENTA = 0xd0, + TTM_BG_CYAN = 0xe0, + TTM_BG_WHITE = 0xf0, + + //--- grayscale: \e[48;5;m + + TTM_GRAY0 = 0x04, // black + TTM_GRAY1 = 0x05, // dark gray + TTM_GRAY2 = 0x06, // light gray + TTM_GRAY3 = 0x07, // real white + + TTM_BG_GRAY0 = 0x40, // black + TTM_BG_GRAY1 = 0x50, // dark gray + TTM_BG_GRAY2 = 0x60, // light gray + TTM_BG_GRAY3 = 0x70, // real white + + //--- misc + + TTM_RESET = 0x0100, + TTM_BOLD = 0x0200, + TTM_NO_BOLD = 0x0400, + TTM_UL = 0x0800, + TTM_NO_UL = 0x1000, + + //--- flags and masks ans shifts + + TTM_F_COLOR = 0x08, + TTM_M_COLOR = 0x07, + TTM_F_GRAYSCALE = 0x04, // only valid if !TTM_F_COLOR + TTM_M_GRAYSCALE = 0x03, + + TTM_F_BG_COLOR = 0x80, + TTM_M_BG_COLOR = 0x70, + TTM_F_BG_GRAYSCALE = 0x40, // only valid if !TTM_F_BG_COLOR + TTM_M_BG_GRAYSCALE = 0x30, + + TTM_SHIFT_BG = 4, + + + //--- and her are some recommendations for unique colors + //--- these Colors are also used to setup 'ColorSet_t' + + TTM_COL_SETUP = TTM_BOLD | TTM_BLUE | TTM_BG_BLACK, + TTM_COL_RUN = TTM_BOLD | TTM_WHITE | TTM_BG_BLACK, + TTM_COL_ABORT = TTM_BOLD | TTM_MAGENTA | TTM_BG_BLACK, + TTM_COL_FINISH = TTM_BOLD | TTM_CYAN | TTM_BG_BLACK, + TTM_COL_SCRIPT = TTM_NO_BOLD | TTM_BLACK | TTM_BG_WHITE, + TTM_COL_OPEN = TTM_NO_BOLD | TTM_RED | TTM_BG_BLACK, + TTM_COL_CLOSE = TTM_NO_BOLD | TTM_RED | TTM_BG_BLACK, + TTM_COL_FILE = TTM_NO_BOLD | TTM_YELLOW | TTM_BG_BLACK, + TTM_COL_JOB = TTM_BOLD | TTM_BLUE | TTM_BG_BLACK, + + TTM_COL_INFO = TTM_BOLD | TTM_CYAN | TTM_BG_BLACK, + TTM_COL_HINT = TTM_BOLD | TTM_YELLOW | TTM_BG_BLACK, + TTM_COL_WARN = TTM_BOLD | TTM_RED | TTM_BG_BLACK, + TTM_COL_DEBUG = TTM_BOLD | TTM_RED | TTM_BG_BLACK, + TTM_COL_LOG = TTM_BOLD | TTM_WHITE | TTM_BG_BLUE, + + TTM_COL_NAME = TTM_NO_BOLD | TTM_CYAN | TTM_BG_BLACK, + TTM_COL_VALUE = TTM_NO_BOLD | TTM_GREEN | TTM_BG_BLACK, + TTM_COL_SUCCESS = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, + TTM_COL_ERROR = TTM_NO_BOLD | TTM_BLACK | TTM_BG_YELLOW, + TTM_COL_FAIL = TTM_NO_BOLD | TTM_WHITE | TTM_BG_RED, + TTM_COL_FAIL2 = TTM_BOLD | TTM_WHITE | TTM_BG_RED, + TTM_COL_FATAL = TTM_BOLD | TTM_WHITE | TTM_BG_MAGENTA, + TTM_COL_MARK = TTM_BOLD | TTM_WHITE | TTM_BG_BLACK, + TTM_COL_BAD = TTM_BOLD | TTM_MAGENTA | TTM_BG_BLACK, + + TTM_COL_SELECT = TTM_NO_BOLD | TTM_YELLOW | TTM_BG_BLACK, + TTM_COL_DIFFER = TTM_BOLD | TTM_YELLOW | TTM_BG_BLUE, + TTM_COL_STAT_LINE = TTM_BOLD | TTM_WHITE | TTM_BG_BLUE, + TTM_COL_WARN_LINE = TTM_BOLD | TTM_RED | TTM_BG_BLUE, + TTM_COL_PROC_LINE = TTM_BOLD | TTM_WHITE | TTM_BG_MAGENTA, + TTM_COL_CITE = TTM_NO_BOLD | TTM_GREEN | TTM_BG_BLACK, + TTM_COL_STATUS = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, + TTM_COL_HIGHLIGHT = TTM_BOLD | TTM_YELLOW | TTM_BG_BLACK, + TTM_COL_HIDE = TTM_BOLD | TTM_BLACK | TTM_BG_BLACK, + + TTM_COL_HEADING = TTM_BOLD | TTM_BLUE | TTM_BG_BLACK, + TTM_COL_CAPTION = TTM_BOLD | TTM_CYAN | TTM_BG_BLACK, + TTM_COL_SECTION = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, + TTM_COL_SYNTAX = TTM_BOLD | TTM_WHITE | TTM_BG_BLACK, + TTM_COL_CMD = TTM_NO_BOLD | TTM_CYAN | TTM_BG_BLACK, + TTM_COL_OPTION = TTM_NO_BOLD | TTM_GREEN | TTM_BG_BLACK, + TTM_COL_PARAM = TTM_NO_BOLD | TTM_YELLOW | TTM_BG_BLACK, + + TTM_COL_ON = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, + TTM_COL_OFF = TTM_BOLD | TTM_RED | TTM_BG_BLACK, +} +TermTextMode_t; + +//----------------------------------------------------------------------------- +// [[TermColorIndex_t]] + +typedef enum TermColorIndex_t +{ + #if SUPPORT_36_COLORS + + TCI_GRAY0, // gray scale + TCI_GRAY1, + TCI_GRAY2, + TCI_GRAY3, + TCI_GRAY4, + TCI_GRAY5, + TCI_GRAY6, + TCI_GRAY7, + + TCI_RED, // normal colors in rainbow order + TCI_RED_ORANGE, + TCI_ORANGE, + TCI_ORANGE_YELLOW, + TCI_YELLOW, + TCI_YELLOW_GREEN, + TCI_GREEN, + TCI_GREEN_CYAN, + TCI_CYAN, + TCI_CYAN_BLUE, + TCI_BLUE, + TCI_BLUE_MAGENTA, + TCI_MAGENTA, + TCI_MAGENTA_RED, + + TCI_B_RED, // bold colors in rainbow order + TCI_B_RED_ORANGE, + TCI_B_ORANGE, + TCI_B_ORANGE_YELLOW, + TCI_B_YELLOW, + TCI_B_YELLOW_GREEN, + TCI_B_GREEN, + TCI_B_GREEN_CYAN, + TCI_B_CYAN, + TCI_B_CYAN_BLUE, + TCI_B_BLUE, + TCI_B_BLUE_MAGENTA, + TCI_B_MAGENTA, + TCI_B_MAGENTA_RED, + + TCI__IGNORE, // ignore this param, for GetColorMode() + + //--- alternative names + + TCI_BLACK = TCI_GRAY0, + TCI_DARKGRAY = TCI_GRAY2, + TCI_LIGHTGRAY = TCI_GRAY5, + TCI_WHITE = TCI_GRAY7, + + //--- counters + + TCI__N = TCI__IGNORE, // number of supported colors + TCI__N_FONT = TCI_B_RED, // number of recommended font colors + TCI__N_BG = TCI__N, // number of recommended background colors + + #else // !SUPPORT_36_COLORS + + TCI_BLACK, // gray scale + TCI_DARKGRAY, + TCI_LIGHTGRAY, + TCI_WHITE, + + TCI_RED, // normal colors in rainbow order + TCI_ORANGE, + TCI_YELLOW, + TCI_GREEN, + TCI_CYAN, + TCI_BLUE, + TCI_MAGENTA, + + TCI_B_RED, // bold colors in rainbow order + TCI_B_ORANGE, + TCI_B_YELLOW, + TCI_B_GREEN, + TCI_B_CYAN, + TCI_B_BLUE, + TCI_B_MAGENTA, + + TCI__IGNORE, // ignore this param, for GetColorMode() + + //--- counters + + TCI__N = TCI__IGNORE, // number of supported colors + TCI__N_FONT = TCI_B_RED, // number of recommended font colors + TCI__N_BG = TCI__N, // number of recommended background colors + + #endif // !SUPPORT_36_COLORS +} +TermColorIndex_t; + +#if SUPPORT_36_COLORS + extern const char ColorIndexName[TCI__N][16]; +#else + extern const char ColorIndexName[TCI__N][11]; +#endif + +extern const int Color18Index[18+1]; // 18 elements + -1 as terminator + +//----------------------------------------------------------------------------- +// [[ColorSet_t]] + +typedef struct ColorSet_t +{ + //--- header + + ColorMode_t col_mode; + u8 colorize; + u16 n_colors; + + //--- some strings for formatted output if colors active + + ccp space; // pointer to 'EmptyString' or to 'Space200'; + ccp tab; // pointer to 'EmptyString' or to 'TAB20'; + ccp lf; // pointer to 'EmptyString' or to 'LF20'; + + //--- data, always begins with 'reset' + + ccp reset; + + ccp setup; + ccp run; + ccp abort; + ccp finish; + ccp script; + ccp open; + ccp close; + ccp file; + ccp job; + + ccp info; + ccp hint; + ccp warn; + ccp debug; + ccp log; + + ccp name; + ccp value; + ccp success; + ccp error; + ccp fail; + ccp fail2; + ccp fatal; + ccp mark; + ccp bad; + + ccp select; + ccp differ; + ccp stat_line; + ccp warn_line; + ccp proc_line; + ccp cite; + ccp status; + ccp highlight; + ccp hide; + + ccp heading; + ccp caption; + ccp section; + ccp syntax; + ccp cmd; + ccp option; + ccp param; + + ccp on; + ccp off; + + ccp black; + ccp b_black; + ccp white; + ccp b_white; + + ccp red; + ccp red_orange; + ccp orange; + ccp orange_yellow; + ccp yellow; + ccp yellow_green; + ccp green; + ccp green_cyan; + ccp cyan; + ccp cyan_blue; + ccp blue; + ccp blue_magenta; + ccp magenta; + ccp magenta_red; + + ccp b_red; + ccp b_red_orange; + ccp b_orange; + ccp b_orange_yellow; + ccp b_yellow; + ccp b_yellow_green; + ccp b_green; + ccp b_green_cyan; + ccp b_cyan; + ccp b_cyan_blue; + ccp b_blue; + ccp b_blue_magenta; + ccp b_magenta; + ccp b_magenta_red; + + ccp matrix[TCI__N_FONT][TCI__N_BG]; +} +ColorSet_t; + +//----------------------------------------------------------------------------- + +ccp GetTextMode +( + // returns a pointer to an internal circulary buffer (not GetCircBuf()) + ColorMode_t col_mode, // if < COLMD_ON: return an empty string + TermTextMode_t mode // mode for text generation +); + +//----------------------------------------------------------------------------- +// [[GetColorOption_t]] + +typedef enum GetColorOption_t +{ + GCM_ALT = 0x01, // use recommended font color if font+bg is not readable + GCM_SHORT = 0x02, // allow alternative short codes (codes 30-37 and 40-47) +} +GetColorOption_t; + +ccp GetColorMode +( + // returns a pointer to an internal circulary buffer (not GetCircBuf()) + + ColorMode_t col_mode, // if < COLMD_ON: return an empty string + TermColorIndex_t font_index, // index of font color + TermColorIndex_t bg_index, // index of background color + GetColorOption_t option // execution options +); + +//----------------------------------------------------------------------------- + +typedef void (*PrintColorFunc) +( + // On start of group: col_name := NULL, col_string := NULL + + FILE *f, // valid output file + int indent, // normalized indention of output + const ColorSet_t *cs, // valid color set, never NULL + uint mode, // output mode of PrintColorSetHelper() + ccp col_name, // name of color + ccp col_string // escape string for the color +); + +//----------------------------------------------------------------------------- + +const ColorSet_t * GetColorSet ( ColorMode_t col_mode ); +const ColorSet_t * GetColorSet0(void); +const ColorSet_t * GetColorSet8(void); +const ColorSet_t * GetColorSet256(void); +const ColorSet_t * GetColorSetAuto ( bool force_on ); + +const ColorSet_t * GetFileColorSet ( FILE *f ); +bool SetupColorSet ( ColorSet_t *cc, FILE *f ); +ccp GetColorByName ( const ColorSet_t *colset, ccp name ); + +void PrintColorSet +( + FILE *f, // valid output file + int indent, // indention of output + const ColorSet_t *cs // valid color set; if NULL: use std colors +); + +void PrintColorSetEx +( + FILE *f, // valid output file + int indent, // indention of output + const ColorSet_t *cs, // valid color set; if NULL: use std colors + uint mode, // output mode => see PrintColorSetHelper() + uint format // output format + // 0: colored list + // 1: shell definitions +); + +void PrintColorSetHelper +( + FILE *f, // valid output file + int indent, // indention of output + const ColorSet_t *cs, // valid color set; if NULL: use std colors + PrintColorFunc func, // output function, never NULL + uint mode // output mode (bit field, NULL=default): + // 1: print normal colors (e.g. RED) + // 2: print bold colors (e.g. B_RED) + // 4: print background (e.g. BLUE_RED) + // 8: print color names (e.g. HIGHLIGHT) + // 16: include alternative names too (e.g. HL) +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u32 ColorTab_M0_M15[16]; // first 16 colors of "\e[m" + +// return a m256 index +u8 ConvertColorRGB3ToM256 ( u8 r, u8 g, u8 b ); +u8 ConvertColorRGBToM256 ( u32 rgb ); + +u32 ConvertColorM256ToRGB ( u8 m256 ); + +static inline uint SingleColorToM6 ( u8 col ) + { return col > 114 ? ( col - 35 ) / 40 : col > 47; } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ViewColor*() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ColorView_t]] : parameters to view color sets + +typedef struct ColorView_t +{ + FILE *f; // valid output file + int indent; // indention of output + + const struct TerminalInfo_t + *term; // not NULL: us it to find 'colset' + ColorMode_t col_mode; // color mode -> NormalizeColorMode() + const ColorSet_t *colset; // not NULL: ignore 'col_mode' + + TermColorIndex_t std_col; // standard bg or fg color, TCI__IGNORE allowed + int order; // 0|'r' | 1|'g' | 2|'b' + GetColorOption_t col_option; // color option + uint mode; // function specific mode (modulo 60) + + void *user_ptr; // any pointer + int user_int; // any number or id +} +ColorView_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeColorView ( ColorView_t *cv ) + { DASSERT(cv); memset(cv,0,sizeof(*cv)); cv->std_col = TCI__IGNORE; } + +void SetupColorView ( ColorView_t *cv ); +void SetupColorViewMode ( ColorView_t *cv, ColorMode_t col_mode ); + +void ViewColorsAttrib8 ( ColorView_t *cv ); +void ViewColorsCombi8 ( ColorView_t *cv ); +void ViewColors18 ( ColorView_t *cv ); +void ViewColorsDC ( ColorView_t *cv, bool optimized ); +void ViewColors256 ( ColorView_t *cv ); +void ViewColorsPredef ( ColorView_t *cv, uint mode ); // mode: 1=name, 2=colors + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// legacy functions (deprecated) ==> wrapper for ViewColors*() + +void PrintTextModes +( + // => ViewColorsAttrib8() & ViewColorsCombi8() + + FILE *f, // valid output file + int indent, // indention of output + ColorMode_t col_mode // color mode -> NormalizeColorMode() +); + +void PrintColorModes +( + FILE *f, // valid output file + int indent, // indention of output + ColorMode_t col_mode, // color mode -> NormalizeColorMode() + GetColorOption_t option // execution options (GCM_ALT ignored) +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// std*, col* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// We have 5 standard output files +// +// stdout, colout: +// Standard output like usual. +// +// stderr, colerr: +// Standard error output like usual. It is usually not redirected by +// RedirectStdFiles() and points always to its original. +// +// stdlog, collog: +// Logging output, may be NULL to tell: no logging. +// RedirectStdFiles() don't cange it, if it is NULL. +// +// stdmsg, colmsg: +// Special output for catcable messages. Initalized by SetupStdMsg() +// and set to the first not NULL of stdout, stderr and stdlog. +// +// stdwrn, colwrn: +// Output file of PrintError() functions. +// Initalized to a copy of stderr. +// +//////////////////////////////////////////////////////////////////////////// + + +extern FILE *stdlog; // NULL or logfile +extern bool close_stdlog; // true: close file behing 'stdlog' + +extern FILE *stdmsg; // 'stdlog' or 'stdout' or 'stderr' or NULL +extern FILE *stdwrn; // used by PrintError(), initialized with 'stderr' + +extern ColorMode_t colorize_stdout; // <0:no color, 0:auto, >0:force color +extern ColorMode_t colorize_stderr; +extern ColorMode_t colorize_stdlog; +extern ColorMode_t colorize_stdmsg; +extern ColorMode_t colorize_stdwrn; + +extern const ColorSet_t *colout; // color set for 'stdout' +extern const ColorSet_t *colerr; // color set for 'stderr' +extern const ColorSet_t *collog; // color set for 'stdlog' +extern const ColorSet_t *colmsg; // color set for 'stdmsg' +extern const ColorSet_t *colwrn; // color set for 'stdwrn' + +//----------------------------------------------------------------------------- + +ColorMode_t GetFileColorized ( FILE *f ); +static inline bool IsFileColorized ( FILE *f ) + { return GetFileColorized(f) >= COLMD_ON; } + +enumError OpenStdLog +( + ccp fname, // If NULL or empty: don't open and use 'fallback' + // If first char == '+': open file in append mode + FILE *fallback // Fall back to this file, if no file name given + // If NULL: just clode 'stdlog'. +); + +void CloseStdLog(void); +void SetupStdMsg(void); + +//----------------------------------------------------------------------------- +// [[SavedStdFiles_t]] + +typedef struct SavedStdFiles_t +{ + //-- 5 output streams + + FILE *std_out; + FILE *std_err; + FILE *std_log; + FILE *std_msg; + FILE *std_wrn; + + //-- 5 related color sets + + const ColorSet_t *col_out; + const ColorSet_t *col_err; + const ColorSet_t *col_log; + const ColorSet_t *col_msg; + const ColorSet_t *col_wrn; + + //--- misc + + uint stdout_seq_count; + + //--- support for CatchStdFiles() + + FILE *f; // NULL or result of open_memstream() + char *data; // data pointer + size_t size; // size of 'data' + uint marker; // marker, free member, e.g. grow_buffer->used +} +SavedStdFiles_t; + +void SaveStdFiles ( SavedStdFiles_t *ssf ); +void RestoreStdFiles ( SavedStdFiles_t *ssf ); + +void RedirectStdFiles +( + SavedStdFiles_t *ssf, // not NULL: save old output here + FILE *f, // use this as out,err,log and msg, never NULL + const ColorSet_t *colset,// new colset; if NULL: use GetFileColorSet(f) + bool err_too // true: redirect stderr too +); + +#ifndef __APPLE__ + enumError CatchStdFiles + ( + SavedStdFiles_t *ssf, // save old output and data here, never NULL + const ColorSet_t *colset // new colset; if NULL: no color support + ); +#endif + +// call XFREE(ssf->data) after 'data' is used. +void TermCatchStdFiles ( SavedStdFiles_t *ssf ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_COLOR_H diff --git a/project/dclib/dclib-debug.c b/project/dclib/dclib-debug.c new file mode 100644 index 0000000..506be18 --- /dev/null +++ b/project/dclib/dclib-debug.c @@ -0,0 +1,1891 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define DCLIB_DEBUG_C 1 +#include "dclib-debug.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "dclib-basics.h" +#include "dclib-file.h" + +#if ENABLE_HEXDUMP_WRAPPER > 0 + #include "dclib-xdump.h" +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// error messages /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp TRACE_PREFIX = ""; +FILE *TRACE_FILE = 0; +FILE *MEM_LOG_FILE = 0; + +ccp (*GetErrorNameHook)( int stat, ccp ret_not_found ) = 0; +ccp (*GetErrorTextHook)( int stat, ccp ret_not_found ) = 0; + +ccp GENERIC_ERROR_MESSAGE = "generic err"; + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetErrorName +( + int stat, // error status, err := abs(stat) + ccp ret_not_found // not NULL: return this, if no message is found + // GENERIC_ERROR_MESSAGE: return a generic message +) +{ + if (GetErrorNameHook) + { + ccp res = GetErrorNameHook(stat,0); + if ( res && *res ) + return res; + } + + if ( stat < 0 ) + stat = -stat; + + switch(stat) + { + case ERR_OK: return "OK"; + + case ERR_DIFFER: return "DIFFER"; + case ERR_NOTHING_TO_DO: return "NOTHING TO DO"; + case ERR_SOURCE_FOUND: return "SOURCE FOUND"; + case ERR_NO_SOURCE_FOUND: return "NO SOURCE"; + case ERR_JOB_IGNORED: return "JOB IGNORED"; + case ERR_SUBJOB_WARNING: return "SUB JOB WARNINGS"; + case ERR_NOT_EXISTS: return "NOT EXISTS"; + + case ERR_WARNING: return "WARNING"; + + case ERR_WRONG_FILE_TYPE: return "WRONG FILE TYPE"; + case ERR_INVALID_FILE: return "INVALID FILE"; + case ERR_INVALID_VERSION: return "INVALID VERSION"; + case ERR_INVALID_DATA: return "INVALID DATA"; + + case ERR_ENCODING: return "ENCODING FAILED"; + case ERR_DECODING: return "DECODING FAILED"; + case ERR_ALREADY_EXISTS: return "FILE ALREADY EXISTS"; + case ERR_SUBJOB_FAILED: return "SUB JOB FAILED"; + + case ERR_CANT_REMOVE: return "CAN'T REMOVE FILE"; + case ERR_CANT_RENAME: return "CAN'T RENAME FILE"; + case ERR_CANT_CLOSE: return "CAN'T CLOSE FILE"; + case ERR_CANT_CONNECT: return "CAN'T CONNECT"; + case ERR_CANT_OPEN: return "CAN'T OPEN FILE"; + case ERR_CANT_APPEND: return "CAN'T APPEND FILE"; + case ERR_CANT_CREATE: return "CAN'T CREATE FILE"; + case ERR_CANT_CREATE_DIR: return "CAN'T CREATE DIRECTORY"; + + case ERR_READ_FAILED: return "READ FILE FAILED"; + case ERR_REMOVE_FAILED: return "REMOVE FILE FAILED"; + case ERR_WRITE_FAILED: return "WRITE FILE FAILED"; + case ERR_DATABASE: return "DATABASE ACCESS FAILED"; + + case ERR_MISSING_PARAM: return "MISSING PARAMETERS"; + case ERR_SEMANTIC: return "SEMANTIC ERROR"; + case ERR_SYNTAX: return "SYNTAX ERROR"; + + case ERR_INTERRUPT: return "INTERRUPT"; + + case ERR_ERROR: return "ERROR"; + + case ERR_NOT_IMPLEMENTED: return "NOT IMPLEMENTED YET"; + case ERR_INTERNAL: return "INTERNAL ERROR"; + case ERR_OUT_OF_MEMORY: return "OUT OF MEMORY"; + + case ERR_FATAL: return "FATAL ERROR"; + } + + if ( ret_not_found != GENERIC_ERROR_MESSAGE ) + return ret_not_found; + + //--- generic messages + + if (GetErrorNameHook) + { + ccp res = GetErrorNameHook(stat,GENERIC_ERROR_MESSAGE); + if ( res && *res ) + return res; + } + + if ( stat >= ERU_WARN_00 & stat <= ERU_WARN_XX ) + { + const uint buf_size = 20; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER WARNING #%02u", stat - ERU_WARN_00 ); + return buf; + } + + if ( stat >= ERU_ERROR1_00 & stat <= ERU_ERROR1_XX ) + { + const uint buf_size = 20; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER ERROR #%02u", stat - ERU_ERROR1_00 ); + return buf; + } + + if ( stat >= ERU_ERROR2_00 & stat <= ERU_ERROR2_XX ) + { + const uint buf_size = 20; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER ERROR #%02u", + stat - ERU_ERROR2_00 + ERU_ERROR1_XX+1 - ERU_ERROR1_00 ); + return buf; + } + + if ( stat >= ERU_FATAL_00 & stat <= ERU_FATAL_XX ) + { + const uint buf_size = 24; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER FATAL ERROR #%02u", stat - ERU_FATAL_00 ); + return buf; + } + + ccp res = GetErrorName(stat+1,""); + return res && *res ? res : "?"; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetErrorText +( + int stat, // error status, err := abs(stat) + ccp ret_not_found // not NULL: return this, if no message is found + // GENERIC_ERROR_MESSAGE: return a generic message +) +{ + if (GetErrorTextHook) + { + ccp res = GetErrorTextHook(stat,0); + if (res) + return res; + } + + if ( stat < 0 ) + stat = -stat; + + switch(stat) + { + case ERR_OK: return "Ok"; + + case ERR_DIFFER: return "Files differ"; + case ERR_NOTHING_TO_DO: return "Nothing to do"; + case ERR_SOURCE_FOUND: return "Source found"; + case ERR_NO_SOURCE_FOUND: return "No source found"; + case ERR_JOB_IGNORED: return "Job ignored"; + case ERR_SUBJOB_WARNING: return "Sub job had warnings"; + case ERR_NOT_EXISTS: return "File does not exists"; + + case ERR_WARNING: return "Unspecific warning"; + + case ERR_WRONG_FILE_TYPE: return "Wrong type of file"; + case ERR_INVALID_FILE: return "Invalid file"; + case ERR_INVALID_VERSION: return "Invalid file version"; + case ERR_INVALID_DATA: return "Invalid data"; + + case ERR_ENCODING: return "Encoding data failed"; + case ERR_DECODING: return "Decoding data failed"; + case ERR_ALREADY_EXISTS: return "File already exists"; + case ERR_SUBJOB_FAILED: return "Sub job failed"; + + case ERR_CANT_REMOVE: return "Can't remove file"; + case ERR_CANT_RENAME: return "Can't rename file"; + case ERR_CANT_CLOSE: return "Can't close file"; + case ERR_CANT_CONNECT: return "Can't connect"; + case ERR_CANT_OPEN: return "Can't open file"; + case ERR_CANT_APPEND: return "Can't open file for appending"; + case ERR_CANT_CREATE: return "Can't create file"; + case ERR_CANT_CREATE_DIR: return "Can't create directory"; + + case ERR_READ_FAILED: return "Reading from file failed"; + case ERR_REMOVE_FAILED: return "Removing a file failed"; + case ERR_WRITE_FAILED: return "Writing to file failed"; + case ERR_DATABASE: return "Access to database failed"; + + case ERR_MISSING_PARAM: return "Missing at least one parameter"; + case ERR_SEMANTIC: return "Semantic error"; + case ERR_SYNTAX: return "Syntax error"; + + case ERR_INTERRUPT: return "Program interrupted by user"; + + case ERR_ERROR: return "Unspecific error"; + + case ERR_NOT_IMPLEMENTED: return "Not implemented yet"; + case ERR_INTERNAL: return "Internal error"; + case ERR_OUT_OF_MEMORY: return "Allocation of dynamic memory failed"; + + case ERR_FATAL: return "Unspecific fatal error"; + } + + if ( ret_not_found != GENERIC_ERROR_MESSAGE ) + return ret_not_found; + + //--- generic messages + + if (GetErrorTextHook) + { + ccp res = GetErrorTextHook(stat,GENERIC_ERROR_MESSAGE); + if ( res && *res ) + return res; + } + + if ( stat >= ERU_WARN_00 & stat <= ERU_WARN_XX ) + { + const uint buf_size = 20; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER WARNING #%02u", stat - ERU_WARN_00 ); + return buf; + } + + if ( stat >= ERU_ERROR1_00 & stat <= ERU_ERROR1_XX ) + { + const uint buf_size = 20; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER ERROR #%02u", stat - ERU_ERROR1_00 ); + return buf; + } + + if ( stat >= ERU_ERROR2_00 & stat <= ERU_ERROR2_XX ) + { + const uint buf_size = 20; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER ERROR #%02u", + stat - ERU_ERROR2_00 + ERU_ERROR1_XX+1 - ERU_ERROR1_00 ); + return buf; + } + + if ( stat >= ERU_FATAL_00 & stat <= ERU_FATAL_XX ) + { + const uint buf_size = 24; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"USER FATAL ERROR #%02u", stat - ERU_FATAL_00 ); + return buf; + } + + ccp res = GetErrorText(stat+1,0); + return res && *res ? res : "?"; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ListErrorCodes +( + FILE * f, // valid output stream + int indent, // indent of output + bool all // true: print all user reserved messages too. +) +{ + if (!f) + return; + + indent = NormalizeIndent(indent); + fprintf(f,"\n%*sList of error messages:\n\n",indent,""); + + ccp p2 = all ? 0 : ""; + + uint i, fw_name = 4, fw_text = 7; + for ( i = 0; i < ERR__N; i++ ) + { + ccp name = GetErrorName(i,p2); + if (name) + { + const uint len = strlen(name); + if ( fw_name < len ) + fw_name = len; + } + + ccp text = GetErrorText(i,p2); + { + const uint len = strlen(text); + if ( fw_text < len ) + fw_text = len; + } + } + + fprintf(f, + "%*s err | %-*s | %-*s\n" + "%*s-----+%.*s+%.*s\n", + indent, "", + fw_name, "name", + fw_text, "message", + indent, "", + fw_name+2, Minus300, + fw_text+2, Minus300 ); + + for ( i = 0; i < ERR__N; i++ ) + { + ccp name = GetErrorName(i,p2); + ccp text = GetErrorText(i,p2); + if ( name && *name || text && *text ) + fprintf(f,"%*s%4u | %-*s | %-*s\n", + indent, "", i, + fw_name, name ? name : "-", + fw_text, text ? text : "-" ); + + if ( i == ERR_OK || i == ERR_WARNING || i == ERR_ERROR ) + fprintf(f,"%*s-----+%.*s+%.*s\n", + indent, "", + fw_name+2, Minus300, + fw_text+2, Minus300 ); + } + + fprintf(f,"%*s-----+%.*s+%.*s\n\n", + indent, "", + fw_name+2, Minus300, + fw_text+2, Minus300 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// error handling /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp progpath = 0; +ccp progdir = 0; +ccp progname = 0; + +ccp toolname = 0; +ccp toolversion = 0; +ccp tooltitle = 0; + +bool multi_processing = false; +enumError last_error = ERR_OK; +enumError max_error = ERR_OK; +u32 error_count = 0; + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintError ( ccp func, ccp file, uint line, + int syserr, enumError err_code, ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + const int stat = PrintErrorArg(func,file,line,syserr,err_code,format,arg); + va_end(arg); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintErrorFile ( ccp func, ccp file, uint line, struct File_t *F, + int syserr, enumError err_code, ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + const int stat = PrintErrorArg(func,file,line,syserr,err_code,format,arg); + va_end(arg); + return F ? RegisterFileError(F,stat) : stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintErrorArg ( ccp func, ccp file, unsigned int line, + int syserr, enumError err_code, ccp format, va_list arg ) +{ + fflush(stdout); + if (!stdwrn) + { + SetupStdMsg(); + if (!stdwrn) + { + stdwrn = stderr; + if (!stdwrn) + return err_code; + } + } + + char msg[1000]; + if (!progname) + progname = "?"; + const int plen = strlen(progname)+2; + + if ( !format && err_code > ERR_NOT_IMPLEMENTED ) + format = "Program is aborted immediately!"; + + if (format) + { + vsnprintf(msg,sizeof(msg),format,arg); + msg[sizeof(msg)-2] = 0; + + const int mlen = strlen(msg); + if ( mlen > 0 && msg[mlen-1] != '\n' ) + { + msg[mlen] = '\n'; + msg[mlen+1] = 0; + } + } + else + StringCat2S(msg,sizeof(msg),GetErrorText(err_code,0),"\n"); + + ccp prefix = err_code == ERR_OK ? "" : err_code <= ERR_WARNING ? "! " : "!! "; + const int fw = GetTermWidth(80,40) - 1; + + #ifdef DEBUG + TRACE("%s%s #%d [%s] in %s() @ %s#%d\n", + prefix, err_code <= ERR_WARNING ? "WARNING" : "ERROR", + err_code, GetErrorName(err_code,0), func, file, line ); + TRACE("%s%*s%s",prefix,plen,"",msg); + if (syserr) + TRACE("!! %s(), ERRNO=%d: %s\n",__FUNCTION__,syserr,strerror(syserr)); + fflush(TRACE_FILE); + #endif + + #if defined(EXTENDED_ERRORS) + if ( err_code > ERR_WARNING ) + #else + if ( err_code >= ERR_NOT_IMPLEMENTED ) + #endif + { + ccp coln = EmptyString; + ccp col1 = EmptyString; + ccp col0 = EmptyString; + if (IsFileColorized(stdwrn)) + { + coln = GetTextMode(1,TTM_COL_INFO); + col1 = GetTextMode(1, err_code > ERR_WARNING ? TTM_COL_WARN : TTM_COL_HINT ); + col0 = TermTextModeReset; + } + + if ( stdwrn == stderr ) + { + if ( err_code > ERR_WARNING ) + fprintf(stdwrn,"%s%s%s:%s ERROR #%d [%s] in %s() @ %s#%d%s\n", + prefix, coln, progname, col1, err_code, + GetErrorName(err_code,0), func, file, line, col0 ); + else + fprintf(stdwrn,"%s%s%s:%s WARNING in %s() @ %s#%d%s\n", + prefix, coln, progname, col1, func, file, line, col0 ); + } + else + { + if ( err_code > ERR_WARNING ) + fprintf(stdwrn,"%s%s%s:%s ERROR #%d [%s]%s\n", + prefix, coln, progname, col1, err_code, + GetErrorName(err_code,0), col0 ); + else + fprintf(stdwrn,"%s%s%s:%s WARNING%s\n", + prefix, coln, progname, col1, col0 ); + } + + #if defined(EXTENDED_ERRORS) && EXTENDED_ERRORS > 1 + fprintf(stdwrn,"%s -> %s/%s?annotate=%d#l%d\n", + prefix, URI_VIEWVC, file, REVISION_NEXT, line ); + #endif + + fputs(prefix,stdwrn); + PutLines(stdwrn,plen,fw,0,prefix,msg,0); + } + else + { + fprintf(stdwrn,"%s%s:",prefix,progname); + PutLines(stdwrn,plen,fw,strlen(progname)+1,prefix,msg,0); + } + + if (syserr) + { + fprintf(stdwrn,"%s%*s-> ",prefix,plen,""); + snprintf(msg,sizeof(msg),"%s [%d]",strerror(syserr),syserr); + PutLines(stdwrn,plen+3,fw,plen+3,prefix,msg,0); + } + fflush(stdwrn); + + if ( err_code > ERR_OK ) + error_count++; + + last_error = err_code; + if ( max_error < err_code ) + max_error = err_code; + + if ( err_code > ERR_NOT_IMPLEMENTED ) + exit(err_code); + + return err_code; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintErrorStat ( enumError err, int verbose, ccp cmdname ) +{ + if ( verbose > 0 && err >= ERR_WARNING + || verbose > 1 && err + || err == ERR_NOT_IMPLEMENTED ) + { + if (!stdwrn) + { + SetupStdMsg(); + if (!stdwrn) + { + stdwrn = stderr; + if (!stdwrn) + return err; + } + } + + fprintf(stdwrn,"%s: Command '%s' returns with status #%d [%s]\n", + progname, cmdname, err, GetErrorName(err,0) ); + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool mark_used ( ccp name, ... ) +{ + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +#if HAVE_PRINT + + void PRINT_TIME ( time_t time, ccp title ) + { + struct tm utc, loc; + gmtime_r(&time,&utc); + localtime_r(&time,&loc); + PRINT( "%s: UTC= %4u-%02u-%02u %2u:%02u:%02u dst=%d" + " LOCAL= %4u-%02u-%02u %2u:%02u:%02u dst=%d\n", + title ? title : "TIME", + utc.tm_year+1900, utc.tm_mon+1, utc.tm_mday, + utc.tm_hour, utc.tm_min, utc.tm_sec, utc.tm_isdst, + loc.tm_year+1900, loc.tm_mon+1, loc.tm_mday, + loc.tm_hour, loc.tm_min, loc.tm_sec, loc.tm_isdst ); + } + +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// hexdump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if ENABLE_HEXDUMP_WRAPPER > 1 + int enable_xdump_wrapper = 1; +#elif ENABLE_HEXDUMP_WRAPPER + int enable_xdump_wrapper = 0; +#endif + +ccp hexdump_prefix = ""; +ccp hexdump_eol = "\n"; +bool hexdump_align = false; + +//----------------------------------------------------------------------------- + +#if ENABLE_HEXDUMP_WRAPPER > 0 + +static void SetupXDumpWrapper +( + XDump_t *xd, + FILE *f, + int indent, + u64 addr, + int addr_fw, + int row_len +) +{ + DASSERT(xd); + InitializeXDump(xd); + + xd->f = f; + xd->prefix = hexdump_prefix; + xd->eol = hexdump_eol; + xd->mode_align = hexdump_align; + xd->indent = indent; + xd->start_addr = addr; + xd->min_addr_fw = addr_fw; + xd->min_width = row_len; + xd->format = XDUMPF_INT_1; + xd->print_format = false; + xd->print_summary = false; + xd->print_diff_sep = false; + + hexdump_prefix = EmptyString; + hexdump_eol = "\n"; + hexdump_align = false; +} + +#endif + +//----------------------------------------------------------------------------- + +uint HexDump16 ( FILE * f, int indent, u64 addr, + const void * data, size_t count ) +{ + // return the number of printed lines + return HexDump(f,indent,addr,4,16,data,count); +} + +//----------------------------------------------------------------------------- + +uint HexDump20 ( FILE * f, int indent, u64 addr, + const void * data, size_t count ) +{ + // return the number of printed lines + return HexDump(f,indent,addr,4,20,data,count); +} + +//----------------------------------------------------------------------------- + +uint HexDump ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, + const void * p_data, size_t count ) +{ + // return the number of printed lines + if ( !f || !p_data || !count ) + return 0; + + #if ENABLE_HEXDUMP_WRAPPER == 1 || ENABLE_HEXDUMP_WRAPPER == 2 + if (enable_xdump_wrapper) + #endif + + #if ENABLE_HEXDUMP_WRAPPER > 0 + { + XDump_t xd; + SetupXDumpWrapper(&xd,f,indent,addr,addr_fw,row_len); + const int stat = XDump(&xd,p_data,count,true); + return stat < 0 ? 0 : stat; + } + #endif + + #if ENABLE_HEXDUMP_WRAPPER < 3 + + const int MAX_LEN = 256; + char buf[MAX_LEN+2]; + + const u8 * data = (const u8 *)p_data; + + indent = NormalizeIndent(indent); + addr_fw = NormalizeIndent(addr_fw); + + const bool show_ascii = row_len >= 0; + if ( row_len < 0 ) + row_len = -row_len; + else if ( row_len < 1 ) + row_len = 16; + else if ( row_len > MAX_LEN ) + row_len = MAX_LEN; + + uint skip = 0; + if ( (s64)addr != -1 ) + { + const int fw = snprintf(buf,sizeof(buf),"%llx",addr+count-1); + if ( addr_fw < fw ) + addr_fw = fw; + + if (hexdump_align) + { + skip = addr % row_len; + addr -= skip; + } + } + + uint line_count = 0; + while ( count > 0 ) + { + if ( (s64)addr == -1 ) + fprintf(f,"%s%*s", hexdump_prefix, indent,"" ); + else + { + fprintf(f,"%s%*s%*llx:", hexdump_prefix, indent,"", addr_fw, addr ); + addr += row_len; + } + char * dest = buf; + if (!skip) + *dest++ = ':'; + + int i; + for ( i = 0; i < row_len; i++ ) + { + if ( skip > 0 ) + { + fprintf(f,"%s ", i&3 ? "" : " " ); + *dest++ = ' '; + if (!--skip) + *dest++ = ':'; + } + else + { + u8 ch = *data++; + if ( count > 0 ) + { + count--; + fprintf(f,"%s%02x ", i&3 ? "" : " ", ch ); + *dest++ = ch < ' ' || ch >= 0x7f ? '.' : ch; + } + else + fprintf(f,"%s ", i&3 ? "" : " " ); + } + } + *dest = 0; + if (show_ascii) + fprintf(f,"%s:%s",buf,hexdump_eol); + else + fputs(hexdump_eol,f); + line_count++; + } + + hexdump_prefix = ""; + hexdump_eol = "\n"; + hexdump_align = false; + return line_count; + #endif +} + +//----------------------------------------------------------------------------- + +uint HexDump016 ( FILE * f, int indent, u64 addr, + const void * data, size_t count ) +{ + // return the number of printed lines + return HexDump0(f,indent,addr,4,16,data,count); +} + +//----------------------------------------------------------------------------- + +uint HexDump0 ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, + const void * p_data, size_t count ) +{ + // return the number of printed lines + if ( !f || !p_data || !count ) + return 0; + + #if ENABLE_HEXDUMP_WRAPPER == 1 || ENABLE_HEXDUMP_WRAPPER == 2 + if (enable_xdump_wrapper) + #endif + + #if ENABLE_HEXDUMP_WRAPPER > 0 + { + XDump_t xd; + SetupXDumpWrapper(&xd,f,indent,addr,addr_fw,row_len); + xd.mode_ignore = true; + const int stat = XDump(&xd,p_data,count,true); + return stat < 0 ? 0 : stat; + } + #endif + + #if ENABLE_HEXDUMP_WRAPPER < 3 + return HexDump( f, indent, addr, addr_fw, row_len, p_data, count ); + #endif +} + +//----------------------------------------------------------------------------- + +void HexDiff16 ( FILE * f, int indent, u64 addr, + const void * data1, size_t count1, + const void * data2, size_t count2 ) +{ + HexDiff(f,indent,addr,4,16,data1,count1,data2,count2); +} + +//----------------------------------------------------------------------------- + +void HexDiff ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, + const void * p_data1, size_t count1, + const void * p_data2, size_t count2 ) +{ + if (!f) + return; + + #if ENABLE_HEXDUMP_WRAPPER == 1 || ENABLE_HEXDUMP_WRAPPER == 2 + if (enable_xdump_wrapper) + #endif + + #if ENABLE_HEXDUMP_WRAPPER > 0 + { + XDump_t xd; + SetupXDumpWrapper(&xd,f,indent,addr,addr_fw,row_len); + XDiff(&xd, p_data1,count1,true, p_data2,count2,true, 0,false ); + return; + } + #endif + + #if ENABLE_HEXDUMP_WRAPPER < 3 + + const int MAX_LEN = 256; + char buf[MAX_LEN+2]; + + const u8 * data1 = p_data1 ? (const u8 *)p_data1 : (u8*)EmptyString; + const u8 * data2 = p_data2 ? (const u8 *)p_data2 : (u8*)EmptyString; + + indent = NormalizeIndent(indent); + addr_fw = NormalizeIndent(addr_fw); + + const bool show_ascii = row_len >= 0; + if ( row_len < 0 ) + row_len = -row_len; + else if ( row_len < 1 ) + row_len = 16; + else if ( row_len > MAX_LEN ) + row_len = MAX_LEN; + + uint skip = 0; + + if ( (s64)addr != -1 ) + { + const size_t count = count1 > count2 ? count1 : count2; + const int fw = snprintf(buf,sizeof(buf),"%llx",addr+count-1); + if ( addr_fw < fw ) + addr_fw = fw; + + if (hexdump_align) + { + skip = addr % row_len; + addr -= skip; + } + } + + while ( count1 > 0 && count2 > 0 ) + { + size_t count = count1 < count2 ? count1 : count2; + if ( count > row_len ) + count = row_len; + noPRINT(" cnt=%zd,%zd -> %zd; cmp=%d\n", + count1, count2, count, memcmp(data1,data2,count) ); + + if (memcmp(data1,data2,count)) + { + //--- print first line + + if (IS_M1(addr)) + fprintf(f,"%s%*s<", hexdump_prefix, indent,"" ); + else + fprintf(f,"%s%*s<%*llx:", hexdump_prefix, indent,"", addr_fw, addr ); + + char * dest = buf; + if (!skip) + *dest++ = ':'; + + int i; + for ( i = 0; i < row_len; i++ ) + { + if ( skip > 0 ) + { + fprintf(f,"%s ", i&3 ? "" : " " ); + *dest++ = ' '; + if (!--skip) + *dest++ = ':'; + } + else + { + u8 ch = data1[i]; + if ( i < count1 ) + { + fprintf(f,"%s%02x ", i&3 ? "" : " ", ch ); + *dest++ = ch < ' ' || ch >= 0x7f ? '.' : ch; + } + else + fprintf(f,"%s ", i&3 ? "" : " " ); + } + } + *dest = 0; + if (show_ascii) + fprintf(f,"%s:%s",buf,hexdump_eol); + else + fputs(hexdump_eol,f); + + + //--- print second line + + if (IS_M1(addr)) + fprintf(f,"%s%*s>", hexdump_prefix, indent,"" ); + else + fprintf(f,"%s%*s>%*llx:", hexdump_prefix, indent,"", addr_fw, addr ); + + dest = buf; + if (!skip) + *dest++ = ':'; + + for ( i = 0; i < row_len; i++ ) + { + if ( skip > 0 ) + { + fprintf(f,"%s ", i&3 ? "" : " " ); + *dest++ = ' '; + if (!--skip) + *dest++ = ':'; + } + else + { + u8 ch = data2[i]; + if ( i < count2 ) + { + if ( ch == data1[i] ) + fprintf(f,"%s . ", i&3 ? "" : " " ); + else + fprintf(f,"%s%02x ", i&3 ? "" : " ", ch ); + *dest++ = ch < ' ' || ch >= 0x7f ? '.' : ch; + } + else + fprintf(f,"%s ", i&3 ? "" : " " ); + } + } + *dest = 0; + if (show_ascii) + fprintf(f,"%s:%s",buf,hexdump_eol); + else + fputs(hexdump_eol,f); + } + data1 += count; + data2 += count; + count1 -= count; + count2 -= count; + if (!IS_M1(addr)) + addr += count; + + } + hexdump_prefix = ""; + hexdump_eol = "\n"; + hexdump_align = false; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// trace functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u_msec_t GetTimerMSec(); + +/////////////////////////////////////////////////////////////////////////////// + +static void trace_helper ( int print_stderr, ccp format, va_list arg ) +{ + static uint last_day = 0; + DayTime_t dt = GetDayTime(true); + + if (!TRACE_FILE) + { + char extend[16]; + if (multi_processing) + snprintf(extend,sizeof(extend),"-%u",getpid()); + else + *extend = 0; + + char fname[1000]; + if ( progname && *progname && *progname != '?' ) + snprintf( fname, sizeof(fname), "_trace-%s%s.tmp", progname, extend ); + else + snprintf( fname, sizeof(fname), "_trace-%s.tmp", extend ); + + TRACE_FILE = fopen(fname,"wb"); + if (TRACE_FILE) + { + fcntl(fileno(TRACE_FILE),F_SETFD,FD_CLOEXEC); + fprintf(TRACE_FILE,"\n#\f\n#OPEN %s, pid=%d\n\n", + PrintTimeByFormat("%F %T %z",dt.time), getpid() ); + } + else + TRACE_FILE = stderr; + last_day = dt.day; + } + + char pre[200]; + if ( last_day != dt.day ) + { + SetupTimezone(true); + dt = GetDayTime(true); + last_day = dt.day; + snprintf(pre,sizeof(pre), + "\n#DAY: %s, pid=%d\n%02u:%02u:%02u.%03u ", + PrintTimeByFormat("%F %T %z",dt.time), getpid(), + dt.hour, dt.min, dt.sec, dt.usec/1000 ); + } + else + snprintf(pre,sizeof(pre),"%02u:%02u:%02u.%03u ", + dt.hour, dt.min, dt.sec, dt.usec/1000 ); + + if ( print_stderr || TRACE_FILE == stderr ) + { + fflush(stdout); + fputs(pre,stderr); + fputs(TRACE_PREFIX,stderr); + va_list arg2; + va_copy(arg2,arg); + vfprintf(stderr,format,arg2); + va_end(arg2); + fflush(stderr); + } + + if ( TRACE_FILE && TRACE_FILE != stderr ) + { + fputs(pre,TRACE_FILE); + fputs(TRACE_PREFIX,TRACE_FILE); + vfprintf(TRACE_FILE,format,arg); + fflush(TRACE_FILE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef TRACE_ARG_FUNC + +void TRACE_ARG_FUNC ( ccp format, va_list arg ) +{ + trace_helper(0,format,arg); +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef TRACE_FUNC + +void TRACE_FUNC ( ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + trace_helper(0,format,arg); + va_end(arg); +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef PRINT_ARG_FUNC + +void PRINT_ARG_FUNC ( ccp format, va_list arg ) +{ + trace_helper(1,format,arg); +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef PRINT_FUNC + +void PRINT_FUNC ( ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + trace_helper(1,format,arg); + va_end(arg); +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef BINGO_FUNC + +void BINGO_FUNC ( ccp func, int line, ccp src ) +{ + #if defined(DEBUG) && defined(TEST) + if (colerr) + PRINT_FUNC("%sBINGO!%s %s() #%d @ %s\n", + colerr->magenta, colerr->reset, func, line, src ); + else + PRINT_FUNC("BINGO! %s() #%d @ %s\n", func, line, src ); + #else + if (colerr) + fprintf(stderr,"%sBINGO!%s %s() #%d @ %s\n", + colerr->magenta, colerr->reset, func, line, src ); + else + fprintf(stderr,"BINGO! %s() #%d @ %s\n", func, line, src ); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef WAIT_ARG_FUNC + +void WAIT_ARG_FUNC ( ccp format, va_list arg ) +{ + if ( format && *format ) + trace_helper(1,format,arg); + PRINT_FUNC(">>>>>> PRESS RETURN: "); + getchar(); +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef WAIT_FUNC + +void WAIT_FUNC ( ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + WAIT_ARG_FUNC(format,arg); + va_end(arg); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alloc/free system /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if TRACE_ALLOC_MODE > 2 + #define MEM_FILLER_SIZE 8 + static u8 mem_filler[MEM_FILLER_SIZE]; +#else + #define MEM_FILLER_SIZE 0 +#endif + +#if TRACE_ALLOC_MODE > 1 + #define MPARAM ccp func, ccp file, uint line, + #define MCALL func,file,line, + #define PRINT_OOM(...) \ + PrintError(func,file,line,0,ERR_OUT_OF_MEMORY,__VA_ARGS__) +#else + #define MPARAM + #define MCALL + #define PRINT_OOM(...) \ + PrintError(__FUNCTION__,__FILE__,__LINE__,0,ERR_OUT_OF_MEMORY,__VA_ARGS__) +#endif + +/////////////////////////////////////////////////////////////////////////////// + +void dclib_free ( void * ptr ) +{ + free(ptr); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_xcalloc ( size_t nmemb, size_t size ) +{ + void * res = calloc(nmemb,size); + if (!res) + PrintError(__FUNCTION__,__FILE__,__LINE__,0,ERR_OUT_OF_MEMORY, + "Out of memory while calloc() %zu bytes (%zu*%zu=0x%zx)\n", + nmemb*size, nmemb, size, nmemb*size ); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_xmalloc ( size_t size ) +{ + void * res = malloc(size); + if (!res) + PrintError(__FUNCTION__,__FILE__,__LINE__,0,ERR_OUT_OF_MEMORY, + "Out of memory while malloc() %zu bytes (0x%zx)\n", + size, size ); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_xrealloc ( void * ptr, size_t size ) +{ + void * res = realloc(ptr,size); + if ( !res && size ) + PrintError(__FUNCTION__,__FILE__,__LINE__,0,ERR_OUT_OF_MEMORY, + "Out of memory while realloc() %zu bytes (0x%zx)\n", + size, size ); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * dclib_xstrdup ( ccp src ) +{ + char * res = strdup(src?src:""); + if (!res) + { + const uint size = src ? strlen(src)+1 : 0; + PrintError(__FUNCTION__,__FILE__,__LINE__,0,ERR_OUT_OF_MEMORY, + "Out of memory while strdup() %u bytes (0x%x)\n", + size, size ); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_calloc ( MPARAM size_t nmemb, size_t size ) +{ + void * res = calloc(nmemb,size); + if (!res) + PRINT_OOM("Out of memory while calloc() %zu bytes (%zu*%zu=0x%zx)\n", + nmemb*size, nmemb, size, nmemb*size ); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_malloc ( MPARAM size_t size ) +{ + void * res = malloc(size); + if (!res) + PRINT_OOM("Out of memory while malloc() %zu bytes (0x%zx)\n", + size, size ); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_realloc ( MPARAM void * ptr, size_t size ) +{ + void * res = realloc(ptr,size); + if ( !res && size ) + PRINT_OOM("Out of memory while realloc() %zu bytes (0x%zx)\n", + size, size ); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * dclib_strdup ( MPARAM ccp src ) +{ + char * res = strdup(src?src:""); + if (!res) + { + const uint size = src ? strlen(src)+1 : 0; + PRINT_OOM("Out of memory while strdup() %u bytes (0x%x)\n", + size, size ); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * dclib_strdup2 ( MPARAM ccp src1, ccp src2 ) +{ + const uint len1 = src1 ? strlen(src1) : 0; + const uint len2 = src2 ? strlen(src2) : 0; + char * res = dclib_malloc( MCALL len1+len2+1 ); + if (len1) + memcpy(res,src1,len1); + if (len2) + memcpy(res+len1,src2,len2); + res[len1+len2] = 0; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * dclib_strdup3 ( MPARAM ccp src1, ccp src2, ccp src3 ) +{ + const uint len1 = src1 ? strlen(src1) : 0; + const uint len2 = src2 ? strlen(src2) : 0; + const uint len3 = src3 ? strlen(src3) : 0; + char * res = dclib_malloc( MCALL len1+len2+len3+1 ); + char * dest = res; + if (len1) + { + memcpy(dest,src1,len1); + dest += len1; + } + if (len2) + { + memcpy(dest,src2,len2); + dest += len2; + } + if (len3) + { + memcpy(dest,src3,len3); + dest += len3; + } + *dest = 0; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_memdup ( MPARAM const void * src, size_t copylen ) +{ + char * dest = dclib_malloc( MCALL copylen+1); + memcpy(dest,src,copylen); + dest[copylen] = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * dclib_allocdup ( MPARAM const void * src, size_t copylen ) +{ + char * dest = dclib_malloc( MCALL copylen); + memcpy(dest,src,copylen); + return dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#if TRACE_ALLOC_MODE > 2 +/////////////////////////////////////////////////////////////////////////////// +// [[mem_info_t]] + +typedef struct mem_info_t +{ + ccp func; + ccp file; + uint line; + u8 * data; + uint size; + bool filler; + uint seqnum; + +} mem_info_t; + +//----------------------------------------------------------------------------- + +static mem_info_t * mem_list = 0; +static uint mem_used = 0; +static uint mem_size = 0; +static uint mem_seqnum = 0; + +/////////////////////////////////////////////////////////////////////////////// + +static FILE * OpenMemLog() +{ + static bool done = false; + if (!done) + { + done = true; + char fname[500]; + if ( progname && *progname && *progname != '?' ) + snprintf(fname,sizeof(fname),"_trace_alloc-%s-%u.tmp",progname,getpid()); + else + snprintf(fname,sizeof(fname),"_trace_alloc-%u.tmp",getpid()); + + MEM_LOG_FILE = fopen(fname,"ab"); + if (MEM_LOG_FILE) + { + fcntl(fileno(MEM_LOG_FILE),F_SETFD,FD_CLOEXEC); + fprintf(MEM_LOG_FILE,"#\f\n# OPEN FILE %s\n", + PrintTimeByFormat("%F %T %z",time(0))); + fflush(MEM_LOG_FILE); + } + } + + return MEM_LOG_FILE; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void MemLog + ( ccp func, ccp file, uint line, ccp info, const u8 * ptr, bool no_stderr ) +{ + static uint line_no = 0; + + if (!no_stderr) + { + fprintf(stderr,"MEM-ERR[%s,%p] %s() @ %s#%u\n", + info, ptr, func, file, line ); + fflush(stderr); + } + + if (OpenMemLog()) + { + if (!line_no) + fputs("#index addr type function line file\n",MEM_LOG_FILE); + + fprintf(MEM_LOG_FILE,"%07u %p %s %s %u %s\n", + line_no, ptr, info, func, line, file ); + fflush(MEM_LOG_FILE); + } + + line_no++; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void MemLogItem + ( ccp func, ccp file, uint line, ccp info, const mem_info_t * mi ) +{ + if (func) + fprintf(stderr,"MEM-ERR[%s] %s() @ %s#%u\n", + info, func, file, line ); + fprintf(stderr,"MEM-ERR[%s,%u] %s() @ %s#%u\n", + info, mi->seqnum, mi->func, mi->file, mi->line ); + fflush(stderr); + + if (OpenMemLog()) + { + if (func) + fprintf(MEM_LOG_FILE,"[%s] %s() @ %s#%u\n", + info, func, file, line ); + fprintf(MEM_LOG_FILE,"[%s,%u] %s() @ %s#%u\n", + info, mi->seqnum, mi->func, mi->file, mi->line ); + if (mi->filler) + { + HexDump16( MEM_LOG_FILE, 10, 0, + mi->data - MEM_FILLER_SIZE, 2*MEM_FILLER_SIZE ); + HexDump16( MEM_LOG_FILE, 10, mi->size + MEM_FILLER_SIZE, + mi->data + mi->size - MEM_FILLER_SIZE, 2 * MEM_FILLER_SIZE ); + } + fputc('\n',MEM_LOG_FILE); + fflush(MEM_LOG_FILE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindMemInfoHelper ( u8 * data, uint size ) +{ + int beg = 0; + int end = mem_used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + mem_info_t * mi = mem_list + idx; + if ( data < mi->data ) + end = idx - 1 ; + else if ( data > mi->data ) + beg = idx + 1; + else if ( size < mi->size ) + end = idx - 1 ; + else if ( size > mi->size ) + beg = idx + 1; + else + { + noTRACE("FindMemInfoHelper(%llx,%llx) FOUND=%d/%d/%d\n", + (u64)off, (u64)size, idx, mem_used, mem_size ); + return idx; + } + } + + noTRACE("FindStringFieldHelper(%llx,%llx) failed=%d/%d/%d\n", + (u64)off, (u64)size, beg, mem_used, mem_size ); + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeTraceAlloc() +{ + static bool done = false; + if (done) + return; + done = true; + + OpenMemLog(); + + memset(mem_filler,0xdc,sizeof(mem_filler)); + mem_filler[MEM_FILLER_SIZE/2] = 0xcd; + mem_filler[MEM_FILLER_SIZE/3] = 0xcd; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckTraceAlloc ( ccp func, ccp file, uint line ) +{ + int count = 0; + + mem_info_t *ptr, *end = mem_list + mem_used; + for ( ptr = mem_list; ptr < end; ptr++ ) + { + if (!ptr->filler) + continue; + + if (memcmp(ptr->data-MEM_FILLER_SIZE,mem_filler,sizeof(mem_filler))) + { + MemLogItem(0,0,0,"BEG",ptr); + memcpy(ptr->data-MEM_FILLER_SIZE,mem_filler,sizeof(mem_filler)); + count++; + } + + if (memcmp(ptr->data+ptr->size,mem_filler,sizeof(mem_filler))) + { + MemLogItem(0,0,0,"END",ptr); + memcpy(ptr->data+ptr->size,mem_filler,sizeof(mem_filler)); + count++; + } + } + + if (count) + { + fprintf(stderr,"MEM-ERR: %u errors found -> %s() @ %s#%u\n", + count, func, file, line ); + fflush(stderr); + + if (OpenMemLog()) + { + fprintf(MEM_LOG_FILE,"--- %u errors found -> %s() @ %s#%u\n\n", + count, func, file, line ); + fflush(MEM_LOG_FILE); + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpTraceAlloc ( ccp func, ccp file, uint line, FILE * f ) +{ + CheckTraceAlloc(func,file,line); + + if (!f) + return; + + fprintf(f,"---- MEM-DUMP [N=%u] -----\n",mem_used); + mem_info_t *ptr, *end = mem_list + mem_used; + for ( ptr = mem_list; ptr < end; ptr++ ) + { + ccp file = strrchr(ptr->file,'/'); + file = file ? file+1 : ptr->file; + fprintf(f,"%6u %p %6u %-20.20s %5u %-.25s\n", + ptr->seqnum, ptr->data, ptr->size, + ptr->func, ptr->line, file ); + } + fflush(stderr); +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_info_t * RegisterAlloc + ( ccp func, ccp file, uint line, cvp data, uint size, bool filler ) +{ + if (!mem_seqnum) + InitializeTraceAlloc(); + + if (filler) + { + memcpy((u8*)data-MEM_FILLER_SIZE,mem_filler,MEM_FILLER_SIZE); + memcpy((u8*)data+size,mem_filler,MEM_FILLER_SIZE); + } + + uint idx = FindMemInfoHelper((u8*)data,size); + + ASSERT( mem_used <= mem_size ); + if ( mem_used == mem_size ) + { + mem_size += 1000 + mem_size/2; + const uint alloc_size = mem_size * sizeof(*mem_list); + mem_list = realloc(mem_list,alloc_size); + if (!mem_list) + PRINT_OOM("Out of memory while RegisterAlloc() %u bytes (0x%x)\n", + alloc_size, alloc_size ); + } + + DASSERT( idx <= mem_used ); + mem_info_t * mi = mem_list + idx; + memmove(mi+1,mi,(mem_used-idx)*sizeof(*mi)); + mem_used++; + + mi->func = func; + mi->file = file; + mi->line = line; + mi->data = (u8*)data; + mi->size = size; + mi->filler = filler; + mi->seqnum = mem_seqnum++; + + #if TRACE_ALLOC_MODE > 3 + MemLog(func,file,line,"ALLOC",data,true); + #endif + return mi; +} + +// +/////////////////////////////////////////////////////////////////////////////// +#endif // TRACE_ALLOC_MODE > 2 +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint trace_test_alloc + ( ccp func, ccp file, uint line, const void *p_data, bool hexdump ) +{ + #if TRACE_ALLOC_MODE > 2 + if (!p_data) + return 0; + const u8 *data = p_data; + + int beg = 0; + int end = mem_used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + mem_info_t * mi = mem_list + idx; + if ( data < mi->data ) + end = idx - 1 ; + else if ( data > mi->data ) + beg = idx + 1; + else + { + #if TRACE_ALLOC_MODE > 3 + MemLog(func,file,line,"TEST",data,true); + #endif + + uint stat = 0; + if (memcmp(data-MEM_FILLER_SIZE,mem_filler,MEM_FILLER_SIZE)) + { + MemLogItem(func,file,line,"BEG",mi); + stat = 1; + HexDump16(stderr,0,0,data-MEM_FILLER_SIZE,16); + } + + if (memcmp(data+mi->size,mem_filler,MEM_FILLER_SIZE)) + { + MemLogItem(func,file,line,"END",mi); + stat |= 2; + HexDump16(stderr,0,mi->size+sizeof(mem_filler),data+mi->size,16); + } + + return stat; + } + } + MemLog(func,file,line,"NOT FOUND",data,false); + return 4; + #else + return 0; + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static void * UnregisterAlloc ( ccp func, ccp file, uint line, u8 * data ) +{ + #if TRACE_ALLOC_MODE > 2 + if (!data) + return 0; + + int beg = 0; + int end = mem_used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + mem_info_t * mi = mem_list + idx; + if ( data < mi->data ) + end = idx - 1 ; + else if ( data > mi->data ) + beg = idx + 1; + else + { + #if TRACE_ALLOC_MODE > 3 + MemLog(func,file,line,"FREE",data,true); + #endif + + if (mi->filler) + { + if (memcmp(data-MEM_FILLER_SIZE,mem_filler,MEM_FILLER_SIZE)) + MemLogItem(func,file,line,"BEG",mi); + + if (memcmp(data+mi->size,mem_filler,MEM_FILLER_SIZE)) + MemLogItem(func,file,line,"END",mi); + + data -= MEM_FILLER_SIZE; + } + + memmove(mi,mi+1,(--mem_used-idx)*sizeof(*mi)); + return data; + } + } + MemLog(func,file,line,"NOT FOUND",data,false); + #endif + return data; +} + +/////////////////////////////////////////////////////////////////////////////// + +void trace_free ( ccp func, ccp file, uint line, void * ptr ) +{ + free(UnregisterAlloc(func,file,line,ptr)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void * trace_malloc ( ccp func, ccp file, uint line, size_t size ) +{ + u8 * res = malloc( size + 2 * MEM_FILLER_SIZE ); + if (!res) + PRINT_OOM("Out of memory while allocate %zu+%u bytes (0x%zx)\n", + size, 2 * MEM_FILLER_SIZE, size + 2 * MEM_FILLER_SIZE ); + + res += MEM_FILLER_SIZE; + #if TRACE_ALLOC_MODE > 2 + RegisterAlloc(func,file,line,res,size,true); + #endif + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * trace_calloc ( ccp func, ccp file, uint line, size_t nmemb, size_t size ) +{ + uint total_size = nmemb * size; + void * res = trace_malloc(func,file,line,total_size); + memset(res,0,total_size); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * trace_realloc ( ccp func, ccp file, uint line, void *ptr, size_t size ) +{ + ptr = UnregisterAlloc(func,file,line,ptr); + u8 * res = realloc( ptr, size + 2 * MEM_FILLER_SIZE ); + if (!res) + PRINT_OOM("Out of memory while re allocate %zu+%u bytes (0x%zx)\n", + size, 2 * MEM_FILLER_SIZE, size + 2 * MEM_FILLER_SIZE ); + + res += MEM_FILLER_SIZE; + #if TRACE_ALLOC_MODE > 2 + RegisterAlloc(func,file,line,res,size,true); + #endif + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * trace_strdup ( ccp func, ccp file, uint line, ccp src ) +{ + const uint len = src ? strlen(src)+1 : 0; + char * res = trace_malloc(func,file,line,len); + memcpy(res,src,len); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * trace_strdup2 ( ccp func, ccp file, uint line, ccp src1, ccp src2 ) +{ + const uint len1 = src1 ? strlen(src1) : 0; + const uint len2 = src2 ? strlen(src2) : 0; + char * res = trace_malloc(func,file,line,len1+len2+1); + if (len1) + memcpy(res,src1,len1); + if (len2) + memcpy(res+len1,src2,len2); + res[len1+len2] = 0; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * trace_strdup3 ( ccp func, ccp file, uint line, ccp src1, ccp src2, ccp src3 ) +{ + const uint len1 = src1 ? strlen(src1) : 0; + const uint len2 = src2 ? strlen(src2) : 0; + const uint len3 = src3 ? strlen(src3) : 0; + char * res = trace_malloc(func,file,line,len1+len2+len3+1); + char * dest = res; + if (len1) + { + memcpy(dest,src1,len1); + dest += len1; + } + if (len2) + { + memcpy(dest,src2,len2); + dest += len2; + } + if (len3) + { + memcpy(dest,src3,len3); + dest += len3; + } + *dest = 0; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * trace_memdup + ( ccp func, ccp file, uint line, const void * src, size_t copylen ) +{ + char * dest = trace_malloc(func,file,line,copylen+1); + memcpy(dest,src,copylen); + dest[copylen] = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * trace_allocdup + ( ccp func, ccp file, uint line, const void * src, size_t copylen ) +{ + char * dest = trace_malloc(func,file,line,copylen); + memcpy(dest,src,copylen); + return dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// mem check /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 mem_check_buf[0x100]; +static const void * mem_check_ptr = 0; +static uint mem_check_size = 0; + +/////////////////////////////////////////////////////////////////////////////// + +void MemCheckSetup ( const void * ptr, uint size ) +{ + if ( ptr && size > 0 ) + { + mem_check_ptr = ptr; + if ( size > sizeof(mem_check_buf) ) + { + ERROR0(ERR_WARNING, + "MemCheckSetup(), max watch size = %zx (<%x)", + sizeof(mem_check_buf), size ); + size = sizeof(mem_check_buf); + } + mem_check_size = size; + memcpy(mem_check_buf,ptr,size); + } + else + mem_check_size = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void MemCheck ( ccp func, ccp file, uint line ) +{ + if (!mem_check_size) + return; + + if ( memcmp(mem_check_buf,mem_check_ptr,mem_check_size) ) + { + fprintf(stderr,"--- MemCheck: %p should be:\n",mem_check_ptr); + HexDump16(stderr,0,0,mem_check_buf,mem_check_size); + fprintf(stderr,"--- MemCheck: ... but is:\n"); + HexDump16(stderr,0,0,mem_check_ptr,mem_check_size); + + TRACE("--- MemCheck: %p should be:\n",mem_check_ptr); + TRACE_HEXDUMP16(0,0,mem_check_buf,mem_check_size); + TRACE("--- MemCheck: ... but is:\n"); + TRACE_HEXDUMP16(0,0,mem_check_ptr,mem_check_size); + + PrintError(func,file,line,0,ERR_FATAL,"MemCheck() failed!\n"); + } + else + TRACE("MemCheck OK: %s @ %s#%u\n",func,file,line); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-debug.h b/project/dclib/dclib-debug.h new file mode 100644 index 0000000..34726f1 --- /dev/null +++ b/project/dclib/dclib-debug.h @@ -0,0 +1,714 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_DEBUG_H +#define DCLIB_DEBUG_H 1 + +#include "dclib-types.h" +#include +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// error handling /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern ccp progpath; // full path of program, use ProgramPath() to access +extern ccp progdir; // directory part of progpath, use ProgramDirectory() to access +extern ccp progname; // program name, based on path or directly set + +extern ccp toolname; // name of the tool, set by SetupProgname() +extern ccp toolversion; // version of the tool, set by SetupProgname() +extern ccp tooltitle; // title of the tool, set by SetupProgname() + +extern bool multi_processing; +extern enumError last_error; +extern enumError max_error; +extern u32 error_count; + +//----------------------------------------------------------------------------- + +extern ccp (*GetErrorNameHook)( int stat, ccp ret_not_found ); +extern ccp (*GetErrorTextHook)( int stat, ccp ret_not_found ); +extern ccp GENERIC_ERROR_MESSAGE; + +ccp GetErrorName +( + int stat, // error status, err := abs(stat) + ccp ret_not_found // not NULL: return this, if no message is found + // GENERIC_ERROR_MESSAGE: return a generic message +); + +ccp GetErrorText +( + int stat, // error status, err := abs(stat) + ccp ret_not_found // not NULL: return this, if no message is found + // GENERIC_ERROR_MESSAGE: return a generic message +); + +void ListErrorCodes +( + FILE * f, // valid output stream + int indent, // indent of output + bool all // true: print all user reserved messages too. +); + +enumError PrintErrorArg ( ccp func, ccp file, unsigned int line, + int syserr, enumError err_code, ccp format, va_list arg ); + +enumError PrintError ( ccp func, ccp file, unsigned int line, + int syserr, enumError err_code, ccp format, ... ) + __attribute__ ((__format__(__printf__,6,7))); + +struct File_t; +enumError PrintErrorFile ( ccp func, ccp file, uint line, struct File_t *F, + int syserr, enumError err_code, ccp format, ... ) + __attribute__ ((__format__(__printf__,7,8))); + +enumError PrintErrorStat ( enumError err, int verbose, ccp cmdname ); + +#undef ERROR +#undef ERROR0 +#undef ERROR1 +#undef FILEERROR +#undef FILEERROR0 +#undef FILEERROR1 + +#define ERROR(se,code,...) PrintError(__FUNCTION__,__FILE__,__LINE__,se,code,__VA_ARGS__) +#define ERROR0(code,...) PrintError(__FUNCTION__,__FILE__,__LINE__,0,code,__VA_ARGS__) +#define ERROR1(code,...) PrintError(__FUNCTION__,__FILE__,__LINE__,errno,code,__VA_ARGS__) + +#define FILEERROR(f,se,code,...) \ + PrintErrorFile(__FUNCTION__,__FILE__,__LINE__,f,se,code,__VA_ARGS__) +#define FILEERROR0(f,code,...) \ + PrintErrorFile(__FUNCTION__,__FILE__,__LINE__,f,0,code,__VA_ARGS__) +#define FILEERROR1(f,code,...) \ + PrintErrorFile(__FUNCTION__,__FILE__,__LINE__,f,errno,code,__VA_ARGS__) + +//----------------------------------------------------------------------------- + +bool mark_used ( ccp name, ... ); +#define MARK_USED(...) mark_used(0,__VA_ARGS__) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// hexdump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// 0: don't use XDump +// 1: use 'enable_xdump_wrapper' to decide and init it with FALSE +// 2: use 'enable_xdump_wrapper' to decide and init it with TRUE +// 3: force usage of XDump + +#ifndef ENABLE_HEXDUMP_WRAPPER + #ifdef TEST + #define ENABLE_HEXDUMP_WRAPPER 3 + #else + #define ENABLE_HEXDUMP_WRAPPER 3 + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#if ENABLE_HEXDUMP_WRAPPER + extern int enable_xdump_wrapper; +#endif + +extern ccp hexdump_prefix; +extern ccp hexdump_eol; +extern bool hexdump_align; + +// HexDump*() return the number of printed lines + +uint HexDump ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, + const void * data, size_t count ); + +uint HexDump16 ( FILE * f, int indent, u64 addr, + const void * data, size_t count ); +uint HexDump20 ( FILE * f, int indent, u64 addr, + const void * data, size_t count ); + +// HexDump0*() will supress null-only lines. + +uint HexDump0 ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, + const void * data, size_t count ); + +uint HexDump016 ( FILE * f, int indent, u64 addr, + const void * data, size_t count ); + +void HexDiff ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, + const void * p_data1, size_t count1, + const void * p_data2, size_t count2 ); + +void HexDiff16 ( FILE * f, int indent, u64 addr, + const void * data1, size_t count1, + const void * data2, size_t count2 ); +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DEBUG and TRACING /////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// ------------------- +// DEBUG and TRACING +// ------------------- +// +// If symbol 'DEBUG' or symbol _DEBUG' is defined, then and only then +// DEBUG, DASSERT and TRACING are enabled. +// +// There are to function like macros defined: +// +// TRACE ( ccp format, ... ); +// Print to console only if TRACING is enabled. +// Ignored when TRACING is disabled. +// It works like the well known printf() function and include flushing. +// +// TRACE_IF ( bool condition, ccp format, ... ); +// Like TRACE(), but print only if 'condition' is true. +// +// TRACELINE +// Print out current line and source. +// +// TRACE_SIZEOF ( object_or_type ); +// Print out `sizeof(object_or_type)´ +// +// ASSERT(cond); +// If condition is false: print info and exit program. +// +// DASSERT(cond); +// Like ASSERT; but only active on DEBUG. +// +// BINGO +// Like TRACELINE, but print to stderr instead custom file. +// +// +// There are more macros with a preceding 'no' defined: +// +// noTRACE ( ccp format, ... ); +// noTRACE_IF ( bool condition, ccp format, ... ); +// noTRACELINE ( bool condition, ccp format, ... ); +// noTRACE_SIZEOF ( object_or_type ); +// noASSERT(cond); +// +// If you add a 'no' before a TRACE-Call it is disabled always. +// This makes the usage and disabling of multi lines traces very easy. +// +// +// The macros with a preceding 'x' are always active: +// xTRACELINE +// xBINGO +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(RELEASE) + #undef TESTTRACE + #undef DEBUG + #undef _DEBUG + #undef DEBUG_ASSERT + #undef _DEBUG_ASSERT + #undef WAIT_ENABLED +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#if defined(IGNORE_DEBUG) + #undef DEBUG + #undef _DEBUG +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#undef TRACE +#undef TRACE_IF +#undef TRACELINE +#undef TRACE_SIZEOF + +#undef TRACE0 +#undef TRACE_IF0 +#undef TRACELINE0 +#undef TRACE_SIZEOF0 + +#undef TRACE1 +#undef TRACE_IF1 +#undef TRACELINE1 +#undef TRACE_SIZEOF1 + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef WIN_SZS_LIB + #define __attribute__(x) +#endif + +extern ccp TRACE_PREFIX; // never NULL +extern FILE *TRACE_FILE; +extern FILE *MEM_LOG_FILE; + +void TRACE_FUNC ( ccp format, ... ) + __attribute__ ((__format__(__printf__,1,2))); +void TRACE_ARG_FUNC ( ccp format, va_list arg ); + +void PRINT_FUNC ( ccp format, ... ) + __attribute__ ((__format__(__printf__,1,2))); +void PRINT_ARG_FUNC ( ccp format, va_list arg ); + +void BINGO_FUNC ( ccp func, int line, ccp src ); + +void WAIT_FUNC ( ccp format, ... ) + __attribute__ ((__format__(__printf__,1,2))); +void WAIT_ARG_FUNC ( ccp format, va_list arg ); + +/////////////////////////////////////////////////////////////////////////////// + +#undef TEST0 // never defined + +#ifdef TESTTRACE + + #undef DEBUG + #define DEBUG 1 + + #undef TEST + #define TEST 1 + + #undef WAIT_ENABLED + #define WAIT_ENABLED 1 + + #undef NEW_FEATURES + #define NEW_FEATURES 1 + + #define TRACE_FUNC printf + #define PRINT_FUNC printf + #define WAIT_FUNC printf + + #define TRACE_ARG_FUNC vprintf + #define PRINT_ARG_FUNC vprintf + #define WAIT_ARG_FUNC vprintf + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// always disabled +#define TRACE0(...) +#define TRACE_IF0(cond,...) +#define TRACELINE0 +#define TRACE_SIZEOF0(t) + +// always enabled +#define TRACE1(...) TRACE_FUNC(__VA_ARGS__) +#define TRACE_IF1(cond,...) if (cond) TRACE_FUNC(__VA_ARGS__) +#define TRACELINE1 TRACE_FUNC("%s() %s #%u\n",__FUNCTION__,__FILE__,__LINE__) +#define TRACE_SIZEOF1(t) TRACE_FUNC("%7zd ==%6zx/hex == sizeof(%s)\n",sizeof(t),sizeof(t),#t) + +#if defined(DEBUG) || defined(_DEBUG) + + #define HAVE_TRACE 1 + + #undef DEBUG + #define DEBUG 1 + + #undef DEBUG_ASSERT + #define DEBUG_ASSERT 1 + + #define TRACE TRACE1 + #define TRACE_IF TRACE_IF1 + #define TRACELINE TRACELINE1 + #define TRACE_SIZEOF TRACE_SIZEOF1 + + #define HEXDUMP(i,a,af,rl,d,c) HexDump(stdout,i,a,af,rl,d,c); + #define HEXDUMP16(i,a,d,c) HexDump16(stdout,i,a,d,c); + #define TRACE_HEXDUMP(i,a,af,rl,d,c) HexDump(TRACE_FILE,i,a,af,rl,d,c); + #define TRACE_HEXDUMP16(i,a,d,c) HexDump16(TRACE_FILE,i,a,d,c); + + #define HEXDIFF(i,a,af,rl,d1,c1,d2,c2) HexDiff(stdout,i,a,af,rl,d1,c1,d2,c2); + #define HEXDIFF16(i,a,d1,c1,d2,c2) HexDiff16(stdout,i,a,d1,c1,d2,c2); + #define TRACE_HEXDIFF(i,a,af,rl,d1,c1,d2,c2) HexDiff(TRACE_FILE,i,a,af,rl,d1,c1,d2,c2); + #define TRACE_HEXDIFF16(i,a,d1,c1,d2,c2) HexDiff16(TRACE_FILE,i,a,d1,c1,d2,c2); + +#else + + #define HAVE_TRACE 0 + + #undef DEBUG + + #define TRACE(...) + #define TRACE_IF(cond,...) + #define TRACELINE + #define TRACE_SIZEOF(t) + #define HEXDUMP(i,a,af,rl,d,c) + #define HEXDUMP16(a,i,d,c) + #define TRACE_HEXDUMP(i,a,af,rl,d,c) + #define TRACE_HEXDUMP16(i,a,d,c) + #define HEXDIFF(i,a,af,rl,d1,c1,d2,c2) + #define HEXDIFF16(a,i,d1,c1,d2,c2) + #define TRACE_HEXDIFF(i,a,af,rl,d1,c1,d2,c2) + #define TRACE_HEXDIFF16(i,a,d1,c1,d2,c2) + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#undef ASSERT +#undef ASSERT_MSG + +#if defined(DEBUG_ASSERT) || defined(_DEBUG_ASSERT) || defined(TEST) + + #define HAVE_ASSERT 1 + + #undef DEBUG_ASSERT + #define DEBUG_ASSERT 1 + + #define ASSERT(a) if (!(a)) ERROR0(ERR_FATAL,"ASSERTION FAILED !!!\n") + #define ASSERT_MSG(a,...) if (!(a)) ERROR0(ERR_FATAL,__VA_ARGS__) + +#else + + #define HAVE_ASSERT 0 + + #undef DEBUG_ASSERT + #define ASSERT(cond) + #define ASSERT_MSG(a,...) + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#undef PRINT +#undef PRINT_IF +#undef PRINT_SIZEOF +#undef BINGO + +#undef PRINT0 +#undef PRINT_IF0 +#undef PRINT_SIZEOF0 +#undef BINGO0 + +#undef PRINT1 +#undef PRINT_IF1 +#undef PRINT_SIZEOF1 +#undef BINGO1 + +#undef xBINGO +#undef HAVE_PRINT +#undef PRINT_TIME + +#undef HAVE_PRINT0 // always false +#define HAVE_PRINT0 0 + +// always disabled +#define PRINT0 TRACE +#define PRINT_IF0 TRACE_IF +#define PRINT_SIZEOF0 TRACE_SIZEOF +#define BINGO0 TRACELINE + +// always enabled +#define PRINT1(...) PRINT_FUNC(__VA_ARGS__) +#define PRINT_IF1(cond,...) if (cond) PRINT_FUNC(__VA_ARGS__) +#define PRINT_SIZEOF1(t) PRINT_FUNC("%7zd ==%6zx/hex == sizeof(%s)\n",sizeof(t),sizeof(t),#t) +#define BINGO1 BINGO_FUNC(__FUNCTION__,__LINE__,__FILE__) + +#if defined(DEBUG) && defined(TEST) + + #define HAVE_PRINT 1 + + #define PRINT PRINT1 + #define PRINT_IF PRINT_IF1 + #define PRINT_SIZEOF PRINT_SIZEOF1 + #define BINGO BINGO_FUNC(__FUNCTION__,__LINE__,__FILE__) + + void PRINT_TIME ( time_t time, ccp title ); + +#else + + #define HAVE_PRINT HAVE_TRACE + + #define PRINT TRACE + #define PRINT_IF TRACE_IF + #define PRINT_SIZEOF TRACE_SIZEOF + #define BINGO TRACELINE + + #define PRINT_TIME(...) + +#endif + +#define xBINGO BINGO_FUNC(__FUNCTION__,__LINE__,__FILE__) +#define xTRACELINE TRACE_FUNC("%s() line #%d @ %s\n",__FUNCTION__,__LINE__,__FILE__) + +#undef PRINTF +#define PRINTF PRINT + +/////////////////////////////////////////////////////////////////////////////// + +#undef WAIT +#undef WAIT_IF +#undef HAVE_WAIT + +#if defined(WAIT_ENABLED) + + #define HAVE_WAIT 1 + + #define WAIT(...) WAIT_FUNC(__VA_ARGS__) + #define WAIT_IF(cond,...) if (cond) WAIT_FUNC(__VA_ARGS__) + +#else + + #define HAVE_WAIT 0 + + #define WAIT(...) + #define WAIT_IF(cond,...) + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#undef DASSERT +#undef DASSERT_MSG +#undef HAVE_DASSERT + +#if defined(DEBUG) || defined(TEST) + + #define HAVE_DASSERT 1 + + #define DASSERT ASSERT + #define DASSERT_MSG ASSERT_MSG + +#else + + #define HAVE_DASSERT 0 + + #define DASSERT(cond) + #define DASSERT_MSG(a,...) + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// prefix 'no' deactivates traces + +#undef noTRACE +#undef noTRACE_IF +#undef noTRACELINE +#undef noTRACE_SIZEOF +#undef noPRINT +#undef noPRINT_IF +#undef noPRINT_TIME +#undef noWAIT +#undef noWAIT_IF +#undef noASSERT + +#ifdef TESTTRACE + + #define noTRACE TRACE + #define noTRACE_IF TRACE_IF + #define noTRACELINE TRACELINE + #define noTRACE_SIZEOF TRACE_SIZEOF + #define noPRINT PRINT + #define noPRINT_IF PRINT_IF + #define noPRINT_TIME PRINT_TIME + #define noWAIT WAIT + #define noWAIT_IF WAIT_IF + #define noASSERT ASSERT + #define noASSERT_MSG ASSERT_MSG + +#else + + #define noTRACE(...) + #define noTRACE_IF(cond,...) + #define noTRACELINE + #define noTRACE_SIZEOF(t) + #define noPRINT(...) + #define noPRINT_IF(...) + #define noPRINT_TIME(...) + #define noWAIT(...) + #define noWAIT_IF(...) + #define noASSERT(cond) + #define noASSERT_MSG(cond,...) + +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alloc/free system /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// define TRACE_ALLOC_MODE +// +// 0: standard malloc without 'out of memory' detection (don't use it!) +// 1: standard malloc with 'out of memory' detection +// 2: standard malloc with 'out of memory' detection and source identification +// 3: like mode #2, but alloc debuging enabled too +// 4: like mode #3, but alloc tracing enabled too + +// define ENABLE_MEM_CHECK (check specific memory areas) +// 0: disabled +// 1: enable a watch point for a memory location + +#ifndef TRACE_ALLOC_MODE + #ifdef MEMDEBUG + #define TRACE_ALLOC_MODE 4 + #elif TEST + #define TRACE_ALLOC_MODE 3 + #elif DEBUG + #define TRACE_ALLOC_MODE 2 + #else + #define TRACE_ALLOC_MODE 1 + #endif +#endif + +#ifndef ENABLE_MEM_CHECK + #define ENABLE_MEM_CHECK 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#if ENABLE_MEM_CHECK + extern void MemCheckSetup ( const void * ptr, unsigned int size ); + extern void MemCheck ( ccp func, ccp file, unsigned int line ); + #define MEM_CHECK_SETUP(p,s) MemCheckSetup(p,s) + #define MEM_CHECK MemCheck(__FUNCTION__,__FILE__,__LINE__) +#else + #define MEM_CHECK_SETUP(p,s) + #define MEM_CHECK +#endif + +/////////////////////////////////////////////////////////////////////////////// + +void dclib_free ( void * ptr ); + +void * dclib_xcalloc ( size_t nmemb, size_t size ); +void * dclib_xmalloc ( size_t size ); +void * dclib_xrealloc ( void * ptr, size_t size ); +char * dclib_xstrdup ( ccp src ); + +#if TRACE_ALLOC_MODE > 1 + void * dclib_calloc ( ccp,ccp,uint, size_t nmemb, size_t size ); + void * dclib_malloc ( ccp,ccp,uint, size_t size ); + void * dclib_realloc ( ccp,ccp,uint, void * ptr, size_t size ); + char * dclib_strdup ( ccp,ccp,uint, ccp src ); + char * dclib_strdup2 ( ccp,ccp,uint, ccp src1, ccp src2 ); + char * dclib_strdup3 ( ccp,ccp,uint, ccp src1, ccp src2, ccp src3 ); + void * dclib_memdup ( ccp,ccp,uint, const void * src, size_t copylen ); + void * dclib_allocdup( ccp,ccp,uint, const void * src, size_t copylen ); +#else + void * dclib_calloc ( size_t nmemb, size_t size ); + void * dclib_malloc ( size_t size ); + void * dclib_realloc ( void * ptr, size_t size ); + char * dclib_strdup ( ccp src ); + char * dclib_strdup2 ( ccp src1, ccp src2 ); + char * dclib_strdup3 ( ccp src1, ccp src2, ccp src3 ); + void * dclib_memdup ( const void * src, size_t copylen ); + void * dclib_allocdup( const void * src, size_t copylen ); +#endif + +uint trace_test_alloc ( ccp,ccp,uint, const void * ptr, bool hexdump ); +void trace_free ( ccp,ccp,uint, void * ptr ); +void * trace_calloc ( ccp,ccp,uint, size_t nmemb, size_t size ); +void * trace_malloc ( ccp,ccp,uint, size_t size ); +void * trace_realloc ( ccp,ccp,uint, void *ptr, size_t size ); +char * trace_strdup ( ccp,ccp,uint, ccp src ); +char * trace_strdup2 ( ccp,ccp,uint, ccp src1, ccp src2 ); +char * trace_strdup3 ( ccp,ccp,uint, ccp src1, ccp src2, ccp src3 ); +void * trace_memdup ( ccp,ccp,uint, const void * src, size_t copylen ); +void * trace_allocdup ( ccp,ccp,uint, const void * src, size_t copylen ); + +#if TRACE_ALLOC_MODE > 2 + void InitializeTraceAlloc(void); + int CheckTraceAlloc ( ccp func, ccp file, unsigned int line ); + void DumpTraceAlloc ( ccp func, ccp file, unsigned int line, FILE * f ); + struct mem_info_t *RegisterAlloc + ( ccp func, ccp file, uint line, cvp data, uint size, bool filler ); +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#if TRACE_ALLOC_MODE > 2 + #define TEST_ALLOC(ptr,hexd) trace_test_alloc(__FUNCTION__,__FILE__,__LINE__,ptr,hexd) + #define FREE(ptr) trace_free(__FUNCTION__,__FILE__,__LINE__,ptr) + #define CALLOC(nmemb,size) trace_calloc(__FUNCTION__,__FILE__,__LINE__,nmemb,size) + #define MALLOC(size) trace_malloc(__FUNCTION__,__FILE__,__LINE__,size) + #define REALLOC(ptr,size) trace_realloc(__FUNCTION__,__FILE__,__LINE__,ptr,size) + #define STRDUP(src) trace_strdup(__FUNCTION__,__FILE__,__LINE__,src) + #define STRDUP2(src1,src2) trace_strdup2(__FUNCTION__,__FILE__,__LINE__,src1,src2) + #define STRDUP3(src1,src2,src3) trace_strdup3(__FUNCTION__,__FILE__,__LINE__,src1,src2,src3) + #define MEMDUP(src,size) trace_memdup(__FUNCTION__,__FILE__,__LINE__,src,size) + #define ALLOCDUP(src,size) trace_allocdup(__FUNCTION__,__FILE__,__LINE__,src,size) + #define INIT_TRACE_ALLOC InitializeTraceAlloc() + #define CHECK_TRACE_ALLOC CheckTraceAlloc(__FUNCTION__,__FILE__,__LINE__) + #define DUMP_TRACE_ALLOC(f) DumpTraceAlloc(__FUNCTION__,__FILE__,__LINE__,f) +#elif TRACE_ALLOC_MODE > 1 + #define TEST_ALLOC(ptr,hexd) + #define FREE(ptr) dclib_free(ptr) + #define CALLOC(nmemb,size) dclib_calloc(__FUNCTION__,__FILE__,__LINE__,nmemb,size) + #define MALLOC(size) dclib_malloc(__FUNCTION__,__FILE__,__LINE__,size) + #define REALLOC(ptr,size) dclib_realloc(__FUNCTION__,__FILE__,__LINE__,ptr,size) + #define STRDUP(src) dclib_strdup(__FUNCTION__,__FILE__,__LINE__,src) + #define STRDUP2(src1,src2) dclib_strdup2(__FUNCTION__,__FILE__,__LINE__,src1,src2) + #define STRDUP3(src1,src2,src3) dclib_strdup3(__FUNCTION__,__FILE__,__LINE__,src1,src2,src3) + #define MEMDUP(src,size) dclib_memdup(__FUNCTION__,__FILE__,__LINE__,src,size) + #define ALLOCDUP(src,size) dclib_allocdup(__FUNCTION__,__FILE__,__LINE__,src,size) + #define INIT_TRACE_ALLOC + #define CHECK_TRACE_ALLOC + #define DUMP_TRACE_ALLOC(f) +#else + #define TEST_ALLOC(ptr,hexd) + #define FREE(ptr) dclib_free(ptr) + #define CALLOC(nmemb,size) dclib_calloc(nmemb,size) + #define MALLOC(size) dclib_malloc(size) + #define REALLOC(ptr,size) dclib_realloc(ptr,size) + #define STRDUP(src) dclib_strdup(src) + #define STRDUP2(src1,src2) dclib_strdup2(src1,src2) + #define STRDUP3(src1,src2,src3) dclib_strdup3(src1,src2,src3) + #define MEMDUP(src,size) dclib_memdup(src,size) + #define ALLOCDUP(src,size) dclib_allocdup(src,size) + #define INIT_TRACE_ALLOC + #define CHECK_TRACE_ALLOC + #define DUMP_TRACE_ALLOC(f) +#endif + +#define XFREE(ptr) dclib_free(ptr) + +#ifndef DCLIB_DEBUG_C + #define free do_not_use_free + #define calloc do_not_use_calloc + #define malloc do_not_use_malloc + #define realloc do_not_use_realloc + #undef strdup + #define strdup do_not_use_strdup +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_DEBUG_H diff --git a/project/dclib/dclib-file.c b/project/dclib/dclib-file.c new file mode 100644 index 0000000..8a69370 --- /dev/null +++ b/project/dclib/dclib-file.c @@ -0,0 +1,5360 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dclib-basics.h" +#include "dclib-file.h" +#include "dclib-debug.h" +#include "dclib-network.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// termios support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static struct termios termios_data; +int termios_valid = 0; + +void ResetTermios() +{ + if ( termios_valid > 0 ) + tcsetattr(0,TCSANOW,&termios_data); +} + +static bool LoadTermios() +{ + if (!termios_valid) + { + termios_valid = -1; + if ( isatty(0) && !tcgetattr(0,&termios_data) ) + termios_valid = 1; + } + return termios_valid > 0; +} + +bool EnableSingleCharInput() +{ + if (!LoadTermios()) + return false; + + struct termios tios; + memcpy(&tios,&termios_data,sizeof(tios)); + tios.c_lflag &= ~(ICANON|ECHO); + tcsetattr(0,TCSANOW,&tios); + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum FileMode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFileModeStatus +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + FileMode_t file_mode, // filemode to print + uint print_mode // 0: short vector: 1 char for 1 attrib + // 1: long mode: keyword for each set attrib +) +{ + struct info_t + { + FileMode_t mode; + char ch; + ccp name; + }; + + static const struct info_t info[] = + { + { FM_TEST, 't', "test" }, + { FM_SILENT, 's', "silent" }, + { FM_IGNORE, 'i', "ignore" }, + { FM_MODIFY, 'M', "modify" }, + { FM_APPEND, 'A', "append" }, + { FM_UPDATE, 'U', "update" }, + { FM_OVERWRITE, 'O', "overwrite" }, + { FM_NUMBER, 'N', "number" }, + { FM_REMOVE, 'R', "remove" }, + { FM_MKDIR, 'M', "mkdir" }, + { FM_STDIO, 'S', "stdio" }, + { FM_TCP, 'I', "tcp" }, + { FM_DEV, 'D', "dev" }, + { FM_SPC, 'P', "spc" }, + { FM_TOUCH, 'H', "touch" }, + { FM_TEMP, 'T', "temp" }, + {0,0,0} + }; + + if ( print_mode == 0 ) + { + if ( !buf || !buf_size ) + buf = GetCircBuf( buf_size = 16 ); + char *dest = buf, *end = buf + buf_size - 1; + const struct info_t *ip; + for ( ip = info; ip->mode && dest < end; ip++ ) + *dest++ = ip->mode & file_mode ? ip->ch : '-'; + *dest = 0; + return buf; + } + + if ( !buf || !buf_size ) + buf = GetCircBuf( buf_size = 100 ); + + char *dest = buf, *end = buf + buf_size - 1; + const struct info_t *ip; + for ( ip = info; ip->mode && dest < end; ip++ ) + if ( ip->mode & file_mode ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,ip->name); + } + *dest = 0; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFileOpenMode +( + bool create, // false: open, true: create + FileMode_t file_mode // open modes +) +{ + if ( file_mode & FM_TEST ) + return "r"; + + if (!create) + return file_mode & FM_MODIFY ? "r+b" : "rb"; + + switch ( (uint)file_mode & (FM_MODIFY|FM_APPEND) ) + { + case FM_MODIFY | FM_APPEND : return "a+b"; + case FM_MODIFY : return "w+b"; + case FM_APPEND : return "ab"; + default : return "wb"; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct FileAttrib_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +FileAttrib_t * ClearFileAttrib +( + FileAttrib_t * dest // NULL or destination attribute +) +{ + if (dest) + memset(dest,0,sizeof(*dest)); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +FileAttrib_t * TouchFileAttrib +( + FileAttrib_t * dest // valid destination attribute +) +{ + DASSERT(dest); + dest->mtime = dest->ctime = dest->atime = dest->itime = time(0); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +FileAttrib_t * SetFileAttrib +( + FileAttrib_t * dest, // valid destination attribute + const FileAttrib_t * src_fa, // NULL or source attribute + const struct stat * src_stat // NULL or source attribute + // only used if 'src_fa==NULL' +) +{ + DASSERT(dest); + + if (src_fa) + memcpy(dest,src_fa,sizeof(*dest)); + else if (src_stat) + { + memset(dest,0,sizeof(*dest)); + if ( S_ISREG(src_stat->st_mode) ) + { + dest->mtime = src_stat->st_mtime; + dest->ctime = src_stat->st_ctime; + dest->itime = dest->mtime > dest->ctime ? dest->mtime : dest->ctime; + dest->atime = src_stat->st_atime; + dest->size = src_stat->st_size; + } + dest->mode = src_stat->st_mode; + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +FileAttrib_t * MaxFileAttrib +( + FileAttrib_t * dest, // valid source and destination attribute + const FileAttrib_t * src_fa, // NULL or second source attribute + const struct stat * src_stat // NULL or third source attribute +) +{ + DASSERT(dest); + + if (src_fa) + { + if ( dest->mtime < src_fa->mtime ) dest->mtime = src_fa->mtime; + if ( dest->ctime < src_fa->ctime ) dest->ctime = src_fa->ctime; + if ( dest->atime < src_fa->atime ) dest->atime = src_fa->atime; + if ( dest->itime < src_fa->itime ) dest->itime = src_fa->itime; + + if ( dest->size < src_fa->size ) dest->size = src_fa->size; + + dest->mode = src_fa->mode; + } + + if (src_stat) + { + if ( S_ISREG(src_stat->st_mode) ) + { + if ( dest->mtime < src_stat->st_mtime ) dest->mtime = src_stat->st_mtime; + if ( dest->ctime < src_stat->st_ctime ) dest->ctime = src_stat->st_ctime; + if ( dest->atime < src_stat->st_atime ) dest->atime = src_stat->st_atime; + + if ( dest->itime < dest->mtime ) dest->itime = dest->mtime; + if ( dest->itime < dest->ctime ) dest->itime = dest->ctime; + + if ( dest->size < src_stat->st_size ) dest->size = src_stat->st_size; + } + dest->mode = src_stat->st_mode; + } + + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +FileAttrib_t * NormalizeFileAttrib +( + FileAttrib_t * fa // valid attribute +) +{ + DASSERT(fa); + + if ( fa->size < 0 ) + fa->size = 0; + + time_t mtime = fa->mtime; + if (!mtime) + { + mtime = fa->itime; + if (!mtime) + { + mtime = fa->ctime; + if (!mtime) + mtime = fa->atime; + } + } + fa->mtime = mtime; + + if (!fa->itime) + fa->itime = fa->ctime > mtime ? fa->ctime : mtime; + + if (!fa->ctime) + fa->ctime = fa->itime > mtime ? fa->itime : mtime; + + if (!fa->atime) + fa->atime = fa->itime > fa->ctime ? fa->itime : fa->ctime; + + return fa; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// simple UNIX+TCP socket connect /////////////// +/////////////////////////////////////////////////////////////////////////////// +// not stored in dclib-network.c to allow static linking! + +int ConnectUnixTCP +( + ccp fname, // unix socket filename + bool silent // true: suppress error messages +) +{ + DASSERT(fname); + + + //--- check file name + + struct sockaddr_un sa; + const uint flen = fname ? strlen(fname)+1 : 0; + if ( flen > sizeof(sa.sun_path) ) + { + if (!silent) + ERROR0(ERR_CANT_CONNECT, + "Path name to long for UNIX/STREAM socket: %s\n",fname); + return -1; + } + + memset(&sa,0,sizeof(sa)); + memcpy(sa.sun_path,fname,flen); + sa.sun_family = AF_UNIX; + + + //--- create socket + + const int sock = socket(AF_UNIX,SOCK_STREAM,0); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CONNECT, + "Can't create UNIX/STREAM socket: %s\n",fname); + return -1; + } + + + //--- connect + + if (connect(sock,(struct sockaddr *)&sa,sizeof(sa))) + { + if (!silent) + ERROR1(ERR_CANT_CONNECT, + "Can't connect to UNIX/STREAM socket: %s\n",fname); + close(sock); + return -1; + } + + return sock; +} + +/////////////////////////////////////////////////////////////////////////////// + +int (*ConnectTCP_Hook) +( + // if set: ConnectNumericTCP() uses ConnectTCP() + + ccp addr, // TCP address: ['tcp':] IPv4 [:PORT] + int default_port, // NULL or default port, if not found in 'addr' + bool silent // true: suppress error messages +) = 0; + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNumericIP4 +( + // returns next unread character or NULL on error + + ccp addr, // address to scan + u32 *r_ipv4, // not NULL: store result here (local endian) + u32 *r_port, // not NULL: scan port too (local endian) + uint default_port // return this if no port found +) +{ + if (!addr) + { + abort: + if (r_ipv4) + *r_ipv4 = 0; + if (r_port) + *r_port = 0; + return 0; + } + + u32 num[4] = {0}; + uint i; + ccp ptr = addr; + for ( i = 0; i < 4; ) + { + char *end; + uint temp = str2ul(ptr,&end,10); + if ( !end || end == ptr ) + break; + num[i++] = temp; + ptr = addr = end; + if ( *ptr != '.' ) + break; + ptr++; + } + + uint ip4; + switch (i) + { + case 1: + ip4 = num[0]; + break; + + case 2: + ip4 = num[0] << 24 | num[1]; + break; + + case 3: + ip4 = num[0] << 24 | num[1] << 16 | num[2]; + break; + + case 4: + ip4 = num[0] << 24 | num[1] << 16 | num[2] << 8 | num[3]; + break; + + default: + goto abort; + } + if (r_ipv4) + *r_ipv4 = ip4; + + if (r_port) + { + if ( *addr == ':' ) + { + char *end; + uint num = str2ul(addr+1,&end,10); + if ( end && end > addr+1 && num < 0x10000 ) + { + addr = end; + default_port = num; + } + } + *r_port = default_port; + } + + return (char*)addr; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t ScanNumericIP4Mem +( + // returns unread character or NullMem on error + + mem_t addr, // address to scan + u32 *r_ipv4, // not NULL: store result here (local endian) + u32 *r_port, // not NULL: scan port too (local endian) + uint default_port // return this if no port found +) +{ + if ( !addr.ptr || !addr.len ) + { + abort: + if (r_ipv4) + *r_ipv4 = 0; + if (r_port) + *r_port = 0; + return NullMem; + } + + u32 num[4] = {0}; + uint i; + ccp ptr = addr.ptr; + ccp end = ptr + addr.len; + + for ( i = 0; i < 4; ) + { + uint temp; + ccp next = ScanNumber(&temp,ptr,end,10,10); + if ( !next || next == ptr ) + break; + num[i++] = temp; + addr = BehindMem(addr,next); + ptr = addr.ptr; + if ( *ptr != '.' ) + break; + ptr++; + } + + uint ip4; + switch (i) + { + case 1: + ip4 = num[0]; + break; + + case 2: + ip4 = num[0] << 24 | num[1]; + break; + + case 3: + ip4 = num[0] << 24 | num[1] << 16 | num[2]; + break; + + case 4: + ip4 = num[0] << 24 | num[1] << 16 | num[2] << 8 | num[3]; + break; + + default: + goto abort; + } + if (r_ipv4) + *r_ipv4 = ip4; + + if (r_port) + { + ptr = addr.ptr; + if ( ptr < end && *ptr == ':' ) + { + ptr++; + uint num; + ccp next = ScanNumber(&num,ptr,end,10,10); + if ( next > ptr && num < 0x10000 ) + { + addr = BehindMem(addr,next); + default_port = num; + } + } + *r_port = default_port; + } + + return addr; +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns a NETWORK MASK "/a.b.c.d" or as CIDR number "/num" between 0 and 32. +// An optional slash '/' at the beginning is skipped. +// Returns modified 'source' if a MASK or CDIR is detected. +// If no one is detected, source is unmodified and returned mask = ~0. + +mem_t ScanNetworkMaskMem ( u32 *mask, mem_t source ) +{ + u32 m = M1(m); + if ( source.ptr && source.len ) + { + ccp ptr = source.ptr; + ccp end = ptr + source.len; + if ( *ptr == '/' ) + ptr++; + + uint num; + ccp next = ScanNumber(&num,ptr,end,10,10); + if ( next > ptr ) + { + if ( next < end && *next == '.' ) + { + // is a network mask + source = ScanNumericIP4Mem(BehindMem(source,ptr),&m,0,0); + } + else if ( num <= 32 ) + { + m = num ? ~(( 1 << (32-num) ) - 1) : 0; + source = BehindMem(source,next); + } + } + } + + if (mask) + *mask = m; + return source; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ConnectNumericTCP +( + // like ConnectTCP(), but without name resolution (only numeric ip+port) + + ccp addr, // TCP address: ['tcp':] IPv4 [:PORT] + int default_port, // NULL or default port, if not found in 'addr' + bool silent // true: suppress error messages +) +{ + if (ConnectTCP_Hook) + return ConnectTCP_Hook(addr,default_port,silent); + + ccp unix_path = CheckUnixSocketPath(addr,0); + if (unix_path) + return ConnectUnixTCP(unix_path,silent); + + if ( !strncasecmp(addr,"tcp:",4) ) + addr += 4; + + u32 ip4, port; + ccp end = ScanNumericIP4(addr,&ip4,&port,default_port); + if (!end) + { + if (!silent) + ERROR1(ERR_CANT_CONNECT, + "Invalid IPv4 address: %s\n",addr); + return -1; + } + + int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CONNECT, + "Can't create socket: %s\n",addr); + return -1; + } + + struct sockaddr_in sa; + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(ip4); + sa.sin_port = htons(port); + + if (connect(sock,(struct sockaddr *)&sa,sizeof(sa))) + { + if (!silent) + ERROR1(ERR_CANT_CONNECT, + "Can't connect to %s\n",PrintIP4(0,0,ip4,port) ); + close(sock); + return -1; + } + + return sock; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct File_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeFile +( + File_t * f // file structure +) +{ + DASSERT(f); + memset(f,0,sizeof(*f)); + f->fname = EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ResetFile +( + File_t * f, // file structure + uint set_time // 0: don't set + // 1: set time before closing using 'fatt' + // 2: set current time before closing +) +{ + DASSERT(f); + const enumError err = CloseFile(f,set_time); + FreeString(f->fname); + if (f->data_alloced) + FREE(f->data); + InitializeFile(f); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CloseFile +( + File_t * f, // file structure + uint set_time // 0: don't set + // 1: set time before closing using 'fatt' + // 2: set current time before closing +) +{ + DASSERT(f); + TRACE("CloseFile(%p,%d) fname=%s\n",f,set_time,f->fname); + + if (f->f) + { + if (!f->is_stdio) + { + if ( fclose(f->f) && f->max_err <= ERR_WARNING ) + f->max_err = ERROR1(ERR_WRITE_FAILED, + "Error while closing file: %s\n",f->fname); + + if (!f->is_socket) + { + if ( f->fmode & FM_TEMP ) + { + TRACE("UNLINK TEMP %s\n",f->fname); + unlink(f->fname); + } + else if ( f->is_writing ) + { + if ( f->max_err > ERR_WARNING ) + { + TRACE("UNLINK %s\n",f->fname); + unlink(f->fname); + } + else if ( set_time == 1 && f->fatt.mtime ) + { + struct utimbuf ubuf; + ubuf.actime = f->fatt.atime + ? f->fatt.atime : f->fatt.mtime; + ubuf.modtime = f->fatt.mtime; + utime(f->fname,&ubuf); + } + else if ( set_time > 1 || f->fmode & FM_TOUCH ) + { + utime(f->fname,0); + } + } + } + } + f->f = 0; + } + + return f->max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool is_seekable +( + File_t * F // file structure +) +{ + const mode_t mode = F->st.st_mode; + F->is_seekable = ( S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode) ) + && F->st.st_size + && lseek(fileno(F->f),0,SEEK_SET) != (off_t)-1; + return F->is_seekable; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenFile +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + FileMode_t file_mode, // open modes + off_t limit, // >0: don't open, if file size > limit + ccp limit_message // NULL or a with LF terminated text. + // It is printed after the message. +) +{ + DASSERT(f); + DASSERT(fname); + + if (initialize) + InitializeFile(f); + else + ResetFile(f,false); + f->fmode = file_mode & FM_M_OPEN; + + TRACE("OpenFile(%p,%d,,%s,%llu) [%s] %s\n", + f, initialize, GetFileModeStatus(0,0,file_mode,0), (u64)limit, + GetFileModeStatus(0,0,f->fmode,1), fname ); + + + //--- test for TCP + + if ( file_mode & FM_TCP && !strncasecmp(fname,"tcp:",4) ) + { + const int fd = ConnectNumericTCP(fname,0,(f->fmode&FM_SILENT)!=0); + if ( fd == -1 ) + return f->max_err = ERR_CANT_OPEN; + + f->f = fdopen(fd,"rw"); + f->is_socket = f->is_reading = f->is_writing = true; + return f->max_err; + } + + + //--- test for stdin + + if ( *fname == '-' && !fname[1] ) + { + f->fname = MinusString; + if ( f->fmode & FM_STDIO ) + { + f->is_stdio = true; + f->f = stdin; + fstat(fileno(stdin),&f->st); + is_seekable(f); + return ERR_OK; + } + } + + f->fname = STRDUP(fname); + + + //--- test 'unix:' prefix + + bool have_unix = file_mode & FM_TCP && !strncasecmp(fname,"unix:",5); + if (have_unix) + fname += 5; + + + //--- does file exist? + + for(;;) + { + if (!stat(fname,&f->st)) + break; + + if (have_unix) + { + have_unix = false; + fname -= 5; + continue; + } + + not_found: + memset(&f->st,0,sizeof(f->st)); + if ( f->fmode & FM_IGNORE ) + return f->max_err = ERR_NOT_EXISTS; + + if ( !(f->fmode & FM_SILENT) ) + ERROR1(ERR_CANT_OPEN,"Can't find file: %s\n",fname); + return f->max_err = ERR_CANT_OPEN; + } + + + //--- UNIX socket -> try STREAM connect + + if (S_ISSOCK(f->st.st_mode)) + { + f->fmode &= ~(FM_REMOVE|FM_TEMP); + + const int fd = ConnectUnixTCP(fname,(f->fmode&FM_SILENT)!=0); + if ( fd == -1 ) + return f->max_err = ERR_CANT_OPEN; + + f->f = fdopen(fd,"rw"); + f->is_socket = f->is_reading = f->is_writing = true; + return f->max_err; + } + + if (have_unix) + goto not_found; + + + //--- check size limit + + SetFileAttrib(&f->fatt,0,&f->st); + if ( limit && f->st.st_size > limit ) + { + char sbuf[12], lbuf[12]; + PrintSize1024(sbuf,sizeof(sbuf),(u64)f->st.st_size,false); + PrintSize1024(lbuf,sizeof(lbuf),(u64)limit,false); + + // be tolerant, if both values produce same text + if (strcmp(sbuf,lbuf)) + { + if ( !(f->fmode & FM_SILENT) ) + ERROR0(ERR_CANT_OPEN, + "File too large (size=%s, limit=%s): %s\n%s", + sbuf, lbuf, fname, + limit_message ? limit_message : "" ); + return f->max_err = ERR_CANT_OPEN; + } + } + + + //--- open file + + ccp omode = GetFileOpenMode(false,f->fmode); + f->f = fopen(fname,omode); + TRACE("OpenFile(%s,%s) : [%s], %p\n", + fname, omode, GetFileModeStatus(0,0,f->fmode,1), f->f ); + + if (!f->f) + { + if ( !(f->fmode & FM_SILENT) ) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",fname); + return f->max_err = ERR_CANT_OPEN; + } + + f->is_reading = true; + if ( f->fmode & FM_MODIFY ) + f->is_writing = true; + + is_seekable(f); + return f->max_err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CheckCreateFile +( + // returns: + // ERR_DIFFER: [FM_STDIO] source is "-" => 'st' is zeroed + // ERR_WARNING: [FM_DEV,FM_SOCK,FM_SPC] not a regular file + // ERR_WRONG_FILE_TYPE: file exists, but is not a regular file + // ERR_ALREADY_EXISTS: file already exists + // ERR_INVALID_VERSION: already exists, but FM_NUMBER set => no msg printed + // ERR_CANT_CREATE: FM_UPDATE is set, but file don't exist + // ERR_OK: file not exist or can be overwritten + + ccp fname, // filename to open + FileMode_t file_mode, // open modes + struct stat *st // not NULL: store file status here +) +{ + DASSERT(fname); + + if ( file_mode & FM_STDIO && fname[0] == '-' && !fname[1] ) + { + if (st) + memset(st,0,sizeof(*st)); + return ERR_DIFFER; + } + + struct stat local_st; + if (!st) + st = &local_st; + + if (!stat(fname,st)) + { + if ( S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) ) + { + if ( file_mode & FM_DEV ) + return ERR_WARNING; + + if ( !(file_mode & FM_SILENT) ) + ERROR0( ERR_ALREADY_EXISTS, + "Can't write to %s device: %s\n", + S_ISBLK(st->st_mode) ? "block" : "character", fname ); + return ERR_WRONG_FILE_TYPE; + } + + if ( S_ISSOCK(st->st_mode) ) + { + if ( file_mode & FM_SOCK ) + return ERR_WARNING; + + if ( !(file_mode & FM_SILENT) ) + ERROR0( ERR_ALREADY_EXISTS, + "Can't write to UNIX socket: %s\n", fname ); + return ERR_WRONG_FILE_TYPE; + } + + if (!S_ISREG(st->st_mode)) + { + if ( file_mode & FM_SPC ) + return ERR_WARNING; + + if ( !(file_mode & FM_SILENT) ) + ERROR0( ERR_WRONG_FILE_TYPE, + "Not a plain file: %s\n", fname ); + return ERR_WRONG_FILE_TYPE; + } + + if (!( file_mode & (FM_OVERWRITE|FM_REMOVE|FM_UPDATE|FM_APPEND) )) + { + if ( file_mode & FM_NUMBER ) + return ERR_INVALID_VERSION; + + if ( !(file_mode & FM_SILENT) ) + ERROR0( ERR_ALREADY_EXISTS, + "File already exists: %s\n", fname ); + return ERR_ALREADY_EXISTS; + } + } + else + { + memset(st,0,sizeof(*st)); + if ( file_mode & FM_UPDATE ) + { + if ( !(file_mode & FM_SILENT) ) + ERROR0(ERR_CANT_CREATE, + "Try to update non existing file: %s\n", fname ); + return ERR_CANT_CREATE; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateFile +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + FileMode_t file_mode // open modes +) +{ + DASSERT(f); + DASSERT(fname); + + if (initialize) + InitializeFile(f); + else + ResetFile(f,false); + f->fmode = file_mode & FM_M_CREATE; + + noPRINT("CreateFile(%p,%d,,%s) [%s] %s\n", + f, initialize, GetFileModeStatus(0,0,file_mode,0), + GetFileModeStatus(0,0,f->fmode,1), fname ); + + + //--- test for stdout + + if ( *fname == '-' && !fname[1] ) + { + f->fname = MinusString; + if ( f->fmode & FM_STDIO ) + { + f->is_stdio = true; + f->f = stdout; + fstat(fileno(stdout),&f->st); + is_seekable(f); + return ERR_OK; + } + } + + + //--- mode calculations + +// if ( f->fmode & FM_UPDATE ) +// f->fmode = f->fmode & ~(FM_REMOVE|FM_MKDIR) | FM_OVERWRITE; + + enumError err = CheckCreateFile(fname,f->fmode,&f->st); + + char num_fname[PATH_MAX]; + if ( err == ERR_INVALID_VERSION ) + { + NumberedFilename(num_fname,sizeof(num_fname),fname,0,0,false); + fname = num_fname; + err = CheckCreateFile( fname, f->fmode & ~FM_NUMBER, &f->st ); + } + + f->fname = STRDUP(fname); + if ( f->fmode & (FM_MODIFY|FM_APPEND) ) + SetFileAttrib(&f->fatt,0,&f->st); + + if (err) + { + if ( err > ERR_WARNING ) + return err; + + // is a device, socket or special file + err = ERR_OK; + f->fmode = f->fmode & ~(FM_REMOVE|FM_TEMP) | FM_OVERWRITE; + } + + if ( f->fmode & FM_TEST ) + { + // never create or remove a file in TEST mode. + return ERR_OK; + } + + if (S_ISSOCK(f->st.st_mode)) + { + const int fd = ConnectUnixTCP(fname,(f->fmode&FM_SILENT)!=0); + if ( fd == -1 ) + return f->max_err = ERR_CANT_OPEN; + + f->f = fdopen(fd,"rw"); + f->is_socket = f->is_reading = f->is_writing = true; + return ERR_OK; + } + + if ( f->st.st_mode && f->fmode & FM_REMOVE ) + { + unlink(fname); + + struct stat st; + if (!stat(fname,&st)) + { + if ( !(f->fmode & FM_SILENT) ) + ERROR0(ERR_REMOVE_FAILED,"Can't remove file: %s\n",fname); + return f->max_err = ERR_REMOVE_FAILED; + } + } + + ccp omode = GetFileOpenMode(true,f->fmode); + f->f = fopen(fname,omode); + noPRINT("CreateFile(%s,%s) : [%s], %p\n", + fname, omode, GetFileModeStatus(0,0,f->fmode,1), f->f ); + + if ( !f->f ) + { + if ( f->fmode & FM_MKDIR ) + { + CreatePath(fname,false); + f->f = fopen(fname,omode); + } + + if ( !f->f ) + { + if ( !(f->fmode & FM_SILENT) ) + ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",fname); + return f->max_err = ERR_CANT_CREATE; + } + } + + f->is_writing = true; + if ( f->fmode & FM_MODIFY) + f->is_reading = true; + + if (!fstat(fileno(f->f),&f->st)) + { + const mode_t mode = f->st.st_mode; + f->is_seekable = S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode); + } + + return f->max_err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError WriteFileAt +( + File_t * F, // file to write + size_t * cur_offset, // pointer to current file offset, modified + size_t offset, // offset to write + const void * data, // data to write + size_t size // size of 'data' +) +{ + DASSERT(F); + DASSERT(F->f); + DASSERT(data||!size); + DASSERT(cur_offset); + + if (!size) + return ERR_OK; + + if ( offset != *cur_offset ) + { + if (F->is_seekable) + { + noPRINT("FSEEK %zx -> %zx\n",*cur_offset,offset); + if (fseek(F->f,offset,SEEK_SET)) + return ERROR1(ERR_WRITE_FAILED,"Can't set file pointer: %s\n",F->fname); + } + else if ( offset < *cur_offset ) + { + noPRINT("FSEEK %zx -> %zx\n",*cur_offset,offset); + return ERROR0(ERR_WRITE_FAILED, + "Can't set file pointer on non seekable file: %s\n",F->fname); + } + else + { + char buf[0x8000] = {0}; + size_t fill_size = offset - *cur_offset; + while (fill_size) + { + const size_t write_size = fill_size < sizeof(buf) ? fill_size : sizeof(buf); + noPRINT("FILL/ZERO %zx/%zx\n",write_size,fill_size); + const size_t written = fwrite(buf,1,write_size,F->f); + if ( written != write_size ) + return ERROR1(ERR_WRITE_FAILED,"Writing %zu NULL bytes failed: %s\n", + write_size, F->fname ); + fill_size -= write_size; + } + } + } + + noPRINT("WRITE %zx @%zx -> %zx\n",size,offset,offset+size); + const size_t written = fwrite(data,1,size,F->f); + if ( written != size ) + return ERROR1(ERR_WRITE_FAILED,"Writing %zu bytes at offset %zu failed: %s\n", + size,offset,F->fname); + + *cur_offset = offset + size; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SetFileSize +( + File_t * F, // file to write + size_t * cur_offset, // pointer to current file offset, modified + size_t size // offset to write +) +{ + DASSERT(F); + DASSERT(F->f); + DASSERT(cur_offset); + + fflush(F->f); + if (!F->is_seekable) + return WriteFileAt(F,cur_offset,size,0,0); + + if (ftruncate(fileno(F->f),size)) + { + if ( F->max_err < ERR_WRITE_FAILED ) + F->max_err = ERR_WRITE_FAILED; + return ERROR1( ERR_WRITE_FAILED, + "Set file size to %llu failed: %s\n", + (u64)size, F->fname ); + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SkipFile +( + File_t *F, // file to write + size_t skip // number of bytes to skip +) +{ + DASSERT(F); + DASSERT(F->f); + + if (!skip) + return ERR_OK; + + if ( F->is_seekable && !fseek(F->f,skip,SEEK_CUR) ) + return ERR_OK; + + char buf[0x8000]; + while ( skip > 0 ) + { + size_t max = skip < sizeof(buf) ? skip : sizeof(buf); + size_t stat = fread(buf,1,max,F->f); + if (!stat) + { + if (feof(F->f)) + break; + return ERROR1(ERR_READ_FAILED, + "Reading %zu to skip failed: %s\n", + skip, F->fname ); + } + skip -= stat; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError RegisterFileError +( + File_t * f, // file structure + enumError new_error // new error code +) +{ + DASSERT(f); + if ( f->max_err < new_error ) + f->max_err = new_error; + return f->max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +s64 GetFileSize +( + ccp path1, // NULL or part 1 of path + ccp path2, // NULL or part 2 of path + s64 not_found_val, // return value if no regular file found + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store max values to 'fatt' +) +{ + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + TRACE("GetFileSize(%s,%lld)\n",path,not_found_val); + + struct stat st; + if ( stat(path,&st) || !S_ISREG(st.st_mode) ) + { + if ( fatt && !fatt_max ) + memset(fatt,0,sizeof(*fatt)); + return not_found_val; + } + + if (fatt) + { + if (fatt_max) + MaxFileAttrib(fatt,0,&st); + else + SetFileAttrib(fatt,0,&st); + } + + return st.st_size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenReadFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t file_mode, // open modes + off_t limit, // >0: don't open, if file size > limit + ccp limit_message, // NULL or a with LF terminated text. + // It is printed after the message. + + u8 ** res_data, // store alloced data here (always NULL terminated) + uint * res_size, // not NULL: store data size + ccp * res_fname, // not NULL: store alloced filename + FileAttrib_t * res_fatt // not NULL: store file attributes +) +{ + DASSERT( path1 || path2 ); + DASSERT(res_data); + + *res_data = 0; + if (res_size) + *res_size = 0; + if (res_fname) + *res_fname = 0; + if (res_fatt) + memset(res_fatt,0,sizeof(*res_fatt)); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + PRINT("OpenReadFile(%s,%s)\n",path,GetFileModeStatus(0,0,file_mode,0)); + + File_t F; + enumError err = OpenFile(&F,true,path,file_mode,limit,limit_message); + if (err) + return err; + + uint data_size = F.st.st_size; + if (!F.is_seekable) + data_size = limit; + noPRINT("OpenReadFile() fsize=%u, seekable=%d\n",data_size,F.is_seekable); + u8 *data = MALLOC(data_size+1); + + size_t read_size = fread(data,1,data_size,F.f); + if ( read_size && !F.is_seekable ) + { + PRINT("non seekable read: %zu\n",read_size); + data_size = read_size; + data = REALLOC(data,data_size+1); + } + data[data_size] = 0; // termination for text files + + if ( read_size != data_size ) + { + if (!(file_mode&FM_SILENT)) + FILEERROR1(&F,ERR_READ_FAILED,"Read failed: %s\n",path); + ResetFile(&F,false); + FREE(data); + return ERR_READ_FAILED; + } + + *res_data = data; + if (res_size) + *res_size = data_size; + if (res_fname) + { + *res_fname = F.fname; + F.fname = EmptyString; + } + if (res_fatt) + SetFileAttrib(res_fatt,&F.fatt,0); + + ResetFile(&F,false); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // skip num of bytes before reading + void * data, // destination buffer, size = 'size' + size_t size, // size to read + int silent, // 0: print all error messages + // 1: suppress file size warning + // 2: suppress all error messages + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store *max* values to 'fatt' +) +{ + ASSERT(data); + if (!size) + return ERR_OK; + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + TRACE("LoadFile(%s,%zu,%zu,%d)\n",path,skip,size,silent); + + if (fatt) + { + struct stat st; + if (!stat(path,&st)) + UseFileAttrib(fatt,0,&st,fatt_max); + } + + FILE * f = fopen(path,"rb"); + if (!f) + { + if (silent<2) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path); + return ERR_CANT_OPEN; + } + + if ( skip > 0 ) + fseek(f,skip,SEEK_SET); + + size_t read_stat = fread(data,1,size,f); + fclose(f); + + if ( read_stat == size ) + return ERR_OK; + + enumError err = ERR_READ_FAILED; + if ( silent == 1 && size > 0 ) + err = ERR_WARNING; + else if ( silent < 2 ) + ERROR1(ERR_READ_FAILED,"Can't read file: %s\n",path); + + noPRINT("D=%p, s=%zu/%zu: %s\n",data,read_stat,size,path); + if ( read_stat >= 0 && read_stat < size ) + memset((char*)data+read_stat,0,size-read_stat); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadFileAlloc +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // skip num of bytes before reading + u8 ** res_data, // result: free existing data, store ptr to alloc data + // always one more byte is alloced and set to NULL + size_t * res_size, // result: size of 'res_data' + size_t max_size, // >0: a file size limit + int silent, // 0: print all error messages + // 1: suppress file size warning + // 2: suppress all error messages + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store max values to 'fatt' +) +{ + DASSERT(res_data); + DASSERT(res_size); + + //--- clear return data + + if (res_data) + *res_data = 0; + + if (res_size) + *res_size = 0; + + if ( fatt && !fatt_max ) + memset(fatt,0,sizeof(*fatt)); + + + //--- get file size + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + + const s64 size = GetFileSize(path,0,-1,0,0); + if ( size == -1 ) + { + if ( silent < 2 ) + ERROR0(ERR_SYNTAX,"File not found: %s\n",path); + return ERR_SYNTAX; + } + + if ( max_size && size > max_size ) + { + if ( silent < 2 ) + ERROR0(ERR_INVALID_FILE,"File too large: %s\n",path); + return ERR_INVALID_FILE; + } + + u8 *data = MALLOC(size+1); + enumError err = LoadFile(path,0,skip,data,size,silent,fatt,fatt_max); + if (err) + { + FREE(data); + return err; + } + + if (res_data) + { + data[size] = 0; + *res_data = data; + } + else + FREE(data); + + if (res_size) + *res_size = size; + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t file_mode, // open modes + const void * data, // data to write + uint data_size, // size of 'data' + FileAttrib_t * fatt // not NULL: set timestamps using this attrib +) +{ + DASSERT(data); + + File_t F; + enumError err = OpenWriteFile(&F,true,path1,path2,file_mode,data,data_size); + if ( err == ERR_OK ) + { + if (fatt) + memcpy(&F.fatt,fatt,sizeof(F.fatt)); + err = ResetFile(&F,true); + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenWriteFile +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t file_mode, // open modes + const void * data, // data to write + uint data_size // size of 'data' +) +{ + DASSERT(f); + DASSERT(data); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + TRACE("SaveFile(%s) %s\n",GetFileModeStatus(0,0,file_mode,0),path); + + enumError err = CreateFile(f,true,path,file_mode); + if ( err || !f->f ) + { + ResetFile(f,0); + return err; + } + + const uint written = fwrite(data,1,data_size,f->f); + if ( written != data_size ) + { + ERROR1(ERR_WRITE_FAILED,"Write to file failed: %s\n",path); + ResetFile(f,0); + return ERR_WRITE_FAILED; + } + + return ERR_OK; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int CompareInt ( const int * a, const int *b ) +{ + DASSERT( a && b ); + return *a < *b ? -1 : *a > *b; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CloseAllExcept +( + int *list, // exception list of unordered FD, max contain -1 + uint n_list, // number of elements in 'list' + int min, // close all until and including this fd + int add // if a FD was found, incremet 'min' to 'fd+add' +) +{ + //--- use a private copy of the except list + + enum { LOCAL_LIST_SIZE = 1000 }; + int local_list[LOCAL_LIST_SIZE]; + int *except_list = n_list < LOCAL_LIST_SIZE + ? local_list : MALLOC((n_list+1)*sizeof(*except_list)); + + int *ptr = except_list; + while ( n_list-- > 0 ) + { + const int fd = *list++; + if ( fd != -1 ) + *ptr++ = fd; + } + *ptr = -1; + + //--- ... and sort it + + n_list = ptr - except_list; + if ( n_list > 1 ) + qsort(except_list,n_list,sizeof(*except_list),(qsort_func)CompareInt); + + + //--- adjust 'min' and 'add' + + if ( add <= 0 ) + add = 1; + const int temp = ( n_list ? except_list[n_list-1] : 0 ) + add; + if ( min < temp ) + min = temp; + + + //--- close files + + ptr = except_list; + int *end = ptr + n_list; + int i, count = 0; + for ( i = 0; i <= min; i++ ) + { + while ( ptr < end && *ptr < i ) + ptr++; + if ( i != *ptr && !close(i) ) + { + count++; + const int temp = i + add; + if ( min < temp ) + min = temp; + //printf("closed: %d/%d\n",i,min); + } + } + + if ( except_list != local_list ) + FREE(local_list); + return count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CopyFile*(), TransferFile() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef LOGPRINT +//#define LOGPRINT PRINT +#define LOGPRINT noPRINT + +/////////////////////////////////////////////////////////////////////////////// + +bool IsSameFile ( ccp path1, ccp path2 ) +{ + if (!strcmp(path1,path2)) + return true; + + struct stat st1, st2; + if ( stat(path1,&st1) || stat(path2,&st2) ) + return false; + + return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CopyFileHelper +( + ccp src, // source path + ccp dest, // destination path + int open_flags, // flags for open() + mode_t open_mode, // mode for open() + uint temp_and_move // >0: ignore open_flags, + // create temp, then move temp to dest +) +{ + LOGPRINT("CopyFileHelper(%x,%x,%d) %s -> %s\n", + open_flags, open_mode, temp_and_move>0, src, dest ); + + int fd_dest, fd_src; + char buf[16*1024], temp[PATH_MAX]; + + if ( !dest || !src || !*dest || !*src ) + return ERR_MISSING_PARAM; + + if (IsSameFile(src,dest)) + return ERR_OK; + + fd_src = open(src,O_RDONLY); + if ( fd_src < 0 ) + return ERR_CANT_OPEN; + + if (temp_and_move) + { + StringCat2S(temp,sizeof(temp),dest,"XXXXXX"); + fd_dest = mkstemp(temp); + LOGPRINT("CopyFileHelper(TEMP) %s -> %d\n",temp,fd_dest); + } + else + fd_dest = open(dest,open_mode,open_mode); + if ( fd_dest < 0 ) + goto error; + + ssize_t nread; + while ( nread = read(fd_src,buf,sizeof(buf)), nread > 0 ) + { + char *ptr = buf; + do + { + ssize_t nwritten = write(fd_dest,ptr,nread); + + if ( nwritten >= 0 ) + { + nread -= nwritten; + ptr += nwritten; + } + else if ( errno != EINTR ) + goto error; + } + while (nread > 0); + } + + if ( nread == 0 ) + { + if ( close(fd_dest) < 0 ) + { + fd_dest = -1; + goto error; + } + if (temp_and_move) + { + chmod(temp,open_mode); + if (rename(temp,dest)) + { + unlink(temp); + goto error; + } + } + close(fd_src); + return ERR_OK; + } + + error:; + int saved_errno = errno; + + close(fd_src); + if ( fd_dest >= 0 ) + { + close(fd_dest); + if (temp_and_move) + unlink(temp); + } + + errno = saved_errno; + return ERR_WRITE_FAILED; +} + +//----------------------------------------------------------------------------- + +enumError CopyFile +( + ccp src, // source path + ccp dest, // destination path + mode_t open_mode // mode for open() +) +{ + return CopyFileHelper(src,dest,O_WRONLY|O_TRUNC|O_EXCL,open_mode,false); +} + +//----------------------------------------------------------------------------- + +enumError CopyFileCreate +( + ccp src, // source path + ccp dest, // destination path + mode_t open_mode // mode for open() +) +{ + return CopyFileHelper(src,dest,O_WRONLY|O_CREAT|O_EXCL,open_mode,false); +} + +//----------------------------------------------------------------------------- + +enumError CopyFileTemp +( + ccp src, // source path + ccp dest, // destination path + mode_t open_mode // mode for open() +) +{ + return CopyFileHelper(src,dest,0,open_mode,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError TransferFile +( + ccp src, // source path + ccp dest, // destination path + TransferMode_t tfer_mode, // transfer mode + mode_t open_mode // mode for CopyFile*() -> open() +) +{ + if ( !dest || !src || !*dest || !*src ) + return ERR_MISSING_PARAM; + + if (IsSameFile(src,dest)) + return ERR_OK; + + if ( tfer_mode & TFMD_J_MOVE1 ) + { + LOGPRINT("TransferFile(MOVE1) %s -> %s\n",src,dest); + struct stat st; + if ( !stat(src,&st) && st.st_nlink == 1 && !rename(src,dest) ) + return true; + } + else if ( tfer_mode & TFMD_J_MOVE ) + { + LOGPRINT("TransferFile(MOVE) %s -> %s\n",src,dest); + if (!rename(src,dest)) + return true; + } + + if ( tfer_mode & TFMD_J_LINK ) + { + LOGPRINT("TransferFile(RM_DEST) %s\n",dest); + if ( tfer_mode & TFMD_J_RM_DEST ) + unlink(dest); + + LOGPRINT("TransferFile(LINK) %s -> %s\n",src,dest); + if (!link(src,dest)) + return true; + } + + if ( tfer_mode & TFMD_J_COPY ) + { + LOGPRINT("TransferFile(COPY) %s -> %s\n",src,dest); + enumError err = tfer_mode & TFMD_J_RM_DEST + ? CopyFileTemp(src,dest,open_mode) + : CopyFile(src,dest,open_mode); + if ( err == ERR_OK ) + { + if ( tfer_mode & TFMD_J_RM_SRC ) + { + PRINT("TransferFile(RM_SRC) %s",src); + unlink(src); + } + return true; + } + } + + return ERR_ERROR; +} + +#undef LOGPRINT + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MemFile_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool IsValidMemFile ( MemFile_t *mf ) +{ + return mf + && ( mf->data || !mf->size ) + && mf->size >= mf->fend + && mf->fend >= mf->fpos; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpMemFile +( + FILE *f, // valid output stream + int indent, // indent of output + MemFile_t *mf // valid MemFile_t data +) +{ + DASSERT(f); + DASSERT(mf); + + indent = NormalizeIndent(indent); + fprintf(f,"%*sfpos=0x%x/0x%x, size=0x%x/0x%x, zero=%d, valid=%d\n", + indent,"", + mf->fpos, mf->fend, mf->size, mf->max_size, + mf->zero_extend, IsValidMemFile(mf) ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool AssertMemFile +( + MemFile_t *mf // valid MemFile_t data +) +{ + if (IsValidMemFile(mf)) + return true; + + if (mf) + DumpMemFile(stderr,0,mf); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u8 * GetDataMemFile +( + // return NULL on err or a pointer to the data posisiton. + + MemFile_t *mf, // valid MemFile_t data + uint fpos, // fpos to read from + uint size // needed data size +) +{ + DASSERT(mf); + uint end = fpos + size; + if ( end < fpos || end < size ) + return 0; // integer overflow + + if ( end > mf->max_size ) + return 0; // too large + + if ( end > mf->size ) + { + // grow buffer + uint new_size = ALIGN32( end + end/10 + 0x1000, 0x1000 ); + if ( new_size > mf->max_size ) + new_size = mf->max_size; + DASSERT( new_size >= end ); + mf->data = REALLOC(mf->data,new_size); + DASSERT(mf->data); + memset(mf->data+mf->size,0,new_size-mf->size); + mf->size = new_size; + } + DASSERT( end <= mf->size ); + + mf->fpos = fpos; + if ( mf->fend < end ) + mf->fend = end; + DASSERT(AssertMemFile(mf)); + + return mf->data + fpos; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMemFile +( + MemFile_t *mf, // structure to initialize + uint start_size, // >0: alloc this for data + uint max_size // >0: file never grows above 'max_size' +) +{ + DASSERT(mf); + memset(mf,0,sizeof(*mf)); + mf->max_size = max_size ? max_size : M1(mf->max_size); + + if ( start_size > 0 ) + { + GetDataMemFile( mf, 0, start_size < mf->max_size ? start_size : mf->max_size ); + mf->fend = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMemFile +( + MemFile_t *mf // valid MemFile_t data +) +{ + DASSERT(mf); + FREE(mf->data); + if (mf->err_name_alloced) + FreeString(mf->err_name); + InitializeMemFile(mf,0,mf->max_size); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError WriteMemFileAt +( + MemFile_t *mf, // valid MemFile_t data + uint fpos, // fpos to write + const void *data, // data to write + uint size // size to write +) +{ + DASSERT(mf); + DASSERT(data||!size); + + u8 *dest = GetDataMemFile(mf,fpos,size); + if (!dest) + { + if (mf->err_name) + ERROR0(ERR_WRITE_FAILED,"Memory file is full: %s\n",mf->err_name); + return ERR_WRITE_FAILED; + } + + mf->eof = false; + if (size) + memcpy(dest,data,size); + mf->fpos = fpos + size; + if ( mf->fend < mf->fpos ) + mf->fend = mf->fpos; + + DASSERT(AssertMemFile(mf)); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ReadMemFileAt +( + MemFile_t *mf, // valid MemFile_t data + uint fpos, // fpos to read from + void *buf, // destination buffer + uint size // size to read +) +{ + DASSERT(mf); + DASSERT(buf); + + uint read_count = 0; + u8 *dest = buf; + if ( fpos < mf->fend ) + { + read_count = mf->fend - fpos; + if ( read_count > size ) + read_count = size; + memcpy(dest,mf->data+fpos,read_count); + mf->fpos = fpos + read_count; + dest += read_count; + size -= read_count; + } + + mf->eof = size > 0; + if ( mf->eof && mf->zero_extend ) + { + memset(dest,0,size); + mf->fpos += size; + read_count += size; + } + + return read_count; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadMemFile +( + MemFile_t *mf, // MemFile_t data + bool init_mf, // true: initalize 'mf' first + + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // >0: skip num of bytes before reading + size_t size, // >0: max size to read; 0: read all (remaining) + bool silent // true: suppress error messages +) +{ + DASSERT(mf); + + if (init_mf) + InitializeMemFile(mf,0,0); + else + ResetMemFile(mf); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + PRINT("LoadMemFile(silent=%d) %s\n",silent,path); + + struct stat st; + if (stat(path,&st)) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't get file status: %s\n",path); + return ERR_CANT_OPEN; + } + UseFileAttrib(&mf->fatt,0,&st,0); + + if ( skip >= st.st_size ) + return ERR_OK; + + const uint max_size = st.st_size - skip; + if ( !size || size > max_size ) + size = max_size; + + u8 *dest = GetDataMemFile(mf,0,size); + if (!dest) + { + if (!silent) + ERROR0(ERR_READ_FAILED,"Memory file is too small: %s\n",path); + return ERR_READ_FAILED; + } + + FILE * f = fopen(path,"rb"); + if (!f) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path); + return ERR_CANT_OPEN; + } + + if ( skip > 0 ) + fseek(f,skip,SEEK_SET); + + mf->fend = fread(dest,1,size,f); + fclose(f); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveMemFile +( + MemFile_t *mf, // MemFile_t data + + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t fmode, // mode for file creation + bool sparse // true: enable sparse support +) +{ + DASSERT(mf); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + PRINT("SaveMemFile(fmode=0x%x,sparse=%d) %u/%u/%u %s\n", + fmode, sparse, mf->fpos, mf->fend, mf->size, path ); + + File_t F; + enumError err = CreateFile(&F,true,path,fmode); + if ( err || !F.f ) + { + ResetFile(&F,0); + return err; + } + + if (!sparse) + { + const uint written = fwrite(mf->data,1,mf->fend,F.f); + if ( written != mf->fend ) + { + if (!(fmode&FM_SILENT)) + ERROR1(ERR_WRITE_FAILED,"Write to file failed: %s\n",path); + err_exit: + ResetFile(&F,0); + return ERR_WRITE_FAILED; + } + } + else + { + enum { BLOCK_SIZE = 512 }; + char zero[BLOCK_SIZE]; + memset(zero,0,sizeof(zero)); + + size_t cur_offset = 0; + uint off = 0; + while ( off < mf->fend ) + { + //--- skip sparse blocks + + while ( off < mf->fend ) + { + uint max = mf->fend - off; + if ( max > BLOCK_SIZE ) + max = BLOCK_SIZE; + if (memcmp(mf->data+off,zero,max)) + break; + off += max; + } + + //--- search next sparse block + + uint start = off; + while ( off < mf->fend ) + { + uint max = mf->fend - off; + if ( max > BLOCK_SIZE ) + max = BLOCK_SIZE; + if (!memcmp(mf->data+off,zero,max)) + break; + off += max; + } + + err = WriteFileAt(&F,&cur_offset,start,mf->data+start,off-start); + if (err) + goto err_exit; + } + + err = SetFileSize(&F,&cur_offset,mf->fend); + if (err) + goto err_exit; + } + return ResetFile(&F,true); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TraceLog_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool OpenTraceLog ( TraceLog_t *tl ) +{ + if ( !tl || tl->level < 0 ) + return false; + + if ( !tl->log && tl->fname ) + { + tl->log = fopen(tl->fname,"wb"); + if (tl->log) + { + fcntl(fileno(tl->log),F_SETFD,FD_CLOEXEC); + fprintf(tl->log,"# %s, pid=%d\n", + PrintTimeByFormat("%F %T %z",time(0)), getpid() ); + } + } + + if (!tl->log) + { + tl->level = -1; + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool TraceLogText ( TraceLog_t *tl, ccp text ) +{ + if (!OpenTraceLog(tl)) + return false; + + if (text) + { + fputs(text,tl->log); + fflush(tl->log); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool TraceLogPrint ( TraceLog_t *tl, ccp format, ... ) +{ + if (!OpenTraceLog(tl)) + return false; + + if (format) + { + va_list arg; + va_start(arg,format); + vfprintf(tl->log,format,arg); + va_end(arg); + fflush(tl->log); + } + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct LineBuffer_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifndef __APPLE__ +/////////////////////////////////////////////////////////////////////////////// + +void InitializeLineBuffer ( LineBuffer_t *lb, uint max_buf_size ) +{ + DASSERT(lb); + memset(lb,0,sizeof(*lb)); + InitializeGrowBuffer(&lb->buf,max_buf_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetLineBuffer ( LineBuffer_t *lb ) +{ + DASSERT(lb); + CloseLineBuffer(lb); + ResetGrowBuffer(&lb->buf); + FREE(lb->line); + lb->line = 0; + lb->seq_count++; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearLineBuffer ( LineBuffer_t *lb ) +{ + DASSERT(lb); + ClearGrowBuffer(&lb->buf); + lb->used_lines = 0; + lb->seq_count++; +} + +/////////////////////////////////////////////////////////////////////////////// + +void RedirectLineBuffer +( + LineBuffer_t *lb, // valid line buffer + FILE *f, // output file, if NULL use 'lb->old_fd' + int max_lines // >=0: max number of redirected lines +) +{ + DASSERT(lb); + if ( lb->buf.used && max_lines ) + { + if (!f) + f = lb->old_fp; + if (f) + { + if ( max_lines < 0 ) + fwrite(lb->buf.ptr,1,lb->buf.used,f); + else + { + mem_t *line; + uint n = GetMemListLineBuffer(lb,&line,-max_lines); + while ( n-- > 0 ) + { + fprintf(f,"%.*s\n",line->len-1,line->ptr); + line++; + } +//DEL fprintf(f,"==> %zd %d\n", +//DEL line[-1].ptr + line[-1].len - (ccp)lb->buf.ptr, +//DEL lb->buf.used ); + } + + fflush(f); + if ( f == stdout ) + stdout_seq_count++; + } + } + ClearGrowBuffer(&lb->buf); + lb->used_lines = 0; + lb->seq_count++; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +LineBuffer_t * OpenLineBuffer +( + LineBuffer_t *lb, // line buffer; if NULL, malloc() one + bool init_lb, // true: initialize 'lb' + FILE **fp_pos, // NULL or external place of the file pointer + uint max_lines, // max numbers of lines + uint max_line_size, // >0: max size of a single line + uint max_buf_size // >0: max total buffer size +) +{ + if (!lb) + lb = MALLOC(sizeof(*lb)); + else if (!init_lb) + ResetLineBuffer(lb); + InitializeLineBuffer(lb,max_buf_size); + + DASSERT(lb); + if (fp_pos) + { + lb->old_fp = *fp_pos; + lb->old_fp_pos = fp_pos; + } + + lb->max_lines = max_lines > 1 ? max_lines : 1; + lb->max_line_size = max_line_size; + lb->line = MALLOC(lb->max_lines*sizeof(*lb->line)); + + static cookie_io_functions_t funcs = + { + 0, // read + (cookie_write_function_t*)WriteLineBuffer, + 0, // seek + (cookie_close_function_t*)CloseLineBuffer + }; + lb->fp = fopencookie(lb,"wb",funcs); + if ( fp_pos && lb->fp ) + *fp_pos = lb->fp; + return lb; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CloseLineBuffer ( LineBuffer_t *lb ) +{ + DASSERT(lb); + if (lb->fp) + { + if ( lb->old_fp_pos && *lb->old_fp_pos == lb->fp ) + *lb->old_fp_pos = lb->old_fp; + lb->fp = 0; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ssize_t WriteLineBuffer ( LineBuffer_t *lb, ccp buf, size_t size ) +{ + DASSERT(lb); + + if ( lb->redirect > 0 ) + { + if (lb->old_fp) + { + ssize_t res = fwrite(buf,1,size,lb->old_fp); + fflush(lb->old_fp); + if ( lb->old_fp == stdout ) + stdout_seq_count++; + return res; + } + } + else if (size) + { + if ( lb->open_line && lb->buf.used ) + lb->buf.used--; + + if ( lb->buf.max_size ) + { + const int max_used = lb->buf.max_size - size - 1; + if ( max_used <= 0 ) + { + ClearGrowBuffer(&lb->buf); + buf -= max_used; + size += max_used; + } + else if ( lb->buf.used > max_used ) + { + u8 *ptr = lb->buf.ptr; + u8 *end = ptr + lb->buf.used; + ptr += lb->buf.used - max_used; + while ( ptr < end ) + if ( *ptr++ == '\n' ) + break; + DropGrowBuffer(&lb->buf,ptr-lb->buf.ptr); + } + } + + uint new_lines = 0; + ccp ptr = buf, end = ptr + size; + while ( ptr < end ) + if ( *ptr++ == '\n' ) + new_lines++; + lb->line_count += new_lines; + + InsertGrowBuffer(&lb->buf,buf,size); + lb->open_line = buf[size-1] != '\n'; + if (lb->open_line) + InsertCharGrowBuffer(&lb->buf,'\n'); + + FixMemListLineBuffer(lb,true); + lb->seq_count++; + PRINT("WRITE %3zu bytes, is_open: %d, total lines: %d\n", + size, lb->open_line, lb->used_lines ); + } + return size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void FixMemListLineBuffer ( LineBuffer_t *lb, bool force ) +{ + DASSERT(lb); + if (!lb->buf.used) + lb->used_lines = 0; + else if ( force + || lb->prev_buf_ptr != lb->buf.ptr + || lb->prev_seq_count != lb->seq_count ) + { + u8 *beg = lb->buf.ptr; + u8 *ptr = beg + lb->buf.used; + + mem_t *dest = lb->line + lb->max_lines; + while ( beg < ptr && dest > lb->line ) + { + u8* end = ptr--; + while ( beg < ptr && ptr[-1] != '\n' ) + ptr--; + + dest--; + dest->ptr = (ccp)ptr; + dest->len = end - ptr; + } + + lb->used_lines = lb->line + lb->max_lines - dest; + if ( dest > lb->line ) + memmove(lb->line,dest,lb->used_lines*sizeof(*dest)); + + if ( ptr > beg ) + { + // don't use DropGrowBuffer() to hold buf stable + lb->buf.used -= ptr - beg; + lb->buf.ptr = ptr; + } + + lb->prev_buf_ptr = lb->buf.ptr; + lb->prev_seq_count = lb->seq_count; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetMemListLineBuffer +( + // returns the number of elements + + LineBuffer_t *lb, // line buffer; if NULL, malloc() one + mem_t **res, // store pointer to first element here + int max_lines // !0: limit lines, <0: count from end +) +{ + DASSERT(lb); + + if ( lb->prev_buf_ptr != lb->buf.ptr ) + FixMemListLineBuffer(lb,false); + + uint pos = 0; + if (!max_lines) + max_lines = lb->used_lines; + else if ( max_lines < 0 ) + { + max_lines = -max_lines; + if ( max_lines > lb->used_lines ) + max_lines = lb->used_lines; + pos = lb->used_lines - max_lines; + } + else if ( max_lines > lb->used_lines ) + max_lines = lb->used_lines; + + if (res) + *res = lb->line + pos; + return max_lines; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintLineBuffer +( + // returns number of written lines + + FILE *f, // output stream + int indent, // indention + LineBuffer_t *lb, // line buffer; if NULL, malloc() one + int max_lines, // !0: limit lines, <0: count from end + ccp open_line // not NULL: append this text for an open line +) +{ + DASSERT(f); + DASSERT(lb); + indent = NormalizeIndent(indent); + + mem_t *line; + uint i, n = GetMemListLineBuffer(lb,&line,max_lines); + for ( i = 1; i <= n; i++, line++ ) + fprintf(f,"%*s%.*s%s\n", + indent,"", + line->len-1, line->ptr, + open_line && i == n && lb->open_line ? open_line : "" ); + return n; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif // !__APPLE__ +/////////////////////////////////////////////////////////////////////////////// + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// file helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IsDirectory +( + // analyse filename (last char == '/') and stat(fname) + + ccp fname, // NULL or path + int answer_if_empty // answer, if !fname || !*fname +) +{ + if ( !fname || !*fname ) + return answer_if_empty; + + if ( *fname == '-' && !fname[1] ) + return false; + + if ( fname[strlen(fname)-1] == '/' ) + return true; + + struct stat st; + return !stat(fname,&st) && S_ISDIR(st.st_mode); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ExistDirectory +( + // check only real file (stat(fname)) + + ccp fname, // NULL or path + int answer_if_empty // answer, if !fname || !*fname +) +{ + if ( !fname || !*fname ) + return answer_if_empty; + + if ( *fname == '-' && !fname[1] ) + return false; + + struct stat st; + return !stat(fname,&st) && S_ISDIR(st.st_mode); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsSameFilename ( ccp fn1, ccp fn2 ) +{ + if ( !fn1 || !fn2 ) + return false; + + while ( fn1[0] == '.' && fn1[1] == '/' ) + fn1 += 2; + while ( fn2[0] == '.' && fn2[1] == '/' ) + fn2 += 2; + + return !strcmp(fn1,fn2); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool enable_config_search_log = false; + +ccp FindConfigFile +( + // returns NULL or found file (alloced) + + FindConfigFile_t mode, // bit field: select search destinations + ccp share_name, // NULL or + ccp file_name, // NULL or + ccp prog_ext // , if NULL use '.' + +) +{ + struct stat st; + char path[PATH_MAX]; + + //--- empty strings like NULL + + if ( share_name && !*share_name ) share_name = progname; + if ( file_name && !*file_name ) file_name = 0; + if ( prog_ext && !*prog_ext ) prog_ext = 0; + + if ( file_name && share_name ) + { + //--- the first attemps need FILENAME + + if ( mode & FCF_HOME ) + { + ccp home = getenv("HOME"); + if ( home && *home ) + { + snprintf(path,sizeof(path),"%s/.%s/%s",home,share_name,file_name); + if ( mode & FCF_F_DEBUG ) + fprintf(stderr,"SEARCH: %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return STRDUP(path); + } + } + + if ( mode & FCF_REL_SHARE ) + { + ccp pdir = ProgramDirectory(); + if (pdir) + { + snprintf(path,sizeof(path),"%s/../share/%s/%s",pdir,share_name,file_name ); + if ( mode & FCF_F_DEBUG ) + fprintf(stderr,"SEARCH: %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return STRDUP(path); + } + } + + if ( mode & FCF_LOC_SHARE ) + { + snprintf(path,sizeof(path),"/usr/local/share/%s/%s",share_name,file_name); + if ( mode & FCF_F_DEBUG ) + fprintf(stderr,"SEARCH: %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return STRDUP(path); + } + + if ( mode & FCF_USR_SHARE ) + { + snprintf(path,sizeof(path),"/usr/share/%s/%s",share_name,file_name); + if ( mode & FCF_F_DEBUG ) + fprintf(stderr,"SEARCH: %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return STRDUP(path); + } + } + + if ( mode & (FCF_PROG_SHARE|FCF_PROG) && ( prog_ext || file_name ) ) + { + ccp ppath = ProgramPathNoExt(); + if (ppath) + { + if ( mode & FCF_PROG_SHARE ) + { + ccp pdir = ProgramDirectory(); + noPRINT("pdir=%s\n",pdir); + if (pdir) + { + ccp fname = strrchr(ppath,'/'); + fname = fname ? fname+1 : ppath; + + if (prog_ext) + snprintf(path,sizeof(path),"%s/share/%s%s", + pdir, fname, prog_ext ); + else + snprintf(path,sizeof(path),"%s/share/%s.%s", + pdir, fname, file_name ); + if ( mode & FCF_F_DEBUG ) + fprintf(stderr,"SEARCH: %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return STRDUP(path); + } + } + + if ( mode & FCF_PROG ) + { + if (prog_ext) + snprintf(path,sizeof(path),"%s%s",ppath,prog_ext); + else + snprintf(path,sizeof(path),"%s.%s",ppath,file_name); + if ( mode & FCF_F_DEBUG ) + fprintf(stderr,"SEARCH: %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return STRDUP(path); + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int SearchFiles +( + ccp path1, // not NULL: part #1 of base path + ccp path2, // not NULL: part #2 of base path + ccp match, // not NULL: filter files by MatchPattern() + // if empty: extract from combined path + SearchFilesFunc func, // callback function, never NULL + void *param // third parameter of func() +) +{ + DASSERT(func); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + + if ( match && !*match ) + { + match = 0; + if ( path != pathbuf ) + { + // path is modified! + StringCopyS(pathbuf,sizeof(pathbuf),path); + path = pathbuf; + } + + char *found = strrchr(pathbuf,'/'); + if (found) + { + if ( found == pathbuf ) + path = "/"; + *found++ = 0; + match = *found ? found : 0; + } + else + { + match = pathbuf; + path = "."; + } + } + + if ( !path || !*path ) + path = "."; + + DIR *fdir = opendir(path); + int stat = errno; + + if (fdir) + { + for(;;) + { + struct dirent *dent = readdir(fdir); + if (!dent) + break; + + if ( !match || MatchPatternFull(match,dent->d_name) ) + { + stat = func(path,dent,param); + if ( stat < 0 ) + break; + } + } + closedir(fdir); + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreatePath +( + ccp path, // path to create + bool is_pure_dir // true: 'path' don't contains a filename part +) +{ + TRACE("CreatePath(%s)\n",path); + + char buf[PATH_MAX]; + char *dest = StringCopyS(buf,sizeof(buf)-1,path); + if (is_pure_dir) + { + *dest++ = '/'; + *dest = 0; + } + dest = buf; + + for(;;) + { + // skip slashes + while ( *dest == '/' ) + dest++; + + // search end of current directory + while ( *dest && *dest != '/' ) + dest++; + if (!*dest) + break; + + *dest = 0; + if ( mkdir(buf,0777) && errno != EEXIST && !IsDirectory(buf,0) ) + { + noTRACE("CREATE-DIR: %s -> err=%d (ENOTDIR=%d)\n",buf,errno,ENOTDIR); + if ( errno == ENOTDIR ) + { + while ( dest > buf && *dest != '/' ) + dest--; + if ( dest > buf ) + *dest = 0; + } + return ERROR1( ERR_CANT_CREATE_DIR, + errno == ENOTDIR + ? "Not a directory: %s\n" + : "Can't create directory: %s\n", buf ); + } + TRACE("CREATE-DIR: %s -> OK\n",buf); + *dest++ = '/'; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError RemoveSource +( + ccp fname, // file to remove + ccp dest_fname, // NULL or dest file name + // If real pathes are same: don't remove + bool print_log, // true: print a log message + bool testmode // true: don't remove, log only +) +{ + DASSERT(fname); + if (dest_fname) + { + if (!strcmp(fname,dest_fname)) + return ERR_OK; + + char p1[PATH_MAX], p2[PATH_MAX]; + if ( realpath(fname,p1) && realpath(dest_fname,p2) && !strcmp(p1,p2) ) + return ERR_OK; + } + + if ( print_log || testmode ) + fprintf( stdlog ? stdlog : stdout, + "%sREMOVE %s\n", + testmode ? "WOULD " : "", fname ); + + return !testmode && unlink(fname) + ? ERROR1( ERR_CANT_REMOVE, "Can't remove source file: %s\n", fname ) + : ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * FindFilename +( + // Find the last part of the path, trailing '/' are ignored + // return poiner to found string + + ccp path, // path to analyze, valid string + uint * result_len // not NULL: return length of found string +) +{ + DASSERT(path); + + ccp end = path + strlen(path); + if ( end > path && end[-1] == '/' ) + end--; + + ccp start = end; + while ( start > path && start[-1] != '/' ) + start--; + if (result_len) + *result_len = end - start; + return (char*)start; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint NumberedFilename +( + // return used index number or 0, if not modified. + + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + ccp source, // source filename, may be part of 'buf' + // if NULL or empty: + ccp ext, // not NULL & not empty: file extension ('.ext') + uint ext_mode, // only relevant, if 'ext' is neither NULL not empty + // 0: use 'ext' only for detection + // 1: replace 'source' extension by 'ext' + // 2: append 'ext' if 'source' differ + // 3: append 'ext' always + bool detect_stdio // true: don't number "-" +) +{ + //--- analyse stdin/stdout + + char *end = StringCopyS(buf,bufsize,source); + if ( !source || !*source || detect_stdio && *source == '-' && !source[1] ) + return 0; + + + //--- analyse parameter 'ext' + + uint ext_len = 0; + if ( ext && *ext ) + { + ext_len = strlen(ext); + if ( ext_mode >= 3 ) + { + // append extension always + StringCopyE(end,buf+bufsize,ext); + } + else if ( end - buf <= ext_len || memcmp(end-ext_len,ext,ext_len) ) + { + // extensions differ + if (!ext_mode) + ext_len = 0; + else if ( ext_mode == 2 ) + StringCopyE(end,buf+bufsize,ext); + else + { + char *slash = strrchr(buf,'/'); + char *point = strrchr(buf,'.'); + if ( point && ( !slash || point > slash ) ) + end = point; + StringCopyE(end,buf+bufsize,ext); + } + } + } + + + //--- does files exist? => if not: return without numbering + + struct stat st; + if (stat(buf,&st)) + return 0; + + + + //--- split filename into dir + name + index + ext + + char temp[PATH_MAX]; + + ccp dir = buf, search_dir; + uint dir_len; + ccp name = strrchr(buf,'/'); + if (name) + { + dir_len = name++ - dir; + StringCopyS(temp,sizeof(temp),buf); + if ( dir_len < sizeof(temp) ) + temp[dir_len] = 0; + search_dir = temp; + dir_len++; + } + else + { + name = buf; + dir_len = 0; + dir = ""; + search_dir = "."; + } + + uint name_len; + if (ext_len) + { + name_len = strlen(name); + if ( ext_len >= name_len ) + goto find_ext; + + name_len -= ext_len; + ext = name + name_len; + } + else + { + find_ext: + ext = strrchr(name,'.'); + if ( ext && ext > name ) + { + name_len = ext - name; + ext_len = strlen(ext); + } + else + { + name_len = strlen(name); + ext_len = 0; + ext = ""; + } + } + + ccp ptr = name + name_len - 1; + while ( ptr >= name && *ptr >= '0' && *ptr <= '9' ) + ptr--; + name_len = ++ptr - name; + uint index = strtoul(ptr,0,10); + + PRINT("|%s| : |%.*s|%.*s|#%u|%.*s|\n", + search_dir, dir_len, dir, name_len, name, index, ext_len, ext ); + + + //--- iterate directory to find highest index + + DIR *fdir = opendir(search_dir); + if (fdir) + { + for(;;) + { + struct dirent *dent = readdir(fdir); + if (!dent) + break; + + ccp fname = dent->d_name; + const uint fname_len = strlen(fname); + if ( fname_len > name_len + ext_len + && !strncasecmp(name,fname,name_len) ) + { + ccp fext = fname + fname_len - ext_len; + if (!strncasecmp(ext,fext,ext_len)) + { + char *end; + uint idx = strtoul(fname+name_len,&end,10); + if ( end == fext && idx > index ) + index = idx; + } + } + } + closedir(fdir); + } + + + //--- create new filename + + index++; + snprintf(temp,sizeof(temp),"%.*s%u%s",name_len,name,index,ext); + StringCopyE(buf+dir_len,buf+bufsize,temp); + + return index; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// normalize filenames /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * NormalizeFileName +( + // returns a pointer to the NULL terminator within 'buf' + + char * buf, // valid destination buffer + uint buf_size, // size of buf + ccp source, // NULL or source + bool allow_slash, // true: allow '/' in source + bool is_utf8 // true: enter UTF-8 mode +) +{ + DASSERT(buf); + DASSERT(buf_size>1); + + char *dest = buf, *end = buf + buf_size - 1; + TRACE("NormalizeFileName(%s,%d)\n",source,allow_slash); + + if (source) + { + #ifdef __CYGWIN__ + if (allow_slash) + { + const int drv_len = IsWindowsDriveSpec(source); + if (drv_len) + { + dest = StringCopyE(dest,end,"/cygdrive/c/"); + if ( dest < end ) + dest[-2] = tolower((int)*source); + source += drv_len; + } + } + #endif + + bool skip_space = true; + while ( *source && dest < end ) + { + unsigned char ch = *source++; + if ( ch == ':' ) + { + if (!skip_space) + *dest++ = ' '; + if ( dest + 2 <= end ) + { + *dest++ = '-'; + *dest++ = ' '; + } + skip_space = true; + } + else + { + if ( isalnum(ch) + || ( is_utf8 + ? ch >= 0x80 + : ch == 0xe4 // ä + || ch == 0xf6 // ö + || ch == 0xfc // ü + || ch == 0xdf // ß + || ch == 0xc4 // Ä + || ch == 0xd6 // Ö + || ch == 0xdc // Ü + ) + || strchr("_+-=%'\"$%&,.!()[]{}<>",ch) + || ch == '/' && allow_slash ) + { + *dest++ = ch; + skip_space = false; + } + #ifdef __CYGWIN__ + else if ( ch == '\\' && allow_slash ) + { + *dest++ = '/'; + skip_space = false; + } + #endif + else if (!skip_space) + { + *dest++ = ' '; + skip_space = true; + } + } + } + } + if ( dest > buf && dest[-1] == ' ' ) + dest--; + + DASSERT( dest <= end ); + *dest = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __CYGWIN__ + + uint IsWindowsDriveSpec + ( + // returns the length of the found windows drive specification (0|2|3) + + ccp src // valid string + ) + { + DASSERT(src); + + if ( ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' ) + && src[1] == ':' ) + { + if (!src[2]) + return 2; + + if ( src[2] == '/' || src[2] == '\\' ) + return 3; + } + return 0; + } + +#endif // __CYGWIN__ + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __CYGWIN__ + + uint NormalizeFilenameCygwin + ( + // returns a pointer to the NULL terminator within 'buf' + + char * buf, // valid destination buffer + uint buf_size, // size of buf + ccp src // NULL or source + ) + { + static char prefix[] = "/cygdrive/"; + + if ( buf_size < sizeof(prefix) + 5 || !src ) + { + *buf = 0; + return 0; + } + + char * end = buf + buf_size - 1; + char * dest = buf; + + if ( ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' ) + && src[1] == ':' + && ( src[2] == 0 || src[2] == '/' || src[2] == '\\' )) + { + memcpy(buf,prefix,sizeof(prefix)); + dest = buf + sizeof(prefix)-1; + *dest++ = tolower((int)*src); // cygwin needs the '(int)' + *dest = 0; + if (IsDirectory(buf,false)) + { + *dest++ = '/'; + src += 2; + if (*src) + src++; + } + else + dest = buf; + } + ASSERT( dest < buf + buf_size ); + + while ( dest < end && *src ) + if ( *src == '\\' ) + { + *dest++ = '/'; + src++; + } + else + *dest++ = *src++; + + *dest = 0; + ASSERT( dest < buf + buf_size ); + return dest - buf; + } + +#endif // __CYGWIN__ + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __CYGWIN__ + + char * AllocNormalizedFilenameCygwin + ( + // returns an alloced buffer with the normalized filename + + ccp source // NULL or string + ) + { + char buf[PATH_MAX]; + const uint len = NormalizeFilenameCygwin(buf,sizeof(buf),source); + char * result = MEMDUP(buf,len); // MEMDUP allocs +1 byte and set it to NULL + DASSERT(buf[len]==0); + return result; + } + +#endif // __CYGWIN__ + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// FDList_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ClearFDList ( FDList_t *fdl ) +{ + DASSERT(fdl); + + fdl->max_fd = -1; + FD_ZERO(&fdl->readfds); + FD_ZERO(&fdl->writefds); + FD_ZERO(&fdl->exceptfds); + + fdl->poll_used = 0; + fdl->n_sock = 0; + + fdl->now_usec = GetTimeUSec(false); + fdl->timeout_usec = M1(fdl->timeout_usec); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeFDList ( FDList_t *fdl, bool use_poll ) +{ + DASSERT(fdl); + memset(fdl,0,sizeof(*fdl)); + fdl->use_poll = use_poll; + ClearFDList(fdl); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetFDList ( FDList_t *fdl ) +{ + DASSERT(fdl); + FREE(fdl->poll_list); + InitializeFDList(fdl,fdl->use_poll); +} + +/////////////////////////////////////////////////////////////////////////////// + +struct pollfd * AllocFDList ( FDList_t *fdl, uint n ) +{ + DASSERT(fdl); + uint min_size = fdl->poll_used + n; + if ( fdl->poll_size < min_size ) + { + fdl->poll_size = ( min_size/0x18 + 2 ) * 0x20; + DASSERT( fdl->poll_size >= min_size ); + fdl->poll_list = REALLOC(fdl->poll_list,fdl->poll_size*sizeof(*fdl->poll_list)); + } + DASSERT( fdl->poll_used + n <= fdl->poll_size ); + + struct pollfd *ptr = fdl->poll_list + fdl->poll_used; + fdl->poll_used += n; + memset(ptr,0,n*sizeof(*ptr)); + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AnnounceFDList ( FDList_t *fdl, uint n ) +{ + if (fdl->use_poll) + { + AllocFDList(fdl,n); + fdl->poll_used -= n; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AddFDList +( + // returns the pool-index if available, ~0 otherwise + + FDList_t *fdl, // valid socket list + int sock, // socket to add + uint events // bit field: POLLIN|POLLOUT|POLLERR +) +{ + if ( sock == -1 || !(events&(POLLIN|POLLOUT|POLLERR)) ) + return ~0; + + fdl->n_sock++; + if ( fdl->max_fd < sock ) + fdl->max_fd = sock; + + DASSERT(fdl); + if (fdl->use_poll) + { + struct pollfd *pp = AllocFDList(fdl,1); + DASSERT(pp); + pp->fd = sock; + pp->events = events; + return pp - fdl->poll_list; + } + + noPRINT("ADD-SOCK %d = %x\n",sock,events); + if ( events & POLLIN ) + FD_SET(sock,&fdl->readfds); + if ( events & POLLOUT ) + FD_SET(sock,&fdl->writefds); + if ( events & POLLERR ) + FD_SET(sock,&fdl->exceptfds); + return ~0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetEventFDList +( + // returns bit field: POLLIN|POLLOUT|POLLERR + + FDList_t *fdl, // valid socket list + int sock, // socket to look for + uint poll_index // if use_poll: use the index for a fast search +) +{ + if ( sock == -1 ) + return 0; + + DASSERT(fdl); + if (fdl->use_poll) + { + const struct pollfd *pp = fdl->poll_list; + if ( poll_index < fdl->poll_used && pp[poll_index].fd == sock ) + return pp[poll_index].revents; + + const struct pollfd *pend = pp + fdl->poll_used; + for ( ; pp < pend; pp++ ) + if ( pp->fd == sock ) + return pp->revents; + return 0; + } + + uint revents = 0; + if (FD_ISSET(sock,&fdl->readfds)) + revents |= POLLIN; + if (FD_ISSET(sock,&fdl->writefds)) + revents |= POLLOUT; + if (FD_ISSET(sock,&fdl->exceptfds)) + revents |= POLLERR; + return revents; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int WaitFDList +( + FDList_t *fdl // valid socket list +) +{ + DASSERT(fdl); + + int stat; + u64 now_usec = GetTimeUSec(false); + + if (fdl->use_poll) + { + int timeout; + if ( fdl->timeout_usec > now_usec ) + { + u64 delta = ( fdl->timeout_usec - now_usec ) / 1000; + timeout = delta > 0x7fffffffull ? -1 : delta; + } + else + timeout = 0; + + stat = poll( fdl->poll_list, fdl->poll_used, timeout ); + } + else + { + struct timeval tv, *ptv = &tv; + if ( fdl->timeout_usec > now_usec ) + { + const u64 delta = fdl->timeout_usec - now_usec; + if ( delta > 1000000ull * 0x7fffffffull ) + ptv = NULL; + else + { + tv.tv_sec = delta / 1000000; + tv.tv_usec = delta % 1000000; + } + } + else + tv.tv_sec = tv.tv_usec = 0; + +//DEL fprintf(stderr,"#SELECT %lld: ptv=%d: %lu.%06lu\n", +//DEL fdl->timeout_usec-now_usec, ptv!=0, tv.tv_sec, tv.tv_usec ); + stat = select( fdl->max_fd+1, &fdl->readfds, &fdl->writefds, + &fdl->exceptfds, ptv ); + } + + fdl->now_usec = GetTimeUSec(false); + fdl->cur_usec = fdl->now_usec - now_usec; + fdl->wait_usec += fdl->cur_usec; + fdl->wait_count++; + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PWaitFDList +( + FDList_t *fdl, // valid socket list + const sigset_t *sigmask // NULL or signal mask +) +{ + DASSERT(fdl); + + #ifdef __APPLE__ + // [[2do]] ??? untested work around + if (fdl->use_poll) + { + sigset_t origmask; + if (sigmask) + sigprocmask(SIG_SETMASK,sigmask,&origmask); + const int stat = WaitFDList(fdl); + if (sigmask) + sigprocmask(SIG_SETMASK,&origmask,NULL); + return stat; + } + #endif + + struct timespec ts, *pts = &ts; + u64 now_usec = GetTimeUSec(false); + if ( fdl->timeout_usec > now_usec ) + { + const u64 delta = fdl->timeout_usec - now_usec; + if ( delta > 1000000ull * 0x7fffffffull ) + pts = NULL; + else + { + ts.tv_sec = delta / 1000000; + ts.tv_nsec = delta % 1000000 * 1000; + } + } + else + ts.tv_sec = ts.tv_nsec = 0; + + #ifdef __APPLE__ + const int stat = pselect( fdl->max_fd+1, &fdl->readfds, &fdl->writefds, + &fdl->exceptfds, pts, sigmask ); + #else + const int stat = fdl->use_poll + ? ppoll( fdl->poll_list, fdl->poll_used, pts, sigmask ) + : pselect( fdl->max_fd+1, &fdl->readfds, &fdl->writefds, + &fdl->exceptfds, pts, sigmask ); + #endif + + fdl->now_usec = GetTimeUSec(false); + fdl->cur_usec = fdl->now_usec - now_usec; + fdl->wait_usec += fdl->cur_usec; + fdl->wait_count++; + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t CheckUnixSocketPathMem +( + mem_t src, // NULL or source path to analyse + int tolerance // <1: 'unix:', 'file:', '/', './' and '../' detected + // 1: not 'NAME:' && at relast one '/' + // 2: not 'NAME:' +) +{ + if ( src.ptr && src.len ) + { + ccp ptr = src.ptr; + ccp end = ptr + src.len; + + switch (*ptr) + { + case '/': + return src; + + case '.': + { + ccp s = ptr+1; + if ( s < end && *s == '.' ) + s++; + if ( s < end && *s == '/' ) + return src; + } + break; + + case 'f': + if ( end-ptr >= 5 && !memcmp(ptr,"file:",5) ) + return RightMem(src,-5); + + case 'u': + if ( end-ptr >= 5 && !memcmp(ptr,"unix:",5) ) + return RightMem(src,-5); + break; + } + + if ( tolerance > 0 ) + { + while ( ptr < end && isalnum((int)*ptr) ) + ptr++; + if ( ptr < end && *ptr != ':' && ( tolerance > 1 || strchr(ptr,'/')) ) + return BehindMem(src,ptr); + } + } + + return NullMem; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp CheckUnixSocketPath +( + ccp src, // NULL or source path to analyse + int tolerance // <1: 'unix:', 'file:', '/', './' and '../' detected + // 1: not 'NAME:' && at relast one '/' + // 2: not 'NAME:' +) +{ + mem_t res = CheckUnixSocketPathMem(MemByString(src),tolerance); + return res.ptr; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Catch Output /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int CatchIgnoreOutput +( + struct CatchOutput_t *ctrl, // control struct incl. data + int call_mode // 0:init, 1:new data, 2:term +) +{ + DASSERT(ctrl); + ClearGrowBuffer(&ctrl->buf); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetCatchOutput ( CatchOutput_t *co, uint n ) +{ + while ( n-- > 0 ) + { + if ( co->pipe_fd[0] != -1 ) + { + close(co->pipe_fd[0]); + co->pipe_fd[0] = -1; + } + + if ( co->pipe_fd[1] != -1 ) + { + close(co->pipe_fd[1]); + co->pipe_fd[1] = -1; + } + + ResetGrowBuffer(&co->buf); + co++; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CatchOutput +( + ccp command, // command to execute + int argc, // num(arguments) in 'argv'; -1: argv is NULL terminated + char *const* argv, // list of arguments + + CatchOutputFunc stdout_func, // NULL or function to catch stdout + CatchOutputFunc stderr_func, // NULL or function to catch stderr + void *user_ptr, // NULL or user defined parameter + bool silent // true: suppress error messages +) +{ + if ( !command || !*command ) + return ERR_NOTHING_TO_DO; + + + //--- setup streams & pipes + + uint n_catch_out = 0; + CatchOutput_t catch_out[2]; + memset(catch_out,0,sizeof(catch_out)); + + uint i; + for ( i = 1; i <= 2; i++ ) + { + CatchOutputFunc func = i==1 ? stdout_func : stderr_func; + if (!func) + continue; + + CatchOutput_t *co = catch_out + n_catch_out++; + co->mode = i; + co->func = func; + co->user_ptr = user_ptr; + InitializeGrowBuffer(&co->buf,16*KiB); + + if ( pipe(co->pipe_fd) == -1 ) + { + ResetCatchOutput(catch_out,n_catch_out); + if (!silent) + ERROR1(ERR_CANT_CREATE,"Can't create pipe\n"); + return ERR_CANT_CREATE; + } + } + + + //--- setup arguments + + if (!argv) + argc = 0; + else if ( argc < 0 ) + for ( argc = 0; argv[argc]; argc++ ) + ; + + const uint local_args_size = 100; + char *local_args[local_args_size+2]; + char **args = local_args; + if ( argc >= local_args_size ) + args = MALLOC((argc+2)*sizeof(*args)); + + char **destarg = args; + *destarg++ = (char*)command; + + for ( i = 0; i < argc; i++ ) + *destarg++ = argv[i]; + *destarg = 0; + + #ifdef TEST + { + printf("EXEC '%s' [argc=%u]\n",command,argc); + uint i; + for ( i = 0; i < argc+2; i++ ) + printf("%3d: %s\n",i,args[i]); + } + #endif + + + //--- fork + + enumError err = ERR_OK; + + int fork_stat = fork(); + if ( fork_stat < 0 ) + { + if (!silent) + ERROR1(ERR_ERROR,"Can't fork: %s\n",command); + err = ERR_ERROR; + } + else if (fork_stat) + { + PRINT("WAIT FOR %d\n",fork_stat); + + CatchOutput_t *co, *co_end = catch_out + n_catch_out; + for ( co = catch_out; co < co_end; co++ ) + { + DASSERT(co->func); + co->func(co,0); + close(co->pipe_fd[1]); // close childs (write) end + co->pipe_fd[1] = -1; + } + + FDList_t fdl; + InitializeFDList(&fdl,false); + char buf[0x1000]; + + for(;;) + { + ClearFDList(&fdl); + fdl.timeout_usec = GetTimeUSec(false) + 60*USEC_PER_SEC; + for ( co = catch_out; co < co_end; co++ ) + AddFDList(&fdl,co->pipe_fd[0],POLLIN); + if ( fdl.max_fd == -1 ) + break; + + const int stat = WaitFDList(&fdl); + if ( stat > 0 ) + { + for ( co = catch_out; co < co_end; co++ ) + { + int fd = co->pipe_fd[0]; + uint event = GetEventFDList(&fdl,fd,0); + if ( event & POLLIN ) + { + ssize_t read_size = read(fd,buf,sizeof(buf)); + PRINT("READ %d[%d], %zd bytes\n",co->mode,fd,read_size); + if ( read_size > 0 ) + { + PrepareGrowBuffer(&co->buf,read_size,true); + InsertGrowBuffer(&co->buf,buf,read_size); + if (co->func) + co->func(co,1); + } + else + { + close(fd); + co->pipe_fd[0] = -1; + } + } + } + } + } + ResetFDList(&fdl); + + int wait_stat; + for(;;) + { + int pid = waitpid(fork_stat,&wait_stat,0); + + PRINT("RESUME: %d = %x: exited=%d, wait_stat=%d [%s]\n", + wait_stat, wait_stat, WIFEXITED(wait_stat), + WEXITSTATUS(wait_stat), GetErrorName(WEXITSTATUS(wait_stat),"?") ); + if ( pid == fork_stat ) + break; + } + + for ( co = catch_out; co < co_end; co++ ) + if (co->func) + co->func(co,2); + ResetCatchOutput(catch_out,n_catch_out); + + if (WIFEXITED(wait_stat)) + return WEXITSTATUS(wait_stat); + if (!silent) + ERROR1(ERR_SYNTAX,"Child teminated with error: %s\n",command); + return ERR_SYNTAX; + } + else + { + PRINT("CHILD\n"); + + CatchOutput_t *co, *co_end = catch_out + n_catch_out; + for ( co = catch_out; co < co_end; co++ ) + { + close(co->pipe_fd[0]); // close parents (read) end + co->pipe_fd[0] = -1; + + if ( dup2(co->pipe_fd[1],co->mode) == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CREATE,"dup2(,%d) failed: %s\n", + co->mode, command ); + _exit(ERR_CANT_CREATE); + } + } + + execvp(command,args); + if (!silent) + ERROR1(ERR_CANT_CREATE,"Can't create process: %s\n",command); + _exit(ERR_CANT_CREATE); + } + + if ( args != local_args ) + FREE(args); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CatchOutputLine +( + ccp command_line, // command line to execute + CatchOutputFunc stdout_func, // NULL or function to catch stdout + CatchOutputFunc stderr_func, // NULL or function to catch stderr + void *user_ptr, // NULL or user defined parameter + bool silent // true: suppress error messages +) +{ + SplitArg_t sa; + ScanSplitArg(&sa,true,command_line,0,0); + + enumError err = ERR_SYNTAX; + if ( sa.argc > 1 ) + err = CatchOutput( sa.argv[1], sa.argc-2, sa.argv+2, + stdout_func, stderr_func, user_ptr, silent ); + ResetSplitArg(&sa); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan sections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FindSection +( + // search file until next section found + // => read line and store data into 'si' + // => return TRUE, if section found + + SectionInfo_t *si, // valid section info + char *buf, // use this buffer for line scanning + uint buf_size, // size of 'buf' + bool scan_buf // true: buffer contains already a valid + // and NULL terminated line +) +{ + DASSERT(si); + DASSERT(si->f); + DASSERT(buf); + DASSERT( buf_size > 10 ); + + FreeString(si->section); + FreeString(si->path); + si->section = si->path = 0; + si->index = -1; + ResetParamField(&si->param); + + buf_size--; + for(;;) + { + if ( !scan_buf && !fgets(buf,buf_size,si->f) ) + break; + buf[buf_size] = 0; + scan_buf = false; + + char *ptr = buf; + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + + if ( *ptr != '[' ) + continue; + + ccp sect = ++ptr; + while ( isalnum((int)*ptr) || *ptr == '-' || *ptr == '_' ) + ptr++; + + ccp path = ptr; + if ( *ptr == '/' ) + { + *ptr++ = 0; + path = ptr; + while ( *ptr > ' ' && *ptr != ':' && *ptr != ']' ) + ptr++; + } + + int index = -1; + if ( *ptr == ':' ) + { + *ptr++ = 0; + index = str2ul(ptr,&ptr,10); + } + + if ( *ptr != ']' ) + continue; + *ptr = 0; + + si->section = STRDUP(sect); + if (*path) + si->path = STRDUP(path); + si->index = index; + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanSections +( + // return the last returned value by On*() + + FILE *f, // source file + SectionFunc OnSection, // not NULL: call this function for each section + // on result: <0: abort, 0:next section, 1:scan param + SectionFunc OnParams, // not NULL: call this function after param scan + // on result: <0: abort, continue + void *user_param // user defined parameter + +) +{ + DASSERT(f); + + SectionInfo_t si; + memset(&si,0,sizeof(si)); + si.f = f; + si.user_param = user_param; + InitializeParamField(&si.param); + si.param.free_data = true; + + int stat = 0; + char buf[10000]; + bool scan_buf = false; + while (FindSection(&si,buf,sizeof(buf),scan_buf)) + { + scan_buf = false; + if (OnSection) + { + int stat = OnSection(&si); + if ( stat < 0 ) + break; + if (!stat) + continue; + } + + if (!OnParams) + continue; + + // scan param + + while (fgets(buf,sizeof(buf)-1,f)) + { + buf[sizeof(buf)-1] = 0; + char *ptr = buf; + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + if ( *ptr == '[' ) + { + scan_buf = true; + break; + } + + //--- scan name + ccp name = ptr; + while ( *ptr > ' ' && *ptr != '=' ) + ptr++; + char *name_end = ptr; + + //--- scan param + while ( *ptr == ' ' || *ptr == '\t' ) + ptr++; + if ( *ptr != '=' ) + continue; + if ( *++ptr == ' ' ) // skip max 1 space + ptr++; + + ccp value = ptr; + while ( *ptr && *ptr != '\n' && *ptr != '\r' ) + ptr++; + + *name_end = *ptr = 0; + InsertParamField(&si.param,name,false,0,STRDUP(value)); + } + + stat = OnParams(&si); + if ( stat < 0 ) + break; + } + + FreeString(si.section); + FreeString(si.path); + ResetParamField(&si.param); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan configuration /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError RestoreState +( + const RestoreStateTab_t + *table, // section table, terminated by name==0 + void *data, // file data, modified, terminated by NULL or LF + uint size, // size of file data + RestoreStateLog_t log_mode, // print warnings (bit field) + FILE *log_file, // error file; if NULL use stderr + cvp user_param // pointer provided by user +) +{ + DASSERT(data||!size); + if (!size) + return ERR_OK; + + if (!log_file) + log_file = stderr; + + char *ptr = data; + char *end = ptr + size; + if ( !*end && *end != '\n' ) + end[-1] = 0; + + while ( ptr < end ) + { + //--- search next section + + while ( ptr < end && (uchar)*ptr <= ' ' ) + ptr++; + if ( *ptr != '[' ) + { + while ( ptr < end && *ptr && *ptr != '\n' ) + ptr++; + if ( ptr < end ) + ptr++; + continue; + } + + ccp sect = ++ptr; + while ( isalnum((int)*ptr) || *ptr == '-' || *ptr == '_' ) + ptr++; + + ccp path = ptr; + if ( *ptr == '/' ) + { + *ptr++ = 0; + path = ptr; + while ( isalnum((int)*ptr) || *ptr == '-' || *ptr == '_' || *ptr == '/' ) + ptr++; + } + + int index = -1; + if ( *ptr == ':' ) + { + *ptr++ = 0; + index = strtoul(ptr,0,10); + } + *ptr = 0; + + const RestoreStateTab_t *tab; + for ( tab = table; tab->name; tab++ ) + if (!strcmp(tab->name,sect)) + break; + + if (!tab->name) + { + if ( log_mode & RSL_WARNINGS ) + fprintf(log_file,"! #RESTORE: Unknown section: %s\n",sect); + continue; + } + + TRACE("SECT: [%s/%s:%d]\n",sect,path,index); + if (!tab->func) // no need to analyse section + continue; + + + //--- scan name=value + + RestoreState_t rs; + rs.sect = sect; + rs.path = path; + rs.index = index; + rs.user_param = user_param; + rs.user_info = 0; + rs.log_mode = log_mode; + rs.log_file = log_file; + InitializeParamField(&rs.param); + + while ( ptr < end ) + { + //--- next line + while ( ptr < end && *ptr && *ptr != '\n' ) + ptr++; + + //--- skip lines and blanks + while ( ptr < end && (uchar)*ptr <= ' ' ) + ptr++; + + if ( *ptr == '[' ) + break; + + //--- scan name + ccp name = ptr; + while ( isalnum((int)*ptr) || *ptr == '-' || *ptr == '_' || *ptr == '@' ) + ptr++; + char *name_end = ptr; + + //--- scan param + while ( *ptr == ' ' || *ptr == '\t' ) + ptr++; + if ( *ptr != '=' ) + continue; + if ( *++ptr == ' ' ) // skip max 1 space + ptr++; + + ccp value = ptr; + while ( (uchar)*ptr >= ' ' || *ptr == '\t' ) + ptr++; + + *name_end = *ptr = 0; + InsertParamField(&rs.param,name,false,0,value); + } + + #ifdef TEST0 + { + ParamFieldItem_t *ptr = rs.param.field, *end; + for ( end = ptr + rs.param.used; ptr < end; ptr++ ) + printf("%4u: %20s = %s|\n",ptr->num,ptr->key,(ccp)ptr->data); + } + #endif + + tab->func(&rs,tab->user_table); + if ( log_mode & RSL_UNUSED_NAMES ) + { + ParamFieldItem_t *ptr = rs.param.field, *end; + for ( end = ptr + rs.param.used; ptr < end; ptr++ ) + if (!ptr->num) + { + const uint len = strlen((ccp)ptr->data); + if ( len > 20 ) + fprintf(log_file,"! #RESTORE[%s]: Unused: %s [%u] %.20s...\n", + PrintRestoreStateSection(&rs), + ptr->key, len, (ccp)ptr->data ); + else + fprintf(log_file,"! #RESTORE[%s]: Unused: %s [%u] %s\n", + PrintRestoreStateSection(&rs), + ptr->key, len, (ccp)ptr->data ); + } + } + ResetParamField(&rs.param); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintRestoreStateSection ( const RestoreState_t *rs ) +{ + // print into cyclic buffer + DASSERT(rs); + + uint len = strlen(rs->sect) + strlen(rs->path) + 2; + + char index[20]; + *index = 0; + if ( rs->index >= 0 ) + len += snprintf(index,sizeof(index),":%u",rs->index); + + char *buf = GetCircBuf(len); + if (*rs->path) + snprintf(buf,len,"%s/%s%s",rs->sect,rs->path,index); + else + snprintf(buf,len,"%s%s",rs->sect,index); + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * GetParamField +( + const RestoreState_t *rs, // valid restore-state structure + ccp name // name of member +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = FindParamField(&rs->param,name); + if (it) + it->num++; + else if ( rs->log_mode & RSL_MISSED_NAMES ) + fprintf(rs->log_file,"! #RESTORE[%s]: Missed: %s\n", + PrintRestoreStateSection(rs), name ); + + return it; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetParamFieldINT +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + int not_found // return this value if not found +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it ? str2l((ccp)it->data,0,10) : not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetParamFieldUINT +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + uint not_found // return this value if not found +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it ? str2ul((ccp)it->data,0,10) : not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetParamFieldS64 +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + s64 not_found // return this value if not found +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it ? str2ll((ccp)it->data,0,10) : not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetParamFieldU64 +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + u64 not_found // return this value if not found +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it ? str2ull((ccp)it->data,0,10) : not_found; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +float GetParamFieldFLOAT +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + float not_found // return this value if not found +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it ? strtof((ccp)it->data,0) : not_found; +} + +//----------------------------------------------------------------------------- + +double GetParamFieldDBL +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + double not_found // return this value if not found +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it ? strtod((ccp)it->data,0) : not_found; +} + +//----------------------------------------------------------------------------- + +long double GetParamFieldLDBL +( + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + long double not_found // return this value if not found +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it ? strtold((ccp)it->data,0) : not_found; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int GetParamFieldBUF +( + // returns >=0: length of read data; -1:nothing done (not_found==NULL) + + char *buf, // buffer to store result + uint buf_size, // size of buffer + + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + EncodeMode_t decode, // decoding mode, fall back to OFF + // supported: STRING, UTF8, BASE64, BASE64X + ccp not_found // not NULL: store this value if not found +) +{ + DASSERT(buf); + DASSERT(buf_size>1); + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + if (!it) + return not_found ? StringCopyS(buf,buf_size,not_found) - buf : -1; + + return DecodeByMode(buf,buf_size,(ccp)it->data,-1,decode); +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t GetParamFieldMEM +( + // Returns the decoded 'source'. Result is NULL-terminated. + // It points either to 'buf' or is alloced (on buf==NULL or to less space) + // If alloced (mem.ptr!=buf) => call FreeMem(&mem) + + char *buf, // buffer to store result + uint buf_size, // size of buffer + + const RestoreState_t *rs, // valid restore-state structure + ccp name, // name of member + EncodeMode_t decode, // decoding mode, fall back to OFF + // supported: STRING, UTF8, BASE64, BASE64X + mem_t not_found // not NULL: return this value +) +{ + DASSERT(rs); + DASSERT(name); + + ParamFieldItem_t *it = GetParamField(rs,name); + return it + ? DecodeByModeMem(buf,buf_size,(ccp)it->data,-1,decode) + : not_found; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// save + restore config by table /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void DumpStateTable +( + FILE *f, // valid output file + int indent, + const SaveRestoreTab_t + *srt // list of variables +) +{ + if ( !f || !srt ) + return; + + indent = NormalizeIndent(indent); + for ( ; srt->type != SRT__TERM; srt++ ) + { + if ( srt->type == SRT__IS_LIST ) + { + if ( srt->name ) + fprintf(f,"%*s# %s\n",indent,"",srt->name); + else + putc('\n',f); + } + else + fprintf(f,"%*s%4u %4u *%-3u %2u %2u %s\n", + indent,"", + srt->offset, srt->size, srt->n_elem, srt->type, srt->emode, + srt->name ? srt->name : "-" ); + } +} + + +/////////////////////////////////////////////////////////////////////////////// + +static void print_scs_name ( FILE *f, int fw, int use_tab, ccp format, ... ) + __attribute__ ((__format__(__printf__,4,5))); + +static void print_scs_name ( FILE *f, int fw, int use_tab, ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + const int len = vfprintf(f,format,arg); + va_end(arg); + + if (use_tab) + { + const int tablen = ( use_tab - len ) / 8; + fprintf(f,"%.*s= ", tablen<0 ? 0 : tablen, Tabs20 ); + } + else + fprintf(f,"%*s= ", len < fw ? fw-len : 0, "" ); +} + +//----------------------------------------------------------------------------- + +void SaveCurrentStateByTable +( + FILE *f, // valid output file + cvp data0, // valid pointer to source struct + const SaveRestoreTab_t + *srt, // list of variables + ccp prefix, // NULL or prefix for names + uint fw_name // field width of name, 0=AUTO + // tabs are used for multiple of 8 and for AUTO +) +{ + DASSERT(data0); + DASSERT(srt); + if ( !f || !data0 || !srt ) + return; + + if (!prefix) + prefix = EmptyString; + const uint prelen = strlen(prefix); + + if (!fw_name) + { + const SaveRestoreTab_t *ptr; + for ( ptr = srt; ptr->type != SRT__TERM; ptr++ ) + if (ptr->name) + { + int slen = strlen(ptr->name); + switch(srt->type) + { + case SRT_STRING_FIELD: + case SRT_PARAM_FIELD: + slen += 3; + break; + } + + if ( fw_name < slen ) + fw_name = slen; + } + fw_name += prelen; + fw_name = (fw_name+7)/8*8; + } + + const int use_tab = (fw_name&7) ? 0 : (fw_name/8)*8+7; + + //------------------------------------------------------------------------- + + u64 get_uint ( const u8* data, int idx, const SaveRestoreTab_t *srt ) + { + DASSERT(srt); + DASSERT(srt->var); + + switch(srt->size) + { + case 1: return ((u8*) (data+srt->offset))[idx]; + case 2: return ((u16*)(data+srt->offset))[idx]; + case 4: return ((u32*)(data+srt->offset))[idx]; + case 8: return ((u64*)(data+srt->offset))[idx]; + } + return 0; + } + + //------------------------------------------------------------------------- + + s64 get_int ( const u8* data, int idx, const SaveRestoreTab_t *srt ) + { + DASSERT(srt); + DASSERT(srt->var); + + switch(srt->size) + { + case 1: return ((s8*) (data+srt->offset))[idx]; + case 2: return ((s16*)(data+srt->offset))[idx]; + case 4: return ((s32*)(data+srt->offset))[idx]; + case 8: return ((s64*)(data+srt->offset))[idx]; + } + return 0; + } + + //------------------------------------------------------------------------- + + void save_string ( FILE *f, ccp src, int slen, EncodeMode_t emode ) + { + char buf[4000]; + mem_t res = EncodeByModeMem(buf,sizeof(buf),src,slen,emode); + + fwrite(res.ptr,1,res.len,f); + fputc('\n',f); + if ( res.ptr != buf ) + FreeMem(&res); + } + + //------------------------------------------------------------------------- + + const u8 *data = data0; + + for ( ; srt->type != SRT__TERM; srt++ ) + { + if ( srt->type == SRT__IS_LIST ) + { + if ( srt->name ) + fprintf(f,"# %s\n",srt->name); + else + putc('\n',f); + continue; + } + + if (!srt->name) + continue; + + if ( srt->type < SRT__IS_LIST ) + print_scs_name(f,fw_name,use_tab,"%s%s",prefix,srt->name); + + switch(srt->type) + { + case SRT_BOOL: + if ( srt->n_elem > 1 ) + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%u", i ? "," : "", get_uint(data,i,srt)!=0 ); + fputc('\n',f); + } + else + fprintf(f,"%u\n",get_uint(data,0,srt)!=0); + break; + + case SRT_UINT: + if ( srt->n_elem > 1 ) + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%llu", i ? "," : "", get_uint(data,i,srt) ); + fputc('\n',f); + } + else + fprintf(f,"%llu\n",get_uint(data,0,srt)); + break; + + case SRT_HEX: + if ( srt->n_elem > 1 ) + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%#llx", i ? "," : "", get_uint(data,i,srt) ); + fputc('\n',f); + } + else + fprintf(f,"%#llx\n",get_uint(data,0,srt)); + break; + + case SRT_INT: + if ( srt->n_elem > 1 ) + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%lld", i ? "," : "", get_int(data,i,srt) ); + fputc('\n',f); + } + else + fprintf(f,"%lld\n",get_int(data,0,srt)); + break; + + case SRT_FLOAT: + switch(srt->size) + { + case sizeof(float): + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%.8g", i ? "," : "", + ((float*)(data+srt->offset))[i] ); + } + break; + + case sizeof(double): + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%.16g", i ? "," : "", + ((double*)(data+srt->offset))[i] ); + } + break; + + case sizeof(long double): + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%.20Lg", i ? "," : "", + ((long double*)(data+srt->offset))[i] ); + } + break; + + default: + fputs("0.0",f); + } + fputc('\n',f); + break; + + case SRT_XFLOAT: + switch(srt->size) + { + case sizeof(float): + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%a", i ? "," : "", + ((float*)(data+srt->offset))[i] ); + } + break; + + case sizeof(double): + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%a", i ? "," : "", + ((double*)(data+srt->offset))[i] ); + } + break; + + case sizeof(long double): + { + uint i; + for ( i = 0; i < srt->n_elem; i++ ) + fprintf(f,"%s%La", i ? "," : "", + ((long double*)(data+srt->offset))[i] ); + } + break; + + default: + fputs("0.0",f); + } + fputc('\n',f); + break; + + //----- + + case SRT_STRING_SIZE: + save_string(f,(char*)(data+srt->offset),-1,srt->emode); + break; + + case SRT_STRING_ALLOC: + save_string(f,*(ccp*)(data+srt->offset),-1,srt->emode); + break; + + case SRT_MEM: + { + mem_t *mem = (mem_t*)(data+srt->offset); + save_string(f,mem->ptr,mem->len,srt->emode); + break; + } + + //----- + + case SRT_STRING_FIELD: + { + print_scs_name(f,fw_name,use_tab,"%s%s@n",prefix,srt->name); + const StringField_t *sf = (StringField_t*)(data+srt->offset); + fprintf(f,"%u\n",sf->used); + + int i; + ccp *ptr = sf->field; + for ( i = 0; i < sf->used; i++, ptr++ ) + { + print_scs_name(f,fw_name,use_tab,"%s%s@%u",prefix,srt->name,i); + save_string(f,*ptr,-1,srt->emode); + } + break; + } + + case SRT_PARAM_FIELD: + { + print_scs_name(f,fw_name,use_tab,"%s%s@n",prefix,srt->name); + const ParamField_t *pf = (ParamField_t*)(data+srt->offset); + fprintf(f,"%u\n",pf->used); + + int i; + ParamFieldItem_t *ptr = pf->field; + for ( i = 0; i < pf->used; i++, ptr++ ) + { + print_scs_name(f,fw_name,use_tab,"%s%s@%u",prefix,srt->name,i); + fprintf(f,"%d ",ptr->num); + save_string(f,ptr->key,-1,srt->emode); + } + break; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateByTable +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + void *data0, // valid pointer to destination struct + const SaveRestoreTab_t *srt, // list of variables + ccp prefix // NULL or prefix for names +) +{ + DASSERT(rs); + DASSERT(srt); + + char buf[4000]; + if (!prefix) + prefix = EmptyString; + + for ( ; srt->type != SRT__TERM; srt++ ) + { + if ( srt->type == SRT__IS_LIST || !srt->name ) + continue; + + const u8 *data = data0; + + if ( srt->type < SRT__IS_LIST ) + StringCat2S(buf,sizeof(buf),prefix,srt->name); + + switch(srt->type) + { + case SRT_BOOL: + case SRT_UINT: + case SRT_HEX: + { + ParamFieldItem_t *it = GetParamField(rs,buf); + if (!it) + break; + + char *src = (char*)it->data; + u8 *val = (u8*)(data+srt->offset); + uint i = 0; + while (*src) + { + u64 num = str2ull(src,&src,10); + switch(srt->size) + { + case 1: *(u8*) val = num; break; + case 2: *(u16*)val = num; break; + case 4: *(u32*)val = num; break; + case 8: *(u64*)val = num; break; + } + if ( ++i == srt->n_elem ) + break; + val += srt->size; + if ( *src == ',' ) + src++; + } + break; + } + + case SRT_INT: + { + ParamFieldItem_t *it = GetParamField(rs,buf); + if (!it) + break; + + char *src = (char*)it->data; + u8 *val = (u8*)(data+srt->offset); + uint i = 0; + while (*src) + { + s64 num = str2ll(src,&src,10); + switch(srt->size) + { + case 1: *(s8*) val = num; break; + case 2: *(s16*)val = num; break; + case 4: *(s32*)val = num; break; + case 8: *(s64*)val = num; break; + } + if ( ++i == srt->n_elem ) + break; + val += srt->size; + if ( *src == ',' ) + src++; + } + break; + } + + case SRT_FLOAT: + case SRT_XFLOAT: + { + ParamFieldItem_t *it = GetParamField(rs,buf); + if (!it) + break; + + char *src = (char*)it->data; + u8 *val = (u8*)(data+srt->offset); + uint i = 0; + while (*src) + { + switch(srt->size) + { + case sizeof(float): *(float*)val = strtof(src,&src); break; + case sizeof(double): *(double*)val = strtod(src,&src); break; + case sizeof(long double): *(long double*)val = strtold(src,&src); break; + } + if ( ++i == srt->n_elem ) + break; + val += srt->size; + if ( *src == ',' ) + src++; + } + break; + } + + //----- + + case SRT_STRING_SIZE: + { + char *val = (char*)(data+srt->offset); + mem_t res = GetParamFieldMEM(buf,sizeof(buf),rs,buf, + srt->emode,MemByString(val)); + StringCopyS(val,srt->size,res.ptr); + if ( res.ptr != buf && res.ptr != val ) + FreeString(res.ptr); + break; + } + + case SRT_STRING_ALLOC: + { + ccp *val = (ccp*)(data+srt->offset); + mem_t res = GetParamFieldMEM(buf,sizeof(buf),rs,buf, + srt->emode,MemByString0(*val)); + if ( res.ptr != *val ) + { + FreeString(*val); + *val = MEMDUP(res.ptr,res.len); + } + else if ( res.ptr != buf ) + FreeString(res.ptr); + break; + } + + case SRT_MEM: + { + mem_t *val = (mem_t*)(data+srt->offset); + mem_t res = GetParamFieldMEM(buf,sizeof(buf),rs,buf,srt->emode,*val); + if ( res.ptr != val->ptr ) + { + FreeString(val->ptr); + *val = res.ptr == buf ? DupMem(res) : res; + } + else if ( res.ptr != buf ) + FreeString(res.ptr); + break; + } + + //----- + + case SRT_STRING_FIELD: + { + StringField_t *sf = (StringField_t*)(data+srt->offset); + ResetStringField(sf); + + snprintf(buf,sizeof(buf),"%s%s@n",prefix,srt->name); + const int n = GetParamFieldINT(rs,buf,0); + int i; + for ( i = 0; i < n; i++ ) + { + snprintf(buf,sizeof(buf),"%s%s@%u",prefix,srt->name,i); + mem_t res = GetParamFieldMEM(buf,sizeof(buf),rs,buf,srt->emode,NullMem); + InsertStringField(sf,res.ptr,res.ptr!=buf); + } + break; + } + + case SRT_PARAM_FIELD: + { + ParamField_t *sf = (ParamField_t*)(data+srt->offset); + ResetParamField(sf); + + snprintf(buf,sizeof(buf),"%s%s@n",prefix,srt->name); + const int n = GetParamFieldINT(rs,buf,0); + int i; + for ( i = 0; i < n; i++ ) + { + snprintf(buf,sizeof(buf),"%s%s@%u",prefix,srt->name,i); + ParamFieldItem_t *it = GetParamField(rs,buf); + if (it) + { + char *str; + const int num = strtol((ccp)it->data,&str,10); + if ( *str == ' ' ) // space should always exist + str++; + + mem_t res = DecodeByModeMem(buf,sizeof(buf),str,-1,srt->emode); + InsertParamField(sf,res.ptr,false,num,0); + if ( res.ptr != buf ) + FreeString(res.ptr); + } + } + break; + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan socket type /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ScanSocketInfo +( + // Syntax: [ PREFIX ',' ]... [ PREFIX ':' ] address + // returns TRUE, if a protocol or a type is found + + socket_info_t *si, // result, not NULL, will be initialized + ccp address, // address to analyze + uint tolerance // tolerace: + // 0: invalid without prefix + // 1: analyse beginning of address part: + // '/' or './' or '../' -> AF_UNIX + // 2: estimate type + // '/' before first ':' -> AF_UNIX + // 1.2 | 1.2.3 | 1.2.3.4 -> AF_INET + // a HEX:IP combi -> AF_INET6 + // domain name -> AF_INET_ANY +) +{ + DASSERT(si); + InitializeSocketInfo(si); + + if (!address) + return false; + + while ( *address && (uchar)*address <= ' ' ) + address++; + + static const KeywordTab_t keytab[] = + { + { -1, "STREAM", 0, SOCK_STREAM }, + { -1, "DGRAM", "DATAGRAM", SOCK_DGRAM }, + + { AF_UNIX, "UNIX", "FILE", -1 }, + { AF_UNIX, "UNIX-TCP", "FILE-TCP", SOCK_STREAM }, + { AF_UNIX, "UNIX-STREAM", "FILE-STREAM", SOCK_STREAM }, + { AF_UNIX, "UNIX-UDP", "FILE-UDP", SOCK_DGRAM }, + { AF_UNIX, "UNIX-DGRAM", "FILE-DGRAM", SOCK_DGRAM }, + { AF_UNIX, "UNIX-DATAGRAM","FILE-DATAGRAM",SOCK_DGRAM }, + { AF_UNIX, "UNIX-SEQ", "FILE-SEQ", SOCK_SEQPACKET }, + { AF_UNIX, "SEQ", 0, SOCK_SEQPACKET }, + + { AF_INET_ANY, "IP", 0, -1 }, + { AF_INET_ANY, "TCP", 0, SOCK_STREAM }, + { AF_INET_ANY, "UDP", 0, SOCK_DGRAM }, + { AF_INET_ANY, "RAW", 0, SOCK_RAW }, + + { AF_INET, "IP4", "IPV4", -1 }, + { AF_INET, "TCP4", "TCPV4", SOCK_STREAM }, + { AF_INET, "UDP4", "UDPV4", SOCK_DGRAM }, + { AF_INET, "RAW4", "RAWV4", SOCK_RAW }, + + { AF_INET6, "IP6", "IPV6", -1 }, + { AF_INET6, "TCP6", "TCPV6", SOCK_STREAM }, + { AF_INET6, "UDP6", "UDPV6", SOCK_DGRAM }, + { AF_INET6, "RAW6", "RAWV6", SOCK_RAW }, + + {0,0,0,0} + }; + + bool ok = false; + int proto = -1; + int type = -1; + ccp start = address; + for(;;) + { + char namebuf[10]; + char *dest = namebuf; + ccp ptr = start; + while ( dest < namebuf + sizeof(namebuf) - 1 + && isalnum((int)*ptr) || *ptr == '-' ) + { + *dest++ = *ptr++; + } + if ( ptr == start || *ptr != ',' && *ptr != ':' ) + break; + + *dest = 0; + int abbrev; + const KeywordTab_t *k = ScanKeyword(&abbrev,namebuf,keytab); + if ( !k || abbrev ) + break; + + if ( k->id == AF_INET_ANY ? ( proto == -1 ) : ( k->id >= 0 ) ) + proto = k->id; + if ( k->opt >= 0 ) + type = k->opt; + + noPRINT("%3lld %3lld |%s|%s| -> %d,%d\n", + k->id, k->opt, k->name1, k->name2 ? k->name2 : "", + proto, type ); + start = ptr; + if ( *start++ == ':' ) + { + ok = true; + break; + } + } + + if (!ok) + { + start = address; + proto = -1; + type = -1; + } + + if ( proto < 0 && tolerance > 0 ) + { + if ( *start == '/' || !memcmp(start,"./",2) || !memcmp(start,"../",3) ) + { + proto = AF_UNIX; + goto proto_found; + } + + if ( tolerance > 1 ) + { + ccp colon = strchr(start,':'); + if (!colon) + colon = start + strlen(start); + + //--- test for UNIX + + ccp slash = strchr(start,'/'); + if ( slash && slash < colon ) + { + proto = AF_UNIX; + goto proto_found; + } + + //--- test for IPv4 + + ccp ptr = start; + uint count = 0; + while ( ptr < colon ) + { + const char ch = *ptr; + if ( ch == '.' ) + count++; + else if ( ch < '0' || ch > '9' ) + break; + ptr++; + } + if ( ptr == colon && count < 4 ) + { + proto = AF_INET; + if ( count > 0 ) + goto proto_found; + } + + //--- test for IPv6 + + ptr = start; + const bool have_bracket = *ptr == '['; + if (have_bracket) + ptr++; + count = 0; + while (*ptr) + { + const char ch = *ptr; + if ( ch == ':' ) + count++; + else if ( ch != '.' + && !( ch >= '0' && ch <= '9' ) + && !( ch >= 'A' && ch <= 'F' ) + && !( ch >= 'a' && ch <= 'f' ) + ) + { + break; + } + ptr++; + } + if ( count > 1 && count < 8 && ( !have_bracket || *ptr == ']' )) + { + proto = AF_INET6; + goto proto_found; + } + + //--- test for a domain name + + ptr = start; + count = 0; + ccp beg_name = ptr; + while ( ptr < colon ) + { + const int ch = *ptr; + if ( ptr > beg_name && ch == '.' ) + { + count++; + beg_name = ptr+1; + } + else if ( ptr == beg_name ? !isalpha(ch) : !isalnum(ch) ) + break; + ptr++; + } + if ( count > 0 && ptr == colon ) + { + proto = AF_INET_ANY; + goto proto_found; + } + } + } + + if ( proto || type ) + { + proto_found: + si->is_valid = true; + si->protocol = proto; + si->type = type; + si->address = start; + } + //printf(">>> v=%d {%d,%d}: %s\n",ok,proto,type,start); + return si->is_valid; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintSocketInfo ( int protocol, int type ) +{ + switch (protocol) + { + case AF_UNIX: + switch(type) + { + case SOCK_STREAM: return "unix-tcp:"; + case SOCK_DGRAM: return "unix-udp:"; + case SOCK_SEQPACKET: return "unix-seq:"; + default: return "unix:"; + } + break; + + case AF_INET: + switch(type) + { + case SOCK_STREAM: return "tcp4:"; + case SOCK_DGRAM: return "udp4:"; + case SOCK_RAW: return "raw4:"; + default: return "ipv4:"; + } + break; + + case AF_INET6: + switch(type) + { + case SOCK_STREAM: return "tcp6:"; + case SOCK_DGRAM: return "udp6:"; + case SOCK_RAW: return "raw6:"; + default: return "ipv6:"; + } + break; + + case AF_INET_ANY: + switch(type) + { + case SOCK_STREAM: return "tcp:"; + case SOCK_DGRAM: return "udp:"; + case SOCK_RAW: return "raw:"; + default: return "ip:"; + } + break; + }; + + return ""; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// stat_file_count_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +stat_file_count_t stat_file_count = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +uint CountOpenFiles() +{ + DIR *fdir = opendir("/proc/self/fd"); + if (fdir) + { + uint count = 0; + for(;;) + { + struct dirent *dent = readdir(fdir); + if (!dent) + break; + if (dent->d_name[0] != '.' ) + count++; + } + closedir(fdir); + + stat_file_count.cur_files = count; + if ( stat_file_count.max_files < count ) + stat_file_count.max_files = count; + } + + return stat_file_count.cur_files; +} + +/////////////////////////////////////////////////////////////////////////////// + +void RegisterFileId ( int fd ) +{ + if ( (int)stat_file_count.max_files <= fd ) + stat_file_count.max_files = fd+1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateOpenFiles ( bool count_current ) +{ + struct rlimit rlim; + if (!getrlimit(RLIMIT_NOFILE,&rlim)) + { + stat_file_count.cur_limit = rlim.rlim_cur; + stat_file_count.max_limit = rlim.rlim_max; + } + + if ( count_current || !stat_file_count.max_files ) + CountOpenFiles(); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SetOpenFilesLimit ( uint limit ) +{ + struct rlimit rlim; + if (!getrlimit(RLIMIT_NOFILE,&rlim)) + { + rlim.rlim_cur = limit; + setrlimit(RLIMIT_NOFILE,&rlim); + } + + UpdateOpenFiles(false); + return stat_file_count.cur_limit; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintOpenFiles ( bool count_current ) +{ + if (!stat_file_count.cur_limit) + UpdateOpenFiles(count_current); + else if (count_current) + CountOpenFiles(); + + return PrintCircBuf("cur=%u, max=%u, limit=%u/%u", + stat_file_count.cur_files, + stat_file_count.max_files, + stat_file_count.cur_limit, + stat_file_count.max_limit ); +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-file.h b/project/dclib/dclib-file.h new file mode 100644 index 0000000..532ff5f --- /dev/null +++ b/project/dclib/dclib-file.h @@ -0,0 +1,1278 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_FILE_H +#define DCLIB_FILE_H 1 + +#include +#include +#include + +#include "dclib-types.h" +#include "dclib-basics.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum FileMode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[FileMode_t]] + +typedef enum FileMode_t +{ + FM_TEST = 0x00001, // test mode, don't modify any file + FM_SILENT = 0x00002, // suppress error messages + FM_IGNORE = 0x00004, // ignore, if no file on OpenFile() + // and return ERR_NOT_EXISTS + + FM_MODIFY = 0x00010, // open file for reading and writing + FM_APPEND = 0x00020, // append to existing files + FM_UPDATE = 0x00040, // update existing file + FM_OVERWRITE = 0x00080, // overwrite existing files silently + FM_NUMBER = 0x00100, // renumber filename for existing files + FM_REMOVE = 0x00200, // remove file before open + FM_MKDIR = 0x00400, // create path automatically + FM_TCP = 0x00800, // detect 'tcp:' and 'unix:' prefix on open + + FM_STDIO = 0x01000, // detect and allow "-" as stdin or stdout + FM_DEV = 0x02000, // allow character and block devices + FM_SOCK = 0x04000, // allow sockets and connect as unix stream + FM_SPC = 0x08000, // allow special files like pipes and sockets + + FM_TOUCH = 0x40000, // touch file (set timestamp) on close + FM_TEMP = 0x80000, // temporary file: remove file on close + + FM_M_OPEN = FM_TEST + | FM_SILENT + | FM_IGNORE + | FM_MODIFY + | FM_STDIO + | FM_DEV + | FM_SPC + | FM_TCP + | FM_TEMP, + + FM_M_CREATE = FM_TEST + | FM_SILENT + | FM_MODIFY + | FM_APPEND + | FM_UPDATE + | FM_OVERWRITE + | FM_NUMBER + | FM_REMOVE + | FM_MKDIR + | FM_STDIO + | FM_DEV + | FM_SOCK + | FM_SPC + | FM_TOUCH + | FM_TEMP, + + FM_M_ALL = FM_M_OPEN + | FM_M_CREATE +} +FileMode_t; + +//----------------------------------------------------------------------------- + +ccp GetFileModeStatus +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + FileMode_t file_mode, // filemode to print + uint print_mode // 0: short vector, 1 char for 1 attrib + // 1: long mode: keyword for each set attrib +); + + +ccp GetFileOpenMode +( + bool create, // false: open, true: create + FileMode_t file_mode // open modes +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct FileAttrib_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[FileAttrib_t]] + +typedef struct FileAttrib_t +{ + time_t mtime; // time of last content modification + time_t ctime; // time of file creation or last status change + time_t atime; // last ascces time + time_t itime; // insertion time (special time for archives) + + size_t size; // file size + mode_t mode; // file mode +} +FileAttrib_t; + +/////////////////////////////////////////////////////////////////////////////// + +FileAttrib_t * ClearFileAttrib +( + FileAttrib_t * dest // NULL or destination attribute +); + +FileAttrib_t * TouchFileAttrib +( + FileAttrib_t * dest // valid destination attribute +); + +FileAttrib_t * SetFileAttrib +( + FileAttrib_t * dest, // valid destination attribute + const FileAttrib_t * src_fa, // NULL or source attribute + const struct stat * src_stat // NULL or source attribute + // only used if 'src_fa==NULL' +); + +FileAttrib_t * MaxFileAttrib +( + FileAttrib_t * dest, // valid source and destination attribute + const FileAttrib_t * src_fa, // NULL or second source attribute + const struct stat * src_stat // NULL or third source attribute +); + +static inline FileAttrib_t * UseFileAttrib +( + FileAttrib_t * dest, // valid destination attribute + const FileAttrib_t * src_fa, // NULL or source attribute + const struct stat * src_stat, // NULL or source attribute + bool use_max // switch for SetFileAttrib() | MaxFileAttrib() +) +{ + return ( use_max ? MaxFileAttrib : SetFileAttrib )(dest,src_fa,src_stat); +} + +FileAttrib_t * NormalizeFileAttrib +( + FileAttrib_t * fa // valid attribute +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct File_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[File_t]] + +typedef struct File_t +{ + //--- file handle + + FILE *f; // file descriptor + + //--- basic status + + ccp fname; // file name, alloced + FileMode_t fmode; // file open mode + struct stat st; // file status just before opened + FileAttrib_t fatt; // file attributes + + //--- advanced status + + bool is_stdio; // true: read from stdin or to stdout + bool is_socket; // true: file is a UNIX stream socket + bool is_reading; // true: file opened for reading + bool is_writing; // true: file opened for writing + bool is_seekable; // true: file is seekable + enumError max_err; // maximum file related error + + //--- file data + + u8 * data; // NULL or file data + size_t size; // size of 'data' + bool data_alloced; // true: 'data' is alloced, + // free it on ResetFile() +} +File_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeFile +( + File_t * f // file structure +); + +enumError ResetFile +( + File_t * f, // file structure + uint set_time // 0: don't set + // 1: set time before closing using 'fatt' + // 2: set current time before closing +); + +enumError CloseFile +( + File_t * f, // file structure + uint set_time // 0: don't set + // 1: set time before closing using 'fatt' + // 2: set current time before closing +); + +//----------------------------------------------------------------------------- + +enumError OpenFile +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + FileMode_t file_mode, // open modes + off_t limit, // >0: don't open, if file size > limit + ccp limit_message // NULL or a with LF terminated text. + // It is printed after the message. +); + +//----------------------------------------------------------------------------- + +enumError CheckCreateFile +( + // returns: + // ERR_DIFFER: [FM_STDIO] source is "-" => 'st' is zeroed + // ERR_WARNING: [FM_DEV,FM_SPC] not a regular file + // ERR_WRONG_FILE_TYPE: file exists, but is not a regular file + // ERR_ALREADY_EXISTS: file already exists + // ERR_INVALID_VERSION: already exists, but FM_NUMBER set => no msg printed + // ERR_CANT_CREATE: FM_UPDATE is set, but file don't exist + // ERR_OK: file not exist or can be overwritten + + ccp fname, // filename to open + FileMode_t file_mode, // open modes + struct stat *st // not NULL: store file status here +); + +enumError CreateFile +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + FileMode_t file_mode // open modes +); + +static inline enumError AppendFile +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + FileMode_t file_mode // open modes +) +{ + return CreateFile(f,initialize,fname,file_mode|FM_APPEND); +} + +//----------------------------------------------------------------------------- + +enumError WriteFileAt +( + File_t * F, // file to write + size_t * cur_offset, // pointer to current file offset, modified + size_t offset, // offset to write + const void * data, // data to write + size_t size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError SetFileSize +( + File_t * F, // file to write + size_t * cur_offset, // pointer to current file offset, modified + size_t size // offset to write +); + +//----------------------------------------------------------------------------- + +enumError SkipFile +( + File_t *F, // file to write + size_t skip // number of bytes to skip +); + +//----------------------------------------------------------------------------- + +enumError RegisterFileError +( + File_t * f, // file structure + enumError new_error // new error code +); + +/////////////////////////////////////////////////////////////////////////////// + +s64 GetFileSize +( + ccp path1, // NULL or part 1 of path + ccp path2, // NULL or part 2 of path + s64 not_found_val, // return value if no regular file found + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store max values to 'fatt' +); + +//----------------------------------------------------------------------------- + +enumError OpenReadFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t file_mode, // open modes + off_t limit, // >0: don't open, if file size > limit + ccp limit_message, // NULL or a with LF terminated text. + // It is printed after the message. + + u8 ** res_data, // store alloced data here (always NULL terminated) + uint * res_size, // not NULL: store data size + ccp * res_fname, // not NULL: store alloced filename + FileAttrib_t * res_fatt // not NULL: store file attributes +); + +//----------------------------------------------------------------------------- + +enumError LoadFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // skip num of bytes before reading + void * data, // destination buffer, size = 'size' + size_t size, // size to read + int silent, // 0: print all error messages + // 1: suppress file size warning + // 2: suppress all error messages + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store *max* values to 'fatt' +); + +//----------------------------------------------------------------------------- + +enumError LoadFileAlloc +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // skip num of bytes before reading + u8 ** res_data, // result: free existing data, store ptr to alloc data + // always one more byte is alloced and set to NULL + size_t * res_size, // result: size of 'res_data' + size_t max_size, // >0: a file size limit + int silent, // 0: print all error messages + // 1: suppress file size warning + // 2: suppress all error messages + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store max values to 'fatt' +); + +//----------------------------------------------------------------------------- + +enumError SaveFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t file_mode, // open modes + const void * data, // data to write + uint data_size, // size of 'data' + FileAttrib_t * fatt // not NULL: set timestamps using this attrib +); + +//----------------------------------------------------------------------------- + +enumError OpenWriteFile +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t file_mode, // open modes + const void * data, // data to write + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +uint CloseAllExcept +( + int *list, // exception list of unordered FD, max contain -1 + uint n_list, // number of elements in 'list' + int min, // close all until and including this fd + int add // if a FD was found, incremet 'min' to 'fd+add' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CopyFile*(), TransferFile() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// compare pathes and by stat() +bool IsSameFile ( ccp path1, ccp path2 ); + +/////////////////////////////////////////////////////////////////////////////// + +// returns: +// ERR_OK file copied +// ERR_NOTHING_TO_DO src and dest are the same +// ERR_MISSING_PARAM invalid parameters +// ERR_CANT_OPEN can't open dest +// ERR_WRITE_FAILED write error + +enumError CopyFileHelper +( + ccp src, // source path + ccp dest, // destination path + int open_flags, // flags for open() + mode_t open_mode, // mode for open() + uint temp_and_move // >0: ignore open_flags, + // create temp, then move temp to dest +); + +//----------------------------------------------------------------------------- + +enumError CopyFile +( + ccp src, // source path + ccp dest, // destination path + mode_t open_mode // mode for open() +); + +//----------------------------------------------------------------------------- + +enumError CopyFileCreate +( + ccp src, // source path + ccp dest, // destination path + mode_t open_mode // mode for open() +); + +//----------------------------------------------------------------------------- + +enumError CopyFileTemp +( + ccp src, // source path + ccp dest, // destination path + mode_t open_mode // mode for open() +); + +/////////////////////////////////////////////////////////////////////////////// +// [[TransferMode_t]] + +typedef enum TransferMode_t +{ + //--- jobs + + TFMD_J_MOVE = 0x01, // move file + TFMD_J_MOVE1 = 0x02, // move file, but only if not linked (ignore TFMD_J_MOVE) + TFMD_J_RM_DEST = 0x04, // remove dest before TFMD_J_LINK and TFMD_J_COPY + TFMD_J_LINK = 0x08, // link file + TFMD_J_COPY = 0x10, // copy file + TFMD_J_RM_SRC = 0x20, // remove source after TFMD_J_COPY +// TFMD_J_TOUCH = 0x40, // touch destination => not supported yet + + //--- modes + + TFMD_MOVE = TFMD_J_MOVE | TFMD_J_RM_DEST | TFMD_J_COPY | TFMD_J_RM_SRC, + TFMD_MOVE1 = TFMD_J_MOVE1 | TFMD_J_RM_DEST | TFMD_J_COPY | TFMD_J_RM_SRC, + TFMD_LINK = TFMD_J_LINK | TFMD_J_RM_DEST | TFMD_J_COPY, + TFMD_COPY = TFMD_J_RM_DEST | TFMD_J_COPY, + TFMD_CAT = TFMD_J_COPY, +} +__attribute__ ((packed)) TransferMode_t; + +//----------------------------------------------------------------------------- + +// returns: +// ERR_OK file copied +// ERR_NOTHING_TO_DO src and dest are the same +// ERR_MISSING_PARAM invalid parameters +// ERR_CANT_OPEN can't open dest +// ERR_WRITE_FAILED write error +// ERR_ERROR other error + +enumError TransferFile +( + ccp src, // source path + ccp dest, // destination path + TransferMode_t tfer_mode, // transfer mode + mode_t open_mode // mode for CopyFile*() -> open() +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MemFile_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MemFile_t]] + +typedef struct MemFile_t +{ + u8 *data; // pointer to alloced data + uint size; // size of 'data' + uint max_size; // >0: max allowed size of 'data' + uint fend; // current end of file position, always '<=size' + uint fpos; // current file pointer, always '<=fend' + + ccp err_name; // not NULL: print error messages + bool err_name_alloced; // true: 'err_name' must be freed + bool zero_extend; // true: assume unlimted file on Read*() + bool eof; // set by Read*(), if read behind 'fend' + + FileAttrib_t fatt; // file attribute for loaded files +} +MemFile_t; + +/////////////////////////////////////////////////////////////////////////////// + +bool IsValidMemFile ( MemFile_t *mf ); + +void DumpMemFile +( + FILE *f, // valid output stream + int indent, // indent of output + MemFile_t *mf // valid MemFile_t data +); + +bool AssertMemFile +( + MemFile_t *mf // valid MemFile_t data +); + +/////////////////////////////////////////////////////////////////////////////// + +u8 * GetDataMemFile +( + // return NULL on err or a pointer to the data posisiton. + + MemFile_t *mf, // valid MemFile_t data + uint fpos, // fpos to read from + uint size // needed data size +); + +void InitializeMemFile +( + MemFile_t *mf, // structure to initialize + uint start_size, // >0: alloc this for data + uint max_size // >0: file never grows above 'max_size' +); + +void ResetMemFile +( + MemFile_t *mf // valid MemFile_t data +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError WriteMemFileAt +( + MemFile_t *mf, // valid MemFile_t data + uint fpos, // fpos to write + const void *data, // data to write + uint size // size to write +); + +static inline enumError WriteMemFile +( + MemFile_t *mf, // valid MemFile_t data + const void *data, // data to write + uint size // size to write +) +{ + DASSERT(mf); + return WriteMemFileAt(mf,mf->fpos,data,size); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ReadMemFileAt +( + MemFile_t *mf, // valid MemFile_t data + uint fpos, // fpos to read from + void *buf, // destination buffer + uint size // size to read +); + +static inline uint ReadMemFile +( + MemFile_t *mf, // valid MemFile_t data + void *buf, // destination buffer + uint size // size to read +) +{ + DASSERT(mf); + return ReadMemFileAt(mf,mf->fpos,buf,size); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadMemFile +( + MemFile_t *mf, // MemFile_t data + bool init_mf, // true: initalize 'mf' first + + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // >0: skip num of bytes before reading + size_t size, // >0: max size to read; 0: read all (remaining) + bool silent // true: suppress error messages +); + +enumError SaveMemFile +( + MemFile_t *mf, // MemFile_t data + + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileMode_t fmode, // mode for file creation + bool sparse // true: enable sparse support +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TraceLog_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[TraceLog_t]] + +typedef struct TraceLog_t +{ + ccp fname; // name of file to open + FILE *log; // NULL or open log file + int level; // log level, don't log if <0 + +} +TraceLog_t; + +//----------------------------------------------------------------------------- + +bool OpenTraceLog ( TraceLog_t *tl ); +bool TraceLogText ( TraceLog_t *tl, ccp text ); +bool TraceLogPrint ( TraceLog_t *tl, ccp format, ... ) + __attribute__ ((__format__(__printf__,2,3))); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct LineBuffer_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifndef __APPLE__ +/////////////////////////////////////////////////////////////////////////////// +// [[LineBuffer_t]] + +typedef struct LineBuffer_t +{ + FILE *fp; // current file pointer + FILE *old_fp; // NULL or file before opened + FILE **old_fp_pos; // not NULL: store 'old_fp' on close() + int redirect; // >0: redirect to 'old_fp' (waste if old_fp==0) + + GrowBuffer_t buf; // grow buffer to hold lines + u8 *prev_buf_ptr; // previous 'buf.ptr', if changed recalc 'line' + + uint max_lines; // max numbers of lines + uint used_lines; // numbers of used lines + uint max_line_size; // >0: max size of a single line + mem_t *line; // list with 'max_lines' elements + bool open_line; // true: last line not terminated yet + uint seq_count; // increased by 1 on every change + uint prev_seq_count; // previous 'seq_count', if changed recalc 'line' + uint line_count; // increased for each insertet LF, no internal impact +} +LineBuffer_t; + +//----------------------------------------------------------------------------- + +void InitializeLineBuffer ( LineBuffer_t *lb, uint max_buf_size ); +void ResetLineBuffer ( LineBuffer_t *lb ); +void ClearLineBuffer ( LineBuffer_t *lb ); + +void RedirectLineBuffer +( + LineBuffer_t *lb, // valid line buffer + FILE *f, // output file, if NULL use 'lb->old_fd' + int max_lines // >=0: max number of redirected lines +); + +LineBuffer_t * OpenLineBuffer +( + LineBuffer_t *lb, // line buffer; if NULL, malloc() one + bool init_lb, // true: initialize 'lb' + FILE **fp_pos, // NULL or external place of the file pointer + uint max_lines, // max numbers of lines + uint max_line_size, // >0: max size of a single line + uint max_buf_size // >0: max total buffer size +); + +int CloseLineBuffer ( LineBuffer_t *lb ); +ssize_t WriteLineBuffer ( LineBuffer_t *lb, ccp buf, size_t size ); + +void FixMemListLineBuffer ( LineBuffer_t *lb, bool force ); +uint GetMemListLineBuffer +( + // returns the number of elements + + LineBuffer_t *lb, // line buffer; if NULL, malloc() one + mem_t **res, // not NULL: store pointer to first element here + int max_lines // !0: limit lines, <0: count from end +); + +uint PrintLineBuffer +( + // returns number of written lines + + FILE *f, // output stream + int indent, // indention + LineBuffer_t *lb, // line buffer; if NULL, malloc() one + int max_lines, // !0: limit lines, <0: count from end + ccp open_line // not NULL: append this text for an open line +); + +/////////////////////////////////////////////////////////////////////////////// +#endif // !__APPLE__ +/////////////////////////////////////////////////////////////////////////////// + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// file helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IsDirectory +( + // analyse filename (last char == '/') and stat(fname) + + ccp fname, // NULL or path + int answer_if_empty // answer, if !fname || !*fname +); + +int ExistDirectory +( + // check only real file (stat(fname)) + + ccp fname, // NULL or path + int answer_if_empty // answer, if !fname || !*fname +); + +bool IsSameFilename ( ccp fn1, ccp fn2 ); + +/////////////////////////////////////////////////////////////////////////////// + +typedef enum FindConfigFile_t +{ + FCF_HOME = 0x001, // ~/./ + FCF_REL_SHARE = 0x002, // /../share// + FCF_LOC_SHARE = 0x004, // /usr/local/share// + FCF_USR_SHARE = 0x008, // /usr/share// + FCF_PROG_SHARE = 0x010, // /share/ + FCF_PROG = 0x020, // / + + FCF_STD_SHARE = FCF_REL_SHARE + | FCF_LOC_SHARE + | FCF_USR_SHARE, + + FCF_STD_PROG = FCF_PROG_SHARE + | FCF_PROG, + + FCF_ALL = 0x03f, + + FCF_F_DEBUG = 0x100, // enable logging + +} +FindConfigFile_t; + +//----------------------------------------------------------------------------- + +// Managed and read by user, ignored by FindConfigFile() +// Intention: if set globally, call FindConfigFile() with FCF_F_DEBUG + +extern bool enable_config_search_log; + +//----------------------------------------------------------------------------- + +ccp FindConfigFile +( + // returns NULL or found file (alloced) + + FindConfigFile_t mode, // bit field: select search destinations + ccp share_name, // NULL or + ccp file_name, // NULL or + ccp prog_ext // , if NULL use '.' + +); + +//----------------------------------------------------------------------------- + +struct dirent; +typedef int (*SearchFilesFunc) ( ccp basepath, struct dirent *dent, void * param ); + +int SearchFiles +( + ccp path1, // not NULL: part #1 of base path + ccp path2, // not NULL: part #2 of base path + ccp match, // not NULL: filter files by MatchPattern() + // if empty: extract from combined path + SearchFilesFunc func, // callback function, never NULL + void *param // third parameter of func() +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreatePath +( + ccp path, // path to create + bool is_pure_dir // true: 'path' don't contains a filename part +); + +enumError RemoveSource +( + ccp fname, // file to remove + ccp dest_fname, // NULL or dest file name + // If real pathes are same: don't remove + bool print_log, // true: print a log message + bool testmode // true: don't remove, log only +); + +/////////////////////////////////////////////////////////////////////////////// + +char * FindFilename +( + // Find the last part of the path, trailing '/' are ignored + // return poiner to found string + + ccp path, // path to analyze, valid string + uint * result_len // not NULL: return length of found string +); + +uint NumberedFilename +( + // return used index number or 0, if not modified. + + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + ccp source, // source filename, may be part of 'buf' + // if NULL or empty: + ccp ext, // not NULL & not empty: file extension ('.ext') + uint ext_mode, // only relevant, if 'ext' is neither NULL not empty + // 0: use 'ext' only for detection + // 1: replace 'source' extension by 'ext' + // 2: append 'ext' if 'source' differ + // 3: append 'ext' always + bool detect_stdio // true: don't number "-" +); + +/////////////////////////////////////////////////////////////////////////////// +#if 0 +/////////////////////////////////////////////////////////////////////////////// + +char * SplitSubPath +( + char * buf, // destination buffer + size_t buf_size, // size of 'buf' + ccp path // source path, if NULL: use 'buf' +); + +enumError LoadFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // skip num of bytes before reading + void * data, // destination buffer, size = 'size' + size_t size, // size to read + int silent, // 0: print all error messages + // 1: suppress file size warning + // 2: suppress all error messages + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store *max* values to 'fatt' +); + +enumError SaveFile +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + bool overwrite, // true: force overwriting + const void * data, // data to write + uint data_size, // size of 'data' + FileAttrib_t * fatt // not NULL: set timestamps using this attribs +); + +/////////////////////////////////////////////////////////////////////////////// +#endif +/////////////////////////////////////////////////////////////////////////////// + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// normalize filenames /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * NormalizeFileName +( + // returns a pointer to the NULL terminator within 'buf' + + char * buf, // valid destination buffer + uint buf_size, // size of buf + ccp source, // NULL or source + bool allow_slash, // true: allow '/' in source + bool is_utf8 // true: enter UTF-8 mode +); + +/////////////////////////////////////////////////////////////////////////////// +#ifdef __CYGWIN__ + + uint IsWindowsDriveSpec + ( + // returns the length of the found windows drive specification (0|2|3) + + ccp src // valid string + ); + + uint NormalizeFilenameCygwin + ( + // returns a pointer to the NULL terminator within 'buf' + + char * buf, // valid destination buffer + uint buf_size, // size of buf + ccp src // NULL or source + ); + + char * AllocNormalizedFilenameCygwin + ( + // returns an alloced buffer with the normalized filename + + ccp source // NULL or string + ); + +#endif // __CYGWIN__ + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// FDList_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[FDList_t]] + +struct pollfd; + +typedef struct FDList_t +{ + bool use_poll; // false: use select(), true: use poll() + + u64 now_usec; // set on Clear() and Wait(), result of GetTimeUSec(false) + u64 timeout_usec; // next timeout, based on GetTimeUSec(false) + + //--- select() params + + int max_fd; // highest-numbered file descriptor, add 1 on select() + fd_set readfds; // ready to read + fd_set writefds; // ready to write + fd_set exceptfds; // ready to except + + //--- poll() params + + struct pollfd *poll_list; // list of poll parameters + uint poll_used; // poll_list: number of used elements + uint poll_size; // poll_list: number of alloced elements + + //--- statistics + + uint n_sock; // current number of registered sockets + // equals poll_used if poll is used + uint wait_count; // total number of waits + u64 wait_usec; // total wait time in usec + u64 cur_usec; // current wait time in usec +} +FDList_t; + +/////////////////////////////////////////////////////////////////////////////// + +void ClearFDList ( FDList_t *fdl ); +void InitializeFDList ( FDList_t *fdl, bool use_poll ); +void ResetFDList ( FDList_t *fdl ); + +//----------------------------------------------------------------------------- + +// announce new sockets +void AnnounceFDList ( FDList_t *fdl, uint n ); +struct pollfd * AllocFDList ( FDList_t *fdl, uint n ); + +uint AddFDList +( + // returns the pool-index if available, ~0 otherwise + + FDList_t *fdl, // valid socket list + int sock, // socket to add + uint events // bit field: POLLIN|POLLOUT|POLLERR +); + +uint GetEventFDList +( + // returns bit field: POLLIN|POLLOUT|POLLERR + + FDList_t *fdl, // valid socket list + int sock, // socket to look for + uint poll_index // if use_poll: use the index for a fast search +); + +//----------------------------------------------------------------------------- + +// use select() or poll() +int WaitFDList ( FDList_t *fdl ); + +// use pselect() or ppoll() +int PWaitFDList ( FDList_t *fdl, const sigset_t *sigmask ); + +// return ptr to file path, if begins with 1 of: file: unix: / ./ ../ +ccp CheckUnixSocketPath +( + ccp src, // NULL or source path to analyse + int tolerance // <1: 'unix:', 'file:', '/', './' and '../' detected + // 1: not 'NAME:' && at relast one '/' + // 2: not 'NAME:' +); + +// return mem_t to file path, if begins with 1 of: file: unix: / ./ ../ +mem_t CheckUnixSocketPathMem +( + mem_t src, // NULL or source path to analyse + int tolerance // <1: 'unix:', 'file:', '/', './' and '../' detected + // 1: not 'NAME:' && at relast one '/' + // 2: not 'NAME:' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Catch Output /////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct CatchOutput_t; + +typedef int (*CatchOutputFunc) +( + struct CatchOutput_t *ctrl, // control struct incl. data + int call_mode // 0:init, 1:new data, 2:term +); + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct CatchOutput_t +{ + int mode; // -1: disabled, 1:stdout, 2:stderr + int pipe_fd[2]; // pipe + CatchOutputFunc func; // function + GrowBuffer_t buf; // output buffer + void *user_ptr; // pointer by user +} +CatchOutput_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int CatchIgnoreOutput +( + struct CatchOutput_t *ctrl, // control struct incl. data + int call_mode // 0:init, 1:new data, 2:term +); + +//----------------------------------------------------------------------------- + +void ResetCatchOutput ( CatchOutput_t *co, uint n ); + +//----------------------------------------------------------------------------- + +enumError CatchOutput +( + ccp command, // command to execute + int argc, // num(arguments) in 'argv'; -1: argv is NULL terminated + char *const* argv, // list of arguments + + CatchOutputFunc stdout_func, // NULL or function to catch stdout + CatchOutputFunc stderr_func, // NULL or function to catch stderr + void *user_ptr, // NULL or user defined parameter + bool silent // true: suppress error messages +); + +//----------------------------------------------------------------------------- + +enumError CatchOutputLine +( + ccp command_line, // command line to execute + CatchOutputFunc stdout_func, // NULL or function to catch stdout + CatchOutputFunc stderr_func, // NULL or function to catch stderr + void *user_ptr, // NULL or user defined parameter + bool silent // true: suppress error messages +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan sections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct SectionInfo_t +{ + FILE *f; // source file + ccp section; // section name, alloced + ccp path; // path name, NULL or alloced + int index; // index of section, -1 if not available + ParamField_t param; // parameter list + void *user_param; // user defined parameter +} +SectionInfo_t; + +typedef int (*SectionFunc) ( SectionInfo_t *si ); + +/////////////////////////////////////////////////////////////////////////////// + +bool FindSection +( + // search file until next section found + // => read line and store data into 'si' + // => return TRUE, if section found + + SectionInfo_t *si, // valid section info + char *buf, // use this buffer for line scanning + uint buf_size, // size of 'buf' + bool scan_buf // true: buffer contains already a valid + // and NULL terminated line +); + +int ScanSections +( + // return the last returned value by On*() + + FILE *f, // source file + SectionFunc OnSection, // not NULL: call this function for each section + // on result: <0: abort, 0:next section, 1:scan param + SectionFunc OnParams, // not NULL: call this function after param scan + // on result: <0: abort, continue + void *user_param // user defined parameter + +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan socket type /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[socket_info_t]] + +#define AF_INET_ANY (AF_INET6+100) + +typedef struct socket_info_t +{ + bool is_valid; // true: structure is valid + + int sock; // -1 or used socket + int protocol; // -1 | AF_UNIX | AF_INET | AF_INET6 | AF_INET_ANY + int type; // -1 | SOCK_STREAM | SOCK_DGRAM | SOCK_RAW | SOCK_SEQPACKET + ccp address; // NULL or pointer to relevant address or path + bool alloced; // true: address is alloced +} +socket_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeSocketInfo ( socket_info_t *si ) +{ + DASSERT(si); + memset(si,0,sizeof(*si)); + si->sock = si->protocol = si->type = -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void ResetSocketInfo ( socket_info_t *si ) +{ + if (si) + { + if ( si->alloced ) + FreeString(si->address); + InitializeSocketInfo(si); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ScanSocketInfo +( + // Syntax: [ PREFIX ',' ]... [ PREFIX ':' ] address + // returns TRUE, if a protocol or a type is found + + socket_info_t *si, // result, not NULL, will be initialized + ccp address, // address to analyze + uint tolerance // tolerace: + // 0: invalid without prefix + // 1: analyse beginning of address part: + // '/' or './' or '../' -> AF_UNIX + // 2: estimate type + // '/' before first ':' -> AF_UNIX + // 1.2 | 1.2.3 | 1.2.3.4 -> AF_INET + // a HEX:IP combi -> AF_INET6 + // domain name -> AF_INET_ANY +); + +/////////////////////////////////////////////////////////////////////////////// + +bool GetSocketInfoBySocket +( + // returns TRUE, if infos retrived + + socket_info_t *si, // result, not NULL, will be initialized + int sock, // socket id, maybe -1 + bool get_name // true: alloc 'address' and copy name + // -> call ResetSocketInfo() to free mem +); + +/////////////////////////////////////////////////////////////////////////////// + +// returns a static string +ccp PrintSocketInfo ( int protocol, int type ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// stat_file_count_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[stat_file_count_t]] + +typedef struct stat_file_count_t +{ + uint cur_files; // currently open files + uint max_files; // max open files + uint cur_limit; // current limit + uint max_limit; // max limit +} +stat_file_count_t; + +//----------------------------------------------------------------------------- + +extern stat_file_count_t stat_file_count; + +uint CountOpenFiles(void); +void RegisterFileId ( int fd ); +void UpdateOpenFiles ( bool count_current ); +uint SetOpenFilesLimit ( uint limit ); +ccp PrintOpenFiles ( bool count_current ); // print to circ buffer + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_FILE_H + diff --git a/project/dclib/dclib-gen-ui.h b/project/dclib/dclib-gen-ui.h new file mode 100644 index 0000000..482cadd --- /dev/null +++ b/project/dclib/dclib-gen-ui.h @@ -0,0 +1,162 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_GEN_UI_H +#define DCLIB_GEN_UI_H + +#include "dclib-basics.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// text controls /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// \1 : the following text is only for the built in help +// \2 : the following text is only for the web site +// \3 : the following text is for both, built in help and web site +// \4 : replace by '<' for html tags on web site + +// Only for tools and commands: +// \f : Text before: short help for lists +// Text behind: extended help for tool/command help + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// declarations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumType +{ + //----- base types + + T_END = 0x0001, // end of list + T_DEF_TOOL = 0x0002, // start of a new tool + T_DEF_CMD = 0x0004, // define a command + T_SEP_CMD = 0x0008, // define a command separator + T_DEF_OPT = 0x0010, // define an option + T_SEP_OPT = 0x0020, // define an option separator + T_GRP_BEG = 0x0040, // start option definitions for a group + T_CMD_BEG = 0x0080, // start option definitions for a command + T_CMD_OPT = 0x0100, // allowed option for command + T_COPY_CMD = 0x0200, // copy options of other command + T_COPY_GRP = 0x0400, // copy options of group + T_ALL_OPT = 0x0800, // allow all options + + //----- option flags + + F_OPT_COMMAND = 0x010000, // option is command specific + F_OPT_GLOBAL = 0x020000, // option is global + F_OPT_MULTIUSE = 0x040000, // multiple usage of option possible + F_OPT_PARAM = 0x080000, // option needs a parameter + F_OPT_OPTPARAM = 0x100000, // option accepts an optional parameter + F_SEPARATOR = 0x200000, // separator element + F_SUPERSEDE = 0x400000, // supersedes all other commands and options + F_IGNORE = 0x800000, // ignore on command 'HELP OPTIONS' + + F_OPT_XPARAM = F_OPT_PARAM | F_OPT_OPTPARAM, + + + //----- global flags + + F_HIDDEN = 0x1000000, // tool, command or option is hidden from help + F_DEPRECATED = 0x2000000, // command or option is depreacted + + + //----- option combinations + + T_OPT_C = T_DEF_OPT | F_OPT_COMMAND, + T_OPT_CM = T_DEF_OPT | F_OPT_COMMAND | F_OPT_MULTIUSE, + T_OPT_CP = T_DEF_OPT | F_OPT_COMMAND | F_OPT_PARAM, + T_OPT_CMP = T_DEF_OPT | F_OPT_COMMAND | F_OPT_MULTIUSE | F_OPT_PARAM, + T_OPT_CO = T_DEF_OPT | F_OPT_COMMAND | F_OPT_OPTPARAM, + T_OPT_CMO = T_DEF_OPT | F_OPT_COMMAND | F_OPT_MULTIUSE | F_OPT_OPTPARAM, + + T_OPT_G = T_DEF_OPT | F_OPT_GLOBAL, + T_OPT_GM = T_DEF_OPT | F_OPT_GLOBAL | F_OPT_MULTIUSE, + T_OPT_GP = T_DEF_OPT | F_OPT_GLOBAL | F_OPT_PARAM, + T_OPT_GMP = T_DEF_OPT | F_OPT_GLOBAL | F_OPT_MULTIUSE | F_OPT_PARAM, + T_OPT_GO = T_DEF_OPT | F_OPT_GLOBAL | F_OPT_OPTPARAM, + T_OPT_GMO = T_DEF_OPT | F_OPT_GLOBAL | F_OPT_MULTIUSE | F_OPT_OPTPARAM, + + T_OPT_S = T_DEF_OPT | F_OPT_GLOBAL | F_SUPERSEDE, + + + T_COPT = T_CMD_OPT, + T_COPT_M = T_CMD_OPT | F_OPT_MULTIUSE, + + + //----- hidden options and commands (hide from help) + + H_DEF_TOOL = F_HIDDEN | T_DEF_TOOL, + H_DEF_CMD = F_HIDDEN | T_DEF_CMD, + + H_OPT_C = F_HIDDEN | T_OPT_C, + H_OPT_CM = F_HIDDEN | T_OPT_CM, + H_OPT_CP = F_HIDDEN | T_OPT_CP, + H_OPT_CMP = F_HIDDEN | T_OPT_CMP, + H_OPT_CO = F_HIDDEN | T_OPT_CO, + H_OPT_CMO = F_HIDDEN | T_OPT_CMO, + + H_OPT_G = F_HIDDEN | T_OPT_G, + H_OPT_GM = F_HIDDEN | T_OPT_GM, + H_OPT_GP = F_HIDDEN | T_OPT_GP, + H_OPT_GMP = F_HIDDEN | T_OPT_GMP, + H_OPT_GO = F_HIDDEN | T_OPT_GO, + H_OPT_GMO = F_HIDDEN | T_OPT_GMO, + + H_COPT = F_HIDDEN | T_COPT, + H_COPT_M = F_HIDDEN | T_COPT_M, + +} enumType; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct info_t +{ + enumType type; // entry type + ccp c_name; // the C name + ccp namelist; // list of names + ccp param; // name of parameter + ccp help; // help text + + int index; // calculated index + +} info_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_GEN_UI_H diff --git a/project/dclib/dclib-gen-ui.inc b/project/dclib/dclib-gen-ui.inc new file mode 100644 index 0000000..a35f540 --- /dev/null +++ b/project/dclib/dclib-gen-ui.inc @@ -0,0 +1,1303 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include + +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct control_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct control_t +{ + FILE * cf; // output c file + FILE * hf; // output h file + FILE * df; // output def file + + const info_t * info; // pointer to first info_t + const info_t * end; // pointer to end of info_t + + int n_cmd; // number of commands + int n_grp; // number of helper groups + int n_opt_specific; // number of cmd specific options (=OPT__N_SPECIFIC) + int n_opt; // number of options (=OPT__N_TOTAL) + int n_cmd_opt; // number of options of current command + bool need_sep; // separator needed + ccp cmd_name; // name of current command + ccp opt_prefix; // prefix for option variables, never NULL + + u8 *opt_allow_grp; // grp allowed options: field[n_grp][n_opt_specific] + u8 *opt_allow_cmd; // cmd allowed options: field[n_cmd][n_opt_specific] + + StringField_t gopt; // global options + StringField_t copt; // command specfic options + StringField_t opt_done; // option handled + +} control_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static char tabs[] = "\t\t\t\t"; + +static char sep1[] = + "////////////////////////////////////////" + "///////////////////////////////////////"; + +static char sep2[] = + "########################################" + "#######################################"; + +//----------------------------------------------------------------------------- + +static void print_section ( FILE *f, ccp sep, ccp name ) +{ + const size_t namelen = strlen(name); + const int fill = ( 49 - namelen ) /2; + const int fw = 49 - fill; + fprintf(f,"\n%.2s\f\n%s\n%.15s%*s%*s%.15s\n%s\n\n", + sep, sep, sep, fill, "", -fw, name, sep, sep ); +} + +//----------------------------------------------------------------------------- + +static void print_info_opt +( + control_t * ctrl, + const info_t * info, + const info_t * default_info, + const info_t * first_info, // NULL or first info of a list of infos + ccp cmd_name +) +{ + ASSERT(ctrl); + ASSERT(info); + + FILE * cf = ctrl->cf; + ASSERT(cf); + + if (cmd_name) + fprintf(cf,"static const InfoOption_t option_%s_%s_%s =\n", + ctrl->opt_prefix, cmd_name, info->c_name ); + + fprintf(cf," {\tOPT_%s, %s, %s, %s, %s, %s, ", + info->c_name, + info->type & F_OPT_OPTPARAM ? "true" : "false", + info->type & F_HIDDEN ? "true" : "false", + info->type & F_DEPRECATED ? "true" : "false", + info->type & F_IGNORE ? "true" : "false", + first_info && info>first_info && info[-1].type&T_SEP_OPT ? "true" : "false" ); + ccp start = ( default_info ? default_info : info ) -> namelist; + if ( *start && start[1] == '|' ) + { + fprintf(cf,"'%c', ",*start); + start += 2; + } + else + fprintf(cf,"0, "); + + ccp end = start; + while ( *end && *end != '|' ) + end++; + #ifdef TEST0 + fprintf(cf,"\"%.*s\", // hid=%x, dep=%x, sep=%x\n", + (int)(end-start), start, + info->type & F_HIDDEN, + info->type & F_DEPRECATED, + info > ctrl->info ? info[-1].type & T_SEP_OPT : 0 ); + #else + fprintf(cf,"\"%.*s\",\n", (int)(end-start), start ); + #endif + + ccp param = info->param; + ccp help = info->help; + if (default_info) + { + if (!help) + help = default_info->help; + if (!param) + param = default_info->param; + } + + if ( info->type & F_OPT_XPARAM || default_info && default_info->type & F_OPT_XPARAM ) + fprintf(cf,"\t\"%s\",\n", param && *param ? param : "param" ); + else + fprintf(cf,"\t0,\n"); + + DumpText(cf,0,0,help,-1,0,""); + fprintf(cf,"\n }%c\n\n",cmd_name ? ';' : ',' ); +} + +//----------------------------------------------------------------------------- + +static void print_opt +( + control_t * ctrl, + const info_t * info, + const info_t * first_info // NULL or first info of a list of infos +) +{ + ASSERT(ctrl); + ASSERT(info); + + ctrl->n_opt++; + print_info_opt(ctrl,info,0,first_info,0); + fprintf( ctrl->hf, "\tOPT_%s,\n", info->c_name ); +}; + +//----------------------------------------------------------------------------- + +static void print_opt_link ( control_t * ctrl, const info_t * info ) +{ + ASSERT(ctrl); + ASSERT(ctrl->cf); + ASSERT(ctrl->opt_prefix); + ASSERT(info); + + if (!InsertStringField(&ctrl->opt_done,info->c_name,false)) + return; + + if ( ctrl->need_sep ) + { + ctrl->need_sep = false; + if ( ctrl->n_cmd_opt ) + fprintf(ctrl->cf,"\n\tOptionInfo + OPT_NONE, // separator\n\n" ); + } + + if ( !info->help || info->type & T_DEF_OPT ) + fprintf(ctrl->cf,"\tOptionInfo + OPT_%s,\n", info->c_name ); + else + fprintf(ctrl->cf,"\t&option_%s_%s,\n", ctrl->opt_prefix, info->c_name ); + + if (ctrl->cmd_name) + { + fprintf(ctrl->df,"#:def_cmd_opt( \"%s\", \"%s\", \\\n", + ctrl->cmd_name, info->c_name ); + DumpText(ctrl->df,0,0,info->param,-1,1,", \\\n"); + DumpText(ctrl->df,0,0,info->help,-1,1," )\n\n"); + } + + ctrl->n_cmd_opt++; +} + +//----------------------------------------------------------------------------- + +static const info_t * print_links_iterator + ( control_t * ctrl, const info_t * info, ccp prefix ); + +//----------------------------------------------------------------------------- + +static void print_cmd_links ( control_t * ctrl, const info_t * ref ) +{ + ASSERT(ctrl); + ASSERT(ref); + + const info_t * info; + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & T_CMD_BEG && !strcmp(info->c_name,ref->c_name) ) + { + print_links_iterator(ctrl,info,"cmd"); + break; + } +} + +//----------------------------------------------------------------------------- + +static void print_grp_links ( control_t * ctrl, const info_t * ref ) +{ + ASSERT(ctrl); + ASSERT(ref); + + const info_t * info; + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & T_GRP_BEG && !strcmp(info->c_name,ref->c_name) ) + { + print_links_iterator(ctrl,info,"grp"); + break; + } +} + +//----------------------------------------------------------------------------- + +static const info_t * print_links_iterator + ( control_t * ctrl, const info_t * info, ccp prefix ) +{ + ASSERT(ctrl); + ASSERT(info); + + ccp saved_prefix = ctrl->opt_prefix; + char buf[100]; + if (prefix) + { + snprintf(buf,sizeof(buf),"%s_%s",prefix,info->c_name); + ctrl->opt_prefix = buf; + }; + + for ( info++; info < ctrl->end && !(info->type & (T_CMD_BEG|T_GRP_BEG) ); info++ ) + { + if ( info->type & F_HIDDEN ) + { + // ignored -> do nothing + } + else if ( info->type & T_SEP_OPT ) + ctrl->need_sep = true; + else if ( info->type & (F_OPT_GLOBAL|T_CMD_OPT) ) + print_opt_link(ctrl,info); + else if ( info->type & T_COPY_CMD ) + print_cmd_links(ctrl,info); + else if ( info->type & T_COPY_GRP ) + print_grp_links(ctrl,info); + } + + ctrl->opt_prefix = saved_prefix; + return info; +} + +//----------------------------------------------------------------------------- + +static void dump_help ( const info_t * info_cmd ) +{ + char * temp_param = iobuf; + char * temp_help = iobuf + sizeof(iobuf)/8; + char * temp_xhelp = iobuf + sizeof(iobuf)/4; + + DumpText(0,temp_param,iobuf+sizeof(iobuf),info_cmd->param,-1,0,""); + ccp ff = info_cmd->help ? strchr(info_cmd->help,'\f') : 0; + if (ff) + { + DumpText(0,temp_help,iobuf+sizeof(iobuf),info_cmd->help,ff-info_cmd->help,0,""); + DumpText(0,temp_xhelp,iobuf+sizeof(iobuf),ff+1,-1,0,""); + } + else + { + DumpText(0,temp_help,iobuf+sizeof(iobuf),info_cmd->help,-1,0,""); + StringCopyE(temp_xhelp,iobuf+sizeof(iobuf),"\t0"); + } +} + +//----------------------------------------------------------------------------- + +static void print_links ( control_t * ctrl ) +{ + ASSERT(ctrl); + FILE * cf = ctrl->cf; + ASSERT(cf); + + print_section(cf,sep1,"InfoOption tabs"); + + char * temp_param = iobuf; + char * temp_help = iobuf + sizeof(iobuf)/8; + char * temp_xhelp = iobuf + sizeof(iobuf)/4; + char * sum_beg = iobuf + sizeof(iobuf)/2; + char * sum_end = iobuf + sizeof(iobuf); + char * sum = sum_beg; + + const info_t * info_cmd = ctrl->info; + ASSERT ( info_cmd->type & T_DEF_TOOL ); + fprintf(cf,"static const InfoOption_t * option_tab_tool[] =\n{\n"); + + ctrl->n_cmd_opt = 0; + ctrl->need_sep = false; + + print_links_iterator(ctrl,info_cmd,"def"); + fprintf(cf,"\n\t0\n};\n\n"); + + dump_help(info_cmd); + sum = snprintfE(sum,sum_end, + " {\t0,\n" // id + "\tfalse,\n" // hidden + "\tfalse,\n" // deprecated + "\tfalse,\n" // separator + "\t\"%s\",\n" // name1 + "\t0,\n" // name2 + "%s,\n" // param + "%s,\n" // help + "%s,\n" // xhelp + "\t%u,\n" // n_opt + "\toption_tab_tool,\n" // opt + "\t0\n" // opt_allowed + " },\n\n" + ,info_cmd->c_name + ,temp_param + ,temp_help + ,temp_xhelp + ,ctrl->n_cmd_opt + ); + + bool separator = false; + for ( info_cmd++; info_cmd < ctrl->end; info_cmd++ ) + { + if ( info_cmd->type & T_SEP_CMD ) + separator = true; + //if ( !( info_cmd->type & T_DEF_CMD ) || info_cmd->type & F_HIDDEN ) + if ( !( info_cmd->type & T_DEF_CMD ) ) + continue; + + fprintf(cf,"static const InfoOption_t * option_tab_cmd_%s[] =\n{\n", + info_cmd->c_name); + + ctrl->n_cmd_opt = 0; + ctrl->need_sep = false; + ctrl->cmd_name = info_cmd->c_name; + ResetStringField(&ctrl->opt_done); + + const info_t * info; + for ( info = info_cmd; info < ctrl->end; info++ ) + if ( info->type & T_CMD_BEG && !strcmp(info->c_name,info_cmd->c_name) ) + { + print_links_iterator(ctrl,info,"cmd"); + break; + } + fprintf(cf,"\n\t0\n};\n\n"); + + ccp name1 = info_cmd->namelist, ptr = name1; + while ( *ptr && *ptr != '|' ) + ptr++; + const int len1 = ptr - name1; + char name2[100] = "0"; + if ( *ptr == '|' ) + { + ccp n2 = ++ptr; + while ( *ptr && *ptr != '|' ) + ptr++; + if ( ptr > n2 ) + snprintf(name2,sizeof(name2),"\"%.*s\"",(int)(ptr-n2),n2); + } + + dump_help(info_cmd); + sum = snprintfE(sum,sum_end, + " {\tCMD_%s,\n" // id + "\t%s,\n" // hidden + "\t%s,\n" // deprecated + "\t%s,\n" // separator + "\t\"%.*s\",\n" // name1 + "\t%s,\n" // name2 + "%s,\n" // param + "%s,\n" // help + "%s,\n" // xhelp + "\t%u,\n" // n_opt + "\toption_tab_cmd_%s,\n" // opt + "\toption_allowed_cmd_%s\n" // opt_allowed + " },\n\n" + ,info_cmd->c_name + ,info_cmd->type & F_HIDDEN ? "true" : "false" + ,info_cmd->type & F_DEPRECATED ? "true" : "false" + ,separator ? "true" : "false" + ,len1 ,name1 + ,name2 + ,temp_param + ,temp_help + ,temp_xhelp + ,ctrl->n_cmd_opt + ,info_cmd->c_name + ,info_cmd->c_name + ); + separator = false; + } + + print_section(cf,sep1,"InfoCommand"); + fprintf(cf,"static const InfoCommand_t CommandInfo[CMD__N+1] =\n{\n"); + fputs(sum_beg,cf); + fprintf(cf," {0,0,0,0,0,0,0,0,0,0,0,0}\n};\n"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Generate() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static char var_buf[10000]; + +//----------------------------------------------------------------------------- + +static void CheckUsedName ( StringField_t *list, ccp name, uint len, ccp title ) +{ + //printf("CHECK: |%.*s|\n",len,name); + ccp key = MEMDUP(name,len); + if (!InsertStringField(list,key,len)) + fprintf(stderr,"!UI-WARNING: %s already defined: %.*s\n", title, len, name ); +} + +//----------------------------------------------------------------------------- + +static enumError Generate ( control_t * ctrl ) +{ + ASSERT(ctrl); + ASSERT(ctrl->info); + + FILE * cf = ctrl->cf; + FILE * hf = ctrl->hf; + ASSERT(cf); + ASSERT(hf); + + FREE(ctrl->opt_allow_grp); + FREE(ctrl->opt_allow_cmd); + ctrl->opt_allow_grp = ctrl->opt_allow_cmd = 0; + + const info_t *info; + StringField_t used_name; + InitializeStringField(&used_name); + + + //----- ui header + + fprintf(cf,text_ui_head); + fprintf(hf,text_ui_head); + + + //----- setup guard + + char guard[100]; + snprintf(guard,sizeof(guard),"SZS_UI_%s_h",ctrl->info->c_name); + char * ptr; + for ( ptr = guard; *ptr; ptr++ ) + *ptr = *ptr == '-' ? '_' : toupper((int)*ptr); + fprintf(hf,"\n#ifndef %s\n#define %s\n",guard,guard); + + + //----- header + + ASSERT( ctrl->info->type & T_DEF_TOOL ); + ccp tool_name = ctrl->info->c_name; + + fprintf(cf,"#include \n"); + fprintf(cf,"#include \"ui-%s.h\"\n",tool_name); + + fprintf(hf,"#include \"dclib-basics.h\"\n"); + fprintf(hf,"#include \"dclib-ui.h\"\n"); + + + //----- print enum enumOptions & OptionInfo[] + + print_section(cf,sep1,"OptionInfo[]"); + print_section(hf,sep1,"enum enumOptions"); + + char * var_ptr = var_buf; + char * var_end = var_buf + sizeof(var_buf); + var_ptr = snprintfE(var_ptr,var_end, + "//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1];\n"); + + fprintf(cf, + "static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] =\n" + "{\n" + " {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE,\n" + "\n" + ); + + fprintf(hf, + "typedef enum enumOptions\n" + "{\n" + "\tOPT_NONE,\n" + "\n" + ); + + ctrl->n_opt = 1; + ctrl->n_opt_specific = 0; + if ( ctrl->n_cmd ) + { + fprintf(cf," //----- command specific options -----\n\n"); + fprintf(hf,"\t//----- command specific options -----\n\n"); + + const info_t *first_info = 0; + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & F_OPT_COMMAND ) + { + if (!first_info) + first_info = info; + print_opt(ctrl,info,first_info); + } + + ctrl->n_opt_specific = ctrl->n_opt; + + fprintf(cf, + " {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == %d\n\n" + " //----- global options -----\n\n", + ctrl->n_opt_specific ); + + fprintf(hf, + "\n\tOPT__N_SPECIFIC, // == %d\n\n" + "\t//----- global options -----\n\n", + ctrl->n_opt_specific ); + } + + const info_t *first_info = 0; + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & F_OPT_GLOBAL ) + { + if (!first_info) + first_info = info; + print_opt(ctrl,info,first_info); + } + + fprintf(cf, + " {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == %d\n\n" + "};\n" + ,ctrl->n_opt ); + + fprintf(hf, + "\n\tOPT__N_TOTAL // == %d\n\n" + "} enumOptions;\n" + ,ctrl->n_opt ); + + if (ctrl->n_opt_specific) + { + noTRACE("opt_allowed = ( %2u + %2u ) * %2u\n", + ctrl->n_grp, ctrl->n_cmd, ctrl->n_opt_specific ); + if (ctrl->n_grp) + { + ctrl->opt_allow_grp = CALLOC(ctrl->n_grp,ctrl->n_opt_specific); + } + ctrl->opt_allow_cmd = CALLOC(ctrl->n_cmd,ctrl->n_opt_specific); + } + + + //----- print alternate option infos + + bool done = false; + const info_t * last_cmd = ctrl->info; + ctrl->opt_prefix = "def"; + for ( info = ctrl->info; info < ctrl->end; info++ ) + { + if ( info->type & T_CMD_BEG ) + { + ctrl->opt_prefix = "cmd"; + last_cmd = info; + } + else if ( info->type & T_GRP_BEG ) + { + ctrl->opt_prefix = "grp"; + last_cmd = info; + } + else if ( info->type & T_CMD_OPT && info->help && *info->help ) + { + if (!done) + { + print_section(cf,sep1,"alternate option infos"); + done = true; + } + + const info_t * info0; + for ( info0 = ctrl->info; info0 < info; info0++ ) + if ( info0->type & T_DEF_OPT && !strcmp(info->c_name,info0->c_name) ) + { + print_info_opt(ctrl,info,info0,0,last_cmd->c_name); + break; + } + } + } + + //----- print enum enumOptionsBit + + if ( ctrl->n_cmd ) + { + print_section(hf,sep1,"enum enumOptionsBit"); + + fprintf(hf, + "//\t***** only for verification *****\n" + "\n" + "//typedef enum enumOptionsBit\n" + "//{\n" + "//\t//----- command specific options -----\n" + "//\n" + ); + + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & F_OPT_COMMAND ) + fprintf(hf,"//\tOB_%s%.*s= 1llu << OPT_%s,\n", + info->c_name, + ( 28 - (int)strlen(info->c_name) ) / 8, tabs, + info->c_name ); + + fprintf(hf,"//\n//\t//----- group & command options -----\n"); + + for ( info = ctrl->info; info < ctrl->end; ) + { + ccp cmd_name; + u8 * opt_allow = 0; + if ( info->type & T_CMD_BEG ) + { + cmd_name = info->c_name; + fprintf(hf,"//\n//\tOB_CMD_%s%.*s=", + info->c_name, + ( 24 - (int)strlen(info->c_name) ) / 8, tabs ); + if (ctrl->opt_allow_cmd) + { + //PRINT("SELECT ALLOW CMD %u/%s\n",info->index,info->c_name); + opt_allow = ctrl->opt_allow_cmd + info->index * ctrl->n_opt_specific; + } + } + else if ( info->type & T_GRP_BEG ) + { + cmd_name = info->c_name; + fprintf(hf,"//\n//\tOB_GRP_%s%.*s=", + info->c_name, + ( 24 - (int)strlen(info->c_name) ) / 8, tabs ); + if (ctrl->opt_allow_grp) + { + //PRINT("SELECT ALLOW GRP %u/%s\n",info->index,info->c_name); + opt_allow = ctrl->opt_allow_grp + info->index * ctrl->n_opt_specific; + } + } + else + { + info++; + continue; + } + + info++; + char * dest = iobuf; + while ( info < ctrl->end ) + { + if ( info->type & T_ALL_OPT ) + { + dest += sprintf(dest,"\n//\t\t\t\t| ~(u64)0"); + if (opt_allow) + { + //PRINT("ALLOW ALL\n"); + memset(opt_allow,1,ctrl->n_opt_specific); + } + } + else if ( info->type & T_COPY_CMD ) + { + dest += sprintf(dest,"\n//\t\t\t\t| OB_CMD_%s",info->c_name); + if (opt_allow) + { + //PRINT("OR CMD %u/%s\n",info->index,info->c_name); + DASSERT(ctrl->opt_allow_cmd); + u8 * src = ctrl->opt_allow_cmd + info->index * ctrl->n_opt_specific; + u8 * dest = opt_allow; + int count = ctrl->n_opt_specific; + while ( count-- > 0 ) + *dest++ |= *src++; + } + } + else if ( info->type & T_COPY_GRP ) + { + dest += sprintf(dest,"\n//\t\t\t\t| OB_GRP_%s",info->c_name); + if ( opt_allow && ctrl->opt_allow_grp ) + { + //PRINT("OR GRP %u/%s\n",info->index,info->c_name); + u8 * src = ctrl->opt_allow_grp + info->index * ctrl->n_opt_specific; + u8 * dest = opt_allow; + int count = ctrl->n_opt_specific; + while ( count-- > 0 ) + *dest++ |= *src++; + } + } + else if ( info->type & T_CMD_OPT ) + { + if (FindStringField(&ctrl->copt,info->c_name)) + { + dest += sprintf(dest,"\n//\t\t\t\t| OB_%s",info->c_name); + if ( opt_allow && info->index ) + { + //PRINT("ALLOW OPT %u/%s\n",info->index,info->c_name); + opt_allow[info->index] = 1; + } + } + else if (!FindStringField(&ctrl->gopt,info->c_name)) + ERROR0(ERR_SEMANTIC,"Option not defined: %s %s --%s", + tool_name, cmd_name, info->c_name ); + } + else if ( info->type & (T_CMD_BEG|T_GRP_BEG) ) + break; + ASSERT( dest < iobuf + sizeof(iobuf) ); + info++; + } + if ( dest == iobuf ) + fprintf(hf," 0,\n"); + else + fprintf(hf,"%s,\n",iobuf+8); + } + + fprintf(hf,"//\n//} enumOptionsBit;\n"); + } + + + //----- print enum enumCommands & CommandTab[] + + print_section(hf,sep1,"enum enumCommands"); + fprintf(hf, + "typedef enum enumCommands\n" + "{\n" + "\tCMD__NONE," + ); + + if ( ctrl->n_cmd ) + { + print_section(cf,sep1,"CommandTab[]"); + fputs("\n\n",hf); + + var_ptr = snprintfE(var_ptr,var_end, + "//extern const KeywordTab_t CommandTab[];\n"); + + fprintf(cf, + "static const KeywordTab_t CommandTab[] =\n" + "{\n" + ); + + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & T_DEF_CMD ) + { + fprintf( hf, "\tCMD_%s,\n",info->c_name); + ccp ptr = info->namelist; + while (*ptr) + { + ccp n1 = ptr; + while ( *ptr && *ptr != '|' ) + ptr++; + const int l1 = ptr - n1; + CheckUsedName(&used_name,n1,l1,"Command"); + while ( *ptr == '|' ) + ptr++; + if (*ptr) + { + ccp n2 = ptr; + while ( *ptr && *ptr != '|' ) + ptr++; + const int l2 = ptr - n2; + CheckUsedName(&used_name,n2,l2,"Command"); + + fprintf(cf, + " { CMD_%s,%.*s\"%.*s\",%.*s\"%.*s\",%.*s0 },\n", + info->c_name, (20-(int)strlen(info->c_name))/8, tabs, + l1, n1, (20-l1)/8, tabs, + l2, n2, (20-l2)/8, tabs ); + + while ( *ptr == '|' ) + ptr++; + } + else + fprintf(cf, " { CMD_%s,%.*s\"%.*s\",%.*s0,\t\t0 },\n", + info->c_name, (20-(int)strlen(info->c_name))/8, tabs, + l1, n1, (20-l1)/8, tabs ); + } + } + else if ( info->type == T_SEP_CMD ) + fprintf(hf,"\n"); + + fprintf(cf, + "\n { CMD__N,0,0,0 }\n" + "};\n" + ); + + ResetStringField(&used_name); + } + + fprintf(hf, + "\n\tCMD__N // == %u\n\n" + "} enumCommands;\n" + , ctrl->n_cmd ); + + + //----- print options + + print_section(cf,sep1,"OptionShort & OptionLong"); + + char * dest = iobuf; + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & T_DEF_OPT && info->namelist[1] == '|' ) + { + CheckUsedName(&used_name,info->namelist,1,"Short option"); + *dest++ = info->namelist[0]; + if ( ( info->type & (F_OPT_OPTPARAM|F_OPT_PARAM) ) == F_OPT_PARAM ) + *dest++ = ':'; + } + *dest = 0; + fprintf(cf,"static const char OptionShort[] = \"%s\";\n\n",iobuf); + var_ptr = snprintfE(var_ptr,var_end, + "//extern const char OptionShort[];\n"); + + ccp opt_buf[UIOPT_INDEX_SIZE]; + memset(opt_buf,0,sizeof(opt_buf)); + int getopt_idx = UIOPT_LONG_BASE; + ResetStringField(&used_name); + + fprintf(cf,"static const struct option OptionLong[] =\n{\n"); + var_ptr = snprintfE(var_ptr,var_end, + "//extern const struct option OptionLong[];\n"); + + for ( info = ctrl->info; info < ctrl->end; info++ ) + if ( info->type & T_DEF_OPT ) + { + ccp ptr = info->namelist; + const int pmode = (info->type & F_OPT_OPTPARAM) + ? 2 + : (info->type & F_OPT_PARAM) + ? 1 + : 0; + + if ( info->namelist[1] == '|' ) + { + snprintf(iobuf,sizeof(iobuf),"%d, 0, '%c'", + pmode, info->namelist[0] ); + ptr += 2; + opt_buf[(u8)(info->namelist[0])] = info->c_name; + } + else + { + snprintf(iobuf,sizeof(iobuf),"%d, 0, GO_%s", + pmode, info->c_name ); + ASSERT_MSG( getopt_idx < UIOPT_INDEX_SIZE, + "getopt_idx >= UIOPT_INDEX_SIZE [ 0x%x >= 0x%x ]\n", + getopt_idx, UIOPT_INDEX_SIZE ); + if ( getopt_idx < UIOPT_INDEX_SIZE ) + opt_buf[getopt_idx++] = info->c_name; + } + + int indent = 0; + while (*ptr) + { + ccp start = ptr; + while ( *ptr && *ptr != '|' ) + ptr++; + const int len = ptr - start; + if (len) + { + CheckUsedName(&used_name,start,len,"Long option"); + fprintf(cf,"\t%s{ \"%.*s\",%.*s%s },\n", + indent ? " " : "", len, start, (26-len-indent)/8, tabs, iobuf ); + indent = 1; + } + if (*ptr) + ptr++; + } + } + fprintf(cf,"\n\t{0,0,0,0}\n};\n"); + + + //----- print enumGetOpt + + print_section(hf,sep1,"enumGetOpt"); + fprintf(hf,"typedef enum enumGetOpt\n{"); + + // add '?' temporary; + ASSERT(!opt_buf['?']); + opt_buf['?'] = "_ERR"; + + static const int septab[] = { 0, '0', '9'+1, '?', '?'+1, + 'A', 'Z'+1, 'a', 'z'+1, + UIOPT_LONG_BASE, UIOPT_INDEX_SIZE }; + const int * sepptr = septab; + int i; + for ( i = 0; i < UIOPT_INDEX_SIZE; i++ ) + if ( opt_buf[i] ) + { + if ( i >= *sepptr ) + { + fputc('\n',hf); + while ( i >= *sepptr ) + sepptr++; + } + if ( i < UIOPT_LONG_BASE ) + fprintf(hf,"\tGO_%s%.*s= '%c',\n", + opt_buf[i], (28-(int)strlen(opt_buf[i]))/8, tabs, i ); + else if ( i == UIOPT_LONG_BASE ) + fprintf(hf,"\tGO_%s%.*s= 0x%02x,\n", + opt_buf[i], (28-(int)strlen(opt_buf[i]))/8, tabs, i ); + else + fprintf(hf,"\tGO_%s,\n",opt_buf[i]); + } + + fprintf(hf,"\n} enumGetOpt;\n"); + opt_buf['?'] = 0; + + + //----- print option index + + print_section(cf,sep1,"OptionUsed & OptionIndex"); + + fprintf(cf,"static u8 OptionUsed[OPT__N_TOTAL+1] = {0};\n\n"); + var_ptr = snprintfE(var_ptr,var_end, + "//extern u8 OptionUsed[OPT__N_TOTAL+1];\n"); + + fprintf(cf,"static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] =\n{\n"); + var_ptr = snprintfE(var_ptr,var_end, + "//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE];\n" + "//UIOPT_INDEX_SIZE := 0x%x = %u\n", + UIOPT_INDEX_SIZE, UIOPT_INDEX_SIZE ); + + for ( i = 0; i < UIOPT_INDEX_SIZE; ) + { + int start = i; + while ( i < UIOPT_INDEX_SIZE && !opt_buf[i] ) + i++; + int len = i - start; + while ( len > 0 ) + { + const int now_len = len < 16 ? len : 16 - start % 16; + fprintf(cf,"\t/* 0x%0*x */\t %.*s\n", + UIOPT_INDEX_FW, + start, + 2*now_len + now_len/4, + "0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0," ); + start += now_len; + len -= now_len; + } + + while ( i < UIOPT_INDEX_SIZE && opt_buf[i] ) + { + fprintf(cf,"\t/* 0x%0*x %c */\tOPT_%s,\n", + UIOPT_INDEX_FW, i, i > ' ' && i < 0x7f ? i : ' ', opt_buf[i]); + i++; + } + } + fprintf(cf,"};\n"); + + + //----- option allowed + + if (ctrl->opt_allow_cmd) + { + print_section(cf,sep1,"opt_allowed_cmd_*"); + for ( info = ctrl->info; info < ctrl->end; info++ ) + { + if ( !( info->type & T_DEF_CMD ) ) + continue; + + fprintf(cf,"static u8 option_allowed_cmd_%s[%u] = // cmd #%u\n{", + info->c_name, ctrl->n_opt_specific, info->index ); + + int i; + u8 * ptr = ctrl->opt_allow_cmd + info->index * ctrl->n_opt_specific; + for ( i = 0; i < ctrl->n_opt_specific; i++ ) + fprintf(cf, "%s%u%s", + !(i%30) ? "\n " : !(i%10) ? " " : !(i%5) ? " " : "", + ptr[i], + i < ctrl->n_opt_specific-1 ? "," : "" ); + + fprintf(cf,"\n};\n\n"); + } + } + + //----- InfoCommand + + print_links(ctrl); + var_ptr = snprintfE(var_ptr,var_end, + "//extern const InfoCommand_t CommandInfo[CMD__N+1];\n"); + + //----- InfoUI + + char xname[100]; + { + StringCopyS(xname,sizeof(xname),ctrl->info->c_name); + char *ptr; + for ( ptr = xname; *ptr; ptr++ ) + if (!isalnum((int)*ptr)) + *ptr = '_'; + } + + print_section(cf,sep1,"InfoUI"); + + var_ptr = snprintfE(var_ptr,var_end, + "extern const InfoUI_t InfoUI_%s;\n", + xname ); + + fprintf(cf, + "const InfoUI_t InfoUI_%s =\n" + "{\n" + "\t\"%s\",\n" // tool_name + "\t%s\n" // n_cmd + "\t%s\n" // cmd_tab + "\tCommandInfo,\n" // cmd_info + "\t%s\n" // n_opt_specific + "\tOPT__N_TOTAL,\n" // n_opt_total + "\tOptionInfo,\n" // opt_info + "\tOptionUsed,\n" // opt_used + "\tOptionIndex,\n" // opt_index + "\tOptionShort,\n" // opt_short + "\tOptionLong\n" // opt_long + "};\n", + xname, + ctrl->info->c_name, + ctrl->n_cmd ? "CMD__N," : "0, // n_cmd", + ctrl->n_cmd ? "CommandTab," : "0, // cmd_tab", + ctrl->n_cmd ? "OPT__N_SPECIFIC," : "0, // n_opt_specific" ); + + + //----- external vars + + print_section(hf,sep1,"external vars"); + fputs(var_buf,hf); + + + //----- terminate + + print_section(cf,sep1,"END"); + print_section(hf,sep1,"END"); + fprintf(hf,"#endif // %s\n\n",guard); + ResetStringField(&used_name); + + return ERR_OK; +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dummy functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) {} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddTables() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void AddTables ( FILE * df ) +{ + DASSERT(df); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ui_main ( int argc, char ** argv ) +{ + static ccp def_null_name = "/dev/null"; + FILE * nf = fopen(def_null_name,"wb"); + if (!nf) + { + fprintf(stderr,"!!! Can't create file: %s\n",def_null_name); + return ERR_CANT_CREATE; + } + + static ccp def_fname = "src/ui/ui.def"; + FILE * df = fopen(def_fname,"wb"); + if (!df) + { + fprintf(stderr,"!!! Can't create file: %s\n",def_fname); + return ERR_CANT_CREATE; + } + + char fname_c[200], fname_c_temp[210]; + char fname_h[200], fname_h_temp[210]; + info_t * info = info_tab; + + while ( info->type != T_END ) + { + if ( ! ( info->type & T_DEF_TOOL ) ) + { + fprintf(stderr,"!!! Missing T_DEF_TOOL entry.\n"); + return ERR_SYNTAX; + } + + control_t ctrl; + memset(&ctrl,0,sizeof(ctrl)); + ctrl.df = nf; + ctrl.opt_prefix = ""; + InitializeStringField(&ctrl.gopt); + InitializeStringField(&ctrl.copt); + InitializeStringField(&ctrl.opt_done); + + if ( !( info->type & F_HIDDEN ) ) + { + ctrl.df = df; + snprintf(iobuf,sizeof(iobuf),"Tool '%s'",info->c_name); + print_section(df,sep2,iobuf); + fprintf(df,"#:def_tool( \"%s\", \\\n",info->c_name); + DumpText(df,0,0,info->param,-1,1,", \\\n"); + DumpText(df,0,0,info->help,-1,1," )\n\n"); + } + + snprintf(fname_c,sizeof(fname_c),"src/ui/ui-%s.c",info->c_name); + StringCat2S(fname_c_temp,sizeof(fname_c_temp),fname_c,".temp"); + ctrl.cf = fopen(fname_c_temp,"wb"); + if (!ctrl.cf) + { + fprintf(stderr,"!!! Can't create file: %s\n",fname_c_temp); + return ERR_CANT_CREATE; + } + + snprintf(fname_h,sizeof(fname_h),"src/ui/ui-%s.h",info->c_name); + StringCat2S(fname_h_temp,sizeof(fname_h_temp),fname_h,".temp"); + ctrl.hf = fopen(fname_h_temp,"wb"); + if (!ctrl.hf) + { + fclose(ctrl.cf); + fprintf(stderr,"!!! Can't create file: %s\n",fname_h_temp); + return ERR_CANT_CREATE; + } + + ctrl.info = info++; + while ( ! ( info->type & (T_END|T_DEF_TOOL)) ) + { + if ( info->type & T_DEF_OPT ) + { + if ( info->type & F_OPT_GLOBAL ) + { + InsertStringField(&ctrl.gopt,info->c_name,false); + ++ctrl.n_opt; + //opt_index = 0 for global options + } + else + { + InsertStringField(&ctrl.copt,info->c_name,false); + info->index = ++ctrl.n_opt_specific; + } + + if ( !info->help ) + { + // copy 'param' and 'help' info from previous tool + const info_t * search; + for ( search = info; search >= info_tab; search-- ) + if ( search->type & T_DEF_OPT + && search->help + && !strcmp(search->c_name,info->c_name) ) + { + info->help = search->help; + if (!info->param) + info->param = search->param; + break; + } + } + + if ( !( info->type & F_HIDDEN ) ) + { + fprintf(ctrl.df,"#:def_opt( \"%s\", \"%s\", \"%s%s%s%s%s\", \\\n", + info->c_name, info->namelist, + info->type & F_OPT_COMMAND ? "C" : "", + info->type & F_OPT_GLOBAL ? "G" : "", + info->type & F_OPT_MULTIUSE ? "M" : "", + info->type & F_OPT_PARAM ? "P" : "", + info->type & F_OPT_OPTPARAM ? "O" : "" ); + DumpText(ctrl.df,0,0,info->param,-1,1,", \\\n"); + DumpText(ctrl.df,0,0,info->help,-1,1," )\n\n"); + } + } + else if ( info->type & T_DEF_CMD ) + { + info->index = ++ctrl.n_cmd; + + if ( !info->help ) + { + // copy 'param' and 'help' info from previous tool + const info_t * search = info-1; + for ( search = info; search >= info_tab; search-- ) + if ( search->type & T_DEF_CMD + && search->help + && !strcmp(search->c_name,info->c_name) ) + { + info->help = search->help; + if (!info->param) + info->param = search->param; + break; + } + } + + if ( !( info->type & F_HIDDEN ) ) + { + fprintf(ctrl.df,"#:def_cmd( \"%s\", \"%s\", \\\n", + info->c_name, info->namelist ); + DumpText(ctrl.df,0,0,info->param,-1,1,", \\\n"); + + ccp ff = info->help ? strchr(info->help,'\f') : 0; + if (ff) + { + DumpText(ctrl.df,0,0,info->help,ff-info->help,1,", \\\n"); + DumpText(ctrl.df,0,0,ff+1,-1,1," )\n\n"); + } + else + DumpText(ctrl.df,0,0,info->help,-1,1," )\n\n"); + } + } + else if ( info->type & T_GRP_BEG ) + info->index = ctrl.n_grp++; // NULL based + else if ( info->type & (T_CMD_OPT|T_CMD_BEG|T_COPY_GRP|T_COPY_CMD) ) + { + const int type = info->type & T_CMD_OPT ? T_DEF_OPT + : info->type & T_COPY_GRP ? T_GRP_BEG + : T_DEF_CMD; + + const info_t * search; + for ( search = ctrl.info; search < info; search++ ) + if ( search->type & type + && !strcmp(search->c_name,info->c_name) ) + { + //PRINT("COPY INDEX #%x: %s\n",info->type,info->c_name); + info->index = search->index; + break; + } + } + + info++; + } + ctrl.end = info; + + if (ctrl.n_cmd) + ctrl.n_cmd++; // one more for CMD_NONE; + + if (ctrl.n_opt_specific) + ctrl.n_opt += ++ctrl.n_opt_specific; + + TRACE("N: cmd=%u, grp=%u, opt=%d/%d\n", + ctrl.n_cmd, ctrl.n_grp, ctrl.n_opt_specific, ctrl.n_opt ); + const enumError err = Generate(&ctrl); + + fclose(ctrl.cf); + fclose(ctrl.hf); + rename(fname_c_temp,fname_c); + rename(fname_h_temp,fname_h); + + ResetStringField(&ctrl.gopt); + ResetStringField(&ctrl.copt); + ResetStringField(&ctrl.opt_done); + + if (err) + return err; + } + + AddTables(df); + print_section(df,sep2,"END"); + fclose(df); + fclose(nf); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-mysql.c b/project/dclib/dclib-mysql.c new file mode 100644 index 0000000..e3ee297 --- /dev/null +++ b/project/dclib/dclib-mysql.c @@ -0,0 +1,1560 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include + +#include "dclib-mysql.h" +#include "dclib-network.h" + +// http://dev.mysql.com/doc/refman/5.1/en/c-api-functions.html + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MySql_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// management + +void InitializeMYSQL ( MySql_t *my ) +{ + #if DEBUG + static uint done = 0; + if (!done) + { + done++; + TRACE("- MYSQL\n"); + TRACE_SIZEOF(MySql_t); + TRACE_SIZEOF(MySqlResult_t); + TRACE_SIZEOF(MySqlStatus_t); + TRACE_SIZEOF(st_mysql); + TRACE_SIZEOF(st_mysql_res); + TRACE_SIZEOF(st_mysql_field); + } + #endif + + DASSERT(my); + memset(my,0,sizeof(*my)); + my->auto_reconnect = true; + my->prefix = EmptyString; + InitializeStatusMYSQL(&my->status); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMYSQL ( MySql_t *my ) +{ + DASSERT(my); + CloseMYSQL(my); + + FREE((char*)my->server); + FREE((char*)my->user); + FREE((char*)my->password); + FREE((char*)my->database); + FreeString(my->prefix); + + InitializeMYSQL(my); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenMYSQL ( MySql_t *my, uint loglevel ) +{ + DASSERT(my); + ClearStatusMYSQL(&my->status); + + if (my->mysql) + return ERR_OK; // already connected + + if ( !my || !my->server ) + { + if ( loglevel >= MYLL_ERROR ) + ERROR0(ERR_MISSING_PARAM,"No MySql server defined.\n"); + return ERR_MISSING_PARAM; + } + + NetworkHost_t host; + ResolveHost(&host,true,my->server,MYSQL_DEFAULT_PORT,false,true); + + PRINT("MYSQL CONNECT: %s:%u -> %s\n", + host.name, host.port, PrintIP4(0,0,host.ip4,host.port) ); + + static bool lib_init_done = false; + if (!lib_init_done) + { + noPRINT("mysql_library_init()\n"); + if (mysql_library_init(0,NULL,NULL)) + { + if ( loglevel >= MYLL_ERROR ) + ERROR0(ERR_DATABASE,"Can't initialize MySql library.\n"); + return ERR_DATABASE; + } + lib_init_done = true; + } + + my->mysql = mysql_init(NULL); + //HEXDUMP16(0,0,my->mysql,sizeof(*my->mysql)); + if (!my->mysql) + { + if ( loglevel >= MYLL_ERROR ) + ERROR0(ERR_DATABASE,"Can't initialize MySql data.\n"); + ResetHost(&host); + return ERR_DATABASE; + } + + if (my->auto_reconnect) + { + my_bool reconnect = 1; + mysql_options(my->mysql,MYSQL_OPT_RECONNECT,&reconnect); + } + + noPRINT("connect(%p,%s,%s,%s,%s,%u,,)\n", + my->mysql, host.name, my->user, my->password, my->database, host.port ); + MYSQL *stat = mysql_real_connect ( my->mysql, host.name, + my->user, my->password, my->database, + host.port, NULL, 0 ); + + if (!stat) + { + GetStatusMYSQL(&my->status,my->mysql); + if ( loglevel >= MYLL_ERROR ) + ERROR0(ERR_CANT_CONNECT,"Can't connect to %s:\n-> %s\n", + PrintIP4(0,0,host.ip4,host.port),my->status.message); + ResetHost(&host); + mysql_close(my->mysql); + my->mysql = 0; + return ERR_CANT_CONNECT; + } + + mysql_set_character_set(my->mysql,"utf8mb4"); + my->total_connect_count++; + ResetHost(&host); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseMYSQL ( MySql_t *my ) +{ + DASSERT(my); + + //--- close results + + FreeResultListMYSQL(my->first_result); + my->first_result = 0; + FreeResultListMYSQL(my->pool_result); + my->pool_result = 0; + + + //--- close database + + if (my->mysql) + { + mysql_close(my->mysql); + my->mysql = 0; + } + + + //--- reset status + + ClearStatusMYSQL(&my->status); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineDatabase +( + MySql_t *my, // valid struct + ccp server, // NULL or server name + ccp user, // NULL or user name + ccp password, // NULL or user password + ccp database // NULL or name of database +) +{ + DASSERT(my); + CloseMYSQL(my); + + if (server) + { + FREE((char*)my->server); + my->server = STRDUP(server); + } + + if (user) + { + FREE((char*)my->user); + my->user = STRDUP(user); + } + + if (password) + { + FREE((char*)my->password); + my->password = STRDUP(password); + } + + if (database) + { + FREE((char*)my->database); + my->database = STRDUP(database); + } + + FreeString(my->prefix); + my->prefix = EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineDatabasePrefix +( + MySql_t *my, // valid struct + ccp prefix // NULL or default prefix, only for external usage +) +{ + DASSERT(my); + + FreeString(my->prefix); + my->prefix = prefix && *prefix ? STRDUP(prefix) : EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOpenMYSQL ( MySql_t *my, uint loglevel ) +{ + if (AutoOpenMYSQL(my,loglevel)) + return ERR_OK; + + if ( loglevel >= MYLL_ERROR ) + ERROR0(ERR_SEMANTIC,"No database connected.\n"); + return ERR_SEMANTIC; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// queries + +enumError DoQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp query, // query string + int query_length // length of 'query', -1 if unknown +) +{ + DASSERT(my); + DASSERT(query); + + const u64 start_time = GetTimeUSec(false); + + enumError err = CheckOpenMYSQL(my,loglevel); + if (err) + return err; + + if ( query_length < 0 ) + query_length = strlen(query); + my->total_query_count++; + my->total_query_size += query_length; + + if (mysql_real_query(my->mysql,query,query_length)) + { + TRACE("QUERY: %.*s\n",query_length,query); + my->field_count = 0; + GetStatusMYSQL(&my->status,my->mysql); + + bool fail = true; + if ( my->status.status == CR_SERVER_GONE_ERROR && my->auto_reconnect ) + { + MySqlStatus_t save; + MoveStatusMYSQL(&save,true,&my->status); + CloseMYSQL(my); + if ( OpenMYSQL(my,loglevel) || mysql_real_query(my->mysql,query,query_length) ) + MoveStatusMYSQL(&my->status,false,&save); + else + { + ClearStatusMYSQL(&save); + fail = false; + } + } + + if (fail) + { + if ( loglevel >= MYLL_QUERY_ON_ERROR ) + ERROR0(ERR_DATABASE,"%s (#%d)\n--QUERY--\n%.*s\n", + my->status.message, my->status.status, + query_length, query ); + else if ( loglevel >= MYLL_ERROR ) + ERROR0(ERR_DATABASE,"%s (#%d)\n", + my->status.message, my->status.status ); + + my->last_duration_usec = GetTimeUSec(false) - start_time; + if ( my->max_duration_usec < my->last_duration_usec ) + my->max_duration_usec = my->last_duration_usec; + my->total_duration_usec += my->last_duration_usec; + return ERR_DATABASE; + } + } + + if ( loglevel >= MYLL_QUERY_ALWAYS ) + ERROR0(ERR_OK,"%.*s\n", query_length, query ); + + my->field_count = mysql_field_count(my->mysql); + ClearStatusMYSQL(&my->status); + + my->last_duration_usec = GetTimeUSec(false) - start_time; + if ( my->max_duration_usec < my->last_duration_usec ) + my->max_duration_usec = my->last_duration_usec; + my->total_duration_usec += my->last_duration_usec; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintArgQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +) +{ + DASSERT(my); + DASSERT(format); + + char buf[10000]; + + va_list arg2; + va_copy(arg2,arg); + int stat = vsnprintf(buf,sizeof(buf),format,arg2); + va_end(arg2); + + if ( stat < sizeof(buf) ) + return DoQueryMYSQL(my,loglevel,buf,stat); + + + //--- buffer too small, use dynamic memory + + noPRINT("PrintQueryMYSQL() -> MALLOC(%u)\n",stat+1); + + char *dest = MALLOC(stat+1); + stat = vsnprintf(dest,stat+1,format,arg); + + const enumError err = DoQueryMYSQL(my,loglevel,dest,stat); + FREE(dest); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +{ + DASSERT(my); + DASSERT(format); + + va_list arg; + va_start(arg,format); + enumError err = PrintArgQueryMYSQL(my,loglevel,format,arg); + va_end(arg); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// support of multi queries + +mem_t GetFirstQuery ( ccp src, int src_len ) +{ + if (!src) + { + mem_t res = {0,0}; + return res; + } + + ccp ptr = src; + ccp end = ptr + ( src_len < 0 ? strlen(src) : src_len ); + + //--- skip all leading blanks, controls and semicolons + + while ( ptr < end && ( *ptr >= 0 && *ptr <= ' ' || *ptr == ';' ) ) + ptr++; + + if ( ptr == end ) + { + mem_t res = {ptr,0}; + return res; + } + + ccp start = ptr; + while ( ptr < end ) + { + const char ch = *ptr++; + + if ( ch == ';' ) + { + mem_t res = {start,ptr-start-1}; + return res; + } + + if ( ch == '\\' && ptr < end ) + ptr++; + else if ( ch == '\'' || ch == '\"' ) + { + while ( ptr < end ) + { + const char ch2 = *ptr++; + if ( ch2 == '\\' && ptr < end ) + ptr++; + else if ( ch2 == ch ) + break; + } + } + } + mem_t res = {start,ptr-start}; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DoMultiQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp query, // query string + int query_length // length of 'query', -1 if unknown +) +{ + DASSERT(my); + DASSERT(query); + + mem_t q; + q.ptr = query; + q.len = query_length < 0 ? strlen(query) : query_length; + + for(;;) + { + mem_t q1 = GetFirstQuery(q.ptr,q.len); + if (!q1.len) + break; + const enumError err = DoQueryMYSQL(my,loglevel,q1.ptr,q1.len); + if (err) + return err; + q = BehindMem(q,q1.ptr+q1.len); + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintMultiQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +{ + DASSERT(my); + DASSERT(format); + + char buf[10000]; + va_list arg; + va_start(arg,format); + int stat = vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + + if ( stat < sizeof(buf) ) + return DoMultiQueryMYSQL(my,loglevel,buf,stat); + + + //--- buffer too small, use dynamic memory + + noPRINT("PrintQueryMYSQL() -> MALLOC(%u)\n",stat+1); + + char *dest = MALLOC(stat+1); + va_start(arg,format); + stat = vsnprintf(dest,stat+1,format,arg); + va_end(arg); + + const enumError err = DoMultiQueryMYSQL(my,loglevel,dest,stat); + FREE(dest); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// temp buffer support + +mem_t CopyMemMYSQL ( MySql_t * my, mem_t * src ) // 'src' may be NULL +{ + if ( src && src->len > 0 ) + { + if ( src->len < sizeof(my->tempbuf) && !my->tempbuf_used ) + { + noPRINT("MYSQL: use tempbuf(%u)\n",src->len); + memcpy(my->tempbuf,src->ptr,src->len); + my->tempbuf[src->len] = 0; + my->tempbuf_used = true; + + mem_t res = { my->tempbuf, src->len }; + return res; + } + + return DupMem(*src); + } + + mem_t res = {NULL,0}; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeMemMYSQL ( MySql_t * my, mem_t mem ) +{ + DASSERT(my); + if ( mem.ptr == my->tempbuf ) + { + noPRINT("MYSQL: free tempbuf\n"); + my->tempbuf_used = false; + } + else + FREE((char*)mem.ptr); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// fetch single value as result of a query + +mem_t FetchScalarMYSQL // FREE the result! +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel // log level for mysql errors and queries +) +{ + DASSERT(my); + mem_t ret = {NULL,0}; + + MySqlResult_t *res = UseResultMYSQL(my,loglevel); + if (res) + { + ret = CopyMemMYSQL(my,GetNextRowMYSQL(res)); + while (GetNextRowMYSQL(res)) // get all rows + ; + FreeResultMYSQL(res); + } + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// + +s64 FetchIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + s64 return_default // return this value on non existent result +) +{ + DASSERT(my); + mem_t res = FetchScalarMYSQL(my,loglevel); + if (!res.len) + return return_default; + + char *end; + const s64 val = strtoll(res.ptr,&end,10); + FreeMemMYSQL(my,res); + return end == res.ptr + res.len ? val : return_default; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 FetchUIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + u64 return_default // return this value on non existent result +) +{ + DASSERT(my); + mem_t res = FetchScalarMYSQL(my,loglevel); + if (!res.len) + return return_default; + + char *end; + const u64 val = strtoull(res.ptr,&end,10); + FreeMemMYSQL(my,res); + return end == res.ptr + res.len ? val : return_default; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// print query & fetch single value as result + +mem_t PrintArgFetchScalarMYSQL +( + // if res.len>0: FREE the result using FreeMemMYSQL(my,mem) + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +) +{ + DASSERT(my); + DASSERT(format); + + mem_t ret = {NULL,0}; + if (!PrintArgQueryMYSQL(my,loglevel,format,arg)) + { + MySqlResult_t *res = UseResultMYSQL(my,loglevel); + if (res) + { + ret = CopyMemMYSQL(my,GetNextRowMYSQL(res)); + while (GetNextRowMYSQL(res)) // get all rows + ; + FreeResultMYSQL(res); + } + } + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t PrintFetchScalarMYSQL +( + // if res.len>0: FREE the result using FreeMemMYSQL(my,mem) + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +{ + DASSERT(my); + DASSERT(format); + + va_list arg; + va_start(arg,format); + mem_t res = PrintArgFetchScalarMYSQL(my,loglevel,format,arg); + va_end(arg); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +s64 PrintFetchIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + s64 return_default, // return this value on non existent result + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +{ + DASSERT(my); + DASSERT(format); + + va_list arg; + va_start(arg,format); + mem_t res = PrintArgFetchScalarMYSQL(my,loglevel,format,arg); + va_end(arg); + + if (!res.len) + return return_default; + + char *end; + const s64 val = strtoll(res.ptr,&end,10); + FreeMemMYSQL(my,res); + return end == res.ptr + res.len ? val : return_default; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 PrintFetchUIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + u64 return_default, // return this value on non existent result + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +{ + DASSERT(my); + DASSERT(format); + + va_list arg; + va_start(arg,format); + mem_t res = PrintArgFetchScalarMYSQL(my,loglevel,format,arg); + va_end(arg); + + if (!res.len) + return return_default; + + char *end; + const u64 val = strtoull(res.ptr,&end,10); + FreeMemMYSQL(my,res); + return end == res.ptr + res.len ? val : return_default; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// fetch single row + +void * FetchSingleRowN +( + // returns NULL on error or emtpy result, + // or a temporary buffer => call FREE(result) + // following rows will be skipped + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + mem_t *dest, // dest buffer, unused elements are set to {0,0} + uint n_dest // number of elements in 'dest' +) +{ + DASSERT(my); + DASSERT( dest || !n_dest ); + memset(dest,0,sizeof(*dest)*n_dest); + + char *buf = 0; + MySqlResult_t *res = UseResultMYSQL(my,loglevel); + if (res) + { + mem_t *row = GetNextRowMYSQL(res); + if (row) + { + if ( n_dest > res->field_count ) + n_dest = res->field_count; + + //--- count memory usage and alloc buffer + + uint i, need = 0; + for ( i = 0; i < n_dest; i++ ) + if ( row[i].len ) + need += row[i].len + 1; + + //--- get row data + + buf = MALLOC(need); + char *ptr = buf; + for ( i = 0; i < n_dest; i++, row++, dest++ ) + { + if ( row->len ) + { + dest->ptr = ptr; + memcpy(ptr,row->ptr,row->len); + dest->len = row->len; + ptr += row->len; + *ptr++ = 0; + DASSERT( ptr <= buf+need ); + } + } + DASSERT( ptr == buf+need ); + + + //--- get and ignore all remaining rows + + while (GetNextRowMYSQL(res)) + ; + } + FreeResultMYSQL(res); + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int FetchSingleRowList +( + // returns -1 on error, 0 on empty result or the number of retrieved field + // following rows will be skipped + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + mem_list_t *dest, // destination list + bool init_dest, // true: initialize 'dest' + bool replace_null // true: replace NULL by EmptyString +) +{ + DASSERT(my); + DASSERT(dest); + + if (init_dest) + InitializeMemList(dest); + dest->used = 0; + + int stat = -1; + MySqlResult_t *res = UseResultMYSQL(my,loglevel); + if (res) + { + stat = 0; + mem_t *row = GetNextRowMYSQL(res); + if (row) + { + stat = res->field_count; + AssignMemListN(dest,row,stat,replace_null?3:0); + + //--- get and ignore all remaining rows + while (GetNextRowMYSQL(res)) + ; + } + FreeResultMYSQL(res); + } + + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// count tables and columns + +int CountTablesMYSQL +( + MySql_t *my, // valid DB connection + ccp table, // like "table"; if NULL|EMPTY: count tables + ccp database // database name; if NULL|EMPTY: use default DB +) +{ + DASSERT(my); + + int stat = -1; + mem_t db = EscapeStringMYSQL(my, database && *database ? database : my->database ); + mem_t tab = EscapeStringMYSQL(my, table && *table ? table : "%" ); + + stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,-1, + "SELECT COUNT(DISTINCT TABLE_NAME) FROM information_schema.COLUMNS\n" + "WHERE TABLE_SCHEMA = \"%s\" && TABLE_NAME like \"%s\"\n", + db.ptr, tab.ptr ); + + FreeString(tab.ptr); + FreeString(db.ptr); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CountColumnsMYSQL +( + MySql_t *my, // valid DB connection + ccp column, // column name; if NULL|EMPTY: count columns + ccp table, // like "table"; if NULL|EMPTY: all tables + ccp database // database name; if NULL|EMPTY: use default DB +) +{ + DASSERT(my); + + int stat = -1; + mem_t db = EscapeStringMYSQL(my, database && *database ? database : my->database ); + mem_t tab = EscapeStringMYSQL(my, table && *table ? table : "%" ); + + if ( column && *column ) + { + mem_t col = EscapeStringMYSQL(my,column); + stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,-1, + "SELECT COUNT(*) FROM information_schema.COLUMNS\n" + "WHERE TABLE_SCHEMA = \"%s\" && TABLE_NAME like \"%s\"" + " && COLUMN_NAME = \"%s\"\n", + db.ptr, tab.ptr, col.ptr ); + FreeString(col.ptr); + } + else + { + stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,-1, + "SELECT COUNT(*) FROM information_schema.COLUMNS\n" + "WHERE TABLE_SCHEMA = \"%s\" && TABLE_NAME like \"%s\"\n", + db.ptr, tab.ptr ); + } + + FreeString(tab.ptr); + FreeString(db.ptr); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// helpers + +mem_t EscapeMYSQL ( MySql_t *my, cvp ptr, int len ) +{ + DASSERT(my); + + if ( len < 0 ) + len = ptr ? strlen(ptr) : 0; + + mem_t res; + if ( !ptr || !len ) + { + res.ptr = EmptyString; + res.len = 0; + } + else + { + char *buf = MALLOC(2*len+1); + res.ptr = buf; + res.len = mysql_real_escape_string(my->mysql,buf,ptr,len); + } + return res; +} + +//----------------------------------------------------------------------------- + +mem_t EscapeMYSQLCirc ( MySql_t *my, cvp ptr, int len ) +{ + DASSERT(my); + + if ( len < 0 ) + len = ptr ? strlen(ptr) : 0; + + mem_t res; + if ( !ptr || !len ) + { + res.ptr = EmptyString; + res.len = 0; + } + else if ( len >= CIRC_BUF_MAX_ALLOC ) + { + // no chance + res.ptr = 0; + res.len = 0; + } + else + { + char buf[2*CIRC_BUF_MAX_ALLOC+10]; + res.len = mysql_real_escape_string(my->mysql,buf,ptr,len); + if ( res.len < CIRC_BUF_MAX_ALLOC ) + res.ptr = CopyCircBuf(buf,res.len+1); + else + { + res.ptr = 0; + res.len = 0; + } + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t QuoteMYSQL ( MySql_t *my, cvp ptr, int len, bool null_if_empty ) +{ + if ( len < 0 ) + len = ptr ? strlen(ptr) : 0; + + mem_t res; + if ( !ptr || !len && null_if_empty ) + { + res.ptr = STRDUP("NULL"); + res.len = 4; + } + else if (!len) + { + res.ptr = EmptyQuote; + res.len = 0; + } + else + { + char *buf = MALLOC(2*len+3); + res.ptr = buf; + res.len = mysql_real_escape_string(my->mysql,buf+1,ptr,len) + 2; + buf[0] = buf[res.len-1] = '"'; + buf[res.len] = 0; + } + return res; +} + +//----------------------------------------------------------------------------- + +mem_t QuoteMYSQLCirc ( MySql_t *my, cvp ptr, int len, bool null_if_empty ) +{ + if ( len < 0 ) + len = ptr ? strlen(ptr) : 0; + + mem_t res; + if ( !ptr || !len && null_if_empty ) + { + res.ptr = CopyCircBuf("NULL",5); + res.len = 4; + } + else if (!len) + { + res.ptr = EmptyQuote; + res.len = 2; + } + else if ( len >= CIRC_BUF_MAX_ALLOC-2 ) + { + // no chance + res.ptr = 0; + res.len = 0; + } + else + { + char buf[2*CIRC_BUF_MAX_ALLOC+10]; + res.len = mysql_real_escape_string(my->mysql,buf+1,ptr,len) + 2; + if ( res.len < CIRC_BUF_MAX_ALLOC ) + { + buf[0] = buf[res.len-1] = '"'; + buf[res.len] = 0; + res.ptr = CopyCircBuf(buf,res.len+1); + } + else + { + res.ptr = 0; + res.len = 0; + } + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +TransferStats_t GetStatsMYSQL ( MySql_t *my ) +{ + TransferStats_t tf; + memset(&tf,0,sizeof(tf)); + + if (my) + { + tf.conn_count = my->total_connect_count; + tf.recv_count = my->total_row_count; + tf.recv_size = my->total_field_size; + tf.send_count = my->total_query_count; + tf.send_size = my->total_query_size; + } + return tf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MySqlResult_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void InsertResultMYSQL ( MySqlResult_t ** first, MySqlResult_t * res ) +{ + DASSERT(first); + DASSERT(res); + DASSERT( res != *first ); // already cleared! + + //--- remove res from previous list + + if (res->prev_result) + res->prev_result->next_result = res->next_result; + if (res->next_result) + res->next_result->prev_result = res->prev_result; + + //--- insert at top of the new list + + res->prev_result = 0; + res->next_result = *first; + if (*first) + (*first)->prev_result = res; + *first = res; + + FREE(res->row); + res->row = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +MySqlResult_t * UseResultMYSQL ( MySql_t *my, uint loglevel ) +{ + DASSERT(my); + enumError err = CheckOpenMYSQL(my,loglevel); + if (err) + { + AssignStatusMYSQL(&my->status,1,"No connection to database!",1); + return NULL; + } + + MYSQL_RES *myres = mysql_use_result(my->mysql); + if (!myres) + { + GetStatusMYSQL(&my->status,my->mysql); + if ( loglevel >= MYLL_ERROR ) + ERROR0(ERR_DATABASE,"%s\n",my->status.message); + return NULL; + } + my->total_result_count++; + + MySqlResult_t *res; + if (my->pool_result) + { + res = my->pool_result; + my->pool_result = res->next_result; + } + else + res = CALLOC(1,sizeof(*res)); + InsertResultMYSQL(&my->first_result,res); + + res->mysql = my; + res->myres = myres; + res->loglevel = loglevel; + res->row_count = 0; + res->field_count = mysql_num_fields(myres); + res->row = CALLOC(sizeof(*res->row),res->field_count); + + GetStatusMYSQL(&res->status,my->mysql); + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeResultMYSQL ( MySqlResult_t * res ) +{ + if (res) + { + MySql_t *my = res->mysql; + DASSERT(my); + + CloseResultMYSQL(res); + ClearStatusMYSQL(&res->status); + if ( my->first_result == res ) + my->first_result = res->next_result; + InsertResultMYSQL(&my->pool_result,res); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseResultMYSQL ( MySqlResult_t * res ) +{ + DASSERT(res); + if (res->myres) + { + mysql_free_result(res->myres); + res->myres = 0; + } + FREE(res->row); + res->row = 0; + res->field_count = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeResultListMYSQL ( MySqlResult_t * first ) +{ + while (first) + { + MySqlResult_t *res = first; + first = res->next_result; + CloseResultMYSQL(res); + ClearStatusMYSQL(&res->status); + FREE(res); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t * GetNextRowMYSQL ( MySqlResult_t * res ) +{ + DASSERT(res); + if ( !res || !res->myres || !res->row ) + return NULL; + + MYSQL_ROW myrow = mysql_fetch_row(res->myres); + ulong *mylen = mysql_fetch_lengths(res->myres); + res->myrow = myrow; + if ( !myrow || !mylen ) + { + FREE(res->row); + res->row = 0; + return NULL; + } + res->mysql->total_row_count++; + + mem_t *dest = res->row; + uint n = res->field_count; + res->mysql->total_field_count += n; + while ( n-- > 0 ) + { + dest->ptr = *myrow++; + dest->len = *mylen++; + res->mysql->total_field_size += dest->len; + noPRINT("%3u: |%.*s|\n",dest->len,dest->len<50?dest->len:50,dest->ptr); + dest++; + } + + return res->row; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MySqlStatus_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ClearStatusMYSQL ( MySqlStatus_t *stat ) +{ + DASSERT(stat); + if (stat->alloced) + { + FREE((char*)stat->message); + stat->alloced = false; + } + stat->message = stat->msgbuf; + stat->msgbuf[0] = 0; + stat->status = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetStatusMYSQL ( MySqlStatus_t *stat, MYSQL *my ) +{ + DASSERT(stat); + if (my) + AssignStatusMYSQL(stat,mysql_errno(my),mysql_error(my),0); + else + ClearStatusMYSQL(stat); + + return stat->status; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignStatusMYSQL +( + MySqlStatus_t *stat, // valid struct + int stat_code, // error status to assign + ccp message, // NULL or pointer to message + int copy_mode // 0:copy message, 1:use ptr, 2:use+free ptr +) +{ + DASSERT(stat); + ClearStatusMYSQL(stat); + stat->status = stat_code; + + if (message) + { + if ( copy_mode > 0 ) + { + stat->message = message; + stat->alloced = copy_mode > 1; + } + else + { + const uint len = strlen(message); + if ( len < sizeof(stat->msgbuf) ) + { + memcpy(stat->msgbuf,message,len+1); + stat->message = stat->msgbuf; + stat->alloced = false; + } + else + { + stat->message = MEMDUP(message,len); + stat->alloced = true; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyStatusMYSQL +( + MySqlStatus_t *dest, // NULL or initialized status + bool init_dest, // true: initialize 'dest' + const MySqlStatus_t *src // NULL (clear dest) + // or initialized status (assign to dest) +) +{ + if (dest) + { + if (init_dest) + InitializeStatusMYSQL(dest); + + if ( dest != src ) + { + if (src) + AssignStatusMYSQL(dest,src->status,src->message,0); + else + ClearStatusMYSQL(dest); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveStatusMYSQL +( + MySqlStatus_t *dest, // NULL or initialized status + bool init_dest, // true: initialize 'dest' + MySqlStatus_t *src // NULL (clear dest) + // or initialized status (move to dest) +) +{ + if ( dest != src ) + { + if (dest) + { + if (init_dest) + InitializeStatusMYSQL(dest); + else + ClearStatusMYSQL(dest); + + if (src) + { + memcpy(dest,src,sizeof(*dest)); + if ( src->message == src->msgbuf ) + dest->message = dest->msgbuf; + memset(src,0,sizeof(*src)); + } + } + else if (src) + ClearStatusMYSQL(src); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintStatusMYSQL +( + MySqlStatus_t *stat, // valid struct + int stat_code, // error status to assign + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +{ + DASSERT(stat); + DASSERT(format); + + char buf[10000]; + va_list arg; + va_start(arg,format); + int len = vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + + if ( len < sizeof(buf) ) + AssignStatusMYSQL(stat,stat_code,buf,0); + else + { + //--- buffer too small, use dynamic memory + + noPRINT("PrintQueryMYSQL() -> MALLOC(%u)\n",stat+1); + + char *dest = MALLOC(len+1); + va_start(arg,format); + len = vsnprintf(dest,len+1,format,arg); + va_end(arg); + + AssignStatusMYSQL(stat,stat_code,dest,2); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MySqlServerStats_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError GetMySqlServerStats ( MySql_t *my, MySqlServerStats_t *stat ) +{ + DASSERT(my); + DASSERT(stat); + InitializeMySqlServerStats(stat); + + static const char query[] + = "show status where variable_name in" + " ('Uptime','Max_used_connections','Connections','Queries')"; + + enumError err + = DoQueryMYSQL(my,MYLL_QUERY_ON_ERROR,query,sizeof(query)-1); + if (!err) + { + MySqlResult_t *res = UseResultMYSQL(my,false); + if (res) + { + for(;;) + { + mem_t *row = GetNextRowMYSQL(res); + if (!row) + break; + + //printf("|%s|%s|\n",row[0].ptr,row[1].ptr); + switch(row[0].ptr[0]) + { + case 'C': + if (!strcmp(row[0].ptr,"Connections")) + stat->connections = strtoull(row[1].ptr,0,10); + break; + + case 'M': + if (!strcmp(row[0].ptr,"Max_used_connections")) + stat->max_connections = strtoull(row[1].ptr,0,10); + break; + + case 'Q': + if (!strcmp(row[0].ptr,"Queries")) + stat->queries = strtoull(row[1].ptr,0,10); + break; + + case 'U': + if (!strcmp(row[0].ptr,"Uptime")) + stat->uptime = strtoull(row[1].ptr,0,10); + break; + } + } + FreeResultMYSQL(res); + } + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +MySqlServerStats_t * Add3MySqlServerStats +( + // return dest + // calculate: dest = src1 + src2 + + MySqlServerStats_t *dest, // NULL or destination (maybe same as source) + const MySqlServerStats_t *src1, // NULL or first source + const MySqlServerStats_t *src2 // NULL or second source +) +{ + if (dest) + { + if (!src1) + { + if (src2) + memcpy(dest,src2,sizeof(*dest)); + else + InitializeMySqlServerStats(dest); + } + else if (!src2) + { + DASSERT(src1); + memcpy(dest,src1,sizeof(*dest)); + } + else + { + DASSERT(src1); + DASSERT(src2); + + dest->max_connections = src1->max_connections > src2->max_connections + ? src1->max_connections : src2->max_connections; + dest->uptime = src1->uptime + src2->uptime; + dest->connections = src1->connections + src2->connections; + dest->queries = src1->queries + src2->queries; + } + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +MySqlServerStats_t * Sub3MySqlServerStats +( + // return dest + // calculate: dest = src1 - src2 + + MySqlServerStats_t *dest, // NULL or destination (maybe same as source) + const MySqlServerStats_t *src1, // NULL or first source + const MySqlServerStats_t *src2 // NULL or second source +) +{ + if (dest) + { + if (!src1) + { + if (src2) + { + MySqlServerStats_t temp; + InitializeMySqlServerStats(&temp); + Sub3MySqlServerStats(dest,&temp,src2); + } + else + InitializeMySqlServerStats(dest); + } + else if (!src2) + { + DASSERT(src1); + memcpy(dest,src1,sizeof(*dest)); + } + else + { + DASSERT(src1); + DASSERT(src2); + dest->max_connections = src1->max_connections; + dest->uptime = src1->uptime - src2->uptime; + dest->connections = src1->connections - src2->connections; + dest->queries = src1->queries - src2->queries; + } + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +MySqlServerStats_t * Max2MySqlServerStats +( + // return dest + // calculate: dest := max(dest,src) + + MySqlServerStats_t *dest, // NULL or destination (maybe same as source) + const MySqlServerStats_t *src // NULL or source +) +{ + if ( src && dest ) + { + if ( dest->max_connections < src->max_connections ) + dest->max_connections = src->max_connections; + + if ( dest->uptime < src->uptime ) + dest->uptime = src->uptime; + + if ( dest->connections < src->connections ) + dest->connections = src->connections; + + if ( dest->queries < src->queries ) + dest->queries = src->queries; + } + return dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-mysql.h b/project/dclib/dclib-mysql.h new file mode 100644 index 0000000..3fdb617 --- /dev/null +++ b/project/dclib/dclib-mysql.h @@ -0,0 +1,544 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_MYSQL_H +#define DCLIB_MYSQL_H 1 + +#include + +#include "dclib-types.h" +#include "dclib-debug.h" +#include "dclib-basics.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// mysql definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct st_mysql st_mysql; +typedef struct st_mysql_res st_mysql_res; +typedef struct st_mysql_field st_mysql_field; + +#define MYSQL_DEFAULT_PORT 3306 + +struct MySql_t; + +typedef enum LogLevelMYSQL +{ + MYLL_SILENT, + MYLL_ERROR, + MYLL_QUERY_ON_ERROR, + MYLL_QUERY_ALWAYS, + MYLL_ALL +} +LogLevelMYSQL; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MySqlStatus_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct MySqlStatus_t +{ + int status; // status of last operation + ccp message; // pointer to last error message + bool alloced; // 'message' is alloced + char msgbuf[120]; // buffer for short messages +} +MySqlStatus_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MySqlResult_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MySqlResult_t]] + +typedef struct MySqlResult_t +{ + //--- linked double-list for management + + struct MySqlResult_t *prev_result; // NULL or link to previous result + struct MySqlResult_t *next_result; // NULL or link to next result + struct MySql_t *mysql; // related MySql_t struct + + //--- mysql result and status + + MYSQL_RES *myres; // mysql result + LogLevelMYSQL loglevel; // log level for mysql errors and queries + MySqlStatus_t status; // last error status + + //--- rows and fields + + int row_count; // number of read rows + int field_count; // number of fields in a row + mem_t *row; // list with 'field_count' entries + MYSQL_ROW myrow; // mysql row + +} +MySqlResult_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct MySql_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MySql_t]] + +typedef struct MySql_t +{ + //--- database definitions, strings are alloced + + ccp server; // server + ccp user; // user + ccp password; // password + ccp database; // database name + ccp prefix; // default prefix, only for external usage + bool auto_connect; // true: auto connect if closed + bool auto_reconnect; // true(default): try 1 reconnect on connection lost + + //--- mysql data + + MYSQL *mysql; // mysql connection + uint field_count; // mysql_field_count() of last query + MySqlStatus_t status; // last error status + + MySqlResult_t *first_result; // pointer to first result + MySqlResult_t *pool_result; // list with unused objects + + //--- a small temporary buffer for e.g. small results + + char tempbuf[120]; // temporary buffer + bool tempbuf_used; // true: 'tempbuf' is used + + //--- statistics + + uint total_connect_count; // total number of connections (usualy 1) + uint total_query_count; // total number of queries + u64 total_query_size; // total size of all queries + uint total_result_count; // total number of fetched results + u64 total_row_count; // total number of fetched rows + u64 total_field_count; // total number of fetched fields + u64 total_field_size; // total size of all fetched fields + + //--- both timers count the execution time of DoQueryMYSQL() + //--- inclusive database reopen and error handling. + u64 last_duration_usec; // duration of last query + u64 max_duration_usec; // max duration of all queries + u64 total_duration_usec; // total duration of all queries +} +MySql_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// interface for MySql_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMYSQL ( MySql_t *my ); +void ResetMYSQL ( MySql_t *my ); +enumError OpenMYSQL ( MySql_t *my, uint loglevel ); +void CloseMYSQL ( MySql_t *my ); + +void DefineDatabase +( + MySql_t *my, // valid struct + ccp server, // NULL or server name + ccp user, // NULL or user name + ccp password, // NULL or user password + ccp database // NULL or name of database +); + +void DefineDatabasePrefix +( + MySql_t *my, // valid struct + ccp prefix // NULL or default prefix, only for external usage +); + +//----------------------------------------------------------------------------- + +static inline bool IsOpenMYSQL ( MySql_t *my ) // 'my' may be NULL +{ + return my && my->mysql; +} + +static inline bool AutoOpenMYSQL ( MySql_t *my, uint loglevel ) // 'my' may be NULL +{ + return my + && ( my->mysql || my->auto_connect && OpenMYSQL(my,loglevel) == ERR_OK ); +} + +//----------------------------------------------------------------------------- + +enumError DoQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp query, // query string + int query_length // length of 'query', -1 if unknown +); + +enumError PrintArgQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +); + +enumError PrintQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,3,4))); + +//----------------------------------------------------------------------------- +// multi queries + +mem_t GetFirstQuery ( ccp src, int src_len ); + +enumError DoMultiQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp query, // query string + int query_length // length of 'query', -1 if unknown +); + +enumError PrintMultiQueryMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,3,4))); + +//----------------------------------------------------------------------------- +// temp buffer for small results as optimization + +mem_t CopyMemMYSQL ( MySql_t * my, mem_t * src ); // 'src' may be NULL +void FreeMemMYSQL ( MySql_t * my, mem_t mem ); + +//----------------------------------------------------------------------------- +// fetch single value as result of a query + +mem_t FetchScalarMYSQL // call FreeScalarMYSQL() for the result +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel // log level for mysql errors and queries +); + +s64 FetchIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + s64 return_default // return this value on non existent result +); + +u64 FetchUIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + u64 return_default // return this value on non existent result +); + +//----------------------------------------------------------------------------- +// print query & fetch single value as result + +mem_t PrintArgFetchScalarMYSQL +( + // if res.len>0: FREE the result using FreeMemMYSQL(my,mem) + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'format' +); + +mem_t PrintFetchScalarMYSQL +( + // if res.len>0: FREE the result using FreeMemMYSQL(my,mem) + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,3,4))); + +s64 PrintFetchIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + s64 return_default, // return this value on non existent result + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,4,5))); + +u64 PrintFetchUIntMYSQL +( + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + u64 return_default, // return this value on non existent result + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,4,5))); + +//----------------------------------------------------------------------------- +// fetch single row + +void * FetchSingleRowN +( + // returns NULL on error or emtpy result, + // or a temporary buffer => call FREE(result) + // following rows will be skipped + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + mem_t *dest, // dest buffer, unused elements are set to {0,0} + uint n_dest // number of elements in 'dest' +); + +int FetchSingleRowList +( + // returns -1 on error, 0 on empty result or the number of retrieved field + // following rows will be skipped + + MySql_t *my, // valid struct + LogLevelMYSQL loglevel, // log level for mysql errors and queries + mem_list_t *dest, // destination list + bool init_dest, // true: initialize 'dest' + bool replace_null // true: replace NULL by EmptyString +); + +//----------------------------------------------------------------------------- +// count tables and columns + +int CountTablesMYSQL +( + MySql_t *my, // valid DB connection + ccp table, // like "table"; if NULL|EMPTY: count tables + ccp database // database name; if NULL|EMPTY: use default DB +); + +int CountColumnsMYSQL +( + MySql_t *my, // valid DB connection + ccp column, // column name; if NULL|EMPTY: count columns + ccp table, // like "table"; if NULL|EMPTY: all tables + ccp database // database name; if NULL|EMPTY: use default DB +); + +//----------------------------------------------------------------------------- +// helpers + +static inline int LastInsertIdMYSQL ( MySql_t *my ) + { return mysql_insert_id(my->mysql); } + +struct TransferStats_t GetStatsMYSQL ( MySql_t *my ); + + +//----------------------------------------------------------------------------- +// escaping and quoting + +// All functions are robust for NULL pointers. +// If len<0 und ptr!=0, then strlen(ptr) is used. +// The `circ´ variants return a buffer alloced by GetCircBuf() with +// valid pointer and null terminated. If result is too large +// (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. +// The result of `circ´ variants must not be freed! +// All others results must be freed by `FreeString(mem.ptr)´. + +//--------------------------------------------------- +// In general: Use faster `circ´ variants if you can +// guarantee, that src_len < CIRC_BUF_MAX_ALLOC/2. +//--------------------------------------------------- + + mem_t EscapeMYSQL ( MySql_t *my, cvp ptr, int len ); + mem_t EscapeMYSQLCirc ( MySql_t *my, cvp ptr, int len ); +static inline mem_t EscapeMemMYSQL ( MySql_t *my, mem_t src ); +static inline mem_t EscapeMemMYSQLCirc ( MySql_t *my, mem_t src ); +static inline mem_t EscapeStringMYSQL ( MySql_t *my, ccp src ); +static inline mem_t EscapeStringMYSQLCirc(MySql_t *my, ccp src ); + + mem_t QuoteMYSQL ( MySql_t *my, cvp ptr, int len, bool null_if_empty ); + mem_t QuoteMYSQLCirc ( MySql_t *my, cvp ptr, int len, bool null_if_empty ); +static inline mem_t QuoteMemMYSQL ( MySql_t *my, mem_t src, bool null_if_empty ); +static inline mem_t QuoteMemMYSQLCirc ( MySql_t *my, mem_t src, bool null_if_empty ); +static inline mem_t QuoteStringMYSQL ( MySql_t *my, ccp src, bool null_if_empty ); +static inline mem_t QuoteStringMYSQLCirc( MySql_t *my, ccp src, bool null_if_empty ); + +//----------------------------------------------------------------------------- +// inlines of escaping and quoting + +static inline mem_t EscapeMemMYSQL ( MySql_t *my, mem_t src ) + { return EscapeMYSQL(my,src.ptr,src.len); } +static inline mem_t EscapeMemMYSQLCirc ( MySql_t *my, mem_t src ) + { return EscapeMYSQLCirc(my,src.ptr,src.len); } +static inline mem_t EscapeStringMYSQL ( MySql_t *my, ccp src ) + { return EscapeMYSQL(my,src,-1); } +static inline mem_t EscapeStringMYSQLCirc( MySql_t *my, ccp src ) + { return EscapeMYSQLCirc(my,src,-1); } + +static inline mem_t QuoteMemMYSQL ( MySql_t *my, mem_t src, bool null_if_empty ) + { return QuoteMYSQL(my,src.ptr,src.len,null_if_empty); } +static inline mem_t QuoteMemMYSQLCirc ( MySql_t *my, mem_t src, bool null_if_empty ) + { return QuoteMYSQLCirc(my,src.ptr,src.len,null_if_empty); } +static inline mem_t QuoteStringMYSQL ( MySql_t *my, ccp src, bool null_if_empty ) + { return QuoteMYSQL(my,src,-1,null_if_empty); } +static inline mem_t QuoteStringMYSQLCirc ( MySql_t *my, ccp src, bool null_if_empty ) + { return QuoteMYSQLCirc(my,src,-1,null_if_empty); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// interface MySqlResult_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +MySqlResult_t * UseResultMYSQL ( MySql_t * my, uint loglevel ); +void FreeResultMYSQL ( MySqlResult_t * res ); // res maybe NULL +void CloseResultMYSQL ( MySqlResult_t * res ); + +void FreeResultListMYSQL ( MySqlResult_t * first_result ); + +mem_t * GetNextRowMYSQL ( MySqlResult_t * res ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// interface for MySqlStatus_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeStatusMYSQL ( MySqlStatus_t *stat ) +{ + DASSERT(stat); + memset(stat,0,sizeof(*stat)); + stat->message = stat->msgbuf; +} + +void ClearStatusMYSQL ( MySqlStatus_t *stat ); +uint GetStatusMYSQL ( MySqlStatus_t *stat, MYSQL *my ); + +void AssignStatusMYSQL +( + MySqlStatus_t *stat, // valid struct + int stat_code, // error status to assign + ccp message, // NULL or pointer to message + int copy_mode // 0:copy message, 1:use ptr, 2:use+free ptr +); + +void CopyStatusMYSQL +( + MySqlStatus_t *dest, // NULL or initialized status + bool init_dest, // true: initialize 'dest' + const MySqlStatus_t *src // NULL (clear dest) + // or initialized status (assign to dest) +); + +void MoveStatusMYSQL +( + MySqlStatus_t *dest, // NULL or initialized status + bool init_dest, // true: initialize 'dest' + MySqlStatus_t *src // NULL (clear dest) + // or initialized status (move to dest) +); + +void PrintStatusMYSQL +( + MySqlStatus_t *stat, // valid struct + int stat_code, // error status to assign + ccp format, // format string for vsnprintf() + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,3,4))); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_mysql_stats() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MySqlServerStats_t]] + +typedef struct MySqlServerStats_t +{ + u32 max_connections; // maximal used connections + s32 uptime; // uptime in seconds + s64 connections; // number of total connections + s64 queries; // number of total queries +} +MySqlServerStats_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeMySqlServerStats ( MySqlServerStats_t *stat ) + { DASSERT(stat); memset(stat,0,sizeof(*stat)); } + +enumError GetMySqlServerStats ( MySql_t *my, MySqlServerStats_t *stat ); + +MySqlServerStats_t * Add3MySqlServerStats +( + // return dest + // calculate: dest = src1 + src2 + + MySqlServerStats_t *dest, // NULL or destination (maybe same as source) + const MySqlServerStats_t *src1, // NULL or first source + const MySqlServerStats_t *src2 // NULL or second source +); + +MySqlServerStats_t * Sub3MySqlServerStats +( + // return dest + // calculate: dest = src1 - src2 + + MySqlServerStats_t *dest, // NULL or destination (maybe same as source) + const MySqlServerStats_t *src1, // NULL or first source + const MySqlServerStats_t *src2 // NULL or second source +); + +MySqlServerStats_t * Max2MySqlServerStats +( + // return dest + // calculate: dest := max(dest,src) + + MySqlServerStats_t *dest, // NULL or destination (maybe same as source) + const MySqlServerStats_t *src // NULL or source +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_MYSQL_H + diff --git a/project/dclib/dclib-network-linux.c b/project/dclib/dclib-network-linux.c new file mode 100644 index 0000000..900714e --- /dev/null +++ b/project/dclib/dclib-network-linux.c @@ -0,0 +1,461 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include + +#ifdef SYSTEM_LINUX + #include +#endif + +#include "dclib-network.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SendRawUDP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SendRawUDP +( + // Capability CAP_NET_RAW needed (or effective user ID of 0). + + int sock, // RAW socket to use, + // if -1: open and close private socket + + ccp send_addr, // NULL or sender address:port + u32 send_ip4, // fall back IP4, if 'send_addr' empty + u16 send_port, // default sender port + + ccp recv_addr, // NULL or receiver address:port + u32 recv_ip4, // fall back IP4, if 'recv_addr' empty + u16 recv_port, // default receiver port + + const void *data, // data to send + uint size, // size of 'data' + uint log_mode // 0:silent, >0:print errors, + // >=0x10: hexdump data, 'lmode' bytes max +) +{ + DASSERT( data || !size ); + + + //--- get addresses + + NetworkHost_t send_host; + if ( send_addr && *send_addr ) + { + if (!ResolveHost(&send_host,true,send_addr,send_port,false,false)) + { + if (log_mode) + ERROR1(ERR_ERROR,"Can't resolve sender host: %s\n",send_addr); + return ERR_ERROR; + } + } + else + { + send_host.sa.sin_family = AF_INET; + send_host.sa.sin_addr.s_addr = htonl(send_ip4); + send_host.sa.sin_port = htons(send_port); + } + + + NetworkHost_t recv_host; + if ( recv_addr && *recv_addr ) + { + if (!ResolveHost(&recv_host,true,recv_addr,recv_port,false,false)) + { + if (log_mode) + ERROR1(ERR_ERROR,"Can't resolve sender host: %s\n",recv_addr); + return ERR_ERROR; + } + } + else + { + recv_host.sa.sin_family = AF_INET; + recv_host.sa.sin_addr.s_addr = htonl(recv_ip4); + recv_host.sa.sin_port = htons(recv_port); + } + + return SendRawUDPsa(sock,&send_host.sa,&recv_host.sa,data,size,log_mode); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SendRawUDPsa +( + // Capability CAP_NET_RAW needed (or effective user ID of 0). + + int sock, // RAW socket to use, + // if -1: open and close private socket + + struct sockaddr_in *sa_send, // sockaddr of sender + struct sockaddr_in *sa_recv, // sockaddr of receiver + + const void *data, // data to send + uint size, // size of 'data' + uint log_mode // 0:silent, >0:print errors, + // >=0x10: hexdump data, 'lmode' bytes max +) +{ + DASSERT(sa_send); + DASSERT(sa_recv); + DASSERT( data || !size ); + if ( size > MAX_UDP_PACKET_DATA ) + size = MAX_UDP_PACKET_DATA; + + TRACE("Send %zu+%u bytes from %s to %s\n", + sizeof(ip4_head_t) + sizeof(udp_head_t), size, + PrintIP4sa(0,0,sa_send), + PrintIP4sa(0,0,sa_recv) ); + + + //--- open socket + + enumError err = ERR_OK; + + const bool own_sock = sock == -1; + if (own_sock) + { + sock = socket(AF_INET,SOCK_RAW,IPPROTO_RAW); + if ( sock == -1 ) + { + if (log_mode) + ERROR1(ERR_CANT_CREATE,"Can't create RAW socket.\n"); + err = ERR_CANT_CREATE; + goto abort; + } + + int on = 1; + setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)); + } + + + //--- setup packet + + udp_packet_t pkt; + const uint total_len = SetupRawUDPsa(&pkt,sa_send,sa_recv,data,size); + if ( log_mode >= 0x10 ) + HexDump16( stdwrn, 0, 0, &pkt, + total_len < log_mode ? total_len : log_mode ); + + + //--- send data + + //HEXDUMP16(0,0,&pkt,total_len); + //HexDump16(stderr,0,0,&pkt,total_len); + + int stat = sendto(sock,&pkt,total_len,0,(struct sockaddr*)sa_recv,sizeof(*sa_recv)); + if ( stat == -1 ) + { + ERROR1(ERR_ERROR,"Can't send to sock %d\n",sock); + if (log_mode) + ERROR1(ERR_WRITE_FAILED,"Can't send to socket %d\n",sock); + err = ERR_WRITE_FAILED; + goto abort; + } + + + //--- close socket and terminate + + abort: + if ( own_sock && sock != -1 ) + close(sock); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SetupRawUDPsa +( + // returns total packet len (mabe with limited 'size') + + udp_packet_t *pkt, // paket header to setup (cleared and written) + + struct sockaddr_in *sa_send, // sockaddr of sender + struct sockaddr_in *sa_recv, // sockaddr of receiver + const void *data, // data to send + uint size // size of 'data' +) +{ + DASSERT(pkt); + DASSERT(sa_send); + DASSERT(sa_recv); + DASSERT( data || !size ); + if ( size > MAX_UDP_PACKET_DATA ) + size = MAX_UDP_PACKET_DATA; + + + //--- determine sender ip4 + + u32 sender_ip4 = sa_send->sin_addr.s_addr; + if (!sender_ip4) + { + RouteIP4_t rt; + if (FindGatewayIP4(&rt,sa_recv->sin_addr.s_addr)) + sender_ip4 = GetIP4ByInterface(rt.iface); + } + + + //--- create data structure + + static const u8 ip4_head[] + = { 0x45,0x00, 0x00,0x00, 0x12,0x34, 0x40,0x00, 0x40,0x11 }; + + const uint total_len = sizeof(ip4_head_t) + sizeof(udp_head_t) + size; + + memset(pkt,0,sizeof(*pkt)); + memcpy(&pkt->ip4,ip4_head,sizeof(ip4_head)); + memcpy(pkt->data,data,size); + + pkt->ip4.ip_src = sender_ip4; + pkt->ip4.ip_dest = sa_recv->sin_addr.s_addr; + pkt->ip4.total_len = htons(total_len); + pkt->ip4.checksum = CalcChecksumIP4(&pkt->ip4,sizeof(pkt->ip4)); + + pkt->udp.port_src = sa_send->sin_port; + pkt->udp.port_dest = sa_recv->sin_port; + pkt->udp.data_len = htons(size+8); + pkt->udp.checksum = CalcChecksumUDP(&pkt->ip4,&pkt->udp,data); + + + //--- terminate + + //HEXDUMP16(0,0,&pkt,total_len); + //HexDump16(stderr,0,0,&pkt,total_len); + + return total_len; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Routing Support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool OpenRouteScanIP4 +( + // returns TRUE on success and FALSE if no element is found + RouteIP4_t *rt // valid data, will be initialized +) +{ + DASSERT(rt); + memset(rt,0,sizeof(*rt)); + + rt->index = -1; + rt->col_iface = 0; + rt->col_dest = 1; + rt->col_mask = 7; + rt->col_gate = 2; + rt->col_flags = 3; + + #ifdef __CYGWIN__ + return false; + #else + rt->f = fopen("/proc/net/route","rb"); + if (!rt->f) + return false; + + char *ptr = fgets(rt->buf,sizeof(rt->buf),rt->f); + if (!ptr) + return false; + + uint col; + for ( col = 0; *ptr; col++ ) + { + while ( *ptr == ' ' || *ptr == '\t' ) + ptr++; + if (!*ptr) + break; + char *beg = ptr; + while ( *ptr && *ptr != ' ' && *ptr != '\t' ) + ptr++; + if (*ptr) + *ptr++ = 0; + + if (!strcasecmp(beg,"Iface")) rt->col_iface = col; + else if (!strcasecmp(beg,"Destination"))rt->col_dest = col; + else if (!strcasecmp(beg,"Mask")) rt->col_mask = col; + else if (!strcasecmp(beg,"Gateway")) rt->col_gate = col; + else if (!strcasecmp(beg,"Flags")) rt->col_flags = col; + } + + TRACE("f=%p, COLS: if=%d, dest=%d, mask=%d, gate=%d, flags=%d\n", + rt->f, rt->col_iface, rt->col_dest, + rt->col_mask, rt->col_gate, rt->col_flags ); + + return NextRouteScanIP4(rt); + + #endif // !__CYGWIN__ +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NextRouteScanIP4 +( + // returns TRUE on success and FALSE if no more element is found + RouteIP4_t *rt // valid data +) +{ + DASSERT(rt); + + #ifdef __CYGWIN__ + return false; + #else + char *ptr = fgets(rt->buf,sizeof(rt->buf),rt->f); + if (!ptr) + { + rt->index = -1; + return false; + } + + uint col; + for ( col = 0; *ptr; col++ ) + { + while ( *ptr == ' ' || *ptr == '\t' ) + ptr++; + if (!*ptr) + break; + char *beg = ptr; + while ( *ptr && *ptr != ' ' && *ptr != '\t' ) + ptr++; + if (*ptr) + *ptr++ = 0; + + if ( col == rt->col_iface ) rt->iface = beg; + else if ( col==rt->col_dest ) rt->dest = strtoul(beg,0,16); + else if ( col==rt->col_mask ) rt->mask = strtoul(beg,0,16); + else if ( col==rt->col_gate ) rt->gate = strtoul(beg,0,16); + else if ( col==rt->col_flags ) rt->flags = strtoul(beg,0,16); + } + + return true; + #endif // !__CYGWIN__ +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseRouteScanIP4 +( + RouteIP4_t *rt // valid data +) +{ + #ifndef __CYGWIN__ + DASSERT(rt); + if (rt->f) + { + fclose(rt->f); + rt->f = 0; + } + #endif // !__CYGWIN__ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool FindGatewayIP4 +( + // returns TRUE if gateway found + RouteIP4_t *rt, // valid data, will be initialized + u32 ip4 // search gateway for this address (network byte order) +) +{ + DASSERT(rt); + + bool found; + for ( found = OpenRouteScanIP4(rt); found; found = NextRouteScanIP4(rt) ) + { + #ifdef __CYGWIN__ + if ( rt->dest == ( ip4 & rt->mask )) + break; + #else + if ( !(rt->flags & RTF_UP) ) + continue; + + if ( rt->flags & RTF_HOST ) + { + if ( rt->dest == ip4 ) + break; + } + else if ( rt->dest == ( ip4 & rt->mask )) + break; + #endif + } + CloseRouteScanIP4(rt); + return found; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u32 GetIP4ByInterface +( // return 0 or IP4 in network byte order + ccp iface // interface name. If NULL: Use default gateway +) +{ + RouteIP4_t rt; + if ( !iface || !*iface ) + { + if (!FindGatewayIP4(&rt,0)) + return 0; + iface = rt.iface; + } + + int sock = socket(AF_INET,SOCK_DGRAM,0); + if ( sock == -1 ) + return 0; + + struct ifreq ifr; + memset(&ifr,0,sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name,iface,sizeof(ifr.ifr_name)-1); + int stat = ioctl(sock,SIOCGIFADDR,&ifr); + close(sock); + if ( stat == -1 ) + return 0; + + struct sockaddr_in *sa = (struct sockaddr_in*)&ifr.ifr_addr; + return sa->sin_addr.s_addr; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-network-linux.h b/project/dclib/dclib-network-linux.h new file mode 100644 index 0000000..fc09ed8 --- /dev/null +++ b/project/dclib/dclib-network-linux.h @@ -0,0 +1,171 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_NETWORK_LINUX_H +#define DCLIB_NETWORK_LINUX_H 1 + +#ifndef DCLIB_NETWORK_H + #include "dclib-network.h" +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IP interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SendRawUDP +( + // Capability CAP_NET_RAW needed (or effective user ID of 0). + + int sock, // RAW socket to use, + // if -1: open and close private socket + + ccp send_addr, // NULL or sender address:port + u32 send_ip4, // fall back IP4, if 'send_addr' empty + u16 send_port, // default sender port + + ccp recv_addr, // NULL or receiver address:port + u32 recv_ip4, // fall back IP4, if 'recv_addr' empty + u16 recv_port, // default receiver port + + const void *data, // data to send + uint size, // size of 'data' + uint log_mode // 0:silent, >0:print errors, + // >=0x10: hexdump data, 'lmode' bytes max +); + +//----------------------------------------------------------------------------- + +enumError SendRawUDPsa +( + // Capability CAP_NET_RAW needed (or effective user ID of 0). + + int sock, // RAW socket to use, + // if -1: open and close private socket + + struct sockaddr_in *sa_send, // sockaddr of sender + struct sockaddr_in *sa_recv, // sockaddr of receiver + + const void *data, // data to send + uint size, // size of 'data' + uint log_mode // 0:silent, >0:print errors, + // >=0x10: hexdump data, 'lmode' bytes max +); + +//----------------------------------------------------------------------------- + +uint SetupRawUDPsa +( + // returns total packet len (mabe with limited 'size') + + udp_packet_t *pkt, // paket header to setup (cleared and written) + + struct sockaddr_in *sa_send, // sockaddr of sender + struct sockaddr_in *sa_recv, // sockaddr of receiver + const void *data, // data to send + uint size // size of 'data' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Routing Support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// all IP4 in network byte order + +typedef struct RouteIP4_t +{ + //--- returned data + + int index; // entry index, starts with 0, incremented for each found + // -1: invalid data + + char *iface; // pointer into 'buf': interface name + u32 dest; // destination address, network byte order + u32 mask; // destination network mask, network byte order + u32 gate; // gateway address, network byte order + u32 flags; // routing flags + + //--- internal data + + FILE *f; // open file + uint col_iface; // column index for 'iface' + uint col_dest; // column index for 'dest' + uint col_mask; // column index for 'mask' + uint col_gate; // column index for 'gate' + uint col_flags; // column index for 'flags' + char buf[200]; // line buffer for /proc/net/route +} +RouteIP4_t; + +/////////////////////////////////////////////////////////////////////////////// + +bool OpenRouteScanIP4 +( + // returns TRUE on success and FALSE if no element is found + RouteIP4_t *rt // valid data, will be initialized +); + +bool NextRouteScanIP4 +( + // returns TRUE on success and FALSE if no more element is found + RouteIP4_t *rt // valid data +); + +void CloseRouteScanIP4 +( + RouteIP4_t *rt // valid data +); + +/////////////////////////////////////////////////////////////////////////////// + +bool FindGatewayIP4 +( + // returns TRUE if gateway found + RouteIP4_t *rt, // valid data, will be initialized + u32 ip4 // search gateway for this address (network byte order) +); + +u32 GetIP4ByInterface +( // return 0 or IP4 in network byte order + ccp iface // interface name. If NULL: Use default gateway +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_NETWORK_LINUX_H + + diff --git a/project/dclib/dclib-network.c b/project/dclib/dclib-network.c new file mode 100644 index 0000000..a1fb253 --- /dev/null +++ b/project/dclib/dclib-network.c @@ -0,0 +1,4546 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dclib-network.h" + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __APPLE__ + #ifndef SOCK_NONBLOCK + #define SOCK_NONBLOCK 0 + #endif +#endif + +#ifndef POLLRDHUP + #define POLLRDHUP 0 +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// host name support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ResolveHost +( + // returns TRUE, if a hostname is detected + // if result is FALSE => check host->filename + + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool init_host, // true: initialize 'host' + ccp name, // name to analyze + int default_port, // use this as port, if no other is found + bool check_filename, // true: check for unix filename + bool store_name // true: setup 'host->name' +) +{ + return ResolveHostMem( host, init_host, MemByString(name), + default_port, check_filename, store_name ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ResolveHostMem +( + // returns TRUE, if a hostname is detected + // if result is FALSE => check host->filename + + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool init_host, // true: initialize 'host' + mem_t name, // name to analyze + int default_port, // use this as port, if no other is found + bool check_filename, // true: check for unix filename + bool store_name // true: setup 'host->name' +) +{ + Setup_ConnectTCP_Hook(false); + + DASSERT(host); + if (!init_host) + FREE((char*)host->name); + InitializeHost(host); + host->port = default_port; + + if (check_filename) + { + mem_t res = CheckUnixSocketPathMem(name,0); + host->filename = res.ptr; + if (host->filename) + return false; + } + + char buf[1000]; + + ccp ptr = name.ptr; + ccp end = ptr + name.len; + if ( ptr < end + && ( *ptr >= 'a' && *ptr <= 'z' || *ptr >= 'A' && *ptr <= 'Z' )) + { + while ( ptr < end + && ( *ptr >= 'a' && *ptr <= 'z' + || *ptr >= 'A' && *ptr <= 'Z' + || *ptr >= '0' && *ptr <= '9' + || *ptr == '_' || *ptr == '-' + )) + { + ptr++; + } + + uint len = ptr - name.ptr; + if ( len && len < sizeof(buf) && ptr+1 < end && *ptr == ':' && ptr[1] == '/' ) + { + memcpy(buf,name.ptr,len); + buf[len] = 0; + struct servent *s = getservbyname(buf,0); + if (s) + { + const int port = ntohs(s->s_port); + if ( port > 0 ) + host->port = port; + } + + ptr += 2; + if ( *ptr == '/' ) + ptr++; + name = BehindMem(name,ptr); + check_filename = false; + } + else + ptr = name.ptr; + } + + while ( ptr < end + && ( *ptr >= 'a' && *ptr <= 'z' + || *ptr >= 'A' && *ptr <= 'Z' + || *ptr >= '0' && *ptr <= '9' + || *ptr == '_' || *ptr == '-' || *ptr == '.' + )) + { + ptr++; + } + + uint len = ptr - name.ptr; + if ( len > 0 && len < sizeof(buf) ) + { + memcpy(buf,name.ptr,len); + buf[len] = 0; + if (store_name) + host->name = MEMDUP(buf,len); + + struct hostent *h = gethostbyname(buf); + if ( h && h->h_addrtype == AF_INET && h->h_length == 4 ) + { + host->ip4 = ntohl(*(u32*)h->h_addr_list[0]); + host->ip4_valid = true; + } + + if ( ptr < end && *ptr == ':' ) + { + ccp pptr = ptr+1; + if ( pptr < end && *pptr >= '0' && *pptr <= '9' ) + { + char *end; + const uint port = strtoul(pptr,&end,10); + if ( port <= 0xffff && end > pptr ) + { + host->port = port; + ptr = end; + } + } + else + { + ccp pstart = pptr; + while ( pptr < end + && ( *pptr >= 'a' && *pptr <= 'z' + || *pptr >= 'A' && *pptr <= 'Z' + || *pptr >= '0' && *pptr <= '9' + || *pptr == '_' || *pptr == '-' + )) + { + pptr++; + } + len = pptr - pstart; + if ( len > 0 && len < sizeof(buf) ) + { + memcpy(buf,pstart,len); + buf[len] = 0; + struct servent *s = getservbyname(buf,0); + if (s) + { + const int port = ntohs(s->s_port); + if ( port > 0 ) + host->port = port; + ptr = pptr; + } + } + } + } + + host->sa.sin_family = AF_INET; + host->sa.sin_addr.s_addr = htonl(host->ip4); + host->sa.sin_port = htons(host->port); + } + + if ( *ptr && check_filename ) + { + host->filename = name.ptr; + host->ip4_valid = false; + } + + host->not_scanned = BehindMem(name,ptr); + return host->ip4_valid; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct AllowIP4_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t AllowIP4KeyTable[] = +{ + { 0, "0", 0, 1 }, + { ALLOW_MODE_DENY, "DENY", 0, 1 }, + { ALLOW_MODE_ALLOW, "ALLOW", 0, 0 }, + { ALLOW_MODE_AKEY, "AKEY", "JACC", 0 }, + + { ALLOW_MODE_LOCAL, "LOCAL", 0, 0 }, + { ALLOW_MODE_LAN, "LAN", 0, 0 }, + { ALLOW_MODE_EXTERN, "EXTERN", 0, 0 }, + + { ALLOW_MODE_PUBLIC, "PUBLIC", 0, 0 }, + { ALLOW_MODE_USER, "USER", 0, 0 }, + { ALLOW_MODE_MOD, "MOD", "MODERATOR", 0 }, + { ALLOW_MODE_ADMIN, "ADMIN", "ADMINISTRATOR",0 }, + { ALLOW_MODE_DEVELOP, "DEVELOP", "DEVELOPER", 0 }, + + { ALLOW_MODE_LOG, "LOG", 0, 0 }, + { ALLOW_MODE_VERBOSE, "VERBOSE", 0, 0 }, + + { ALLOW_MODE_NOT, "NOT", 0, 0 }, + { ALLOW_MODE_SET, "SET", 0, ALLOW_MODE__OP }, + { ALLOW_MODE_AND, "AND", 0, ALLOW_MODE__OP }, + { 0, "OR", 0, ALLOW_MODE__OP }, + { ALLOW_MODE_CONTINUE, "CONTINUE", 0, 0 }, + {0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeAllowIP4 ( AllowIP4_t *ai ) +{ + DASSERT(ai); + memset(ai,0,sizeof(*ai)); + ai->ref_counter = 1; + ai->fallback_mode = ALLOW_MODE_DENY; + ai->allow_mode = ALLOW_MODE_ALLOW|ALLOW_MODE_AKEY; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetAllowIP4 ( AllowIP4_t *ai ) +{ + if (ai) + { + FREE(ai->list); + ai->list = 0; + ai->used = ai->size = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearAllowIP4 ( AllowIP4_t *ai ) +{ + if (ai) + { + FREE(ai->list); + InitializeAllowIP4(ai); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +AllowIP4_t * NewReferenceAllowIP4 ( AllowIP4_t *ai ) +{ + if (ai) + ai->ref_counter++; + else + { + ai = MALLOC(sizeof(*ai)); + DASSERT(ai); + InitializeAllowIP4(ai); + + } + return ai; +} + +/////////////////////////////////////////////////////////////////////////////// + +AllowIP4_t * DeleteReferenceAllowIP4 ( AllowIP4_t *ai ) +{ + if ( ai && !--ai->ref_counter ) + { + ResetAllowIP4(ai); + FREE(ai); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanLineAllowIP4 +( + AllowIP4_t *ai, // valid structure; new elements are appended + mem_t line, // single line to analyse + const KeywordTab_t *tab // NULL or keyword table. + // If NULL, a local table with keywords 0 DENY + // ALLOW SET REMOVE AND OR and CONTINUE is used. +) +{ + if ( !line.ptr || !line.len ) + return ERR_OK; + + ccp ptr = line.ptr; + ccp end = ptr + line.len; + + //--- find first character + while ( ptr < end && *(uchar*)ptr <= ' ' ) + ptr++; + if ( ptr == end || *ptr == '#' ) + return ERR_OK; + + NetworkHost_t host; + ResolveHostMem(&host,true,BehindMem(line,ptr),0,false,false); + if (!host.ip4_valid) + return ERR_ERROR; + line = host.not_scanned; + + u32 mask = M1(mask); + if ( line.len && *line.ptr == '/' ) + line = ScanNetworkMaskMem(&mask,line); + + + //--- scan options + + if (!tab) + tab = AllowIP4KeyTable; + + char arg[1000]; + StringCopySMem(arg,sizeof(arg),line); // to be NULL terminated + s64 res = ScanKeywordList(arg,tab,0,true,0,0,"AllowIP4",ERR_SYNTAX); + + if ( res == M1(res) ) + return ERR_SYNTAX; + + if ( res & ALLOW_MODE_NOT ) + res = res ^ ALLOW_MODE__MASK; + + + //--- add item + + if ( ai->used == ai->size ) + { + ai->size = 11*ai->size/10 + 10; + ai->list = REALLOC(ai->list,ai->size*sizeof(*ai->list)); + } + DASSERT( ai->used < ai->size ); + DASSERT( ai->list ); + + AllowIP4Item_t *it = ai->list + ai->used++; + //memset(it,0,sizeof(*it)); + it->addr = host.ip4 & mask; + it->mask = mask; + it->mode = res; + it->count = 0; + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanFileAllowIP4 +( + AllowIP4_t *ai, // valid structure; new elements are appended + ccp fname, // ffilename of the source + FileMode_t fmode, // flags for OpenFile() + const KeywordTab_t *tab // NULL or keyword table -> see ScanLineAllowIP4() +) +{ + File_t F; + enumError err = OpenFile(&F,true,fname,fmode,0,0); + if (err) + return err; + + char iobuf[10000]; + while (fgets(iobuf,sizeof(iobuf)-1,F.f)) + { + err = ScanLineAllowIP4(ai,MemByString(iobuf),tab); + if (err) + break; + } + + CloseFile(&F,0); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetCountersAllowIP4 +( + AllowIP4_t *ai // NULL or dest for addition +) +{ + if (ai) + { + uint i; + AllowIP4Item_t *p = ai->list; + for ( i = 0; i < ai->used; i++, p++ ) + p->count = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddCountersAllowIP4 +( + AllowIP4_t *dest, // NULL or dest for addition + const AllowIP4_t *src // NULL or source for addition +) +{ + if ( dest && dest->used && src && src->used ) + { + uint is; + const AllowIP4Item_t *ps = src->list; + for ( is = 0; is < src->used; is++, ps++ ) + { + uint id; + AllowIP4Item_t *pd = dest->list; + for ( id = 0; id < dest->used; id++, pd++ ) + if ( pd->addr == ps->addr + && pd->mask == ps->mask + && pd->mode == ps->mode + ) + { + pd->count += ps->count; + break; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint print_mode_helper + ( char *buf, uint bufsize, const KeywordTab_t *keytab, u64 mode ) +{ + ASSERT(buf); + ASSERT(bufsize>1); + char *dest = buf, *end = buf + bufsize-1; + if (keytab) + { + mode &= ~(ALLOW_MODE_SET|ALLOW_MODE_AND); + for ( ; mode && keytab->name1; keytab++ ) + { + if ( keytab->id & mode ) + { + dest = StringCat2E(dest,end,keytab->name1," "); + mode &= ~keytab->id; + } + } + } + *dest = 0; + return dest-buf; +} + +//----------------------------------------------------------------------------- + +void DumpAllowIP4 +( + FILE *f, // NULL or output file + int indent, // indention + const AllowIP4_t *ai, // NULL or source + const KeywordTab_t *keytab, // NULL or key table for output + ccp title, // not NULL: Add title + bool print_tab_head // true: add table headings and separators +) +{ + if ( !f || !ai ) + return; + + char buf[200]; + uint i, fw_mode = 5, fw_settings = 8; + for ( i = 0; i < ai->used; i++ ) + { + const AllowIP4Item_t *it = ai->list + i; + uint len = snprintf(buf,sizeof(buf),"%llx",it->mode); + if ( fw_mode < len ) + fw_mode = len; + + len = print_mode_helper(buf,sizeof(buf),keytab,it->mode); + if ( fw_settings < len ) + fw_settings = len; + } + + indent = NormalizeIndent(indent); + fprintf(f, "%*sAllowIP4[%s]: %u reference%s, %u/%u elements," + " fallback:%llx, allow:%llx\n", + indent,"", title ? title : "", + ai->ref_counter, ai->ref_counter == 1 ? "" : "s", + ai->used, ai->size, + ai->fallback_mode, ai->allow_mode ); + + const uint seplen = 52 + fw_mode + fw_settings; + if (print_tab_head) + fprintf(f,"\n" + "%*s counter ipv4 address ipv4/netmask > %-*s" + " settings\n%*s%.*s\n", + indent,"", fw_mode+4, "operation", + indent,"", seplen, Minus300 ); + + for ( i = 0; i < ai->used; i++ ) + { + const AllowIP4Item_t *it = ai->list + i; + print_mode_helper(buf,sizeof(buf),keytab,it->mode); + fprintf(f, "%*s%8s %-15s %08x/%08x > %s %*llx %s\n", + indent, "", + PrintNumberU7(0,0,it->count,DC_SFORM_ALIGN|DC_SFORM_DASH), + PrintIP4(0,0,it->addr,-1), + it->addr, it->mask, + it->mode & ALLOW_MODE_SET ? "SET" + : it->mode & ALLOW_MODE_AND ? "AND" : "OR ", + fw_mode, it->mode, *buf ? buf : "-" ); + } + + if (print_tab_head) + fprintf(f,"%*s%.*s\n", indent,"", seplen, Minus300 ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u64 GetAllowIP4ByAddr ( const AllowIP4_t *ai, u32 addr, u64 if_not_found ) +{ + DASSERT(ai); + bool found = false; + uint i; + u64 res = 0; + AllowIP4Item_t *it = (AllowIP4Item_t*)ai->list; + for ( i = 0; i < ai->used; i++, it++ ) + { + if ( ( addr & it->mask ) == it->addr ) + { + it->count++; + found = true; + + if ( it->mode & ALLOW_MODE_SET ) + res = it->mode; + else if ( it->mode & ALLOW_MODE_AND ) + res &= it->mode; + else + res |= it->mode; + if ( !(it->mode & ALLOW_MODE_CONTINUE) ) + break; + } + } + + return found ? res & ALLOW_MODE__MASK : if_not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetAllowIP4ByName ( const AllowIP4_t *ai, mem_t name, u64 if_not_found ) +{ + DASSERT(ai); + NetworkHost_t host; + ResolveHostMem(&host,true,name,0,false,false); + return host.ip4_valid + ? GetAllowIP4ByAddr(ai,host.ip4,if_not_found) + : if_not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetAutoAllowIP4ByAddr ( const AllowIP4_t *ai, u32 addr ) +{ + if (!ai) + return ALLOW_MODE_ALLOW; + + const u64 res = GetAllowIP4ByAddr(ai,addr,M1(res)); + return IS_M1(res) ? ai->fallback_mode : res & ALLOW_MODE__MASK; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetAutoAllowIP4ByName ( const AllowIP4_t *ai, mem_t name ) +{ + if (!ai) + return ALLOW_MODE_ALLOW; + + NetworkHost_t host; + ResolveHostMem(&host,true,name,0,false,false); + return host.ip4_valid + ? GetAllowIP4ByAddr(ai,host.ip4,ai->fallback_mode) + : ai->fallback_mode; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetAutoAllowIP4BySock ( const AllowIP4_t *ai, int sock ) +{ + if (!ai) + return ALLOW_MODE_ALLOW; + + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + if ( getpeername(sock,(struct sockaddr*)&sa,&sa_len) || sa.sin_family != AF_INET ) + return ai->fallback_mode; + + return GetAutoAllowIP4ByAddr(ai,ntohl(sa.sin_addr.s_addr)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateAllowIP4 +( + FILE *f, // output file + ccp section, // not NULL and not empty: create own section + ccp name_prefix, // NULL or prefix of name + uint tab_pos, // tab pos of '=' + const AllowIP4_t *ai // valid struct +) +{ + DASSERT(f); + DASSERT(ai); + + if ( section && *section ) + fprintf(f,"\n#%.50s\n[%s]\n\n",Minus300,section); + + if (!name_prefix) + name_prefix = EmptyString; + + const int base = 8*tab_pos + 7 - strlen(name_prefix); + + fprintf(f, + "%s" "fallback%.*s= %#llx\n" + "%s" "allow%.*s= %#llx\n" + "%s" "n-rules%.*s= %u\n" + ,name_prefix, (base-8)/8, Tabs20, ai->fallback_mode + ,name_prefix, (base-5)/8, Tabs20, ai->allow_mode + ,name_prefix, (base-7)/8, Tabs20, ai->used + ); + + uint i; + char name[30]; + + for ( i = 0; i < ai->used; i++ ) + { + const AllowIP4Item_t *it = ai->list + i; + + const uint len = snprintf(name,sizeof(name),"rule-%u",i); + fprintf(f, "%s%s%.*s= 0x%08x,0x%08x,%#llx,%llu\n", + name_prefix, name, (base-len)/8, Tabs20, + it->addr, it->mask, it->mode, it->count ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ScanAllowIP4Item +( + // return true on success; counter is optional + + AllowIP4Item_t *it, // store data here, cleared first + ccp line // NULL or line to scan +) +{ + DASSERT(it); + memset(it,0,sizeof(*it)); + if (line) + { + char *end; + it->addr = str2ul(line,&end,10); + if ( end > line ) + { + line = SkipControls1(end,','); + it->mask = str2ul(line,&end,10); + if ( end > line ) + { + line = SkipControls1(end,','); + it->mode = str2ull(line,&end,10); + if ( end > line ) + { + line = SkipControls1(end,','); + it->count = str2ull(line,0,10); // optional -> no error check + return true; + } + } + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateAllowIP4 +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +) +{ + DASSERT(rs); + if (user_table) + RestoreStateAllowIP4Ex(rs,(AllowIP4_t*)user_table,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateAllowIP4Ex +( + RestoreState_t *rs, // info data, modified + AllowIP4_t *ai, // valid struct + ccp name_prefix // NULL or prefix of name +) +{ + DASSERT(ai); + DASSERT(rs); + + ClearAllowIP4(ai); + + if (!name_prefix) + name_prefix = EmptyString; + + char name[200]; // name buf + + snprintf(name,sizeof(name),"%sfallback",name_prefix); + ai->fallback_mode = GetParamFieldU64(rs,name,ai->fallback_mode); + + snprintf(name,sizeof(name),"%sallow",name_prefix); + ai->allow_mode = GetParamFieldU64(rs,name,ai->allow_mode); + + snprintf(name,sizeof(name),"%sn-rules",name_prefix); + const uint n_rules = GetParamFieldUINT(rs,name,0); + + uint i; + for ( i = 0; i < n_rules; i++ ) + { + snprintf(name,sizeof(name),"%srule-%u",name_prefix,i); + ParamFieldItem_t *pfi = GetParamField(rs,name); + AllowIP4Item_t ait; + if ( pfi && ScanAllowIP4Item(&ait,(ccp)pfi->data) ) + { + if ( ai->used == ai->size ) + { + ai->size = 11*ai->size/10 + 10; + ai->list = REALLOC(ai->list,ai->size*sizeof(*ai->list)); + } + DASSERT( ai->used < ai->size ); + DASSERT( ai->list ); + ai->list[ai->used++] = ait; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCP + UDP connections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ConnectByHost +( + NetworkHost_t *host, // valid pointer to NetworkHost_t + int type, // AF_INET type, e.g. SOCK_STREAM, SOCK_DGRAM + int protocol, // AF_INET protocol, e.g. IPPROTO_TCP, IPPROTO_UDP + bool silent // true: suppress error messages +) +{ + DASSERT(host); + + int sock = socket(AF_INET,type,protocol); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CONNECT, + "Can't create socket: %s\n", + GetHostName(host)); + return -1; + } + + if (connect(sock,(struct sockaddr *)&host->sa,sizeof(host->sa))) + { + if (!silent) + ERROR1(ERR_CANT_CONNECT, + "Can't connect to %s:%d\n", + GetHostName(host), host->port ); + close(sock); + return -1; + } + + return sock; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ConnectByHostTCP +( + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool silent // true: suppress error messages +) +{ + DASSERT(host); + return ConnectByHost(host,SOCK_STREAM,IPPROTO_TCP,silent); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ConnectTCP +( + ccp addr, // optional service + server + optional host + // e.g.: "service://host:port/....." + int default_port, // default port + bool silent // true: suppress error messages +) +{ + Setup_ConnectTCP_Hook(false); + + ccp unix_path = CheckUnixSocketPath(addr,0); + if (unix_path) + return ConnectUnixTCP(unix_path,silent); + + if (!strncasecmp(addr,"tcp:",4)) + addr += 4; + + NetworkHost_t host; + int sock = -1; + if (ResolveHost(&host,true,addr,default_port,false,true)) + sock = ConnectByHost(&host,SOCK_STREAM,IPPROTO_TCP,silent); + ResetHost(&host); + return sock; +} + +/////////////////////////////////////////////////////////////////////////////// + +void Setup_ConnectTCP_Hook ( bool force ) +{ + if ( force || !ConnectTCP_Hook ) + ConnectTCP_Hook = ConnectTCP; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ConnectByHostUDP +( + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool silent // true: suppress error messages +) +{ + DASSERT(host); + return ConnectByHost(host,SOCK_DGRAM,IPPROTO_UDP,silent); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ConnectUDP +( + ccp name, // optional service + server + optional host + // e.g.: "service://host:port/....." + int default_host, // default host + bool silent // true: suppress error messages +) +{ + NetworkHost_t host; + int sock = -1; + if (ResolveHost(&host,true,name,default_host,false,true)) + sock = ConnectByHost(&host,SOCK_DGRAM,IPPROTO_UDP,silent); + ResetHost(&host); + return sock; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int WillReceiveNotBlock +( + // returns: -1:error, 0:may block, 1:will not block + + int fd, // file handle of source + uint msec // timeout: 0:no timeout, >0: milliseconds +) +{ + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(fd,&read_set); + + struct timeval timeout; + timeout.tv_sec = msec/1000; + timeout.tv_usec = (msec%1000) * 1000; + + errno = 0; + const int stat = select(fd+1,&read_set,0,0,&timeout); + return stat < 0 ? stat : stat > 0 && FD_ISSET(fd,&read_set); +} + +/////////////////////////////////////////////////////////////////////////////// + +ssize_t ReceiveTimeout +( + // returns: <0: on error, 0:timeout, >0: bytes read + + int fd, // file handle of source + void *buf, // valid pointer to buffer + uint size, // size to receive + int flags, // flags for recv() + int msec // timeout: -1:unlimited, 0:no timeout, >0: milliseconds +) +{ + if ( fd == -1 ) + return -1; + if ( !buf || !size ) + return 0; + + if ( msec >= 0 ) + { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(fd,&read_set); + + struct timeval timeout; + timeout.tv_sec = msec/1000; + timeout.tv_usec = (msec%1000) * 1000; + + errno = 0; + const int stat = select(fd+1,&read_set,0,0,&timeout); + if ( stat <= 0 ) + return stat; + } + + return recv(fd,buf,size,flags); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int WillSendNotBlock +( + // returns: -1:error, 0:may block, 1:will not block + + int fd, // file handle of source + uint msec // timeout: 0:no timeout, >0: milliseconds +) +{ + fd_set write_set; + FD_ZERO(&write_set); + FD_SET(fd,&write_set); + + struct timeval timeout; // kein Warten + timeout.tv_sec = msec/1000; + timeout.tv_usec = (msec%1000) * 1000; + + errno = 0; + const int stat = select(fd+1,0,&write_set,0,&timeout); + return stat < 0 ? stat : stat > 0 && FD_ISSET(fd,&write_set); +} + +/////////////////////////////////////////////////////////////////////////////// + +ssize_t SendTimeout +( + // returns: <0: on error, 0:timeout, >0: bytes written + + int fd, // file hande of destination + const void *data, // valid pointer to data + uint size, // size to receive + int flags, // flags for send() + int msec, // total timeout: -1:unlimited, 0:no timeout, >0: milliseconds + bool all // true: send all data until done, total timeout or error +) +{ + if ( fd == -1 ) + return -1; + if ( !data || !size ) + return 0; + + //--- send loop for complete sending + + if (all) + { + uint max_time = msec < 0 ? 0 : GetTimerMSec() + msec; + ssize_t total = 0; + while (size) + { + ssize_t stat = SendTimeout(fd,data,size,flags,msec,false); + if ( stat < 0 ) + return stat; + data = (u8*)data + stat; + size -= stat; + total += stat; + + if ( msec >= 0 ) + { + msec = max_time - GetTimerMSec(); + if ( msec < 0 ) + break; + } + } + return total; + } + + //--- send, single try + + if ( msec >= 0 ) + { + fd_set write_set; + FD_ZERO(&write_set); + FD_SET(fd,&write_set); + + struct timeval timeout; + timeout.tv_sec = msec/1000; + timeout.tv_usec = (msec%1000) * 1000; + + errno = 0; + const int stat = select(fd+1,0,&write_set,0,&timeout); + if ( stat <= 0 ) + return stat; + } + + return send(fd,data,size,flags); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IP interface: check sums /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u16 CalcChecksumIP4 +( + const void *data, // ip4 header + // make sure, that the checksum member is NULL + uint size // size of ip4 header +) +{ + // http://en.wikipedia.org/wiki/IPv4_header_checksum + + DASSERT(data); + const u16 *ptr = (u16*)data; + u32 csum = 0; + + while ( size > 1 ) + { + csum += ntohs(*ptr++); + size -= 2; + } + + if (size) + csum += *(u8*)ptr << 8; + + return ~( ( csum >> 16 ) + ( csum & 0xffff )); +} + +/////////////////////////////////////////////////////////////////////////////// + +u16 CalcChecksumUDP +( + const ip4_head_t *ip4, // IP4 header + const udp_head_t *udp, // UDP header + const void *data // UDP data, size is 'udp->data_len' +) +{ + DASSERT(ip4); + DASSERT(udp); + DASSERT( data || !udp->data_len ); + + int dlen = ntohs(udp->data_len); + u32 csum = ntohs(((u16*)&ip4->ip_src)[0]) + + ntohs(((u16*)&ip4->ip_src)[1]) + + ntohs(((u16*)&ip4->ip_dest)[0]) + + ntohs(((u16*)&ip4->ip_dest)[1]) + + ip4->protocol + + ntohs(udp->port_src) + + ntohs(udp->port_dest) + + 2*dlen; + + dlen -= 8; + const u16 *ptr = (u16*)data; + while ( dlen > 1 ) + { + csum += ntohs(*ptr++); + dlen -= 2; + } + + if ( dlen > 0 ) + csum += *(u8*)ptr << 8; + + while ( csum >= 0x10000 ) + csum = (u32)((u16*)&csum)[0] + (u32)((u16*)&csum)[1]; + + return csum == 0xffff ? 0xffff : htons(~csum); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// UNIX sockets /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SendUnixUDP +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + bool silent, // suppress all error messages + cvp data, // data to send + uint size // size of 'data' +) +{ + if ( !data || !size ) + return ERR_NOTHING_TO_DO; + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + PRINT("SendUnixUDP(%s)\n",path); + + static int sock = -1; + if ( sock == -1 ) + { + sock = socket(AF_UNIX,SOCK_STREAM|SOCK_CLOEXEC,0); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CREATE, + "Can't create UNIX stream socket: %s\n",path); + return ERR_CANT_CREATE; + } + } + + struct sockaddr_un sa; + memset(&sa,0,sizeof(sa)); + sa.sun_family = AF_UNIX; + StringCopyS(sa.sun_path,sizeof(sa.sun_path),path); + + if ( sendto(sock,data,size,0,(struct sockaddr*)&sa,sizeof(sa)) < 0 ) + { + if (!silent) + ERROR1(ERR_WRITE_FAILED, + "Can't send %u bytes to UNIX stream socket: %s\n", + size, path ); + return ERR_WRITE_FAILED; + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transfer Statistics /////////////// +/////////////////////////////////////////////////////////////////////////////// + +TransferStats_t * Add2TransferStats +( + // return dest + // calculate: dest += src + + TransferStats_t *dest, // NULL or destination and first source + const TransferStats_t *src // NULL or second source +) +{ + if ( dest && src ) + { + dest->conn_count += src->conn_count; + dest->recv_count += src->recv_count; + dest->recv_size += src->recv_size; + dest->send_count += src->send_count; + dest->send_size += src->send_size; + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +TransferStats_t * Sub2TransferStats +( + // return dest + // calculate: dest -= src + + TransferStats_t *dest, // NULL or destination and first source + const TransferStats_t *src // NULL or second source +) +{ + if ( dest && src ) + { + dest->conn_count -= src->conn_count; + dest->recv_count -= src->recv_count; + dest->recv_size -= src->recv_size; + dest->send_count -= src->send_count; + dest->send_size -= src->send_size; + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +TransferStats_t * Add3TransferStats +( + // return dest + // calculate: dest = src1 + src2 + + TransferStats_t *dest, // NULL or destination (maybe same as source) + const TransferStats_t *src1, // NULL or first source + const TransferStats_t *src2 // NULL or second source +) +{ + if (dest) + { + if (!src1) + { + if (src2) + memcpy(dest,src2,sizeof(*dest)); + else + memset(dest,0,sizeof(*dest)); + } + else if (!src2) + { + DASSERT(src1); + memcpy(dest,src1,sizeof(*dest)); + } + else + { + DASSERT(src1); + DASSERT(src2); + dest->conn_count = src1->conn_count + src2->conn_count; + dest->recv_count = src1->recv_count + src2->recv_count; + dest->recv_size = src1->recv_size + src2->recv_size; + dest->send_count = src1->send_count + src2->send_count; + dest->send_size = src1->send_size + src2->send_size; + } + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +TransferStats_t * Sub3TransferStats +( + // return dest + // calculate: dest = src1 - src2 + + TransferStats_t *dest, // NULL or destination (maybe same as source) + const TransferStats_t *src1, // NULL or first source + const TransferStats_t *src2 // NULL or second source +) +{ + if (dest) + { + if (!src1) + { + if (src2) + { + TransferStats_t temp; + memset(&temp,0,sizeof(temp)); + Sub3TransferStats(dest,&temp,src2); + } + else + memset(dest,0,sizeof(*dest)); + } + else if (!src2) + { + DASSERT(src1); + memcpy(dest,src1,sizeof(*dest)); + } + else + { + DASSERT(src1); + DASSERT(src2); + dest->conn_count = src1->conn_count - src2->conn_count; + dest->recv_count = src1->recv_count - src2->recv_count; + dest->recv_size = src1->recv_size - src2->recv_size; + dest->send_count = src1->send_count - src2->send_count; + dest->send_size = src1->send_size - src2->send_size; + } + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +TransferStats_t * SumTransferStats +( + // return dest + // calculate: dest = sum(all_sources) + + TransferStats_t *dest, // NULL or destination (maybe same as source) + const TransferStats_t * const *src, // NULL or source list, elements may be NULL + int n_src // number of element; if -1: term at first NULL +) +{ + if (dest) + { + TransferStats_t temp; + memset(&temp,0,sizeof(temp)); + + if (src) + { + if ( n_src < 0 ) + while ( *src ) + Add2TransferStats(&temp,*src++); + else + for ( ; *src; src++ ) + if (*src) + Add2TransferStats(&temp,*src); + } + memcpy(dest,&temp,sizeof(*dest)); + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTransferStatsSQL +( + // print statistic as SQL assigning list. + // arguments are speparated by a comma. + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + const TransferStats_t *ts, // valid source + ccp prefix // not NULL: prefix member names +) +{ + DASSERT(ts); + const bool alloced = !buf; + if (alloced) + buf = GetCircBuf( buf_size = CIRC_BUF_MAX_ALLOC ); + + if (!prefix) + prefix = ""; + + const uint len + = snprintf( buf, buf_size, + "%sconn=%u, %srecv_n=%u, %srecv=%llu, %ssend_n=%u, %ssend=%llu", + prefix, ts->conn_count, + prefix, ts->recv_count, + prefix, ts->recv_size, + prefix, ts->send_count, + prefix, ts->send_size ); + + if ( alloced && len < buf_size ) + ReleaseCircBuf(buf+buf_size,buf_size-len-1); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static inline u32 calc_rate ( u64 count, u64 duration ) + { return ( count * USEC_PER_SEC + duration/2 ) / duration; } + +static inline double calc_drate ( u64 count, u64 duration ) + { return (double)( count * USEC_PER_SEC ) / duration; } + +/////////////////////////////////////////////////////////////////////////////// + +void PrintTranferStatistics +( + FILE *f, // output file, never NULL + ccp prefix, // NULL or prefix for each line + ccp name, // statistics name, up to 5 chars including colon + const TransferStats_t *stat,// statistics record + + TransferStats_t *prev, // NULL statistics record with previous data + u64 duration, // delta duration in usec + ccp duration_info, // text representation of 'duration' + + const ColorSet_t *colset // NULL (no colors) or color set to use +) +{ + DASSERT(f); + DASSERT(name); + DASSERT(stat); + + if (!prefix) + prefix = ""; + + if (!colset) + colset = GetColorSet0(); + ccp color1 = colset->status; + ccp color0 = colset->reset; + + if (stat->conn_count) + { + fprintf(f, + "%s%s#STAT-%-5s %u connect%s," + " %u packet%s (%s) received, %u packet%s (%s) send.%s\n", + prefix, color1, name, + stat->conn_count, stat->conn_count == 1 ? "" : "s", + stat->recv_count, stat->recv_count == 1 ? "" : "s", + PrintSize1024(0,0,stat->recv_size,false), + stat->send_count, stat->send_count == 1 ? "" : "s", + PrintSize1024(0,0,stat->send_size,false), + color0 ); + } + else if ( stat->recv_count || stat->send_count ) + { + fprintf(f, + "%s%s#STAT-%-5s %u packet%s (%s) received, %u packet%s (%s) send.%s\n", + prefix, color1, name, + stat->recv_count, stat->recv_count == 1 ? "" : "s", + PrintSize1024(0,0,stat->recv_size,false), + stat->send_count, stat->send_count == 1 ? "" : "s", + PrintSize1024(0,0,stat->send_size,false), + color0 ); + } + else + return; + + if ( duration > 0 && prev ) + { + TransferStats_t delta; + Sub3TransferStats(&delta,stat,prev); + + char duration_buf[10]; + if (!duration_info) + { + PrintTimerUSec6(duration_buf,sizeof(duration_buf),duration,false); + duration_info = duration_buf; + } + + if ( delta.recv_count ) + { + const double crate = calc_drate( delta.recv_count, duration ); + const u32 srate = calc_rate( delta.recv_size, duration ); + fprintf(f, + "%s%s#STAT-%-5s >Last " + "%s: %u (%4.2f/s) packet%s with %s (%s/s) received.%s\n", + prefix, color1, name, + duration_info, + delta.recv_count, crate, + delta.recv_count == 1 ? "" : "s", + PrintSize1024(0,0,delta.recv_size,false), + PrintSize1024(0,0,srate,false), + color0 ); + } + + if ( delta.send_count ) + { + const double crate = calc_drate( delta.send_count, duration ); + const u32 srate = calc_rate( delta.send_size, duration ); + fprintf(f, + "%s%s#STAT-%-5s >Last " + "%s: %u (%4.2f/s) packet%s with %s (%s/s) send.%s\n", + prefix, color1, name, + duration_info, + delta.send_count, crate, + delta.send_count == 1 ? "" : "s", + PrintSize1024(0,0,delta.send_size,false), + PrintSize1024(0,0,srate,false), + color0 ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateTransferStats +( + FILE *f, // output file + const TransferStats_t *stat // valid stat object +) +{ + DASSERT(f); + DASSERT(stat); + + fprintf(f, + "conn-count = %u\n" + "recv-count = %u\n" + "recv-size = %llu\n" + "send-count = %u\n" + "send-size = %llu\n" + ,stat->conn_count + ,stat->recv_count + ,stat->recv_size + ,stat->send_count + ,stat->send_size + ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateTransferStats1 +( + // print as single line + FILE *f, // output file + ccp name, // var name; use "tfer-stat" if NULL or EMPTY + const TransferStats_t *stat // valid stat object +) +{ + DASSERT(f); + DASSERT(stat); + + if ( !name || !*name ) + name = "tfer-stat"; + const int tab_size = ( 23 - strlen(name) ) / 8; + + fprintf(f, + "%s%.*s= %u,%u,%llu,%u,%llu\n" + ,name + ,tab_size > 0 ? tab_size : 0 + ,"\t\t" + ,stat->conn_count + ,stat->recv_count + ,stat->recv_size + ,stat->send_count + ,stat->send_size + ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateTransferStatsN +( + // print multiple stats each as single line + FILE *f, // output file + ccp name, // var name; use "tfer-stat-" if NULL or EMPTY + const TransferStats_t *stat, // list of valid stat objects + uint n_stat // number of elements in 'stat' +) +{ + DASSERT(f); + DASSERT(stat||!n_stat); + + if ( !name || !*name ) + name = "tfer-stat-"; + const int tab_size = ( 23 - strlen(name) ) / 8; + + fprintf(f,"%sn%.*s= %u\n", + name, tab_size > 0 ? tab_size : 0, "\t\t", n_stat ); + + uint i; + char buf[100]; + for ( i = 0; i < n_stat; i++ ) + { + snprintf(buf,sizeof(buf),"%s%u",name,i); + SaveCurrentStateTransferStats1(f,buf,stat++); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateTransferStats +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +) +{ + DASSERT(rs); + + TransferStats_t *stat = (TransferStats_t*)user_table; + if (!stat) + return; + + stat->conn_count = GetParamFieldUINT( rs, "conn-count", stat->conn_count ); + stat->recv_count = GetParamFieldUINT( rs, "recv-count", stat->recv_count ); + stat->send_count = GetParamFieldUINT( rs, "send-count", stat->send_count ); + stat->recv_size = GetParamFieldU64 ( rs, "recv-size", stat->recv_size ); + stat->send_size = GetParamFieldU64 ( rs, "send-size", stat->send_size ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateTransferStats1 +( + // scan single line + RestoreState_t *rs, // info data, can be modified (cleaned after call) + ccp name, // var name; use "tfer-stat" if NULL or EMPTY + TransferStats_t *stat, // valid stat object + bool fall_back // true: fall back to RestoreStateTransferStats() +) +{ + DASSERT(rs); + DASSERT(stat); + + if ( !name || !*name ) + name = "tfer-stat"; + + char buf[100]; + int res = GetParamFieldBUF( buf, sizeof(buf), rs, name, ENCODE_OFF, 0 ); + if ( res > 0 ) + { + memset(stat,0,sizeof(*stat)); + + char *ptr; + stat->conn_count = strtoul (buf,&ptr,10); if (*ptr == ',' ) ptr++; + stat->recv_count = strtoul (ptr,&ptr,10); if (*ptr == ',' ) ptr++; + stat->recv_size = strtoull(ptr,&ptr,10); if (*ptr == ',' ) ptr++; + stat->send_count = strtoul (ptr,&ptr,10); if (*ptr == ',' ) ptr++; + stat->send_size = strtoull(ptr,&ptr,10); + return; + } + + if (fall_back) + RestoreStateTransferStats(rs,stat); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RestoreStateTransferStatsN +( + // print multiple stats each as single line + // returns the number of read elements; all others are set to NULL + RestoreState_t *rs, // info data, can be modified (cleaned after call) + ccp name, // var name; use "tfer-stat-" if NULL or EMPTY + TransferStats_t *stat, // list of valid stat objects + uint n_stat // number of elements in 'stat' +) +{ + DASSERT(rs); + DASSERT(stat||!n_stat); + + if ( !name || !*name ) + name = "tfer-stat-"; + + memset(stat,0,n_stat*sizeof(*stat)); + + char buf[100]; + snprintf(buf,sizeof(buf),"%sn",name); + uint n_read = GetParamFieldUINT(rs,buf,0); + if ( n_read > n_stat ) + n_read = n_stat; + + uint i; + for ( i = 0; i < n_read; i++ ) + { + snprintf(buf,sizeof(buf),"%s%u",name,i); + RestoreStateTransferStats1(rs,buf,stat++,false); + } + + return n_read; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCP HANDLER /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define LOG_TCP HAVE_PRINT0 + +#undef INC_TCP_INDENT +#undef DEC_TCP_INDENT +#undef LOG_TCP_BUFFER + +#if LOG_TCP + static int tcp_indent = 5; + #define INC_TCP_INDENT tcp_indent += 2 + #define DEC_TCP_INDENT tcp_indent -= 2 + #define LOG_TCP_BUFFER(tb,f,...) LogGrowBuffer(stderr,tcp_indent,tb,f,__VA_ARGS__) + #define LOG_TCP_STREAM(ts,r,f,...) LogTCPStream(stderr,tcp_indent,ts,r,f,__VA_ARGS__) + #define LOG_TCP_HANDLER(th,r,f,...) LogTCPHandler(stderr,tcp_indent,th,r,f,__VA_ARGS__) +#else + #define INC_TCP_INDENT + #define DEC_TCP_INDENT + #define LOG_TCP_BUFFER(tb,f,...) + #define LOG_TCP_STREAM(ts,r,f,...) + #define LOG_TCP_HANDLER(th,r,f,...) +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCPStream_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeTCPStream ( TCPStream_t *ts, int sock ) +{ + DASSERT(ts); + memset(ts,0,sizeof(*ts)); + ts->unique_id = CreateUniqueId(1); + ts->sock = sock; + ts->poll_index = M1(ts->poll_index); + LOG_TCP_STREAM(ts,0,"%s","INIT()"); + INC_TCP_INDENT; + InitializeGrowBuffer(&ts->ibuf,0x800); + InitializeGrowBuffer(&ts->obuf,0x800); + DEC_TCP_INDENT; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetTCPStream +( + TCPStream_t *ts // valid TCP handler +) +{ + DASSERT(ts); + LogTCPStreamActivity(ts,"ResetTCPStream"); + TCPHandler_t *th = ts->handler; + + if (th) + { + if (th->OnDestroyStream) + th->OnDestroyStream(ts); + th->used_streams--; + } + + if ( ts->sock != -1 ) + { + shutdown(ts->sock,SHUT_RDWR); + close(ts->sock); + ts->sock = -1; + } + + INC_TCP_INDENT; + ResetGrowBuffer(&ts->ibuf); + ResetGrowBuffer(&ts->obuf); + DEC_TCP_INDENT; + + if (th) + { + // chain is only valid if part of TCP-handler + if (ts->prev) + ts->prev->next = ts->next; + if (ts->next) + ts->next->prev = ts->prev; + if ( th->first == ts ) + th->first = ts->next; + } + memset(ts,0,sizeof(*ts)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DestroyTCPStream +( + TCPStream_t *ts // valid TCP handler +) +{ + DASSERT(ts); + LogTCPStreamActivity(ts,"DestroyTCPStream"); + ResetTCPStream(ts); + FREE(ts); +} + +/////////////////////////////////////////////////////////////////////////////// + +ssize_t UpdateRecvStatTCPStream +( + // returns 'send_stat' + + TCPStream_t *ts, // valid TCP handler + ssize_t recv_stat, // result of recv(), update stats on >0 + u64 now_usec // NULL or current time +) +{ + DASSERT(ts); + if ( recv_stat > 0 ) + { + ts->receive_usec = now_usec ? now_usec : GetTimeUSec(false); + ts->stat.recv_count++; + ts->stat.recv_size += recv_stat; + + if (ts->xstat) + { + ts->xstat->recv_count++; + ts->xstat->recv_size += recv_stat; + } + + if (ts->handler) + { + ts->handler->stat.recv_count++; + ts->handler->stat.recv_size += recv_stat; + } + } + return recv_stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +ssize_t UpdateSendStatTCPStream +( + // returns 'send_stat' + + TCPStream_t *ts, // valid TCP handler + ssize_t send_stat, // result of send(), update stats on >0 + u64 now_usec // NULL or current time +) +{ + DASSERT(ts); + if ( send_stat > 0 ) + { + ts->send_usec = now_usec ? now_usec : GetTimeUSec(false); + ts->stat.send_count++; + ts->stat.send_size += send_stat; + + if (ts->xstat) + { + ts->xstat->send_count++; + ts->xstat->send_size += send_stat; + } + + if (ts->handler) + { + ts->handler->stat.send_count++; + ts->handler->stat.send_size += send_stat; + } + } + return send_stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int WriteDirectGrowBuffer +( + // write() data direct without blocking and without calling any notifier. + // If output buf is not empty or send failed, append the data to 'gb'. + // Returns the number of bytes added to the grow buffer or -1 on error. + // The data is send+stored completely (returns 'size') or not at all + // (returns '0'). -1 is returned if the socket is invalid. + + GrowBuffer_t *gb, // grow buffer to cache data + int fd, // destination file + bool flush_output, // true: try to flush 'gb' before + cvp data, // data to send + uint size, // size of 'data', If NULL && flush: flush only + uint *send_count // not NULL: add num of sent bytes +) +{ + DASSERT(gb); + DASSERT(data||!size); + + const u8 *d = data; + if ( fd != -1 ) + { + int final_stat = 0; + if ( gb->disabled <= 0 ) + { + if ( flush_output && gb->used ) + { + ssize_t stat = write(fd,gb->ptr,gb->used); + if ( stat > 0 ) + { + if (send_count) + *send_count += stat; + DropGrowBuffer(gb,stat); + } + } + + if ( !gb->used && size ) + { + ssize_t stat = write(fd,d,size); + if ( stat > 0 ) + { + if (send_count) + *send_count += stat; + + size -= stat; + if (!size) // likely + return stat; + + d += stat; + final_stat = stat; + + // for this case: force growing buffer (because of 'all or none') + PrepareGrowBuffer(gb,size,true); + } + } + } + + if ( size && size <= GetSpaceGrowBuffer(gb) ) + final_stat += InsertGrowBuffer(gb,d,size); + return final_stat; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int SendDirectGrowBuffer +( + // send() data direct without blocking and without calling any notifier. + // If output buf is not empty or send failed, append the data to 'gb'. + // Returns the number of bytes added to the grow buffer or -1 on error. + // The data is send+stored completely (returns 'size') or not at all + // (returns '0'). -1 is returned if the socket is invalid. + + GrowBuffer_t *gb, // grow buffer to cache data + int sock, // destination socket + bool flush_output, // true: try to flush 'gb' before + cvp data, // data to send + uint size, // size of 'data', If NULL && flush: flush only + uint *send_count // not NULL: add num of sent bytes +) +{ + DASSERT(gb); + DASSERT(data||!size); + + const u8 *d = data; + if ( sock != -1 ) + { + int final_stat = 0; + if ( gb->disabled <= 0 ) + { + if ( flush_output && gb->used ) + { + ssize_t stat = send(sock,gb->ptr,gb->used,MSG_DONTWAIT); + if ( stat > 0 ) + { + if (send_count) + *send_count += stat; + DropGrowBuffer(gb,stat); + } + } + + if ( !gb->used && size ) + { + ssize_t stat = send(sock,d,size,MSG_DONTWAIT); + if ( stat > 0 ) + { + if (send_count) + *send_count += stat; + + size -= stat; + if (!size) // likely + return stat; + + d += stat; + final_stat = stat; + + // for this case: force growing buffer (because of 'all or none') + PrepareGrowBuffer(gb,size,true); + } + } + } + + if ( size && size <= GetSpaceGrowBuffer(gb) ) + final_stat += InsertGrowBuffer(gb,d,size); + return final_stat; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int SendDirectTCPStream +( + // Send data direct without blocking and without calling any notifier. + // If output buf is not empty or send failed, append the data to output buf. + // Returns the number of bytes added to the output buffer or -1 on error. + // The data is send+stored completely (returns 'size') or not at all + // (returns '0'). -1 is returned if the socket is invalid. + + TCPStream_t *ts, // valid TCP handler + bool flush_output, // true: try to flush out-buf before + cvp data, // data to send + uint size // size of 'data', If NULL && flush: flush only +) +{ + DASSERT(ts); + DASSERT(data||!size); + + uint count = 0; + const int stat + = SendDirectGrowBuffer(&ts->obuf,ts->sock,flush_output,data,size,&count); + if ( count > 0 ) + UpdateSendStatTCPStream(ts,count,0); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintArgDirectTCPStream +( + // Printing interface for SendDirectTCPStream() + + TCPStream_t *ts, // valid TCP handler + bool flush_output, // true: try to flush out-buf before + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'vfprintf(...,format,...)' +) +{ + DASSERT(ts); + + if (!format) + return SendDirectTCPStream(ts,flush_output,0,0); + + char buf[10000]; + + va_list arg2; + va_copy(arg2,arg); + int stat = vsnprintf(buf,sizeof(buf),format,arg2); + va_end(arg2); + + if ( stat < sizeof(buf) ) + return SendDirectTCPStream(ts,flush_output,buf,stat); + + + //--- buffer too small, use dynamic memory + + noPRINT("PrintArgGrowBuffer() -> MALLOC(%u)\n",stat+1); + + char *temp = MALLOC(stat+1); + stat = vsnprintf(temp,stat+1,format,arg); + stat = SendDirectTCPStream(ts,flush_output,temp,stat); + FREE(temp); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintDirectTCPStream +( + // Printing interface for SendDirectTCPStream() + + TCPStream_t *ts, // valid TCP handler + bool flush_output, // true: try to flush out-buf before + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(...,format,...)' +) +{ + DASSERT(ts); + + if (format) + { + va_list arg; + va_start(arg,format); + const int stat = PrintArgDirectTCPStream(ts,flush_output,format,arg); + va_end(arg); + return stat; + } + + return SendDirectTCPStream(ts,flush_output,0,0); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int PrintBinary1TCPStream +( + TCPStream_t *ts, // NULL or destination + ccp cmd, // command name + cvp bin_data, // pointer to binary data + uint bin_size, // size of binary data + ccp format, // NULL or format string with final ';' + ... // parameters for 'format' +) +{ + DASSERT(cmd); + DASSERT(bin_data||!bin_size); + + if (!ts) + return -1; + + char buf[10000], *end_buf = buf + sizeof(buf) - 4; + char *dest = StringCopyE(buf,end_buf,cmd); + if ( dest + 6 + bin_size >= end_buf ) + return -1; + + *dest++ = ' '; + *dest++ = 1; + write_be16(dest,bin_size); + dest += 2; + memcpy(dest,bin_data,bin_size); + dest += bin_size; + + *dest++ = ' '; + *dest++ = '='; + + int len; + if (format) + { + va_list arg; + va_start(arg,format); + len = vsnprintf(dest,end_buf-dest,format,arg); + va_end(arg); + + if ( len < 0 ) + return -1; + len += dest - buf; + if ( len >= sizeof(buf) ) + return -1; + } + else + { + *dest++ = ';'; + len = dest - buf; + } + + return SendDirectTCPStream(ts,false,buf,len); +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintBinary2TCPStream +( + TCPStream_t *ts, // NULL or destination + ccp cmd, // command name + cvp bin1_data, // pointer to binary data + uint bin1_size, // size of binary data + cvp bin2_data, // pointer to binary data + uint bin2_size, // size of binary data + ccp format, // NULL or format string with final ';' + ... // parameters for 'format' +) +{ + DASSERT(cmd); + DASSERT(bin1_data||!bin1_size); + DASSERT(bin2_data||!bin2_size); + + if (!ts) + return -1; + + char buf[10000], *end_buf = buf + sizeof(buf) - 4; + char *dest = StringCopyE(buf,end_buf,cmd); + if ( dest + 9 + bin1_size + bin2_size >= end_buf ) + return -1; + + *dest++ = ' '; + *dest++ = 1; + write_be16(dest,bin1_size); + dest += 2; + memcpy(dest,bin1_data,bin1_size); + dest += bin1_size; + + *dest++ = 1; + write_be16(dest,bin2_size); + dest += 2; + memcpy(dest,bin2_data,bin2_size); + dest += bin2_size; + + *dest++ = ' '; + *dest++ = '='; + + int len; + if (format) + { + va_list arg; + va_start(arg,format); + len = vsnprintf(dest,end_buf-dest,format,arg); + va_end(arg); + + if ( len < 0 ) + return -1; + len += dest - buf; + if ( len >= sizeof(buf) ) + return -1; + } + else + { + *dest++ = ';'; + len = dest - buf; + } + + return SendDirectTCPStream(ts,false,buf,len); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void LogTCPStream +( + FILE *f, // output file + int indent, // indention + const TCPStream_t *ts, // valid TCP handler + int recurse, // >0: print stream list, >1: print buffer status + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(format,...)' +) +{ + DASSERT(f); + DASSERT(ts); + indent = NormalizeIndent(indent); + + fprintf(f,"%*sTS: id:%x, sock=%d, %u packets received (%s), %u packets send (%s)", + indent,"", + ts->unique_id, ts->sock, + ts->stat.recv_count, PrintSize1024(0,0,ts->stat.recv_size,false), + ts->stat.send_count, PrintSize1024(0,0,ts->stat.send_size,false) ); + + if (format) + { + fputs(" : ",f); + va_list arg; + va_start(arg,format); + vfprintf(f,format,arg); + va_end(arg); + } + + fputc('\n',f); + + if (recurse>0) + { + LogGrowBuffer(f,indent+2,&ts->ibuf,"in buf"); + LogGrowBuffer(f,indent+2,&ts->obuf,"out buf"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void LogTCPStreamActivity +( + const TCPStream_t *ts, // valid TCP handler + ccp activity +) +{ + if (!ts->tracelog) + return; + + char hbuf[200]; + if (ts->handler) + { + const TCPHandler_t *th = ts->handler; + const Socket_t *so = th->listen; + + DASSERT( TCP_HANDLER_MAX_LISTEN >= 3 ); + snprintf(hbuf,sizeof(hbuf), + " | TH#%u: %u/%u/%u, sock=%d,%d,%d", + th->unique_id, + th->used_streams, th->max_used_streams, th->total_streams, + so[0].sock, so[1].sock, so[2].sock ); + } + else + *hbuf = 0; + + TraceLogPrint(ts->tracelog, + "%p %-20s sock=%d%s\n", ts, activity, ts->sock, hbuf ); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * BufInfoHeadTCPStream +( + // returns a pointer to the head line + + uint line // 0|1 +) +{ + switch (line) + { + case 0: + return "connect in buf out buf" + " received data send data"; + + case 1: + return " time sock status use/ max use/ max" + " time pkt size time pkt size stream info"; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * BufInfoTCPStream +( + // returns a pointer to the buffer + + char * buf, // result (BUF_INFO_TCP_STREAM_SIZE bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + const TCPStream_t *ts, // valid TCP handler + u64 now_usec // NULL or current time, GetTimeUSec(false) +) +{ + DASSERT(ts); + + if (!now_usec) + now_usec = GetTimeUSec(false); + + char connect_usec[7]; + if ( ts->connect_usec ) + PrintTimerUSec6(connect_usec,sizeof(connect_usec), + now_usec - ts->connect_usec, true ); + else + connect_usec[0] = '-', connect_usec[1] = 0; + + char receive_usec[7]; + if ( ts->receive_usec ) + PrintTimerUSec6(receive_usec,sizeof(receive_usec), + now_usec - ts->receive_usec, true ); + else + receive_usec[0] = '-', receive_usec[1] = 0; + + char send_usec[7]; + if ( ts->send_usec ) + PrintTimerUSec6(send_usec,sizeof(send_usec), + now_usec - ts->send_usec, true ); + else + send_usec[0] = '-', send_usec[1] = 0; + + if (!buf) + buf = GetCircBuf( buf_size = BUF_INFO_TCP_STREAM_SIZE ); + + snprintf(buf,buf_size, + "%6.6s%5d %c%c%c%c%c%c%c %s/%s %s/%s %6s %s %s %6s %s %s %s" + ,connect_usec + ,ts->sock + ,ts->protect > 0 ? 'P' : '-' + ,ts->auto_close > 0 ? 'A' : '-' + ,ts->rescan > 0 ? 'S' : '-' + ,ts->eof > 0 ? 'X' : '-' + ,ts->error > 0 ? 'E' : '-' + ,ts->ibuf.disabled ? 'D' : 'i' + ,ts->obuf.disabled ? 'D' : 'o' + ,PrintNumberU4(0,0,ts->ibuf.used,true) + ,PrintNumberU4(0,0,ts->ibuf.max_used,true) + ,PrintNumberU4(0,0,ts->obuf.used,true) + ,PrintNumberU4(0,0,ts->obuf.max_used,true) + ,receive_usec + ,PrintNumberU5(0,0,ts->stat.recv_count,true) + ,PrintSize1024(0,0,ts->stat.recv_size,DC_SFORM_TINY|DC_SFORM_ALIGN) + ,send_usec + ,PrintNumberU5(0,0,ts->stat.send_count,true) + ,PrintSize1024(0,0,ts->stat.send_size,DC_SFORM_TINY|DC_SFORM_ALIGN) + ,ts->info + ); + + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCPHandler_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddSocketTCPStream +( + TCPStream_t *ts, // valid TCP handler + FDList_t *fdl // valid file descriptor list +) +{ + DASSERT(ts); + DASSERT(fdl); + + if ( ts->OnFDList && ts->OnFDList(ts,fdl,TCP_FM_ADD_SOCK,false) < 0 || ts->sock == -1 ) + return; + + uint events = POLLERR|POLLRDHUP; + if ( !ts->eof && !ts->ibuf.disabled && GetSpaceGrowBuffer(&ts->ibuf) ) + events |= POLLIN; + if ( !ts->obuf.disabled && ts->obuf.used ) + events |= POLLOUT; + + ts->poll_index = AddFDList(fdl,ts->sock,events); + + if ( ts->OnTimeout && ts->trigger_usec && fdl->timeout_usec > ts->trigger_usec ) + fdl->timeout_usec = ts->trigger_usec; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CheckSocketTCPStream +( + TCPStream_t *ts, // valid TCP handler + FDList_t *fdl, // valid socket list + bool check_timeout // true: enable timeout checks +) +{ + DASSERT(ts); + DASSERT(fdl); + + if ( ts->OnFDList && ts->OnFDList(ts,fdl,TCP_FM_CHECK_SOCK,check_timeout) < 0 + || ts->sock == -1 ) + { + return; + } + + const u64 now_usec = GetTimeUSec(false); + + typeof(ts->rescan) rescan = ts->rescan; + ts->rescan = 0; + + const uint revents = GetEventFDList(fdl,ts->sock,ts->poll_index); + + if ( !ts->ibuf.disabled && revents & POLLIN ) + { + noPRINT("RECV: %d\n",ts->sock); + OnReceivedStream(ts,now_usec); + rescan = 0; + } + + if ( !ts->obuf.disabled && ts->obuf.used && revents & POLLOUT ) + OnWriteStream(ts,now_usec); + + if ( revents & (POLLERR|POLLHUP|POLLRDHUP|POLLNVAL) ) + { + ts->error |= 1; + OnCloseStream(ts,now_usec); + } + + if ( rescan && ts->OnReceived ) + { + uchar ch = 0; + ts->OnReceived(ts,&ch,0); + } + + if (check_timeout) + CheckTimeoutTCPStream(ts); + + if (ts->rescan) + ts->trigger_usec = ts->accept_usec = now_usec; +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef PRINT_TIMEOUT +#define PRINT_TIMEOUT(ts,now,info) \ + noPRINT("%s[%d]: now=%lld, trig=%lld, accept=%lld, OnTimeout=%d\n", \ + info, ts->sock, now, ts->trigger_usec ? ts->trigger_usec - now : 0, \ + ts->accept_usec ? ts->accept_usec - now : 0, ts->OnTimeout != 0 ) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CheckTimeoutTCPStream ( TCPStream_t *ts ) +{ + DASSERT(ts); + + if ( ts->OnFDList && ts->OnFDList(ts,0,TCP_FM_TIMEOUT,true) < 0 || ts->sock == -1 ) + return; + + const u64 now_usec = GetTimeUSec(false); + PRINT_TIMEOUT(ts,now_usec,"TIMEOUT"); + + if ( ts->trigger_usec && ts->trigger_usec <= now_usec + || ts->accept_usec && ts->accept_usec <= now_usec + ) + { + ts->trigger_usec = ts->accept_usec = 0; + if (ts->OnTimeout) + ts->OnTimeout(ts,now_usec); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetLastActivityTCPStream ( TCPStream_t *ts ) +{ + DASSERT(ts); + const u64 ref = ts->connect_usec > ts->receive_usec + ? ts->connect_usec : ts->receive_usec; + return ref > ts->send_usec ? ref : ts->send_usec; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetTimeoutTCPStream ( TCPStream_t *ts ) +{ + DASSERT(ts); + return ts->timeout_usec + ? GetLastActivityTCPStream(ts) + ts->timeout_usec + : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int OnTimeoutTCPStream ( TCPStream_t *ts, u64 now_usec ) +{ + DASSERT(ts); + if ( ts->sock != -1 && ts->timeout_usec ) + { + if (!now_usec) + now_usec = GetTimeUSec(false); + + if ( now_usec >= GetLastActivityTCPStream(ts) + ts->timeout_usec ) + OnCloseStream(ts,now_usec); + else + SetupTriggerTCPStream(ts,now_usec); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupTriggerTCPStream ( TCPStream_t *ts, u64 now_usec ) +{ + DASSERT(ts); + + const u64 next = GetTimeoutTCPStream(ts); + if (next) + { + if (!now_usec) + now_usec = GetTimeUSec(false); + + if ( ts->accept_usec <= now_usec || ts->accept_usec > next ) + ts->accept_usec = next; + ts->trigger_usec = ts->accept_usec + ts->delay_usec; + + noPRINT("next=%lld [%lld], accept=%lld [%lld]\n", + next, next-now_usec, ts->accept_usec, ts->accept_usec-now_usec ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCPHandler_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeTCPHandler +( + TCPHandler_t *th, // not NULL + uint data_size // size of 'TCPStream_t::data' +) +{ + DASSERT(th); + memset(th,0,sizeof(*th)); + th->unique_id = CreateUniqueId(1); + th->data_size = data_size; + th->OnAllowStream = IsStreamAllowed; + + uint i; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + th->listen[i].sock = -1; + LOG_TCP_HANDLER(th,0,"INIT(%u)",data_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetTCPHandler +( + TCPHandler_t *th // valid TCP handler +) +{ + DASSERT(th); + th->allow_ip4 = DeleteReferenceAllowIP4(th->allow_ip4); + UnlistenAllTCP(th); + + while (th->first) + DestroyTCPStream(th->first); + usleep(1); + + InitializeTCPHandler(th,th->data_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CloseTCPHandler +( + // returns the number of waiting clients (obuf not empty and !disabled) + TCPHandler_t *th // valid TCP handler +) +{ + DASSERT(th); + UnlistenAllTCP(th); + + uint count = 0; + u64 now = GetTimeUSec(false); + TCPStream_t *ts; + for ( ts = th->first; ts; ts = ts->next ) + { + OnCloseStream(ts,now); + if ( ts->obuf.used && !ts->obuf.disabled ) + count++; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * AddTCPStream +( + TCPHandler_t *th, // valid TCP handler + TCPStream_t *ts // the new stream +) +{ + DASSERT(th); + DASSERT(ts); + + ts->handler = th; + ts->prev = 0; + ts->next = th->first; + if (th->first) + th->first->prev = ts; + th->first = ts; + + if ( th->max_used_streams < ++th->used_streams ) + th->max_used_streams = th->used_streams; + th->total_streams++; + th->stat.conn_count++; + ts->connect_usec = GetTimeUSec(false); + + LogTCPStreamActivity(ts,"AddTCPStream"); + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * CreateTCPStream +( + TCPHandler_t *th, // valid TCP handler + int sock, // -1 or valid socket + u64 allow_mode, // stored in 'allow_mode' + const Socket_t *listen // NULL or related listen object +) +{ + DASSERT(th); + + TCPStream_t *ts = listen && listen->OnCreateStream + ? listen->OnCreateStream(th,sock) : 0; + if (!ts) + { + if (th->OnCreateStream) + ts = th->OnCreateStream(th,sock); + + if (!ts) + { + ts = CALLOC(sizeof(*ts)+th->data_size,1); + InitializeTCPStream(ts,sock); + } + } + + if ( th->tracelog.level >= 0 ) + { + ts->tracelog = &th->tracelog; + LogTCPStreamActivity(ts,"CreateTCPStream"); + } + + ts->allow_mode = allow_mode; + AddTCPStream(th,ts); + + if ( listen && listen->OnAddedStream ) + listen->OnAddedStream(ts); + else if (th->OnAddedStream) + th->OnAddedStream(ts); + + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * ConnectUnixTCPStream +( + TCPHandler_t *th, // valid TCP handler + ccp path, // NULL or path part 1 to socket file + bool silent // suppress error messages +) +{ + DASSERT(th); + DASSERT(path); + + int sock = socket(AF_UNIX,SOCK_STREAM,0); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CREATE,"Can't create UNIX stream socket: %s\n",path); + return 0; + } + + struct sockaddr_un sa; + memset(&sa,0,sizeof(sa)); + sa.sun_family = AF_UNIX; + StringCopyS(sa.sun_path,sizeof(sa.sun_path),path); + + int stat = connect(sock,(struct sockaddr*)&sa,sizeof(sa)); + if (stat) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't connect UNIX stream socket: %s\n",path); + shutdown(sock,SHUT_RDWR); + close(sock); + return 0; + } + + return CreateTCPStream(th,sock,ALLOW_MODE_ALLOW,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * ConnectTCPStream +( + TCPHandler_t *th, // valid TCP handler + ccp addr, // address -> NetworkHost_t + u16 default_port, // default port + bool silent // suppress error messages +) +{ + DASSERT(th); + DASSERT(addr); + + ccp unix_path = CheckUnixSocketPath(addr,0); + if (unix_path) + return ConnectUnixTCPStream(th,unix_path,silent); + + if (!strncasecmp(addr,"tcp:",4)) + addr += 4; + + NetworkHost_t nh; + ResolveHost(&nh,true,addr,default_port,false,false); + PRINT("SINGLE/CONNECT/TCP: %s -> %s\n", + addr, PrintIP4(0,0,nh.ip4,nh.port) ); + + int sock = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CREATE,"Can't create TCP socket: %s\n", + PrintIP4(0,0,nh.ip4,nh.port)); + return 0; + } + + struct sockaddr_in sa; + memset(&sa,0,sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(nh.ip4); + sa.sin_port = htons(nh.port); + + int stat = connect(sock,(struct sockaddr*)&sa,sizeof(sa)); + if ( stat && errno != EINPROGRESS ) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't connect TCP socket: %s\n", + PrintIP4(0,0,nh.ip4,nh.port)); + shutdown(sock,SHUT_RDWR); + close(sock); + ResetHost(&nh); + return 0; + } + + return CreateTCPStream(th,sock,ALLOW_MODE_ALLOW,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * FindTCPStreamByUniqueID +( + TCPHandler_t *th, // valid TCP handler + uint unique_id // id to search +) +{ + TCPStream_t *ts; + for ( ts = th->first; ts; ts = ts->next ) + if ( ts->unique_id == unique_id ) + return ts; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint NumOfSocketsTCP ( const TCPHandler_t *th ) +{ + uint i, count = 0; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + if ( th->listen[i].sock != -1 ) + count++; + + const TCPStream_t *ts; + for ( ts = th->first; ts; ts = ts->next ) + if ( ts->sock != -1 + && ( !ts->eof && !ts->ibuf.disabled + || !ts->obuf.disabled && ts->obuf.used )) + { + count++; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +Socket_t * GetUnusedListenSocketTCP ( TCPHandler_t *th, bool silent ) +{ + DASSERT(th); + + uint i; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + if ( th->listen[i].sock == -1 ) + return th->listen+i; + + if (!silent) + ERROR0(ERR_CANT_CREATE,"No socket available.\n"); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ListenUnixTCP +( + TCPHandler_t *th, // valid TCP handler + ccp path // path to unix file +) +{ + DASSERT(th); + DASSERT(path); + PRINT("LISTEN/UNIX: %s\n",path); + + Socket_t *lsock = GetUnusedListenSocketTCP(th,false); + if (!lsock) + return ERR_CANT_CREATE; + unlink(path); + + int sock = socket(AF_UNIX,SOCK_STREAM,0); + if ( sock == -1 ) + return ERROR1(ERR_CANT_CREATE,"Can't create UNIX socket: %s\n",path); + + struct sockaddr_un sa; + memset(&sa,0,sizeof(sa)); + sa.sun_family = AF_UNIX; + StringCopyS(sa.sun_path,sizeof(sa.sun_path),path); + + int stat = bind(sock,(struct sockaddr*)&sa,sizeof(sa)); + if ( stat == -1 ) + { + close(sock); + return ERROR1(ERR_CANT_CREATE,"Can't bind UNIX socket: %s\n",path); + } + + stat = listen(sock,50); + if ( stat == -1 ) + { + close(sock); + return ERROR1(ERR_CANT_CREATE,"Can't listen UNIX socket: %s\n",path); + } + //SetSocketBlocking(cmdi_sock,false); + + DASSERT(lsock); + lsock->sock = sock; + lsock->is_unix = true; + snprintf(lsock->info,sizeof(lsock->info),"UNIX socket %d",sock); + PRINT("LISTEN: %s [idx=%zu]\n",lsock->info,lsock-th->listen); + LOG_TCP_HANDLER(th,0,"LISTEN/UNIX[%u] %s",listen_idx,path); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ListenTCP +( + TCPHandler_t *th, // valid TCP handler + ccp addr, // IPv4 address with optional port -> NetworkHost_t + // fall back to ListenUnixTCP() if addr begins with + // 'unix:' or '/' or './ or '../' + u16 default_port // default port +) +{ + DASSERT(th); + DASSERT(addr); + + Socket_t *lsock = GetUnusedListenSocketTCP(th,false); + if (!lsock) + return ERR_CANT_CREATE; + + const bool allow_unix = memcmp(addr,"tcp:",4) != 0; + if (!allow_unix) + { + addr += 4; + while ( *addr == '/' ) + addr++; + } + + NetworkHost_t nh; + if ( !ResolveHost(&nh,true,addr,default_port,allow_unix,true) && nh.filename ) + { + ccp unix_path = nh.filename; + ResetHost(&nh); + return ListenUnixTCP(th,unix_path); + } + ccp path = GetHostName(&nh); + PRINT("LISTEN/TCP: %s:%u -> %s\n",nh.name,default_port,PrintIP4(0,0,nh.ip4,nh.port)); + + int sock = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0); + if ( sock == -1 ) + { + ERROR1(ERR_CANT_CREATE,"Can't create TCP socket: %s\n",path); + ResetHost(&nh); + return ERR_CANT_CREATE; + } + + int on = 1; + setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (ccp)&on, sizeof(on) ); + + struct sockaddr_in sa; + memset(&sa,0,sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(nh.ip4); + sa.sin_port = htons(nh.port); + + int stat = bind(sock,(struct sockaddr*)&sa,sizeof(sa)); + if ( stat == -1 ) + { + ERROR1(ERR_CANT_CREATE,"Can't bind TCP socket: %s\n", + PrintIP4(0,0,nh.ip4,nh.port)); + close(sock); + ResetHost(&nh); + return ERR_CANT_CREATE; + } + + stat = listen(sock,50); + if ( stat == -1 ) + { + ERROR1(ERR_CANT_CREATE,"Can't listen TCP socket: %s\n", + PrintIP4(0,0,nh.ip4,nh.port)); + close(sock); + ResetHost(&nh); + return ERR_CANT_CREATE; + } + //SetSocketBlocking(cmdi_sock,false); + + DASSERT(lsock); + lsock->sock = sock; + lsock->is_unix = false; + snprintf(lsock->info,sizeof(lsock->info),"TCP socket %d",sock); + PRINT("LISTEN: %s [idx=%zu]\n",lsock->info,lsock-th->listen); + + ResetHost(&nh); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int UnlistenTCP +( + // returns the index of the closed socket, or if not found + + TCPHandler_t *th, // valid TCP handler + int sock // socket to close +) +{ + uint i; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + if ( th->listen[i].sock == sock ) + { + close(sock); + th->listen[i].sock = -1; + return i; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint UnlistenAllTCP +( + // returns the number of closed sockets + + TCPHandler_t *th // valid TCP handler +) +{ + uint i, count = 0; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + if ( th->listen[i].sock != -1 ) + { + socket_info_t si; + GetSocketInfoBySocket(&si,th->listen[i].sock,true); + if ( si.is_valid && si.protocol == AF_UNIX + && si.address && *si.address ) + { + PRINT("unlink(%s)\n",si.address); + unlink(si.address); + } + ResetSocketInfo(&si); + + close(th->listen[i].sock); + th->listen[i].sock = -1; + count++; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddSocketsTCP +( + TCPHandler_t *th, // valid TCP handler + FDList_t *fdl // valid file descriptor list +) +{ + DASSERT(th); + DASSERT(fdl); + + AnnounceFDList(fdl,TCP_HANDLER_MAX_LISTEN+th->used_streams+1); + + if ( !th->max_conn || th->used_streams < th->max_conn ) + { + uint i; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + { + Socket_t *lsock = th->listen + i; + lsock->poll_index = AddFDList(fdl,lsock->sock,POLLIN); + } + } + + TCPStream_t *ts; + for ( ts = th->first; ts; ts = ts->next ) + AddSocketTCPStream(ts,fdl); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool IsStreamAllowed +( + // returns true if access is allowed + + struct TCPHandler_t *th, // valid TCP handler + const Socket_t *lsock, // valid listen-socket + int sock, // valid socket to verify + u64 *allow // not NULL: store allow code here +) +{ + DASSERT(th); + if ( !th->allow_ip4 || lsock && lsock->is_unix ) + { + // no checks for unix sockets + if (allow) + *allow = ALLOW_MODE_ALLOW; + return true; + } + + const u64 res = GetAutoAllowIP4BySock(th->allow_ip4,sock); + if (allow) + *allow = res; + return ( res & th->allow_ip4->allow_mode ) != 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * OnAcceptStream +( + TCPHandler_t *th, // valid TCP handler + const Socket_t *lsock, // listen-socket + u64 now_usec // time for timestamps, GetTimeUSec(false) +) +{ + DASSERT(th); + DASSERT(lsock); + DASSERT( lsock >= th->listen ); + DASSERT( lsock < th->listen + TCP_HANDLER_MAX_LISTEN ); + DASSERT( lsock->sock != -1 ); + PRINT("OnAcceptStream() lsock=%d,%s\n",lsock->sock,lsock->info); + + TCPStream_t *ts = NULL; + int new_sock = accept(lsock->sock,0,0); + if ( new_sock != -1 ) + { + RegisterFileId(new_sock); + + u64 allow_code = 0; + const u64 is_allowed = th->OnAllowStream + ? th->OnAllowStream(th,lsock,new_sock,&allow_code) + : ALLOW_MODE_ALLOW; + if ( !is_allowed || th->OnAcceptStream && th->OnAcceptStream(th,lsock,new_sock) ) + { + PRINT("NOT ACCEPTED: %d -> %d\n",lsock->sock,new_sock); + shutdown(new_sock,SHUT_RDWR); + close(new_sock); + } + else + { + PRINT("ACCEPTED: %d -> %d [alloe=%llx]\n",lsock->sock,new_sock,allow_code); + ts = CreateTCPStream(th,new_sock,allow_code,lsock); + if (ts) + { + if (!ts->info[0]) + { + if (lsock->info[0]) + snprintf(ts->info,sizeof(ts->info), + "by %s [%x]", + lsock->info, ts->unique_id ); + else + snprintf(ts->info,sizeof(ts->info), + "by socket %d [%x]", + lsock->sock, ts->unique_id ); + } + } + else + { + shutdown(new_sock,SHUT_RDWR); + close(new_sock); + } + } + } + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// + +void OnCloseStream +( + TCPStream_t *ts, // valid TCP stream + u64 now_usec // time for timestamps, GetTimeUSec(false) +) +{ + DASSERT(ts); + if ( ts->sock != -1 ) + { + PRINT("CLOSE: %d\n",ts->sock); + if (ts->OnClose) + ts->OnClose(ts,now_usec); + if ( ts->sock != -1 ) + { + shutdown(ts->sock,SHUT_RDWR); + close(ts->sock); + ts->sock = -1; + } + if ( !ts->protect && ts->handler ) + ts->handler->need_maintenance |= 1; + + ts->OnReceived = 0; + ts->OnSend = 0; + ts->OnTimeout = 0; + ts->OnClose = 0; + ts->OnFDList = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetNotSocketStream +( + TCPStream_t *ts // valid TCP stream +) +{ + DASSERT(ts); + if ( ts->sock != -1 ) + { + ts->not_socket = true; + + int flags = fcntl(ts->sock,F_GETFL,0); + if ( flags != -1 ) + { + flags |= O_NONBLOCK; + fcntl(ts->sock,F_SETFL,flags); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void OnReceivedStream +( + TCPStream_t *ts, // valid TCP stream + u64 now_usec // time for timestamps, GetTimeUSec(false) +) +{ + DASSERT(ts); + + uint max_read = GetSpaceGrowBuffer(&ts->ibuf); + if (max_read) + { + u8 buf[0x4000]; + if ( max_read > sizeof(buf) ) + max_read = sizeof(buf); + + ssize_t stat; + retry: + if (ts->not_socket) + stat = read(ts->sock,buf,max_read); + else + stat = recv(ts->sock,buf,max_read,MSG_DONTWAIT); + + if ( stat < 0 && !ts->not_socket && errno == ENOTSOCK ) + { + SetNotSocketStream(ts); + goto retry; + } + + #if HAVE_PRINT + noPRINT("RECV[%d] %lld/%u, err=%d\n",ts->sock,(s64)stat,max_read,errno); + if ( stat < 0 ) + PRINT("!! %s(), ERRNO=%d: %s\n",__FUNCTION__,errno,strerror(errno)); + #endif + + if ( stat >= 0 ) + { + if ( stat > 0 ) + UpdateRecvStatTCPStream(ts,stat,now_usec); + else + ts->eof |= 1; + + if (ts->OnReceived) + stat = ts->OnReceived(ts,buf,stat); + if ( stat > 0 ) + InsertGrowBuffer(&ts->ibuf,buf,stat); + +//X if ( ts->eof && !ts->ibuf.used && !ts->obuf.used ) +//X OnCloseStream(ts,now_usec); + } + else if ( errno != EWOULDBLOCK ) + OnCloseStream(ts,now_usec); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void OnWriteStream +( + TCPStream_t *ts, // valid TCP stream + u64 now_usec // time for timestamps, GetTimeUSec(false) +) +{ + DASSERT(ts); + + restart:; + ssize_t stat; + if (ts->not_socket) + stat = write(ts->sock,ts->obuf.ptr,ts->obuf.used); + else + stat = send(ts->sock,ts->obuf.ptr,ts->obuf.used,MSG_DONTWAIT); + noPRINT("SEND[%d] %lld/%u, err=%d\n",ts->sock,(s64)stat,ts->obuf.used,errno); + + if ( stat < 0 ) + { + if ( !ts->not_socket && errno == ENOTSOCK ) + { + SetNotSocketStream(ts); + goto restart; + } + + PRINT("!! %s(), ERRNO=%d: %s\n",__FUNCTION__,errno,strerror(errno)); + OnCloseStream(ts,now_usec); + } + else if (!stat) + ts->obuf.disabled |= 1; + else + { + UpdateSendStatTCPStream(ts,stat,now_usec); + + if (ts->OnSend) + stat = ts->OnSend(ts,ts->obuf.ptr,stat); + if ( stat > 0 ) + DropGrowBuffer(&ts->obuf,stat); + if ( ts->auto_close && !ts->obuf.used ) + OnCloseStream(ts,now_usec); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CheckSocketsTCP +( + TCPHandler_t *th, // valid TCP handler + FDList_t *fdl, // valid socket list + bool check_timeout // true: enable timeout checks +) +{ + DASSERT(th); + DASSERT(fdl); + + const u64 now_usec = GetTimeUSec(false); + + uint i; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + { + Socket_t *lsock = th->listen + i; + if ( lsock->sock != -1 ) + { + const uint revents = GetEventFDList(fdl,lsock->sock,lsock->poll_index); + if ( revents & POLLIN ) + OnAcceptStream(th,lsock,now_usec); + } + } + + TCPStream_t *next = th->first; + while (next) + { + // Do it in this way, because 'ts' may become invalid + TCPStream_t *ts = next; + next = ts->next; + CheckSocketTCPStream(ts,fdl,check_timeout); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CheckTimeoutTCP ( TCPHandler_t *th ) +{ + DASSERT(th); + + TCPStream_t *next = th->first; + while (next) + { + // Do it in this way, because 'ts' may becomes invalid + TCPStream_t *ts = next; + next = ts->next; + CheckTimeoutTCPStream(ts); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MaintenanceTCP ( TCPHandler_t *th ) +{ + DASSERT(th); + + th->need_maintenance = 0; + const u64 now_usec = GetTimeUSec(false); + TCPStream_t *next = th->first; + while (next) + { + // Do it in this way, because 'ts' may becomes invalid + TCPStream_t *ts = next; + next = ts->next; + + if ( ts->sock == -1 && ts->protect <= 0 ) + DestroyTCPStream(ts); + else if (ts->OnMaintenance) + ts->OnMaintenance(ts,now_usec); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ManageSocketsTCP +( + // call CheckSocketsTCP(), CheckTimeoutTCP(), MaintenanceTCP() + + TCPHandler_t *th, // valid TCP handler + FDList_t *fdl, // valid socket list + int stat // result of a WAIT function +) +{ + if ( stat > 0 ) + CheckSocketsTCP(th,fdl,true); + else + CheckTimeoutTCP(th); + + if (th->need_maintenance) + MaintenanceTCP(th); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void LogTCPHandler +( + FILE *f, // output file + int indent, // indention + const TCPHandler_t *th, // valid TCP handler + int recurse, // >0: print stream list, >1: print buffer status + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(format,...)' +) +{ + DASSERT(f); + DASSERT(th); + indent = NormalizeIndent(indent); + + fprintf(f,"%*sTH: id:%x, n=%u/%u/%u, listen=", + indent,"", th->unique_id, + th->used_streams, th->max_used_streams, th->total_streams ); + + uint i; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + fprintf(f,"%d,",th->listen[i].sock); + + fprintf(f," max=%u, dsize=%u, ready=%u", + th->max_conn, th->data_size, NumOfSocketsTCP(th) ); + + if (format) + { + fputs(" : ",f); + va_list arg; + va_start(arg,format); + vfprintf(f,format,arg); + va_end(arg); + } + + fprintf(f,"\n%*s %u connects, %u packets received (%s), %u packets send (%s)\n", + indent,"", + th->stat.conn_count, + th->stat.recv_count, PrintSize1024(0,0,th->stat.recv_size,false), + th->stat.send_count, PrintSize1024(0,0,th->stat.send_size,false) ); + + if (recurse>0) + { + recurse--; + uint idx = 0; + const TCPStream_t *ts; + for ( ts = th->first; ts; ts = ts->next ) + LogTCPStream(f,indent+2,ts,recurse,"Stream #%u",idx++); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintStreamTableTCPHandler +( + FILE *f, // output file + const ColorSet_t *colset, // NULL (no colors) or valid color set + int indent, // indention + const TCPHandler_t *th // valid TCP handler +) +{ + DASSERT(f); + DASSERT(th); + indent = NormalizeIndent(indent); + if (!colset) + colset = GetColorSet0(); + + const u64 now_usec = GetTimeUSec(false); + + const TCPStream_t *ts; + ccp line1 = BufInfoHeadTCPStream(0); + ccp line2 = BufInfoHeadTCPStream(1); + const uint sep_len = strlen(line2)+sizeof(ts->info)-13; + + fprintf(f,"%s%*s%.*s\n%s%*s%s\n%s%*s%s\n%s%*s%.*s%s\n", + colset->heading, indent,"", sep_len, Minus300, + colset->heading, indent,"", line1, + colset->heading, indent,"", line2, + colset->heading, indent,"", sep_len, Minus300, + colset->reset ); + + char buf[BUF_INFO_TCP_STREAM_SIZE]; + for ( ts = th->first; ts; ts = ts->next ) + { + BufInfoTCPStream(buf,BUF_INFO_TCP_STREAM_SIZE,ts,now_usec); + fprintf(f,"%*s%s\n",indent,"",buf); + } + + + if (th->used_streams) + fprintf(f, + "%s%*s%.*s%s\n", + colset->heading, indent,"", sep_len, Minus300, + colset->reset ); + + if ( th->total_streams > 1 || th->total_streams > th->used_streams ) + { + snprintf(buf,sizeof(buf),"TOTAL: %u current client%s, %u max", + th->used_streams, th->used_streams == 1 ? "" : "s", + th->max_used_streams ); + + fprintf(f, + "%s%*s%-45s%s %s%13s %s Summary of %u stream%s\n%s%*s%.*s%s\n", + colset->status, indent,"", buf, + PrintNumberU7(0,0,th->stat.recv_count,true), + PrintSize1024(0,0,th->stat.recv_size,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintNumberU7(0,0,th->stat.send_count,true), + PrintSize1024(0,0,th->stat.send_size,DC_SFORM_TINY|DC_SFORM_ALIGN), + th->total_streams, th->total_streams == 1 ? "" : "s", + colset->heading, indent,"", sep_len,Minus300, + colset->reset ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool GetSocketInfoBySocket +( + // returns TRUE, if infos retrived + + socket_info_t *si, // result, not NULL, will be initialized + int sock, // socket id, maybe -1 + bool get_name // true: alloc 'address' and copy name + // -> call ResetSocketInfo() to free mem +) +{ + DASSERT(si); + InitializeSocketInfo(si); + if ( sock == -1 ) + return false; + si->sock = sock; + + DASSERT( sizeof(struct sockaddr_un) >= sizeof(struct sockaddr_in) ); + DASSERT( sizeof(struct sockaddr_un) >= sizeof(struct sockaddr_in6) ); + + struct sockaddr_un sa; + memset(&sa,0,sizeof(sa)); + socklen_t sa_len = sizeof(sa); + if (getsockname(sock,(struct sockaddr*)&sa,&sa_len)) + return false; + + char buf[INET6_ADDRSTRLEN+10]; + switch (sa.sun_family) + { + case AF_UNIX: + si->protocol = AF_UNIX; + if ( get_name && sa.sun_path[0] ) + { + si->address = STRDUP(sa.sun_path); + si->alloced = true; + } + break; + + case AF_INET: + si->protocol = AF_INET; + if (get_name) + { + const struct sockaddr_in *sin = (struct sockaddr_in*)&sa; + PrintIP4sa(buf,sizeof(buf),sin); + si->address = STRDUP(buf); + si->alloced = true; + } + break; + + case AF_INET6: + si->protocol = AF_INET6; + if (get_name) + { + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&sa; + if (inet_ntop(AF_INET6,&sin6->sin6_addr,buf+1,sizeof(buf)-1)) + { + *buf = '['; + char *dest = buf + strlen(buf); + snprintf(dest,buf+sizeof(buf)-dest,"]:%u",ntohs(sin6->sin6_port)); + si->address = STRDUP(buf); + si->alloced = true; + } + } + break; + + default: + return false; + } + + int socktype = -1; + socklen_t psize = sizeof(socktype); + if (!getsockopt(sock,SOL_SOCKET,SO_TYPE,&socktype,&psize)) + si->type = socktype; + + return si->is_valid = true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint GetSocketNameBySA +( + char *buf, // dest buffer + uint bufsize, // size of 'dest' + const struct sockaddr *sa, // socket address + socklen_t sa_len // length of 'sa' +) +{ + DASSERT(buf); + DASSERT(bufsize>0); + DASSERT(sa); + DASSERT(sa_len>0); + + if ( sa_len >= sizeof(struct sockaddr_un) ) + { + const struct sockaddr_un *sun = (struct sockaddr_un*)sa; + if ( sun->sun_family == AF_UNIX && sun->sun_path[0] ) + return snprintfS(buf,bufsize,"unix:%s",sun->sun_path); + } + + if ( sa_len >= sizeof(struct sockaddr_in) ) + { + const struct sockaddr_in *sin = (struct sockaddr_in*)sa; + if ( sin->sin_family == AF_INET && bufsize > 5 ) + { + strcpy(buf,"tcp:"); + return PrintIP4sa(buf+4,bufsize-4,sin) - buf; + } + } + + if ( sa_len >= sizeof(struct sockaddr_in6) ) + { + const struct sockaddr_in6 *sin = (struct sockaddr_in6*)sa; + if ( sin->sin6_family == AF_INET6 && bufsize >= 5 + INET6_ADDRSTRLEN ) + { + // [[2do]] ??? "[addr]:port" + strcpy(buf,"tcp6:"); + if (inet_ntop(AF_INET6,&sin->sin6_addr,buf+5,bufsize-5)) + return strlen(buf); + } + } + + *buf = 0; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetSocketName ( char *buf, uint bufsize, int sock ) +{ + DASSERT(buf); + DASSERT(bufsize>0); + + if ( sock != -1 ) + { + DASSERT( sizeof(struct sockaddr_un) >= sizeof(struct sockaddr_in) ); + DASSERT( sizeof(struct sockaddr_un) >= sizeof(struct sockaddr_in6) ); + struct sockaddr_un sun; + memset(&sun,0,sizeof(sun)); + socklen_t sun_len = sizeof(sun); + if (!getsockname(sock,(struct sockaddr*)&sun,&sun_len)) + return GetSocketNameBySA(buf,bufsize,(struct sockaddr*)&sun,sun_len); + } + *buf = 0; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetPeerName ( char *buf, uint bufsize, int sock ) +{ + DASSERT(buf); + DASSERT(bufsize>0); + + if ( sock != -1 ) + { + DASSERT( sizeof(struct sockaddr_un) >= sizeof(struct sockaddr_in) ); + struct sockaddr_un sun; + memset(&sun,0,sizeof(sun)); + socklen_t sun_len = sizeof(sun); + if (!getpeername(sock,(struct sockaddr*)&sun,&sun_len)) + return GetSocketNameBySA(buf,bufsize,(struct sockaddr*)&sun,sun_len); + } + *buf = 0; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool DeleteSocketFile ( int sock ) +{ + if ( sock != -1 ) + { + DASSERT( sizeof(struct sockaddr_un) >= sizeof(struct sockaddr_in) ); + struct sockaddr_un sun; + memset(&sun,0,sizeof(sun)); + socklen_t sun_len = sizeof(sun); + if (!getsockname(sock,(struct sockaddr*)&sun,&sun_len)) + { + if ( sun.sun_family == AF_UNIX ) + { + unlink(sun.sun_path); + return true; + } + } + } + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCP: send single strings /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int OnTimeoutSingleTCP ( struct TCPStream_t * ts, u64 now_usec ) +{ + PRINT("OnTimeoutSingleTCP(%p,%llu) sock=%d, to=%lld,%lld d=%lld,%lld\n", + ts, now_usec, ts->sock, + ts->trigger_usec, ts->trigger_usec - now_usec, + ts->accept_usec, ts->accept_usec - now_usec ); + DASSERT(ts); + OnCloseStream(ts,now_usec); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int OnReceivedSingleTCP ( struct TCPStream_t * ts, u8 *buf, uint size ) +{ + PRINT("OnReceivedSingleTCP(%p,%p,%u) to=%lld\n",ts,buf,size,*(u64*)ts->data); + DASSERT(ts); + ts->accept_usec = GetTimeUSec(false) + *(u64*)ts->data; + ts->trigger_usec = ts->accept_usec + 100000; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int OnSendSingleTCP ( struct TCPStream_t * ts, u8 *buf, uint size ) +{ + PRINT("OnSendSingleTCP(%p,%p,%u) to=%lld\n",ts,buf,size,*(u64*)ts->data); + DASSERT(ts); + ts->accept_usec = GetTimeUSec(false) + *(u64*)ts->data; + ts->trigger_usec = ts->accept_usec + 100000; + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +static TCPStream_t * SendSingleTCPHelper +( + TCPHandler_t *th, // valid handle + int sock, // valid socket + + const void *data, // data to send + uint size, // size of 'data' + u64 timeout_usec, // timeout before closing the connection + TransferStats_t *xstat, // NULL or pointer to summary statistics + + bool silent // suppress error messages +) +{ + DASSERT(th); + DASSERT(sock!=-1); + DASSERT(data||!size); + + PRINT("SendSingleStringTCPHelper(%p,%d,%lld,%d)\n",th,sock,timeout_usec,silent); + + th->stat.conn_count++; + TCPStream_t *ts = CALLOC(sizeof(*ts)+sizeof(u64),1); + InitializeTCPStream(ts,sock); + AddTCPStream(th,ts); + + ts->xstat = xstat; + ts->OnTimeout = OnTimeoutSingleTCP; + ts->OnReceived = OnReceivedSingleTCP; + ts->OnSend = OnSendSingleTCP; + ts->accept_usec = GetTimeUSec(false) + timeout_usec; + ts->trigger_usec = ts->accept_usec + 100000; + *(u64*)ts->data = timeout_usec; + snprintf(ts->info,sizeof(ts->info),"send single, %u bytes",size); + + #if 1 + ts->obuf.max_size = size > 10240 ? size : 10240; + SendDirectTCPStream(ts,false,data,size); + #else + ts->obuf.max_size = size; + InsertGrowBuffer(&ts->obuf,data,size); + #endif + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * SendSingleUnixTCP +( + TCPHandler_t *th, // valid handle + ccp path, // path to socket file + + const void *data, // data to send + uint size, // size of 'data' + u64 timeout_usec, // timeout before closing the connection + TransferStats_t *xstat, // NULL or pointer to summary statistics + + bool silent // suppress error messages +) +{ + DASSERT(th); + DASSERT(path); + DASSERT(data||!size); + + PRINT("SINGLE/CONNECT/UNIX: %s\n",path); + + int sock = socket(AF_UNIX,SOCK_STREAM,0); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CREATE,"Can't create UNIX socket: %s\n",path); + return 0; + } + + struct sockaddr_un sa; + memset(&sa,0,sizeof(sa)); + sa.sun_family = AF_UNIX; + StringCopyS(sa.sun_path,sizeof(sa.sun_path),path); + + int stat = connect(sock,(struct sockaddr*)&sa,sizeof(sa)); + if (stat) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't connect UNIX socket: %s\n",path); + shutdown(sock,SHUT_RDWR); + close(sock); + return 0; + } + + return SendSingleTCPHelper(th,sock,data,size,timeout_usec,xstat,silent); +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * SendSingleTCP +( + TCPHandler_t *th, // valid handle + ccp addr, // address -> NetworkHost_t + u16 default_port, // default port + + const void *data, // data to send + uint size, // size of 'data' + u64 timeout_usec, // timeout before closing the connection + TransferStats_t *xstat, // NULL or pointer to summary statistics + + bool silent // suppress error messages +) +{ + DASSERT(th); + DASSERT(addr); + DASSERT(data||!size); + + ccp unix_path = CheckUnixSocketPath(addr,0); + if (unix_path) + return SendSingleUnixTCP(th,unix_path,data,size,timeout_usec,xstat,silent); + + if (!strncasecmp(addr,"tcp:",4)) + addr += 4; + + NetworkHost_t nh; + ResolveHost(&nh,true,addr,default_port,false,false); + PRINT("SINGLE/CONNECT/TCP: %s -> %s\n", + addr, PrintIP4(0,0,nh.ip4,nh.port) ); + + int sock = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0); + if ( sock == -1 ) + { + if (!silent) + ERROR1(ERR_CANT_CREATE,"Can't create TCP socket: %s\n", + PrintIP4(0,0,nh.ip4,nh.port)); + return 0; + } + + struct sockaddr_in sa; + memset(&sa,0,sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(nh.ip4); + sa.sin_port = htons(nh.port); + + int stat = connect(sock,(struct sockaddr*)&sa,sizeof(sa)); + if ( stat && errno != EINPROGRESS ) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't connect TCP socket: %s\n", + PrintIP4(0,0,nh.ip4,nh.port)); + shutdown(sock,SHUT_RDWR); + close(sock); + ResetHost(&nh); + return 0; + } + + TCPStream_t *ts = SendSingleTCPHelper(th,sock,data,size,timeout_usec,xstat,silent); + ResetHost(&nh); + return ts; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCP: CommandTCP /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetTimeoutCommandTCP +( + TCPStream_t *ts, // valid stream + u64 now_usec // NULL or current time in usec +) +{ + DASSERT(ts); + + const CommandTCPInfo_t *ci = (CommandTCPInfo_t*)ts->data; + if (!ci->timeout_usec) + ts->accept_usec = ts->trigger_usec = 0; + else + { + if (!now_usec) + now_usec = GetTimeUSec(false); + ts->accept_usec = now_usec + ci->timeout_usec; + ts->trigger_usec = ts->accept_usec + 100000; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static char * ScanLineCommandTCP +( + // return first non scanned character, or NULL on error + + struct TCPStream_t *ts, + char *line, + char *line_end, + u64 now_usec +) +{ + DASSERT(ts); + DASSERT(line); + DASSERT(line_end); + DASSERT( line <= line_end ); + + *line_end = 0; + const CommandTCPInfo_t *ci = (CommandTCPInfo_t*)ts->data; + + if (ci->OnScanArg) + { + SplitArg_t sa; + ScanSplitArg(&sa,true,line,line_end,line); + sa.argv[0] = ts->info; + const int stat = ci->OnScanArg(ts,sa.argc,sa.argv,now_usec); + ResetSplitArg(&sa); + return stat > ERR_WARNING ? line : line_end; + } + + if (ci->OnScanLine) + return ci->OnScanLine(ts,line,line_end,now_usec); + + //--- fall back: mirror + InsertGrowBuffer(&ts->obuf,line,line_end-line); + InsertCharGrowBuffer(&ts->obuf,'\n'); + return line_end; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ScanBufferCommandTCP ( struct TCPStream_t * ts, bool finish ) +{ + DASSERT(ts); + + //--- setup + + const u64 now_usec = GetTimeUSec(false); + + const CommandTCPInfo_t *ci = (CommandTCPInfo_t*)ts->data; + const u8 comma = ci->comma_is_eol ? ',' : ';'; + + + //--- skip uneeded data + + u8 *beg = ts->ibuf.ptr, *end = beg + ts->ibuf.used; + while ( beg < end ) + { + const u8 ch = *beg; + if ( ch > ' ' && ch != ';' && ch != comma ) + break; + beg++; + } + DropGrowBuffer(&ts->ibuf,beg-ts->ibuf.ptr); + + + //--- find end of command + + u8 *ptr; + for ( ptr = beg; ptr < end; ptr++ ) + { + const u8 ch = *ptr; + if ( !ch || ch == '\n' || ch == '\r' || ch == ';' || ch == comma ) + { + DropGrowBuffer(&ts->ibuf,ptr+1-beg); + ScanLineCommandTCP(ts,(char*)beg,(char*)ptr,now_usec); + return; + } + } + + if ( ptr > beg && ( finish || ts->eof ) ) + { + DropGrowBuffer(&ts->ibuf,ptr-beg); + ScanLineCommandTCP(ts,(char*)beg,(char*)ptr,now_usec); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static int OnCommandTCP ( struct TCPStream_t * ts ) +{ + DASSERT(ts); + SetTimeoutCommandTCP(ts,0); + + while ( ts->sock != -1 && !ts->obuf.used ) + { + const uint watermark = ts->ibuf.used; + ScanBufferCommandTCP(ts,false); + if ( watermark == ts->ibuf.used ) + break; + } + + if ( ts->eof && !ts->obuf.used && !ts->ibuf.used ) + OnCloseStream(ts,0); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int OnReceivedCommandTCP ( struct TCPStream_t * ts, u8 *buf, uint size ) +{ + DASSERT(ts); + noPRINT("---------- OnReceivedCommandTCP(%p,%p,%u), used=%d,%d\n", + ts, buf, size, ts->ibuf.used, ts->obuf.used ); + InsertGrowBuffer(&ts->ibuf,buf,size); + return OnCommandTCP(ts); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int OnSendCommandTCP ( struct TCPStream_t * ts, u8 *buf, uint size ) +{ + DASSERT(ts); + noPRINT("---------- OnSendCommandTCP(%p,%p,%u), used=%d,%d\n", + ts, buf, size, ts->ibuf.used, ts->obuf.used ); + + DropGrowBuffer(&ts->obuf,size); + return OnCommandTCP(ts); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int OnTimeoutCommandTCP ( struct TCPStream_t * ts, u64 now_usec ) +{ + DASSERT(ts); + noPRINT("---------- OnTimeoutCommandTCP(%p,%llu)\n",ts,now_usec); + + if ( ts->sock != -1 ) + { + if (!ts->obuf.used) + { + ScanBufferCommandTCP(ts,true); + if (ts->obuf.used) + { + SetTimeoutCommandTCP(ts,now_usec); + return 0; + } + } + + OnCloseStream(ts,now_usec); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int OnCreateCommandTCP ( TCPStream_t *ts ) +{ + noPRINT("---------- OnCreateCommandTCP(%p)\n",ts); + DASSERT(ts); + ts->OnReceived = OnReceivedCommandTCP; + ts->OnSend = OnSendCommandTCP; + ts->OnTimeout = OnTimeoutCommandTCP; + + CommandTCPInfo_t *ci = (CommandTCPInfo_t*)ts->data; + ci->OnScanLine = 0; + ci->timeout_usec = 10*USEC_PER_SEC; + ci->comma_is_eol = false; + + SetTimeoutCommandTCP(ts,0); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveCurrentState*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateSocket +( + FILE *f, // output file + const Socket_t *sock // valid socket object +) +{ + DASSERT(f); + DASSERT(sock); + + char info[3*sizeof(sock->info)]; + PrintEscapedString(info,sizeof(info),sock->info,-1,CHMD__ALL,0,0); + + fprintf(f, + "sock = %d\n" + "is-unix = %d\n" + "info = %s\n" + ,sock->sock + ,sock->is_unix + ,info + ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateTCPStream +( + FILE *f, // output file + const TCPStream_t *ts, // valid TCP stream + TCPStreamSaveFunc func // NULL or function for 'TCPStream_t.data' extend +) +{ + DASSERT(f); + DASSERT(ts); + + char info[1000]; + PrintEscapedString(info,sizeof(info),ts->info,-1,CHMD__ALL,0,0); + + fprintf(f, + "sock = %d\n" + "rescan = %u\n" + "eof = %u\n" + "protect = %u\n" + "auto-close = %u\n" + "accept-usec = %llu\n" + "trigger-usec = %llu\n" + "delay-usec = %llu\n" + "timeout-usec = %llu\n" + "connect-usec = %llu\n" + "receive-usec = %llu\n" + "send-usec = %llu\n" + "info = %s\n" + ,ts->sock + ,ts->rescan + ,ts->eof + ,ts->protect + ,ts->auto_close + ,ts->accept_usec + ,ts->trigger_usec + ,ts->delay_usec + ,ts->timeout_usec + ,ts->connect_usec + ,ts->receive_usec + ,ts->send_usec + ,info + ); + + if (func) + func(f,ts); + + SaveCurrentStateTransferStats1(f,"tfer-stat",&ts->stat); + SaveCurrentStateGrowBuffer(f,"ibuf-",2,&ts->ibuf); + SaveCurrentStateGrowBuffer(f,"obuf-",2,&ts->obuf); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateCommandTCP +( + FILE *f, // output file + const TCPStream_t *ts // valid TCP stream +) +{ + DASSERT(f); + DASSERT(ts); + + const CommandTCPInfo_t *ci = (CommandTCPInfo_t*)ts->data; + + fprintf(f, + "cmd-comma = %d\n" + "cmd-timeout = %llu\n" + ,ci->comma_is_eol + ,ci->timeout_usec + ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateTCPHandler +( + FILE *f, // output file + ccp sect_name, // section base name + const TCPHandler_t *th, // valid TCP handler + TCPStreamSaveFunc func // NULL or function for 'TCPStream_t.data' extend +) +{ + DASSERT(f); + DASSERT(sect_name); + DASSERT(th); + + fprintf(f, + "\n#%.50s\n" + "[%s]\n" + "\n" + "data-size = %u\n" + "max-conn = %u\n" + "maintenance = %u\n" + "used-streams = %u\n" + "max-used = %u\n" + "total-streams = %u\n" + ,Minus300 + ,sect_name + ,th->data_size + ,th->max_conn + ,th->need_maintenance + ,th->used_streams + ,th->max_used_streams + ,th->total_streams + ); + + SaveCurrentStateTransferStats1(f,"tfer-stat",&th->stat); + + uint i; + for ( i = 0; i < TCP_HANDLER_MAX_LISTEN; i++ ) + { + if ( th->listen[i].sock != -1 ) + { + fprintf(f,"\n[%s/socket:%u]\n\n",sect_name,i); + SaveCurrentStateSocket(f,th->listen+i); + } + } + + const TCPStream_t *ts; + for ( ts = th->first, i = 0; ts; ts = ts->next, i++ ) + { + fprintf(f,"\n[%s/stream:%u]\n\n",sect_name,i); + SaveCurrentStateTCPStream(f,ts,func); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// RestoreState*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateSocket +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +) +{ + DASSERT(rs); + + Socket_t *sock = (Socket_t*)user_table; + if (!sock) + return; + + int fd = GetParamFieldINT(rs,"sock",-1); + if ( fd != -1 && fcntl(fd,F_GETFD) != -1 ) + { + sock->sock = fd; + sock->is_unix = GetParamFieldINT(rs,"is-unix",0); + GetParamFieldBUF(sock->info,sizeof(sock->info),rs,"info",ENCODE_STRING,"?"); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * RestoreStateTCPStream +( + // return 'ts' or the new TCPStream_t + RestoreState_t *rs, // info data, can be modified (cleaned after call) + TCPStream_t *ts, // if NULL: create it + uint extra_size // if 'ts' alloced: alloc some byte more +) +{ + DASSERT(rs); + TRACE("RestoreStateTCPStream()\n"); + + int fd = ts ? ts->sock : -1; + if ( fd == -1 ) + { + fd = GetParamFieldINT(rs,"sock",-1); + if ( fd != -1 && fcntl(fd,F_GETFD) == -1 ) + fd = -1; + } + + if (!ts) + { + ts = CALLOC(sizeof(*ts)+extra_size,1); + InitializeTCPStream(ts,fd); + } + else + ts->sock = fd; + + DASSERT(ts); + ts->protect = GetParamFieldUINT(rs, "protect", ts->protect); + ts->auto_close = GetParamFieldUINT(rs, "auto-close", ts->auto_close); + ts->rescan = GetParamFieldUINT(rs, "rescan", ts->rescan ); + ts->eof = GetParamFieldUINT(rs, "eof", ts->eof ); + ts->accept_usec = GetParamFieldU64 (rs, "accept-usec", ts->accept_usec ); + ts->trigger_usec = GetParamFieldU64 (rs, "trigger-usec", ts->trigger_usec ); + ts->delay_usec = GetParamFieldU64 (rs, "delay-usec", ts->delay_usec ); + ts->timeout_usec = GetParamFieldU64 (rs, "timeout-usec", ts->timeout_usec ); + ts->connect_usec = GetParamFieldU64 (rs, "connect-usec", ts->connect_usec ); + ts->receive_usec = GetParamFieldU64 (rs, "receive-usec", ts->receive_usec ); + ts->send_usec = GetParamFieldU64 (rs, "send-usec", ts->send_usec ); + + GetParamFieldBUF(ts->info,sizeof(ts->info),rs,"info",ENCODE_STRING,"?"); + + RestoreStateTransferStats1(rs,"tfer-stat",&ts->stat,true); + RestoreStateGrowBuffer(&ts->ibuf,"ibuf-",rs); + RestoreStateGrowBuffer(&ts->obuf,"obuf-",rs); + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateTCPHandler_base +( + TCPHandler_t *th, // valid TCPHandler_t + RestoreState_t *rs // info data, can be modified (cleaned after call) +) +{ + DASSERT(th); + DASSERT(rs); + TRACE("RestoreStateTCPHandler_base()\n"); + + //ResetTCPHandler(th); // don't call, because call back functions are cleared + + struct uint_parm_t + { + ccp name; + uint *param; + }; + + const struct uint_parm_t uint_parm_tab[] = + { + { "max-conn", &th->max_conn }, + { "maintenance", &th->need_maintenance }, + { "max-used", &th->max_used_streams }, + { "total-streams", &th->total_streams }, + {0,0} + }; + + const struct uint_parm_t *up; + for ( up = uint_parm_tab; up->name; up++ ) + *up->param = GetParamFieldUINT(rs,up->name,*up->param); + + RestoreStateTransferStats1(rs,"tfer-stat",&th->stat,true); + + if ( rs->log_mode & RSL_UNUSED_NAMES ) + { + // known as 'unused' + GetParamField(rs,"data-size"); + GetParamField(rs,"used-streams"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +Socket_t * RestoreStateTCPHandler_socket +( + TCPHandler_t *th, // valid TCPHandler_t + RestoreState_t *rs // info data, can be modified (cleaned after call) +) +{ + DASSERT(th); + DASSERT(rs); + TRACE("RestoreStateTCPHandler_socket()\n"); + + Socket_t *sock = GetUnusedListenSocketTCP(th,false); + if (sock) + RestoreStateSocket(rs,sock); + return sock; +} + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * RestoreStateTCPHandler_stream +( + TCPHandler_t *th, // valid TCPHandler_t + RestoreState_t *rs // info data, can be modified (cleaned after call) +) +{ + DASSERT(th); + DASSERT(rs); + TRACE("RestoreStateTCPHandler_stream()\n"); + + int fd = GetParamFieldINT(rs,"sock",-1); + bool protect = GetParamFieldUINT(rs,"protect",0); + #ifdef TEST + if ( !protect && fd == -1 ) + return 0; + #else + if ( !protect && ( fd == -1 || fcntl(fd,F_GETFD) == -1 )) + return 0; + #endif + + TCPStream_t *ts = CreateTCPStream(th,fd,ALLOW_MODE_ALLOW,0); + RestoreStateTCPStream(rs,ts,th->data_size); + return ts; +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateTCPHandler +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +) +{ + DASSERT(rs); + TRACE("Scan TCP Handler (%s/%s,%d)\n",rs->sect,rs->path,rs->index); + + TCPHandler_t *th = (TCPHandler_t*)user_table; + if (!th) + return; + + if (!*rs->path) + RestoreStateTCPHandler_base(th,rs); + else if (!strcmp(rs->path,"socket")) + RestoreStateTCPHandler_socket(th,rs); + else if (!strcmp(rs->path,"stream")) + RestoreStateTCPHandler_stream(th,rs); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateCommandTCP +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +) +{ + DASSERT(rs); + + TCPHandler_t *th = (TCPHandler_t*)user_table; + if (!th) + return; + + if (!strcmp(rs->path,"stream")) + { + TCPStream_t *ts = RestoreStateTCPHandler_stream(th,rs); + if (ts) + { + CommandTCPInfo_t *ci = (CommandTCPInfo_t*)ts->data; + ci->comma_is_eol = GetParamFieldINT( rs, "cmd-comma", ci->comma_is_eol ) > 0; + ci->timeout_usec = GetParamFieldU64( rs, "cmd-timeout", ci->timeout_usec ); + } + } + else + RestoreStateTCPHandler(rs,user_table); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool fix_ether_head_vlan ( ether_head_vlan_t *head ) +{ + DASSERT(head); + const u16 vlan_tag_type = ntohs(head->vlan_tag_type); + if ( vlan_tag_type == 0x8100 ) + { + head->have_vlan = true; + head->head_size = 18; + return true; + } + + head->ether_type = head->vlan_tag_type; + head->have_vlan = false; + head->head_size = 14; + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_ether_head_vlan ( ether_head_vlan_t *dest, const void * src ) +{ + if (dest) + { + if (src) + memcpy(dest,src,sizeof(*dest)); + + fix_ether_head_vlan(dest); + dest->vlan_tag_type = ntohs(dest->vlan_tag_type); + dest->vlan_param = ntohs(dest->vlan_param); + dest->ether_type = ntohs(dest->ether_type); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_ether_head ( ether_head_t *dest, const void * src ) +{ + if (dest) + { + if (src) + memcpy(dest,src,sizeof(*dest)); + + dest->ether_type = ntohs(dest->ether_type); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_ip4_head ( ip4_head_t *dest, const void * src ) +{ + if (dest) + { + if (src) + memcpy(dest,src,sizeof(*dest)); + + dest->total_len = ntohs(dest->total_len); + dest->id = ntohs(dest->id); + dest->frag_off = ntohs(dest->frag_off); + dest->checksum = ntohs(dest->checksum); + dest->ip_src = ntohl(dest->ip_src); + dest->ip_dest = ntohl(dest->ip_dest); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_udp_head ( udp_head_t *dest, const void * src ) +{ + if (dest) + { + const udp_head_t *usrc = src ? src : dest; + + dest->port_src = ntohs(usrc->port_src); + dest->port_dest = ntohs(usrc->port_dest); + dest->data_len = ntohs(usrc->data_len); + dest->checksum = ntohs(usrc->checksum); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_tcp_head ( tcp_head_t *dest, const void * src ) +{ + if (dest) + { + const tcp_head_t *usrc = src ? src : dest; + + dest->port_src = ntohs(usrc->port_src); + dest->port_dest = ntohs(usrc->port_dest); + dest->seq_num = ntohl(usrc->seq_num); + dest->acc_num = ntohl(usrc->acc_num); + dest->data_off = usrc->data_off; + dest->flags = usrc->flags; + dest->window = ntohs(usrc->window); + dest->checksum = ntohs(usrc->checksum); + dest->urgent = ntohs(usrc->urgent); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Linux support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(SYSTEM_LINUX) || defined(__CYGWIN__) + #include "dclib-network-linux.c" +#endif // SYSTEM_LINUX + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-network.h b/project/dclib/dclib-network.h new file mode 100644 index 0000000..b533761 --- /dev/null +++ b/project/dclib/dclib-network.h @@ -0,0 +1,1486 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_NETWORK_H +#define DCLIB_NETWORK_H 1 + +#include "dclib-debug.h" +#include "dclib-system.h" +#include "dclib-basics.h" +#include "dclib-file.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// host name support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[NetworkHost_t]] + +typedef struct NetworkHost_t +{ + bool ip4_valid; // true: 'ip4' is valid + u32 ip4; // ip4 or 0 if invalid + int port; // port number, <0: invalid + ccp name; // NULL or pointer to canonicalized name + struct sockaddr_in sa; // setup for AF_INET if 'ip4_valid' + + ccp filename; // not NULL: unix filename detected + // in this case: ip4_valid := FALSE + mem_t not_scanned; // (0,0) or not by ResolveHost*() scanned name +} +NetworkHost_t; + +//----------------------------------------------------------------------------- + +static inline void InitializeHost ( NetworkHost_t *host ) + { memset(host,0,sizeof(*host)); } + +static inline void ResetHost ( NetworkHost_t *host ) + { FREE((char*)host->name); memset(host,0,sizeof(*host)); } + +static inline ccp GetHostName ( NetworkHost_t *host ) + { return host->name ? host->name : PrintIP4(0,0,host->ip4,-1); } + +//----------------------------------------------------------------------------- + +bool ResolveHost +( + // returns TRUE (=ip4_valid), if a hostname is detected + // if result is FALSE => check host->filename + + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool init_host, // true: initialize 'host' + ccp name, // name to analyze + int default_port, // use this as port, if no other is found + bool check_filename, // true: check for unix filename + bool store_name // true: setup 'host->name' +); + +bool ResolveHostMem +( + // returns TRUE (=ip4_valid), if a hostname is detected + // if result is FALSE => check host->filename + + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool init_host, // true: initialize 'host' + mem_t name, // name to analyze + int default_port, // use this as port, if no other is found + bool check_filename, // true: check for unix filename + bool store_name // true: setup 'host->name' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct AllowIP4_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// standard values +#define ALLOW_MODE_DENY 0 // access denied +#define ALLOW_MODE_ALLOW 0x001 // access allowed +#define ALLOW_MODE_AKEY 0x002 // access allowed by access key + +// locations +#define ALLOW_MODE_EXTERN 0x004 // keyword 'EXTERN' set +#define ALLOW_MODE_LAN 0x008 // keyword 'LAN' set +#define ALLOW_MODE_LOCAL 0x010 // keyword 'LOCAL' set + +// users +#define ALLOW_MODE_PUBLIC 0x020 // keyword 'PUBLIC' set +#define ALLOW_MODE_USER 0x040 // keyword 'USER' set +#define ALLOW_MODE_MOD 0x080 // keyword 'MOD' or 'MODERATOR' set +#define ALLOW_MODE_ADMIN 0x100 // keyword 'ADMIN' or 'ADMINISTRATOR' set +#define ALLOW_MODE_DEVELOP 0x200 // keyword 'DEVELOP' or 'DEVELOPER' set + +// logging +#define ALLOW_MODE_LOG 0x400 // keyword 'LOG' set +#define ALLOW_MODE_VERBOSE 0x800 // keyword 'VERBOSE' set + +// special processing values +#define ALLOW_MODE_NOT 0x1000000000000000ull // negate bits +#define ALLOW_MODE_SET 0x2000000000000000ull // set bits, ignore AND +#define ALLOW_MODE_AND 0x4000000000000000ull // use AND operation (otherwise OR) +#define ALLOW_MODE_CONTINUE 0x8000000000000000ull // continue search (otherwise break) + +#define ALLOW_MODE__OP 0x6000000000000000ull // mask for operation +#define ALLOW_MODE__MASK 0x0fffffffffffffffull // mask out control bits + +//----------------------------------------------------------------------------- + +extern const KeywordTab_t AllowIP4KeyTable[]; + +//----------------------------------------------------------------------------- +// [[AllowIP4Item_t]] + +typedef struct AllowIP4Item_t +{ + u32 addr; // IP address + u32 mask; // netmask + u64 mode; // new mode, see ALLOW_MODE_* + u64 count; // incremented on IP match +} +AllowIP4Item_t; + +//----------------------------------------------------------------------------- +// [[AllowIP4_t]] + +typedef struct AllowIP4_t +{ + AllowIP4Item_t *list; // list of addresses, alloced + uint used; // number of used elements + uint size; // number of alloced elements + + int ref_counter; // reference counter + + // for automatic usage + u64 fallback_mode; // mode if IP not found + u64 allow_mode; // allow, if any of these bits is set +} +AllowIP4_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeAllowIP4 ( AllowIP4_t *ai ); +void ResetAllowIP4 ( AllowIP4_t *ai ); +void ClearAllowIP4 ( AllowIP4_t *ai ); + +// if ai==NULL: create a new ai +AllowIP4_t * NewReferenceAllowIP4 ( AllowIP4_t *ai ); + +//returns always NULL +AllowIP4_t * DeleteReferenceAllowIP4 ( AllowIP4_t *ai ); + +void DumpAllowIP4 +( + FILE *f, // NULL or output file + int indent, // indention + const AllowIP4_t *ai, // NULL or source + const KeywordTab_t *keytab, // NULL or key table for output + ccp title, // not NULL: Add title + bool print_tab_head // true: add table headings and separators +); + +//----------------------------------------------------------------------------- + +enumError ScanLineAllowIP4 +( + AllowIP4_t *ai, // valid structure; new elements are appended + mem_t line, // single line to analyse + const KeywordTab_t *tab // NULL or keyword table. + // If NULL, a local table with keywords 0 DENY + // ALLOW SET REMOVE AND OR and CONTINUE is used. +); + +enumError ScanFileAllowIP4 +( + AllowIP4_t *ai, // valid structure; new elements are appended + ccp fname, // ffilename of the source + FileMode_t fmode, // flags for OpenFile() + const KeywordTab_t *tab // NULL or keyword table -> see ScanLineAllowIP4() +); + +//----------------------------------------------------------------------------- + +void ResetCountersAllowIP4 +( + AllowIP4_t *ai // NULL or dest for addition +); + +void AddCountersAllowIP4 +( + AllowIP4_t *dest, // NULL or dest for addition + const AllowIP4_t *src // NULL or source for addition +); + +//----------------------------------------------------------------------------- + +// if found, result is masked by ALLOWIP4_MODE__MASK +u64 GetAllowIP4ByAddr ( const AllowIP4_t *ai, u32 addr, u64 if_not_found ); +u64 GetAllowIP4ByName ( const AllowIP4_t *ai, mem_t name, u64 if_not_found ); + +// if ai==NULL => return ALLOW_MODE_ALLOW +// if found, result is masked by ALLOWIP4_MODE__MASK and by 'ai->allow_mode' +// if not found, 'ai->fallback_mode' is returned. +u64 GetAutoAllowIP4ByAddr ( const AllowIP4_t *ai, u32 addr ); +u64 GetAutoAllowIP4ByName ( const AllowIP4_t *ai, mem_t name ); +u64 GetAutoAllowIP4BySock ( const AllowIP4_t *ai, int sock ); + +//----------------------------------------------------------------------------- + +void SaveCurrentStateAllowIP4 +( + FILE *f, // output file + ccp section, // not NULL: create own section + ccp name_prefix, // NULL or prefix of name + uint tab_pos, // tab pos of '=' + const AllowIP4_t *ai // valid struct +); + +//----------------------------------------------------------------------------- + +bool ScanAllowIP4Item +( + // return true on success; counter is optional + + AllowIP4Item_t *it, // store data here, cleared first + ccp line // NULL or line to scan +); + +void RestoreStateAllowIP4 +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +); + +void RestoreStateAllowIP4Ex +( + RestoreState_t *rs, // info data, modified + AllowIP4_t *ai, // valid struct + ccp name_prefix // NULL or prefix of name +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCP + UDP connections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ConnectByHost +( + NetworkHost_t *host, // valid pointer to NetworkHost_t + int type, // AF_INET type, e.g. SOCK_STREAM, SOCK_DGRAM + int protocol, // AF_INET protocol, e.g. IPPROTO_TCP, IPPROTO_UDP + bool silent // true: suppress error messages +); + +/////////////////////////////////////////////////////////////////////////////// + +int ConnectByHostTCP +( + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool silent // true: suppress error messages +); + +int ConnectTCP +( + ccp name, // optional service + server + optional host + // e.g.: "service://host:port/....." + int default_host, // default host + bool silent // true: suppress error messages +); + +int ConnectNumericTCP +( + // like ConnectTCP(), but without name resolution (only numeric ip+port) + + ccp addr, // TCP address: ['tcp':] IPv4 [:PORT] + int default_port, // NULL or default port, if not found in 'addr' + bool silent // true: suppress error messages +); + +extern int (*ConnectTCP_Hook) +( + // if set: ConnectNumericTCP() uses ConnectTCP() + + ccp addr, // TCP address: ['tcp':] IPv4 [:PORT] + int default_port, // NULL or default port, if not found in 'addr' + bool silent // true: suppress error messages +); + +void Setup_ConnectTCP_Hook ( bool force ); + +int ConnectUnixTCP +( + ccp fname, // unix socket filename + bool silent // true: suppress error messages +); + +/////////////////////////////////////////////////////////////////////////////// + +int ConnectByHostUDP +( + NetworkHost_t *host, // valid pointer to NetworkHost_t + bool silent // true: suppress error messages +); + +int ConnectUDP +( + ccp name, // optional service + server + optional host + // e.g.: "service://host:port/....." + int default_host, // default host + bool silent // true: suppress error messages +); + +/////////////////////////////////////////////////////////////////////////////// + +int WillReceiveNotBlock +( + // returns: -1:error, 0:may block, 1:will not block + + int fd, // file handle of source + uint msec // timeout: 0:no timeout, >0: milliseconds +); + +int WillSendNotBlock +( + // returns: -1:error, 0:may block, 1:will not block + + int fd, // file handle of source + uint msec // timeout: 0:no timeout, >0: milliseconds +); + +/////////////////////////////////////////////////////////////////////////////// + +ssize_t ReceiveTimeout +( + // returns: <0: on error, 0:timeout, >0: bytes read + + int fd, // file handle of source + void *buf, // valid pointer to buffer + uint size, // size to receive + int flags, // flags for recv() + int msec // timeout: -1:unlimited, 0:no timeout, >0: milliseconds +); + +ssize_t SendTimeout +( + // returns: <0: on error, 0:timeout, >0: bytes written + + int fd, // file hande of destination + const void *data, // valid pointer to data + uint size, // size to receive + int flags, // flags for send() + int msec, // total timeout: -1:unlimited, 0:no timeout, >0: milliseconds + bool all // true: send all data until done, total timeout or error +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IP data structures /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ether_head_t]] + +typedef struct ether_head_t +{ + // http://en.wikipedia.org/wiki/Ethertype + + u8 mac_dest[6]; + u8 mac_src[6]; + u16 ether_type; +} +__attribute__ ((packed)) ether_head_t; + +//----------------------------------------------------------------------------- +// [[ether_head_vlan_t]] + +typedef struct ether_head_vlan_t +{ + // http://en.wikipedia.org/wiki/Ethertype + + u8 mac_dest[6]; + u8 mac_src[6]; + u16 vlan_tag_type; // 0x8100 for VLAN + u16 vlan_param; // Bits 0-11: ID / Bits 12-15: flags + u16 ether_type; + + //-- secondary params + bool have_vlan; // true: VLAN trag available + u8 head_size; // >0: size of ethernet header (local endian): 14|18 +} +__attribute__ ((packed)) ether_head_vlan_t; + +//----------------------------------------------------------------------------- +// [[arp_head_t]] + +typedef struct arp_head_t +{ + // http://de.wikipedia.org/wiki/Address_Resolution_Protocol + + /* 0x00 */ u16 hardware_addr_type; // always 0x0001 for IPv4 + /* 0x02 */ u16 protocol_addr_type; // always 0x8000 for IPv4 + /* 0x04 */ u8 mac_size; // always 6 for IPv4 + /* 0x05 */ u8 addr_size; // always 4 for IPv4 + /* 0x06 */ u16 operation; + /* 0x08 */ u8 src_mac[6]; // MAC of source + /* 0x0e */ u32 src_ip; // IPv4 of source + /* 0x12 */ u8 dest_mac[6]; // MAC of destination + /* 0x18 */ u32 dest_ip; // IPv4 of destination + /* 0x1c */ +} +__attribute__ ((packed)) arp_head_t; + +//----------------------------------------------------------------------------- +// [[ip4_head_t]] + +typedef struct ip4_head_t +{ + // http://de.wikipedia.org/wiki/IP-Paket + + u8 vers_ihl; // high nibble: version, low nibble: IHL (Ip Header Len) + u8 tos; + u16 total_len; + u16 id; + u16 frag_off; // low 12 bits: fragment offset, high 4 bits: flags + u8 ttl; + u8 protocol; + u16 checksum; + u32 ip_src; + u32 ip_dest; + // optional: options + padding +} +__attribute__ ((packed)) ip4_head_t; + +//----------------------------------------------------------------------------- +// [[udp_head_t]] + +typedef struct udp_head_t +{ + // http://de.wikipedia.org/wiki/User_Datagram_Protocol#UDP-Datagramm + + u16 port_src; + u16 port_dest; + u16 data_len; + u16 checksum; +} +__attribute__ ((packed)) udp_head_t; + +//----------------------------------------------------------------------------- +// [[udp_packet_t]] + +// ethernet packet limit is 1500 +#define MAX_UDP_PACKET_DATA ( 1500 - sizeof(ip4_head_t) - sizeof(udp_head_t) ) + +typedef struct udp_packet_t +{ + ip4_head_t ip4; + udp_head_t udp; + u8 data[MAX_UDP_PACKET_DATA]; +} +__attribute__ ((packed)) udp_packet_t; + +//----------------------------------------------------------------------------- +// [[tcp_flags_t]] + +typedef enum tcp_flags_t +{ + TCP_F_FINISH = 0x01, + TCP_F_SYNC = 0x02, + TCP_F_RESET = 0x04, + TCP_F_PSH = 0x08, + TCP_F_ACK = 0x10, + TCP_F_URGENT = 0x20, + TCP_M_RESERVED = 0xc0, +} +__attribute__ ((packed)) tcp_flags_t; + +//----------------------------------------------------------------------------- +// [[tcp_head_t]] + +typedef struct tcp_head_t +{ + // http://de.wikipedia.org/wiki/Transmission_Control_Protocol#Aufbau_des_TCP-Headers + + u16 port_src; + u16 port_dest; + u32 seq_num; + u32 acc_num; + u8 data_off; // bit 4-7 only, multiply it by 4 + u8 flags; // bit field (think big endian) -> tcp_flags_t + u16 window; + u16 checksum; + u16 urgent; + u8 data[]; +} +__attribute__ ((packed)) tcp_head_t; + +//----------------------------------------------------------------------------- + +bool fix_ether_head_vlan ( ether_head_vlan_t *head ); +void ntoh_ether_head_vlan ( ether_head_vlan_t *dest, const void *src ); +void ntoh_ether_head ( ether_head_t *dest, const void *src ); +void ntoh_ip4_head ( ip4_head_t *dest, const void *src ); +void ntoh_udp_head ( udp_head_t *dest, const void *src ); +void ntoh_tcp_head ( tcp_head_t *dest, const void *src ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IP interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNumericIP4 +( + // returns next unread character or NULL on error + + ccp addr, // address to scan + u32 *r_ipv4, // not NULL: store result here (local endian) + u32 *r_port, // not NULL: scan port too (local endian) + uint default_port // return this if no port found +); + +//----------------------------------------------------------------------------- + +mem_t ScanNumericIP4Mem +( + // returns unread character or NullMem on error + + mem_t addr, // address to scan + u32 *r_ipv4, // not NULL: store result here (local endian) + u32 *r_port, // not NULL: scan port too (local endian) + uint default_port // return this if no port found +); + +//----------------------------------------------------------------------------- + +// Returns a NETWORK MASK "/a.b.c.d" or as CIDR number "/num" between 0 and 32. +// An optional slash '/' at the beginning is skipped. +// Returns modified 'source' if a MASK or CDIR is detected. +// If no one is detected, source is unmodified and returned mask = ~0. + +mem_t ScanNetworkMaskMem ( u32 *mask, mem_t source ); + +//----------------------------------------------------------------------------- + +u16 CalcChecksumIP4 +( + const void *data, // ip4 header + // make sure, that the checksum member is NULL + uint size // size of ip4 header +); + +//----------------------------------------------------------------------------- + +u16 CalcChecksumUDP +( + const ip4_head_t *ip4, // IP4 header + const udp_head_t *udp, // UDP header + const void *data // UDP data, size is 'udp->data_len' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// UNIX sockets /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SendUnixUDP +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + bool silent, // suppress all error messages + cvp data, // data to send + uint size // size of 'data' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transfer Statistics /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[TransferStats_t]] + +typedef struct TransferStats_t +{ + u32 conn_count; // number of connections + u32 recv_count; // number of received packets + u64 recv_size; // total size of received packets + u32 send_count; // number of send packets + u64 send_size; // total size of send packets +} +TransferStats_t; + +//----------------------------------------------------------------------------- + +static inline void InitializeTransferStats ( TransferStats_t *ts ) + { DASSERT(ts); memset(ts,0,sizeof(*ts)); } + +//----------------------------------------------------------------------------- + +TransferStats_t * Add2TransferStats +( + // return dest + // calculate: dest += src + + TransferStats_t *dest, // NULL or destination and first source + const TransferStats_t *src // NULL or second source +); + +TransferStats_t * Sub2TransferStats +( + // return dest + // calculate: dest -= src + + TransferStats_t *dest, // NULL or destination and first source + const TransferStats_t *src // NULL or second source +); + +TransferStats_t * Add3TransferStats +( + // return dest + // calculate: dest = src1 + src2 + + TransferStats_t *dest, // NULL or destination (maybe same as source) + const TransferStats_t *src1, // NULL or first source + const TransferStats_t *src2 // NULL or second source +); + +TransferStats_t * Sub3TransferStats +( + // return dest + // calculate: dest = src1 - src2 + + TransferStats_t *dest, // NULL or destination (maybe same as source) + const TransferStats_t *src1, // NULL or first source + const TransferStats_t *src2 // NULL or second source +); + +TransferStats_t * SumTransferStats +( + // return dest + // calculate: dest = sum(all_sources) + + TransferStats_t *dest, // NULL or destination (maybe same as source) + const TransferStats_t * const *src, // NULL or source list, elements may be NULL + int n_src // number of element; if -1: term at first NULL +); + +ccp PrintTransferStatsSQL +( + // print statistic as SQL assigning list. + // arguments are speparated by a comma. + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + const TransferStats_t *ts, // valid source + ccp prefix // not NULL: prefix memebr names +); + +//----------------------------------------------------------------------------- + +void PrintTranferStatistics +( + FILE *f, // output file, never NULL + ccp prefix, // NULL or prefix for each line + ccp name, // statistics name, up to 5 chars including colon + const TransferStats_t *stat,// statistics record + + TransferStats_t *prev, // NULL statistics record with previous data + u64 duration, // delta duration in usec + ccp duration_info, // text representation of 'duration' + + const ColorSet_t *colset // NULL (no colors) or color set to use +); + +//----------------------------------------------------------------------------- + +void SaveCurrentStateTransferStats +( + FILE *f, // output file + const TransferStats_t *stat // valid stat object +); + +void SaveCurrentStateTransferStats1 +( + // print as single line + FILE *f, // output file + ccp name, // var name; use "tfer-stat" if NULL or EMPTY + const TransferStats_t *stat // valid stat object +); + +void SaveCurrentStateTransferStatsN +( + // print multiple stats each as single line + FILE *f, // output file + ccp name, // var name; use "tfer-stat-" if NULL or EMPTY + const TransferStats_t *stat, // list of valid stat objects + uint n_stat // number of elements in 'stat' +); + +//----------------------------------------------------------------------------- + +void RestoreStateTransferStats +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +); + +void RestoreStateTransferStats1 +( + // scan single line + RestoreState_t *rs, // info data, can be modified (cleaned after call) + ccp name, // var name; use "tfer-stat" if NULL or EMPTY + TransferStats_t *stat, // valid stat object + bool fall_back // true: fall back to RestoreStateTransferStats() +); + +uint RestoreStateTransferStatsN +( + // print multiple stats each as single line + // returns the number of read elements; all others are set to NULL + RestoreState_t *rs, // info data, can be modified (cleaned after call) + ccp name, // var name; use "tfer-stat-" if NULL or EMPTY + TransferStats_t *stat, // list of valid stat objects + uint n_stat // number of elements in 'stat' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Socket_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// define some TCP call back function types + +struct TCPStream_t; +typedef int (*TCPStreamFunc) ( struct TCPStream_t * ts ); +typedef int (*TCPIOFunc) ( struct TCPStream_t * ts, u8 *buf, uint size ); +typedef int (*TCPTimeFunc) ( struct TCPStream_t * ts, u64 now_usec ); + +typedef enum TCPFDList_t +{ + TCP_FM_ADD_SOCK, // add sockets to 'fdl' + TCP_FM_CHECK_SOCK, // check sockts of 'fdl' + TCP_FM_TIMEOUT, // on timeout (fdl is NULL) +} +TCPFDList_t; + +typedef int (*TCPFDListFunc) +( + // if return <0, then don't do standard action with the stream + struct TCPStream_t *ts, // valid TCP stream + FDList_t *fdl, // valid socket list + TCPFDList_t mode, // execution mode + bool check_timeout // true: enable timeout checks +); + +struct TCPHandler_t; +typedef struct TCPStream_t * (*TCPCreateFunc) ( struct TCPHandler_t * th, int sock ); + +/////////////////////////////////////////////////////////////////////////////// +// [[Socket_t]] + +typedef struct Socket_t +{ + int sock; // tcp socket + uint poll_index; // if poll() is used: index of 'poll_list' + bool is_unix; // true: Is a unix file socket + char info[23]; // info about this socket, for debugging + + // if defined, these function replace the TCPHandler_t functions. + + TCPCreateFunc OnCreateStream; // not NULL: called to create + // an initialize a stream object + + TCPStreamFunc OnAddedStream; // not NULL: called after the stream + // is created and added +} +Socket_t; + +uint GetSocketNameBySA + ( char *buf, uint bufsize, const struct sockaddr *sa, socklen_t sa_len ); +uint GetSocketName ( char *buf, uint bufsize, int sock ); +uint GetPeerName ( char *buf, uint bufsize, int sock ); +bool DeleteSocketFile ( int sock ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCPStream_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[TCPStream_t]] + +typedef struct TCPStream_t +{ + //--- double linked list + + struct TCPStream_t *prev; // link to previous stream, unused for the pool + struct TCPStream_t *next; // link to next stream + struct TCPHandler_t *handler; // related TCP handler + + + //--- base data + + uint unique_id; // unique id, created by CreateUniqueId() + int sock; // tcp socket + uchar protect; // >0: protect member, don't destroy it + uchar auto_close; // >0: close stream after last byte sent + uchar rescan; // rescan input buffer also without new data + uchar eof; // >0: end of file, bit-0 is set by framework + uchar error; // >0: connection error, bit-0 is set by framework + uchar not_socket; // >0: fd is not a socket! // =0: unknown + GrowBuffer_t ibuf; // input buffer + GrowBuffer_t obuf; // output buffer + u64 accept_usec; // >0: next trigger, not used for timeout + u64 trigger_usec; // >0: next timeout and trigger time + u64 delay_usec; // >0: delay of trigger after accept_usec + u64 timeout_usec; // >0: auto disconnect after inactivity + u64 allow_mode; // >0: access allowed with this code + uint poll_index; // if 'use_poll': index of 'poll_list' + + + //--- call back functions + + TCPTimeFunc OnMaintenance; // not NULL: called on maintenance + TCPTimeFunc OnTimeout; // not NULL: called on timeout + TCPIOFunc OnReceived; // not NULL: called after read, but before buffer insert + TCPIOFunc OnSend; // not NULL: called after write, but before buffer drop + TCPTimeFunc OnClose; // not NULL: called when the stream is closed + TCPFDListFunc OnFDList; // not NULL: Call this for FDList actions + + //--- logging + + TraceLog_t *tracelog; // NULL or valid trace-log data + + //--- statistics + + char info[32]; // info about this stream, for debugging + u64 connect_usec; // time of connection, GetTimeUSec(false) + u64 receive_usec; // time of last receive, GetTimeUSec(false) + u64 send_usec; // time of last send, GetTimeUSec(false) + + TransferStats_t stat; // transfer statistics + TransferStats_t *xstat; // NULL or pointer to summary statistics + + //--- data extension + + u8 data[0]; // user specific data +} +TCPStream_t; + +/////////////////////////////////////////////////////////////////////////////// + +void LogTCPStream +( + FILE *f, // output file + int indent, // indention + const TCPStream_t *ts, // valid TCP handler + int recurse, // >0: print stream list, >1: print buffer status + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(format,...)' +) +__attribute__ ((__format__(__printf__,5,6))); + +//----------------------------------------------------------------------------- + +void LogTCPStreamActivity +( + const TCPStream_t *ts, // valid TCP handler + ccp activity +); + +//----------------------------------------------------------------------------- + +char * BufInfoHeadTCPStream +( + // returns a pointer to the head line + + uint line // 0|1 +); + +#define BUF_INFO_TCP_STREAM_SIZE 120 + +char * BufInfoTCPStream +( + // returns a pointer to the buffer + + char * buf, // result (BUF_INFO_TCP_STREAM_SIZE bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + const TCPStream_t *ts, // valid TCP handler + u64 now_usec // NULL or current time, GetTimeUSec(false) +); + +//----------------------------------------------------------------------------- + +void InitializeTCPStream ( TCPStream_t *ts, int sock ); +void ResetTCPStream ( TCPStream_t *ts ); +void DestroyTCPStream ( TCPStream_t *ts ); + +int SendDirectTCPStream +( + // Send data direct without blocking and without calling any notifier. + // If output buf is not empty or send failed, append the data to output buf. + // Returns the number of bytes added to the output buffer or -1 on error. + // The data is send+stored completely (returns 'size') or not at all + // (returns '0'). -1 is returned if the socket is invalid. + + TCPStream_t *ts, // valid TCP handler + bool flush_output, // true: try to flush out-buf before + cvp data, // data to send + uint size // size of 'data', If NULL && flush: flush only +); + +int PrintArgDirectTCPStream +( + // Printing interface for SendDirectTCPStream() + + TCPStream_t *ts, // valid TCP handler + bool flush_output, // true: try to flush out-buf before + ccp format, // format string for vsnprintf() + va_list arg // parameters for 'vfprintf(...,format,...)' +); + +int PrintDirectTCPStream +( + // Printing interface for SendDirectTCPStream() + + TCPStream_t *ts, // valid TCP handler + bool flush_output, // true: try to flush out-buf before + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(...,format,...)' +) +__attribute__ ((__format__(__printf__,3,4))); + +//----------------------------------------------------------------------------- + +int PrintBinary1TCPStream +( + TCPStream_t *ts, // NULL or destination + ccp cmd, // command name + cvp bin_data, // pointer to binary data + uint bin_size, // size of binary data + ccp format, // NULL or format string with final ';' + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,5,6))); + +int PrintBinary2TCPStream +( + TCPStream_t *ts, // NULL or destination + ccp cmd, // command name + cvp bin1_data, // pointer to binary data + uint bin1_size, // size of binary data + cvp bin2_data, // pointer to binary data + uint bin2_size, // size of binary data + ccp format, // NULL or format string with final ';' + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,7,8))); + +//----------------------------------------------------------------------------- + +static inline int FlushTCPStream +( + // Flush the output buffer using SendDirectTCPStream() without data + + TCPStream_t *ts // valid TCP handler +) +{ + return SendDirectTCPStream(ts,true,0,0); +} + +//----------------------------------------------------------------------------- + +ssize_t UpdateRecvStatTCPStream +( + // returns 'send_stat' + + TCPStream_t *ts, // valid TCP handler + ssize_t recv_stat, // result of recv(), update stats on >0 + u64 now_usec // NULL or current time +); + +ssize_t UpdateSendStatTCPStream +( + // returns 'send_stat' + + TCPStream_t *ts, // valid TCP handler + ssize_t send_stat, // result of send(), update stats on >0 + u64 now_usec // NULL or current time +); + +//----------------------------------------------------------------------------- + +void AddSocketTCPStream +( + TCPStream_t *ts, // valid TCP handler + FDList_t *fdl // valid file descriptor list +); + +void CheckSocketTCPStream +( + TCPStream_t *ts, // valid TCP handler + FDList_t *fdl, // valid socket list + bool check_timeout // true: enable timeout checks +); + +//----------------------------------------------------------------------------- + +void CheckTimeoutTCPStream ( TCPStream_t *ts ); +u64 GetLastActivityTCPStream ( TCPStream_t *ts ); +u64 GetTimeoutTCPStream ( TCPStream_t *ts ); +int OnTimeoutTCPStream ( TCPStream_t *ts, u64 now_usec ); +void SetupTriggerTCPStream ( TCPStream_t *ts, u64 now_usec ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCPHandler_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef int (*TCPHandlerFunc) +( + // returns 0 on success, !=0 otherwise + + struct TCPHandler_t *th, // valid TCP handler + const Socket_t *lsock, // valid listen-socket + int sock // valid socket to verify +); + +typedef bool (*TCPAllowFunc) +( + // returns true if access is allowed + + struct TCPHandler_t *th, // valid TCP handler + const Socket_t *lsock, // valid listen-socket + int sock, // valid socket to verify + u64 *allow // not NULL: store allow code here +); + +/////////////////////////////////////////////////////////////////////////////// +// [[TCPHandler_t]] + +#define TCP_HANDLER_MAX_LISTEN 3 + +typedef struct TCPHandler_t +{ + uint unique_id; // unique id, created by CreateUniqueId() + uint data_size; // size of TCPStream_t::data + uint max_conn; // max allowed connections + + TCPStream_t *first; // pointer to first active stream + uint need_maintenance; // >0: a stream is ready for maintenance + + TCPAllowFunc OnAllowStream; // not NULL: called before allowing a connection + // initialized with IsStreamAllowed() + TCPHandlerFunc OnAcceptStream; // not NULL: called before accpeting a connection + TCPCreateFunc OnCreateStream; // not NULL: called to create + // and initialize a stream object + TCPStreamFunc OnAddedStream; // not NULL: called after the stream + // is created and added + TCPStreamFunc OnDestroyStream; // not NULL: called by ResetTCPStream() + + Socket_t listen[TCP_HANDLER_MAX_LISTEN]; + // sockets to listen + + AllowIP4_t *allow_ip4; // NULL or filter for accept (not for unix files). + // Analysis is done by OnAllowStream() + // before calling OnAcceptStream(). + + //--- logging + + TraceLog_t tracelog; // trace activities + + //--- statistics + + uint used_streams; // number of currently used streams + uint max_used_streams; // number of max used streams + uint total_streams; // total number of used streams + + TransferStats_t stat; // transfer statistics +} +TCPHandler_t; + +/////////////////////////////////////////////////////////////////////////////// + +void LogTCPHandler +( + FILE *f, // output file + int indent, // indention + const TCPHandler_t *th, // valid TCP handler + int recurse, // >0: print stream list, >1: print buffer status + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(format,...)' +) +__attribute__ ((__format__(__printf__,5,6))); + +//----------------------------------------------------------------------------- + +void PrintStreamTableTCPHandler +( + FILE *f, // output file + const ColorSet_t *colset, // NULL (no colors) or valid color set + int indent, // indention + const TCPHandler_t *th // valid TCP handler +); + +//----------------------------------------------------------------------------- + +void InitializeTCPHandler +( + TCPHandler_t *th, // not NULL + uint data_size // size of 'TCPStream_t::data' +); + +void ResetTCPHandler +( + TCPHandler_t *th // valid TCP handler +); + +uint CloseTCPHandler +( + // returns the number of waiting clients (obuf not empty and !disabled) + TCPHandler_t *th // valid TCP handler +); + +TCPStream_t * AddTCPStream +( + TCPHandler_t *th, // valid TCP handler + TCPStream_t *ts // the new stream +); + +TCPStream_t * CreateTCPStream +( + TCPHandler_t *th, // valid TCP handler + int sock, // related socket + u64 allow_mode, // stored in 'allow_mode' + const Socket_t *listen // NULL or related listen object +); + +TCPStream_t * ConnectUnixTCPStream +( + TCPHandler_t *th, // valid TCP handler + ccp path, // NULL or path part 1 to socket file + bool silent // suppress error messages +); + +TCPStream_t * ConnectTCPStream +( + TCPHandler_t *th, // valid TCP handler + ccp addr, // address -> NetworkHost_t + u16 default_port, // default port + bool silent // suppress error messages +); + +TCPStream_t * FindTCPStreamByUniqueID +( + TCPHandler_t *th, // valid TCP handler + uint unique_id // id to search +); + +//----------------------------------------------------------------------------- + +uint NumOfSocketsTCP ( const TCPHandler_t *th ); +Socket_t * GetUnusedListenSocketTCP ( TCPHandler_t *th, bool silent ); + +//----------------------------------------------------------------------------- + +enumError ListenUnixTCP +( + TCPHandler_t *th, // valid TCP handler + ccp path // path to unix file +); + +enumError ListenTCP +( + TCPHandler_t *th, // valid TCP handler + ccp addr, // IPv4 address with optional port -> NetworkHost_t + // fall back to ListenUnixTCP() if addr begins with + // 'unix:' or '/' or './ or '../' + u16 default_port // default port +); + +int UnlistenTCP +( + // returns the index of the closed socket, or if not found + + TCPHandler_t *th, // valid TCP handler + int sock // socket to close +); + +uint UnlistenAllTCP +( + // returns the number of closed sockets + + TCPHandler_t *th // valid TCP handler +); + +//----------------------------------------------------------------------------- + +void AddSocketsTCP +( + TCPHandler_t *th, // valid TCP handler + FDList_t *fdl // valid file descriptor list +); + +//----------------------------------------------------------------------------- + +bool IsStreamAllowed +( + // returns true if access is allowed + + struct TCPHandler_t *th, // valid TCP handler + const Socket_t *lsock, // valid listen-socket + int sock, // valid socket to verify + u64 *allow // not NULL: store allow code here +); + +TCPStream_t * OnAcceptStream +( + TCPHandler_t *th, // valid TCP handler + const Socket_t *lsock, // listen-socket + u64 now_usec // time for timestamps, GetTimeUSec(false) +); + +void OnCloseStream +( + TCPStream_t *ts, // valid TCP stream + u64 now_usec // time for timestamps, GetTimeUSec(false) +); + +void SetNotSocketStream +( + TCPStream_t *ts // valid TCP stream +); + +void OnReceivedStream +( + TCPStream_t *ts, // valid TCP stream + u64 now_usec // time for timestamps, GetTimeUSec(false) +); + +void OnWriteStream +( + TCPStream_t *ts, // valid TCP stream + u64 now_usec // time for timestamps, GetTimeUSec(false) +); + +void CheckSocketsTCP +( + TCPHandler_t *th, // valid TCP handler + FDList_t *fdl, // valid socket list + bool check_timeout // true: enable timeout checks +); + +void CheckTimeoutTCP ( TCPHandler_t *th ); +bool MaintenanceTCP ( TCPHandler_t *th ); + +void ManageSocketsTCP +( + // call CheckSocketsTCP(), CheckTimeoutTCP(), MaintenanceTCP() + + TCPHandler_t *th, // valid TCP handler + FDList_t *fdl, // valid socket list + int stat // result of a WAIT function +); + +/////////////////////////////////////////////////////////////////////////////// +// special case: send single data packtes + +TCPStream_t * SendSingleUnixTCP +( + TCPHandler_t *th, // valid handle + ccp path, // path to socket file + + const void *data, // data to send + uint size, // size of 'data' + u64 timeout_usec, // timeout before closing the connection + TransferStats_t *xstat, // NULL or pointer to summary statistics + + bool silent // suppress error messages +); + +TCPStream_t * SendSingleTCP +( + TCPHandler_t *th, // valid handle + ccp addr, // address -> NetworkHost_t + u16 default_port, // default port + + const void *data, // data to send + uint size, // size of 'data' + u64 timeout_usec, // timeout before closing the connection + TransferStats_t *xstat, // NULL or pointer to summary statistics + + bool silent // suppress error messages +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TCP: CommandTCP /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef char * (*CommandTCPLineFunc) +( + // return first non scanned character, or NULL on error + + TCPStream_t *ts, // valid stream data + char *line, // begin of line + char *line_end, // end of line + u64 now_usec // NULL or current time +); + +/////////////////////////////////////////////////////////////////////////////// + +typedef enumError (*CommandTCPArgFunc) +( + TCPStream_t *ts, // valid stream data + int argc, // number of arguments in 'argv' + char **argv, // array with 'argc' arguments + a NULL term + u64 now_usec // NULL or current time in usec +); + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct CommandTCPInfo_t +{ + //--- base parameters + + bool comma_is_eol; // true: comma is 'end of command line' + u64 timeout_usec; // wanted timeout, default 10s + + //--- scanning functions, high priority first + + CommandTCPArgFunc OnScanArg; // function to scan arguments + CommandTCPLineFunc OnScanLine; // function to scan line + + //--- user specific data extension + + u8 data[0]; +} +CommandTCPInfo_t; + +/////////////////////////////////////////////////////////////////////////////// + +int OnCreateCommandTCP ( TCPStream_t *ts ); + +void SetTimeoutCommandTCP +( + TCPStream_t *ts, // valid stream + u64 now_usec // NULL or current time in usec +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveCurrentState*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateSocket +( + FILE *f, // output file + const Socket_t *sock // valid socket object +); + +/////////////////////////////////////////////////////////////////////////////// + +typedef void (*TCPStreamSaveFunc) +( + FILE *f, // output file + const TCPStream_t *ts // valid TCP stream +); + +//----------------------------------------------------------------------------- + +void SaveCurrentStateTCPStream +( + FILE *f, // output file + const TCPStream_t *ts, // valid TCP stream + TCPStreamSaveFunc func // NULL or function for 'TCPStream_t.data' extend +); + +void SaveCurrentStateTCPHandler +( + FILE *f, // output file + ccp sect_name, // section base name + const TCPHandler_t *th, // valid TCP handler + TCPStreamSaveFunc func // NULL or function for 'TCPStream_t.data' extend +); + +void SaveCurrentStateCommandTCP +( + FILE *f, // output file + const TCPStream_t *ts // valid TCP stream +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// RestoreState*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateSocket +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +); + +/////////////////////////////////////////////////////////////////////////////// + +TCPStream_t * RestoreStateTCPStream +( + // return 'ts' or the new TCPStream_t + RestoreState_t *rs, // info data, can be modified (cleaned after call) + TCPStream_t *ts, // if NULL: create it + uint extra_size // if 'ts' alloced: alloc some byte more +); + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateTCPHandler_base +( + TCPHandler_t *th, // valid TCPHandler_t + RestoreState_t *rs // info data, can be modified (cleaned after call) +); + +Socket_t * RestoreStateTCPHandler_socket +( + TCPHandler_t *th, // valid TCPHandler_t + RestoreState_t *rs // info data, can be modified (cleaned after call) +); + +TCPStream_t * RestoreStateTCPHandler_stream +( + TCPHandler_t *th, // valid TCPHandler_t + RestoreState_t *rs // info data, can be modified (cleaned after call) +); + +void RestoreStateTCPHandler +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +); + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateCommandTCP +( + RestoreState_t *rs, // info data, can be modified (cleaned after call) + cvp user_table // pointer provided by RestoreStateTab_t[] +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Linux support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(SYSTEM_LINUX) || defined(__CYGWIN__) + #include "dclib-network-linux.h" +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_NETWORK_H + diff --git a/project/dclib/dclib-numeric.c b/project/dclib/dclib-numeric.c new file mode 100644 index 0000000..572211a --- /dev/null +++ b/project/dclib/dclib-numeric.c @@ -0,0 +1,6102 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include + +#include "dclib-basics.h" +#include "dclib-debug.h" +#include "dclib-utf8.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// bits /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint Count1Bits ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + uint count = 0; + const uchar *ptr = addr; + while ( size-- > 0 ) + count += TableBitCount[*ptr++]; + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int FindLowest0BitLE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr; + uint idx; + for ( idx = 0; idx < size; idx++ ) + { + const int val = TableLowest0Bit[*ptr++]; + if ( val >= 0 ) + return 8*idx + val; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindLowest1BitLE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr; + uint idx; + for ( idx = 0; idx < size; idx++ ) + { + const int val = TableLowest1Bit[*ptr++]; + if ( val >= 0 ) + return 8*idx + val; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindHighest0BitLE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr + size; + while ( size-- > 0 ) + { + const int val = TableHighest0Bit[*--ptr]; + if ( val >= 0 ) + return 8*size + val; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindHighest1BitLE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr + size; + while ( size-- > 0 ) + { + const int val = TableHighest1Bit[*--ptr]; + if ( val >= 0 ) + return 8*size + val; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u32 GetAlign32 ( u32 data ) +{ + const int index = FindLowest1Bit(&data,sizeof(data)); + return index < 0 ? 0 : 1 << index; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetAlign64 ( u64 data ) +{ + const int index = FindLowest1Bit(&data,sizeof(data)); + return index < 0 ? 0 : 1llu << index; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int FindLowest0BitBE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr + size; + uint idx; + for ( idx = 0; idx < size; idx++ ) + { + const int val = TableLowest0Bit[*--ptr]; + if ( val >= 0 ) + return 8*idx + val; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindLowest1BitBE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr + size; + uint idx; + for ( idx = 0; idx < size; idx++ ) + { + const int val = TableLowest1Bit[*--ptr]; + if ( val >= 0 ) + return 8*idx + val; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindHighest0BitBE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr; + while ( size-- > 0 ) + { + const int val = TableHighest0Bit[*ptr++]; + if ( val >= 0 ) + return 8*size + val; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindHighest1BitBE ( cvp addr, uint size ) +{ + DASSERT( CHAR_BIT == 8 ); + + const uchar *ptr = addr; + while ( size-- > 0 ) + { + const int val = TableHighest1Bit[*ptr++]; + if ( val >= 0 ) + return 8*size + val; + } + return -1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// bit manipulation /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool TestSetBit ( void *addr, uint bit_num ) +{ + const u8 mask = 1 << ( bit_num & 7 ); + u8 *ptr = ((u8*)addr) + ( bit_num >> 3 ); + const bool result = ( *ptr & mask ) != 0; + *ptr |= mask; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool TestClearBit ( void *addr, uint bit_num ) +{ + const u8 mask = 1 << ( bit_num & 7 ); + u8 *ptr = ((u8*)addr) + ( bit_num >> 3 ); + const bool result = ( *ptr & mask ) != 0; + *ptr &= ~mask; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool TestInvertBit ( void *addr, uint bit_num ) +{ + const u8 mask = 1 << ( bit_num & 7 ); + u8 *ptr = ((u8*)addr) + ( bit_num >> 3 ); + const bool result = ( *ptr & mask ) != 0; + *ptr ^= mask; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetBits ( void *addr, uint beg_bitnum, uint end_bitnum ) +{ + static uchar tab1[] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 }; + static uchar tab2[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + + if ( beg_bitnum < end_bitnum ) + { + const uint lo8 = beg_bitnum >> 3; + const uint hi8 = --end_bitnum >> 3; + + if ( lo8 < hi8 ) + { + + uchar * dest = (uchar*)addr + lo8; + *dest++ |= tab1[ beg_bitnum & 7 ]; + + uchar * end = (uchar*)addr + hi8; + *end |= tab2[ end_bitnum & 7 ]; + + while ( dest < end ) + *dest++ = 0xff; + } + else + { + uchar * dest = (uchar*)addr + lo8; + *dest |= tab1[ beg_bitnum & 7 ] & tab2[ end_bitnum & 7 ]; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearBits ( void *addr, uint beg_bitnum, uint end_bitnum ) +{ + static uchar tab1[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f }; + static uchar tab2[] = { 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 }; + + if ( beg_bitnum < end_bitnum ) + { + const uint lo8 = beg_bitnum >> 3; + const uint hi8 = --end_bitnum >> 3; + + if ( lo8 < hi8 ) + { + + uchar * dest = (uchar*)addr + lo8; + *dest++ &= tab1[ beg_bitnum & 7 ]; + + uchar * end = (uchar*)addr + hi8; + *end &= tab2[ end_bitnum & 7 ]; + + while ( dest < end ) + *dest++ = 0; + } + else + { + uchar * dest = (uchar*)addr + lo8; + *dest &= tab1[ beg_bitnum & 7 ] | tab2[ end_bitnum & 7 ]; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InvertBits ( void *addr, uint beg_bitnum, uint end_bitnum ) +{ + static uchar tab1[] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80 }; + static uchar tab2[] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + + if ( beg_bitnum < end_bitnum ) + { + const uint lo8 = beg_bitnum >> 3; + const uint hi8 = --end_bitnum >> 3; + + if ( lo8 < hi8 ) + { + + uchar * dest = (uchar*)addr + lo8; + *dest++ ^= tab1[ beg_bitnum & 7 ]; + + uchar * end = (uchar*)addr + hi8; + *end ^= tab2[ end_bitnum & 7 ]; + + while ( dest < end ) + *dest++ ^= 0xff; + } + else + { + uchar * dest = (uchar*)addr + lo8; + *dest ^= tab1[ beg_bitnum & 7 ] & tab2[ end_bitnum & 7 ]; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef GET_NEXT +#define GET_NEXT ch = *src++; \ + if ( ++min_idx > max_idx ) goto term; \ + if ( min_idx == max_idx ) ch &= max_mask + +int PrintBitfieldEx +( + // returns the number of printed characters, or -1 if buffer is to small + + char *buf, // buffer + uint buf_size, // size of 'buf' + cvp bit_field, // address of bit field + uint min_idx, // minimal index (inclusive) to check + uint max_idx, // maximal index (inclusive) to check + bool use_ranges, // true: create ranges 'a:b' + int mult, // each printed index is first multiplyed by 'mult' + int add // ... and then added to 'add'. +) +{ + DASSERT(buf); + DASSERT(buf_size); + DASSERT(bit_field); + + char *dest = buf; + char *end = buf + buf_size; + char *sep = ""; + + const u8 *src = (u8*)bit_field + (min_idx >> 3); + u8 ch = *src++; // always preload the current byte + if ( min_idx & 7 ) + ch &= ~((1<<(min_idx&7))-1); + min_idx >>= 3; + const u8 max_mask = (2<<(max_idx&7))-1; + max_idx >>= 3; + noPRINT("SETUP[%zx] %02x, %u..%u, max_mask=%02x\n", + src-(u8*)bit_field-1, ch, min_idx, max_idx, max_mask ); + + while ( min_idx <= max_idx ) + { + //--- first skip bytes with value 0 + + if (!ch) + do { GET_NEXT; } while (!ch); + + + //-- now find the first set bit + + DASSERT(ch); + noPRINT("CHECK[%zx] %02x, %u..%u\n", src-(u8*)bit_field-1, ch, min_idx, max_idx); + u8 mask = 1; + uint bit_idx = 0; + while ( !(ch & mask) ) + mask <<= 1, bit_idx++; + + uint first = 8*min_idx + bit_idx; + noPRINT("FOUND-BEG[%zx.%u=%u] %02x, %u..%u\n", + src-(u8*)bit_field-1, bit_idx, first, ch, min_idx, max_idx ); + + + //-- now find the first bit not set + + while ( ch & mask ) + mask <<= 1, bit_idx++; + + if ( bit_idx == 8 ) + { + do { GET_NEXT; } while (ch == 0xff); + + mask = 1; + bit_idx = 0; + while ( ch & mask ) + mask <<= 1, bit_idx++; + } + + uint last = 8*min_idx + bit_idx - 1; + noPRINT("FOUND-END[%zx.%u=%u] %02x, %u..%u\n", + src-(u8*)bit_field-1, bit_idx, last, ch, min_idx, max_idx ); + + if ( first == last ) + dest = snprintfE(dest,end,"%s%d",sep,first*mult+add); + else if ( use_ranges ) + dest = snprintfE(dest,end,"%s%d:%d", + sep, + first * mult + add, + last * mult + add ); + else + while ( first <= last ) + { + dest = snprintfE(dest,end,"%s%d",sep,first++*mult+add); + sep = ","; + } + + if ( dest >= end-1 ) + { + *dest = 0; + return -1; + } + + sep = ","; + ch &= ~((1<> 8; + *dest = data; +} + +void write_be24 ( void * be_data_ptr, u32 data ) +{ + DASSERT(be_data_ptr); + u8 * dest = be_data_ptr + 2; + *dest-- = data; data >>= 8; + *dest-- = data; + *dest = data >> 8; +} + +void write_be32 ( void * be_data_ptr, u32 data ) +{ + DASSERT(be_data_ptr); + u8 * dest = be_data_ptr + 3; + *dest-- = data; data >>= 8; + *dest-- = data; data >>= 8; + *dest-- = data; + *dest = data >> 8; +} + +void write_be40 ( void * be_data_ptr, u64 data ) +{ + DASSERT(be_data_ptr); + *(u8*)be_data_ptr = data>>32; + write_be32( (u8*)be_data_ptr+1, data ); +} + +void write_be48 ( void * be_data_ptr, u64 data ) +{ + DASSERT(be_data_ptr); + write_be16( be_data_ptr, data>>32 ); + write_be32( (u8*)be_data_ptr+2, data ); +} + +void write_be56 ( void * be_data_ptr, u64 data ) +{ + DASSERT(be_data_ptr); + write_be24( be_data_ptr, data>>32 ); + write_be32( (u8*)be_data_ptr+3, data ); +} + +void write_be64 ( void * be_data_ptr, u64 data ) +{ + DASSERT(be_data_ptr); + write_be32( be_data_ptr, data >> 32 ); + write_be32( (u8*)be_data_ptr+4, data ); +} + +void write_bef4 ( void * be_data_ptr, float data ) +{ + // assume: local system supports IEEE 754 + + write_be32(be_data_ptr,*(u32*)&data); +} + +void write_bef8 ( void * be_data_ptr, double data ) +{ + // assume: local system supports IEEE 754 + + write_be64(be_data_ptr,*(u64*)&data); +} + +/////////////////////////////////////////////////////////////////////////////// + +void write_le16 ( void * le_data_ptr, u16 data ) +{ + DASSERT(le_data_ptr); + u8 * dest = le_data_ptr; + *dest++ = data; + *dest = data >> 8; +} + +void write_le24 ( void * le_data_ptr, u32 data ) +{ + DASSERT(le_data_ptr); + u8 * dest = le_data_ptr; + *dest++ = data; data >>= 8; + *dest++ = data; + *dest = data >> 8; +} + +void write_le32 ( void * le_data_ptr, u32 data ) +{ + DASSERT(le_data_ptr); + u8 * dest = le_data_ptr; + *dest++ = data; data >>= 8; + *dest++ = data; data >>= 8; + *dest++ = data; + *dest = data >> 8; +} + +void write_le40 ( void * le_data_ptr, u64 data ) +{ + DASSERT(le_data_ptr); + *(u8*)le_data_ptr = (u8)data; + write_le32( (u8*)le_data_ptr+1, data >> 8 ); +} + +void write_le48 ( void * le_data_ptr, u64 data ) +{ + DASSERT(le_data_ptr); + write_le16( le_data_ptr, data ); + write_le32( (u8*)le_data_ptr+2, data >> 16 ); +} + +void write_le56 ( void * le_data_ptr, u64 data ) +{ + DASSERT(le_data_ptr); + write_le32( le_data_ptr, data ); + write_le24( (u8*)le_data_ptr+4, data >> 32 ); +} + +void write_le64 ( void * le_data_ptr, u64 data ) +{ + DASSERT(le_data_ptr); + write_le32( le_data_ptr, data ); + write_le32( (u8*)le_data_ptr+4, data >> 32 ); +} + +void write_lef4 ( void * le_data_ptr, float data ) +{ + // assume: local system supports IEEE 754 + + write_le32(le_data_ptr,*(u32*)&data); +} + +void write_lef8 ( void * le_data_ptr, double data ) +{ + // assume: local system supports IEEE 754 + + write_le64(le_data_ptr,*(u64*)&data); +} + +/////////////////////////////////////////////////////////////////////////////// +// convert lists + +void be16n ( u16 * dest, const u16 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + *dest++ = be16(src++); +} + +void be32n ( u32 * dest, const u32 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + *dest++ = be32(src++); +} + +void bef4n ( float32 * dest, const float32 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + *dest++ = bef4(src++); +} + +void write_be16n ( u16 * dest, const u16 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + write_be16(dest++,*src++); +} + +void write_be32n ( u32 * dest, const u32 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + write_be32(dest++,*src++); +} + +void write_bef4n ( float32 * dest, const float32 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + write_bef4(dest++,*src++); +} + +/////////////////////////////////////////////////////////////////////////////// +// convert lists + +void le16n ( u16 * dest, const u16 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + *dest++ = le16(src++); +} + +void lef4n ( float32 * dest, const float32 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + *dest++ = lef4(src++); +} + +void write_le16n ( u16 * dest, const u16 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + write_le16(dest++,*src++); +} + +void write_lef4n ( float32 * dest, const float32 * src, int n ) +{ + DASSERT( dest ); + DASSERT( n >= 0 ); + DASSERT( src || !n ); + + while ( n-- > 0 ) + write_lef4(dest++,*src++); +} + +/////////////////////////////////////////////////////////////////////////////// + +const endian_func_t be_func = +{ + {0xfe,0xff}, true, false, DC_BIG_ENDIAN, + be16, be24, be32, be40, be48, be56, be64, bef4, bef8, + write_be16, write_be24, write_be32, + write_be40, write_be48, write_be56, write_be64, + write_bef4, write_bef8 +}; + +const endian_func_t le_func = +{ + {0xff,0xfe}, false, true, DC_LITTLE_ENDIAN, + le16, le24, le32, le40, le48, le56, le64, lef4, lef8, + write_le16, write_le24, write_le32, + write_le40, write_le48, write_le56, write_le64, + write_lef4, write_lef8 +}; + +const endian_func_t * GetEndianFunc ( const void * byte_order_mark ) +{ + DASSERT(byte_order_mark); + const u8 * bom = byte_order_mark; + return bom[0] == 0xfe && bom[1] == 0xff + ? &be_func + : bom[0] == 0xff && bom[1] == 0xfe + ? &le_func + : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetTextBOMLen ( const void * data, uint data_size ) +{ + static const u8 be_bom[] = { 0xef, 0xbb, 0xbf }; + static const u8 le_bom[] = { 0xef, 0xbf, 0xbe }; + + if ( data_size >= 3 && ( !memcmp(data,be_bom,3) || !memcmp(data,le_bom,3) )) + return 3; + + if ( data_size >= 2 && ( *(u16*)data == 0xfeff || *(u16*)data == 0xfffe )) + return 2; + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check index /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int CheckIndex1 ( int max, int index ) +{ + DASSERT( max >= 0 ); + + if ( index >= 0 ) + return index <= max ? index : max; + + index += max; + return index >= 0 ? index : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckIndex1End ( int max, int index ) +{ + DASSERT( max >= 0 ); + if ( index > 0 ) + return index <= max ? index : max; + + index += max; + return index >= 0 ? index : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckIndex2 ( int max, int * p_begin, int * p_end ) +{ + DASSERT( max >= 0 ); + DASSERT(p_begin); + DASSERT(p_end); + + int begin = *p_begin; + if ( begin < 0 ) + { + begin += max; + if ( begin < 0 ) + begin = 0; + } + else if ( begin > max ) + begin = max; + + int end = *p_end; + if ( end < 0 ) + { + end += max; + // don't check for 'end<0' here, because 'end max ) + end = max; + + if ( end < begin ) + end = begin; + + *p_begin = begin; + *p_end = end; + return end - begin; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckIndex2End ( int max, int * p_begin, int * p_end ) +{ + DASSERT( max >= 0 ); + DASSERT(p_begin); + DASSERT(p_end); + + int begin = *p_begin; + if ( begin <= 0 ) + { + begin += max; + if ( begin < 0 ) + begin = 0; + } + else if ( begin > max ) + begin = max; + + int end = *p_end; + if ( end <= 0 ) + { + end += max; + // don't check for 'end<0' here, because 'end max ) + end = max; + + if ( end < begin ) + end = begin; + + *p_begin = begin; + *p_end = end; + return end - begin; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// encoding/decoding /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintEscapedString +( + // returns 'buf' + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 10 + ccp source, // NULL string to print + int len, // length of string. if -1, str is null terminated + CharMode_t char_mode, // modes, bit field (CHMD_*) + char quote, // NULL or quotation char, that must be quoted + uint *dest_len // not NULL: Store length of result here +) +{ + DASSERT(buf); + DASSERT(buf_size>=10); + + const CharMode_t utf8 = char_mode & CHMD_UTF8; + const CharMode_t allow_e = char_mode & CHMD_ESC; + + char *dest = buf; + char *dest_end = dest + buf_size - 4; + + if (!source) + source = ""; + ccp str = source; + ccp end = len < 0 ? 0 : str + len; + + while ( dest < dest_end && ( !end || str < end ) ) + { + const u8 ch = (u8)*str++; + switch (ch) + { + case 0: + if (!end) + { + str--; + goto eos; + } + *dest++ = '\\'; + *dest++ = 'x'; + *dest++ = '0'; + *dest++ = '0'; + break; + + case '\\': *dest++ = '\\'; *dest++ = '\\'; break; + case '\a': *dest++ = '\\'; *dest++ = 'a'; break; + case '\b': *dest++ = '\\'; *dest++ = 'b'; break; + case '\f': *dest++ = '\\'; *dest++ = 'f'; break; + case '\n': *dest++ = '\\'; *dest++ = 'n'; break; + case '\r': *dest++ = '\\'; *dest++ = 'r'; break; + case '\t': *dest++ = '\\'; *dest++ = 't'; break; + case '\v': *dest++ = '\\'; *dest++ = 'v'; break; + + case '\033': + *dest++ = '\\'; + if (allow_e) + *dest++ = 'e'; + else + { + *dest++ = 'x'; + *dest++ = '1'; + *dest++ = 'B'; + } + break; + + default: + if ( ch == quote ) + { + *dest++ = '\\'; + *dest++ = quote; + } + else if ( ch < ' ' || !utf8 && (ch&0x7f) < ' ' || (ch&0x7f) == 0x7f ) + { + *dest++ = '\\'; + *dest++ = 'x'; + *dest++ = HiDigits[ch>>4]; + *dest++ = HiDigits[ch&15]; + } + else + *dest++ = ch; + } + } + eos: + *dest = 0; + if (dest_len) + *dest_len = dest - buf; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ScanEscapedString +( + // returns the number of valid bytes in 'buf' (NULL term not counted) + + char *buf, // valid destination buffer, maybe source + uint buf_size, // size of 'buf' + ccp source, // string to scan + int len, // length of string. if -1, str is null terminated + bool utf8, // true: source and output is UTF-8 + int quote, // 0:none, -1:auto, >0: quotation char + uint *scanned_len // not NULL: Store number of scanned 'source' bytes here +) +{ + DASSERT(buf); + DASSERT(buf_size>3); + DASSERT(source); + + char *dest = buf; + char *dest_end = dest + buf_size - ( utf8 ? 4 : 1 ); + + ccp src = source; + ccp src_end = src + ( len < 0 ? strlen(src) : len ); + + bool have_quote = quote > 0; + if ( quote == -1 && src < src_end && ( *src == '"' || *src == '\'' )) + { + quote = *src++; + have_quote = true; + } + + while ( dest < dest_end && src < src_end ) + { + uint code; + if ( *src == '\\' ) + src = ScanEscape(&code,src+1,src_end); + else + { + if (utf8) + code = ScanUTF8AnsiCharE(&src,src_end); + else + code = *src++; + + if ( code == quote && have_quote ) + break; + } + + if (utf8) + dest = PrintUTF8Char(dest,code); + else + *dest++ = code; + } + + if (scanned_len) + *scanned_len = src - source; + + *dest = 0; + return dest - buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u32 CalcCRC32 ( u32 crc, cvp buf, uint size ) +{ + #if HAVE_PRINT0 + static bool done = false; + if ( !done && TRACE_FILE ) + { + done = true; + fprintf(TRACE_FILE,"\nstatic const u32 TableCRC32[0x100] =\n{\n"); + uint i; + for ( i = 0; i < 0x100; i += 8 ) + fprintf(TRACE_FILE," %#010x,%#010x,%#010x,%#010x, %#010x,%#010x,%#010x,%#010x,\n", + TableCRC32[i+0], + TableCRC32[i+1], + TableCRC32[i+2], + TableCRC32[i+3], + TableCRC32[i+4], + TableCRC32[i+5], + TableCRC32[i+6], + TableCRC32[i+7] ); + fprintf(TRACE_FILE,"};\n"); + } + #endif + + crc = ~crc; + const u8 *p = buf; + while ( size-- ) + crc = TableCRC32[ (crc ^ *p++) & 0xff ] ^ crc >> 8; + + return ~crc; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u64 DecodeU64 +( + ccp source, // string to decode + int len, // length of string. if -1, str is null terminated + uint base, // number of possible digits (2..36): + uint *scanned_len // not NULL: Store number of scanned 'str' bytes here +) +{ + DASSERT(source); + DASSERT( base <= 36 ); + + u64 num = 0; + ccp src = source; + ccp end = len < 0 ? 0 : src + len; + + while ( !end || src < end ) + { + u8 digit = TableNumbers[(u8)*src]; + if ( digit >= base ) + break; + num = num * base + digit; + src++; + } + + if (scanned_len) + *scanned_len = src - source; + return num; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp EncodeU64 +( + char * buf, // result buffer (size depends on base) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 num, // number to encaode + uint base // number of possible digits (2..64): +) +{ + DASSERT( base >= 2 ); + DASSERT( base <= 64 ); + if ( base < 2 ) + base = 2; + else if ( base > 64 ) + base = 64; + + char digits[65], *dest = digits + sizeof(digits); + *--dest = 0; + + while ( num ) + { + *--dest = LoDigits[ num % base ]; + num /= base; + } + + if (!buf) + buf = GetCircBuf ( buf_size = digits + sizeof(digits)-1 - dest ); + + StringCopyS(buf,buf_size,dest); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// Default tables for DecodeBase64() and EncodeBase64(), if no table is defined. + +ccp TableDecode64default = TableDecode64; +ccp TableEncode64default = TableEncode64; + +/////////////////////////////////////////////////////////////////////////////// + +uint DecodeBase64 +( + // returns the number of valid bytes in 'buf' + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 3 + ccp source, // NULL or string to decode + int len, // length of string. if -1, str is null terminated + const char decode64[256], // decoding table; if NULL: use TableDecode64default + bool allow_white_spaces, // true: skip white spaces + uint *scanned_len // not NULL: Store number of scanned 'str' bytes here +) +{ + DASSERT(buf); + DASSERT(buf_size>=3); + u8 *dest = (u8*)buf; + u8 *dest_end = dest + ( scanned_len ? buf_size / 3 * 3 : buf_size ); + + if (!source) + source = ""; + ccp src = source; + ccp end = src + ( len < 0 ? strlen(src) : len ); + int ch1, ch2; + + if (!decode64) + decode64 = TableDecode64default; + + while ( dest < dest_end && src < end ) + { + ch1 = decode64[(u8)*src++]; + if (allow_white_spaces) + while ( ch1 == DECODE_SPACE && src < end ) + ch1 = decode64[(u8)*src++]; + if ( ch1 < 0 ) + { + src--; + break; + } + if ( src == end ) + break; + + ch2 = decode64[(u8)*src++]; + if (allow_white_spaces) + while ( ch2 == DECODE_SPACE && src < end ) + ch2 = decode64[(u8)*src++]; + if ( ch2 < 0 ) + { + src--; + break; + } + + *dest++ = ch1 << 2 | ch2 >> 4; + if ( src == end || dest == dest_end ) + break; + + ch1 = decode64[(u8)*src++]; + if (allow_white_spaces) + while ( ch1 == DECODE_SPACE && src < end ) + ch1 = decode64[(u8)*src++]; + if ( ch1 < 0 ) + { + src--; + break; + } + + *dest++ = ch2 << 4 | ch1 >> 2; + if ( src == end || dest == dest_end ) + break; + + ch2 = decode64[(u8)*src++]; + if (allow_white_spaces) + while ( ch2 == DECODE_SPACE && src < end ) + ch2 = decode64[(u8)*src++]; + if ( ch2 < 0 ) + { + src--; + break; + } + *dest++ = ch1 << 6 | ch2; + } + + if (scanned_len) + { + while ( src < end && decode64[(u8)*src] == DECODE_FILLER ) + src++; + *scanned_len = src - source; + } + return dest - (u8*)buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t DecodeBase64Circ +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + ccp source, // NULL or string to decode + int len, // length of string. if -1, str is null terminated + const char decode64[256] // decoding table; if NULL: use TableDecode64default +) +{ + char buf[CIRC_BUF_MAX_ALLOC+10]; + mem_t mem; + mem.len = DecodeBase64(buf,sizeof(buf),source,len,decode64,false,0); + if ( mem.len < CIRC_BUF_MAX_ALLOC ) + mem.ptr = CopyCircBuf0(buf,mem.len); + else + { + mem.ptr = 0; + mem.len = 0; + } + return mem; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint EncodeBase64 +( + // returns the number of scanned bytes of 'source' + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 4 + const void *source, // NULL or data to encode + int source_len, // length of 'source'; if <0: use strlen(source) + const char encode64[64+1], // encoding table; if NULL: use TableEncode64default + bool use_filler, // use filler for aligned output + ccp next_line, // not NULL: use this string as new line sep + uint next_line_trigger // >0: use 'next_line' every # input bytes +) +{ + DASSERT(buf); + DASSERT(buf_size>3); + DASSERT(source||!source_len); + + noPRINT("EncodeBase64(sz=%u,len=%d,fil=%d)\n",buf_size,source_len,use_filler); + + if (!next_line) + next_line_trigger = ~0; + else if (!next_line_trigger) + next_line_trigger = 19; + else + next_line_trigger /= 3; + + char *dest = buf; + char *dest_end = buf + buf_size - 4; + + if (!source) + source = ""; + const u8 *src = source; + const u8 *src_end = src + ( source_len < 0 ? strlen(source) : source_len ); + + if (!encode64) + encode64 = TableEncode64default; + + uint n_tupel = 0; + + while ( src < src_end && dest < dest_end ) + { + if ( ++n_tupel > next_line_trigger && next_line ) + { + char *next = StringCopyE(dest,(char*)buf+buf_size,next_line); + if ( next >= dest_end ) + break; + dest = next; + n_tupel = 1; + } + + //----- start: no pending bits + + u8 ch1 = *src++; + *dest++ = encode64 [ ch1 >> 2 ]; + ch1 &= 0x03; + DASSERT( (ch1 & ~0x03) == 0 ); + if ( src == src_end ) + { + *dest++ = encode64[ch1<<4]; + if (use_filler) + { + *dest++ = encode64[64]; + *dest++ = encode64[64]; + } + break; + } + + //----- 2 pending bits in 'ch1' + + u8 ch2 = *src++; + *dest++ = encode64 [ ch1 << 4 | ch2 >> 4 ]; + ch2 &= 0x0f; + DASSERT( (ch2 & ~0x0f) == 0 ); + if ( src == src_end ) + { + *dest++ = encode64[ch2<<2]; + if (use_filler) + *dest++ = encode64[64]; + break; + } + + //----- 4 pending bits in 'ch2' + + ch1 = *src++; + *dest++ = encode64 [ ch2 << 2 | ch1 >> 6 ]; + + //----- 6 pending bits in 'ch1' + + *dest++ = encode64[ch1&0x3f]; + } + + *dest = 0; + + return src - (const u8*)source; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t EncodeBase64Circ +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + const void *source, // NULL or data to encode + int source_len, // length of 'source'; if <0: use strlen(source) + const char encode64[64+1] // encoding table; if NULL: use TableEncode64default +) +{ + char buf[CIRC_BUF_MAX_ALLOC+10]; + EncodeBase64(buf,sizeof(buf),source,source_len,encode64,false,0,0); + mem_t mem; + mem.len = strlen(buf); + if ( mem.len < CIRC_BUF_MAX_ALLOC ) + mem.ptr = CopyCircBuf(buf,mem.len+1); + else + { + mem.ptr = 0; + mem.len = 0; + } + return mem; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint EncodeJSON +( + // returns the number of valid bytes in 'buf'. Result is NULL-terminated. + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >2 and 2 bytes longer than needed + const void *source, // NULL or data to encode + int source_len // length of 'source'; if <0: use strlen(source) +) +{ + DASSERT(buf); + DASSERT(buf_size>2); + + char *dest = buf; + char *dest_end = dest + buf_size - 2; + + if (!source) + source = ""; + ccp str = source; + ccp end = str + ( source_len < 0 ? strlen(str) : source_len ); + + while ( dest < dest_end && str < end ) + { + const u8 ch = (u8)*str++; + switch (ch) + { + case '"': *dest++ = '\\'; *dest++ = '"'; break; + case '\\': *dest++ = '\\'; *dest++ = '\\'; break; + case '\b': *dest++ = '\\'; *dest++ = 'b'; break; + case '\f': *dest++ = '\\'; *dest++ = 'f'; break; + case '\n': *dest++ = '\\'; *dest++ = 'n'; break; + case '\r': *dest++ = '\\'; *dest++ = 'r'; break; + case '\t': *dest++ = '\\'; *dest++ = 't'; break; + + default: + if ( ch >= ' ' ) + *dest++ = ch; + else if ( dest + 5 > dest_end ) + goto eos; + else + { + *dest++ = '\\'; + *dest++ = 'u'; + *dest++ = '0'; + *dest++ = '0'; + *dest++ = HiDigits[ch>>4]; + *dest++ = HiDigits[ch&15]; + } + break; + } + } + + eos: + *dest = 0; + return dest - buf; +} + +//----------------------------------------------------------------------------- + +mem_t EncodeJSONCirc +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + const void *source, // NULL or data to encode + int source_len // length of 'source'; if <0: use strlen(source) +) +{ + char buf[CIRC_BUF_MAX_ALLOC+10]; + mem_t mem; + mem.len = EncodeJSON(buf,sizeof(buf),source,source_len); + if ( mem.len < CIRC_BUF_MAX_ALLOC ) + mem.ptr = CopyCircBuf(buf,mem.len+1); + else + { + mem.ptr = 0; + mem.len = 0; + } + return mem; +} + +//----------------------------------------------------------------------------- + +mem_t QuoteJSONCirc +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + const void *source, // NULL or data to encode + int source_len, // length of 'source'; if <0: use strlen(source) + int null_if // return 'null' without quotes ... + // <=0: never + // >=1: if source == NULL + // >=2: if source_len == 0 +) +{ + if ( source_len < 0 ) + source_len = source ? strlen(source) : 0; + + mem_t mem; + if ( null_if > 0 && !source || null_if > 1 && !source_len ) + { + mem.ptr = "null"; + mem.len = 4; + } + else + { + char buf[CIRC_BUF_MAX_ALLOC+10]; + mem.len = EncodeJSON(buf+1,sizeof(buf)-2,source,source_len) + 2; + if ( mem.len < CIRC_BUF_MAX_ALLOC - 2 ) + { + buf[0] = buf[mem.len-1] = '"'; + buf[mem.len] = 0; + mem.ptr = CopyCircBuf(buf,mem.len+1); + } + else + { + mem.ptr = 0; + mem.len = 0; + } + } + return mem; +} + +//----------------------------------------------------------------------------- + +mem_t DecodeJSONCirc +( + // Returns a buffer alloced by GetCircBuf() + // with valid pointer and null terminated. + // If result is too large (>CIRC_BUF_MAX_ALLOC) then (0,0) is returned. + + ccp source, // NULL or string to decode + int source_len // length of 'source'. If -1, str is NULL terminated +) +{ + char buf[CIRC_BUF_MAX_ALLOC+10]; + mem_t mem; + mem.len = DecodeJSON(buf,sizeof(buf),source,source_len,0); + if ( mem.len < CIRC_BUF_MAX_ALLOC ) + mem.ptr = CopyCircBuf(buf,mem.len+1); + else + { + mem.ptr = 0; + mem.len = 0; + } + return mem; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint DecodeByMode +( + // returns the number of valid bytes in 'buf'. Result is NULL-terminated. + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 3 + ccp source, // string to decode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +) +{ + DASSERT(buf); + DASSERT( buf_size >= 1 ); + DASSERT( source || !slen ); + DASSERT( (uint)emode < ENCODE__N ); + + uint len = 0; + if ( buf_size < 4 ) + { + char temp[4]; + len = DecodeByMode(temp,sizeof(temp),source,slen,emode); + memcpy(buf,temp,buf_size); + goto term; + } + + DASSERT( buf_size >= 3 ); + switch (emode) + { + case ENCODE_STRING: + len = ScanEscapedString(buf,buf_size,source,slen,false,0,0); + break; + + case ENCODE_UTF8: + len = ScanEscapedString(buf,buf_size,source,slen,true,0,0); + break; + + case ENCODE_BASE64: + len = DecodeBase64(buf,buf_size-1,source,slen,TableDecode64,true,0); + break; + + case ENCODE_BASE64URL: + case ENCODE_BASE64STAR: + len = DecodeBase64(buf,buf_size-1,source,slen,TableDecode64url,true,0); + break; + + case ENCODE_BASE64XML: + len = DecodeBase64(buf,buf_size-1,source,slen,TableDecode64xml,true,0); + break; + + case ENCODE_JSON: + len = DecodeJSON(buf,buf_size,source,slen,0); + break; + + default: + if ( slen < 0 ) + len = StringCopyS(buf,buf_size,source) - buf; + else + len = StringCopySM(buf,buf_size,source,slen) - buf; + break; + } + + //--- terminate with NULL + + term:; + if ( len >= buf_size ) + len = buf_size - 1; + buf[len] = 0; + return len; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t DecodeByModeMem +( + // Returns the decoded 'source'. Result is NULL-terminated. + // It points either to 'buf' or is alloced (on buf==NULL or to less space) + // If alloced (mem.ptr!=buf) => call FreeMem(&mem) + + char *buf, // NULL or destination buffer + uint buf_size, // size of 'buf' + ccp source, // string to decode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +) +{ + DASSERT( source || !slen ); + DASSERT( (uint)emode < ENCODE__N ); + + if ( slen < 0 ) + slen = source ? strlen(source) : 0; + + uint need; + switch (emode) + { + case ENCODE_STRING: + case ENCODE_UTF8: + need = slen + 5; + break; + + case ENCODE_BASE64: + case ENCODE_BASE64URL: + case ENCODE_BASE64STAR: + case ENCODE_BASE64XML: + need = ( 3*slen ) / 4 + 12; + break; + + //case ENCODE_JSON: // [[json]] + default: + need = slen + 3; // at least 3 bytes + break; + } + + const bool alloced = !buf || buf_size < need; + if (alloced) + { + buf = MALLOC(need); + buf_size = need; + } + + mem_t mem; + mem.len = DecodeByMode(buf,buf_size,source,slen,emode); + if ( alloced && mem.len+10 < buf_size ) + buf = REALLOC( buf, mem.len+1 ); + mem.ptr = buf; + return mem; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint EncodeByMode +( + // returns the number of valid bytes in 'buf'. Result is NULL-terminated. + + char *buf, // valid destination buffer + uint buf_size, // size of 'buf', >= 4 + ccp source, // string to encode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +) +{ + DASSERT(buf); + DASSERT( buf_size >= 3 ); + DASSERT( source || !slen ); + DASSERT( (uint)emode < ENCODE__N ); + + noPRINT("EncodeByMode(,%u,%d,%d)\n",buf_size,slen,emode); + + uint len = 0; + switch (emode) + { + case ENCODE_STRING: + PrintEscapedString(buf,buf_size,source,slen,0,0,&len); + break; + + case ENCODE_UTF8: + PrintEscapedString(buf,buf_size,source,slen,CHMD__ALL,0,&len); + break; + + case ENCODE_BASE64: + len = EncodeBase64(buf,buf_size,source,slen,TableEncode64,true,0,0); + len = EncodeBase64FillLen(len); + break; + + case ENCODE_BASE64URL: + len = EncodeBase64(buf,buf_size,source,slen,TableEncode64url,true,0,0); + len = EncodeBase64FillLen(len); + break; + + case ENCODE_BASE64STAR: + len = EncodeBase64(buf,buf_size,source,slen,TableEncode64star,true,0,0); + len = EncodeBase64FillLen(len); + break; + + case ENCODE_BASE64XML: + len = EncodeBase64(buf,buf_size,source,slen,TableEncode64xml,true,0,0); + len = EncodeBase64FillLen(len); + break; + + case ENCODE_JSON: + len = EncodeJSON(buf,buf_size,source,slen); + break; + + default: + if ( slen < 0 ) + len = StringCopyS(buf,buf_size,source) - buf; + else + len = StringCopySM(buf,buf_size,source,slen) - buf; + break; + } + + //--- terminate with NULL + + if ( len >= buf_size ) + len = buf_size - 1; + buf[len] = 0; + return len; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t EncodeByModeMem +( + // Returns the encoded 'source'. Result is NULL-terminated. + // It points either to 'buf' or is alloced (on buf==NULL or to less space) + // If alloced (mem.ptr!=buf) => call FreeMem(&mem) + + char *buf, // NULL or destination buffer + uint buf_size, // size of 'buf' + ccp source, // string to encode + int slen, // length of string. if -1, str is null terminated + EncodeMode_t emode // encoding mode +) +{ + DASSERT( source || !slen ); + DASSERT( (uint)emode < ENCODE__N ); + + if ( slen < 0 ) + slen = source ? strlen(source) : 0; + + uint need; + switch (emode) + { + case ENCODE_STRING: + case ENCODE_UTF8: + need = slen * 4 + 5; + break; + + case ENCODE_BASE64: + case ENCODE_BASE64URL: + case ENCODE_BASE64STAR: + case ENCODE_BASE64XML: + need = ( 4*slen ) / 3 + 10; + break; + + case ENCODE_JSON: // [[json]] + { + need = slen + 1; + ccp ptr = source; + ccp end = ptr + slen; + while ( ptr < end ) + if ( (uchar)*ptr++ < ' ' ) + need += 5; + } + break; + + default: + need = slen + 1; + break; + } + + const bool alloced = !buf || buf_size < need; + if (alloced) + { + buf = MALLOC(need); + buf_size = need; + } + + mem_t mem; + mem.len = EncodeByMode(buf,buf_size,source,slen,emode); + if ( alloced && mem.len+10 < buf_size ) + buf = REALLOC( buf, mem.len+1 ); + mem.ptr = buf; + return mem; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// time /////////////// +/////////////////////////////////////////////////////////////////////////////// + +s64 timezone_adjust_sec = -1; +s64 timezone_adjust_usec = -1; +int timezone_adjust_isdst = -1; + +/////////////////////////////////////////////////////////////////////////////// + +void SetupTimezone ( bool force ) +{ + if ( force || timezone_adjust_sec == -1 ) + { + tzset(); + timezone_adjust_sec = timezone; + + time_t tim = GetTimeSec(false); + struct tm *tm = localtime(&tim); + timezone_adjust_isdst = tm->tm_isdst; + if (tm->tm_isdst) + timezone_adjust_sec -= 3600; + + timezone_adjust_usec = 1000000ll * timezone_adjust_sec; + TRACE("TZ: %s,%s, %ld, %d => %lld %lld\n", + tzname[0], tzname[1], timezone, daylight, + timezone_adjust_sec, timezone_adjust_usec ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +struct timeval GetTimeOfDay ( bool localtime ) +{ + static uint last_hour = 0; + struct timeval tval; + gettimeofday(&tval,NULL); + if (localtime) + { + const uint hour = tval.tv_sec / 3600; + if ( last_hour != hour ) + { + last_hour = hour; + SetupTimezone(true); + } + tval.tv_sec -= timezone_adjust_sec; + } + return tval; +} + +/////////////////////////////////////////////////////////////////////////////// + +DayTime_t GetDayTime ( bool localtime ) +{ + struct timeval tval = GetTimeOfDay(localtime); + div_t d; + + DayTime_t dt; + dt.time = tval.tv_sec; + d = div(tval.tv_sec,86400); + dt.day = d.quot; + d = div(d.rem,3600); + dt.hour = d.quot; + d = div(d.rem,60); + dt.min = d.quot; + dt.sec = d.rem; + dt.usec = tval.tv_usec; + + return dt; +} + +/////////////////////////////////////////////////////////////////////////////// + +u_sec_t GetTimeSec ( bool localtime ) +{ + struct timeval tval = GetTimeOfDay(localtime); + return tval.tv_sec; +} + +/////////////////////////////////////////////////////////////////////////////// + +u_msec_t GetTimeMSec ( bool localtime ) +{ + struct timeval tval = GetTimeOfDay(localtime); + return (u64)(tval.tv_sec) * 1000ll + tval.tv_usec/1000; +} + +/////////////////////////////////////////////////////////////////////////////// + +u_usec_t GetTimeUSec ( bool localtime ) +{ + struct timeval tval = GetTimeOfDay(localtime); + return (u64)(tval.tv_sec) * 1000000ll + tval.tv_usec; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// timer /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static time_t time_base = 0; + +/////////////////////////////////////////////////////////////////////////////// + +u_msec_t GetTimerMSec() +{ + struct timeval tval; + gettimeofday(&tval,NULL); + if (!time_base) + time_base = tval.tv_sec; + + return (u_msec_t)( tval.tv_sec - time_base ) * 1000 + tval.tv_usec/1000; +} + +/////////////////////////////////////////////////////////////////////////////// + +u_usec_t GetTimerUSec() +{ + struct timeval tval; + gettimeofday(&tval,NULL); + if (!time_base) + time_base = tval.tv_sec; + + return (u_usec_t)( tval.tv_sec - time_base ) * 1000000 + tval.tv_usec; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time // seconds since epoch -> time() +) +{ + char buf[100]; + struct tm *tm = localtime(&time); + const uint len = strftime(buf,sizeof(buf),format,tm); + return CopyCircBuf(buf,len+1); +} + +//----------------------------------------------------------------------------- + +ccp PrintTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time // seconds since epoch -> time() +) +{ + char buf[100]; + struct tm *tm = gmtime(&time); + const uint len = strftime(buf,sizeof(buf),format,tm); + return CopyCircBuf(buf,len+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintUsecByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time, // seconds since epoch -> time() + uint usec // micro second of second +) +{ + char buf[100]; + struct tm *tm = localtime(&time); + const uint len = strftime(buf,sizeof(buf),format,tm); + char *at = strchr(buf,'@'); + if (at) + { + char ubuf[8]; + snprintf(ubuf,sizeof(ubuf),"%06u",usec); + ccp src = ubuf; + while ( *at == '@' && *src ) + *at++ = *src++; + } + + return CopyCircBuf(buf,len+1); +} + +//----------------------------------------------------------------------------- + +ccp PrintUsecByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + time_t time, // seconds since epoch -> time() + uint usec // micro second of second +) +{ + char buf[100]; + struct tm *tm = gmtime(&time); + const uint len = strftime(buf,sizeof(buf),format,tm); + char *at = strchr(buf,'@'); + if (at) + { + char ubuf[8]; + snprintf(ubuf,sizeof(ubuf),"%06u",usec); + ccp src = ubuf; + while ( *at == '@' && *src ) + *at++ = *src++; + } + + return CopyCircBuf(buf,len+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimevalByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + const struct timeval *tv // time to print, if NULL use gettimeofday() +) +{ + struct timeval temp; + if (!tv) + { + gettimeofday(&temp,0); + tv = &temp; + } + + return PrintUsecByFormat(format,tv->tv_sec,tv->tv_usec); +} + +//----------------------------------------------------------------------------- + +ccp PrintTimevalByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + const struct timeval *tv // time to print, if NULL use gettimeofday() +) +{ + struct timeval temp; + if (!tv) + { + gettimeofday(&temp,0); + tv = &temp; + } + + return PrintUsecByFormatUTC(format,tv->tv_sec,tv->tv_usec); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimeSec +( + char * buf, // result buffer (>19 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + uint sec // seconds (=time) to print +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 20 ); + + time_t time = sec; + struct tm *tm = localtime(&time); + strftime(buf,buf_size,"%F %T",tm); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimeMSec +( + char * buf, // result buffer (>23 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_msec_t msec, // milliseconds to print + uint fraction // number of digits (0-3) to print as fraction +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 24 ); + + time_t time = msec / 1000; + struct tm *tm = localtime(&time); + uint len = strftime(buf,buf_size,"%F %T",tm); + + if ( fraction && len + 4 < buf_size ) + { + len += snprintf(buf+len, buf_size-len, ".%03llu", msec % 1000 ); + const uint pos = len - 3 + ( fraction < 3 ? fraction : 3 ); + if ( pos < buf_size ) + buf[pos] = 0; + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimeUSec +( + char * buf, // result buffer (>26 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_usec_t usec, // microseconds to print + uint fraction // number of digits (0-6) to print as fraction +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 28 ); + + time_t time = usec / 1000000; + struct tm *tm = localtime(&time); + uint len = strftime(buf,buf_size,"%F %T",tm); + + if ( fraction && len + 7 < buf_size ) + { + len += snprintf(buf+len, buf_size-len, ".%06llu", usec % 1000000 ); + const uint pos = len - 6 + ( fraction < 6 ? fraction : 6 ); + if ( pos < buf_size ) + buf[pos] = 0; + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimerMSec +( + char * buf, // result buffer (>12 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 msec, // milliseconds to print + uint fraction // number of digits (0-3) to print as fraction +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 16 ); + + if (fraction) + { + const uint len = snprintf(buf,buf_size,"%02d:%02d:%02d.%03d", + msec/3600000, msec/60000%60, msec/1000%60, msec%1000 ); + if ( len > 3 && fraction < 3 ) + { + const uint pos = len - 3 + fraction; + if ( pos < buf_size ) + buf[pos] = 0; + } + } + else + snprintf(buf,buf_size,"%02d:%02d:%02d", + msec/3600000, msec/60000%60, msec/1000%60 ); + + ccp ptr = buf; + int colon_counter = 0; + while ( *ptr == '0' || *ptr == ':' && !colon_counter++ ) + ptr++; + return *ptr == ':' ? ptr-1 : ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimerUSec +( + char * buf, // result buffer (>16 bytes are good) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_usec_t usec, // microseconds to print + uint fraction // number of digits (0-3) to print as fraction +) +{ + if ( !buf || buf_size < 4 ) + buf = GetCircBuf( buf_size = 20 ); + + const bool minus = usec < 0; + if (minus) + usec = -usec; + + u32 sec = (u64)usec / 1000000; + + if (fraction) + { + const uint len = snprintf(buf+1,buf_size-1,"%02d:%02d:%02d.%06lld", + sec/3600, sec/60%60, sec%60, (u64)usec%1000000 ); + if ( len > 6 && fraction < 6 ) + { + const uint pos = len - 6 + fraction; + if ( pos < buf_size ) + buf[pos] = 0; + } + } + else + snprintf(buf+1,buf_size-1,"%02d:%02d:%02d", + sec/3600, sec/60%60, sec%60 ); + + char *ptr = buf+1; + while ( *ptr == '0' || *ptr == ':' ) + ptr++; + if ( *ptr == '.' ) + ptr--; + if (minus) + *--ptr = '-'; + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimer3 +( + char * buf, // result buffer (>3 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 sec, // seconds to print + int usec, // 0...999999: usec fraction, + // otherwise suppress ms/us output + bool aligned // true: print aligned 3 character output +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 4 ); + + if ( !sec && usec >= 0 && usec <= 999999 ) + { + if (!usec) + StringCopyS(buf,buf_size,aligned?" 0":"0"); + else if ( usec < 100 ) + snprintf(buf,buf_size,aligned?"%2uu":"%uu",usec); + else if ( usec < 1000 ) + snprintf(buf,buf_size,".%ui",usec/100); + else if ( usec < 100000 ) + snprintf(buf,buf_size,aligned?"%2ui":"%ui",usec/1000); + else + snprintf(buf,buf_size,".%us",usec/100000); + } + else if ( sec < 100 ) + snprintf(buf,buf_size,aligned?"%2llus":"%llus",sec); + else if ( sec < 6000 ) + snprintf(buf,buf_size,aligned?"%2llum":"%llum",sec/60); + else if ( sec < 360000 ) + snprintf(buf,buf_size,aligned?"%2lluh":"%lluh",sec/3600); + else + { + const u64 days = sec / (24*3600); + if ( days < 100 ) + snprintf(buf,buf_size,aligned?"%2llud":"%llud",days); + else if ( days < 700 ) + snprintf(buf,buf_size,aligned?"%2lluw":"%lluw",days/7); + else + { + const u32 years = days / 365; + if ( years < 100 ) + snprintf(buf,buf_size,aligned?"%2uy":"%uy",years); + else + buf = "***"; + } + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimer4 +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 sec, // seconds to print + int usec, // 0...999999: usec fraction, + // otherwise suppress ms/us output + bool aligned // true: print aligned 4 character output +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 5 ); + + if ( sec < 10 && usec >= 0 && usec <= 999999 ) + { + if (sec) + snprintf(buf,buf_size,"%llu.%us",sec,usec/100000); + else if (!usec) + StringCopyS(buf,buf_size,aligned?" 0":"0"); + else if ( usec < 1000 ) + snprintf(buf,buf_size,aligned?"%3uu":"%uu",usec); + else if ( usec < 10000 ) + snprintf(buf,buf_size,"%u.%ui",usec/1000,usec/100%10); + else + snprintf(buf,buf_size,aligned?"%3ui":"%ui",usec/1000); + } + else if ( sec < 600 ) + snprintf(buf,buf_size,aligned?"%3llus":"%llus",sec); + else if ( sec < 36000 ) + snprintf(buf,buf_size,aligned?"%3llum":"%llum",sec/60); + else if ( sec < 168*3600 ) + snprintf(buf,buf_size,aligned?"%3lluh":"%lluh",sec/3600); + else + { + const u64 days = sec / (24*3600); + if ( days < 365 ) + snprintf(buf,buf_size,aligned?"%3llud":"%llud",days); + else if ( days < 10*365 ) + snprintf(buf,buf_size,aligned?"%3lluw":"%lluw",days/7); + else + { + const u32 years = days / 365; + if ( years < 1000 ) + snprintf(buf,buf_size,aligned?"%3uy":"%uy",years); + else + buf = "****"; + } + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimer6 +( + char * buf, // result buffer (>6 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 sec, // seconds to print + int usec, // 0...999999: usec fraction, + // otherwise suppress ms/us output + bool aligned // true: print aligned 6 character output +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 7 ); + + if (aligned) + { + if ( sec < 10 && usec >= 0 && usec <= 999999 ) + { + usec += 1000000 * sec; + if (!usec) + StringCopyS(buf,buf_size," 0"); + else if ( usec < 10000 ) + snprintf(buf,buf_size,"%4uus",usec); + else + snprintf(buf,buf_size,"%4ums",usec/1000); + } + else if ( sec < 100 ) + { + if ( usec >= 0 && usec <= 999999 ) + snprintf(buf,buf_size,"%2llu.%02us",sec,usec/10000); + else + snprintf(buf,buf_size,"%5llus",sec); + } + else if ( sec < 100*60 ) + snprintf(buf,buf_size,"%2llum%02llus",sec/60,sec%60); + else if ( sec < 100*3600 ) + snprintf(buf,buf_size,"%2lluh%02llum",sec/3600,sec/60%60); + else + { + const u64 days = sec / (24*3600); + if ( days < 100 ) + snprintf(buf,buf_size,"%2llud%02lluh",days,sec/3600%24); + else if ( days < 7000 ) + snprintf(buf,buf_size,"%3lluw%llud",days/7,days%7); + else + { + const u32 years = days / 365; + if ( years < 100 ) + snprintf(buf,buf_size,"%2uy%02lluw",years,days%365/7); + else if ( years < 100000 ) + snprintf(buf,buf_size,"%5uy",years); + else + buf = "******"; + } + } + } + else + { + if ( sec < 10 ) + { + usec += 1000000 * sec; + if (!usec) + StringCopyS(buf,buf_size,"0"); + else if ( usec < 10000 ) + snprintf(buf,buf_size,"%uus",usec); + else + snprintf(buf,buf_size,"%ums",usec/1000); + } + else if ( sec < 100 ) + { + if ( usec >= 0 && usec <= 999999 ) + { + const uint val2 = usec/10000; + if (val2) + snprintf(buf,buf_size,"%llu.%02us",sec,val2); + else + snprintf(buf,buf_size,"%llus",sec); + } + else + snprintf(buf,buf_size,"%llus",sec); + } + else if ( sec < 100*60 ) + { + const uint val2 = sec%60; + if (val2) + snprintf(buf,buf_size,"%llum%02us",sec/60,val2); + else + snprintf(buf,buf_size,"%llum",sec/60); + } + else if ( sec < 100*3600 ) + { + const uint val2 = sec/60%60; + if (val2) + snprintf(buf,buf_size,"%lluh%02um",sec/3600,val2); + else + snprintf(buf,buf_size,"%lluh",sec/3600); + } + else + { + const u64 days = sec / (24*3600); + if ( days < 100 ) + { + const uint val2 = sec/3600%24; + if (val2) + snprintf(buf,buf_size,"%llud%02uh",days,val2); + else + snprintf(buf,buf_size,"%llud",days); + } + else if ( days < 7000 ) + { + const uint val2 = days%7; + if (val2) + snprintf(buf,buf_size,"%lluw%ud",days/7,val2); + else + snprintf(buf,buf_size,"%lluw",days/7); + } + else + { + const u32 years = days / 365; + if ( years < 100 ) + { + const uint val2 = days%365/7; + if (val2) + snprintf(buf,buf_size,"%uy%02uw",years,val2); + else + snprintf(buf,buf_size,"%uy",years); + } + else if ( years < 100000 ) + snprintf(buf,buf_size,"%uy",years); + else + buf = "******"; + } + } + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimerUSec4s +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_usec_t usec, // microseconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +) +{ + if ( !buf || buf_size < 2 ) + buf = GetCircBuf( buf_size = 5 ); + + if (!usec) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + else + { + char sign; + if ( usec < 0 ) + { + usec = -usec; + sign = '-'; + } + else + sign = mode & DC_SFORM_PLUS ? '+' : ' '; + + PrintTimerUSec3(buf+1,buf_size-1,usec,mode&DC_SFORM_ALIGN); + *buf = ' '; + + char *ptr = buf+1; + while ( *ptr == ' ' ) + ptr++; + ptr[-1] = sign; + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimerUSec7s +( + char * buf, // result buffer (>7 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_usec_t usec, // microseconds to print + sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS +) +{ + if ( !buf || buf_size < 2 ) + buf = GetCircBuf( buf_size = 8 ); + + if (!usec) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + else + { + char sign; + if ( usec < 0 ) + { + usec = -usec; + sign = '-'; + } + else + sign = mode & DC_SFORM_PLUS ? '+' : ' '; + + PrintTimerUSec6(buf+1,buf_size-1,usec, mode & DC_SFORM_ALIGN ); + *buf = ' '; + + char *ptr = buf+1; + while ( *ptr == ' ' ) + ptr++; + ptr[-1] = sign; + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if 0 +u64 GetXTime ( xtime_t * xtime ) +{ + struct timeval tval; + gettimeofday(&tval,0); + if (!time_base) + time_base = tval.tv_sec; + if (!rel_usec_base) + rel_usec_base = (u64)( tval.tv_sec - time_base ) * 1000000 + tval.tv_usec; + + const ull total_usec = 1000000ull * tval.tv_sec + tval.tv_usec; + + if (xtime) + { + xtime->time = tval.tv_sec; + xtime->usec = tval.tv_usec; + xtime->total_usec = total_usec; + xtime->rel_usec = total_usec - rel_usec_base; + } + return total_usec; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan date & time /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanInterval +( + time_t *res_time, // not NULL: store seconds + u32 *res_usec, // not NULL: store micro seconds of second + ccp source // source text +) +{ + uint days = 0, sec = 0, usec = 0; + bool day_possible = true; + uint colon_count = 0; + char *src = source ? (char*)source : ""; + + while ( *src > 0 && *src <= ' ' ) + src++; + + for(;;) + { + if ( *src < '0' || *src >='9' ) + break; + ulong num = strtoul(src,&src,10); + if ( *src == 'd' && day_possible ) + { + src++; + days = num; + day_possible = false; + } + else if ( *src == ':' && colon_count < 2 ) + { + src++; + colon_count++; + sec = ( sec + num ) * 60; + } + else + { + sec += num; + if ( *src == '.' ) + { + src++; + uint i; + for ( i = 0; i < 6; i++ ) + { + usec *= 10; + if ( *src >= '0' && *src <= '9' ) + usec += *src++ - '0'; + } + while ( *src >= '0' && *src <= '9' ) + src++; + } + else if ( colon_count == 1 ) + sec *= 60; + } + } + + if (res_time) + *res_time = days * 86400 + sec; + if (res_usec) + *res_usec = usec; + return src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanInterval64 +( + u64 *res_usec, // not NULL: store total micro seconds + ccp source // source text +) +{ + time_t time; + u32 usec; + char *src = ScanInterval(&time,&usec,source); + if (res_usec) + *res_usec = time * 1000000ull + usec; + return src; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanDateTime +( + time_t *res_time, // not NULL: store seconds here + u32 *res_usec, // not NULL: store useconds of second here + ccp source, // source text + bool allow_delta // true: allow +|- interval +) +{ + //--- scan date + + time_t total_time = 0; + int total_usec = 0; + ccp src = source ? source : ""; + while ( *src > 0 && *src <= ' ' ) + src++; + + struct tm tm; + memset(&tm,0,sizeof(tm)); + src = strptime(src,"%Y-%m-%d",&tm); + + noPRINT(" %4u-%02u-%02u %2u:%02u:%02u |%s|\n", + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, src ); + + if (src) + total_time = mktime(&tm); + else + src = source ? source : ""; + noPRINT_TIME(total_time,"DATE"); + + + //--- scan time + + time_t time; + u32 usec; + src = ScanInterval(&time,&usec,src); + total_time += time; + total_usec += usec; + noPRINT_TIME(total_time,"TIME"); + + + //--- scan timezone + + ccp src2 = src; + while ( *src2 > 0 && *src2 <= ' ' ) + src2++; + if ( ( src2[0] == '-' || src2[0] == '+' ) + && src2[1] >= '0' && src2[1] <= '9' + && src2[2] >= '0' && src2[2] <= '9' + && src2[3] >= '0' && src2[3] <= '9' + && src2[4] >= '0' && src2[4] <= '9' + && ( src2[5] < '0' || src2[5] > '9' )) + { + int delta = 36000 * src2[1] + + 3600 * src2[2] + + 600 * src2[3] + + 60 * src2[4] + - 40260 * '0'; + if ( src2[0] == '-' ) + delta = -delta; + + noPRINT("GOT TIMEZONE: %.5s => %d\n",src2,delta); + total_time -= delta + timezone; + src = src2 + 5; + } + else + { + PRINT("timezone=%ld\n",timezone); + struct tm *tm = localtime(&total_time); + if (tm->tm_isdst) + total_time -= 3600; + } + noPRINT_TIME(total_time,"ZONE"); + + + //--- scan delta + + if (allow_delta) + { + ccp src2 = src; + while ( *src2 > 0 && *src2 <= ' ' ) + src2++; + bool minus = *src2 == '-'; + if ( minus || *src2 == '+' ) + { + src = ScanInterval(&time,&usec,src2+1); + if (minus) + { + total_time -= time; + total_usec -= usec; + } + else + { + total_time += time; + total_usec += usec; + } + } + } + + if ( total_usec < 0 ) + { + total_time -= ( 999999 - total_usec ) / 1000000; + total_usec = 1000000 - (-total_usec) % 1000000; + } + else if ( total_usec >= 1000000 ) + { + total_time += total_usec / 1000000; + total_usec %= 1000000; + } + + if (res_time) + *res_time = total_time; + if (res_usec) + *res_usec = total_usec; + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanDateTime64 +( + u64 *res_usec, // not NULL: store total micro seconds + ccp source, // source text + bool allow_delta // true: allow +|- interval +) +{ + time_t time; + u32 usec; + char *src = ScanDateTime(&time,&usec,source,allow_delta); + if (res_usec) + *res_usec = time * 1000000ull + usec; + return src; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Since 2001 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// hour support + +ccp hour2text +( + int hour, // hour to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use '-' + ccp sep_dt // NULL or separator of between date and time + // if NULL: use ' ' +) +{ + time_t tim = hour2time(hour); + struct tm tm; + gmtime_r(&tim,&tm); + + if (!sep_date) + sep_date = "-"; + + if (!sep_dt) + sep_dt = " "; + + return fw >= 13 + ? PrintCircBuf("%04d%s%02u%s%02u%s%02u", + tm.tm_year + 1900, sep_date, + tm.tm_mon + 1, sep_date, + tm.tm_mday, sep_dt, + tm.tm_hour + ) + : fw >= 11 + ? PrintCircBuf("%02d%s%02u%s%02u%s%02u", + tm.tm_year % 100, sep_date, + tm.tm_mon + 1, sep_date, + tm.tm_mday, sep_dt, + tm.tm_hour + ) + : fw >= 10 + ? PrintCircBuf("%2u.%s%02u:xx", + tm.tm_mday, sep_dt, + tm.tm_hour + ) + : PrintCircBuf("%02u%s%02u%s%02u", + tm.tm_mon + 1, sep_date, + tm.tm_mday, sep_dt, + tm.tm_hour + ); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// day support + +ccp day2text +( + int day, // day to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use '-' + ccp sep_dt // ignored +) +{ + time_t tim = day2time(day); + struct tm tm; + gmtime_r(&tim,&tm); + + if (!sep_date) + sep_date = "-"; + + return fw >= 10 + ? PrintCircBuf("%04d%s%02u%s%02u", + tm.tm_year + 1900, sep_date, + tm.tm_mon + 1, sep_date, + tm.tm_mday ) + : PrintCircBuf("%02d%s%02u%s%02u", + tm.tm_year % 100, sep_date, + tm.tm_mon + 1, sep_date, + tm.tm_mday ); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// week support + +ccp week2text +( + int week, // week to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use 'w' + ccp sep_dt // ignored +) +{ + time_t tim = week2time(week) + 4*86400; + struct tm tm; + gmtime_r(&tim,&tm); + + return PrintCircBuf("%04d%s%02u", + tm.tm_year + 1900, + sep_date ? sep_date : "w", + tm.tm_yday / 7 + 1 ); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// month support + +int time2month ( time_t tim ) +{ + struct tm tm; + gmtime_r(&tim,&tm); + return ( tm.tm_mon + 12 * tm.tm_year ) - 101*12; +} + +/////////////////////////////////////////////////////////////////////////// + +time_t month2time ( int month ) +{ + SetupTimezone(false); + struct tm tm = {0}; + + tm.tm_isdst = -1; + tm.tm_year = month / 12 + 101; + tm.tm_mon = month % 12; + tm.tm_mday = 1; + + int delta = 43200-timezone_adjust_sec; + if ( delta < 0 ) + { + delta += 86400; + tm.tm_mday++; + } + + tm.tm_hour = delta / 3600; + tm.tm_min = delta / 60 % 60; + return mktime(&tm) / 86400 * 86400; +} + +/////////////////////////////////////////////////////////////////////////// + +ccp month2text +( + int month, // month to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use '-' + ccp sep_dt // ignored +) +{ + month += 2001*12; + return PrintCircBuf("%4d%s%02u", + month/12, + sep_date ? sep_date : "-", + month%12 + 1 ); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// quarter support + +int time2quarter ( time_t tim ) +{ + struct tm tm; + gmtime_r(&tim,&tm); + return ( tm.tm_mon/3 + 4 * tm.tm_year ) - 101*4; +} + +/////////////////////////////////////////////////////////////////////////// + +time_t quarter2time ( int quarter ) +{ + SetupTimezone(false); + struct tm tm = {0}; + + tm.tm_isdst = -1; + tm.tm_year = quarter / 4 + 101; + tm.tm_mon = quarter % 4 * 3; + tm.tm_mday = 1; + + int delta = 43200-timezone_adjust_sec; + if ( delta < 0 ) + { + delta += 86400; + tm.tm_mday++; + } + + tm.tm_hour = delta / 3600; + tm.tm_min = delta / 60 % 60; + return mktime(&tm) / 86400 * 86400; +} + +/////////////////////////////////////////////////////////////////////////// + +ccp quarter2text +( + int quarter, // quarter to print + int fw, // wanted field width assuming separators are 1 char + ccp sep_date, // NULL or separator within date + // if NULL: use 'q' + ccp sep_dt // ignored +) +{ + quarter += 2001*4; + return PrintCircBuf("%4d%s%u", + quarter/4, + sep_date ? sep_date : "q", + quarter%4 + 1 ); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// year support + +int time2year ( time_t tim ) +{ + struct tm tm; + gmtime_r(&tim,&tm); + return tm.tm_year + 1900; +} + +/////////////////////////////////////////////////////////////////////////// + +time_t year2time ( int year ) +{ + SetupTimezone(false); + struct tm tm = {0}; + + tm.tm_isdst = -1; + tm.tm_year = year - 1900; + tm.tm_mday = 1; + + int delta = 43200-timezone_adjust_sec; + if ( delta < 0 ) + { + delta += 86400; + tm.tm_mday++; + } + + tm.tm_hour = delta / 3600; + tm.tm_min = delta / 60 % 60; + return mktime(&tm) / 86400 * 86400; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// urandom support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int urandom_available = 0; // <0:not, =0:not tested, >0:ok +bool use_urandom_for_myrandom = true; + +uint ReadFromUrandom ( void *dest, uint size ) +{ + static int fd = -1; + + if ( fd == -1 ) + { + if ( urandom_available < 0 ) + return 0; + #ifdef O_CLOEXEC + fd = open("/dev/urandom",O_CLOEXEC|O_NONBLOCK); + #else + fd = open("/dev/urandom",O_NONBLOCK); + #endif + if ( fd == -1 ) + { + urandom_available = -1; + return 0; + } + #ifndef O_CLOEXEC + fcntl(fd,F_SETFD,FD_CLOEXEC); + #endif + urandom_available = 1; + } + + u8 *buf = dest; + while ( size > 0 ) + { + ssize_t stat = read(fd,buf,size); + if ( stat < 0 ) + { + urandom_available = -1; + close(fd); + return 0; + } + buf += stat; + size -= stat; + } + + return buf - (u8*)dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// random numbers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------ +// Algorithm: The Art of Computer Programming, part II, section 3.6 +// by Donald E. Knuth +//------------------------------------------------------------------ + +const u32 RANDOM32_C_ADD = 2 * 197731421; // 2 Primzahlen +const u32 RANDOM32_COUNT_BASE = 4294967; // Primzahl == ~UINT_MAX32/1000; + +static int random32_a_index = -1; // Index in die a-Tabelle +static u32 random32_count = 1; // Abwärts-Zähler bis zum Wechsel von a,c +static u32 random32_a, + random32_c, + random32_X; // Die letzten Werte + +static u32 random32_a_tab[] = // Init-Tabelle +{ + 0xbb40e62d, 0x3dc8f2f5, 0xdc024635, 0x7a5b6c8d, + 0x583feb95, 0x91e06dbd, 0xa7ec03f5, 0 +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u32 MyRandom ( u32 max ) +{ + if (use_urandom_for_myrandom) + { + u32 res32; + if (ReadFromUrandom(&res32,sizeof(res32))) + { + if (!max) + return res32; + + u64 res64 = (u64)max * res32; + return res64 >> 32; + } + use_urandom_for_myrandom = false; + } + + if (!--random32_count) + { + // Neue Berechnung von random32_a und random32_c fällig + + if ( random32_a_index < 0 ) + { + // allererste Initialisierung auf Zeitbasis + + MySeedByTime(); + } + else + { + random32_c += RANDOM32_C_ADD; + random32_a = random32_a_tab[++random32_a_index]; + if (!random32_a) + { + random32_a_index = 0; + random32_a = random32_a_tab[0]; + } + + random32_count = RANDOM32_COUNT_BASE; + } + } + + // Jetzt erfolgt die eigentliche Berechnung + + random32_X = random32_a * random32_X + random32_c; + + if (!max) + return random32_X; + + u64 res = (u64)max * random32_X; + return res >> 32; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 MySeed ( u64 base ) +{ + uint a_tab_len = 0; + while (random32_a_tab[a_tab_len]) + a_tab_len++; + const u32 base32 = base / a_tab_len; + + random32_a_index = (int)( base % a_tab_len ); + random32_a = random32_a_tab[random32_a_index]; + random32_c = ( base32 & 15 ) * RANDOM32_C_ADD + 1; + random32_X = base32 ^ ( base >> 32 ); + random32_count = RANDOM32_COUNT_BASE; + + return base; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 MySeedByTime() +{ + #if WIN_SZS_LIB + + u64 time; + QueryPerformanceCounter((LARGE_INTEGER*)&time); + return MySeed(time); + + #else + + struct timeval tval; + gettimeofday(&tval,NULL); + return MySeed( tval.tv_sec ^ tval.tv_usec ); + + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void MyRandomFill ( void * buf, size_t size ) +{ + size_t xsize = size / sizeof(u32); + if (xsize) + { + size -= xsize * sizeof(u32); + u32 * ptr = buf; + while ( xsize-- > 0 ) + *ptr++ = MyRandom(0); + buf = ptr; + } + + u8 * ptr = buf; + while ( size-- > 0 ) + *ptr++ = MyRandom(0); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// UUID /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CreateUUID ( uuid_buf_t dest ) +{ + DASSERT(dest); + const uint stat = ReadFromUrandom(dest,sizeof(uuid_buf_t)); + if (!stat) + MyRandomFill(dest,sizeof(uuid_buf_t)); + + dest[6] = dest[6] & 0x0f | 0x40; + dest[8] = dest[8] & 0x3f | 0x80; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CreateTextUUID ( char *buf, uint bufsize ) +{ + uuid_buf_t uuid; + CreateUUID(uuid); + return PrintUUID(buf,bufsize,uuid); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintUUID ( char *buf, uint bufsize, uuid_buf_t uuid ) +{ + return snprintf(buf,bufsize, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid[0],uuid[1],uuid[2],uuid[3], + uuid[4],uuid[5], uuid[6],uuid[7], uuid[8],uuid[9], + uuid[10],uuid[11],uuid[12],uuid[13],uuid[14],uuid[15] ); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanUUID ( uuid_buf_t uuid, ccp source ) +{ + memset(uuid,0,sizeof(uuid_buf_t)); + + uint i; + ccp src = source; + for ( i = 0; i < sizeof(uuid_buf_t); i++ ) + { + if ( *src == '-' ) + src++; + + uint num; + ccp end = ScanNumber(&num,src,0,16,2); + if ( end != src+2 ) + return (char*)source; + uuid[i] = num; + src = end; + } + return (char*)src; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// gcd(), lcm() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint gcd ( uint n1, uint n2 ) // greatest common divisor, german: ggt +{ + if ( !n1 || !n2 ) + return 0; + + if ( n1 > n2 ) + { + uint temp = n1; + n1 = n2; + n2 = temp; + } + + while ( n1 != 1 ) + { + DASSERT ( n1 > 1 ); + DASSERT ( n2 >= n1 ); + + uint temp = n2 % n1; + if ( !temp ) + break; + n2 = n1; + n1 = temp; + } + return n1; +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 gcd32 ( u32 n1, u32 n2 ) // greatest common divisor, german: ggt +{ + if ( !n1 || !n2 ) + return 0; + + if ( n1 > n2 ) + { + u32 temp = n1; + n1 = n2; + n2 = temp; + } + + while ( n1 != 1 ) + { + DASSERT ( n1 > 1 ); + DASSERT ( n2 > n1 ); + + u32 temp = n2 % n1; + if ( !temp ) + break; + n2 = n1; + n1 = temp; + } + return n1; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 gcd64 ( u64 n1, u64 n2 ) // greatest common divisor, german: ggt +{ + if ( !n1 || !n2 ) + return 0; + + if ( n1 > n2 ) + { + u64 temp = n1; + n1 = n2; + n2 = temp; + } + + while ( n1 != 1 ) + { + DASSERT ( n1 > 1 ); + DASSERT ( n2 > n1 ); + + u64 temp = n2 % n1; + if ( !temp ) + break; + n2 = n1; + n1 = temp; + } + return n1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint lcm ( uint n1, uint n2 ) // lowest common multiple, german: kgv +{ + uint g = gcd(n1,n2); + if ( g > 0 ) + { + n1 /= g; + g = n1 * n2; + if ( g < n1 || g < n2 ) // overflow? + g = 0; + } + return g; +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 lcm32 ( u32 n1, u32 n2 ) // lowest common multiple, german: kgv +{ + u32 g = gcd32(n1,n2); + if ( g > 0 ) + { + n1 /= g; + g = n1 * n2; + if ( g < n1 || g < n2 ) // overflow? + g = 0; + } + return g; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 lcm64 ( u64 n1, u64 n2 ) // lowest common multiple, german: kgv +{ + u64 g = gcd64(n1,n2); + if ( g > 0 ) + { + n1 /= g; + g = n1 * n2; + if ( g < n1 || g < n2 ) // overflow? + g = 0; + } + return g; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan number /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define NN NUMBER_NULL +#define NC NUMBER_CONTROL +#define NL NUMBER_LINE +#define NS NUMBER_SPACE +#define NT NUMBER_TIE +#define NP NUMBER_SEPARATE +#define NX NUMBER_OTHER + +const char DigitTable[] = +{ + NN, NC, NC, NC, NC, NC, NC, NC, NC, NS, NL, NC, NS, NL, NC, NC, + NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, + NS, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NP, NT, NT, NX, // !"#$%&' ()*+,-./ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, NT, NP, NX, NX, NX, NX, // 01234567 89:;<=>? + + NX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // @ABCDEFG HIJKLMNO + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NX, NX, NX, NX, NX, // PQRSTUVW XYZ[\]^_ + NX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // `abcdefg hijklmno + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, NX, NX, NX, NX, NX, // pqrstuvw xyz{|}~ + + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, + + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, + NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, NX, +}; + +#undef NN +#undef NC +#undef NL +#undef NS +#undef NT +#undef NP +#undef NX + +/////////////////////////////////////////////////////////////////////////////// + +u64 ScanDigits +( + // same as ScanNumber(), but other interface + // returns the scanned number + + ccp *source, // pointer to source string, modified + ccp end_source, // NULL or end of 'source' + uint intbase, // integer base, 2..36 + int maxchar, // max number of digits to read + uint *count // not NULL: store number of scanned digits here +) +{ + DASSERT(source); + DASSERT( intbase >= 2 && intbase <= 36 ); + + ccp src = *source; + u64 num = 0; + while ( maxchar-- && ( !end_source || src < end_source ) ) + { + const u8 digit = (u8)DigitTable[(u8)*src]; + if ( digit >= intbase ) + break; + num = num * intbase + digit; + src++; + } + if (count) + *count = src - *source; + *source = src; + return num; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNumber +( + // same as ScanDigits(), but other interface + // returns a pointer to the first not used character + + uint *dest_num, // store result here, never NULL + ccp src, // pointer to source string, modified + ccp src_end, // NULL or end of 'src' + uint intbase, // integer base, 2..36 + int maxchar // max number of digits to read +) +{ + DASSERT(dest_num); + uint num = 0; + if (src) + { + while ( maxchar-- && ( !src_end || src < src_end ) ) + { + const uint n = (uint)DigitTable[*(uchar*)src]; + if ( n >= (uint)intbase ) + break; + num = num * intbase + n; + src++; + } + } + *dest_num = num; + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanEscape +( + // returns a pointer to the first not used character + + uint *dest_code, // store result here, never NULL + ccp src, // pointer to source string (behind escape char) + ccp src_end // NULL or end of 'src' +) +{ + DASSERT(dest_code); + DASSERT(src); + if ( !src || ( src_end ? src >= src_end : !*src ) ) + { + *dest_code = 0; + return (char*)src; + } + + uint code = 0; + switch (*src++) + { + case '\\': code = '\\'; break; + case 'a': code = '\a'; break; + case 'b': code = '\b'; break; + case 'E': + case 'e': code = '\e'; break; + case 'f': code = '\f'; break; + case 'n': code = '\n'; break; + case 'r': code = '\r'; break; + case 't': code = '\t'; break; + case 'v': code = '\v'; break; + + case 'x': + src = ScanNumber(&code,src,src_end,16,2); + break; + + case 'u': + src = ScanNumber(&code,src,src_end,16,4); + break; + + case 'U': + src = ScanNumber(&code,src,src_end,16,8); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + src = ScanNumber(&code,src-1,src_end,8,3); + break; + + default: + code = src[-1]; + break; + } + + *dest_code = code; + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ScanHexString +( + // return the number of written bytes + + void *buf, // write scanned data here + uint buf_size, // size of buf + ccp *source, // pointer to source string, modified + ccp end_source, // NULL or end of 'source' + bool allow_tie // allow chars ' .:-' and TAB,VT as byte separator +) +{ + DASSERT(buf); + DASSERT(source); + + u8 *dest = buf; + uint count = 0; + while ( count < buf_size ) + { + ccp src = *source; + if (allow_tie) + { + while ( !end_source || src < end_source ) + { + const char res = DigitTable[(u8)*src]; + if ( res != NUMBER_SPACE && res != NUMBER_TIE ) + break; + src++; + } + *source = src; + } + + while ( !end_source || src < end_source ) + { + const char res = DigitTable[(u8)*src]; + if ( res < 0 || res >= 16 ) + break; + src++; + } + + uint n_digits = src - *source; + if (!n_digits) + break; + + while ( n_digits > 0 ) + { + uint scanned; + uint num = ScanDigits( source, end_source, 16, + n_digits & 1 ? 1 : 2, &scanned ); + if (!scanned) + return count; + + n_digits -= scanned; + *dest++ = num; + count++; + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanS32 +( + // return 'source' on error + + s32 *res_num, // not NULL: store result (only on success) + ccp source, // NULL or source text + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +) +{ + if (!source) + return 0; + + ccp src = source; + while ( *src > 0 && *src <= ' ' ) + src++; + + const bool minus = *src == '-'; + if ( minus || *src == '+' ) + { + src++; + while ( *src > 0 && *src <= ' ' ) + src++; + } + const uint base = src[0] == '0' && ( src[1] == 'x' || src[1] == 'X' ) + ? 16 : default_base; + char *end; + const s32 num = strtoul( src, &end, base ); + if ( (ccp)end > src ) + { + if (res_num) + *res_num = minus ? -num : num; + return end; + } + + return (char*)source; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanS64 +( + // return 'source' on error + + s64 *res_num, // not NULL: store result (only on success) + ccp source, // NULL or source text + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +) +{ + if (!source) + return 0; + + ccp src = source; + while ( *src > 0 && *src <= ' ' ) + src++; + + const bool minus = *src == '-'; + if ( minus || *src == '+' ) + { + src++; + while ( *src > 0 && *src <= ' ' ) + src++; + } + const uint base = src[0] == '0' && ( src[1] == 'x' || src[1] == 'X' ) + ? 16 : default_base; + char *end; + const s64 num = strtoull( src, &end, base ); + if ( (ccp)end > src ) + { + if (res_num) + *res_num = minus ? -num : num; + return end; + } + + return (char*)source; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// strto*() replacements, base ion Scan*() with better base support + +long int str2l ( const char *nptr, char **endptr, int base ) +{ + s32 res = 0; + char *end = ScanS32(&res,nptr,base); + if (endptr) + *endptr = end; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +long long int str2ll ( const char *nptr, char **endptr, int base ) +{ + s64 res = 0; + char *end = ScanS64(&res,nptr,base); + if (endptr) + *endptr = end; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +unsigned long int str2ul ( const char *nptr, char **endptr, int base ) +{ + u32 res = 0; + char *end = ScanU32(&res,nptr,base); + if (endptr) + *endptr = end; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +unsigned long long int str2ull ( const char *nptr, char **endptr, int base ) +{ + u64 res = 0; + char *end = ScanU64(&res,nptr,base); + if (endptr) + *endptr = end; + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// round float/double (zero LSB) /////////////// +/////////////////////////////////////////////////////////////////////////////// + +float RoundF2bytes ( float f ) +{ + union { float f; u32 u; } x; + x.f = f; + if ( x.u & 0x8000 ) + { + x.u &= 0xffff0000; + int exp; + const float frac = frexpf(x.f,&exp); + x.f = ldexpf(frac+0.00585938,exp); + } + + x.u &= 0xffff0000; + return x.f; +} + +//----------------------------------------------------------------------------- + +float RoundF3bytes ( float f ) +{ + union { float f; u32 u; } x; + x.f = f; + if ( x.u & 0x80 ) + { + x.u &= 0xffffff00; + int exp; + const float frac = frexpf(x.f,&exp); + x.f = ldexpf(frac+0.00002289,exp); + } + + x.u &= 0xffffff00; + return x.f; +} + +//----------------------------------------------------------------------------- + +double RoundD6bytes ( double d ) +{ + union { double d; u64 u; } x; + x.d = d; + if ( x.u & 0x8000 ) + { + x.u &= 0xffffffffffff0000ull; + int exp; + const double frac = frexp(x.d,&exp); + x.d = ldexp(frac+0.000000000010913936,exp); + } + + x.u &= 0xffffffffffff0000ull; + return x.d; +} + +//----------------------------------------------------------------------------- + +double RoundD7bytes ( double d ) +{ + union { double d; u64 u; } x; + x.d = d; + if ( x.u & 0x80 ) + { + x.u &= 0xffffffffffffff00ull; + int exp; + const double frac = frexp(x.d,&exp); + x.d = ldexp(frac+0.000000000000042633,exp); + } + + x.u &= 0xffffffffffffff00ull; + return x.d; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print number /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberU4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 5 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + if ( num < 1000000000 ) // 1e9 + { + u32 n32 = num; + if ( !n32 && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, aligned ? " -" : "-" ); + else if ( n32 < 10000 ) + snprintf(buf,buf_size, aligned ? "%4u" : "%u", n32 ); + else if ( n32 < 1000000 ) + snprintf(buf,buf_size, aligned ? "%3uk" : "%uk", n32/1000 ); + else if ( n32 < 10000000 ) + { + n32 /= 100000; + snprintf(buf,buf_size,"%u.%uM", n32/10, n32%10 ); + } + else + snprintf(buf,buf_size, aligned ? "%3uM" : "%uM", n32/1000000 ); + } + else + { + u32 n32; + ccp factor = "GTPE"; + if ( num < 1000000000000000ull ) // 1e15 + n32 = num / 100000000; // 1e8 + else + { + factor += 2; + n32 = num / 100000000000000ull; // 1e14 + } + + while (*factor) + { + if ( n32 < 100 ) + { + snprintf(buf,buf_size,"%u.%u%c", n32/10, n32%10, *factor ); + break; + } + else if ( n32 < 10000 ) + { + snprintf(buf,buf_size, aligned ? "%3u%c" : "%u%c", n32/10, *factor ); + break; + } + n32 /= 1000; + factor++; + } + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberU5 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 6 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + if ( num < 1000000000 ) // 1e9 + { + u32 n32 = num; + if ( !n32 && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, aligned ? " -" : "-" ); + else if ( n32 < 100000 ) + snprintf(buf,buf_size, aligned ? "%5u" : "%u", n32 ); + else if ( n32 < 10000000 ) + snprintf(buf,buf_size, aligned ? "%4uk" : "%uk", n32/1000 ); + else + snprintf(buf,buf_size, aligned ? "%4uM" : "%uM", n32/1000000 ); + } + else + { + u32 n32; + ccp factor = "MGTPE"; + if ( num < 1000000000000000ull ) // 1e15 + n32 = num / 1000000; // 1e6 + else + { + factor += 2; + n32 = num / 1000000000000ull; // 1e12 + } + + while (*factor) + { + if ( n32 < 10000 ) + { + snprintf(buf,buf_size, aligned ? "%4u%c" : "%u%c", n32, *factor ); + break; + } + n32 /= 1000; + factor++; + } + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberU6 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 7 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + if ( num < 1000000000 ) // 1e9 + { + u32 n32 = num; + if ( !n32 && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, aligned ? " -" : "-" ); + else if ( n32 < 1000000 ) + snprintf(buf,buf_size, aligned ? "%6u" : "%u", n32 ); + else if ( n32 < 100000000 ) + snprintf(buf,buf_size, aligned ? "%5uk" : "%uk", n32/1000 ); + else + snprintf(buf,buf_size, aligned ? "%5uM" : "%uM", n32/1000000 ); + } + else + { + u32 n32; + ccp factor = "MGTPE"; + if ( num < 1000000000000000ull ) // 1e15 + n32 = num / 1000000; // 1e6 + else + { + factor += 2; + n32 = num / 1000000000000ull; // 1e12 + } + + while (*factor) + { + if ( n32 < 100000 ) + { + snprintf(buf,buf_size, aligned ? "%5u%c" : "%u%c", n32, *factor ); + break; + } + n32 /= 1000; + factor++; + } + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberU7 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 8 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + if ( num < 1000000000 ) // 1e9 + { + u32 n32 = num; + if ( !n32 && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, aligned ? " -" : "-" ); + else if ( n32 < 10000000 ) + snprintf(buf,buf_size, aligned ? "%7u" : "%u", n32 ); + else + snprintf(buf,buf_size, aligned ? "%6uk" : "%uk", n32/1000 ); + } + else + { + u32 n32; + ccp factor = "MGTPE"; + if ( num < 1000000000000000ull ) // 1e15 + n32 = num / 1000000; // 1e6 + else + { + factor += 2; + n32 = num / 1000000000000ull; // 1e12 + } + + while (*factor) + { + if ( n32 < 1000000 ) + { + snprintf(buf,buf_size, aligned ? "%6u%c" : "%u%c", n32, *factor ); + break; + } + n32 /= 1000; + factor++; + } + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberS5 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + s64 num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH +) +{ + if ( num >= 0 ) + return PrintNumberU5(buf,buf_size,num,mode); + + if (!buf) + buf = GetCircBuf( buf_size = 6 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + if ( num > -1000000000 ) // -1e9 + { + s32 n32 = num; + if ( n32 > -10000 ) + snprintf(buf,buf_size, aligned ? "%5d" : "%d", n32 ); + else if ( n32 > -1000000 ) + snprintf(buf,buf_size, aligned ? "%4dk" : "%dk", n32/1000 ); + else if ( n32 > -10000000 ) + { + n32 /= -100000; + snprintf(buf,buf_size,"-%u.%uM", n32/10, n32%10 ); + } + else + snprintf(buf,buf_size, aligned ? "%4dM" : "%dM", n32/1000000 ); + } + else + { + s32 n32; + ccp factor = "GTPE"; + if ( num > -1000000000000000ull ) // -1e15 + n32 = num / 100000000; // -1e8 + else + { + factor += 2; + n32 = num / 100000000000000ull; // -1e14 + } + + while (*factor) + { + if ( n32 > -100 ) + { + snprintf(buf,buf_size,"-%u.%u%c", -n32/10, -n32%10, *factor ); + break; + } + else if ( n32 > -10000 ) + { + snprintf(buf,buf_size, aligned ? "%4d%c" : "%d%c", n32/10, *factor ); + break; + } + n32 /= 1000; + factor++; + } + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberS6 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + s64 num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH +) +{ + if ( num >= 0 ) + return PrintNumberU6(buf,buf_size,num,mode); + + if (!buf) + buf = GetCircBuf( buf_size = 7 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + if ( num > -1000000000 ) // -1e9 + { + s32 n32 = num; + if ( n32 > -100000 ) + snprintf(buf,buf_size, aligned ? "%6d" : "%d", n32 ); + else if ( n32 > -10000000 ) + snprintf(buf,buf_size, aligned ? "%5dk" : "%dk", n32/1000 ); + else + snprintf(buf,buf_size, aligned ? "%5dM" : "%dM", n32/1000000 ); + } + else + { + s32 n32; + ccp factor = "MGTPE"; + if ( num > -1000000000000000ull ) // -1e15 + n32 = num / 1000000; // -1e6 + else + { + factor += 2; + n32 = num / 1000000000000ull; // -1e12 + } + + while (*factor) + { + if ( n32 > -10000 ) + { + snprintf(buf,buf_size, aligned ? "%5d%c" : "%d%c", n32, *factor ); + break; + } + n32 /= 1000; + factor++; + } + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberS7 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + s64 num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH +) +{ + if ( num >= 0 ) + return PrintNumberU7(buf,buf_size,num,mode); + + if (!buf) + buf = GetCircBuf( buf_size = 8 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + if ( num > -100000000 ) // -1e8 + { + s32 n32 = num; + if ( n32 > -1000000 ) + snprintf(buf,buf_size, aligned ? "%7d" : "%d", n32 ); + else + snprintf(buf,buf_size, aligned ? "%6dk" : "%dk", n32/1000 ); + } + else + { + s32 n32; + ccp factor = "MGTPE"; + if ( num > -1000000000000000ull ) // -1e15 + n32 = num / 1000000; // -1e6 + else + { + factor += 2; + n32 = num / 1000000000000ull; // -1e12 + } + + while (*factor) + { + if ( n32 > -100000 ) + { + snprintf(buf,buf_size, aligned ? "%6d%c" : "%d%c", n32, *factor ); + break; + } + n32 /= 1000; + factor++; + } + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberD5 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + double num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 16 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + double abs = fabs(num); + if ( abs >= 1e2 ) + { + if ( num <= 0 ) + { + if ( abs <= S64_MAX ) + return PrintNumberS5(buf,buf_size,double2int64(num),mode); + return "-****"; + } + else + { + if ( abs <= U64_MAX ) + return PrintNumberU5(buf,buf_size,double2int64(num),mode); + } + snprintf(buf,buf_size,"%5.0e",num); + return buf; + } + + if ( num < 0.0 ) + { + if ( abs < 1e-2 ) + return aligned ? " -0" : "-0"; + + if ( abs < 10.0 ) + snprintf(buf,buf_size,"%5.2f",num); + else + snprintf(buf,buf_size,"%5.1f",num); + } + else + { + if ( num < 1e-3 ) + return aligned ? " 0" : "0"; + + if ( num < 10.0 ) + snprintf(buf,buf_size,"%5.3f",num); + else + snprintf(buf,buf_size,"%5.2f",num); + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberD6 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + double num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 7 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + double abs = fabs(num); + if ( abs >= 1e3 ) + { + if ( num <= 0 ) + { + if ( abs <= S64_MAX ) + return PrintNumberS6(buf,buf_size,double2int64(num),mode); + } + else + { + if ( abs <= U64_MAX ) + return PrintNumberU6(buf,buf_size,double2int64(num),mode); + } + snprintf(buf,buf_size,"%6.0e",num); + return buf; + } + + if ( num < 0.0 ) + { + if ( abs < 1e-3 ) + return aligned ? " -0" : "-0"; + + if ( abs < 10.0 ) + snprintf(buf,buf_size,"%6.3f",num); + else if ( abs < 100.0 ) + snprintf(buf,buf_size,"%6.2f",num); + else + snprintf(buf,buf_size,"%6.1f",num); + } + else + { + if ( num < 1e-4 ) + return aligned ? " 0" : "0"; + + if ( num < 10.0 ) + snprintf(buf,buf_size,"%6.4f",num); + else if ( num < 100.0 ) + snprintf(buf,buf_size,"%6.3f",num); + else + snprintf(buf,buf_size,"%6.2f",num); + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintNumberD7 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + double num, // number to print + sizeform_mode_t mode // any of DC_SFORM_ALIGN +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 18 ); + const sizeform_mode_t aligned = mode & DC_SFORM_ALIGN; + + double abs = fabs(num); + if ( abs >= 1e4 ) + { + if ( num <= 0 ) + { + if ( abs <= S64_MAX ) + return PrintNumberS7(buf,buf_size,double2int64(num),mode); + snprintf(buf,buf_size,"%7.0e",num); + return buf; + } + else + { + if ( abs <= U64_MAX ) + return PrintNumberU7(buf,buf_size,double2int64(num),mode); + } + snprintf(buf,buf_size,"%7.1e",num); + return buf; + } + + if ( num < 0.0 ) + { + if ( abs < 1e-4 ) + return aligned ? " -0" : "-0"; + + if ( abs < 10.0 ) + snprintf(buf,buf_size,"%7.4f",num); + else if ( abs < 100.0 ) + snprintf(buf,buf_size,"%7.3f",num); + else if ( abs < 1000.0 ) + snprintf(buf,buf_size,"%7.2f",num); + else + snprintf(buf,buf_size,"%7.1f",num); + } + else + { + if ( num < 1e-5 ) + return aligned ? " 0" : "0"; + + if ( num < 10.0 ) + snprintf(buf,buf_size,"%7.5f",num); + else if ( num < 100.0 ) + snprintf(buf,buf_size,"%7.4f",num); + else if ( num < 1000.0 ) + snprintf(buf,buf_size,"%7.3f",num); + else + snprintf(buf,buf_size,"%7.2f",num); + } + + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print size /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp dc_size_tab_1000[DC_SIZE_N_MODES+1] = +{ + 0, // DC_SIZE_DEFAULT + 0, // DC_SIZE_AUTO + + "B", // DC_SIZE_BYTES + "kB", // DC_SIZE_K + "MB", // DC_SIZE_M + "GB", // DC_SIZE_G + "TB", // DC_SIZE_T + "PB", // DC_SIZE_P + "EB", // DC_SIZE_E + + 0 // all others +}; + +//----------------------------------------------------------------------------- + +ccp dc_size_tab_1024[DC_SIZE_N_MODES+1] = +{ + 0, // DC_SIZE_DEFAULT + 0, // DC_SIZE_AUTO + + "B", // DC_SIZE_BYTES + "KiB", // DC_SIZE_K + "MiB", // DC_SIZE_M + "GiB", // DC_SIZE_G + "TiB", // DC_SIZE_T + "PiB", // DC_SIZE_P + "EiB", // DC_SIZE_E + + 0 // all others +}; + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSizeUnit // get a unit for column headers +( + size_mode_t mode, // print mode + ccp if_invalid // output for invalid modes +) +{ + const bool force_1000 = ( mode & DC_SIZE_M_BASE ) == DC_SIZE_F_1000; + + switch ( mode & DC_SIZE_M_MODE ) + { + //---- SI and IEC units + + case DC_SIZE_DEFAULT: + case DC_SIZE_AUTO: return "size"; + case DC_SIZE_BYTES: return "bytes"; + + case DC_SIZE_K: return force_1000 ? "kB" : "KiB"; + case DC_SIZE_M: return force_1000 ? "MB" : "MiB"; + case DC_SIZE_G: return force_1000 ? "GB" : "GiB"; + case DC_SIZE_T: return force_1000 ? "TB" : "TiB"; + case DC_SIZE_P: return force_1000 ? "PB" : "PiB"; + case DC_SIZE_E: return force_1000 ? "EB" : "EiB"; + } + + return if_invalid; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetSizeFW // get a good value field width +( + size_mode_t mode, // print mode + int min_fw // minimum fw => return max(calc_fw,min_fw); + // this value is also returned for invalid modes +) +{ + int fw = mode & (DC_SIZE_F_AUTO_UNIT|DC_SIZE_F_NO_UNIT) ? 0 : 4; + + switch ( mode & DC_SIZE_M_MODE ) + { + case DC_SIZE_DEFAULT: + case DC_SIZE_AUTO: + if ( !(mode & DC_SIZE_F_NO_UNIT) ) + fw = 4; + fw += 4; + break; + + case DC_SIZE_BYTES: fw += 10; break; + case DC_SIZE_K: fw += 7; break; + case DC_SIZE_M: fw += 4; break; + case DC_SIZE_G: fw += 3; break; + case DC_SIZE_T: fw += 3; break; + case DC_SIZE_P: fw += 3; break; + case DC_SIZE_E: fw += 3; break; + + default: fw = min_fw; + } + + return fw > min_fw ? fw : min_fw; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[PrintSize]] + +char * PrintSize +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 size, // size to print + sizeform_mode_t sform_mode, // output format, bit field + size_mode_t mode // print mode +) +{ + DASSERT( DC_SIZE_N_MODES <= DC_SIZE_M_MODE + 1 ); + + + if (!buf) + buf = GetCircBuf( buf_size = 20 ); + + const bool force_1000 = ( mode & DC_SIZE_M_BASE ) == DC_SIZE_F_1000; + + // ??? [[2do]] follow 'sform_mode' + const sizeform_mode_t aligned = sform_mode & DC_SFORM_ALIGN; + + switch ( mode & DC_SIZE_M_MODE ) + { + //---- SI and IEC units + + case DC_SIZE_BYTES: + snprintf(buf,buf_size, aligned ? "%6llu B" : "%llu B", size ); + break; + + case DC_SIZE_K: + if (force_1000) + snprintf(buf,buf_size, aligned ? "%5llu kB" : "%llu kB", + ( size + KB_SI/2 ) / KB_SI ); + else + snprintf(buf,buf_size, aligned ? "%4llu KiB" : "%llu KiB", + ( size + KiB/2 ) / KiB ); + break; + + case DC_SIZE_M: + if (force_1000) + snprintf(buf,buf_size, aligned ? "%5llu MB" : "%llu MB", + ( size + MB_SI /2 ) / MB_SI ); + else + snprintf(buf,buf_size, aligned ? "%4llu MiB" : "%llu MiB", + ( size + MiB /2 ) / MiB ); + break; + + case DC_SIZE_G: + if (force_1000) + snprintf(buf,buf_size, aligned ? "%5llu GB" : "%llu GB", + ( size + GB_SI/2 ) / GB_SI ); + else + snprintf(buf,buf_size, aligned ? "%4llu GiB" : "%llu GiB", + ( size + GiB/2 ) / GiB ); + break; + + case DC_SIZE_T: + if (force_1000) + snprintf(buf,buf_size, aligned ? "%5llu TB" : "%llu TB", + ( size + TB_SI/2 ) / TB_SI ); + else + snprintf(buf,buf_size, aligned ? "%4llu TiB" : "%llu TiB", + ( size + TiB/2 ) / TiB ); + break; + + case DC_SIZE_P: + if (force_1000) + snprintf(buf,buf_size, aligned ? "%5llu PB" : "%llu PB", + ( size + PB_SI/2 ) / PB_SI ); + else + snprintf(buf,buf_size, aligned ? "%4llu PiB" : "%llu PiB", + ( size + PiB/2 ) / PiB ); + break; + + case DC_SIZE_E: + if (force_1000) + snprintf(buf,buf_size, aligned ? "%5llu EB" : "%llu EB", + ( size + EB_SI/2 ) / EB_SI ); + else + snprintf(buf,buf_size, aligned ? "%4llu EiB" : "%llu EiB", + ( size + EiB/2 ) / EiB ); + break; + + + //----- default == auto + + default: + buf = force_1000 + ? PrintSize1000(buf,buf_size,size,sform_mode) + : PrintSize1024(buf,buf_size,size,sform_mode); + if ( !(mode&DC_SIZE_F_NO_UNIT) ) + return buf; + } + + if ( mode & (DC_SIZE_F_AUTO_UNIT|DC_SIZE_F_NO_UNIT) ) + { + char * ptr = buf; + while ( *ptr == ' ' ) + ptr++; + while ( *ptr && *ptr != ' ' ) + ptr++; + *ptr = 0; + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[PrintSize1000]] + +char * PrintSize1000 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 size, // size to print + sizeform_mode_t sform_mode // output format, bit field +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 20 ); + + u64 num; + size_mode_t unit; + + u64 mb = (size+MB_SI/2)/MB_SI; // maybe an overflow => extra if + if ( mb < 10000 && size < EB_SI ) + { + u64 kb = (size+KB_SI/2)/KB_SI; + if ( kb < 10 ) + { + num = size; + unit = DC_SIZE_BYTES; + } + else if ( kb < 10000 ) + { + num = kb; + unit = DC_SIZE_K; + } + else + { + num = mb; + unit = DC_SIZE_M; + } + } + else + { + mb = size / MB_SI; // recalc because of possible overflow + u64 tb = (mb+MB_SI/2)/MB_SI; + if ( tb < 10000 ) + { + if ( tb < 10 ) + { + num = (mb+KB_SI/2)/KB_SI; + unit = DC_SIZE_G; + } + else + { + num = tb; + unit = DC_SIZE_T; + } + } + else + { + u64 pb = (mb+GB_SI/2)/GB_SI; + if ( pb < 10000 ) + { + num = pb; + unit = DC_SIZE_P; + } + else + { + num = (mb+TB_SI/2)/TB_SI; + unit = DC_SIZE_E; + } + } + } + + if ( sform_mode & DC_SFORM_INC + && num + && !( num % 1000 ) + && dc_size_tab_1000[unit+1] + ) + { + unit++; + num /= 1000; + } + + char unit_buf[8]; + char *dest = unit_buf; + if ( !(sform_mode & DC_SFORM_NARROW) ) + *dest++ = ' '; + + if ( sform_mode & DC_SFORM_UNIT1 ) + { + if ( unit != DC_SIZE_BYTES ) + *dest++ = dc_size_tab_1000[unit][0]; + else if ( sform_mode & DC_SFORM_ALIGN ) + *dest++ = ' '; + else + dest = unit_buf; // not any char + *dest = 0; + } + else if ( sform_mode & DC_SFORM_ALIGN ) + snprintf(dest,sizeof(unit_buf)-1,"%-3s",dc_size_tab_1000[unit]); + else + StringCopyS(dest,sizeof(unit_buf)-1,dc_size_tab_1000[unit]); + + if ( !num && unit == DC_SIZE_BYTES && sform_mode & DC_SFORM_DASH ) + { + if ( sform_mode & DC_SFORM_ALIGN ) + snprintf(buf,buf_size," -%s",unit_buf); + else + StringCopyS(buf,buf_size,"-"); + } + if ( sform_mode & DC_SFORM_ALIGN ) + snprintf(buf,buf_size,"%4llu%s",num,unit_buf); + else + snprintf(buf,buf_size,"%llu%s",num,unit_buf); + + return buf; +}; + +/////////////////////////////////////////////////////////////////////////////// +// [[PrintSize1024]] + +char * PrintSize1024 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u64 size, // size to print + sizeform_mode_t sform_mode // output format, bit field +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 20 ); + + u64 num; + size_mode_t unit; + + u64 mib = (size+MiB/2)/MiB; // maybe an overflow => extra if + if ( mib < 10000 && size < EiB ) + { + u64 kib = (size+KiB/2)/KiB; + if ( kib < 10 ) + { + num = size; + unit = DC_SIZE_BYTES; + } + else if ( kib < 10000 ) + { + num = kib; + unit = DC_SIZE_K; + } + else + { + num = mib; + unit = DC_SIZE_M; + } + } + else + { + mib = size / MiB; // recalc because of possible overflow + u64 tib = (mib+MiB/2)/MiB; + if ( tib < 10000 ) + { + if ( tib < 10 ) + { + num = (mib+KiB/2)/KiB; + unit = DC_SIZE_G; + } + else + { + num = tib; + unit = DC_SIZE_T; + } + } + else + { + u64 pib = (mib+GiB/2)/GiB; + if ( pib < 10000 ) + { + num = pib; + unit = DC_SIZE_P; + } + else + { + num = (mib+TiB/2)/TiB; + unit = DC_SIZE_E; + } + } + } + + if ( sform_mode & DC_SFORM_INC + && num + && !( num & 0x3ff ) + && dc_size_tab_1024[unit+1] + ) + { + unit++; + num /= 0x400; + } + + char unit_buf[8]; + char *dest = unit_buf; + if ( !(sform_mode & DC_SFORM_NARROW) ) + *dest++ = ' '; + + if ( sform_mode & DC_SFORM_UNIT1 ) + { + if ( unit != DC_SIZE_BYTES ) + *dest++ = dc_size_tab_1000[unit][0]; + else if ( sform_mode & DC_SFORM_ALIGN ) + *dest++ = ' '; + else + dest = unit_buf; // not any char + *dest = 0; + } + else if ( sform_mode & DC_SFORM_ALIGN ) + snprintf(dest,sizeof(unit_buf)-1,"%-3s",dc_size_tab_1024[unit]); + else + StringCopyS(dest,sizeof(unit_buf)-1,dc_size_tab_1024[unit]); + + if ( !num && unit == DC_SIZE_BYTES && sform_mode & DC_SFORM_DASH ) + { + if ( sform_mode & DC_SFORM_ALIGN ) + snprintf(buf,buf_size," -%s",unit_buf); + else + StringCopyS(buf,buf_size,"-"); + } + else if ( sform_mode & DC_SFORM_ALIGN ) + snprintf(buf,buf_size,"%4llu%s",num,unit_buf); + else + snprintf(buf,buf_size,"%llu%s",num,unit_buf); + + return buf; +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan size /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u64 (*ScanSizeFactorHook) +( + char ch_factor, // char to analyze + int force_base // if 1000|1024: force multiple of this +) = 0; + +//----------------------------------------------------------------------------- + +u64 ScanSizeFactor +( + char ch_factor, // char to analyze + int force_base // if 1000|1024: force multiple of this +) +{ + if (ScanSizeFactorHook) + { + u64 res = ScanSizeFactorHook(ch_factor,force_base); + if (res) + return res; + } + + if ( force_base == 1000 ) + { + switch (ch_factor) + { + case 'b': case 'c': return 1; + case 'k': case 'K': return KB_SI; + case 'm': case 'M': return MB_SI; + case 'g': case 'G': return GB_SI; + case 't': case 'T': return TB_SI; + case 'p': case 'P': return PB_SI; + case 'e': case 'E': return EB_SI; + } + } + else if ( force_base == 1024 ) + { + switch (ch_factor) + { + case 'b': case 'c': return 1; + case 'k': case 'K': return KiB; + case 'm': case 'M': return MiB; + case 'g': case 'G': return GiB; + case 't': case 'T': return TiB; + case 'p': case 'P': return PiB; + case 'e': case 'E': return EiB; + } + } + else + { + switch (ch_factor) + { + case 'b': + case 'c': return 1; + + case 'k': return KB_SI; + case 'm': return MB_SI; + case 'g': return GB_SI; + case 't': return TB_SI; + case 'p': return PB_SI; + case 'e': return EB_SI; + + case 'K': return KiB; + case 'M': return MiB; + case 'G': return GiB; + case 'T': return TiB; + case 'P': return PiB; + case 'E': return EiB; + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeTerm +( + double *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + int force_base // if 1000|1024: force multiple of this +) +{ + ASSERT(source); + + char * end; + double d = strtod(source,&end); + if ( end > source ) + { + // something was read + + if ( *end == '/' ) + { + const double div = strtod(end+1,&end); + if ( div > 0.0 ) + d /= div; + } + + u64 factor = ScanSizeFactor(*end,force_base); + if (factor) + end++; + else + factor = default_factor; + + if (factor) + d *= factor; + else + end = (char*)source; + } + + if (num) + *num = d; + + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSize +( + double *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base // if 1000|1024: force multiple of this +) +{ + DASSERT(source); + TRACE("ScanSize(df=%llx,%llx, base=%u)\n", + default_factor, default_factor_add, force_base ); + + double sum = 0.0; + bool add = true; + char * end = 0; + for(;;) + { + double term; + end = ScanSizeTerm(&term,source,default_factor,force_base); + if ( end == source ) + break; + + if (add) + sum += term; + else + sum -= term; + + while ( *end > 0 && *end <= ' ' ) + end++; + + if ( *end == '+' ) + add = true; + else if ( *end == '-' ) + add = false; + else + break; + + source = end+1; + while ( *source > 0 && *source <= ' ' ) + source++; + + if ( !*source && default_factor_add ) + { + if (add) + sum += default_factor_add; + else + sum -= default_factor_add; + end = (char*)source; + break; + } + + default_factor = default_factor_add; + } + + if (num) + *num = sum; + + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeU32 +( + u32 *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base // if 1000|1024: force multiple of this +) +{ + double d; + char * end = ScanSize(&d,source,default_factor,default_factor_add,force_base); + if ( d < 0 || d > ~(u32)0 ) + end = (char*)source; + else if (num) + *num = (u32)d; + + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeU64 +( + u64 *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base // if 1000|1024: force multiple of this +) +{ + double d; + char * end = ScanSize(&d,source,default_factor,default_factor_add,force_base); + if ( d < 0 || d > ~(u64)0 ) + end = (char*)source; + else if (num) + *num = (u64)d; + + return end; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRange +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + double *num1, // not NULL: store 'from' result + double *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + double max_value // >0: max value for open ranges +) +{ + int count = 0; + double d1, d2; + + char *end = ScanSize(&d1,source,default_factor,default_factor_add,force_base); + if ( end == source ) + { + d1 = d2 = 0.0; + count = 0; + } + else + { + count++; + d2 = d1; + if ( *end == '#' || *end == ':' ) + { + source = end; + end = ScanSize(&d2,source+1,default_factor,default_factor_add,force_base); + if ( end == source+1 ) + { + if ( *source == ':' && max_value > 0.0 ) + { + count++; + d2 = max_value; + if ( *end == '*' ) + end++; + } + else + { + end--; + d2 = d1; + } + } + else + { + count++; + if ( *source == '#' ) + d2 += d1-1; + } + if ( d2 < d1 ) + d2 = d1; + } + } + + if (stat) + *stat = count; + if (num1) + *num1 = d1; + if (num2) + *num2 = d2; + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRangeU32 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + u32 *num1, // not NULL: store 'from' result + u32 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u32 max_value // >0: max value for open ranges +) +{ + double d1, d2; + char * end = ScanSizeRange(stat,&d1,&d2,source, + default_factor,default_factor_add,force_base, + max_value>0 ? max_value : U32_MAX ); + + if ( d1 < 0 || d1 > U32_MAX || d2 < 0 || d2 > U32_MAX ) + end = (char*)source; + else + { + if (num1) + *num1 = (u32)d1; + if (num2) + *num2 = (u32)d2; + } + + return end; + +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRangeU64 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + u64 *num1, // not NULL: store 'from' result + u64 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u64 max_value // >0: max value for open ranges +) +{ + double d1, d2; + char * end = ScanSizeRange(stat,&d1,&d2,source, + default_factor,default_factor_add,force_base, + max_value>0 ? max_value : U64_MAX ); + + if ( d1 < 0 || d1 > U64_MAX || d2 < 0 || d2 > U64_MAX ) + end = (char*)source; + else + { + if (num1) + *num1 = (u64)d1; + if (num2) + *num2 = (u64)d2; + } + + return end; + +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRangeList +( + uint *n_range, // not NULL: store number of scanned ranges + double *num, // array with '2*max_range' elements + // unused elements are filled with 0.0 + uint max_range, // max number of allowed ranges + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + double max_value // >0: max value for open ranges +) +{ + DASSERT(num); + + uint i = 0; + char *ptr = (char*)source; + + while ( i < max_range ) + { + while ( *ptr > 0 && *ptr <= ' ' || *ptr == ',' ) + ptr++; + + int stat; + ptr = ScanSizeRange( &stat, num, num+1, ptr, + default_factor, default_factor_add, force_base, max_value ); + if (!stat) + break; + num += 2; + i++; + + char *ptr2 = ptr; + while ( *ptr2 > 0 && *ptr2 <= ' ' ) + ptr2++; + if ( *ptr2 != ',' ) + break; + + ptr = ptr2 + 1; + } + + if (n_range) + *n_range = i; + + while ( i < max_range ) + { + *num++ = 0.0; + *num++ = 0.0; + i++; + } + + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRangeListU32 +( + uint *n_range, // not NULL: store number of scanned ranges + u32 *num, // array with '2*max_range' elements + // unused elements are filled with 0 + uint max_range, // max number of allowed ranges + + ccp source, // source text + u32 default_factor, // use this factor if number hasn't one + u32 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u32 max_value // >0: max value for open ranges +) +{ + DASSERT(num); + + const uint max = 100; + double d_buf[2*max]; + double *d = max_range < max ? d_buf : MALLOC(max_range*2*sizeof(double)); + + char *end = ScanSizeRangeList( n_range, d, max_range, + source, default_factor, default_factor_add, + force_base, max_value ); + + uint i; + for ( i = 0; i < 2*max_range; i++ ) + num[i] = d[i] < 0.0 ? 0.0 : d[i] < U32_MAX ? d[i] : U32_MAX; + + if ( d != d_buf ) + FREE(d); + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRangeListU64 +( + uint *n_range, // not NULL: store number of scanned ranges + u64 *num, // array with '2*max_range' elements + // unused elements are filled with 0 + uint max_range, // max number of allowed ranges + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + u64 max_value // >0: max value for open ranges +) +{ + DASSERT(num); + + const uint max = 100; + double d_buf[2*max]; + double *d = max_range < max ? d_buf : MALLOC(max_range*2*sizeof(double)); + + char *end = ScanSizeRangeList( n_range, d, max_range, + source, default_factor, default_factor_add, + force_base, max_value ); + + uint i; + for ( i = 0; i < 2*max_range; i++ ) + num[i] = d[i] < 0.0 ? 0.0 : d[i] < U64_MAX ? d[i] : U64_MAX; + + if ( d != d_buf ) + FREE(d); + return end; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanSizeOpt +( + double *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + + ccp opt_name, // NULL or name of option for error messages + u64 min, // minimum allowed value + u64 max, // maximum allowed value + bool print_err // true: print error messages +) +{ + double d; + char * end = ScanSize(&d,source,default_factor,default_factor_add,force_base); + + #ifdef DEBUG + { + u64 size = d; + TRACE("--%s %8.6g ~ %llu ~ %llu GiB ~ %llu GB\n", + opt_name, d, size, (size+GiB/2)/GiB, (size+500000000)/1000000000 ); + } + #endif + + enumError err = ERR_OK; + + if ( source == end || *end ) + { + err = ERR_SYNTAX; + if (print_err) + ERROR0(ERR_SYNTAX, + "Illegal number for option --%s: %s\n", + opt_name, source ); + } + else if ( min > 0 && d < min ) + { + err = ERR_SYNTAX; + if (print_err) + ERROR0(ERR_SEMANTIC, + "Value of --%s too small (must not <%llu): %s\n", + opt_name, min, source ); + } + else if ( max > 0 && d > max ) + { + err = ERR_SYNTAX; + if (print_err) + ERROR0(ERR_SEMANTIC, + "Value of --%s too large (must not >%llu): %s\n", + opt_name, max, source ); + } + + if ( num && !err ) + *num = d; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanSizeOptU64 +( + u64 *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + int force_base, // if 1000|1024: force multiple of this + + ccp opt_name, // NULL or name of option for error messages + u64 min, // >0: minimum allowed value + u64 max, // >0: maximum allowed value + u32 multiple, // >0: result must be multiple + u32 pow2, // >0: result must power of '1< 0 ) + { + int shift_count = 0; + u64 shift_val = val; + if (val) + { + while (!(shift_val&1)) + { + shift_count++; + shift_val >>= 1; + } + } + + if ( shift_val != 1 || shift_count/pow2*pow2 != shift_count ) + { + err = ERR_SEMANTIC; + if (print_err) + ERROR0(ERR_SYNTAX, + "--%s: value must be a power of %d but not %llu\n", + opt_name, 1< 1 ) + { + u64 xval = val / multiple * multiple; + if ( xval != val ) + { + if ( min > 0 && xval < min ) + xval += multiple; + + if (print_err) + ERROR0(ERR_WARNING, + "--%s: value must be a multiple of %u -> use %llu instead of %llu.\n", + opt_name, multiple, xval, val ); + val = xval; + } + } + + if ( num && !err ) + *num = val; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanSizeOptU32 +( + u32 *num, // not NULL: store result + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + int force_base, // if 1000|1024: force multiple of this + + ccp opt_name, // NULL or name of option for error messages + u64 min, // >0: minimum allowed value + u64 max, // >0: maximum allowed value + u32 multiple, // >0: result must be multiple + u32 pow2, // >0: result must power of '1< ~(u32)0 ) + max = ~(u32)0; + + u64 val = 0; + enumError err = ScanSizeOptU64( &val, source, default_factor, force_base, + opt_name, min, max, multiple, pow2, print_err ); + + if ( num && !err ) + *num = (u32)val; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// strto*() like wrappers + +u32 GetSizeU32 ( ccp src, char **end, int force_base ) +{ + u32 num = 0; + char *ptr = ScanSizeU32(&num,src,1,1,force_base); + if (end) + *end = ptr; + return num; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 GetSizeU64 ( ccp src, char **end, int force_base ) +{ + u64 num = 0; + char *ptr = ScanSizeU64(&num,src,1,1,force_base); + if (end) + *end = ptr; + return num; +} + +/////////////////////////////////////////////////////////////////////////////// + +double GetSizeD ( ccp src, char **end, int force_base ) +{ + double num = 0.0; + char *ptr = ScanSize(&num,src,1,1,force_base); + if (end) + *end = ptr; + return num; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Scan Duration /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSIFactor +( + // returns end of scanned string + + double *num, // not NULL: store result + ccp source, // source text + double default_factor // return this if no factor found +) +{ + DASSERT(source); + switch (*(uchar*)source++) + { + case 'y': default_factor = 1e-24; break; + case 'z': default_factor = 1e-21; break; + case 'a': default_factor = 1e-18; break; + case 'f': default_factor = 1e-15; break; + case 'p': default_factor = 1e-12; break; + case 'n': default_factor = 1e-9; break; + case 0xb5: // µ as ANSI + case 'u': default_factor = 1e-6; break; + case 'm': default_factor = 1e-3; break; + case 'c': default_factor = 1e-2; break; + case 'd': default_factor = 1e-1; break; + + case 'h': default_factor = 1e2; break; + case 'k': default_factor = 1e3; break; + case 'M': default_factor = 1e6; break; + case 'G': default_factor = 1e9; break; + case 'T': default_factor = 1e12; break; + case 'P': default_factor = 1e15; break; + case 'E': default_factor = 1e18; break; + case 'Z': default_factor = 1e21; break; + case 'Y': default_factor = 1e24; break; + + case 0xc2: + if ( (uchar)*source == 0xb5 ) + { + // µ as UTF-8 + source++; + default_factor = 1e-6; + break; + } + else + source--; + break; + + default: + source--; + } + if (num) + *num = default_factor; + return (char*)source; +} + +/////////////////////////////////////////////////////////////////////////////// + +double GetDurationFactor ( char ch ) +{ + switch (ch) + { + case 's': return 1.0; break; + case 'm': return 60.0; break; + case 'h': return 3600.0; break; + case 't': + case 'd': return 86400.0; break; + case 'w': return 7*86400.0; break; + case 'j': + case 'y': return 365*86400.0; break; + default: return 0.0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanDuration +( + double *num, // not NULL: store result + ccp source, // source text + double default_factor, // default factor if no SI unit found + ScanDuration_t mode +) +{ + if (!source) + return 0; + + ccp src = source; + if ( mode & SDUMD_SKIP_BLANKS ) + while ( *src == ' ' || *src == '\t' ) + src++; + + if ( mode & SDUMD_ALLOW_LIST ) + { + bool valid = false; + mode &= ~(SDUMD_SKIP_BLANKS|SDUMD_ALLOW_LIST); + double sum = 0.0; + bool sub = false; + for(;;) + { + double val; + ccp end = ScanDuration(&val,src,default_factor,mode); + if ( src == end ) + break; + if (sub) + { + sub = false; + sum -= val; + } + else + sum += val; + src = end; + valid = true; + + if ( mode & SDUMD_ALLOW_SPACE_SEP ) + while ( *src == ' ' || *src == '\t' ) + src++; + if ( *src == '+' && mode & SDUMD_ALLOW_PLUS_SEP ) + src++; + else if ( *src == '-' && mode & SDUMD_ALLOW_MINUS_SEP ) + { + src++; + sub = true; + } + mode |= SDUMD_SKIP_BLANKS; + } + if (num) + *num = sum; + return (char*)( valid ? src : source ); + } + + + //--- single scan + + char *end; + double val = strtod(src,&end); + if ( end > src ) + { + if ( *end == ':' && mode & SDUMD_ALLOW_COLON ) + { + // 'H:M' or 'H:M:S' + src = end + 1; + val *= 3600; + double min = strtod(src,&end); + if ( end > src ) + { + val += 60*min; + src = end; + if ( *src == ':' ) + { + // 'H:M:S' + double sec = strtod(++src,&end); + if ( end > src ) + { + src = end; + val += sec; + } + } + } + } + else + { + if ( *end == '/' && mode & SDUMD_ALLOW_DIV ) + { + const double div = strtod(end+1,&end); + if ( div > 0.0 ) + val /= div; + } + src = end; + + double si_factor; + end = ScanSIFactor(&si_factor,src,1.0); + + double unit = GetDurationFactor(*end); + if (unit) + { + val *= unit * si_factor; + src = end+1; + } + else + { + unit = GetDurationFactor(*src); + if (unit) + { + val *= unit; + src++; + } + else + { + val *= (end>src) ? si_factor : default_factor; + src = end; + } + } + } + if (num) + *num = val; + return (char*)src; + } + + if (num) + *num = 0.0; + return (char*)source; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrintIP4*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintIP4A +( + // print in A-notation + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = FW_IPV4_A_PORT+1 ); + + u8 addr[4]; + write_be32(addr,ip4); + + if ( port >= 0 && port <= 0xffff ) + snprintf(buf,buf_size,"%u.%u:%u", + addr[0], ip4 & 0xffffff, port ); + else + snprintf(buf,buf_size,"%u.%u", + addr[0], ip4 & 0xffffff ); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintIP4B +( + // print in A-notation + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = FW_IPV4_B_PORT+1 ); + + u8 addr[4]; + write_be32(addr,ip4); + + if ( port >= 0 && port <= 0xffff ) + snprintf(buf,buf_size,"%u.%u.%u:%u", + addr[0], addr[1], ip4 & 0xffff, port ); + else + snprintf(buf,buf_size,"%u.%u.%u", + addr[0], addr[1], ip4 & 0xffff ); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = FW_IPV4_C_PORT+1 ); + + u8 addr[4]; + write_be32(addr,ip4); + + if ( port >= 0 && port <= 0xffff ) + snprintf(buf,buf_size,"%u.%u.%u.%u:%u", + addr[0], addr[1], addr[2], addr[3], port ); + else + snprintf(buf,buf_size,"%u.%u.%u.%u", + addr[0], addr[1], addr[2], addr[3] ); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintLeftIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = FW_IPV4_C_PORT+1 ); + + u8 addr[4]; + write_be32(addr,ip4); + uint len, fw; + + if ( port >= 0 && port <= 0xffff ) + { + fw = 21; + len = snprintf(buf,buf_size,"%u.%u.%u.%u:%u", + addr[0], addr[1], addr[2], addr[3], port ); + } + else + { + fw = 15; + len = snprintf(buf,buf_size,"%u.%u.%u.%u", + addr[0], addr[1], addr[2], addr[3] ); + } + + char *dest = buf + len; + char *end = buf + ( fw < buf_size ? fw : buf_size ); + while ( dest < end ) + *dest++ = ' '; + *dest = 0; + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintRightIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port, // 0..0xffff: add ':port' + uint port_mode // 0: 'ip:port', 1: 'ip :port', 2: 'ip : port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 24 ); + + u8 addr[4]; + write_be32(addr,ip4); + + char tempbuf[16]; + snprintf(tempbuf,sizeof(tempbuf),"%u.%u.%u.%u", + addr[0], addr[1], addr[2], addr[3] ); + + if ( port >= 0 && port <= 0xffff ) + { + if (!port_mode) + snprintf(buf,buf_size,"%15s:%-5u",tempbuf,port); + else if ( port_mode == 1 ) + snprintf(buf,buf_size,"%15s :%-5u",tempbuf,port); + else + snprintf(buf,buf_size,"%15s : %-5u",tempbuf,port); + } + else + snprintf(buf,buf_size,"%15s",tempbuf); + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintAlignedIP4 +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = FW_IPV4_C_PORT+1 ); + + u8 addr[4]; + write_be32(addr,ip4); + + if ( port >= 0 && port <= 0xffff ) + snprintf(buf,buf_size,"%3u.%3u.%3u.%3u:%5u", + addr[0], addr[1], addr[2], addr[3], port ); + else + snprintf(buf,buf_size,"%3u.%3u.%3u.%3u", + addr[0], addr[1], addr[2], addr[3] ); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CRC16 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CreateCRC16Table ( u16 table[0x100], u16 polynom ) +{ + int i, j; + for ( i = 0; i < 256; i++ ) + { + u16 val = i << 8; + for ( j = 0; j < 8; j++ ) + { + if ( val & 0x8000 ) + val = val << 1 ^ polynom; + else + val <<= 1; + } + table[i] = val; + } +} + +//----------------------------------------------------------------------------- + +const u16 * GetCRC16Table ( u16 polynom ) +{ + static u16 *table = 0; + static u16 active_polynom = 0; + + if ( !table || active_polynom != polynom ) + { + if (!table) + table = MALLOC(sizeof(u16)*0x100); + CreateCRC16Table(table,polynom); + active_polynom = polynom; + } + return table; +} + +//----------------------------------------------------------------------------- + +u16 CalcCRC16 ( cvp data, uint data_size, u16 polynom, u16 preset ) +{ + DASSERT( data || !data_size ); + + register u16 crc = preset; + if ( data_size > 0 ) + { + const u16 *table = GetCRC16Table(polynom); + const u8 *ptr = data; + while ( data_size-- > 0 ) + crc = table[ ( *ptr++ ^ crc >> 8 ) & 0xff ] ^ crc << 8; + } + return crc; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +float double2float ( double d ) +{ + // reduce precision + return d; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintNiceID4 ( uint id ) +{ + if (!id) + return "----"; + + char *buf = GetCircBuf(5); + DASSERT(buf); + + #if 0 // the second solution is much faster! + + buf[4] = 0; + div_t d = div( (int)( id % (26*26*100) ), 10 ); + buf[3] = '0' + d.rem; + d = div(d.quot,10); + buf[2] = '0' + d.rem; + d = div(d.quot,26); + buf[1] = 'A' + d.rem; + buf[0] = 'A' + d.quot; + + #else + + buf[0] = 'A' + id / 2600 % 26; + buf[1] = 'A' + id / 100 % 26; + buf[2] = '0' + id / 10 % 10; + buf[3] = '0' + id % 10; + buf[4] = 0; + + #endif + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNiceID4 ( int * res, ccp arg ) +{ + DASSERT(res); + *res = -1; + + if (!arg) + return 0; + + char ch = tolower((int)arg[0]); + if ( ch >= 'a' && ch <= 'z' ) + { + int num = ch - 'a'; + ch = tolower((int)arg[1]); + if ( ch >= 'a' && ch <= 'z' ) + { + num = 26 * num + ch - 'a'; + ch = arg[2]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[3]; + if ( ch >= '0' && ch <= '9' ) + { + *res = 10 * num + ch - '0'; + return (char*)arg+4; + } + } + } + } + return (char*)arg; +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-numeric.h b/project/dclib/dclib-numeric.h new file mode 100644 index 0000000..19fefda --- /dev/null +++ b/project/dclib/dclib-numeric.h @@ -0,0 +1,839 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_NUMERIC_H +#define DCLIB_NUMERIC_H 1 + +#include "dclib-types.h" +#include "dclib-debug.h" +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// float vector /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[float3]] + +typedef struct float3 +{ + union + { + u32 u[3]; + float v[3]; + struct + { + float x; + float y; + float z; + } + __attribute__ ((packed)); + } + __attribute__ ((packed)); +} +__attribute__ ((packed)) float3; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[float3List_t]] + +typedef struct float3List_t +{ + float3 *list; // list of data + uint *sort; // NULL or 'size' alloced elements + uint used; // used elements of 'list' + uint size; // alloced elements of 'list' + +} float3List_t; + +//----------------------------------------------------------------------------- + +void InitializeF3L +( + float3List_t *f3l, // valid data + uint bin_search // >0: enable binary search + // and allocate 'bin_search' elements +); + +void ResetF3L ( float3List_t * f3l ); +float3 * AppendF3L ( float3List_t * f3l ); +uint FindInsertFloatF3L ( float3List_t * f3l, float3 *val, bool fast ); +float3 * GrowF3L ( float3List_t * f3l, uint n ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// double vector /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[double3]] + +typedef struct double3 +{ + union + { + u64 u[3]; + double v[3]; + struct + { + double x; + double y; + double z; + }; + }; + +} double3; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[double3List_t]] + +typedef struct double3List_t +{ + double3 *list; // list of data + uint used; // used elements of 'list' + uint size; // alloced elements of 'list' + +} double3List_t; + +//----------------------------------------------------------------------------- + +void InitializeD3L ( double3List_t * d3l ); +void ResetD3L ( double3List_t * d3l ); +double3 * AppendD3L ( double3List_t * d3l ); +uint FindInsertFloatD3L ( double3List_t * d3l, double3 *val, bool fast ); +double3 * GrowD3L ( double3List_t * d3l, uint n ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 2D vector macros /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define SideOfLine(ax,ay,bx,by,px,py) \ + ( ((bx)-(ax)) * ((py)-(ay)) - ((by)-(ay)) * ((px)-(ax)) ) +#define SideOfLineXY(a,b,p) \ + ( ((b).x-(a).x) * ((p).y-(a).y) - ((b).y-(a).y) * ((p).x-(a).x) ) +#define SideOfLineXZ(a,b,p) \ + ( ((b).x-(a).x) * ((p).z-(a).z) - ((b).z-(a).z) * ((p).x-(a).x) ) +#define SideOfLineYZ(a,b,p) \ + ( ((b).y-(a).y) * ((p).z-(a).z) - ((b).z-(a).z) * ((p).y-(a).y) ) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 3D vector macros /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define NULL_EPSILON 1e-9 // 'fabs(value) < NULL_EPSILON' is 0.0 +#define MIN_DEGREE 1e-4 // 'fabs(value) < MIN_DEGREE' is 0.0 + +#define Assign3(d,s) do { (d).x = (s).x; (d).y = (s).y; (d).z = (s).z; } while (0) + +#define IsNull3(d) ( (d).x == 0.0 && (d).y == 0.0 && (d).z == 0.0 ) +#define IsNull3e(d,e) ( fabs((d).x) < (e) \ + && fabs((d).y) < (e) \ + && fabs((d).z) < (e) ) + +#define IsOne3(d) ( (d).x == 1.0 && (d).y == 1.0 && (d).z == 1.0 ) +#define IsOne3e(d,e) ( fabs((d).x-1.0) < (e) \ + && fabs((d).y-1.0) < (e) \ + && fabs((d).z-1.0) < (e) ) + +#define IsVal3(d,v) ( (d).x == (v) && (d).y == (v) && (d).z == (v) ) +#define IsVal3e(d,v,e) ( fabs( (d).x - (v) ) < (e) \ + && fabs( (d).y - (v) ) < (e) \ + && fabs( (d).z - (v) ) < (e) ) + +#define IsEQ3(a,b) ( (a).x == (b).x && (a).y == (b).y && (a).z == (b).z ) +#define IsEQ3e(a,b,e) ( fabs( (a).x - (b).x ) < (e) \ + && fabs( (a).y - (b).y ) < (e) \ + && fabs( (a).z - (b).z ) < (e) ) + +#define Add3(d,a,b) do { (d).x = (a).x + (b).x; \ + (d).y = (a).y + (b).y; \ + (d).z = (a).z + (b).z; \ + } while (0) + +#define Sub3(d,a,b) do { (d).x = (a).x - (b).x; \ + (d).y = (a).y - (b).y; \ + (d).z = (a).z - (b).z; \ + } while (0) + +#define Neg3(v) do { (v).x = -(v).x; (v).y = -(v).y; (v).z = -(v).z; } while (0) + +#define Div3f(d,a,f) do { (d).x = (a).x / (f); \ + (d).y = (a).y / (f); \ + (d).z = (a).z / (f); \ + } while (0) + +#define Mult3f(d,a,f) do { (d).x = (a).x * (f); \ + (d).y = (a).y * (f); \ + (d).z = (a).z * (f); \ + } while (0) + +#define LengthSqare3(d) ( (d).x*(d).x + (d).y*(d).y + (d).z*(d).z ) +#define Length3(d) sqrt( (d).x*(d).x + (d).y*(d).y + (d).z*(d).z ) +#define DotProd3(a,b) ( (a).x*(b).x + (a).y*(b).y + (a).z*(b).z ) + +#define CrossProd3(d,a,b) do { (d).x = (a).y*(b).z - (a).z*(b).y; \ + (d).y = (a).z*(b).x - (a).x*(b).z; \ + (d).z = (a).x*(b).y - (a).y*(b).x; \ + } while (0) + +#define CrossProd3x(d,a,b) do { typeof((d).x) xx = (a).y*(b).z - (a).z*(b).y; \ + typeof((d).y) yy = (a).z*(b).x - (a).x*(b).z; \ + (d).z = (a).x*(b).y - (a).y*(b).x; \ + (d).x = xx; (d).y = yy; \ + } while (0) + +#define Unit3(v) do { double l = Length3(v); \ + if (l) { (v).x /= l; (v).y /= l; (v).z /= l; }} while (0) +#define Unit3f(v,f) do { double l = Length3(v); \ + if (l) { l = (f)/l; \ + (v).x *= l; (v).y *= l; (v).z *= l; }} while (0) + +#define MinMax3p(min,max,a,b,c) do { \ + if (a c ? b : c; } \ + else { min = b < c ? b : c; max = a > c ? a : c; } } while (0) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check numbers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static inline bool IsNormalF ( float f ) +{ + return isfinite(f); +// const int fpclass = fpclassify(f); +// return fpclass == FP_NORMAL || fpclass == FP_ZERO; +} + +static inline bool IsNormalD ( double d ) +{ + return isfinite(d); +// const int fpclass = fpclassify(d); +// return fpclass == FP_NORMAL || fpclass == FP_ZERO; +} + +bool IsNormalF3 ( float *f3 ); +bool IsNormalF3be ( float *f3 ); // big endian! +bool IsNormalD3 ( double *d3 ); + +bool IsEqualD3 ( const double3 *a, const double3 *b, + double null_epsilon, double diff_epsilon ); + +//--- new functions +bool IsSameF ( float a, float b, int bit_diff ); +bool IsSameD ( double a, double b, int bit_diff ); +bool IsSameF3 ( const float *a, const float *b, int bit_diff ); +bool IsSameD3 ( const double *a, const double *b, int bit_diff ); + +//--- radiant and gegree +#define rad2deg(n) ( (n)*(180.0/M_PI) ) +#define deg2rad(n) ( (n)*(M_PI/180.0) ) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 2D vector functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint PointsInConvexPolygonF +( + // return, how many points are inside the polygon + + float *pts, // points: pointer to a array with '2*n_pts' elements + uint n_pts, // Number of (x,y) pairs in 'pts' + + float *pol, // polygon: pointer to a array with '2*n_pol' elements + uint n_pol, // number of (x,y) pairs in 'pol' + + bool all, // false: count the numbers of points inside + // true: return '1' for all points inside and otherwise '0' + + int *r_dir // If not NULL then return: + // -1: polygon is defined counterclockwise + // 0: unknown + // +1: polygon is defined clockwise + // If at least one point is inside, the result is known. +); + +//----------------------------------------------------------------------------- + +uint PointsInConvexPolygonD +( + // return, how many points are inside the polygon + + double *pts, // points: pointer to a array with '2*n_pts' elements + uint n_pts, // Number of (x,y) pairs in 'pts' + + double *pol, // polygon: pointer to a array with '2*n_pol' elements + uint n_pol, // number of (x,y) pairs in 'pol' + + bool all, // false: count the numbers of points inside + // true: return '1' for all points inside and otherwise '0' + + int *r_dir // If not NULL then return: + // -1: polygon is defined counterclockwise + // 0: unknown + // +1: polygon is defined clockwise + // If at least one point is inside, the result is known. +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// float34 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[float34]] + +typedef struct float34 // transformation matrix +{ + union + { + float v[12]; + float m[3][4]; + struct + { + float x[4]; + float y[4]; + float z[4]; + } + __attribute__ ((packed)); + } + __attribute__ ((packed)); +} +__attribute__ ((packed)) float34; + +//----------------------------------------------------------------------------- + +void ClearFloat34 ( float34 *f34 ); +void SetupFloat34 ( float34 *f34 ); + +void SetupMatrixF34 +( + float34 *mat, // data to setup + float3 *scale, // NULL or scaling vector + float3 *rotate, // NULL or rotation vector in *radiant* + float3 *translate // NULL or translation vector +); + +void CalcInverseMatrixF34 +( + float34 *i, // store inverse matrix here + const float34 *t // transformation matrix +); + +void MultiplyF34 +( + float34 *dest, // valid, store result here, may be 'src1' or 'src2' + const float34 *src1, // first valid source, if NULL use dest + const float34 *src2 // second valid source, if NULL use dest +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// double34 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[double34]] + +typedef struct double34 // transformation matrix +{ + union + { + double v[12]; + double m[3][4]; + struct + { + double x[4]; + double y[4]; + double z[4]; + } + __attribute__ ((packed)); + } + __attribute__ ((packed)); +} +__attribute__ ((packed)) double34; + +//----------------------------------------------------------------------------- + +void ClearDouble34 ( double34 *d34 ); +void SetupDouble34 ( double34 *d34 ); + +void SetupMatrixD34 +( + double34 *mat, // data to setup + double3 *scale, // NULL or scaling vector + double3 *rotate, // NULL or rotation vector in *radiant* + double3 *translate // NULL or translation vector +); + +void CalcInverseMatrixD34 +( + double34 *i, // store inverse matrix here + const double34 *t // transformation matrix +); + +void MultiplyD34 +( + double34 *dest, // valid, store result here, may be 'src1' or 'src2' + const double34 *src1, // first valid source, if NULL use dest + const double34 *src2 // second valid source, if NULL use dest +); + +//----------------------------------------------------------------------------- + +void CopyF34toD34 +( + double34 *dest, // destination matrix + const float34 *src // source matrix +); + +void CopyD34toF34 +( + float34 *dest, // destination matrix + const double34 *src // source matrix +); + +//----------------------------------------------------------------------------- + +static inline float3 TransformF3D34 ( const double34 *d34, const float3 *val ) +{ + DASSERT(d34); + DASSERT(val); + const double *m = d34->v; + float3 res; + res.x = m[ 0] * val->x + m[ 1] * val->y + m[ 2] * val->z + m[ 3]; + res.y = m[4+0] * val->x + m[4+1] * val->y + m[4+2] * val->z + m[4+3]; + res.z = m[8+0] * val->x + m[8+1] * val->y + m[8+2] * val->z + m[8+3]; + return res; +} + +static inline double3 TransformD3D34 ( const double34 *d34, const double3 *val ) +{ + DASSERT(d34); + DASSERT(val); + const double *m = d34->v; + double3 res; + res.x = m[ 0] * val->x + m[ 1] * val->y + m[ 2] * val->z + m[ 3]; + res.y = m[4+0] * val->x + m[4+1] * val->y + m[4+2] * val->z + m[4+3]; + res.z = m[8+0] * val->x + m[8+1] * val->y + m[8+2] * val->z + m[8+3]; + return res; +} + +static inline float3 TransformF3F34 ( const float34 *d34, const float3 *val ) +{ + DASSERT(d34); + DASSERT(val); + const float *m = d34->v; + float3 res; + res.x = m[ 0] * val->x + m[ 1] * val->y + m[ 2] * val->z + m[ 3]; + res.y = m[4+0] * val->x + m[4+1] * val->y + m[4+2] * val->z + m[4+3]; + res.z = m[8+0] * val->x + m[8+1] * val->y + m[8+2] * val->z + m[8+3]; + return res; +} + +static inline double3 TransformD3F34 ( const float34 *d34, const double3 *val ) +{ + DASSERT(d34); + DASSERT(val); + const float *m = d34->v; + double3 res; + res.x = m[ 0] * val->x + m[ 1] * val->y + m[ 2] * val->z + m[ 3]; + res.y = m[4+0] * val->x + m[4+1] * val->y + m[4+2] * val->z + m[4+3]; + res.z = m[8+0] * val->x + m[8+1] * val->y + m[8+2] * val->z + m[8+3]; + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MatrixD_t]] + +typedef struct MatrixD_t +{ + //--- status + + bool valid; // false: data structure needs initialization + bool norm_valid; // false: normed values are invalid + bool tmatrix_valid; // false: transformation matrix is invalid + bool imatrix_valid; // false: inverse matrix is invalid + uint sequence_number; // Used for tracking. It is incremented + // on every new calculation by CalcNormMatrixD() + + //--- matrix usage status + + u8 use_matrix; // 0: matrix not needed for transformation + // 1: matrix needed for transformation + // 2: matrix is the only valid reference + + //--- status, only valid, if 'norm_valid' + + u8 scale_enabled; // scaling is enabled: 1=x, 2=y, 4=z + u8 rotate_enabled; // rotation is enabled: 1=x, 2=y, 4=z + u8 translate_enabled; // translation is enabled: 1=x, 2=y, 4=z + u8 transform_enabled; // 'OR' result of the 3 enabled values above + // OR 'use_matrix<<3' + + //--- base values: clear 'norm_valid' and '*matrix_valid' on change + + double3 scale; // scale vector + double3 scale_origin; // origin for scaling + double3 shift; // shift vector + double3 rotate_deg; // rotation in degree and radiant. + double3 rotate_rad; // both values are used as sum. + double3 rotate_origin[3]; // origin for rotation + double3 translate; // translation vector + + //--- normed values, calculated by CalcNormMatrixD() + + double3 norm_scale; // scale vector + double3 norm_rotate_deg; // rotation vector in degree + double3 norm_rotate_rad; // rotation vector in radiant + double3 norm_translate; // translation vector + double3 norm_pos2d; // recommended positions for 2d transformations + + //--- matrices, calculated by CalcMatrixD() + + double34 trans_matrix; // transformation matrix + double34 inv_matrix; // inverse transformation matrix +} +MatrixD_t; + +//----------------------------------------------------------------------------- + +// statistics +extern u64 N_MatrixD_forward; // total number of forward transformations +extern u64 N_MatrixD_inverse; // total number of inverse transformations + +//----------------------------------------------------------------------------- + +void InitializeMatrixD ( MatrixD_t * mat ); + +void CopyMatrixD +( + MatrixD_t *dest, // valid destination + const + MatrixD_t *src // NULL or source +); + +void TermAssignMatrixD +( + // called after dirext matrix manipulations + MatrixD_t * mat // valid data structure +); + +//----------------------------------------------------------------------------- + +MatrixD_t * SetScaleMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * scale, // NULL: clear scale, not NULL: set scale + double3 * origin // not NULL: Origin for scaling +); + +MatrixD_t * SetShiftMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * shift // NULL: clear Shift, not NULL: set Shift +); + +MatrixD_t * SetScaleShiftMatrixD +( + MatrixD_t * mat, // valid data structure + uint xyz, // 0..2: x/y/z coordinate to set + double old1, // old value of first point + double new1, // new value of first point + double old2, // old value of second point + double new2 // new value of second point +); + +MatrixD_t * SetRotateMatrixD +( + MatrixD_t * mat, // valid data structure + uint xyz, // 0..2: x/y/z coordinate to set + double degree, // angle in degree + double radiant, // angle in radiant + double3 * origin // not NULL: Origin for the rotation +); + +MatrixD_t * SetTranslateMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * translate // NULL: clear translate, not NULL: set translate +); + +MatrixD_t * SetMatrixD +( + MatrixD_t * mat, // valid data structure + const + double34 * src // NULL or matrix to set +); + +//----------------------------------------------------------------------------- + +MatrixD_t * SetAScaleMatrixD +( + // calculate the matrix as rotation around a specified axis + + MatrixD_t * mat, // valid data structure + double scale, // scale factor + double3 * dir // NULL or scaling direction as vector +); + +MatrixD_t * SetARotateMatrixD +( + // calculate the matrix as rotation around a specified axis + + MatrixD_t * mat, // valid data structure + double degree, // angle in degree + double radiant, // angle in radiant, add to degree with factor + double3 * pt1, // point #1 to define the axis; if NULL use (0,0,0) + double3 * pt2 // point #2 to define the axis; if NULL use (0,0,0) +); + +//----------------------------------------------------------------------------- + +MatrixD_t * CheckStatusMatrixD +( + MatrixD_t * mat // valid data structure +); + +MatrixD_t * CalcNormMatrixD +( + MatrixD_t * mat // valid data structure +); + +MatrixD_t * CalcTransMatrixD +( + MatrixD_t * mat, // valid data structure + bool always // false: calc transformation matrix only + // if needed for transformation + // true: ignore 'rotate_enabled' +); + +MatrixD_t * CalcMatrixD +( + MatrixD_t * mat, // valid data structure + bool always // false: calc both matrices only + // if needed for transformation + // true: calc matrices always +); + +MatrixD_t * MultiplyMatrixD +( + MatrixD_t *dest, // valid, store result here, may be 'src1' or 'src2' + MatrixD_t *src1, // first valid source, if NULL use dest + MatrixD_t *src2, // second valid source, if NULL use dest + uint inverse_mode // bit field: + // 1: use inverse matrix of src1 + // 2: use inverse matrix of src2 +); + +MatrixD_t * CalcVectorsMatrixD +( + MatrixD_t * mat, // valid data structure + bool always // false: calc vectors only if matrix is valid + // true: calc matrix if invalid & vectors always +); + +static inline bool TransformationEnabledMatrixD ( MatrixD_t * mat ) +{ + return CalcNormMatrixD(mat)->transform_enabled != 0; +} + +//----------------------------------------------------------------------------- + +double3 TransformD3MatrixD +( + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +); + +double3 InvTransformD3MatrixD +( + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +); + +void TransformD3NMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +); + +void InvTransformD3NMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +); + +//----------------------------------------------------------------------------- + +float3 TransformF3MatrixD +( + MatrixD_t * mat, // valid data structure + const + float3 * val // value to transform +); + +float3 InvTransformF3MatrixD +( + MatrixD_t * mat, // valid data structure + const + float3 * val // value to transform +); + +void TransformF3NMatrixD +( + MatrixD_t * mat, // valid data structure + float3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +); + +void InvTransformF3NMatrixD +( + MatrixD_t * mat, // valid data structure + float3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +); + +//----------------------------------------------------------------------------- + +double3 BaseTransformD3MatrixD +( + // transform using the base parameters + + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +); + + +double3 NormTransformD3MatrixD +( + // transform using the norm parameters + + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +); + +double3 TransformHelperD3MatrixD +( + // transform using vectors + + double3 *scale, // NULL or scale vector + double3 *scale_origin, // NULL or origin for scaling + double3 *shift, // NULL or shift vector + double3 *rotate_deg, // NULL or rotation in degree + double3 *xrot_origin, // NULL or origin for x-rotation + double3 *yrot_origin, // NULL or origin for y-rotation + double3 *zrot_origin, // NULL or origin for z-rotation + double3 *translate, // NULL or translation vector + + const + double3 * val // value to transform +); + +//----------------------------------------------------------------------------- + +void PrintMatrixD +( + FILE * f, // file to print to + uint indent, // indention + ccp eol, // not NULL: print as 'end of line' + + MatrixD_t * mat, // valid data structure + uint create, // bitfield for setup: + // 1: create normals if invalid + // 2: create transform matrix if invalid + // 4: create inverse matrix if invalid + uint print // bitfield print selection: + // 1: print base vectors + // 2: print normalized values, if valid + // 4: print transformation matrix, if valid + // 8: print inverse matrix, if valid + // 0x10: print also header, if only 1 section is print + // 0x20: print all, don't suppress NULL vectors + // 0x40: print an additional EOL behind each section +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CRC16 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CRC16_CCITT_POLYNOM 0x1021 + +void CreateCRC16Table ( u16 table[0x100], u16 polynom ); +const u16 * GetCRC16Table ( u16 polynom ); + +u16 CalcCRC16 ( cvp data, uint data_size, u16 polynom, u16 preset ); +static inline u16 CalcCRC16CCITT ( cvp data, uint data_size ) + { return CalcCRC16(data,data_size,CRC16_CCITT_POLYNOM,0); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static inline int float2int ( float f ) { return (int) truncf(f+0.5); } +static inline int double2int ( double d ) { return (int) trunc (d+0.5); } +static inline s64 float2int64 ( float f ) { return (s64) truncf(f+0.5); } +static inline s64 double2int64 ( double d ) { return (s64) trunc (d+0.5); } + +/////////////////////////////////////////////////////////////////////////////// + +// all values are multiple of 26*26*100 = 67600 = 0x10810 +#define GOOD_MAX_NICE4_5 67600 // 1 * 67600 +#define GOOD_MAX_NICE4_6 946400 // 14 * 67600 +#define GOOD_MAX_NICE4_7 9937200 // 147 * 67600 +#define GOOD_MAX_NICE4_8 99980400 // 1479 * 67600 +#define GOOD_MAX_NICE4_9 999939200 // 14792 * 67600 +#define GOOD_MAX_NICE4 4294966000 // 63535 * 67600 = 0xfffffaf0 = -1296 + +ccp PrintNiceID4 ( uint nice_gid ); +char * ScanNiceID4 ( int * res, ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_NUMERIC_H + diff --git a/project/dclib/dclib-option.c b/project/dclib/dclib-option.c new file mode 100644 index 0000000..5a2044b --- /dev/null +++ b/project/dclib/dclib-option.c @@ -0,0 +1,1012 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include "dclib-option.h" +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GenericOpt_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeGenericOpt ( GenericOpt_t *go, char first_char ) +{ + DASSERT(go); + memset(go,0,sizeof(*go)); + InitializeMemPool(&go->str_pool,2000); + + char *dest = go->sopt; + if (first_char) + *dest++ = first_char; + *dest++ = ':'; + *dest = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetGenericOpt ( GenericOpt_t *go ) +{ + DASSERT(go); + ResetMemPool(&go->str_pool); + FREE(go->lopt); + InitializeGenericOpt(go,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddGenericOpt +( + GenericOpt_t *go, // data structure + const GenericOptParm_t *gp, // list to add, list end on name==NULL + bool rm_minus // remove '-' on options for alternatives +) +{ + DASSERT(go); + DASSERT(gp); + + char * sopt_ptr = go->sopt + strlen(go->sopt); + char * sopt_end = go->sopt + sizeof(go->sopt) - 3; + + for ( ; gp->name; gp++ ) + { + ccp ptr = gp->name; + for(;;) + { + while ( *ptr == '|' ) + ptr++; + + uint have_minus = 0; + char name_buf[50], *name_ptr = name_buf; + while ( *ptr && *ptr != '|' ) + { + char ch = *ptr++; + if ( ch == '-' ) + have_minus++; + if ( name_ptr < name_buf + sizeof(name_buf) - 1 ) + *name_ptr++ = ch; + } + *name_ptr = 0; + uint len = name_ptr - name_buf; + if (!len) + break; + + //--- short option + + if ( len == 1 ) + { + uchar ch = *name_buf; + if ( ch > ' ' && ch < 0x80 && ch != ':' + && !go->sopt_id[ch] && sopt_ptr < sopt_end ) + { + go->sopt_id[ch] = gp->id; + *sopt_ptr++ = ch; + if ( gp->arg ) + { + *sopt_ptr++ = ':'; + if ( gp->arg > 1 ) + *sopt_ptr++ = ':'; + } + } + } + + + //--- long option + + for(;;) + { + #if 0 // avoid duplicates, needed? + uint i; + for ( i = 0; i < go->lopt_used; i++ ) + if (!strcmp(name_buf,go->lopt[i].name)) + goto skip; + #endif + + if ( go->lopt_used == go->lopt_size ) + { + go->lopt_size += 100; + go->lopt = REALLOC( go->lopt, go->lopt_size * sizeof(*go->lopt) ); + } + + struct option *opt = go->lopt + go->lopt_used++; + opt->name = StrDupMemPool(&go->str_pool,name_buf); + opt->has_arg = gp->arg; + opt->flag = 0; + opt->val = gp->id; + + //skip:; + if ( !have_minus || !rm_minus ) + break; + have_minus = 0; + + char *src = name_buf, *dest = name_buf; + while ( *src ) + { + if ( *src != '-' ) + *dest++ = *src; + src++; + } + *dest = 0; + } + } + } + + *sopt_ptr = 0; + + if ( go->lopt_used == go->lopt_size ) + { + go->lopt_size += 100; + go->lopt = REALLOC( go->lopt, go->lopt_size * sizeof(*go->lopt) ); + } + struct option *opt = go->lopt + go->lopt_used; + memset(opt,0,sizeof(*opt)); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global options: parameters /////////////// +/////////////////////////////////////////////////////////////////////////////// + +GParamValueType_t GetGParamValueType ( GOPT_t gt ) +{ + if ( gt & GOPT_RESERVED_BEG ) + gt = GetGOPType(gt); + + switch (gt) + { + case GOPT_IDX_INC0: + case GOPT_IDX_INC: + case GOPT_IDX_DEC0: + case GOPT_IDX_DEC: + case GOPT_IDX_SETP: + case GOPT_IDX_SETM: + case GOPT_IDX_SINT: + return GPT_SN; + + case GOPT_IDX_UINT: + case GOPT_IDX_OFF_ON: + case GOPT_IDX_OR: + case GOPT_IDX_SETBIT: + return GPT_UN; + + case GOPT_IDX_SETP64: + case GOPT_IDX_SETM64: + case GOPT_IDX_S64: + return GPT_SN64; + + case GOPT_IDX_U64: + case GOPT_IDX_OR64: + case GOPT_IDX_SETBIT64: + case GOPT_IDX_HEX: + case GOPT_IDX_SIZE: + return GPT_UN64; + + case GOPT_IDX_DOUBLE: + case GOPT_IDX_DURATION: + return GPT_D; + + case GOPT_IDX_STRING: + return GPT_STR; + + case GOPT_IDX_BIT: + return GPT_BIT; + + default: + return GPT_NONE; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +GetGParam_t GetGParamINT ( const GParam_t *par, int *return_val ) +{ + if (par) + { + DASSERT( par->vtype == GetGParamValueType(par->ptype) ); + switch (par->vtype) + { + case GPT_NONE: + case GPT_STR: + case GPT_BIT: + return GGP_NONE; + + case GPT_SN: + if (return_val) + *return_val = par->sn; + return GGP_OK; + + case GPT_UN: + if (return_val) + *return_val = par->un; + return GGP_SIGN; + + case GPT_SN64: + if (return_val) + *return_val = par->sn64; + return GGP_CONVERT; + + case GPT_UN64: + if (return_val) + *return_val = par->un64; + return GGP_CONVERT; + + case GPT_D: + if (return_val) + *return_val = par->d; + return GGP_CONVERT; + } + } + return GGP_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +GetGParam_t GetGParamUINT ( const GParam_t *par, uint *return_val ) +{ + if (par) + { + DASSERT( par->vtype == GetGParamValueType(par->ptype) ); + switch (par->vtype) + { + case GPT_NONE: + case GPT_STR: + case GPT_BIT: + return GGP_NONE; + + case GPT_SN: + if (return_val) + *return_val = par->sn; + return GGP_SIGN; + + case GPT_UN: + if (return_val) + *return_val = par->un; + return GGP_OK; + + case GPT_SN64: + if (return_val) + *return_val = par->sn64; + return GGP_CONVERT; + + case GPT_UN64: + if (return_val) + *return_val = par->un64; + return GGP_CONVERT; + + case GPT_D: + if (return_val) + *return_val = par->d; + return GGP_CONVERT; + } + } + return GGP_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +GetGParam_t GetGParamS64 ( const GParam_t *par, s64 *return_val ) +{ + if (par) + { + DASSERT( par->vtype == GetGParamValueType(par->ptype) ); + switch (par->vtype) + { + case GPT_NONE: + case GPT_STR: + case GPT_BIT: + return GGP_NONE; + + case GPT_SN: + if (return_val) + *return_val = par->sn; + return GGP_OK; + + case GPT_UN: + if (return_val) + *return_val = par->un; + return GGP_SIGN; + + case GPT_SN64: + if (return_val) + *return_val = par->sn64; + return GGP_OK; + + case GPT_UN64: + if (return_val) + *return_val = par->un64; + return GGP_SIGN; + + case GPT_D: + if (return_val) + *return_val = par->d; + return GGP_CONVERT; + } + } + return GGP_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +GetGParam_t GetGParamU64 ( const GParam_t *par, u64 *return_val ) +{ + if (par) + { + DASSERT( par->vtype == GetGParamValueType(par->ptype) ); + switch (par->vtype) + { + case GPT_NONE: + case GPT_STR: + case GPT_BIT: + return GGP_NONE; + + case GPT_SN: + if (return_val) + *return_val = par->sn; + return GGP_SIGN; + + case GPT_UN: + if (return_val) + *return_val = par->un; + return GGP_OK; + + case GPT_SN64: + if (return_val) + *return_val = par->sn64; + return GGP_SIGN; + + case GPT_UN64: + if (return_val) + *return_val = par->un64; + return GGP_OK; + + case GPT_D: + if (return_val) + *return_val = par->d; + return GGP_CONVERT; + } + } + return GGP_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +GetGParam_t GetGParamDBL ( const GParam_t *par, double *return_val ) +{ + if (par) + { + DASSERT( par->vtype == GetGParamValueType(par->ptype) ); + switch (par->vtype) + { + case GPT_NONE: + case GPT_STR: + case GPT_BIT: + return GGP_NONE; + + case GPT_SN: + if (return_val) + *return_val = par->sn; + return GGP_CONVERT; + + case GPT_UN: + if (return_val) + *return_val = par->un; + return GGP_CONVERT; + + case GPT_SN64: + if (return_val) + *return_val = par->sn64; + return GGP_CONVERT; + + case GPT_UN64: + if (return_val) + *return_val = par->un64; + return GGP_CONVERT; + + case GPT_D: + if (return_val) + *return_val = par->d; + return GGP_OK; + } + } + return GGP_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +GetGParam_t GetGParamSTR ( const GParam_t *par, ccp *return_val ) +{ + DASSERT( !par || par->vtype == GetGParamValueType(par->ptype) ); + if ( par && par->vtype == GPT_STR ) + { + if (return_val) + *return_val = par->str; + return GGP_OK; + } + return GGP_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +GetGParam_t GetGParamBIT ( const GParam_t *par, u32 *return_val, u32 *return_mask ) +{ + DASSERT( !par || par->vtype == GetGParamValueType(par->ptype) ); + if ( par && par->vtype == GPT_BIT ) + { + if (return_val) + *return_val = par->bit.val; + if (return_mask) + *return_mask = par->bit.mask; + return GGP_OK; + } + return GGP_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintGParam ( const GParam_t *par ) +{ + DASSERT(par); + DASSERT( par->vtype == GetGParamValueType(par->ptype) ); + char buf[100]; + ccp extra = ""; + + switch (par->ptype) + { + case GOPT_IDX_NULL: + snprintf(buf,sizeof(buf),"[-]"); + break; + + case GOPT_IDX_INC0: + case GOPT_IDX_DEC0: + case GOPT_IDX_DEC: + extra = "?"; + case GOPT_IDX_INC: + snprintf(buf,sizeof(buf),"[%sINC] %d",extra,par->sn); + break; + + case GOPT_IDX_SETP: + case GOPT_IDX_SETM: + extra = "?"; + case GOPT_IDX_SINT: + snprintf(buf,sizeof(buf),"[%sSINT] %d",extra,par->sn); + break; + + case GOPT_IDX_OR: + case GOPT_IDX_SETBIT: + case GOPT_IDX_OFF_ON: + extra = "?"; + case GOPT_IDX_UINT: + snprintf(buf,sizeof(buf),"[%sUINT] %u",extra,par->un); + break; + + case GOPT_IDX_BIT: + snprintf(buf,sizeof(buf),"[BIT] set=%x, clr=%x", + par->bit.val, par->bit.mask & ~par->bit.val ); + break; + + case GOPT_IDX_SETP64: + case GOPT_IDX_SETM64: + extra = "?"; + case GOPT_IDX_S64: + snprintf(buf,sizeof(buf),"[%sS64] %lld",extra,par->sn64); + break; + + case GOPT_IDX_OR64: + case GOPT_IDX_SETBIT64: + extra = "?"; + case GOPT_IDX_U64: + snprintf(buf,sizeof(buf),"[%sU64] %llu",extra,par->un64); + break; + + case GOPT_IDX_HEX: + snprintf(buf,sizeof(buf),"[HEX] 0x%llx",par->un64); + break; + + case GOPT_IDX_SIZE: + snprintf(buf,sizeof(buf),"[SIZE] %llu = %s", + par->un64, PrintSize1024(0,0,par->un64,false) ); + break; + + case GOPT_IDX_DOUBLE: + snprintf(buf,sizeof(buf),"[DBL] %12g",par->d); + break; + + case GOPT_IDX_DURATION: + snprintf(buf,sizeof(buf),"[DUR] %12g = %s", + par->d, PrintTimerUSec6(0,0,double2usec(par->d),false) ); + break; + + case GOPT_IDX_STRING: + snprintf(buf,sizeof(buf),"[STR] \"%s\"",par->str); + break; + + + default: + snprintf(buf,sizeof(buf),"[?%u]",par->ptype); + break; + } + + const uint size = strlen(buf) + 1; + char *res = GetCircBuf(size); + memcpy(res,buf,size); + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanGOptionsHelper() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const GenericOptParm_t StandardGOptions[] = +{ + { GOPT_O_TEST_OPT, 0, "_opt" }, + { GOPT_O_HELP, 0, "h|help" }, + { GOPT_O_CLEAR, 0, "c|clear" }, + { GOPT_O_PAGER, 0, "p|pager" }, + { GOPT_O_QUIT, 0, "Q|quit" }, + { GOPT_O_QUIET, 0, "q|quiet" }, + { GOPT_O_VERBOSE, 0, "v|verbose" }, + {0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +static int ScanGOffOn ( GenericOpt_t *go, int opt_stat, bool use_max, ccp optarg ) +{ + ccp object = "'off/on' value"; + + char buf[100]; + if (go) + { + uint i; + for ( i = 0; i < go->lopt_used; i++ ) + { + struct option *lp = go->lopt + i; + if ( lp->val == opt_stat && lp->has_arg && lp->name ) + { + snprintf(buf,sizeof(buf),"'off/on' value of --%s",lp->name); + object = buf; + break; + } + } + } + + const uint max_num = use_max ? ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE : 0; + return ScanKeywordOffOn(optarg,max_num,object); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanGOptionsHelper +( + GOptions_t *gopt, // valid pointer + GenericOpt_t *go, // NULL or list with options + // => ResetGenericOpt() is called at finish + int argc, // argument counter + char **argv, // list of arguments + int max_param, // >=0: max allowed parameters + ScanGOpt_t scan_opt, // scan options + + //--- call back functions + ScanOptionFunc OnAddOptions, // not NULL: called once to add options + ScanOptionFunc OnOption, // not NULL: called for each option + ScanOptionFunc OnArgument, // not NULL: called for each argument + void *any_ptr // NULL or user specific pointer for call back +) +{ + DASSERT(gopt); + DASSERT(argc>1); + DASSERT(argv); + + //--- setup options + + GenericOpt_t go0; + if (!go) + { + go = &go0; + InitializeGenericOpt( go, scan_opt & SGO_KNOWN ? '+' : 0 ); + } + + if (OnAddOptions) + OnAddOptions(go,gopt,any_ptr,scan_opt,0); + AddGenericOpt(go,StandardGOptions,true); + + if ( argc > 1 ) + { + // skip command + argc--; + argv++; + } + + optind = 0; // '0' is correct for GNU to reset scanning + #if !defined(TEST) && !defined(DEBUG) + opterr = 0; // suppress error messages to stderr + #endif + + + //--- check options + + InitializeGOptions(gopt); + + int err = 0, test_opt = 0; + enumError stat = ERR_OK; + while ( stat == ERR_OK ) + { + int opt_stat = getopt_long(argc,argv,go->sopt,go->lopt,0); + if ( opt_stat == -1 ) + break; + + // translate short option names + if ( (uint)opt_stat < sizeof(go->sopt_id)/sizeof(*go->sopt_id) && go->sopt_id[opt_stat] ) + opt_stat = go->sopt_id[opt_stat]; + + if ( opt_stat >= GOPT_RESERVED_BEG && opt_stat < GOPT_RESERVED_END ) + { + const uint pidx = opt_stat & GOPT_M_INDEX; + if ( pidx >= GOPT_N_PARAM ) + { + printf("%s!!! %s: Parameter index %u must be <%u: %s%s\n", + colout->warn, *argv, + pidx, GOPT_N_PARAM, argv[optind-1], + colout->reset ); + stat = ERR_SYNTAX; + break; + } + GParam_t *par = gopt->param + pidx; + + const uint ptype = GetGOPType(opt_stat); + switch(ptype) + { + //--- increment & decrement: indicator 'ptype' is always GOPT_IDX_INC + + case GOPT_IDX_INC0: + if ( par->ptype != GOPT_IDX_INC ) + { + par->ptype = GOPT_IDX_INC; + par->sn = 1; + } + else + par->sn = par->sn >= 0 ? par->sn+1 : 0; + break; + + case GOPT_IDX_INC: + if ( par->ptype != GOPT_IDX_INC ) + { + par->ptype = GOPT_IDX_INC; + par->sn = 1; + } + else + par->sn = par->sn > 0 ? par->sn+1 : 1; + break; + + case GOPT_IDX_DEC0: + if ( par->ptype != GOPT_IDX_INC ) + { + par->ptype = GOPT_IDX_INC; + par->sn = -1; + } + else + par->sn = par->sn <= 0 ? par->sn-1 : 0; + break; + + case GOPT_IDX_DEC: + if ( par->ptype != GOPT_IDX_INC ) + { + par->ptype = GOPT_IDX_INC; + par->sn = -1; + } + else + par->sn = par->sn < 0 ? par->sn-1 : -1; + break; + + + //--- set values: indicator 'ptype' is GOPT_IDX_SINT or GOPT_IDX_S64 + + case GOPT_IDX_SETP: + par->ptype = GOPT_IDX_SINT; + par->un = ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE; + break; + + case GOPT_IDX_SETM: + par->ptype = GOPT_IDX_SINT; + par->sn = - ( ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE ); + break; + + case GOPT_IDX_SETP64: + par->ptype = GOPT_IDX_S64; + par->un64 = ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE; + break; + + case GOPT_IDX_SETM64: + par->ptype = GOPT_IDX_S64; + par->sn64 = - ( ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE ); + break; + + + //--- set bits: indicator 'ptype' is GOPT_IDX_UINT or GOPT_IDX_U64 + + case GOPT_IDX_OR: + if ( par->ptype != GOPT_IDX_UINT ) + { + par->ptype = GOPT_IDX_UINT; + par->un = 0; + } + par->un |= ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE; + break; + + case GOPT_IDX_SETBIT: + { + if ( par->ptype != GOPT_IDX_UINT ) + { + par->ptype = GOPT_IDX_UINT; + par->un = 0; + } + const uint bitnum = ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE; + if ( bitnum < 8*sizeof(par->un) ) + par->un |= 1u << bitnum; + } + break; + + case GOPT_IDX_OR64: + if ( par->ptype != GOPT_IDX_U64 ) + { + par->ptype = GOPT_IDX_U64; + par->un64 = 0; + } + par->un64 |= ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE; + break; + + case GOPT_IDX_SETBIT64: + if ( par->ptype != GOPT_IDX_U64 ) + { + par->ptype = GOPT_IDX_U64; + par->un64 = 0; + } + { + const uint bitnum = ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE; + if ( bitnum < 64 ) + par->un64 |= 1llu << bitnum; + } + break; + + case GOPT_IDX_BIT: + { + if ( par->ptype != GOPT_IDX_BIT ) + { + par->ptype = GOPT_IDX_BIT; + par->bit.val = par->bit.mask = 0; + } + + const int stat = ScanGOffOn(go,opt_stat,false,optarg); + if ( stat >= 0 ) + { + const uint bitnum = ( opt_stat & GOPT_M_VALUE ) >> GOPT_S_VALUE; + if ( bitnum < 32 ) + { + const u32 m = 1u << bitnum; + par->bit.mask |= m; + if ( stat > 0 ) + par->un |= m; + else + par->un &= ~m; + } + } + } + break; + + + //--- scan parameters + + case GOPT_IDX_SINT: + par->ptype = GOPT_IDX_SINT; + par->sn = str2l(optarg,0,10); + break; + + case GOPT_IDX_UINT: + par->ptype = GOPT_IDX_UINT; + par->un = str2ul(optarg,0,10); + break; + + case GOPT_IDX_OFF_ON: + { + const int stat = ScanGOffOn(go,opt_stat,true,optarg); + par->un = stat >= 0 ? stat : 0; + par->ptype = GOPT_IDX_UINT; + } + break; + + case GOPT_IDX_S64: + par->ptype = GOPT_IDX_S64; + par->sn64 = str2ll(optarg,0,10); + break; + + case GOPT_IDX_U64: + par->ptype = GOPT_IDX_U64; + par->un64 = str2ull(optarg,0,10); + break; + + case GOPT_IDX_HEX: + par->ptype = GOPT_IDX_HEX; + par->un64 = str2ull(optarg,0,16); + break; + + case GOPT_IDX_SIZE: + par->ptype = GOPT_IDX_SIZE; + ScanSizeU64(&par->un64,optarg,1,1,0); break; + break; + + case GOPT_IDX_DOUBLE: + par->ptype = GOPT_IDX_DOUBLE; + par->d = strtod(optarg,0); + break; + + case GOPT_IDX_DURATION: + par->ptype = GOPT_IDX_DURATION; + ScanDuration(&par->d,optarg,1,SDUMD_M_ALL); break; + break; + + case GOPT_IDX_STRING: + par->ptype = GOPT_IDX_STRING; + par->str = optarg; + break; + + default: + printf("!!! %s: Undefinef GOPT_* type 0x%02x [0x%02x]: %s\n", + *argv, GetGOPType(opt_stat), opt_stat, argv[optind-1] ); + stat = ERR_ERROR; + } + par->vtype = GetGParamValueType(par->ptype); + continue; + } + + switch (opt_stat) + { + case '?': + if ( optopt && (uint)optopt < 0x7f ) + printf("%s!!! %s: Invalid option: %s-%c [0x%02x]%s\n", + colout->warn, *argv, + colout->value, optopt, optopt, + colout->reset ); + else + printf("%s!!! %s: Invalid option: %s%s%s\n", + colout->warn, *argv, + colout->value, argv[optind-1], + colout->reset ); + stat = ERR_SYNTAX; + break; + + case ':': + printf("%s!!! %s: Missing parameter for %s%s%s\n", + colout->warn, *argv, + colout->value, argv[optind-1], + colout->reset ); + stat = ERR_SYNTAX; + break; + + + //--- standard options + + case GOPT_O_QUIET: + gopt->verbose = gopt->verbose > -1 ? -1 : gopt->verbose - 1; + break; + case GOPT_O_VERBOSE: + gopt->verbose = gopt->verbose < 0 ? 0 : gopt->verbose + 1; + break; + + case GOPT_O_HELP: gopt->help++; scan_opt |= SGO_PAGER; break; + case GOPT_O_CLEAR: gopt->clear++; break; + case GOPT_O_PAGER: gopt->pager++; break; + case GOPT_O_QUIT: gopt->quit++; break; + case GOPT_O_TEST_OPT: test_opt++; break; + + + //--- misc + + default: + if (OnOption) + { + const int ostat = OnOption(go,gopt,any_ptr,opt_stat,optarg); + if ( ostat <= 0 ) + { + if (!ostat) + printf("!!! %s: Internal error while scanning options [0x%02x]!\n", + *argv, opt_stat ); + stat = ERR_ERROR; + } + } + break; + } + } + + if (test_opt) + { + printf("\nOPTIONS [N=%u/%u]: %s\n",go->lopt_used,go->lopt_size,go->sopt); + const struct option *opt; + for ( opt = go->lopt; opt->name; opt++ ) + printf(" %04x %u %s\n", opt->val, opt->has_arg, opt->name ); + } + + char **dest = argv + optind; + gopt->argv = dest; + + uint i; + for ( i = optind; i < argc; i++ ) + { + char *arg = argv[i]; + if ( !OnArgument || !OnArgument(go,gopt,any_ptr,dest-gopt->argv,arg) ) + *dest++ = arg; + } + gopt->argc = dest - gopt->argv; + + if (err) + { + printf("%s!!! %s: Syntax error!%s\n",colout->warn,*argv,colout->reset); + stat = ERR_SYNTAX; + goto abort; + } + + if ( max_param >= 0 && gopt->argc > max_param ) + { + if (!max_param) + printf( + "%s!!! %s: No parameters allowed, but %u found!%s\n", + colout->warn, *argv, gopt->argc, colout->reset ); + else + printf( + "%s!!! %s: Maximal %u parameter%s allowed, but %u found!%s\n", + colout->warn, *argv, + max_param, max_param == 1 ? "" : "s", + gopt->argc, colout->reset ); + stat = ERR_SYNTAX; + goto abort; + } + + if ( scan_opt & SGO_PAGER && !gopt->pager ) + gopt->pager++; + + abort: + ResetGenericOpt(go); + if (stat) + InitializeGOptions(gopt); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-option.h b/project/dclib/dclib-option.h new file mode 100644 index 0000000..f1bf8f0 --- /dev/null +++ b/project/dclib/dclib-option.h @@ -0,0 +1,383 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_OPTION_H +#define DCLIB_OPTION_H 1 + +#include "dclib-basics.h" +struct TCPStream_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GenericOpt_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[GenericOptParm_t]] + +typedef struct GenericOptParm_t +{ + u32 id; // id of the option, + u8 arg; // 0:no arg, 1:arg, 2:optional arg + ccp name; // pipe separated option names +} +GenericOptParm_t; + +//----------------------------------------------------------------------------- +// [[GenericOpt_t]] + +struct option; + +typedef struct GenericOpt_t +{ + //--- short options + + char sopt[0x100]; // short options string + u32 sopt_id[0x80]; + + //--- long options + + struct option *lopt; // long options list + uint lopt_used; // number of used 'lopt' elements + uint lopt_size; // number of alloced 'lopt' elements + + //--- string pool + + MemPool_t str_pool; // pool for option names +} +GenericOpt_t; + +//----------------------------------------------------------------------------- + +void InitializeGenericOpt ( GenericOpt_t *go, char first_char ); +void ResetGenericOpt ( GenericOpt_t *go ); + +void AddGenericOpt +( + GenericOpt_t *go, // data structure + const GenericOptParm_t *gp, // list to add, list end on name==NULL + bool rm_minus // remove '-' on options for alternatives +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GOPT_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[GOPT_t]] + +typedef enum GOPT_t +{ + GOPT_RESERVED_BEG = 0x01000000, // begin of reserved range + GOPT_RESERVED_END = 0x02000000, // end of reserved range + + GOPT_M_INDEX = 0x000000ff, // mask to get the index + GOPT_N_PARAM = 25, // max used index+1 = num of params + GOPT_M_VALUE = 0x0003ff00, // mask for GOPT_T_PLUS|GOPT_T_MINUS + GOPT_S_VALUE = 8, // value is shifted by this num + GOPT_M_TYPE = 0x00fc0000, // mask to isolate option type + GOPT_I_TYPE = 0x00040000, // increment between types + GOPT_S_TYPE = 18, // type is shifted by this num + + + //--- parameters by index + + GOPT_IDX_NULL = 0, // type undefined + + GOPT_IDX_INC, // increment 'sn', ptype := GOPT_T_INC + GOPT_IDX_INC0, // increment 'sn', ptype := GOPT_T_INC + GOPT_IDX_DEC, // decrement 'sn', ptype := GOPT_T_INC + GOPT_IDX_DEC0, // decrement 'sn', ptype := GOPT_T_INC + + GOPT_IDX_SETP, // set 'sn' to value 0..0x3ff, ptype := GOPT_IDX_SINT + GOPT_IDX_SETM, // set 'sn' to value -0..-0x3ff, ptype := GOPT_IDX_SINT + GOPT_IDX_SETP64, // set 'sn64' to value 0..0x3ff, ptype := GOPT_IDX_S64 + GOPT_IDX_SETM64, // set 'sn64' to value -0..-0x3ff, ptype := GOPT_IDX_S64 + + GOPT_IDX_OR, // set 'un' |= value 0..0x3ff, ptype := GOPT_IDX_UINT + GOPT_IDX_SETBIT, // set bit VALUE in 'un', ptype := GOPT_IDX_UINT + GOPT_IDX_BIT, // ana param and set/clear bit VALUE in 'un', ptype := GOPT_IDX_UINT + GOPT_IDX_OR64, // set 'un64' |= value 0..0x3ff, ptype := GOPT_IDX_U64 + GOPT_IDX_SETBIT64, // set bit VALUE in 'un64', ptype := GOPT_IDX_UINT64 + + GOPT_IDX_SINT, // scan signed integer + GOPT_IDX_UINT, // scan unsigned integer + GOPT_IDX_OFF_ON, // scan 'off|onn|0|1' and otional a number, ptype := GOPT_IDX_UINT + GOPT_IDX_S64, // scan signed 64-bit integer + GOPT_IDX_U64, // scan unsigned 64-bit integer + GOPT_IDX_HEX, // scan unsigned 64-bit integer, assume hex + GOPT_IDX_SIZE, // scan size (double with optional SI) and store as 'un64' + GOPT_IDX_DOUBLE, // scan double float + GOPT_IDX_DURATION, // scan duration (double with optional SI) and store as 'd' + GOPT_IDX_STRING, // store ccp pointer to text + + GOPT_IDX_END, // end of parameters + + + //--- parameters types + + GOPT_T_NULL = 0, // type undefined + GOPT_T_INC = GOPT_RESERVED_BEG | ( GOPT_IDX_INC << GOPT_S_TYPE ), + GOPT_T_INC0 = GOPT_RESERVED_BEG | ( GOPT_IDX_INC0 << GOPT_S_TYPE ), + GOPT_T_DEC = GOPT_RESERVED_BEG | ( GOPT_IDX_DEC << GOPT_S_TYPE ), + GOPT_T_DEC0 = GOPT_RESERVED_BEG | ( GOPT_IDX_DEC0 << GOPT_S_TYPE ), + GOPT_T_SETP = GOPT_RESERVED_BEG | ( GOPT_IDX_SETP << GOPT_S_TYPE ), + GOPT_T_SETM = GOPT_RESERVED_BEG | ( GOPT_IDX_SETM << GOPT_S_TYPE ), + GOPT_T_SETP64 = GOPT_RESERVED_BEG | ( GOPT_IDX_SETP64 << GOPT_S_TYPE ), + GOPT_T_SETM64 = GOPT_RESERVED_BEG | ( GOPT_IDX_SETM64 << GOPT_S_TYPE ), + GOPT_T_OR = GOPT_RESERVED_BEG | ( GOPT_IDX_OR << GOPT_S_TYPE ), + GOPT_T_SETBIT = GOPT_RESERVED_BEG | ( GOPT_IDX_SETBIT << GOPT_S_TYPE ), + GOPT_T_BIT = GOPT_RESERVED_BEG | ( GOPT_IDX_BIT << GOPT_S_TYPE ), + GOPT_T_OR64 = GOPT_RESERVED_BEG | ( GOPT_IDX_OR64 << GOPT_S_TYPE ), + GOPT_T_SETBIT64 = GOPT_RESERVED_BEG | ( GOPT_IDX_SETBIT64 << GOPT_S_TYPE ), + GOPT_T_SINT = GOPT_RESERVED_BEG | ( GOPT_IDX_SINT << GOPT_S_TYPE ), + GOPT_T_UINT = GOPT_RESERVED_BEG | ( GOPT_IDX_UINT << GOPT_S_TYPE ), + GOPT_T_OFF_ON = GOPT_RESERVED_BEG | ( GOPT_IDX_OFF_ON << GOPT_S_TYPE ), + GOPT_T_S64 = GOPT_RESERVED_BEG | ( GOPT_IDX_S64 << GOPT_S_TYPE ), + GOPT_T_U64 = GOPT_RESERVED_BEG | ( GOPT_IDX_U64 << GOPT_S_TYPE ), + GOPT_T_HEX = GOPT_RESERVED_BEG | ( GOPT_IDX_HEX << GOPT_S_TYPE ), + GOPT_T_SIZE = GOPT_RESERVED_BEG | ( GOPT_IDX_SIZE << GOPT_S_TYPE ), + GOPT_T_DOUBLE = GOPT_RESERVED_BEG | ( GOPT_IDX_DOUBLE << GOPT_S_TYPE ), + GOPT_T_DURATION = GOPT_RESERVED_BEG | ( GOPT_IDX_DURATION << GOPT_S_TYPE ), + GOPT_T_STRING = GOPT_RESERVED_BEG | ( GOPT_IDX_STRING << GOPT_S_TYPE ), + GOPT_T_END = GOPT_RESERVED_BEG | ( GOPT_IDX_END << GOPT_S_TYPE ), + + + //--- named options + + GOPT_O__BEGIN = 0x100, + GOPT_O_TEST_OPT, // test: print all defined options + + GOPT_O_HELP, // force help + GOPT_O_CLEAR, // clear screen -> ScanGOptionsHelperTN() + GOPT_O_PAGER, // activate pager -> ScanGOptionsHelperTN() + GOPT_O_QUIT, // quit if command done -> ScanGOptionsHelperTN() + + GOPT_O_QUIET, // be quiet + GOPT_O_VERBOSE, // be verbose + + GOPT_O__CONTINUE // continue with this value on more options +} +GOPT_t; + +static inline GOPT_t GetGOPType ( GOPT_t gt ) + { return ( gt & GOPT_M_TYPE ) >> GOPT_S_TYPE; } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global options: parameters /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[GParamValueType_t]] + +typedef enum GParamValueType_t +{ + GPT_NONE, // no param set + GPT_SN, // value stored as 'sn' + GPT_UN, // value stored as 'un' + GPT_SN64, // value stored as 'sn64' + GPT_UN64, // value stored as 'un64' + GPT_D, // value stored as 'd' + GPT_STR, // value stored as 'str' + GPT_BIT, // value stored as 'bit.val' & 'bit.mask' +} +GParamValueType_t; + +GParamValueType_t GetGParamValueType ( GOPT_t gt ); + +/////////////////////////////////////////////////////////////////////////////// +// [[GParam_t]] + +typedef struct GParam_t +{ + u16 ptype; // GOPT_IDX_* + u16 vtype; // GParamValueType_t + + union + { + int sn; // used by GOPT_T_INC*, GOPT_T_DEC*, GOPT_T_SINT + uint un; // used by GOPT_T_UINT + s64 sn64; // used by GOPT_T_S64, GOPT_T_SETM + u64 un64; // used by GOPT_T_U64, GOPT_T_HEX, GOPT_T_SIZE, GOPT_T_SETP + double d; // used by GOPT_T_DOUBLE, GOPT_T_DURATION + ccp str; // used by GOPT_T_STRING + struct { u32 val; u32 mask; } bit; + // used by GOPT_T_BIT + }; +} +GParam_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[GetGParam_t]] + +typedef enum GetGParam_t +{ + GGP_NONE, // no param set or param unusable, 'return_val' untouched + GGP_CONVERT, // param set, but converted (different numeric type) + GGP_SIGN, // param returned, but sign maybe converted + GGP_OK // original or extended param returned +} +GetGParam_t; + +// If gp==NULL: return GGP_NONE +// If return_val==NULL: analyse only +GetGParam_t GetGParamINT ( const GParam_t *par, int *return_val ); +GetGParam_t GetGParamUINT ( const GParam_t *par, uint *return_val ); +GetGParam_t GetGParamS64 ( const GParam_t *par, s64 *return_val ); +GetGParam_t GetGParamU64 ( const GParam_t *par, u64 *return_val ); +GetGParam_t GetGParamDBL ( const GParam_t *par, double *return_val ); +GetGParam_t GetGParamSTR ( const GParam_t *par, ccp *return_val ); +GetGParam_t GetGParamBIT ( const GParam_t *par, u32 *return_val, u32 *return_mask ); + +ccp PrintGParam ( const GParam_t *par ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global options /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[GOptions_t]] + +typedef struct GOptions_t +{ + //--- parameters + + int argc; // number of parameters + char **argv; // paramters without options + + + //--- named options + + int help; // number of GOPT_O_HELP + int clear; // number of GOPT_O_CLEAR + int pager; // number of GOPT_O_PAGER, + int quit; // number of GOPT_O_QUIT, + int verbose; // combi of GOPT_O_QUIET & GOPT_O_VERBOSE, + + + //--- generic parameter support + + GParam_t param[GOPT_N_PARAM]; +} +GOptions_t; + +//----------------------------------------------------------------------------- + +static inline void InitializeGOptions ( GOptions_t *go ) +{ + DASSERT(go); + memset(go,0,sizeof(*go)); +} + +static inline const GParam_t * GetGParam ( const GOptions_t *go, uint par_index ) + { return go && par_index < GOPT_N_PARAM ? go->param+par_index : 0; } + +/////////////////////////////////////////////////////////////////////////////// + +extern const GenericOptParm_t StandardGOptions[]; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef int (*ScanOptionFunc) +( + // returns: 0 on not procecced / <0 on error / >0 on success + + GenericOpt_t *go, // valid pointer to generic options + GOptions_t *gopt, // valid pointer to options data + void *any_ptr, // user specific pointer + int num, // numeric param: + // OnAddOptions(): 'scan_opt' + // OnOption(): option id + // OnArgument(): argument index + ccp arg // NULL or text argument +); + +/////////////////////////////////////////////////////////////////////////////// +// [[ScanGOpt_t]] + +typedef enum ScanGOpt_t +{ + SGO_KNOWN = 0x0001, // scan only until first unknown parameter + SGO_PAGER = 0x0002, // auto enable pager + + SGO__CONTINUE = 0x0004 // define more flags based on this value +} +ScanGOpt_t; + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanGOptionsHelper +( + GOptions_t *gopt, // valid pointer + GenericOpt_t *go, // NULL or list with options + // => ResetGenericOpt() is called at finish + int argc, // argument counter + char **argv, // list of arguments + int max_param, // >=0: max allowed parameters + ScanGOpt_t scan_opt, // scan options + + //--- call back functions + ScanOptionFunc OnAddOptions, // not NULL: called once to add options + ScanOptionFunc OnOption, // not NULL: called for each option + ScanOptionFunc OnArgument, // not NULL: called for each argument + void *any_ptr // NULL or user specific pointer for call back +); + +//----------------------------------------------------------------------------- + +enumError ScanGOptionsHelperTN +( + // like ScanGOptionsHelper(), but with TELNET support + // for 'gopt->clear', 'gopt->pager' and 'gopt->quit', + // if 'ts' is not NULL and telnet is active. + + GOptions_t *gopt, // valid pointer + GenericOpt_t *go, // NULL or list with options + // => ResetGenericOpt() is called at finish + int argc, // argument counter + char **argv, // list of arguments + int max_param, // >=0: max allowed parameters + ScanGOpt_t scan_opt, // scan options + struct TCPStream_t *ts, // NULL or current stream with TELNET support + + //--- call back functions + ScanOptionFunc OnAddOptions, // not NULL: called once to add options + ScanOptionFunc OnOption, // not NULL: called for each option + ScanOptionFunc OnArgument, // not NULL: called for each argument + void *any_ptr // NULL or user specific pointer for call back +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_OPTION_H + diff --git a/project/dclib/dclib-regex.c b/project/dclib/dclib-regex.c new file mode 100644 index 0000000..d9bdf99 --- /dev/null +++ b/project/dclib/dclib-regex.c @@ -0,0 +1,465 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include "dclib-regex.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// some macro definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef REGEX_FREE +#undef REOPT_ICASE +#undef MATCH_BEG +#undef MATCH_END + +#if DCLIB_USE_PCRE + #define REGEX_FREE pcre_free + #define REOPT_ICASE PCRE_CASELESS + #define MATCH_BEG(i) match[2*i] + #define MATCH_END(i) match[2*i+1] +#else + #define REGEX_FREE regfree + #define REOPT_ICASE REG_ICASE + #define MATCH_BEG(i) match[i].rm_so + #define MATCH_END(i) match[i].rm_eo +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct Regex_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeRegex ( Regex_t *re ) +{ + DASSERT(re); + memset(re,0,sizeof(*re)); + re->re_list = re->re_pool; + re->re_size = sizeof(re->re_pool)/sizeof(*re->re_pool); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetRegex ( Regex_t *re ) +{ + DASSERT(re); + + RegexElem_t *e, *eend = re->re_list + re->re_used; + for ( e = re->re_list; e < eend; e++ ) + { + #if DCLIB_USE_PCRE + if (e->regex) + pcre_free(e->regex); + #else + regfree(&e->regex); + #endif + + FreeString(e->pattern); + FreeString(e->replace.ptr); + FREE(e->repl); + } + + if ( re->re_list != re->re_pool ) + FREE(re->re_list); + + InitializeRegex(re); +} + +/////////////////////////////////////////////////////////////////////////////// + +static RegexElem_t * GetNextRegexElem ( Regex_t *re ) +{ + DASSERT(re); + if ( re->re_used >= re->re_size ) + { + re->re_size = 3*re->re_size/2 + 10; + if ( re->re_list == re->re_pool ) + { + re->re_list = MALLOC( sizeof(*re->re_list) * re->re_size ); + memcpy( re->re_list, re->re_pool, sizeof(*re->re_list) * re->re_used ); + } + else + re->re_list = REALLOC( re->re_list, sizeof(*re->re_list) * re->re_size ); + } + + RegexElem_t *e = re->re_list + re->re_used; + memset(e,0,sizeof(*e)); + return e; +} + +/////////////////////////////////////////////////////////////////////////////// + +static RegexReplace_t * GetNextRegexRepl + ( RegexElem_t *elem, char *ptr, int len, int ref ) +{ + DASSERT(elem); + if ( !len && ref < 0 ) + return 0; + + if ( elem->repl_used >= elem->repl_size ) + { + elem->repl_size = 3*elem->repl_size/2 + 10; + elem->repl = REALLOC( elem->repl, sizeof(*elem->repl) * elem->repl_size ); + } + + RegexReplace_t *repl = elem->repl + elem->repl_used++; + repl->str.ptr = ptr; + repl->str.len = len; + repl->ref = ref; + PRINT("REPL[%d]: |%.*s| %d\n",elem->repl_used-1,len,ptr,ref); + return repl; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRegex ( Regex_t *re, bool init_re, ccp regex ) +{ + DASSERT(re); + if (init_re) + InitializeRegex(re); + else + ResetRegex(re); + + if ( !regex || !*regex ) + return ERR_NOTHING_TO_DO; + + struct { FastBuf_t b; char space[500]; } temp; + InitializeFastBuf(&temp,sizeof(temp)); + + //--- scanning + + enumError err = ERR_OK; + for(;;) + { + while ( isspace(*regex) || *regex == ';' ) + regex++; + if (!*regex) + break; + + noPRINT("RE: ANALYZE #%u/%u: %s\n",re->re_used, re->re_size,regex); + + char sep = *regex++; + while ( *regex && *regex != sep ) + { + char ch = *regex++; + if ( ch == '\\' && *regex == sep ) + ch = *regex++; + AppendCharFastBuf(&temp.b,ch); + } + + if ( *regex != sep ) + { + err = ERR_SYNTAX; + goto exit; + } + + ccp repl = ++regex; + while ( *regex && *regex != sep ) + regex++; + const uint repl_len = regex - repl; + + RegexElem_t *elem = GetNextRegexElem(re); + + if ( *regex == sep ) + { + regex++; + while ( *regex && *regex != ';' ) + switch (*regex++) + { + case 'g': elem->global = true; break; + case 'i': elem->icase = true; elem->opt |= REOPT_ICASE; break; + } + } + + PRINT("RE: PATTERN #%u/%u: [%s] %s -> %.*s : %s\n", + re->re_used, re->re_size, + GetFastBufStatus(&temp.b), + GetFastBufString(&temp.b), + repl_len, repl, regex ); + + // store data as strings + elem->pattern = MEMDUP(temp.b.buf,temp.b.ptr-temp.b.buf); + elem->replace.ptr = repl_len ? MEMDUP(repl,repl_len) : EmptyString; + elem->replace.len = repl_len; + re->re_used++; + ClearFastBuf(&temp.b); + } + + + //--- finalize + + re->valid = true; + + uint e; + RegexElem_t *elem; + for ( e = 0, elem = re->re_list; e < re->re_used; e++, elem++ ) + { + #if DCLIB_USE_PCRE + + ccp errptr; + int erroffset; + re->regex = pcre_compile(elem->pattern,opt,&errptr,&erroffset,0); + if (!re->regex) + { + PRINT("PCRE error: %s: %s\n",errptr,pattern+erroffset); + return ERR_SEMANTIC; + err = ERR_SEMANTIC; + goto exit; + } + + #else + + int stat = regcomp(&elem->regex,elem->pattern,elem->opt|REG_EXTENDED); + if (stat) + { + #if HAVE_PRINT0 + char error[100]; + regerror(stat,&re->regex,errormsizeof(error)); + PRINT("PCRE error: %s: %s\n",errptr,pattern+erroffset); + #endif + re->valid = false; + err = ERR_SEMANTIC; + } + else + elem->valid = true; + #endif + + + //--- analyse replace + + char *ptr = (char*)elem->replace.ptr; + char *dest = ptr; + char *start = dest; + char *end = ptr + elem->replace.len; + + while ( ptr < end ) + { + while ( ptr < end && *ptr != '\\' && *ptr != '$' ) + *dest++ = *ptr++; + if ( ptr == end ) + { + GetNextRegexRepl(elem,start,dest-start,-1); + break; + } + + if ( *ptr == ptr[1] ) + { + ptr++; + *dest++ = *ptr++; + } + else if ( ptr[1] >= '0' && ptr[1] <= '9' ) + { + GetNextRegexRepl(elem,start,dest-start,ptr[1]-'0'); + start = dest; + ptr += 2; + } + else if ( ptr[1] == '{' ) + { + char *p = ptr+2; + if ( *p >= '0' && *p <= '9' ) + { + uint num = *p++ - '0'; + if ( *p >= '0' && *p <= '9' ) + num = 10*num + *p++ - '0'; + if ( p < end && *p == '}' ) + { + GetNextRegexRepl(elem,start,dest-start,num); + start = dest; + ptr = p+1; + continue; + } + } + *dest++ = *ptr++; + } + else + *dest++ = *ptr++; + } + + if ( dest != EmptyString ) + *dest = 0; + elem->replace.len = dest - elem->replace.ptr; + } + + PRINT("RE: n=%d, valid=%d\n",re->re_used,re->valid); + + exit: + ResetFastBuf(&temp.b); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ReplaceRegex +( + Regex_t *re, // valid Regex_t + FastBuf_t *res, // return buffer, cleared + ccp src, + int src_len // -1: use strlen() +) +{ + DASSERT(re); + DASSERT(res); + + + //--- sanity checks + + if (!res) + return -ERR_INVALID_DATA; + + ClearFastBuf(res); + + if ( !re || !re->valid ) + return -ERR_INVALID_DATA; + + if ( !src || !*src ) + return 0; + + if ( src_len < 0 ) + src_len = strlen(src); + + + //--- prepare data + + enumError err = ERR_OK; + uint count = 0; + + struct { FastBuf_t b; char space[500]; } temp; + InitializeFastBuf(&temp,sizeof(temp)); + FastBuf_t *dest = res; + + #if DCLIB_USE_PCRE + DASSERT(re->regex); + enum { N_MATCH = 100 }; + int match[N_MATCH]; + #else + enum { N_MATCH = 100 }; + regmatch_t match[N_MATCH]; + #endif + + + //--- exec RE + + uint e; + RegexElem_t *elem; + for ( e = 0, elem = re->re_list; e < re->re_used; e++, elem++ ) + { + uint pos = 0, copied = 0; + while( pos < src_len ) + { + memset(match,0,sizeof(match)); + + #if DCLIB_USE_PCRE + int stat = pcre_exec(re->regex,0,src,src_len,pos,0,match,N_MATCH); + noPRINT("RE: stat=%2d : %2d %2d : %2d %2d : %2d %2d\n", + stat, match[0],match[1], match[2],match[3], match[4],match[5] ); + if ( stat < 0 ) + { + if ( stat == PCRE_ERROR_NOMATCH ) + break; + err = -ERR_ERROR; + goto exit; + } + const uint match_beg = match[0]; + const uint match_end = match[1]; + + #else + + int stat = regexec(&elem->regex,src+pos,N_MATCH,match,0); + PRINT("RE: pos=%2d, stat=%2d : %2d %2d : %2d %2d : %2d %2d\n", + pos, stat, match[0].rm_so,match[0].rm_eo, + match[1].rm_so,match[1].rm_eo, match[2].rm_so,match[2].rm_eo ); + if (stat) + { + if ( stat == REG_NOMATCH ) + break; + err = -ERR_ERROR; + goto exit; + } + const uint match_beg = match[0].rm_so + pos; + const uint match_end = match[0].rm_eo + pos; + + #endif + + if ( match_beg > copied ) + AppendFastBuf(dest,src+copied,match_beg-copied); + copied = match_end; + + RegexReplace_t *repl = elem->repl; + RegexReplace_t *repl_end = repl + elem->repl_used; + for ( ; repl < repl_end; repl++ ) + { + AppendMemFastBuf(dest,repl->str); + #if DCLIB_USE_PCRE + #error + #else + if ( repl->ref >= 0 && repl->ref < N_MATCH ) + { + const regmatch_t *m = match + repl->ref; + if ( m->rm_so >= 0 ) + AppendFastBuf( dest, + src + m->rm_so + pos, + m->rm_eo - m->rm_so ); + } + #endif + } + + pos = match_end + ( match_beg == match_end ); + count++; + + if (!elem->global) + break; + } + + if ( src_len > copied ) + AppendFastBuf(dest,src+copied,src_len-copied); + + PRINT("DEST: %s\n",GetFastBufString(dest)); + src = GetFastBufString(dest); + src_len = GetFastBufLen(dest); + dest = dest == res ? &temp.b : res; + ClearFastBuf(dest); + } + + exit:; + AssignFastBuf(res,src,src_len); + return err < 0 ? err : count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-regex.h b/project/dclib/dclib-regex.h new file mode 100644 index 0000000..9c8484d --- /dev/null +++ b/project/dclib/dclib-regex.h @@ -0,0 +1,131 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_REGEX_H +#define DCLIB_REGEX_H 1 + +#if DCLIB_USE_PCRE + #include + #define DC_REGEX_TYPE pcre +#else + #ifndef DCLIB_USE_REGEX + #define DCLIB_USE_REGEX 1 + #endif + #if DCLIB_USE_REGEX + #include + #define DC_REGEX_TYPE regex_t + #else + #error Nn Regex Support! + #endif +#endif + +#include "dclib-types.h" +#include "dclib-debug.h" +#include "dclib-basics.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct Regex_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[RegexReplace_t]] + +typedef struct RegexReplace_t +{ + mem_t str; // first: string to replace (maybe 0 bytes) + int ref; // second: back reference to copy (if >=0) +} +RegexReplace_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[RegexElem_t]] + +typedef struct RegexElem_t +{ + bool valid; // true: successfull compiled + bool global; // true: replace all ('g') + bool icase; // true: ignore case ('i') + + #if DCLIB_USE_PCRE + DC_REGEX_TYPE *regex; // not NULL: compiled regular expression + #else + DC_REGEX_TYPE regex; // compiled regular expression + #endif + + int opt; // compile options + ccp pattern; // pattern string, alloced + mem_t replace; // replace string, alloced + + RegexReplace_t *repl; // replace data, use 'replace' as reference + uint repl_used; // number of used elements in 'repl' + uint repl_size; // number of available elements in 'repl' +} +RegexElem_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[RegexMain_t]] + +typedef struct Regex_t +{ + bool valid; // true: succesfull initialized + + RegexElem_t *re_list; // list; either 're_pool' or alloced + RegexElem_t re_pool[3]; // pool for fast access + uint re_used; // number of used elements in 're_list' + uint re_size; // number of available elements in 're_list' +} +Regex_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeRegex ( Regex_t *re ); +void ResetRegex ( Regex_t *re ); + +enumError ScanRegex ( Regex_t *re, bool init_re, ccp regex ); + +int ReplaceRegex +( + Regex_t *re, // valid Regex_t + FastBuf_t *res, // return buffer, cleared + ccp src, + int src_len // -1: use strlen() +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_REGEX_H + diff --git a/project/dclib/dclib-shift-jis.c b/project/dclib/dclib-shift-jis.c new file mode 100644 index 0000000..13c5a93 --- /dev/null +++ b/project/dclib/dclib-shift-jis.c @@ -0,0 +1,1408 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +//#include +//#include + +#include "dclib-basics.h" +//#include "dclib-debug.h" +//#include "dclib-utf8.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// shift_jis_single[] /////////////// +/////////////////////////////////////////////////////////////////////////////// +// all UTF-16 values by 'iconv' + +#define X SJIS_TAB_MAPPING_BEG +#define INV SJIS_INVALID_CODE + +static const u16 shift_jis_single[0x100] = +{ + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, // 0x00..0x07 + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, // 0x08..0x0f + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // 0x10..0x17 + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // 0x18..0x1f + + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // 0x20..0x27 + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, // 0x28..0x2f + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // 0x30..0x37 + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // 0x38..0x3f + + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // 0x40..0x47 + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, // 0x48..0x4f + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // 0x50..0x57 + 0x58, 0x59, 0x5a, 0x5b, 0xa5, 0x5d, 0x5e, 0x5f, // 0x58..0x5f + + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, // 0x60..0x67 + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, // 0x68..0x6f + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // 0x70..0x77 + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d,0x203e, 0x7f, // 0x78..0x7f + + INV, X+0, X+1, X+2, X+3, INV, INV, INV, // 0x80..0x87 + X+4, X+5, X+6, X+7, X+8, X+9, X+10, X+11, // 0x88..0x8f + X+12, X+13, X+14, X+15, X+16, X+17, X+18, X+19, // 0x90..0x97 + X+20, X+21, X+22, X+23, X+24, X+25, X+26, X+27, // 0x98..0x9f + + INV,0xff61,0xff62,0xff63, 0xff64,0xff65,0xff66,0xff67, // 0xa0..0xa7 + 0xff68,0xff69,0xff6a,0xff6b, 0xff6c,0xff6d,0xff6e,0xff6f, // 0xa8..0xaf + 0xff70,0xff71,0xff72,0xff73, 0xff74,0xff75,0xff76,0xff77, // 0xb0..0xb7 + 0xff78,0xff79,0xff7a,0xff7b, 0xff7c,0xff7d,0xff7e,0xff7f, // 0xb8..0xbf + + 0xff80,0xff81,0xff82,0xff83, 0xff84,0xff85,0xff86,0xff87, // 0xc0..0xc7 + 0xff88,0xff89,0xff8a,0xff8b, 0xff8c,0xff8d,0xff8e,0xff8f, // 0xc8..0xcf + 0xff90,0xff91,0xff92,0xff93, 0xff94,0xff95,0xff96,0xff97, // 0xd0..0xd7 + 0xff98,0xff99,0xff9a,0xff9b, 0xff9c,0xff9d,0xff9e,0xff9f, // 0xd8..0xdf + + X+28, X+29, X+30, X+31, X+32, X+33, X+34, X+35, // 0xe0..0xe7 + X+36, X+37, X+38, INV, INV, INV, INV, INV, // 0xe8..0xef + INV, INV, INV, INV, INV, INV, INV, INV, // 0xf0..0xf7 + INV, INV, INV, INV, INV, INV, INV, X+39, // 0xf8..0xff +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// shift_jis_double_*[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + + +static const u16 shift_jis_double_81[] = +{ + 0x3000,0x3001,0x3002,0xff0c, 0xff0e,0x30fb,0xff1a,0xff1b, // 81 40..47 + 0xff1f,0xff01,0x309b,0x309c, 0xb4,0xff40, 0xa8,0xff3e, // 81 48..4f + 0xffe3,0xff3f,0x30fd,0x30fe, 0x309d,0x309e,0x3003,0x4edd, // 81 50..57 + 0x3005,0x3006,0x3007,0x30fc, 0x2015,0x2010,0xff0f,0xff3c, // 81 58..5f + 0x301c,0x2016,0xff5c,0x2026, 0x2025,0x2018,0x2019,0x201c, // 81 60..67 + 0x201d,0xff08,0xff09,0x3014, 0x3015,0xff3b,0xff3d,0xff5b, // 81 68..6f + 0xff5d,0x3008,0x3009,0x300a, 0x300b,0x300c,0x300d,0x300e, // 81 70..77 + 0x300f,0x3010,0x3011,0xff0b, 0x2212, 0xb1, 0xd7, INV, // 81 78..7f + 0xf7,0xff1d,0x2260,0xff1c, 0xff1e,0x2266,0x2267,0x221e, // 81 80..87 + 0x2234,0x2642,0x2640, 0xb0, 0x2032,0x2033,0x2103,0xffe5, // 81 88..8f + 0xff04, 0xa2, 0xa3,0xff05, 0xff03,0xff06,0xff0a,0xff20, // 81 90..97 + 0xa7,0x2606,0x2605,0x25cb, 0x25cf,0x25ce,0x25c7,0x25c6, // 81 98..9f + 0x25a1,0x25a0,0x25b3,0x25b2, 0x25bd,0x25bc,0x203b,0x3012, // 81 a0..a7 + 0x2192,0x2190,0x2191,0x2193, 0x3013, INV, INV, INV, // 81 a8..af + INV, INV, INV, INV, INV, INV, INV, INV, // 81 b0..b7 + 0x2208,0x220b,0x2286,0x2287, 0x2282,0x2283,0x222a,0x2229, // 81 b8..bf + INV, INV, INV, INV, INV, INV, INV, INV, // 81 c0..c7 + 0x2227,0x2228, 0xac,0x21d2, 0x21d4,0x2200,0x2203, INV, // 81 c8..cf + INV, INV, INV, INV, INV, INV, INV, INV, // 81 d0..d7 + INV, INV,0x2220,0x22a5, 0x2312,0x2202,0x2207,0x2261, // 81 d8..df + 0x2252,0x226a,0x226b,0x221a, 0x223d,0x221d,0x2235,0x222b, // 81 e0..e7 + 0x222c, INV, INV, INV, INV, INV, INV, INV, // 81 e8..ef + 0x212b,0x2030,0x266f,0x266d, 0x266a,0x2020,0x2021, 0xb6, // 81 f0..f7 + INV, INV, INV, INV, 0x25ef, // 81 f8..fc +}; + +static const u16 shift_jis_double_82[] = +{ + 0xff10,0xff11,0xff12,0xff13, 0xff14,0xff15,0xff16,0xff17, // 82 4f..56 + 0xff18,0xff19, INV, INV, INV, INV, INV, INV, // 82 57..5e + INV,0xff21,0xff22,0xff23, 0xff24,0xff25,0xff26,0xff27, // 82 5f..66 + 0xff28,0xff29,0xff2a,0xff2b, 0xff2c,0xff2d,0xff2e,0xff2f, // 82 67..6e + 0xff30,0xff31,0xff32,0xff33, 0xff34,0xff35,0xff36,0xff37, // 82 6f..76 + 0xff38,0xff39,0xff3a, INV, INV, INV, INV, INV, // 82 77..7e + INV, INV,0xff41,0xff42, 0xff43,0xff44,0xff45,0xff46, // 82 7f..86 + 0xff47,0xff48,0xff49,0xff4a, 0xff4b,0xff4c,0xff4d,0xff4e, // 82 87..8e + 0xff4f,0xff50,0xff51,0xff52, 0xff53,0xff54,0xff55,0xff56, // 82 8f..96 + 0xff57,0xff58,0xff59,0xff5a, INV, INV, INV, INV, // 82 97..9e + 0x3041,0x3042,0x3043,0x3044, 0x3045,0x3046,0x3047,0x3048, // 82 9f..a6 + 0x3049,0x304a,0x304b,0x304c, 0x304d,0x304e,0x304f,0x3050, // 82 a7..ae + 0x3051,0x3052,0x3053,0x3054, 0x3055,0x3056,0x3057,0x3058, // 82 af..b6 + 0x3059,0x305a,0x305b,0x305c, 0x305d,0x305e,0x305f,0x3060, // 82 b7..be + 0x3061,0x3062,0x3063,0x3064, 0x3065,0x3066,0x3067,0x3068, // 82 bf..c6 + 0x3069,0x306a,0x306b,0x306c, 0x306d,0x306e,0x306f,0x3070, // 82 c7..ce + 0x3071,0x3072,0x3073,0x3074, 0x3075,0x3076,0x3077,0x3078, // 82 cf..d6 + 0x3079,0x307a,0x307b,0x307c, 0x307d,0x307e,0x307f,0x3080, // 82 d7..de + 0x3081,0x3082,0x3083,0x3084, 0x3085,0x3086,0x3087,0x3088, // 82 df..e6 + 0x3089,0x308a,0x308b,0x308c, 0x308d,0x308e,0x308f,0x3090, // 82 e7..ee + 0x3091,0x3092,0x3093, // 82 ef..f1 +}; + +static const u16 shift_jis_double_83[] = +{ + 0x30a1,0x30a2,0x30a3,0x30a4, 0x30a5,0x30a6,0x30a7,0x30a8, // 83 40..47 + 0x30a9,0x30aa,0x30ab,0x30ac, 0x30ad,0x30ae,0x30af,0x30b0, // 83 48..4f + 0x30b1,0x30b2,0x30b3,0x30b4, 0x30b5,0x30b6,0x30b7,0x30b8, // 83 50..57 + 0x30b9,0x30ba,0x30bb,0x30bc, 0x30bd,0x30be,0x30bf,0x30c0, // 83 58..5f + 0x30c1,0x30c2,0x30c3,0x30c4, 0x30c5,0x30c6,0x30c7,0x30c8, // 83 60..67 + 0x30c9,0x30ca,0x30cb,0x30cc, 0x30cd,0x30ce,0x30cf,0x30d0, // 83 68..6f + 0x30d1,0x30d2,0x30d3,0x30d4, 0x30d5,0x30d6,0x30d7,0x30d8, // 83 70..77 + 0x30d9,0x30da,0x30db,0x30dc, 0x30dd,0x30de,0x30df, INV, // 83 78..7f + 0x30e0,0x30e1,0x30e2,0x30e3, 0x30e4,0x30e5,0x30e6,0x30e7, // 83 80..87 + 0x30e8,0x30e9,0x30ea,0x30eb, 0x30ec,0x30ed,0x30ee,0x30ef, // 83 88..8f + 0x30f0,0x30f1,0x30f2,0x30f3, 0x30f4,0x30f5,0x30f6, INV, // 83 90..97 + INV, INV, INV, INV, INV, INV, INV, 0x391, // 83 98..9f + 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, // 83 a0..a7 + 0x39a, 0x39b, 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, // 83 a8..af + 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, INV, // 83 b0..b7 + INV, INV, INV, INV, INV, INV, INV, 0x3b1, // 83 b8..bf + 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, // 83 c0..c7 + 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf, 0x3c0, 0x3c1, // 83 c8..cf + 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, // 83 d0..d6 +}; + +static const u16 shift_jis_double_84[] = +{ + 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x401, 0x416, // 84 40..47 + 0x417, 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, // 84 48..4f + 0x41f, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, // 84 50..57 + 0x427, 0x428, 0x429, 0x42a, 0x42b, 0x42c, 0x42d, 0x42e, // 84 58..5f + 0x42f, INV, INV, INV, INV, INV, INV, INV, // 84 60..67 + INV, INV, INV, INV, INV, INV, INV, INV, // 84 68..6f + 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x451, 0x436, // 84 70..77 + 0x437, 0x438, 0x439, 0x43a, 0x43b, 0x43c, 0x43d, INV, // 84 78..7f + 0x43e, 0x43f, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, // 84 80..87 + 0x446, 0x447, 0x448, 0x449, 0x44a, 0x44b, 0x44c, 0x44d, // 84 88..8f + 0x44e, 0x44f, INV, INV, INV, INV, INV, INV, // 84 90..97 + INV, INV, INV, INV, INV, INV, INV,0x2500, // 84 98..9f + 0x2502,0x250c,0x2510,0x2518, 0x2514,0x251c,0x252c,0x2524, // 84 a0..a7 + 0x2534,0x253c,0x2501,0x2503, 0x250f,0x2513,0x251b,0x2517, // 84 a8..af + 0x2523,0x2533,0x252b,0x253b, 0x254b,0x2520,0x252f,0x2528, // 84 b0..b7 + 0x2537,0x253f,0x251d,0x2530, 0x2525,0x2538,0x2542, // 84 b8..be +}; + +static const u16 shift_jis_double_88[] = +{ + 0x4e9c,0x5516,0x5a03,0x963f, 0x54c0,0x611b,0x6328,0x59f6, // 88 9f..a6 + 0x9022,0x8475,0x831c,0x7a50, 0x60aa,0x63e1,0x6e25,0x65ed, // 88 a7..ae + 0x8466,0x82a6,0x9bf5,0x6893, 0x5727,0x65a1,0x6271,0x5b9b, // 88 af..b6 + 0x59d0,0x867b,0x98f4,0x7d62, 0x7dbe,0x9b8e,0x6216,0x7c9f, // 88 b7..be + 0x88b7,0x5b89,0x5eb5,0x6309, 0x6697,0x6848,0x95c7,0x978d, // 88 bf..c6 + 0x674f,0x4ee5,0x4f0a,0x4f4d, 0x4f9d,0x5049,0x56f2,0x5937, // 88 c7..ce + 0x59d4,0x5a01,0x5c09,0x60df, 0x610f,0x6170,0x6613,0x6905, // 88 cf..d6 + 0x70ba,0x754f,0x7570,0x79fb, 0x7dad,0x7def,0x80c3,0x840e, // 88 d7..de + 0x8863,0x8b02,0x9055,0x907a, 0x533b,0x4e95,0x4ea5,0x57df, // 88 df..e6 + 0x80b2,0x90c1,0x78ef,0x4e00, 0x58f1,0x6ea2,0x9038,0x7a32, // 88 e7..ee + 0x8328,0x828b,0x9c2f,0x5141, 0x5370,0x54bd,0x54e1,0x56e0, // 88 ef..f6 + 0x59fb,0x5f15,0x98f2,0x6deb, 0x80e4,0x852d, // 88 f7..fc +}; + +static const u16 shift_jis_double_89[] = +{ + 0x9662,0x9670,0x96a0,0x97fb, 0x540b,0x53f3,0x5b87,0x70cf, // 89 40..47 + 0x7fbd,0x8fc2,0x96e8,0x536f, 0x9d5c,0x7aba,0x4e11,0x7893, // 89 48..4f + 0x81fc,0x6e26,0x5618,0x5504, 0x6b1d,0x851a,0x9c3b,0x59e5, // 89 50..57 + 0x53a9,0x6d66,0x74dc,0x958f, 0x5642,0x4e91,0x904b,0x96f2, // 89 58..5f + 0x834f,0x990c,0x53e1,0x55b6, 0x5b30,0x5f71,0x6620,0x66f3, // 89 60..67 + 0x6804,0x6c38,0x6cf3,0x6d29, 0x745b,0x76c8,0x7a4e,0x9834, // 89 68..6f + 0x82f1,0x885b,0x8a60,0x92ed, 0x6db2,0x75ab,0x76ca,0x99c5, // 89 70..77 + 0x60a6,0x8b01,0x8d8a,0x95b2, 0x698e,0x53ad,0x5186, INV, // 89 78..7f + 0x5712,0x5830,0x5944,0x5bb4, 0x5ef6,0x6028,0x63a9,0x63f4, // 89 80..87 + 0x6cbf,0x6f14,0x708e,0x7114, 0x7159,0x71d5,0x733f,0x7e01, // 89 88..8f + 0x8276,0x82d1,0x8597,0x9060, 0x925b,0x9d1b,0x5869,0x65bc, // 89 90..97 + 0x6c5a,0x7525,0x51f9,0x592e, 0x5965,0x5f80,0x5fdc,0x62bc, // 89 98..9f + 0x65fa,0x6a2a,0x6b27,0x6bb4, 0x738b,0x7fc1,0x8956,0x9d2c, // 89 a0..a7 + 0x9d0e,0x9ec4,0x5ca1,0x6c96, 0x837b,0x5104,0x5c4b,0x61b6, // 89 a8..af + 0x81c6,0x6876,0x7261,0x4e59, 0x4ffa,0x5378,0x6069,0x6e29, // 89 b0..b7 + 0x7a4f,0x97f3,0x4e0b,0x5316, 0x4eee,0x4f55,0x4f3d,0x4fa1, // 89 b8..bf + 0x4f73,0x52a0,0x53ef,0x5609, 0x590f,0x5ac1,0x5bb6,0x5be1, // 89 c0..c7 + 0x79d1,0x6687,0x679c,0x67b6, 0x6b4c,0x6cb3,0x706b,0x73c2, // 89 c8..cf + 0x798d,0x79be,0x7a3c,0x7b87, 0x82b1,0x82db,0x8304,0x8377, // 89 d0..d7 + 0x83ef,0x83d3,0x8766,0x8ab2, 0x5629,0x8ca8,0x8fe6,0x904e, // 89 d8..df + 0x971e,0x868a,0x4fc4,0x5ce8, 0x6211,0x7259,0x753b,0x81e5, // 89 e0..e7 + 0x82bd,0x86fe,0x8cc0,0x96c5, 0x9913,0x99d5,0x4ecb,0x4f1a, // 89 e8..ef + 0x89e3,0x56de,0x584a,0x58ca, 0x5efb,0x5feb,0x602a,0x6094, // 89 f0..f7 + 0x6062,0x61d0,0x6212,0x62d0, 0x6539, // 89 f8..fc +}; + +static const u16 shift_jis_double_8a[] = +{ + 0x9b41,0x6666,0x68b0,0x6d77, 0x7070,0x754c,0x7686,0x7d75, // 8a 40..47 + 0x82a5,0x87f9,0x958b,0x968e, 0x8c9d,0x51f1,0x52be,0x5916, // 8a 48..4f + 0x54b3,0x5bb3,0x5d16,0x6168, 0x6982,0x6daf,0x788d,0x84cb, // 8a 50..57 + 0x8857,0x8a72,0x93a7,0x9ab8, 0x6d6c,0x99a8,0x86d9,0x57a3, // 8a 58..5f + 0x67ff,0x86ce,0x920e,0x5283, 0x5687,0x5404,0x5ed3,0x62e1, // 8a 60..67 + 0x64b9,0x683c,0x6838,0x6bbb, 0x7372,0x78ba,0x7a6b,0x899a, // 8a 68..6f + 0x89d2,0x8d6b,0x8f03,0x90ed, 0x95a3,0x9694,0x9769,0x5b66, // 8a 70..77 + 0x5cb3,0x697d,0x984d,0x984e, 0x639b,0x7b20,0x6a2b, INV, // 8a 78..7f + 0x6a7f,0x68b6,0x9c0d,0x6f5f, 0x5272,0x559d,0x6070,0x62ec, // 8a 80..87 + 0x6d3b,0x6e07,0x6ed1,0x845b, 0x8910,0x8f44,0x4e14,0x9c39, // 8a 88..8f + 0x53f6,0x691b,0x6a3a,0x9784, 0x682a,0x515c,0x7ac3,0x84b2, // 8a 90..97 + 0x91dc,0x938c,0x565b,0x9d28, 0x6822,0x8305,0x8431,0x7ca5, // 8a 98..9f + 0x5208,0x82c5,0x74e6,0x4e7e, 0x4f83,0x51a0,0x5bd2,0x520a, // 8a a0..a7 + 0x52d8,0x52e7,0x5dfb,0x559a, 0x582a,0x59e6,0x5b8c,0x5b98, // 8a a8..af + 0x5bdb,0x5e72,0x5e79,0x60a3, 0x611f,0x6163,0x61be,0x63db, // 8a b0..b7 + 0x6562,0x67d1,0x6853,0x68fa, 0x6b3e,0x6b53,0x6c57,0x6f22, // 8a b8..bf + 0x6f97,0x6f45,0x74b0,0x7518, 0x76e3,0x770b,0x7aff,0x7ba1, // 8a c0..c7 + 0x7c21,0x7de9,0x7f36,0x7ff0, 0x809d,0x8266,0x839e,0x89b3, // 8a c8..cf + 0x8acc,0x8cab,0x9084,0x9451, 0x9593,0x9591,0x95a2,0x9665, // 8a d0..d7 + 0x97d3,0x9928,0x8218,0x4e38, 0x542b,0x5cb8,0x5dcc,0x73a9, // 8a d8..df + 0x764c,0x773c,0x5ca9,0x7feb, 0x8d0b,0x96c1,0x9811,0x9854, // 8a e0..e7 + 0x9858,0x4f01,0x4f0e,0x5371, 0x559c,0x5668,0x57fa,0x5947, // 8a e8..ef + 0x5b09,0x5bc4,0x5c90,0x5e0c, 0x5e7e,0x5fcc,0x63ee,0x673a, // 8a f0..f7 + 0x65d7,0x65e2,0x671f,0x68cb, 0x68c4, // 8a f8..fc +}; + +static const u16 shift_jis_double_8b[] = +{ + 0x6a5f,0x5e30,0x6bc5,0x6c17, 0x6c7d,0x757f,0x7948,0x5b63, // 8b 40..47 + 0x7a00,0x7d00,0x5fbd,0x898f, 0x8a18,0x8cb4,0x8d77,0x8ecc, // 8b 48..4f + 0x8f1d,0x98e2,0x9a0e,0x9b3c, 0x4e80,0x507d,0x5100,0x5993, // 8b 50..57 + 0x5b9c,0x622f,0x6280,0x64ec, 0x6b3a,0x72a0,0x7591,0x7947, // 8b 58..5f + 0x7fa9,0x87fb,0x8abc,0x8b70, 0x63ac,0x83ca,0x97a0,0x5409, // 8b 60..67 + 0x5403,0x55ab,0x6854,0x6a58, 0x8a70,0x7827,0x6775,0x9ecd, // 8b 68..6f + 0x5374,0x5ba2,0x811a,0x8650, 0x9006,0x4e18,0x4e45,0x4ec7, // 8b 70..77 + 0x4f11,0x53ca,0x5438,0x5bae, 0x5f13,0x6025,0x6551, INV, // 8b 78..7f + 0x673d,0x6c42,0x6c72,0x6ce3, 0x7078,0x7403,0x7a76,0x7aae, // 8b 80..87 + 0x7b08,0x7d1a,0x7cfe,0x7d66, 0x65e7,0x725b,0x53bb,0x5c45, // 8b 88..8f + 0x5de8,0x62d2,0x62e0,0x6319, 0x6e20,0x865a,0x8a31,0x8ddd, // 8b 90..97 + 0x92f8,0x6f01,0x79a6,0x9b5a, 0x4ea8,0x4eab,0x4eac,0x4f9b, // 8b 98..9f + 0x4fa0,0x50d1,0x5147,0x7af6, 0x5171,0x51f6,0x5354,0x5321, // 8b a0..a7 + 0x537f,0x53eb,0x55ac,0x5883, 0x5ce1,0x5f37,0x5f4a,0x602f, // 8b a8..af + 0x6050,0x606d,0x631f,0x6559, 0x6a4b,0x6cc1,0x72c2,0x72ed, // 8b b0..b7 + 0x77ef,0x80f8,0x8105,0x8208, 0x854e,0x90f7,0x93e1,0x97ff, // 8b b8..bf + 0x9957,0x9a5a,0x4ef0,0x51dd, 0x5c2d,0x6681,0x696d,0x5c40, // 8b c0..c7 + 0x66f2,0x6975,0x7389,0x6850, 0x7c81,0x50c5,0x52e4,0x5747, // 8b c8..cf + 0x5dfe,0x9326,0x65a4,0x6b23, 0x6b3d,0x7434,0x7981,0x79bd, // 8b d0..d7 + 0x7b4b,0x7dca,0x82b9,0x83cc, 0x887f,0x895f,0x8b39,0x8fd1, // 8b d8..df + 0x91d1,0x541f,0x9280,0x4e5d, 0x5036,0x53e5,0x533a,0x72d7, // 8b e0..e7 + 0x7396,0x77e9,0x82e6,0x8eaf, 0x99c6,0x99c8,0x99d2,0x5177, // 8b e8..ef + 0x611a,0x865e,0x55b0,0x7a7a, 0x5076,0x5bd3,0x9047,0x9685, // 8b f0..f7 + 0x4e32,0x6adb,0x91e7,0x5c51, 0x5c48, // 8b f8..fc +}; + +static const u16 shift_jis_double_8c[] = +{ + 0x6398,0x7a9f,0x6c93,0x9774, 0x8f61,0x7aaa,0x718a,0x9688, // 8c 40..47 + 0x7c82,0x6817,0x7e70,0x6851, 0x936c,0x52f2,0x541b,0x85ab, // 8c 48..4f + 0x8a13,0x7fa4,0x8ecd,0x90e1, 0x5366,0x8888,0x7941,0x4fc2, // 8c 50..57 + 0x50be,0x5211,0x5144,0x5553, 0x572d,0x73ea,0x578b,0x5951, // 8c 58..5f + 0x5f62,0x5f84,0x6075,0x6176, 0x6167,0x61a9,0x63b2,0x643a, // 8c 60..67 + 0x656c,0x666f,0x6842,0x6e13, 0x7566,0x7a3d,0x7cfb,0x7d4c, // 8c 68..6f + 0x7d99,0x7e4b,0x7f6b,0x830e, 0x834a,0x86cd,0x8a08,0x8a63, // 8c 70..77 + 0x8b66,0x8efd,0x981a,0x9d8f, 0x82b8,0x8fce,0x9be8, INV, // 8c 78..7f + 0x5287,0x621f,0x6483,0x6fc0, 0x9699,0x6841,0x5091,0x6b20, // 8c 80..87 + 0x6c7a,0x6f54,0x7a74,0x7d50, 0x8840,0x8a23,0x6708,0x4ef6, // 8c 88..8f + 0x5039,0x5026,0x5065,0x517c, 0x5238,0x5263,0x55a7,0x570f, // 8c 90..97 + 0x5805,0x5acc,0x5efa,0x61b2, 0x61f8,0x62f3,0x6372,0x691c, // 8c 98..9f + 0x6a29,0x727d,0x72ac,0x732e, 0x7814,0x786f,0x7d79,0x770c, // 8c a0..a7 + 0x80a9,0x898b,0x8b19,0x8ce2, 0x8ed2,0x9063,0x9375,0x967a, // 8c a8..af + 0x9855,0x9a13,0x9e78,0x5143, 0x539f,0x53b3,0x5e7b,0x5f26, // 8c b0..b7 + 0x6e1b,0x6e90,0x7384,0x73fe, 0x7d43,0x8237,0x8a00,0x8afa, // 8c b8..bf + 0x9650,0x4e4e,0x500b,0x53e4, 0x547c,0x56fa,0x59d1,0x5b64, // 8c c0..c7 + 0x5df1,0x5eab,0x5f27,0x6238, 0x6545,0x67af,0x6e56,0x72d0, // 8c c8..cf + 0x7cca,0x88b4,0x80a1,0x80e1, 0x83f0,0x864e,0x8a87,0x8de8, // 8c d0..d7 + 0x9237,0x96c7,0x9867,0x9f13, 0x4e94,0x4e92,0x4f0d,0x5348, // 8c d8..df + 0x5449,0x543e,0x5a2f,0x5f8c, 0x5fa1,0x609f,0x68a7,0x6a8e, // 8c e0..e7 + 0x745a,0x7881,0x8a9e,0x8aa4, 0x8b77,0x9190,0x4e5e,0x9bc9, // 8c e8..ef + 0x4ea4,0x4f7c,0x4faf,0x5019, 0x5016,0x5149,0x516c,0x529f, // 8c f0..f7 + 0x52b9,0x52fe,0x539a,0x53e3, 0x5411, // 8c f8..fc +}; + +static const u16 shift_jis_double_8d[] = +{ + 0x540e,0x5589,0x5751,0x57a2, 0x597d,0x5b54,0x5b5d,0x5b8f, // 8d 40..47 + 0x5de5,0x5de7,0x5df7,0x5e78, 0x5e83,0x5e9a,0x5eb7,0x5f18, // 8d 48..4f + 0x6052,0x614c,0x6297,0x62d8, 0x63a7,0x653b,0x6602,0x6643, // 8d 50..57 + 0x66f4,0x676d,0x6821,0x6897, 0x69cb,0x6c5f,0x6d2a,0x6d69, // 8d 58..5f + 0x6e2f,0x6e9d,0x7532,0x7687, 0x786c,0x7a3f,0x7ce0,0x7d05, // 8d 60..67 + 0x7d18,0x7d5e,0x7db1,0x8015, 0x8003,0x80af,0x80b1,0x8154, // 8d 68..6f + 0x818f,0x822a,0x8352,0x884c, 0x8861,0x8b1b,0x8ca2,0x8cfc, // 8d 70..77 + 0x90ca,0x9175,0x9271,0x783f, 0x92fc,0x95a4,0x964d, INV, // 8d 78..7f + 0x9805,0x9999,0x9ad8,0x9d3b, 0x525b,0x52ab,0x53f7,0x5408, // 8d 80..87 + 0x58d5,0x62f7,0x6fe0,0x8c6a, 0x8f5f,0x9eb9,0x514b,0x523b, // 8d 88..8f + 0x544a,0x56fd,0x7a40,0x9177, 0x9d60,0x9ed2,0x7344,0x6f09, // 8d 90..97 + 0x8170,0x7511,0x5ffd,0x60da, 0x9aa8,0x72db,0x8fbc,0x6b64, // 8d 98..9f + 0x9803,0x4eca,0x56f0,0x5764, 0x58be,0x5a5a,0x6068,0x61c7, // 8d a0..a7 + 0x660f,0x6606,0x6839,0x68b1, 0x6df7,0x75d5,0x7d3a,0x826e, // 8d a8..af + 0x9b42,0x4e9b,0x4f50,0x53c9, 0x5506,0x5d6f,0x5de6,0x5dee, // 8d b0..b7 + 0x67fb,0x6c99,0x7473,0x7802, 0x8a50,0x9396,0x88df,0x5750, // 8d b8..bf + 0x5ea7,0x632b,0x50b5,0x50ac, 0x518d,0x6700,0x54c9,0x585e, // 8d c0..c7 + 0x59bb,0x5bb0,0x5f69,0x624d, 0x63a1,0x683d,0x6b73,0x6e08, // 8d c8..cf + 0x707d,0x91c7,0x7280,0x7815, 0x7826,0x796d,0x658e,0x7d30, // 8d d0..d7 + 0x83dc,0x88c1,0x8f09,0x969b, 0x5264,0x5728,0x6750,0x7f6a, // 8d d8..df + 0x8ca1,0x51b4,0x5742,0x962a, 0x583a,0x698a,0x80b4,0x54b2, // 8d e0..e7 + 0x5d0e,0x57fc,0x7895,0x9dfa, 0x4f5c,0x524a,0x548b,0x643e, // 8d e8..ef + 0x6628,0x6714,0x67f5,0x7a84, 0x7b56,0x7d22,0x932f,0x685c, // 8d f0..f7 + 0x9bad,0x7b39,0x5319,0x518a, 0x5237, // 8d f8..fc +}; + +static const u16 shift_jis_double_8e[] = +{ + 0x5bdf,0x62f6,0x64ae,0x64e6, 0x672d,0x6bba,0x85a9,0x96d1, // 8e 40..47 + 0x7690,0x9bd6,0x634c,0x9306, 0x9bab,0x76bf,0x6652,0x4e09, // 8e 48..4f + 0x5098,0x53c2,0x5c71,0x60e8, 0x6492,0x6563,0x685f,0x71e6, // 8e 50..57 + 0x73ca,0x7523,0x7b97,0x7e82, 0x8695,0x8b83,0x8cdb,0x9178, // 8e 58..5f + 0x9910,0x65ac,0x66ab,0x6b8b, 0x4ed5,0x4ed4,0x4f3a,0x4f7f, // 8e 60..67 + 0x523a,0x53f8,0x53f2,0x55e3, 0x56db,0x58eb,0x59cb,0x59c9, // 8e 68..6f + 0x59ff,0x5b50,0x5c4d,0x5e02, 0x5e2b,0x5fd7,0x601d,0x6307, // 8e 70..77 + 0x652f,0x5b5c,0x65af,0x65bd, 0x65e8,0x679d,0x6b62, INV, // 8e 78..7f + 0x6b7b,0x6c0f,0x7345,0x7949, 0x79c1,0x7cf8,0x7d19,0x7d2b, // 8e 80..87 + 0x80a2,0x8102,0x81f3,0x8996, 0x8a5e,0x8a69,0x8a66,0x8a8c, // 8e 88..8f + 0x8aee,0x8cc7,0x8cdc,0x96cc, 0x98fc,0x6b6f,0x4e8b,0x4f3c, // 8e 90..97 + 0x4f8d,0x5150,0x5b57,0x5bfa, 0x6148,0x6301,0x6642,0x6b21, // 8e 98..9f + 0x6ecb,0x6cbb,0x723e,0x74bd, 0x75d4,0x78c1,0x793a,0x800c, // 8e a0..a7 + 0x8033,0x81ea,0x8494,0x8f9e, 0x6c50,0x9e7f,0x5f0f,0x8b58, // 8e a8..af + 0x9d2b,0x7afa,0x8ef8,0x5b8d, 0x96eb,0x4e03,0x53f1,0x57f7, // 8e b0..b7 + 0x5931,0x5ac9,0x5ba4,0x6089, 0x6e7f,0x6f06,0x75be,0x8cea, // 8e b8..bf + 0x5b9f,0x8500,0x7be0,0x5072, 0x67f4,0x829d,0x5c61,0x854a, // 8e c0..c7 + 0x7e1e,0x820e,0x5199,0x5c04, 0x6368,0x8d66,0x659c,0x716e, // 8e c8..cf + 0x793e,0x7d17,0x8005,0x8b1d, 0x8eca,0x906e,0x86c7,0x90aa, // 8e d0..d7 + 0x501f,0x52fa,0x5c3a,0x6753, 0x707c,0x7235,0x914c,0x91c8, // 8e d8..df + 0x932b,0x82e5,0x5bc2,0x5f31, 0x60f9,0x4e3b,0x53d6,0x5b88, // 8e e0..e7 + 0x624b,0x6731,0x6b8a,0x72e9, 0x73e0,0x7a2e,0x816b,0x8da3, // 8e e8..ef + 0x9152,0x9996,0x5112,0x53d7, 0x546a,0x5bff,0x6388,0x6a39, // 8e f0..f7 + 0x7dac,0x9700,0x56da,0x53ce, 0x5468, // 8e f8..fc +}; + +static const u16 shift_jis_double_8f[] = +{ + 0x5b97,0x5c31,0x5dde,0x4fee, 0x6101,0x62fe,0x6d32,0x79c0, // 8f 40..47 + 0x79cb,0x7d42,0x7e4d,0x7fd2, 0x81ed,0x821f,0x8490,0x8846, // 8f 48..4f + 0x8972,0x8b90,0x8e74,0x8f2f, 0x9031,0x914b,0x916c,0x96c6, // 8f 50..57 + 0x919c,0x4ec0,0x4f4f,0x5145, 0x5341,0x5f93,0x620e,0x67d4, // 8f 58..5f + 0x6c41,0x6e0b,0x7363,0x7e26, 0x91cd,0x9283,0x53d4,0x5919, // 8f 60..67 + 0x5bbf,0x6dd1,0x795d,0x7e2e, 0x7c9b,0x587e,0x719f,0x51fa, // 8f 68..6f + 0x8853,0x8ff0,0x4fca,0x5cfb, 0x6625,0x77ac,0x7ae3,0x821c, // 8f 70..77 + 0x99ff,0x51c6,0x5faa,0x65ec, 0x696f,0x6b89,0x6df3, INV, // 8f 78..7f + 0x6e96,0x6f64,0x76fe,0x7d14, 0x5de1,0x9075,0x9187,0x9806, // 8f 80..87 + 0x51e6,0x521d,0x6240,0x6691, 0x66d9,0x6e1a,0x5eb6,0x7dd2, // 8f 88..8f + 0x7f72,0x66f8,0x85af,0x85f7, 0x8af8,0x52a9,0x53d9,0x5973, // 8f 90..97 + 0x5e8f,0x5f90,0x6055,0x92e4, 0x9664,0x50b7,0x511f,0x52dd, // 8f 98..9f + 0x5320,0x5347,0x53ec,0x54e8, 0x5546,0x5531,0x5617,0x5968, // 8f a0..a7 + 0x59be,0x5a3c,0x5bb5,0x5c06, 0x5c0f,0x5c11,0x5c1a,0x5e84, // 8f a8..af + 0x5e8a,0x5ee0,0x5f70,0x627f, 0x6284,0x62db,0x638c,0x6377, // 8f b0..b7 + 0x6607,0x660c,0x662d,0x6676, 0x677e,0x68a2,0x6a1f,0x6a35, // 8f b8..bf + 0x6cbc,0x6d88,0x6e09,0x6e58, 0x713c,0x7126,0x7167,0x75c7, // 8f c0..c7 + 0x7701,0x785d,0x7901,0x7965, 0x79f0,0x7ae0,0x7b11,0x7ca7, // 8f c8..cf + 0x7d39,0x8096,0x83d6,0x848b, 0x8549,0x885d,0x88f3,0x8a1f, // 8f d0..d7 + 0x8a3c,0x8a54,0x8a73,0x8c61, 0x8cde,0x91a4,0x9266,0x937e, // 8f d8..df + 0x9418,0x969c,0x9798,0x4e0a, 0x4e08,0x4e1e,0x4e57,0x5197, // 8f e0..e7 + 0x5270,0x57ce,0x5834,0x58cc, 0x5b22,0x5e38,0x60c5,0x64fe, // 8f e8..ef + 0x6761,0x6756,0x6d44,0x72b6, 0x7573,0x7a63,0x84b8,0x8b72, // 8f f0..f7 + 0x91b8,0x9320,0x5631,0x57f4, 0x98fe, // 8f f8..fc +}; + +static const u16 shift_jis_double_90[] = +{ + 0x62ed,0x690d,0x6b96,0x71ed, 0x7e54,0x8077,0x8272,0x89e6, // 90 40..47 + 0x98df,0x8755,0x8fb1,0x5c3b, 0x4f38,0x4fe1,0x4fb5,0x5507, // 90 48..4f + 0x5a20,0x5bdd,0x5be9,0x5fc3, 0x614e,0x632f,0x65b0,0x664b, // 90 50..57 + 0x68ee,0x699b,0x6d78,0x6df1, 0x7533,0x75b9,0x771f,0x795e, // 90 58..5f + 0x79e6,0x7d33,0x81e3,0x82af, 0x85aa,0x89aa,0x8a3a,0x8eab, // 90 60..67 + 0x8f9b,0x9032,0x91dd,0x9707, 0x4eba,0x4ec1,0x5203,0x5875, // 90 68..6f + 0x58ec,0x5c0b,0x751a,0x5c3d, 0x814e,0x8a0a,0x8fc5,0x9663, // 90 70..77 + 0x976d,0x7b25,0x8acf,0x9808, 0x9162,0x56f3,0x53a8, INV, // 90 78..7f + 0x9017,0x5439,0x5782,0x5e25, 0x63a8,0x6c34,0x708a,0x7761, // 90 80..87 + 0x7c8b,0x7fe0,0x8870,0x9042, 0x9154,0x9310,0x9318,0x968f, // 90 88..8f + 0x745e,0x9ac4,0x5d07,0x5d69, 0x6570,0x67a2,0x8da8,0x96db, // 90 90..97 + 0x636e,0x6749,0x6919,0x83c5, 0x9817,0x96c0,0x88fe,0x6f84, // 90 98..9f + 0x647a,0x5bf8,0x4e16,0x702c, 0x755d,0x662f,0x51c4,0x5236, // 90 a0..a7 + 0x52e2,0x59d3,0x5f81,0x6027, 0x6210,0x653f,0x6574,0x661f, // 90 a8..af + 0x6674,0x68f2,0x6816,0x6b63, 0x6e05,0x7272,0x751f,0x76db, // 90 b0..b7 + 0x7cbe,0x8056,0x58f0,0x88fd, 0x897f,0x8aa0,0x8a93,0x8acb, // 90 b8..bf + 0x901d,0x9192,0x9752,0x9759, 0x6589,0x7a0e,0x8106,0x96bb, // 90 c0..c7 + 0x5e2d,0x60dc,0x621a,0x65a5, 0x6614,0x6790,0x77f3,0x7a4d, // 90 c8..cf + 0x7c4d,0x7e3e,0x810a,0x8cac, 0x8d64,0x8de1,0x8e5f,0x78a9, // 90 d0..d7 + 0x5207,0x62d9,0x63a5,0x6442, 0x6298,0x8a2d,0x7a83,0x7bc0, // 90 d8..df + 0x8aac,0x96ea,0x7d76,0x820c, 0x8749,0x4ed9,0x5148,0x5343, // 90 e0..e7 + 0x5360,0x5ba3,0x5c02,0x5c16, 0x5ddd,0x6226,0x6247,0x64b0, // 90 e8..ef + 0x6813,0x6834,0x6cc9,0x6d45, 0x6d17,0x67d3,0x6f5c,0x714e, // 90 f0..f7 + 0x717d,0x65cb,0x7a7f,0x7bad, 0x7dda, // 90 f8..fc +}; + +static const u16 shift_jis_double_91[] = +{ + 0x7e4a,0x7fa8,0x817a,0x821b, 0x8239,0x85a6,0x8a6e,0x8cce, // 91 40..47 + 0x8df5,0x9078,0x9077,0x92ad, 0x9291,0x9583,0x9bae,0x524d, // 91 48..4f + 0x5584,0x6f38,0x7136,0x5168, 0x7985,0x7e55,0x81b3,0x7cce, // 91 50..57 + 0x564c,0x5851,0x5ca8,0x63aa, 0x66fe,0x66fd,0x695a,0x72d9, // 91 58..5f + 0x758f,0x758e,0x790e,0x7956, 0x79df,0x7c97,0x7d20,0x7d44, // 91 60..67 + 0x8607,0x8a34,0x963b,0x9061, 0x9f20,0x50e7,0x5275,0x53cc, // 91 68..6f + 0x53e2,0x5009,0x55aa,0x58ee, 0x594f,0x723d,0x5b8b,0x5c64, // 91 70..77 + 0x531d,0x60e3,0x60f3,0x635c, 0x6383,0x633f,0x63bb, INV, // 91 78..7f + 0x64cd,0x65e9,0x66f9,0x5de3, 0x69cd,0x69fd,0x6f15,0x71e5, // 91 80..87 + 0x4e89,0x75e9,0x76f8,0x7a93, 0x7cdf,0x7dcf,0x7d9c,0x8061, // 91 88..8f + 0x8349,0x8358,0x846c,0x84bc, 0x85fb,0x88c5,0x8d70,0x9001, // 91 90..97 + 0x906d,0x9397,0x971c,0x9a12, 0x50cf,0x5897,0x618e,0x81d3, // 91 98..9f + 0x8535,0x8d08,0x9020,0x4fc3, 0x5074,0x5247,0x5373,0x606f, // 91 a0..a7 + 0x6349,0x675f,0x6e2c,0x8db3, 0x901f,0x4fd7,0x5c5e,0x8cca, // 91 a8..af + 0x65cf,0x7d9a,0x5352,0x8896, 0x5176,0x63c3,0x5b58,0x5b6b, // 91 b0..b7 + 0x5c0a,0x640d,0x6751,0x905c, 0x4ed6,0x591a,0x592a,0x6c70, // 91 b8..bf + 0x8a51,0x553e,0x5815,0x59a5, 0x60f0,0x6253,0x67c1,0x8235, // 91 c0..c7 + 0x6955,0x9640,0x99c4,0x9a28, 0x4f53,0x5806,0x5bfe,0x8010, // 91 c8..cf + 0x5cb1,0x5e2f,0x5f85,0x6020, 0x614b,0x6234,0x66ff,0x6cf0, // 91 d0..d7 + 0x6ede,0x80ce,0x817f,0x82d4, 0x888b,0x8cb8,0x9000,0x902e, // 91 d8..df + 0x968a,0x9edb,0x9bdb,0x4ee3, 0x53f0,0x5927,0x7b2c,0x918d, // 91 e0..e7 + 0x984c,0x9df9,0x6edd,0x7027, 0x5353,0x5544,0x5b85,0x6258, // 91 e8..ef + 0x629e,0x62d3,0x6ca2,0x6fef, 0x7422,0x8a17,0x9438,0x6fc1, // 91 f0..f7 + 0x8afe,0x8338,0x51e7,0x86f8, 0x53ea, // 91 f8..fc +}; + +static const u16 shift_jis_double_92[] = +{ + 0x53e9,0x4f46,0x9054,0x8fb0, 0x596a,0x8131,0x5dfd,0x7aea, // 92 40..47 + 0x8fbf,0x68da,0x8c37,0x72f8, 0x9c48,0x6a3d,0x8ab0,0x4e39, // 92 48..4f + 0x5358,0x5606,0x5766,0x62c5, 0x63a2,0x65e6,0x6b4e,0x6de1, // 92 50..57 + 0x6e5b,0x70ad,0x77ed,0x7aef, 0x7baa,0x7dbb,0x803d,0x80c6, // 92 58..5f + 0x86cb,0x8a95,0x935b,0x56e3, 0x58c7,0x5f3e,0x65ad,0x6696, // 92 60..67 + 0x6a80,0x6bb5,0x7537,0x8ac7, 0x5024,0x77e5,0x5730,0x5f1b, // 92 68..6f + 0x6065,0x667a,0x6c60,0x75f4, 0x7a1a,0x7f6e,0x81f4,0x8718, // 92 70..77 + 0x9045,0x99b3,0x7bc9,0x755c, 0x7af9,0x7b51,0x84c4, INV, // 92 78..7f + 0x9010,0x79e9,0x7a92,0x8336, 0x5ae1,0x7740,0x4e2d,0x4ef2, // 92 80..87 + 0x5b99,0x5fe0,0x62bd,0x663c, 0x67f1,0x6ce8,0x866b,0x8877, // 92 88..8f + 0x8a3b,0x914e,0x92f3,0x99d0, 0x6a17,0x7026,0x732a,0x82e7, // 92 90..97 + 0x8457,0x8caf,0x4e01,0x5146, 0x51cb,0x558b,0x5bf5,0x5e16, // 92 98..9f + 0x5e33,0x5e81,0x5f14,0x5f35, 0x5f6b,0x5fb4,0x61f2,0x6311, // 92 a0..a7 + 0x66a2,0x671d,0x6f6e,0x7252, 0x753a,0x773a,0x8074,0x8139, // 92 a8..af + 0x8178,0x8776,0x8abf,0x8adc, 0x8d85,0x8df3,0x929a,0x9577, // 92 b0..b7 + 0x9802,0x9ce5,0x52c5,0x6357, 0x76f4,0x6715,0x6c88,0x73cd, // 92 b8..bf + 0x8cc3,0x93ae,0x9673,0x6d25, 0x589c,0x690e,0x69cc,0x8ffd, // 92 c0..c7 + 0x939a,0x75db,0x901a,0x585a, 0x6802,0x63b4,0x69fb,0x4f43, // 92 c8..cf + 0x6f2c,0x67d8,0x8fbb,0x8526, 0x7db4,0x9354,0x693f,0x6f70, // 92 d0..d7 + 0x576a,0x58f7,0x5b2c,0x7d2c, 0x722a,0x540a,0x91e3,0x9db4, // 92 d8..df + 0x4ead,0x4f4e,0x505c,0x5075, 0x5243,0x8c9e,0x5448,0x5824, // 92 e0..e7 + 0x5b9a,0x5e1d,0x5e95,0x5ead, 0x5ef7,0x5f1f,0x608c,0x62b5, // 92 e8..ef + 0x633a,0x63d0,0x68af,0x6c40, 0x7887,0x798e,0x7a0b,0x7de0, // 92 f0..f7 + 0x8247,0x8a02,0x8ae6,0x8e44, 0x9013, // 92 f8..fc +}; + +static const u16 shift_jis_double_93[] = +{ + 0x90b8,0x912d,0x91d8,0x9f0e, 0x6ce5,0x6458,0x64e2,0x6575, // 93 40..47 + 0x6ef4,0x7684,0x7b1b,0x9069, 0x93d1,0x6eba,0x54f2,0x5fb9, // 93 48..4f + 0x64a4,0x8f4d,0x8fed,0x9244, 0x5178,0x586b,0x5929,0x5c55, // 93 50..57 + 0x5e97,0x6dfb,0x7e8f,0x751c, 0x8cbc,0x8ee2,0x985b,0x70b9, // 93 58..5f + 0x4f1d,0x6bbf,0x6fb1,0x7530, 0x96fb,0x514e,0x5410,0x5835, // 93 60..67 + 0x5857,0x59ac,0x5c60,0x5f92, 0x6597,0x675c,0x6e21,0x767b, // 93 68..6f + 0x83df,0x8ced,0x9014,0x90fd, 0x934d,0x7825,0x783a,0x52aa, // 93 70..77 + 0x5ea6,0x571f,0x5974,0x6012, 0x5012,0x515a,0x51ac, INV, // 93 78..7f + 0x51cd,0x5200,0x5510,0x5854, 0x5858,0x5957,0x5b95,0x5cf6, // 93 80..87 + 0x5d8b,0x60bc,0x6295,0x642d, 0x6771,0x6843,0x68bc,0x68df, // 93 88..8f + 0x76d7,0x6dd8,0x6e6f,0x6d9b, 0x706f,0x71c8,0x5f53,0x75d8, // 93 90..97 + 0x7977,0x7b49,0x7b54,0x7b52, 0x7cd6,0x7d71,0x5230,0x8463, // 93 98..9f + 0x8569,0x85e4,0x8a0e,0x8b04, 0x8c46,0x8e0f,0x9003,0x900f, // 93 a0..a7 + 0x9419,0x9676,0x982d,0x9a30, 0x95d8,0x50cd,0x52d5,0x540c, // 93 a8..af + 0x5802,0x5c0e,0x61a7,0x649e, 0x6d1e,0x77b3,0x7ae5,0x80f4, // 93 b0..b7 + 0x8404,0x9053,0x9285,0x5ce0, 0x9d07,0x533f,0x5f97,0x5fb3, // 93 b8..bf + 0x6d9c,0x7279,0x7763,0x79bf, 0x7be4,0x6bd2,0x72ec,0x8aad, // 93 c0..c7 + 0x6803,0x6a61,0x51f8,0x7a81, 0x6934,0x5c4a,0x9cf6,0x82eb, // 93 c8..cf + 0x5bc5,0x9149,0x701e,0x5678, 0x5c6f,0x60c7,0x6566,0x6c8c, // 93 d0..d7 + 0x8c5a,0x9041,0x9813,0x5451, 0x66c7,0x920d,0x5948,0x90a3, // 93 d8..df + 0x5185,0x4e4d,0x51ea,0x8599, 0x8b0e,0x7058,0x637a,0x934b, // 93 e0..e7 + 0x6962,0x99b4,0x7e04,0x7577, 0x5357,0x6960,0x8edf,0x96e3, // 93 e8..ef + 0x6c5d,0x4e8c,0x5c3c,0x5f10, 0x8fe9,0x5302,0x8cd1,0x8089, // 93 f0..f7 + 0x8679,0x5eff,0x65e5,0x4e73, 0x5165, // 93 f8..fc +}; + +static const u16 shift_jis_double_94[] = +{ + 0x5982,0x5c3f,0x97ee,0x4efb, 0x598a,0x5fcd,0x8a8d,0x6fe1, // 94 40..47 + 0x79b0,0x7962,0x5be7,0x8471, 0x732b,0x71b1,0x5e74,0x5ff5, // 94 48..4f + 0x637b,0x649a,0x71c3,0x7c98, 0x4e43,0x5efc,0x4e4b,0x57dc, // 94 50..57 + 0x56a2,0x60a9,0x6fc3,0x7d0d, 0x80fd,0x8133,0x81bf,0x8fb2, // 94 58..5f + 0x8997,0x86a4,0x5df4,0x628a, 0x64ad,0x8987,0x6777,0x6ce2, // 94 60..67 + 0x6d3e,0x7436,0x7834,0x5a46, 0x7f75,0x82ad,0x99ac,0x4ff3, // 94 68..6f + 0x5ec3,0x62dd,0x6392,0x6557, 0x676f,0x76c3,0x724c,0x80cc, // 94 70..77 + 0x80ba,0x8f29,0x914d,0x500d, 0x57f9,0x5a92,0x6885, INV, // 94 78..7f + 0x6973,0x7164,0x72fd,0x8cb7, 0x58f2,0x8ce0,0x966a,0x9019, // 94 80..87 + 0x877f,0x79e4,0x77e7,0x8429, 0x4f2f,0x5265,0x535a,0x62cd, // 94 88..8f + 0x67cf,0x6cca,0x767d,0x7b94, 0x7c95,0x8236,0x8584,0x8feb, // 94 90..97 + 0x66dd,0x6f20,0x7206,0x7e1b, 0x83ab,0x99c1,0x9ea6,0x51fd, // 94 98..9f + 0x7bb1,0x7872,0x7bb8,0x8087, 0x7b48,0x6ae8,0x5e61,0x808c, // 94 a0..a7 + 0x7551,0x7560,0x516b,0x9262, 0x6e8c,0x767a,0x9197,0x9aea, // 94 a8..af + 0x4f10,0x7f70,0x629c,0x7b4f, 0x95a5,0x9ce9,0x567a,0x5859, // 94 b0..b7 + 0x86e4,0x96bc,0x4f34,0x5224, 0x534a,0x53cd,0x53db,0x5e06, // 94 b8..bf + 0x642c,0x6591,0x677f,0x6c3e, 0x6c4e,0x7248,0x72af,0x73ed, // 94 c0..c7 + 0x7554,0x7e41,0x822c,0x85e9, 0x8ca9,0x7bc4,0x91c6,0x7169, // 94 c8..cf + 0x9812,0x98ef,0x633d,0x6669, 0x756a,0x76e4,0x78d0,0x8543, // 94 d0..d7 + 0x86ee,0x532a,0x5351,0x5426, 0x5983,0x5e87,0x5f7c,0x60b2, // 94 d8..df + 0x6249,0x6279,0x62ab,0x6590, 0x6bd4,0x6ccc,0x75b2,0x76ae, // 94 e0..e7 + 0x7891,0x79d8,0x7dcb,0x7f77, 0x80a5,0x88ab,0x8ab9,0x8cbb, // 94 e8..ef + 0x907f,0x975e,0x98db,0x6a0b, 0x7c38,0x5099,0x5c3e,0x5fae, // 94 f0..f7 + 0x6787,0x6bd8,0x7435,0x7709, 0x7f8e, // 94 f8..fc +}; + +static const u16 shift_jis_double_95[] = +{ + 0x9f3b,0x67ca,0x7a17,0x5339, 0x758b,0x9aed,0x5f66,0x819d, // 95 40..47 + 0x83f1,0x8098,0x5f3c,0x5fc5, 0x7562,0x7b46,0x903c,0x6867, // 95 48..4f + 0x59eb,0x5a9b,0x7d10,0x767e, 0x8b2c,0x4ff5,0x5f6a,0x6a19, // 95 50..57 + 0x6c37,0x6f02,0x74e2,0x7968, 0x8868,0x8a55,0x8c79,0x5edf, // 95 58..5f + 0x63cf,0x75c5,0x79d2,0x82d7, 0x9328,0x92f2,0x849c,0x86ed, // 95 60..67 + 0x9c2d,0x54c1,0x5f6c,0x658c, 0x6d5c,0x7015,0x8ca7,0x8cd3, // 95 68..6f + 0x983b,0x654f,0x74f6,0x4e0d, 0x4ed8,0x57e0,0x592b,0x5a66, // 95 70..77 + 0x5bcc,0x51a8,0x5e03,0x5e9c, 0x6016,0x6276,0x6577, INV, // 95 78..7f + 0x65a7,0x666e,0x6d6e,0x7236, 0x7b26,0x8150,0x819a,0x8299, // 95 80..87 + 0x8b5c,0x8ca0,0x8ce6,0x8d74, 0x961c,0x9644,0x4fae,0x64ab, // 95 88..8f + 0x6b66,0x821e,0x8461,0x856a, 0x90e8,0x5c01,0x6953,0x98a8, // 95 90..97 + 0x847a,0x8557,0x4f0f,0x526f, 0x5fa9,0x5e45,0x670d,0x798f, // 95 98..9f + 0x8179,0x8907,0x8986,0x6df5, 0x5f17,0x6255,0x6cb8,0x4ecf, // 95 a0..a7 + 0x7269,0x9b92,0x5206,0x543b, 0x5674,0x58b3,0x61a4,0x626e, // 95 a8..af + 0x711a,0x596e,0x7c89,0x7cde, 0x7d1b,0x96f0,0x6587,0x805e, // 95 b0..b7 + 0x4e19,0x4f75,0x5175,0x5840, 0x5e63,0x5e73,0x5f0a,0x67c4, // 95 b8..bf + 0x4e26,0x853d,0x9589,0x965b, 0x7c73,0x9801,0x50fb,0x58c1, // 95 c0..c7 + 0x7656,0x78a7,0x5225,0x77a5, 0x8511,0x7b86,0x504f,0x5909, // 95 c8..cf + 0x7247,0x7bc7,0x7de8,0x8fba, 0x8fd4,0x904d,0x4fbf,0x52c9, // 95 d0..d7 + 0x5a29,0x5f01,0x97ad,0x4fdd, 0x8217,0x92ea,0x5703,0x6355, // 95 d8..df + 0x6b69,0x752b,0x88dc,0x8f14, 0x7a42,0x52df,0x5893,0x6155, // 95 e0..e7 + 0x620a,0x66ae,0x6bcd,0x7c3f, 0x83e9,0x5023,0x4ff8,0x5305, // 95 e8..ef + 0x5446,0x5831,0x5949,0x5b9d, 0x5cf0,0x5cef,0x5d29,0x5e96, // 95 f0..f7 + 0x62b1,0x6367,0x653e,0x65b9, 0x670b, // 95 f8..fc +}; + +static const u16 shift_jis_double_96[] = +{ + 0x6cd5,0x6ce1,0x70f9,0x7832, 0x7e2b,0x80de,0x82b3,0x840c, // 96 40..47 + 0x84ec,0x8702,0x8912,0x8a2a, 0x8c4a,0x90a6,0x92d2,0x98fd, // 96 48..4f + 0x9cf3,0x9d6c,0x4e4f,0x4ea1, 0x508d,0x5256,0x574a,0x59a8, // 96 50..57 + 0x5e3d,0x5fd8,0x5fd9,0x623f, 0x66b4,0x671b,0x67d0,0x68d2, // 96 58..5f + 0x5192,0x7d21,0x80aa,0x81a8, 0x8b00,0x8c8c,0x8cbf,0x927e, // 96 60..67 + 0x9632,0x5420,0x982c,0x5317, 0x50d5,0x535c,0x58a8,0x64b2, // 96 68..6f + 0x6734,0x7267,0x7766,0x7a46, 0x91e6,0x52c3,0x6ca1,0x6b86, // 96 70..77 + 0x5800,0x5e4c,0x5954,0x672c, 0x7ffb,0x51e1,0x76c6, INV, // 96 78..7f + 0x6469,0x78e8,0x9b54,0x9ebb, 0x57cb,0x59b9,0x6627,0x679a, // 96 80..87 + 0x6bce,0x54e9,0x69d9,0x5e55, 0x819c,0x6795,0x9baa,0x67fe, // 96 88..8f + 0x9c52,0x685d,0x4ea6,0x4fe3, 0x53c8,0x62b9,0x672b,0x6cab, // 96 90..97 + 0x8fc4,0x4fad,0x7e6d,0x9ebf, 0x4e07,0x6162,0x6e80,0x6f2b, // 96 98..9f + 0x8513,0x5473,0x672a,0x9b45, 0x5df3,0x7b95,0x5cac,0x5bc6, // 96 a0..a7 + 0x871c,0x6e4a,0x84d1,0x7a14, 0x8108,0x5999,0x7c8d,0x6c11, // 96 a8..af + 0x7720,0x52d9,0x5922,0x7121, 0x725f,0x77db,0x9727,0x9d61, // 96 b0..b7 + 0x690b,0x5a7f,0x5a18,0x51a5, 0x540d,0x547d,0x660e,0x76df, // 96 b8..bf + 0x8ff7,0x9298,0x9cf4,0x59ea, 0x725d,0x6ec5,0x514d,0x68c9, // 96 c0..c7 + 0x7dbf,0x7dec,0x9762,0x9eba, 0x6478,0x6a21,0x8302,0x5984, // 96 c8..cf + 0x5b5f,0x6bdb,0x731b,0x76f2, 0x7db2,0x8017,0x8499,0x5132, // 96 d0..d7 + 0x6728,0x9ed9,0x76ee,0x6762, 0x52ff,0x9905,0x5c24,0x623b, // 96 d8..df + 0x7c7e,0x8cb0,0x554f,0x60b6, 0x7d0b,0x9580,0x5301,0x4e5f, // 96 e0..e7 + 0x51b6,0x591c,0x723a,0x8036, 0x91ce,0x5f25,0x77e2,0x5384, // 96 e8..ef + 0x5f79,0x7d04,0x85ac,0x8a33, 0x8e8d,0x9756,0x67f3,0x85ae, // 96 f0..f7 + 0x9453,0x6109,0x6108,0x6cb9, 0x7652, // 96 f8..fc +}; + +static const u16 shift_jis_double_97[] = +{ + 0x8aed,0x8f38,0x552f,0x4f51, 0x512a,0x52c7,0x53cb,0x5ba5, // 97 40..47 + 0x5e7d,0x60a0,0x6182,0x63d6, 0x6709,0x67da,0x6e67,0x6d8c, // 97 48..4f + 0x7336,0x7337,0x7531,0x7950, 0x88d5,0x8a98,0x904a,0x9091, // 97 50..57 + 0x90f5,0x96c4,0x878d,0x5915, 0x4e88,0x4f59,0x4e0e,0x8a89, // 97 58..5f + 0x8f3f,0x9810,0x50ad,0x5e7c, 0x5996,0x5bb9,0x5eb8,0x63da, // 97 60..67 + 0x63fa,0x64c1,0x66dc,0x694a, 0x69d8,0x6d0b,0x6eb6,0x7194, // 97 68..6f + 0x7528,0x7aaf,0x7f8a,0x8000, 0x8449,0x84c9,0x8981,0x8b21, // 97 70..77 + 0x8e0a,0x9065,0x967d,0x990a, 0x617e,0x6291,0x6b32, INV, // 97 78..7f + 0x6c83,0x6d74,0x7fcc,0x7ffc, 0x6dc0,0x7f85,0x87ba,0x88f8, // 97 80..87 + 0x6765,0x83b1,0x983c,0x96f7, 0x6d1b,0x7d61,0x843d,0x916a, // 97 88..8f + 0x4e71,0x5375,0x5d50,0x6b04, 0x6feb,0x85cd,0x862d,0x89a7, // 97 90..97 + 0x5229,0x540f,0x5c65,0x674e, 0x68a8,0x7406,0x7483,0x75e2, // 97 98..9f + 0x88cf,0x88e1,0x91cc,0x96e2, 0x9678,0x5f8b,0x7387,0x7acb, // 97 a0..a7 + 0x844e,0x63a0,0x7565,0x5289, 0x6d41,0x6e9c,0x7409,0x7559, // 97 a8..af + 0x786b,0x7c92,0x9686,0x7adc, 0x9f8d,0x4fb6,0x616e,0x65c5, // 97 b0..b7 + 0x865c,0x4e86,0x4eae,0x50da, 0x4e21,0x51cc,0x5bee,0x6599, // 97 b8..bf + 0x6881,0x6dbc,0x731f,0x7642, 0x77ad,0x7a1c,0x7ce7,0x826f, // 97 c0..c7 + 0x8ad2,0x907c,0x91cf,0x9675, 0x9818,0x529b,0x7dd1,0x502b, // 97 c8..cf + 0x5398,0x6797,0x6dcb,0x71d0, 0x7433,0x81e8,0x8f2a,0x96a3, // 97 d0..d7 + 0x9c57,0x9e9f,0x7460,0x5841, 0x6d99,0x7d2f,0x985e,0x4ee4, // 97 d8..df + 0x4f36,0x4f8b,0x51b7,0x52b1, 0x5dba,0x601c,0x73b2,0x793c, // 97 e0..e7 + 0x82d3,0x9234,0x96b7,0x96f6, 0x970a,0x9e97,0x9f62,0x66a6, // 97 e8..ef + 0x6b74,0x5217,0x52a3,0x70c8, 0x88c2,0x5ec9,0x604b,0x6190, // 97 f0..f7 + 0x6f23,0x7149,0x7c3e,0x7df4, 0x806f, // 97 f8..fc +}; + +static const u16 shift_jis_double_98[] = +{ + 0x84ee,0x9023,0x932c,0x5442, 0x9b6f,0x6ad3,0x7089,0x8cc2, // 98 40..47 + 0x8def,0x9732,0x52b4,0x5a41, 0x5eca,0x5f04,0x6717,0x697c, // 98 48..4f + 0x6994,0x6d6a,0x6f0f,0x7262, 0x72fc,0x7bed,0x8001,0x807e, // 98 50..57 + 0x874b,0x90ce,0x516d,0x9e93, 0x7984,0x808b,0x9332,0x8ad6, // 98 58..5f + 0x502d,0x548c,0x8a71,0x6b6a, 0x8cc4,0x8107,0x60d1,0x67a0, // 98 60..67 + 0x9df2,0x4e99,0x4e98,0x9c10, 0x8a6b,0x85c1,0x8568,0x6900, // 98 68..6f + 0x6e7e,0x7897,0x8155, INV, INV, INV, INV, INV, // 98 70..77 + INV, INV, INV, INV, INV, INV, INV, INV, // 98 78..7f + INV, INV, INV, INV, INV, INV, INV, INV, // 98 80..87 + INV, INV, INV, INV, INV, INV, INV, INV, // 98 88..8f + INV, INV, INV, INV, INV, INV, INV, INV, // 98 90..97 + INV, INV, INV, INV, INV, INV, INV,0x5f0c, // 98 98..9f + 0x4e10,0x4e15,0x4e2a,0x4e31, 0x4e36,0x4e3c,0x4e3f,0x4e42, // 98 a0..a7 + 0x4e56,0x4e58,0x4e82,0x4e85, 0x8c6b,0x4e8a,0x8212,0x5f0d, // 98 a8..af + 0x4e8e,0x4e9e,0x4e9f,0x4ea0, 0x4ea2,0x4eb0,0x4eb3,0x4eb6, // 98 b0..b7 + 0x4ece,0x4ecd,0x4ec4,0x4ec6, 0x4ec2,0x4ed7,0x4ede,0x4eed, // 98 b8..bf + 0x4edf,0x4ef7,0x4f09,0x4f5a, 0x4f30,0x4f5b,0x4f5d,0x4f57, // 98 c0..c7 + 0x4f47,0x4f76,0x4f88,0x4f8f, 0x4f98,0x4f7b,0x4f69,0x4f70, // 98 c8..cf + 0x4f91,0x4f6f,0x4f86,0x4f96, 0x5118,0x4fd4,0x4fdf,0x4fce, // 98 d0..d7 + 0x4fd8,0x4fdb,0x4fd1,0x4fda, 0x4fd0,0x4fe4,0x4fe5,0x501a, // 98 d8..df + 0x5028,0x5014,0x502a,0x5025, 0x5005,0x4f1c,0x4ff6,0x5021, // 98 e0..e7 + 0x5029,0x502c,0x4ffe,0x4fef, 0x5011,0x5006,0x5043,0x5047, // 98 e8..ef + 0x6703,0x5055,0x5050,0x5048, 0x505a,0x5056,0x506c,0x5078, // 98 f0..f7 + 0x5080,0x509a,0x5085,0x50b4, 0x50b2, // 98 f8..fc +}; + +static const u16 shift_jis_double_99[] = +{ + 0x50c9,0x50ca,0x50b3,0x50c2, 0x50d6,0x50de,0x50e5,0x50ed, // 99 40..47 + 0x50e3,0x50ee,0x50f9,0x50f5, 0x5109,0x5101,0x5102,0x5116, // 99 48..4f + 0x5115,0x5114,0x511a,0x5121, 0x513a,0x5137,0x513c,0x513b, // 99 50..57 + 0x513f,0x5140,0x5152,0x514c, 0x5154,0x5162,0x7af8,0x5169, // 99 58..5f + 0x516a,0x516e,0x5180,0x5182, 0x56d8,0x518c,0x5189,0x518f, // 99 60..67 + 0x5191,0x5193,0x5195,0x5196, 0x51a4,0x51a6,0x51a2,0x51a9, // 99 68..6f + 0x51aa,0x51ab,0x51b3,0x51b1, 0x51b2,0x51b0,0x51b5,0x51bd, // 99 70..77 + 0x51c5,0x51c9,0x51db,0x51e0, 0x8655,0x51e9,0x51ed, INV, // 99 78..7f + 0x51f0,0x51f5,0x51fe,0x5204, 0x520b,0x5214,0x520e,0x5227, // 99 80..87 + 0x522a,0x522e,0x5233,0x5239, 0x524f,0x5244,0x524b,0x524c, // 99 88..8f + 0x525e,0x5254,0x526a,0x5274, 0x5269,0x5273,0x527f,0x527d, // 99 90..97 + 0x528d,0x5294,0x5292,0x5271, 0x5288,0x5291,0x8fa8,0x8fa7, // 99 98..9f + 0x52ac,0x52ad,0x52bc,0x52b5, 0x52c1,0x52cd,0x52d7,0x52de, // 99 a0..a7 + 0x52e3,0x52e6,0x98ed,0x52e0, 0x52f3,0x52f5,0x52f8,0x52f9, // 99 a8..af + 0x5306,0x5308,0x7538,0x530d, 0x5310,0x530f,0x5315,0x531a, // 99 b0..b7 + 0x5323,0x532f,0x5331,0x5333, 0x5338,0x5340,0x5346,0x5345, // 99 b8..bf + 0x4e17,0x5349,0x534d,0x51d6, 0x535e,0x5369,0x536e,0x5918, // 99 c0..c7 + 0x537b,0x5377,0x5382,0x5396, 0x53a0,0x53a6,0x53a5,0x53ae, // 99 c8..cf + 0x53b0,0x53b6,0x53c3,0x7c12, 0x96d9,0x53df,0x66fc,0x71ee, // 99 d0..d7 + 0x53ee,0x53e8,0x53ed,0x53fa, 0x5401,0x543d,0x5440,0x542c, // 99 d8..df + 0x542d,0x543c,0x542e,0x5436, 0x5429,0x541d,0x544e,0x548f, // 99 e0..e7 + 0x5475,0x548e,0x545f,0x5471, 0x5477,0x5470,0x5492,0x547b, // 99 e8..ef + 0x5480,0x5476,0x5484,0x5490, 0x5486,0x54c7,0x54a2,0x54b8, // 99 f0..f7 + 0x54a5,0x54ac,0x54c4,0x54c8, 0x54a8, // 99 f8..fc +}; + +static const u16 shift_jis_double_9a[] = +{ + 0x54ab,0x54c2,0x54a4,0x54be, 0x54bc,0x54d8,0x54e5,0x54e6, // 9a 40..47 + 0x550f,0x5514,0x54fd,0x54ee, 0x54ed,0x54fa,0x54e2,0x5539, // 9a 48..4f + 0x5540,0x5563,0x554c,0x552e, 0x555c,0x5545,0x5556,0x5557, // 9a 50..57 + 0x5538,0x5533,0x555d,0x5599, 0x5580,0x54af,0x558a,0x559f, // 9a 58..5f + 0x557b,0x557e,0x5598,0x559e, 0x55ae,0x557c,0x5583,0x55a9, // 9a 60..67 + 0x5587,0x55a8,0x55da,0x55c5, 0x55df,0x55c4,0x55dc,0x55e4, // 9a 68..6f + 0x55d4,0x5614,0x55f7,0x5616, 0x55fe,0x55fd,0x561b,0x55f9, // 9a 70..77 + 0x564e,0x5650,0x71df,0x5634, 0x5636,0x5632,0x5638, INV, // 9a 78..7f + 0x566b,0x5664,0x562f,0x566c, 0x566a,0x5686,0x5680,0x568a, // 9a 80..87 + 0x56a0,0x5694,0x568f,0x56a5, 0x56ae,0x56b6,0x56b4,0x56c2, // 9a 88..8f + 0x56bc,0x56c1,0x56c3,0x56c0, 0x56c8,0x56ce,0x56d1,0x56d3, // 9a 90..97 + 0x56d7,0x56ee,0x56f9,0x5700, 0x56ff,0x5704,0x5709,0x5708, // 9a 98..9f + 0x570b,0x570d,0x5713,0x5718, 0x5716,0x55c7,0x571c,0x5726, // 9a a0..a7 + 0x5737,0x5738,0x574e,0x573b, 0x5740,0x574f,0x5769,0x57c0, // 9a a8..af + 0x5788,0x5761,0x577f,0x5789, 0x5793,0x57a0,0x57b3,0x57a4, // 9a b0..b7 + 0x57aa,0x57b0,0x57c3,0x57c6, 0x57d4,0x57d2,0x57d3,0x580a, // 9a b8..bf + 0x57d6,0x57e3,0x580b,0x5819, 0x581d,0x5872,0x5821,0x5862, // 9a c0..c7 + 0x584b,0x5870,0x6bc0,0x5852, 0x583d,0x5879,0x5885,0x58b9, // 9a c8..cf + 0x589f,0x58ab,0x58ba,0x58de, 0x58bb,0x58b8,0x58ae,0x58c5, // 9a d0..d7 + 0x58d3,0x58d1,0x58d7,0x58d9, 0x58d8,0x58e5,0x58dc,0x58e4, // 9a d8..df + 0x58df,0x58ef,0x58fa,0x58f9, 0x58fb,0x58fc,0x58fd,0x5902, // 9a e0..e7 + 0x590a,0x5910,0x591b,0x68a6, 0x5925,0x592c,0x592d,0x5932, // 9a e8..ef + 0x5938,0x593e,0x7ad2,0x5955, 0x5950,0x594e,0x595a,0x5958, // 9a f0..f7 + 0x5962,0x5960,0x5967,0x596c, 0x5969, // 9a f8..fc +}; + +static const u16 shift_jis_double_9b[] = +{ + 0x5978,0x5981,0x599d,0x4f5e, 0x4fab,0x59a3,0x59b2,0x59c6, // 9b 40..47 + 0x59e8,0x59dc,0x598d,0x59d9, 0x59da,0x5a25,0x5a1f,0x5a11, // 9b 48..4f + 0x5a1c,0x5a09,0x5a1a,0x5a40, 0x5a6c,0x5a49,0x5a35,0x5a36, // 9b 50..57 + 0x5a62,0x5a6a,0x5a9a,0x5abc, 0x5abe,0x5acb,0x5ac2,0x5abd, // 9b 58..5f + 0x5ae3,0x5ad7,0x5ae6,0x5ae9, 0x5ad6,0x5afa,0x5afb,0x5b0c, // 9b 60..67 + 0x5b0b,0x5b16,0x5b32,0x5ad0, 0x5b2a,0x5b36,0x5b3e,0x5b43, // 9b 68..6f + 0x5b45,0x5b40,0x5b51,0x5b55, 0x5b5a,0x5b5b,0x5b65,0x5b69, // 9b 70..77 + 0x5b70,0x5b73,0x5b75,0x5b78, 0x6588,0x5b7a,0x5b80, INV, // 9b 78..7f + 0x5b83,0x5ba6,0x5bb8,0x5bc3, 0x5bc7,0x5bc9,0x5bd4,0x5bd0, // 9b 80..87 + 0x5be4,0x5be6,0x5be2,0x5bde, 0x5be5,0x5beb,0x5bf0,0x5bf6, // 9b 88..8f + 0x5bf3,0x5c05,0x5c07,0x5c08, 0x5c0d,0x5c13,0x5c20,0x5c22, // 9b 90..97 + 0x5c28,0x5c38,0x5c39,0x5c41, 0x5c46,0x5c4e,0x5c53,0x5c50, // 9b 98..9f + 0x5c4f,0x5b71,0x5c6c,0x5c6e, 0x4e62,0x5c76,0x5c79,0x5c8c, // 9b a0..a7 + 0x5c91,0x5c94,0x599b,0x5cab, 0x5cbb,0x5cb6,0x5cbc,0x5cb7, // 9b a8..af + 0x5cc5,0x5cbe,0x5cc7,0x5cd9, 0x5ce9,0x5cfd,0x5cfa,0x5ced, // 9b b0..b7 + 0x5d8c,0x5cea,0x5d0b,0x5d15, 0x5d17,0x5d5c,0x5d1f,0x5d1b, // 9b b8..bf + 0x5d11,0x5d14,0x5d22,0x5d1a, 0x5d19,0x5d18,0x5d4c,0x5d52, // 9b c0..c7 + 0x5d4e,0x5d4b,0x5d6c,0x5d73, 0x5d76,0x5d87,0x5d84,0x5d82, // 9b c8..cf + 0x5da2,0x5d9d,0x5dac,0x5dae, 0x5dbd,0x5d90,0x5db7,0x5dbc, // 9b d0..d7 + 0x5dc9,0x5dcd,0x5dd3,0x5dd2, 0x5dd6,0x5ddb,0x5deb,0x5df2, // 9b d8..df + 0x5df5,0x5e0b,0x5e1a,0x5e19, 0x5e11,0x5e1b,0x5e36,0x5e37, // 9b e0..e7 + 0x5e44,0x5e43,0x5e40,0x5e4e, 0x5e57,0x5e54,0x5e5f,0x5e62, // 9b e8..ef + 0x5e64,0x5e47,0x5e75,0x5e76, 0x5e7a,0x9ebc,0x5e7f,0x5ea0, // 9b f0..f7 + 0x5ec1,0x5ec2,0x5ec8,0x5ed0, 0x5ecf, // 9b f8..fc +}; + +static const u16 shift_jis_double_9c[] = +{ + 0x5ed6,0x5ee3,0x5edd,0x5eda, 0x5edb,0x5ee2,0x5ee1,0x5ee8, // 9c 40..47 + 0x5ee9,0x5eec,0x5ef1,0x5ef3, 0x5ef0,0x5ef4,0x5ef8,0x5efe, // 9c 48..4f + 0x5f03,0x5f09,0x5f5d,0x5f5c, 0x5f0b,0x5f11,0x5f16,0x5f29, // 9c 50..57 + 0x5f2d,0x5f38,0x5f41,0x5f48, 0x5f4c,0x5f4e,0x5f2f,0x5f51, // 9c 58..5f + 0x5f56,0x5f57,0x5f59,0x5f61, 0x5f6d,0x5f73,0x5f77,0x5f83, // 9c 60..67 + 0x5f82,0x5f7f,0x5f8a,0x5f88, 0x5f91,0x5f87,0x5f9e,0x5f99, // 9c 68..6f + 0x5f98,0x5fa0,0x5fa8,0x5fad, 0x5fbc,0x5fd6,0x5ffb,0x5fe4, // 9c 70..77 + 0x5ff8,0x5ff1,0x5fdd,0x60b3, 0x5fff,0x6021,0x6060, INV, // 9c 78..7f + 0x6019,0x6010,0x6029,0x600e, 0x6031,0x601b,0x6015,0x602b, // 9c 80..87 + 0x6026,0x600f,0x603a,0x605a, 0x6041,0x606a,0x6077,0x605f, // 9c 88..8f + 0x604a,0x6046,0x604d,0x6063, 0x6043,0x6064,0x6042,0x606c, // 9c 90..97 + 0x606b,0x6059,0x6081,0x608d, 0x60e7,0x6083,0x609a,0x6084, // 9c 98..9f + 0x609b,0x6096,0x6097,0x6092, 0x60a7,0x608b,0x60e1,0x60b8, // 9c a0..a7 + 0x60e0,0x60d3,0x60b4,0x5ff0, 0x60bd,0x60c6,0x60b5,0x60d8, // 9c a8..af + 0x614d,0x6115,0x6106,0x60f6, 0x60f7,0x6100,0x60f4,0x60fa, // 9c b0..b7 + 0x6103,0x6121,0x60fb,0x60f1, 0x610d,0x610e,0x6147,0x613e, // 9c b8..bf + 0x6128,0x6127,0x614a,0x613f, 0x613c,0x612c,0x6134,0x613d, // 9c c0..c7 + 0x6142,0x6144,0x6173,0x6177, 0x6158,0x6159,0x615a,0x616b, // 9c c8..cf + 0x6174,0x616f,0x6165,0x6171, 0x615f,0x615d,0x6153,0x6175, // 9c d0..d7 + 0x6199,0x6196,0x6187,0x61ac, 0x6194,0x619a,0x618a,0x6191, // 9c d8..df + 0x61ab,0x61ae,0x61cc,0x61ca, 0x61c9,0x61f7,0x61c8,0x61c3, // 9c e0..e7 + 0x61c6,0x61ba,0x61cb,0x7f79, 0x61cd,0x61e6,0x61e3,0x61f6, // 9c e8..ef + 0x61fa,0x61f4,0x61ff,0x61fd, 0x61fc,0x61fe,0x6200,0x6208, // 9c f0..f7 + 0x6209,0x620d,0x620c,0x6214, 0x621b, // 9c f8..fc +}; + +static const u16 shift_jis_double_9d[] = +{ + 0x621e,0x6221,0x622a,0x622e, 0x6230,0x6232,0x6233,0x6241, // 9d 40..47 + 0x624e,0x625e,0x6263,0x625b, 0x6260,0x6268,0x627c,0x6282, // 9d 48..4f + 0x6289,0x627e,0x6292,0x6293, 0x6296,0x62d4,0x6283,0x6294, // 9d 50..57 + 0x62d7,0x62d1,0x62bb,0x62cf, 0x62ff,0x62c6,0x64d4,0x62c8, // 9d 58..5f + 0x62dc,0x62cc,0x62ca,0x62c2, 0x62c7,0x629b,0x62c9,0x630c, // 9d 60..67 + 0x62ee,0x62f1,0x6327,0x6302, 0x6308,0x62ef,0x62f5,0x6350, // 9d 68..6f + 0x633e,0x634d,0x641c,0x634f, 0x6396,0x638e,0x6380,0x63ab, // 9d 70..77 + 0x6376,0x63a3,0x638f,0x6389, 0x639f,0x63b5,0x636b, INV, // 9d 78..7f + 0x6369,0x63be,0x63e9,0x63c0, 0x63c6,0x63e3,0x63c9,0x63d2, // 9d 80..87 + 0x63f6,0x63c4,0x6416,0x6434, 0x6406,0x6413,0x6426,0x6436, // 9d 88..8f + 0x651d,0x6417,0x6428,0x640f, 0x6467,0x646f,0x6476,0x644e, // 9d 90..97 + 0x652a,0x6495,0x6493,0x64a5, 0x64a9,0x6488,0x64bc,0x64da, // 9d 98..9f + 0x64d2,0x64c5,0x64c7,0x64bb, 0x64d8,0x64c2,0x64f1,0x64e7, // 9d a0..a7 + 0x8209,0x64e0,0x64e1,0x62ac, 0x64e3,0x64ef,0x652c,0x64f6, // 9d a8..af + 0x64f4,0x64f2,0x64fa,0x6500, 0x64fd,0x6518,0x651c,0x6505, // 9d b0..b7 + 0x6524,0x6523,0x652b,0x6534, 0x6535,0x6537,0x6536,0x6538, // 9d b8..bf + 0x754b,0x6548,0x6556,0x6555, 0x654d,0x6558,0x655e,0x655d, // 9d c0..c7 + 0x6572,0x6578,0x6582,0x6583, 0x8b8a,0x659b,0x659f,0x65ab, // 9d c8..cf + 0x65b7,0x65c3,0x65c6,0x65c1, 0x65c4,0x65cc,0x65d2,0x65db, // 9d d0..d7 + 0x65d9,0x65e0,0x65e1,0x65f1, 0x6772,0x660a,0x6603,0x65fb, // 9d d8..df + 0x6773,0x6635,0x6636,0x6634, 0x661c,0x664f,0x6644,0x6649, // 9d e0..e7 + 0x6641,0x665e,0x665d,0x6664, 0x6667,0x6668,0x665f,0x6662, // 9d e8..ef + 0x6670,0x6683,0x6688,0x668e, 0x6689,0x6684,0x6698,0x669d, // 9d f0..f7 + 0x66c1,0x66b9,0x66c9,0x66be, 0x66bc, // 9d f8..fc +}; + +static const u16 shift_jis_double_9e[] = +{ + 0x66c4,0x66b8,0x66d6,0x66da, 0x66e0,0x663f,0x66e6,0x66e9, // 9e 40..47 + 0x66f0,0x66f5,0x66f7,0x670f, 0x6716,0x671e,0x6726,0x6727, // 9e 48..4f + 0x9738,0x672e,0x673f,0x6736, 0x6741,0x6738,0x6737,0x6746, // 9e 50..57 + 0x675e,0x6760,0x6759,0x6763, 0x6764,0x6789,0x6770,0x67a9, // 9e 58..5f + 0x677c,0x676a,0x678c,0x678b, 0x67a6,0x67a1,0x6785,0x67b7, // 9e 60..67 + 0x67ef,0x67b4,0x67ec,0x67b3, 0x67e9,0x67b8,0x67e4,0x67de, // 9e 68..6f + 0x67dd,0x67e2,0x67ee,0x67b9, 0x67ce,0x67c6,0x67e7,0x6a9c, // 9e 70..77 + 0x681e,0x6846,0x6829,0x6840, 0x684d,0x6832,0x684e, INV, // 9e 78..7f + 0x68b3,0x682b,0x6859,0x6863, 0x6877,0x687f,0x689f,0x688f, // 9e 80..87 + 0x68ad,0x6894,0x689d,0x689b, 0x6883,0x6aae,0x68b9,0x6874, // 9e 88..8f + 0x68b5,0x68a0,0x68ba,0x690f, 0x688d,0x687e,0x6901,0x68ca, // 9e 90..97 + 0x6908,0x68d8,0x6922,0x6926, 0x68e1,0x690c,0x68cd,0x68d4, // 9e 98..9f + 0x68e7,0x68d5,0x6936,0x6912, 0x6904,0x68d7,0x68e3,0x6925, // 9e a0..a7 + 0x68f9,0x68e0,0x68ef,0x6928, 0x692a,0x691a,0x6923,0x6921, // 9e a8..af + 0x68c6,0x6979,0x6977,0x695c, 0x6978,0x696b,0x6954,0x697e, // 9e b0..b7 + 0x696e,0x6939,0x6974,0x693d, 0x6959,0x6930,0x6961,0x695e, // 9e b8..bf + 0x695d,0x6981,0x696a,0x69b2, 0x69ae,0x69d0,0x69bf,0x69c1, // 9e c0..c7 + 0x69d3,0x69be,0x69ce,0x5be8, 0x69ca,0x69dd,0x69bb,0x69c3, // 9e c8..cf + 0x69a7,0x6a2e,0x6991,0x69a0, 0x699c,0x6995,0x69b4,0x69de, // 9e d0..d7 + 0x69e8,0x6a02,0x6a1b,0x69ff, 0x6b0a,0x69f9,0x69f2,0x69e7, // 9e d8..df + 0x6a05,0x69b1,0x6a1e,0x69ed, 0x6a14,0x69eb,0x6a0a,0x6a12, // 9e e0..e7 + 0x6ac1,0x6a23,0x6a13,0x6a44, 0x6a0c,0x6a72,0x6a36,0x6a78, // 9e e8..ef + 0x6a47,0x6a62,0x6a59,0x6a66, 0x6a48,0x6a38,0x6a22,0x6a90, // 9e f0..f7 + 0x6a8d,0x6aa0,0x6a84,0x6aa2, 0x6aa3, // 9e f8..fc +}; + +static const u16 shift_jis_double_9f[] = +{ + 0x6a97,0x8617,0x6abb,0x6ac3, 0x6ac2,0x6ab8,0x6ab3,0x6aac, // 9f 40..47 + 0x6ade,0x6ad1,0x6adf,0x6aaa, 0x6ada,0x6aea,0x6afb,0x6b05, // 9f 48..4f + 0x8616,0x6afa,0x6b12,0x6b16, 0x9b31,0x6b1f,0x6b38,0x6b37, // 9f 50..57 + 0x76dc,0x6b39,0x98ee,0x6b47, 0x6b43,0x6b49,0x6b50,0x6b59, // 9f 58..5f + 0x6b54,0x6b5b,0x6b5f,0x6b61, 0x6b78,0x6b79,0x6b7f,0x6b80, // 9f 60..67 + 0x6b84,0x6b83,0x6b8d,0x6b98, 0x6b95,0x6b9e,0x6ba4,0x6baa, // 9f 68..6f + 0x6bab,0x6baf,0x6bb2,0x6bb1, 0x6bb3,0x6bb7,0x6bbc,0x6bc6, // 9f 70..77 + 0x6bcb,0x6bd3,0x6bdf,0x6bec, 0x6beb,0x6bf3,0x6bef, INV, // 9f 78..7f + 0x9ebe,0x6c08,0x6c13,0x6c14, 0x6c1b,0x6c24,0x6c23,0x6c5e, // 9f 80..87 + 0x6c55,0x6c62,0x6c6a,0x6c82, 0x6c8d,0x6c9a,0x6c81,0x6c9b, // 9f 88..8f + 0x6c7e,0x6c68,0x6c73,0x6c92, 0x6c90,0x6cc4,0x6cf1,0x6cd3, // 9f 90..97 + 0x6cbd,0x6cd7,0x6cc5,0x6cdd, 0x6cae,0x6cb1,0x6cbe,0x6cba, // 9f 98..9f + 0x6cdb,0x6cef,0x6cd9,0x6cea, 0x6d1f,0x884d,0x6d36,0x6d2b, // 9f a0..a7 + 0x6d3d,0x6d38,0x6d19,0x6d35, 0x6d33,0x6d12,0x6d0c,0x6d63, // 9f a8..af + 0x6d93,0x6d64,0x6d5a,0x6d79, 0x6d59,0x6d8e,0x6d95,0x6fe4, // 9f b0..b7 + 0x6d85,0x6df9,0x6e15,0x6e0a, 0x6db5,0x6dc7,0x6de6,0x6db8, // 9f b8..bf + 0x6dc6,0x6dec,0x6dde,0x6dcc, 0x6de8,0x6dd2,0x6dc5,0x6dfa, // 9f c0..c7 + 0x6dd9,0x6de4,0x6dd5,0x6dea, 0x6dee,0x6e2d,0x6e6e,0x6e2e, // 9f c8..cf + 0x6e19,0x6e72,0x6e5f,0x6e3e, 0x6e23,0x6e6b,0x6e2b,0x6e76, // 9f d0..d7 + 0x6e4d,0x6e1f,0x6e43,0x6e3a, 0x6e4e,0x6e24,0x6eff,0x6e1d, // 9f d8..df + 0x6e38,0x6e82,0x6eaa,0x6e98, 0x6ec9,0x6eb7,0x6ed3,0x6ebd, // 9f e0..e7 + 0x6eaf,0x6ec4,0x6eb2,0x6ed4, 0x6ed5,0x6e8f,0x6ea5,0x6ec2, // 9f e8..ef + 0x6e9f,0x6f41,0x6f11,0x704c, 0x6eec,0x6ef8,0x6efe,0x6f3f, // 9f f0..f7 + 0x6ef2,0x6f31,0x6eef,0x6f32, 0x6ecc, // 9f f8..fc +}; + +static const u16 shift_jis_double_e0[] = +{ + 0x6f3e,0x6f13,0x6ef7,0x6f86, 0x6f7a,0x6f78,0x6f81,0x6f80, // e0 40..47 + 0x6f6f,0x6f5b,0x6ff3,0x6f6d, 0x6f82,0x6f7c,0x6f58,0x6f8e, // e0 48..4f + 0x6f91,0x6fc2,0x6f66,0x6fb3, 0x6fa3,0x6fa1,0x6fa4,0x6fb9, // e0 50..57 + 0x6fc6,0x6faa,0x6fdf,0x6fd5, 0x6fec,0x6fd4,0x6fd8,0x6ff1, // e0 58..5f + 0x6fee,0x6fdb,0x7009,0x700b, 0x6ffa,0x7011,0x7001,0x700f, // e0 60..67 + 0x6ffe,0x701b,0x701a,0x6f74, 0x701d,0x7018,0x701f,0x7030, // e0 68..6f + 0x703e,0x7032,0x7051,0x7063, 0x7099,0x7092,0x70af,0x70f1, // e0 70..77 + 0x70ac,0x70b8,0x70b3,0x70ae, 0x70df,0x70cb,0x70dd, INV, // e0 78..7f + 0x70d9,0x7109,0x70fd,0x711c, 0x7119,0x7165,0x7155,0x7188, // e0 80..87 + 0x7166,0x7162,0x714c,0x7156, 0x716c,0x718f,0x71fb,0x7184, // e0 88..8f + 0x7195,0x71a8,0x71ac,0x71d7, 0x71b9,0x71be,0x71d2,0x71c9, // e0 90..97 + 0x71d4,0x71ce,0x71e0,0x71ec, 0x71e7,0x71f5,0x71fc,0x71f9, // e0 98..9f + 0x71ff,0x720d,0x7210,0x721b, 0x7228,0x722d,0x722c,0x7230, // e0 a0..a7 + 0x7232,0x723b,0x723c,0x723f, 0x7240,0x7246,0x724b,0x7258, // e0 a8..af + 0x7274,0x727e,0x7282,0x7281, 0x7287,0x7292,0x7296,0x72a2, // e0 b0..b7 + 0x72a7,0x72b9,0x72b2,0x72c3, 0x72c6,0x72c4,0x72ce,0x72d2, // e0 b8..bf + 0x72e2,0x72e0,0x72e1,0x72f9, 0x72f7,0x500f,0x7317,0x730a, // e0 c0..c7 + 0x731c,0x7316,0x731d,0x7334, 0x732f,0x7329,0x7325,0x733e, // e0 c8..cf + 0x734e,0x734f,0x9ed8,0x7357, 0x736a,0x7368,0x7370,0x7378, // e0 d0..d7 + 0x7375,0x737b,0x737a,0x73c8, 0x73b3,0x73ce,0x73bb,0x73c0, // e0 d8..df + 0x73e5,0x73ee,0x73de,0x74a2, 0x7405,0x746f,0x7425,0x73f8, // e0 e0..e7 + 0x7432,0x743a,0x7455,0x743f, 0x745f,0x7459,0x7441,0x745c, // e0 e8..ef + 0x7469,0x7470,0x7463,0x746a, 0x7476,0x747e,0x748b,0x749e, // e0 f0..f7 + 0x74a7,0x74ca,0x74cf,0x74d4, 0x73f1, // e0 f8..fc +}; + +static const u16 shift_jis_double_e1[] = +{ + 0x74e0,0x74e3,0x74e7,0x74e9, 0x74ee,0x74f2,0x74f0,0x74f1, // e1 40..47 + 0x74f8,0x74f7,0x7504,0x7503, 0x7505,0x750c,0x750e,0x750d, // e1 48..4f + 0x7515,0x7513,0x751e,0x7526, 0x752c,0x753c,0x7544,0x754d, // e1 50..57 + 0x754a,0x7549,0x755b,0x7546, 0x755a,0x7569,0x7564,0x7567, // e1 58..5f + 0x756b,0x756d,0x7578,0x7576, 0x7586,0x7587,0x7574,0x758a, // e1 60..67 + 0x7589,0x7582,0x7594,0x759a, 0x759d,0x75a5,0x75a3,0x75c2, // e1 68..6f + 0x75b3,0x75c3,0x75b5,0x75bd, 0x75b8,0x75bc,0x75b1,0x75cd, // e1 70..77 + 0x75ca,0x75d2,0x75d9,0x75e3, 0x75de,0x75fe,0x75ff, INV, // e1 78..7f + 0x75fc,0x7601,0x75f0,0x75fa, 0x75f2,0x75f3,0x760b,0x760d, // e1 80..87 + 0x7609,0x761f,0x7627,0x7620, 0x7621,0x7622,0x7624,0x7634, // e1 88..8f + 0x7630,0x763b,0x7647,0x7648, 0x7646,0x765c,0x7658,0x7661, // e1 90..97 + 0x7662,0x7668,0x7669,0x766a, 0x7667,0x766c,0x7670,0x7672, // e1 98..9f + 0x7676,0x7678,0x767c,0x7680, 0x7683,0x7688,0x768b,0x768e, // e1 a0..a7 + 0x7696,0x7693,0x7699,0x769a, 0x76b0,0x76b4,0x76b8,0x76b9, // e1 a8..af + 0x76ba,0x76c2,0x76cd,0x76d6, 0x76d2,0x76de,0x76e1,0x76e5, // e1 b0..b7 + 0x76e7,0x76ea,0x862f,0x76fb, 0x7708,0x7707,0x7704,0x7729, // e1 b8..bf + 0x7724,0x771e,0x7725,0x7726, 0x771b,0x7737,0x7738,0x7747, // e1 c0..c7 + 0x775a,0x7768,0x776b,0x775b, 0x7765,0x777f,0x777e,0x7779, // e1 c8..cf + 0x778e,0x778b,0x7791,0x77a0, 0x779e,0x77b0,0x77b6,0x77b9, // e1 d0..d7 + 0x77bf,0x77bc,0x77bd,0x77bb, 0x77c7,0x77cd,0x77d7,0x77da, // e1 d8..df + 0x77dc,0x77e3,0x77ee,0x77fc, 0x780c,0x7812,0x7926,0x7820, // e1 e0..e7 + 0x792a,0x7845,0x788e,0x7874, 0x7886,0x787c,0x789a,0x788c, // e1 e8..ef + 0x78a3,0x78b5,0x78aa,0x78af, 0x78d1,0x78c6,0x78cb,0x78d4, // e1 f0..f7 + 0x78be,0x78bc,0x78c5,0x78ca, 0x78ec, // e1 f8..fc +}; + +static const u16 shift_jis_double_e2[] = +{ + 0x78e7,0x78da,0x78fd,0x78f4, 0x7907,0x7912,0x7911,0x7919, // e2 40..47 + 0x792c,0x792b,0x7940,0x7960, 0x7957,0x795f,0x795a,0x7955, // e2 48..4f + 0x7953,0x797a,0x797f,0x798a, 0x799d,0x79a7,0x9f4b,0x79aa, // e2 50..57 + 0x79ae,0x79b3,0x79b9,0x79ba, 0x79c9,0x79d5,0x79e7,0x79ec, // e2 58..5f + 0x79e1,0x79e3,0x7a08,0x7a0d, 0x7a18,0x7a19,0x7a20,0x7a1f, // e2 60..67 + 0x7980,0x7a31,0x7a3b,0x7a3e, 0x7a37,0x7a43,0x7a57,0x7a49, // e2 68..6f + 0x7a61,0x7a62,0x7a69,0x9f9d, 0x7a70,0x7a79,0x7a7d,0x7a88, // e2 70..77 + 0x7a97,0x7a95,0x7a98,0x7a96, 0x7aa9,0x7ac8,0x7ab0, INV, // e2 78..7f + 0x7ab6,0x7ac5,0x7ac4,0x7abf, 0x9083,0x7ac7,0x7aca,0x7acd, // e2 80..87 + 0x7acf,0x7ad5,0x7ad3,0x7ad9, 0x7ada,0x7add,0x7ae1,0x7ae2, // e2 88..8f + 0x7ae6,0x7aed,0x7af0,0x7b02, 0x7b0f,0x7b0a,0x7b06,0x7b33, // e2 90..97 + 0x7b18,0x7b19,0x7b1e,0x7b35, 0x7b28,0x7b36,0x7b50,0x7b7a, // e2 98..9f + 0x7b04,0x7b4d,0x7b0b,0x7b4c, 0x7b45,0x7b75,0x7b65,0x7b74, // e2 a0..a7 + 0x7b67,0x7b70,0x7b71,0x7b6c, 0x7b6e,0x7b9d,0x7b98,0x7b9f, // e2 a8..af + 0x7b8d,0x7b9c,0x7b9a,0x7b8b, 0x7b92,0x7b8f,0x7b5d,0x7b99, // e2 b0..b7 + 0x7bcb,0x7bc1,0x7bcc,0x7bcf, 0x7bb4,0x7bc6,0x7bdd,0x7be9, // e2 b8..bf + 0x7c11,0x7c14,0x7be6,0x7be5, 0x7c60,0x7c00,0x7c07,0x7c13, // e2 c0..c7 + 0x7bf3,0x7bf7,0x7c17,0x7c0d, 0x7bf6,0x7c23,0x7c27,0x7c2a, // e2 c8..cf + 0x7c1f,0x7c37,0x7c2b,0x7c3d, 0x7c4c,0x7c43,0x7c54,0x7c4f, // e2 d0..d7 + 0x7c40,0x7c50,0x7c58,0x7c5f, 0x7c64,0x7c56,0x7c65,0x7c6c, // e2 d8..df + 0x7c75,0x7c83,0x7c90,0x7ca4, 0x7cad,0x7ca2,0x7cab,0x7ca1, // e2 e0..e7 + 0x7ca8,0x7cb3,0x7cb2,0x7cb1, 0x7cae,0x7cb9,0x7cbd,0x7cc0, // e2 e8..ef + 0x7cc5,0x7cc2,0x7cd8,0x7cd2, 0x7cdc,0x7ce2,0x9b3b,0x7cef, // e2 f0..f7 + 0x7cf2,0x7cf4,0x7cf6,0x7cfa, 0x7d06, // e2 f8..fc +}; + +static const u16 shift_jis_double_e3[] = +{ + 0x7d02,0x7d1c,0x7d15,0x7d0a, 0x7d45,0x7d4b,0x7d2e,0x7d32, // e3 40..47 + 0x7d3f,0x7d35,0x7d46,0x7d73, 0x7d56,0x7d4e,0x7d72,0x7d68, // e3 48..4f + 0x7d6e,0x7d4f,0x7d63,0x7d93, 0x7d89,0x7d5b,0x7d8f,0x7d7d, // e3 50..57 + 0x7d9b,0x7dba,0x7dae,0x7da3, 0x7db5,0x7dc7,0x7dbd,0x7dab, // e3 58..5f + 0x7e3d,0x7da2,0x7daf,0x7ddc, 0x7db8,0x7d9f,0x7db0,0x7dd8, // e3 60..67 + 0x7ddd,0x7de4,0x7dde,0x7dfb, 0x7df2,0x7de1,0x7e05,0x7e0a, // e3 68..6f + 0x7e23,0x7e21,0x7e12,0x7e31, 0x7e1f,0x7e09,0x7e0b,0x7e22, // e3 70..77 + 0x7e46,0x7e66,0x7e3b,0x7e35, 0x7e39,0x7e43,0x7e37, INV, // e3 78..7f + 0x7e32,0x7e3a,0x7e67,0x7e5d, 0x7e56,0x7e5e,0x7e59,0x7e5a, // e3 80..87 + 0x7e79,0x7e6a,0x7e69,0x7e7c, 0x7e7b,0x7e83,0x7dd5,0x7e7d, // e3 88..8f + 0x8fae,0x7e7f,0x7e88,0x7e89, 0x7e8c,0x7e92,0x7e90,0x7e93, // e3 90..97 + 0x7e94,0x7e96,0x7e8e,0x7e9b, 0x7e9c,0x7f38,0x7f3a,0x7f45, // e3 98..9f + 0x7f4c,0x7f4d,0x7f4e,0x7f50, 0x7f51,0x7f55,0x7f54,0x7f58, // e3 a0..a7 + 0x7f5f,0x7f60,0x7f68,0x7f69, 0x7f67,0x7f78,0x7f82,0x7f86, // e3 a8..af + 0x7f83,0x7f88,0x7f87,0x7f8c, 0x7f94,0x7f9e,0x7f9d,0x7f9a, // e3 b0..b7 + 0x7fa3,0x7faf,0x7fb2,0x7fb9, 0x7fae,0x7fb6,0x7fb8,0x8b71, // e3 b8..bf + 0x7fc5,0x7fc6,0x7fca,0x7fd5, 0x7fd4,0x7fe1,0x7fe6,0x7fe9, // e3 c0..c7 + 0x7ff3,0x7ff9,0x98dc,0x8006, 0x8004,0x800b,0x8012,0x8018, // e3 c8..cf + 0x8019,0x801c,0x8021,0x8028, 0x803f,0x803b,0x804a,0x8046, // e3 d0..d7 + 0x8052,0x8058,0x805a,0x805f, 0x8062,0x8068,0x8073,0x8072, // e3 d8..df + 0x8070,0x8076,0x8079,0x807d, 0x807f,0x8084,0x8086,0x8085, // e3 e0..e7 + 0x809b,0x8093,0x809a,0x80ad, 0x5190,0x80ac,0x80db,0x80e5, // e3 e8..ef + 0x80d9,0x80dd,0x80c4,0x80da, 0x80d6,0x8109,0x80ef,0x80f1, // e3 f0..f7 + 0x811b,0x8129,0x8123,0x812f, 0x814b, // e3 f8..fc +}; + +static const u16 shift_jis_double_e4[] = +{ + 0x968b,0x8146,0x813e,0x8153, 0x8151,0x80fc,0x8171,0x816e, // e4 40..47 + 0x8165,0x8166,0x8174,0x8183, 0x8188,0x818a,0x8180,0x8182, // e4 48..4f + 0x81a0,0x8195,0x81a4,0x81a3, 0x815f,0x8193,0x81a9,0x81b0, // e4 50..57 + 0x81b5,0x81be,0x81b8,0x81bd, 0x81c0,0x81c2,0x81ba,0x81c9, // e4 58..5f + 0x81cd,0x81d1,0x81d9,0x81d8, 0x81c8,0x81da,0x81df,0x81e0, // e4 60..67 + 0x81e7,0x81fa,0x81fb,0x81fe, 0x8201,0x8202,0x8205,0x8207, // e4 68..6f + 0x820a,0x820d,0x8210,0x8216, 0x8229,0x822b,0x8238,0x8233, // e4 70..77 + 0x8240,0x8259,0x8258,0x825d, 0x825a,0x825f,0x8264, INV, // e4 78..7f + 0x8262,0x8268,0x826a,0x826b, 0x822e,0x8271,0x8277,0x8278, // e4 80..87 + 0x827e,0x828d,0x8292,0x82ab, 0x829f,0x82bb,0x82ac,0x82e1, // e4 88..8f + 0x82e3,0x82df,0x82d2,0x82f4, 0x82f3,0x82fa,0x8393,0x8303, // e4 90..97 + 0x82fb,0x82f9,0x82de,0x8306, 0x82dc,0x8309,0x82d9,0x8335, // e4 98..9f + 0x8334,0x8316,0x8332,0x8331, 0x8340,0x8339,0x8350,0x8345, // e4 a0..a7 + 0x832f,0x832b,0x8317,0x8318, 0x8385,0x839a,0x83aa,0x839f, // e4 a8..af + 0x83a2,0x8396,0x8323,0x838e, 0x8387,0x838a,0x837c,0x83b5, // e4 b0..b7 + 0x8373,0x8375,0x83a0,0x8389, 0x83a8,0x83f4,0x8413,0x83eb, // e4 b8..bf + 0x83ce,0x83fd,0x8403,0x83d8, 0x840b,0x83c1,0x83f7,0x8407, // e4 c0..c7 + 0x83e0,0x83f2,0x840d,0x8422, 0x8420,0x83bd,0x8438,0x8506, // e4 c8..cf + 0x83fb,0x846d,0x842a,0x843c, 0x855a,0x8484,0x8477,0x846b, // e4 d0..d7 + 0x84ad,0x846e,0x8482,0x8469, 0x8446,0x842c,0x846f,0x8479, // e4 d8..df + 0x8435,0x84ca,0x8462,0x84b9, 0x84bf,0x849f,0x84d9,0x84cd, // e4 e0..e7 + 0x84bb,0x84da,0x84d0,0x84c1, 0x84c6,0x84d6,0x84a1,0x8521, // e4 e8..ef + 0x84ff,0x84f4,0x8517,0x8518, 0x852c,0x851f,0x8515,0x8514, // e4 f0..f7 + 0x84fc,0x8540,0x8563,0x8558, 0x8548, // e4 f8..fc +}; + +static const u16 shift_jis_double_e5[] = +{ + 0x8541,0x8602,0x854b,0x8555, 0x8580,0x85a4,0x8588,0x8591, // e5 40..47 + 0x858a,0x85a8,0x856d,0x8594, 0x859b,0x85ea,0x8587,0x859c, // e5 48..4f + 0x8577,0x857e,0x8590,0x85c9, 0x85ba,0x85cf,0x85b9,0x85d0, // e5 50..57 + 0x85d5,0x85dd,0x85e5,0x85dc, 0x85f9,0x860a,0x8613,0x860b, // e5 58..5f + 0x85fe,0x85fa,0x8606,0x8622, 0x861a,0x8630,0x863f,0x864d, // e5 60..67 + 0x4e55,0x8654,0x865f,0x8667, 0x8671,0x8693,0x86a3,0x86a9, // e5 68..6f + 0x86aa,0x868b,0x868c,0x86b6, 0x86af,0x86c4,0x86c6,0x86b0, // e5 70..77 + 0x86c9,0x8823,0x86ab,0x86d4, 0x86de,0x86e9,0x86ec, INV, // e5 78..7f + 0x86df,0x86db,0x86ef,0x8712, 0x8706,0x8708,0x8700,0x8703, // e5 80..87 + 0x86fb,0x8711,0x8709,0x870d, 0x86f9,0x870a,0x8734,0x873f, // e5 88..8f + 0x8737,0x873b,0x8725,0x8729, 0x871a,0x8760,0x875f,0x8778, // e5 90..97 + 0x874c,0x874e,0x8774,0x8757, 0x8768,0x876e,0x8759,0x8753, // e5 98..9f + 0x8763,0x876a,0x8805,0x87a2, 0x879f,0x8782,0x87af,0x87cb, // e5 a0..a7 + 0x87bd,0x87c0,0x87d0,0x96d6, 0x87ab,0x87c4,0x87b3,0x87c7, // e5 a8..af + 0x87c6,0x87bb,0x87ef,0x87f2, 0x87e0,0x880f,0x880d,0x87fe, // e5 b0..b7 + 0x87f6,0x87f7,0x880e,0x87d2, 0x8811,0x8816,0x8815,0x8822, // e5 b8..bf + 0x8821,0x8831,0x8836,0x8839, 0x8827,0x883b,0x8844,0x8842, // e5 c0..c7 + 0x8852,0x8859,0x885e,0x8862, 0x886b,0x8881,0x887e,0x889e, // e5 c8..cf + 0x8875,0x887d,0x88b5,0x8872, 0x8882,0x8897,0x8892,0x88ae, // e5 d0..d7 + 0x8899,0x88a2,0x888d,0x88a4, 0x88b0,0x88bf,0x88b1,0x88c3, // e5 d8..df + 0x88c4,0x88d4,0x88d8,0x88d9, 0x88dd,0x88f9,0x8902,0x88fc, // e5 e0..e7 + 0x88f4,0x88e8,0x88f2,0x8904, 0x890c,0x890a,0x8913,0x8943, // e5 e8..ef + 0x891e,0x8925,0x892a,0x892b, 0x8941,0x8944,0x893b,0x8936, // e5 f0..f7 + 0x8938,0x894c,0x891d,0x8960, 0x895e, // e5 f8..fc +}; + +static const u16 shift_jis_double_e6[] = +{ + 0x8966,0x8964,0x896d,0x896a, 0x896f,0x8974,0x8977,0x897e, // e6 40..47 + 0x8983,0x8988,0x898a,0x8993, 0x8998,0x89a1,0x89a9,0x89a6, // e6 48..4f + 0x89ac,0x89af,0x89b2,0x89ba, 0x89bd,0x89bf,0x89c0,0x89da, // e6 50..57 + 0x89dc,0x89dd,0x89e7,0x89f4, 0x89f8,0x8a03,0x8a16,0x8a10, // e6 58..5f + 0x8a0c,0x8a1b,0x8a1d,0x8a25, 0x8a36,0x8a41,0x8a5b,0x8a52, // e6 60..67 + 0x8a46,0x8a48,0x8a7c,0x8a6d, 0x8a6c,0x8a62,0x8a85,0x8a82, // e6 68..6f + 0x8a84,0x8aa8,0x8aa1,0x8a91, 0x8aa5,0x8aa6,0x8a9a,0x8aa3, // e6 70..77 + 0x8ac4,0x8acd,0x8ac2,0x8ada, 0x8aeb,0x8af3,0x8ae7, INV, // e6 78..7f + 0x8ae4,0x8af1,0x8b14,0x8ae0, 0x8ae2,0x8af7,0x8ade,0x8adb, // e6 80..87 + 0x8b0c,0x8b07,0x8b1a,0x8ae1, 0x8b16,0x8b10,0x8b17,0x8b20, // e6 88..8f + 0x8b33,0x97ab,0x8b26,0x8b2b, 0x8b3e,0x8b28,0x8b41,0x8b4c, // e6 90..97 + 0x8b4f,0x8b4e,0x8b49,0x8b56, 0x8b5b,0x8b5a,0x8b6b,0x8b5f, // e6 98..9f + 0x8b6c,0x8b6f,0x8b74,0x8b7d, 0x8b80,0x8b8c,0x8b8e,0x8b92, // e6 a0..a7 + 0x8b93,0x8b96,0x8b99,0x8b9a, 0x8c3a,0x8c41,0x8c3f,0x8c48, // e6 a8..af + 0x8c4c,0x8c4e,0x8c50,0x8c55, 0x8c62,0x8c6c,0x8c78,0x8c7a, // e6 b0..b7 + 0x8c82,0x8c89,0x8c85,0x8c8a, 0x8c8d,0x8c8e,0x8c94,0x8c7c, // e6 b8..bf + 0x8c98,0x621d,0x8cad,0x8caa, 0x8cbd,0x8cb2,0x8cb3,0x8cae, // e6 c0..c7 + 0x8cb6,0x8cc8,0x8cc1,0x8ce4, 0x8ce3,0x8cda,0x8cfd,0x8cfa, // e6 c8..cf + 0x8cfb,0x8d04,0x8d05,0x8d0a, 0x8d07,0x8d0f,0x8d0d,0x8d10, // e6 d0..d7 + 0x9f4e,0x8d13,0x8ccd,0x8d14, 0x8d16,0x8d67,0x8d6d,0x8d71, // e6 d8..df + 0x8d73,0x8d81,0x8d99,0x8dc2, 0x8dbe,0x8dba,0x8dcf,0x8dda, // e6 e0..e7 + 0x8dd6,0x8dcc,0x8ddb,0x8dcb, 0x8dea,0x8deb,0x8ddf,0x8de3, // e6 e8..ef + 0x8dfc,0x8e08,0x8e09,0x8dff, 0x8e1d,0x8e1e,0x8e10,0x8e1f, // e6 f0..f7 + 0x8e42,0x8e35,0x8e30,0x8e34, 0x8e4a, // e6 f8..fc +}; + +static const u16 shift_jis_double_e7[] = +{ + 0x8e47,0x8e49,0x8e4c,0x8e50, 0x8e48,0x8e59,0x8e64,0x8e60, // e7 40..47 + 0x8e2a,0x8e63,0x8e55,0x8e76, 0x8e72,0x8e7c,0x8e81,0x8e87, // e7 48..4f + 0x8e85,0x8e84,0x8e8b,0x8e8a, 0x8e93,0x8e91,0x8e94,0x8e99, // e7 50..57 + 0x8eaa,0x8ea1,0x8eac,0x8eb0, 0x8ec6,0x8eb1,0x8ebe,0x8ec5, // e7 58..5f + 0x8ec8,0x8ecb,0x8edb,0x8ee3, 0x8efc,0x8efb,0x8eeb,0x8efe, // e7 60..67 + 0x8f0a,0x8f05,0x8f15,0x8f12, 0x8f19,0x8f13,0x8f1c,0x8f1f, // e7 68..6f + 0x8f1b,0x8f0c,0x8f26,0x8f33, 0x8f3b,0x8f39,0x8f45,0x8f42, // e7 70..77 + 0x8f3e,0x8f4c,0x8f49,0x8f46, 0x8f4e,0x8f57,0x8f5c, INV, // e7 78..7f + 0x8f62,0x8f63,0x8f64,0x8f9c, 0x8f9f,0x8fa3,0x8fad,0x8faf, // e7 80..87 + 0x8fb7,0x8fda,0x8fe5,0x8fe2, 0x8fea,0x8fef,0x9087,0x8ff4, // e7 88..8f + 0x9005,0x8ff9,0x8ffa,0x9011, 0x9015,0x9021,0x900d,0x901e, // e7 90..97 + 0x9016,0x900b,0x9027,0x9036, 0x9035,0x9039,0x8ff8,0x904f, // e7 98..9f + 0x9050,0x9051,0x9052,0x900e, 0x9049,0x903e,0x9056,0x9058, // e7 a0..a7 + 0x905e,0x9068,0x906f,0x9076, 0x96a8,0x9072,0x9082,0x907d, // e7 a8..af + 0x9081,0x9080,0x908a,0x9089, 0x908f,0x90a8,0x90af,0x90b1, // e7 b0..b7 + 0x90b5,0x90e2,0x90e4,0x6248, 0x90db,0x9102,0x9112,0x9119, // e7 b8..bf + 0x9132,0x9130,0x914a,0x9156, 0x9158,0x9163,0x9165,0x9169, // e7 c0..c7 + 0x9173,0x9172,0x918b,0x9189, 0x9182,0x91a2,0x91ab,0x91af, // e7 c8..cf + 0x91aa,0x91b5,0x91b4,0x91ba, 0x91c0,0x91c1,0x91c9,0x91cb, // e7 d0..d7 + 0x91d0,0x91d6,0x91df,0x91e1, 0x91db,0x91fc,0x91f5,0x91f6, // e7 d8..df + 0x921e,0x91ff,0x9214,0x922c, 0x9215,0x9211,0x925e,0x9257, // e7 e0..e7 + 0x9245,0x9249,0x9264,0x9248, 0x9295,0x923f,0x924b,0x9250, // e7 e8..ef + 0x929c,0x9296,0x9293,0x929b, 0x925a,0x92cf,0x92b9,0x92b7, // e7 f0..f7 + 0x92e9,0x930f,0x92fa,0x9344, 0x932e, // e7 f8..fc +}; + +static const u16 shift_jis_double_e8[] = +{ + 0x9319,0x9322,0x931a,0x9323, 0x933a,0x9335,0x933b,0x935c, // e8 40..47 + 0x9360,0x937c,0x936e,0x9356, 0x93b0,0x93ac,0x93ad,0x9394, // e8 48..4f + 0x93b9,0x93d6,0x93d7,0x93e8, 0x93e5,0x93d8,0x93c3,0x93dd, // e8 50..57 + 0x93d0,0x93c8,0x93e4,0x941a, 0x9414,0x9413,0x9403,0x9407, // e8 58..5f + 0x9410,0x9436,0x942b,0x9435, 0x9421,0x943a,0x9441,0x9452, // e8 60..67 + 0x9444,0x945b,0x9460,0x9462, 0x945e,0x946a,0x9229,0x9470, // e8 68..6f + 0x9475,0x9477,0x947d,0x945a, 0x947c,0x947e,0x9481,0x947f, // e8 70..77 + 0x9582,0x9587,0x958a,0x9594, 0x9596,0x9598,0x9599, INV, // e8 78..7f + 0x95a0,0x95a8,0x95a7,0x95ad, 0x95bc,0x95bb,0x95b9,0x95be, // e8 80..87 + 0x95ca,0x6ff6,0x95c3,0x95cd, 0x95cc,0x95d5,0x95d4,0x95d6, // e8 88..8f + 0x95dc,0x95e1,0x95e5,0x95e2, 0x9621,0x9628,0x962e,0x962f, // e8 90..97 + 0x9642,0x964c,0x964f,0x964b, 0x9677,0x965c,0x965e,0x965d, // e8 98..9f + 0x965f,0x9666,0x9672,0x966c, 0x968d,0x9698,0x9695,0x9697, // e8 a0..a7 + 0x96aa,0x96a7,0x96b1,0x96b2, 0x96b0,0x96b4,0x96b6,0x96b8, // e8 a8..af + 0x96b9,0x96ce,0x96cb,0x96c9, 0x96cd,0x894d,0x96dc,0x970d, // e8 b0..b7 + 0x96d5,0x96f9,0x9704,0x9706, 0x9708,0x9713,0x970e,0x9711, // e8 b8..bf + 0x970f,0x9716,0x9719,0x9724, 0x972a,0x9730,0x9739,0x973d, // e8 c0..c7 + 0x973e,0x9744,0x9746,0x9748, 0x9742,0x9749,0x975c,0x9760, // e8 c8..cf + 0x9764,0x9766,0x9768,0x52d2, 0x976b,0x9771,0x9779,0x9785, // e8 d0..d7 + 0x977c,0x9781,0x977a,0x9786, 0x978b,0x978f,0x9790,0x979c, // e8 d8..df + 0x97a8,0x97a6,0x97a3,0x97b3, 0x97b4,0x97c3,0x97c6,0x97c8, // e8 e0..e7 + 0x97cb,0x97dc,0x97ed,0x9f4f, 0x97f2,0x7adf,0x97f6,0x97f5, // e8 e8..ef + 0x980f,0x980c,0x9838,0x9824, 0x9821,0x9837,0x983d,0x9846, // e8 f0..f7 + 0x984f,0x984b,0x986b,0x986f, 0x9870, // e8 f8..fc +}; + +static const u16 shift_jis_double_e9[] = +{ + 0x9871,0x9874,0x9873,0x98aa, 0x98af,0x98b1,0x98b6,0x98c4, // e9 40..47 + 0x98c3,0x98c6,0x98e9,0x98eb, 0x9903,0x9909,0x9912,0x9914, // e9 48..4f + 0x9918,0x9921,0x991d,0x991e, 0x9924,0x9920,0x992c,0x992e, // e9 50..57 + 0x993d,0x993e,0x9942,0x9949, 0x9945,0x9950,0x994b,0x9951, // e9 58..5f + 0x9952,0x994c,0x9955,0x9997, 0x9998,0x99a5,0x99ad,0x99ae, // e9 60..67 + 0x99bc,0x99df,0x99db,0x99dd, 0x99d8,0x99d1,0x99ed,0x99ee, // e9 68..6f + 0x99f1,0x99f2,0x99fb,0x99f8, 0x9a01,0x9a0f,0x9a05,0x99e2, // e9 70..77 + 0x9a19,0x9a2b,0x9a37,0x9a45, 0x9a42,0x9a40,0x9a43, INV, // e9 78..7f + 0x9a3e,0x9a55,0x9a4d,0x9a5b, 0x9a57,0x9a5f,0x9a62,0x9a65, // e9 80..87 + 0x9a64,0x9a69,0x9a6b,0x9a6a, 0x9aad,0x9ab0,0x9abc,0x9ac0, // e9 88..8f + 0x9acf,0x9ad1,0x9ad3,0x9ad4, 0x9ade,0x9adf,0x9ae2,0x9ae3, // e9 90..97 + 0x9ae6,0x9aef,0x9aeb,0x9aee, 0x9af4,0x9af1,0x9af7,0x9afb, // e9 98..9f + 0x9b06,0x9b18,0x9b1a,0x9b1f, 0x9b22,0x9b23,0x9b25,0x9b27, // e9 a0..a7 + 0x9b28,0x9b29,0x9b2a,0x9b2e, 0x9b2f,0x9b32,0x9b44,0x9b43, // e9 a8..af + 0x9b4f,0x9b4d,0x9b4e,0x9b51, 0x9b58,0x9b74,0x9b93,0x9b83, // e9 b0..b7 + 0x9b91,0x9b96,0x9b97,0x9b9f, 0x9ba0,0x9ba8,0x9bb4,0x9bc0, // e9 b8..bf + 0x9bca,0x9bb9,0x9bc6,0x9bcf, 0x9bd1,0x9bd2,0x9be3,0x9be2, // e9 c0..c7 + 0x9be4,0x9bd4,0x9be1,0x9c3a, 0x9bf2,0x9bf1,0x9bf0,0x9c15, // e9 c8..cf + 0x9c14,0x9c09,0x9c13,0x9c0c, 0x9c06,0x9c08,0x9c12,0x9c0a, // e9 d0..d7 + 0x9c04,0x9c2e,0x9c1b,0x9c25, 0x9c24,0x9c21,0x9c30,0x9c47, // e9 d8..df + 0x9c32,0x9c46,0x9c3e,0x9c5a, 0x9c60,0x9c67,0x9c76,0x9c78, // e9 e0..e7 + 0x9ce7,0x9cec,0x9cf0,0x9d09, 0x9d08,0x9ceb,0x9d03,0x9d06, // e9 e8..ef + 0x9d2a,0x9d26,0x9daf,0x9d23, 0x9d1f,0x9d44,0x9d15,0x9d12, // e9 f0..f7 + 0x9d41,0x9d3f,0x9d3e,0x9d46, 0x9d48, // e9 f8..fc +}; + +static const u16 shift_jis_double_ea[] = +{ + 0x9d5d,0x9d5e,0x9d64,0x9d51, 0x9d50,0x9d59,0x9d72,0x9d89, // ea 40..47 + 0x9d87,0x9dab,0x9d6f,0x9d7a, 0x9d9a,0x9da4,0x9da9,0x9db2, // ea 48..4f + 0x9dc4,0x9dc1,0x9dbb,0x9db8, 0x9dba,0x9dc6,0x9dcf,0x9dc2, // ea 50..57 + 0x9dd9,0x9dd3,0x9df8,0x9de6, 0x9ded,0x9def,0x9dfd,0x9e1a, // ea 58..5f + 0x9e1b,0x9e1e,0x9e75,0x9e79, 0x9e7d,0x9e81,0x9e88,0x9e8b, // ea 60..67 + 0x9e8c,0x9e92,0x9e95,0x9e91, 0x9e9d,0x9ea5,0x9ea9,0x9eb8, // ea 68..6f + 0x9eaa,0x9ead,0x9761,0x9ecc, 0x9ece,0x9ecf,0x9ed0,0x9ed4, // ea 70..77 + 0x9edc,0x9ede,0x9edd,0x9ee0, 0x9ee5,0x9ee8,0x9eef, INV, // ea 78..7f + 0x9ef4,0x9ef6,0x9ef7,0x9ef9, 0x9efb,0x9efc,0x9efd,0x9f07, // ea 80..87 + 0x9f08,0x76b7,0x9f15,0x9f21, 0x9f2c,0x9f3e,0x9f4a,0x9f52, // ea 88..8f + 0x9f54,0x9f63,0x9f5f,0x9f60, 0x9f61,0x9f66,0x9f67,0x9f6c, // ea 90..97 + 0x9f6a,0x9f77,0x9f72,0x9f76, 0x9f95,0x9f9c,0x9fa0,0x582f, // ea 98..9f + 0x69c7,0x9059,0x7464,0x51dc, 0x7199, // ea a0..a4 +}; + +static const u16 shift_jis_double_ff[] = +{ + 0xffff, // ff ff..ff +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// shift_jis_list[] /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[shift_jis_list_t]] + +typedef struct shift_jis_list_t +{ + u8 sect; // section + u8 from; // first index + u8 to; // last index + u8 count; // := to - from + 1 + const u16 *tab; // UTF-16 table +} +shift_jis_list_t; + +//----------------------------------------------------------------------------- + +static const shift_jis_list_t shift_jis_list[] = +{ + { 0x81, 0x40, 0xfc, 189, shift_jis_double_81 }, // 0 + { 0x82, 0x4f, 0xf1, 163, shift_jis_double_82 }, // 1 + { 0x83, 0x40, 0xd6, 151, shift_jis_double_83 }, // 2 + { 0x84, 0x40, 0xbe, 127, shift_jis_double_84 }, // 3 + { 0x88, 0x9f, 0xfc, 94, shift_jis_double_88 }, // 4 + { 0x89, 0x40, 0xfc, 189, shift_jis_double_89 }, // 5 + { 0x8a, 0x40, 0xfc, 189, shift_jis_double_8a }, // 6 + { 0x8b, 0x40, 0xfc, 189, shift_jis_double_8b }, // 7 + { 0x8c, 0x40, 0xfc, 189, shift_jis_double_8c }, // 8 + { 0x8d, 0x40, 0xfc, 189, shift_jis_double_8d }, // 9 + { 0x8e, 0x40, 0xfc, 189, shift_jis_double_8e }, // 10 + { 0x8f, 0x40, 0xfc, 189, shift_jis_double_8f }, // 11 + { 0x90, 0x40, 0xfc, 189, shift_jis_double_90 }, // 12 + { 0x91, 0x40, 0xfc, 189, shift_jis_double_91 }, // 13 + { 0x92, 0x40, 0xfc, 189, shift_jis_double_92 }, // 14 + { 0x93, 0x40, 0xfc, 189, shift_jis_double_93 }, // 15 + { 0x94, 0x40, 0xfc, 189, shift_jis_double_94 }, // 16 + { 0x95, 0x40, 0xfc, 189, shift_jis_double_95 }, // 17 + { 0x96, 0x40, 0xfc, 189, shift_jis_double_96 }, // 18 + { 0x97, 0x40, 0xfc, 189, shift_jis_double_97 }, // 19 + { 0x98, 0x40, 0xfc, 189, shift_jis_double_98 }, // 20 + { 0x99, 0x40, 0xfc, 189, shift_jis_double_99 }, // 21 + { 0x9a, 0x40, 0xfc, 189, shift_jis_double_9a }, // 22 + { 0x9b, 0x40, 0xfc, 189, shift_jis_double_9b }, // 23 + { 0x9c, 0x40, 0xfc, 189, shift_jis_double_9c }, // 24 + { 0x9d, 0x40, 0xfc, 189, shift_jis_double_9d }, // 25 + { 0x9e, 0x40, 0xfc, 189, shift_jis_double_9e }, // 26 + { 0x9f, 0x40, 0xfc, 189, shift_jis_double_9f }, // 27 + { 0xe0, 0x40, 0xfc, 189, shift_jis_double_e0 }, // 28 + { 0xe1, 0x40, 0xfc, 189, shift_jis_double_e1 }, // 29 + { 0xe2, 0x40, 0xfc, 189, shift_jis_double_e2 }, // 30 + { 0xe3, 0x40, 0xfc, 189, shift_jis_double_e3 }, // 31 + { 0xe4, 0x40, 0xfc, 189, shift_jis_double_e4 }, // 32 + { 0xe5, 0x40, 0xfc, 189, shift_jis_double_e5 }, // 33 + { 0xe6, 0x40, 0xfc, 189, shift_jis_double_e6 }, // 34 + { 0xe7, 0x40, 0xfc, 189, shift_jis_double_e7 }, // 35 + { 0xe8, 0x40, 0xfc, 189, shift_jis_double_e8 }, // 36 + { 0xe9, 0x40, 0xfc, 189, shift_jis_double_e9 }, // 37 + { 0xea, 0x40, 0xa4, 101, shift_jis_double_ea }, // 38 + { 0xff, 0xff, 0xff, 1, shift_jis_double_ff }, // 39 + {0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanShiftJISChar ( cucp * str ) +{ + const u8 *s = *str; + u16 code = *s++; + if (!code) + return 0; + *str = s; + + code = shift_jis_single[code]; + if ( code == SJIS_INVALID_CODE ) + return -1; + if ( code < SJIS_TAB_MAPPING_BEG || code > SJIS_TAB_MAPPING_END ) + return code; + + const shift_jis_list_t *ptr = shift_jis_list + code - SJIS_TAB_MAPPING_BEG; + code = *s++; + if (!code) + return 0; + *str = s; + + if ( code < ptr->from || code > ptr->to ) + return -1; + code = ptr->tab[ code - ptr->from ]; + return code == SJIS_INVALID_CODE ? -1 : code; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanShiftJISCharE ( cucp * str, cvp end ) +{ + if (!end) + return ScanShiftJISChar(str); + + const u8 *s = *str; + if ( s >= (u8*)end ) + return 0; + + u16 code = *s++; + code = shift_jis_single[code]; + *str = s; + + if ( code < SJIS_TAB_MAPPING_BEG || code > SJIS_TAB_MAPPING_END ) + return code; + if ( code == SJIS_INVALID_CODE || s == end ) + return -1; + + const shift_jis_list_t *ptr = shift_jis_list + code - SJIS_TAB_MAPPING_BEG; + code = *s++; + *str = s; + + if ( code < ptr->from || code > ptr->to ) + return -1; + code = ptr->tab[ code - ptr->from ]; + return code == SJIS_INVALID_CODE ? -1 : code; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u16 *get_shift_jis_cache = 0; + +void SetupGetShiftJISCache() +{ + if (get_shift_jis_cache) + return; + + const uint total_size = SJIS_CACHE_SIZE * sizeof(*get_shift_jis_cache); + get_shift_jis_cache = MALLOC(total_size); + memset(get_shift_jis_cache,~0,total_size); + + const shift_jis_list_t *ptr; + for ( ptr = shift_jis_list; ptr->tab; ptr++ ) + { + u16 code = ptr->sect << 8 | ptr->from; + const u16 *p = ptr->tab; + const u16 *e = p + ptr->count; + for ( ; p < e; p++, code++ ) + { + DASSERT_MSG( IsValidShiftJIS(*p) || *p == SJIS_INVALID_CODE, + "table %02x[%zu], val=%04x\n", ptr->sect, p-ptr->tab, *p ); + if ( *p != SJIS_INVALID_CODE ) + get_shift_jis_cache[*p] = code; + } + } + + uint i; + for ( i = 0; i < 0x100; i++ ) + { + const u16 code = shift_jis_single[i]; + if (IsValidShiftJIS(code)) + get_shift_jis_cache[code] = i; + } + + PRINT("Shift-JIS: %s\n",GetShiftJISStatistics()); +} + +//----------------------------------------------------------------------------- + +int GetShiftJISChar ( uint code ) +{ + if (get_shift_jis_cache) + { + if ( code >= SJIS_CACHE_SIZE ) + return -1; + u16 res = get_shift_jis_cache[code]; + return res == SJIS_INVALID_CODE ? -1 : res; + } + + if (!IsValidShiftJIS(code)) + return -1; + + uint i; + for ( i = 0; i < 0x100; i++ ) + if ( shift_jis_single[i] == code ) + return i; + + const shift_jis_list_t *ptr; + for ( ptr = shift_jis_list; ptr->tab; ptr++ ) + { + const u16 *p = ptr->tab; + const u16 *e = p + ptr->count; + for ( ; p < e; p++ ) + if ( *p == code ) + return ptr->sect << 8 | (p-ptr->tab) + ptr->from; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetShiftJISStatistics() +{ + SetupGetShiftJISCache(); + + uint i, cache_count = 0,cache_max = 0; + for ( i = 0; i < SJIS_CACHE_SIZE; i++ ) + if ( get_shift_jis_cache[i] != SJIS_INVALID_CODE ) + { + cache_count++; + cache_max = i; + } + + + uint single_count = 0; + for ( i = 0; i < 0x100; i++ ) + if (IsValidShiftJIS(shift_jis_single[i])) + single_count++; + + uint dbl_count = 0, dbl_total = 0, dbl_tab = 0; + const shift_jis_list_t *ptr; + for ( ptr = shift_jis_list; ptr->tab; ptr++ ) + { + dbl_tab++; + dbl_total += ptr->count; + u16 code = ptr->sect << 8 | ptr->from; + const u16 *p = ptr->tab; + const u16 *e = p + ptr->count; + for ( ; p < e; p++, code++ ) + if ( *p != SJIS_INVALID_CODE ) + dbl_count++; + } + + char buf[100]; + const uint len = snprintf(buf,sizeof(buf), + "%u singles, %u/%u doubles in %u tables, " + "max:%04x, cache=%u/%u" + ,single_count + ,dbl_count,dbl_total,dbl_tab + ,cache_max + ,cache_count,SJIS_CACHE_SIZE + ); + return CopyCircBuf(buf,len+1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-system.h b/project/dclib/dclib-system.h new file mode 100644 index 0000000..64a021d --- /dev/null +++ b/project/dclib/dclib-system.h @@ -0,0 +1,101 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_SYSTEM_H +#define DCLIB_SYSTEM_H 1 + +#include + +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumSystemID +{ + SYSID_UNKNOWN = 0x00000000, + SYSID_I386 = 0x01000000, + SYSID_X86_64 = 0x02000000, + SYSID_CYGWIN = 0x03000000, + SYSID_APPLE = 0x04000000, + SYSID_LINUX = 0x05000000, + SYSID_UNIX = 0x06000000, + +} enumSystemID; + +/////////////////////////////////////////////////////////////////////////////// + +#undef SYSTEM_LINUX + +#ifdef __CYGWIN__ + #define SYSTEM "cygwin" + #define SYSTEMID SYSID_CYGWIN +#elif __APPLE__ + #define SYSTEM "mac" + #define SYSTEMID SYSID_APPLE +#elif __linux__ + #define SYSTEM_LINUX 1 + #ifdef __i386__ + #define SYSTEM "i386" + #define SYSTEMID SYSID_I386 + #elif __x86_64__ + #define SYSTEM "x86_64" + #define SYSTEMID SYSID_X86_64 + #else + #define SYSTEM "linux" + #define SYSTEMID SYSID_LINUX + #endif +#elif __unix__ + #define SYSTEM "unix" + #define SYSTEMID SYSID_UNIX +#else + #define SYSTEM "unknown" + #define SYSTEMID SYSID_UNKNOWN +#endif + +/////////////////////////////////////////////////////////////////////////////// + +static inline void dclibPrintSystem ( FILE * f ) +{ + fprintf(f, + "SYSTEM\t\t:= %s\n" + "SYSTEMID\t:= 0x%x\n" + #ifdef SYSTEM_LINUX + "SYSTEM_LINUX\t:= 1\n" + #endif + ,SYSTEM + ,SYSTEMID ); +} + +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_SYSTEM_H 1 + diff --git a/project/dclib/dclib-tables.c b/project/dclib/dclib-tables.c new file mode 100644 index 0000000..7206f6a --- /dev/null +++ b/project/dclib/dclib-tables.c @@ -0,0 +1,417 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +//#include +//#include +//#include +//#include +//#include +//#include + +#include "dclib-basics.h" +//#include "dclib-debug.h" +//#include "dclib-utf8.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// table element abbreviations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define DN DECODE_NULL +#define DC DECODE_CONTROL +#define DL DECODE_LINE +#define DS DECODE_SPACE +#define DP DECODE_SEPARATE +#define DF DECODE_FILLER +#define DX DECODE_OTHER + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// bits /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const uchar TableBitCount[0x100] = +{ + 0,1,1,2, 1,2,2,3, 1,2,2,3, 2,3,3,4, + 1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5, + 1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5, + 2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6, + + 1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5, + 2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6, + 2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6, + 3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7, + + 1,2,2,3, 2,3,3,4, 2,3,3,4, 3,4,4,5, + 2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6, + 2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6, + 3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7, + + 2,3,3,4, 3,4,4,5, 3,4,4,5, 4,5,5,6, + 3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7, + 3,4,4,5, 4,5,5,6, 4,5,5,6, 5,6,6,7, + 4,5,5,6, 5,6,6,7, 5,6,6,7, 6,7,7,8 +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableLowest0Bit[0x100] = +{ + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,5, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,6, + + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,5, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,7, + + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,5, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,6, + + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,5, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,4, + 0,1,0,2, 0,1,0,3, 0,1,0,2, 0,1,0,-1 +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableLowest1Bit[0x100] = +{ + -1,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 5,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + + 6,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 5,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + + 7,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 5,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + + 6,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 5,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0, + 4,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0 +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableHighest0Bit[0x100] = +{ + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + + 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, + 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, + 4,4,4,4, 4,4,4,4, 4,4,4,4, 4,4,4,4, + 3,3,3,3, 3,3,3,3, 2,2,2,2, 1,1,0,-1 +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableHighest1Bit[0x100] = +{ + -1,0,1,1, 2,2,2,2, 3,3,3,3, 3,3,3,3, + 4,4,4,4, 4,4,4,4, 4,4,4,4, 4,4,4,4, + 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, + 5,5,5,5, 5,5,5,5, 5,5,5,5, 5,5,5,5, + + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + 6,6,6,6, 6,6,6,6, 6,6,6,6, 6,6,6,6, + + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7, + 7,7,7,7, 7,7,7,7, 7,7,7,7, 7,7,7,7 +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scanning numbers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const char TableNumbers[256] = +{ + DN, DC, DC, DC, DC, DC, DC, DC, DC, DS, DL, DC, DS, DL, DC, DC, + DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, + DS, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DP, DX, DX, DX, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, DX, DP, DX, DX, DX, DX, + + DX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, DX, DX, DX, DX, DX, + DX, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, DX, DX, DX, DX, DX, + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CRC32 support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const u32 TableCRC32[0x100] = +{ + 0x00000000,0x77073096,0xee0e612c,0x990951ba, 0x076dc419,0x706af48f,0xe963a535,0x9e6495a3, + 0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988, 0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91, + 0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de, 0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7, + 0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec, 0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5, + 0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172, 0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b, + 0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940, 0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59, + 0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116, 0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f, + 0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924, 0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d, + 0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a, 0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433, + 0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818, 0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01, + 0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, 0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457, + 0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c, 0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65, + 0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2, 0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb, + 0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0, 0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9, + 0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086, 0x5768b525,0x206f85b3,0xb966d409,0xce61e49f, + 0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4, 0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad, + 0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a, 0xead54739,0x9dd277af,0x04db2615,0x73dc1683, + 0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8, 0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1, + 0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe, 0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7, + 0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc, 0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5, + 0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252, 0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, + 0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60, 0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79, + 0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236, 0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f, + 0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04, 0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d, + 0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a, 0x9c0906a9,0xeb0e363f,0x72076785,0x05005713, + 0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38, 0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21, + 0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e, 0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777, + 0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c, 0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45, + 0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2, 0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db, + 0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0, 0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9, + 0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6, 0xbad03605,0xcdd70693,0x54de5729,0x23d967bf, + 0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, 0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers for codepages /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const u16 TableCP1252_80[0x20] = +{ + 0x20ac, 0,0x201a,0x0192, 0x201e,0x2026,0x2020,0x2021, // 80..87 + 0x02c6,0x2030,0x0160,0x2039, 0x0152, 0,0x017d, 0, // 88..8f + 0,0x2018,0x2019,0x201c, 0x201d,0x2022,0x2013,0x2014, // 90..97 + 0x02dc,0x2122,0x0161,0x203a, 0x0153, 0,0x017e,0x0178, // 98..9f +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BASE64 support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// https://en.wikipedia.org/wiki/Base64 + +char TableDecode64[256] = +{ + DN, DC, DC, DC, DC, DC, DC, DC, DC, DS, DS, DC, DS, DS, DC, DC, // 0x + DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, // 1x + DS, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, 62, DP, DX, DX, 63, // 2x + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, DX, DP, DX, DF, DX, DX, // 3x + + DX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 4x + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, DX, DX, DX, DX, DX, // 5x + DX, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6x + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, DX, DX, DX, DX, DX, // 7x + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // 8x + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // 9x + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Ax + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Bx + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Cx + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Dx + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Ex + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX // Fx +}; + +/////////////////////////////////////////////////////////////////////////////// + +char TableDecode64url[256] = +{ + DN, DC, DC, DC, DC, DC, DC, DC, DC, DS, DS, DC, DS, DS, DC, DC, // 0x + DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, // 1x + DS, DX, DX, DX, DX, DX, DX, DX, DX, DX, DF, 62, DP, 62, DX, 63, // 2x + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, DX, DP, DX, DF, DX, DX, // 3x + + DX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 4x + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, DX, DX, DX, DX, 63, // 5x + DX, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6x + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, DX, DX, DX, DX, DX, // 7x + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // 8x + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // 9x + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Ax + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Bx + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Cx + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Dx + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Ex + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX // Fx +}; + +/////////////////////////////////////////////////////////////////////////////// + +char TableDecode64xml[256] = +{ + DN, DC, DC, DC, DC, DC, DC, DC, DC, DS, DS, DC, DS, DS, DC, DC, // 0x + DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, DC, // 1x + DS, DX, DX, DX, DX, DX, DX, DX, DX, DX, DF, 62, DP, 63, 62, 63, // 2x + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 63, DP, DX, DF, DX, DX, // 3x + + DX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 4x + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, DX, DX, DX, DX, 62, // 5x + DX, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6x + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, DX, DX, DX, DX, DX, // 7x + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // 8x + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // 9x + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Ax + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Bx + + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Cx + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Dx + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, // Ex + DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX, DX // Fx +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableEncode64[64+1] = +{ + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9', + '+','/', + '=' // fill char +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableEncode64url[64+1] = +{ + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9', + '-','_', + '=' // fill char +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableEncode64star[64+1] = +{ + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9', + '-','_', + '*' // fill char +}; + +/////////////////////////////////////////////////////////////////////////////// + +const char TableEncode64xml[64+1] = +{ + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9', + '.','-', + '*' // fill char +}; + +/////////////////////////////////////////////////////////////////////////////// + +// for tests: encode the 48 bytes to get the full BASE64 alphabet + +const u8 TableAlphabet64[48] = +{ + 0x00,0x10,0x83,0x10, 0x51,0x87,0x20,0x92, 0x8b,0x30,0xd3,0x8f, 0x41,0x14,0x93,0x51, + 0x55,0x97,0x61,0x96, 0x9b,0x71,0xd7,0x9f, 0x82,0x18,0xa3,0x92, 0x59,0xa7,0xa2,0x9a, + 0xab,0xb2,0xdb,0xaf, 0xc3,0x1c,0xb3,0xd3, 0x5d,0xb7,0xe3,0x9e, 0xbb,0xf3,0xdf,0xbf, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-types.h b/project/dclib/dclib-types.h new file mode 100644 index 0000000..8631d9a --- /dev/null +++ b/project/dclib/dclib-types.h @@ -0,0 +1,403 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_TYPES_H +#define DCLIB_TYPES_H 1 + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include + +#ifndef WIN_DCLIB + #include +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// endian /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[dcEndian_t]] + +typedef enum dcEndian_t +{ + //--- always the same + + DC_DEFAULT_ENDIAN = 0, // always 0 + DC_SECOND_ENDIAN = 1, // always >0 + + + //--- define local & reverse endian, if possible + + #if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN + + DC_LOCAL_ENDIAN = DC_DEFAULT_ENDIAN, + DC_REVERSE_ENDIAN = DC_SECOND_ENDIAN, + + #endif + + + //--- define aliases for little & big endian + + #if __BYTE_ORDER == __BIG_ENDIAN + + DC_BIG_ENDIAN = DC_DEFAULT_ENDIAN, + DC_LITTLE_ENDIAN = DC_SECOND_ENDIAN, + + #else + + DC_LITTLE_ENDIAN = DC_DEFAULT_ENDIAN, + DC_BIG_ENDIAN = DC_SECOND_ENDIAN, + + #endif +} +__attribute__ ((packed)) dcEndian_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// constants /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define KB_SI 1000 +#define MB_SI (1000*1000) +#define GB_SI (1000*1000*1000) +#define TB_SI (1000ull*1000*1000*1000) +#define PB_SI (1000ull*1000*1000*1000*1000) +#define EB_SI (1000ull*1000*1000*1000*1000*1000) + +#define KiB 1024 +#define MiB (1024*1024) +#define GiB (1024*1024*1024) +#define TiB (1024ull*1024*1024*1024) +#define PiB (1024ull*1024*1024*1024*1024) +#define EiB (1024ull*1024*1024*1024*1024*1024) + +// specal '-1' support +#define M1(a) ( (typeof(a))~0 ) +#define IS_M1(a) ( (a) == (typeof(a))~0 ) + +#define ALIGN32(d,a) ((d)+((a)-1)&~(u32)((a)-1)) +#define ALIGN64(d,a) ((d)+((a)-1)&~(u64)((a)-1)) +#define ALIGNOFF32(d,a) ((d)&~(u32)((a)-1)) +#define ALIGNOFF64(d,a) ((d)&~(u64)((a)-1)) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// error codes /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[enumError]] + +typedef enum enumError +{ + //------------------------------------------------------------------- + ERR_OK, // 100% success; below = warnings + //------------------------------------------------------------------- + + ERU_DIFFER, + ERR_DIFFER, + ERU_NOTHING_TO_DO, + ERR_NOTHING_TO_DO, + ERU_SOURCE_FOUND, + ERR_SOURCE_FOUND, + ERU_NO_SOURCE_FOUND, + ERR_NO_SOURCE_FOUND, + ERU_JOB_IGNORED, + ERR_JOB_IGNORED, + ERU_SUBJOB_WARNING, + ERR_SUBJOB_WARNING, + ERU_NOT_EXISTS, + ERR_NOT_EXISTS, + + ERU_WARN_00, + ERU_WARN_01, + ERU_WARN_02, + ERU_WARN_03, + ERU_WARN_04, + ERU_WARN_05, + ERU_WARN_06, + ERU_WARN_07, + ERU_WARN_08, + ERU_WARN_09, + ERU_WARN_10, + ERU_WARN_XX, + + //------------------------------------------------------------------- + ERU_WARNING, + ERR_WARNING, // separator: below = real errors and not warnings + //------------------------------------------------------------------- + + ERU_WRONG_FILE_TYPE, + ERR_WRONG_FILE_TYPE, + ERU_INVALID_FILE, + ERR_INVALID_FILE, + ERU_INVALID_VERSION, + ERR_INVALID_VERSION, + ERU_INVALID_DATA, + ERR_INVALID_DATA, + + ERU_ERROR1_00, + ERU_ERROR1_01, + ERU_ERROR1_02, + ERU_ERROR1_03, + ERU_ERROR1_04, + ERU_ERROR1_05, + ERU_ERROR1_06, + ERU_ERROR1_07, + ERU_ERROR1_08, + ERU_ERROR1_09, + ERU_ERROR1_10, + ERU_ERROR1_11, + ERU_ERROR1_12, + ERU_ERROR1_13, + ERU_ERROR1_14, + ERU_ERROR1_15, + ERU_ERROR1_16, + ERU_ERROR1_17, + ERU_ERROR1_18, + ERU_ERROR1_19, + ERU_ERROR1_20, + ERU_ERROR1_XX, + + ERU_ENCODING, + ERR_ENCODING, + ERU_DECODING, + ERR_DECODING, + ERU_ALREADY_EXISTS, + ERR_ALREADY_EXISTS, + ERU_SUBJOB_FAILED, + ERR_SUBJOB_FAILED, + + ERR_CANT_REMOVE, + ERU_CANT_REMOVE, + ERU_CANT_RENAME, + ERR_CANT_RENAME, + ERU_CANT_CLOSE, + ERR_CANT_CLOSE, + ERU_CANT_CONNECT, + ERR_CANT_CONNECT, + ERU_CANT_OPEN, + ERR_CANT_OPEN, + ERU_CANT_APPEND, + ERR_CANT_APPEND, + ERU_CANT_CREATE, + ERR_CANT_CREATE, + ERU_CANT_CREATE_DIR, + ERR_CANT_CREATE_DIR, + + ERU_READ_FAILED, + ERR_READ_FAILED, + ERU_REMOVE_FAILED, + ERR_REMOVE_FAILED, + ERU_WRITE_FAILED, + ERR_WRITE_FAILED, + ERU_DATABASE, + ERR_DATABASE, + + ERU_ERROR2_00, + ERU_ERROR2_01, + ERU_ERROR2_02, + ERU_ERROR2_03, + ERU_ERROR2_04, + ERU_ERROR2_05, + ERU_ERROR2_06, + ERU_ERROR2_07, + ERU_ERROR2_08, + ERU_ERROR2_09, + ERU_ERROR2_10, + ERU_ERROR2_XX, + + ERU_MISSING_PARAM, + ERR_MISSING_PARAM, + ERU_SEMANTIC, + ERR_SEMANTIC, + ERU_SYNTAX, + ERR_SYNTAX, + + ERU_INTERRUPT, + ERR_INTERRUPT, + + //------------------------------------------------------------------- + ERU_ERROR, + ERR_ERROR, // separator: below = hard/fatal errors => exit + //------------------------------------------------------------------- + + ERU_NOT_IMPLEMENTED, + ERR_NOT_IMPLEMENTED, + ERU_INTERNAL, + ERR_INTERNAL, + + ERU_FATAL_00, + ERU_FATAL_01, + ERU_FATAL_02, + ERU_FATAL_03, + ERU_FATAL_04, + ERU_FATAL_XX, + + ERU_OUT_OF_MEMORY, + ERR_OUT_OF_MEMORY, + + //------------------------------------------------------------------- + ERU_FATAL, + ERR_FATAL, + //------------------------------------------------------------------- + + ERR__N + +} enumError; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// base types /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef WIN_DCLIB + + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + typedef unsigned long long u64; + + typedef signed char s8; + typedef signed short s16; + typedef signed int s32; + typedef signed long long s64; + +#else + + typedef uint8_t u8; + typedef uint16_t u16; + typedef uint32_t u32; + //typedef uint64_t u64; // is 'long unsigned' in 64 bit linux + typedef unsigned long long u64; + + typedef int8_t s8; + typedef int16_t s16; + typedef int32_t s32; + //typedef int64_t s64; // is 'long int' in 64 bit linux + typedef long long int s64; + + #ifndef __cplusplus + typedef enum bool { false, true } __attribute__ ((packed)) bool; + #endif + +#endif + +typedef u16 be16_t; +typedef u32 be32_t; +typedef u64 be64_t; + +typedef float float32; // float as 32 bit binary data, endian is file dependent + +/////////////////////////////////////////////////////////////////////////////// + +#define U8_MIN (0) +#define U8_MAX (0xff) +#define S8_MIN (-0x80) +#define S8_MAX (0x7f) + +#define U16_MIN (0) +#define U16_MAX (0xffff) +#define S16_MIN (-0x8000) +#define S16_MAX (0x7fff) + +#define U32_MIN (0u) +#define U32_MAX (0xffffffffu) +#define S32_MIN (-0x80000000) +#define S32_MAX (0x7fffffff) + +#define U64_MIN (0ull) +#define U64_MAX (0xffffffffffffffffull) +#define S64_MIN (-0x8000000000000000ll) +#define S64_MAX (0x7fffffffffffffffll) + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef DCLIB_BASIC_TYPES + #define DCLIB_BASIC_TYPES 1 + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned long ulong; + typedef const void * cvp; + typedef const char * ccp; + typedef const uchar * cucp; +#endif + +// [[sha1_hash_t]] [[sha1_hex_t]] [[sha1_id_t]] +typedef u8 sha1_hash_t[20]; +typedef char sha1_hex_t[41]; // 40 chars + NULL +typedef u8 sha1_id_t[9]; // 8*chars + NULL + +// [[uuid_buf_t]] [[uuid_text_t]] +typedef u8 uuid_buf_t[16]; +typedef char uuid_text_t[37]; // "12345678-1234-1234-1234-123456789012" + NULL + +typedef int (*qsort_func)( const void *, const void * ); +typedef int (*qsort_r_func)( const void *, const void *, void * ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// some structs /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[KeywordTab_t]] + +typedef struct KeywordTab_t +{ + s64 id; // id + ccp name1; // first name + ccp name2; // NULL or second name + s64 opt; // option + +} KeywordTab_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MSVC extensions (not CYGWIN) /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef WIN_DCLIB + + typedef u32 mode_t; + +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_TYPES_H + diff --git a/project/dclib/dclib-ui.c b/project/dclib/dclib-ui.c new file mode 100644 index 0000000..67226dd --- /dev/null +++ b/project/dclib/dclib-ui.c @@ -0,0 +1,950 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include + +#include "dclib-basics.h" +#include "dclib-ui.h" +#include "dclib-debug.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// register options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError RegisterOptionByIndex +( + const InfoUI_t * iu, // valid pointer + int opt_index, // index of option (OPT_*) + int level, // the level of registration + bool is_env // true: register environment pre setting +) +{ + ASSERT(iu); + DASSERT(iu->opt_info); + DASSERT(iu->opt_used); + DASSERT(iu->opt_index); + + if ( level <= 0 ) + return ERR_OK; + + if ( opt_index >= 0 + && opt_index < iu->n_opt_total + && opt_index < UIOPT_INDEX_SIZE ) + { + TRACE("OPT: RegisterOptionByIndex(,%02x,%d,%d) opt_index=%02x,%s\n", + opt_index, level, is_env, opt_index, + iu->opt_info[opt_index].long_name ); + u8 * obj = iu->opt_used + opt_index; + u32 count = *obj; + if (is_env) + { + if ( count < 0x7f ) + { + count += level; + *obj = count < 0x7f ? count : 0x7f; + } + } + else + { + if ( count < 0x80 ) + count = 0x80; + count += level; + *obj = count < 0xff ? count : 0xff; + } + + return ERR_OK; + } + + PRINT("OPT: RegisterOptionbyIndex(,%02x/%02x/%02x,%d,%d) => WARNING\n", + opt_index, iu->n_opt_total, UIOPT_INDEX_SIZE, level, is_env ); + return ERR_WARNING; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError RegisterOptionByName +( + const InfoUI_t * iu, // valid pointer + int opt_name, // short name of GO_* value of option + int level, // the level of registration + bool is_env // true: register environment pre setting +) +{ + ASSERT(iu); + DASSERT(iu->opt_index); + + if ( opt_name >= 0 && opt_name < UIOPT_INDEX_SIZE ) + return RegisterOptionByIndex(iu,iu->opt_index[opt_name],level,is_env); + + PRINT("OPT: RegisterOptionbyName(,%02x,%d,%d) => WARNING\n", + opt_name, level, is_env ); + return ERR_WARNING; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError VerifySpecificOptions ( const InfoUI_t * iu, const KeywordTab_t * cmd ) +{ + ASSERT(iu); + ASSERT(cmd); + + ASSERT( cmd->id > 0 && cmd->id < iu->n_cmd ); + const InfoCommand_t * ic = iu->cmd_info + cmd->id; + ASSERT(ic->opt_allowed); + u8 * allow = ic->opt_allowed; + u8 * active = iu->opt_used; + + TRACE("ALLOWED SPECIFIC OPTIONS:\n"); + TRACE_HEXDUMP(10,0,0,-20,allow,iu->n_opt_specific); + TRACE("ACTIVE SPECIFIC OPTIONS:\n"); + TRACE_HEXDUMP(10,0,0,-20,active,iu->n_opt_specific); + + enumError err = ERR_OK; + int idx; + for ( idx = 0; idx < iu->n_opt_specific; idx++, allow++, active++ ) + { + if (!*allow) + { + if ( *active & 0x80 ) + { + const InfoOption_t * io = iu->opt_info + idx; + if (io->short_name) + ERROR0(ERR_SEMANTIC,"Command '%s' doesn't allow the option --%s (-%c).\n", + cmd->name1, io->long_name, io->short_name ); + else + ERROR0(ERR_SEMANTIC,"Command '%s' doesn't allow the option --%s.\n", + cmd->name1, io->long_name ); + err = ERR_SEMANTIC; + + } + else + *active = 0; // clear environ settings + } + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetOptionCount ( const InfoUI_t * iu, int option ) +{ + ASSERT(iu); + ASSERT(iu->opt_info); + ASSERT(iu->opt_used); + + if ( option > 0 && option < iu->n_opt_total ) + { + TRACE("GetOptionCount(,%02x) option=%s => %d\n", + option, iu->opt_info[option].long_name, + iu->opt_used[option] & UIOPT_USED_MASK ); + return iu->opt_used[option] & UIOPT_USED_MASK; + } + + TRACE("GetOptionCount(,%02x) => INVALID [-1]\n",option); + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpUsedOptions ( const InfoUI_t * iu, FILE * f, int indent ) +{ + ASSERT(iu); + ASSERT(iu->opt_info); + ASSERT(iu->opt_used); + + TRACE("OptionUsed[]:\n"); + TRACE_HEXDUMP16(9,0,iu->opt_used,iu->n_opt_total+1); + + if (!f) + return; + + indent = NormalizeIndent(indent); + + int i; + for ( i = 0; i < iu->n_opt_total; i++ ) + { + const u8 opt = iu->opt_used[i]; + if (opt) + fprintf(f,"%*s%s %s %2u* [%02x,%s]\n", + indent, "", + i <= iu->n_opt_specific ? "CMD" : "GLB", + opt & 0x80 ? "PRM" : "ENV", + opt & UIOPT_USED_MASK, + i, iu->opt_info[i].long_name ); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +void WarnDepractedOptions ( const struct InfoUI_t * iu ) +{ + DASSERT(iu); + + uint i, count = 0; + char buf[PATH_MAX]; + char *dest = buf; + + for ( i = 0; i < iu->n_opt_total; i++ ) + { + if ( iu->opt_used[i] ) + { + const InfoOption_t *io = iu->opt_info + i; + if ( io->deprecated ) + { + if (io->long_name) + { + count++; + dest = snprintfE(dest,buf+sizeof(buf)," --%s",io->long_name); + } + else if (io->short_name) + { + count++; + dest = snprintfE(dest,buf+sizeof(buf)," -%c",io->short_name); + } + } + } + } + + if (count) + ERROR0(ERR_WARNING, + "Attention: Deprecated option%s:%s\n", + count == 1 ? "" : "s", buf ); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CheckEnvOptions ( ccp varname, check_opt_func func ) +{ + TRACE("CheckEnvOptions(%s,%p)\n",varname,func); + ccp env = getenv(varname); + if ( !env || !*env ) + return ERR_OK; + + TRACE("env[%s] = %s\n",varname,env); + + const int envlen = strlen(env); + char * buf = MALLOC(envlen+1); + char * dest = buf; + + int argc = 1; // argv[0] = progname + ccp src = env; + while (*src) + { + while ( *src > 0 && *src <= ' ' ) // skip blanks & control + src++; + + if (!*src) + break; + + argc++; + while ( *(u8*)src > ' ' ) + *dest++ = *src++; + *dest++ = 0; + ASSERT( dest <= buf+envlen+1 ); + } + TRACE("argc = %d\n",argc); + + char ** argv = MALLOC((argc+1)*sizeof(*argv)); + argv[0] = (char*)progname; + argv[argc] = 0; + dest = buf; + int i; + for ( i = 1; i < argc; i++ ) + { + TRACE("argv[%d] = %s\n",i,dest); + argv[i] = dest; + while (*dest) + dest++; + dest++; + ASSERT( dest <= buf+envlen+1 ); + } + + enumError stat = func(argc,argv,true); + if (stat) + fprintf(stderr, + "Error while scanning the environment variable '%s'\n",varname); + + // don't free() because it's possible that there are pointers to arguments + //FREE(argv); + //FREE(buf); + + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// text helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void DumpText +( + FILE * f, // output file, if NULL: 'pbuf' must be set + char * pbuf, // output buffer, ignored if 'f' not NULL + char * pbuf_end, // end of output buffer + ccp text, // source text + int text_len, // >=0: length of text, <0: use strlen(text) + bool is_makedoc, // true: write makedoc format + ccp end // write this text at the end of all +) +{ + ASSERT(f||pbuf); + + ccp tie = is_makedoc ? " \\" : ""; + char buf[100], *buf_end = buf + 70, *dest = buf; + + bool set_apos = false; + bool brace_active = false; + + if (!text) + text = ""; + if ( text_len < 0 ) + text_len = strlen(text) ; + ccp text_end = text + text_len; + + while ( text < text_end ) + { + char * dest_break = 0; + ccp text_break = 0; + + bool eol = false; + while ( !eol && text < text_end && dest < buf_end ) + { + if ( *text >= 1 && *text <= 3 ) + { + // \1 : Text only for built in help + // \2 : Text only for ui.def + // \3 : Text for all (default) + + const bool skip = *text == ( is_makedoc ? 1 : 2 ); + //PRINT("CH=%d, SKIP=%d\n",*text,skip); + text++; + if (skip) + while ( text < text_end && (u8)*text > 3 ) + text++; + continue; + } + + switch (*text) + { + case 0: + // ignore + break; + + case ' ': + if ( dest > buf ) + { + dest_break = dest; + text_break = text; + } + *dest++ = *text++; + break; + + case '\n': + eol = true; + *dest++ = '\\'; + *dest++ = 'n'; + text++; + dest_break = 0; + text_break = 0; + break; + + case '\\': + case '"': + *dest++ = '\\'; + *dest++ = *text++; + break; + + case '{': + if (is_makedoc) + *dest++ = *text++; + else if ( *++text == '{' ) + *dest++ = *text++; + else + { + brace_active = true; + ccp ptr = text; + while ( ptr < text_end && *ptr > ' ' && *ptr != '}' ) + ptr++; + set_apos = *ptr != '}'; + if (set_apos) + *dest++ = '\''; + } + break; + + + case '}': + if ( is_makedoc || !brace_active ) + *dest++ = *text++; + else if ( *++text == '}' ) + *dest++ = *text++; + else + { + brace_active = false; + if (set_apos) + { + set_apos = false; + *dest++ = '\''; + } + } + break; + + case '@': + case '$': + case '§': + if (!is_makedoc) + { + if ( text[1] == *text ) + *dest++ = *text++; + text++; + break; + } + // fall through + + default: + *dest++ = *text++; + } + DASSERT( dest < buf_end + 10 ); + } + + if ( dest >= buf_end && dest_break ) + { + dest = dest_break; + text = text_break; + } + + if ( dest > buf && text < text_end ) + { + *dest = 0; + if (f) + fprintf(f,"\t\"%s\"%s\n",buf,tie); + else + pbuf = snprintfE(pbuf,pbuf_end,"\t\"%s\"%s\n",buf,tie); + dest = buf; + } + } + + *dest = 0; + if (f) + fprintf(f,"\t\"%s\"%s",buf,end); + else + snprintf(pbuf,pbuf_end-pbuf,"\t\"%s\"%s",buf,end); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_single_option +( + FILE * f, // valid output stream + int indent, // normalized indent of output + const InfoOption_t * io, // option to print + uint opt_fw, // field width for options names + uint disp_fw // total field width of displayed output +) +{ + DASSERT(f); + DASSERT(io); + + if ( !io->short_name && !io->long_name ) + { + fputc('\n',f); + return; + } + + ccp col_reset, col_param; + if (IsFileColorized(f)) + { + fputs(GetTextMode(1,TTM_COL_OPTION),f); + col_param = io->param ? GetTextMode(1,TTM_COL_PARAM) : ""; + col_reset = TermTextModeReset; + } + else + col_reset = col_param = ""; + + ccp iop = io->param; + ccp sep = !iop ? "" + : !io->optional_parm ? " " + : iop[0] == '=' || iop[0] == '[' && iop[0] == '=' ? "" + : "="; + + int len; + if (!io->short_name) + len = fprintf(f,"%*s --%s%s%s%s ", + indent, "", + io->long_name, + sep, + col_param, iop ? iop : "" ); + else if (!io->long_name) + len = fprintf(f,"%*s -%c%s%s%s ", + indent, "", + io->short_name, + iop ? " " : "", + col_param, iop ? iop : "" ); + else + len = fprintf(f,"%*s -%c --%s%s%s%s ", + indent, "", + io->short_name, + io->long_name, + sep, + col_param, iop ? iop : "" ); + + len -= strlen(col_param); + if ( len > opt_fw ) + { + fprintf(f,"%s\n",col_reset); + PutLines(f,opt_fw,disp_fw,0,0,io->help,0); + } + else + { + fputs(col_reset,f); + PutLines(f,opt_fw,disp_fw,len,0,io->help,0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline bool is_opt_selected +( + const InfoUI_t * iu, // valid pointer + const InfoOption_t * io, // active option + bool use_filter // true: user filter +) +{ + DASSERT(io); + return use_filter + ? ( iu->opt_used[io->id] & 0x80 ) != 0 + : !io->hidden; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_help_options +( + const InfoUI_t * iu, // valid pointer + FILE * f, // valid output stream + bool filter_opt, // true: filter options by 'iu->opt_used' + int indent, // indent of output + uint beg_index, // index of first option to print + uint end_index, // index of last option to print +1 + ccp title // NULL or title text +) +{ + DASSERT(iu); + + if ( end_index > iu->n_opt_total ) + end_index = iu->n_opt_total; + if ( beg_index >= end_index || !f ) + return; + + if (title) + { + const bool is_col = IsFileColorized(f); + fprintf(f,"%*s%s%s:%s\n\n", + indent,"", + GetTextMode(is_col,TTM_COL_HEADING), title, + GetTextMode(is_col,TTM_RESET) ); + } + + const int fw = GetTermWidth(80,40) - 1; + + const InfoOption_t *io_beg = iu->opt_info + beg_index; + const InfoOption_t *io_end = iu->opt_info + end_index; + const InfoOption_t *io; + + // calculate the base option field width + int base_opt_fw = 0; + for ( io = io_beg; io < io_end; io++ ) + { + if ( io->long_name && is_opt_selected(iu,io,filter_opt) ) + { + int len = strlen(io->long_name); + if (io->param) + len += 1 + strlen(io->param); + if ( base_opt_fw < len ) + base_opt_fw = len; + } + } + + const int max_fw = 2 + (fw+5)/8; + if ( base_opt_fw > max_fw ) + base_opt_fw = max_fw; + + // calculate the option field width + int opt_fw = 0; + for ( io = io_beg; io < io_end; io++ ) + { + if ( io->long_name && is_opt_selected(iu,io,filter_opt) ) + { + int len = strlen(io->long_name); + if (io->param) + len += 1 + strlen(io->param); + if ( len <= base_opt_fw && opt_fw < len ) + opt_fw = len; + } + } + opt_fw += indent + 9; + + for ( io = io_beg; io < io_end; io++ ) + { + if ( is_opt_selected(iu,io,filter_opt) ) + { + if (io->separator) + fputc('\n',f); + print_single_option(f,indent,io,opt_fw,fw); + } + } + fputc('\n',f); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintHelpOptions +( + const InfoUI_t * iu, // valid pointer + FILE * f, // valid output stream + int indent // indent of output +) +{ + DASSERT(iu); + + bool filter_opt = false; + uint i; + for ( i = 1; i < iu->n_opt_total; i++ ) + { + const u8 used = iu->opt_used[i]; + if ( used & 0x80 && ( used != 0x81 || !iu->opt_info[i].ignore ) ) + { + filter_opt = true; + break; + } + } + + fputc('\n',f); + if (filter_opt) + { + print_help_options( iu, f, true, indent, + 1, iu->n_opt_total, + "Selected options with common description" ); + } + else + { + print_help_options( iu, f, false, indent, + iu->n_opt_specific+1, iu->n_opt_total, + "Global options" ); + + print_help_options( iu, f, false, indent, + 1, iu->n_opt_specific, + "Command specific options with common description" ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintHelpCommands +( + const InfoUI_t * iu, // valid pointer + FILE * f, // valid output stream + int indent, // indent of output + ccp help_cmd // NULL or name of help command +) +{ + DASSERT(iu); + + if ( !iu->n_cmd ) + return; + + uint col_len; + ccp col_head, col_cmd, col_reset; + if ( IsFileColorized(f)) + { + col_head = GetTextMode(1,TTM_COL_HEADING); + col_cmd = GetTextMode(1,TTM_COL_CMD); + col_reset = TermTextModeReset; + col_len = strlen(col_cmd) + strlen(col_reset); + } + else + { + col_head = col_cmd = col_reset = ""; + col_len = 0; + } + + fprintf(f,"\n%*s%sCommands:%s\n\n", indent,"", col_head, col_reset ); + const int fw = GetTermWidth(80,40) - 1; + + int fw1 = 0, fw2 = 0; + const InfoCommand_t * ic; + + for ( ic = iu->cmd_info; ic->name1; ic++ ) + if (!ic->hidden) + { + + const int len = strlen(ic->name1); + if ( fw1 < len ) + fw1 = len; + if ( ic->name2 ) + { + const int len = strlen(ic->name2); + if ( fw2 < len ) + fw2 = len; + } + } + const int fw12 = fw1 + ( fw2 ? fw2 + 3 : 0 ); + + for ( ic = iu->cmd_info+1; ic->name1; ic++ ) + if (!ic->hidden) + { + if (ic->separator) + fputc('\n',f); + int len; + if ( ic->name2 ) + len = fprintf(f,"%*s %s%-*s%s | %s%-*s%s : ", + indent, "", + col_cmd, fw1, ic->name1, col_reset, + col_cmd, fw2, ic->name2, col_reset ) + - 2*col_len; + else + len = fprintf(f,"%*s %s%-*s%s : ", + indent, "", + col_cmd, fw12, ic->name1, col_reset ) + - col_len; + PutLines(f,indent+len,fw,len,0,ic->help,0); + } + + if (help_cmd) + fprintf(f, + "\n%*sType '%s %s command' to get command specific help.\n\n", + indent, "", iu->tool_name, help_cmd ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print help /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintHelp +( + const InfoUI_t * iu, // valid pointer + FILE * f, // valid output stream + int indent, // indent of output + ccp help_cmd, // NULL or name of help command + ccp info, // NULL or additional text + ccp base_uri, // NULL or base URI for a external help link + ccp first_param // NULL or first param of argv[] +) +{ + ASSERT(iu); + + int cmd = 0; + if ( iu->n_cmd && first_param && *first_param ) + { + int cmd_stat; + const KeywordTab_t * ct = ScanKeyword(&cmd_stat,first_param,iu->cmd_tab); + if (ct) + cmd = ct->id; + else + { + char buf[100]; + StringCat2S(buf,sizeof(buf),"+",first_param); + ct = ScanKeyword(&cmd_stat,buf,iu->cmd_tab); + if (ct) + cmd = ct->id; + else + { + char key_buf[12]; + char *dest = key_buf; + char *end = key_buf + sizeof(key_buf) - 1; + while ( *first_param && dest < end ) + *dest++ = toupper((int)*first_param++); + *dest = 0; + const int key_len = dest - key_buf; + if ( key_len >= 3 ) + { + if (!memcmp(key_buf,"OPTIONS",key_len) ) + { + PrintHelpOptions(iu,f,indent); + return; + } + + if ( !memcmp(key_buf,"COMMANDS",key_len) + || !memcmp(key_buf,"CMD",key_len) ) + { + PrintHelpCommands(iu,f,indent,help_cmd); + return; + } + } + } + } + } + + PrintHelpCmd(iu,f,indent,cmd,help_cmd,info,base_uri); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintHelpCmd +( + const InfoUI_t * iu, // valid pointer + FILE * f, // valid output stream + int indent, // indent of output + int cmd, // index of command + ccp help_cmd, // NULL or name of help command + ccp info, // NULL or poiner to additional text + ccp base_uri // NULL or base URI for a external help link +) +{ + ASSERT(iu); + if (!f) + return; + indent = NormalizeIndent(indent); + + if ( cmd < 0 || cmd >= iu->n_cmd ) + cmd = 0; + const InfoCommand_t * ic = iu->cmd_info + cmd; + + const int fw = GetTermWidth(80,40) - 1; + + uint col_len; + ccp col_head, col_reset; + if (IsFileColorized(f)) + { + col_head = GetTextMode(1,TTM_COL_HEADING); + col_reset = GetTextMode(1,TTM_RESET); + col_len = strlen(col_head) + strlen(col_reset); + } + else + { + col_head = col_reset = ""; + col_len = 0; + } + + char iobuf[10000]; + + if (!cmd) + snprintf(iobuf,sizeof(iobuf),"%s",iu->tool_name ); + else if (ic->name2) + snprintf(iobuf,sizeof(iobuf),"%s %s|%s",iu->tool_name,ic->name1,ic->name2); + else + snprintf(iobuf,sizeof(iobuf),"%s %s",iu->tool_name,ic->name1); + + int len = fprintf(f,"\n%*s%s : ",indent,"",iobuf) - 1; + PutLines(f,indent+len,fw,len,0, ic->xhelp ? ic->xhelp : ic->help, 0); + + len = fprintf(f,"\n%*s%sSyntax:%s ", indent, "", col_head, col_reset ) - 1 - col_len; + PutLines(f,indent+len,fw,len,0,ic->syntax,0); + fputc('\n',f); + + if (!cmd) + PrintHelpCommands(iu,f,indent,help_cmd); + + if ( ic->n_opt ) + { + fprintf(f,"%*s%s%sptions:%s\n\n", + indent,"", col_head, + !cmd && iu->n_cmd ? "Global o" : "O", + col_reset ); + + const InfoOption_t ** iop; + + // calculate the base option field width + int base_opt_fw = 0; + for ( iop = ic->opt; *iop; iop++ ) + { + const InfoOption_t * io = *iop; + if (io->long_name) + { + int len = strlen(io->long_name); + if (io->param) + len += 1 + strlen(io->param); + if ( base_opt_fw < len ) + base_opt_fw = len; + } + } + + const int max_fw = 2 + (fw+5)/8; + if ( base_opt_fw > max_fw ) + base_opt_fw = max_fw; + + // calculate the option field width + int opt_fw = 0; + for ( iop = ic->opt; *iop; iop++ ) + { + const InfoOption_t * io = *iop; + if (io->long_name) + { + int len = strlen(io->long_name); + if (io->param) + len += 1 + strlen(io->param); + if ( len <= base_opt_fw && opt_fw < len ) + opt_fw = len; + } + } + opt_fw += indent + 9; + + for ( iop = ic->opt; *iop; iop++ ) + print_single_option(f,indent,*iop,opt_fw,fw); + fputc('\n',f); + } + + if (info) + fputs(info,f); + + if ( base_uri && *base_uri ) + { + if (!cmd) + fprintf(f,"%*sMore help is available at %s%s\n\n", + indent, "", base_uri, iu->tool_name ); + else if (!ic->hidden) + { + char *dest = iobuf; + ccp src; + for ( src = ic->name1; *src; src++ ) + *dest++ = *src == '_' ? '-' : tolower((int)*src); + *dest = 0; + + fprintf(f,"%*sMore help is available at %scmd/%s/%s\n\n", + indent, "", base_uri, iu->tool_name, iobuf ); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-ui.h b/project/dclib/dclib-ui.h new file mode 100644 index 0000000..1b45d43 --- /dev/null +++ b/project/dclib/dclib-ui.h @@ -0,0 +1,215 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_UI_H +#define DCLIB_UI_H + +#include "dclib-basics.h" +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[OptionIndex_t]] // max size of OptionIndex[] + +#ifndef UIOPT_INDEX_SIZE + #define UIOPT_INDEX_SIZE 0x100 +#endif + +#undef UIOPT_INDEX_FW + +#if UIOPT_INDEX_SIZE <= 0x100 + #define UIOPT_INDEX_FW 2 + typedef u8 OptionIndex_t; +#else + #define UIOPT_INDEX_FW 3 + typedef u16 OptionIndex_t; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +enum // some const +{ + UIOPT_USED_MASK = 0x7f, // mask to calculate usage count + UIOPT_LONG_BASE = 0x80, // first index for "only long options" +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[InfoOption_t]] + +typedef struct InfoOption_t +{ + int id; // id of the option + bool optional_parm; // true: option has optional parameters + bool hidden; // true: option is hidden + bool deprecated; // true: option is deprecated + bool ignore; // true: ignore on 'HELP OPTIONS' + bool separator; // true: print a separator above + char short_name; // short option name + ccp long_name; // the main long option name + ccp param; // parameter name + ccp help; // help info + +} InfoOption_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[InfoCommand_t]] + +typedef struct InfoCommand_t +{ + int id; // id of the command + bool hidden; // true: command is hidden + bool deprecated; // true: command is deprecated + bool separator; // true: print a separator above + ccp name1; // main name + ccp name2; // NULL or alternative name + ccp syntax; // NULL or syntax info + ccp help; // help text + ccp xhelp; // NULL or a extended help text + int n_opt; // number of options == elements of 'opt' + const InfoOption_t ** opt; // field with option info + u8 * opt_allowed; // field with OPT__N_SPECIFIC elements + // 0: option permitted, 1: option allowed + +} InfoCommand_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[InfoUI_t]] + +typedef struct InfoUI_t +{ + ccp tool_name; // name of the tool + + //----- commands ----- + + int n_cmd; // == CMD__N + const KeywordTab_t * cmd_tab; // NULL or pointer to command table + const InfoCommand_t * cmd_info; // pointer to 'CommandInfo[]' + + //----- options ----- + + int n_opt_specific; // == OPT__N_SPECIFIC + int n_opt_total; // == OPT__N_TOTAL + const InfoOption_t * opt_info; // pointer to 'OptionInfo[]' + u8 * opt_used; // pointer to 'OptionUsed[]' + const OptionIndex_t * opt_index; // pointer to 'OptionIndex[]' + ccp opt_short; // pointer to 'OptionShort[]' + const struct option * opt_long; // pointer to 'OptionLong[]' + +} InfoUI_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Interface /////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError RegisterOptionByIndex +( + const InfoUI_t * iu, // valid pointer + int opt_index, // index of option (OPT_*) + int level, // the level of registration + bool is_env // true: register environment pre setting +); + +enumError RegisterOptionByName +( + const InfoUI_t * iu, // valid pointer + int opt_name, // short name of GO_* value of option + int level, // the level of registration + bool is_env // true: register environment pre setting +); + +enumError VerifySpecificOptions ( const InfoUI_t * iu, const KeywordTab_t * cmd ); +int GetOptionCount ( const InfoUI_t * iu, int option ); +void DumpUsedOptions ( const InfoUI_t * iu, FILE * f, int indent ); +void WarnDepractedOptions ( const InfoUI_t * iu ); + +typedef enumError (*check_opt_func) ( int argc, char ** argv, bool mode ); +enumError CheckEnvOptions ( ccp varname, check_opt_func ); + +/////////////////////////////////////////////////////////////////////////////// + +void DumpText +( + FILE * f, // output file, if NULL: 'pbuf' must be set + char * pbuf, // output buffer, ignored if 'f' not NULL + char * pbuf_end, // end of output buffer + ccp text, // source text + int text_len, // >=0: length of text, <0: use strlen(text) + bool is_makedoc, // true: write makedoc format + ccp end // write this text at the end of all +); + +/////////////////////////////////////////////////////////////////////////////// + +void PrintHelp +( + const InfoUI_t * iu, // valid pointer + FILE * f, // valid output stream + int indent, // indent of output + ccp help_cmd, // NULL or name of help command + ccp info, // NULL or additional text + ccp base_uri, // NULL or base URI for a external help link + ccp first_param // NULL or first param of argv[] +); + +void PrintHelpCmd +( + const InfoUI_t * iu, // valid pointer + FILE * f, // valid output stream + int indent, // indent of output + int cmd, // index of command + ccp help_cmd, // NULL or name of help command + ccp info, // NULL or poiner to additional text + ccp base_uri // NULL or base URI for a external help link +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_UI_H + diff --git a/project/dclib/dclib-utf8.c b/project/dclib/dclib-utf8.c new file mode 100644 index 0000000..af11a68 --- /dev/null +++ b/project/dclib/dclib-utf8.c @@ -0,0 +1,1910 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include + +#include "dclib-basics.h" +#include "dclib-debug.h" +#include "dclib-utf8.h" + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define u0 DC_UTF8_ILLEGAL +#define u1 DC_UTF8_1CHAR +#define u2 DC_UTF8_2CHAR +#define u3 DC_UTF8_3CHAR +#define u4 DC_UTF8_4CHAR +#define uc DC_UTF8_CONT_ANY + +const unsigned short TableUTF8Mode[256] = +{ + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0000xxxx + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0001xxxx + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0010xxxx + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0011xxxx + + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0100xxxx + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0101xxxx + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0110xxxx + u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, u1,u1,u1,u1, // 0111xxxx + + uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, // 1000xxxx + uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, // 1001xxxx + uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, // 1010xxxx + uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, uc,uc,uc,uc, // 1011xxxx + + u2,u2,u2,u2, u2,u2,u2,u2, u2,u2,u2,u2, u2,u2,u2,u2, // 1100xxxx + u2,u2,u2,u2, u2,u2,u2,u2, u2,u2,u2,u2, u2,u2,u2,u2, // 1101xxxx + u3,u3,u3,u3, u3,u3,u3,u3, u3,u3,u3,u3, u3,u3,u3,u3, // 1110xxxx + u4,u4,u4,u4, u4,u4,u4,u4, u0,u0,u0,u0, u0,u0,u0,u0 // 1111xxxx +}; + +#undef u0 +#undef u1 +#undef u2 +#undef u3 +#undef u4 +#undef uc + +/////////////////////////////////////////////////////////////////////////////// + +#define CheckUTF8Mode(ch) ((dcUTF8Mode)TableUTF8Mode[(unsigned char)(ch)]) + +/////////////////////////////////////////////////////////////////////////////// + +int GetUTF8CharLength ( u32 code ) +{ + // returns the length of the char 'code' + // 0 : illegal code + + return code <= DCLIB_UNICODE_MAX_UTF8_1 + ? 1 + : code <= DCLIB_UNICODE_MAX_UTF8_2 + ? 2 + : code <= DCLIB_UNICODE_MAX_UTF8_3 + ? 3 + : code <= DCLIB_UNICODE_MAX_UTF8_4 + ? 4 + : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * NextUTF8Char ( ccp ptr ) +{ + // go to next next UTF8 character + + switch (CheckUTF8Mode(*ptr)) + { + case DC_UTF8_1CHAR: + return *ptr ? (char*)ptr+1 : (char*)ptr; + + case DC_UTF8_4CHAR: + if ( CheckUTF8Mode(*++ptr) != DC_UTF8_CONT_ANY ) + break; + + // fall through + + case DC_UTF8_3CHAR: + case DC_UTF8_CONT_ANY: + if ( CheckUTF8Mode(*++ptr) != DC_UTF8_CONT_ANY ) + break; + + // fall through + + case DC_UTF8_2CHAR: + if ( CheckUTF8Mode(*++ptr) != DC_UTF8_CONT_ANY ) + break; + + // fall through + + default: // DC_UTF8_ILLEGAL + return (char*)ptr+1; + } + return (char*)ptr; +} + +//----------------------------------------------------------------------------- + +char * NextUTF8CharE( ccp ptr, ccp end ) +{ + // go to next next UTF8 character + + if ( ptr >= end ) + return (char*)end; + + switch (CheckUTF8Mode(*ptr)) + { + case DC_UTF8_1CHAR: + return (char*)ptr+1; + + case DC_UTF8_4CHAR: + if ( ++ptr >= end || CheckUTF8Mode(*ptr) != DC_UTF8_CONT_ANY ) + break; + + // fall through + + case DC_UTF8_3CHAR: + case DC_UTF8_CONT_ANY: + if ( ++ptr >= end || CheckUTF8Mode(*ptr) != DC_UTF8_CONT_ANY ) + break; + + // fall through + + case DC_UTF8_2CHAR: + if ( ++ptr >= end || CheckUTF8Mode(*ptr) != DC_UTF8_CONT_ANY ) + break; + + // fall through + + default: // DC_UTF8_ILLEGAL + return (char*)ptr+1; + } + return (char*)ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrevUTF8Char ( ccp str ) +{ + // go to the previous UTC character + + // kleine Optimierung für den Standardfall + if ( (uchar)str[-1] < (uint)DCLIB_UNICODE_MAX_UTF8_1 ) + return (char*)str-1; + + ccp ptr = str; + int n = 0; + dcUTF8Mode mode = CheckUTF8Mode(*--ptr); + while ( n < 3 && mode == DC_UTF8_CONT_ANY ) + { + n++; + mode = CheckUTF8Mode(*--ptr); + } + + switch (mode) + { + case DC_UTF8_1CHAR: + return (char*)( n<1 ? ptr : ptr+1 ); + + case DC_UTF8_2CHAR: + return (char*)( n<2 ? ptr : ptr+2 ); + + case DC_UTF8_3CHAR: + return (char*)( n<3 ? ptr : ptr+3 ); + + case DC_UTF8_4CHAR: + return (char*)( n<4 ? ptr : ptr+4 ); + + case DC_UTF8_CONT_ANY: + return (char*)str-3; + + default: + return (char*)str-1; + } +} + +//----------------------------------------------------------------------------- + +char * PrevUTF8CharB ( ccp str, ccp begin ) +{ + // go to the previous UTC character + + ccp ptr = str; + if ( ptr <= begin ) + return (char*)begin; + + // kleine Optimierung für den Standardfall + if ( (uchar)ptr[-1] < (uint)DCLIB_UNICODE_MAX_UTF8_1 ) + return (char*)ptr-1; + + int n = 0; + dcUTF8Mode mode = CheckUTF8Mode(*--ptr); + while ( ptr > begin && n < 3 && mode == DC_UTF8_CONT_ANY ) + { + n++; + mode = CheckUTF8Mode(*--ptr); + } + switch (mode) + { + case DC_UTF8_1CHAR: + return (char*)( n<1 ? ptr : ptr+1 ); + + case DC_UTF8_2CHAR: + return (char*)( n<2 ? ptr : ptr+2 ); + + case DC_UTF8_3CHAR: + return (char*)( n<3 ? ptr : ptr+3 ); + + case DC_UTF8_4CHAR: + return (char*)( n<4 ? ptr : ptr+4 ); + + case DC_UTF8_CONT_ANY: + return (char*)str-3; + + default: + return (char*)str-1; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 GetUTF8Char ( ccp str ) +{ + // fast scan of a UTF8 char -> ignore errors in continuation bytes + + const u32 result = (uchar)*str; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_1CHAR: + return result; + + case DC_UTF8_2CHAR: + return ( result & 0x1f ) << 6 + | ( str[1] & 0x3f ); + + case DC_UTF8_3CHAR: + return ( result & 0x0f ) << 12 + | ( str[1] & 0x3f ) << 6 + | ( str[2] & 0x3f ); + + case DC_UTF8_4CHAR: + return ( result & 0x07 ) << 18 + | ( str[1] & 0x3f ) << 12 + | ( str[2] & 0x3f ) << 6 + | ( str[3] & 0x3f ); + + case DC_UTF8_CONT_ANY: + return ( result & 0x3f ) << 12 + | ( str[1] & 0x3f ) << 6 + | ( str[2] & 0x3f ); + + default: // DC_UTF8_ILLEGAL + return result & 0x7f | S32_MIN; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 ScanUTF8Char ( ccp * p_str ) +{ + // scan a UTF8 char. Set bit LONG_MAX if an error seen. + // *str is set to next next char if on no error. + + int n_cont; + ccp ptr = *p_str; + u32 result = (uchar)*ptr++; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_1CHAR: + if (result) + (*p_str)++; + return result; + + case DC_UTF8_2CHAR: + result &= 0x1f; + n_cont = 1; + break; + + case DC_UTF8_3CHAR: + result &= 0x0f; + n_cont = 2; + break; + + case DC_UTF8_4CHAR: + result &= 0x07; + n_cont = 3; + break; + + case DC_UTF8_CONT_ANY: + if ( CheckUTF8Mode((uchar)*ptr) && CheckUTF8Mode((uchar)*++ptr) ) + ptr++; + *p_str = ptr; + return S32_MIN; + + default: // DC_UTF8_ILLEGAL + (*p_str)++; + return result & 0x7f | S32_MIN; + } + + while ( n_cont-- > 0 ) + { + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + result = result << 6 | *ptr++ & 0x3f; + else + { + result |= S32_MIN; + break; + } + } + + *p_str = ptr; + return result; +} + +//----------------------------------------------------------------------------- + +u32 ScanUTF8CharE ( ccp * p_str, ccp end ) +{ + // scan a UTF8 char. Set bit S32_MIN if an error seen + // *str is set to next next char if on no error. + + ccp ptr = *p_str; + if ( ptr >= end ) + return S32_MIN; + + int n_cont; + u32 result = (uchar)*ptr++; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_1CHAR: + (*p_str)++; + return result; + + case DC_UTF8_2CHAR: + if ( ptr >= end ) + return S32_MIN; + result &= 0x1f; + n_cont = 1; + break; + + case DC_UTF8_3CHAR: + if ( ptr+1 >= end ) + return S32_MIN; + result &= 0x0f; + n_cont = 2; + break; + + case DC_UTF8_4CHAR: + if ( ptr+2 >= end ) + return S32_MIN; + result &= 0x07; + n_cont = 3; + break; + + case DC_UTF8_CONT_ANY: + if ( ptr < end && CheckUTF8Mode((uchar)*ptr) + && ++ptr < end && CheckUTF8Mode((uchar)*ptr) ) + ptr++; + *p_str = ptr; + return S32_MIN; + + default: // DC_UTF8_ILLEGAL + (*p_str)++; + return result & 0x7f | S32_MIN; + } + + while ( n_cont-- > 0 ) + { + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + result = result << 6 | *ptr++ & 0x3f; + else + { + result |= S32_MIN; + break; + } + } + + *p_str = ptr; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 ScanUTF8CharInc ( ccp * p_str ) +{ + // scan a UTF8 char. Set bit LONG_MAX if an error seen + // *str is set to next next char => it is incremetned always! + + int n_cont; + ccp ptr = *p_str; + u32 result = (uchar)*ptr++; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_1CHAR: + (*p_str)++; + return result; + + case DC_UTF8_2CHAR: + result &= 0x1f; + n_cont = 1; + break; + + case DC_UTF8_3CHAR: + result &= 0x0f; + n_cont = 2; + break; + + case DC_UTF8_4CHAR: + result &= 0x07; + n_cont = 3; + break; + + case DC_UTF8_CONT_ANY: + if ( CheckUTF8Mode((uchar)*ptr) && CheckUTF8Mode((uchar)*++ptr) ) + ptr++; + *p_str = ptr; + return S32_MIN; + + default: // DC_UTF8_ILLEGAL + (*p_str)++; + return result & 0x7f | S32_MIN; + } + + while ( n_cont-- > 0 ) + { + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + result = result << 6 | *ptr++ & 0x3f; + else + { + result |= S32_MIN; + break; + } + } + + *p_str = ptr; + return result; +} + +//----------------------------------------------------------------------------- + +u32 ScanUTF8CharIncE ( ccp * p_str, ccp end ) +{ + // scan a UTF8 char. Set bit LONG_MAX if an error seen + // *str is set to next next char => it is incremetned always! + + ccp ptr = *p_str; + if ( ptr >= end ) + { + (*p_str)++; + return S32_MIN; + } + + int n_cont = 0; + u32 result = (uchar)*ptr++; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_1CHAR: + (*p_str)++; + return result; + + case DC_UTF8_2CHAR: + result &= 0x1f; + n_cont = 1; + break; + + case DC_UTF8_3CHAR: + result &= 0x0f; + n_cont = 2; + break; + + case DC_UTF8_4CHAR: + result &= 0x07; + n_cont = 3; + break; + + case DC_UTF8_CONT_ANY: + if ( ptr < end && CheckUTF8Mode((uchar)*ptr) + && ++ptr < end && CheckUTF8Mode((uchar)*ptr) ) + ptr++; + *p_str = ptr; + return S32_MIN; + + default: // DC_UTF8_ILLEGAL + (*p_str)++; + return result & 0x7f | S32_MIN; + } + + while ( n_cont-- > 0 ) + { + if ( ptr < end && CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + result = result << 6 | *ptr++ & 0x3f; + else + { + result |= S32_MIN; + break; + } + } + + *p_str = ptr; + return result; +} + +// //////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u32 GetUTF8AnsiChar ( ccp str ) +{ + // scan an UTF8 char. On error scan an ANSI char! + + const uchar * ptr = (const uchar *)str; + u32 result = *ptr++; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_2CHAR: + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + { + return ( result & 0x1f ) << 6 | *ptr & 0x3f; + } + return result; + + case DC_UTF8_3CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY ) + { + return (( result & 0x0f ) << 6 + | ptr[0] & 0x3f ) << 6 + | ptr[1] & 0x3f; + } + return result; + + case DC_UTF8_4CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[2]) == DC_UTF8_CONT_ANY ) + { + return ((( result & 0x07 ) << 6 + | ptr[0] & 0x3f ) << 6 + | ptr[1] & 0x3f ) << 6 + | ptr[2] & 0x3f; + } + return result; + + default: + return result; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 ScanUTF8AnsiChar ( ccp * p_str ) +{ + // scan an UTF8 char. On error scan an ANSI char! + + const uchar * ptr = (const uchar *)*p_str; + u32 result = *ptr++; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_2CHAR: + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + { + result = ( result & 0x1f ) << 6 | *ptr++ & 0x3f; + } + break; + + case DC_UTF8_3CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY ) + { + result = (( result & 0x0f ) << 6 + | ptr[0] & 0x3f ) << 6 + | ptr[1] & 0x3f; + ptr += 2; + } + break; + + case DC_UTF8_4CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[2]) == DC_UTF8_CONT_ANY ) + { + result = ((( result & 0x07 ) << 6 + | ptr[0] & 0x3f ) << 6 + | ptr[1] & 0x3f ) << 6 + | ptr[2] & 0x3f; + ptr += 3; + } + break; + + default: + break; + } + *p_str = (ccp)ptr; + return result; +} + +//----------------------------------------------------------------------------- + +u32 ScanUTF8AnsiCharE ( ccp * p_str, ccp end ) +{ + // scan an UTF8 char. On error scan an ANSI char! + + const uchar * ptr = (const uchar *)*p_str; + if ( (ccp)ptr >= end ) + { + (*p_str)++; + return S32_MIN; + } + u32 result = *ptr++; + switch (CheckUTF8Mode(result)) + { + case DC_UTF8_2CHAR: + if ( (ccp)ptr < end + && CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + { + result = ( result & 0x1f ) << 6 | *ptr++ & 0x3f; + } + break; + + case DC_UTF8_3CHAR: + if ( (ccp)ptr+1 < end + && CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY ) + { + result = (( result & 0x0f ) << 6 + | ptr[0] & 0x3f ) << 6 + | ptr[1] & 0x3f; + ptr += 2; + } + break; + + case DC_UTF8_4CHAR: + if ( (ccp)ptr+2 < end + && CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[2]) == DC_UTF8_CONT_ANY ) + { + result = ((( result & 0x07 ) << 6 + | ptr[0] & 0x3f ) << 6 + | ptr[1] & 0x3f ) << 6 + | ptr[2] & 0x3f; + ptr += 3; + } + break; + + default: + break; + } + *p_str = (ccp)ptr; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanUTF8Length ( ccp str, ccp end ) +{ + int count = 0; + ccp ptr = str; + if (end) + { + while ( ptr < end ) + { + const char ch = *ptr++; + switch (CheckUTF8Mode(ch)) + { + case DC_UTF8_2CHAR: + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + ptr++; + break; + + case DC_UTF8_3CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY ) + ptr += 2; + break; + + case DC_UTF8_4CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[2]) == DC_UTF8_CONT_ANY ) + ptr += 3; + break; + + default: + break; + } + count++; + } + } + else + { + for(;;) + { + const char ch = *ptr++; + if (!ch) + break; + switch (CheckUTF8Mode(ch)) + { + case DC_UTF8_2CHAR: + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + ptr++; + break; + + case DC_UTF8_3CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY ) + ptr += 2; + break; + + case DC_UTF8_4CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[2]) == DC_UTF8_CONT_ANY ) + ptr += 3; + break; + + default: + break; + } + count++; + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int CalcUTF8PrintFW ( ccp str, ccp end, uint wanted_fw ) +{ + const uint bytecount = end ? end - str : strlen(str); + const uint utf8len = ScanUTF8Length(str,end); + return utf8len < wanted_fw + ? wanted_fw - utf8len + bytecount + : bytecount; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintUTF8Char ( char * buf, u32 code ) +{ + code &= DCLIB_UNICODE_CODE_MASK; + + if ( code <= DCLIB_UNICODE_MAX_UTF8_1 ) + { + *buf++ = code; + } + else if ( code <= DCLIB_UNICODE_MAX_UTF8_2 ) + { + *buf++ = code >> 6 | 0xc0; + *buf++ = code & 0x3f | 0x80; + } + else if ( code <= DCLIB_UNICODE_MAX_UTF8_3 ) + { + *buf++ = code >> 12 | 0xe0; + *buf++ = code >> 6 & 0x3f | 0x80; + *buf++ = code & 0x3f | 0x80; + } + else + { + *buf++ = code >> 18 & 0x07 | 0xf0; + *buf++ = code >> 12 & 0x3f | 0x80; + *buf++ = code >> 6 & 0x3f | 0x80; + *buf++ = code & 0x3f | 0x80; + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintUTF8CharToCircBuf ( u32 code ) +{ + char *buf; + code &= DCLIB_UNICODE_CODE_MASK; + + if ( code <= DCLIB_UNICODE_MAX_UTF8_1 ) + { + buf = GetCircBuf(2); + buf[0] = code; + buf[1] = 0; + } + else if ( code <= DCLIB_UNICODE_MAX_UTF8_2 ) + { + buf = GetCircBuf(3); + buf[0] = code >> 6 | 0xc0; + buf[1] = code & 0x3f | 0x80; + buf[2] = 0; + } + else if ( code <= DCLIB_UNICODE_MAX_UTF8_3 ) + { + buf = GetCircBuf(4); + buf[0] = code >> 12 | 0xe0; + buf[1] = code >> 6 & 0x3f | 0x80; + buf[2] = code & 0x3f | 0x80; + buf[3] = 0; + } + else + { + buf = GetCircBuf(5); + buf[0] = code >> 18 & 0x07 | 0xf0; + buf[1] = code >> 12 & 0x3f | 0x80; + buf[2] = code >> 6 & 0x3f | 0x80; + buf[3] = code & 0x3f | 0x80; + buf[4] = 0; + } + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// this table is directly generated from +// http://unicode.org/Public/UNIDATA/UnicodeData.txt + +const dcUnicodeTripel TableUnicodeDecomp[] = +{ + { 0x0000c0, 0x000041,0x000300 }, + { 0x0000c1, 0x000041,0x000301 }, + { 0x0000c2, 0x000041,0x000302 }, + { 0x0000c3, 0x000041,0x000303 }, + { 0x0000c4, 0x000041,0x000308 }, + { 0x0000c5, 0x000041,0x00030a }, + { 0x0000c7, 0x000043,0x000327 }, + { 0x0000c8, 0x000045,0x000300 }, + { 0x0000c9, 0x000045,0x000301 }, + { 0x0000ca, 0x000045,0x000302 }, + { 0x0000cb, 0x000045,0x000308 }, + { 0x0000cc, 0x000049,0x000300 }, + { 0x0000cd, 0x000049,0x000301 }, + { 0x0000ce, 0x000049,0x000302 }, + { 0x0000cf, 0x000049,0x000308 }, + { 0x0000d1, 0x00004e,0x000303 }, + { 0x0000d2, 0x00004f,0x000300 }, + { 0x0000d3, 0x00004f,0x000301 }, + { 0x0000d4, 0x00004f,0x000302 }, + { 0x0000d5, 0x00004f,0x000303 }, + { 0x0000d6, 0x00004f,0x000308 }, + { 0x0000d9, 0x000055,0x000300 }, + { 0x0000da, 0x000055,0x000301 }, + { 0x0000db, 0x000055,0x000302 }, + { 0x0000dc, 0x000055,0x000308 }, + { 0x0000dd, 0x000059,0x000301 }, + { 0x0000e0, 0x000061,0x000300 }, + { 0x0000e1, 0x000061,0x000301 }, + { 0x0000e2, 0x000061,0x000302 }, + { 0x0000e3, 0x000061,0x000303 }, + { 0x0000e4, 0x000061,0x000308 }, + { 0x0000e5, 0x000061,0x00030a }, + { 0x0000e7, 0x000063,0x000327 }, + { 0x0000e8, 0x000065,0x000300 }, + { 0x0000e9, 0x000065,0x000301 }, + { 0x0000ea, 0x000065,0x000302 }, + { 0x0000eb, 0x000065,0x000308 }, + { 0x0000ec, 0x000069,0x000300 }, + { 0x0000ed, 0x000069,0x000301 }, + { 0x0000ee, 0x000069,0x000302 }, + { 0x0000ef, 0x000069,0x000308 }, + { 0x0000f1, 0x00006e,0x000303 }, + { 0x0000f2, 0x00006f,0x000300 }, + { 0x0000f3, 0x00006f,0x000301 }, + { 0x0000f4, 0x00006f,0x000302 }, + { 0x0000f5, 0x00006f,0x000303 }, + { 0x0000f6, 0x00006f,0x000308 }, + { 0x0000f9, 0x000075,0x000300 }, + { 0x0000fa, 0x000075,0x000301 }, + { 0x0000fb, 0x000075,0x000302 }, + { 0x0000fc, 0x000075,0x000308 }, + { 0x0000fd, 0x000079,0x000301 }, + { 0x0000ff, 0x000079,0x000308 }, + { 0x000100, 0x000041,0x000304 }, + { 0x000101, 0x000061,0x000304 }, + { 0x000102, 0x000041,0x000306 }, + { 0x000103, 0x000061,0x000306 }, + { 0x000104, 0x000041,0x000328 }, + { 0x000105, 0x000061,0x000328 }, + { 0x000106, 0x000043,0x000301 }, + { 0x000107, 0x000063,0x000301 }, + { 0x000108, 0x000043,0x000302 }, + { 0x000109, 0x000063,0x000302 }, + { 0x00010a, 0x000043,0x000307 }, + { 0x00010b, 0x000063,0x000307 }, + { 0x00010c, 0x000043,0x00030c }, + { 0x00010d, 0x000063,0x00030c }, + { 0x00010e, 0x000044,0x00030c }, + { 0x00010f, 0x000064,0x00030c }, + { 0x000112, 0x000045,0x000304 }, + { 0x000113, 0x000065,0x000304 }, + { 0x000114, 0x000045,0x000306 }, + { 0x000115, 0x000065,0x000306 }, + { 0x000116, 0x000045,0x000307 }, + { 0x000117, 0x000065,0x000307 }, + { 0x000118, 0x000045,0x000328 }, + { 0x000119, 0x000065,0x000328 }, + { 0x00011a, 0x000045,0x00030c }, + { 0x00011b, 0x000065,0x00030c }, + { 0x00011c, 0x000047,0x000302 }, + { 0x00011d, 0x000067,0x000302 }, + { 0x00011e, 0x000047,0x000306 }, + { 0x00011f, 0x000067,0x000306 }, + { 0x000120, 0x000047,0x000307 }, + { 0x000121, 0x000067,0x000307 }, + { 0x000122, 0x000047,0x000327 }, + { 0x000123, 0x000067,0x000327 }, + { 0x000124, 0x000048,0x000302 }, + { 0x000125, 0x000068,0x000302 }, + { 0x000128, 0x000049,0x000303 }, + { 0x000129, 0x000069,0x000303 }, + { 0x00012a, 0x000049,0x000304 }, + { 0x00012b, 0x000069,0x000304 }, + { 0x00012c, 0x000049,0x000306 }, + { 0x00012d, 0x000069,0x000306 }, + { 0x00012e, 0x000049,0x000328 }, + { 0x00012f, 0x000069,0x000328 }, + { 0x000130, 0x000049,0x000307 }, + { 0x000134, 0x00004a,0x000302 }, + { 0x000135, 0x00006a,0x000302 }, + { 0x000136, 0x00004b,0x000327 }, + { 0x000137, 0x00006b,0x000327 }, + { 0x000139, 0x00004c,0x000301 }, + { 0x00013a, 0x00006c,0x000301 }, + { 0x00013b, 0x00004c,0x000327 }, + { 0x00013c, 0x00006c,0x000327 }, + { 0x00013d, 0x00004c,0x00030c }, + { 0x00013e, 0x00006c,0x00030c }, + { 0x000143, 0x00004e,0x000301 }, + { 0x000144, 0x00006e,0x000301 }, + { 0x000145, 0x00004e,0x000327 }, + { 0x000146, 0x00006e,0x000327 }, + { 0x000147, 0x00004e,0x00030c }, + { 0x000148, 0x00006e,0x00030c }, + { 0x00014c, 0x00004f,0x000304 }, + { 0x00014d, 0x00006f,0x000304 }, + { 0x00014e, 0x00004f,0x000306 }, + { 0x00014f, 0x00006f,0x000306 }, + { 0x000150, 0x00004f,0x00030b }, + { 0x000151, 0x00006f,0x00030b }, + { 0x000154, 0x000052,0x000301 }, + { 0x000155, 0x000072,0x000301 }, + { 0x000156, 0x000052,0x000327 }, + { 0x000157, 0x000072,0x000327 }, + { 0x000158, 0x000052,0x00030c }, + { 0x000159, 0x000072,0x00030c }, + { 0x00015a, 0x000053,0x000301 }, + { 0x00015b, 0x000073,0x000301 }, + { 0x00015c, 0x000053,0x000302 }, + { 0x00015d, 0x000073,0x000302 }, + { 0x00015e, 0x000053,0x000327 }, + { 0x00015f, 0x000073,0x000327 }, + { 0x000160, 0x000053,0x00030c }, + { 0x000161, 0x000073,0x00030c }, + { 0x000162, 0x000054,0x000327 }, + { 0x000163, 0x000074,0x000327 }, + { 0x000164, 0x000054,0x00030c }, + { 0x000165, 0x000074,0x00030c }, + { 0x000168, 0x000055,0x000303 }, + { 0x000169, 0x000075,0x000303 }, + { 0x00016a, 0x000055,0x000304 }, + { 0x00016b, 0x000075,0x000304 }, + { 0x00016c, 0x000055,0x000306 }, + { 0x00016d, 0x000075,0x000306 }, + { 0x00016e, 0x000055,0x00030a }, + { 0x00016f, 0x000075,0x00030a }, + { 0x000170, 0x000055,0x00030b }, + { 0x000171, 0x000075,0x00030b }, + { 0x000172, 0x000055,0x000328 }, + { 0x000173, 0x000075,0x000328 }, + { 0x000174, 0x000057,0x000302 }, + { 0x000175, 0x000077,0x000302 }, + { 0x000176, 0x000059,0x000302 }, + { 0x000177, 0x000079,0x000302 }, + { 0x000178, 0x000059,0x000308 }, + { 0x000179, 0x00005a,0x000301 }, + { 0x00017a, 0x00007a,0x000301 }, + { 0x00017b, 0x00005a,0x000307 }, + { 0x00017c, 0x00007a,0x000307 }, + { 0x00017d, 0x00005a,0x00030c }, + { 0x00017e, 0x00007a,0x00030c }, + { 0x0001a0, 0x00004f,0x00031b }, + { 0x0001a1, 0x00006f,0x00031b }, + { 0x0001af, 0x000055,0x00031b }, + { 0x0001b0, 0x000075,0x00031b }, + { 0x0001cd, 0x000041,0x00030c }, + { 0x0001ce, 0x000061,0x00030c }, + { 0x0001cf, 0x000049,0x00030c }, + { 0x0001d0, 0x000069,0x00030c }, + { 0x0001d1, 0x00004f,0x00030c }, + { 0x0001d2, 0x00006f,0x00030c }, + { 0x0001d3, 0x000055,0x00030c }, + { 0x0001d4, 0x000075,0x00030c }, + { 0x0001d5, 0x0000dc,0x000304 }, + { 0x0001d6, 0x0000fc,0x000304 }, + { 0x0001d7, 0x0000dc,0x000301 }, + { 0x0001d8, 0x0000fc,0x000301 }, + { 0x0001d9, 0x0000dc,0x00030c }, + { 0x0001da, 0x0000fc,0x00030c }, + { 0x0001db, 0x0000dc,0x000300 }, + { 0x0001dc, 0x0000fc,0x000300 }, + { 0x0001de, 0x0000c4,0x000304 }, + { 0x0001df, 0x0000e4,0x000304 }, + { 0x0001e0, 0x000226,0x000304 }, + { 0x0001e1, 0x000227,0x000304 }, + { 0x0001e2, 0x0000c6,0x000304 }, + { 0x0001e3, 0x0000e6,0x000304 }, + { 0x0001e6, 0x000047,0x00030c }, + { 0x0001e7, 0x000067,0x00030c }, + { 0x0001e8, 0x00004b,0x00030c }, + { 0x0001e9, 0x00006b,0x00030c }, + { 0x0001ea, 0x00004f,0x000328 }, + { 0x0001eb, 0x00006f,0x000328 }, + { 0x0001ec, 0x0001ea,0x000304 }, + { 0x0001ed, 0x0001eb,0x000304 }, + { 0x0001ee, 0x0001b7,0x00030c }, + { 0x0001ef, 0x000292,0x00030c }, + { 0x0001f0, 0x00006a,0x00030c }, + { 0x0001f4, 0x000047,0x000301 }, + { 0x0001f5, 0x000067,0x000301 }, + { 0x0001f8, 0x00004e,0x000300 }, + { 0x0001f9, 0x00006e,0x000300 }, + { 0x0001fa, 0x0000c5,0x000301 }, + { 0x0001fb, 0x0000e5,0x000301 }, + { 0x0001fc, 0x0000c6,0x000301 }, + { 0x0001fd, 0x0000e6,0x000301 }, + { 0x0001fe, 0x0000d8,0x000301 }, + { 0x0001ff, 0x0000f8,0x000301 }, + { 0x000200, 0x000041,0x00030f }, + { 0x000201, 0x000061,0x00030f }, + { 0x000202, 0x000041,0x000311 }, + { 0x000203, 0x000061,0x000311 }, + { 0x000204, 0x000045,0x00030f }, + { 0x000205, 0x000065,0x00030f }, + { 0x000206, 0x000045,0x000311 }, + { 0x000207, 0x000065,0x000311 }, + { 0x000208, 0x000049,0x00030f }, + { 0x000209, 0x000069,0x00030f }, + { 0x00020a, 0x000049,0x000311 }, + { 0x00020b, 0x000069,0x000311 }, + { 0x00020c, 0x00004f,0x00030f }, + { 0x00020d, 0x00006f,0x00030f }, + { 0x00020e, 0x00004f,0x000311 }, + { 0x00020f, 0x00006f,0x000311 }, + { 0x000210, 0x000052,0x00030f }, + { 0x000211, 0x000072,0x00030f }, + { 0x000212, 0x000052,0x000311 }, + { 0x000213, 0x000072,0x000311 }, + { 0x000214, 0x000055,0x00030f }, + { 0x000215, 0x000075,0x00030f }, + { 0x000216, 0x000055,0x000311 }, + { 0x000217, 0x000075,0x000311 }, + { 0x000218, 0x000053,0x000326 }, + { 0x000219, 0x000073,0x000326 }, + { 0x00021a, 0x000054,0x000326 }, + { 0x00021b, 0x000074,0x000326 }, + { 0x00021e, 0x000048,0x00030c }, + { 0x00021f, 0x000068,0x00030c }, + { 0x000226, 0x000041,0x000307 }, + { 0x000227, 0x000061,0x000307 }, + { 0x000228, 0x000045,0x000327 }, + { 0x000229, 0x000065,0x000327 }, + { 0x00022a, 0x0000d6,0x000304 }, + { 0x00022b, 0x0000f6,0x000304 }, + { 0x00022c, 0x0000d5,0x000304 }, + { 0x00022d, 0x0000f5,0x000304 }, + { 0x00022e, 0x00004f,0x000307 }, + { 0x00022f, 0x00006f,0x000307 }, + { 0x000230, 0x00022e,0x000304 }, + { 0x000231, 0x00022f,0x000304 }, + { 0x000232, 0x000059,0x000304 }, + { 0x000233, 0x000079,0x000304 }, + { 0x000344, 0x000308,0x000301 }, + { 0x000385, 0x0000a8,0x000301 }, + { 0x000386, 0x000391,0x000301 }, + { 0x000388, 0x000395,0x000301 }, + { 0x000389, 0x000397,0x000301 }, + { 0x00038a, 0x000399,0x000301 }, + { 0x00038c, 0x00039f,0x000301 }, + { 0x00038e, 0x0003a5,0x000301 }, + { 0x00038f, 0x0003a9,0x000301 }, + { 0x000390, 0x0003ca,0x000301 }, + { 0x0003aa, 0x000399,0x000308 }, + { 0x0003ab, 0x0003a5,0x000308 }, + { 0x0003ac, 0x0003b1,0x000301 }, + { 0x0003ad, 0x0003b5,0x000301 }, + { 0x0003ae, 0x0003b7,0x000301 }, + { 0x0003af, 0x0003b9,0x000301 }, + { 0x0003b0, 0x0003cb,0x000301 }, + { 0x0003ca, 0x0003b9,0x000308 }, + { 0x0003cb, 0x0003c5,0x000308 }, + { 0x0003cc, 0x0003bf,0x000301 }, + { 0x0003cd, 0x0003c5,0x000301 }, + { 0x0003ce, 0x0003c9,0x000301 }, + { 0x0003d3, 0x0003d2,0x000301 }, + { 0x0003d4, 0x0003d2,0x000308 }, + { 0x000400, 0x000415,0x000300 }, + { 0x000401, 0x000415,0x000308 }, + { 0x000403, 0x000413,0x000301 }, + { 0x000407, 0x000406,0x000308 }, + { 0x00040c, 0x00041a,0x000301 }, + { 0x00040d, 0x000418,0x000300 }, + { 0x00040e, 0x000423,0x000306 }, + { 0x000419, 0x000418,0x000306 }, + { 0x000439, 0x000438,0x000306 }, + { 0x000450, 0x000435,0x000300 }, + { 0x000451, 0x000435,0x000308 }, + { 0x000453, 0x000433,0x000301 }, + { 0x000457, 0x000456,0x000308 }, + { 0x00045c, 0x00043a,0x000301 }, + { 0x00045d, 0x000438,0x000300 }, + { 0x00045e, 0x000443,0x000306 }, + { 0x000476, 0x000474,0x00030f }, + { 0x000477, 0x000475,0x00030f }, + { 0x0004c1, 0x000416,0x000306 }, + { 0x0004c2, 0x000436,0x000306 }, + { 0x0004d0, 0x000410,0x000306 }, + { 0x0004d1, 0x000430,0x000306 }, + { 0x0004d2, 0x000410,0x000308 }, + { 0x0004d3, 0x000430,0x000308 }, + { 0x0004d6, 0x000415,0x000306 }, + { 0x0004d7, 0x000435,0x000306 }, + { 0x0004da, 0x0004d8,0x000308 }, + { 0x0004db, 0x0004d9,0x000308 }, + { 0x0004dc, 0x000416,0x000308 }, + { 0x0004dd, 0x000436,0x000308 }, + { 0x0004de, 0x000417,0x000308 }, + { 0x0004df, 0x000437,0x000308 }, + { 0x0004e2, 0x000418,0x000304 }, + { 0x0004e3, 0x000438,0x000304 }, + { 0x0004e4, 0x000418,0x000308 }, + { 0x0004e5, 0x000438,0x000308 }, + { 0x0004e6, 0x00041e,0x000308 }, + { 0x0004e7, 0x00043e,0x000308 }, + { 0x0004ea, 0x0004e8,0x000308 }, + { 0x0004eb, 0x0004e9,0x000308 }, + { 0x0004ec, 0x00042d,0x000308 }, + { 0x0004ed, 0x00044d,0x000308 }, + { 0x0004ee, 0x000423,0x000304 }, + { 0x0004ef, 0x000443,0x000304 }, + { 0x0004f0, 0x000423,0x000308 }, + { 0x0004f1, 0x000443,0x000308 }, + { 0x0004f2, 0x000423,0x00030b }, + { 0x0004f3, 0x000443,0x00030b }, + { 0x0004f4, 0x000427,0x000308 }, + { 0x0004f5, 0x000447,0x000308 }, + { 0x0004f8, 0x00042b,0x000308 }, + { 0x0004f9, 0x00044b,0x000308 }, + { 0x000622, 0x000627,0x000653 }, + { 0x000623, 0x000627,0x000654 }, + { 0x000624, 0x000648,0x000654 }, + { 0x000625, 0x000627,0x000655 }, + { 0x000626, 0x00064a,0x000654 }, + { 0x0006c0, 0x0006d5,0x000654 }, + { 0x0006c2, 0x0006c1,0x000654 }, + { 0x0006d3, 0x0006d2,0x000654 }, + { 0x000929, 0x000928,0x00093c }, + { 0x000931, 0x000930,0x00093c }, + { 0x000934, 0x000933,0x00093c }, + { 0x000958, 0x000915,0x00093c }, + { 0x000959, 0x000916,0x00093c }, + { 0x00095a, 0x000917,0x00093c }, + { 0x00095b, 0x00091c,0x00093c }, + { 0x00095c, 0x000921,0x00093c }, + { 0x00095d, 0x000922,0x00093c }, + { 0x00095e, 0x00092b,0x00093c }, + { 0x00095f, 0x00092f,0x00093c }, + { 0x0009cb, 0x0009c7,0x0009be }, + { 0x0009cc, 0x0009c7,0x0009d7 }, + { 0x0009dc, 0x0009a1,0x0009bc }, + { 0x0009dd, 0x0009a2,0x0009bc }, + { 0x0009df, 0x0009af,0x0009bc }, + { 0x000a33, 0x000a32,0x000a3c }, + { 0x000a36, 0x000a38,0x000a3c }, + { 0x000a59, 0x000a16,0x000a3c }, + { 0x000a5a, 0x000a17,0x000a3c }, + { 0x000a5b, 0x000a1c,0x000a3c }, + { 0x000a5e, 0x000a2b,0x000a3c }, + { 0x000b48, 0x000b47,0x000b56 }, + { 0x000b4b, 0x000b47,0x000b3e }, + { 0x000b4c, 0x000b47,0x000b57 }, + { 0x000b5c, 0x000b21,0x000b3c }, + { 0x000b5d, 0x000b22,0x000b3c }, + { 0x000b94, 0x000b92,0x000bd7 }, + { 0x000bca, 0x000bc6,0x000bbe }, + { 0x000bcb, 0x000bc7,0x000bbe }, + { 0x000bcc, 0x000bc6,0x000bd7 }, + { 0x000c48, 0x000c46,0x000c56 }, + { 0x000cc0, 0x000cbf,0x000cd5 }, + { 0x000cc7, 0x000cc6,0x000cd5 }, + { 0x000cc8, 0x000cc6,0x000cd6 }, + { 0x000cca, 0x000cc6,0x000cc2 }, + { 0x000ccb, 0x000cca,0x000cd5 }, + { 0x000d4a, 0x000d46,0x000d3e }, + { 0x000d4b, 0x000d47,0x000d3e }, + { 0x000d4c, 0x000d46,0x000d57 }, + { 0x000dda, 0x000dd9,0x000dca }, + { 0x000ddc, 0x000dd9,0x000dcf }, + { 0x000ddd, 0x000ddc,0x000dca }, + { 0x000dde, 0x000dd9,0x000ddf }, + { 0x000f43, 0x000f42,0x000fb7 }, + { 0x000f4d, 0x000f4c,0x000fb7 }, + { 0x000f52, 0x000f51,0x000fb7 }, + { 0x000f57, 0x000f56,0x000fb7 }, + { 0x000f5c, 0x000f5b,0x000fb7 }, + { 0x000f69, 0x000f40,0x000fb5 }, + { 0x000f73, 0x000f71,0x000f72 }, + { 0x000f75, 0x000f71,0x000f74 }, + { 0x000f76, 0x000fb2,0x000f80 }, + { 0x000f78, 0x000fb3,0x000f80 }, + { 0x000f81, 0x000f71,0x000f80 }, + { 0x000f93, 0x000f92,0x000fb7 }, + { 0x000f9d, 0x000f9c,0x000fb7 }, + { 0x000fa2, 0x000fa1,0x000fb7 }, + { 0x000fa7, 0x000fa6,0x000fb7 }, + { 0x000fac, 0x000fab,0x000fb7 }, + { 0x000fb9, 0x000f90,0x000fb5 }, + { 0x001026, 0x001025,0x00102e }, + { 0x001b06, 0x001b05,0x001b35 }, + { 0x001b08, 0x001b07,0x001b35 }, + { 0x001b0a, 0x001b09,0x001b35 }, + { 0x001b0c, 0x001b0b,0x001b35 }, + { 0x001b0e, 0x001b0d,0x001b35 }, + { 0x001b12, 0x001b11,0x001b35 }, + { 0x001b3b, 0x001b3a,0x001b35 }, + { 0x001b3d, 0x001b3c,0x001b35 }, + { 0x001b40, 0x001b3e,0x001b35 }, + { 0x001b41, 0x001b3f,0x001b35 }, + { 0x001b43, 0x001b42,0x001b35 }, + { 0x001e00, 0x000041,0x000325 }, + { 0x001e01, 0x000061,0x000325 }, + { 0x001e02, 0x000042,0x000307 }, + { 0x001e03, 0x000062,0x000307 }, + { 0x001e04, 0x000042,0x000323 }, + { 0x001e05, 0x000062,0x000323 }, + { 0x001e06, 0x000042,0x000331 }, + { 0x001e07, 0x000062,0x000331 }, + { 0x001e08, 0x0000c7,0x000301 }, + { 0x001e09, 0x0000e7,0x000301 }, + { 0x001e0a, 0x000044,0x000307 }, + { 0x001e0b, 0x000064,0x000307 }, + { 0x001e0c, 0x000044,0x000323 }, + { 0x001e0d, 0x000064,0x000323 }, + { 0x001e0e, 0x000044,0x000331 }, + { 0x001e0f, 0x000064,0x000331 }, + { 0x001e10, 0x000044,0x000327 }, + { 0x001e11, 0x000064,0x000327 }, + { 0x001e12, 0x000044,0x00032d }, + { 0x001e13, 0x000064,0x00032d }, + { 0x001e14, 0x000112,0x000300 }, + { 0x001e15, 0x000113,0x000300 }, + { 0x001e16, 0x000112,0x000301 }, + { 0x001e17, 0x000113,0x000301 }, + { 0x001e18, 0x000045,0x00032d }, + { 0x001e19, 0x000065,0x00032d }, + { 0x001e1a, 0x000045,0x000330 }, + { 0x001e1b, 0x000065,0x000330 }, + { 0x001e1c, 0x000228,0x000306 }, + { 0x001e1d, 0x000229,0x000306 }, + { 0x001e1e, 0x000046,0x000307 }, + { 0x001e1f, 0x000066,0x000307 }, + { 0x001e20, 0x000047,0x000304 }, + { 0x001e21, 0x000067,0x000304 }, + { 0x001e22, 0x000048,0x000307 }, + { 0x001e23, 0x000068,0x000307 }, + { 0x001e24, 0x000048,0x000323 }, + { 0x001e25, 0x000068,0x000323 }, + { 0x001e26, 0x000048,0x000308 }, + { 0x001e27, 0x000068,0x000308 }, + { 0x001e28, 0x000048,0x000327 }, + { 0x001e29, 0x000068,0x000327 }, + { 0x001e2a, 0x000048,0x00032e }, + { 0x001e2b, 0x000068,0x00032e }, + { 0x001e2c, 0x000049,0x000330 }, + { 0x001e2d, 0x000069,0x000330 }, + { 0x001e2e, 0x0000cf,0x000301 }, + { 0x001e2f, 0x0000ef,0x000301 }, + { 0x001e30, 0x00004b,0x000301 }, + { 0x001e31, 0x00006b,0x000301 }, + { 0x001e32, 0x00004b,0x000323 }, + { 0x001e33, 0x00006b,0x000323 }, + { 0x001e34, 0x00004b,0x000331 }, + { 0x001e35, 0x00006b,0x000331 }, + { 0x001e36, 0x00004c,0x000323 }, + { 0x001e37, 0x00006c,0x000323 }, + { 0x001e38, 0x001e36,0x000304 }, + { 0x001e39, 0x001e37,0x000304 }, + { 0x001e3a, 0x00004c,0x000331 }, + { 0x001e3b, 0x00006c,0x000331 }, + { 0x001e3c, 0x00004c,0x00032d }, + { 0x001e3d, 0x00006c,0x00032d }, + { 0x001e3e, 0x00004d,0x000301 }, + { 0x001e3f, 0x00006d,0x000301 }, + { 0x001e40, 0x00004d,0x000307 }, + { 0x001e41, 0x00006d,0x000307 }, + { 0x001e42, 0x00004d,0x000323 }, + { 0x001e43, 0x00006d,0x000323 }, + { 0x001e44, 0x00004e,0x000307 }, + { 0x001e45, 0x00006e,0x000307 }, + { 0x001e46, 0x00004e,0x000323 }, + { 0x001e47, 0x00006e,0x000323 }, + { 0x001e48, 0x00004e,0x000331 }, + { 0x001e49, 0x00006e,0x000331 }, + { 0x001e4a, 0x00004e,0x00032d }, + { 0x001e4b, 0x00006e,0x00032d }, + { 0x001e4c, 0x0000d5,0x000301 }, + { 0x001e4d, 0x0000f5,0x000301 }, + { 0x001e4e, 0x0000d5,0x000308 }, + { 0x001e4f, 0x0000f5,0x000308 }, + { 0x001e50, 0x00014c,0x000300 }, + { 0x001e51, 0x00014d,0x000300 }, + { 0x001e52, 0x00014c,0x000301 }, + { 0x001e53, 0x00014d,0x000301 }, + { 0x001e54, 0x000050,0x000301 }, + { 0x001e55, 0x000070,0x000301 }, + { 0x001e56, 0x000050,0x000307 }, + { 0x001e57, 0x000070,0x000307 }, + { 0x001e58, 0x000052,0x000307 }, + { 0x001e59, 0x000072,0x000307 }, + { 0x001e5a, 0x000052,0x000323 }, + { 0x001e5b, 0x000072,0x000323 }, + { 0x001e5c, 0x001e5a,0x000304 }, + { 0x001e5d, 0x001e5b,0x000304 }, + { 0x001e5e, 0x000052,0x000331 }, + { 0x001e5f, 0x000072,0x000331 }, + { 0x001e60, 0x000053,0x000307 }, + { 0x001e61, 0x000073,0x000307 }, + { 0x001e62, 0x000053,0x000323 }, + { 0x001e63, 0x000073,0x000323 }, + { 0x001e64, 0x00015a,0x000307 }, + { 0x001e65, 0x00015b,0x000307 }, + { 0x001e66, 0x000160,0x000307 }, + { 0x001e67, 0x000161,0x000307 }, + { 0x001e68, 0x001e62,0x000307 }, + { 0x001e69, 0x001e63,0x000307 }, + { 0x001e6a, 0x000054,0x000307 }, + { 0x001e6b, 0x000074,0x000307 }, + { 0x001e6c, 0x000054,0x000323 }, + { 0x001e6d, 0x000074,0x000323 }, + { 0x001e6e, 0x000054,0x000331 }, + { 0x001e6f, 0x000074,0x000331 }, + { 0x001e70, 0x000054,0x00032d }, + { 0x001e71, 0x000074,0x00032d }, + { 0x001e72, 0x000055,0x000324 }, + { 0x001e73, 0x000075,0x000324 }, + { 0x001e74, 0x000055,0x000330 }, + { 0x001e75, 0x000075,0x000330 }, + { 0x001e76, 0x000055,0x00032d }, + { 0x001e77, 0x000075,0x00032d }, + { 0x001e78, 0x000168,0x000301 }, + { 0x001e79, 0x000169,0x000301 }, + { 0x001e7a, 0x00016a,0x000308 }, + { 0x001e7b, 0x00016b,0x000308 }, + { 0x001e7c, 0x000056,0x000303 }, + { 0x001e7d, 0x000076,0x000303 }, + { 0x001e7e, 0x000056,0x000323 }, + { 0x001e7f, 0x000076,0x000323 }, + { 0x001e80, 0x000057,0x000300 }, + { 0x001e81, 0x000077,0x000300 }, + { 0x001e82, 0x000057,0x000301 }, + { 0x001e83, 0x000077,0x000301 }, + { 0x001e84, 0x000057,0x000308 }, + { 0x001e85, 0x000077,0x000308 }, + { 0x001e86, 0x000057,0x000307 }, + { 0x001e87, 0x000077,0x000307 }, + { 0x001e88, 0x000057,0x000323 }, + { 0x001e89, 0x000077,0x000323 }, + { 0x001e8a, 0x000058,0x000307 }, + { 0x001e8b, 0x000078,0x000307 }, + { 0x001e8c, 0x000058,0x000308 }, + { 0x001e8d, 0x000078,0x000308 }, + { 0x001e8e, 0x000059,0x000307 }, + { 0x001e8f, 0x000079,0x000307 }, + { 0x001e90, 0x00005a,0x000302 }, + { 0x001e91, 0x00007a,0x000302 }, + { 0x001e92, 0x00005a,0x000323 }, + { 0x001e93, 0x00007a,0x000323 }, + { 0x001e94, 0x00005a,0x000331 }, + { 0x001e95, 0x00007a,0x000331 }, + { 0x001e96, 0x000068,0x000331 }, + { 0x001e97, 0x000074,0x000308 }, + { 0x001e98, 0x000077,0x00030a }, + { 0x001e99, 0x000079,0x00030a }, + { 0x001e9b, 0x00017f,0x000307 }, + { 0x001ea0, 0x000041,0x000323 }, + { 0x001ea1, 0x000061,0x000323 }, + { 0x001ea2, 0x000041,0x000309 }, + { 0x001ea3, 0x000061,0x000309 }, + { 0x001ea4, 0x0000c2,0x000301 }, + { 0x001ea5, 0x0000e2,0x000301 }, + { 0x001ea6, 0x0000c2,0x000300 }, + { 0x001ea7, 0x0000e2,0x000300 }, + { 0x001ea8, 0x0000c2,0x000309 }, + { 0x001ea9, 0x0000e2,0x000309 }, + { 0x001eaa, 0x0000c2,0x000303 }, + { 0x001eab, 0x0000e2,0x000303 }, + { 0x001eac, 0x001ea0,0x000302 }, + { 0x001ead, 0x001ea1,0x000302 }, + { 0x001eae, 0x000102,0x000301 }, + { 0x001eaf, 0x000103,0x000301 }, + { 0x001eb0, 0x000102,0x000300 }, + { 0x001eb1, 0x000103,0x000300 }, + { 0x001eb2, 0x000102,0x000309 }, + { 0x001eb3, 0x000103,0x000309 }, + { 0x001eb4, 0x000102,0x000303 }, + { 0x001eb5, 0x000103,0x000303 }, + { 0x001eb6, 0x001ea0,0x000306 }, + { 0x001eb7, 0x001ea1,0x000306 }, + { 0x001eb8, 0x000045,0x000323 }, + { 0x001eb9, 0x000065,0x000323 }, + { 0x001eba, 0x000045,0x000309 }, + { 0x001ebb, 0x000065,0x000309 }, + { 0x001ebc, 0x000045,0x000303 }, + { 0x001ebd, 0x000065,0x000303 }, + { 0x001ebe, 0x0000ca,0x000301 }, + { 0x001ebf, 0x0000ea,0x000301 }, + { 0x001ec0, 0x0000ca,0x000300 }, + { 0x001ec1, 0x0000ea,0x000300 }, + { 0x001ec2, 0x0000ca,0x000309 }, + { 0x001ec3, 0x0000ea,0x000309 }, + { 0x001ec4, 0x0000ca,0x000303 }, + { 0x001ec5, 0x0000ea,0x000303 }, + { 0x001ec6, 0x001eb8,0x000302 }, + { 0x001ec7, 0x001eb9,0x000302 }, + { 0x001ec8, 0x000049,0x000309 }, + { 0x001ec9, 0x000069,0x000309 }, + { 0x001eca, 0x000049,0x000323 }, + { 0x001ecb, 0x000069,0x000323 }, + { 0x001ecc, 0x00004f,0x000323 }, + { 0x001ecd, 0x00006f,0x000323 }, + { 0x001ece, 0x00004f,0x000309 }, + { 0x001ecf, 0x00006f,0x000309 }, + { 0x001ed0, 0x0000d4,0x000301 }, + { 0x001ed1, 0x0000f4,0x000301 }, + { 0x001ed2, 0x0000d4,0x000300 }, + { 0x001ed3, 0x0000f4,0x000300 }, + { 0x001ed4, 0x0000d4,0x000309 }, + { 0x001ed5, 0x0000f4,0x000309 }, + { 0x001ed6, 0x0000d4,0x000303 }, + { 0x001ed7, 0x0000f4,0x000303 }, + { 0x001ed8, 0x001ecc,0x000302 }, + { 0x001ed9, 0x001ecd,0x000302 }, + { 0x001eda, 0x0001a0,0x000301 }, + { 0x001edb, 0x0001a1,0x000301 }, + { 0x001edc, 0x0001a0,0x000300 }, + { 0x001edd, 0x0001a1,0x000300 }, + { 0x001ede, 0x0001a0,0x000309 }, + { 0x001edf, 0x0001a1,0x000309 }, + { 0x001ee0, 0x0001a0,0x000303 }, + { 0x001ee1, 0x0001a1,0x000303 }, + { 0x001ee2, 0x0001a0,0x000323 }, + { 0x001ee3, 0x0001a1,0x000323 }, + { 0x001ee4, 0x000055,0x000323 }, + { 0x001ee5, 0x000075,0x000323 }, + { 0x001ee6, 0x000055,0x000309 }, + { 0x001ee7, 0x000075,0x000309 }, + { 0x001ee8, 0x0001af,0x000301 }, + { 0x001ee9, 0x0001b0,0x000301 }, + { 0x001eea, 0x0001af,0x000300 }, + { 0x001eeb, 0x0001b0,0x000300 }, + { 0x001eec, 0x0001af,0x000309 }, + { 0x001eed, 0x0001b0,0x000309 }, + { 0x001eee, 0x0001af,0x000303 }, + { 0x001eef, 0x0001b0,0x000303 }, + { 0x001ef0, 0x0001af,0x000323 }, + { 0x001ef1, 0x0001b0,0x000323 }, + { 0x001ef2, 0x000059,0x000300 }, + { 0x001ef3, 0x000079,0x000300 }, + { 0x001ef4, 0x000059,0x000323 }, + { 0x001ef5, 0x000079,0x000323 }, + { 0x001ef6, 0x000059,0x000309 }, + { 0x001ef7, 0x000079,0x000309 }, + { 0x001ef8, 0x000059,0x000303 }, + { 0x001ef9, 0x000079,0x000303 }, + { 0x001f00, 0x0003b1,0x000313 }, + { 0x001f01, 0x0003b1,0x000314 }, + { 0x001f02, 0x001f00,0x000300 }, + { 0x001f03, 0x001f01,0x000300 }, + { 0x001f04, 0x001f00,0x000301 }, + { 0x001f05, 0x001f01,0x000301 }, + { 0x001f06, 0x001f00,0x000342 }, + { 0x001f07, 0x001f01,0x000342 }, + { 0x001f08, 0x000391,0x000313 }, + { 0x001f09, 0x000391,0x000314 }, + { 0x001f0a, 0x001f08,0x000300 }, + { 0x001f0b, 0x001f09,0x000300 }, + { 0x001f0c, 0x001f08,0x000301 }, + { 0x001f0d, 0x001f09,0x000301 }, + { 0x001f0e, 0x001f08,0x000342 }, + { 0x001f0f, 0x001f09,0x000342 }, + { 0x001f10, 0x0003b5,0x000313 }, + { 0x001f11, 0x0003b5,0x000314 }, + { 0x001f12, 0x001f10,0x000300 }, + { 0x001f13, 0x001f11,0x000300 }, + { 0x001f14, 0x001f10,0x000301 }, + { 0x001f15, 0x001f11,0x000301 }, + { 0x001f18, 0x000395,0x000313 }, + { 0x001f19, 0x000395,0x000314 }, + { 0x001f1a, 0x001f18,0x000300 }, + { 0x001f1b, 0x001f19,0x000300 }, + { 0x001f1c, 0x001f18,0x000301 }, + { 0x001f1d, 0x001f19,0x000301 }, + { 0x001f20, 0x0003b7,0x000313 }, + { 0x001f21, 0x0003b7,0x000314 }, + { 0x001f22, 0x001f20,0x000300 }, + { 0x001f23, 0x001f21,0x000300 }, + { 0x001f24, 0x001f20,0x000301 }, + { 0x001f25, 0x001f21,0x000301 }, + { 0x001f26, 0x001f20,0x000342 }, + { 0x001f27, 0x001f21,0x000342 }, + { 0x001f28, 0x000397,0x000313 }, + { 0x001f29, 0x000397,0x000314 }, + { 0x001f2a, 0x001f28,0x000300 }, + { 0x001f2b, 0x001f29,0x000300 }, + { 0x001f2c, 0x001f28,0x000301 }, + { 0x001f2d, 0x001f29,0x000301 }, + { 0x001f2e, 0x001f28,0x000342 }, + { 0x001f2f, 0x001f29,0x000342 }, + { 0x001f30, 0x0003b9,0x000313 }, + { 0x001f31, 0x0003b9,0x000314 }, + { 0x001f32, 0x001f30,0x000300 }, + { 0x001f33, 0x001f31,0x000300 }, + { 0x001f34, 0x001f30,0x000301 }, + { 0x001f35, 0x001f31,0x000301 }, + { 0x001f36, 0x001f30,0x000342 }, + { 0x001f37, 0x001f31,0x000342 }, + { 0x001f38, 0x000399,0x000313 }, + { 0x001f39, 0x000399,0x000314 }, + { 0x001f3a, 0x001f38,0x000300 }, + { 0x001f3b, 0x001f39,0x000300 }, + { 0x001f3c, 0x001f38,0x000301 }, + { 0x001f3d, 0x001f39,0x000301 }, + { 0x001f3e, 0x001f38,0x000342 }, + { 0x001f3f, 0x001f39,0x000342 }, + { 0x001f40, 0x0003bf,0x000313 }, + { 0x001f41, 0x0003bf,0x000314 }, + { 0x001f42, 0x001f40,0x000300 }, + { 0x001f43, 0x001f41,0x000300 }, + { 0x001f44, 0x001f40,0x000301 }, + { 0x001f45, 0x001f41,0x000301 }, + { 0x001f48, 0x00039f,0x000313 }, + { 0x001f49, 0x00039f,0x000314 }, + { 0x001f4a, 0x001f48,0x000300 }, + { 0x001f4b, 0x001f49,0x000300 }, + { 0x001f4c, 0x001f48,0x000301 }, + { 0x001f4d, 0x001f49,0x000301 }, + { 0x001f50, 0x0003c5,0x000313 }, + { 0x001f51, 0x0003c5,0x000314 }, + { 0x001f52, 0x001f50,0x000300 }, + { 0x001f53, 0x001f51,0x000300 }, + { 0x001f54, 0x001f50,0x000301 }, + { 0x001f55, 0x001f51,0x000301 }, + { 0x001f56, 0x001f50,0x000342 }, + { 0x001f57, 0x001f51,0x000342 }, + { 0x001f59, 0x0003a5,0x000314 }, + { 0x001f5b, 0x001f59,0x000300 }, + { 0x001f5d, 0x001f59,0x000301 }, + { 0x001f5f, 0x001f59,0x000342 }, + { 0x001f60, 0x0003c9,0x000313 }, + { 0x001f61, 0x0003c9,0x000314 }, + { 0x001f62, 0x001f60,0x000300 }, + { 0x001f63, 0x001f61,0x000300 }, + { 0x001f64, 0x001f60,0x000301 }, + { 0x001f65, 0x001f61,0x000301 }, + { 0x001f66, 0x001f60,0x000342 }, + { 0x001f67, 0x001f61,0x000342 }, + { 0x001f68, 0x0003a9,0x000313 }, + { 0x001f69, 0x0003a9,0x000314 }, + { 0x001f6a, 0x001f68,0x000300 }, + { 0x001f6b, 0x001f69,0x000300 }, + { 0x001f6c, 0x001f68,0x000301 }, + { 0x001f6d, 0x001f69,0x000301 }, + { 0x001f6e, 0x001f68,0x000342 }, + { 0x001f6f, 0x001f69,0x000342 }, + { 0x001f70, 0x0003b1,0x000300 }, + { 0x001f72, 0x0003b5,0x000300 }, + { 0x001f74, 0x0003b7,0x000300 }, + { 0x001f76, 0x0003b9,0x000300 }, + { 0x001f78, 0x0003bf,0x000300 }, + { 0x001f7a, 0x0003c5,0x000300 }, + { 0x001f7c, 0x0003c9,0x000300 }, + { 0x001f80, 0x001f00,0x000345 }, + { 0x001f81, 0x001f01,0x000345 }, + { 0x001f82, 0x001f02,0x000345 }, + { 0x001f83, 0x001f03,0x000345 }, + { 0x001f84, 0x001f04,0x000345 }, + { 0x001f85, 0x001f05,0x000345 }, + { 0x001f86, 0x001f06,0x000345 }, + { 0x001f87, 0x001f07,0x000345 }, + { 0x001f88, 0x001f08,0x000345 }, + { 0x001f89, 0x001f09,0x000345 }, + { 0x001f8a, 0x001f0a,0x000345 }, + { 0x001f8b, 0x001f0b,0x000345 }, + { 0x001f8c, 0x001f0c,0x000345 }, + { 0x001f8d, 0x001f0d,0x000345 }, + { 0x001f8e, 0x001f0e,0x000345 }, + { 0x001f8f, 0x001f0f,0x000345 }, + { 0x001f90, 0x001f20,0x000345 }, + { 0x001f91, 0x001f21,0x000345 }, + { 0x001f92, 0x001f22,0x000345 }, + { 0x001f93, 0x001f23,0x000345 }, + { 0x001f94, 0x001f24,0x000345 }, + { 0x001f95, 0x001f25,0x000345 }, + { 0x001f96, 0x001f26,0x000345 }, + { 0x001f97, 0x001f27,0x000345 }, + { 0x001f98, 0x001f28,0x000345 }, + { 0x001f99, 0x001f29,0x000345 }, + { 0x001f9a, 0x001f2a,0x000345 }, + { 0x001f9b, 0x001f2b,0x000345 }, + { 0x001f9c, 0x001f2c,0x000345 }, + { 0x001f9d, 0x001f2d,0x000345 }, + { 0x001f9e, 0x001f2e,0x000345 }, + { 0x001f9f, 0x001f2f,0x000345 }, + { 0x001fa0, 0x001f60,0x000345 }, + { 0x001fa1, 0x001f61,0x000345 }, + { 0x001fa2, 0x001f62,0x000345 }, + { 0x001fa3, 0x001f63,0x000345 }, + { 0x001fa4, 0x001f64,0x000345 }, + { 0x001fa5, 0x001f65,0x000345 }, + { 0x001fa6, 0x001f66,0x000345 }, + { 0x001fa7, 0x001f67,0x000345 }, + { 0x001fa8, 0x001f68,0x000345 }, + { 0x001fa9, 0x001f69,0x000345 }, + { 0x001faa, 0x001f6a,0x000345 }, + { 0x001fab, 0x001f6b,0x000345 }, + { 0x001fac, 0x001f6c,0x000345 }, + { 0x001fad, 0x001f6d,0x000345 }, + { 0x001fae, 0x001f6e,0x000345 }, + { 0x001faf, 0x001f6f,0x000345 }, + { 0x001fb0, 0x0003b1,0x000306 }, + { 0x001fb1, 0x0003b1,0x000304 }, + { 0x001fb2, 0x001f70,0x000345 }, + { 0x001fb3, 0x0003b1,0x000345 }, + { 0x001fb4, 0x0003ac,0x000345 }, + { 0x001fb6, 0x0003b1,0x000342 }, + { 0x001fb7, 0x001fb6,0x000345 }, + { 0x001fb8, 0x000391,0x000306 }, + { 0x001fb9, 0x000391,0x000304 }, + { 0x001fba, 0x000391,0x000300 }, + { 0x001fbc, 0x000391,0x000345 }, + { 0x001fc1, 0x0000a8,0x000342 }, + { 0x001fc2, 0x001f74,0x000345 }, + { 0x001fc3, 0x0003b7,0x000345 }, + { 0x001fc4, 0x0003ae,0x000345 }, + { 0x001fc6, 0x0003b7,0x000342 }, + { 0x001fc7, 0x001fc6,0x000345 }, + { 0x001fc8, 0x000395,0x000300 }, + { 0x001fca, 0x000397,0x000300 }, + { 0x001fcc, 0x000397,0x000345 }, + { 0x001fcd, 0x001fbf,0x000300 }, + { 0x001fce, 0x001fbf,0x000301 }, + { 0x001fcf, 0x001fbf,0x000342 }, + { 0x001fd0, 0x0003b9,0x000306 }, + { 0x001fd1, 0x0003b9,0x000304 }, + { 0x001fd2, 0x0003ca,0x000300 }, + { 0x001fd6, 0x0003b9,0x000342 }, + { 0x001fd7, 0x0003ca,0x000342 }, + { 0x001fd8, 0x000399,0x000306 }, + { 0x001fd9, 0x000399,0x000304 }, + { 0x001fda, 0x000399,0x000300 }, + { 0x001fdd, 0x001ffe,0x000300 }, + { 0x001fde, 0x001ffe,0x000301 }, + { 0x001fdf, 0x001ffe,0x000342 }, + { 0x001fe0, 0x0003c5,0x000306 }, + { 0x001fe1, 0x0003c5,0x000304 }, + { 0x001fe2, 0x0003cb,0x000300 }, + { 0x001fe4, 0x0003c1,0x000313 }, + { 0x001fe5, 0x0003c1,0x000314 }, + { 0x001fe6, 0x0003c5,0x000342 }, + { 0x001fe7, 0x0003cb,0x000342 }, + { 0x001fe8, 0x0003a5,0x000306 }, + { 0x001fe9, 0x0003a5,0x000304 }, + { 0x001fea, 0x0003a5,0x000300 }, + { 0x001fec, 0x0003a1,0x000314 }, + { 0x001fed, 0x0000a8,0x000300 }, + { 0x001ff2, 0x001f7c,0x000345 }, + { 0x001ff3, 0x0003c9,0x000345 }, + { 0x001ff4, 0x0003ce,0x000345 }, + { 0x001ff6, 0x0003c9,0x000342 }, + { 0x001ff7, 0x001ff6,0x000345 }, + { 0x001ff8, 0x00039f,0x000300 }, + { 0x001ffa, 0x0003a9,0x000300 }, + { 0x001ffc, 0x0003a9,0x000345 }, + { 0x00219a, 0x002190,0x000338 }, + { 0x00219b, 0x002192,0x000338 }, + { 0x0021ae, 0x002194,0x000338 }, + { 0x0021cd, 0x0021d0,0x000338 }, + { 0x0021ce, 0x0021d4,0x000338 }, + { 0x0021cf, 0x0021d2,0x000338 }, + { 0x002204, 0x002203,0x000338 }, + { 0x002209, 0x002208,0x000338 }, + { 0x00220c, 0x00220b,0x000338 }, + { 0x002224, 0x002223,0x000338 }, + { 0x002226, 0x002225,0x000338 }, + { 0x002241, 0x00223c,0x000338 }, + { 0x002244, 0x002243,0x000338 }, + { 0x002247, 0x002245,0x000338 }, + { 0x002249, 0x002248,0x000338 }, + { 0x002260, 0x00003d,0x000338 }, + { 0x002262, 0x002261,0x000338 }, + { 0x00226d, 0x00224d,0x000338 }, + { 0x00226e, 0x00003c,0x000338 }, + { 0x00226f, 0x00003e,0x000338 }, + { 0x002270, 0x002264,0x000338 }, + { 0x002271, 0x002265,0x000338 }, + { 0x002274, 0x002272,0x000338 }, + { 0x002275, 0x002273,0x000338 }, + { 0x002278, 0x002276,0x000338 }, + { 0x002279, 0x002277,0x000338 }, + { 0x002280, 0x00227a,0x000338 }, + { 0x002281, 0x00227b,0x000338 }, + { 0x002284, 0x002282,0x000338 }, + { 0x002285, 0x002283,0x000338 }, + { 0x002288, 0x002286,0x000338 }, + { 0x002289, 0x002287,0x000338 }, + { 0x0022ac, 0x0022a2,0x000338 }, + { 0x0022ad, 0x0022a8,0x000338 }, + { 0x0022ae, 0x0022a9,0x000338 }, + { 0x0022af, 0x0022ab,0x000338 }, + { 0x0022e0, 0x00227c,0x000338 }, + { 0x0022e1, 0x00227d,0x000338 }, + { 0x0022e2, 0x002291,0x000338 }, + { 0x0022e3, 0x002292,0x000338 }, + { 0x0022ea, 0x0022b2,0x000338 }, + { 0x0022eb, 0x0022b3,0x000338 }, + { 0x0022ec, 0x0022b4,0x000338 }, + { 0x0022ed, 0x0022b5,0x000338 }, + { 0x002adc, 0x002add,0x000338 }, + { 0x00304c, 0x00304b,0x003099 }, + { 0x00304e, 0x00304d,0x003099 }, + { 0x003050, 0x00304f,0x003099 }, + { 0x003052, 0x003051,0x003099 }, + { 0x003054, 0x003053,0x003099 }, + { 0x003056, 0x003055,0x003099 }, + { 0x003058, 0x003057,0x003099 }, + { 0x00305a, 0x003059,0x003099 }, + { 0x00305c, 0x00305b,0x003099 }, + { 0x00305e, 0x00305d,0x003099 }, + { 0x003060, 0x00305f,0x003099 }, + { 0x003062, 0x003061,0x003099 }, + { 0x003065, 0x003064,0x003099 }, + { 0x003067, 0x003066,0x003099 }, + { 0x003069, 0x003068,0x003099 }, + { 0x003070, 0x00306f,0x003099 }, + { 0x003071, 0x00306f,0x00309a }, + { 0x003073, 0x003072,0x003099 }, + { 0x003074, 0x003072,0x00309a }, + { 0x003076, 0x003075,0x003099 }, + { 0x003077, 0x003075,0x00309a }, + { 0x003079, 0x003078,0x003099 }, + { 0x00307a, 0x003078,0x00309a }, + { 0x00307c, 0x00307b,0x003099 }, + { 0x00307d, 0x00307b,0x00309a }, + { 0x003094, 0x003046,0x003099 }, + { 0x00309e, 0x00309d,0x003099 }, + { 0x0030ac, 0x0030ab,0x003099 }, + { 0x0030ae, 0x0030ad,0x003099 }, + { 0x0030b0, 0x0030af,0x003099 }, + { 0x0030b2, 0x0030b1,0x003099 }, + { 0x0030b4, 0x0030b3,0x003099 }, + { 0x0030b6, 0x0030b5,0x003099 }, + { 0x0030b8, 0x0030b7,0x003099 }, + { 0x0030ba, 0x0030b9,0x003099 }, + { 0x0030bc, 0x0030bb,0x003099 }, + { 0x0030be, 0x0030bd,0x003099 }, + { 0x0030c0, 0x0030bf,0x003099 }, + { 0x0030c2, 0x0030c1,0x003099 }, + { 0x0030c5, 0x0030c4,0x003099 }, + { 0x0030c7, 0x0030c6,0x003099 }, + { 0x0030c9, 0x0030c8,0x003099 }, + { 0x0030d0, 0x0030cf,0x003099 }, + { 0x0030d1, 0x0030cf,0x00309a }, + { 0x0030d3, 0x0030d2,0x003099 }, + { 0x0030d4, 0x0030d2,0x00309a }, + { 0x0030d6, 0x0030d5,0x003099 }, + { 0x0030d7, 0x0030d5,0x00309a }, + { 0x0030d9, 0x0030d8,0x003099 }, + { 0x0030da, 0x0030d8,0x00309a }, + { 0x0030dc, 0x0030db,0x003099 }, + { 0x0030dd, 0x0030db,0x00309a }, + { 0x0030f4, 0x0030a6,0x003099 }, + { 0x0030f7, 0x0030ef,0x003099 }, + { 0x0030f8, 0x0030f0,0x003099 }, + { 0x0030f9, 0x0030f1,0x003099 }, + { 0x0030fa, 0x0030f2,0x003099 }, + { 0x0030fe, 0x0030fd,0x003099 }, + { 0x00fb1d, 0x0005d9,0x0005b4 }, + { 0x00fb1f, 0x0005f2,0x0005b7 }, + { 0x00fb2a, 0x0005e9,0x0005c1 }, + { 0x00fb2b, 0x0005e9,0x0005c2 }, + { 0x00fb2c, 0x00fb49,0x0005c1 }, + { 0x00fb2d, 0x00fb49,0x0005c2 }, + { 0x00fb2e, 0x0005d0,0x0005b7 }, + { 0x00fb2f, 0x0005d0,0x0005b8 }, + { 0x00fb30, 0x0005d0,0x0005bc }, + { 0x00fb31, 0x0005d1,0x0005bc }, + { 0x00fb32, 0x0005d2,0x0005bc }, + { 0x00fb33, 0x0005d3,0x0005bc }, + { 0x00fb34, 0x0005d4,0x0005bc }, + { 0x00fb35, 0x0005d5,0x0005bc }, + { 0x00fb36, 0x0005d6,0x0005bc }, + { 0x00fb38, 0x0005d8,0x0005bc }, + { 0x00fb39, 0x0005d9,0x0005bc }, + { 0x00fb3a, 0x0005da,0x0005bc }, + { 0x00fb3b, 0x0005db,0x0005bc }, + { 0x00fb3c, 0x0005dc,0x0005bc }, + { 0x00fb3e, 0x0005de,0x0005bc }, + { 0x00fb40, 0x0005e0,0x0005bc }, + { 0x00fb41, 0x0005e1,0x0005bc }, + { 0x00fb43, 0x0005e3,0x0005bc }, + { 0x00fb44, 0x0005e4,0x0005bc }, + { 0x00fb46, 0x0005e6,0x0005bc }, + { 0x00fb47, 0x0005e7,0x0005bc }, + { 0x00fb48, 0x0005e8,0x0005bc }, + { 0x00fb49, 0x0005e9,0x0005bc }, + { 0x00fb4a, 0x0005ea,0x0005bc }, + { 0x00fb4b, 0x0005d5,0x0005b9 }, + { 0x00fb4c, 0x0005d1,0x0005bf }, + { 0x00fb4d, 0x0005db,0x0005bf }, + { 0x00fb4e, 0x0005e4,0x0005bf }, + { 0x01d15e, 0x01d157,0x01d165 }, + { 0x01d15f, 0x01d158,0x01d165 }, + { 0x01d160, 0x01d15f,0x01d16e }, + { 0x01d161, 0x01d15f,0x01d16f }, + { 0x01d162, 0x01d15f,0x01d170 }, + { 0x01d163, 0x01d15f,0x01d171 }, + { 0x01d164, 0x01d15f,0x01d172 }, + { 0x01d1bb, 0x01d1b9,0x01d165 }, + { 0x01d1bc, 0x01d1ba,0x01d165 }, + { 0x01d1bd, 0x01d1bb,0x01d16e }, + { 0x01d1be, 0x01d1bc,0x01d16e }, + { 0x01d1bf, 0x01d1bb,0x01d16f }, + { 0x01d1c0, 0x01d1bc,0x01d16f }, + + {0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +const dcUnicodeTripel * DecomposeUnicode ( u32 code ) +{ + int beg = 0; + int end = sizeof(TableUnicodeDecomp)/sizeof(*TableUnicodeDecomp) - 2; + while ( beg <= end ) + { + const int pos = (beg+end)/2; + const dcUnicodeTripel * ptr = TableUnicodeDecomp+pos; + if ( code < ptr->code1 ) + end = pos-1; + else if ( code > ptr->code1 ) + beg = pos+1; + else + { + noTRACE("DecomposeUnicode(%lx) -> %lx,%lx\n",code,ptr->code2,ptr->code3); + return ptr; + } + } + noTRACE("DecomposeUnicode(%lx) -> NONE\n",code); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-utf8.h b/project/dclib/dclib-utf8.h new file mode 100644 index 0000000..2e3e79b --- /dev/null +++ b/project/dclib/dclib-utf8.h @@ -0,0 +1,130 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_UTF8_H +#define DCLIB_UTF8_H 1 +#ifndef WIN_DCLIB + +#include "dclib-types.h" + +/////////////////////////////////////////////////////////////////////////////// +///// this software is taken from dcLib2 and now publiced under GPL2. ///// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum dcUnicodeConsts +{ + DCLIB_UNICODE_MAX_UTF8_1 = 0x7f, + DCLIB_UNICODE_MAX_UTF8_2 = 0x7ff, + DCLIB_UNICODE_MAX_UTF8_3 = 0xffff, + DCLIB_UNICODE_MAX_UTF8_4 = 0x1fffff, + + DCLIB_UNICODE_CODE_MASK = 0x1fffff, + +} dcUnicodeConsts; + +/////////////////////////////////////////////////////////////////////////////// + +typedef enum dcUTF8Mode +{ + DC_UTF8_ILLEGAL = 0x0000, // Illegale UTF8 Zeichen-Kombination + + DC_UTF8_1CHAR = 0x0001, // Das Zeichen ist ein Einzelzeichen + + DC_UTF8_2CHAR = 0x0002, // Beginn einer 2-Zeichen Sequenz + DC_UTF8_CONT_22 = 0x0004, // ein Fortsetzungszeichen an Pos 2 einer 2-er Sequenz + + DC_UTF8_3CHAR = 0x0008, // Beginn einer 3-Zeichen Sequenz + DC_UTF8_CONT_23 = 0x0010, // ein Fortsetzungszeichen an Pos 2 einer 3-er Sequenz + DC_UTF8_CONT_33 = 0x0020, // ein Fortsetzungszeichen an Pos 3 einer 3-er Sequenz + + DC_UTF8_4CHAR = 0x0040, // Beginn einer 4-Zeichen Sequenz + DC_UTF8_CONT_24 = 0x0080, // ein Fortsetzungszeichen an Pos 2 einer 4-er Sequenz + DC_UTF8_CONT_34 = 0x0100, // ein Fortsetzungszeichen an Pos 3 einer 4-er Sequenz + DC_UTF8_CONT_44 = 0x0200, // ein Fortsetzungszeichen an Pos 4 einer 4-er Sequenz + + DC_UTF8_CONT_ANY = 0x0400, // ein Fortsetzungszeichen an beliebger Stelle + + DC_UTF8_1CHAR_POSSIBLE = 0x0800, // als Einzelzeichen darstellbar + DC_UTF8_2CHAR_POSSIBLE = 0x1000, // als 2-er Sequenz darstellbar + DC_UTF8_3CHAR_POSSIBLE = 0x2000, // als 3-er Sequenz darstellbar + DC_UTF8_4CHAR_POSSIBLE = 0x4000, // als 4-er Sequenz darstellbar + +} dcUTF8Mode; + +/////////////////////////////////////////////////////////////////////////////// + +extern const unsigned short TableUTF8Mode[0x100]; +static inline dcUTF8Mode CheckUTF8Mode ( unsigned char ch ) + { return (dcUTF8Mode)TableUTF8Mode[ch]; } + +int GetUTF8CharLength ( u32 code ); +char * NextUTF8Char ( ccp str ); +char * NextUTF8CharE ( ccp str, ccp end ); +char * PrevUTF8Char ( ccp str ); +char * PrevUTF8CharB ( ccp str, ccp begin ); +u32 GetUTF8Char ( ccp str ); +u32 ScanUTF8Char ( ccp * str ); +u32 ScanUTF8CharE ( ccp * str, ccp end ); +u32 ScanUTF8CharInc ( ccp * str ); +u32 ScanUTF8CharIncE ( ccp * str, ccp end ); +u32 GetUTF8AnsiChar ( ccp str ); +u32 ScanUTF8AnsiChar ( ccp * str ); +u32 ScanUTF8AnsiCharE ( ccp * str, ccp end ); +int ScanUTF8Length ( ccp str, ccp end ); +int CalcUTF8PrintFW ( ccp str, ccp end, uint wanted_fw ); + +char * PrintUTF8Char ( char * buf, u32 code ); +char * PrintUTF8CharToCircBuf ( u32 code ); + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct dcUnicodeTripel +{ + u32 code1; + u32 code2; + u32 code3; + +} dcUnicodeTripel; + +extern const dcUnicodeTripel TableUnicodeDecomp[]; +const dcUnicodeTripel * DecomposeUnicode ( u32 code ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // WIN_DCLIB +#endif // DCLIB_UTF8_H + diff --git a/project/dclib/dclib-vector.c b/project/dclib/dclib-vector.c new file mode 100644 index 0000000..070d719 --- /dev/null +++ b/project/dclib/dclib-vector.c @@ -0,0 +1,2582 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include + +#include "dclib-basics.h" +#include "dclib-numeric.h" +#include "dclib-debug.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// float3 list support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeF3L +( + float3List_t *f3l, // valid data + uint bin_search // >0: enable binary search + // and allocate 'bin_search' elements +) +{ + DASSERT(f3l); + memset(f3l,0,sizeof(*f3l)); + if (bin_search) + { + f3l->size = bin_search; + f3l->list = MALLOC( f3l->size * sizeof(*f3l->list) ); + f3l->sort = MALLOC( f3l->size * sizeof(*f3l->sort) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetF3L ( float3List_t * f3l ) +{ + DASSERT(f3l); + FREE(f3l->list); + FREE(f3l->sort); + memset(f3l,0,sizeof(*f3l)); +} + +/////////////////////////////////////////////////////////////////////////////// + +float3 * AppendF3L ( float3List_t * f3l ) +{ + DASSERT( f3l ); + DASSERT( f3l->used <= f3l->size ); + DASSERT( !f3l->list == !f3l->size ); + + if ( f3l->used == f3l->size ) + { + f3l->size = 3*f3l->size/2 + 100; + f3l->list = REALLOC( f3l->list, f3l->size * sizeof(*f3l->list) ); + if (f3l->sort) + f3l->sort = REALLOC( f3l->sort, f3l->size * sizeof(*f3l->sort) ); + } + + DASSERT( f3l->list ); + DASSERT( f3l->used < f3l->size ); + + if (f3l->sort) + f3l->sort[f3l->used] = 0; + + float3 * item = f3l->list + f3l->used++; + item->x = item->y = item->z = 0.0; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindInsertFloatF3L ( float3List_t * f3l, float3 *val, bool fast ) +{ + DASSERT( f3l ); + DASSERT( f3l->used <= f3l->size ); + DASSERT( !f3l->list == !f3l->size ); + DASSERT( val ); + + int beg = 0; + if (!fast) + { + if (f3l->sort) + { + int end = f3l->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + DASSERT( idx >= 0 && idx < f3l->used ); + DASSERT_MSG( f3l->sort[idx] < f3l->used, + "idx=%d, sort-idx=%d, used=%u, size=%u\n", + idx, f3l->sort[idx], f3l->used, f3l->size ); + int stat = memcmp( val,f3l->list + f3l->sort[idx], sizeof(*val) ); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + return f3l->sort[idx]; + } + } + else + { + float3 *ptr = f3l->list; + float3 *end = ptr + f3l->used; + for ( ; ptr < end; ptr++ ) + if (!memcmp(val,ptr,sizeof(*val))) + return ptr - f3l->list; + } + } + else if (f3l->sort) + { + // 'sort' not needed + BINGO; // don't remove this line! + FREE(f3l->sort); + f3l->sort = 0; + } + + float3 *res = AppendF3L(f3l); + DASSERT(res); + memcpy(res,val,sizeof(*res)); + + const uint idx = f3l->used - 1; + DASSERT( idx < f3l->used ); + if (f3l->sort) + { + DASSERT( beg >= 0 && beg <= idx ); + uint *dest = f3l->sort + beg; + memmove( dest+1, dest, (idx-beg)*sizeof(*f3l->sort) ); + *dest = idx; + + #if HAVE_PRINT0 + if ( f3l->used < 10 ) + { + uint i; + for ( i = 0; i < f3l->used; i++ ) + { + float3 *f = f3l->list + f3l->sort[i]; + PRINT("INS %2u: %2u -> %08x %08x %08x %11.5f %11.5f %11.5f\n", + i, f3l->sort[i], + be32(&f->x), be32(&f->y), be32(&f->z), + f->x, f->y, f->z ); + } + PRINT("---\n"); + } + #endif + } + + return idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +float3 * GrowF3L ( float3List_t * f3l, uint n ) +{ + DASSERT( f3l ); + DASSERT( f3l->used <= f3l->size ); + DASSERT( !f3l->list == !f3l->size ); + + const uint new_used = f3l->used + n; + if ( new_used > f3l->size || !f3l->size ) + { + f3l->size = new_used ? new_used : 10; + f3l->list = REALLOC( f3l->list, f3l->size * sizeof(*f3l->list)) ; + } + + DASSERT( f3l->list ); + DASSERT( f3l->used + n <= f3l->size ); + + float3 * result = f3l->list + f3l->used; + f3l->used = new_used; + return result; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// double3 list support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeD3L ( double3List_t * d3l ) +{ + DASSERT(d3l); + memset(d3l,0,sizeof(*d3l)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetD3L ( double3List_t * d3l ) +{ + DASSERT(d3l); + FREE(d3l->list); + memset(d3l,0,sizeof(*d3l)); +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 * AppendD3L ( double3List_t * d3l ) +{ + DASSERT( d3l ); + DASSERT( d3l->used <= d3l->size ); + DASSERT( !d3l->list == !d3l->size ); + + if ( d3l->used == d3l->size ) + { + d3l->size = 3*d3l->size/2 + 100; + d3l->list = REALLOC( d3l->list, d3l->size * sizeof(*d3l->list)) ; + } + + DASSERT( d3l->list ); + DASSERT( d3l->used < d3l->size ); + + double3 * item = d3l->list + d3l->used++; + item->x = item->y = item->z = 0.0; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindInsertFloatD3L ( double3List_t * d3l, double3 *val, bool fast ) +{ + DASSERT( d3l ); + DASSERT( d3l->used <= d3l->size ); + DASSERT( !d3l->list == !d3l->size ); + DASSERT( val ); + + // first: reduce precision + double3 temp; + temp.x = double2float(val->x); + temp.y = double2float(val->y); + temp.z = double2float(val->z); + + if (!fast) + { + double3 *ptr = d3l->list; + double3 *end = ptr + d3l->used; + for ( ; ptr < end; ptr++ ) + if (!memcmp(&temp,ptr,sizeof(temp))) + return ptr - d3l->list; + } + + double3 *res = AppendD3L(d3l); + DASSERT(res); + memcpy(res,&temp,sizeof(*res)); + return d3l->used - 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 * GrowD3L ( double3List_t * d3l, uint n ) +{ + DASSERT( d3l ); + DASSERT( d3l->used <= d3l->size ); + DASSERT( !d3l->list == !d3l->size ); + + const uint new_used = d3l->used + n; + if ( new_used > d3l->size || !d3l->size ) + { + d3l->size = new_used ? new_used : 10; + d3l->list = REALLOC( d3l->list, d3l->size * sizeof(*d3l->list)) ; + } + + DASSERT( d3l->list ); + DASSERT( d3l->used + n <= d3l->size ); + + double3 * result = d3l->list + d3l->used; + d3l->used = new_used; + return result; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check numbers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool IsNormalF3 ( float *f3 ) +{ + DASSERT(f3); + const int fpclass = fpclassify(*f3++); + if ( fpclass == FP_NORMAL || fpclass == FP_ZERO ) + { + const int fpclass = fpclassify(*f3++); + if ( fpclass == FP_NORMAL || fpclass == FP_ZERO ) + { + const int fpclass = fpclassify(*f3); + return fpclass == FP_NORMAL || fpclass == FP_ZERO; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsNormalF3be ( float *f3 ) +{ + DASSERT(f3); + const int fpclass = fpclassify(bef4(f3)); + if ( fpclass == FP_NORMAL || fpclass == FP_ZERO ) + { + const int fpclass = fpclassify(bef4(++f3)); + if ( fpclass == FP_NORMAL || fpclass == FP_ZERO ) + { + const int fpclass = fpclassify(bef4(++f3)); + return fpclass == FP_NORMAL || fpclass == FP_ZERO; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsNormalD3 ( double *d3 ) +{ + DASSERT(d3); + const int fpclass = fpclassify(*d3++); + if ( fpclass == FP_NORMAL || fpclass == FP_ZERO ) + { + const int fpclass = fpclassify(*d3++); + if ( fpclass == FP_NORMAL || fpclass == FP_ZERO ) + { + const int fpclass = fpclassify(*d3); + return fpclass == FP_NORMAL || fpclass == FP_ZERO; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsEqualD3 ( const double3 *a, const double3 *b, + double null_epsilon, double diff_epsilon ) +{ + DASSERT(a); + DASSERT(b); + DASSERT( null_epsilon > 0.0 ); + DASSERT( diff_epsilon > 0.0 ); + + double diff; + + diff = fabs( a->x - b->x ); + if ( diff > null_epsilon ) + { + const double abs_a = fabs(a->x); + const double abs_b = fabs(b->x); + if ( diff > ( abs_a > abs_b ? abs_a : abs_b ) * diff_epsilon ) + return false; + } + + diff = fabs( a->y - b->y ); + if ( diff > null_epsilon ) + { + const double abs_a = fabs(a->y); + const double abs_b = fabs(b->y); + if ( diff > ( abs_a > abs_b ? abs_a : abs_b ) * diff_epsilon ) + return false; + } + + diff = fabs( a->z - b->z ); + if ( diff > null_epsilon ) + { + const double abs_a = fabs(a->z); + const double abs_b = fabs(b->z); + if ( diff > ( abs_a > abs_b ? abs_a : abs_b ) * diff_epsilon ) + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsSameF ( float a, float b, int bit_diff ) +{ + float delta = ldexpf( fabsf(a-b), 23-bit_diff ); + return IsNormalF(delta) && delta <= fabsf(a) && delta <= fabsf(b); +} + +bool IsSameD ( double a, double b, int bit_diff ) +{ + double delta = ldexp( fabs(a-b), 52-bit_diff ); + return IsNormalD(delta) && delta <= fabs(a) && delta <= fabs(b); +} + +bool IsSameF3 ( const float *a, const float *b, int bit_diff ) +{ + return IsSameF(a[0],b[0],bit_diff) + && IsSameF(a[1],b[1],bit_diff) + && IsSameF(a[2],b[2],bit_diff); +} + +bool IsSameD3 ( const double *a, const double *b, int bit_diff ) +{ + return IsSameD(a[0],b[0],bit_diff) + && IsSameD(a[1],b[1],bit_diff) + && IsSameD(a[2],b[2],bit_diff); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 2D vector functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint PointsInConvexPolygonF +( + // return, how many points are inside the polygon + + float *pts, // points: pointer to a array with '2*n_pts' elements + uint n_pts, // Number of (x,y) pairs in 'pts' + + float *pol, // polygon: pointer to a array with '2*n_pol' elements + uint n_pol, // number of (x,y) pairs in 'pol' + + bool all, // false: count the numbers of points inside + // true: return '1' for all points inside and otherwise '0' + + int *r_dir // If not NULL then return: + // -1: polygon is defined counterclockwise + // 0: unknown + // +1: polygon is defined clockwise + // If at least one point is inside, the result is known. +) +{ + if ( !n_pts || n_pol < 3 ) + return 0; + DASSERT(pts); + DASSERT(pol); + + int pol_dir = 0; + uint count = 0; + for(;;) + { + float *a = pol + 2*(n_pol-1); + float *b = pol; + int dir = pol_dir; + uint i = n_pol; + for(;;) + { + const double dstat = SideOfLine(a[0],a[1],b[0],b[1],pts[0],pts[1]); + const int new_dir = dstat > 0.0 ? -1 : dstat < 0; + noPRINT("STAT: %2d %2d %2d %4g\n", pol_dir, dir, new_dir, dstat ); + if (!dir) + dir = new_dir; + else if ( new_dir && dir != new_dir ) + { + if (!all) + break; + if (r_dir) + *r_dir = pol_dir; + return 0; + } + + if ( --i > 0 ) + { + a = b; + b += 2; + continue; + } + + if (!pol_dir) + pol_dir = dir; + count++; + break; + } + + if ( --n_pts > 0 ) + { + pts += 2; + continue; + } + + if (r_dir) + *r_dir = pol_dir; + noPRINT("DIR: %2d\n",pol_dir); + return all ? 1 : count; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PointsInConvexPolygonD +( + // return, how many points are inside the polygon + + double *pts, // points: pointer to a array with '2*n_pts' elements + uint n_pts, // Number of (x,y) pairs in 'pts' + + double *pol, // polygon: pointer to a array with '2*n_pol' elements + uint n_pol, // number of (x,y) pairs in 'pol' + + bool all, // false: count the numbers of points inside + // true: return '1' for all points inside and otherwise '0' + + int *r_dir // If not NULL then return: + // -1: polygon is defined counterclockwise + // 0: unknown + // +1: polygon is defined clockwise + // If at least one point is inside, the result is known. +) +{ + if ( !n_pts || n_pol < 3 ) + return 0; + DASSERT(pts); + DASSERT(pol); + + int pol_dir = 0; + uint count = 0; + for(;;) + { + double *a = pol + 2*(n_pol-1); + double *b = pol; + int dir = pol_dir; + uint i = n_pol; + for(;;) + { + const double dstat = SideOfLine(a[0],a[1],b[0],b[1],pts[0],pts[1]); + const int new_dir = dstat > 0.0 ? -1 : dstat < 0; + noPRINT("STAT: %2d %2d %2d %4g\n", pol_dir, dir, new_dir, dstat ); + if (!dir) + dir = new_dir; + else if ( new_dir && dir != new_dir ) + { + if (!all) + break; + if (r_dir) + *r_dir = pol_dir; + return 0; + } + + if ( --i > 0 ) + { + a = b; + b += 2; + continue; + } + + if (!pol_dir) + pol_dir = dir; + count++; + break; + } + + if ( --n_pts > 0 ) + { + pts += 2; + continue; + } + + if (r_dir) + *r_dir = pol_dir; + noPRINT("DIR: %2d\n",pol_dir); + return all ? 1 : count; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// float34 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ClearFloat34 ( float34 *f34 ) +{ + DASSERT(f34); + float *d = f34->v; + uint n; + for ( n = 0; n < 12; n++ ) + *d++ = 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupFloat34 ( float34 *f34 ) +{ + DASSERT(f34); + float *d = f34->v; + uint n; + for ( n = 0; n < 12; n++ ) + *d++ = 0.0; + f34->m[0][0] = f34->m[1][1] = f34->m[2][2] = 1.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupMatrixF34 +( + float34 *mat, // data to setup + float3 *scale, // NULL or scaling vector + float3 *rotate, // NULL or rotation vector in *radiant* + float3 *translate // NULL or translation vector +) +{ + // This function use 'scale', 'rotate' and 'translate' to calculate the + // transformation matrix. The calculations have been verified against + // original MDL files of Nintendo. The rotation is done in right-handed + // rotation with Euler angles (in degree) and x-y-z convention: + // => http://en.wikipedia.org/wiki/Matrix_rotation#General_rotations + + DASSERT(mat); + float *t = mat->v; + + + //--- setup scale + + float3 myscale; + if (scale) + { + myscale.x = fabs(scale->x) < NULL_EPSILON ? 1.0 : scale->x; + myscale.y = fabs(scale->y) < NULL_EPSILON ? 1.0 : scale->y; + myscale.z = fabs(scale->z) < NULL_EPSILON ? 1.0 : scale->z; + } + else + myscale.x = myscale.y = myscale.z = 1.0; + + + //--- calc translation matrix by scale and rotation + + if (rotate) + { + // be sure, that null values are exact + const double sin_x = fabs(rotate->x) < NULL_EPSILON ? 0.0 : sin( rotate->x ); + const double cos_x = fabs(rotate->x) < NULL_EPSILON ? 1.0 : cos( rotate->x ); + const double sin_y = fabs(rotate->y) < NULL_EPSILON ? 0.0 : sin( rotate->y ); + const double cos_y = fabs(rotate->y) < NULL_EPSILON ? 1.0 : cos( rotate->y ); + const double sin_z = fabs(rotate->z) < NULL_EPSILON ? 0.0 : sin( rotate->z ); + const double cos_z = fabs(rotate->z) < NULL_EPSILON ? 1.0 : cos( rotate->z ); + + t[ 0] = myscale.x * ( cos_y * cos_z ); + t[ 1] = myscale.y * ( - cos_x * sin_z + sin_x * sin_y * cos_z ); + t[ 2] = myscale.z * ( sin_x * sin_z + cos_x * sin_y * cos_z ); + + t[4+0] = myscale.x * ( cos_y * sin_z ); + t[4+1] = myscale.y * ( cos_x * cos_z + sin_x * sin_y * sin_z ); + t[4+2] = myscale.z * ( - sin_x * cos_z + cos_x * sin_y * sin_z ); + + t[8+0] = myscale.x * ( - sin_y ); + t[8+1] = myscale.y * ( sin_x * cos_y ); + t[8+2] = myscale.z * ( cos_x * cos_y ); + } + else + { + t[ 0] = myscale.x; + t[4+1] = myscale.y; + t[8+2] = myscale.z; + + t[ 1] = 0.0; + t[ 2] = 0.0; + t[4+0] = 0.0; + t[4+2] = 0.0; + t[8+0] = 0.0; + t[8+1] = 0.0; + } + + + //--- assign translation + + if (translate) + { + t[ 3] = translate->x; + t[4+3] = translate->y; + t[8+3] = translate->z; + } + else + { + t[ 3] = 0.0; + t[4+3] = 0.0; + t[8+3] = 0.0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcInverseMatrixF34 +( + float34 *i, // store inverse matrix here + const float34 *t // transformation matrix +) +{ + DASSERT(i); + DASSERT(t); + + //--- calc inverse matrix + + const double det = t->v[ 0] * t->v[4+1] * t->v[8+2] + + t->v[ 1] * t->v[4+2] * t->v[8+0] + + t->v[ 2] * t->v[4+0] * t->v[8+1] + - t->v[ 2] * t->v[4+1] * t->v[8+0] + - t->v[ 1] * t->v[4+0] * t->v[8+2] + - t->v[ 0] * t->v[4+2] * t->v[8+1]; + + if ( fabs(det) < NULL_EPSILON ) + { + // this should never happen => initialize inv_matrix with 0.0 + ClearFloat34(i); + } + else + { + const double det1 = 1.0 / det; // multplication is faster than division + + i->v[ 0] = ( t->v[4+1] * t->v[8+2] - t->v[4+2] * t->v[8+1] ) * det1; + i->v[ 1] = ( t->v[ 2] * t->v[8+1] - t->v[ 1] * t->v[8+2] ) * det1; + i->v[ 2] = ( t->v[ 1] * t->v[4+2] - t->v[ 2] * t->v[4+1] ) * det1; + + i->v[4+0] = ( t->v[4+2] * t->v[8+0] - t->v[4+0] * t->v[8+2] ) * det1; + i->v[4+1] = ( t->v[ 0] * t->v[8+2] - t->v[ 2] * t->v[8+0] ) * det1; + i->v[4+2] = ( t->v[ 2] * t->v[4+0] - t->v[ 0] * t->v[4+2] ) * det1; + + i->v[8+0] = ( t->v[4+0] * t->v[8+1] - t->v[4+1] * t->v[8+0] ) * det1; + i->v[8+1] = ( t->v[ 1] * t->v[8+0] - t->v[ 0] * t->v[8+1] ) * det1; + i->v[8+2] = ( t->v[ 0] * t->v[4+1] - t->v[ 1] * t->v[4+0] ) * det1; + } + + //--- calc inverse translation + + i->v[ 3] = - t->v[ 3] * i->v[ 0] + - t->v[4+3] * i->v[ 1] + - t->v[8+3] * i->v[ 2]; + + i->v[4+3] = - t->v[ 3] * i->v[4+0] + - t->v[4+3] * i->v[4+1] + - t->v[8+3] * i->v[4+2]; + + i->v[8+3] = - t->v[ 3] * i->v[8+0] + - t->v[4+3] * i->v[8+1] + - t->v[8+3] * i->v[8+2]; +} + +/////////////////////////////////////////////////////////////////////////////// + +void MultiplyF34 +( + float34 *dest, // valid, store result here, may be 'src1' or 'src2' + const float34 *src1, // first valid source, if NULL use dest + const float34 *src2 // second valid source, if NULL use dest +) +{ + DASSERT(dest); + + if ( dest == src1 || dest == src2 || !src1 || !src2 ) + { + float34 temp; + MultiplyF34( &temp, src1 ? src1 : dest, src2 ? src2 : dest ); + *dest = temp; + return; + } + DASSERT( src1 && src1 != dest ); + DASSERT( src2 && src2 != dest ); + + float *res = dest->v; + const float *a = src1->v; + const float *b = src2->v; + + res[0+0] = a[0+0]*b[0+0] + a[4+0]*b[0+1] + a[8+0]*b[0+2]; + res[0+1] = a[0+1]*b[0+0] + a[4+1]*b[0+1] + a[8+1]*b[0+2]; + res[0+2] = a[0+2]*b[0+0] + a[4+2]*b[0+1] + a[8+2]*b[0+2]; + res[0+3] = a[0+3]*b[0+0] + a[4+3]*b[0+1] + a[8+3]*b[0+2] + b[0+3]; + + res[4+0] = a[0+0]*b[4+0] + a[4+0]*b[4+1] + a[8+0]*b[4+2]; + res[4+1] = a[0+1]*b[4+0] + a[4+1]*b[4+1] + a[8+1]*b[4+2]; + res[4+2] = a[0+2]*b[4+0] + a[4+2]*b[4+1] + a[8+2]*b[4+2]; + res[4+3] = a[0+3]*b[4+0] + a[4+3]*b[4+1] + a[8+3]*b[4+2] + b[4+3]; + + res[8+0] = a[0+0]*b[8+0] + a[4+0]*b[8+1] + a[8+0]*b[8+2]; + res[8+1] = a[0+1]*b[8+0] + a[4+1]*b[8+1] + a[8+1]*b[8+2]; + res[8+2] = a[0+2]*b[8+0] + a[4+2]*b[8+1] + a[8+2]*b[8+2]; + res[8+3] = a[0+3]*b[8+0] + a[4+3]*b[8+1] + a[8+3]*b[8+2] + b[8+3]; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// double34 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ClearDouble34 ( double34 *d34 ) +{ + DASSERT(d34); + double *d = d34->v; + uint n; + for ( n = 0; n < 12; n++ ) + *d++ = 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupDouble34 ( double34 *d34 ) +{ + DASSERT(d34); + double *d = d34->v; + uint n; + for ( n = 0; n < 12; n++ ) + *d++ = 0.0; + d34->m[0][0] = d34->m[1][1] = d34->m[2][2] = 1.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupMatrixD34 +( + double34 *mat, // data to setup + double3 *scale, // NULL or scaling vector + double3 *rotate, // NULL or rotation vector in *radiant* + double3 *translate // NULL or translation vector +) +{ + // This function use 'scale', 'rotate' and 'translate' to calculate the + // transformation matrix. The calculations have been verified against + // original MDL files of Nintendo. The rotation is done in right-handed + // rotation with Euler angles (in degree) and x-y-z convention: + // => http://en.wikipedia.org/wiki/Matrix_rotation#General_rotations + + DASSERT(mat); + double *t = mat->v; + + + //--- setup scale + + double3 myscale; + if (scale) + { + myscale.x = fabs(scale->x) < NULL_EPSILON ? 1.0 : scale->x; + myscale.y = fabs(scale->y) < NULL_EPSILON ? 1.0 : scale->y; + myscale.z = fabs(scale->z) < NULL_EPSILON ? 1.0 : scale->z; + } + else + myscale.x = myscale.y = myscale.z = 1.0; + + + //--- calc translation matrix by scale and rotation + + if (rotate) + { + // be sure, that null values are exact + const double sin_x = fabs(rotate->x) < NULL_EPSILON ? 0.0 : sin( rotate->x ); + const double cos_x = fabs(rotate->x) < NULL_EPSILON ? 1.0 : cos( rotate->x ); + const double sin_y = fabs(rotate->y) < NULL_EPSILON ? 0.0 : sin( rotate->y ); + const double cos_y = fabs(rotate->y) < NULL_EPSILON ? 1.0 : cos( rotate->y ); + const double sin_z = fabs(rotate->z) < NULL_EPSILON ? 0.0 : sin( rotate->z ); + const double cos_z = fabs(rotate->z) < NULL_EPSILON ? 1.0 : cos( rotate->z ); + + t[ 0] = myscale.x * ( cos_y * cos_z ); + t[ 1] = myscale.y * ( - cos_x * sin_z + sin_x * sin_y * cos_z ); + t[ 2] = myscale.z * ( sin_x * sin_z + cos_x * sin_y * cos_z ); + + t[4+0] = myscale.x * ( cos_y * sin_z ); + t[4+1] = myscale.y * ( cos_x * cos_z + sin_x * sin_y * sin_z ); + t[4+2] = myscale.z * ( - sin_x * cos_z + cos_x * sin_y * sin_z ); + + t[8+0] = myscale.x * ( - sin_y ); + t[8+1] = myscale.y * ( sin_x * cos_y ); + t[8+2] = myscale.z * ( cos_x * cos_y ); + } + else + { + t[ 0] = myscale.x; + t[4+1] = myscale.y; + t[8+2] = myscale.z; + + t[ 1] = 0.0; + t[ 2] = 0.0; + t[4+0] = 0.0; + t[4+2] = 0.0; + t[8+0] = 0.0; + t[8+1] = 0.0; + } + + + //--- assign translation + + if (translate) + { + t[ 3] = translate->x; + t[4+3] = translate->y; + t[8+3] = translate->z; + } + else + { + t[ 3] = 0.0; + t[4+3] = 0.0; + t[8+3] = 0.0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcInverseMatrixD34 +( + double34 *i, // store inverse matrix here + const double34 *t // transformation matrix +) +{ + DASSERT(i); + DASSERT(t); + + //--- calc inverse matrix + + const double det = t->v[ 0] * t->v[4+1] * t->v[8+2] + + t->v[ 1] * t->v[4+2] * t->v[8+0] + + t->v[ 2] * t->v[4+0] * t->v[8+1] + - t->v[ 2] * t->v[4+1] * t->v[8+0] + - t->v[ 1] * t->v[4+0] * t->v[8+2] + - t->v[ 0] * t->v[4+2] * t->v[8+1]; + + if ( fabs(det) < NULL_EPSILON ) + { + // this should never happen => initialize inv_matrix with 0.0 + ClearDouble34(i); + } + else + { + const double det1 = 1.0 / det; // multplication is faster than division + + i->v[ 0] = ( t->v[4+1] * t->v[8+2] - t->v[4+2] * t->v[8+1] ) * det1; + i->v[ 1] = ( t->v[ 2] * t->v[8+1] - t->v[ 1] * t->v[8+2] ) * det1; + i->v[ 2] = ( t->v[ 1] * t->v[4+2] - t->v[ 2] * t->v[4+1] ) * det1; + + i->v[4+0] = ( t->v[4+2] * t->v[8+0] - t->v[4+0] * t->v[8+2] ) * det1; + i->v[4+1] = ( t->v[ 0] * t->v[8+2] - t->v[ 2] * t->v[8+0] ) * det1; + i->v[4+2] = ( t->v[ 2] * t->v[4+0] - t->v[ 0] * t->v[4+2] ) * det1; + + i->v[8+0] = ( t->v[4+0] * t->v[8+1] - t->v[4+1] * t->v[8+0] ) * det1; + i->v[8+1] = ( t->v[ 1] * t->v[8+0] - t->v[ 0] * t->v[8+1] ) * det1; + i->v[8+2] = ( t->v[ 0] * t->v[4+1] - t->v[ 1] * t->v[4+0] ) * det1; + } + + //--- calc inverse translation + + i->v[ 3] = - t->v[ 3] * i->v[ 0] + - t->v[4+3] * i->v[ 1] + - t->v[8+3] * i->v[ 2]; + + i->v[4+3] = - t->v[ 3] * i->v[4+0] + - t->v[4+3] * i->v[4+1] + - t->v[8+3] * i->v[4+2]; + + i->v[8+3] = - t->v[ 3] * i->v[8+0] + - t->v[4+3] * i->v[8+1] + - t->v[8+3] * i->v[8+2]; +} + +/////////////////////////////////////////////////////////////////////////////// + +void MultiplyD34 +( + double34 *dest, // valid, store result here, may be 'src1' or 'src2' + const double34 *src1, // first valid source, if NULL use dest + const double34 *src2 // second valid source, if NULL use dest +) +{ + DASSERT(dest); + + if ( dest == src1 || dest == src2 || !src1 || !src2 ) + { + double34 temp; + MultiplyD34( &temp, src1 ? src1 : dest, src2 ? src2 : dest ); + *dest = temp; + return; + } + DASSERT( src1 && src1 != dest ); + DASSERT( src2 && src2 != dest ); + + double *res = dest->v; + const double *a = src1->v; + const double *b = src2->v; + + res[0+0] = a[0+0]*b[0+0] + a[4+0]*b[0+1] + a[8+0]*b[0+2]; + res[0+1] = a[0+1]*b[0+0] + a[4+1]*b[0+1] + a[8+1]*b[0+2]; + res[0+2] = a[0+2]*b[0+0] + a[4+2]*b[0+1] + a[8+2]*b[0+2]; + res[0+3] = a[0+3]*b[0+0] + a[4+3]*b[0+1] + a[8+3]*b[0+2] + b[0+3]; + + res[4+0] = a[0+0]*b[4+0] + a[4+0]*b[4+1] + a[8+0]*b[4+2]; + res[4+1] = a[0+1]*b[4+0] + a[4+1]*b[4+1] + a[8+1]*b[4+2]; + res[4+2] = a[0+2]*b[4+0] + a[4+2]*b[4+1] + a[8+2]*b[4+2]; + res[4+3] = a[0+3]*b[4+0] + a[4+3]*b[4+1] + a[8+3]*b[4+2] + b[4+3]; + + res[8+0] = a[0+0]*b[8+0] + a[4+0]*b[8+1] + a[8+0]*b[8+2]; + res[8+1] = a[0+1]*b[8+0] + a[4+1]*b[8+1] + a[8+1]*b[8+2]; + res[8+2] = a[0+2]*b[8+0] + a[4+2]*b[8+1] + a[8+2]*b[8+2]; + res[8+3] = a[0+3]*b[8+0] + a[4+3]*b[8+1] + a[8+3]*b[8+2] + b[8+3]; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CopyF34toD34 +( + double34 *dest, // destination matrix + const float34 *src // source matrix +) +{ + DASSERT(dest); + DASSERT(src); + + double *d = dest->v; + const float *s = src->v; + + uint n; + for ( n = 0; n < 12; n++ ) + *d++ = *s++; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyD34toF34 +( + float34 *dest, // destination matrix + const double34 *src // source matrix +) +{ + DASSERT(dest); + DASSERT(src); + + float *d = dest->v; + const double *s = src->v; + + uint n; + for ( n = 0; n < 12; n++ ) + *d++ = *s++; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t: setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// statistics +u64 N_MatrixD_forward = 0; // total number of forward transformations +u64 N_MatrixD_inverse = 0; // total number of inverse transformations + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMatrixD ( MatrixD_t * mat ) +{ + DASSERT(mat); + const uint seqnum = mat->valid ? mat->sequence_number : 0; + memset(mat,0,sizeof(*mat)); + mat->sequence_number = seqnum; + + mat->scale.x = mat->scale.y = mat->scale.z = 1.0; + mat->scale_origin.x = mat->scale_origin.y = mat->scale_origin.z = 0.0; + + mat->shift = mat->scale_origin; + mat->rotate_deg = mat->scale_origin; + mat->rotate_rad = mat->scale_origin; + mat->translate = mat->scale_origin; + + double *d = (double*)mat->rotate_origin; + uint n; + for ( n = 0; n < 9; n++ ) + *d++ = 0.0; + + mat->valid = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyMatrixD +( + MatrixD_t *dest, // valid destination + const + MatrixD_t *src // NULL or source +) +{ + DASSERT(dest); + + if ( src && src->valid ) + { + const uint seqnum = dest->sequence_number; + *dest = *src; + dest->sequence_number = seqnum+1; + } + else + InitializeMatrixD(dest); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetScaleMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * scale, // NULL: clear scale, not NULL: set scale + double3 * origin // not NULL: Origin for scaling +) +{ + DASSERT(mat); + if (!mat->valid) + InitializeMatrixD(mat); + mat->norm_valid = mat->tmatrix_valid = mat->imatrix_valid = false; + + if (scale) + mat->scale = *scale; + else + { + mat->scale.x = mat->scale.y = mat->scale.z = 1.0; + origin = 0; + } + + if (origin) + mat->scale_origin = *origin; + else + mat->scale_origin.x = mat->scale_origin.y = mat->scale_origin.z = 0.0; + + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetShiftMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * shift // NULL: clear Shift, not NULL: set Shift +) +{ + DASSERT(mat); + if (!mat->valid) + InitializeMatrixD(mat); + mat->norm_valid = mat->tmatrix_valid = mat->imatrix_valid = false; + + if (shift) + mat->shift = *shift; + else + mat->shift.x = mat->shift.y = mat->shift.z = 0.0; + + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetScaleShiftMatrixD +( + MatrixD_t * mat, // valid data structure + uint xyz, // 0..2: x/y/z coordinate to set + double old1, // old value of first point + double new1, // new value of first point + double old2, // old value of second point + double new2 // new value of second point +) +{ + DASSERT(mat); + if (!mat->valid) + InitializeMatrixD(mat); + + if ( fabs(old1-old2) >= NULL_EPSILON ) // ignore othwerwise + { + mat->norm_valid = mat->tmatrix_valid = mat->imatrix_valid = false; + + const double scale = ( new2 - new1 ) / ( old2 - old1 ); + mat->scale.v[xyz] = scale; + mat->scale_origin.v[xyz] = 0.0; + + // new = old * scale + shift + mat->shift.v[xyz] = new1 - old1 * scale; + } + + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetRotateMatrixD +( + MatrixD_t * mat, // valid data structure + uint xyz, // 0..2: x/y/z coordinate to set + double degree, // angle in degree + double radiant, // angle in radiant + double3 * origin // not NULL: Origin for the rotation +) +{ + DASSERT(mat); + DASSERT(xyz<3); + + if (!mat->valid) + InitializeMatrixD(mat); + mat->norm_valid = mat->tmatrix_valid = mat->imatrix_valid = false; + + mat->rotate_deg.v[xyz] = degree; + mat->rotate_rad.v[xyz] = radiant; + + if (origin) + mat->rotate_origin[xyz] = *origin; + else + { + origin = mat->rotate_origin + xyz; + origin->x = origin->y = origin->z = 0.0; + } + + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetTranslateMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * translate // NULL: clear translate, not NULL: set translate +) +{ + DASSERT(mat); + if (!mat->valid) + InitializeMatrixD(mat); + mat->norm_valid = mat->tmatrix_valid = mat->imatrix_valid = false; + + if (translate) + mat->translate = *translate; + else + mat->translate.x = mat->translate.y = mat->translate.z = 0.0; + + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * CheckStatusMatrixD +( + MatrixD_t * mat // valid data structure +) +{ + DASSERT(mat); + + if (mat->tmatrix_valid) + { + double *t = mat->trans_matrix.v; + + //--- check rotation (only disabled for all axis) + + if ( mat->rotate_enabled & 1 + && fabs( t[4+2] ) < NULL_EPSILON + && fabs( t[8+1] ) < NULL_EPSILON + ) + { + mat->rotate_enabled &= ~1; + t[4+2] = 0.0; + t[8+1] = 0.0; + } + + if ( mat->rotate_enabled & 2 + && fabs( t[ 2] ) < NULL_EPSILON + && fabs( t[8+0] ) < NULL_EPSILON + ) + { + mat->rotate_enabled &= ~2; + t[ 2] = 0.0; + t[8+0] = 0.0; + } + + if ( mat->rotate_enabled & 4 + && fabs( t[ 1] ) < NULL_EPSILON + && fabs( t[4+0] ) < NULL_EPSILON + ) + { + mat->rotate_enabled &= ~4; + t[ 1] = 0.0; + t[4+0] = 0.0; + } + + + //--- check scaling (don't touch scale if roating is enabled) + + if ( mat->scale_enabled & 1 + && !( mat->rotate_enabled & ~1 ) + && fabs( t[ 0] - 1.0 ) < NULL_EPSILON ) + { + t[ 0] = 1.0; + mat->scale_enabled &= ~1; + } + + if ( mat->scale_enabled & 2 + && !( mat->rotate_enabled & ~2 ) + && fabs( t[4+1] - 1.0 ) < NULL_EPSILON ) + { + t[4+1] = 1.0; + mat->scale_enabled &= ~2; + } + + if ( mat->scale_enabled & 4 + && !( mat->rotate_enabled & ~4 ) + && fabs( t[8+2] - 1.0 ) < NULL_EPSILON ) + { + t[8+2] = 1.0; + mat->scale_enabled &= ~4; + } + + + //--- check transformation + + if ( mat->translate_enabled & 1 && fabs( t[ 3] ) < NULL_EPSILON ) + { + t[ 3] = 0.0; + mat->translate_enabled &= ~1; + } + + if ( mat->translate_enabled & 2 && fabs( t[4+3] ) < NULL_EPSILON ) + { + t[4+3] = 0.0; + mat->translate_enabled &= ~2; + } + + if ( mat->translate_enabled & 4 && fabs( t[8+3] ) < NULL_EPSILON ) + { + t[8+3] = 0.0; + mat->translate_enabled &= ~4; + } + + mat->transform_enabled = mat->scale_enabled + | mat->rotate_enabled + | mat->translate_enabled + | mat->use_matrix << 3; + } + + return mat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t: specials /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void TermAssignMatrixD +( + // called after dirext matrix manipulations + MatrixD_t * mat // valid data structure +) +{ + DASSERT(mat); + + mat->valid = true; + mat->norm_valid = false; + mat->tmatrix_valid = true; + mat->imatrix_valid = false; + mat->use_matrix = 2; + mat->scale_enabled = 7; + mat->rotate_enabled = 7; + mat->translate_enabled = 7; + mat->sequence_number++; + + double34 *m = &mat->trans_matrix; + mat->norm_scale.x = m->x[0]; + mat->norm_scale.y = m->y[1]; + mat->norm_scale.z = m->z[2]; + mat->norm_translate.x = m->x[3]; + mat->norm_translate.y = m->y[3]; + mat->norm_translate.z = m->z[3]; + + CheckStatusMatrixD(mat); + + if (!mat->rotate_enabled) + { + mat->use_matrix = 0; + mat->norm_valid = true; + mat->norm_rotate_deg.x = 0; + mat->norm_rotate_deg.y = 0; + mat->norm_rotate_deg.z = 0; + mat->norm_rotate_rad = mat->norm_rotate_deg; + + mat->scale = mat->norm_scale; + mat->translate = mat->norm_translate; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetMatrixD +( + MatrixD_t * mat, // valid data structure + const + double34 * src // NULL or matrix to set +) +{ + DASSERT(mat); + InitializeMatrixD(mat); + if (src) + { + mat->trans_matrix = *src; + TermAssignMatrixD(mat); + } + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetAScaleMatrixD +( + // calculate the matrix as rotation around a specified axis + + MatrixD_t * mat, // valid data structure + double scale, // scale factor + double3 * dir // NULL or scaling direction as vector +) +{ + DASSERT(mat); + InitializeMatrixD(mat); + + if (dir) + { + const double hrot = atan2(dir->x,dir->z); + const double hlen = sqrt( dir->x*dir->x + dir->z*dir->z ); + const double vrot = -atan2(dir->y,hlen); + + PRINT("HROT: %6.4f %7.2f / %11.3f\n",hrot,hrot*180.0/M_PI,hlen); + PRINT("VROT: %6.4f %7.2f\n",vrot,vrot*180.0/M_PI); + + MatrixD_t mrot; + InitializeMatrixD(&mrot); + mrot.rotate_rad.x = vrot; + mrot.rotate_rad.y = hrot; + CalcMatrixD(&mrot,true); + DASSERT(mrot.imatrix_valid); + SetMatrixD(mat,&mrot.inv_matrix); + + double3 scale3; + scale3.x = scale3.y = 0; + scale3.z = scale; + SetScaleMatrixD(&mrot,&scale3,0); + MultiplyMatrixD(mat,0,&mrot,0); + TermAssignMatrixD(mat); + } + + //--- terminate + + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * SetARotateMatrixD +( + // calculate the matrix as rotation around a specified axis + + MatrixD_t * mat, // valid data structure + double degree, // angle in degree + double radiant, // angle in radiant, add to degree with factor + double3 * pt1, // point #1 to define the axis; if NULL use (0,0,0) + double3 * pt2 // point #2 to define the axis; if NULL use (0,0,0) +) +{ + DASSERT(mat); + InitializeMatrixD(mat); + + double3 dir, base; + if (pt1) + base = *pt1; + else + base.x = base.y = base.z = 0.0; + + if (pt2) + Sub3(dir,*pt2,base); + else + { + dir.x = -base.x; + dir.y = -base.y; + dir.z = -base.z; + } + + #if 1 + radiant += degree * (M_PI/180.0); + const double len = Length3(dir); + if ( len >= NULL_EPSILON ) + #else + radiant = fmod( degree * (M_PI/180.0) + radiant + M_PI, 2*M_PI) - M_PI; + const double len = Length3(dir); + if ( len >= NULL_EPSILON && fabs(radiant) >= NULL_EPSILON ) + #endif + { + dir.x /= len; + dir.y /= len; + dir.z /= len; + + const double sin0 = sin(radiant); + const double cos0 = cos(radiant); + const double cos1 = 1.0 - cos0; + + double *t = mat->trans_matrix.v; + + t[ 0] = dir.x * dir.x * cos1 + cos0; + t[ 1] = dir.x * dir.y * cos1 - dir.z * sin0; + t[ 2] = dir.x * dir.z * cos1 + dir.y * sin0; + + t[4+0] = dir.y * dir.x * cos1 + dir.z * sin0; + t[4+1] = dir.y * dir.y * cos1 + cos0; + t[4+2] = dir.y * dir.z * cos1 - dir.x * sin0; + + t[8+0] = dir.z * dir.x * cos1 - dir.y * sin0; + t[8+1] = dir.z * dir.y * cos1 + dir.x * sin0; + t[8+2] = dir.z * dir.z * cos1 + cos0; + + double3 delta = TransformD3D34(&mat->trans_matrix,&base); + t[ 3] = base.x - delta.x; + t[4+3] = base.y - delta.y; + t[8+3] = base.z - delta.z; + + TermAssignMatrixD(mat); + } + + //--- terminate + + return mat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t: calc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * CalcNormMatrixD +( + MatrixD_t * mat // valid data structure +) +{ + DASSERT(mat); + if (!mat->valid) + InitializeMatrixD(mat); + + if ( mat->norm_valid || mat->tmatrix_valid ) + return mat; + + mat->sequence_number++; + mat->norm_valid = true; + mat->scale_enabled = mat->rotate_enabled = mat->translate_enabled = 0; + + + //--- scale & shift + + double3 trans; + + uint xyz; + for ( xyz = 0; xyz < 3; xyz++ ) + { + double scale = mat->scale.v[xyz]; + if ( fabs(scale) < NULL_EPSILON || fabs(scale-1.0) < NULL_EPSILON ) + mat->scale.v[xyz] = scale = 1.0; + else + mat->scale_enabled |= 1 << xyz; + + mat->norm_scale.v[xyz] = scale; + + trans.v[xyz] = mat->shift.v[xyz] + + ( 1.0 - scale ) * mat->scale_origin.v[xyz]; + } + + + //--- rotate + + mat->norm_pos2d.x = mat->norm_pos2d.y = mat->norm_pos2d.z = 0; + + for ( xyz = 0; xyz < 3; xyz++ ) + { + double deg = fmod( mat->rotate_deg.v[xyz] + + mat->rotate_rad.v[xyz] * (180.0/M_PI) + + 180.0, 360.0 ) - 180.0; + if ( fabs(deg) < MIN_DEGREE ) + { + mat->norm_rotate_deg.v[xyz] = 0.0; + mat->norm_rotate_rad.v[xyz] = 0.0; + } + else + { + mat->rotate_enabled |= 1 << xyz; + mat->norm_rotate_deg.v[xyz] = deg; + double rad = deg * (M_PI/180.0); + mat->norm_rotate_rad.v[xyz] = rad; + + // translate = rot_origin + rotated(translate-rot_origin) + + Sub3(trans,trans,mat->rotate_origin[xyz]); + + const uint a = (xyz+2) % 3; + const uint b = (xyz+1) % 3; + rad += atan2(trans.v[a],trans.v[b]); + const double len = sqrt(trans.v[a]*trans.v[a]+trans.v[b]*trans.v[b]); + + trans.v[a] = sin(rad) * len; + trans.v[b] = cos(rad) * len; + + Add3(trans,trans,mat->rotate_origin[xyz]); + + mat->norm_pos2d.v[a] += mat->rotate_origin[xyz].v[a]; + mat->norm_pos2d.v[b] += mat->rotate_origin[xyz].v[b]; + } + } + + if ( (mat->rotate_enabled|1) == 7 ) mat->norm_pos2d.x /= 2; + if ( (mat->rotate_enabled|2) == 7 ) mat->norm_pos2d.y /= 2; + if ( (mat->rotate_enabled|4) == 7 ) mat->norm_pos2d.z /= 2; + + + //--- translate + + mat->translate_enabled = false; + + for ( xyz = 0; xyz < 3; xyz++ ) + { + const double temp = trans.v[xyz] + mat->translate.v[xyz]; + if ( fabs(temp) < NULL_EPSILON ) + mat->norm_translate.v[xyz] = 0.0; + else + { + mat->norm_translate.v[xyz] = temp; + mat->translate_enabled |= 1 << xyz; + } + } + + + //--- status + + if ( mat->use_matrix < 2 ) + { + mat->use_matrix = mat->rotate_enabled ? 1 : 0; + mat->tmatrix_valid = mat->imatrix_valid = false; + } + + mat->transform_enabled = mat->scale_enabled + | mat->rotate_enabled + | mat->translate_enabled + | mat->use_matrix << 3; + + TRACE("%%%%%%%%%% CalcNormMatrixD(%p) => %u|%u|%u = %02x %%%%%%%%%%\n", + mat, mat->scale_enabled, mat->rotate_enabled, + mat->translate_enabled, mat->transform_enabled ); + + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * CalcTransMatrixD +( + MatrixD_t * mat, // valid data structure + bool always // false: calc transformation matrix only + // if needed for transformation + // true: calc matrix always + // if 'tmatrix_valid', the matrix is never calculated +) +{ + DASSERT(mat); + CalcNormMatrixD(mat); + + if ( mat->tmatrix_valid || !always && !mat->use_matrix ) + return mat; + + TRACE("%%%%%%%%%% CalcTransMatrixD(%p,%d) => %u|%u|%u = %02x %%%%%%%%%%\n", + mat, always, mat->scale_enabled, mat->rotate_enabled, + mat->translate_enabled, mat->transform_enabled ); + DASSERT(mat->use_matrix<2); + + SetupMatrixD34( &mat->trans_matrix, + &mat->norm_scale, + &mat->norm_rotate_rad, + &mat->norm_translate ); + + mat->tmatrix_valid = true; + mat->imatrix_valid = false; + CheckStatusMatrixD(mat); + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * CalcMatrixD +( + MatrixD_t * mat, // valid data structure + bool always // false: calc both matrices only + // if needed for transformation + // true: calc matrices always +) +{ + DASSERT(mat); + + if ( mat->imatrix_valid || !always && !mat->use_matrix ) + return mat; + + CalcTransMatrixD(mat,true); + + TRACE("%%%%%%%%%% CalcMatrixD (%p,%d) => %u|%u|%u = %02x %%%%%%%%%%\n", + mat, always, mat->scale_enabled, mat->rotate_enabled, + mat->translate_enabled, mat->transform_enabled ); + + CalcInverseMatrixD34(&mat->inv_matrix,&mat->trans_matrix); + mat->imatrix_valid = true; + return mat; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * MultiplyMatrixD +( + MatrixD_t *dest, // valid, store result here, may be 'src1' or 'src2' + MatrixD_t *src1, // first valid source, if NULL use dest + MatrixD_t *src2, // second valid source, if NULL use dest + uint inverse_mode // bit field: + // 1: use inverse matrix of src1 + // 2: use inverse matrix of src2 +) +{ + DASSERT(dest); + + if ( dest == src1 || dest == src2 || !src1 || !src2 ) + { + MatrixD_t temp; + MultiplyMatrixD( &temp, src1 ? src1 : dest, src2 ? src2 : dest, inverse_mode ); + *dest = temp; + return dest; + } + DASSERT( src1 && src1 != dest ); + DASSERT( src2 && src2 != dest ); + + CalcMatrixD(src1,true); + CalcMatrixD(src2,true); + InitializeMatrixD(dest); + MultiplyD34( &dest->trans_matrix, + inverse_mode & 1 ? &src1->inv_matrix : &src1->trans_matrix, + inverse_mode & 2 ? &src2->inv_matrix : &src2->trans_matrix ); + + + //--- setup normals, simply assumption + + dest->norm_scale.x = dest->trans_matrix.m[0][0]; + dest->norm_scale.y = dest->trans_matrix.m[1][1]; + dest->norm_scale.z = dest->trans_matrix.m[2][2]; + + dest->norm_rotate_deg.x = src1->norm_rotate_deg.x + src2->norm_rotate_deg.x; + dest->norm_rotate_deg.y = src1->norm_rotate_deg.y + src2->norm_rotate_deg.y; + dest->norm_rotate_deg.z = src1->norm_rotate_deg.z + src2->norm_rotate_deg.z; + Mult3f(dest->norm_rotate_rad,dest->norm_rotate_deg,M_PI/180.0); + + dest->norm_translate.x = dest->trans_matrix.m[0][3]; + dest->norm_translate.y = dest->trans_matrix.m[1][3]; + dest->norm_translate.z = dest->trans_matrix.m[2][3]; + + + //--- setup base values + + dest->scale = dest->norm_scale; + dest->rotate_deg = dest->norm_rotate_deg; + dest->translate = dest->norm_translate; + + + //--- terminate + + TermAssignMatrixD(dest); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +MatrixD_t * CalcVectorsMatrixD +( + MatrixD_t * mat, // valid data structure + bool always // false: calc vectors only if matrix is valid + // true: calc matrix if invalid & vectors always +) +{ + DASSERT(mat); + // search: Decomposing a rotation matrix + + if ( !always && !mat->tmatrix_valid ) + return mat; + + CalcMatrixD(mat,false); + + // [[2do]] ??? calculate scale + //mat->norm_scale.x = 1.0; + //mat->norm_scale.y = 1.0; + //mat->norm_scale.z = 1.0; + + double (*m)[4] = mat->trans_matrix.m; + + // [[2do]] ??? works only for scale == 1 ==> + mat->norm_rotate_rad.x = atan2( m[2][1], m[2][2] ); + mat->norm_rotate_rad.y = atan2( -m[2][0], sqrt( m[2][1]*m[2][1] + m[2][2]*m[2][2] ) ); + mat->norm_rotate_rad.z = atan2( m[1][0], m[0][0] ); + + mat->norm_rotate_deg.x = rad2deg(mat->norm_rotate_rad.x); + mat->norm_rotate_deg.y = rad2deg(mat->norm_rotate_rad.y); + mat->norm_rotate_deg.z = rad2deg(mat->norm_rotate_rad.z); + + mat->norm_translate.x = m[0][3]; + mat->norm_translate.y = m[1][3]; + mat->norm_translate.z = m[2][3]; + + return mat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t: double transform /////////////// +/////////////////////////////////////////////////////////////////////////////// + +double3 TransformD3MatrixD +( + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +) +{ + DASSERT(mat); + DASSERT(val); + + N_MatrixD_forward++; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->tmatrix_valid) + CalcTransMatrixD(mat,true); + return TransformD3D34(&mat->trans_matrix,val); + } + + double3 res; + res.x = val->x * mat->norm_scale.x + mat->norm_translate.x; + res.y = val->y * mat->norm_scale.y + mat->norm_translate.y; + res.z = val->z * mat->norm_scale.z + mat->norm_translate.z; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 InvTransformD3MatrixD +( + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +) +{ + DASSERT(mat); + DASSERT(val); + + N_MatrixD_inverse++; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->imatrix_valid) + CalcMatrixD(mat,true); + return TransformD3D34(&mat->inv_matrix,val); + } + + double3 res; + res.x = ( val->x - mat->norm_translate.x ) / mat->norm_scale.x; + res.y = ( val->y - mat->norm_translate.y ) / mat->norm_scale.y; + res.z = ( val->z - mat->norm_translate.z ) / mat->norm_scale.z; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void TransformD3NMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +) +{ + DASSERT(mat); + DASSERT(val); + DASSERT( n <= 1 || off >= sizeof(*val) ); + + N_MatrixD_forward += n; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->tmatrix_valid) + CalcTransMatrixD(mat,true); + + while ( n-- > 0 ) + { + *val = TransformD3D34(&mat->trans_matrix,val); + val = (double3*)( (u8*)val + off ); + } + } + else if (mat->transform_enabled) + { + while ( n-- > 0 ) + { + val->x = val->x * mat->norm_scale.x + mat->norm_translate.x; + val->y = val->y * mat->norm_scale.y + mat->norm_translate.y; + val->z = val->z * mat->norm_scale.z + mat->norm_translate.z; + val = (double3*)( (u8*)val + off ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InvTransformD3NMatrixD +( + MatrixD_t * mat, // valid data structure + double3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +) +{ + DASSERT(mat); + DASSERT(val); + DASSERT( n <= 1 || off >= sizeof(*val) ); + + N_MatrixD_inverse += n; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->imatrix_valid) + CalcMatrixD(mat,true); + + while ( n-- > 0 ) + { + *val = TransformD3D34(&mat->inv_matrix,val); + val = (double3*)( (u8*)val + off ); + } + } + else if (mat->transform_enabled) + { + double3 f; // multiplication is faster than division + f.x = 1.0 / mat->norm_scale.x; + f.y = 1.0 / mat->norm_scale.y; + f.z = 1.0 / mat->norm_scale.z; + + while ( n-- > 0 ) + { + val->x = ( val->x - mat->norm_translate.x ) * f.x; + val->y = ( val->y - mat->norm_translate.y ) * f.y; + val->z = ( val->z - mat->norm_translate.z ) * f.z; + val = (double3*)( (u8*)val + off ); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t: float transform /////////////// +/////////////////////////////////////////////////////////////////////////////// + +float3 TransformF3MatrixD +( + MatrixD_t * mat, // valid data structure + const + float3 * val // value to transform +) +{ + DASSERT(mat); + DASSERT(val); + + N_MatrixD_forward++; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->tmatrix_valid) + CalcTransMatrixD(mat,true); + return TransformF3D34(&mat->trans_matrix,val); + } + + float3 res; + res.x = val->x * mat->norm_scale.x + mat->norm_translate.x; + res.y = val->y * mat->norm_scale.y + mat->norm_translate.y; + res.z = val->z * mat->norm_scale.z + mat->norm_translate.z; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +float3 InvTransformF3MatrixD +( + MatrixD_t * mat, // valid data structure + const + float3 * val // value to transform +) +{ + DASSERT(mat); + DASSERT(val); + + N_MatrixD_inverse++; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->imatrix_valid) + CalcMatrixD(mat,true); + return TransformF3D34(&mat->inv_matrix,val); + } + + float3 res; + res.x = ( val->x - mat->norm_translate.x ) / mat->norm_scale.x; + res.y = ( val->y - mat->norm_translate.y ) / mat->norm_scale.y; + res.z = ( val->z - mat->norm_translate.z ) / mat->norm_scale.z; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void TransformF3NMatrixD +( + MatrixD_t * mat, // valid data structure + float3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +) +{ + DASSERT(mat); + DASSERT(val); + DASSERT_MSG( n <= 1 || off >= sizeof(*val), "n=%u,off=%u\n",n,off ); + + N_MatrixD_forward += n; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->tmatrix_valid) + CalcTransMatrixD(mat,true); + + while ( n-- > 0 ) + { + *val = TransformF3D34(&mat->trans_matrix,val); + val = (float3*)( (u8*)val + off ); + } + } + else if (mat->transform_enabled) + { + while ( n-- > 0 ) + { + val->x = val->x * mat->norm_scale.x + mat->norm_translate.x; + val->y = val->y * mat->norm_scale.y + mat->norm_translate.y; + val->z = val->z * mat->norm_scale.z + mat->norm_translate.z; + val = (float3*)( (u8*)val + off ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InvTransformF3NMatrixD +( + MatrixD_t * mat, // valid data structure + float3 * val, // value array to transform + int n, // number of 3D vectors in 'val' + uint off // if n>1: offset from one to next 'val' vector +) +{ + DASSERT(mat); + DASSERT(val); + DASSERT( n <= 1 || off >= sizeof(*val) ); + + N_MatrixD_inverse += n; + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if (mat->use_matrix) + { + if (!mat->imatrix_valid) + CalcMatrixD(mat,true); + + while ( n-- > 0 ) + { + *val = TransformF3D34(&mat->inv_matrix,val); + val = (float3*)( (u8*)val + off ); + } + } + else if (mat->transform_enabled) + { + double3 f; // multiplication is faster than division + f.x = 1.0 / mat->norm_scale.x; + f.y = 1.0 / mat->norm_scale.y; + f.z = 1.0 / mat->norm_scale.z; + + while ( n-- > 0 ) + { + val->x = ( val->x - mat->norm_translate.x ) * f.x; + val->y = ( val->y - mat->norm_translate.y ) * f.y; + val->z = ( val->z - mat->norm_translate.z ) * f.z; + val = (float3*)( (u8*)val + off ); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t: direct transform /////////////// +/////////////////////////////////////////////////////////////////////////////// + +double3 BaseTransformD3MatrixD +( + // transform using the base parameters + + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +) +{ + DASSERT(mat); + DASSERT(val); + + if (!mat->valid) + InitializeMatrixD(mat); + + if ( mat->use_matrix > 1 ) + return TransformD3MatrixD(mat,val); + + double3 rot = mat->rotate_rad; + Mult3f(rot,rot,180.0/M_PI); + Add3(rot,rot,mat->rotate_deg); + + return TransformHelperD3MatrixD + ( &mat->scale, + &mat->scale_origin, + &mat->shift, + &rot, + mat->rotate_origin+0, + mat->rotate_origin+1, + mat->rotate_origin+2, + &mat->translate, + val + ); +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 NormTransformD3MatrixD +( + // transform using the norm parameters + + MatrixD_t * mat, // valid data structure + const + double3 * val // value to transform +) +{ + DASSERT(mat); + DASSERT(val); + + if ( !mat->norm_valid && !mat->tmatrix_valid ) + CalcNormMatrixD(mat); + + if ( mat->use_matrix > 1 ) + return TransformD3MatrixD(mat,val); + + return TransformHelperD3MatrixD + ( &mat->norm_scale, + 0, + 0, + &mat->norm_rotate_deg, + 0, + 0, + 0, + &mat->norm_translate, + val + ); +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 TransformHelperD3MatrixD +( + // transform using vectors + + double3 *scale, // NULL or scale vector + double3 *scale_origin, // NULL or origin for scaling + double3 *shift, // NULL or shift vector + double3 *rotate_deg, // NULL or rotation in degree + double3 *xrot_origin, // NULL or origin for x-rotation + double3 *yrot_origin, // NULL or origin for y-rotation + double3 *zrot_origin, // NULL or origin for z-rotation + double3 *translate, // NULL or translation vector + + const + double3 * val // value to transform +) +{ + DASSERT(val); + + double3 res; //, null3 = { 0.0, 0.0, 0.0 }; + + + //--- scale + + if (!scale) + res = *val; + else if (scale_origin) + { + res.x = ( val->x - scale_origin->x ) * scale->x + scale_origin->x; + res.y = ( val->y - scale_origin->y ) * scale->y + scale_origin->y; + res.z = ( val->z - scale_origin->z ) * scale->z + scale_origin->z; + } + else + { + res.x = val->x * scale->x; + res.y = val->y * scale->y; + res.z = val->z * scale->z; + } + + + //--- shift + + if (shift) + { + res.x += shift->x; + res.y += shift->y; + res.z += shift->z; + } + + + //--- rotate + + if (rotate_deg) + { + uint xyz; + for ( xyz = 0; xyz < 3; xyz++ ) + { + const double deg = fmod(rotate_deg->v[xyz]+180.0,360.0) - 180.0; + if ( fabs(deg) >= MIN_DEGREE ) + { + uint a = xyz + 2; if ( a >= 3 ) a -= 3; + uint b = xyz + 1; if ( b >= 3 ) b -= 3; + double rad = deg * (M_PI/180.0); + + if (xrot_origin) + { + const double da = res.v[a] - xrot_origin->v[a]; + const double db = res.v[b] - xrot_origin->v[b]; + rad += atan2(da,db); + const double len = sqrt(da*da+db*db); + + res.v[a] = sin(rad) * len + xrot_origin->v[a]; + res.v[b] = cos(rad) * len + xrot_origin->v[b]; + } + else + { + rad += atan2(res.v[a],res.v[b]); + const double len = sqrt(res.v[a]*res.v[a]+res.v[b]*res.v[b]); + + res.v[a] = sin(rad) * len; + res.v[b] = cos(rad) * len; + } + } + xrot_origin = yrot_origin; + yrot_origin = zrot_origin; + } + } + + + //--- translate + + if (translate) + { + res.x += translate->x; + res.y += translate->y; + res.z += translate->z; + } + + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatrixD_t: printing /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp print_matrix_stat + ( MatrixD_t * mat, char * buf, uint bufsize, bool for_inverse ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + + char *dest = buf, *bufend = buf + bufsize; + if (mat->scale_enabled) + dest = StringCopyE(dest,bufend,"+scale"); + if (mat->rotate_enabled) + dest = StringCopyE(dest,bufend,"+rotate"); + if (mat->translate_enabled) + dest = StringCopyE(dest,bufend,"+translate"); + + if ( mat->use_matrix > 1 && !for_inverse ) + dest = StringCopyE(dest,bufend,",source"); + else if (mat->use_matrix) + dest = StringCopyE(dest,bufend,",required"); + + if ( dest == buf ) + StringCopyE(dest,bufend,"+no transformation"); + + return buf+1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintMatrixD +( + FILE * f, // file to print to + uint indent, // indention + ccp eol, // not NULL: print as 'end of line' + + MatrixD_t * mat, // valid data structure + uint create, // bitfield for setup: + // 1: create normals if invalid + // 2: create transform matrix if invalid + // 4: create inverse matrix if invalid + uint print // bitfield print selection: + // 1: print base vectors + // 2: print normalized values, if valid + // 4: print transformation matrix, if valid + // 8: print inverse matrix, if valid + // 0x10: print also header, if only 1 section is print + // 0x20: print all, don't suppress NULL vectors + // 0x40: print an additional EOL behind each section +) +{ + DASSERT(f); + DASSERT(mat); + + //--- setup + + indent = NormalizeIndent(indent); + if (!eol) + eol = "\n"; + ccp sep = print & 0x40 ? eol : ""; + + if ( create & 4 ) + CalcMatrixD(mat,true); + else if ( create & 2 ) + CalcTransMatrixD(mat,true); + else if ( create & 1 ) + CalcNormMatrixD(mat); + else if (!mat->valid) + InitializeMatrixD(mat); + + if (!mat->norm_valid) + print &= ~2; + if (!mat->tmatrix_valid) + print &= ~4; + else if (mat->use_matrix>1) + print &= ~1; + if (!mat->imatrix_valid) + print &= ~8; + + const uint print_all = print & 0x20; + uint print_head = print & 0x10; + print &= 0x0f; + if ( print != 1 && print != 2 && print != 4 && print != 8 ) + print_head = 1; + + + //--- print base vectors + + if ( print & 1 ) + { + if (print_head) + fprintf(f,"%*sBase Vectors:%s", indent,"", eol ); + + if ( print_all || !IsOne3(mat->scale) ) + fprintf(f,"%*s Scale: %11.3f %11.3f %11.3f%s", + indent,"", + mat->scale.x, mat->scale.y, mat->scale.z, + eol ); + + if ( print_all || !IsNull3(mat->scale_origin) ) + fprintf(f,"%*s Origin: %11.3f %11.3f %11.3f%s", + indent,"", + mat->scale_origin.x, mat->scale_origin.y, mat->scale_origin.z, + eol ); + + if ( print_all || !IsNull3(mat->shift) ) + fprintf(f,"%*s Shift: %11.3f %11.3f %11.3f%s", + indent,"", + mat->shift.x, mat->shift.y, mat->shift.z, + eol ); + + bool print_rot = false; + if ( print_all || !IsNull3(mat->rotate_deg) ) + { + print_rot = true; + fprintf(f,"%*s Rotate/deg: %11.3f %11.3f %11.3f%s", + indent,"", + mat->rotate_deg.x, mat->rotate_deg.y, mat->rotate_deg.z, + eol ); + } + + if ( print_all || !IsNull3(mat->rotate_rad) ) + { + print_rot = true; + fprintf(f,"%*s Rotate/rad: %11.3f %11.3f %11.3f%s", + indent,"", + mat->rotate_rad.x, mat->rotate_rad.y, mat->rotate_rad.z, + eol ); + } + + if (print_rot) + { + double3 *d = mat->rotate_origin; + uint xyz; + for ( xyz = 0; xyz < 3; xyz++, d++ ) + if ( print_all || !IsNull3(*d) ) + fprintf(f,"%*s %c-origin: %11.3f %11.3f %11.3f%s", + indent,"", 'x'+xyz, + d->x, d->y, d->z, + eol ); + } + + if ( print_all || !IsNull3(mat->translate) ) + fprintf(f,"%*s Translate: %11.3f %11.3f %11.3f%s", + indent,"", + mat->translate.x, mat->translate.y, mat->translate.z, + eol ); + + fputs(sep,f); + } + + + //--- print normalized vectors + + if ( print & 2 ) + { + if (print_head) + fprintf(f,"%*sNormalized Vectors (seq=%u):%s", + indent,"", mat->sequence_number, eol ); + + fprintf(f, + "%*s Scale: %11.3f %11.3f %11.3f [%c%c%c]%s" + "%*s Rotate/deg: %11.3f %11.3f %11.3f [%c%c%c]%s" + #if TEST0 + "%*s Rotate/rad: %11.3f %11.3f %11.3f%s" + #endif + "%*s Translate: %11.3f %11.3f %11.3f [%c%c%c]%s", + + indent,"", + mat->norm_scale.x, mat->norm_scale.y, mat->norm_scale.z, + mat->scale_enabled & 1 ? 'x' : '-', + mat->scale_enabled & 2 ? 'y' : '-', + mat->scale_enabled & 4 ? 'z' : '-', + eol, + + indent,"", + mat->norm_rotate_deg.x, mat->norm_rotate_deg.y, mat->norm_rotate_deg.z, + mat->rotate_enabled & 1 ? 'x' : '-', + mat->rotate_enabled & 2 ? 'y' : '-', + mat->rotate_enabled & 4 ? 'z' : '-', + eol, + #if TEST0 + indent,"", + mat->norm_rotate_rad.x, mat->norm_rotate_rad.y, mat->norm_rotate_rad.z, + eol, + #endif + indent,"", + mat->norm_translate.x, mat->norm_translate.y, mat->norm_translate.z, + mat->translate_enabled & 1 ? 'x' : '-', + mat->translate_enabled & 2 ? 'y' : '-', + mat->translate_enabled & 4 ? 'z' : '-', + eol ); + + if ( print_all || !IsNull3(mat->norm_pos2d) ) + fprintf(f,"%*s 2D tf-base: %11.3f %11.3f %11.3f (for 2D transform)%s", + indent,"", + mat->norm_pos2d.x, mat->norm_pos2d.y, mat->norm_pos2d.z, + eol ); + + fputs(sep,f); + } + + + //--- print transformation matrix + + char buf[50]; + if ( print & 4 ) + { + if (print_head) + fprintf(f,"%*sTransformation Matrix (%s):%s", + indent,"", print_matrix_stat(mat,buf,sizeof(buf),false), eol ); + + fprintf(f, + "%*s x' = %11.3f * x + %11.3f * y + %11.3f * z + %11.3f%s" + "%*s y' = %11.3f * x + %11.3f * y + %11.3f * z + %11.3f%s" + "%*s z' = %11.3f * x + %11.3f * y + %11.3f * z + %11.3f%s" + "%s", + + indent,"", + mat->trans_matrix.x[0], + mat->trans_matrix.x[1], + mat->trans_matrix.x[2], + mat->trans_matrix.x[3], + eol, + + indent,"", + mat->trans_matrix.y[0], + mat->trans_matrix.y[1], + mat->trans_matrix.y[2], + mat->trans_matrix.y[3], + eol, + + indent,"", + mat->trans_matrix.z[0], + mat->trans_matrix.z[1], + mat->trans_matrix.z[2], + mat->trans_matrix.z[3], + eol, sep ); + } + + + //--- print inverse matrix + + if ( print & 8 ) + { + if (print_head) + fprintf(f,"%*sInverse Matrix (%s):%s", + indent,"", print_matrix_stat(mat,buf,sizeof(buf),true), eol ); + + fprintf(f, + "%*s x' = %11.3f * x + %11.3f * y + %11.3f * z + %11.3f%s" + "%*s y' = %11.3f * x + %11.3f * y + %11.3f * z + %11.3f%s" + "%*s z' = %11.3f * x + %11.3f * y + %11.3f * z + %11.3f%s" + "%s", + + indent,"", + mat->inv_matrix.x[0], + mat->inv_matrix.x[1], + mat->inv_matrix.x[2], + mat->inv_matrix.x[3], + eol, + + indent,"", + mat->inv_matrix.y[0], + mat->inv_matrix.y[1], + mat->inv_matrix.y[2], + mat->inv_matrix.y[3], + eol, + + indent,"", + mat->inv_matrix.z[0], + mat->inv_matrix.z[1], + mat->inv_matrix.z[2], + mat->inv_matrix.z[3], + eol, sep ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/dclib-xdump.c b/project/dclib/dclib-xdump.c new file mode 100644 index 0000000..c404966 --- /dev/null +++ b/project/dclib/dclib-xdump.c @@ -0,0 +1,2214 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include "dclib-xdump.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const ccp XDumpCommandName[XDUMPC__N] = +{ + "DUMP", + "DIFF", + "SCAN", +}; + +//----------------------------------------------------------------------------- + +const ccp XDumpFormatName[XDUMPF__N] = +{ + "AUTO", + "INT_1", + "INT_2", + "INT_3", + "INT_4", + "INT_5", + "INT_6", + "INT_7", + "INT_8", + "FLOAT", + "DOUBLE", +}; + +const char XDumpFormatOption[XDUMPF__N+1] = "A12345678FD"; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// output helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool PrintFormat +( + struct XDump_t *xd, // internal copy of params + int force // >0: ignore 'format_printed' + // >1: print always +) +{ + DASSERT(xd); + if ( !xd->f || !xd->format_name ) + return false; + + if ( force > 1 + || ( force > 0 && xd->print_format ) + || ( xd->print_format && !xd->format_printed ) + ) + { + xd->format_printed = true; + fprintf(xd->f,"%s%*s%sFORMAT: %s", + xd->prefix, xd->indent,"", + xd->mode_c ? "//" : "#", + xd->format_name ); + + if ( xd->format > XDUMPF_INT_1 ) + fprintf(xd->f," %s%s", + xd->endian == DC_BIG_ENDIAN ? "BE" : "LE", + xd->eol ); + else + fputs(xd->eol,xd->f); + + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDumpStandardOutput +( + struct XDump_t *xd, // internal copy of params + ccp prefix, // NULL or additional prefix (used by diff) + ccp dump, // dump to print + ccp text // NULL or text to print +) +{ + DASSERT(xd); + DASSERT(dump); + + if (xd->f) + { + fprintf(xd->f,"%s%*s%s", + xd->prefix, xd->indent,"", prefix ? prefix : "" ); + + if (xd->print_addr) + { + if (xd->mode_c) + fprintf(xd->f,"/*%*llx*/ ",xd->addr_fw,xd->addr); + + else + fprintf(xd->f,"%*llx:%s", + xd->addr_fw, xd->addr, + xd->extra_space >= 0 ? " " : "" ); + } + + if (xd->print_number) + { + if (xd->min_number_fw) + fprintf(xd->f,"%-*s",xd->min_number_fw,dump); + else + fputs(dump,xd->f); + } + + if ( text && xd->print_text ) + { + if (xd->mode_c) + fprintf(xd->f," // %s",text); + else + fprintf(xd->f,"%s :%s:", + xd->extra_space >= 0 ? " " : "", text ); + } + + fputs(xd->eol,xd->f); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_text ( char *buf, const u8 *data, int n ) +{ + while ( n-- > 0 ) + { + const u8 ch = *data++; + *buf++ = ch >= ' ' && ch < 0x7f ? ch : '.'; + } + *buf = 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XParam_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[XParam_t]] + +typedef struct XParam_t +{ + const u8 *d; // pointer to input data + const u8 *end; // end of input data + + uint size; // size of data to dump + bool term; // true: data complete + bool recalc_align; // true: recalculate alignment + bool next_line; // true: continue at next line + const u64 *addr_break; // NULL or pointer to next break + + uint skip; // number of columns to skip + uint line_size; // current line size (source bytes) + uint max_col; // max number of columns to print + int extra_space; // extra space counter + + // buffers + char text_buf[XDUMP_GOOD_TEXT_BUF_SIZE]; + char num_buf[XDUMP_GOOD_NUM_BUF_SIZE]; +} +XParam_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupXParam + ( XParam_t *p, XDump_t *xd, cvp data, uint size, bool term ) +{ + DASSERT(p); + DASSERT(xd); + DASSERT(data); + + if ( size > INT_MAX ) + { + size = INT_MAX; // limit because of return value + term = false; + } + + memset( p, 0, offsetof(XParam_t,num_buf) + 15 & ~7 ); + p->size = size; + p->term = term; + p->d = data; + p->end = p->d + size; + p->recalc_align = xd->mode_align; + p->addr_break = xd->addr_break; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint SetupXParamLine ( XParam_t *p, XDump_t *xd ) +{ + DASSERT(p); + DASSERT(xd); + + if ( p->recalc_align ) + { + p->recalc_align = false; + p->skip = ( xd->addr % xd->bytes_per_line ) / xd->bytes_per_col; + const int delta = p->skip * xd->bytes_per_col; + xd->addr -= delta; + if (xd->print_number) + p->size += delta; + } + + p->next_line = false; + p->line_size = xd->bytes_per_line < p->size + ? xd->bytes_per_line + : p->term ? p->size : 0; + + if ( p->addr_break ) + { + while ( *p->addr_break && xd->addr >= *p->addr_break ) + p->addr_break++; + + if (!*p->addr_break) + p->addr_break = 0; + else + { + const s64 max = *p->addr_break - xd->addr; + if ( p->line_size > max ) + p->line_size = max; + } + } + + p->max_col = p->line_size / xd->bytes_per_col; + p->line_size = p->max_col * xd->bytes_per_col; + p->extra_space = xd->col_space ? xd->col_space+1 : 0; + + if (xd->print_text) + { + if (p->skip) + { + const uint skip_size = p->skip * xd->bytes_per_col; + if ( skip_size < p->line_size ) + { + memset(p->text_buf,' ',skip_size); + print_text(p->text_buf+skip_size,p->d,p->line_size-skip_size); + } + else + *p->text_buf = 0; // should never happen! + } + else + print_text(p->text_buf,p->d,p->line_size); + } + + return p->max_col; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool PrintNullSummary ( XDump_t *xd ) +{ + if ( !xd->null_lines || !xd->out_func ) + return false; + + char buf[100]; + const uint skipped = xd->null_lines * xd->bytes_per_line; + snprintf(buf,sizeof(buf),"%s#NULL: 0x%x bytes", + xd->extra_space >= 0 ? " " : "", skipped ); + + xd->addr -= skipped; + xd->out_func(xd,0,buf,0); + xd->addr += skipped; + xd->null_lines = 0; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool CheckXParamNull ( XParam_t *p, XDump_t *xd ) +{ + DASSERT(p); + DASSERT(xd); + + static u8 nullbuf[XDUMP_MAX_BYTES_PER_LINE] = {}; + + + const bool skip = p->line_size == xd->bytes_per_line + && !memcmp(p->d,nullbuf,xd->bytes_per_line); + if (skip) + { + xd->null_lines++; + xd->addr += p->line_size; + p->d += p->line_size; + p->size -= p->line_size; + + return true; + } + + if ( xd->null_lines && ( !skip || p->size <= xd->bytes_per_line )) + PrintNullSummary(xd); + + return skip; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AbortXParamLine ( XParam_t *p, XDump_t *xd, uint col ) +{ + const uint col1 = col + 1; + if ( col1 < p->max_col ) + { + p->max_col = col1; + p->line_size = col1 * xd->bytes_per_col; + p->text_buf[p->line_size] = 0; + p->recalc_align = xd->mode_align; + p->next_line = true; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// output functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int DumpInt32 +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // current params + const void *p_data, // data to dump + uint p_size, // size of 'p_data' + bool p_term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + if ( !p_data || !xd->out_func ) + return 0; + + XParam_t p; + SetupXParam(&p,xd,p_data,p_size,p_term); + + while(SetupXParamLine(&p,xd)) + { + if ( xd->mode_ignore && CheckXParamNull(&p,xd) ) + continue; + + if (xd->print_number) + { + char *num = p.num_buf; + uint col; + for ( col = 0; col < xd->cols_per_line; col++ ) + { + if (!--p.extra_space) + { + p.extra_space = xd->col_space; + *num++ = ' '; + } + + if ( p.skip > 0 ) + { + p.skip--; + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + else if ( col < p.max_col ) + { + u32 val; + switch(xd->bytes_per_col) + { + case 2: val = xd->endian_func->rd16(p.d); p.d += 2; break; + case 3: val = xd->endian_func->rd24(p.d); p.d += 3; break; + case 4: val = xd->endian_func->rd32(p.d); p.d += 4; break; + default: val = *p.d++; break; + } + num += sprintf(num,xd->num_format1,val); + if ( xd->have_trigger && val == (u32)xd->trigger ) + AbortXParamLine(&p,xd,col); + } + else if ( p.term && !p.next_line && !xd->mode_c ) + { + if ( p.d < p.end ) + num += sprintf(num, "%*c%u", xd->num_format_fw-1, + '>', (int)(p.end-p.d) ); + else if ( xd->print_endmarker ) + num += sprintf(num,"%*c", xd->num_format_fw, '/' ); + else if ( xd->print_text ) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + p.term = false; // print only once + } + else if ( xd->print_text ) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + } + if (!xd->print_text) + while ( num > p.num_buf && num[-1] == ' ' ) + num--; + *num = 0; + ASSERT( num < p.num_buf + sizeof(p.num_buf)); + xd->last_number_fw = num - p.num_buf; + } + else + { + p.d += p.line_size - p.skip * xd->bytes_per_col; + p.skip = 0; + } + + int stat = xd->out_func(xd,0,p.num_buf,p.text_buf); + if ( stat < 0 ) + return stat; + + xd->addr += p.line_size; + p.size -= p.line_size; + } + const uint written = p.d - (u8*)p_data; + xd->written += written; + return written; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int DumpInt64 +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // current params + const void *p_data, // data to dump + uint p_size, // size of 'p_data' + bool p_term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + if ( !p_data || !xd->out_func ) + return 0; + + XParam_t p; + SetupXParam(&p,xd,p_data,p_size,p_term); + + while(SetupXParamLine(&p,xd)) + { + if ( xd->mode_ignore && CheckXParamNull(&p,xd) ) + continue; + + if (xd->print_number) + { + char *num = p.num_buf; + uint col; + for ( col = 0; col < xd->cols_per_line; col++ ) + { + if (!--p.extra_space) + { + p.extra_space = xd->col_space; + *num++ = ' '; + } + + if ( p.skip > 0 ) + { + p.skip--; + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + else if ( col < p.max_col ) + { + u64 val; + switch(xd->bytes_per_col) + { + case 5: val = xd->endian_func->rd40(p.d); p.d += 5; break; + case 6: val = xd->endian_func->rd48(p.d); p.d += 6; break; + case 7: val = xd->endian_func->rd56(p.d); p.d += 7; break; + default: val = xd->endian_func->rd64(p.d); p.d += 8; break; + } + num += sprintf(num,xd->num_format1,val); + if ( xd->have_trigger && val == (u32)xd->trigger ) + AbortXParamLine(&p,xd,col); + } + else if ( p.term && !p.next_line && !xd->mode_c ) + { + if ( p.d < p.end ) + num += sprintf(num, "%*c%u", xd->num_format_fw-1, + '>', (int)(p.end-p.d) ); + else if ( xd->print_endmarker ) + num += sprintf(num,"%*c", xd->num_format_fw, '/' ); + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + p.term = false; // print only once + } + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + } + *num = 0; + ASSERT( num < p.num_buf + sizeof(p.num_buf)); + xd->last_number_fw = num - p.num_buf; + } + else + { + p.d += p.line_size - p.skip * xd->bytes_per_col; + p.skip = 0; + } + + int stat = xd->out_func(xd,0,p.num_buf,p.text_buf); + if ( stat < 0 ) + return stat; + + xd->addr += p.line_size; + p.size -= p.line_size; + } + + const uint written = p.d - (u8*)p_data; + xd->written += written; + return written; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int DumpFloat +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // current params + const void *p_data, // data to dump + uint p_size, // size of 'p_data' + bool p_term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + if ( !p_data || !xd->out_func ) + return 0; + + XParam_t p; + SetupXParam(&p,xd,p_data,p_size,p_term); + + while(SetupXParamLine(&p,xd)) + { + if ( xd->mode_ignore && CheckXParamNull(&p,xd) ) + continue; + + if (xd->print_number) + { + char *num = p.num_buf; + uint col; + for ( col = 0; col < xd->cols_per_line; col++ ) + { + if (!--p.extra_space) + { + p.extra_space = xd->col_space; + *num++ = ' '; + } + + if ( p.skip > 0 ) + { + p.skip--; + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + else if ( col < p.max_col ) + { + double val; + ccp format; + if ( xd->bytes_per_col == 4 ) + { + val = xd->endian_func->rdf4(p.d); + format = fabsf(val) <= 9999.0 && fabsf(val) >= 1e-3 + ? xd->num_format1 : xd->num_format2; + p.d += 4; + } + else + { + val = xd->endian_func->rdf8(p.d); + format = fabs(val) <= 999999999.0 && fabs(val) >= 1e-6 + ? xd->num_format1 : xd->num_format2; + p.d += 8; + } + num += sprintf(num,format,val); + } + else if ( p.term && !p.next_line && !xd->mode_c ) + { + if ( p.d < p.end ) + num += sprintf(num, "%*c%u", xd->num_format_fw-1, + '>', (int)(p.end-p.d) ); + else if ( xd->print_text ) + num += sprintf(num,"%*c", xd->num_format_fw, '/' ); + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + p.term = false; // print only once + } + else if (xd->print_text) + { + memset(num,' ',xd->num_format_fw); + num += xd->num_format_fw; + } + } + *num = 0; + ASSERT( num < p.num_buf + sizeof(p.num_buf)); + xd->last_number_fw = num - p.num_buf; + } + else + { + p.d += p.line_size - p.skip * xd->bytes_per_col; + p.skip = 0; + } + + int stat = xd->out_func(xd,0,p.num_buf,p.text_buf); + if ( stat < 0 ) + return stat; + + xd->addr += p.line_size; + p.size -= p.line_size; + } + + const uint written = p.d - (u8*)p_data; + xd->written += written; + return written; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeXDump ( XDump_t *xd ) +{ + DASSERT(xd); + memset(xd,0,sizeof(*xd)); + + xd->f = stdout; + xd->print_format = + xd->print_addr = + xd->print_number = + xd->print_endmarker = + xd->print_text = + xd->print_diff_sep = + xd->print_summary = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupXDump ( XDump_t *xd, XDumpCommand_t cmd ) +{ + DASSERT(xd); + char buf[100]; + + xd->cmd = (uint)cmd < XDUMPC__N ? cmd : XDUMPC_DIFF; + + if ( xd->endian != DC_SECOND_ENDIAN ) + xd->endian = DC_DEFAULT_ENDIAN; + xd->endian_func = xd->endian == DC_LITTLE_ENDIAN ? &le_func : &be_func; + + + //--- address field width + + xd->addr = xd->start_addr; + xd->addr_fw = xd->min_addr_fw; + if ( !xd->min_addr_fw || xd->addr_fw < xd->min_addr_fw ) + { + uint len = snprintf(buf,sizeof(buf),"%llx",xd->addr); + if ( xd->addr_fw < len ) + xd->addr_fw = len; + + const u64 size = xd->assumed_size ? xd->assumed_size : 0x100; + len = snprintf(buf,sizeof(buf),"%llx", xd->addr + size - 1 ); + if ( xd->addr_fw < len ) + xd->addr_fw = len; + + if (xd->max_addr_fw) + { + if ( xd->addr_fw > xd->max_addr_fw ) + xd->addr_fw = xd->max_addr_fw; + if ( xd->addr_fw < xd->min_addr_fw ) + xd->addr_fw = xd->min_addr_fw; + } + } + + + //--- misc + + xd->indent = NormalizeIndent(xd->indent); + if (!xd->out_func) + xd->out_func = XDumpStandardOutput; + if (!xd->prefix) + xd->prefix = EmptyString; + if (!xd->eol) + xd->eol = "\n"; + + xd->num_format2 = 0; + xd->format_printed = 0; + + + //--- format + + if ( (uint)xd->format >= XDUMPF__N ) + xd->format = XDUMPF_AUTO; + + if ( xd->format == XDUMPF_AUTO && xd->cmd != XDUMPC_SCAN ) + xd->format = XDUMPF_INT_1; + + switch(xd->format) + { + case XDUMPF_INT_2: + xd->bytes_per_col = 2; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC2" : "HEX2"; + xd->max_format_fw = 7; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%5u," : "0x%04x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %05u" : " %04x" ) + : ( xd->mode_dec ? " %5u" : " %4x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + + case XDUMPF_INT_3: + xd->bytes_per_col = 3; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC3" : "HEX3"; + xd->max_format_fw = 9; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%8u," : "0x%06x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %08u" : " %06x" ) + : ( xd->mode_dec ? " %8u" : " %6x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + + case XDUMPF_INT_4: + xd->bytes_per_col = 4; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC4" : "HEX4"; + xd->max_format_fw = 11; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%10u," : "0x%08x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %010u" : " %08x" ) + : ( xd->mode_dec ? " %10u" : " %8x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + + case XDUMPF_INT_5: + xd->bytes_per_col = 5; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC5" : "HEX5"; + xd->max_format_fw = 13; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%12llu," : "0x%010llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %012llu" : " %010llx" ) + : ( xd->mode_dec ? " %12llu" : " %10llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_INT_6: + xd->bytes_per_col = 6; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC6" : "HEX6"; + xd->max_format_fw = 16; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%15llu," : "0x%012llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %015llu" : " %012llx" ) + : ( xd->mode_dec ? " %15llu" : " %12llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_INT_7: + xd->bytes_per_col = 7; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC7" : "HEX7"; + xd->max_format_fw = 18; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%17llu," : "0x%014llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %017llu" : " %014llx" ) + : ( xd->mode_dec ? " %17llu" : " %14llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_INT_8: + xd->bytes_per_col = 8; + xd->dump_func = DumpInt64; + xd->format_name = xd->mode_dec ? "DEC8" : "HEX8"; + xd->max_format_fw = 20; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%19llu," : "0x%016llx," ) + : xd->mode_zero ? ( xd->mode_dec ? " %019llu" : " %016llx" ) + : ( xd->mode_dec ? " %19llu" : " %16llx" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1ull); + break; + + case XDUMPF_FLOAT: + xd->bytes_per_col = 4; + xd->dump_func = DumpFloat; + xd->format_name = "FLOAT"; + xd->max_format_fw = 12; + xd->num_format1 = xd->mode_c ? "%11.5g," : " %11.5f"; + xd->num_format2 = xd->mode_c ? "%11.5g," : " %11.5g"; + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1.1); + break; + + case XDUMPF_DOUBLE: + xd->bytes_per_col = 8; + xd->dump_func = DumpFloat; + xd->format_name = "DOUBLE"; + xd->max_format_fw = 21; + xd->num_format1 = xd->mode_c ? "%20.8g," : " %20.8f"; + xd->num_format2 = xd->mode_c ? "%20.8g," : " %20.8g"; + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1.1); + break; + + default: + //case XDUMPF_AUTO: + //case XDUMPF_INT_1: + xd->bytes_per_col = 1; + xd->dump_func = DumpInt32; + xd->format_name = xd->mode_dec ? "DEC1" : "HEX1"; + xd->max_format_fw = 5; + xd->num_format1 + = xd->mode_c ? ( xd->mode_dec ? "%3u," : "0x%02x," ) + : xd->mode_zero ? ( xd->mode_dec ? " %03u" : " %02x" ) + : ( xd->mode_dec ? " %3u" : " %2x" ); + xd->num_format_fw = snprintf(buf,sizeof(buf),xd->num_format1,1); + break; + } + + + //--- bytes per line + + uint min = xd->min_width ? xd->min_width : 16; + uint max = xd->max_width ? xd->max_width : min + 2*xd->bytes_per_col/3; + if ( max > XDUMP_MAX_BYTES_PER_LINE ) + max = XDUMP_MAX_BYTES_PER_LINE; + if ( min > max || !xd->min_width ) + min = max; + + uint ncols = ( min + xd->bytes_per_col - 1 ) / xd->bytes_per_col; + uint ncols_max = max / xd->bytes_per_col; + if ( ncols > ncols_max ) + ncols = ncols_max; + + xd->cols_per_line = ncols ? ncols : 1; + xd->bytes_per_line = xd->cols_per_line * xd->bytes_per_col; + + + //--- extra space + + if ( xd->extra_space < 0 || xd->cols_per_line <= 3 ) + xd->col_space = 0; + else + { + xd->col_space = xd->extra_space; + if (!xd->col_space) + { + xd->col_space = 4 / xd->bytes_per_col; + if ( xd->col_space < 2 ) + xd->col_space = 2; + } + + if ( xd->col_space >= xd->cols_per_line ) + xd->col_space = 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print xdump params /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintXDumpParam +( + FILE *f, // output file, abort if NULL + int indent, // indention + const ColorSet_t *colset, // NULL or color set + const XDump_t *xd // data to print +) +{ + if ( !f || !xd ) + return; + + indent = NormalizeIndent(indent); + if (!colset) + colset = GetColorSet0(); + + fprintf(f, + "%*s%s" "Command: %s%s%s\n" + "%*s%s" "Format: %s%s, %s%s%s => %s%s\n" + "%*s%s" "Endian: %s%s%s\n", + indent,"", colset->name, colset->value, + (uint)xd->cmd < XDUMPC__N ? XDumpCommandName[xd->cmd] : "?", + colset->reset, + indent,"", colset->name, colset->value, + (uint)xd->format < XDUMPF__N ? XDumpFormatName[xd->format] : "?", + xd->mode_dec ? "decimal" : "hexadecimal", + xd->mode_zero ? " with leading zeros" : "", + xd->mode_c ? ", print in C syntax" : "", + xd->format_name ? xd->format_name : "?", + colset->reset, + indent,"", colset->name, colset->value, + xd->endian == DC_LITTLE_ENDIAN ? "LITTLE" + : xd->endian == DC_BIG_ENDIAN ? "BIG" : "?", + colset->reset ); + + if (xd->num_format2) + fprintf(f, + "%*s%s" "Num format: %s'%s' & '%s' (fw=%u,max=%u)%s\n", + indent,"", colset->name, colset->value, + xd->num_format1 ? xd->num_format1 : "?", + xd->num_format2, xd->num_format_fw, xd->max_format_fw, + colset->reset ); + else + fprintf(f, + "%*s%s" "Num format: %s'%s' (fw=%u,max=%u)%s\n", + indent,"", colset->name, colset->value, + xd->num_format1 ? xd->num_format1 : "?", + xd->num_format_fw, xd->max_format_fw, + colset->reset ); + + if (xd->assumed_size) + fprintf(f, "%*s%s" + "Address: %sstart=0x%llx, current=0x%llx, size=0x%llx%s%s\n", + indent,"", colset->name, colset->value, + xd->start_addr, xd->addr, xd->assumed_size, + xd->mode_align ? ", aligned" : "", + colset->reset ); + else + fprintf(f, "%*s%s" + "Address: %sstart=0x%llx, current=0x%llx%s%s\n", + indent,"", colset->name, colset->value, + xd->start_addr, xd->addr, + xd->mode_align ? ", aligned" : "", + colset->reset ); + + if (xd->have_trigger) + fprintf(f, "%*s%s" + "Line trigger: %s%llu = 0x%llx%s%s\n", + indent,"", colset->name, colset->value, + xd->trigger, xd->trigger, + xd->trigger == '\n' ? " (LF)" : + xd->trigger == '\r' ? " (CR)" : + xd->trigger == '\t' ? " (TAB)" : + xd->trigger == ' ' ? " (SPACE)" : + xd->trigger == 0 ? " (NULL)" : + "", + colset->reset ); + + fprintf(f, + "%*s%s" "Address fw: %smin=%u, max=%u, active=%u%s\n", + indent,"", colset->name, colset->value, + xd->min_addr_fw, xd->max_addr_fw, xd->addr_fw, + colset->reset ); + + fprintf(f, + "%*s%s" "Print: %s%sformat, %saddress, %snumbers, %stext, %ssummary, %sseparator%s\n", + indent,"", colset->name, colset->value, + xd->print_format ? "" : "no ", + xd->print_addr ? "" : "no ", + xd->print_number ? "" : "no ", + xd->print_text ? "" : "no ", + xd->print_summary ? "" : "no ", + xd->print_diff_sep ? "" : "no ", + colset->reset ); + + fprintf(f, + "%*s%s" "Output file: %s%s, indent=%d, prefix=%s, eol=%s%s\n", + indent,"", colset->name, colset->value, + xd->f == stdout ? "STDOUT" + : xd->f == stderr ? "STDERR" + : xd->f ? "FILE" : "NONE", + xd->indent, + !xd->prefix ? "NULL" : !*xd->prefix ? "EMPTY" : "DEFINED", + !xd->eol ? "NULL" + : !*xd->eol ? "EMPTY" + : !strcmp(xd->eol,"\n") ? "LF" + : !strcmp(xd->eol,"\r") ? "CR" + : !strcmp(xd->eol,"\r\n") ? "CR+LF" + : "DEFINED", + colset->reset ); + + fprintf(f, + "%*s%s" "Bytes/line: %smin=%u, max=%u%s\n" + "%*s%s" " =>: %sbytes/column = %u, columns/line = %u, bytes/line = %u%s\n" + "%*s%s" "Extra space: %severy %u column => every %u column%s\n", + indent,"", colset->name, colset->value, + xd->min_width, xd->max_width, + colset->reset, + indent,"", colset->name, colset->value, + xd->bytes_per_col, xd->cols_per_line, xd->bytes_per_line, + colset->reset, + indent,"", colset->name, colset->value, + xd->extra_space, xd->col_space, + colset->reset ); + + if (xd->mode_ignore) + fprintf(f, + "%*s" "%sNULL bytes: %sPrint a summary for lines with NULL bytes only.%s\n", + indent,"", colset->name, colset->value, colset->reset ); + fprintf(f, + "%*s%s" "Functions: %sdump=%s, output=%s, endian=%s%s\n", + indent,"", colset->name, colset->value, + !xd->dump_func ? "-" : "SET", + !xd->out_func ? "-" + : xd->out_func == XDumpStandardOutput + ? "STANDARD" : "SET", + !xd->endian_func ? "-" + : xd->endian_func == &le_func ? "LITTLE" + : xd->endian_func == &be_func ? "BIG" + : "SET", + colset->reset ); + + fprintf(f, + "%*s%s" "User param: %sint=%d, ptr=%s%s\n", + indent,"", colset->name, colset->value, + xd->user_int, xd->user_ptr ? "SET" : "-", + colset->reset ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dump interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void SetupRemainder + ( XDump_t *xd, const XDump_t *src, XDumpCommand_t cmd, uint size ) +{ + DASSERT(xd); + DASSERT(src); + + memcpy(xd,src,sizeof(*xd)); + xd->format = XDUMPF_INT_1; + xd->start_addr = xd->addr; + xd->assumed_size = size; + xd->min_width = size; + xd->max_width = size; + xd->extra_space = 0; + xd->mode_align = false; + xd->mode_ignore = false; + + if (xd->print_text) + xd->min_number_fw = xd->last_number_fw; + + SetupXDump(xd,cmd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int XDumpHelper +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; SetupXDump() already done! + const void *data, // data to dump + uint size, // size of 'data' + bool term // false: print only whole lines + // true: print complete data +) +{ + DASSERT(xd); + DASSERT(data||!size); + + PrintFormat(xd,0); + int stat = xd->dump_func(xd,data,size,term); + if ( stat < 0 ) + return stat; + + if (term) + { + PrintNullSummary(xd); + + if ( stat < size ) + { + XDump_t local; + SetupRemainder(&local,xd,XDUMPC_DUMP,size-stat); + PrintFormat(&local,0); + + int stat2 = local.dump_func(&local,data+stat,local.min_width,term); + xd->addr = local.addr; + xd->written = local.written; + + if ( stat2 < 0 ) + return stat2; + stat += stat2; + } + + if ( xd->f && xd->print_summary ) + { + fprintf(xd->f,"%s%*s",xd->prefix,xd->indent,""); + if ( xd->print_addr ) + { + if (xd->mode_c) + fprintf(xd->f,"//%*llx// [%llu Bytes]%s", + xd->addr_fw, xd->addr, + xd->written, xd->eol ); + + else + fprintf(xd->f,"%*llx:%s [%llu Bytes]%s", + xd->addr_fw, xd->addr, + xd->extra_space >= 0 ? " " : "", + xd->written, xd->eol ); + } + else + { + if (xd->mode_c) + fprintf(xd->f,"// [%llu Bytes]%s", + xd->written, xd->eol ); + + else + fprintf(xd->f,"%s [%llu Bytes]%s", + xd->extra_space >= 0 ? " " : "", + xd->written, xd->eol ); + } + } + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDump +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + const void *data, // data to dump + uint size, // size of 'data' + bool term // false: print only whole lines + // true: print complete data +) +{ + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DUMP); + + return XDumpHelper(xd,data,size,term); +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDumpByFile +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FILE *f, // input file + u64 max_size // >0: limit size to print +) +{ + DASSERT(f); + + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DUMP); + + char buf[0x4100]; + uint start = 0; + if (!max_size) + max_size = M1(max_size); + + uint count = 0; + while ( max_size > 0 ) + { + uint max_read = ( sizeof(buf) - start ) / 0x1000 * 0x1000; + if ( max_read > max_size ) + max_read = max_size; + + size_t stat = fread(buf+start,1,max_read,f); + if (!stat) + break; + max_size -= stat; + stat += start; + + int dstat = XDumpHelper(xd,buf,stat,false); + if ( dstat < 0 ) + return dstat; + count += dstat; + + start = stat - dstat; + if (start) + memmove(buf,buf+dstat,start); + } + + int dstat = XDumpHelper(xd,buf,start,true); + return dstat < 0 ? dstat : count + dstat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDump16 // wrapper for a standard hexdump +( + // returns <0 on error, or number of dumped bytes + + FILE *f_out, // output file + int indent, // indention + u64 addr, // start address + const void *data, // data + uint size // size of 'data' +) +{ + XDump_t xd; + InitializeXDump(&xd); + xd.f = f_out; + xd.indent = indent; + xd.start_addr = addr; + xd.assumed_size = size; + + return XDump(&xd,data,size,true); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct XDiff_t +{ + const void *data; // data to compare + uint size; // size of 'data' + bool term; // 'data' reached end + + XDump_t xd; // dump parameters + + //--- buffers to catch output + char num_buf[XDUMP_GOOD_NUM_BUF_SIZE+4]; + char text_buf[XDUMP_GOOD_TEXT_BUF_SIZE]; +} +XDiff_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int XDiffCatchOutput +( + struct XDump_t *xd, // internal copy of params + ccp prefix, // NULL or additional prefix (used by diff) + ccp dump, // dump to print + ccp text // text to print +) +{ + DASSERT(xd); + DASSERT(dump); + DASSERT(text); + + XDiff_t *diff = xd->user_ptr; + DASSERT(diff); + + StringCopyS(diff->num_buf,sizeof(diff->num_buf),dump); + StringCopyS(diff->text_buf,sizeof(diff->text_buf),text); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupXDiff +( + XDiff_t *xdiff, // data to setup + + XDump_t *xd, // NULL or template + + const void *data, // data to compare + uint size, // size of 'data' + bool term // 'data' reached end +) +{ + DASSERT(xdiff); + + xdiff->data = data; + xdiff->size = size; + xdiff->term = term; + *xdiff->num_buf = *xdiff->text_buf = 0; + + if (xd) + memcpy(&xdiff->xd,xd,sizeof(xdiff->xd)); + else + InitializeXDump(&xdiff->xd); + + xd = &xdiff->xd; + SetupXDump(xd,XDUMPC_DIFF); + + xd->user_ptr = xdiff; + xd->out_func = XDiffCatchOutput; + xd->mode_ignore = false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool XDiffText +( + // returns true if differ + + XDump_t *xd, // original params; SetupXDump() already done! + XDiff_t *d1, // first diff file + XDiff_t *d2, // second diff file + bool colorize // true: use red and green on output +) +{ + DASSERT(xd); + DASSERT(d1); + DASSERT(d2); + + char *n1 = d1->num_buf; + char *n2 = d2->num_buf; + + int len1 = strlen(n1); + int len2 = strlen(n2); + if ( len1 < len2 ) + { + memset( n1 + len1, ' ', len2-len1 ); + n1[len2] = 0; + len1 = len2; + } + else if ( len1 > len2 ) + { + memset( n2 + len2, ' ', len1-len2 ); + n2[len1] = 0; + } + + if ( xd->min_number_fw < len1 ) + xd->min_number_fw = len1; + + if (!strcmp(n1,n2)) + return false; + + for(;;) + { + while ( *n1 == ' ' && *n2 == ' ' ) + n1++, n2++; + if ( !*n1 || !*n2 ) + break; + + char *x = n2; + while ( (u8)*n2 > ' ' && *n1 == *n2 && *n2 != '/' && *n2 != '/' ) + n1++, n2++; + if ( n2 > x && (u8)*n2 <= ' ' ) + { + while ( x < n2 ) + *x++ = ' '; + x[-1] = '.'; + } + else + while ( (u8)*n1 > ' ' || (u8)*n2 > ' ' ) + n1++, n2++; + } + + xd->out_func(xd,"< ",d1->num_buf,d1->text_buf); + xd->out_func(xd,"> ",d2->num_buf,d2->text_buf); + if ( xd->f && xd->print_diff_sep ) + fputs(xd->eol,xd->f); + + xd->diff_count++; + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// + +#undef OPTIMIZED_SEARCH +#define OPTIMIZED_SEARCH 0 + +static int XDiffHelper +( + // returns <0 on error, or number of dumped bytes + + XDump_t *xd, // original params; SetupXDump() already done! + XDiff_t *d1, // first diff file + XDiff_t *d2, // second diff file + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + DASSERT(xd); + DASSERT(d1); + DASSERT(d2); + DASSERT( d1->xd.bytes_per_col == d1->xd.bytes_per_col ); + DASSERT( d1->xd.bytes_per_line == d1->xd.bytes_per_line ); + DASSERT( d1->xd.format == d1->xd.format ); + + + //--- size calculations & setup + + uint count = 0; + int size1 = d1->size; + int size2 = d2->size; + if ( !d1->term && !d2->term ) + { + if ( size1 < size2 ) + size2 = size1; + else if ( size1 > size2 ) + size1 = size2; + } + + if (!d1->term) + size1 = size1 / xd->bytes_per_line * xd->bytes_per_line; + + if (!d2->term) + size2 = size2 / xd->bytes_per_line * xd->bytes_per_line; + + const u8 *p1 = d1->data; + const u8 *p2 = d2->data; + + const u64 *save_addr_break = xd->addr_break; + const u64 *addr_break = save_addr_break; + xd->addr_break = 0; + + + //--- main loop + + int done = 0; + while ( size1 > 0 && size2 > 0 + || !done && ( size1 > 0 || size2 > 0 ) ) + { + //--- optimized search + + #if OPTIMIZED_SEARCH + uint same = CountEqual(p1,p2, size1 < size2 ? size1 : size2 ); + if ( same >= xd->bytes_per_line ) + { + same = same / xd->bytes_per_line * xd->bytes_per_line; + p1 += same; + p2 += same; + xd->addr += same; + count += same; + size1 -= same; + size2 -= same; + } + #endif + + //--- + int dumpsize1 = size1 < (int)xd->bytes_per_line ? size1 : xd->bytes_per_line; + int dumpsize2 = size2 < (int)xd->bytes_per_line ? size2 : xd->bytes_per_line; + + bool have_break = false; + if (addr_break) + { + while ( *addr_break && xd->addr >= *addr_break ) + addr_break++; + + if (!*addr_break) + addr_break = 0; + else + { + const s64 max = *addr_break - xd->addr; + if ( dumpsize1 > max || dumpsize2 > max ) + { + have_break = true; + if ( dumpsize1 > max ) dumpsize1 = max; + if ( dumpsize2 > max ) dumpsize2 = max; + } + } + } + + #if !OPTIMIZED_SEARCH + const uint diffsize = dumpsize1 < dumpsize2 ? dumpsize1 : dumpsize2; + #endif + uint donesize = dumpsize1 > dumpsize2 ? dumpsize1 : dumpsize2; + +//DEL fprintf(stderr,">>> %d,%d done=%d, donesize=%d, diffsize=%d\n",size1,size2,done,donesize,diffsize); + + #if !OPTIMIZED_SEARCH + if ( diffsize > 0 + && ( diffsize < xd->bytes_per_line || memcmp(p1,p2,diffsize) )) + #endif + { + if ( dumpsize1 != dumpsize2 && !xd->diff_size ) + { + xd->diff_size = dumpsize1 - dumpsize2; + if (xd->diff_size) + xd->diff_count++; + } + + if ( dumpsize1 >= (int)xd->bytes_per_col ) + { + const int stat = xd->dump_func(&d1->xd,p1,dumpsize1,true); + if ( stat >= 0 && stat < dumpsize1 && donesize > stat ) + donesize = stat; + } + else if ( dumpsize1 > 0 ) + { + SetupRemainder(&d1->xd,&d1->xd,XDUMPC_DIFF,dumpsize1); + d1->xd.dump_func(&d1->xd,p1,dumpsize1,true); + } + else + { + done++; + strcpy(d1->num_buf,"/"); + *d1->text_buf = 0; + } + + if ( dumpsize2 >= (int)xd->bytes_per_col ) + { + const int stat = xd->dump_func(&d2->xd,p2,dumpsize2,true); + if ( stat >= 0 && stat < dumpsize2 && donesize > stat ) + donesize = stat; + } + else if ( dumpsize2 > 0 ) + { + SetupRemainder(&d2->xd,&d2->xd,XDUMPC_DIFF,dumpsize2); + d2->xd.dump_func(&d2->xd,p2,dumpsize2,true); + } + else + { + done++; + strcpy(d2->num_buf,"/"); + *d2->text_buf = 0; + } + + XDiffText(xd,d1,d2,colorize); + if ( limit_lines && !--limit_lines ) + break; + } + + p1 += donesize; + p2 += donesize; + xd->addr += donesize; + count += donesize; + size1 -= donesize; + size2 -= donesize; + + if (have_break) + { + if (!d1->term) + size1 = size1 / xd->bytes_per_line * xd->bytes_per_line; + + if (!d2->term) + size2 = size2 / xd->bytes_per_line * xd->bytes_per_line; + } + } + + xd->addr_break = save_addr_break; + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDiff +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + + const void *data1, // data1 to compare + uint size1, // size of 'data1' + bool term1, // true: 'data1' completed + + const void *data2, // data2 to compare + uint size2, // size of 'data2' + bool term2, // true: 'data2' completed + + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + // fast test for identical data + if ( term1 && term2 && size1 == size2 && ( !size1 || !memcmp(data1,data2,size1) )) + return 0; + + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DIFF); + + XDiff_t diff1, diff2; + SetupXDiff(&diff1,xd,data1,size1,term1); + SetupXDiff(&diff2,xd,data2,size2,term2); + return XDiffHelper(xd,&diff1,&diff2,limit_lines,colorize); +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDiffByFile +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FILE *f1, // first input file + FILE *f2, // second input file + u64 max_size, // >0: limit comparing size + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + DASSERT(f1); + DASSERT(f2); + + XDump_t local_xd; + if (!xd) + { + InitializeXDump(&local_xd); + xd = &local_xd; + } + SetupXDump(xd,XDUMPC_DIFF); + + char buf1[0x4100]; + char buf2[sizeof(buf1)]; + + XDiff_t diff1, diff2; + SetupXDiff(&diff1,xd,buf1,0,false); + SetupXDiff(&diff2,xd,buf2,0,false); + + uint start1 = 0, start2 = 0; + if (!max_size) + max_size = M1(max_size); + + uint count = 0; + while ( max_size > 0 ) + { + uint max_read1 = ( sizeof(buf1) - start1 ) / 0x1000 * 0x1000; + if ( max_read1 > max_size ) + max_read1 = max_size; + size_t stat1 = fread(buf1+start1,1,max_read1,f1); + + uint max_read2 = ( sizeof(buf2) - start2 ) / 0x1000 * 0x1000; + if ( max_read2 > max_size ) + max_read2 = max_size; + size_t stat2 = fread(buf2+start2,1,max_read2,f2); + + uint stat = stat1 < stat2 ? stat1 : stat2; + max_size -= stat; + + diff1.size = stat1 + start1; + diff1.term = !stat1; + diff2.size = stat2 + start2; + diff2.term = !stat2; + + int dstat = XDiffHelper(xd,&diff1,&diff2,limit_lines,colorize); + + if ( dstat <= 0 ) + return dstat; + count += dstat; + if (xd->diff_size) + return count; + + start1 = stat1 + start1 - dstat; + if (start1) + memmove(buf1,buf1+dstat,start1); + + start2 = stat2 + start2 - dstat; + if (start2) + memmove(buf2,buf2+dstat,start2); + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XDiff16 +( + // returns <0 on error, or number of dumped bytes + + FILE *f_out, // output file + int indent, // indention + u64 addr, // start address + + const void *data1, // data + uint size1, // size of 'data' + + const void *data2, // data + uint size2, // size of 'data' + + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +) +{ + XDump_t xd; + InitializeXDump(&xd); + xd.f = f_out; + xd.indent = indent; + xd.start_addr = addr; + xd.assumed_size = size1 > size2 ? size1 : size2; + + return XDiff( &xd, data1,size1,true, + data2,size2,true, limit_lines, colorize ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan interface /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[XScan_t]] + +typedef struct XScan_t +{ + FastBuf_t *dest; // not NULL: append scanned data here & ignore 'fout' + FILE *fout; // valid output file + u64 written; // number of written bytes + + bool auto_format; // true: detect '#FORMAT:' + XDumpFormat_t format; // current scan format + dcEndian_t endian; // current endian + uint num_base; // 8:octal, 10:decimal, 16:hexadecimal + ccp num_allowed; // string with allowed first characters + + ccp prefix; // not NULL: skip this prefix + uint prefix_len; // length of prefix + + const endian_func_t *endian_func; // endian functions +} +XScan_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void UpdateXScan ( XScan_t *scan ) +{ + DASSERT(scan); + + if ( !scan->dest && !scan->fout ) + scan->fout = stdout; + + if (scan->prefix) + { + ccp prefix = scan->prefix; + while ( *prefix > 0 && *prefix <= ' ' ) + prefix++; + scan->prefix = *prefix ? prefix : 0; + scan->prefix_len = strlen(prefix); + } + else + scan->prefix_len = 0; + + scan->endian_func = scan->endian == DC_LITTLE_ENDIAN ? &le_func : &be_func; + + if ( scan->format == XDUMPF_AUTO ) + scan->format = XDUMPF_INT_1; + + if ( scan->num_base < 2 || scan->num_base > 36 ) + scan->num_base = 16; + + scan->num_allowed + = scan->format == XDUMPF_FLOAT || scan->format == XDUMPF_DOUBLE + ? "+-." + : "+-~!"; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupXScan +( + XScan_t *scan, // data to setup + XDump_t *xd, // NULL or template + FastBuf_t *dest // not NULL: append scanned data here +) +{ + DASSERT(scan); + memset(scan,0,sizeof(*scan)); + + if (xd) + { + scan->fout = xd->f; + scan->auto_format = xd->format == XDUMPF_AUTO; + scan->format = xd->format; + scan->endian = xd->endian; + scan->num_base = xd->mode_dec ? 10 : 16; + scan->prefix = xd->prefix; + } + else + { + scan->fout = stdout; + scan->auto_format = true; + scan->format = XDUMPF_INT_1; + scan->endian = DC_DEFAULT_ENDIAN; + scan->num_base = 16; + scan->prefix = 0; + } + + scan->dest = dest; + UpdateXScan(scan); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void XScanFormat +( + // returns <0 on error, or number of dumped bytes + + XScan_t *scan, // valid scan parameters + ccp src, // source pointer + ccp eol // end of line +) +{ + DASSERT(scan); + DASSERT(src); + DASSERT(eol); + DASSERT( src <= eol ); + + noPRINT(stderr,"FORMAT: %.*s\n",(int)(eol-src),src); + + enum { MD_HEX, MD_DEC, MD_OCT, MD_NUM, MD_ENDIAN }; + + static const KeywordTab_t keytab[] = + { + { MD_HEX, "HEX", 0, XDUMPF_AUTO }, + { MD_HEX, "HEX-1", "HEX1", XDUMPF_INT_1 }, + { MD_HEX, "HEX-2", "HEX2", XDUMPF_INT_2 }, + { MD_HEX, "HEX-3", "HEX3", XDUMPF_INT_3 }, + { MD_HEX, "HEX-4", "HEX4", XDUMPF_INT_4 }, + { MD_HEX, "HEX-5", "HEX5", XDUMPF_INT_5 }, + { MD_HEX, "HEX-6", "HEX6", XDUMPF_INT_6 }, + { MD_HEX, "HEX-7", "HEX7", XDUMPF_INT_7 }, + { MD_HEX, "HEX-8", "HEX8", XDUMPF_INT_8 }, + + { MD_DEC, "DEC", 0, XDUMPF_AUTO }, + { MD_DEC, "DEC-1", "DEC1", XDUMPF_INT_1 }, + { MD_DEC, "DEC-2", "DEC2", XDUMPF_INT_2 }, + { MD_DEC, "DEC-3", "DEC3", XDUMPF_INT_3 }, + { MD_DEC, "DEC-4", "DEC4", XDUMPF_INT_4 }, + { MD_DEC, "DEC-5", "DEC5", XDUMPF_INT_5 }, + { MD_DEC, "DEC-6", "DEC6", XDUMPF_INT_6 }, + { MD_DEC, "DEC-7", "DEC7", XDUMPF_INT_7 }, + { MD_DEC, "DEC-8", "DEC8", XDUMPF_INT_8 }, + + { MD_OCT, "OCT", 0, XDUMPF_AUTO }, + { MD_OCT, "OCT-1", "OCT1", XDUMPF_INT_1 }, + { MD_OCT, "OCT-2", "OCT2", XDUMPF_INT_2 }, + { MD_OCT, "OCT-3", "OCT3", XDUMPF_INT_3 }, + { MD_OCT, "OCT-4", "OCT4", XDUMPF_INT_4 }, + { MD_OCT, "OCT-5", "OCT5", XDUMPF_INT_5 }, + { MD_OCT, "OCT-6", "OCT6", XDUMPF_INT_6 }, + { MD_OCT, "OCT-7", "OCT7", XDUMPF_INT_7 }, + { MD_OCT, "OCT-8", "OCT8", XDUMPF_INT_8 }, + + { MD_NUM, "1", 0, XDUMPF_INT_1 }, + { MD_NUM, "2", 0, XDUMPF_INT_2 }, + { MD_NUM, "3", 0, XDUMPF_INT_3 }, + { MD_NUM, "4", 0, XDUMPF_INT_4 }, + { MD_NUM, "5", 0, XDUMPF_INT_5 }, + { MD_NUM, "6", 0, XDUMPF_INT_6 }, + { MD_NUM, "7", 0, XDUMPF_INT_7 }, + { MD_NUM, "8", 0, XDUMPF_INT_8 }, + + { MD_NUM, "FLOAT", 0, XDUMPF_FLOAT }, + { MD_NUM, "DOUBLE", 0, XDUMPF_DOUBLE }, + + { MD_ENDIAN, "LE", "LITTLE", DC_LITTLE_ENDIAN }, + { MD_ENDIAN, "BE", "BIG", DC_BIG_ENDIAN }, + + {0,0,0,0} + }; + + for(;;) + { + while ( src < eol && !isalnum((int)*src) ) + src++; + + char namebuf[20], *name = namebuf; + while ( src < eol && (u8)*src > ' ' ) + { + if ( name < namebuf + sizeof(namebuf) - 1 ) + *name++ = toupper((int)*src); + src++; + } + *name = 0; + + if ( name == namebuf ) + break; + + noPRINT(stderr,"SCAN FORMAT: %s\n",namebuf); + const KeywordTab_t *key = ScanKeyword(0,namebuf,keytab); + if (!key) + continue; + + XDumpFormat_t format = key->opt; + switch(key->id) + { + case MD_HEX: + scan->num_base = 16; + break; + + case MD_DEC: + scan->num_base = 10; + break; + + case MD_OCT: + scan->num_base = 8; + break; + + case MD_NUM: + break; + + case MD_ENDIAN: + scan->endian = key->opt; + format = XDUMPF_AUTO; + break; + } + if ( format != XDUMPF_AUTO ) + scan->format = format; + + noPRINT(stderr,"F: %lld %lld => base=%d, format=%d, endian=%d\n", + key->id, key->opt, + scan->num_base, scan->format, scan->endian ); + } + UpdateXScan(scan); +} + +/////////////////////////////////////////////////////////////////////////////// + +static size_t XScanHelper +( + // returns <0 on error, or number of dumped bytes + + XScan_t *scan, // valid scan parameters + ccp source, // source pointer + ccp end, // end of source + bool term, // true: accept uncomplete last line + u64 max_size // >0: limit output size +) +{ + DASSERT(scan); + DASSERT( scan->dest || scan->fout ); + DASSERT(source); + DASSERT(end); + DASSERT( source <= end ); + + if (!max_size) + max_size = M1(max_size); + max_size -= scan->written; + + ccp src = source; + u8 destbuf[0x4000+10]; + u8 *destmax = destbuf + sizeof(destbuf) - 10; + u8 *dest = destbuf; + + while ( src < end && max_size > 0 ) + { + //--- skip all leading contral characters + + while ( src < end && (u8)*src <= ' ' ) + src++; + + + //--- find end of line + + bool found_eol = term; + ccp eol = src; + while ( eol < end ) + { + const u8 ch = *eol; + if ( !ch || ch == '\n' || ch == '\r' ) + { + found_eol = true; + break; + } + eol++; + } + if (!found_eol) + break; + + PRINT0("LINE: |%.*s|\n",(int)(eol-src),src); + + + //--- skip prefix + + if ( scan->prefix_len + && scan->prefix_len <= eol-src + && !memcmp(scan->prefix,src,scan->prefix_len) ) + { + src += scan->prefix_len; + while ( src < eol && (u8)*src <= ' ' ) + src++; + } + + if ( src == eol ) + continue; + + + //--- check for "#FORMAT:" + + if (!strncasecmp(src,"#FORMAT:",8)) + { + if (scan->auto_format) + XScanFormat(scan,src+8,eol); + src = eol; + continue; + } + + + //--- skip address column + + ccp ptr = src; + while ( ptr < eol && isalnum((int)*ptr) ) + ptr++; + noPRINT(stderr,"ADDR: |%.*s|\n",(int)(ptr-src),src); + if ( ptr > src && ptr < eol && *ptr == ':' ) + src = ptr + 1; + + + //--- scan numbers until unknown character + + noPRINT(stderr,"SCAN: |%.*s| base=%d\n",(int)(eol-src),src,scan->num_base); + + for(;;) + { + while ( src < eol && ( (u8)*src <= ' ' || *src == ',' ) ) + src++; + if ( src == eol + || (u8)TableNumbers[(u8)*src] >= scan->num_base + && !strchr(scan->num_allowed,*src) ) + { + break; + } + + if ( dest >= destmax ) + { + size_t len = dest - destbuf; + if ( len > max_size ) + len = max_size; + if (scan->dest) + AppendFastBuf(scan->dest,destbuf,len); + else + fwrite(destbuf,1,len,scan->fout); + max_size -= len; + scan->written += len; + dest = destbuf; + } + + //u64 num; + //double d; + char *end; + + switch(scan->format) + { + case XDUMPF_INT_2: + scan->endian_func->wr16(dest,str2ll(src,&end,scan->num_base)); + dest += 2; + break; + + case XDUMPF_INT_3: + scan->endian_func->wr24(dest,str2ll(src,&end,scan->num_base)); + dest += 3; + break; + + case XDUMPF_INT_4: + scan->endian_func->wr32(dest,str2ll(src,&end,scan->num_base)); + dest += 4; + break; + + case XDUMPF_INT_5: + scan->endian_func->wr40(dest,str2ll(src,&end,scan->num_base)); + dest += 5; + break; + + case XDUMPF_INT_6: + scan->endian_func->wr48(dest,str2ll(src,&end,scan->num_base)); + dest += 6; + break; + + case XDUMPF_INT_7: + scan->endian_func->wr56(dest,str2ll(src,&end,scan->num_base)); + dest += 7; + break; + + case XDUMPF_INT_8: + scan->endian_func->wr64(dest,str2ll(src,&end,scan->num_base)); + dest += 8; + break; + + case XDUMPF_FLOAT: + scan->endian_func->wrf4(dest,strtod(src,&end)); + dest += 4; + break; + + case XDUMPF_DOUBLE: + scan->endian_func->wrf8(dest,strtod(src,&end)); + dest += 8; + break; + + default: + *dest++ = str2ll(src,&end,scan->num_base); + break; + } + if ( src == end ) + break; + src = end; + } + + //--- prepare next line + + src = eol; + } + + if ( dest > destbuf ) + { + size_t len = dest - destbuf; + if ( len > max_size ) + len = max_size; + if (scan->dest) + AppendFastBuf(scan->dest,destbuf,len); + else + fwrite(destbuf,1,len,scan->fout); + scan->written += len; + } + + return src - source; +} + +/////////////////////////////////////////////////////////////////////////////// + +int XScan +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FastBuf_t *dest, // not NULL: append scanned data here + + const void *data, // data to scan + uint size, // size of 'data' + bool term, // true: accept uncomplete last line + u64 max_size // >0: limit output size +) +{ + if ( !data || !size ) + return 0; + + XScan_t scan; + SetupXScan(&scan,xd,dest); + return XScanHelper(&scan,data,data+size,term,max_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +s64 XScanByFile +( + // returns <0 on error, or number of written bytes + XDump_t *xd, // params; if NULL: use local version + FastBuf_t *dest, // not NULL: append scanned data here + FILE *f, // input file + u64 max_size // >0: limit output size +) +{ + if (!f) + return 0; + + if (!max_size) + max_size = M1(max_size); + + XScan_t scan; + SetupXScan(&scan,xd,dest); + + char buf[0x4100]; + uint start = 0; + + while ( scan.written < max_size ) + { + const uint max_read = ( sizeof(buf) - start ) / 0x1000 * 0x1000; + + size_t stat = fread(buf+start,1,max_read,f); + if (!stat) + break; + stat += start; + + const bool eof = feof(f) != 0; + int scanstat = XScanHelper(&scan,buf,buf+stat,eof,max_size); + if ( scanstat < 0 ) + return scanstat; + + start = stat - scanstat; + if (start) + memmove(buf,buf+scanstat,start); + if (eof) + break; + } + + if (start) + { + int scanstat = XScanHelper(&scan,buf,buf+start,true,max_size); + if ( scanstat < 0 ) + return scanstat; + } + + return scan.written; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-xdump.h b/project/dclib/dclib-xdump.h new file mode 100644 index 0000000..2943cb4 --- /dev/null +++ b/project/dclib/dclib-xdump.h @@ -0,0 +1,348 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DCLIB_XDUMP_H +#define DCLIB_XDUMP_H 1 + +#include "dclib-basics.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// consts and structs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define XDUMP_MAX_BYTES_PER_LINE 600 +#define XDUMP_GOOD_NUM_BUF_SIZE (6*XDUMP_MAX_BYTES_PER_LINE+1) +#define XDUMP_GOOD_TEXT_BUF_SIZE (XDUMP_MAX_BYTES_PER_LINE+1) + +#define XDUMP_MAX_LINE_WIDTH (XDUMP_GOOD_NUM_BUF_SIZE+XDUMP_GOOD_TEXT_BUF_SIZE+30) + +/////////////////////////////////////////////////////////////////////////////// +// [[XDumpCommand_t]] + +typedef enum XDumpCommand_t +{ + XDUMPC_DUMP, + XDUMPC_DIFF, + XDUMPC_SCAN, + XDUMPC__N +} +__attribute__ ((packed)) XDumpCommand_t; + +extern const ccp XDumpCommandName[XDUMPC__N]; + +/////////////////////////////////////////////////////////////////////////////// +// [[XDumpFormat_t]] + +typedef enum XDumpFormat_t +{ + XDUMPF_AUTO, + XDUMPF_INT_1, + XDUMPF_INT_2, + XDUMPF_INT_3, + XDUMPF_INT_4, + XDUMPF_INT_5, + XDUMPF_INT_6, + XDUMPF_INT_7, + XDUMPF_INT_8, + XDUMPF_FLOAT, + XDUMPF_DOUBLE, + XDUMPF__N, +} +__attribute__ ((packed)) XDumpFormat_t; + +extern const ccp XDumpFormatName[XDUMPF__N]; +extern const char XDumpFormatOption[XDUMPF__N+1]; + +/////////////////////////////////////////////////////////////////////////////// +// [[XDumpOutputFunc]] + +struct XDump_t; +typedef int (*XDumpOutputFunc) +( + struct XDump_t *xd, // internal copy of params + ccp prefix, // NULL or additional prefix (used by diff) + ccp dump, // dump to print + ccp text // text to print +); + +int XDumpStandardOutput +( + struct XDump_t *xd, // internal copy of params + ccp prefix, // NULL or additional prefix (used by diff) + ccp dump, // dump to print + ccp text // text to print +); + +/////////////////////////////////////////////////////////////////////////////// +// [[XDumpDumpFunc]] + +typedef int (*XDumpDumpFunc) +( + // returns <0 on error, or number of dumped bytes + + struct XDump_t *xd, // current params + const void *data, // data to dump + uint size, // size of 'data' + bool term // false: print only whole lines + // true: print complete data +); + +/////////////////////////////////////////////////////////////////////////////// +// [[XDump_t]] + +typedef struct XDump_t +{ + //--- options + + XDumpCommand_t cmd; // command + XDumpFormat_t format; // scan or output format + dcEndian_t endian; // endian + + bool mode_dec; // print integers as decimal instead of hex + bool mode_zero; // print leading zeros + bool mode_c; // print in c format + bool mode_align; // align addresses by 'addr % bytes_per_line' + bool mode_ignore; // ignore lines with NULL bytes only + + bool print_format; // print an info line about the format + bool print_addr; // print address field + bool print_number; // print number field + bool print_endmarker;// print "/" as end of data marker + bool print_text; // print ascii text field + bool print_summary; // print a summary at end of dump + bool print_diff_sep; // print an empty line after differ lines + + bool have_trigger; // true: trigger is active + u64 trigger; // the trigger value + + u64 start_addr; // start address + u64 assumed_size; // assumed output size, used for 'addr_fw' + uint min_addr_fw; // min field width of address + uint max_addr_fw; // max field width of address; 0=AUTO + uint min_number_fw; // >0: minimal field width for number dump + uint min_width; // minimal output width in bytes per line + uint max_width; // maximal output width in bytes per line + int extra_space; // >0: print an extra space after each # column + // =0: auto + + FILE *f; // not NULL: output file + int indent; // indention before 'prefix' + ccp prefix; // not NULL: prefix each line by this + ccp eol; // NULL or eol + + XDumpOutputFunc out_func; // NULL or a private function + + + //--- user parameters, not used, not changed + + int user_int; + void *user_ptr; + + + //--- runtime parameters + + ccp num_format1; // pointer to first numeric format + ccp num_format2; // pointer to second numeric format + uint num_format_fw; // current field width of a number + uint max_format_fw; // max possible fw for format + ccp format_name; // name of the format + + u64 written; // total written bytes + u64 addr; // current address + uint addr_fw; // field width of address + const u64 *addr_break; // NULL or null terminated list: + // break line at this addresses + + uint bytes_per_col; // bytes per column + uint cols_per_line; // columns per line + uint bytes_per_line; // bytes per line + uint col_space; // >0: print an extra space after each # column + uint null_lines; // number of currently skipped NULL lines + + bool format_printed; // true: format line printed + uint last_number_fw; // last field with of number column + uint diff_count; // number of lines with differences + uint diff_size; // <0: size1>size2 / >0: size1>size2 + + //--- functions + + XDumpDumpFunc dump_func; // dump processing function + const endian_func_t *endian_func; // endian functions +} +XDump_t; + +//----------------------------------------------------------------------------- + +void InitializeXDump ( XDump_t *xd ); +void SetupXDump ( XDump_t *xd, XDumpCommand_t cmd ); + +static inline void ResetXDump ( XDump_t *xd ) {} + +void PrintXDumpParam +( + FILE *f, // output file, abort if NULL + int indent, // indention + const ColorSet_t *colset, // NULL or color set + const XDump_t *xd // data to print +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dump interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int XDump +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + const void *data, // data to dump + uint size, // size of 'data' + bool term // false: print only whole lines + // true: print complete data +); + +/////////////////////////////////////////////////////////////////////////////// + +int XDumpByFile +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FILE *f, // input file + u64 max_size // >0: limit size to print +); + +/////////////////////////////////////////////////////////////////////////////// + +int XDump16 // wrapper for a standard hexdump +( + // returns <0 on error, or number of dumped bytes + + FILE *f_out, // output file + int indent, // indention + u64 addr, // start address + const void *data, // data + uint size // size of 'data' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int XDiff +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + + const void *data1, // data1 to compare + uint size1, // size of 'data1' + bool term1, // true: 'data1' completed + + const void *data2, // data2 to compare + uint size2, // size of 'data2' + bool term2, // true: 'data2' completed + + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +); + +/////////////////////////////////////////////////////////////////////////////// + +int XDiffByFile +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FILE *f1, // first input file + FILE *f2, // second input file + u64 max_size, // >0: limit comparing size + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +); + +/////////////////////////////////////////////////////////////////////////////// + +int XDiff16 +( + // returns <0 on error, or number of dumped bytes + + FILE *f_out, // output file + int indent, // indention + u64 addr, // start address + + const void *data1, // data + uint size1, // size of 'data' + + const void *data2, // data + uint size2, // size of 'data' + + uint limit_lines, // >0: limit number of differ-lines + bool colorize // true: use red and green on output +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int XScan +( + // returns <0 on error, or number of dumped bytes + XDump_t *xd, // params; if NULL: use local version + FastBuf_t *dest, // not NULL: append scanned data here + + const void *data, // data to scan + uint size, // size of 'data' + bool term, // true: accept uncomplete last line + u64 max_size // >0: limit output size +); + +/////////////////////////////////////////////////////////////////////////////// + +s64 XScanByFile +( + // returns <0 on error, or number of written bytes + XDump_t *xd, // params; if NULL: use local version + FastBuf_t *dest, // not NULL: append scanned data here + FILE *f, // input file + u64 max_size // >0: limit output size +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DCLIB_XDUMP_H + diff --git a/project/dclib/lib-bmg.c b/project/dclib/lib-bmg.c new file mode 100755 index 0000000..4e42c9c --- /dev/null +++ b/project/dclib/lib-bmg.c @@ -0,0 +1,6346 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 +#include + +#include "lib-bmg.h" +#include "dclib-utf8.h" +#include "dclib-xdump.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// compiler options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if 1 + #define ENABLE_BMG_REGEX 1 +#else + #define ENABLE_BMG_REGEX 0 +#endif + +#if ENABLE_BMG_REGEX + #include "dclib-regex.h" +#endif + +//----------------------------------------------------------------------------- + +#if defined(TEST) || defined(DEBUG) || HAVE_WIIMM_EXT + #define COMPARE_CREATE 1 // 0=off, 1=on +#else + #define COMPARE_CREATE 0 +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global bmg options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint opt_bmg_force_count = 0; // >0: force some operations +uint opt_bmg_align = 0x20; // alignment of raw-bg sections +bool opt_bmg_export = 0; // true: optimize for exports +bool opt_bmg_no_attrib = 0; // true: suppress attributes +bool opt_bmg_x_escapes = 0; // true: use x-scapes insetad of z-escapes +bool opt_bmg_old_escapes = 0; // true: use only old escapes +int opt_bmg_single_line = 0; // >0: single line, >1: suppress atrributes +bool opt_bmg_support_mkw = 0; // true: support MKW specific extensions +bool opt_bmg_support_ctcode = 0; // true: support MKW/CT-CODE specific extensions +bool opt_bmg_support_lecode = 0; // true: support MKW/LE-CODE specific extensions +uint opt_bmg_rcup_fill_limit = 0; // >0: limit racing cups to reduce BMG size +uint opt_bmg_track_fill_limit= 0; // >0: limit tracks to reduce BMG size +bool opt_bmg_inline_attrib = true; // true: print attrinbutes inline +int opt_bmg_colors = 1; // use c-escapss: 0:off, 1:auto, 2:on +ColNameLevelBMG opt_bmg_color_name = BMG_CNL_ALL; +uint opt_bmg_max_recurse = 10; // max recurse depth +bool opt_bmg_allow_print = 0; // true: allow '$' to print a log message +bool opt_bmg_use_slots = 0; // true: use predifined slots +bool opt_bmg_use_raw_sections= 0; // true: use raw sections + +//----------------------------------------------------------------------------- + +int opt_bmg_encoding = BMG_ENC__UNDEFINED; +uint opt_bmg_inf_size = 0; +OffOn_t opt_bmg_mid = OFFON_AUTO; +bool opt_bmg_force_attrib = false; +bool opt_bmg_def_attrib = false; + +u8 bmg_force_attrib[BMG_ATTRIB_SIZE]; +u8 bmg_def_attrib[BMG_ATTRIB_SIZE]; + +bmg_t *bmg_macros = 0; + +//----------------------------------------------------------------------------- + +const KeywordTab_t PatchKeysBMG[] = +{ + // 'opt' + // 0: no param + // 1: file param + // 2: string param + + { BMG_PM_PRINT, "PRINT", 0, 1 }, + { BMG_PM_FORMAT, "FORMAT", 0, 2 }, + #if ENABLE_BMG_REGEX + { BMG_PM_REGEX, "REGEXP", 0, 2 }, + { BMG_PM_RM_REGEX, "RM-REGEXP", "RMREGEXP", 2 }, + #endif + { BMG_PM_ID, "ID", 0, 0 }, + { BMG_PM_ID_ALL, "ID-ALL", "IDALL", 0 }, + { BMG_PM_UNICODE, "UNICODE", 0, 0 }, + { BMG_PM_RM_ESCAPES, "RM-ESCAPES", "RMESCAPES", 0 }, + + { BMG_PM_REPLACE, "REPLACE", 0, 1 }, + { BMG_PM_INSERT, "INSERT", 0, 1 }, + { BMG_PM_OVERWRITE, "OVERWRITE", 0, 1 }, + { BMG_PM_DELETE, "DELETE", 0, 1 }, + { BMG_PM_MASK, "MASK", 0, 1 }, + { BMG_PM_EQUAL, "EQUAL", 0, 1 }, + { BMG_PM_NOT_EQUAL, "NOTEQUAL", "NEQUAL", 1 }, + + { BMG_PM_GENERIC, "GENERIC", 0, 0 }, + { BMG_PM_RM_CUPS, "RM-CUPS", "RMCUPS", 0 }, + + { BMG_PM_CT_COPY, "CT-COPY", "CTCOPY", 0 }, + { BMG_PM_CT_FORCE_COPY, "CT-FORCE-COPY","CTFORCECOPY", 0 }, + { BMG_PM_CT_FILL, "CT-FILL", "CTFILL", 0 }, + + { BMG_PM_LE_COPY, "LE-COPY", "LECOPY", 0 }, + { BMG_PM_LE_FORCE_COPY, "LE-FORCE-COPY","LEFORCECOPY", 0 }, + { BMG_PM_LE_FILL, "LE-FILL", "LEFILL", 0 }, + + { BMG_PM_X_COPY, "X-COPY", "XCOPY", 0 }, + { BMG_PM_X_FORCE_COPY, "X-FORCE-COPY", "XFORCECOPY", 0 }, + { BMG_PM_X_FILL, "X-FILL", "XFILL", 0 }, + + { BMG_PM_RM_FILLED, "RM-FILLED", "XFILLED", 0 }, + + { 0,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// print & scan string16 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t bmg_color[] = +{ + { 0x0000, "OFF", "NONE", BMG_CNL_BASICS }, + { 0x0000, "GRAY", "GREY", BMG_CNL_BASICS }, + { 0x0001, "RED4", 0, BMG_CNL_BASICS }, + { 0x0002, "WHITE", 0, BMG_CNL_BASICS }, + + { 0x0008, "TRANSP", "TRANSPARENT", BMG_CNL_BASICS }, + { 0x0008, "CLEAR", 0, BMG_CNL_BASICS }, + + { 0x0010, "YOR0", "YR0", BMG_CNL_YOR }, + { 0x0011, "YOR1", "YR1", BMG_CNL_YOR }, + { 0x0012, "YOR2", "YR2", BMG_CNL_YOR }, + { 0x0013, "YOR3", "YR3", BMG_CNL_YOR }, + { 0x0014, "YOR4", "YR4", BMG_CNL_YOR }, + { 0x0015, "YOR5", "YR5", BMG_CNL_YOR }, + { 0x0016, "YOR6", "YR6", BMG_CNL_YOR }, + { 0x0017, "YOR7", "YR7", BMG_CNL_YOR }, + + { 0x0020, "RED2", 0, BMG_CNL_BASICS }, + { 0x0021, "BLUE1", "BLUE", BMG_CNL_BASICS }, + + { 0x0030, "YELLOW", 0, BMG_CNL_BASICS }, + { 0x0031, "BLUE2", 0, BMG_CNL_BASICS }, + { 0x0032, "RED3", 0, BMG_CNL_BASICS }, + { 0x0033, "GREEN", 0, BMG_CNL_BASICS }, + + { 0x0040, "RED1", "RED", BMG_CNL_BASICS }, + + {0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +uint GetWordLength16BMG ( const u16 *source ) +{ + DASSERT(source); + u16 code = ntohs(*source); + if ( code != 0x1a ) + return 1; + const uint len = ( ((u8*)source)[2] + 1 & 0xfe ) / 2; + return len ? len : 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetLength16BMG +( + const u16 *source, // source + uint max_len // max possible length +) +{ + DASSERT(source); + + const u16 *src = source, *src_end = src + max_len; + + while ( src < src_end ) + { + u16 code = ntohs(*src++); + if (!code) + return src - source - 1; + + if ( code == 0x1a ) + { + // nintendos special escape sequence! + const uint len = *(u8*)src+1 & 0xfe; + if ( len > 0 ) + src += len - 1; + } + } + + const uint len = src - source; + return len < max_len ? len : max_len; +} + +/////////////////////////////////////////////////////////////////////////////// +#undef TEST_MODE // 0:off, 1:compare, 2:replace +#if HAVE_WIIMM_EXT + #define TEST_MODE 1 +#else + #define TEST_MODE 0 +#endif +//-------------------------------------------------- + +uint PrintString16BMG +( + char *buf, // destination buffer + uint buf_size, // size of 'buf' + const u16 *src, // source + int src_len, // length of source in u16 words, -1:NULL term + u16 utf8_max, // max code printed as UTF-8 + uint quote, // 0:no quotes, 1:escape ", 2: print in quotes + int use_color // 0:no, 1:use, 2:use all +) +{ + DASSERT(buf); + DASSERT( buf_size >= 100 ); + DASSERT(src); + + //-------------------------------------------------- + #if TEST_MODE > 1 + //-------------------------------------------------- + + if ( buf_size < 2 ) + { + *buf = 0; + return 0; + } + + struct { FastBuf_t b; char space[1000]; } fb; + InitializeFastBuf(&fb,sizeof(fb)); + PrintFastBuf16BMG(&fb.b,src,src_len,utf8_max,quote,use_color); + mem_t mem = GetFastBufMem(&fb.b); + + if ( mem.len >= buf_size ) + mem.len = buf_size - 1; + memcpy(buf,mem.ptr,mem.len); + buf[mem.len] = 0; + ResetFastBuf(&fb.b); + return mem.len; + + //-------------------------------------------------- + #else // TEST_MODE <= 1 + //-------------------------------------------------- + + #if TEST_MODE == 1 + struct { FastBuf_t b; char space[1000]; } fb; + InitializeFastBuf(&fb,sizeof(fb)); + PrintFastBuf16BMG(&fb.b,src,src_len,utf8_max,quote,use_color); + #endif + + if ( buf_size < 30 ) + { + #if TEST_MODE == 1 + ResetFastBuf(&fb.b); + #endif + *buf = 0; + return 0; + } + + #undef PRINT_UTF8_MAX + //#define PRINT_UTF8_MAX 0xdb7f + + if ( utf8_max < 0x7f ) + utf8_max = 0x7f; + #ifdef PRINT_UTF8_MAX + else if ( utf8_max > PRINT_UTF8_MAX ) + utf8_max = PRINT_UTF8_MAX; + #endif + + char *dest = buf, *last_u = 0; + char *dest_end = dest + buf_size - 25; + const u16 * src_end = src + ( src_len < 0 ? buf_size : src_len ); + + if (quote>1) + *dest++ = '"'; + + while ( dest < dest_end && src < src_end ) + { + u16 code = ntohs(*src++); + if ( !code && src_len < 0 ) + break; + + if ( code == 0x1a ) + { + // nintendos special escape sequence! + uint total_words = *(u8*)src+1 >> 1; + if ( total_words >= 3 && src + total_words - 1 <= src_end ) + { + //HexDump(stdout,1,0,2,2*total_words,src-1,2*total_words); + const u16 code2 = ntohs(*src++); + if ( code2 == 0x800 && use_color > 0 && be16(src) == 1 ) + { + const u16 color = be16(src+1); + const KeywordTab_t *kt; + for ( kt = bmg_color; kt->name1 && kt->id != color; kt++ ) + ; + if ( kt->name1 && opt_bmg_color_name >= kt->opt ) + { + *dest++ = '\\'; + *dest++ = 'c'; + *dest++ = '{'; + ccp name = kt->name1; + while (*name) + *dest++ = tolower((int)*name++); + *dest++ = '}'; + } + else + dest += sprintf(dest,"\\c{%x}",color); + src += 2; + continue; + } + + if ( code2 == 0x801 ) + { + if ( opt_bmg_old_escapes || last_u != dest ) + dest = snprintfE(dest,dest_end,"\\u{%x}",be32(src)); + else + dest = snprintfE(dest-1,dest_end,",%x}",be32(src)); + last_u = dest; + src += 2; + continue; + } + + if ( ( total_words > 6 || opt_bmg_x_escapes ) && opt_bmg_old_escapes ) + { + src -= 2; + while ( total_words-- > 0 ) + dest = snprintfE(dest,dest_end,"\\x{%x}",ntohs(*src++)); + continue; + } + else if ( opt_bmg_x_escapes ) + { + if ( dest < dest_end ) + { + dest = StringCopyE(dest,dest_end,"\\x{1a"); + src--; + while ( total_words-- > 1 ) + dest = snprintfE(dest,dest_end,",%x",ntohs(*src++)); + } + *dest++ = '}'; + continue; + } + + u64 xcode = 0; + switch( total_words & 3 ) + { + case 3 & 3: xcode = be16(src); src += 1; break; + case 4 & 3: xcode = be32(src); src += 2; break; + case 5 & 3: xcode = be48(src); src += 3; break; + case 6 & 3: xcode = be64(src); src += 4; break; + } + + dest = snprintfE(dest,dest_end,"\\z{%x,%llx",code2,xcode); + while ( total_words > 6 ) + { + dest = snprintfE(dest,dest_end,",%llx",be64(src)); + src += 4; + total_words -= 4; + } + *dest++ = '}'; + continue; + } + } + + if ( code < ' ' || code == 0x7f || code == 0xff ) + { + *dest++ = '\\'; + switch(code) + { + case '\\': *dest++ = '\\'; break; + case '\a': *dest++ = 'a'; break; + case '\b': *dest++ = 'b'; break; + case '\f': *dest++ = 'f'; break; + case '\n': *dest++ = 'n'; break; + case '\r': *dest++ = 'r'; break; + case '\t': *dest++ = 't'; break; + case '\v': *dest++ = 'v'; break; + + default: + *dest++ = ( code >> 6 & 3 ) | '0'; + *dest++ = ( code >> 3 & 7 ) | '0'; + *dest++ = ( code & 7 ) | '0'; + } + } + #ifdef PRINT_UTF8_MAX + else if ( code > utf8_max ) + dest += sprintf(dest,"\\x{%x}",code); + #else + else if ( quote && code == '"' ) + { + *dest++ = '\\'; + *dest++ = '"'; + } + else if ( code > utf8_max + || code >= 0xdb80 && code <= 0xf8ff // surrogates & private use + || ( code & 0xffff ) >= 0xfffe // invalid + ) + { + dest = snprintfE(dest,dest_end,"\\x{%x}",code); + } + #endif + else + dest = PrintUTF8Char(dest,code); + } + + if ( dest > buf && dest[-1] == ' ' ) + { + dest[-1] = '\\'; + *dest++ = '0'; + *dest++ = '4'; + *dest++ = '0'; + } + + if (quote>1) + *dest++ = '"'; + + *dest = 0; + + #if TEST_MODE == 1 + const mem_t mem = GetFastBufMem(&fb.b); + if ( mem.len < buf_size ) + { + + XDump_t xd; + InitializeXDump(&xd); + xd.f = stderr; + xd.indent = 2; + xd.assumed_size = mem.len; + XDiff( &xd, buf,dest-buf,true, mem.ptr,mem.len,true, 5,false ); + } + ResetFastBuf(&fb.b); + #endif + + return dest - buf; + + //-------------------------------------------------- + #endif // TEST_MODE <= 1 + //-------------------------------------------------- +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintFastBuf16BMG +( + // return the number of appended bytes + + FastBuf_t *fb, // destination buffer (data appended) + const u16 *src, // source + int src_len, // length of source in u16 words, -1:NULL term + u16 utf8_max, // max code printed as UTF-8 + uint quote, // 0:no quotes, 1:escape ", 2: print in quotes + int use_color // >0: use \c{...} +) +{ + DASSERT(fb); + DASSERT(src); + + #undef PRINT_UTF8_MAX + //#define PRINT_UTF8_MAX 0xdb7f + + if ( utf8_max < 0x7f ) + utf8_max = 0x7f; + #ifdef PRINT_UTF8_MAX + else if ( utf8_max > PRINT_UTF8_MAX ) + utf8_max = PRINT_UTF8_MAX; + #endif + + const uint start_len = GetFastBufLen(fb); + + char buf[1300]; + char *dest = buf; + char *dest_term = dest + sizeof(buf) - 1; + char *dest_end = dest_term - 270; // escape sequence with 256 bytes possible + char *last_u = 0; + + if ( src_len < 0 ) + src_len = GetLength16BMG(src,1000000); + const u16 * src_end = src + src_len; + + if (quote>1) + *dest++ = '"'; + + while ( src < src_end ) + { + if ( dest >= dest_end ) + { + AppendFastBuf(fb,buf,dest-buf); + dest = buf; + last_u = 0; + } + + u16 code = ntohs(*src++); + if ( !code && src_len < 0 ) + break; + + if ( code == 0x1a ) + { + // nintendos special escape sequence! + uint total_words = *(u8*)src+1 >> 1; + if ( total_words >= 3 && src + total_words - 1 <= src_end ) + { + //HexDump(stdout,1,0,2,2*total_words,src-1,2*total_words); + const u16 code2 = ntohs(*src++); + if ( code2 == 0x800 && use_color > 0 && be16(src) == 1 ) + { + const u16 color = be16(src+1); + const KeywordTab_t *kt; + for ( kt = bmg_color; kt->name1 && kt->id != color; kt++ ) + ; + if ( kt->name1 && opt_bmg_color_name >= kt->opt ) + { + *dest++ = '\\'; + *dest++ = 'c'; + *dest++ = '{'; + ccp name = kt->name1; + while (*name) + *dest++ = tolower((int)*name++); + *dest++ = '}'; + } + else + dest += sprintf(dest,"\\c{%x}",color); + src += 2; + continue; + } + + if ( code2 == 0x801 ) + { + if ( opt_bmg_old_escapes || last_u != dest ) + dest = snprintfE(dest,dest_term,"\\u{%x}",be32(src)); + else + dest = snprintfE(dest-1,dest_term,",%x}",be32(src)); + last_u = dest; + src += 2; + continue; + } + + if ( ( total_words > 6 || opt_bmg_x_escapes ) && opt_bmg_old_escapes ) + { + src -= 2; + while ( total_words-- > 0 ) + dest = snprintfE(dest,dest_term,"\\x{%x}",ntohs(*src++)); + continue; + } + else if ( opt_bmg_x_escapes ) + { + if ( dest < dest_term ) + { + dest = StringCopyE(dest,dest_term,"\\x{1a"); + src--; + while ( total_words-- > 1 ) + dest = snprintfE(dest,dest_term,",%x",ntohs(*src++)); + } + *dest++ = '}'; + continue; + } + + u64 xcode = 0; + switch( total_words & 3 ) + { + case 3 & 3: xcode = be16(src); src += 1; break; + case 4 & 3: xcode = be32(src); src += 2; break; + case 5 & 3: xcode = be48(src); src += 3; break; + case 6 & 3: xcode = be64(src); src += 4; break; + } + + dest = snprintfE(dest,dest_term,"\\z{%x,%llx",code2,xcode); + while ( total_words > 6 ) + { + dest = snprintfE(dest,dest_term,",%llx",be64(src)); + src += 4; + total_words -= 4; + } + *dest++ = '}'; + continue; + } + } + + if ( code < ' ' || code == 0x7f || code == 0xff ) + { + *dest++ = '\\'; + switch(code) + { + case '\\': *dest++ = '\\'; break; + case '\a': *dest++ = 'a'; break; + case '\b': *dest++ = 'b'; break; + case '\f': *dest++ = 'f'; break; + case '\n': *dest++ = 'n'; break; + case '\r': *dest++ = 'r'; break; + case '\t': *dest++ = 't'; break; + case '\v': *dest++ = 'v'; break; + + default: + *dest++ = ( code >> 6 & 3 ) | '0'; + *dest++ = ( code >> 3 & 7 ) | '0'; + *dest++ = ( code & 7 ) | '0'; + } + } + #ifdef PRINT_UTF8_MAX + else if ( code > utf8_max ) + dest += sprintf(dest,"\\x{%x}",code); + #else + else if ( quote && code == '"' ) + { + *dest++ = '\\'; + *dest++ = '"'; + } + else if ( code > utf8_max + || code >= 0xdb80 && code <= 0xf8ff // surrogates & private use + || ( code & 0xffff ) >= 0xfffe // invalid + ) + { + dest = snprintfE(dest,dest_term,"\\x{%x}",code); + } + #endif + else + dest = PrintUTF8Char(dest,code); + } + + if ( dest > buf && dest[-1] == ' ' ) + { + dest[-1] = '\\'; + *dest++ = '0'; + *dest++ = '4'; + *dest++ = '0'; + } + + if (quote>1) + *dest++ = '"'; + + AppendFastBuf(fb,buf,dest-buf); + return GetFastBufLen(fb) - start_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +#if 0 +uint ScanFastBuf16BMG +( + + FastBuf_t *fb, // destination buffer + ccp src, // UTF-8 source + int src_len, // length of source, -1: determine with strlen() + const bmg_t *bmg // NULL or support for \m{...} +); +#endif + +uint ScanString16BMG +( + u16 * buf, // destination buffer + uint buf_size, // size of 'buf' in u16 words + ccp src, // UTF-8 source + int src_len, // length of source, -1: determine with strlen() + const bmg_t *bmg // NULL or support for \m{...} +) +{ + DASSERT(buf); + DASSERT(src); + + u16 * dest = buf; + u16 * dest_end = dest + buf_size; + ccp src_end = src + ( src_len < 0 ? strlen(src) : src_len ); + + while ( dest < dest_end && src < src_end ) + { + if ( *src == '\\' && src+1 < src_end ) + { + src++; + switch(*src++) + { + case '\\': *dest++ = htons('\\'); break; + case 'a': *dest++ = htons('\a'); break; + case 'b': *dest++ = htons('\b'); break; + case 'f': *dest++ = htons('\f'); break; + case 'n': *dest++ = htons('\n'); break; + case 'r': *dest++ = htons('\r'); break; + case 't': *dest++ = htons('\t'); break; + case 'v': *dest++ = htons('\v'); break; + + case 'm': + case 'M': + if ( src < src_end && *src == '{' ) + { + const char mode = src[-1]; + src++; + bmg_scan_mid_t scan; + for(;;) + { + while ( src < src_end && *src == ',' ) + src++; + if ( ScanBMGMID(&scan,0,src,src_end) < 1 ) + break; + src = scan.scan_end; + + PRINT("\\M{%x} : bmg=%d\n",scan.mid[0],bmg!=0); + + const bmg_item_t *dptr = 0; + if ( bmg && mode == 'm' ) + dptr = FindItemBMG(bmg,scan.mid[0]); + if ( !dptr && bmg_macros ) + dptr = FindItemBMG(bmg_macros,scan.mid[0]); + + if (!dptr) + { + char buf[16]; + snprintf(buf,sizeof(buf),"\\%c{%x}",mode,scan.mid[0]); + ccp src = buf; + while ( *src && dest < dest_end ) + *dest++ = htons(*src++); + } + else if ( dptr->text ) + { + const u16 *copy = dptr->text; + const u16 *copy_end = copy + dptr->len; + while ( copy < copy_end && dest < dest_end ) + *dest++ = *copy++; + } + if ( *src != ',' ) + break; + } + if ( *src == '}' ) + src++; + } + break; + + case 'x': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + for(;;) + { + char *end; + u16 code = strtoul(src,&end,16); + src = end; + if ( dest < dest_end ) + *dest++ = htons(code); + if ( !have_brace || *src != ',' ) + break; + src++; + } + if ( have_brace && *src == '}' ) + src++; + } + break; + + case 'z': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + char * end; + const u16 code1 = strtoul(src,&end,16); + src = end; + uint total_words = code1+0x100 >> 9; + if ( dest + total_words <= dest_end ) + { + *dest++ = htons(0x1a); + *dest++ = htons(code1); + u64 xcode = 0; + if ( *src == ',' ) + { + xcode = strtoull(src+1,&end,16); + src = end; + } + switch( total_words & 3 ) + { + case 3 & 3: write_be16(dest,xcode); dest += 1; break; + case 4 & 3: write_be32(dest,xcode); dest += 2; break; + case 5 & 3: write_be48(dest,xcode); dest += 3; break; + case 6 & 3: write_be64(dest,xcode); dest += 4; break; + } + while ( total_words > 6 ) + { + total_words -= 4; + if ( *src == ',' ) + { + xcode = strtoull(src+1,&end,16); + src = end; + } + else + xcode = 0; + write_be64(dest,xcode); + dest += 4; + } + while ( *src == ',' ) + { + strtoull(src+1,&end,16); + src = end; + } + } + if ( have_brace && *src == '}' ) + src++; + } + break; + + case 'u': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + for(;;) // >>>>> comma list support since v1.44 <<<<< + { + char * end; + u32 code = strtoul(src,&end,16); + src = end; + if ( dest + 4 <= dest_end ) + { + *dest++ = htons(0x001a); + *dest++ = htons(0x0801); + *dest++ = htons(code>>16); + *dest++ = htons(code); + } + if ( !have_brace || *src != ',' ) + break; + src++; + } + if ( have_brace && *src == '}' ) + src++; + } + break; + + case 'c': + { + const bool have_brace = *src == '{'; + if (have_brace) + src++; + + u16 code = 0; + if ( *src >= '0' && *src <= '9' ) + { + char *end; + code = strtoul(src,&end,16); + src = end; + } + else + { + char keyname[50], *kp = keyname; + while (isalnum((int)*src)) + { + if ( kp < keyname + sizeof(keyname) - 1 ) + *kp++ = *src; + src++; + } + *kp = 0; + + const KeywordTab_t *key = ScanKeyword(0,keyname,bmg_color); + if (key) + code = key->id; + else + code = strtoul(keyname,0,16); + } + if ( have_brace && *src == '}' ) + src++; + + if ( dest + 4 <= dest_end ) + { + *dest++ = htons(0x001a); + *dest++ = htons(0x0800); + *dest++ = htons(0x0001); + *dest++ = htons(code); + } + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + src--; + u16 code = 0; + int idx; + for ( idx = 0; idx < 3 && *src >= '0' && *src <= '7'; idx++ ) + code = code << 3 | *src++ - '0'; + *dest++ = htons(code); + } + break; + + default: + *dest++ = htons('\\'); + src--; + break; + } + } + else + *dest++ = htons(ScanUTF8AnsiChar(&src)); + } + + return dest - buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static unsigned long int strtoul16 +( + const u16 * ptr16, + u16 ** end16, + int base +) +{ + DASSERT(ptr16); + + char buf[100]; + char * dest = buf; + const u16 * p16 = ptr16; + while ( dest < buf + sizeof(buf)-1 ) + { + int ch = be16(p16++); + if ( ch >= '0' && ch <= '9' + || ch >= 'a' && ch <= 'f' + || ch >= 'A' && ch <= 'F' + || ch == 'x' + || ch == 'X' + || isspace(ch) + ) + { + *dest++ = ch; + } + else + break; + } + DASSERT( dest < buf + sizeof(buf) ); + *dest = 0; + + char *end; + unsigned long num = strtoul(buf,&end,base); + if (end16) + *end16 = (u16*)ptr16 + (end-buf); + return num; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u16 * skip_ctrl16 ( const u16 * ptr ) +{ + DASSERT(ptr); + for(;;) + { + const u16 ch = be16(ptr); + if ( !ch || ch > ' ' ) + return (u16*)ptr; + ptr++; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static u16 * ScanRange16U32 +( + const u16 * arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..2) + u32 * p_n1, // first return value (valid pointer) + u32 * p_n2, // second return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +) +{ + DASSERT(arg); + DASSERT(p_n1); + DASSERT(p_n2); + TRACE("ScanRange16U32()\n"); + + int stat = 0; + + u32 n1 = ~(u32)0, n2 = 0; + arg = skip_ctrl16(arg); + + if ( be16(arg) == '-' ) + n1 = min; + else + { + u16 * end; + u32 num = strtoul16(arg,&end,0); + if ( arg == end ) + goto abort; + + stat = 1; + arg = skip_ctrl16(end); + n1 = num; + } + + if ( be16(arg) != '-' ) + { + stat = 1; + n2 = n1; + goto abort; + } + + arg = skip_ctrl16(arg+1); + u16 * end; + n2 = strtoul16(arg,&end,0); + if ( end == arg ) + n2 = max; + stat = 2; + arg = end; + + abort: + + if ( stat > 0 ) + { + if ( n1 < min ) + n1 = min; + if ( n2 > max ) + n2 = max; + } + + if ( !stat || n1 > n2 ) + { + stat = 0; + n1 = ~(u32)0; + n2 = 0; + } + + if (p_stat) + *p_stat = stat; + *p_n1 = n1; + *p_n2 = n2; + + arg = skip_ctrl16(arg); + + TRACE("END ScanRange16U32() stat=%u, n=%u..%u\n",stat,n1,n2); + return (u16*)arg; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanSectionsBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bmg_sect_list_t * ScanSectionsBMG ( cvp data, uint size ) +{ + //--- initialize + + if ( !data || size < sizeof(bmg_header_t) || memcmp(data,BMG_MAGIC,8) ) + return 0; + + bmg_sect_list_t sl; + memset(&sl,0,sizeof(sl)); + sl.header = (bmg_header_t*)data; + sl.source_size = ntohl(sl.header->size); + sl.n_sections = ntohl(sl.header->n_sections); + + FastBuf_t fb; + InitializeFastBufAlloc(&fb,10*sizeof(bmg_sect_info_t)); + + bmg_sect_info_t si; + + + //--- scan sections + + u8 *data_end = (u8*)data + size; + bmg_section_t *sect = (bmg_section_t*)(sl.header+1); + for(;;) + { + if ( (u8*)sect + sizeof(*sect) > data_end ) + break; + + memset(&si,0,sizeof(si)); + si.offset = (u8*)sect - (u8*)data; + si.sect_size = si.real_size = ntohl(sect->size); + if ( (u8*)sect + si.real_size > data_end ) + si.real_size = data_end - (u8*)sect; + + si.sect = sect; + si.info = "?"; + + if (!memcmp(sect->magic,BMG_INF_MAGIC,sizeof(sect->magic))) + { + sl.pinf = (bmg_inf_t*)sect; + si.head_size = sizeof(bmg_inf_t); + si.elem_size = ntohs(sl.pinf->inf_size); + si.n_elem_head = ntohs(sl.pinf->n_msg); + si.known = true; + si.supported = true; + si.info = "offset & attributes"; + } + else if (!memcmp(sect->magic,BMG_DAT_MAGIC,sizeof(sect->magic))) + { + sl.pdat = (bmg_dat_t*)sect; + si.head_size = sizeof(bmg_dat_t); + si.known = true; + si.supported = true; + si.info = "string pool"; + } + else if (!memcmp(sect->magic,BMG_STR_MAGIC,sizeof(sect->magic))) + { + sl.pstr = (bmg_str_t*)sect; + si.head_size = sizeof(bmg_str_t); + si.known = true; + si.supported = false; + si.info = "secondary string pool"; + } + else if (!memcmp(sect->magic,BMG_MID_MAGIC,sizeof(sect->magic))) + { + sl.pmid = (bmg_mid_t*)sect; + si.head_size = sizeof(bmg_mid_t); + si.elem_size = sizeof(u32); + si.n_elem_head = ntohs(sl.pinf->n_msg); + si.known = true; + si.supported = true; + si.info = "message ids"; + } + else if (!memcmp(sect->magic,BMG_FLW_MAGIC,sizeof(sect->magic))) + { + sl.pflw = (bmg_flw_t*)sect; + si.known = true; + } + else if (!memcmp(sect->magic,BMG_FLI_MAGIC,sizeof(sect->magic))) + { + sl.pfli = (bmg_fli_t*)sect; + si.known = true; + } + + if (si.elem_size) + { + si.n_elem = ( si.real_size - si.head_size ) / si.elem_size; + if ( si.n_elem_head && si.n_elem > si.n_elem_head ) + si.n_elem = si.n_elem_head; + } + + if ( si.head_size && si.head_size < si.real_size ) + { + si.is_empty = true; + const u8 *ptr = (u8*)sect + si.head_size; + const u8 *end = (u8*)sect + si.real_size; + while ( ptr < end ) + if ( *ptr++ ) + { + si.is_empty = false; + break; + } + } + + sect = (bmg_section_t*)( (u8*)sect + si.real_size ); + AppendFastBuf(&fb,&si,sizeof(si)); + sl.n_info++; + } + + + //--- finalize + + memset(&si,0,sizeof(si)); + AppendFastBuf(&fb,&si,sizeof(si)); + + mem_t mem = GetFastBufMem(&fb); + sl.this_size = sizeof(sl) + mem.len; + bmg_sect_list_t *dest = MALLOC(sl.this_size); + memcpy(dest,&sl,sizeof(sl)); + memcpy(dest->info,mem.ptr,mem.len); + ResetFastBuf(&fb); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +const bmg_sect_info_t * SearchSectionBMG +( + const bmg_sect_list_t *sl, // valid, created by ScanSectionsBMG() + cvp search, // magic to search + bool abbrev, // if not found: allow abbreviations _> strcmp() + bool icase // if not found: ignore case and try again +) +{ + DASSERT(sl); + + // make sure, that magic is null terminated + char magic[5]; + memcpy(magic,search,4); + magic[4] = 0; + + + //--- search exact + + const bmg_sect_info_t *si; + for ( si = sl->info; si->sect; si++ ) + if (!memcmp(magic,si->sect->magic,4)) + return si; + + + //--- search abbrev + + const uint mlen = strlen(magic); + if (!mlen) + abbrev = false; + + if (abbrev) + { + for ( si = sl->info; si->sect; si++ ) + if (!memcmp(magic,si->sect->magic,mlen)) + return si; + } + + + //--- search icase + + if (icase) + { + uint i; + for ( i = 0; i < 4; i++ ) + magic[i] = tolower((int)magic[i]); + + const bmg_sect_info_t *abbrev_found = 0; + char buf[5]; + + for ( si = sl->info; si->sect; si++ ) + { + for ( i = 0; i < 4; i++ ) + buf[i] = tolower((int)si->sect->magic[i]); + + if (!memcmp(magic,buf,4)) + return si; + if ( abbrev && !memcmp(magic,buf,mlen) ) + abbrev_found = si; + } + return abbrev_found; + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_raw_section_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bmg_raw_section_t * GetRawSectionBMG ( bmg_t *bmg, cvp magic ) +{ + DASSERT(bmg); + DASSERT(magic); + + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + if (!memcmp(raw->magic,magic,sizeof(raw->magic))) + { + PRINT("GetRawSectionBMG(%s), found\n",PrintID(magic,4,0)); + return raw; + } + + PRINT("GetRawSectionBMG(%s), not found\n",PrintID(magic,4,0)); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_raw_section_t * CreateRawSectionBMG ( bmg_t *bmg, cvp magic ) +{ + bmg_raw_section_t **raw; + for ( raw = &bmg->first_raw; *raw; raw = &(*raw)->next ) + if (!memcmp((*raw)->magic,magic,sizeof((*raw)->magic))) + { + PRINT("CreateRawSectionBMG(%s), found\n",PrintID(magic,4,0)); + return (*raw); + } + + DASSERT(raw); + PRINT("CreateRawSectionBMG(%s), create\n",PrintID(magic,4,0)); + bmg_raw_section_t *p = CALLOC(1,sizeof(*p)); + memcpy(p->magic,magic,sizeof(p->magic)); + InitializeFastBuf(&p->data,sizeof(p->data)); + return *raw = p; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// attribute helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetAttribBMG +( + const bmg_t * bmg, // pointer to bmg + bmg_item_t * item // dest item +) +{ + DASSERT(bmg); + DASSERT(item); + + item->slot = BMG_NO_PREDEF_SLOT; + item->attrib_used = bmg->attrib_used; + memcpy(item->attrib,bmg->attrib,sizeof(item->attrib)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyAttribBMG +( + const bmg_t * dest, // pointer to destination bmg + bmg_item_t * dptr, // dest item + const bmg_item_t * sptr // source item +) +{ + DASSERT(dest); + DASSERT(dptr); + DASSERT(sptr); + + DASSERT( dest->attrib_used <= BMG_ATTRIB_SIZE ); + DASSERT( sptr->attrib_used <= BMG_ATTRIB_SIZE ); + + if ( sptr->slot != BMG_NO_PREDEF_SLOT ) + dptr->slot = sptr->slot; + + if ( opt_bmg_force_attrib ) + { + //dptr->attrib_used = dest->attrib_used; + memcpy(dptr->attrib,bmg_force_attrib,dptr->attrib_used); + } + else if ( sptr->attrib_used ) + { + if ( dest->attrib_used <= sptr->attrib_used ) + dptr->attrib_used = dest->attrib_used; + else + { + dptr->attrib_used = sptr->attrib_used; + memset( dptr->attrib+dptr->attrib_used, 0, BMG_ATTRIB_SIZE - sptr->attrib_used ); + } + memcpy(dptr->attrib,sptr->attrib,dptr->attrib_used); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static char * scan_attrib ( u8 *dest, uint size, ccp src ) +{ + DASSERT(dest); + DASSERT( size <= BMG_ATTRIB_SIZE ); + memset(dest,0,BMG_ATTRIB_SIZE); + + while ( *src == ' ' || *src == '\t' ) + src++; + const bool have_brackets = *src == '['; + if ( have_brackets ) + src++; + + int idx = 0, add_idx = 0; + for(;;) + { + while ( *src == ' ' || *src == '\t' ) + src++; + + if ( *src == ',' ) + { + src++; + idx++; + add_idx = 0; + } + else if ( *src == '/' ) + { + src++; + idx = ALIGN32(idx+1,4); + add_idx = 0; + } + else if ( *src == '%' ) + { + src++; + char *end; + u32 num = str2ul(src,&end,16); + if ( src == end ) + break; + + idx = ALIGN32(idx+add_idx,4); + noPRINT("U32[0x%02x/%02x]: %8x\n",idx,size,num); + + char buf[4]; + write_be32(buf,num); + const int max_copy = (int)size - (int)idx; + if ( max_copy > 0 ) + memcpy(dest+idx,buf, max_copy < 4 ? max_copy : 4 ); + idx += 3; + add_idx = 1; + src = end; + } + else + { + char *end; + u8 num = str2ul(src,&end,16); + if ( src == end ) + break; + + idx += add_idx; + noPRINT("U8[0x%02x/%02x]: %8x\n",idx,size,num); + if ( idx < size ) + dest[idx] = num; + add_idx = 1; + src = end; + } + } + //HexDump16(stderr,0,0,dest,size); + + if ( have_brackets && *src == ']' ) + src++; + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void copy_param +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + DASSERT(dest); + DASSERT(src); + + if ( !dest->param_defined && src->param_defined ) + { + memcpy(dest->attrib,src->attrib,sizeof(dest->attrib)); + dest->inf_size = src->inf_size; + dest->have_mid = src->have_mid; + dest->attrib_used = src->attrib_used; + dest->use_color_names = src->use_color_names; + dest->use_mkw_messages = src->use_mkw_messages; + dest->param_defined = true; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_item_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// special values for bmg_item_t::text +u16 bmg_null_entry[] = {0}; + +////////////////////////////////////////////////////////////////////////////// + +void FreeItemBMG( bmg_item_t * bi ) +{ + DASSERT(bi); + if (bi->text) + { + if ( bi->alloced_size && !isSpecialEntryBMG(bi->text) ) + FREE( bi->text); + bi->text = bmg_null_entry; + } + bi->len = bi->alloced_size = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignItemText16BMG +( + bmg_item_t * bi, // valid item + const u16 * text16, // text to store + int len // length of 'text16'. + // if -1: detect length by finding + // first NULL value. +) +{ + DASSERT(bi); + if ( !text16 || isSpecialEntryBMG(text16) ) + { + FreeItemBMG(bi); + bi->text = (u16*)text16; + return; + } + + if ( len < 0 ) + { + const u16 * ptr = text16; + while (*ptr) + ptr++; + len = ptr - text16; + } + + if ( bi->text && bi->alloced_size ) + { + if ( len+1 <= bi->alloced_size ) + { + memcpy(bi->text,text16,len*sizeof(*bi->text)); + bi->text[len] = 0; + bi->len = len; + return; + } + FREE(bi->text); + } + + bi->alloced_size = len + 1; + bi->len = len; + bi->text = CALLOC(bi->alloced_size,sizeof(*bi->text)); + memcpy(bi->text,text16,len*sizeof(*bi->text)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignItemScanTextBMG +( + bmg_item_t * bi, // valid item + ccp utf8, // UTF-8 text to scan + int len // length of 'utf8'. + // if -1: detect length with strlen() +) +{ + DASSERT(bi); + + if (!utf8) + AssignItemText16BMG(bi,0,0); + else + { + const uint bufsize = 10000; + u16 buf[bufsize]; + len = ScanString16BMG(buf,bufsize,utf8,len,0); + AssignItemText16BMG(bi,buf,len); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignItemTextBMG +( + bmg_item_t * bi, // valid item + ccp utf8, // UTF-8 text to store + int len // length of 'utf8'. + // if -1: detect length with strlen() +) +{ + DASSERT(bi); + + if (!utf8) + AssignItemText16BMG(bi,0,0); + else + { + u16 buf[10000]; + u16 *ptr = buf; + u16 *end = buf + sizeof(buf)/sizeof(*buf); + + if ( len < 0 ) + { + while ( ptr < end ) + { + ulong code = ScanUTF8AnsiChar(&utf8); + if (!code) + break; + *ptr++ = htons(code); + } + } + else + { + ccp utf8_end = utf8 + len; + while ( utf8 < utf8_end && ptr < end ) + *ptr++ = htons(ScanUTF8AnsiCharE(&utf8,utf8_end)); + } + AssignItemText16BMG(bi,buf,ptr-buf); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsItemEqualBMG +( + const bmg_item_t * bi1, // NULL or valid item + const bmg_item_t * bi2 // NULL or valid item +) +{ + if ( bi1 == bi2 ) + return true; + if ( !bi1 || !bi2 ) + return false; + + return bi1->len == bi2->len + && bi1->attrib_used == bi2->attrib_used + && !memcmp(bi1->text,bi2->text,bi1->len*sizeof(*bi2->text)) + && !memcmp(bi1->attrib,bi2->attrib,bi1->attrib_used); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint find_item_helper ( const bmg_t * bmg, u32 mid, bool * found ) +{ + DASSERT(bmg); + DASSERT(found); + + int beg = 0; + int end = bmg->item_used - 1; + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + const bmg_item_t * item = bmg->item + idx; + if ( mid < item->mid ) + end = idx - 1 ; + else if ( mid > item->mid ) + beg = idx + 1; + else + { + *found = true; + return idx; + } + } + + *found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_item_t * FindItemBMG ( const bmg_t * bmg, u32 mid ) +{ + DASSERT(bmg); + + bool found; + uint idx = find_item_helper(bmg,mid,&found); + DASSERT( idx < bmg->item_used || idx == bmg->item_used && !found ); + + return found ? bmg->item + idx : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_item_t * FindAnyItemBMG ( const bmg_t * bmg, u32 mid ) +{ + DASSERT(bmg); + + bool found; + uint idx = find_item_helper(bmg,mid,&found); + DASSERT( idx < bmg->item_used || idx == bmg->item_used && !found ); + + bmg_item_t * item = bmg->item + idx; + if (!found) + { + bmg_item_t * item_end = bmg->item + bmg->item_used; + while ( item < item_end && item->mid < mid ) + item++; + } + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +bmg_item_t * InsertItemBMG + ( bmg_t *bmg, u32 mid, u8 *attrib, uint attrib_used, bool *old_item ) +{ + DASSERT(bmg); + DASSERT( attrib_used <= BMG_ATTRIB_SIZE ); + + if (opt_bmg_force_attrib) + { + attrib_used = bmg->attrib_used; + attrib = bmg_force_attrib; + } + else if ( attrib_used > bmg->attrib_used ) + attrib_used = bmg->attrib_used; + + bmg->param_defined = true; // don't accept modifications on patch + + //if ( attrib && attrib_used ) HexDump16(stderr,0,0x100,attrib,attrib_used); + + bool found; + uint idx = find_item_helper(bmg,mid,&found); + DASSERT( idx < bmg->item_used || idx == bmg->item_used && !found ); + + if (old_item) + *old_item = found; + + if (found) + { + bmg_item_t * item = bmg->item + idx; + if ( attrib && attrib_used ) + { + item->attrib_used = bmg->attrib_used; + memcpy(item->attrib,bmg->attrib,sizeof(item->attrib)); + memcpy(item->attrib,attrib,attrib_used); + } + return item; + } + + DASSERT( bmg->item_used <= bmg->item_size ); + if ( bmg->item_used == bmg->item_size ) + { + bmg->item_size += 1000; + bmg->item = REALLOC(bmg->item,bmg->item_size*sizeof(*bmg->item)); + } + + bmg_item_t * item = bmg->item + idx; + memmove( item+1, item, (bmg->item_used-idx) * sizeof(*bmg->item) ); + bmg->item_used++; + DASSERT( idx < bmg->item_used ); + memset(item,0,sizeof(*item)); + item->mid = mid; + item->slot = BMG_NO_PREDEF_SLOT; + + memcpy(item->attrib,bmg->attrib,sizeof(item->attrib)); + if ( attrib && attrib_used ) + { + item->attrib_used = bmg->attrib_used; + memcpy(item->attrib,attrib,attrib_used); + } + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DeleteItemBMG ( bmg_t *bmg, u32 mid ) +{ + DASSERT(bmg); + + bmg_item_t *dptr = FindItemBMG(bmg,mid); + if (dptr) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(bmg,dptr); + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool HavePredifinedSlots ( bmg_t *bmg ) +{ + DASSERT(bmg); + + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + for ( bi = bmg->item; bi < bi_end; bi++ ) + if ( bi->text && bi->slot != BMG_NO_PREDEF_SLOT ) + return bmg->have_predef_slots = true; + return bmg->have_predef_slots = false; +} + +/////////////////////////////////////////////////////////////////////////////// + +u16 * ModifiyItem +( + // return a fixed 'ptr' + bmg_item_t * bi, // valid item, prepare text modification + const u16 * ptr // NULL or a pointer within 'bi->text' +) +{ + DASSERT(bi); + + if (!bi->alloced_size) + { + const uint size = (bi->len+1) * sizeof(*bi->text); + u16 *text = MALLOC(size); + memcpy(text,bi->text,size); + if (ptr) + ptr = text + ( ptr - bi->text ); + bi->text = text; + bi->alloced_size = bi->len; + } + return (u16*)ptr; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MKW hooks /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int GetTrackIndexBMG0 ( uint idx, int result_on_invalid ) +{ + static const u8 track_pos[BMG_N_TRACK] = + { + 8, 1, 2, 4, 0, 5, 6, 7, 9,15,11, 3, 14,10,12,13, + 16,20,25,26, 27,31,23,18, 21,30,29,17, 24,22,19,28 + }; + + return idx < BMG_N_TRACK + ? track_pos[idx] + : idx < BMG_N_CT_TRACK + ? idx + : result_on_invalid; +} + +int (*GetTrackIndexBMG) ( uint idx, int result_on_invalid ) = GetTrackIndexBMG0; + +/////////////////////////////////////////////////////////////////////////////// + +static int GetArenaIndexBMG0 ( uint idx, int result_on_invalid ) +{ + static const u8 arena_pos[BMG_N_ARENA] = { 1,0,3,2,4, 7,8,9,5,6 }; + + return idx < BMG_N_ARENA + ? arena_pos[idx] + : result_on_invalid; +} + +int (*GetArenaIndexBMG) ( uint idx, int result_on_invalid ) = GetArenaIndexBMG0; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// bmg encoding support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t TabEncodingBMG[] = +{ + // if opt==1: not implemented yet + { BMG_ENC_CP1252, "CP-1252", "CP1252", 0 }, + { BMG_ENC_CP1252, "1", 0, 0 }, + + { BMG_ENC_UTF16BE, "UTF-16BE", "UTF16BE", 0 }, + { BMG_ENC_UTF16BE, "2", 0, 0 }, + + { BMG_ENC_SHIFT_JIS, "SHIFT-JIS", "SHIFTJIS", 0 }, + { BMG_ENC_SHIFT_JIS, "3", "SJIS", 0 }, + + { BMG_ENC_UTF8, "UTF-8", "UTF8", 0 }, + { BMG_ENC_UTF8, "4", 0, 0 }, + + { BMG_ENC__UNDEFINED, "AUTO", "UNDEFINED", 0 }, + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +ccp GetEncodingNameBMG ( int encoding, ccp return_if_invalid ) +{ + switch (encoding) + { + case BMG_ENC_CP1252: return "CP1252"; + case BMG_ENC_UTF16BE: return "UTF-16/be"; + case BMG_ENC_SHIFT_JIS: return "Shift-JIS"; + case BMG_ENC_UTF8: return "UTF-8"; + } + return return_if_invalid; +} + +//----------------------------------------------------------------------------- + +int CheckEncodingBMG ( int encoding, int return_if_invalid ) +{ + return encoding >= BMG_ENC__MIN && encoding <= BMG_ENC__MAX + ? encoding : return_if_invalid; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup/reset bmg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AssignEncodingBMG ( bmg_t * bmg, int encoding ) +{ + if ( encoding >= BMG_ENC__MIN && encoding <= BMG_ENC__MAX ) + bmg->encoding = encoding; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignInfSizeBMG ( bmg_t * bmg, uint inf_size ) +{ + bmg->param_defined = true; + + if ( inf_size < 4 || inf_size > BMG_INF_LIMIT ) + { + ERROR0(ERR_WARNING, + "INF data size is outside allowed range 4..%u => ignored: %s\n", + BMG_INF_LIMIT, bmg->fname ); + return; + } + + if ( inf_size > BMG_INF_MAX_SIZE ) + ERROR0(ERR_WARNING, + "INF data size is %u bytes, but only %u bytes are supported." + " The additional bytes are always assumed to be NULL: %s", + inf_size, BMG_INF_MAX_SIZE, bmg->fname ); + + bmg->inf_size = opt_bmg_inf_size ? opt_bmg_inf_size : inf_size; + bmg->attrib_used = bmg->inf_size - 4; + if ( bmg->attrib_used > sizeof(bmg->attrib) ) + bmg->attrib_used = sizeof(bmg->attrib); + if (opt_bmg_def_attrib) + memcpy(bmg->attrib,bmg_def_attrib,bmg->attrib_used); + + if ( inf_size != 8 ) + { + // not MKW => change defaults + if ( bmg->use_color_names == 2 ) + bmg->use_color_names = 1; + if ( bmg->use_mkw_messages == 2 ) + bmg->use_mkw_messages = 1; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeBMG ( bmg_t * bmg ) +{ + ASSERT( MID_VERSUS_POINTS >= MID_PARAM_BEG + && MID_VERSUS_POINTS < MID_PARAM_END ); + + DASSERT(bmg); + memset(bmg,0,sizeof(*bmg)); + bmg->have_mid = 1; + bmg->encoding = BMG_ENC__DEFAULT; + bmg->use_slots = opt_bmg_use_slots; + bmg->use_raw_sections = opt_bmg_use_raw_sections; + + bmg->unknown_inf_0c = BMG_INF_DEFAULT_0C; + bmg->unknown_mid_0a = BMG_MID_DEFAULT_0A; + bmg->unknown_mid_0c = BMG_MID_DEFAULT_0C; + + //-- setup for MKW + + if (opt_bmg_support_mkw) + { + bmg->use_color_names = 1; + bmg->use_mkw_messages = 1; + AssignInfSizeBMG(bmg,BMG_INF_STD_SIZE); + + if ( !opt_bmg_def_attrib && bmg->attrib_used == 4 ) + write_be32(bmg->attrib,BMG_INF_STD_ATTRIB); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetBMG ( bmg_t * bmg ) +{ + DASSERT(bmg); + + bmg_item_t * bi = bmg->item; + bmg_item_t * bi_end = bi + bmg->item_used; + while ( bi < bi_end ) + FreeItemBMG(bi++); + + FREE(bmg->item); + + if (bmg->data_alloced) + FREE(bmg->data); + FREE(bmg->raw_data); + FreeString(bmg->fname); + + bmg_raw_section_t *raw = bmg->first_raw; + while (raw) + { + ResetFastBuf(&raw->data); + bmg_raw_section_t *del = raw; + raw = raw->next; + FREE(del); + } + + memset(bmg,0,sizeof(*bmg)); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int CompareU32 ( const u32 *a, const u32 *b ) +{ + return *a < *b ? -1 : *a > *b; +} + +//----------------------------------------------------------------------------- + +static u32 FindMajority32 ( cvp first, uint n_elem, uint delta ) +{ + DASSERT( first || !n_elem ); + + if ( n_elem < 3 ) + return n_elem ? *(u32*)first : 0; + + u32 *buf = MALLOC(n_elem*sizeof(*buf)); + + uint i; + for ( i = 0; i < n_elem; i++ ) + { + buf[i] = *(u32*)first; + first = (u32*)( (u8*)first + delta ); + } + //HexDump16(stderr,0,0,buf,64); + qsort(buf,n_elem,sizeof(*buf),(qsort_func)CompareU32); + + uint max_val = 0, max_count = 0; + const u32 *ptr = buf; + const u32 *end = ptr + n_elem; + while ( ptr < end ) + { + const u32 *beg = ptr; + const u32 val = *ptr++; + while ( ptr < end && *ptr == val ) + ptr++; + if ( ptr - beg > max_count ) + { + max_count = ptr - beg; + max_val = val; + } + } + + FREE(buf); + return max_val; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void scan_raw_cp1252 + ( bmg_item_t *bi, const u8 *start, const u8 *end, FastBuf_t *fb ) +{ + u8 buf[4]; + const u8 *ptr; + for ( ptr = start; ptr < end && *ptr; ) + { + u16 code = *ptr++; + if ( code >= 0x80 && code < 0xa0 ) + code = TableCP1252_80[code-0x80]; + if ( code == 0x1a ) + { + const uint len = *ptr++; + if ( len > 2 ) + { + buf[0] = 0; + buf[1] = 0x1a; + buf[2] = len+1; + AppendFastBuf(fb,buf,3); + AppendFastBuf(fb,ptr,len-2); + if (!(len&1)) + AppendFastBuf(fb,buf,1); + ptr += len - 2; + } + } + else + { + code = htons(code); + AppendFastBuf(fb,&code,2); + } + } + + const mem_t mem = GetFastBufMem(fb); + AssignItemText16BMG(bi,(u16*)mem.ptr,mem.len/2); +} + +//----------------------------------------------------------------------------- + +static void scan_raw_utf16 ( bmg_item_t *bi, const u8 *start, const u8 *end ) +{ + const u8 *ptr; + for ( ptr = start; ptr < end; ptr += 2 ) + { + if (!ptr[0]) + { + if (!ptr[1]) + break; + if ( ptr[1] == 0x1a ) + { + ptr += 2; + const uint skip = *ptr + 1 & 0x1e; + if ( skip > 4 ) + ptr += skip - 4; + } + } + } + + AssignItemText16BMG( bi, (u16*)start, ( ptr - start )/2 ); +} + +//----------------------------------------------------------------------------- + +static void scan_raw_shift_jis + ( bmg_item_t *bi, const u8 *start, const u8 *end, FastBuf_t *fb ) +{ + u8 buf[4]; + const u8 *ptr; + for ( ptr = start; ptr < end; ) + { + int code = ScanShiftJISChar(&ptr); + if (!code) + break; + if ( code == 0x1a ) + { + const uint len = *ptr++; + if ( len > 2 ) + { + buf[0] = 0; + buf[1] = 0x1a; + buf[2] = len+1; + AppendFastBuf(fb,buf,3); + AppendFastBuf(fb,ptr,len-2); + if (!(len&1)) + AppendFastBuf(fb,buf,1); + ptr += len - 2; + } + } + else if ( code >= 0 ) + { + code = htons(code); + AppendFastBuf(fb,&code,2); + } + } + + const mem_t mem = GetFastBufMem(fb); + AssignItemText16BMG(bi,(u16*)mem.ptr,mem.len/2); +} + +//----------------------------------------------------------------------------- + +static void scan_raw_utf8 + ( bmg_item_t *bi, const u8 *start, const u8 *end, FastBuf_t *fb ) +{ + u8 buf[4]; + const u8 *ptr; + for ( ptr = start; ptr < end && *ptr; ) + { + u32 code = ScanUTF8AnsiCharE((ccp*)&ptr,(ccp)end); + if ( code >= 0x80 && code < 0xa0 ) + code = TableCP1252_80[code -0x80]; + if ( code == 0x1a ) + { + const uint len = *ptr++; + if ( len > 2 ) + { + buf[0] = 0; + buf[1] = 0x1a; + buf[2] = len+1; + AppendFastBuf(fb,buf,3); + AppendFastBuf(fb,ptr,len-2); + if (!(len&1)) + AppendFastBuf(fb,buf,1); + ptr += len - 2; + } + } + else + { + code = htons(code); + AppendFastBuf(fb,&code,2); + } + } + + const mem_t mem = GetFastBufMem(fb); + AssignItemText16BMG(bi,(u16*)mem.ptr,mem.len/2); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawBMG ( bmg_t * bmg ) +{ + DASSERT(bmg); + DASSERT(bmg->data); + + const bmg_header_t * bh = (bmg_header_t*)bmg->data; + DASSERT(!memcmp(bh->magic,BMG_MAGIC,sizeof(bh->magic))); + + u32 data_size = ntohl(bh->size); + if ( data_size > bmg->data_size ) + { + ERROR0(ERR_WARNING, + "BMG is %u bytes shorter than announced: %s", + data_size - bmg->data_size, bmg->fname ); + data_size = bmg->data_size; + } + + const bmg_sect_list_t *sl = ScanSectionsBMG(bmg->data,data_size); + if ( !sl || !sl->header ) + return ERROR0(ERR_INVALID_DATA,"Invalid BMG data: %s",bmg->fname); + + + //--- check encoding + + bmg->encoding = bh->encoding; + switch (bmg->encoding) + { + case BMG_ENC_CP1252: + case BMG_ENC_UTF16BE: + case BMG_ENC_SHIFT_JIS: + case BMG_ENC_UTF8: + //>>> ok! <<< + break; + + default: + if (!opt_bmg_force_count) + return ERROR0(ERR_ERROR, + "Can't read BMG with encoding #%u (%s): %s\n", + bmg->encoding, GetEncodingNameBMG(bmg->encoding,"?"), + bmg->fname ); + ERROR0(ERR_WARNING, + "Try to read BMG with unsupported encoding #%u (%s): %s\n", + bmg->encoding, GetEncodingNameBMG(bmg->encoding,"?"), + bmg->fname ); + } + + + //--- iterate sections + + const bmg_sect_info_t *si; + for ( si = sl->info; si->sect; si++ ) + { + if ( si->real_size < si->sect_size ) + ERROR0(ERR_WARNING, + "Size of BMG section %s set to %u bytes (%u lesser than announced): %s\n", + PrintID(si->sect->magic,4,0), + si->real_size, si->sect_size - si->real_size, bmg->fname ); + + if ( !si->supported ) + { + bmg_raw_section_t *raw = CreateRawSectionBMG(bmg,si->sect->magic); + DASSERT(raw); + AssignFastBuf( &raw->data, si->sect->data, + si->real_size - sizeof(bmg_section_t) ); + raw->total_size = si->real_size; + PRINT("SCAN/RAW SECTION »%s« data: %u = 0x%x, total: %u = 0x%x\n", + PrintID(raw->magic,4,0), + GetFastBufLen(&raw->data), GetFastBufLen(&raw->data), + raw->total_size, raw->total_size ); + } + } + + PRINT("%p %p %p\n",sl->pinf,sl->pdat,sl->pmid); + const bmg_inf_t *pinf = bmg->inf = sl->pinf; + const bmg_dat_t *pdat = bmg->dat = sl->pdat; + const bmg_mid_t *pmid = bmg->mid = sl->pmid; + + if ( !sl->pinf || !sl->pdat ) + return ERROR0(ERR_INVALID_DATA,"Corrupted BMG file: %s\n",bmg->fname); + + bmg->unknown_inf_0c = ntohl(pinf->unknown_0c); + + uint max_item; + bmg->have_mid = pmid != 0; + if (bmg->have_mid) + { + bmg->unknown_mid_0a = ntohs(pmid->unknown_0a); + bmg->unknown_mid_0c = ntohl(pmid->unknown_0c); + + max_item = ( ntohl(pmid->size) - sizeof(*pmid) ) / sizeof(*pmid->mid); + if ( max_item > ntohs(pmid->n_msg) ) + max_item = ntohs(pmid->n_msg); + PRINT("BMG with MID1: N=%u [inf=%u,mid=%u], %s\n", + max_item, ntohs(pinf->n_msg), ntohs(pmid->n_msg), bmg->fname ); + } + else + { + max_item = ntohs(pinf->n_msg); + PRINT("BMG without MID1: N=%u, %s\n",max_item,bmg->fname); + } + + const uint max_offset = ( ntohl(pdat->size) - sizeof(*pdat) ) + / sizeof(*pdat->text_pool); + const u8 *text_end = pdat->text_pool + max_offset; + + const u8 *raw_inf = (u8*)pinf->list; + const uint src_inf_size = ntohs(pinf->inf_size); + AssignInfSizeBMG(bmg,src_inf_size); + + + //--- find default attributes + + if (!opt_bmg_def_attrib) + { + uint idx; + for ( idx = 0; idx < bmg->attrib_used; idx += 4 ) + { + const u32 major = FindMajority32(raw_inf+idx+4,max_item,src_inf_size); + noPRINT("-> IDX: %2u -> %8x\n",idx,ntohl(major)); + memcpy(bmg->attrib+idx,&major,4); + } + //HexDump16(stderr,8,0x10,bmg->attrib,bmg->attrib_used); + } + + + //--- decode raw files + + struct { FastBuf_t b; char space[500]; } fb_mgr; + InitializeFastBuf(&fb_mgr,sizeof(fb_mgr)); + #if HAVE_PRINT + uint ins_count = 0, empty_count = 0; + #endif + + uint prev_mid = 0; + uint slot, unsort_count = 0, ffff_count = 0; + for ( slot = 0; slot < max_item; slot++, raw_inf += src_inf_size ) + { + bmg_inf_item_t *item = (bmg_inf_item_t*)raw_inf; + const u32 offset = ntohl(item->offset); + + u32 mid = pmid ? ntohl(pmid->mid[slot]) : slot; + if ( mid == 0xffff && !offset ) + ffff_count++; + if ( mid < prev_mid ) + unsort_count++; + prev_mid = mid; + + #ifdef TEST + if ( offset >= max_offset ) + return ERROR0(ERR_INVALID_DATA, + "Invalid pointer at file offset 0x%zx (INF item #%u): %s\n", + (u8*)&pinf->list[slot].offset - bmg->data, slot, bmg->fname); + #else + if ( offset >= max_offset ) + return ERROR0(ERR_INVALID_DATA,"Corrupted BMG file: %s\n",bmg->fname); + #endif + + #if HAVE_PRINT0 + { + bmg_item_t *bi = FindItemBMG(bmg,mid); + if (bi) + PRINT("MID EXISTS: %x, slot:%x\n",mid,slot); + } + #endif + + bmg_item_t *bi = InsertItemBMG(bmg,mid,item->attrib,bmg->attrib_used,0); + DASSERT(bi); + bi->slot = slot < BMG_NO_PREDEF_SLOT ? slot : BMG_NO_PREDEF_SLOT; + + if (!offset) + { + #if HAVE_PRINT + empty_count++; + #endif + AssignItemText16BMG(bi,bmg_null_entry,0); + continue; + } + + switch (bmg->encoding) + { + case BMG_ENC_CP1252: + ClearFastBuf(&fb_mgr.b); + scan_raw_cp1252( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + + case BMG_ENC_UTF16BE: + scan_raw_utf16( bi, pdat->text_pool + offset, text_end ); + break; + + case BMG_ENC_SHIFT_JIS: + ClearFastBuf(&fb_mgr.b); + scan_raw_shift_jis( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + + case BMG_ENC_UTF8: + ClearFastBuf(&fb_mgr.b); + scan_raw_utf8( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + + default: + // fallback for cast opt_bmg_force_count>0 + DASSERT(opt_bmg_force_count); + ClearFastBuf(&fb_mgr.b); + scan_raw_cp1252( bi, pdat->text_pool + offset, text_end, &fb_mgr.b ); + break; + } + + #if HAVE_PRINT + ins_count++; + #endif + } + + #if HAVE_PRINT + PRINT("BMG: %u+%u=%u/%u messages inserted => %u | n(unsort)=%d, n(ffff)=%d\n", + empty_count, ins_count, empty_count+ins_count, + max_item, bmg->item_used, + unsort_count, ffff_count ); + #endif + ResetFastBuf(&fb_mgr.b); + + bmg->have_predef_slots = pmid && max_item > 1 + && ( unsort_count > 1 || ffff_count > 1 ); + + if (bmg->have_predef_slots) + { + bmg_item_t *bi = FindItemBMG(bmg,0xffff); + if ( bi && isSpecialEntryBMG(bi->text) ) + AssignItemText16BMG(bi,0,0); + } + else + { + // clear slots settings + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + for ( bi = bmg->item; bi < bi_end; bi++ ) + bi->slot = BMG_NO_PREDEF_SLOT; + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int (*AtHookBMG) ( bmg_t * bmg, ccp line, ccp line_end ) = 0; + +/////////////////////////////////////////////////////////////////////////////// + +static char * GetNext ( ccp ptr, ccp end ) +{ + DASSERT(ptr); + DASSERT(end); + + while ( ptr < end && ( *ptr == ' ' || *ptr == '\t' || *ptr == '\r' ) ) + ptr++; + return (char*)ptr; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xbuf]] GetText ( FastBuf_t *fb ccp ptr, ccp end, uint *len ) + +static char * GetText ( char *buf, uint bufsize, ccp ptr, ccp end, uint *len ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + + char *dest = buf; + char *dest_end = buf + bufsize - 1; + + ptr = GetNext(ptr,end); + + if ( ptr < end && ( *ptr == '\'' || *ptr == '\"' ) ) + { + const char quote = *ptr++; + while ( ptr < end && *ptr != quote ) + { + if ( dest < dest_end ) + *dest++ = *ptr; + ptr++; + } + if ( *ptr == quote ) + ptr++; + } + else + { + while ( ptr < end && (uchar)*ptr > ' ' ) + { + if ( dest < dest_end ) + *dest++ = *ptr; + ptr++; + } + } + + *dest = 0; + if (len) + *len = dest - buf; + return GetNext(ptr,end); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextBMG ( bmg_t * bmg ) +{ + DASSERT(bmg); + TRACE("SCAN BMG/TEXT: %s\n",bmg->fname); + bmg->is_text_src = true; + + ccp ptr = (ccp)bmg->data; + ccp end = ptr + bmg->data_size; + ptr += GetTextBOMLen(ptr,bmg->data_size); + + ccp bmg_name = 0; +// [[xbuf]] + char xbuf[2*BMG_MSG_BUF_SIZE]; + enumError err_stat = ERR_OK; + u32 cond = 0; + + ParamField_t assign; + InitializeParamField(&assign); + assign.free_data = true; + + for(;;) + { + while ( ptr < end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr >= end ) + break; + + ccp start = ptr = GetNext(ptr,end); + while ( ptr < end && *ptr != '\r' && *ptr != '\n' ) + ptr++; + + if ( *start == '@' ) + { + if (AtHookBMG) + { + const int stat = AtHookBMG(bmg,start,ptr); + if ( stat < -1 ) + continue; + if ( stat >= 0 ) + { + err_stat = stat; + break; + } + } + + start = GetNext(start+1,ptr); + const bool silent = start < ptr && *start == '!'; + if (silent) + start = GetNext(start+1,ptr); + + if ( *start == '?' ) + { + // SYNTAX/IF: '@?' MID_COND + + u32 mid; + ScanMidBMG(bmg,&mid,0,0,start+1,end,(char**)&start); + if ( ptr >= end ) + break; + cond = mid; + continue; + } + + if ( *start == '=' ) + { + // SYNTAX/MATCH: '@=' PATTERN [':'] LINE + + uint len; +// [[xbuf]] + start = GetText(xbuf,sizeof(xbuf),start+1,ptr,&len); + if (len) + { +// [[xbuf]] + PRINT("MATCH |%s|\n",xbuf); + if (!bmg_name) + { + bmg_name = GetNameBMG(bmg,0); + PRINT("NAME |%s|\n",bmg_name); + } + +// [[xbuf?]] + if (MatchPattern(xbuf,bmg_name,'/')) + { + ptr = GetNext(start,ptr); + if ( *ptr == ':' ) + ptr++; + } + } + continue; + } + + if ( *start == '$' ) + { + // SYNTAX/ECHO: '@$' TEXT_TO_EOL + + if (opt_bmg_allow_print) + { + start++; + printf("#%.*s\n",(int)(ptr-start),start); + } + continue; + } + + const bool close_current = *start == '>'; + if ( close_current || *start == '<' ) + { + // SYNTAX/GOTO: '@' ['!'] '>' FILE + // SYNTAX/INCLUDE: '@' ['!'] '<' FILE + + uint len; +// [[xbuf]] + start = GetText(xbuf,sizeof(xbuf),start+1,ptr,&len); + if ( len && bmg->recurse_depth < opt_bmg_max_recurse ) + { + PRINT("BMG/%s: %s\n",close_current?"GOTO":"INCLUDE",xbuf); + bmg_t bmg2; +// [[xbuf?]] + const enumError err + = LoadBMG(&bmg2,true,bmg->fname,xbuf,silent?FM_SILENT:0); + if ( err == ERR_OK ) + PatchOverwriteBMG(bmg,&bmg2,true); + ResetBMG(&bmg2); + if ( close_current && err != ERR_CANT_OPEN ) + break; + } + continue; + } + + char namebuf[100], *nptr = namebuf; + while ( start < ptr && *start > ' ' && *start < 'z' && *start != '=' ) + { + if ( nptr < namebuf + sizeof(namebuf) - 1 ) + *nptr++ = toupper((int)*start); + start++; + } + *nptr = 0; + start = GetNext(start,ptr); + + + //--- settings without mandatory '=' + + const bool have_equal = start < ptr && *start == '='; + if (have_equal) + start = GetNext(start+1,ptr); + + if (!strcmp(namebuf,"SECTION")) + { + bmg->current_raw = 0; + uint len = ScanEscapedString(namebuf,sizeof(namebuf), + start, ptr-start, false, -1, 0 ); + if (!len) + continue; + if ( len > 4 ) + { + ERROR0(ERR_WARNING, + "@SECTION name exceeds 4 [%u] characters: %s\n", + len, namebuf ); + continue; + } + while ( len < 4 ) + namebuf[len++] = 0; + PRINT("@SECTION »%s« %02x %02x %02x %02x\n", + PrintID(namebuf,4,0), + namebuf[0], namebuf[1], namebuf[2], namebuf[3] ); + bmg->current_raw = CreateRawSectionBMG(bmg,namebuf); + DASSERT(bmg->current_raw); + ClearFastBuf(&bmg->current_raw->data); + continue; + } + + if (!strcmp(namebuf,"X")) + { + if (!bmg->current_raw) + continue; + + // skip addr + + ccp p = start; + while (isalnum((int)*p)) + p++; + if ( p > start && *p == ':' ) + start = p+1; + + for (;;) + { + start = GetNext(start,ptr); + if ( start >= ptr || *start == ':' ) + break; + char *end; + uint num = str2ul(start,&end,16); + if ( end == start ) + break; + start = end; + AppendCharFastBuf(&bmg->current_raw->data,num); + } + continue; + } + + + //--- settings with mandatory '=' + + if ( !have_equal || start >= ptr ) + continue; + + noPRINT("PARAM: %s = |%.*s|\n",namebuf,(int)(ptr-start),start); + if (!strcmp(namebuf,"ENCODING")) + AssignEncodingBMG(bmg,str2l(start,0,10)); + else if (!strcmp(namebuf,"BMG-MID")) + bmg->have_mid = str2ul(start,0,10) > 0; + else if (!strcmp(namebuf,"INF-SIZE")) + AssignInfSizeBMG(bmg,str2ul(start,0,10)); + else if (!strcmp(namebuf,"DEFAULT-ATTRIBS")) + { + noPRINT("--- SCAN ATTRIB: %.*s\n",(int)(ptr-start),start); + scan_attrib(bmg->attrib,bmg->attrib_used,start); + //HEXDUMP16(0,0,bmg->attrib,bmg->attrib_used); + } + else if (!strcmp(namebuf,"UNKNOWN-INF32-0C")) + bmg->unknown_inf_0c = str2ul(start,0,10); + else if (!strcmp(namebuf,"UNKNOWN-MID16-0A")) + bmg->unknown_mid_0a = str2ul(start,0,10); + else if (!strcmp(namebuf,"UNKNOWN-MID32-0C")) + bmg->unknown_mid_0c = str2ul(start,0,10); + continue; + } + + + //--- scan message ids + + u32 mid1 = 0, mid2 = 0, mid3 = 0; + const int mid_stat + = ScanMidBMG(bmg,&mid1,&mid2,&mid3,start,end,(char**)&start); + + if ( mid_stat <= 0 ) // not a valid message line + continue; + + //--- scan predifined slot + + bmg_slot_t slot = BMG_NO_PREDEF_SLOT; + if ( *start == '@' ) + { + char *end; + uint num = str2l(++start,&end,16); + PRINT0("SLOT[%#x] = %#x\n",mid1,num); + if ( end > start && num < BMG_NO_PREDEF_SLOT ) + slot = num; + start = GetNext(end,ptr); + } + + + //--- scan attributes + + u8 attrib_buf[BMG_ATTRIB_SIZE]; + memcpy(attrib_buf,bmg->attrib,sizeof(attrib_buf)); + uint attrib_used = 0; + + if ( *start == '[' ) + { + ccp aptr = scan_attrib(attrib_buf,bmg->attrib_used,start+1); + aptr = GetNext(aptr,ptr); + if ( *aptr == ']' ) + { + attrib_used = bmg->attrib_used; + start = GetNext(aptr+1,ptr); + } + } + + if ( *start == '/' || *start == '~' ) + { + if ( *start == '~' ) + { + attrib_used = bmg->attrib_used; + start = GetNext(start+1,ptr); + + if ( *start == '[' ) + scan_attrib(attrib_buf,attrib_used,start); + else + { + memset(attrib_buf,0,sizeof(attrib_buf)); + const u32 attr = str2ul(start,0,10); + write_be32(attrib_buf,attr); + } + } + + bmg_item_t * bi = InsertItemBMG(bmg,mid1,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->slot = slot; + AssignItemText16BMG(bi,bmg_null_entry,0); + if (mid2) + { + bi = InsertItemBMG(bmg,mid2,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->slot = slot; + AssignItemText16BMG(bi,bmg_null_entry,0); + } + if (mid3) + { + bi = InsertItemBMG(bmg,mid3,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->slot = slot; + AssignItemText16BMG(bi,bmg_null_entry,0); + } + continue; + } + + if ( *start == ':' ) + { + start = GetNext(start+1,end); + ccp ptr = start; + while ( ptr < end && isalnum((int)*ptr) ) + ptr++; + + if ( ptr > start ) + { + u32 *tab[] = { &mid1, &mid2, &mid3, 0 }, **tptr; + for ( tptr = tab; *tptr; tptr++ ) + { + const u32 mid = **tptr; + if (mid) + { + DeleteItemBMG(bmg,mid); + + char midbuf[20]; + snprintf(midbuf,sizeof(midbuf),"%x",mid); + noPRINT("BMG ASSIGN: %5s = |%.*s|\n", + midbuf,(int)(ptr-start),start); + InsertParamField(&assign,midbuf,false,mid,MEMDUP(start,ptr-start)); + } + } + } + continue; + } + + if ( *start != '=' ) // not a valid line + continue; + +// [[xbuf]] + // assume that xbuf is large enough + // => make only simple and silent buffer overrun tests + +// [[xbuf?]] + char * in_beg = xbuf + sizeof(xbuf)/2; + char * in_end = xbuf + sizeof(xbuf) - 0x10; + DASSERT( in_beg < in_end ); + char * in_ptr = in_beg; + + for(;;) + { + if ( ptr[-1] == '\r' ) + ptr--; + start++; + if ( *start == ' ' ) + start++; + if ( start > ptr ) + start = ptr; + + int len = ptr - start; + if ( len > in_end - in_ptr ) + len = in_end - in_ptr; + memcpy(in_ptr,start,len); + in_ptr += len; + + while ( ptr < end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr == end || *ptr != '+' ) + break; + + start = ptr; + while ( ptr < end && *ptr != '\n' ) + ptr++; + } + *in_ptr = 0; + + const int in_len = in_ptr - in_beg; + noPRINT("%x,%x,%x |%.*s|\n",mid1,mid2,mid3,in_len,in_beg); +// [[xbuf]] ScanFastBuf16BMG(); + u16 * dest_buf = (u16*)xbuf; + const uint len16 + = ScanString16BMG( dest_buf, sizeof(xbuf)/sizeof(u16), in_beg, in_len, bmg ); + + bmg_item_t * bi = InsertItemBMG(bmg,mid1,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->cond = cond; + bi->slot = slot; + AssignItemText16BMG(bi,dest_buf,len16); + if (mid2) + { + bi = InsertItemBMG(bmg,mid2,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->cond = cond; + bi->slot = slot; + AssignItemText16BMG(bi,dest_buf,len16); + } + if (mid3) + { + bi = InsertItemBMG(bmg,mid3,attrib_buf,attrib_used,0); + DASSERT(bi); + bi->cond = cond; + bi->slot = slot; + AssignItemText16BMG(bi,dest_buf,len16); + } + } + + + //--- manage assignments + + bool dirty = assign.used > 0; + while (dirty) + { + dirty = false; + ParamFieldItem_t *pfi = assign.field, *end; + for ( end = pfi + assign.used; pfi < end; pfi++ ) + { + if (!pfi->num) + continue; + + bmg_item_t *bi = FindItemBMG(bmg,pfi->num); + noPRINT("## %5x = %5s = |%s| -> found=%d, len=%d\n", + pfi->num, pfi->key, (ccp)pfi->data, bi!=0, bi ? bi->len : -1 ); + pfi->key = 0; + if ( bi && bi->len ) + { + pfi->num = 0; + continue; + } + + bmg_scan_mid_t scan; + ScanBMGMID(&scan,bmg,(ccp)pfi->data,0); + if ( scan.status < 1 ) + { + pfi->num = 0; + continue; + } + + uint i; + for ( i = 0; i < scan.n_mid; i++ ) + { + bmg_item_t *src = FindItemBMG(bmg,scan.mid[i]); + if ( src && src->text ) + { + bmg_item_t *dest + = InsertItemBMG(bmg,pfi->num,src->attrib,src->attrib_used,0); + DASSERT(dest); + + // src is invalid now => search again + src = FindItemBMG(bmg,scan.mid[i]); + if (src) + { + AssignItemText16BMG(dest,src->text,src->len); + dest->attrib_used = src->attrib_used; + memcpy(dest->attrib,src->attrib,dest->attrib_used); + dirty = true; + pfi->num = 0; + } + break; + } + } + } + } + ResetParamField(&assign); + + + //--- terminate + + if (bmg->data_alloced) + FREE(bmg->data); + bmg->data = 0; + bmg->data_size = 0; + + return err_stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanBMG(), LoadBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp fname, // NULL or filename for error messages + const u8 * data, // data / if NULL: use internal data + size_t data_size // size of 'data' if data not NULL +) +{ + if (initialize) + InitializeBMG(bmg); + else if (data) + ResetBMG(bmg); + + if (data) + { + bmg->data = (u8*)data; + bmg->data_size = data_size; + } + + if (fname) + bmg->fname = STRDUP(fname); + + const bmg_header_t * bh = (bmg_header_t*)bmg->data; + if ( !memcmp(bh->magic,BMG_MAGIC,sizeof(bh->magic)) + && ntohl(bh->size) <= bmg->data_size ) + return ScanRawBMG(bmg); + + ccp ptr = (ccp)bmg->data + GetTextBOMLen(bmg->data,bmg->data_size); + if ( !memcmp(ptr,BMG_TEXT_MAGIC,4) ) + return ScanTextBMG(bmg); + + return ERROR0(ERR_INVALID_DATA,"No BMG file: %s\n",bmg->fname); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp parent_fname, // NULL or filename of parent for dir-extract + ccp fname, // filename of source + FileMode_t file_mode // open modes +) +{ + DASSERT(bmg); + DASSERT(fname); + PRINT("LoadBMG(%p,init=%d) fname=%s\n", bmg, initialize, fname ); + + if (initialize) + InitializeBMG(bmg); + else + ResetBMG(bmg); + + if (!strcmp(fname,"0")) + { + bmg->is_text_src = false; + bmg->fname = STRDUP(fname); + return ERR_OK; + } + + char path[PATH_MAX]; + if ( *fname != '/' && parent_fname ) + { + ccp slash = strrchr(parent_fname,'/'); + char *dest; + if (slash) + dest = StringCopySM(path,sizeof(path)-2,parent_fname,slash-parent_fname); + else + dest = StringCopyS(path,sizeof(path)-2,parent_fname); + *dest++ = '/'; + StringCopyE(dest,path+sizeof(path),fname); + fname = path; + } + noPRINT(">>> fname: |%s|d\n",fname); + + u8 *data; + uint size; + enumError err = OpenReadFile( fname, 0, file_mode, 0,0, + &data, &size, &bmg->fname, &bmg->fatt ); + if ( err == ERR_OK ) + { + bmg->data = data; + bmg->data_size = size; + bmg->data_alloced = true; + return ScanBMG(bmg,false,0,0,0); + } + + FREE(data); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_create_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void grow_bi_list ( bmg_create_t *bc, uint slot ) +{ + if ( bc->bi_size <= slot ) + { + const uint esize = sizeof(*bc->bi_list); + const uint old_size = bc->bi_size; + const uint byte_size = ( slot + slot/8 + 10 ) * esize; + bc->bi_size = GetGoodAllocSize(byte_size) / esize; + bc->bi_list = REALLOC( bc->bi_list, bc->bi_size * esize ); + memset( bc->bi_list + old_size, 0, ( bc->bi_size - old_size ) * esize ); + PRINT("GROW BI-LIST: %u/%u/%u\n",slot,bc->bi_used,bc->bi_size); + } +} + +//----------------------------------------------------------------------------- + +void InitializeCreateBMG ( bmg_create_t *bc, bmg_t *bmg ) +{ + DASSERT(bc); + DASSERT(bmg); + + memset(bc,0,sizeof(*bc)); + bc->bmg = bmg; + bc->have_mid = opt_bmg_mid == OFFON_AUTO + ? bmg->have_mid : opt_bmg_mid >= OFFON_ON; + + uint n_predef = 0, other_count = 0; + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + for ( bi = bmg->item; bi < bi_end; bi++ ) + if (bi->text) + { + if ( bi->slot == BMG_NO_PREDEF_SLOT ) + other_count++; + else if ( n_predef <= bi->slot ) + n_predef = bi->slot + 1; + } + + bmg->have_predef_slots = n_predef >= 0; + if (n_predef) + { + grow_bi_list( bc, n_predef + other_count ); + + uint next = n_predef; + for ( bi = bc->bmg->item; bi < bi_end; bi++ ) + if (bi->text) + { + uint slot = bi->slot; + if ( slot >= n_predef || bc->bi_list[slot] ) + { + slot = next++; + grow_bi_list(bc,slot); + } + PRINT0(">>> ASSIGN %4x = %4x\n",slot,bi->mid); + bc->bi_list[slot] = bi; + } + + bc->bi_used = next; + PRINT("BI-LIST: %u/%u/%u\n",next,bc->bi_used,bc->bi_size); + } + + uint n_item = bc->bi_used; + if ( n_item < bmg->item_used ) + n_item = bmg->item_used; + + InitializeFastBufAlloc( &bc->inf, n_item * bmg->inf_size ); + InitializeFastBufAlloc( &bc->dat, n_item * 50 ); + InitializeFastBufAlloc( &bc->mid, n_item * sizeof(u32) ); + + if (bc->have_mid) + { + uint i; + for ( i = 0; i < bc->bi_used; i++ ) + AppendBE32FastBuf(&bc->mid,0xffff); + } +} + +//----------------------------------------------------------------------------- + +void ResetCreateBMG ( bmg_create_t *bc ) +{ + DASSERT(bc); + + FREE(bc->bi_list); + ResetFastBuf(&bc->inf); + ResetFastBuf(&bc->dat); + ResetFastBuf(&bc->mid); + memset(bc,0,sizeof(0)); +} + +//----------------------------------------------------------------------------- + +bmg_item_t * GetFirstBI ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + + bc->bi = bc->bmg->item; + bc->bi_end = bc->bi + bc->bmg->item_used; + bc->slot = -1; + return GetNextBI(bc); +} + +//----------------------------------------------------------------------------- + +bmg_item_t * GetNextBI ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + + bmg_t *bmg = bc->bmg; + + bmg_item_t *bi = 0; + int slot = bc->slot + 1; + if (bc->bi_list) + { + while ( slot < bc->bi_used && !bc->bi_list[slot] ) + slot++; + if ( slot < bc->bi_used ) + { + bi = bc->bi_list[slot]; + PRINT0(">>> BI/LIST: %4x: %04x\n",slot,bi->mid); + } + } + else if ( bc->bi < bc->bi_end ) + { + bi = bc->bi++; + PRINT0(">>> BI/DIRECT: %4x: %04x\n",slot,bi->mid); + } + + if (!bi) + { + bc->slot = -1; + return 0; + } + + //--- found: assign data + + bc->slot = slot; + + u32 *pmid = (u32*)WriteFastBuf( &bc->mid, bc->slot*sizeof(u32), + 0, sizeof(u32) ); + DASSERT(pmid); + *pmid = htonl(bi->mid); + + const uint inf_size = bmg->inf_size; + bmg_inf_item_t *ii = (bmg_inf_item_t*)WriteFastBuf + ( &bc->inf, bc->slot * inf_size, 0, inf_size ); + DASSERT(ii); + if ( !isSpecialEntryBMG(bi->text) ) + ii->offset = htonl(GetFastBufLen(&bc->dat)); + memcpy(ii->attrib,bi->attrib,bmg->attrib_used); + + if ( bc->n_msg <= bc->slot ) + bc->n_msg = bc->slot + 1; + return bi; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void create_raw_cp1252 ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendCharFastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + const u16 *ptr = bi->text; + const u16 *end = ptr + bi->len; + while ( ptr < end ) + { + u16 code = ntohs(*ptr++); + if ( code == 0x1a ) + { + AppendCharFastBuf(&bc->dat,code); + uint len = *(u8*)ptr; + AppendCharFastBuf(&bc->dat,len-1); + AppendFastBuf(&bc->dat,(u8*)ptr+1,len-3); + ptr += (len-1)/2; + } + else if (code) + { + if ( code >= 0x100 ) + { + uint i; + for ( i = 0; i < 0x20; i++ ) + if ( TableCP1252_80[i] == code ) + { + code = i + 0x80; + break; + } + } + if ( code < 0x100 ) + AppendCharFastBuf(&bc->dat,code); + } + } + AppendCharFastBuf(&bc->dat,0); + } + } +} + +//----------------------------------------------------------------------------- + +static void create_raw_utf16 ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendBE16FastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + AppendFastBuf( &bc->dat, bi->text, bi->len*2 ); + AppendBE16FastBuf(&bc->dat,0); + } + } +} + +//----------------------------------------------------------------------------- + +static void create_raw_shift_jis ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendCharFastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + const u16 *ptr = bi->text; + const u16 *end = ptr + bi->len; + while ( ptr < end ) + { + const u16 code = ntohs(*ptr++); + if ( code == 0x1a ) + { + AppendCharFastBuf(&bc->dat,code); + uint len = *(u8*)ptr; + AppendCharFastBuf(&bc->dat,len-1); + AppendFastBuf(&bc->dat,(u8*)ptr+1,len-3); + ptr += (len-1)/2; + } + else + { + const int val = GetShiftJISChar(code); + if ( val >= 0x100 ) + AppendBE16FastBuf(&bc->dat,val); + else if ( val > 0 ) + AppendCharFastBuf(&bc->dat,val); + } + } + AppendCharFastBuf(&bc->dat,0); + } + } +} + +//----------------------------------------------------------------------------- + +static void create_raw_utf8 ( bmg_create_t *bc ) +{ + DASSERT(bc); + DASSERT(bc->bmg); + DASSERT(!GetFastBufLen(&bc->dat)); + + AppendCharFastBuf(&bc->dat,0); + + bmg_item_t *bi; + for ( bi = GetFirstBI(bc); bi; bi = GetNextBI(bc) ) + { + if ( !isSpecialEntryBMG(bi->text) ) + { + const u16 *ptr = bi->text; + const u16 *end = ptr + bi->len; + while ( ptr < end ) + { + u16 code = ntohs(*ptr++); + if ( code == 0x1a ) + { + AppendCharFastBuf(&bc->dat,code); + uint len = *(u8*)ptr; + AppendCharFastBuf(&bc->dat,len-1); + AppendFastBuf(&bc->dat,(u8*)ptr+1,len-3); + ptr += (len-1)/2; + } + else if (code) + AppendUTF8CharFastBuf(&bc->dat,code); + } + AppendCharFastBuf(&bc->dat,0); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawBMG +( + bmg_t * bmg // pointer to valid BMG +) +{ + DASSERT(bmg); + TRACE("CreateRawBMG()\n"); + + + //--- setup bmg_create_t + + bmg_create_t bc; + InitializeCreateBMG(&bc,bmg); + PRINT("fb:inf: %s\n", GetFastBufStatus(&bc.inf) ); + PRINT("fb:dat: %s\n", GetFastBufStatus(&bc.dat) ); + PRINT("fb:mid: %s\n", GetFastBufStatus(&bc.mid) ); + + + //--- check encoding + + const int encoding = CheckEncodingBMG(opt_bmg_encoding,bmg->encoding); + switch (encoding) + { + case BMG_ENC_CP1252: + create_raw_cp1252(&bc); + break; + + case BMG_ENC_UTF16BE: + create_raw_utf16(&bc); + break; + + case BMG_ENC_SHIFT_JIS: + SetupGetShiftJISCache(); + create_raw_shift_jis(&bc); + break; + + case BMG_ENC_UTF8: + create_raw_utf8(&bc); + break; + + default: + return ERROR0(ERR_ERROR, + "Can't create BMG with encoding #%u (%s): %s\n", + encoding, GetEncodingNameBMG(encoding,"?"), bmg->fname ); + } + + + //--- count messages + +#if 0 + bmg_item_t *bi, *bi_end = bmg->item + bmg->item_used; + u32 n_msg = 0; + + for ( bi = bmg->item; bi < bi_end; bi++ ) + { + if (bi->text) + n_msg++; + } +#endif + + //--- count size of raw sections + + uint total_raw_size = 0, raw_count = 0; + if (bmg->use_raw_sections) + { + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + { + const uint good_size = sizeof(bmg_section_t) + GetFastBufLen(&raw->data); + if ( raw->total_size < good_size ) + raw->total_size = ALIGN32(good_size, raw->next ? opt_bmg_align : 4 ); + total_raw_size += raw->total_size; + raw_count++; + } + } + + + //--- calculate sizes + + const mem_t inf = GetFastBufMem(&bc.inf); + const mem_t mid = GetFastBufMem(&bc.mid); + const mem_t dat = GetFastBufMem(&bc.dat); + + const u32 inf_size = ALIGN32( sizeof(bmg_inf_t) + inf.len, opt_bmg_align ); + const u32 dat_size = ALIGN32( sizeof(bmg_dat_t) + dat.len, opt_bmg_align ); + const u32 mid_size = bc.have_mid + ? ALIGN32( sizeof(bmg_mid_t) + mid.len, opt_bmg_align ) : 0; + + const u32 total_size = sizeof(bmg_header_t) + + inf_size + dat_size + mid_size + + total_raw_size; + + PRINT("SIZE(%u msg): %zx + %x[/%x] + %x + %x = %x\n", + bc.n_msg, sizeof(bmg_header_t), + inf_size, bmg->inf_size, dat_size, mid_size, total_size ); + + + //--- alloc data + + FREE(bmg->raw_data); + bmg->raw_data = CALLOC(total_size,1); + bmg->raw_data_size = total_size; + + + //---- setup bmg header + + bmg_header_t *bh = (bmg_header_t*)bmg->raw_data; + bh->size = htonl(total_size); + bh->n_sections = htonl( ( bc.have_mid ? 3 : 2 ) + raw_count ); + bh->encoding = encoding; + memcpy(bh->magic,BMG_MAGIC,sizeof(bh->magic)); + + u8 *dest = (u8*)(bh+1); + DASSERT( dest <= bmg->raw_data + total_size ); + + + //---- setup inf section + + bmg_inf_t *pinf = (bmg_inf_t*)dest; + pinf->size = htonl(inf_size); + pinf->n_msg = htons(bc.n_msg); + pinf->inf_size = htons(bmg->inf_size); + pinf->unknown_0c = htonl(bmg->unknown_inf_0c); + memcpy(pinf->magic,BMG_INF_MAGIC,sizeof(pinf->magic)); + memcpy(pinf->list,inf.ptr,inf.len); + + dest += inf_size; + DASSERT( dest <= bmg->raw_data + total_size ); + + + //---- setup dat section + + bmg_dat_t * pdat = (bmg_dat_t*)dest; + pdat->size = htonl(dat_size); + memcpy(pdat->magic,BMG_DAT_MAGIC,sizeof(pdat->magic)); + memcpy(pdat->text_pool,dat.ptr,dat.len); + + dest += dat_size; + DASSERT( dest <= bmg->raw_data + total_size ); + + + //---- setup mid section + + if (bc.have_mid) + { + bmg_mid_t *pmid = (bmg_mid_t*)dest; + pmid->size = htonl(mid_size); + pmid->n_msg = htons(bc.n_msg); + pmid->unknown_0a = htons(bmg->unknown_mid_0a); + pmid->unknown_0c = htonl(bmg->unknown_mid_0c); + memcpy(pmid->magic,BMG_MID_MAGIC,sizeof(pmid->magic)); + memcpy(pmid->mid,mid.ptr,mid.len); + + dest += mid_size; + DASSERT( dest <= bmg->raw_data + total_size ); + } + + + //---- copy raw sections + + if (total_raw_size) + { + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + { + PRINT("SAVE/RAW SECTION »%s« data: %u = 0x%x, total: %u = 0x%x\n", + PrintID(raw->magic,4,0), + GetFastBufLen(&raw->data), GetFastBufLen(&raw->data), + raw->total_size, raw->total_size ); + + bmg_section_t *sect = (bmg_section_t*)dest; + memcpy(sect->magic,raw->magic,sizeof(sect->magic)); + sect->size = htonl(raw->total_size); + const mem_t mem = GetFastBufMem(&raw->data); + memcpy(sect->data,mem.ptr,mem.len); + + dest += raw->total_size; + DASSERT( dest <= bmg->raw_data + total_size ); + } + } + + + //--- clean + + DASSERT( dest == bmg->raw_data + total_size ); + + PRINT("fb:inf: %s\n", GetFastBufStatus(&bc.inf) ); + PRINT("fb:mid: %s\n", GetFastBufStatus(&bc.mid) ); + PRINT("fb:dat: %s\n", GetFastBufStatus(&bc.dat) ); + ResetCreateBMG(&bc); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + FileMode_t fmode, // create-file mode + bool set_time // true: set time stamps +) +{ + DASSERT(bmg); + DASSERT(fname); + PRINT("SaveRawBMG(%s,%d)\n",fname,set_time); + + + //--- create raw data + + enumError err = CreateRawBMG(bmg); + if (err) + return err; + DASSERT(bmg->raw_data); + DASSERT(bmg->raw_data_size); + + + //--- write to file + + File_t F; + err = CreateFile(&F,true,fname,fmode); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&bmg->fatt,0); + + if ( fwrite(bmg->raw_data,1,bmg->raw_data_size,F.f) != bmg->raw_data_size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintAttribBMG +( + char *buf, // destination buffer + uint buf_size, // size of 'buf', BMG_ATTRIB_BUF_SIZE is good + const u8 *attrib, // NULL or attrib to print + uint attrib_used, // used length of attrib + const u8 *def_attrib, // NULL or default attrib + bool force // true: always print '[]' +) +{ + DASSERT(buf); + DASSERT( buf_size >= 10 ); + DASSERT( attrib_used <= BMG_ATTRIB_SIZE); + + static u8 all0[BMG_ATTRIB_SIZE] = {0}; + + if ( !attrib + || !attrib_used +// [[BMG_ATTRIB_SIZE]] + || def_attrib && !memcmp(attrib,def_attrib,attrib_used) ) + { + if (force) + { + *buf++ = '['; + *buf++ = ']'; + } + *buf = 0; + return buf; + } + + char *buf_end = buf + buf_size - 2; + *buf++ = '['; + +// [[BMG_ATTRIB_SIZE]] + if (!memcmp(attrib,all0,attrib_used)) + *buf++ = '0'; + else + { + uint idx; + for ( idx = 0; idx < attrib_used; idx += 4 ) + { + if ( idx && buf < buf_end ) + *buf++ = '/'; + + u32 attrib_val = be32(attrib+idx); + ccp sep = ""; + while (attrib_val) + { + const uint val = (attrib_val >> 24) & 0xff; + if (val) + buf = snprintfE(buf,buf_end,"%s%x",sep,val); + else if ( *sep && buf < buf_end ) + *buf++ = *sep; + sep = ","; + attrib_val <<= 8; + } + } + while ( buf[-1] == '/' ) + buf--; + } + *buf++ = ']'; + *buf = 0; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_message + ( File_t *F, bmg_t *bmg, const bmg_item_t *bi, ccp mid_name ) +{ + DASSERT(bmg); + DASSERT(bi); + + if ( bmg->active_cond != bi->cond ) + { + bmg->active_cond = bi->cond; + fprintf(F->f,"@? %x\n",bmg->active_cond); + } + + ccp slot; + char slot_buf[10]; + if (!bmg->have_predef_slots) + slot = EmptyString; + else if ( bi->slot == BMG_NO_PREDEF_SLOT ) + slot = " "; + else + { + snprintf(slot_buf,sizeof(slot_buf)," @%04x",bi->slot); + slot = slot_buf; + } + + char attrib[100+BMG_ATTRIB_BUF_SIZE]; +// [[xbuf]] + char xbuf[BMG_MSG_BUF_SIZE]; + + const bool print_attrib + = !opt_bmg_no_attrib + && opt_bmg_single_line < 2 + && bi->attrib_used + && memcmp(bi->attrib,bmg->attrib,bmg->attrib_used); + + if ( print_attrib && !opt_bmg_inline_attrib ) + { + if ( bmg->attrib_used == 4 ) + { + // classic, compatible with all versions + fprintf(F->f,"%s\t~%s 0x%08x\r\n",mid_name,slot,be32(bi->attrib)); + } + else + { + // use new attrib vector + PrintAttribBMG( attrib, sizeof(attrib), + bi->attrib, bi->attrib_used, bmg->attrib, true ); + fprintf(F->f,"%s\t~%s %s\r\n",mid_name,slot,attrib); + } + } + + if ( print_attrib && opt_bmg_inline_attrib ) + { + char *dest = attrib + snprintfS(attrib,sizeof(attrib),"%s%s ",mid_name,slot); + dest = PrintAttribBMG( dest, attrib+sizeof(attrib)-dest, + bi->attrib, bi->attrib_used, bmg->attrib, false ); + *dest++ = ' '; + *dest = 0; + DASSERT( dest < attrib + sizeof(attrib) - 1 ); + } + else + if (*slot) + snprintf(attrib,sizeof(attrib),"%s%s ",mid_name,slot); + else + snprintf(attrib,sizeof(attrib),"%s\t",mid_name); + + if ( bi->text == bmg_null_entry ) + fprintf(F->f,"%s/\r\n",attrib); + else if ( bi->text ) + { + fputs(attrib,F->f); + const int use_colors = opt_bmg_colors != 1 + ? opt_bmg_colors + : bmg->use_color_names > 0; +// [[xbuf?]] + uint len = PrintString16BMG( xbuf, sizeof(xbuf), bi->text, bi->len, + BMG_UTF8_MAX, 0, use_colors ); +// [[xbuf?]] + ccp ptr = xbuf; + ccp end = ptr + len; + ccp sep = "="; + do + { + ccp start = ptr; + ccp temp_end = ptr + 72; + if ( temp_end > end ) + temp_end = end; + ccp good_break = 0; + if ( opt_bmg_single_line || opt_bmg_export ) + ptr = end; + + bool first_loop = true; + while ( ptr < temp_end && !good_break ) + { + for ( ; ptr <= temp_end; ptr++ ) + { + if ( ptr == end ) + { + good_break = ptr; + break; + } + + if ( *ptr == '\\' ) + { + if ( ++ptr >= temp_end ) + break; + if ( *ptr == 'n' ) + { + good_break = ++ptr; + break; + } + if ( *ptr != '\\' && ptr-start > 40 ) + { + good_break = ptr-1; + if (!first_loop) + break; + } + } + else if ( *ptr == ' ' && ptr-start > 40 ) + { + good_break = ptr; + if (!first_loop) + break; + } + } + temp_end = end; + first_loop = false; + } + if ( good_break && good_break < end - 8 ) + ptr = good_break; + + fprintf(F->f,"%s %.*s\r\n", sep, (int)(ptr-start), start ); + sep = "\t+"; + + } while ( ptr < end ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + FileMode_t fmode, // create-file mode + bool set_time, // true: set time stamps + bool force_numeric, // true: force numric MIDs + uint brief_count // >0: suppress syntax info + // >1: suppress all comments + // >2: suppress '#BMG' magic +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(bmg); + DASSERT(fname); + PRINT("SaveTextBMG(%s,%d)\n",fname,set_time); + bmg->active_cond = 0; + + enum { BRIEF_ALL, BRIEF_NO_SYNTAX, BRIEF_NO_COMMENT, BRIEF_NO_MAGIC }; + + bool already_printed[BMG__VIP_N]; + memset(already_printed,0,sizeof(already_printed)); + char mid_name[10], abuf[BMG_ATTRIB_BUF_SIZE]; + + + static ccp param_announce = + "# All parameters begin with '@'. Unknown parameters are ignored on scanning.\r\n"; + + static const char text_head[] = + { + "#BMG <<< The first 4 characters '#BMG' are the magic for a BMG text file.\r\n" + "# <<< Don't remove them!\r\n" + "#\r\n" + "# Details about BMG text files are available in the Web:\r\n" + "# * Syntax and Semantics: https://szs.wiimm.de/doc/bmg/text\r\n" + "# * The BMG file format: https://szs.wiimm.de/r/wiki/BMG\r\n" + "#\r\n" + "#\f\r\n" + "#------------------------------------------------------------------------------\r\n" + }; + + static const char text_param[] = + { + "%s" + "\r\n" + "# Define encoding of BMG: 1=CP1252, 2=UTF-16/be, 3=Shift-JIS, 4=UTF-8\r\n" + "@ENCODING = %u\r\n" + "\r\n" + "# Create »MID1« section: 0:off, 1=on\r\n" + "@BMG-MID = %u\r\n" + "\r\n" + "# Size of each element of section 'INF1' (MKW=8).\r\n" + "# This setting has impact to attributes and MKW features.\r\n" + "@INF-SIZE = 0x%02x\r\n" + "\r\n" + "# Default attribute values for this BMG (MKW=[1])\r\n" + "@DEFAULT-ATTRIBS = %s\r\n" + "\r\n" + "# Use MKW specific color names: 0=off, 1=auto, 2=on\r\n" + "@COLOR-NAMES = %u\r\n" + "\r\n" + "# Use MKW messages and track names: 0=off, 1=auto, 2=on\r\n" + "@MKW-MESSAGES = %u\r\n" + "\r\n" + "#\f\r\n" + "#------------------------------------------------------------------------------\r\n" + }; + + static const char unknown_param[] = + { + "%s" + "\r\n" + "# This part define values for unknown parameters of section headers.\r\n" + "@UNKNOWN-INF32-0C = %#10x # 32 bit value of section INF1 offset 0x0c\r\n" + "@UNKNOWN-MID16-0A = %#10x # 16 bit value of section MID1 offset 0x0a\r\n" + "@UNKNOWN-MID32-0C = %#10x # 32 bit value of section MID1 offset 0x0c\r\n" + "\r\n" + "#\f\r\n" + "#------------------------------------------------------------------------------\r\n" + }; + + + //--- open file + + File_t F; + enumError err = CreateFile(&F,true,fname,fmode); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&bmg->fatt,0); + + const bool print_full = brief_count == BRIEF_ALL && !opt_bmg_export; + if (print_full) + fwrite(text_head,1,sizeof(text_head)-1,F.f); + else if ( brief_count < BRIEF_NO_MAGIC ) + fprintf(F.f,"%s\r\n\r\n",BMG_TEXT_MAGIC); + + HavePredifinedSlots(bmg); + const int encoding = CheckEncodingBMG(opt_bmg_encoding,bmg->encoding); + + const bool print_settings // print only, if unusual + = encoding != BMG_ENC__DEFAULT + || bmg->attrib_used != 4 + || be32(bmg->attrib) != BMG_INF_STD_ATTRIB + || !bmg->have_mid + || !bmg->use_color_names + || !bmg->use_mkw_messages + ; + + if (print_settings) + { + PrintAttribBMG(abuf,sizeof(abuf),bmg->attrib,bmg->attrib_used,0,true); + if (print_full) + { + fprintf(F.f,text_param, + param_announce, + encoding, bmg->have_mid, bmg->inf_size, abuf, + bmg->use_color_names, bmg->use_mkw_messages ); + param_announce = ""; + } + else if ( brief_count < BRIEF_NO_MAGIC ) + fprintf(F.f,"@ENCODING = %u\r\n" + "@BMG-MID = %u\r\n" + "@INF-SIZE = 0x%02x\r\n" + "@DEFAULT-ATTRIBS = %s\r\n" + "@COLOR-NAMES = %u\r\n" + "@MKW-MESSAGES = %u\r\n" + "\r\n", + encoding, bmg->have_mid, bmg->inf_size, abuf, + bmg->use_color_names, bmg->use_mkw_messages ); + } + + if ( print_settings + || bmg->unknown_inf_0c != BMG_INF_DEFAULT_0C + || bmg->unknown_mid_0a != BMG_MID_DEFAULT_0A + || bmg->unknown_mid_0c != BMG_MID_DEFAULT_0C + ) + { + if (print_full) + fprintf(F.f,unknown_param, + param_announce, + bmg->unknown_inf_0c, + bmg->unknown_mid_0a, bmg->unknown_mid_0c ); + else if ( brief_count < BRIEF_NO_MAGIC ) + fprintf(F.f,"@UNKNOWN-INF32-0C = %#x\r\n" + "@UNKNOWN-MID16-0A = %#x\r\n" + "@UNKNOWN-MID32-0C = %#x\r\n" + "\r\n", + bmg->unknown_inf_0c, + bmg->unknown_mid_0a, bmg->unknown_mid_0c ); + } + + + //--- messages + + ccp intro = "\r\n"; + static const char intro_other[] = "\r\n#--- other messages\r\n\r\n"; + + const bool support_mkw + = opt_bmg_support_mkw + && !force_numeric + && !opt_bmg_export + && bmg->attrib_used == 4 + && be32(bmg->attrib) == BMG_INF_STD_ATTRIB + && bmg->have_mid + && !bmg->have_predef_slots; + + if (support_mkw) + { + //--- find track names + + int idx; + uint last_cup = 0; + for ( idx = 0; idx < BMG_N_TRACK; idx++ ) + { + const int tidx = GetTrackIndexBMG(idx,-1); + if ( tidx < 0 ) + continue; + + const bmg_item_t * bi1 = FindItemBMG(bmg,MID_TRACK1_BEG+tidx); + if (bi1) + { + const bmg_item_t * bi2 = FindItemBMG(bmg,MID_TRACK2_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + { + const uint cup = idx / 4 + '1'; + if ( brief_count < BRIEF_NO_COMMENT && last_cup != cup ) + { + if (!last_cup) + { + intro = intro_other; + fprintf(F.f,"\r\n#--- standard track names\r\n\r\n"); + } + else + fputs("\r\n",F.f); + last_cup = cup; + } + snprintf(mid_name,sizeof(mid_name), " T%c%c", cup, idx % 4 + '1' ); + print_message(&F,bmg,bi1,mid_name); + + already_printed[ bi1->mid - MID__VIP_BEG ] = true; + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + + if (opt_bmg_support_lecode) + { + bi2 = FindItemBMG(bmg,MID_LE_TRACK_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + else if (opt_bmg_support_ctcode) + { + bi2 = FindItemBMG(bmg,MID_CT_TRACK_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + } + } + } + + + //--- find battle names + + last_cup = 0; + for ( idx = 0; idx < BMG_N_ARENA; idx++ ) + { + const int tidx = GetArenaIndexBMG(idx,-1); + if ( tidx < 0 ) + continue; + + const bmg_item_t * bi1 = FindItemBMG(bmg,MID_ARENA1_BEG+tidx); + if (bi1) + { + const bmg_item_t * bi2 = FindItemBMG(bmg,MID_ARENA2_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + { + const uint cup = idx / 5 + '1'; + if ( brief_count < BRIEF_NO_COMMENT && last_cup != cup ) + { + if (!last_cup) + { + intro = intro_other; + fprintf(F.f,"\r\n#--- battle track names\r\n\r\n"); + } + else + fputs("\r\n",F.f); + last_cup = cup; + } + snprintf(mid_name,sizeof(mid_name), " U%c%c", cup, idx % 5 + '1' ); + print_message(&F,bmg,bi1,mid_name); + + already_printed[ bi1->mid - MID__VIP_BEG ] = true; + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + + if (opt_bmg_support_lecode) + { + bi2 = FindItemBMG(bmg,MID_LE_ARENA_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + else if (opt_bmg_support_ctcode) + { + bi2 = FindItemBMG(bmg,MID_CT_ARENA_BEG+tidx); + if ( bi2 && IsItemEqualBMG(bi1,bi2) ) + already_printed[ bi2->mid - MID__VIP_BEG ] = true; + } + } + } + } + + + //--- find chat strings + + const bmg_item_t * bi1 = FindAnyItemBMG(bmg,MID_CHAT_BEG); + const bmg_item_t * bi1end = FindAnyItemBMG(bmg,MID_CHAT_END); + if ( bi1 < bi1end ) + { + if ( brief_count < BRIEF_NO_COMMENT ) + { + intro = intro_other; + fprintf(F.f,"\r\n#--- online chat\r\n\r\n"); + } + + for ( ; bi1 < bi1end; bi1++ ) + { + DASSERT( bi1->mid >= MID_CHAT_BEG && bi1->mid <= MID_CHAT_END ); + snprintf(mid_name,sizeof(mid_name)," M%02u",bi1->mid-MID_CHAT_BEG+1); + print_message(&F,bmg,bi1,mid_name); + already_printed[bi1->mid-MID__VIP_BEG] = true; + } + } + } + + + //--- print messages not already printed + + struct sep_t { u32 mid; ccp info; }; + + static const struct sep_t sep_std_tab[] = + { + {0,0} + }; + + static const struct sep_t sep_mkw_tab[] = + { + { MID_ENGINE_BEG, "engines" }, + { MID_ENGINE_END, 0 }, + { MID_CHAT_BEG, "online chat" }, + { MID_CHAT_END, 0 }, + + { MID_RCUP_BEG, "racing cup names" }, + { MID_RCUP_END, 0 }, + { MID_TRACK1_BEG, "track names, 1. copy" }, + { MID_TRACK1_END, 0 }, + { MID_BCUP_BEG, "battle cup names" }, + { MID_BCUP_END, 0 }, + { MID_TRACK2_BEG, "track names, 2. copy" }, + { MID_TRACK2_END, 0 }, + + { MID_ARENA1_BEG, "arena names, 1. copy" }, + { MID_ARENA1_END, 0 }, + { MID_ARENA2_BEG, "arena names, 2. copy" }, + { MID_ARENA2_END, 0 }, + + { MID_PARAM_IDENTIFY, "identification for third party tools" }, + { MID_PARAM_IDENTIFY, 0 }, + + { MID_PARAM_BEG, "parameters for mkw-ana" }, + { MID_PARAM_END, 0 }, + + { MID_CT_TRACK_BEG, "CT-CODE: track names" }, + { MID_CT_TRACK_END, 0 }, + { MID_CT_CUP_BEG, "CT-CODE: cup names" }, + { MID_CT_CUP_END, 0 }, + { MID_CT_CUP_REF_BEG, "CT-CODE: track-to-cup reference" }, + { MID_CT_CUP_REF_END, 0 }, + + { MID_X_MESSAGE_BEG, "new messages for CT/LE-CODE" }, + { MID_X_MESSAGE_END, 0 }, + + { MID_LE_CUP_BEG, "LE-CODE: cup names" }, + { MID_LE_CUP_END, 0 }, + { MID_LE_TRACK_BEG, "LE-CODE: track names" }, + { MID_LE_TRACK_END, 0 }, + { MID_LE_CUP_REF_BEG, "LE-CODE: track-to-cup reference" }, + { MID_LE_CUP_REF_END, 0 }, + + {0,0} + }; + + #ifdef TEST + { + fflush(stdout); + const struct sep_t *sep; + for ( sep = sep_std_tab+1; sep->mid; sep++ ) + if ( sep[-1].mid > sep->mid ) + fprintf(stderr, + "Wrong order in sep_std_tab[%zu]:\n\t%04x %s\n\t%04x %s\n", + sep-sep_std_tab, sep[-1].mid, sep[-1].info, sep->mid, sep->info ); + for ( sep = sep_mkw_tab+1; sep->mid; sep++ ) + if ( sep[-1].mid > sep->mid ) + fprintf(stderr, + "Wrong order in sep_mkw_tab[%zu]:\n\t%04x %s\n\t%04x %s\n", + sep-sep_mkw_tab, sep[-1].mid, sep[-1].info, sep->mid, sep->info ); + fflush(stderr); + } + #endif + + const struct sep_t *sep_tab = support_mkw ? sep_mkw_tab : sep_std_tab; + const struct sep_t *sep = brief_count < BRIEF_NO_COMMENT ? sep_tab : 0; + + bmg_item_t * bi = bmg->item; + bmg_item_t * bi_end = bi + bmg->item_used; + for ( ; bi < bi_end; bi++ ) + { + const u32 mid = bi->mid; + if ( mid < MID__VIP_BEG + || mid >= MID__VIP_END + || !already_printed[mid-MID__VIP_BEG] + ) + { + if ( sep && sep->mid && mid >= sep->mid ) + { + sep++; + while ( sep->mid && mid >= sep->mid ) + sep++; + if ( sep > sep_tab+1 && sep[-2].mid == mid && sep[-2].info ) + fprintf(F.f,"\r\n#--- [%x] %s\r\n", mid, sep[-2].info ); + else if ( sep[-1].info ) + fprintf(F.f,"\r\n#--- [%x:%x] %s\r\n", + sep[-1].mid, sep->mid-1, sep[-1].info ); + else + fputs("\r\n",F.f); + intro = 0; + } + + if ( brief_count < BRIEF_NO_COMMENT && intro ) + { + fputs(intro,F.f); + intro = 0; + } + snprintf(mid_name,sizeof(mid_name),"%6x",bi->mid); + print_message(&F,bmg,bi,mid_name); + } + } + + + //--- raw sections + + if ( bmg->use_raw_sections && bmg->first_raw ) + { + XDump_t xd; + InitializeXDump(&xd); + SetupXDump(&xd,XDUMPC_DUMP); + xd.f = F.f; + xd.indent = 1; + xd.prefix = "@X"; + xd.eol = "\r\n"; + xd.format = XDUMPF_INT_1; + xd.print_format = false; + xd.print_summary= false; + + bmg_raw_section_t *raw; + for ( raw = bmg->first_raw; raw; raw = raw->next ) + { + PRINT("HEXDUMP SECTION »%s« data: %u = 0x%x, total: %u = 0x%x\n", + PrintID(raw->magic,4,0), + GetFastBufLen(&raw->data), GetFastBufLen(&raw->data), + raw->total_size, raw->total_size ); + + char name[20]; + fprintf(F.f,"\r\n#\f\r\n#--------------------------------------" + "----------------------------------------\r\n\r\n" + "@SECTION \"%s\"\r\n\r\n", + PrintEscapedString( name, sizeof(name), + raw->magic, sizeof(raw->magic), + CHMD_ESC, '"', 0) ); + + const mem_t data = GetFastBufMem(&raw->data); + xd.assumed_size = data.len; + XDump(&xd,data.ptr,data.len,true); + } + } + + + //--- terminate + + if ( brief_count < 2 ) + fputs("\r\n",F.f); + ResetFile(&F,set_time); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanBMGMID() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanBMGMID +( + // returns 'scan->status' + + bmg_scan_mid_t *scan, // result, never NULL + bmg_t *bmg, // NULL or bmg to update parameters + ccp src, // source pointer + ccp src_end // NULL or end of source +) +{ + DASSERT(scan); + + //---- setup + + memset(scan,0,sizeof(*scan)); + + ccp ptr = src; + if (!src) + return 0; + if (!src_end) + src_end = src + strlen(src); + + + //--- skip blanks and controls + + while ( ptr < src_end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr == src_end ) + goto abort; + + + //--- find keyword + + ccp key = ptr; + while ( ptr < src_end && isalnum((int)*ptr) ) + ptr++; + const uint keylen = ptr - key; + + if ( keylen == 3 ) + { + if ( ( *key == 'T' || *key == 't' ) + && key[1] >= '1' && key[1] <= '8' + && key[2] >= '1' && key[2] <= '4' ) + { + int idx = 4 * ( key[1] - '1' ) + ( key[2] - '1' ); + DASSERT(GetTrackIndexBMG); + idx = GetTrackIndexBMG(idx,-1); + if ( idx >= 0 ) + { + scan->mid[0] = MID_TRACK1_BEG + idx; + scan->mid[1] = MID_TRACK2_BEG + idx; + scan->n_mid = 2; + if (opt_bmg_support_lecode) + { + scan->mid[2] = MID_LE_TRACK_BEG + idx; + scan->n_mid++; + } + else if (opt_bmg_support_ctcode) + { + scan->mid[2] = MID_CT_TRACK_BEG + idx; + scan->n_mid++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( ( *key == 'U' || *key == 'u' ) + && key[1] >= '1' && key[1] <= '2' + && key[2] >= '1' && key[2] <= '5' ) + { + int idx = 5 * ( key[1] - '1' ) + ( key[2] - '1' ); + DASSERT(GetArenaIndexBMG); + idx = GetArenaIndexBMG(idx,-1); + if ( idx >= 0 ) + { + scan->mid[0] = MID_ARENA1_BEG + idx; + scan->mid[1] = MID_ARENA2_BEG + idx; + scan->n_mid = 2; + if (opt_bmg_support_lecode) + { + scan->mid[2] = MID_LE_ARENA_BEG + idx; + scan->n_mid++; + } + else if (opt_bmg_support_ctcode) + { + scan->mid[2] = MID_CT_ARENA_BEG + idx; + scan->n_mid++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( *key == 'M' || *key == 'm' ) + { + char *end; + u32 num = strtoul(key+1,&end,10); + if ( num > 0 && num <= BMG_N_CHAT && end == ptr ) + { + scan->mid[0] = MID_CHAT_BEG + num - 1; + scan->n_mid = 1; + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + } + + if ( !scan->n_mid && keylen ) + { + char *end; + uint num = strtoul(key,&end,16); + if ( end == ptr ) + { + scan->mid[0] = num; + scan->n_mid = 1; + } + else + scan->status = -1; + } + + if ( scan->n_mid > 0 ) + while ( ptr < src_end && ( *ptr == ' ' || *ptr == '\t' ) ) + ptr++; + + abort: + if (!scan->status) + scan->status = scan->n_mid; + scan->scan_end = scan->status > 0 ? (char*)ptr : (char*)src; + return scan->status; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanMidBMG +( + // returns: + // -1: invalid MID + // 0: empty line + // 1: single mid found + // 2: double mid found (track or arena) + // 3: triple mid found (track or arena, CTCODE) + + bmg_t *bmg, // NULL or bmg to update parameters + u32 *ret_mid1, // return value: pointer to first MID + u32 *ret_mid2, // return value: pointer to second MID + u32 *ret_mid3, // return value: pointer to third MID + ccp src, // source pointer + ccp src_end, // NULL or end of source + char **scan_end // return value: NULL or end of scanned 'source' +) +{ +// [[2do]] + #if defined(TEST) || defined(DEBUG) || 1 + + bmg_scan_mid_t scan; + ScanBMGMID(&scan,bmg,src,src_end); + + if (ret_mid1) + *ret_mid1 = scan.n_mid > 0 ? scan.mid[0] : 0; + if (ret_mid2) + *ret_mid2 = scan.n_mid > 1 ? scan.mid[1] : 0; + if (ret_mid3) + *ret_mid3 = scan.n_mid > 2 ? scan.mid[2] : 0; + if (scan_end) + *scan_end = scan.scan_end; + return scan.status; + + #else + //---- setup + + u32 mid1 = 0, mid2 = 0, mid3 = 0; + int stat = 0; + ccp ptr = src; + + if (!src) + goto abort; + if (!src_end) + src_end = src + strlen(src); + + + //--- skip blanks and controls + + while ( ptr < src_end && (uchar)*ptr <= ' ' ) + ptr++; + if ( ptr == src_end ) + goto abort; + + + //--- find keyword + + ccp key = ptr; + while ( ptr < src_end && isalnum((int)*ptr) ) + ptr++; + const uint keylen = ptr - key; + + if ( keylen == 3 ) + { + if ( ( *key == 'T' || *key == 't' ) + && key[1] >= '1' && key[1] <= '8' + && key[2] >= '1' && key[2] <= '4' ) + { + int idx = 4 * ( key[1] - '1' )+ ( key[2] - '1' ); + DASSERT(GetTrackIndexBMG); + idx = GetTrackIndexBMG(idx,-1); + if ( idx >= 0 ) + { + mid1 = MID_TRACK1_BEG + idx; + mid2 = MID_TRACK2_BEG + idx; + stat = 2; + if (opt_bmg_support_lecode) + { + mid3 = MID_LE_TRACK_BEG + idx; + stat++; + } + else if (opt_bmg_support_ctcode) + { + mid3 = MID_CT_TRACK_BEG + idx; + stat++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( ( *key == 'U' || *key == 'u' ) + && key[1] >= '1' && key[1] <= '2' + && key[2] >= '1' && key[2] <= '5' ) + { + int idx = 5 * ( key[1] - '1' ) + ( key[2] - '1' ); + DASSERT(GetArenaIndexBMG); + idx = GetArenaIndexBMG(idx,-1); + if ( idx >= 0 ) + { + mid1 = MID_ARENA1_BEG + idx; + mid2 = MID_ARENA2_BEG + idx; + stat = 2; + if (opt_bmg_support_lecode) + { + mid3 = MID_LE_ARENA_BEG + idx; + stat++; + } + else if (opt_bmg_support_ctcode) + { + mid3 = MID_CT_ARENA_BEG + idx; + stat++; + } + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + else if ( *key == 'M' || *key == 'm' ) + { + char *end; + u32 num = strtoul(key+1,&end,10); + if ( num > 0 && num <= BMG_N_CHAT && end == ptr ) + { + mid1 = MID_CHAT_BEG + num - 1; + stat = 1; + if ( bmg && bmg->use_mkw_messages == 1 ) + bmg->use_mkw_messages = 2; + } + } + } + + if ( !stat && keylen ) + { + char *end; + uint num = strtoul(key,&end,16); + if ( end == ptr ) + { + mid1 = num; + stat = 1; + } + else + stat = -1; + } + + if ( stat > 0 ) + while ( ptr < src_end && ( *ptr == ' ' || *ptr == '\t' ) ) + ptr++; + + abort: + if (ret_mid1) + *ret_mid1 = mid1; + if (ret_mid2) + *ret_mid2 = mid2; + if (ret_mid3) + *ret_mid3 = mid3; + if (scan_end) + *scan_end = stat > 0 ? (char*)ptr : (char*)src; + return stat; + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch bmg: helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp (*GetContainerNameBMG) ( const bmg_t *bmg ) = 0; + +//----------------------------------------------------------------------------- + +ccp GetNameBMG ( const bmg_t *bmg, uint *name_len ) +{ + DASSERT(bmg); + + ccp name = 0; + if (GetContainerNameBMG) + name = GetContainerNameBMG(bmg); + + if ( !name && bmg->fname ) + { + name = bmg->src_is_arch ? 0 : strstr(bmg->fname,".d/"); + if (name) + { + while ( name > bmg->fname && name[-1] != '/' ) + name--; + } + else + { + name = strrchr(bmg->fname,'/'); + name = name ? name + 1 : bmg->fname; + } + } + + if (!name) + name = ""; + else + while ( *name == '.' ) + name++; + + if (name_len) + { + ccp end = strchr(name,'.'); + *name_len = end ? end-name : strlen(name); + } + + return name; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct format_message_t +{ + bmg_t *dest; // valid destination bmg + uint mod_count; // incremented for each modification + + ccp full_name; // NULL or full name of source file + uint full_len; // length of 'full_name' + ccp good_name; // NULL or good name of source file + uint good_len; // length of 'good_name' +} +format_message_t; + +//----------------------------------------------------------------------------- + +static bool format_message +( + format_message_t *fm, // valid object + bmg_item_t *dptr, // valid message + const u16 *format, // format string + uint format_len // length of 'format' +) +{ + DASSERT(fm); + DASSERT(fm->dest); + DASSERT(dptr); + DASSERT(format); + + + //--- line support + + typedef struct line_t + { + const u16 *beg; + const u16 *end; + } + line_t; + + const uint MAX_LINE = 100; + line_t line[100]; + uint n_line = 0; + + + //--- setup dest and source + + u16 buf[10000]; + u16 *dest = buf; + u16 *dest_end = buf + sizeof(buf)/sizeof(*buf); + u16* marker = buf; + + const u16 *source = format; + const u16 *source_end = format + format_len; + + + //--- main loop + + while ( dest < dest_end && source < source_end ) + { + if ( be16(source) != '%' ) + { + *dest++ = *source++; + continue; + } + + if ( source[0] == source[1] ) + { + source++; + *dest++ = *source++; + continue; + } + + PRINT("%04x = %c\n",be16(source), be16(source) & 0xff ); + const u16 *start = source++; + + u32 p1, p2, stat; + source = ScanRange16U32(source,&stat,&p1,&p2,0,~(u32)0); + if ( stat == 1 ) + p1 = 0; + else if ( stat < 1 ) + { + p1 = 0; + p2 = M1(p2); + } + + char buf8[50]; + uint copy_len = 0; + const u8 * copy_src8 = 0; + const u16 * copy_src16 = 0; + + const u16 conv_char = be16(source++); + switch (conv_char) + { + case 's': + case 'S': + copy_len = dptr->len; + copy_src16 = dptr->text; + break; + + case 'i': + copy_len = snprintf(buf8,sizeof(buf8),"%x",dptr->mid); + copy_src8 = (u8*)buf8; + break; + + case 'I': + copy_len = snprintf(buf8,sizeof(buf8),"%04x",dptr->mid); + copy_src8 = (u8*)buf8; + break; + + case 'n': + case 'N': + if (!fm->full_name) + { + fm->full_name = GetNameBMG(fm->dest,&fm->full_len); + if ( fm->full_len > 4 && !strncasecmp(fm->full_name,"Menu",4) ) + { + fm->good_name = fm->full_name + 4; + fm->good_len = fm->full_len - 4; + } + else + { + fm->good_name = fm->full_name; + fm->good_len = fm->full_len; + } + } + + if ( conv_char == 'N' ) + { + copy_len = fm->good_len; + copy_src8 = (u8*)fm->good_name; + } + else + { + copy_len = fm->full_len; + copy_src8 = (u8*)fm->full_name; + } + break; + + case 'm': + marker = dest; + break; + + case 'l': + case 'L': + case 'M': + if (!n_line) + { + const u16 *src = dptr->text; + const u16 *end = src + dptr->len; + while ( n_line < MAX_LINE ) + { + line[n_line].beg = src; + while ( src < end ) + { + if ( ntohs(*src) == '\n' ) + break; + + src += GetWordLength16BMG(src); + } + line[n_line].end = src; + n_line++; + if ( src >= end ) + break; + src++; + } + PRINT("L: %u lines found\n",n_line); + } + + if ( stat == 1 ) + p1 = p2; + if ( p2 > n_line-1 ) + p2 = n_line-1; + PRINT("L: select %u..%u/%u\n",p1,p2,n_line); + + if ( p1 <= p2 ) + { + copy_src16 = line[p1].beg; + copy_len = line[p2].end - copy_src16; + } + + if ( conv_char == 'M' ) + { + if ( !copy_len && marker ) + dest = marker; + //marker = 0; + copy_len = 0; + } + else + { + if ( !copy_len && conv_char == 'L' && marker ) + dest = marker; + //if ( conv_char == 'L' ) + // marker = 0; + } + p1 = 0; + p2 = M1(p2); + break; + + default: + copy_len = source - start; + copy_src16 = start; + break; + } + + if (copy_len) + { + // correct p1 and p2 + if ( p1 > copy_len ) + p1 = copy_len; + if ( p2 > copy_len ) + p2 = copy_len; + + u32 count = p2 - p1; + if (copy_src16) + { + copy_src16 += p1; + while ( count-- > 0 && dest < dest_end ) + *dest++ = *copy_src16++; + } + else if (copy_src8) + { + copy_src8 += p1; + while ( count-- > 0 && dest < dest_end ) + *dest++ = htons(*copy_src8++); + } + } + } + + const uint new_len = dest - buf; + if ( new_len != dptr->len || memcmp(buf,dptr->text,new_len*sizeof(*dest)) ) + { + AssignItemText16BMG(dptr,buf,dest-buf); + fm->mod_count++; + return true; + } + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch bmg: functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchPrintBMG +( + bmg_t * bmg_dest, // pointer to destination bmg + const bmg_t * bmg_src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source - mix mix // + //--------------------------------------// + + DASSERT(bmg_dest); + DASSERT(bmg_src); + copy_param(bmg_dest,bmg_src); + + format_message_t fm; + memset(&fm,0,sizeof(fm)); + fm.dest = bmg_dest; + + bmg_item_t * dptr = bmg_dest->item; + bmg_item_t * dend = dptr + bmg_dest->item_used; + + const bmg_item_t * sptr = bmg_src->item; + const bmg_item_t * send = sptr + bmg_src->item_used; + + for ( ; dptr < dend; dptr++ ) + { + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + if ( sptr->mid == mid ) + format_message(&fm,dptr,sptr->text,sptr->len); + } + return fm.mod_count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchFormatBMG +( + bmg_t * bmg_dest, // pointer to destination bmg + const bmg_t * bmg_src, // pointer to source bmg + ccp format // format parameter +) +{ + DASSERT(bmg_dest); + DASSERT(bmg_src); + copy_param(bmg_dest,bmg_src); + + const uint form_size = 2000; + u16 form_buf[form_size]; + uint form_len = ScanString16BMG(form_buf,form_size,format,-1,bmg_dest); + + format_message_t fm; + memset(&fm,0,sizeof(fm)); + fm.dest = bmg_dest; + + bmg_item_t * dptr = bmg_dest->item; + bmg_item_t * dend = dptr + bmg_dest->item_used; + for ( ; dptr < dend; dptr++ ) + format_message(&fm,dptr,form_buf,form_len); + + return fm.mod_count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRegexBMG +( + bmg_t * bmg, // pointer to destination bmg + ccp regex // format parameter +) +{ + #if ENABLE_BMG_REGEX + DASSERT(bmg); + DASSERT(regex); + + PRINT("REGEX=%s\n",regex); + + Regex_t re; + enumError err = ScanRegex(&re,true,regex); + if ( err || !re.valid ) + { + ResetRegex(&re); + return false; + } + + char buf1[1000]; + struct { FastBuf_t b; char space[500]; } res; + InitializeFastBuf(&res,sizeof(res)); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + uint len1 = PrintString16BMG( buf1, sizeof(buf1), iptr->text, iptr->len, + BMG_UTF8_MAX, 0, true ); +// printf("-> [%d] %s\n",len1,buf1); + + const int stat = ReplaceRegex(&re,&res.b,buf1,len1); + if ( stat >= 0 ) + { +// printf("{%d] %.*s\n",stat,stat,GetFastBufString(&res.b)); + AssignItemScanTextBMG(iptr,GetFastBufString(&res.b),stat); + dirty = true; + } + } + + ResetFastBuf(&res.b); + ResetRegex(&re); + return dirty; + + #else + return false; + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmRegexBMG +( + bmg_t * bmg, // pointer to destination bmg + ccp regex // format parameter +) +{ + #if ENABLE_BMG_REGEX + DASSERT(bmg); + DASSERT(regex); + + PRINT("REGEX=%s\n",regex); + + Regex_t re; + enumError err = ScanRegex(&re,true,regex); + if ( err || !re.valid ) + { + ResetRegex(&re); + return false; + } + + char buf1[1000]; + struct { FastBuf_t b; char space[500]; } res; + InitializeFastBuf(&res,sizeof(res)); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + uint len1 = PrintString16BMG( buf1, sizeof(buf1), iptr->text, iptr->len, + BMG_UTF8_MAX, 0, true ); + //printf("-> [%d] %s\n",len1,buf1); + + int stat = ReplaceRegex(&re,&res.b,buf1,len1); + if ( stat >= 0 ) + { + if (stat) + AssignItemScanTextBMG(iptr,GetFastBufString(&res.b),stat); + else + { + FreeItemBMG(iptr); + iptr->text = 0; + ResetAttribBMG(bmg,iptr); + } + dirty = true; + } + } + + ResetFastBuf(&res.b); + ResetRegex(&re); + return dirty; + + #else + return false; + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchReplaceBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source - patch both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + if (!dptr->text) + continue; + + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + + if ( sptr->mid == mid + && !IsItemEqualBMG(dptr,sptr) + && ( !sptr->cond || FindItemBMG(dest,sptr->cond) ) ) + { + FreeItemBMG(dptr); + DASSERT( dptr->mid == sptr->mid ); + + CopyAttribBMG(dest,dptr,sptr); + dptr->len = sptr->len; + dptr->text = sptr->text; + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchInsertBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source patch source both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; sptr < send; sptr++ ) + { + if ( sptr->cond && !FindItemBMG(dest,sptr->cond) ) + continue; + + bool old_item; + bmg_item_t * dptr = InsertItemBMG(dest,sptr->mid,0,0,&old_item); + if (!old_item) + { + FreeItemBMG(dptr); + DASSERT( dptr->mid == sptr->mid ); + + CopyAttribBMG(dest,dptr,sptr); + dptr->len = sptr->len; + dptr->text = sptr->text; + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchOverwriteBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src, // pointer to source bmg + bool dup_strings // true: allocate mem for copied strings +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source patch patch both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; sptr < send; sptr++ ) + { + if ( sptr->cond && !FindItemBMG(dest,sptr->cond) ) + continue; + + bool old_item; + bmg_item_t * dptr = InsertItemBMG(dest,sptr->mid,0,0,&old_item); + if ( !old_item || !IsItemEqualBMG(dptr,sptr) ) + { + if (dup_strings) + AssignItemText16BMG(dptr,sptr->text,sptr->len); + else + { + FreeItemBMG(dptr); + dptr->len = sptr->len; + dptr->text = sptr->text; + } + DASSERT( dptr->mid == sptr->mid ); + + CopyAttribBMG(dest,dptr,sptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchDeleteBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // source - - - // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; sptr < send; sptr++ ) + { + bmg_item_t * dptr = FindItemBMG(dest,sptr->mid); + if (dptr) + { + FreeItemBMG(dptr); + DASSERT( dptr->mid == sptr->mid ); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchMaskBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // - - source both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + bmg_item_t * sptr = FindItemBMG(src,dptr->mid); + if (!sptr) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchEqualBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // - - - both // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + if ( sptr == send || sptr->mid != mid || !IsItemEqualBMG(dptr,sptr) ) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchNotEqualBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // - - source - // + //--------------------------------------// + + DASSERT(dest); + DASSERT(src); + copy_param(dest,src); + + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + const bmg_item_t * sptr = src->item; + const bmg_item_t * send = sptr + src->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + u32 mid = dptr->mid; + while ( sptr < send && sptr->mid < mid ) + sptr++; + if ( sptr == send ) + break; + if ( sptr != send && sptr->mid == mid && IsItemEqualBMG(dptr,sptr) ) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool generic_helper +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src, // pointer to source bmg + uint dest_id, // index fo message to create + uint src_id, // relevant source message + uint src_beg, // source message for begining tests + uint src_end // source message for end tests +) +{ + DASSERT(dest); + DASSERT(src); + + //--- find source messages + + const bmg_item_t *s0 = FindItemBMG(src,src_id); + if ( !s0 || !s0->text || !s0->len ) + return false; + + const bmg_item_t *s1 = FindItemBMG(src,src_beg); + if ( !s1 || !s1->text || !s1->len ) + return false; + + const bmg_item_t *s2 = FindItemBMG(src,src_end); + if ( !s2 || !s2->text || !s2->len ) + return false; + + + //--- compare beginning of text + + const u16 *b0 = s0->text; + const u16 *b1 = s1->text; + const uint maxlen = s0->len < s1->len ? s0->len : s1->len; + const u16 *b0end = b0 + maxlen; + + while ( b0 < b0end && *b0 == *b1 ) + b0++, b1++; + if ( b0 == b0end ) + return false; + + while ( b0 > s0->text && isalnum((int)be16(b0-1)) ) + b0--, b1--; + + PRINT("GENERIC: |%c%c%c| |%c%c%c|\n", + be16(b0+0) & 0xff, be16(b0+1) & 0xff, be16(b0+2) & 0xff, + be16(b1+0) & 0xff, be16(b1+1) & 0xff, be16(b1+2) & 0xff ); + + + //--- compare end of text + + const u16 *e0 = s0->text + s0->len - 1; + const u16 *e2 = s2->text + s2->len - 1; + while ( e0 > b0 && *e0 == *e2 ) + e0--, e2--; + e0++, e2++; + + const u16 *e0end = s0->text + s0->len; + while ( b0 < e0end && isalnum((int)be16(b0-1)) ) + e0++, e2++; + + if ( b0 < e0 ) + { + const uint len = e0 - b0; + bmg_item_t *dptr = InsertItemBMG(dest,dest_id,0,0,0); + DASSERT(dptr); + if ( !dptr->text + || dptr->len != len + || memcmp(dptr->text,b0,len*sizeof(*b0)) ) + { + AssignItemText16BMG(dptr,b0,len); + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool PatchGenericBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src // pointer to source bmg +) +{ + DASSERT(dest); + DASSERT(src); + bool dirty = false; + + dirty |= generic_helper(dest,src, MID_G_SMALL, 0x1f44, 0x1f46, 0x1f46 ); + dirty |= generic_helper(dest,src, MID_G_MEDIUM, 0x1f45, 0x1f46, 0x1f46 ); + dirty |= generic_helper(dest,src, MID_G_LARGE, 0x1f46, 0x1f44, 0x1f44 ); + + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchIdBMG +( + bmg_t * bmg_dest, // pointer to destination bmg + bool prefix_all // true: prefix all messages +) +{ + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + // prefix n.a. n.a. n.a. // + //--------------------------------------// + + + uint name_len; + ccp name = GetNameBMG(bmg_dest,&name_len); + noPRINT("\e[31;1m#### NAME=%s\e[0m\n",name); + if ( name_len > 4 && !strncasecmp(name,"menu",4) ) + name += 4, name_len -= 4; + + u16 buf[10000]; + u16 *bufend = buf + sizeof(buf)/sizeof(*buf); + + bool dirty = false; + bmg_item_t * dptr = bmg_dest->item; + bmg_item_t * dend = dptr + bmg_dest->item_used; + + for ( ; dptr < dend; dptr++ ) + { + if ( dptr->text && ( prefix_all || dptr->text != bmg_null_entry ) ) + { + u16 *dest = buf; + if (name_len) + *dest++ = htons(*name); + char num[20]; + snprintf(num,sizeof(num),"%x",dptr->mid); + ccp src = num; + while (*src) + *dest++ = htons(*src++); + + if ( dptr->text != bmg_null_entry ) + { + *dest++ = htons(':'); + + uint count = bufend - dest; + if ( count > dptr->len ) + count = dptr->len; + memcpy(dest,dptr->text,count*sizeof(*dest)); + dest += count; + } + AssignItemText16BMG(dptr,buf,dest-buf); + dirty = true; + } + } + + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmEscapesBMG +( + bmg_t * bmg, // pointer to destination bmg + bool unicode, // replace '\u{16bit}' by unicode chars + bool rm_escapes // remove 1A escapes +) +{ + DASSERT(bmg); + PRINT("patch_bmg_rm_escapes(unicode=%d,rm_esc=%d)\n",unicode,rm_escapes); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + // fast find first escape + u16 *sp = iptr->text, *end = sp + iptr->len; + if (!sp) + continue; + while ( sp < end && ntohs(*sp) != 0x1a ) + sp++; + if ( sp == end ) + continue; + + sp = ModifiyItem(iptr,sp); + end = iptr->text + iptr->len; + DASSERT( iptr->alloced_size > 0 ); + + u16 *dp = sp; + while ( sp < end ) + { + if ( ntohs(*sp) != 0x1a ) + { + *dp++ = *sp++; + continue; + } + + if ( unicode && ntohs(sp[1]) == 0x0801 && !ntohs(sp[2]) ) + { + const u16 code = ntohs(sp[3]); + if ( code >= ' ' ) + { + *dp++ = sp[3]; + sp += 4; + HexDump16(stdout,0,0x100,iptr->text,(dp-iptr->text)*2); + HexDump16(stdout,0,0x200,sp,(iptr->text+iptr->len-sp)*2); + continue; + } + } + + const uint esc_len = ((u8*)sp)[2] & 0xfe; + if (!rm_escapes) + { + memmove(dp,sp,esc_len); + dp += esc_len/2; + } + sp += esc_len/2; + } + iptr->len = dp - iptr->text; + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmCupsBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + DASSERT(bmg); + bool dirty = false; + uint mid; + for ( mid = MID_RCUP_BEG; mid < MID_RCUP_END; mid++ ) + { + bmg_item_t * ptr = FindItemBMG(bmg,mid); + if (ptr) + { + FreeItemBMG(ptr); + DASSERT( ptr->mid == mid ); + ptr->text = 0; + ResetAttribBMG(bmg,ptr); + dirty = true; + } + } + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint bmg_copy_helper +( + bmg_t * bmg, // pointer to bmg + bool force, // true: force replace + uint mid_src_1, // first source message id + uint mid_src_2, // NULL or second source message id + uint mid_src_3, // NULL or third source message id + uint mid_dest, // destination message id + uint n_mid // numbers of messages to procced +) +{ + DASSERT(bmg); + + uint i, count = 0; + for ( i = 0; i < n_mid; i++ ) + { + bmg_item_t * sptr = FindItemBMG(bmg,mid_src_1+i); + if ( !sptr && mid_src_2 ) + { + sptr = FindItemBMG(bmg,mid_src_2+i); + if ( !sptr && mid_src_3 ) + sptr = FindItemBMG(bmg,mid_src_3+i); + } + + if (sptr) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid_dest+i,0,0,&old_item); + if ( force || !old_item || !dptr->len ) + { + AssignItemText16BMG(dptr,sptr->text,sptr->len); + CopyAttribBMG(bmg,dptr,sptr); + count++; + } + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchCTCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, false, MID_TRACK1_BEG, MID_TRACK2_BEG, 0, + MID_CT_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, false, MID_ARENA1_BEG, MID_ARENA2_BEG, 0, + MID_CT_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, false, MID_RCUP_BEG, 0, 0, + MID_CT_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, false, MID_BCUP_BEG, 0, 0, + MID_CT_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchLECopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, false, MID_ARENA1_BEG, MID_ARENA2_BEG, MID_CT_ARENA_BEG, + MID_LE_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, false, MID_TRACK1_BEG, MID_TRACK2_BEG, MID_CT_TRACK_BEG, + MID_LE_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, false, MID_RCUP_BEG, MID_CT_CUP_BEG, 0, + MID_LE_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, false, MID_BCUP_BEG, MID_CT_BCUP_BEG, 0, + MID_LE_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchXCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + if (opt_bmg_support_lecode) + return PatchLECopyBMG(bmg); + if (opt_bmg_support_ctcode) + return PatchCTCopyBMG(bmg); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchCTForceCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, true, MID_TRACK1_BEG, MID_TRACK2_BEG, 0, + MID_CT_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, true, MID_ARENA1_BEG, MID_ARENA2_BEG, 0, + MID_CT_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, true, MID_RCUP_BEG, 0, 0, + MID_CT_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, true, MID_BCUP_BEG, 0, 0, + MID_CT_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchLEForceCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + uint dirty_count + = bmg_copy_helper( bmg, true, MID_TRACK1_BEG, MID_TRACK2_BEG, MID_CT_TRACK_BEG, + MID_LE_TRACK_BEG, BMG_N_TRACK ) + + bmg_copy_helper( bmg, true, MID_ARENA1_BEG, MID_ARENA2_BEG, MID_CT_ARENA_BEG, + MID_LE_ARENA_BEG, BMG_N_ARENA ) + + bmg_copy_helper( bmg, true, MID_RCUP_BEG, MID_CT_CUP_BEG, 0, + MID_LE_CUP_BEG, + MID_RCUP_END - MID_RCUP_BEG ) + + bmg_copy_helper( bmg, true, MID_BCUP_BEG, MID_CT_BCUP_BEG, 0, + MID_LE_BCUP_BEG, + MID_BCUP_END - MID_BCUP_BEG ); + return dirty_count > 0; +} + +//----------------------------------------------------------------------------- + +bool PatchXForceCopyBMG +( + bmg_t * bmg // pointer to destination bmg +) +{ + if (opt_bmg_support_lecode) + return PatchLEForceCopyBMG(bmg); + if (opt_bmg_support_ctcode) + return PatchCTForceCopyBMG(bmg); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchIdentificationBMG ( bmg_t *bmg, ct_bmg_t *ctb ) +{ + DASSERT(bmg); + DASSERT(ctb); + + bmg_item_t *bi = InsertItemBMG(bmg,MID_PARAM_IDENTIFY,0,0,0); + DASSERT(bi); + AssignItemTextBMG(bi,GetCtBMGIdentification(ctb,true),-1); + return true; +} + +//----------------------------------------------------------------------------- + +bool PatchFillBMG + ( bmg_t *bmg, ct_mode_t ct_mode, uint rcup_limit, uint track_limit ) +{ + ct_bmg_t ctb; + ct_mode = SetupCtBMG(&ctb,ct_mode,ct_mode); + if (!ctb.is_ct_code) + return false; + + + //--- check existence of at least one relevvant message + + bool found = false; + bmg_item_t *dptr = bmg->item; + bmg_item_t *dend = dptr + bmg->item_used; + for ( ; dptr < dend; dptr++ ) + { + const u32 m = dptr->mid; + if ( m >= MID_TRACK1_BEG && m < MID_TRACK1_END + || m >= MID_TRACK2_BEG && m < MID_TRACK2_END + || m >= MID_ARENA1_BEG && m < MID_ARENA1_END + || m >= MID_ARENA2_BEG && m < MID_ARENA2_END + || m >= MID_RCUP_BEG && m < MID_RCUP_END + || m >= MID_BCUP_BEG && m < MID_BCUP_END + || m >= ctb.track_name1.beg && m < ctb.track_name1.end + || m >= ctb.track_name2.beg && m < ctb.track_name2.end + || m >= ctb.arena_name1.beg && m < ctb.arena_name1.end + || m >= ctb.arena_name2.beg && m < ctb.arena_name2.end + || m >= ctb.rcup_name.beg && m < ctb.rcup_name.end + || m >= ctb.bcup_name.beg && m < ctb.bcup_name.end + || m == ctb.random.beg + ) + { + if ( dptr->text && dptr->text != bmg_null_entry ) + { + found = true; + break; + } + } + } + + if (!found) + return false; + + + //--- random + + char buf[100]; + bool dirty = false; + + if (ctb.random.beg) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,ctb.random.beg,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + AssignItemTextBMG(dptr,"???",3); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill arena names + + uint mid; + for ( mid = ctb.arena_name1.beg; mid < ctb.arena_name1.end; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_A%u_",mid-ctb.arena_name1.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill track names + + if (!track_limit) + track_limit = opt_bmg_track_fill_limit; + uint max_track; + if (track_limit) + { + max_track = ctb.track_name1.beg + track_limit; + if ( max_track > ctb.track_name1.end ) + max_track = ctb.track_name1.end; + } + else + max_track = ctb.track_name1.end; + + for ( mid = ctb.track_name1.beg; mid < max_track; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_T%03X_",mid-ctb.track_name1.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill battle cup names + + for ( mid = ctb.bcup_name.beg; mid < ctb.bcup_name.end; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_B%u_",mid-ctb.bcup_name.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + + //--- fill racing cup names + + if (!rcup_limit) + rcup_limit = opt_bmg_rcup_fill_limit; + uint max_cup; + if (rcup_limit) + { + max_cup = ctb.rcup_name.beg + rcup_limit; + if ( max_cup > ctb.rcup_name.end ) + max_cup = ctb.rcup_name.end; + } + else + max_cup = ctb.rcup_name.end; + + for ( mid = ctb.rcup_name.beg; mid < max_cup; mid++ ) + { + bool old_item; + bmg_item_t * dptr = InsertItemBMG(bmg,mid,0,0,&old_item); + if ( !old_item || !dptr->len ) + { + const uint len + = snprintf(buf,sizeof(buf),"_R%u_",mid-ctb.rcup_name.beg); + AssignItemTextBMG(dptr,buf,len); + ResetAttribBMG(bmg,dptr); + dirty = true; + } + } + + //--- identification + + if (dirty) + PatchIdentificationBMG(bmg,&ctb); + + return dirty; +} + +//----------------------------------------------------------------------------- + +bool PatchXFillBMG ( bmg_t *bmg, uint rcup_limit, uint track_limit ) +{ + if (opt_bmg_support_lecode) + return PatchLEFillBMG(bmg,rcup_limit,track_limit); + + if (opt_bmg_support_ctcode) + return PatchCTFillBMG(bmg,rcup_limit,track_limit); + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRmFilledBMG ( bmg_t *bmg ) +{ + DASSERT(bmg); + + bool dirty = false; + bmg_item_t * iptr = bmg->item; + bmg_item_t * iend = iptr + bmg->item_used; + + for ( ; iptr < iend; iptr++ ) + { + if ( iptr->mid >= 0x4000 + && iptr->len > 3 + && ntohs(iptr->text[0]) == '_' + && ntohs(iptr->text[iptr->len-1]) == '_' ) + { + u16 ch = ntohs(iptr->text[1]); + if ( ch == 'A' || ch == 'T' || ch == 'B' || ch == 'R' ) + { + FreeItemBMG(iptr); + iptr->text = 0; + ResetAttribBMG(bmg,iptr); + dirty = true; + } + } + } + + return dirty; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchRemove*BMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRemoveBMG ( bmg_t * bmg, int mid1, int mid2 ) +{ + DASSERT(bmg); + + bmg_item_t * ptr = bmg->item; + bmg_item_t * end = ptr + bmg->item_used; + + bool dirty = false; + for ( ; ptr < end && ptr->mid < mid2; ptr++ ) + { + if ( ptr->mid >= mid1 ) + { + FreeItemBMG(ptr); + ptr->text = 0; + ResetAttribBMG(bmg,ptr); + dirty = true; + } + } + return dirty; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError PatchBMG +( + bmg_t *dest, // pointer to destination bmg + const bmg_t *src, // pointer to source bmg + PatchModeBMG_t patch_mode, // patch mode + ccp string, // NULL or string parameter + bool dup_strings // true: alloc mem for copied strings (OVERWRITE) +) +{ + PRINT("PatchBMG(%p,%p,%u,%s,%d)\n", + dest, src, patch_mode, string, dup_strings ); + DASSERT(dest); + + bool dirty = false; + switch(patch_mode) + { + case BMG_PM_PRINT: dirty = PatchPrintBMG(dest,src); break; + case BMG_PM_FORMAT: dirty = PatchFormatBMG(dest,src,string); break; + case BMG_PM_REGEX: dirty = PatchRegexBMG(dest,string); break; + case BMG_PM_RM_REGEX: dirty = PatchRmRegexBMG(dest,string); break; + case BMG_PM_ID: dirty = PatchIdBMG(dest,false); break; + case BMG_PM_ID_ALL: dirty = PatchIdBMG(dest,true); break; + case BMG_PM_UNICODE: dirty = PatchRmEscapesBMG(dest,true,false); break; + case BMG_PM_RM_ESCAPES: dirty = PatchRmEscapesBMG(dest,false,true); break; + + case BMG_PM_REPLACE: dirty = PatchReplaceBMG(dest,src); break; + case BMG_PM_INSERT: dirty = PatchInsertBMG(dest,src); break; + case BMG_PM_OVERWRITE: dirty = PatchOverwriteBMG(dest,src,dup_strings); break; + case BMG_PM_DELETE: dirty = PatchDeleteBMG(dest,src); break; + case BMG_PM_MASK: dirty = PatchMaskBMG(dest,src); break; + case BMG_PM_EQUAL: dirty = PatchEqualBMG(dest,src); break; + case BMG_PM_NOT_EQUAL: dirty = PatchNotEqualBMG(dest,src); break; + + case BMG_PM_GENERIC: dirty = PatchGenericBMG(dest,dest); break; + case BMG_PM_RM_CUPS: dirty = PatchRmCupsBMG(dest); break; + + case BMG_PM_CT_COPY: dirty = PatchCTCopyBMG(dest); break; + case BMG_PM_CT_FORCE_COPY: dirty = PatchCTForceCopyBMG(dest); break; + case BMG_PM_CT_FILL: dirty = PatchCTFillBMG(dest,0,0); break; + + case BMG_PM_LE_COPY: dirty = PatchLECopyBMG(dest); break; + case BMG_PM_LE_FORCE_COPY: dirty = PatchLEForceCopyBMG(dest); break; + case BMG_PM_LE_FILL: dirty = PatchLEFillBMG(dest,0,0); break; + + case BMG_PM_X_COPY: dirty = PatchXCopyBMG(dest); break; + case BMG_PM_X_FORCE_COPY: dirty = PatchXForceCopyBMG(dest); break; + case BMG_PM_X_FILL: dirty = PatchXFillBMG(dest,0,0); break; + + case BMG_PM_RM_FILLED: dirty = PatchRmFilledBMG(dest); break; + + default: return ERROR0(ERR_NOT_IMPLEMENTED,0); + } + + if (dirty) + TouchFileAttrib(&dest->fatt); + + return dirty ? ERR_DIFFER : ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CT/LE support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ct_mode_t NormalizeCtModeBMG ( ct_mode_t mode, ct_mode_t fallback ) +{ + for(;;) + { + switch(mode) + { + case CTM_NINTENDO: + case CTM_CTCODE: + case CTM_LECODE2: + return mode; + + case CTM_CTCODE_BASE: + return CTM_CTCODE; + + case CTM_LECODE1: + case CTM_LECODE_BASE: + return CTM_LECODE_DEF; + + case CTM_OPTIONS: + if (opt_bmg_support_lecode) + return CTM_LECODE2; + if (opt_bmg_support_ctcode) + return CTM_CTCODE; + return CTM_NINTENDO; + + case CTM_NO_INIT: + case CTM_AUTO: + break; + } + + mode = fallback; + fallback = CTM_OPTIONS; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ct_mode_t SetupCtModeBMG ( ct_mode_t mode ) +{ + mode = NormalizeCtModeBMG(mode,CTM_NINTENDO); + switch (mode) + { + case CTM_NINTENDO: + opt_bmg_support_mkw = true; + opt_bmg_support_ctcode = false; + opt_bmg_support_lecode = false; + break; + + case CTM_CTCODE: + opt_bmg_support_mkw = true; + opt_bmg_support_ctcode = true; + opt_bmg_support_lecode = false; + break; + + case CTM_LECODE2: + opt_bmg_support_mkw = true; + opt_bmg_support_ctcode = false; + opt_bmg_support_lecode = true; + break; + + default: + opt_bmg_support_mkw = false; + opt_bmg_support_ctcode = false; + opt_bmg_support_lecode = false; + break; + } + return mode; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetCtModeNameBMG ( ct_mode_t mode, bool full ) +{ + switch(mode) + { + case CTM_NINTENDO: return "Nintendo"; + case CTM_CTCODE: return "CT-CODE"; + case CTM_LECODE1: return full ? "LE-CODE v1" : "LE-CODE"; + case CTM_LECODE2: return full ? "LE-CODE v2" : "LE-CODE"; + default: return "?"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ct_mode_t SetupCtBMG ( ct_bmg_t *ctb, ct_mode_t mode, ct_mode_t fallback ) +{ + mode = NormalizeCtModeBMG(mode,fallback); + if (ctb) + { + ctb->ct_mode = mode; + ctb->identify = MID_PARAM_IDENTIFY; + ctb->param.beg = MID_PARAM_BEG; + ctb->param.end = MID_PARAM_END; + ctb->param.n = ctb->param.end - ctb->param.beg; + + switch(mode) + { + case CTM_CTCODE: + ctb->version = 0; + ctb->is_ct_code = 1; + ctb->le_phase = 0; + + ctb->rcup_name.beg = MID_CT_RCUP_BEG; + ctb->rcup_name.end = MID_CT_RCUP_END; + ctb->bcup_name.beg = MID_CT_BCUP_BEG; + ctb->bcup_name.end = MID_CT_BCUP_END; + + ctb->track_name1.beg = MID_CT_TRACK_BEG; + ctb->track_name1.end = MID_CT_TRACK_END; + ctb->track_name2.beg = 0; + ctb->track_name2.end = 0; + + ctb->arena_name1.beg = MID_CT_ARENA_BEG; + ctb->arena_name1.end = MID_CT_ARENA_END; + ctb->arena_name2.beg = 0; + ctb->arena_name2.end = 0; + + ctb->cup_ref.beg = MID_CT_CUP_REF_BEG; + ctb->cup_ref.end = MID_CT_CUP_REF_END; + + ctb->random.beg = MID_CT_RANDOM; + ctb->random.end = MID_CT_RANDOM+1; + break; + + case CTM_LECODE1: + case CTM_LECODE2: + ctb->version = mode == CTM_LECODE2 ? 2 : 1; + ctb->le_phase = ctb->version; + ctb->is_ct_code = 2; + + ctb->rcup_name.beg = MID_LE_RCUP_BEG; + ctb->rcup_name.end = MID_LE_RCUP_END; + ctb->bcup_name.beg = MID_LE_BCUP_BEG; + ctb->bcup_name.end = MID_LE_BCUP_END; + + ctb->track_name1.beg = MID_LE_TRACK_BEG; + ctb->track_name1.end = MID_LE_TRACK_END; + ctb->track_name2.beg = 0; + ctb->track_name2.end = 0; + + ctb->arena_name1.beg = MID_LE_ARENA_BEG; + ctb->arena_name1.end = MID_LE_ARENA_END; + ctb->arena_name2.beg = 0; + ctb->arena_name2.end = 0; + + ctb->cup_ref.beg = MID_LE_CUP_REF_BEG; + ctb->cup_ref.end = MID_LE_CUP_REF_END; + + ctb->random.beg = 0; + ctb->random.end = 0; + break; + + default: + ctb->version = 0; + ctb->is_ct_code = 0; + ctb->le_phase = 0; + + ctb->rcup_name.beg = MID_RCUP_BEG; + ctb->rcup_name.end = MID_RCUP_END; + ctb->bcup_name.beg = MID_BCUP_BEG; + ctb->bcup_name.end = MID_BCUP_END; + + ctb->track_name1.beg = MID_TRACK1_BEG; + ctb->track_name1.end = MID_TRACK1_END; + ctb->track_name2.beg = MID_TRACK2_BEG; + ctb->track_name2.end = MID_TRACK2_END; + + ctb->arena_name1.beg = MID_ARENA1_BEG; + ctb->arena_name1.end = MID_ARENA1_END; + ctb->arena_name2.beg = MID_ARENA2_BEG; + ctb->arena_name2.end = MID_ARENA2_END; + + ctb->cup_ref.beg = 0; + ctb->cup_ref.end = 0; + + ctb->random.beg = MID_RANDOM; + ctb->random.end = MID_RANDOM+1; + break; + } + + u32 min = M1(u32), max = 0; + bmg_range_t *ptr; + for ( ptr = &ctb->rcup_name; ptr <= &ctb->random; ptr++ ) + { + ptr->n = ptr->end - ptr->beg; + if (ptr->n) + { + if ( min > ptr->beg ) min = ptr->beg; + if ( max < ptr->end ) max = ptr->end; + } + } + ctb->range.beg = min; + ctb->range.end = max; + ctb->range.n = max - min; + } + return mode; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef TAB +#define TAB(nam) ctb->nam.beg, ctb->nam.n + +ccp GetCtBMGIdentification ( ct_bmg_t *ctb, bool full ) +{ + DASSERT(ctb); + + ccp name = GetCtModeNameBMG(ctb->ct_mode,false); + char buf[200]; + int len; + if (full) + len = snprintf(buf,sizeof(buf), + "%x, %s, v=%x, ct=%x, le=%x, par=%x+%x," + " rcup=%x+%x, bcup=%x+%x, t1=%x+%x, t2=%x+%x," + " a1=%x+%x, a2=%x+%x, ref=%x+%x", + ctb->ct_mode, name, ctb->version, + ctb->is_ct_code==1, ctb->le_phase, + TAB(param), + TAB(rcup_name), TAB(bcup_name), + TAB(track_name1), TAB(track_name2), + TAB(arena_name1), TAB(arena_name2), + TAB(cup_ref) ); + else + len = snprintf(buf,sizeof(buf),"%x, %s v%d",ctb->ct_mode,name,ctb->version); + return len > 0 ? CopyCircBuf(buf,len+1) : EmptyString; +} + +#undef TAB + +/////////////////////////////////////////////////////////////////////////////// + +static void DumpCtMidHelper1 ( FILE *f, int indent, u32 id, ccp info ) +{ + DASSERT(f); + if (id) + fprintf(f,"%*s%-20s %04x\n", indent, "", info, id ); +} + +//----------------------------------------------------------------------------- + +static void DumpCtMidHelper2 ( FILE *f, int indent, bmg_range_t *id, ccp info ) +{ + DASSERT(f); + DASSERT(id); + if (id->beg) + fprintf(f,"%*s%-20s %04x .. %04x, %5u = 0x%04x\n", + indent, "", info, id->beg, id->end, id->n, id->n ); +} + +//----------------------------------------------------------------------------- + +void DumpCtBMG ( FILE *f, int indent, ct_bmg_t *ctb ) +{ + DASSERT(f); + DASSERT(ctb); + indent = NormalizeIndent(indent); + + #if defined(TEST) || defined(DEBUG) + fprintf(f,"%*s%s\n",indent,"",GetCtBMGIdentification(ctb,true)); + #else + fprintf(f,"%*s%s\n",indent,"",GetCtBMGIdentification(ctb,false)); + #endif + + indent += 2; + DumpCtMidHelper1( f,indent, ctb->identify, "Identification:" ); + DumpCtMidHelper2( f,indent, &ctb->param, "Parameters:" ); + DumpCtMidHelper2( f,indent, &ctb->range, "Data range:" ); + + DumpCtMidHelper2( f,indent, &ctb->rcup_name, " Racing cup names:" ); + DumpCtMidHelper2( f,indent, &ctb->bcup_name, " Battle cup names:" ); + DumpCtMidHelper2( f,indent, &ctb->track_name1, " Track names 1:" ); + DumpCtMidHelper2( f,indent, &ctb->track_name2, " Track names 2:" ); + DumpCtMidHelper2( f,indent, &ctb->arena_name1, " Battle names 1:" ); + DumpCtMidHelper2( f,indent, &ctb->arena_name2, " Battle names 2:" ); + DumpCtMidHelper2( f,indent, &ctb->cup_ref, " Cup reference:" ); + DumpCtMidHelper1( f,indent, ctb->random.beg, " Random text:" ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/dclib/lib-bmg.h b/project/dclib/lib-bmg.h new file mode 100755 index 0000000..9ab5967 --- /dev/null +++ b/project/dclib/lib-bmg.h @@ -0,0 +1,1125 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DC_LIB_BMG_H +#define DC_LIB_BMG_H 1 + +#define _GNU_SOURCE 1 +#include "dclib-basics.h" +#include "dclib-file.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// basic consts /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BMG_ATTRIB_SIZE 40 // attrib size in bytes +#define BMG_ATTRIB_BUF_SIZE (10+4*BMG_ATTRIB_SIZE) // good buf size for attrib string +#define BMG_INF_MAX_SIZE (4+BMG_ATTRIB_SIZE) // max supported inf size +#define BMG_INF_LIMIT 1000 // max allowed inf size + +// [[xbuf]] +#define BMG_MSG_BUF_SIZE 20050 // good buf size for messages + +/////////////////////////////////////////////////////////////////////////////// + +#define BMG_MAGIC "MESGbmg1" +#define BMG_MAGIC8_NUM 0x4d455347626d6731ull + +#define BMG_TEXT_MAGIC "#BMG" +#define BMG_TEXT_MAGIC_NUM 0x23424d47 + +#define BMG_UTF8_MAX 0xfffd +#define BMG_UTF8_MAX_DIFF 0x058f // behind: from-right-to-left letters + +#define BMG_NO_PREDEF_SLOT 0xffff // only slots 0: force some operations +extern uint opt_bmg_align; // alignment of raw-bg sections +extern bool opt_bmg_export; // true: optimize for exports +extern bool opt_bmg_no_attrib; // true: suppress attributes +extern bool opt_bmg_x_escapes; // true: use x-scapes insetad of z-escapes +extern bool opt_bmg_old_escapes; // true: use only old escapes +extern int opt_bmg_single_line; // >0: single line, >1: suppress atrributes +extern bool opt_bmg_inline_attrib; // true: print attrinbutes inline +extern bool opt_bmg_support_mkw; // true: support MKW specific extensions +extern bool opt_bmg_support_ctcode; // true: support MKW/CT-CODE specific extensions +extern bool opt_bmg_support_lecode; // true: support MKW/LE-CODE specific extensions +extern uint opt_bmg_rcup_fill_limit;// >0: limit racing cups to reduce BMG size +extern uint opt_bmg_track_fill_limit;// >0: limit tracks to reduce BMG size +extern int opt_bmg_colors; // use c-escapes: 0:off, 1:auto, 2:on +extern ColNameLevelBMG opt_bmg_color_name; +extern uint opt_bmg_max_recurse; // max recurse depth +extern bool opt_bmg_allow_print; // true: allow '$' to print a log message +extern bool opt_bmg_use_slots; // true: use predifined slots +extern bool opt_bmg_use_raw_sections;// true: use raw sections + +//----------------------------------------------------------------------------- + +extern int opt_bmg_encoding; +extern uint opt_bmg_inf_size; +extern OffOn_t opt_bmg_mid; +extern bool opt_bmg_force_attrib; +extern bool opt_bmg_def_attrib; + +extern u8 bmg_force_attrib[BMG_ATTRIB_SIZE]; +extern u8 bmg_def_attrib[BMG_ATTRIB_SIZE]; + + +// 'PatchKeysBMG->opt' +// 0: no param +// 1: file param +// 2: string param +extern const KeywordTab_t PatchKeysBMG[]; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CT/LE support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ct_mode_t]] + +typedef enum ct_mode_t +{ + CTM_NO_INIT, // don't initialize, don't change value + CTM_NINTENDO, // original Nintendo mdoe with 32 tracks + CTM_AUTO, // use fallback + CTM_OPTIONS, // use options opt_bmg_support_{le,ct}code + + CTM_CTCODE_BASE = 0x10, // base for all CT-CODEs + CTM_CTCODE, // CT-CODE (v1) + + CTM_LECODE_BASE = 0x20, // base for all LE-CODEs + CTM_LECODE1, // LE-CODE phase 1 [[not longer used]] + CTM_LECODE2, // LE-CODE phase 2 + + CTM_LECODE_MIN = CTM_LECODE2, // minimal LE-PHASE + CTM_LECODE_DEF = CTM_LECODE2, // default LE-PHASE + CTM_LECODE_MAX = CTM_LECODE2, // maximal LE-PHASE +} +ct_mode_t; + +ct_mode_t NormalizeCtModeBMG ( ct_mode_t mode, ct_mode_t fallback ); +ct_mode_t SetupCtModeBMG ( ct_mode_t mode ); +ccp GetCtModeNameBMG ( ct_mode_t mode, bool full ); + +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_range_t]] + +typedef struct bmg_range_t +{ + u32 beg; // id of first MID + u32 end; // id of last MID+1 + u32 n; // number of MIDs := end-beg +} +bmg_range_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[ct_bmg_t]] + +typedef struct ct_bmg_t +{ + ct_mode_t ct_mode; // normalized CT mode + int version; // a version number + uint is_ct_code; // 0:off, 1:CT-CODE, 2:CT+LE-CODE + uint le_phase; // >0: LE-CODE enabled in phase # + + u32 identify; // MID if ifentification + bmg_range_t param; // MID range of parameters + + bmg_range_t range; // MID range of this section (min,max) + bmg_range_t rcup_name; // MID range of racing cup names + bmg_range_t bcup_name; // MID range of battle cup names (maybe part of rcup_name) + bmg_range_t track_name1; // MID range of track names, first set + bmg_range_t track_name2; // 0 or MID range of track names, second set + bmg_range_t arena_name1; // MID range of arena names, first set + bmg_range_t arena_name2; // 0 or MID range of arena names, second set + bmg_range_t cup_ref; // MID range of battle track_to_cup references + bmg_range_t random; // MID for RANDOM +} +ct_bmg_t; + +ct_mode_t SetupCtBMG ( ct_bmg_t *ctb, ct_mode_t mode, ct_mode_t fallback ); +static inline void ResetCtBMG ( ct_bmg_t *ctb ) {} +ccp GetCtBMGIdentification ( ct_bmg_t *ctb, bool full ); // message for MID_PARAM_IDENTIFY +void DumpCtBMG ( FILE *f, int indent, ct_bmg_t *ctb ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global string functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// returns 1..N +uint GetWordLength16BMG ( const u16 *source ); + +uint GetLength16BMG +( + const u16 * sourse, // source + uint max_len // max possible length +); + +//----------------------------------------------------------------------------- + +uint PrintString16BMG +( + char *buf, // destination buffer + uint buf_size, // size of 'buf' + const u16 *src, // source + int src_len, // length of source in u16 words, -1:NULL term + u16 utf8_max, // max code printed as UTF-8 + uint quote, // 0:no quotes, 1:escape ", 2: print in quotes + int use_color // >0: use \c{...} +); + +// [[xbuf]] [[2do]] not implemented yet +uint PrintFastBuf16BMG +( + FastBuf_t *fb, // destination buffer (data appended) + const u16 *src, // source + int src_len, // length of source in u16 words, -1:NULL term + u16 utf8_max, // max code printed as UTF-8 + uint quote, // 0:no quotes, 1:escape ", 2: print in quotes + int use_color // >0: use \c{...} +); + +//----------------------------------------------------------------------------- + +uint ScanString16BMG +( + u16 *buf, // destination buffer + uint buf_size, // size of 'buf' in u16 words + ccp src, // UTF-8 source + int src_len, // length of source, -1: determine with strlen() + const bmg_t *bmg // NULL or support for \m{...} +); + +// [[xbuf]] [[2do]] not implemented yet +uint ScanFastBuf16BMG +( + FastBuf_t *fb, // destination buffer (data appended) + ccp src, // UTF-8 source + int src_len, // length of source, -1: determine with strlen() + const bmg_t *bmg // NULL or support for \m{...} +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_header_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_section_t]] + +typedef struct bmg_section_t +{ + /*00*/ char magic[4]; // a magic to identity the section + /*04*/ be32_t size; // total size of the section + /*08*/ u8 data[0]; // section data +} +__attribute__ ((packed)) bmg_section_t; + +//----------------------------------------------------------------------------- +// [[bmg_encoding_t]] + +typedef enum bmg_encoding_t +{ + BMG_ENC_CP1252 = 1, + BMG_ENC_UTF16BE = 2, // used in MKWii and AC + BMG_ENC_SHIFT_JIS = 3, + BMG_ENC_UTF8 = 4, + + BMG_ENC__MIN = BMG_ENC_CP1252, + BMG_ENC__DEFAULT = BMG_ENC_UTF16BE, + BMG_ENC__MAX = BMG_ENC_UTF8, + BMG_ENC__UNDEFINED = -1, // only for option --bmg-encoding +} +__attribute__ ((packed)) bmg_encoding_t; + +extern const KeywordTab_t TabEncodingBMG[]; +ccp GetEncodingNameBMG ( int encoding, ccp return_if_invalid ); +int CheckEncodingBMG ( int encoding, int return_if_invalid ); + +//----------------------------------------------------------------------------- +// [[bmg_header_t]] + +typedef struct bmg_header_t +{ + /*00*/ char magic[8]; // = BMG_MAGIC + /*08*/ be32_t size; // total size of file + /*0c*/ be32_t n_sections; // number of sections + /*10*/ bmg_encoding_t encoding; // text encoding + /*11*/ u8 unknown[15]; // unknown data + /*20*/ //bmg_section_t section[0]; // first section header + // -> disabled because MVC +} +__attribute__ ((packed)) bmg_header_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_inf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BMG_INF_MAGIC "INF1" +#define BMG_INF_STD_ATTRIB 0x01000000 +#define BMG_INF_STD_SIZE 8 +#define BMG_INF_DEFAULT_0C 0 + +//----------------------------------------------------------------------------- +// [[bmg_inf_item_t]] + +typedef struct bmg_inf_item_t +{ + /*00*/ u32 offset; // offset into text pool + /*04*/ u8 attrib[0]; // attribute bytes + // NKWii: MSByte used for font selection. +} +__attribute__ ((packed)) bmg_inf_item_t; + +//----------------------------------------------------------------------------- +// [[bmg_inf_t]] + +typedef struct bmg_inf_t +{ + /*00*/ char magic[4]; // = BMG_INF_MAGIC + /*04*/ be32_t size; // total size of the section + /*08*/ be16_t n_msg; // number of messages + /*0a*/ be16_t inf_size; // size of inf items + /*0c*/ be32_t unknown_0c; // = BMG_INF_DEFAULT_0C = 0 for MKW + /*10*/ bmg_inf_item_t list[0]; // message list with (offset,attribute) +} +__attribute__ ((packed)) bmg_inf_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_dat_t + bmg_str_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BMG_DAT_MAGIC "DAT1" +#define BMG_STR_MAGIC "STR1" + +//----------------------------------------------------------------------------- +// [[bmg_dat_t]] + +typedef struct bmg_dat_t +{ + /*00*/ char magic[4]; // = BMG_DAT_MAGIC + /*04*/ be32_t size; // total size of the section + /*08*/ u8 text_pool[]; +} +__attribute__ ((packed)) bmg_dat_t; + +//----------------------------------------------------------------------------- + +// [[bmg_str_t]] +typedef bmg_dat_t bmg_str_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_mid_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BMG_MID_MAGIC "MID1" +#define BMG_MID_DEFAULT_0A 0x1000 +#define BMG_MID_DEFAULT_0C 0 + +//----------------------------------------------------------------------------- +// [[bmg_mid_t]] + +typedef struct bmg_mid_t +{ + /*00*/ char magic[4]; // = BMG_MID_MAGIC + /*04*/ be32_t size; // total size of the section + /*08*/ be16_t n_msg; // number of messages + /*0a*/ be16_t unknown_0a; // = BMG_MID_DEFAULT_0A = 0x1000 for MKW + /*0c*/ be32_t unknown_0c; // = BMG_MID_DEFAULT_0C = 0 for MKW + /*10*/ u32 mid[0]; // message id table +} +__attribute__ ((packed)) bmg_mid_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_flw_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BMG_FLW_MAGIC "FLW1" + +//----------------------------------------------------------------------------- +// [[bmg_flw_t]] + +typedef struct bmg_flw_t +{ + /*00*/ char magic[4]; // = BMG_FLW_MAGIC + /*04*/ be32_t size; // total size of the section + /*08*/ u8 unknown[]; // ??? [[2do]] +} +__attribute__ ((packed)) bmg_flw_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_fli_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BMG_FLI_MAGIC "FLI1" + +//----------------------------------------------------------------------------- +// [[bmg_fli_t]] + +typedef struct bmg_fli_t +{ + /*00*/ char magic[4]; // = BMG_FLI_MAGIC + /*04*/ be32_t size; // total size of the section + /*08*/ u8 unknown[]; // ??? [[2do]] +} +__attribute__ ((packed)) bmg_fli_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// bmg_sect_info_t & bmg_sect_list_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_sect_info_t]] + +typedef struct bmg_sect_info_t +{ + uint offset; // offset from beginning of file + uint sect_size; // section size by section header + uint real_size; // real section size + uint head_size; // size of section header + uint elem_size; // =0: unknown, >0: element size + uint n_elem_head; // elem_size>0: number of elements by header + uint n_elem; // 0: number of real elements + bmg_section_t *sect; // pointer to raw source data + // if NULL: list terminator + bool known; // true: section known + bool supported; // true: section fully supported + bool is_empty; // true: complete data is NULL +// bool use_str1; // true: SINF1 uses 2x 16-bit offsets to STR1 + ccp info; // short description +} +bmg_sect_info_t; + +//----------------------------------------------------------------------------- +// [[bmg_sect_list_t]] + +typedef struct bmg_sect_list_t +{ + // all pointers point into raw source data + + uint source_size; // valid source size + uint this_size; // total size of this including info[] + + bmg_header_t *header; // NULL or pointer to BMG file header + bmg_inf_t *pinf; // NULL or pointer to INF1 header + bmg_dat_t *pdat; // NULL or pointer to DAT1 header + bmg_str_t *pstr; // NULL or pointer to STR1 header + bmg_mid_t *pmid; // NULL or pointer to MID1 header + bmg_flw_t *pflw; // NULL or pointer to FLW1 header + bmg_fli_t *pfli; // NULL or pointer to FLI1 header + + uint n_sections; // number if secton, get by bmg header + uint n_info; // number of info records excluding terimination + bmg_sect_info_t info[]; // list of sections, + // terminated by a record with sect==NULL +} +bmg_sect_list_t; + +//----------------------------------------------------------------------------- + +// result is alloced => FREE(result) +bmg_sect_list_t * ScanSectionsBMG ( cvp data, uint size ); + +const bmg_sect_info_t * SearchSectionBMG +( + const bmg_sect_list_t *sl, // valid, created by ScanSectionsBMG() + cvp search, // magic to search + bool abbrev, // if not found: allow abbreviations + bool icase // if not found: ignore case and try again +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_raw_section_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_raw_section_t]] + +typedef struct bmg_raw_section_t +{ + char magic[4]; // section magic + FastBuf_t data; // data buffer + uint total_size; // total size of section + // if 0: calculate it + struct + bmg_raw_section_t *next; // pointer to next element +} +bmg_raw_section_t; + +//----------------------------------------------------------------------------- + +bmg_raw_section_t * GetRawSectionBMG ( bmg_t *bmg, cvp magic ); +bmg_raw_section_t * CreateRawSectionBMG ( bmg_t *bmg, cvp magic ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_item_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_item_t]] + +typedef struct bmg_item_t +{ + u32 mid; // message ID + u32 cond; // >0: message 'cond' must be defined + bmg_slot_t slot; // BMG_NO_PREDEF_SLOT or predifined slot + + u16 attrib_used; // used size of 'attrib' + u8 attrib[BMG_ATTRIB_SIZE];// attribute buffer, copy from 'inf' + // always padded with NULL + + u16 *text; // pointer to text + u16 len; // length in u16 words of 'text' + u16 alloced_size; // alloced size in u16 words of 'text' + // if 0: pointer to an other area + // ==> do not free or modify +} +__attribute__ ((packed)) bmg_item_t; + +//----------------------------------------------------------------------------- + +// special value for bmg_item_t::text +extern u16 bmg_null_entry[]; // = {0} + +static inline bool isSpecialEntryBMG ( const u16 *text ) +{ + return text == bmg_null_entry; +} + +//----------------------------------------------------------------------------- + +void FreeItemBMG( bmg_item_t * bi ); + +void AssignItemTextBMG +( + bmg_item_t * bi, // valid item + ccp utf8, // UTF-8 text to store + int len // length of 'utf8'. + // if -1: detect length with strlen() +); + +void AssignItemScanTextBMG +( + bmg_item_t * bi, // valid item + ccp utf8, // UTF-8 text to scan + int len // length of 'utf8'. + // if -1: detect length with strlen() +); + +void AssignItemText16BMG +( + bmg_item_t * bi, // valid item + const u16 * text16, // text to store + int len // length of 'text16'. + // if -1: detect length by finding + // first NULL value. +); + +bool IsItemEqualBMG +( + const bmg_item_t * bi1, // NULL or valid item + const bmg_item_t * bi2 // NULL or valid item +); + +u16 * ModifiyItem +( + // return a fixed 'ptr' + bmg_item_t * bi, // valid item, prepare text modification + const u16 * ptr // NULL or a pointer within 'bi->text' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_t]] + +struct szs_file_t; + +typedef struct bmg_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + bool is_text_src; // true: source was text + + //--- container, used by SZS tools + + struct szs_file_t *szs; // NULL or pointer to containing SZS + + //--- multiple load support + + bool src_is_arch; // true: source is an archive + uint src_count; // number of scanned sources + enumError max_err; // max error + uint recurse_depth; // current recurse depth + + //--- raw data + + u8 * data; // raw data + uint data_size; // size of 'data' + bool data_alloced; // true: 'data' must be freed + + bmg_inf_t * inf; // pointer to 'INF1' data + bmg_dat_t * dat; // pointer to 'DAT1' data + bmg_mid_t * mid; // pointer to 'MID1' data + + //--- raw sections + + bmg_raw_section_t *first_raw; // NULL or pointer to chain of raw sections + bmg_raw_section_t *current_raw; // NULL or pointer to current raw sections + + //--- section header data + + u32 unknown_inf_0c; // section INF1 offset 0x0c + u16 unknown_mid_0a; // section MID1 offset 0x0a + u32 unknown_mid_0c; // section MID1 offset 0x0c + + //--- text map + + bmg_item_t * item; // item list + uint item_used; // number of used items + uint item_size; // number of alloced items + + //--- attributes and other parameters + + bmg_encoding_t encoding; // type of encoding + uint inf_size; // size of each 'inf' element + bool have_mid; // TRUE: enable MID1 creation + u8 attrib[BMG_ATTRIB_SIZE];// attribute buffer for defaults + u16 attrib_used; // used size of 'attrib', ALIGN(4) + u8 use_color_names; // 0:off, 1:auto, 2:on + u8 use_mkw_messages; // 0:off, 1:auto, 2:on + bool param_defined; // true: params above finally defined + + bool have_predef_slots; // temporary, set by HavePredifinedSlots() + bool use_slots; // init by opt_bmg_use_slots + bool use_raw_sections; // init by opt_bmg_use_raw_sections + u32 active_cond; // active condition (for text output) + + //--- raw data, created by CreateRawBMG() + + u8 * raw_data; // NULL or data + uint raw_data_size; // size of 'raw_data' + +} bmg_t; + +//----------------------------------------------------------------------------- + +extern bmg_t *bmg_macros; + +//----------------------------------------------------------------------------- + +bmg_item_t * FindItemBMG ( const bmg_t *bmg, u32 mid ); +bmg_item_t * FindAnyItemBMG ( const bmg_t *bmg, u32 mid ); +bmg_item_t * InsertItemBMG ( bmg_t *bmg, u32 mid, + u8 *attrib, uint attrib_used, bool *old_item ); +bool DeleteItemBMG ( bmg_t *bmg, u32 mid ); + +void ResetAttribBMG +( + const bmg_t * bmg, // pointer to bmg + bmg_item_t * item // dest item +); + +void CopyAttribBMG +( + const bmg_t * dest, // pointer to destination bmg + bmg_item_t * dptr, // dest item + const bmg_item_t * sptr // source item +); + +bool HavePredifinedSlots ( bmg_t *bmg ); + +//----------------------------------------------------------------------------- + +void InitializeBMG ( bmg_t * bmg ); +void ResetBMG ( bmg_t * bmg ); +void AssignEncodingBMG ( bmg_t * bmg, int encoding ); +void AssignInfSizeBMG ( bmg_t * bmg, uint inf_size ); + +//----------------------------------------------------------------------------- + +enumError ScanBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp fname, // NULL or filename for error messages + const u8 * data, // data / if NULL: use internal data + size_t data_data // size of 'data' if data not NULL +); + +enumError ScanRawBMG ( bmg_t * bmg ); +enumError ScanTextBMG ( bmg_t * bmg ); + +enumError LoadBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp parent_fname, // NULL or filename of parent for dir-extract + ccp fname, // filename of source + FileMode_t file_mode // open modes +); + +//----------------------------------------------------------------------------- + +char * PrintAttribBMG +( + char *buf, // destination buffer + uint buf_size, // size of 'buf', BMG_ATTRIB_BUF_SIZE is good + const u8 *attrib, // NULL or attrib to print + uint attrib_used, // used length of attrib + const u8 *def_attrib, // NULL or default attrib + bool force // true: always print '[]' +); + +//----------------------------------------------------------------------------- + +enumError CreateRawBMG +( + bmg_t * bmg // pointer to valid BMG +); + +enumError SaveRawBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + FileMode_t fmode, // create-file mode + bool set_time // true: set time stamps +); + +enumError SaveTextBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + FileMode_t fmode, // create-file mode + bool set_time, // true: set time stamps + bool force_numeric, // true: force numric MIDs + uint brief_count // >0: suppress syntax info + // >1: suppress all comments + // >2: suppress '#BMG' magic +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_create_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_create_t]] + +typedef struct bmg_create_t +{ + bmg_t *bmg; // current bmg + FastBuf_t inf; // INF1 data + FastBuf_t dat; // DAT1 data + FastBuf_t mid; // MID1 data + + bool have_mid; // true: have MID1 section + uint n_msg; // number of inserted messages + + //--- iterator -> GetFirstBI() + GetNextBI() + + int slot; // slot number + bmg_item_t *bi; // next bi + bmg_item_t *bi_end; // last bi + + bmg_item_t **bi_list; // list for predifined slots + uint bi_used; // number of used elements in 'bi_list' + uint bi_size; // number of alloced elements in 'bi_list' +} +bmg_create_t; + +//----------------------------------------------------------------------------- + +void InitializeCreateBMG( bmg_create_t *bc, bmg_t *bmg ); +void ResetCreateBMG ( bmg_create_t *bc ); + +bmg_item_t * GetFirstBI ( bmg_create_t *bc ); +bmg_item_t * GetNextBI ( bmg_create_t *bc ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_scan_mid_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[bmg_scan_mid_t]] + +typedef struct bmg_scan_mid_t +{ + int status; // -1 for invalid, or 'n_mid' + int n_mid; // number of valid members in mid (0..3) + u32 mid[3]; // list of message IDs + // mid[0]: standard MID + // mid[1]: twin (track or arena) + // mid[2]: MID for LE_CODE or CT_CODE + char *scan_end; // NULL or end of scanned 'source' +} +bmg_scan_mid_t; + +//----------------------------------------------------------------------------- + +int ScanBMGMID +( + // returns 'scan->status' + + bmg_scan_mid_t *scan, // result, never NULL + bmg_t *bmg, // NULL or bmg to update parameters + ccp src, // source pointer + ccp src_end // NULL or end of source +); + +//----------------------------------------------------------------------------- +// [[obsolete]] wrapper for ScanBMGMID(); + +int ScanMidBMG +( + // returns: + // -1: invalid MID + // 0: empty line + // 1: single mid found + // 2: double mid found (track or arena) + // 3: triple mid found (track or arena, CTCODE) + + bmg_t *bmg, // NULL or bmg to update parameters + u32 *ret_mid1, // return value: pointer to first MID + u32 *ret_mid2, // return value: pointer to second MID + u32 *ret_mid3, // return value: pointer to third MID + ccp src, // source pointer + ccp src_end, // NULL or end of source + char **scan_end // return value: NULL or end of scanned 'source' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MKW hooks /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern int (*GetTrackIndexBMG) ( uint idx, int result_if_not_found ); +extern int (*GetArenaIndexBMG) ( uint idx, int result_if_not_found ); +extern ccp (*GetContainerNameBMG) ( const bmg_t * bmg ); + +extern int (*AtHookBMG) ( bmg_t * bmg, ccp line, ccp line_end ); +// On return status: +// -1: continue with scanning line +// -2: continue with next line +// >=0: close file and return status as error code + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch BMG /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[PatchModeBMG_t]] + +typedef enum PatchModeBMG_t +{ + // this mode descibes the operation: + // source := source x patch + + //--------------------------------------// + // only only source source // + // in in != == // + // source patch patch patch // + //--------------------------------------// + BMG_PM_PRINT, // source - print print // + BMG_PM_FORMAT, // ________use_parameter________ // + BMG_PM_REGEX, // ________use_parameter________ // + BMG_PM_RM_REGEX, // ________use_parameter________ // + BMG_PM_ID, // prefix n.a. n.a. n.a. // + BMG_PM_ID_ALL, // prefix n.a. n.a. n.a. // + BMG_PM_UNICODE, // ___no_patch_file_available___ // + BMG_PM_RM_ESCAPES, // ___no_patch_file_available___ // + //--------------------------------------// + BMG_PM_REPLACE, // source - patch both // + BMG_PM_INSERT, // source patch source both // + BMG_PM_OVERWRITE, // source patch patch both // + BMG_PM_DELETE, // source - - - // + BMG_PM_MASK, // - - source both // + BMG_PM_EQUAL, // - - - both // + BMG_PM_NOT_EQUAL, // - - source - // + //--------------------------------------// + BMG_PM_GENERIC, // ___no_patch_file_available___ // + BMG_PM_RM_CUPS, // ___no_patch_file_available___ // + BMG_PM_CT_COPY, // ___no_patch_file_available___ // + BMG_PM_CT_FORCE_COPY, // ___no_patch_file_available___ // + BMG_PM_CT_FILL, // ___no_patch_file_available___ // + BMG_PM_LE_COPY, // ___no_patch_file_available___ // + BMG_PM_LE_FORCE_COPY, // ___no_patch_file_available___ // + BMG_PM_LE_FILL, // ___no_patch_file_available___ // + BMG_PM_X_COPY, // ___no_patch_file_available___ // + BMG_PM_X_FORCE_COPY, // ___no_patch_file_available___ // + BMG_PM_X_FILL, // ___no_patch_file_available___ // + BMG_PM_RM_FILLED, // ___no_patch_file_available___ // + //--------------------------------------// + + //--- helpers + BMG_PM__MASK_CMD = 0xff, // relevant bits of the command + BMG_PM__SHIFT_OPT = 8 // shift the parameter option by 8 bits + +} PatchModeBMG_t; + +/////////////////////////////////////////////////////////////////////////////// + +enumError PatchBMG +( + bmg_t *dest, // pointer to destination bmg + const bmg_t *src, // pointer to source bmg + PatchModeBMG_t patch_mode, // patch mode + ccp format, // NULL or format parameter + bool dup_strings // true: alloc mem for copied strings (OVERWRITE) +); + +//----------------------------------------------------------------------------- + +bool PatchFormatBMG +( + bmg_t *dest, // pointer to destination bmg + const bmg_t *src, // pointer to source bmg + ccp format // format parameter +); + +bool PatchOverwriteBMG +( + bmg_t * dest, // pointer to destination bmg + const bmg_t * src, // pointer to source bmg + bool dup_strings // true: allocate mem for copied strings +); + +bool PatchPrintBMG ( bmg_t * dest, const bmg_t * src ); +bool PatchReplaceBMG ( bmg_t * dest, const bmg_t * src ); +bool PatchInsertBMG ( bmg_t * dest, const bmg_t * src ); +bool PatchDeleteBMG ( bmg_t * dest, const bmg_t * src ); +bool PatchMaskBMG ( bmg_t * dest, const bmg_t * src ); +bool PatchEqualBMG ( bmg_t * dest, const bmg_t * src ); +bool PatchNotEqualBMG ( bmg_t * dest, const bmg_t * src ); +bool PatchGenericBMG ( bmg_t * dest, const bmg_t * src ); + +bool PatchRmCupsBMG ( bmg_t * bmg ); + +bool PatchCTCopyBMG ( bmg_t * bmg ); +bool PatchLECopyBMG ( bmg_t * bmg ); +bool PatchXCopyBMG ( bmg_t * bmg ); + +bool PatchCTForceCopyBMG( bmg_t * bmg ); +bool PatchLEForceCopyBMG( bmg_t * bmg ); +bool PatchXForceCopyBMG ( bmg_t * bmg ); + +bool PatchFillBMG + ( bmg_t *bmg, ct_mode_t ct_mode, uint rcup_limit, uint track_limit ); +bool PatchXFillBMG ( bmg_t *bmg, uint rcup_limit, uint track_limit ); +static inline bool PatchCTFillBMG ( bmg_t *bmg, uint rcup_limit, uint track_limit ) + { return PatchFillBMG(bmg,CTM_CTCODE,rcup_limit,track_limit); } +static inline bool PatchLEFillBMG ( bmg_t *bmg, uint rcup_limit, uint track_limit ) + { return PatchFillBMG(bmg,CTM_LECODE2,rcup_limit,track_limit); } + +bool PatchIdBMG +( + bmg_t * bmg, // pointer to destination bmg + bool prefix_all // true: prefix all messages +); + +bool PatchRmEscapesBMG +( + bmg_t * bmg, // pointer to destination bmg + bool unicode, // replace '\u{16bit}' by unicode chars + bool rm_escapes // remove 1A escapes +); + +// remove between mid1 and mid2-1 +bool PatchRemoveBMG ( bmg_t * bmg, int mid1, int mid2 ); +//bool PatchRemoveTracksBMG ( bmg_t * bmg, int track_limit ); +bool PatchIdentificationBMG ( bmg_t *bmg, ct_bmg_t *ctb ); + +//----------------------------------------------------------------------------- + +ccp GetNameBMG ( const bmg_t *bmg, uint *name_len ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DC_LIB_BMG_H 1 diff --git a/project/dclib/lib-dol.c b/project/dclib/lib-dol.c new file mode 100644 index 0000000..00d6972 --- /dev/null +++ b/project/dclib/lib-dol.c @@ -0,0 +1,1208 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 +#include "lib-dol.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dol header /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_dol_header ( dol_header_t * dest, const dol_header_t * src ) +{ + DASSERT(dest); + + if (!src) + src = dest; + else if ( dest != src ) + memcpy(dest,src,sizeof(*dest)); + + const u32 * src_ptr = src->sect_off; + u32 * dest_ptr = dest->sect_off; + u32 * dest_end = (u32*)&dest->padding; + + while ( dest_ptr < dest_end ) + *dest_ptr++ = ntohl(*src_ptr++); +} + +/////////////////////////////////////////////////////////////////////////////// + +void hton_dol_header ( dol_header_t * dest, const dol_header_t * src ) +{ + DASSERT(dest); + + if (!src) + src = dest; + else if ( dest != src ) + memcpy(dest,src,sizeof(*dest)); + + const u32 * src_ptr = src->sect_off; + u32 * dest_ptr = dest->sect_off; + u32 * dest_end = (u32*)&dest->padding; + + while ( dest_ptr < dest_end ) + *dest_ptr++ = htonl(*src_ptr++); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsDolHeader ( const void *data, uint data_size, uint file_size ) +{ + if ( data_size < DOL_HEADER_SIZE ) + return false; + + const dol_header_t *dol = data; + u64 total_size = 0; + + uint i; + for ( i = 0; i < DOL_N_SECTIONS; i++ ) + { + const u32 off = ntohl(dol->sect_off[i]); + const u32 size = ntohl(dol->sect_size[i]); + const u32 addr = ntohl(dol->sect_addr[i]); + noPRINT("DOL: %8x %8x %08x\n",off,size,addr); + if ( off || size || addr ) + { + if ( off & 3 + || size & 3 + || addr & 3 + || off < DOL_HEADER_SIZE + || file_size && off + size > file_size ) + { + return false; + } + total_size += size; + } + } + + return total_size && total_size < 0x7fffffff; +} + +/////////////////////////////////////////////////////////////////////////////// + +const char dol_section_name[DOL_NN_SECTIONS+1][4] = +{ + "T0", + "T1", + "T2", + "T3", + "T4", + "T5", + "T6", + + "D0", + "D1", + "D2", + "D3", + "D4", + "D5", + "D6", + "D7", + "D8", + "D9", + "D10", + + "BSS", + "ENT", + + "" +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// advanced interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool GetDolSectionInfo +( + dol_sect_info_t * info, // valid pointer: returned data + const dol_header_t *dol_head, // valid DOL header + uint file_size, // (file) size of 'data' + uint section // 0 .. DOL_NN_SECTIONS + // (7*TEXT, 11*DATA, BSS, ENTRY ) +) +{ + DASSERT(dol_head); + DASSERT(info); + memset(info,0,sizeof(*info)); + + if ( !dol_head || file_size < sizeof(dol_header_t) || section >= DOL_NN_SECTIONS ) + { + info->section = -1; + return false; + } + + info->section = section; + StringCopyS(info->name,sizeof(info->name),GetDolSectionName(section)); + + if ( section < DOL_N_SECTIONS ) + { + const u32 size = be32(&dol_head->sect_size[section]); + if (size) + { + info->sect_valid = true; + info->size = size; + const u32 off = info->off = be32(&dol_head->sect_off [section]); + info->addr = be32(&dol_head->sect_addr[section]); + + if ( off < file_size && size < file_size && off+size <= file_size ) + { + info->data_valid = true; + info->data = (u8*)dol_head + off; + } + } + return true; + } + + if ( section == DOL_IDX_BSS ) + { + info->sect_valid = true; + info->addr = be32(&dol_head->bss_addr); + info->size = be32(&dol_head->bss_size); + return true; + } + + if ( section == DOL_IDX_ENTRY ) + { + info->sect_valid = true; + info->addr = be32(&dol_head->entry_addr); + return true; + } + + ASSERT(0); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FindFirstFreeDolSection +( + // returns TRUE on found + + dol_sect_info_t *info, // result + const dol_header_t *dh, // DOL header to analyse + uint mode // 0: search all sections (TEXT first) + // 1: search only TEXT sections + // 2: search only DATA sections + // 3: search all sections (DATA first) +) +{ + DASSERT(info); + DASSERT(dh); + + memset(info,0,sizeof(*info)); + + uint idx, end; + switch (mode) + { + case 0: + idx = 0; + end = DOL_N_SECTIONS; + break; + + case 3: + if (FindFirstFreeDolSection(info,dh,2)) + return true; + // fall through + + case 1: + idx = 0; + end = DOL_N_TEXT_SECTIONS; + break; + + case 2: + idx = DOL_N_TEXT_SECTIONS; + end = DOL_N_SECTIONS; + break; + + default: + idx = 0; + end = 0; + break; + } + + while ( idx < end && ntohl(dh->sect_size[idx]) ) + idx++; + + if ( idx >= end ) + { + info->section = -1; + return false; + } + + info->section = idx; + StringCopyS(info->name,sizeof(info->name),GetDolSectionName(idx)); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FindDolSectionBySelector +( + // returns TRUE on found + + dol_sect_info_t *info, // result + const dol_header_t *dh, // DOL header to analyse + const dol_sect_select_t *dss // DOL section selector +) +{ + DASSERT(info); + DASSERT(dh); + DASSERT(dss); + + if ( dss->sect_idx >= 0 ) + { + memset(info,0,sizeof(*info)); + info->section = dss->sect_idx; + memcpy(info->name,dss->name,sizeof(info->name)); + return true; + } + + return FindFirstFreeDolSection(info,dh,dss->find_mode); +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 FindFreeSpaceDOL +( + // return address or NULL on not-found + + const dol_header_t *dol_head, // valid DOL header + u32 addr_beg, // first possible address + u32 addr_end, // last possible address + u32 size, // minimal size + u32 align, // aligning + u32 *space // not NULL: store available space here +) +{ + DASSERT(dol_head); + + MemMap_t mm; + InitializeMemMap(&mm); + + int i; + for ( i = 0; i < DOL_N_SECTIONS; i++ ) + { + const u32 size = ntohl(dol_head->sect_size[i]); + if (size) + { + const u32 addr = ntohl(dol_head->sect_addr[i]); + if ( addr < addr_end && addr + size > addr_beg ) + { + MemMapItem_t * mi = InsertMemMap(&mm,addr,size); + if (mi) + { + if ( i < DOL_N_TEXT_SECTIONS ) + snprintf(mi->info,sizeof(mi->info),"T%u",i); + else + snprintf(mi->info,sizeof(mi->info),"D%u", + i - DOL_N_TEXT_SECTIONS); + } + } + } + } + //PrintMemMap(&mm,stdout,3,"section"); + + u64 res_space; + const u32 res = FindFreeSpaceMemMap(&mm,addr_beg,addr_end,size,align,&res_space); + ResetMemMap(&mm); + if (space) + *space = res_space; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 FindFreeBssSpaceDOL +( + // return address or NULL on not-found + + const dol_header_t *dol_head, // valid DOL header + u32 size, // min size + u32 align, // aligning + u32 *space // not NULL: store available space here +) +{ + DASSERT(dol_head); + const u32 bss_addr = ntohl(dol_head->bss_addr); + const u32 bss_size = ntohl(dol_head->bss_size); + if ( !bss_addr || !bss_size ) + { + if (space) + *space = 0; + return 0; + } + + return FindFreeSpaceDOL(dol_head,bss_addr,bss_addr+bss_size,size,align,space); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void DumpDolHeader +( + FILE * f, // dump to this file + int indent, // indention + const dol_header_t *dol_head, // valid DOL header + uint file_size, // NULL or size of file + uint print_mode // bit field: + // 1: print file map + // 2: print virtual mem map + // 4: print delta table +) +{ + DASSERT(f); + DASSERT(dol_head); + + indent = NormalizeIndent(indent); + + dol_header_t dol; + ntoh_dol_header(&dol,dol_head); + + MemMap_t mm1, mm2, mm3; + InitializeMemMap(&mm1); + InitializeMemMap(&mm2); + InitializeMemMap(&mm3); + MemMapItem_t * mi = InsertMemMap(&mm1,0,sizeof(dol_header_t)); + StringCopyS(mi->info,sizeof(mi->info),"DOL header"); + if (file_size) + { + mi = InsertMemMap(&mm1,file_size,0); + StringCopyS(mi->info,sizeof(mi->info),"--- end of file ---"); + } + + int i; + for ( i = 0; i < DOL_N_SECTIONS; i++ ) + { + const u32 size = dol.sect_size[i]; + if (size) + { + const u32 off = dol.sect_off[i]; + const u32 addr = dol.sect_addr[i]; + + char buf[sizeof(mi->info)], buf3[sizeof(mi->info)]; + if ( i < DOL_N_TEXT_SECTIONS ) + { + snprintf(buf,sizeof(buf),"text section T%u",i); + snprintf(buf3,sizeof(buf3),"%8x : T%u",addr-off,i); + } + else + { + const int j = i - DOL_N_TEXT_SECTIONS; + snprintf(buf,sizeof(buf),"data section D%u",j); + snprintf(buf3,sizeof(buf3),"%8x : D%u",addr-off,j); + } + + mi = InsertMemMap(&mm1,off,size); + strcpy(mi->info,buf); + mi = InsertMemMap(&mm2,addr,size); + strcpy(mi->info,buf); + mi = InsertMemMap(&mm3,addr,size); + strcpy(mi->info,buf3); + } + } + + if ( print_mode & 1 ) + { + fprintf(f,"%*sMemory map of DOL file:\n\n",indent,""); + PrintMemMap(&mm1,f,indent+3,"section"); + putc('\n',f); + } + + if ( print_mode & 2 ) + { + mi = InsertMemMap(&mm2,dol.bss_addr,dol.bss_size); + snprintf(mi->info,sizeof(mi->info),"bss section"); + mi = InsertMemMap(&mm2,dol.entry_addr,0); + snprintf(mi->info,sizeof(mi->info),"entry point"); + + fprintf(f,"%*sMemory map of DOL image:\n\n",indent,""); + mm2.begin = 0xffffffff; + PrintMemMap(&mm2,f,indent+3,"section"); + putc('\n',f); + } + + if ( print_mode & 4 ) + { + mm3.begin = 0xffffffff; + fprintf(f,"%*sDelta between file offset and virtual address:\n\n", + indent,"" ); + PrintMemMap(&mm3,f,indent+3," delta : section"); + putc('\n',f); + } + + ResetMemMap(&mm1); + ResetMemMap(&mm2); + ResetMemMap(&mm3); +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetDolSectionAddr +( + // returns dsa->section + dol_sect_addr_t *dsa, // result: section address info + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size // >0: wanted size +) +{ + DASSERT(dsa); + DASSERT(dol_head); + memset(dsa,0,sizeof(*dsa)); + dsa->addr = addr; + dsa->size = size; + + int sect; + for ( sect = 0; sect < DOL_N_SECTIONS; sect++ ) + { + const u32 sect_addr = ntohl(dol_head->sect_addr[sect]); + const u32 sect_size = ntohl(dol_head->sect_size[sect]); + if ( addr >= sect_addr && addr+size <= sect_addr + sect_size ) + { + dsa->sect_addr = sect_addr; + dsa->sect_offset = addr - sect_addr; + dsa->sect_size = sect_size; + StringCopyS(dsa->sect_name,sizeof(dsa->sect_name),GetDolSectionName(sect)); + return dsa->section = sect; + } + } + + //--- bss? + + const u32 bss_addr = ntohl(dol_head->bss_addr); + const u32 bss_size = ntohl(dol_head->bss_size); + if ( addr >= bss_addr && addr+size <= bss_addr + bss_size ) + { + dsa->sect_addr = bss_addr; + dsa->sect_offset = addr - bss_addr; + dsa->sect_size = bss_size; + StringCopyS(dsa->sect_name,sizeof(dsa->sect_name),"BSS"); + return dsa->section = DOL_IDX_BSS; + } + + //--- not found + + return dsa->section = -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetDolSectionAddrInfo +( + // returns GetCircBuf() or EmptyString on error + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size // >0: wanted size +) +{ + DASSERT(dol_head); + dol_sect_addr_t dsa; + if ( GetDolSectionAddr(&dsa,dol_head,addr,size) < 0 ) + return EmptyString; + + return PrintCircBuf(" (%s+%#x)",dsa.sect_name,dsa.sect_offset); +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 GetDolOffsetByAddr +( + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size, // >0: wanted size + u32 *valid_size // not NULL: return valid size +) +{ + DASSERT(dol_head); + + int sect; + for ( sect = 0; sect < DOL_N_SECTIONS; sect++ ) + { + const u32 sect_addr = ntohl(dol_head->sect_addr[sect]); + const u32 sect_size = ntohl(dol_head->sect_size[sect]); + if ( addr >= sect_addr && addr < sect_addr + sect_size ) + { + u32 max_size = sect_addr + sect_size - addr; + if (!size) + { + if (valid_size) + *valid_size = max_size; + } + else if ( size <= max_size ) + { + if (valid_size) + *valid_size = size; + } + else if (valid_size) + *valid_size = max_size; + else + return 0; + + return ntohl(dol_head->sect_off[sect]) + addr - sect_addr; + } + } + + if (valid_size) + *valid_size = 0; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 GetDolAddrByOffset +( + const dol_header_t *dol_head, // valid DOL header + u32 off, // address to search + u32 size, // >0: wanted size + u32 *valid_size // not NULL: return valid size +) +{ + DASSERT(dol_head); + + int sect; + for ( sect = 0; sect < DOL_N_SECTIONS; sect++ ) + { + const u32 sect_off = ntohl(dol_head->sect_off[sect]); + const u32 sect_size = ntohl(dol_head->sect_size[sect]); + + if ( off >= sect_off && off < sect_off + sect_size ) + { + u32 max_size = sect_off + sect_size - off; + if (!size) + { + if (valid_size) + *valid_size = max_size; + } + else if ( size <= max_size ) + { + if (valid_size) + *valid_size = size; + } + else if (valid_size) + *valid_size = max_size; + else + return 0; + + return ntohl(dol_head->sect_addr[sect]) + off - sect_off; + } + } + + if (valid_size) + *valid_size = 0; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AddDolAddrByOffset +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie, // TRUE: use InsertMemMapTie() + u32 off, // address to search + u32 size // size, may overlay multiple sections +) +{ + DASSERT(dol_head); + DASSERT(mm); + + uint count = 0; + while ( size > 0 ) + { + u32 valid_size; + u32 addr = GetDolAddrByOffset(dol_head,off,size,&valid_size); + if ( !addr || !valid_size ) + break; + if (use_tie) + InsertMemMapTie(mm,addr,valid_size); + else + InsertMemMap(mm,addr,valid_size); + off += valid_size; + size -= valid_size; + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint TranslateDolOffsets +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie, // TRUE: use InsertMemMapTie() + const MemMap_t *mm_off // NULL or mem map with offsets +) +{ + DASSERT(dol_head); + DASSERT(mm); + if (!mm_off) + return 0; + + uint mi, count = 0; + for ( mi = 0; mi < mm_off->used; mi++ ) + { + const MemMapItem_t *src = mm_off->field[mi]; + AddDolAddrByOffset(dol_head,mm,use_tie,src->off,src->size); + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint TranslateAllDolOffsets +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie // TRUE: use InsertMemMapTie() +) +{ + DASSERT(dol_head); + DASSERT(mm); + + uint count = 0; + + int i; + for ( i = 0; i < DOL_N_SECTIONS; i++ ) + { + const u32 size = ntohl(dol_head->sect_size[i]); + if (size) + { + count++; + const u32 addr = ntohl(dol_head->sect_addr[i]); + MemMapItem_t *mi = use_tie + ? InsertMemMapTie(mm,addr,size) + : InsertMemMap(mm,addr,size); + if (*mi->info) + StringCopyS(mi->info,sizeof(mi->info),"..."); + else if ( i < DOL_N_TEXT_SECTIONS ) + snprintf(mi->info,sizeof(mi->info),"text section T%u",i); + else + { + const int j = i - DOL_N_TEXT_SECTIONS; + snprintf(mi->info,sizeof(mi->info),"data section D%u",j); + } + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint TranslateDolSections +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie, // TRUE: use InsertMemMapTie() + uint dol_sections // bitfield of sections +) +{ + DASSERT(dol_head); + DASSERT(mm); + + dol_sect_info_t sinfo; + uint sect, count = 0; + for ( sect = 0; sect < DOL_NN_SECTIONS; sect++ ) + { + if ( dol_sections & 1 << sect + && GetDolSectionInfo(&sinfo,dol_head,sizeof(*dol_head),sect) ) + { + count++; + uint size = sect == DOL_IDX_ENTRY ? 16 : sinfo.size; + MemMapItem_t *mi = use_tie + ? InsertMemMapTie(mm,sinfo.addr,size) + : InsertMemMap(mm,sinfo.addr,size); + + if (*mi->info) + StringCopyS(mi->info,sizeof(mi->info),"..."); + else + snprintf(mi->info,sizeof(mi->info), + "section %s",dol_section_name[sect]); + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountOverlappedDolSections +( + // returns the number of overlapped section by [addr,size] + + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size // size of addr to verify +) +{ + DASSERT(dol_head); + + u32 end = addr + size; + if ( end < addr ) // overflow + end = M1(end); + + uint sect, count = 0; + for ( sect = 0; sect < DOL_N_SECTIONS; sect++ ) + { + const u32 sect_addr = ntohl(dol_head->sect_addr[sect]); + const u32 sect_size = ntohl(dol_head->sect_size[sect]); + if ( addr < sect_addr + sect_size && end > sect_addr ) + count++; + + noPRINT("%8x..%8x / %8x..%8x => %d/%u\n", + addr, end, sect_addr, sect_addr + sect_size, + addr < sect_addr + sect_size && end > sect_addr, count ); + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __APPLE__ + void * memmem ( const void *l, size_t l_len, const void *s, size_t s_len ); +#endif // __APPLE__ + +/////////////////////////////////////////////////////////////////////////////// + +static u32 FindDolAddress +( + const u8 *data, // DOL data beginning with dol_header_t + uint off_begin, // begin search here + uint off_end, // end search here + const u8 *search, // data to search + uint serach_size // size of 'search' +) +{ + while ( off_begin < off_end ) + { + const u8 *res + = memmem( data+off_begin, off_end-off_begin, search, serach_size ); + if (!res) + return 0; + off_begin = res - data; + if (!(off_begin&3)) // check alignment + return off_begin; + off_begin++; + } + return 0; +} + +//----------------------------------------------------------------------------- + +u32 FindDolAddressOfVBI +( + // search in available data and return NULL if not found + + cvp data, // DOL data beginning with dol_header_t + uint data_size // size of 'data' +) +{ + if ( data_size < sizeof(dol_header_t) ) + return 0; + const dol_header_t *dh = (dol_header_t*)data; + + static const u8 search1[] = { 0x7C,0xE3,0x3B,0x78, 0x38,0x87,0x00,0x34, + 0x38,0xA7,0x00,0x38, 0x38,0xC7,0x00,0x4C }; + static const u8 search2[] = { 0x4E,0x80,0x00,0x20 }; + + int sect; + for ( sect = 0; sect < DOL_N_SECTIONS; sect++ ) + { + const u32 off1 = ntohl(dh->sect_off[sect]); + const u32 size = ntohl(dh->sect_size[sect]); + if ( !size || off1 >= data_size ) + continue; + + u32 off_end = off1 + size; + if ( off_end > data_size ) + off_end = data_size; + + u32 off2 = FindDolAddress( data, off1, off_end, search1, sizeof(search1) ); + if (off2) + { + off2 = FindDolAddress( data, off2+sizeof(search1), off_end, + search2, sizeof(search2) ); + if (off2) + return ntohl(dh->sect_addr[sect]) + off2 - off1; + } + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanDolSectionName +( + // returns the first uninterpreted character + + ccp arg, // comma/space separated names + int *ret_section, // not NULL: return section index + // <0: error / >=0: section index + enumError *ret_err // not NULL: return error code +) +{ + int section = -1; + enumError err = ERR_OK; + + if (!arg) + err = ERR_MISSING_PARAM; + else + { + while ( *arg && !isalnum((int)*arg) ) + arg++; + + ccp ptr = arg; + char namebuf[10], *np = namebuf; + for(;;) + { + char ch = toupper((int)*ptr); + if ( ch < 'A' || ch > 'Z' ) + break; + if ( np < namebuf + sizeof(namebuf) - 1 ) + *np++ = ch; + ptr++; + } + + int index = -1; + if (!memcmp(namebuf,"TEXT",np-namebuf)) + index = 0; + else if (!memcmp(namebuf,"DATA",np-namebuf)) + index = DOL_N_TEXT_SECTIONS; + else + { + if (!memcmp(namebuf,"BSS",np-namebuf)) + { + section = DOL_IDX_BSS; + arg = ptr; + } + else if (!memcmp(namebuf,"ENTRY",np-namebuf)) + { + section = DOL_IDX_ENTRY; + arg = ptr; + } + else + err = ERR_INVALID_DATA; + goto abort; + } + + char *end; + uint num = str2ul(ptr,&end,10); + if ( end > ptr ) + { + arg = end; + index += num; + if ( index < DOL_N_SECTIONS ) + section = index; + else + err = ERR_INVALID_DATA; + } + } + + abort:; + if (ret_section) + *ret_section = section; + if (ret_err) + *ret_err = err; + return (char*)arg; +} + +//----------------------------------------------------------------------------- + +uint ScanDolSectionList +( + // return a bitfield as section list + + ccp arg, // comma/space separated names + enumError *ret_err // not NULL: return status +) +{ + enumError err = ERR_OK; + uint sect_list = 0; + + while ( arg && *arg ) + { + int section; + enumError scan_err; + arg = ScanDolSectionName(arg,§ion,&scan_err); + if (scan_err) + { + if ( err < scan_err ) + err = scan_err; + break; + } + + if ( section >= 0 ) + sect_list |= 1 << section; + } + + if (ret_err) + *ret_err = !sect_list && err < ERR_MISSING_PARAM ? ERR_MISSING_PARAM : err; + return sect_list; +} + +//----------------------------------------------------------------------------- + +ccp DolSectionList2Text ( uint sect_list ) +{ + char buf[100], *dest = buf; + uint sect; + for ( sect = 0; sect < DOL_NN_SECTIONS; sect++ ) + if ( sect_list & 1 << sect ) + dest = snprintfE(dest,buf+sizeof(buf),",%s",dol_section_name[sect]); + + char *res = GetCircBuf(dest-buf); + memcpy(res,buf+1,dest-buf); + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// add/remove DOL sections /////////////// +/////////////////////////////////////////////////////////////////////////////// + + +uint CompressDOL +( + // return: size of compressed DOL (inline replaced) + + dol_header_t *dol, // valid dol header + FILE *log // not NULL: print memmap to this file +) +{ + DASSERT(dol); + + MemMap_t mm; + InitializeMemMap(&mm); + + uint max_off = 0, total_data_size = 0; + uint s; + for ( s = 0; s < DOL_N_SECTIONS; s++ ) + { + const u32 off = ntohl(dol->sect_off[s]); + const u32 size = ntohl(dol->sect_size[s]); + if ( off && size ) + { + MemMapItem_t *mi = InsertMemMap(&mm,off,size); + snprintf(mi->info,sizeof(mi->info),"%u",s); + total_data_size += size; + if ( max_off < off + size ) + max_off = off + size; + } + } + + if (log) + PrintMemMap(&mm,log,5,"DOL sections"); + + u8 *temp = MALLOC(total_data_size); + + uint i, new_off = 0; + for ( i = 0; i < mm.used; i++ ) + { + MemMapItem_t *mi = mm.field[i]; + uint sect = strtoul(mi->info,0,10); + DASSERT( sect < DOL_N_SECTIONS ); + const u32 off = ntohl(dol->sect_off[sect]); + const u32 size = ntohl(dol->sect_size[sect]); + + memcpy( temp + new_off, (u8*)dol + off, size ); + dol->sect_off[sect] = htonl( sizeof(dol_header_t) + new_off ); + new_off += size; + } + DASSERT( total_data_size == new_off ); + + memcpy(dol+1,temp,new_off); + FREE(temp); + return new_off + sizeof(dol_header_t); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveDolSections +( + // return: 0:if not modifed, >0:size of compressed DOL + + dol_header_t *dol, // valid dol header + uint stay, // bit field of sections (0..17): SET => don't remove + uint *res_stay, // not NULL: store bit field of vlaid sections here + FILE *log // not NULL: print memmap to this file +) +{ + uint s, m, count = 0; + for ( s = 0, m = 1; s < DOL_N_SECTIONS; s++, m <<= 1 ) + { + const u32 off = ntohl(dol->sect_off[s]); + const u32 size = ntohl(dol->sect_size[s]); + if ( !off || !size ) + stay &= ~m; + + if ( !(stay&m) && off ) + { + dol->sect_off[s] = dol->sect_size[s] = dol->sect_addr[s] = 0; + count++; + } + } + + if (res_stay) + *res_stay = stay; + + return count ? CompressDOL(dol,log) : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Gecko Code Handler /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool IsValidGCH +( + const gch_header_t *gh, // valid header + uint file_size // size of file +) +{ + DASSERT(gh); + + if ( file_size < sizeof(*gh) || ntohl(gh->magic) != GCH_MAGIC_NUM ) + return false; + + const u32 addr = ntohl(gh->addr); + const u32 size = ntohl(gh->size); + const u32 entry = ntohl(gh->vbi_entry); + + if ( addr < 0x80000000 || sizeof(*gh) + size > file_size ) + return false; + + if ( entry && ( entry < addr || entry >= addr + size - 4 )) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp DecodeWCH +( + // returns NULL on success, or a short error message + + wch_control_t *wc, // store only, (initialized) + void *data, // source data, modified if type != 0 + uint size, // size of 'data' + DecompressWCH_func decompress // NULL or decompression function +) +{ + DASSERT(wc); + DASSERT( data || !size ); + + memset(wc,0,sizeof(*wc)); + if ( !data || size < sizeof(wch_header_t) + sizeof(wch_segment_t) ) + return "To small for a WCH file"; + + const wch_header_t *wh = data; + wc->wh.magic = ntohl(wh->magic); + wc->wh.version = ntohl(wh->version); + wc->wh.size = ntohl(wh->size); + if ( wc->wh.magic != WCH_MAGIC_NUM || wc->wh.version > 1 ) + return "Invalid WCH header"; + + const wch_segment_t *ptr = wh->segment; + if ( wc->wh.version == 1 && decompress ) + { + wc->temp_size = wc->wh.size; + wc->temp_data = MALLOC(wc->temp_size); + DASSERT(wc->temp_data); + + uint data_size = size - sizeof(wch_header_t); + if ( decompress(wc,(void*)ptr,data_size) != ERR_OK ) + return "Decompression of WCH failed."; + ptr = (wch_segment_t*)wc->temp_data; + } + + const wch_segment_t *end = (wch_segment_t*)( (u8*)ptr + wc->wh.size ); + noPRINT("%p %p [%zd]\n",ptr,end,(u8*)end-(u8*)ptr); + + uint iseg; + wch_segment_t *dseg = wc->seg; + for ( iseg = 0; iseg < WCH_MAX_SEG; iseg++, dseg++ ) + { + if (!ptr->type) + break; + dseg->type = ntohl(ptr->type); + dseg->addr = ntohl(ptr->addr); + dseg->size = ntohl(ptr->size); + dseg->main_entry = ntohl(ptr->main_entry); + dseg->main_entry_old = ntohl(ptr->main_entry_old); + dseg->vbi_entry = ntohl(ptr->vbi_entry); + dseg->vbi_entry_old = ntohl(ptr->vbi_entry_old); + + u8 *src_data = (u8*)ptr->data; + wc->seg_data[iseg] = src_data; + ptr = (wch_segment_t*)(ptr->data + ALIGN32(dseg->size,4)); + if ( ptr > end ) + { + wc->is_valid = false; + return "Damaged WCH data"; + } + } + wc->n_seg = iseg; + memset(dseg,0,sizeof(*dseg)); + wc->is_valid = true; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetWCH ( wch_control_t * wc ) +{ + if ( wc && wc->temp_data ) + { + FREE(wc->temp_data); + wc->temp_data = 0; + wc->temp_size = 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// virtual dumps /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/lib-dol.h b/project/dclib/lib-dol.h new file mode 100644 index 0000000..3197bcc --- /dev/null +++ b/project/dclib/lib-dol.h @@ -0,0 +1,517 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef DC_LIB_DOL_H +#define DC_LIB_DOL_H 1 + +#define _GNU_SOURCE 1 +#include "dclib-basics.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DOL: defs & structs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define DOL_N_TEXT_SECTIONS 7 +#define DOL_N_DATA_SECTIONS 11 +#define DOL_N_SECTIONS 18 + +#define DOL_IDX_BSS DOL_N_SECTIONS // alias for BSS +#define DOL_IDX_ENTRY (DOL_N_SECTIONS+1) // alias for ENTRY +#define DOL_NN_SECTIONS (DOL_N_SECTIONS+2) // N + aliase + +#define DOL_HEADER_SIZE 0x100 + +//----------------------------------------------------------------------------- + +#define ASM_NOP 0x60000000 + +//----------------------------------------------------------------------------- + +static int IsDolTextSection ( uint section ) + { return section < DOL_N_TEXT_SECTIONS; } + +static bool IsDolDataSection ( uint section ) + { return section - DOL_N_TEXT_SECTIONS < DOL_N_DATA_SECTIONS; } + +//----------------------------------------------------------------------------- +// [[dol_header_t]] + +typedef struct dol_header_t +{ + /* 0x00 */ u32 sect_off [DOL_N_SECTIONS]; // file offset + /* 0x48 */ u32 sect_addr[DOL_N_SECTIONS]; // virtual address + /* 0x90 */ u32 sect_size[DOL_N_SECTIONS]; // section size + /* 0xd8 */ u32 bss_addr; // BSS address + /* 0xdc */ u32 bss_size; // BSS size + /* 0xe0 */ u32 entry_addr; // entry point + /* 0xe4 */ u8 padding[DOL_HEADER_SIZE-0xe4]; +} +__attribute__ ((packed)) dol_header_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[dol_sect_info_t]] + +typedef struct dol_sect_info_t +{ + int section; // section index, <0: invalid + char name[4]; // name of section + u32 off; // file offset of data + u32 addr; // image address + u32 size; // size of section + bool sect_valid; // true: section is valid + bool data_valid; // true: data is valid (size>0 and inside DOL file) + bool hash_valid; // true: 'hash' is valid + u8 *data; // NULL (data_valid=0) or pointer to data (data_valid=1) + sha1_hash_t hash; // SHA1 hash of data +} +dol_sect_info_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[dol_sect_select_t]] + +typedef struct dol_sect_select_t +{ + int find_mode; // mode for xFindFirstFreeDolSection(), <0: skip this + int sect_idx; // >=0: section, <0: auto select + char name[4]; // a name based on 'sect_idx' and 'find_mode' + u32 addr; // address of section, if 0: use DATA[0x10] + ccp fname; // file name + bool use_param; // true: write params (entry and '.dol' section) + + u8 *data; // not NULL: data, alloced + uint size; // >0: size of 'data' + u32 offset; // last used file offset (temporary) +} +dol_sect_select_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[dol_sect_addr_t]] + +typedef struct dol_sect_addr_t +{ + //--- source info + u32 addr; // address of data + u32 size; // size of data + + //--- search info + int section; // section index, <0: invalid + char sect_name[4]; // empty or name of found section + u32 sect_addr; // NULL or section address + u32 sect_offset; // NULL or offset in found section + u32 sect_size; // NULL or size of found section +} +dol_sect_addr_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DOL: Interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_dol_header ( dol_header_t * dest, const dol_header_t * src ); +void hton_dol_header ( dol_header_t * dest, const dol_header_t * src ); +bool IsDolHeader ( const void *data, uint data_size, uint file_size ); + +extern const char dol_section_name[DOL_NN_SECTIONS+1][4]; +static inline ccp GetDolSectionName ( uint section ) +{ + return section < DOL_NN_SECTIONS ? dol_section_name[section] : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GetDolSectionInfo +( + dol_sect_info_t * ret_info, // valid pointer: returned data + const dol_header_t *dol_head, // valid DOL header + uint file_size, // (file) size of 'data' + uint section // 0 .. DOL_NN_SECTIONS + // (7*TEXT, 11*DATA, BSS, ENTRY ) +); + +//----------------------------------------------------------------------------- + +bool FindFirstFreeDolSection +( + // returns TRUE on found + + dol_sect_info_t *info, // result + const dol_header_t *dh, // DOL header to analyse + uint mode // 0: search all sections (TEXT first) + // 1: search only TEXT sections + // 2: search only DATA sections + // 3: search all sections (DATA first) +); + +//----------------------------------------------------------------------------- + +bool FindDolSectionBySelector +( + // returns TRUE on found + + dol_sect_info_t *info, // result + const dol_header_t *dh, // DOL header to analyse + const dol_sect_select_t *dss // DOL section selector +); + +//----------------------------------------------------------------------------- + +u32 FindFreeSpaceDOL +( + // return address or NULL on not-found + + const dol_header_t *dol_head, // valid DOL header + u32 addr_beg, // first possible address + u32 addr_end, // last possible address + u32 size, // minimal size + u32 align, // aligning + u32 *space // not NULL: store available space here +); + +//----------------------------------------------------------------------------- + +u32 FindFreeBssSpaceDOL +( + // return address or NULL on not-found + + const dol_header_t *dol_head, // valid DOL header + u32 size, // min size + u32 align, // aligning + u32 *space // not NULL: store available space here +); + +/////////////////////////////////////////////////////////////////////////////// + +void DumpDolHeader +( + FILE * f, // dump to this file + int indent, // indention + const dol_header_t *dol_head, // valid DOL header + uint file_size, // NULL or size of file + uint print_mode // bit field: + // 1: print file map + // 2: print virtual mem map + // 4: print delta table +); + +/////////////////////////////////////////////////////////////////////////////// + +int GetDolSectionAddr +( + // returns dsa->section + dol_sect_addr_t *dsa, // result: section address info + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size // >0: wanted size +); + +//----------------------------------------------------------------------------- + +ccp GetDolSectionAddrInfo +( + // returns GetCircBuf() or EmptyString on error + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size // >0: wanted size +); + +/////////////////////////////////////////////////////////////////////////////// + +u32 GetDolOffsetByAddr +( + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size, // >0: wanted size + u32 *valid_size // not NULL: return valid size +); + +/////////////////////////////////////////////////////////////////////////////// + +u32 GetDolAddrByOffset +( + const dol_header_t *dol_head, // valid DOL header + u32 off, // address to search + u32 size, // >0: wanted size + u32 *valid_size // not NULL: return valid size +); + +/////////////////////////////////////////////////////////////////////////////// + +uint AddDolAddrByOffset +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie, // TRUE: use InsertMemMapTie() + u32 off, // address to search + u32 size // size, may overlay multiple sections +); + +/////////////////////////////////////////////////////////////////////////////// + +uint TranslateDolOffsets +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie, // TRUE: use InsertMemMapTie() + const MemMap_t *mm_off // NULL or mem map with offsets +); + +/////////////////////////////////////////////////////////////////////////////// + +uint TranslateAllDolOffsets +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie // TRUE: use InsertMemMapTie() +); + +/////////////////////////////////////////////////////////////////////////////// + +uint TranslateDolSections +( + const dol_header_t *dol_head, // valid DOL header + MemMap_t *mm, // valid destination mem map, not cleared + bool use_tie, // TRUE: use InsertMemMapTie() + uint dol_sections // bitfield of sections +); + +/////////////////////////////////////////////////////////////////////////////// + +uint CountOverlappedDolSections +( + // returns the number of overlapped section by [addr,size] + + const dol_header_t *dol_head, // valid DOL header + u32 addr, // address to search + u32 size // size of addr to verify +); + +/////////////////////////////////////////////////////////////////////////////// + +u32 FindDolAddressOfVBI +( + // search in available data and return NULL if not found + + cvp data, // DOL data beginning with dol_header_t + uint data_size // size of 'data' +); + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanDolSectionName +( + // returns the first uninterpreted character + + ccp arg, // comma/space separated names + int *ret_section, // not NULL: return section index + // <0: error / >=0: section index + enumError *ret_err // not NULL: return error code +); + +//----------------------------------------------------------------------------- + +uint ScanDolSectionList +( + // return a bitfield as section list + + ccp arg, // comma/space separated names + enumError *ret_err // not NULL: return status +); + +//----------------------------------------------------------------------------- + +ccp DolSectionList2Text ( uint sect_list ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// add/remove DOL sections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint CompressDOL +( + // return: size of compressed DOL (inline replaced) + + dol_header_t *dol, // valid dol header + FILE *log // not NULL: print memmap to this file +); + +//----------------------------------------------------------------------------- + +uint RemoveDolSections +( + // return: 0:if not modifed, >0:size of compressed DOL + + dol_header_t *dol, // valid dol header + uint stay, // bit field of sections (0..17): SET => don't remove + uint *res_stay, // not NULL: store bit field of vlaid sections here + FILE *log // not NULL: print memmap to this file +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Gecko Code Handler /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GCH_MAGIC_NUM 0x47044348 + +#define GCT_MAGIC8_NUM 0x00d0c0de00d0c0deull +#define GCT_TERM_NUM 0xf000000000000000ull +#define GCT_SEP_NUM1 0xf0000001u +#define GCT_REG_OFFSET 8 + +/////////////////////////////////////////////////////////////////////////////// +// [[gch_header_t]] + +typedef struct gch_header_t +{ + // big endian! + + u32 magic; // magic: 'G\x{04}CH' (Gecko Code Handler) + u32 addr; // address of new section + u32 size; // size of code section, file size must be >= 'size' + // a cheat section may follow + u32 vbi_entry; // >0: entry point for VBI interrupts + + u8 data[]; // usually start of code handler +} +__attribute__ ((packed)) gch_header_t; + +/////////////////////////////////////////////////////////////////////////////// + +bool IsValidGCH +( + const gch_header_t *gh, // valid header + uint file_size // size of file +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Wiimms Code Handler /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define WCH_MAGIC_NUM 0x57054348 + +/////////////////////////////////////////////////////////////////////////////// +// [[wch_segment_t]] + +typedef struct wch_segment_t +{ + // big endian! + + u32 type; // 'T': text section + // 'D': data section + // 'S': any section, try data first, then text + // 'P': patch data => patch main.dol + // 0 : end of segment list + + u32 addr; // address, where the data is stored + u32 size; // size of data + + u32 main_entry; // >0: `main entry point´ + u32 main_entry_old; // >0: store old `main entry point´ at this address + u32 vbi_entry; // >0: `vbi entry point´ + u32 vbi_entry_old; // >0: store old `vbi entry point´ at this address + + u8 data[]; // data, aligned by ALIGN(size,4) + // wch_segment_t // next segment, aligned by ALIGN(size,4) +} +__attribute__ ((packed)) wch_segment_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[wch_header_t]] + +typedef struct wch_header_t +{ + // big endian! + + u32 magic; // magic: 'W\x{05}CH' (Gecko Code Handler) + u32 version; // 0: raw, 1: bzip2 + u32 size; // total size of (uncompressed) segment data + + wch_segment_t segment[]; // first segment +} +__attribute__ ((packed)) wch_header_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[wch_control_t]] + +#define WCH_MAX_SEG 50 + +typedef struct wch_control_t +{ + u8 *temp_data; // not NULL: temporary data, alloced + uint temp_size; // not NULL: size of 'temp_data' + + bool is_valid; // true: data is valid + wch_header_t wh; // decoded header, local endian + + uint n_seg; // number of segments excluding terminator + wch_segment_t seg[WCH_MAX_SEG+1]; // decoded segment headers, local endian + // + special terminator segment (type==0) + u8 *seg_data[WCH_MAX_SEG+1]; // pointer to decoded data for each segment +} +wch_control_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enumError (*DecompressWCH_func) +( + // return TRUE on success + + wch_control_t *wc, // valid data, includes type info + void *data, // source data to decompress + uint size // size of 'data' +); + +/////////////////////////////////////////////////////////////////////////////// + +ccp DecodeWCH +( + // returns NULL on success, or a short error message + + wch_control_t *wc, // store only, (initialized) + void *data, // source data, modified if type != 0 + uint size, // size of 'data' + DecompressWCH_func decompress // NULL or decompression function +); + +void ResetWCH ( wch_control_t * wc ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // DC_LIB_DOL_H 1 diff --git a/project/dclib/lib-mkw.c b/project/dclib/lib-mkw.c new file mode 100644 index 0000000..8ada409 --- /dev/null +++ b/project/dclib/lib-mkw.c @@ -0,0 +1,1107 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * Support for Mario Kart Wii * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 +#include +#include "lib-mkw.h" +#include "dclib-numeric.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// assigned points: tables /////////////// +/////////////////////////////////////////////////////////////////////////////// +// working matrix, initialized with NINTENDO + +// [[24P--]] +u8 MkwPointsTab[12*12] = // active table +{ + 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 9, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 9, 5, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 15,10, 6, 3, 1, 0, 0, 0, 0, 0, 0, 0, + 15,10, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, + 15,11, 8, 6, 4, 2, 1, 0, 0, 0, 0, 0, + 15,11, 8, 6, 4, 3, 2, 1, 0, 0, 0, 0, + 15,12,10, 8, 6, 4, 3, 2, 1, 0, 0, 0, + 15,12,10, 8, 6, 5, 4, 3, 2, 1, 0, 0, + 15,12,10, 8, 7, 6, 5, 4, 3, 2, 1, 0 +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// NINTENDO => Orginal Nintendo + +// [[24P--]] +const u8 MkwPointsTab_NINTENDO[12*12] = +{ + 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 9, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 9, 5, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 15,10, 6, 3, 1, 0, 0, 0, 0, 0, 0, 0, + 15,10, 7, 5, 3, 1, 0, 0, 0, 0, 0, 0, + 15,11, 8, 6, 4, 2, 1, 0, 0, 0, 0, 0, + 15,11, 8, 6, 4, 3, 2, 1, 0, 0, 0, 0, + 15,12,10, 8, 6, 4, 3, 2, 1, 0, 0, 0, + 15,12,10, 8, 6, 5, 4, 3, 2, 1, 0, 0, + 15,12,10, 8, 7, 6, 5, 4, 3, 2, 1, 0 +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// LINEAR => Absolute linear schema +// - LAST := 0, INCREMENT := 1 + +// [[24P--]] +const u8 MkwPointsTab_LINEAR[12*12] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, + 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, + 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +}; + +/////////////////////////////////////////////////////////////////////////////// +// LINEAR_1 => Like LINEAR, but LAST := 1 +// - LAST := 1, INCREMENT := 1 + +// [[24P--]] +const u8 MkwPointsTab_LINEAR_1[12*12] = +{ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, + 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, + 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +}; + +/////////////////////////////////////////////////////////////////////////////// +// LINEAR_B => Like LINEAR, but with a bonus of 1 for a victory +// - LAST := 0, INCREMENT := 1, BONUS := 1 + +// [[24P--]] +const u8 MkwPointsTab_LINEAR_B[12*12] = +{ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 7, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, + 8, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, + 9, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, + 10, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, + 11, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, + 12, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +}; + +/////////////////////////////////////////////////////////////////////////////// +// LINEAR_B1 => Like LINEAR_B, but LAST := 1 +// - LAST := 1, INCREMENT := 1, BONUS := 1 + +// [[24P--]] +const u8 MkwPointsTab_LINEAR_B1[12*12] = +{ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, + 7, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, + 8, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, + 9, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, + 10, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, + 11, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, + 12, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, + 13, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Test + +#if MKW_POINTS_TEST_ENABLED + // [[24P--]] + const u8 MkwPointsTab_TEST[12*12] = + { + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 18, 12, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 20, 15, 10, 5, 0, 0, 0, 0, 0, 0, 0, + 25, 20, 16, 12, 8, 4, 0, 0, 0, 0, 0, 0, + 25, 21, 17, 13, 9, 6, 3, 0, 0, 0, 0, 0, + 25, 21, 18, 15, 12, 9, 6, 3, 0, 0, 0, 0, + 25, 22, 19, 16, 13, 10, 7, 4, 2, 0, 0, 0, + 25, 22, 19, 16, 13, 10, 8, 6, 4, 2, 0, 0, + 25, 22, 19, 16, 14, 12, 10, 8, 6, 4, 2, 0, + }; +#endif // MKW_POINTS_TEST_ENABLED + +/////////////////////////////////////////////////////////////////////////////// +// Test/one + +#if MKW_POINTS_TEST_ENABLED + // [[24P--]] + const u8 MkwPointsTab_TEST_1[12*12] = + { + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 13, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 17, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 19, 13, 7, 1, 0, 0, 0, 0, 0, 0, 0, + 25, 20, 15, 10, 5, 1, 0, 0, 0, 0, 0, 0, + 25, 21, 17, 13, 9, 5, 1, 0, 0, 0, 0, 0, + 25, 21, 17, 13, 10, 7, 4, 1, 0, 0, 0, 0, + 25, 22, 19, 16, 13, 10, 7, 4, 1, 0, 0, 0, + 25, 22, 19, 16, 13, 10, 7, 5, 3, 1, 0, 0, + 25, 22, 19, 16, 13, 11, 9, 7, 5, 3, 1, 0, + 25, 22, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, + }; +#endif // MKW_POINTS_TEST_ENABLED + +/////////////////////////////////////////////////////////////////////////////// +// Test/bonus + +#if MKW_POINTS_TEST_ENABLED + // [[24P--]] + const u8 MkwPointsTab_TEST_B[12*12] = + { + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 18, 12, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 19, 14, 9, 4, 0, 0, 0, 0, 0, 0, 0, + 25, 20, 16, 12, 8, 4, 0, 0, 0, 0, 0, 0, + 25, 20, 16, 12, 9, 6, 3, 0, 0, 0, 0, 0, + 25, 21, 18, 15, 12, 9, 6, 3, 0, 0, 0, 0, + 25, 21, 18, 15, 12, 9, 6, 4, 2, 0, 0, 0, + 25, 21, 18, 15, 12, 10, 8, 6, 4, 2, 0, 0, + 25, 21, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, + }; +#endif // MKW_POINTS_TEST_ENABLED + +/////////////////////////////////////////////////////////////////////////////// +// Test/bonus/one + +#if MKW_POINTS_TEST_ENABLED + // [[24P--]] + const u8 MkwPointsTab_TEST_B1[12*12] = + { + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 12, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 16, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 25, 18, 12, 6, 1, 0, 0, 0, 0, 0, 0, 0, + 25, 19, 14, 9, 5, 1, 0, 0, 0, 0, 0, 0, + 25, 20, 16, 12, 8, 4, 1, 0, 0, 0, 0, 0, + 25, 20, 16, 13, 10, 7, 4, 1, 0, 0, 0, 0, + 25, 21, 18, 15, 12, 9, 6, 3, 1, 0, 0, 0, + 25, 21, 18, 15, 12, 9, 7, 5, 3, 1, 0, 0, + 25, 21, 18, 15, 13, 11, 9, 7, 5, 3, 1, 0, + 25, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, + }; +#endif // MKW_POINTS_TEST_ENABLED + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Generic tables => More belanced than NINTENDO +// * FIRST := X, LAST := 0|1 +// * linear in the middle with larger gaps at the beginning + +// [[24P--]] +static u8 MkwPointsTab_WIN[12*12]; +static u8 MkwPointsTab_WIN_1[12*12]; +static u8 MkwPointsTab_WIN_B[12*12]; +static u8 MkwPointsTab_WIN_B1[12*12]; + +// [[24P--]] +static char MkwPointsInfo_WIN[20]; +static char MkwPointsInfo_WIN_1[20]; +static char MkwPointsInfo_WIN_B[20]; +static char MkwPointsInfo_WIN_B1[20]; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[24P--]] +const MkwPointInfo_t MkwPointInfo[MPI__N+1] = +{ + { MPI_UNKNOWN, 3, 0, "self defined", + { MPI_UNKNOWN, MPI_UNKNOWN, MPI_UNKNOWN, MPI_UNKNOWN }}, + + { MPI_NINTENDO, 0, MkwPointsTab_NINTENDO, "Nintendo", + { MPI_NINTENDO, MPI_NINTENDO, MPI_NINTENDO, MPI_NINTENDO }}, + + { MPI_LINEAR, 1, MkwPointsTab_LINEAR, "Linear", + { MPI_LINEAR, MPI_LINEAR_1, MPI_LINEAR_B, MPI_LINEAR_B1 }}, + { MPI_LINEAR_1, 1, MkwPointsTab_LINEAR_1, "Linear, One", + { MPI_LINEAR, MPI_LINEAR_1, MPI_LINEAR_B, MPI_LINEAR_B1 }}, + { MPI_LINEAR_B, 1, MkwPointsTab_LINEAR_B, "Linear, Bonus", + { MPI_LINEAR, MPI_LINEAR_1, MPI_LINEAR_B, MPI_LINEAR_B1 }}, + { MPI_LINEAR_B1, 1, MkwPointsTab_LINEAR_B1, "Linear, Bonus, One", + { MPI_LINEAR, MPI_LINEAR_1, MPI_LINEAR_B, MPI_LINEAR_B1 }}, + + { MPI_WIN, 1, MkwPointsTab_WIN, MkwPointsInfo_WIN, + { MPI_WIN, MPI_WIN_1, MPI_WIN_B, MPI_WIN_B1 }}, + { MPI_WIN_1, 1, MkwPointsTab_WIN_1, MkwPointsInfo_WIN_1, + { MPI_WIN, MPI_WIN_1, MPI_WIN_B, MPI_WIN_B1 }}, + { MPI_WIN_B, 1, MkwPointsTab_WIN_B, MkwPointsInfo_WIN_B, + { MPI_WIN, MPI_WIN_1, MPI_WIN_B, MPI_WIN_B1 }}, + { MPI_WIN_B1, 1, MkwPointsTab_WIN_B1, MkwPointsInfo_WIN_B1, + { MPI_WIN, MPI_WIN_1, MPI_WIN_B, MPI_WIN_B1 }}, + +#if MKW_POINTS_TEST_ENABLED + { MPI_TEST, 2, MkwPointsTab_TEST, "Test", + { MPI_TEST, MPI_TEST_1, MPI_TEST_B, MPI_TEST_B1 }}, + { MPI_TEST_1, 2, MkwPointsTab_TEST_1, "Test, One", + { MPI_TEST, MPI_TEST_1, MPI_TEST_B, MPI_TEST_B1 }}, + { MPI_TEST_B, 2, MkwPointsTab_TEST_B, "Test, Bonus", + { MPI_TEST, MPI_TEST_1, MPI_TEST_B, MPI_TEST_B1 }}, + { MPI_TEST_B1, 2, MkwPointsTab_TEST_B1, "Test, Bonus, One", + { MPI_TEST, MPI_TEST_1, MPI_TEST_B, MPI_TEST_B1 }}, +#endif + + { MPI__N,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// assigned points: create generic /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +static inline void CalcSequence ( u8 *row, int np, int ni, int last_val ) +{ + DASSERT(row); + DASSERT( np >=0 && np < 12 ); + DASSERT( ni >= 0 ); + DASSERT( last_val >= 0 && last_val < 0x100 ); + + int n_2do = np + 1 - ni; + if ( n_2do && ni ) + { + int val = row[ni-1]; + int diff = last_val - val; + int delta = diff / n_2do; + int bonus_count = diff - n_2do * delta, bonus_value = 1; + if ( bonus_count < 0 ) + { + bonus_count = -bonus_count; + bonus_value = -1; + } + PRINT("CLOSE, 2do=%d, last=%u, delta=%d, bonus=%d+%d\n", + n_2do,last_val,delta,bonus_count,bonus_value); + + while ( n_2do-- > 0 ) + { + val += delta; + if ( bonus_count-- > 0 ) + val += bonus_value; + row[ni++] = val; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +static void CreateGenericTable +( + u8 *tab, // victory points table, size= 12*12 + uint win_pts, // points of the winner + uint opt_bonus, // bonus for the first player + uint opt_last // points of the last player +) +{ + DASSERT(tab); + memset(tab,0,12*12); + + if ( opt_bonus > win_pts ) + opt_bonus = win_pts; + win_pts -= opt_bonus; + if ( opt_last > win_pts ) + opt_last = win_pts; + + uint i; + for ( i = 0; i < 12; i++ ) + { + tab[0] = win_pts; + if (i) + CalcSequence(tab,i,1,opt_last); + tab[0] += opt_bonus; + tab += 12; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +static void CreateGenericWinTables ( uint win_pts ) +{ + if ( win_pts > 255 ) + win_pts = 255; + + snprintf( MkwPointsInfo_WIN, sizeof(MkwPointsInfo_WIN), + "Win %u", win_pts ); + snprintf( MkwPointsInfo_WIN_1, sizeof(MkwPointsInfo_WIN_1), + "Win %u+One", win_pts ); + snprintf( MkwPointsInfo_WIN_B, sizeof(MkwPointsInfo_WIN_B), + "Win %u+Bonus", win_pts ); + snprintf( MkwPointsInfo_WIN_B1, sizeof(MkwPointsInfo_WIN_B1), + "Win %u, Bonus, One", win_pts ); + + CreateGenericTable( MkwPointsTab_WIN, win_pts, 0, 0 ); + CreateGenericTable( MkwPointsTab_WIN_1, win_pts, 0, 1 ); + CreateGenericTable( MkwPointsTab_WIN_B, win_pts, 1, 0 ); + CreateGenericTable( MkwPointsTab_WIN_B1, win_pts, 1, 1 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// assigned points: ScanOptMkwPoints() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp SkipSep ( ccp arg ) +{ + while ( *arg && ( *arg <= ' ' || *arg == ',' || *arg == '+' )) + arg++; + return arg; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool opt_points_used = false; + +// [[24P--]] +int ScanOptMkwPoints ( ccp arg, bool silent ) +{ + #if DEBUG + { + uint i; + for ( i = 0; i < MPI__N; i++ ) + ASSERT_MSG( MkwPointInfo[i].id == i, + "MkwPointInfo[%d].id = %d\n",i,MkwPointInfo[i].id); + } + #endif + + enum + { + MODE_MPI, + MODE_WIN, + MODE_ONE, + MODE_BONUS, + MODE_N, + }; + + static const KeywordTab_t key_tab[] = + { + { MODE_MPI, "NINTENDO", "N", MPI_NINTENDO }, + { MODE_MPI, "LINEAR", "L", MPI_LINEAR }, + #if MKW_POINTS_TEST_ENABLED + { MODE_MPI, "TEST", 0, MPI_TEST }, + #endif + + { MODE_WIN, "WIN", "W", 0 }, + { MODE_WIN, "WIN15", "W15", 15 }, + { MODE_WIN, "WIN25", "W25", 25 }, + { MODE_WIN, "WIIMM", "WIIMM25", -25 }, + + { MODE_ONE, "ONE", "O", 1 }, + { MODE_ONE, "NULL", 0, 0 }, + { MODE_ONE, "NO-ONE", "NOONE", 0 }, + + { MODE_BONUS, "BONUS", "B", 1 }, + { MODE_BONUS, "NO-BONUS", "NOBONUS", 0 }, + + { MODE_N, "N1", 0, /*row=*/ 0 }, + { MODE_N, "N2", 0, 1 }, + { MODE_N, "N3", 0, 2 }, + { MODE_N, "N4", 0, 3 }, + { MODE_N, "N5", 0, 4 }, + { MODE_N, "N6", 0, 5 }, + { MODE_N, "N7", 0, 6 }, + { MODE_N, "N8", 0, 7 }, + { MODE_N, "N9", 0, 8 }, + { MODE_N, "N10", 0, 9 }, + { MODE_N, "N11", 0, 10 }, + { MODE_N, "N12", 0, 11 }, + + {0,0,0,0} + }; + + if ( !arg || !*arg ) + { + memcpy(MkwPointsTab,MkwPointsTab_NINTENDO,sizeof(MkwPointsTab)); + return 0; + } + + u8 tab[12*12], row_defined[12]; + memset(tab,0,sizeof(tab)); + memset(row_defined,0,sizeof(row_defined)); + + int next_np = 10, np = 11, ni = 0; + u8 *row = tab + 12*np; + + MkwPointID opt_mpi = MPI_UNKNOWN; + uint opt_bonus = 0; // >0: add 1 point for victory + uint opt_one = 0; // wanted points for last position + + int last_val = -1; // >=0: points of last player + int max_val = -1; + + uint close_sequence = 0; + for(;;) + { + arg = SkipSep(arg); + if ( !*arg || *arg == '/' ) + close_sequence++; + + if ( close_sequence ) + { + if (ni) + { + if ( last_val < 0 ) + last_val = opt_one; + CalcSequence(row,np,ni,last_val); + row_defined[np] = 1; + } + + close_sequence = 0; + last_val = -1; + ni = 0; + np = next_np--; + row = tab + 12*np; + } + + if (!*arg) + break; + if ( *arg == '/' ) + { + arg++; + continue; + } + + //--- scan number + + u32 num; + ccp next = ScanU32(&num,arg,10); + if ( next > arg && !isalpha((int)*next) ) + { + if ( num >= 0x100 ) + { + num_err: + if (!silent) + ERROR0(ERR_SYNTAX, + "Option --point: Missing number less or equal 255: %s\n",arg); + return 1; + } + + if ( max_val < (int)num ) + max_val = num; + noPRINT("NUMBER: %u/%d |%s|\n",num,max_val,next); + if ( ni > np ) + { + if (!silent) + ERROR0(ERR_SYNTAX, + "Option --point: Only %u numbers allowed for %u players: %s\n", + np+1, np+1, arg ); + return 1; + } + row[ni++] = num; + arg = next; + while ( *arg > 0 && *arg <= ' ' ) + arg++; + + if ( arg[0] == '.' && arg[1] == '.' ) + { + next = ScanU32(&num,arg+2,10); + if ( next == arg || num >= 0x100 || isalpha((int)*next) ) + goto num_err; + if ( max_val < (int)num ) + max_val = num; + noPRINT("..NUMBER: %3u |%s|\n",num,next); + arg = next; + last_val = num; + continue; + } + continue; + } + + + //--- scan keyword + + char key_buf[KEYWORD_NAME_MAX]; + char *dest = key_buf; + while ( ( isalnum((int)*arg) || *arg == '-' ) + && dest < key_buf + sizeof(key_buf) -1 ) + *dest++ = *arg++; + *dest = 0; + if ( dest == key_buf ) + { + if (!silent) + ERROR0(ERR_SYNTAX,"Option --point: Syntax error: %s\n",arg); + return 1; + } + + int abbrev_count; + const KeywordTab_t *key = ScanKeyword(&abbrev_count,key_buf,key_tab); + if (!key) + { + if (!silent) + PrintKeywordError(key_tab,key_buf,abbrev_count, + 0,"keyword for --point"); + return 1; + } + + switch(key->id) + { + case MODE_MPI: + opt_mpi = key->opt; + break; + + case MODE_WIN: + if ( key->opt < 0 ) + { + // WIIMM* + opt_one = 1; + opt_bonus = 1; + num = -key->opt; + } + else if (!key->opt) + { + arg = SkipSep(arg); + next = ScanU32(&num,arg,10); + if ( next == arg || num >= 0x100 || isalpha((int)*next) ) + goto num_err; + arg = next; + } + else + num = key->opt; + + CreateGenericWinTables(num); + opt_mpi = MPI_WIN; + break; + + case MODE_ONE: + opt_one = key->opt; + break; + + case MODE_BONUS: + opt_bonus = key->opt; + break; + + case MODE_N: + next_np = key->opt; + close_sequence++; + break; + + default: + printf("%3llu %3llu : %s, %s\n",key->id,key->opt,key->name1,key->name2); + } + } + opt_points_used = true; + + const int alt_index = ( opt_one ? MPI_I_1 : 0 ) + ( opt_bonus ? MPI_I_B : 0 ); + opt_mpi = MkwPointInfo[opt_mpi].alt[alt_index]; + const u8 *src_tab = MkwPointInfo[opt_mpi].table; + if (!src_tab) + src_tab = MkwPointsTab_NINTENDO; + + if ( max_val < 0 ) + { + memcpy(MkwPointsTab,src_tab,sizeof(MkwPointsTab)); + return 0; + } + + uint max_src = 0; + for ( ni = 0; ni < 12*12; ni++ ) + if ( max_src < src_tab[ni] ) + max_src = src_tab[ni]; + + double factor = (double)max_val / (double)max_src; + for ( np = 0; np < 12; np++ ) + { + if (row_defined[np]) + continue; + + const u8 *p1 = src_tab + 12*np; + u8 *p2 = tab + 12*np; + for ( ni = 0; ni < 12; ni++ ) + *p2++ = double2int( *p1++ * factor ); + } + memcpy(MkwPointsTab,tab,sizeof(MkwPointsTab)); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// assigned points: helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +const MkwPointInfo_t * GetMkwPointInfo ( const u8 * data ) +{ + CreateGenericWinTables(data[11*12]); + + const MkwPointInfo_t *ptr; + for ( ptr = MkwPointInfo; ptr->info; ptr++ ) + if ( ptr->table && !memcmp(ptr->table,data,12*12) ) + return ptr; + + return MkwPointInfo + MPI_UNKNOWN; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// assigned points: printing /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +void PrintMkwPointsRAW ( FILE *f, uint indent, const u8 * data ) +{ + DASSERT(f); + DASSERT(data); + + uint i, np, max = 0; + for ( i = 0; i < 12*12; i++ ) + if ( max < data[i] ) + max = data[i]; + const uint fw = max < 100 ? 2 : 3; + + for ( np = 1; np <= 12; np++ ) + { + fprintf(f,"%*u:",indent+2,np); + for ( i = 0; i < np; i++, data++ ) + fprintf(f," %*u ",fw,*data); + for ( ; i < 12; i++, data++ ) + if (*data ) + fprintf(f," %*u!",fw,*data); + else + fprintf(f," %*c ",fw,'-'); + fputc('\n',f); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +void PrintMkwPointsC ( FILE *f, uint indent, const u8 * data ) +{ + DASSERT(f); + DASSERT(data); + + uint i, np, max = 0; + for ( i = 0; i < 12*12; i++ ) + if ( max < data[i] ) + max = data[i]; + const uint fw = max < 100 ? 2 : 3; + + for ( np = 1; np <= 12; np++ ) + { + if (indent > 0 ) + fprintf(f,"%*s",indent-1,""); + for ( i = 0; i < 12; i++, data++ ) + { + if ( i == np ) + fprintf(f," %*c ",fw,' '); + fprintf(f," %*u,",fw,*data); + } + fputc('\n',f); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +void PrintMkwPointsCheat +( + FILE *f, // output file + const u8 *data, // pointer to data table + char region_code, // region code: E|P|J|K + u32 cheat_base // value of first code +) +{ + DASSERT(f); + DASSERT(data); + + fprintf(f, + "RMC%c01\nMario Kart Wii\n\nVersus Points Modifier\n%08x 00000090\n", + region_code, cheat_base ); + + uint line; + for ( line = 0; line < 12*12/8; line++ ) + { + fprintf(f,"%02x%02x%02x%02x %02x%02x%02x%02x\n", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7] ); + data += 8; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +uint PrintMkwPointsLIST +( + // returns the used 'mode' + + char *buf, // valid output buffer + uint buf_size, // size of 'buf', MKW_POINTS_DEF_SIZE recommended + const u8 *data, // 12*12 data table + uint mode // 0: pure number list + // 1: allow 'a..b' + // 2: allow names of permanent tables + // 3: allow names +) +{ + DASSERT(buf); + DASSERT(buf_size>2); + DASSERT(data); + + if ( mode >= 2 ) + { + const MkwPointInfo_t *mpi = GetMkwPointInfo(data); + if ( mpi->mode <= 1 || mpi->mode == 2 && mode >= 3 ) + { + StringCopyS(buf,buf_size,mpi->info); + return mpi->mode <= 1 ? 2 : 3; + } + } + + + //--- fall back to pure numbers + + uint ret_val = 0; + char *dest = buf; + char *end = buf + buf_size - 2; + + int np; + for ( np = 11; np >= 0 && dest < end; np-- ) + { + const u8 *row = data + 12*np; + if ( mode >= 1 && np > 1 ) + { + // ranges allowed + + int delta = (int)row[np-1] - (int)row[np]; + if ( delta >= 0 ) + { + int ni = np - 1; + while ( ni > 0 && (int)row[ni-1] - (int)row[ni] == delta ) + ni--; + delta++; + while ( ni > 0 && (int)row[ni-1] - (int)row[ni] == delta ) + ni--; + if ( np - ni > 2 ) + { + int i; + for ( i = 0; i <= ni && dest < end; i++ ) + dest += snprintf(dest,end-dest,"%u,",row[i]); + dest--; + if ( dest < end ) + dest += snprintf(dest,end-dest,"..%u,",row[np]); + dest[-1] = '/'; + ret_val = 1; + continue; + } + } + } + + int i; + for ( i = 0; i <= np && dest < end; i++ ) + dest += snprintf(dest,end-dest,"%u,",*row++); + dest[-1] = '/'; + } + dest[-1] = 0; + return ret_val; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[24P--]] + +void PrintMkwPoints +( + FILE *f, // destination file + uint indent, // indention of the output + const u8 *data, // data, 12*12 table + uint print_table, // print the table: + // 0: don't print + // 1: print table as RAW + // 2: print table as C listing + // A-Z: Print as cheat code with this region + u32 cheat_base, // not NULL: value of first param of cheat code + uint print_param // print a parameter string: + // 0: don't print + // 1: print single line + // 2: print all modes +) +{ + switch (print_table) + { + case 1: + PrintMkwPointsRAW(f,indent,data); + fputc('\n',f); + break; + + case 2: + PrintMkwPointsC(f,indent,data); + fputc('\n',f); + break; + + default: + if ( print_table >= 'A' && print_table <= 'Z' && cheat_base ) + { + PrintMkwPointsCheat(f,data,print_table,cheat_base); + fputc('\n',f); + } + break; + } + + char buf[MKW_POINTS_DEF_SIZE]; + if ( print_param == 1 ) + { + PrintMkwPointsLIST(buf,sizeof(buf),data,2); + printf("%*s%s\n\n",indent,"",buf); + } + else if ( print_param > 1 ) + { + int mode; + for ( mode = 3; mode >= 0; mode-- ) + { + const uint res = PrintMkwPointsLIST(buf,sizeof(buf),data,mode); + if ( res == mode ) + printf("%*s%s\n\n",indent,"",buf); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GetVrDiff*() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// https://forum.wii-homebrew.com/index.php?page=Thread&postID=612772 +// http://wiki.tockdom.com/wiki/Player_Rating +// https://www.desmos.com/calculator/gr17y7d944 +/////////////////////////////////////////////////////////////////////////////// + +static const s16 vr_diff_tab[112] = +{ + 6598, 4999, 3963, 3233, 2671, 2212, 1819, 1476, // 0 .. 7 + 1168, 889, 633, 395, 174, -34, -231, -418, // 8 .. 15 + -596, -767, -931, -1090, -1243, -1392, -1536, -1676, // 16 .. 23 + -1813, -1946, -2076, -2203, -2328, -2450, -2569, -2687, // 24 .. 31 + -2802, -2915, -3026, -3136, -3243, -3349, -3454, -3557, // 32 .. 39 + -3658, -3759, -3857, -3955, -4051, -4147, -4241, -4334, // 40 .. 47 + -4425, -4516, -4606, -4695, -4783, -4870, -4957, -5042, // 48 .. 55 + -5127, -5211, -5294, -5377, -5459, -5540, -5622, -5702, // 56 .. 63 + -5783, -5863, -5942, -6022, -6101, -6180, -6259, -6338, // 64 .. 71 + -6417, -6495, -6574, -6653, -6731, -6810, -6889, -6968, // 72 .. 79 + -7048, -7127, -7207, -7287, -7367, -7448, -7530, -7611, // 80 .. 87 + -7693, -7776, -7860, -7944, -8029, -8114, -8201, -8288, // 88 .. 95 + -8377, -8466, -8557, -8649, -8743, -8838, -8935, -9034, // 96 .. 103 + -9135, -9239, -9345, -9455, -9568, -9684, -9806, -9933, // 104 .. 111 +}; + +/////////////////////////////////////////////////////////////////////////////// + +int GetVrDiffByTab ( int diff ) // diff = VR(winner) - VR(loser) +{ + if ( diff > 6598 ) + return 0; + if ( diff < -9933 ) + return 112; + + const s16 search = diff; + int i = 0, j = sizeof(vr_diff_tab) / sizeof(*vr_diff_tab) - 1; + + while ( i < j ) + { + const int k = (i+j)/2; + if ( search > vr_diff_tab[k] ) + j = k-1; + else if ( search <= vr_diff_tab[k+1] ) + i = k+1; + else + return k+1; + } + + return i+1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// info tables /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetMkwTrackName3 ( int tid ) +{ + static const char tab[][4] = + { + //-- tracks: 0..31 = 0x00..0x1f + + "T21", "T12", "T13", "T34", + "T14", "T22", "T23", "T24", + "T11", "T31", "T42", "T33", + "T43", "T44", "T41", "T32", + + "T51", "T74", "T64", "T83", + "T52", "T71", "T82", "T63", + "T81", "T53", "T54", "T61", + "T84", "T73", "T72", "T62", + + //-- arenas: 32..41 = 0x20..0x29 + + "A12", "A11", "A14", "A13", "A15", + "A24", "A25", "A21", "A22", "A23", + + //-- special: 42..67 = 0x2a..0x43 + + "", "", "", "", "", "", + "", "", "", "", "", "", "MIS", "WIN", + "LOS", "DRW", "END", "", "", "", "", "", + "", "", "inv", "non", + }; + + if ( (uint)tid < sizeof(tab)/sizeof(*tab) && tab[tid][0] ) + return tab[tid]; + + if ( tid < 0 ) + return "---"; + + if ( tid == 0xff ) + return "rnd"; + + char buf[20]; + const int len = snprintf(buf,sizeof(buf),"_%02x",tid) + 1; + char * dest = GetCircBuf(len); + memcpy(dest,buf,len); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetMkwMusicName3 ( int mid ) +{ + static const char tab[][4] = + { + "T11", "t11", + "T12", "t12", + "T13", "t13", + "T14", "t14", + "T21", "t21", + "T22", "t22", + "T23", "t23", + "T24", "t24", + "T32", "t32", + "T31", "t31", + "T41", "t41", + "T34", "t34", + "T42", "t42", + "T33", "t33", + "T43", "t43", + "T44", "t44", + "T62", "t62", + "T53", "t53", + "T81", "t81", + "T72", "t72", + "T61", "t61", + "T54", "t54", + "T73", "t73", + "T84", "t84", + "T51", "t51", + "T74", "t74", + "T64", "t64", + "T83", "t83", + "T52", "t52", + "T63", "t63", + "T71", "t71", + "T82", "t82", + "A12", "a12", + "A11", "a11", + "A13", "a13", + "A14", "a14", + "A15", "a15", + "A24", "a24", + "A25", "a25", + "A21", "a21", + "A22", "a22", + "A23", "a23", + }; + + if ( mid >= MKW_MUSIC_MIN_ID && mid < MKW_MUSIC_MIN_ID + sizeof(tab)/sizeof(*tab) ) + return tab[mid-MKW_MUSIC_MIN_ID]; + + if ( mid < 0 ) + return "---"; + + char buf[20]; + const int len = snprintf(buf,sizeof(buf),"_%02x",mid) + 1; + char * dest = GetCircBuf(len); + memcpy(dest,buf,len); + return dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/lib-mkw.h b/project/dclib/lib-mkw.h new file mode 100644 index 0000000..8a1c644 --- /dev/null +++ b/project/dclib/lib-mkw.h @@ -0,0 +1,308 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * Support for Mario Kart Wii * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_MKWII_H +#define SZS_LIB_MKWII_H 1 + +#define _GNU_SOURCE 1 +#include "dclib-basics.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CTW definitions / ////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CTW_MAX_TRACK_ID 999999 + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// base definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum mkw_consts_t // some constants +{ + //--- num of players + + MKW_STD_MAX_PLAYER = 12, + MKW_EX_MAX_PLAYER = 24, + + + //--- tracks + + MKW_N_TRACKS = 0x20, + MKW_TRACK_BEG = 0x00, + MKW_TRACK_END = MKW_TRACK_BEG + MKW_N_TRACKS, + + MKW_TRACK_MID_1 = 0x2454, + MKW_TRACK_MID_2 = 0x2490, + + + //--- arenas + + MKW_N_ARENAS = 0x0a, + MKW_ARENA_BEG = 0x20, + MKW_ARENA_END = MKW_ARENA_BEG + MKW_N_ARENAS, + + MKW_ARENA_MID_1 = 0x24b8, + MKW_ARENA_MID_2 = 0x24cc, + + + //--- original track := tracks + arenas + + MKW_N_ORIGINAL = MKW_N_TRACKS + MKW_N_ARENAS, + MKW_ORIGINAL_BEG = MKW_TRACK_BEG, + MKW_ORIGINAL_END = MKW_ARENA_END, + + + //--- music ids + + MKW_MUSIC_MIN_ID = 0x75, + MKW_MUSIC_MAX_ID = 0xc8, + MKW_N_MUSIC = 42, +}; + +//----------------------------------------------------------------------------- + +static inline bool IsMkwTrack ( uint tid ) + { return tid < MKW_TRACK_END; } + +static inline bool IsMkwArena ( uint tid ) + { return tid >= MKW_ARENA_BEG && tid < MKW_ARENA_END; } + +static inline bool IsMkwOriginal ( uint tid ) + { return tid < MKW_ARENA_END; } + +static inline bool IsMkwCustom ( uint tid ) + { return tid >= MKW_ARENA_END; } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// 24p support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mkw_std_pbits1_t]] [[mkw_ex_pbits1_t]] [[mkw_pbits1_t]] +// [[mkw_std_pbits2_t]] [[mkw_ex_pbits2_t]] [[mkw_pbits2_t]] + +#if !defined(SUPPORT_24P) + #define SUPPORT_24P 1 +#endif + +// unsigned types, that can hold 1 or 2 bits for each player +typedef u16 mkw_std_pbits1_t; // 12 bits needed +typedef u32 mkw_std_pbits2_t; // 24 bits needed +typedef u32 mkw_ex_pbits1_t; // 24 bits needed +typedef u64 mkw_ex_pbits2_t; // 48 bits needed + +#if SUPPORT_24P + typedef u32 mkw_pbits1_t; + typedef u64 mkw_pbits2_t; + #define MKW_MAX_PLAYER MKW_EX_MAX_PLAYER +#else + typedef u16 mkw_pbits1_t; + typedef u32 mkw_pbits2_t; + #define MKW_MAX_PLAYER MKW_STD_MAX_PLAYER +#endif + +#define MKW_STD_MAX_PLAYER2 ( MKW_STD_MAX_PLAYER * MKW_STD_MAX_PLAYER ) +#define MKW_EX_MAX_PLAYER2 ( MKW_EX_MAX_PLAYER * MKW_EX_MAX_PLAYER ) +#define MKW_MAX_PLAYER2 ( MKW_MAX_PLAYER * MKW_MAX_PLAYER ) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// assigned points for versus and rooms /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define MKW_POINTS_DEF_SIZE 320 + +#if defined(DEBUG) || defined(TEST) + #define MKW_POINTS_TEST_ENABLED 1 +#else + #define MKW_POINTS_TEST_ENABLED 0 +#endif + +//----------------------------------------------------------------------------- + +typedef enum MkwPointID +{ + //--- tables + MPI_UNKNOWN, // no table assigend + MPI_NINTENDO, // original table + + MPI_LINEAR, // 4 linear tables + MPI_LINEAR_1, + MPI_LINEAR_B, + MPI_LINEAR_B1, + + MPI_WIN, // 4 generic tables tables with winner = X points + MPI_WIN_1, + MPI_WIN_B, + MPI_WIN_B1, + + #if MKW_POINTS_TEST_ENABLED + MPI_TEST, // 4 test tables + MPI_TEST_1, + MPI_TEST_B, + MPI_TEST_B1, + #endif + MPI__N, + + //--- index values + MPI_I_STD = 0, + MPI_I_1, + MPI_I_B, + MPI_I_B1, + MPI_I__N, +} +MkwPointID; + +typedef struct MkwPointInfo_t +{ + MkwPointID id; // ID of table + uint mode; // 0:permanent, 1:good, 2:temp, 3:UNKNOWN + const u8 *table; // related table + ccp info; // info about the table + u8 alt[MPI_I__N]; // index of related alternative tables +} +MkwPointInfo_t; + +//----------------------------------------------------------------------------- + +const MkwPointInfo_t MkwPointInfo[MPI__N+1]; + +// [[24P--]] all tables + +extern u8 MkwPointsTab[MKW_STD_MAX_PLAYER2]; +extern const u8 MkwPointsTab_NINTENDO[MKW_STD_MAX_PLAYER2]; +extern const u8 MkwPointsTab_LINEAR[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_LINEAR_1[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_LINEAR_B[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_LINEAR_B1[MKW_STD_MAX_PLAYER2]; +extern const u8 MkwPointsTab_WIN25[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_WIN25_1[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_WIN25_B[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_WIN25_B1[MKW_STD_MAX_PLAYER2]; +extern const u8 MkwPointsTab_WIN15[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_WIN15_1[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_WIN15_B[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_WIN15_B1[MKW_STD_MAX_PLAYER2]; + +#if MKW_POINTS_TEST_ENABLED + extern const u8 MkwPointsTab_TEST[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_TEST_1[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_TEST_B[MKW_STD_MAX_PLAYER2]; + extern const u8 MkwPointsTab_TEST_B1[MKW_STD_MAX_PLAYER2]; +#endif + +extern bool opt_points_used; + +//----------------------------------------------------------------------------- + +static inline u8 GetMkwPoints ( uint n_player, uint idx_player ) +{ +// [[24P-]] + n_player--; + idx_player--; + return n_player < 12 && idx_player < 12 + ? MkwPointsTab[ 12*n_player + idx_player ] + : 0; +} + +static inline u8 GetDirectMkwPoints ( uint n_player, uint idx_player ) +{ +// [[24P-]] + return MkwPointsTab[ 12*n_player + idx_player - 13 ]; +} + +//----------------------------------------------------------------------------- + +int ScanOptMkwPoints ( ccp arg, bool silent ); +const MkwPointInfo_t * GetMkwPointInfo ( const u8 * data ); + +void PrintMkwPointsRAW ( FILE *f, uint indent, const u8 * data ); +void PrintMkwPointsC ( FILE *f, uint indent, const u8 * data ); + +// [[24P--]] +uint PrintMkwPointsLIST +( + // returns the used 'mode' + + char *buf, // valid output buffer + uint buf_size, // size of 'buf', MKW_POINTS_DEF_SIZE recommended + const u8 *data, // 12*12 data table + uint mode // 0: pure number list + // 1: allow 'a..b' + // 2: allow names of permanent tables + // 3: allow names +); + +// [[24P--]] +void PrintMkwPoints +( + FILE *f, // destination file + uint indent, // indention of the output + const u8 *data, // data, 12*12 table + uint print_table, // print the table: + // 0: don't print + // 1: print table as RAW + // 2: print table as C listing + // A-Z: Print as cheat code with this region + u32 cheat_base, // not NULL: value of first param of cheat code + uint print_param // print a parameter string: + // 0: don't print + // 1: print single line + // 2: print all modes +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GetVrDiff*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int GetVrDiffByTab ( int diff ); // diff = VR(winner) - VR(loser) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// info tables /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetMkwTrackName3 ( int tid ); +ccp GetMkwMusicName3 ( int mid ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_CTCODE_H + diff --git a/project/gen-template.sh b/project/gen-template.sh new file mode 100755 index 0000000..7e137dd --- /dev/null +++ b/project/gen-template.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +#set -v + +rm -f templates/*.bak templates/*.tmp +mkdir -p doc + +list="$*" +[[ $list = "" ]] && list="$(/bin/ls templates/)" + +awkprog=' + /@@MODULE\(.*\)@@/ { + gsub(/@@MODULE\(/,"templates/module/"); + gsub(/\)@@/,""); + system("cat " $0); + next + } + + /@@EXEC\(.*\)@@/ { + gsub(/@@EXEC\(/,""); + gsub(/\)@@/,""); + system($0); + next + } + + { print $0 } +' + +CRLF=0 +uname -s | grep -q CYGWIN && CRLF=1 + +for fname in $list +do + src="setup/$fname" + [[ -f $src ]] || src="templates/$fname" + if [[ -f "$src" ]] + then + dest="doc/$fname" + ext="${dest##*.}" + #[[ $ext == edit-list ]] && continue + [[ $ext = "sh" || $ext = "bat" || $ext = "h" ]] && dest="$fname" + [[ $fname = "INSTALL.txt" ]] && dest="$fname" + #echo "|$src|$dest|" + + if ((CRLF)) && [[ $ext = bat || $ext = txt ]] + then + awk "$awkprog" $src | sed -f templates.sed | sed 's/$/\r/' >"$dest.tmp" + else + awk "$awkprog" $src | sed -f templates.sed >"$dest.tmp" + fi + [[ $ext = "sh" ]] && chmod a+x "$dest.tmp" + mv "$dest.tmp" "$dest" + fi +done + +exit 0 diff --git a/project/gen-text-file.sh b/project/gen-text-file.sh new file mode 100755 index 0000000..428a8a9 --- /dev/null +++ b/project/gen-text-file.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +dir="$1" +shift + +if [[ ! -d $dir ]] +then + echo "Directory not found: $dir" >&2 + exit 1 +fi + +function gen_text() +{ + local varname="$1" + local src="$2" + + printf "\nconst char %s[] =\n{\n" "$varname" + if [[ $varname =~ _bz2$ ]] + then + # '-f%z' for MAC, '-c%s' for GNU + s=$( stat -c%s "$src" 2>/dev/null || stat -f%z "$src" ) + x=$( printf "%08x" "$s" ) + let c=s/100000+1 + ((c>9)) && c=9 + printf " 0x%s, 0x%s, 0x%s, 0x%s, // uncompressed size = $s, compression level = $c\n" \ + "${x:0:2}" "${x:2:2}" "${x:4:2}" "${x:6:2}" + bzip2 -$c < "$src" | xxd -i -c16 + elif [[ $varname =~ _cr$ ]] + then + grep -v '^~' "$src" \ + | sed 's/\\/\\\\/g; s/"/\\"/g; s/^/ "/; s/#FF#/#\\f/; s/$/\\r\\n"/' + else + grep -v '^~' "$src" \ + | sed 's/\\/\\\\/g; s/"/\\"/g; s/^/ "/; s/#FF#/#\\f/; s/$/\\n"/' + fi + printf "};\n\n" +} + +for src in "$@" +do + src="${src##*/}" + name="${src%.*}" + name="text_${name//[-.]/_}" + cname="$( echo "SZS_${name}_INC" | awk '{print toupper($0)}' )" + #echo "$name : $src -> $dest" + + { + printf "\n#ifndef %s\n#define %s 1\n" "$cname" "$cname" + + if [[ -f "$dir/$src" ]] + then + gen_text "$name" "$dir/$src" + echo "$src: $dir/$src" >"$src.d" + elif [[ -d "$dir/$src" ]] + then + printf '%s:' "$src" >"$src.d" + for inc in "$dir/$src"/*.inc + do + name="${inc##*/}" + name="${name%.*}" + name="text_${name//[-.]/_}" + gen_text "$name" "$inc" + printf ' \\\n\t%s' "$inc" >>"$src.d" + done + printf '\n' >>"$src.d" + else + printf '\n!! Text not found: %s\n' "$dir/$src" >&2 + rm -f "$src" + exit 1 + fi + + printf "#endif // %s\n\n" "$cname" + + } >"$src" + +done + diff --git a/project/gpl-2.0.txt b/project/gpl-2.0.txt new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/project/gpl-2.0.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/project/makefiles-local/Makefile.local b/project/makefiles-local/Makefile.local new file mode 100644 index 0000000..612a98f --- /dev/null +++ b/project/makefiles-local/Makefile.local @@ -0,0 +1,17 @@ + +# this is a template for ../Makefile.local + +# copy this file to the main directory +# and remove appropriate comments (the leading '# ' of the include lines) + +#------------------------------------------------------------------------------ + +# Linux, 32 bit, i368 +# -include makefiles-local/Makefile.local.i386 + +# Linux, 64 bit, x86 +# -include makefiles-local/Makefile.local.x86_64 + +# Mac, universal binary +# -include makefiles-local/Makefile.local.mac + diff --git a/project/makefiles-local/Makefile.local.i386 b/project/makefiles-local/Makefile.local.i386 new file mode 100644 index 0000000..19db6a3 --- /dev/null +++ b/project/makefiles-local/Makefile.local.i386 @@ -0,0 +1,10 @@ + +# optimize linux i86 (32-bit) + +# to include this file +# => copy Makefile.local to main directory and remove appropriate comments + +#------------------------------------------------------------------------------ + +#CFLAGS += -mtune=generic +CFLAGS += -march=i686 diff --git a/project/makefiles-local/Makefile.local.mac b/project/makefiles-local/Makefile.local.mac new file mode 100644 index 0000000..419dac2 --- /dev/null +++ b/project/makefiles-local/Makefile.local.mac @@ -0,0 +1,96 @@ + +# optimize mac binaries and/or build universal binaries + +#----------------------------------------------------------------------------- + +SDKROOT = $(shell ls -d /Developer/SDKs/MacOSX* | tail -n1 ) +SDKVERSION = $(shell echo "$(SDKROOT)" | sed -E 's/.*MacOSX//; s/([0-9]*\.[0-9*]).*/\1/g' ) + +CC = "gcc-4.0" +CFLAGS += -mmacosx-version-min=${SDKVERSION} -isysroot ${SDKROOT} +LDFLAGS += -mmacosx-version-min=${SDKVERSION} -isysroot ${SDKROOT} + +#----------------------------------------------------------------------------- + +.PHONY : mac-distrib +mac-distrib: mac2 doc $(INSTALL_SCRIPTS) gen-mac-distrib + +.PHONY : mac2 +mac2: mac-i386 mac-x86_64 + + @echo + @echo "----- building universal binaries -----" + + @for i in $(MAIN_TOOLS); do \ + lipo -create bin/i386/$${i} bin/x86_64/$${i} -output $${i}; \ + ln -f $${i} bin/$${i}; \ + done + @rm -rf bin/i386 bin/x86_64 + @echo "done" + +.PHONY : mac3 +mac3: mac-i386 mac-x86_64 mac-ppc + + @echo + @echo "----- building universal binaries -----" + + @for i in $(MAIN_TOOLS); do \ + lipo -create bin/ppc/$${i} bin/i386/$${i} bin/x86_64/$${i} -output $${i}; \ + ln -f $${i} bin/$${i}; \ + done; + @rm -rf bin/ppc bin/i386 bin/x86_64 + @echo "done" + +#----------------------------------------------------------------------------- + +.PHONY : mac-ppc +mac-ppc: + @echo + @echo "----- building PowerPC [SDK $(SDKVERSION)] -----" + @XFLAGS="-arch ppc -mcpu=powerpc" make clean all + @mkdir -p bin/ppc + @mv $(MAIN_TOOLS) bin/ppc + +#----------------------------------------------------------------------------- + +.PHONY : mac-i386 +mac-i386: + @echo + @echo "----- building i386 [SDK $(SDKVERSION)] -----" + @XFLAGS="-arch i386 -mfpmath=sse -march=prescott" make clean all + @mkdir -p bin/i386 + @mv $(MAIN_TOOLS) bin/i386 + + +#----------------------------------------------------------------------------- + +.PHONY : mac-x86_64 +mac-x86_64: + @echo + @echo "----- building X86-64 [SDK $(SDKVERSION)] -----" + @XFLAGS="-arch x86_64 -mfpmath=sse" make clean all + @mkdir -p bin/x86_64 + @mv $(MAIN_TOOLS) bin/x86_64 + +#----------------------------------------------------------------------------- + +.PHONY : gen-mac-distrib +gen-mac-distrib: + + @printf "$(LOGFORMAT)" create "$(DISTRIB_PATH)" "" + + @rm -rf $(DISTRIB_PATH) + @mkdir -p $(DISTRIB_PATH)/bin $(DISTRIB_PATH)/doc + + @cp -p $(DISTRIB_FILES) $(DISTRIB_PATH) + @ln -f $(MAIN_TOOLS) $(WDF_LINKS) $(DISTRIB_PATH)/bin + cp -p $(DOC_FILES) $(DISTRIB_PATH)/doc +# @cp -p $(SCRIPTS)/*.{sh,txt} $(DISTRIB_PATH)/scripts + + @chmod -R 755 $(DISTRIB_PATH) +# @chmod a+x $(DISTRIB_PATH)/*.sh $(DISTRIB_PATH)/scripts/*.sh $(DISTRIB_PATH)/bin*/* + @chmod a+x $(DISTRIB_PATH)/*.sh $(DISTRIB_PATH)/bin*/* + @chmod -R a+X $(DISTRIB_PATH) + + @tar -czf $(DISTRIB_PATH).tar.gz $(DISTRIB_PATH) + diff --git a/project/makefiles-local/Makefile.local.x86_64 b/project/makefiles-local/Makefile.local.x86_64 new file mode 100644 index 0000000..dc5b934 --- /dev/null +++ b/project/makefiles-local/Makefile.local.x86_64 @@ -0,0 +1,10 @@ + +# optimize linux x86 (64-bit) + +# to include this file +# => copy Makefile.local to main directory and remove appropriate comments + +#------------------------------------------------------------------------------ + +CFLAGS += -march=x86-64 + diff --git a/project/revision.sh b/project/revision.sh new file mode 100644 index 0000000..de473d0 --- /dev/null +++ b/project/revision.sh @@ -0,0 +1 @@ +revision=8217 diff --git a/project/scripts/create-brres-lists.sh b/project/scripts/create-brres-lists.sh new file mode 100755 index 0000000..2f6669a --- /dev/null +++ b/project/scripts/create-brres-lists.sh @@ -0,0 +1,256 @@ +#!/usr/bin/env bash + +# +#------------------------------------------------------------------------------ +echo "* setup" + +listdir=./_lists +[[ $1 = -t || $1 == --test ]] || rm -rf "$listdir" +mkdir -p "$listdir" + +filelist="$listdir/brres.flist" +listing="$listdir/brres.list" + +subfilelist="$listdir/sub-file.list" +submodelist="$listdir/sub-mode.list" +subcountlist="$listdir/sub-count.list" + +#subfilesummary="$listdir/sub-summary.txt" + +subfileflist="${subfilelist%.list}.flist" +submodeflist="${submodelist%.list}.flist" +subcountflist="${subcountlist%.list}.flist" + +info="$listdir/INFO.txt" + +# +#------------------------------------------------------------------------------ +echo "* create file list : $filelist" + +if [[ -s $filelist ]] +then + echo " -> skipped" +else + find . -type f -name '*.brres' | sort -f >"$filelist" +fi + +# +#------------------------------------------------------------------------------ +echo "* create brres listing : $listing" + +if [[ -s $listing ]] +then + echo " -> skipped" +else + wszst lll "@$filelist" >"$listing" +fi + +# +#------------------------------------------------------------------------------ +echo "* create sub file listing : $subfilelist" + +awk=' + BEGIN { file = "" } + END { print "" } + + $1 == "*" && $2 == "Files" { + file = "" + idx = index($0,":"); + if ( idx > 0 ) + { + file = substr($0,idx+1); + print ""; + } + #printf("FILE: %s\n",file); + } + + file != "" && $5+0 > 0 && $4 != "...." { + printf("%s %2u %s %s\n",$4,$5,file,$6); + } + +' + +if [[ -s $subfilelist ]] +then + echo " -> skipped" +else + awk "$awk" "$listing" >"$subfilelist" +fi + +# +#------------------------------------------------------------------------------ +echo "* create sub file listing : $subfileflist" + +awk_flist=' + $4 == "" { print; next } + { printf("%s %2u %s\n",$1,$2,$3) } +' + +if [[ -s $subfileflist ]] +then + echo " -> skipped" +else + awk "$awk_flist" "$subfilelist" | uniq >"$subfileflist" +fi + +# +#------------------------------------------------------------------------------ +echo "* create sub mode listing : $submodelist" + +awk=' + BEGIN { mode = "" } + END { print "" } + + $3 != "" { + newmode = $1 "." $2; + if ( mode != newmode ) + { + if ( mode == "" ) + print ""; + else + print "\f"; + mode = newmode; + } + printf("%s %2u %s %s\n",$1,$2,$3,$4); + } +' + +if [[ -s $submodelist ]] +then + echo " -> skipped" +else + sort -f "$subfilelist" | awk "$awk" >"$submodelist" +fi + +# +#------------------------------------------------------------------------------ +echo "* create sub mode listing : $submodeflist" + +if [[ -s $submodeflist ]] +then + echo " -> skipped" +else + awk "$awk_flist" "$submodelist" | uniq >"$submodeflist" +fi + +# +#------------------------------------------------------------------------------ +echo "* create sub count listings : $subcountlist $subcountflist" + +awk1=' + $3 != "" { printf("%s %2u\n",$1,$2); } +' + +awk2=' + BEGIN { mode = "" } + END { print "" } + + { + if ( mode != $2 ) + { + mode = $2; + print ""; + } + printf("%6u* %s %2u\n",$1,$2,$3); + } +' + +if [[ -s $subcountlist && -s $subcountflist ]] +then + echo " -> skipped" +else + awk "$awk1" "$submodelist" | uniq -c | awk "$awk2" >"$subcountlist" + awk "$awk1" "$submodeflist" | uniq -c | awk "$awk2" >"$subcountflist" +fi + +# +#------------------------------------------------------------------------------ +echo "* create info file : $info" + +cat <<- --EOT-- >"$info" + +OVERVIEW: +--------- + +The list files can be divided into 2 groups: + *.list files are the base listings with all BRRES sub files. + *.flist are reduced listings: + No more than one line for each BRRES and category. + +All files are machine readable, just ignore empty lines (inserted for human +readability) and lines containing only 1 formfeed (ASCII 12, inserted for +fast finding of sections). Columns are separated by multiple spaces (like +awk and other tools does). + + + +FILES: +------ + +${filelist##*/} + File list of all explored BRRES files. + +${listing##*/} + A sub listing of all explored BRRES files. + This listing was made with: wszst lll @${filelist##*/} + +${subfilelist##*/} + A list of sub files with for space separated columns: + MAGIC VERSION BRRES_FILE SUB_FILE + The file is sorted by the BRRES_FILE column. + +${subfileflist##*/} + Same as '${subfilelist##*/}', but reduced to BRRES file level. + A list of sub files with for space separated columns: + MAGIC VERSION BRRES_FILE + The file is sorted by the BRRES_FILE column. + +${submodelist##*/} + Same as '${subfilelist##*/}', but sorted by columns MAGIC and VERSION. + +${submodeflist##*/} + Same as '${subfileflist##*/}', but sorted by columns MAGIC and VERSION. + Same as '${submodelist##*/}', but reduced to BRRES file level. + +${subcountlist##*/} + This is a summary of '${subfilelist##*/}' or '${submodelist##*/}'. + Instead of filenames it contains exist counters. + COUNT* MAGIC VERSION + +${subcountflist##*/} + This is a summary of '${subfileflist##*/}' or '${submodeflist##*/}'. + Instead of filenames it contains exist counters. + COUNT* MAGIC VERSION + + + +STATISTICS: +----------- + +--EOT-- + +{ + printf "SZS files:" + sed 's+\.szs\.d/.*$++' "$filelist" | uniq | wc -l + + printf "BRRES files:" + cat "$filelist" | wc -l + + printf "BRRES sub files:" + grep -v '^$' "$submodelist" | wc -l + +} | awk -F: '{printf("%6u %s\n",$2,$1)}' >>"$info" + + +cat <<- --EOT-- >>"$info" + + + +Wiimm, $(date '+%F %T') + +--EOT-- + +# +#------------------------------------------------------------------------------ +echo "done" + diff --git a/project/scripts/distrib.list b/project/scripts/distrib.list new file mode 100644 index 0000000..11900fa --- /dev/null +++ b/project/scripts/distrib.list @@ -0,0 +1,3 @@ +scripts/kcl +scripts/test-wszst.sh +scripts/test-wu8.sh diff --git a/project/scripts/kcl/lower-walls.txt b/project/scripts/kcl/lower-walls.txt new file mode 100644 index 0000000..6ec281d --- /dev/null +++ b/project/scripts/kcl/lower-walls.txt @@ -0,0 +1,41 @@ +############################### +### (c) Wiimm, 2015-12-18 ### +############################### + +# Setup +@def start = mSec() # start time, for the status line +@def mod_count = 0 # modification counter + +# Get the value for lowering the walls. +# If 'lower' is defined as number by option --const => use it. +# Otherwise use the default of 30. +@def lower = isNumeric(lower) ? lower : 30 + +# Limit to walls with inclination <45 degree +# If 'degree' is defined by option --const as number >0 => use it. +# Otherwise use the default of 45 degrees. +@def degree = isNumeric(degree) && degree > 0 ? degree : 45 +@def sin_degree = sin(degree) + +# Define a function to test the KCL flag for walls +@function isWall # flag + @pdef t = $1 & 0x1f + @return t == 0x0c || t == 0x0d || t == 0x0f || t == 0x14 || t == 0x1e || t == 0x1f +@endfunction + +# Main loop: Iterate through all triangles +@for t=0;tri$n()-1 + @if isWall(tri$flag(t)) + @def norm = tri$normal(t,0) # get the first normal + @if abs(norm.y) < sin_degree + # it's a vertical wall -> lower the wall & increment counter + @def status = tri$shift(t,-vy(lower)) + @def mod_count = mod_count+1 + @endif + @endif +@endfor + +# Print a little status line +@echo " - " mod_count " of " tri$n() " triangles lowered by " lower + > " in " (mSec()-start) " msec." + diff --git a/project/scripts/kcl/minimap.txt b/project/scripts/kcl/minimap.txt new file mode 100644 index 0000000..a79ac08 --- /dev/null +++ b/project/scripts/kcl/minimap.txt @@ -0,0 +1,17 @@ + +@def start = mSec() # start time +@def mod_count = 0 # modification counter + +@function isRoad # flag + @pdef t = $1 & 0x1f + @return t <= 0x05 || t == 0x17 || t == 0x1d +@endfunction + +@for t=0;tri$n()-1 + @if !isRoad(tri$flag(t,2)) + @def status = tri$remove(t) + @endif +@endfor + +@echo " - " mod_count " of " tri$n() " triangles lowered in " (mSec()-start) " msec." + diff --git a/project/scripts/kcl/remove-face-down-roads.txt b/project/scripts/kcl/remove-face-down-roads.txt new file mode 100644 index 0000000..e21bddf --- /dev/null +++ b/project/scripts/kcl/remove-face-down-roads.txt @@ -0,0 +1,20 @@ + +#--- remove face down roads + +@def start = mSec() # start time, for the status line +@def rm_count = 0 # reset status counter + +@for t=0; tri$n()-1 # for each tirangle (index 0 based) + @pdef type = tri$flag(t) & 0x1f # get the KCL type + @if kcl$drive & (1<&2 + continue + fi + + #--- extract ext + + ext="${src##*.}" + if [[ $ext = $src ]] + then + dest="$src" + ext="" + else + dest="${src%.*}" + ext=".$ext" + fi + + d=${dest%_d} + if [[ "$d" != "$dest" ]] + then + dest="$d" + ext="_d${ext}" + fi + + dest="$(add_destname "$dest" 1 minimap)" + + + #--- trim dest + + dest="$( trim "$dest" )$ext" + + if ((plusmode)) + then + idx="${dest:0:2}" + idx="${idx/[^0-9]/}" + let idx=10#0$idx + if (( idx % 10 < 4 )) + then + let idx=idx+1 + else + let idx=idx+7 + fi + dest="$idx ${dest:3}" + #echo "idx=$idx" + fi + + temp="$dest.temp" + echo "DEST=|$dest|" + + + #--- create temp, fix minimap, rename track + + ln -f "$src" "$temp" + wszst -q minimap -r --auto "$temp" $testmode + if ! diff -q "$src" "$temp" >/dev/null + then + ((plusmode)) || rm -f "$src" + mv "$temp" "$dest" + if ((kclmode)) + then + kcldest="$(add_destname "$dest" 0 kcl)" + echo "KCLDEST=|$kcldest|" + cp -p "$dest" "$kcldest" + wszst norm "$kcldest" --kcl new,drop + if diff -q "$dest" "$kcldest" >/dev/null + then + rm "$kcldest" + else + rm "$dest" + fi + fi + fi + + rm -f "$temp" + +done + diff --git a/project/scripts/patch-all-messages.sh b/project/scripts/patch-all-messages.sh new file mode 100644 index 0000000..845260c --- /dev/null +++ b/project/scripts/patch-all-messages.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +#--- pre definitions: enter your local settings + +# This is the directory with all message files (/Scene/UI) +SRC_UI_DIR="./ui" + +# and this is the destination directory. If it is the same as the source +# than all files are replaced by the patched version. +DEST_UI_DIR="$SRC_UI_DIR" + +# This is the path to the message file of above: +PATCH_FILE="bmg-patch.txt" + +# Remove the the hash ('#') of the second line for fast operation. +# This makes the file about 25-30% larger but the operation is much faster +FAST= +#FAST=--fast + +#--- more automated setup + +TEMP="$(mktemp -d)" || exit 1 + +#--- patch it in 3 steps: extract szs + patch bmg + create new szs + +wszst extract --quiet "$SRC_UI_DIR/"*_?.szs --dest "$TEMP/%N.d" +wbmgt patch --quiet "$TEMP/"*.d/message/*.bmg --patch "replace,$PATCH_FILE" +wszst create --quiet --remove "$TEMP/"*.d --dest "$DEST_UI_DIR" $FAST + +#--- clean up (remove temp dir) + +rm -rf "$TEMP" diff --git a/project/scripts/test-wszst.sh b/project/scripts/test-wszst.sh new file mode 100755 index 0000000..ca4a22e --- /dev/null +++ b/project/scripts/test-wszst.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# (c) Wiimm, 2012-11-11 + +export LC_ALL=POSIX + +SZS=wszst +[[ -x ./wszst ]] && SZS=./wszst +SZS0="$SZS -q" +SZS="$SZS0" +echo "SZS=$SZS" + +BMG=wbmgt +[[ -x ./wbmgt ]] && BMG=./wbmgt +BMG0="$BMG -q" +BMG="$BMG0" +echo "BMG=$BMG" + +if [[ $1 = -i || $1 = --ignore ]] +then + DIFF="diff -q -x wszst-setup.txt -x .string-pool.* -x .*.d" + echo "DIFF=$DIFF" + printf "\n!!! WARNING: '.string-pool.*' excluded !!!\n\n" + shift +else + DIFF="diff -q -x wszst-setup.txt" + echo "DIFF=$DIFF" +fi + +function error() +{ + echo "!! Test failed: $*" >&2 + exit 1 +} + +let count=0 +for src in "$@" +do + if [[ $src = -n || $src = --new ]] + then + echo ">> enabled: --NEW" + SZS="$SZS0 --NEW" + BMG="$BMG0 --NEW" + continue + fi + + if [[ $src = -o || $src = --old ]] + then + echo ">> enabled: --OLD" + SZS="$SZS0 --OLD" + BMG="$BMG0 --OLD" + continue + fi + + let count++ + printf "%5u. %s\n" $count "$src" + temp="$(mktemp -ut test-wszst-XXXXXXXX).tmp" || exit 1 + #echo "TEMP=$temp" + + $SZS decom "$src" -D "$temp/11.u8" || error 11 decompress + $SZS compr "$temp/11.u8" -D "$temp/12.szs" || error 12 compress + $SZS decom "$temp/12.szs" -D "$temp/13.u8" || error 13 decompress + $DIFF "$temp/11.u8" "$temp/13.u8" || error raw diff 11.u8 13.u8 + + $SZS extr "$src" -D "$temp/21.d" || error 21 extract + $SZS extr "$temp/11.u8" -D "$temp/22.d" || error 22 extract + $DIFF -r "$temp/21.d" "$temp/22.d" || error raw diff 21.d 22.d + + $SZS create "$temp/22.d" -D "$temp/23.u8" --noc || error 23 create + $SZS diff "$src" "$temp/23.u8" || error file diff src 23 + $SZS extr "$temp/23.u8" -D "$temp/24.d" || error 24 extract + $DIFF -r "$temp/21.d" "$temp/24.d" || error raw diff 21 24 + + $SZS extr "$src" -D "$temp/31.d" -a --no-check || error 31 extract --all + $SZS create "$temp/31.d" -D "$temp/32.u8" --noc || error 32 create + $SZS extr "$temp/32.u8" -D "$temp/33.d" -a --no-check || error 33 extract --all + $DIFF -r "$temp/31.d" "$temp/33.d" || error raw diff 31 33 + + $SZS compr "$src" -D "$temp/41.wbz" --wbz || error 41 to wbz + $SZS compr "$temp/41.wbz" -D "$temp/42.szs" --szs || error 42 to szs + $SZS diff "$src" "$temp/42.szs" || error file diff 41 42 + + find "$temp/24.d" -name '*.bmg' | while read f + do + [[ -f $f ]] || continue + echo " - ${f/*.d}" + + $BMG encode "$f" -d "$f.1.bmg" || error encode "$f" + $DIFF "$f" "$f.1.bmg" || error raw diff src 1 + + $BMG decode "$f" -d "$f.2.txt" || error decode "$f" + $BMG encode "$f.2.txt" -d "$f.3.bmg" || error encode "$f" + $DIFF "$f" "$f.3.bmg" || error raw diff src 3 + + done || exit 1 + rm -rf "$temp" + +done +echo "**OK**" + diff --git a/project/scripts/test-wu8.sh b/project/scripts/test-wu8.sh new file mode 100644 index 0000000..e3da22b --- /dev/null +++ b/project/scripts/test-wu8.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# (c) Wiimm, 2012-10-26 + +export LC_ALL=POSIX +szsmode=0 +logfile= + +SZS=wszst +[[ -x ./wszst ]] && SZS=./wszst +SZS0="$SZS -q" +SZS="$SZS0" +echo "SZS=$SZS" + +DIFF="diff -q" +echo "DIFF=$DIFF" + +HAVE_DCC=0 +which -q dcc &>/dev/null && HAVE_DCC=1 + +function error() +{ + echo "!! Test failed: $*" >&2 + exit 1 +} + +let count=0 +for src in "$@" +do + if [[ $src = -n || $src = --new ]] + then + echo ">> enabled: --NEW" + SZS="$SZS0 --NEW" + continue + fi + + if [[ $src = -o || $src = --old ]] + then + echo ">> enabled: --OLD" + SZS="$SZS0 --OLD" + continue + fi + + if [[ $src = -s || $src = --szs ]] + then + echo ">> enabled: SZS mode" + szsmode=1 + continue + fi + + if [[ $src = -l || $src = --log ]] + then + szsmode=1 + logfile=./wu8-size.log + echo ">> SZS mode enabled, log to: $logfile" + { + echo + date +'--- %F %T ---' + echo + } >>"$logfile" + continue + fi + + let count++ + printf "%5u. %s\n" $count "$src" + temp="$(mktemp -ut test-wu8-XXXXXXXX).tmp" || exit 1 + #echo "TEMP=$temp" + + $SZS decompress "$src" -D "$temp/11.u8" || error 11 decompress + $SZS norm "$temp/11.u8" || error 11 norm + $SZS norm --wu8 "$temp/11.u8" -D "$temp/12.wu8" || error 12 decode WU8 + $SZS norm --u8 "$temp/12.wu8" -D "$temp/13.u8" || error 13 encode WU8 + $DIFF "$temp/11.u8" "$temp/13.u8" || error raw diff 11.u8 13.u8 + + if ((szsmode)) + then + $SZS norm "$src" -D "$temp/21.szs" || error 11 norm + $SZS norm --wu8 "$temp/21.szs" -D "$temp/22.wu8" || error 12 decode WU8 + $SZS norm --u8 "$temp/22.wu8" -D "$temp/23.szs" || error 13 encode WU8 + $DIFF "$temp/21.szs" "$temp/23.szs" || error raw diff 21.szs 23.szs + if [[ $logfile != "" ]] + then + s1=$(stat -c%s "$temp/21.szs") + s2=$(stat -c%s "$temp/22.wu8") + if ((HAVE_DCC)) + then + printf "%8u %8u %6.2f%% %s\n" $s1 $s2 $(dcc "100.0*$s2/$s1") "$src" >>"$logfile" + else + printf "%8u %8u %3d%% %s\n" $s1 $s2 $((100*s2/s1)) "$src" >>"$logfile" + fi + fi + fi + + rm -rf "$temp" +done +echo "**OK**" + diff --git a/project/setup.sh b/project/setup.sh new file mode 100644 index 0000000..205ec80 --- /dev/null +++ b/project/setup.sh @@ -0,0 +1,196 @@ +#!/usr/bin/env bash + +revision=0 +[[ $STATIC = 1 ]] || STATIC=0 + +[[ -s revision.sh ]] && . revision.sh + +if [[ -d .svn ]] && which svn >/dev/null 2>&1 +then + revision="$( svn info . | awk '$1=="Revision:" {print $2}' )" + if which svnversion >/dev/null 2>&1 + then + rev="$(svnversion|sed 's/.*://')" + (( ${revision//[!0-9]/} < ${rev//[!0-9]/} )) && revision=$rev + fi + echo "revision=$revision" > revision.sh +fi + +revision_num="${revision//[!0-9]/}" +revision_next=$revision_num +[[ $revision = $revision_num ]] || let revision_next++ + +tim=($(date '+%s %Y-%m-%d %T %Y')) + +have_fuse=0 +[[ $NO_FUSE != 1 && -r /usr/include/fuse.h || -r /usr/local/include/fuse.h ]] \ + && have_fuse=1 + +#--- + +if [[ $M32 = 1 ]] +then + force_m32=1 + libdir=lib + have_fuse=0 + xflags="-m32" + defines=-DFORCE_M32=1 +else + force_m32=0 + libdir=lib64 + xflags= + defines= +fi + +[[ -r /usr/include/bits/fcntl.h ]] \ + && grep -qw fallocate /usr/include/bits/fcntl.h \ + && defines="$defines -DHAVE_FALLOCATE=1" + +[[ -r /usr/include/fcntl.h ]] \ + && grep -qw posix_fallocate /usr/include/fcntl.h \ + && defines="$defines -DHAVE_POSIX_FALLOCATE=1" + +gcc $xflags system.c -o system.tmp && ./system.tmp >Makefile.setup +rm -f system.tmp +SYSTEM=$(awk '$1=="SYSTEM" {print $3}' Makefile.setup) +#echo "SYSTEM=|$SYSTEM|" >&2 + +libpng="-lpng -lz" +case "$SYSTEM" in + mac) + [[ -s /usr/local/lib/libpng.a ]] && libpng="/usr/local/lib/libpng.a -lz" + ;; + + i386) + [[ -s /usr/lib/libpng.a ]] && libpng="/usr/lib/libpng.a -lz" + ;; + + x86_64) + [[ -s /usr/lib64/libpng.a ]] && libpng="/usr/lib64/libpng.a -lz" + ;; +esac + +#----- have_pcre + +have_pcre=0 +pcre_a= +[[ -r /usr/include/pcre.h || -r /usr/local/include/pcre.h ]] && have_pcre=1 + +if (( STATIC && have_pcre )) +then + have_pcre=0 + for lib in /usr/local/$libdir /usr/$libdir /$libdir + do + if [[ -s $lib/libpcre.a ]] + then + have_pcre=1 + pcre_a=$lib/libpcre.a + break; + fi + done +fi +have_pcre=0 + +#-------------------------------------------------- + +INSTALL_PATH=/usr/local + +if [[ -d $INSTALL_PATH/bin ]] +then + HAVE_INSTBIN=1 + INSTBIN=$INSTALL_PATH/bin +else + HAVE_INSTBIN=0 + INSTBIN=/tmp +fi + +if [[ -d $INSTALL_PATH/bin32 ]] +then + HAVE_INSTBIN_32=1 + INSTBIN_32=$INSTALL_PATH/bin32 +else + HAVE_INSTBIN_32=0 + INSTBIN_32=/tmp +fi + +if [[ -d $INSTALL_PATH/bin64 ]] +then + HAVE_INSTBIN_64=1 + INSTBIN_64=$INSTALL_PATH/bin64 +elif [[ -d $INSTALL_PATH/bin-x86_64 ]] +then + HAVE_INSTBIN_64=1 + INSTBIN_64=$INSTALL_PATH/bin-x86_64 +else + HAVE_INSTBIN_64=0 + INSTBIN_64=/tmp +fi + +HAVE_WORK=0 +[[ -d ./work ]] && HAVE_WORK=1 + +HAVE_WORK_SRC=0 +[[ -d ./work/src ]] && HAVE_WORK_SRC=1 + +[[ $NOWIIMM = 1 || $NO_WIIMM = 1 ]] && HAVE_WORK=0 HAVE_WORK_SRC=0 + +HAVE_XSRC=0 +[[ -h xsrc ]] && rm xsrc +if [[ -d xsrc ]] +then + HAVE_XSRC=1 + cat <<- __EOT__ >xsrc/xversion.tmp + #ifndef SZS_X_VERSION_H + #define SZS_X_VERSION_H 1 + + #define XSRC_BASE_VERSION "$1" + #define XSRC_VERSION_NUM "$2" + #define XSRC_VERSION_INT $3 + + #endif // SZS_X_VERSION_H + __EOT__ + diff -N xsrc/xversion.tmp xsrc/xversion.h >/dev/null \ + || mv xsrc/xversion.tmp xsrc/xversion.h +elif [[ ! -a xsrc && -d ../wiimms-szs-tools/xsrc ]] +then + ln -s ../wiimms-szs-tools/xsrc ./xsrc + HAVE_XSRC=1 +fi + +#-------------------------------------------------- + +cat <<- ---EOT--- >>Makefile.setup + + REVISION := $revision + REVISION_NUM := $revision_num + REVISION_NEXT := $revision_next + BINTIME := ${tim[0]} + DATE := ${tim[1]} + TIME := ${tim[2]} + YEAR := ${tim[3]} + + FORCE_M32 := $force_m32 + HAVE_FUSE := $have_fuse + STATIC := $STATIC + XFLAGS += $xflags + DEFINES1 := $defines -DDEBUG_ASSERT=1 + LIBPNG := $libpng + + HAVE_PCRE := $have_pcre + PCRE_A := $pcre_a + + HAVE_INSTBIN := $HAVE_INSTBIN + HAVE_INSTBIN_32 := $HAVE_INSTBIN_32 + HAVE_INSTBIN_64 := $HAVE_INSTBIN_64 + + INSTALL_PATH := $INSTALL_PATH + INSTBIN := $INSTBIN + INSTBIN_32 := $INSTBIN_32 + INSTBIN_64 := $INSTBIN_64 + + HAVE_WORK := $HAVE_WORK + HAVE_WORK_SRC := $HAVE_WORK_SRC + HAVE_XSRC := $HAVE_XSRC + + ---EOT--- + diff --git a/project/setup/INSTALL.txt b/project/setup/INSTALL.txt new file mode 100644 index 0000000..f405b77 --- /dev/null +++ b/project/setup/INSTALL.txt @@ -0,0 +1,50 @@ + + ##################################################################### + ## _______ _______ _______ ## + ## | ___ |____ | ___ | ## + ## | | |_| / /| | |_| ## + ## | |_____ / / | |_____ ## + ## |_____ | / / |_____ | ## + ## _ | | / / _ | | ## + ## | |___| |/ /____| |___| | ## + ## |_______|_______|_______| ## + ## ## + ## Wiimms SZS Tools ## + ## https://szs.wiimm.de/ ## + ## ## + ##################################################################### + ## ## + ## This file is part of the SZS project. ## + ## Visit https://szs.wiimm.de/ for project details and sources. ## + ## ## + ## Copyright (c) 2011-2013 by Dirk Clemens ## + ## ## + ##################################################################### + + + +How to install Wiimms SZS Tools +=============================== + + Windows: + -------- + + Start: windows-install.exe + After the very first installation of the tools, a reboot is needed. + + + Mac, Linux, Unix: + ----------------- + + First open a console terminal, an admin terminal is the best. + + Then change to the directory, where you have unpacked the distribution. + Therefore use the command: cd DIRECTORY + ... where 'DIRECTORY' is the directory path. + + Call: ./install.sh + + If the script is not started as 'root', the script call itself with 'sudo'. + Depending on your 'sudo' settings, 'sudo' asks you for the root passwort. + To avoid the sudo handling, start the script with: ./install.sh --no-sudo + diff --git a/project/setup/cygwin-copy.sh b/project/setup/cygwin-copy.sh new file mode 100755 index 0000000..3ce6c46 --- /dev/null +++ b/project/setup/cygwin-copy.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + + ##################################################################### + ## _______ _______ _______ ## + ## | ___ |____ | ___ | ## + ## | | |_| / /| | |_| ## + ## | |_____ / / | |_____ ## + ## |_____ | / / |_____ | ## + ## _ | | / / _ | | ## + ## | |___| |/ /____| |___| | ## + ## |_______|_______|_______| ## + ## ## + ## Wiimms SZS Tools ## + ## http://szs.wiimm.de/ ## + ## ## + ##################################################################### + ## ## + ## This file is part of the SZS project. ## + ## Visit http://szs.wiimm.de/ for project details and sources. ## + ## ## + ## Copyright (c) 2011-2019 by Dirk Clemens ## + ## ## + ##################################################################### + +#------------------------------------------------------------------------------ +# setup + +CYGWIN_DIR="@@CYGWIN-DIR@@" +CYGWIN_TOOLS="@@CYGWIN-TOOLS@@" +BIN_FILES="@@BIN-FILES@@" +DISTRIB_PATH="@@DISTRIB-PATH@@" + +BIN_PATH="$DISTRIB_PATH/bin" +mkdir -p "$BIN_PATH" || exit 1 + +#------------------------------------------------------------------------------ +# copy cygwin tools + +for tool in $CYGWIN_TOOLS +do + cp --preserve=time "$CYGWIN_DIR/$tool.exe" "$BIN_PATH" || exit 1 +done + +#------------------------------------------------------------------------------ +# copy wit tools + +for tool in $BIN_FILES +do + if [[ -s ./$tool.exe ]] + then + CYGWIN_TOOLS="$CYGWIN_TOOLS $tool" + ln -f ./$tool.exe "$BIN_PATH" || exit 1 + fi +done + +#------------------------------------------------------------------------------ +# copy needed cygwin dlls + +for tool in $CYGWIN_TOOLS +do + ldd "$BIN_PATH/$tool.exe" | grep -F "=> $CYGWIN_DIR/" | awk '{print $1}' +done | sort | uniq | while read dll +do + cp --preserve=time "$CYGWIN_DIR/$dll" "$BIN_PATH" || exit 1 +done + +#------------------------------------------------------------------------------ +# done + +exit 0 + diff --git a/project/setup/install.sh b/project/setup/install.sh new file mode 100755 index 0000000..d39e8f9 --- /dev/null +++ b/project/setup/install.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + + ##################################################################### + ## _______ _______ _______ ## + ## | ___ |____ | ___ | ## + ## | | |_| / /| | |_| ## + ## | |_____ / / | |_____ ## + ## |_____ | / / |_____ | ## + ## _ | | / / _ | | ## + ## | |___| |/ /____| |___| | ## + ## |_______|_______|_______| ## + ## ## + ## Wiimms SZS Tools ## + ## http://szs.wiimm.de/ ## + ## ## + ##################################################################### + ## ## + ## This file is part of the SZS project. ## + ## Visit http://szs.wiimm.de/ for project details and sources. ## + ## ## + ## Copyright (c) 2011-2019 by Dirk Clemens ## + ## ## + ##################################################################### + +#------------------------------------------------------------------------------ +# sudo? + +try_sudo=1 +if [[ $1 = --no-sudo ]] +then + try_sudo=0 + shift +else + uid="$(id -u 2>/dev/null)" || try_sudo=0 + [[ $uid = 0 ]] && try_sudo=0 +fi + +if ((try_sudo)) +then + echo "*** need root privileges to install => try sudo ***" + sudo "$0" --no-sudo "$@" + exit $? +fi + +#------------------------------------------------------------------------------ +# settings + +BASE_PATH="@@INSTALL-PATH@@" +BIN_PATH="$BASE_PATH/bin" +SHARE_PATH="$BASE_PATH/share/szs" + +BIN_FILES="@@BIN-FILES@@" +SHARE_FILES="@@SHARE-FILES@@" + +INST_FLAGS="-p" + +#------------------------------------------------------------------------------ +# make? + +make=0 +if [[ $1 = --make ]] +then + # it's called from make + make=1 + shift +fi + +#------------------------------------------------------------------------------ +echo "*** install binaries to $BIN_PATH" + +for f in $BIN_FILES +do + [[ -f bin/$f ]] || continue + mkdir -p "$BIN_PATH" + install $INST_FLAGS "bin/$f" "$BIN_PATH/$f" +done + +#------------------------------------------------------------------------------ +echo "*** install share files to $SHARE_PATH" + +mkdir -p "$SHARE_PATH/auto-add/effect" +chmod 775 "$SHARE_PATH/auto-add" "$SHARE_PATH/auto-add/effect" + +for f in $SHARE_FILES +do + install $INST_FLAGS -m 644 share/$f "$SHARE_PATH/$f" +done + +#------------------------------------------------------------------------------ + +exit 0 + diff --git a/project/setup/sendto-install.bat b/project/setup/sendto-install.bat new file mode 100644 index 0000000..c718ea6 --- /dev/null +++ b/project/setup/sendto-install.bat @@ -0,0 +1,2 @@ +@echo off +.\windows-install.exe --script sendto-install.sh diff --git a/project/setup/sendto-install.sh b/project/setup/sendto-install.sh new file mode 100644 index 0000000..abbdf72 --- /dev/null +++ b/project/setup/sendto-install.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + + ##################################################################### + ## _______ _______ _______ ## + ## | ___ |____ | ___ | ## + ## | | |_| / /| | |_| ## + ## | |_____ / / | |_____ ## + ## |_____ | / / |_____ | ## + ## _ | | / / _ | | ## + ## | |___| |/ /____| |___| | ## + ## |_______|_______|_______| ## + ## ## + ## Wiimms SZS Tools ## + ## http://szs.wiimm.de/ ## + ## ## + ##################################################################### + ## ## + ## This file is part of the SZS project. ## + ## Visit http://szs.wiimm.de/ for project details and sources. ## + ## ## + ## Copyright (c) 2011-2019 by Dirk Clemens ## + ## ## + ##################################################################### + +#------------------------------------------------------------------------------ +# simple cygwin check + +if [[ $1 != --cygwin ]] +then + echo "Option --cygwin not set => exit" >&2 + exit 1 +fi + +#------------------------------------------------------------------------------ +# pre definitions + +WIN_INSTALL_PATH="Wiimm/SZS" + +#------------------------------------------------------------------------------ +echo "* setup" + +export PATH=".:$PATH" +mkdir -p /tmp + +#--------------- + +SENDTO_SRC=./windows-sendto +if [[ ! -d $SENDTO_SRC ]] +then + SENDTO_SRC="$WIN_INSTALL_PATH/windows-sendto" + if [[ ! -d $SENDTO_SRC ]] + then + echo "Can't find SendTo source => abort" >&2 + exit 1 + fi +fi + +#--------------- + +key="/user/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/SendTo" +if ! WIN_SENDTO_PATH="$(regtool get "$key")" || [[ $WIN_SENDTO_PATH = "" ]] +then + echo "Can't determine Windows program path => abort" >&2 + exit 1 +fi +CYGWIN_SENDTO_PATH="${WIN_SENDTO_PATH//\\//}" + + +#------------------------------------------------------------------------------ +echo "* install SendTo scripts" +echo " - SendTo source: $SENDTO_SRC" +echo " - SendTo folder: $WIN_SENDTO_PATH" +echo " - ->: $CYGWIN_SENDTO_PATH" + +cp -p "$SENDTO_SRC"/* "$CYGWIN_SENDTO_PATH/" + +#------------------------------------------------------------------------------ + +exit 0 + diff --git a/project/setup/sendto-uninstall.bat b/project/setup/sendto-uninstall.bat new file mode 100644 index 0000000..b41f217 --- /dev/null +++ b/project/setup/sendto-uninstall.bat @@ -0,0 +1,2 @@ +@echo off +.\windows-install.exe --script sendto-uninstall.sh diff --git a/project/setup/sendto-uninstall.sh b/project/setup/sendto-uninstall.sh new file mode 100644 index 0000000..093daea --- /dev/null +++ b/project/setup/sendto-uninstall.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + + ##################################################################### + ## _______ _______ _______ ## + ## | ___ |____ | ___ | ## + ## | | |_| / /| | |_| ## + ## | |_____ / / | |_____ ## + ## |_____ | / / |_____ | ## + ## _ | | / / _ | | ## + ## | |___| |/ /____| |___| | ## + ## |_______|_______|_______| ## + ## ## + ## Wiimms SZS Tools ## + ## http://szs.wiimm.de/ ## + ## ## + ##################################################################### + ## ## + ## This file is part of the SZS project. ## + ## Visit http://szs.wiimm.de/ for project details and sources. ## + ## ## + ## Copyright (c) 2011-2019 by Dirk Clemens ## + ## ## + ##################################################################### + +#------------------------------------------------------------------------------ +# simple cygwin check + +if [[ $1 != --cygwin ]] +then + echo "Option --cygwin not set => exit" >&2 + exit 1 +fi + +#------------------------------------------------------------------------------ +# pre definitions + +WIN_INSTALL_PATH="Wiimm/SZS" + +#------------------------------------------------------------------------------ +echo "* setup" + +export PATH=".:$PATH" + +#--------------- + +SENDTO_SRC=./windows-sendto +if [[ ! -d $SENDTO_SRC ]] +then + SENDTO_SRC="$WIN_INSTALL_PATH/windows-sendto" + if [[ ! -d $SENDTO_SRC ]] + then + echo "Can't find SendTo source => abort" >&2 + exit 1 + fi +fi + +#--------------- + +key="/user/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/SendTo" +if ! WIN_SENDTO_PATH="$(regtool get "$key")" || [[ $WIN_SENDTO_PATH = "" ]] +then + echo "Can't determine Windows program path => abort" >&2 + exit 1 +fi +CYGWIN_SENDTO_PATH="${WIN_SENDTO_PATH//\\//}" + +#------------------------------------------------------------------------------ +echo "* uninstall SendTo scripts" +echo " - SendTo folder: $WIN_SENDTO_PATH" +echo " - ->: $CYGWIN_SENDTO_PATH" + +for f in "$SENDTO_SRC"/* +do + [[ -f $f ]] || continue; + f="${f##*/}" + echo "remove $f" + rm -f "$CYGWIN_SENDTO_PATH/$f" +done + +#------------------------------------------------------------------------------ + +exit 0 + diff --git a/project/setup/szs.def b/project/setup/szs.def new file mode 100644 index 0000000..d4f177e --- /dev/null +++ b/project/setup/szs.def @@ -0,0 +1,47 @@ + +//---------------------------------" +// This file is generated by make." +// Any changes will be lost!" +//---------------------------------" + +#gdef szs$ = @map + +#gdef szs$['TOOLSET_SHORT'] = "@@TOOLSET-SHORT@@" +#gdef szs$['TOOLSET_LONG'] = "@@TOOLSET-LONG@@" +#gdef szs$['WSZST_SHORT'] = "@@WSZST-SHORT@@" +#gdef szs$['WSZST_LONG'] = "@@WSZST-LONG@@" +#gdef szs$['WBMGT_SHORT'] = "@@WBMGT-SHORT@@" +#gdef szs$['WBMGT_LONG'] = "@@WBMGT-LONG@@" +#gdef szs$['WCTCT_SHORT'] = "@@WCTCT-SHORT@@" +#gdef szs$['WCTCT_LONG'] = "@@WCTCT-LONG@@" +#gdef szs$['WIMGT_SHORT'] = "@@WIMGT-SHORT@@" +#gdef szs$['WIMGT_LONG'] = "@@WIMGT-LONG@@" +#gdef szs$['WKCLT_SHORT'] = "@@WKCLT-SHORT@@" +#gdef szs$['WKCLT_LONG'] = "@@WKCLT-LONG@@" +#gdef szs$['WKMPT_SHORT'] = "@@WKMPT-SHORT@@" +#gdef szs$['WKMPT_LONG'] = "@@WKMPT-LONG@@" +#gdef szs$['WLECT_SHORT'] = "@@WLECT-SHORT@@" +#gdef szs$['WLECT_LONG'] = "@@WLECT-LONG@@" +#gdef szs$['WMDLT_SHORT'] = "@@WMDLT-SHORT@@" +#gdef szs$['WMDLT_LONG'] = "@@WMDLT-LONG@@" +#gdef szs$['WPATT_SHORT'] = "@@WPATT-SHORT@@" +#gdef szs$['WPATT_LONG'] = "@@WPATT-LONG@@" +#gdef szs$['WSTRT_SHORT'] = "@@WSTRT-SHORT@@" +#gdef szs$['WSTRT_LONG'] = "@@WSTRT-LONG@@" + +#gdef szs$['AUTHOR'] = "@@AUTHOR@@" + +#gdef szs$['VERSION'] = "@@VERSION@@" +#gdef szs$['VERSION_NUM'] = "@@VERSION-NUM@@" +#gdef szs$['BETA_VERSION'] = "@@BETA-VERSION@@" +#gdef szs$['BETA_SUFFIX'] = "@@BETA-SUFFIX@@" + +#gdef szs$['REVISION'] = "@@REV@@" +#gdef szs$['REVISION_NUM'] = @@REV-NUM@@ +#gdef szs$['REVISION_NEXT'] = @@REV-NEXT@@ + +#gdef szs$['BINTIME'] = @@BINTIME@@ +#gdef szs$['DATE'] = "@@DATE@@" +#gdef szs$['TIME'] = "@@TIME@@" +#gdef szs$['DATETIME'] = "@@DATE@@ @@TIME@@" + diff --git a/project/setup/version.h b/project/setup/version.h new file mode 100644 index 0000000..61a799e --- /dev/null +++ b/project/setup/version.h @@ -0,0 +1,59 @@ + +//---------------------------------" +// This file is generated by make." +// Any changes will be lost!" +//---------------------------------" + +#define TOOLSET_SHORT "@@TOOLSET-SHORT@@" +#define TOOLSET_LONG "@@TOOLSET-LONG@@" +#define WSZST_SHORT "@@WSZST-SHORT@@" +#define WSZST_LONG "@@WSZST-LONG@@" +#define WBMGT_SHORT "@@WBMGT-SHORT@@" +#define WBMGT_LONG "@@WBMGT-LONG@@" +#define WCTCT_SHORT "@@WCTCT-SHORT@@" +#define WCTCT_LONG "@@WCTCT-LONG@@" +#define WIMGT_SHORT "@@WIMGT-SHORT@@" +#define WIMGT_LONG "@@WIMGT-LONG@@" +#define WKCLT_SHORT "@@WKCLT-SHORT@@" +#define WKCLT_LONG "@@WKCLT-LONG@@" +#define WKMPT_SHORT "@@WKMPT-SHORT@@" +#define WKMPT_LONG "@@WKMPT-LONG@@" +#define WLECT_SHORT "@@WLECT-SHORT@@" +#define WLECT_LONG "@@WLECT-LONG@@" +#define WMDLT_SHORT "@@WMDLT-SHORT@@" +#define WMDLT_LONG "@@WMDLT-LONG@@" +#define WPATT_SHORT "@@WPATT-SHORT@@" +#define WPATT_LONG "@@WPATT-LONG@@" +#define WSTRT_SHORT "@@WSTRT-SHORT@@" +#define WSTRT_LONG "@@WSTRT-LONG@@" + +#define AUTHOR "@@AUTHOR@@" + +#define BASE_VERSION "@@VERSION@@" +#define VERSION_NUM "@@VERSION-NUM@@" +#define VERSION_INT @@VERSION-INT@@ +#define BETA_VERSION @@BETA-VERSION@@ + +#if HAVE_WIIMM_EXT + #define VERSION "@@VERSION@@.w" +#else + #define VERSION "@@VERSION@@" +#endif + +#define REVISION "@@REV@@" +#define REVISION_NUM @@REV-NUM@@ +#define REVISION_NEXT @@REV-NEXT@@ + +#define BINTIME @@BINTIME@@ +#define DATE "@@DATE@@" +#define TIME "@@TIME@@" +#define DATETIME "@@DATE@@ @@TIME@@" +#define YEAR @@YEAR@@ + +#define URI_REPOS "@@URI-REPOS@@/" +#define URI_VIEWVC "@@URI-VIEWVC@@/" +#define URI_HOME "@@URI-HOME@@" +#define URI_DOWNLOAD "@@URI-DOWNLOAD@@" + +#define HAVE_XSRC @@HAVE-XSRC@@ + diff --git a/project/setup/windows-install.exe b/project/setup/windows-install.exe new file mode 100644 index 0000000..a7add8b Binary files /dev/null and b/project/setup/windows-install.exe differ diff --git a/project/setup/windows-install.sh b/project/setup/windows-install.sh new file mode 100755 index 0000000..4dbd1bf --- /dev/null +++ b/project/setup/windows-install.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash + + ##################################################################### + ## _______ _______ _______ ## + ## | ___ |____ | ___ | ## + ## | | |_| / /| | |_| ## + ## | |_____ / / | |_____ ## + ## |_____ | / / |_____ | ## + ## _ | | / / _ | | ## + ## | |___| |/ /____| |___| | ## + ## |_______|_______|_______| ## + ## ## + ## Wiimms SZS Tools ## + ## http://szs.wiimm.de/ ## + ## ## + ##################################################################### + ## ## + ## This file is part of the SZS project. ## + ## Visit http://szs.wiimm.de/ for project details and sources. ## + ## ## + ## Copyright (c) 2011-2019 by Dirk Clemens ## + ## ## + ##################################################################### + +#------------------------------------------------------------------------------ +# simple cygwin check + +if [[ $1 != --cygwin ]] +then + echo "Option --cygwin not set => exit" >&2 + exit 1 +fi + +#------------------------------------------------------------------------------ +# pre definitions + +BIN_FILES="wszst wbmgt wimgt wkclt wkmpt wstrt " +SHARE_FILES="" +WIN_INSTALL_PATH="Wiimm/SZS" + +#------------------------------------------------------------------------------ +# setup + +echo "* setup" + +export PATH=".:$PATH" +mkdir -p /tmp + +key="/machine/SOFTWARE/Microsoft/Windows/CurrentVersion/ProgramFilesDir" +if ! WIN_PROG_PATH="$(regtool get "$key")" || [[ $WIN_PROG_PATH = "" ]] +then + echo "Can't determine Windows program path => abort" >&2 + exit 1 +fi +#CYGWIN_PROG_PATH="$( realpath "$WIN_PROG_PATH" )" +CYGWIN_PROG_PATH="${WIN_PROG_PATH//\\//}" + +WDEST="$WIN_PROG_PATH\\${WIN_INSTALL_PATH//\//\\}" +CDEST="$CYGWIN_PROG_PATH/$WIN_INSTALL_PATH" + +#------------------------------------------------------------------------------ +# install files + +# mkdir before testing the directory +mkdir -p "$CDEST" "$CDEST/auto-add/effect" +chmod 666 "$CDEST/auto-add" "$CDEST/auto-add/effect" + +if [[ $(stat -c%d-%i "$PWD") != "$(stat -c%d-%i "$CDEST")" ]] +then + echo "* install files to $WDEST" + cp --preserve=time *.bat *.dll *.exe *.sh "$CDEST" +fi + +#------------------------------------------------------------------------------ +# define application pathes + +for tool in $BIN_FILES +do + [[ -s "$CDEST/$tool.exe" ]] || continue + echo "* define application path for '$tool.exe'" + key="/machine/SOFTWARE/Microsoft/Windows/CurrentVersion/App Paths/$tool.exe" + regtool add "$key" + regtool set -s "$key/" "${WDEST}\\${tool}.exe" + regtool set -s "$key/Path" "${WDEST}\\" +done + +#------------------------------------------------------------------------------ +# add WIT path to environment 'Path' + +echo "* add SZS path to environment 'Path'" + +function set_path() +{ + local key="$1" + + local p= + local count=0 + local new_path= + + # split at ';' & substitute ' ' temporary to ';' to be space save + for p in $( regtool --quiet get "$key" | tr '; ' '\n;' ) + do + p="${p//;/ }" + #echo " -> |$p|" + if [[ "$p" == "$WDEST" ]] + then + ((count++)) || new_path="$new_path;$p" + #echo "$count $WDEST" + elif [[ $p != "" ]] + then + new_path="$new_path;$p" + fi + done + + if [[ $new_path != "" ]] + then + ((count)) || new_path="$new_path;$WDEST" + #echo "count=$count" + #echo "new_path=${new_path:1}" + regtool set -e "$key" "${new_path:1}" + fi +} + +set_path '/machine/SYSTEM/CurrentControlSet/Control/Session Manager/Environment/Path' +set_path '/user/Environment/Path' + +#------------------------------------------------------------------------------ + diff --git a/project/setup/windows-sendto/adjust-minimap.bat b/project/setup/windows-sendto/adjust-minimap.bat new file mode 100644 index 0000000..2878b30 --- /dev/null +++ b/project/setup/windows-sendto/adjust-minimap.bat @@ -0,0 +1,3 @@ +@echo off +wszst normalize -v --minimap --number %1 +pause diff --git a/project/setup/windows-sendto/clean-KCL-of-SZS.bat b/project/setup/windows-sendto/clean-KCL-of-SZS.bat new file mode 100644 index 0000000..2764cbc --- /dev/null +++ b/project/setup/windows-sendto/clean-KCL-of-SZS.bat @@ -0,0 +1,3 @@ +@echo off +wszst normalize -v --kcl drop,new,log --number %1 +pause diff --git a/project/setup/windows-sendto/convert-to-SZS.bat b/project/setup/windows-sendto/convert-to-SZS.bat new file mode 100644 index 0000000..718c1df --- /dev/null +++ b/project/setup/windows-sendto/convert-to-SZS.bat @@ -0,0 +1,3 @@ +@echo off +wszst compress --szs --number %1 +pause diff --git a/project/setup/windows-sendto/convert-to-WBZ.bat b/project/setup/windows-sendto/convert-to-WBZ.bat new file mode 100644 index 0000000..1069a32 --- /dev/null +++ b/project/setup/windows-sendto/convert-to-WBZ.bat @@ -0,0 +1,3 @@ +@echo off +wszst compress --wbz --number %1 +pause diff --git a/project/setup/windows-sendto/create-autoadd-library.bat b/project/setup/windows-sendto/create-autoadd-library.bat new file mode 100644 index 0000000..3d2ffb5 --- /dev/null +++ b/project/setup/windows-sendto/create-autoadd-library.bat @@ -0,0 +1,3 @@ +@echo off +wszst autoadd %1 +pause diff --git a/project/setup/windows-sendto/normalize-SZS.bat b/project/setup/windows-sendto/normalize-SZS.bat new file mode 100644 index 0000000..63aacab --- /dev/null +++ b/project/setup/windows-sendto/normalize-SZS.bat @@ -0,0 +1,3 @@ +@echo off +wszst normalize -v --szs --number %1 +pause diff --git a/project/setup/windows-uninstall.bat b/project/setup/windows-uninstall.bat new file mode 100644 index 0000000..664d123 --- /dev/null +++ b/project/setup/windows-uninstall.bat @@ -0,0 +1,2 @@ +@echo off +.\windows-install.exe --script windows-uninstall.sh diff --git a/project/setup/windows-uninstall.sh b/project/setup/windows-uninstall.sh new file mode 100644 index 0000000..4a19c33 --- /dev/null +++ b/project/setup/windows-uninstall.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash + + ##################################################################### + ## _______ _______ _______ ## + ## | ___ |____ | ___ | ## + ## | | |_| / /| | |_| ## + ## | |_____ / / | |_____ ## + ## |_____ | / / |_____ | ## + ## _ | | / / _ | | ## + ## | |___| |/ /____| |___| | ## + ## |_______|_______|_______| ## + ## ## + ## Wiimms SZS Tools ## + ## http://szs.wiimm.de/ ## + ## ## + ##################################################################### + ## ## + ## This file is part of the SZS project. ## + ## Visit http://szs.wiimm.de/ for project details and sources. ## + ## ## + ## Copyright (c) 2011-2019 by Dirk Clemens ## + ## ## + ##################################################################### + +#------------------------------------------------------------------------------ +# simple cygwin check + +if [[ $1 != --cygwin ]] +then + echo "Option --cygwin not set => exit" >&2 + exit 1 +fi + +#------------------------------------------------------------------------------ +# pre definitions + +BIN_FILES="wszst wbmgt wimgt wkclt wkmpt wstrt " +SHARE_FILES="" +WIN_INSTALL_PATH="Wiimm/SZS" + +#------------------------------------------------------------------------------ +# setup + +echo "* setup" + +export PATH=".:$PATH" + +key="/machine/SOFTWARE/Microsoft/Windows/CurrentVersion/ProgramFilesDir" +if ! WIN_PROG_PATH="$(regtool get "$key")" || [[ $WIN_PROG_PATH = "" ]] +then + echo "Can't determine Windows program path => abort" >&2 + exit 1 +fi +#CYGWIN_PROG_PATH="$( realpath "$WIN_PROG_PATH" )" +CYGWIN_PROG_PATH="${WIN_PROG_PATH//\\//}" + +WDEST="$WIN_PROG_PATH\\${WIN_INSTALL_PATH//\//\\}" +CDEST="$CYGWIN_PROG_PATH/$WIN_INSTALL_PATH" + +#------------------------------------------------------------------------------ +# remove application pathes + +for tool in $BIN_FILES +do + key="/machine/SOFTWARE/Microsoft/Windows/CurrentVersion/App Paths/$tool.exe" + if regtool check "$key" >/dev/null 2>&1 + then + echo "* remove application path for '$tool.exe'" + regtool unset "$key/" "${WDEST}\\${tool}.exe" + regtool unset "$key/Path" "${WDEST}\\" + regtool remove "$key" + fi +done + +#------------------------------------------------------------------------------ +# add WIT path to environment 'Path' + +echo "* add SZS path to environment 'Path'" + +function set_path() +{ + local key="$1" + + local p= + local count=0 + local new_path= + + # split at ';' & substitute ' ' temporary to ';' to be space save + for p in $( regtool --quiet get "$key" | tr '; ' '\n;' ) + do + p="${p//;/ }" + #echo " -> |$p|" + [[ "$p" = "$WDEST" ]] || new_path="$new_path;$p" + done + + [[ $new_path = "" ]] || regtool set -e "$key" "${new_path:1}" +} + +set_path '/machine/SYSTEM/CurrentControlSet/Control/Session Manager/Environment/Path' +set_path '/user/Environment/Path' + +#------------------------------------------------------------------------------ + diff --git a/project/src/analyze.c b/project/src/analyze.c new file mode 100644 index 0000000..0c294b0 --- /dev/null +++ b/project/src/analyze.c @@ -0,0 +1,1361 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-brres.h" +#include "lib-breff.h" +#include "lib-image.h" +#include "db-kcl.h" +#include "lib-kcl.h" +#include "lib-kmp.h" +#include "lib-mdl.h" +#include "lib-pat.h" +#include "db-object.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// option support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + ANA_MD_DEFAULT = 0x0001, + ANA_MD_BRRES = 0x0002, + ANA_MD_MDL = 0x0004, + ANA_MD_PAT = 0x0008, + ANA_MD_TEX = 0x0010, + + ANA_MD_BREFF = 0x0100, + ANA_MD_BREFT = 0x0200, + ANA_MD_KCL = 0x0400, + ANA_MD_TPL = 0x0800, + + ANA_KMP_GOBJ = 0x1000, + ANA_KMP_KTPT = 0x2000, + ANA_MKWANA = 0x4000, + + ANA_M_BRRES = ANA_MD_BRRES | ANA_MD_MDL | ANA_MD_PAT | ANA_MD_TEX, + ANA_M_KMP = ANA_KMP_GOBJ | ANA_KMP_KTPT, + + ANA_M_ALL = 0xffff, +}; + +uint opt_analyze_mode = ANA_MD_DEFAULT; + +//----------------------------------------------------------------------------- + +int ScanOptAnalyzeMode ( ccp arg ) +{ + static const KeywordTab_t tab[] = + { + { 0, "NONE", "-", ANA_M_ALL }, + { ANA_M_ALL, "ALL", 0, 0 }, + + { ANA_MD_BRRES, "BRRES", "BRES", 0 }, + { ANA_MD_MDL, "MDL0", 0, 0 }, + { ANA_MD_PAT, "PAT0", 0, 0 }, + { ANA_MD_TEX, "TEX0", 0, 0 }, + + { ANA_MD_BREFF, "BREFF", "REFF", 0 }, + { ANA_MD_BREFT, "BREFT", "REFT", 0 }, + { ANA_MD_TPL, "TPL", 0, 0 }, + + { ANA_MD_KCL, "KCL", 0, 0 }, + + { ANA_M_KMP, "KMP", 0, 0 }, + { ANA_KMP_GOBJ, "GOBJ", 0, 0 }, + { ANA_KMP_KTPT, "KTPT", 0, 0 }, + + { ANA_MKWANA, "MKW_ANA", "MKWANA", 0 }, + + { 0,0,0,0 } + }; + + int stat = ScanKeywordList(arg,tab,0,true,0,0, + "Option --analyze-mode",ERR_SYNTAX); + if ( stat != -1 ) + { + opt_analyze_mode = stat; + return 0; + } + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// defines /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define DUMP_NULL_OFFSET 0 // dump null values +#define DUMP_INVALID_OFFSET 0 // dump offset, that point before string pool +#define DUMP_GROUP_POINTER 0 // dump group relative offets +#define DUMP_ENTRY_POINTER 0 // dump entry relative offets +#define DUMP_VERSION 1 // dump version info of entries + +#define ANA_SPEC_STRINGS 0 // enable special strings analysis +#define ANA_SPLT_SECT 0 // try to find split sections + +//--- enable special tests +#define ANA_MDL_11_0 1 // MDL vers 11 sect 0 +#define ANA_MDL_11_8 1 // MDL vers 11 sect 8 +#define ANA_MDL_11_8_0_VAL 1 // MDL vers 11 sect 8.0 +#define ANA_MDL_11_8_3_VAL 1 // MDL vers 11 sect 8.3 +#define ANA_TEX_HD 1 // TEX header + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// structs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct analyze_item_t +{ + u32 offset; // data offset + brres_group_t * grp; // pointer to group + brres_entry_t * entry; // pointer to entry + ccp entry_name; // pointer to entry string + ccp info; // short item info + + int index1; // index level 1 (grp/section index) + int index2; // index level 2 (entry index) + int index3; // index level 3 + uint brsub_mode; // brsub mode + +} analyze_item_t; + +/////////////////////////////////////////////////////////////////////////////// + +#define MAX_ANA_ITEM 10000 + +typedef struct analyze_t +{ + szs_file_t * szs; + u8 * data; + uint data_size; + + file_format_t fform; + ccp ff_name; + int version; + + analyze_item_t list[MAX_ANA_ITEM]; + uint used; + +} analyze_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AnalyzeBRSUB(), helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_analyze_item ( const void * va, const void * vb ) +{ + const analyze_item_t * a = va; + const analyze_item_t * b = vb; + + if ( a->offset < b->offset ) return -1; + if ( a->offset > b->offset ) return 1; + if ( a->index1 < b->index1 ) return -1; + if ( a->index1 > b->index1 ) return 1; + if ( a->index2 < b->index2 ) return -1; + if ( a->index2 > b->index2 ) return 1; + if ( a->index3 < b->index3 ) return -1; + if ( a->index3 > b->index3 ) return 1; + if ( a < b ) return -1; + if ( a > b ) return 1; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static analyze_item_t * add_item +( + analyze_t * ana, // analyse + ccp info, // short item info + brres_group_t * grp, // pointer to group + uint grp_index, // index of group/section + uint index3, // free second index + brres_entry_t * entry, // pointer to entry + u8 * data // pointer to entry related data +) +{ + DASSERT(ana); + if ( ana->used < MAX_ANA_ITEM ) + { + analyze_item_t * item = ana->list + ana->used++; + + item->offset = data - ana->data; + item->grp = grp; + item->entry = entry; + item->entry_name = "-"; + item->index1 = grp_index; + item->index2 = grp && entry ? entry - grp->entry : 0; + item->index3 = index3; + item->brsub_mode = BRSUB_MODE3(ana->fform,ana->version,grp_index); + item->info = info; + return item; + } + + ERROR0(ERR_FATAL,"Analyze list full (max=%u): %s\n",MAX_ANA_ITEM,ana->szs->fname); + exit(ERR_FATAL); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int collect_raw_data +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * data, // data pointer + u32 data_size // size of 'data' +) +{ + DASSERT(it); + analyze_t * ana = it->param; + DASSERT(ana); + + // additional data + add_item(ana,"raw data",0,-1,1,0,data); + + // ignore other + add_item(ana,"ignore",0,0xffff,0,0,data+data_size); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int collect_group_data +( + struct + brsub_iterator_t * it, // iterator struct with all infos + brres_group_t * grp, // pointer to group + uint grp_index // index of group +) +{ + DASSERT(it); + analyze_t * ana = it->param; + DASSERT(ana); + add_item(ana,"group",grp,0xffff,0,grp->entry,(u8*)grp); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// collect_entry_data() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int collect_entry_data +( + struct + brsub_iterator_t * it, // iterator struct with all infos + brres_group_t * grp, // pointer to group + uint grp_index, // index of group + brres_entry_t * entry, // pointer to entry + u8 * data // pointer to entry related data +) +{ + DASSERT(it); + analyze_t * ana = it->param; + DASSERT(ana); + + analyze_item_t * item = add_item(ana,"section",grp,grp_index,0,entry,data); + item->entry_name = GetStringBRRES(it->szs,grp, + it->endian->rd32(&entry->name_off),"?"); + + #if ANA_SPLT_SECT + + switch (BRSUB_MODE3(ana->fform,ana->version,grp_index)) + { + case BRSUB_MODE3(FF_MDL,11, 1): + case BRSUB_MODE3(FF_MDL,11, 2): + case BRSUB_MODE3(FF_MDL,11, 3): + case BRSUB_MODE3(FF_MDL,11, 4): + case BRSUB_MODE3(FF_MDL,11, 5): + case BRSUB_MODE3(FF_MDL,11, 9): + case BRSUB_MODE3(FF_MDL,11,10): + { + noPRINT(">>> %s.%u.%02u: %5x,%5x,%5x\n", + ana->ff_name, grp_index, (int)( entry - grp->entry ), + it->endian->rd32(data), + it->endian->rd32(data+0x08), + it->endian->rd32(data+0x0c)); + add_item(ana,"end of section",grp,grp_index,99,entry, + data + it->endian->rd32(data)); + } + break; + + case BRSUB_MODE3(FF_MDL,11,8): + { + u32 off = it->endian->rd32(data); + item = add_item(ana,"end of section",grp,grp_index,99,entry,data+off); + + uint n_lay = it->endian->rd32(data+0x2c); + off = it->endian->rd32(data+0x30); + if ( n_lay && off ) + { + uint i; + for ( i = 0; i < n_lay; i++, off += 0x34 ) + item = add_item(ana,"mdl 8 layer",grp,grp_index,1,entry,data+off); + } + else + off = 0x418; + item = add_item(ana,"end of mdl 8 layer",grp,grp_index,2,entry,data+off); + + off = it->endian->rd32(data+0x3c); + if (off) + item = add_item(ana,"mdl 8 setup code",grp,grp_index,3,entry,data+off); + + off = it->endian->rd32(data+0x28); + if (off) + item = add_item(ana,"mdl 8 shader @28",grp,grp_index,4,entry,data+off); + } + break; + + case BRSUB_MODE3(FF_MDL,11,11): + { + item = add_item(ana,"end of section",grp,grp_index,99,entry, + data + it->endian->rd32(data) * 8 ); + } + break; + + case BRSUB_MODE3(FF_PAT,4,0): + { + uint n = it->endian->rd16(data+0x0c); + u8 * d = data + 0x14 + 8*n; + item = add_item(ana,"pat",grp,grp_index,1,entry,d); + + while ( it->endian->rd32(d) ) + d += 4; + item = add_item(ana,"end of section",grp,grp_index,99,entry,d+4); + } + break; + } + #endif // ANA_SPLT_SECT + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AnalyzeBRSUB() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// use [[subfile]] + +void AnalyzeBRSUB ( szs_file_t * szs, u8 * data, uint data_size, ccp name ) +{ + if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_M_BRRES) ) + return; + + if ( IsValidBRSUB(data,data_size,data_size,0,FF_UNKNOWN,0,0,0) >= VALID_ERROR ) + return; + +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,data_size,data_size); + ccp ff_name = GetNameFF(0,fform); + PRINT("AnalyzeBRSUB() %s %s [size=%x]\n",ff_name,szs->fname,data_size); + + if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_MD_BRRES) ) + { + switch (fform) + { + case FF_MDL: + if (!(opt_analyze_mode&ANA_MD_MDL)) + return; + break; + + case FF_PAT: + if (!(opt_analyze_mode&ANA_MD_PAT)) + return; + break; + + case FF_TEX: + case FF_TEX_CT: + if (!(opt_analyze_mode&ANA_MD_TEX)) + return; + fform = FF_TEX; + break; + + default: + return; + } + } + + FILE * f = CreateAnalyzeFile(); + if (!f) + return; + + static bool init_done = false; + if (!init_done) + { + init_done = true; + + #if ANA_MDL_11_0 + fprintf(f,"#HAVE# HAVE-MDL.11.0\n"); + #endif + + #if ANA_MDL_11_8 + fprintf(f,"#HAVE# HAVE-MDL.11.8\n"); + #if ANA_MDL_11_8_0_VAL + fprintf(f,"#HAVE# HAVE-MDL.11.8.0-VAL HAVE-MDL-VAL0.11.8\n"); + #endif + #if ANA_MDL_11_8_3_VAL + fprintf(f,"#HAVE# HAVE-MDL.11.8.3-VAL HAVE-MDL-VAL3.11.8\n"); + #endif + #endif + + #if ANA_TEX_HD + fprintf(f,"#HAVE# HAVE-TEX-HD\n"); + #endif + } + + //printf("ANALYZE %s: %s\n",ff_name,szs->fname); + fprintf(f,"\n\f\nANALYZE %s: %s\n\n",ff_name,szs->fname); + + const endian_func_t * endian = szs->endian; + DASSERT(endian); + const u32 version = endian->rd32(data+8); + const int n_sect = GetSectionNumBRSUB(data,data_size,endian); + //const uint brsub_mode2 = BRSUB_MODE2(fform,version); + + + //--- special PAT dump + + if ( fform == FF_PAT && version == 4 ) + { + u32 off0 = endian->rd32(data+16+0*4); + u32 off1 = endian->rd32(data+16+1*4); if (!off1) off1 = off0; + u32 off2 = endian->rd32(data+16+2*4); if (!off2) off2 = off1; + u32 off3 = endian->rd32(data+16+3*4); if (!off3) off3 = off2; + u32 off4 = endian->rd32(data+16+4*4); if (!off4) off4 = off3; + u32 off5 = endian->rd32(data+16+5*4); if (!off5) off5 = off4; + + ccp fname = strstr(szs->fname,"/object/"); + if (fname) + fname += 8; + else + { + fname = strstr(szs->fname,"/track/"); + if (fname) + fname += 7; + else + fname = name; + } + + pat_analyse_t ana; + IsValidPAT(data,data_size,0,szs,fname,0,&ana); + fprintf(f,"#PAT-N# %4d %4d %4d %4d %4d %4d %4d %4d" + " | %4d %4d %4d %4d %4d %4d | v=%d,%d %s\n", + endian->rd16(data+0x2c+0*2), + endian->rd16(data+0x2c+1*2), + endian->rd16(data+0x2c+2*2), + endian->rd16(data+0x2c+3*2), + endian->rd16(data+0x2c+4*2), + endian->rd16(data+0x2c+5*2), + endian->rd16(data+0x2c+6*2), + endian->rd16(data+0x2c+7*2), + off1 - off0, + off2 - off1, + off3 - off2, + off4 - off3, + off5 - off4, + data_size - off5, + ana.valid, ana.data_complete, fname ); + + if (ana.data_complete) + { + const pat_s0_bhead_t *bh = ana.s0_base; + fprintf(f, + "#PAT-BH# %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x | %s\n", + be32(&bh->size), + be16(&bh->unknown_04), + be16(&bh->n_elem), + be16(&bh->unknown_08), + be16(&bh->unknown_0a), + be16(&bh->n_unknown), + be16(&bh->unknown_0e), + be16(&bh->unknown_10), + be16(&bh->unknown_12), + be16(&bh->unknown_14), + be16(&bh->unknown_16), + fname ); + + const pat_s0_belem_t *be = bh->elem; + uint i; + for ( i = 0; i < ana.n_sect0; i++, be++ ) + fprintf(f, + "#PAT-BE.%u# %4x %4x %4x %4x %4x %4x | %s\n", + i, + be16(&be->unknown_00), + be16(&be->unknown_02), + be16(&be->unknown_04), + be16(&be->unknown_06), + be32(&be->offset_name), + be32(&be->offset_strref), + fname ); + + for ( i = 0; i < PAT_MAX_ELEM; i++ ) + { + const pat_s0_sref_t *sr = ana.s0_sref[i]; + if (!sr) + continue; + fprintf(f, + "#PAT-SR.%u# %4x %4x %4x %4x | %s\n", + i, + be32(&sr->offset_name), + be16(&sr->unknown_04), + be16(&sr->type), + be32(&sr->offset_strlist), + fname ); + } + + for ( i = 0; i < PAT_MAX_ELEM; i++ ) + { + const pat_s0_shead_t *sh = ana.s0_shead[i]; + if (!sh) + continue; + const float fm = bef4(&sh->factor); + const float f1 = bef4(&sh->elem[0].time); + const float f2 = bef4(&sh->elem[ana.n_s0_list[i]-1].time); + fprintf(f, + "#PAT-SH.%u# %4x %4x %8.5f [%8.5f,%8.5f] | %s\n" + "#PAT-SE#\n", + i, + be16(&sh->n_elem), + be16(&sh->unknown_02), + fm, fm * f2, fm * (f2-f1), + fname ); + + float last = 0.0; + uint j; + for ( j = 0; j < ana.n_s0_list[i]; j++ ) + { + const pat_s0_selem_t *se = sh->elem + j; + float time = bef4(&se->time); + fprintf(f, + "#PAT-SE.%u.%02u# %11.5f [%8.5f] %4x %4x | %s\n", + i, j, + time, time-last, + be16(&se->index), + be16(&se->unknown_06), + fname ); + last = time; + } + } + } + + if ( ana.s3_list && ana.n_sect1 ) + { + fprintf(f,"#PAT-S3#"); + + uint i; + for ( i = 0; i < ana.n_sect1; i++ ) + fprintf(f," %8x",be32(ana.s3_list+i)); + fprintf(f," | %s\n",fname); + } + } + + + //--- version and section number test + + #if DUMP_VERSION + { + //--- find version and section count + + const u32 size = endian->rd32(data+4); + const int n1 = GetKnownSectionNumBRSUB(fform,version); + const int n2 = GetGenericSectionNumBRSUB(data,data_size,endian); + const int n3 = GetSectionNumBRSUB(data,data_size,endian); + + //printf("#VERSION# %s v=%02u n%c%2d,%2d,%2d : size=%05x : %s\n", + // ff_name, version, n1==n2 ? '=' :'?', n1, n2, n3, size, szs->fname ); + + fprintf(f,"#VERSION# %s v=%02u n%c%2d,%2d,%2d : size=%05x : %s\n", + ff_name, version, n1==n2 ? '=' :'?', n1, n2, n3, size, szs->fname ); + } + #endif + + + //--- ffname with version + + char ff_buf[20]; + snprintf(ff_buf,sizeof(ff_buf),"%s-%x",ff_name,version); + ff_name = ff_buf; + + { + fprintf(f,"#OFFSET# %s :",ff_name); + uint i; + for ( i = 0; i < n_sect; i++ ) + fprintf(f," %5x",endian->rd32(data+0x10+4*i)); + //fprintf(f," : %6x\n",endian->rd32(data+0x10+4*n_sect)); + fprintf(f," : %5x\n",data_size); + } + + + //--- setup analyze_t + + analyze_t ana; + memset(&ana,0,sizeof(ana)); + ana.szs = szs; + ana.data = data; + ana.data_size = data_size; + ana.fform = fform; + ana.ff_name = ff_name; + ana.version = version; + + analyze_item_t * item = ana.list; + item->offset = data_size; // end marker + item->index1 = -1; + item->index3 = 99; + item->info = "END OF DATA"; + item++; + item->index1 = -1; // header = data begin + item->entry_name = name; + item->info = "BEGIN OF DATA"; + item++; + ana.used = item - ana.list; + + brsub_iterator_t it; + memset(&it,0,sizeof(it)); + it.szs = szs; + it.brsub = (void*)data; + it.file_size = szs->data + szs->size - (u8*)data; + it.raw_func = collect_raw_data; + it.grp_func = collect_group_data; + it.entry_func = collect_entry_data; + it.param = &ana; + it.adjust = true; // suppress error messages + + IterateStringsBRSUB(&it); + + + //--- special string + + #if ANA_SPEC_STRINGS // find special strings + + enum { MAX_SPECIAL_STR = 100 }; + ccp special_str[MAX_SPECIAL_STR]; + uint n_special_str = 0; + + { + ccp base = (ccp)szs->data + szs->max_data_off; + u32 off = 4; + while ( off < szs->size ) + { + ccp sptr = (ccp)base + off; + if (!*sptr) + break; + + #define ADD_STR(s) if ( n_special_str < MAX_SPECIAL_STR && !strcmp(sptr,s) ) \ + special_str[n_special_str++] = sptr; + + ADD_STR("bds_eye.0"); + ADD_STR("bds_eye.1"); + ADD_STR("bds_eye.2"); + ADD_STR("catherine_eye.0"); + ADD_STR("catherine_eye.1"); + ADD_STR("catherine_eye.2"); + ADD_STR("diddy_eye.0"); + ADD_STR("diddy_eye.1"); + ADD_STR("diddy_eye.2"); + ADD_STR("luigi_eye.0"); + ADD_STR("luigi_eye.1"); + ADD_STR("luigi_eye.2"); + ADD_STR("waluigi_eye.0"); + ADD_STR("waluigi_eye.1"); + ADD_STR("waluigi_eye.2"); + + #undef ADD_STR + + off += ALIGN32(strlen(sptr)+5,4); + ASSERT( n_special_str < MAX_SPECIAL_STR ); + } + } + #endif + + //--- sort & iterate data + + #if ANA_TEX_HD0 + analyze_item_t * texhd = 0; + #endif + + qsort(ana.list,ana.used,sizeof(*ana.list),cmp_analyze_item); + uint i; + for ( i = 0; i < ana.used - 1; i++ ) + { + analyze_item_t * item = ana.list + i; + + char idxbuf1[20], idxbuf2[30]; + if ( item->index1 < 0 ) + snprintf(idxbuf1,sizeof(idxbuf1),"%s.++.%02u", + ff_name, item->index3 ); + else if ( item->index3 == 99 ) + snprintf(idxbuf1,sizeof(idxbuf1),"%s.%02u.~~", + ff_name, item->index1 ); + else + { + snprintf(idxbuf1,sizeof(idxbuf1),"%s.%02u.%02u", + ff_name, item->index1, item->index3 ); + } + snprintf(idxbuf2,sizeof(idxbuf2),"%s.%02d", + idxbuf1, item->index2 ); + + u8 *base = ana.data + item[0].offset; + const uint sect_size = item[1].offset - item[0].offset; + u32 val = endian->rd32(base); + fprintf(f,"\n#SECT# %3u. %6x..%5x, size=%x, u32=%x->%x : %s >>> %s <<<\n", + i, item->offset, item[1].offset, + sect_size, val, item->offset + val, + idxbuf2, item->info ); + + if ( item->index2 > 0 ) // ( && item->index3 != 99 ) + fprintf(f,"#SECT-SIZE# %s : %2u %2u %2u : %4u = 0x%04x : %s\n", + ff_name, + item->index1, item->index2, item->index3, + sect_size, sect_size, item->info ); + + //HexDump(f,0,0,4,16,base,16); + + if ( item[1].offset == item[0].offset ) + continue; + if ( item->index1 == 0xffff ) + continue; + + fprintf(f,"#SIZE# %s size=%5x : %s tot-off=%6x/%x : >>> %s <<<\n", + idxbuf1, sect_size, + idxbuf2, item[0].offset, data_size, item->info ); + + fprintf(f,"### %s.-----.cnt : %s off=%5x\n", + idxbuf1, idxbuf2, item[0].offset ); + + bool found = false; + u8 *end = ana.data + item[1].offset, *ptr; + for ( ptr = base; ptr < end; ptr += 4 ) + { + ccp string; + u32 offset = endian->rd32(ptr); + + if (!offset) + { + #if DUMP_NULL_OFFSET // value == null + fprintf(f,"### %s.%05x._0_ : %s val=%8x\n", + idxbuf1, + (int)( ptr - base ), + idxbuf2, + offset ); + #endif + continue; + } + + #if DUMP_INVALID_OFFSET // value == other + if ( item->offset + offset < data_size ) + { + fprintf(f,"### %s.%05x._<_ : %s val=%8x\n", + idxbuf1, + (int)( ptr - base ), + idxbuf2, + offset ); + } + #endif + + #if DUMP_GROUP_POINTER // group pointer + if ( item->grp && ptr >= (u8*)item->grp ) + { + string = GetStringBRRES(szs,item->grp,offset,0); + if ( string && *string > ' ' && *string < 0x7f ) + { + fprintf(f,"### %s.%05x.GRP : %s val=%8x : %s\n", + idxbuf1, + (int)( ptr - base ), + idxbuf2, + offset, string ); + found = true; + } + } + #endif + + #if DUMP_ENTRY_POINTER // entry pointer + if ( item->entry && ptr >= (u8*)item->entry ) + { + string = GetStringBRRES(szs,item->entry,offset,0); + if ( string && *string > ' ' && *string < 0x7f ) + { + fprintf(f,"### %s.%05x.ENT : %s val=%8x : %s\n", + idxbuf1, + (int)( ptr - base ), + idxbuf2, + offset, string ); + found = true; + } + } + #endif + string = GetStringBRRES(szs,ana.data+item->offset,offset,0); + if ( string && *string > ' ' && *string < 0x7f ) + { + fprintf(f,"### %s.%05x.DAT : %s val=%8x : %s\n", + idxbuf1, + (int)( ptr - base ), + idxbuf2, + offset, string ); + + found = true; + + if ( string == item->entry_name ) + fprintf(f,"### %s.%05x.=== : %s val=%8x : %s\n", + idxbuf1, + (int)( ptr - base ), + idxbuf2, + offset, string ); + + if ( item->index1 == -1 && ptr - base >= 4*n_sect ) + fprintf(f,"### %s.%05x.NNN : %s val=%8x : %s\n", + idxbuf1, + (int)( ptr - base - 4*n_sect ), + idxbuf2, + offset, string ); + } + + #if ANA_SPEC_STRINGS // find special strings + if (n_special_str) + { + u8 * p2; + for ( p2 = ptr; p2 < end; p2 += 4 ) + { + u32 offset = endian->rd32(p2); + if (!offset) + continue; + string = GetStringBRRES(szs,ptr,offset,0); + int i; + for ( i = 0; i < n_special_str; i++ ) + if ( string == special_str[i] ) + fprintf(f,"#SPEC# %s.%05x+%05x=%05x : off=%6x : %s\n", + idxbuf1, + (int)( ptr - base ), + (int)( p2 - ptr ), + (int)( p2 - base ), + offset, string ); + } + } + #endif + } + if (!found) + fprintf(f,"### %s.xxxxx.non : %s\n", idxbuf1, idxbuf2 ); + + #if 0 + if ( fform == FF_MDL && item->old_index == 0xc0 ) + { + u32 *p = (u32*)base; + uint n = endian->rd32(p++), i; + for ( i = 0; i < n; i++ ) + { + const u32 v1 = endian->rd32(p++); + const u32 v2 = endian->rd32(p++); + + fprintf(f,"### MDL-b.%02u: %6x %6x -> %6x %6x [d=%4x]\n", + i, v1, v2, + item[0].offset + v1, item[0].offset + v2, + v2 - v1 ); + } + } + #endif + + #if ANA_MDL_11_0 + if ( item->brsub_mode == BRSUB_MODE3(FF_MDL,11,0) ) + { + #ifdef TEST0 + FILE *f = stdout; + #endif + ccp prefix = "#MDL.s0# "; + fprintf(f,"%s-------------\n%s%s -> %s\n", + prefix, + prefix, idxbuf2, szs->fname ); + + mdl_sect0_t ms0; + InitializeMDLs0(&ms0,base,sect_size); + fprintf(f,"%sSTAT: ok=%d, n=%d, size=%u/%u\n", + prefix, ms0.ok, ms0.n, ms0.data_size, sect_size); + while (NextMDLs0(&ms0)) + { + fprintf(f,"%sDUMP[%02x,%2u]:",prefix,*ms0.cur,ms0.cur_size); + HexDump(f, 1, ms0.cur-ms0.data_beg, 3, -50, ms0.cur, ms0.cur_size ); + } + if (ms0.fail) + { + fprintf(f,"%sFAIL[%02x,*?]:",prefix,*ms0.fail); + HexDump(f, 1, ms0.fail-ms0.data_beg, 3, -50, + ms0.fail, ms0.data_end - ms0.fail ); + } + } + #endif + + #if ANA_MDL_11_8 + if ( item->brsub_mode == BRSUB_MODE3(FF_MDL,11,8) ) + { + ccp prefix = "#MDL.s8# "; + fprintf(f,"%s-------------\n%s%s -> %s -> %s\n", + prefix, + prefix, idxbuf2, szs->fname, name ); + //u8 * off_base = base; + //hexdump_prefix = prefix; + //HexDump(f,0,base-off_base,4,16,base,sect_size); + if ( item->index3 == 0 ) + { + u32 shader_off = endian->rd32(base+0x28); + u32 layer_cnt = endian->rd32(base+0x2c); + u32 layer_off = endian->rd32(base+0x30); + u32 layer_size = 0x34; + u32 grcode_off = endian->rd32(base+0x3c); + u32 end_layer = layer_off + layer_cnt * layer_size; + fprintf(f,"%sMEM #=%05x %u*l=%04x-%04x free=%03x>%02x u=%04x[%04x,s=%03x]" + " : s=%04x[%08x]\n", + prefix, + endian->rd32(base), + layer_cnt, layer_off, end_layer, + grcode_off - end_layer, (int)(base+grcode_off-data)&0x1f, + grcode_off, endian->rd16(base+grcode_off), + endian->rd32(base) - grcode_off, + shader_off, endian->rd32(base+shader_off) ); + + #if ANA_MDL_11_8_0_VAL + for ( ptr = base; ptr < end; ptr += 4 ) + fprintf(f,"%sVAL0 %4x : %8x\n", + prefix, (int)(ptr-base), endian->rd32(ptr) ); + #endif + } + + if ( item->index3 == 3 ) + { + #if ANA_MDL_11_8_3_VAL + for ( ptr = base; ptr < end; ptr += 4 ) + fprintf(f,"%sVAL3 %4x : %8x\n", + prefix, (int)(ptr-base), endian->rd32(ptr) ); + #endif + } + } + #endif + + #if ANA_TEX_HD0 + if ( ana.fform == FF_TEX && item->index1 < 0 && item->index3 == 1 ) + texhd = item; + + PRINT_IF(ana.fform == FF_TEX,"TEX %d %d %d %u\n", + item->index1,item->index2,item->index3,sect_size); + if ( ana.fform == FF_TEX && item->index1 == 0 && texhd ) + { + texhd = 0; + } + #endif + + #if ANA_TEX_HD + if ( ana.fform == FF_TEX && item->index1 < 0 && item->index3 == 1 ) + { + tex_info_t * ti = (tex_info_t*)( ana.data + item->offset - 4 ); + const uint wd = endian->rd16(&ti->width); + const uint ht = endian->rd16(&ti->height); + const uint iform = endian->rd32(&ti->iform); + fprintf(f,"#TEX-HD# %-6s %4u*%-4u",GetImageFormatName(iform,"?"),wd,ht); + noPRINT("OFF: %x %x %x\n",item[0].offset,item[1].offset,item[2].offset); + const uint raw_size = item[2].offset - item[1].offset; + + uint xwd, xht, hb, vb, img_size; + const ImageGeometry_t *geo + = CalcImageGeometry(iform,wd,ht,&xwd,&xht,&hb,&vb,&img_size); + if (geo) + { + const uint n_blk = img_size / geo->block_size; + const uint m_blk = raw_size / geo->block_size; + const uint xbytes = raw_size - m_blk * geo->block_size; + fprintf(f," %6u %+6d b=%5u %4u.%u:", + img_size, raw_size - img_size, + n_blk, m_blk - n_blk, xbytes ); + } + else + fprintf(f," %6u:",raw_size); + + u32 * ptr = (u32*)( ana.data + item->offset ); + u32 * end = (u32*)( ana.data + item->offset + sect_size ); + fprintf(f," %x ..",endian->rd32(ptr)); ptr += 3; + while ( ptr < end ) + fprintf(f," %8x",endian->rd32(ptr++)); + fprintf(f,"\n"); + } + #endif + } + + fprintf(f,"--\n"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BREFF+BREFT sub files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int ana_breff_subfile +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if ( term || it->is_dir || memcmp(it->path,"files/",6) ) + return 0; + + noPRINT("BINGO BREFF %s\n",it->path); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int ana_breft_subfile +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if ( term || it->is_dir || memcmp(it->path,"files/",6) ) + return 0; + noPRINT("BINGO BREFT %s\n",it->path); + + FILE * f = it->param; + DASSERT(f); + + fprintf(f,"#BREFF# file=%s\n",it->path); + + u32 *d = (u32*)( it->szs->data + it->off); + fprintf(f,"#BREFF# HEAD: %8x %8x %8x %8x %8x %8x %8x %8x [%x]\n", + it->endian->rd32(d), + it->endian->rd32(d+1), + it->endian->rd32(d+2), + it->endian->rd32(d+3), + it->endian->rd32(d+4), + it->endian->rd32(d+5), + it->endian->rd32(d+6), + it->endian->rd32(d+7), + it->size ); + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AnalyzeBREFF() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyzeBREFF ( szs_file_t * szs, u8 * data, uint data_size, ccp name ) +{ +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch (fform) + { + case FF_BREFF: + if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_MD_BREFF) ) + return; + break; + + case FF_BREFT: + if ( opt_analyze_mode != ANA_MD_DEFAULT && !(opt_analyze_mode&ANA_MD_BREFT) ) + return; + break; + + default: + return; + } + + ccp ff_name = GetNameFF(0,fform); + noPRINT("AnalyzeBREFF() %s %s\n",ff_name,szs->fname); + + FILE * f = CreateAnalyzeFile(); + if (!f) + return; + + //printf("ANALYZE %s: %s\n",ff_name,szs->fname); + fprintf(f,"\n\f\nANALYZE %s: %s\n\n",ff_name,szs->fname); + + brres_header_t *bh = (brres_header_t*)data; + breff_root_head_t *rh = (breff_root_head_t*)(bh+1); + breff_root_t *root = (breff_root_t*)(&rh->data); + + const endian_func_t * endian = szs->endian; + DASSERT(endian); + + fprintf(f,"#%s#N-SECT: %u\n#%s#VERSION: %u\n", + ff_name, endian->rd16(&bh->n_sections), + ff_name, endian->rd16(&bh->version)); + + fprintf(f,"#%s#UNKNOWN: %8x %8x %4x\n", + ff_name, + endian->rd32(&root->unknown1), + endian->rd32(&root->unknown2), + endian->rd16(&root->unknown3) ); + + + szs_file_t szs2; +// [[fname-]] + InitializeSubSZS(&szs2,szs,data-szs->data,szs->size,fform,0,false); + IterateFilesSZS(&szs2, + fform == FF_BREFF ? ana_breff_subfile : ana_breft_subfile, + f, true, false, -1, SORT_NONE ); + ResetSZS(&szs2); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AnalyzeTPL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyzeTPL ( szs_file_t * szs, u8 * data, uint data_size, ccp name ) +{ +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,data_size,data_size); + if ( fform != FF_TPL + || opt_analyze_mode != ANA_MD_DEFAULT + && !(opt_analyze_mode&ANA_MD_TPL) ) + { + return; + } + + ccp ff_name = GetNameFF(0,fform); + noPRINT("AnalyzeTPL() %s %s / %s\n",ff_name,szs->fname,name); + + FILE * f = CreateAnalyzeFile(); + if (!f) + return; + + //printf("ANALYZE %s: %s / %s\n",ff_name,szs->fname,name); + fprintf(f,"\n\f\nANALYZE %s: %s / %s\n\n",ff_name,szs->fname,name); + + const endian_func_t * endian = szs->endian; + DASSERT(endian); + + const uint n_img = GetNImagesTPL(data,data_size,endian); + uint ni; + for ( ni = 0; ni < n_img; ni++ ) + { + const tpl_header_t * head; + const tpl_imgtab_t * tab; + const tpl_pal_header_t * pal; + const tpl_img_header_t * img; + const u8 *pal_data, *img_data; + + if (SetupPointerTPL( data, data_size, ni, + &head, &tab, &pal, &img, + &pal_data, &img_data, endian )) + { + noPRINT("TPL #%u: %6x %6x %6x %6x : %6x %6x : %6x\n", + ni, + (int)((u8*)head-data), + (int)((u8*)tab-data), + pal ? (int)((u8*)pal-data) : 0, + (int)((u8*)img-data), + pal_data ? (int)(pal_data-data) : 0, + (int)(img_data-data), + data_size ); + fprintf(f,"#TPL-IMG# %u: %6x %6x %6x %6x : %6x %6x\n", + ni, + (int)((u8*)head-data), + (int)((u8*)tab-data), + pal ? (int)((u8*)pal-data) : 0, + (int)((u8*)img-data), + pal_data ? (int)(pal_data-data) : 0, + (int)(img_data-data) ); + } + else + { + PRINT("TPL #%u: FAILED\n",ni); + fprintf(f,"#TPL-IMG# %u: FAILED\n",ni); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AnalyzeKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyzeKMP ( const struct kmp_t * kmp, ccp base_fname ) +{ + if ( opt_analyze_mode != ANA_MD_DEFAULT + && !(opt_analyze_mode&(ANA_M_KMP|ANA_MKWANA)) ) + { + return; + } + + PRINT("AnalyzeKMP(%s) %s\n",base_fname,kmp->fname); + + FILE * f = CreateAnalyzeFile(); + if (!f) + return; + + if ( verbose >= 0 ) + fprintf(f,"\n\f\nANALYZE KMP: %s\n\n",kmp->fname); + + if ( opt_analyze_mode == ANA_MD_DEFAULT + || (opt_analyze_mode&ANA_KMP_GOBJ) ) + { + const uint n = kmp->dlist[KMP_GOBJ].used; + const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint i; + for ( i = 0; i < n; i++, gobj++ ) + { + ccp name = "?"; + if ( gobj->obj_id < N_KMP_GOBJ ) + { + const ObjectInfo_t *oi = ObjectInfo + gobj->obj_id; + if (oi->name) + name = oi->name; + } + fprintf(f,"#KMP-GOBJ# %04x %s\n", gobj->obj_id, name ); + const u16 *s = gobj->setting; + fprintf(f, + "#KMP-GOBJ-ALL# %4x %4x %s %4x %4x %4x %4x %4x %4x %4x %4x %4x %s\n", + gobj->obj_id, gobj->ref_id, + gobj->route_id == 0xffff ? "-" : "R", + s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], + gobj->pflags, + name ); + + uint p; + for ( p = 0; p < 8; p++ ) + fprintf(f,"#KMP-GOBJ-SET# %04x.%u.%04x\n", + gobj->obj_id, p+1, gobj->setting[p] ); + + fprintf(f,"#KMP-GOBJ-SET# %04x.u.%04x\n", + gobj->obj_id, gobj->ref_id ); + fprintf(f,"#KMP-GOBJ-SET# %04x.r.%04x\n", + gobj->obj_id, gobj->route_id ); + fprintf(f,"#KMP-GOBJ-SET# %04x.p.%04x\n", + gobj->obj_id, gobj->pflags ); + } + } + + if ( opt_analyze_mode == ANA_MD_DEFAULT + || (opt_analyze_mode&ANA_KMP_KTPT) ) + { + kmp_finish_t kf; + CheckFinishLine(kmp,&kf); + fprintf(f,"#KTPT-DIST# %11.3f %7.2f *%u,%u %s : %s\n", + kf.distance, kf.dir_delta, kf.n_ckpt_0, kf.n_ktpt_m, + kf.warn ? "WARN" : kf.hint ? "HINT" : "- ", base_fname ); + PRINT0("#FINISH# %s\n",LogFinishLine(&kf)); + } + + if ( opt_analyze_mode & ANA_MKWANA ) + { + if (base_fname) + { + ccp slash = strrchr(base_fname,'/'); + if (slash) + base_fname = slash; + } + else + base_fname = kmp->fname; + fprintf(f,"#MKWANA: ---\n#MKWANA: FILE = %s\n",base_fname); + + uint n = kmp->dlist[KMP_KTPT].used; + if (n>0) + { + const kmp_ktpt_entry_t *ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list; + fprintf(f,"#MKWANA: START-POS = %11.3f %11.3f %11.3f\n", + ktpt->position[0], ktpt->position[1], ktpt->position[2] ); + fprintf(f,"#MKWANA: START-DIR = %11.3f %11.3f %11.3f\n", + ktpt->rotation[0], ktpt->rotation[1], ktpt->rotation[2] ); + } + + n = kmp->dlist[KMP_STGI].used; + if (n>0) + { + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp->dlist[KMP_STGI].list; + fprintf(f,"#MKWANA: POLE-POS = 0x%02x\n",stgi->pole_pos); + fprintf(f,"#MKWANA: NARROW = 0x%02x\n",stgi->narrow_start); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AnalyzeKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyzeKCL ( const struct kcl_t * kcl ) +{ + if ( opt_analyze_mode != ANA_MD_DEFAULT + && !(opt_analyze_mode&ANA_MD_KCL) ) + { + return; + } + + ccp fname = kcl->fname ? kcl->fname : "?"; + PRINT("AnalyzeKCL() %s\n",fname); + + FILE * f = CreateAnalyzeFile(); + if (!f) + return; + + ccp ptr, slash = strrchr(fname,'/'); + slash = slash ? slash+1 : fname; + uint index = 0; + for ( ptr = fname + strlen(fname); ptr > fname; ptr-- ) + if ( *ptr == '/' || *ptr == '\\' ) + { + uint num = strtoul(ptr+1,0,10); + if ( num > 0 ) + { + index = num; + break; + } + } + fprintf(f,"\n\f\nANALYZE KCL %02u: %s\n\n",index,fname); + + u32 *count = CALLOC(N_KCL_FLAG,sizeof(*count)); + + const uint n = kcl->tridata.used; + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + const kcl_tridata_t *end = td + n; + for ( ; td < end; td++ ) + count[td->in_flag]++; + + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + if (count[i]) + fprintf(f,"KCL-TYPE 0x%02x 0x%03x 0x%04x %6.2f%% T%02u %s\n", + i & 0x1f, i >> 5, i & ~0x1f, 100.0 * count[i] / n, index, slash ); + + FREE(count); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/crypt.h b/project/src/crypt.h new file mode 100644 index 0000000..c541bc8 --- /dev/null +++ b/project/src/crypt.h @@ -0,0 +1,45 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef WIIMM_CRYPT_H +#define WIIMM_CRYPT_H 1 + +#include "crypto/wiimm-sha.h" +#define SHA1 WIIMM_SHA1 + +#endif // WIIMM_CRYPT_H + diff --git a/project/src/crypto/md32_common.h b/project/src/crypto/md32_common.h new file mode 100644 index 0000000..7525d04 --- /dev/null +++ b/project/src/crypto/md32_common.h @@ -0,0 +1,408 @@ +/* crypto/md32_common.h */ +/* ==================================================================== + * Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + */ + +/* + * This is a generic 32 bit "collector" for message digest algorithms. + * Whenever needed it collects input character stream into chunks of + * 32 bit values and invokes a block function that performs actual hash + * calculations. + * + * Porting guide. + * + * Obligatory macros: + * + * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN + * this macro defines byte order of input stream. + * HASH_CBLOCK + * size of a unit chunk HASH_BLOCK operates on. + * HASH_LONG + * has to be at lest 32 bit wide, if it's wider, then + * HASH_LONG_LOG2 *has to* be defined along + * HASH_CTX + * context structure that at least contains following + * members: + * typedef struct { + * ... + * HASH_LONG Nl,Nh; + * either { + * HASH_LONG data[HASH_LBLOCK]; + * unsigned char data[HASH_CBLOCK]; + * }; + * unsigned int num; + * ... + * } HASH_CTX; + * data[] vector is expected to be zeroed upon first call to + * HASH_UPDATE. + * HASH_UPDATE + * name of "Update" function, implemented here. + * HASH_TRANSFORM + * name of "Transform" function, implemented here. + * HASH_FINAL + * name of "Final" function, implemented here. + * HASH_BLOCK_DATA_ORDER + * name of "block" function capable of treating *unaligned* input + * message in original (data) byte order, implemented externally. + * HASH_MAKE_STRING + * macro convering context variables to an ASCII hash string. + * + * MD5 example: + * + * #define DATA_ORDER_IS_LITTLE_ENDIAN + * + * #define HASH_LONG MD5_LONG + * #define HASH_LONG_LOG2 MD5_LONG_LOG2 + * #define HASH_CTX MD5_CTX + * #define HASH_CBLOCK MD5_CBLOCK + * #define HASH_UPDATE MD5_Update + * #define HASH_TRANSFORM MD5_Transform + * #define HASH_FINAL MD5_Final + * #define HASH_BLOCK_DATA_ORDER md5_block_data_order + * + * + */ + +#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) +#error "DATA_ORDER must be defined!" +#endif + +#ifndef HASH_CBLOCK +#error "HASH_CBLOCK must be defined!" +#endif +#ifndef HASH_LONG +#error "HASH_LONG must be defined!" +#endif +#ifndef HASH_CTX +#error "HASH_CTX must be defined!" +#endif + +#ifndef HASH_UPDATE +#error "HASH_UPDATE must be defined!" +#endif +#ifndef HASH_TRANSFORM +#error "HASH_TRANSFORM must be defined!" +#endif +#ifndef HASH_FINAL +#error "HASH_FINAL must be defined!" +#endif + +#ifndef HASH_BLOCK_DATA_ORDER +#error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif + +/* + * Engage compiler specific rotate intrinsic function if available. + */ +#undef ROTATE +#ifndef PEDANTIC +# if defined(_MSC_VER) || defined(__ICC) +# define ROTATE(a,n) _lrotl(a,n) +# elif defined(__MWERKS__) +# if defined(__POWERPC__) +# define ROTATE(a,n) __rlwinm(a,n,0,31) +# elif defined(__MC68K__) + /* Motorola specific tweak. */ +# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) ) +# else +# define ROTATE(a,n) __rol(a,n) +# endif +# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) + /* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * + */ +# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r"(ret) \ + : "I"(n), "0"((unsigned int)(a)) \ + : "cc"); \ + ret; \ + }) +# elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \ + defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# elif defined(__s390x__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ("rll %0,%1,%2" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# endif +# endif +#endif /* PEDANTIC */ + +#ifndef ROTATE +#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + +#ifndef PEDANTIC +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +# if ((defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)) || \ + (defined(__x86_64) || defined(__x86_64__)) +# if !defined(B_ENDIAN) + /* + * This gives ~30-40% performance improvement in SHA-256 compiled + * with gcc [on P4]. Well, first macro to be frank. We can pull + * this trick on x86* platforms only, because these CPUs can fetch + * unaligned data without raising an exception. + */ +# define HOST_c2l(c,l) ({ unsigned int r=*((const unsigned int *)(c)); \ + asm ("bswapl %0":"=r"(r):"0"(r)); \ + (c)+=4; (l)=r; }) +# define HOST_l2c(l,c) ({ unsigned int r=(l); \ + asm ("bswapl %0":"=r"(r):"0"(r)); \ + *((unsigned int *)(c))=r; (c)+=4; r; }) +# endif +# endif +# endif +#endif +#if defined(__s390__) || defined(__s390x__) +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l)) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l)) +#endif + +#ifndef HOST_c2l +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) ), \ + l) +#endif +#ifndef HOST_l2c +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff), \ + l) +#endif + +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + +#ifndef PEDANTIC +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +# if defined(__s390x__) +# define HOST_c2l(c,l) ({ asm ("lrv %0,%1" \ + :"=d"(l) :"m"(*(const unsigned int *)(c)));\ + (c)+=4; (l); }) +# define HOST_l2c(l,c) ({ asm ("strv %1,%0" \ + :"=m"(*(unsigned int *)(c)) :"d"(l));\ + (c)+=4; (l); }) +# endif +# endif +#endif +#if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# ifndef B_ENDIAN + /* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */ +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, l) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, l) +# endif +#endif + +#ifndef HOST_c2l +#define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24), \ + l) +#endif +#ifndef HOST_l2c +#define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + l) +#endif + +#endif + +/* + * Time for some action:-) + */ + +int HASH_UPDATE (HASH_CTX *c, const void *data_, size_t len) + { + const unsigned char *data=data_; + unsigned char *p; + HASH_LONG l; + size_t n; + + if (len==0) return 1; + + l=(c->Nl+(((HASH_LONG)len)<<3))&0xffffffffUL; + /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to + * Wei Dai for pointing it out. */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh+=(HASH_LONG)(len>>29); /* might cause compiler warning on 16-bit */ + c->Nl=l; + + n = c->num; + if (n != 0) + { + p=(unsigned char *)c->data; + + if (len >= HASH_CBLOCK || len+n >= HASH_CBLOCK) + { + memcpy (p+n,data,HASH_CBLOCK-n); + HASH_BLOCK_DATA_ORDER (c,p,1); + n = HASH_CBLOCK-n; + data += n; + len -= n; + c->num = 0; + memset (p,0,HASH_CBLOCK); /* keep it zeroed */ + } + else + { + memcpy (p+n,data,len); + c->num += (unsigned int)len; + return 1; + } + } + + n = len/HASH_CBLOCK; + if (n > 0) + { + HASH_BLOCK_DATA_ORDER (c,data,n); + n *= HASH_CBLOCK; + data += n; + len -= n; + } + + if (len != 0) + { + p = (unsigned char *)c->data; + c->num = (unsigned int)len; + memcpy (p,data,len); + } + return 1; + } + + +void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data) + { + HASH_BLOCK_DATA_ORDER (c,data,1); + } + + +int HASH_FINAL (unsigned char *md, HASH_CTX *c) + { + unsigned char *p = (unsigned char *)c->data; + size_t n = c->num; + + p[n] = 0x80; /* there is always room for one */ + n++; + + if (n > (HASH_CBLOCK-8)) + { + memset (p+n,0,HASH_CBLOCK-n); + n=0; + HASH_BLOCK_DATA_ORDER (c,p,1); + } + memset (p+n,0,HASH_CBLOCK-8-n); + + p += HASH_CBLOCK-8; +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + (void)HOST_l2c(c->Nh,p); + (void)HOST_l2c(c->Nl,p); +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + (void)HOST_l2c(c->Nl,p); + (void)HOST_l2c(c->Nh,p); +#endif + p -= HASH_CBLOCK; + HASH_BLOCK_DATA_ORDER (c,p,1); + c->num=0; + memset (p,0,HASH_CBLOCK); + +#ifndef HASH_MAKE_STRING +#error "HASH_MAKE_STRING must be defined!" +#else + HASH_MAKE_STRING(c,md); +#endif + + return 1; + } + +#ifndef MD32_REG_T +#define MD32_REG_T long +/* + * This comment was originaly written for MD5, which is why it + * discusses A-D. But it basically applies to all 32-bit digests, + * which is why it was moved to common header file. + * + * In case you wonder why A-D are declared as long and not + * as MD5_LONG. Doing so results in slight performance + * boost on LP64 architectures. The catch is we don't + * really care if 32 MSBs of a 64-bit register get polluted + * with eventual overflows as we *save* only 32 LSBs in + * *either* case. Now declaring 'em long excuses the compiler + * from keeping 32 MSBs zeroed resulting in 13% performance + * improvement under SPARC Solaris7/64 and 5% under AlphaLinux. + * Well, to be honest it should say that this *prevents* + * performance degradation. + * + * Apparently there're LP64 compilers that generate better + * code if A-D are declared int. Most notably GCC-x86_64 + * generates better code. + * + */ +#endif diff --git a/project/src/crypto/sha1-586-cygwin.s b/project/src/crypto/sha1-586-cygwin.s new file mode 100644 index 0000000..2751a8f --- /dev/null +++ b/project/src/crypto/sha1-586-cygwin.s @@ -0,0 +1,1441 @@ +.file "sha1-586.s" +.text +.globl _wit_sha1_block_data_order +.def _wit_sha1_block_data_order; .scl 2; .type 32; .endef +.align 16 +_wit_sha1_block_data_order: +.L_wit_sha1_block_data_order_begin: + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + movl 20(%esp),%ebp + movl 24(%esp),%esi + movl 28(%esp),%eax + subl $64,%esp + shll $6,%eax + addl %esi,%eax + movl %eax,92(%esp) + movl 16(%ebp),%edi +.align 16 +.L000loop: + movl (%esi),%eax + movl 4(%esi),%ebx + movl 8(%esi),%ecx + movl 12(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,(%esp) + movl %ebx,4(%esp) + movl %ecx,8(%esp) + movl %edx,12(%esp) + movl 16(%esi),%eax + movl 20(%esi),%ebx + movl 24(%esi),%ecx + movl 28(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,16(%esp) + movl %ebx,20(%esp) + movl %ecx,24(%esp) + movl %edx,28(%esp) + movl 32(%esi),%eax + movl 36(%esi),%ebx + movl 40(%esi),%ecx + movl 44(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,32(%esp) + movl %ebx,36(%esp) + movl %ecx,40(%esp) + movl %edx,44(%esp) + movl 48(%esi),%eax + movl 52(%esi),%ebx + movl 56(%esi),%ecx + movl 60(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,48(%esp) + movl %ebx,52(%esp) + movl %ecx,56(%esp) + movl %edx,60(%esp) + movl %esi,88(%esp) + movl (%ebp),%eax + movl 4(%ebp),%ebx + movl 8(%ebp),%ecx + movl 12(%ebp),%edx + # 00_15 0 + movl %ecx,%esi + movl %eax,%ebp + roll $5,%ebp + xorl %edx,%esi + addl %edi,%ebp + andl %ebx,%esi + movl (%esp),%edi + xorl %edx,%esi + rorl $2,%ebx + leal 1518500249(%ebp,%edi,1),%ebp + addl %esi,%ebp + # 00_15 1 + movl %ebx,%edi + movl %ebp,%esi + roll $5,%ebp + xorl %ecx,%edi + addl %edx,%ebp + andl %eax,%edi + movl 4(%esp),%edx + xorl %ecx,%edi + rorl $2,%eax + leal 1518500249(%ebp,%edx,1),%ebp + addl %edi,%ebp + # 00_15 2 + movl %eax,%edx + movl %ebp,%edi + roll $5,%ebp + xorl %ebx,%edx + addl %ecx,%ebp + andl %esi,%edx + movl 8(%esp),%ecx + xorl %ebx,%edx + rorl $2,%esi + leal 1518500249(%ebp,%ecx,1),%ebp + addl %edx,%ebp + # 00_15 3 + movl %esi,%ecx + movl %ebp,%edx + roll $5,%ebp + xorl %eax,%ecx + addl %ebx,%ebp + andl %edi,%ecx + movl 12(%esp),%ebx + xorl %eax,%ecx + rorl $2,%edi + leal 1518500249(%ebp,%ebx,1),%ebp + addl %ecx,%ebp + # 00_15 4 + movl %edi,%ebx + movl %ebp,%ecx + roll $5,%ebp + xorl %esi,%ebx + addl %eax,%ebp + andl %edx,%ebx + movl 16(%esp),%eax + xorl %esi,%ebx + rorl $2,%edx + leal 1518500249(%ebp,%eax,1),%ebp + addl %ebx,%ebp + # 00_15 5 + movl %edx,%eax + movl %ebp,%ebx + roll $5,%ebp + xorl %edi,%eax + addl %esi,%ebp + andl %ecx,%eax + movl 20(%esp),%esi + xorl %edi,%eax + rorl $2,%ecx + leal 1518500249(%ebp,%esi,1),%ebp + addl %eax,%ebp + # 00_15 6 + movl %ecx,%esi + movl %ebp,%eax + roll $5,%ebp + xorl %edx,%esi + addl %edi,%ebp + andl %ebx,%esi + movl 24(%esp),%edi + xorl %edx,%esi + rorl $2,%ebx + leal 1518500249(%ebp,%edi,1),%ebp + addl %esi,%ebp + # 00_15 7 + movl %ebx,%edi + movl %ebp,%esi + roll $5,%ebp + xorl %ecx,%edi + addl %edx,%ebp + andl %eax,%edi + movl 28(%esp),%edx + xorl %ecx,%edi + rorl $2,%eax + leal 1518500249(%ebp,%edx,1),%ebp + addl %edi,%ebp + # 00_15 8 + movl %eax,%edx + movl %ebp,%edi + roll $5,%ebp + xorl %ebx,%edx + addl %ecx,%ebp + andl %esi,%edx + movl 32(%esp),%ecx + xorl %ebx,%edx + rorl $2,%esi + leal 1518500249(%ebp,%ecx,1),%ebp + addl %edx,%ebp + # 00_15 9 + movl %esi,%ecx + movl %ebp,%edx + roll $5,%ebp + xorl %eax,%ecx + addl %ebx,%ebp + andl %edi,%ecx + movl 36(%esp),%ebx + xorl %eax,%ecx + rorl $2,%edi + leal 1518500249(%ebp,%ebx,1),%ebp + addl %ecx,%ebp + # 00_15 10 + movl %edi,%ebx + movl %ebp,%ecx + roll $5,%ebp + xorl %esi,%ebx + addl %eax,%ebp + andl %edx,%ebx + movl 40(%esp),%eax + xorl %esi,%ebx + rorl $2,%edx + leal 1518500249(%ebp,%eax,1),%ebp + addl %ebx,%ebp + # 00_15 11 + movl %edx,%eax + movl %ebp,%ebx + roll $5,%ebp + xorl %edi,%eax + addl %esi,%ebp + andl %ecx,%eax + movl 44(%esp),%esi + xorl %edi,%eax + rorl $2,%ecx + leal 1518500249(%ebp,%esi,1),%ebp + addl %eax,%ebp + # 00_15 12 + movl %ecx,%esi + movl %ebp,%eax + roll $5,%ebp + xorl %edx,%esi + addl %edi,%ebp + andl %ebx,%esi + movl 48(%esp),%edi + xorl %edx,%esi + rorl $2,%ebx + leal 1518500249(%ebp,%edi,1),%ebp + addl %esi,%ebp + # 00_15 13 + movl %ebx,%edi + movl %ebp,%esi + roll $5,%ebp + xorl %ecx,%edi + addl %edx,%ebp + andl %eax,%edi + movl 52(%esp),%edx + xorl %ecx,%edi + rorl $2,%eax + leal 1518500249(%ebp,%edx,1),%ebp + addl %edi,%ebp + # 00_15 14 + movl %eax,%edx + movl %ebp,%edi + roll $5,%ebp + xorl %ebx,%edx + addl %ecx,%ebp + andl %esi,%edx + movl 56(%esp),%ecx + xorl %ebx,%edx + rorl $2,%esi + leal 1518500249(%ebp,%ecx,1),%ebp + addl %edx,%ebp + # 00_15 15 + movl %esi,%ecx + movl %ebp,%edx + roll $5,%ebp + xorl %eax,%ecx + addl %ebx,%ebp + andl %edi,%ecx + movl 60(%esp),%ebx + xorl %eax,%ecx + rorl $2,%edi + leal 1518500249(%ebp,%ebx,1),%ebp + addl %ebp,%ecx + # 16_19 16 + movl (%esp),%ebx + movl %edi,%ebp + xorl 8(%esp),%ebx + xorl %esi,%ebp + xorl 32(%esp),%ebx + andl %edx,%ebp + rorl $2,%edx + xorl 52(%esp),%ebx + roll $1,%ebx + xorl %esi,%ebp + movl %ebx,(%esp) + leal 1518500249(%ebx,%eax,1),%ebx + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + # 16_19 17 + movl 4(%esp),%eax + movl %edx,%ebp + xorl 12(%esp),%eax + xorl %edi,%ebp + xorl 36(%esp),%eax + andl %ecx,%ebp + rorl $2,%ecx + xorl 56(%esp),%eax + roll $1,%eax + xorl %edi,%ebp + movl %eax,4(%esp) + leal 1518500249(%eax,%esi,1),%eax + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + # 16_19 18 + movl 8(%esp),%esi + movl %ecx,%ebp + xorl 16(%esp),%esi + xorl %edx,%ebp + xorl 40(%esp),%esi + andl %ebx,%ebp + rorl $2,%ebx + xorl 60(%esp),%esi + roll $1,%esi + xorl %edx,%ebp + movl %esi,8(%esp) + leal 1518500249(%esi,%edi,1),%esi + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + # 16_19 19 + movl 12(%esp),%edi + movl %ebx,%ebp + xorl 20(%esp),%edi + xorl %ecx,%ebp + xorl 44(%esp),%edi + andl %eax,%ebp + rorl $2,%eax + xorl (%esp),%edi + roll $1,%edi + xorl %ecx,%ebp + movl %edi,12(%esp) + leal 1518500249(%edi,%edx,1),%edi + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + # 20_39 20 + movl %esi,%ebp + movl 16(%esp),%edx + rorl $2,%esi + xorl 24(%esp),%edx + xorl %eax,%ebp + xorl 48(%esp),%edx + xorl %ebx,%ebp + xorl 4(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,16(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + # 20_39 21 + movl %edi,%ebp + movl 20(%esp),%ecx + rorl $2,%edi + xorl 28(%esp),%ecx + xorl %esi,%ebp + xorl 52(%esp),%ecx + xorl %eax,%ebp + xorl 8(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,20(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + # 20_39 22 + movl %edx,%ebp + movl 24(%esp),%ebx + rorl $2,%edx + xorl 32(%esp),%ebx + xorl %edi,%ebp + xorl 56(%esp),%ebx + xorl %esi,%ebp + xorl 12(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,24(%esp) + movl %ecx,%eax + roll $5,%eax + leal 1859775393(%ebx,%ebp,1),%ebx + addl %eax,%ebx + # 20_39 23 + movl %ecx,%ebp + movl 28(%esp),%eax + rorl $2,%ecx + xorl 36(%esp),%eax + xorl %edx,%ebp + xorl 60(%esp),%eax + xorl %edi,%ebp + xorl 16(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,28(%esp) + movl %ebx,%esi + roll $5,%esi + leal 1859775393(%eax,%ebp,1),%eax + addl %esi,%eax + # 20_39 24 + movl %ebx,%ebp + movl 32(%esp),%esi + rorl $2,%ebx + xorl 40(%esp),%esi + xorl %ecx,%ebp + xorl (%esp),%esi + xorl %edx,%ebp + xorl 20(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,32(%esp) + movl %eax,%edi + roll $5,%edi + leal 1859775393(%esi,%ebp,1),%esi + addl %edi,%esi + # 20_39 25 + movl %eax,%ebp + movl 36(%esp),%edi + rorl $2,%eax + xorl 44(%esp),%edi + xorl %ebx,%ebp + xorl 4(%esp),%edi + xorl %ecx,%ebp + xorl 24(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,36(%esp) + movl %esi,%edx + roll $5,%edx + leal 1859775393(%edi,%ebp,1),%edi + addl %edx,%edi + # 20_39 26 + movl %esi,%ebp + movl 40(%esp),%edx + rorl $2,%esi + xorl 48(%esp),%edx + xorl %eax,%ebp + xorl 8(%esp),%edx + xorl %ebx,%ebp + xorl 28(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,40(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + # 20_39 27 + movl %edi,%ebp + movl 44(%esp),%ecx + rorl $2,%edi + xorl 52(%esp),%ecx + xorl %esi,%ebp + xorl 12(%esp),%ecx + xorl %eax,%ebp + xorl 32(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,44(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + # 20_39 28 + movl %edx,%ebp + movl 48(%esp),%ebx + rorl $2,%edx + xorl 56(%esp),%ebx + xorl %edi,%ebp + xorl 16(%esp),%ebx + xorl %esi,%ebp + xorl 36(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,48(%esp) + movl %ecx,%eax + roll $5,%eax + leal 1859775393(%ebx,%ebp,1),%ebx + addl %eax,%ebx + # 20_39 29 + movl %ecx,%ebp + movl 52(%esp),%eax + rorl $2,%ecx + xorl 60(%esp),%eax + xorl %edx,%ebp + xorl 20(%esp),%eax + xorl %edi,%ebp + xorl 40(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,52(%esp) + movl %ebx,%esi + roll $5,%esi + leal 1859775393(%eax,%ebp,1),%eax + addl %esi,%eax + # 20_39 30 + movl %ebx,%ebp + movl 56(%esp),%esi + rorl $2,%ebx + xorl (%esp),%esi + xorl %ecx,%ebp + xorl 24(%esp),%esi + xorl %edx,%ebp + xorl 44(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,56(%esp) + movl %eax,%edi + roll $5,%edi + leal 1859775393(%esi,%ebp,1),%esi + addl %edi,%esi + # 20_39 31 + movl %eax,%ebp + movl 60(%esp),%edi + rorl $2,%eax + xorl 4(%esp),%edi + xorl %ebx,%ebp + xorl 28(%esp),%edi + xorl %ecx,%ebp + xorl 48(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,60(%esp) + movl %esi,%edx + roll $5,%edx + leal 1859775393(%edi,%ebp,1),%edi + addl %edx,%edi + # 20_39 32 + movl %esi,%ebp + movl (%esp),%edx + rorl $2,%esi + xorl 8(%esp),%edx + xorl %eax,%ebp + xorl 32(%esp),%edx + xorl %ebx,%ebp + xorl 52(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + # 20_39 33 + movl %edi,%ebp + movl 4(%esp),%ecx + rorl $2,%edi + xorl 12(%esp),%ecx + xorl %esi,%ebp + xorl 36(%esp),%ecx + xorl %eax,%ebp + xorl 56(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,4(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + # 20_39 34 + movl %edx,%ebp + movl 8(%esp),%ebx + rorl $2,%edx + xorl 16(%esp),%ebx + xorl %edi,%ebp + xorl 40(%esp),%ebx + xorl %esi,%ebp + xorl 60(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,8(%esp) + movl %ecx,%eax + roll $5,%eax + leal 1859775393(%ebx,%ebp,1),%ebx + addl %eax,%ebx + # 20_39 35 + movl %ecx,%ebp + movl 12(%esp),%eax + rorl $2,%ecx + xorl 20(%esp),%eax + xorl %edx,%ebp + xorl 44(%esp),%eax + xorl %edi,%ebp + xorl (%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,12(%esp) + movl %ebx,%esi + roll $5,%esi + leal 1859775393(%eax,%ebp,1),%eax + addl %esi,%eax + # 20_39 36 + movl %ebx,%ebp + movl 16(%esp),%esi + rorl $2,%ebx + xorl 24(%esp),%esi + xorl %ecx,%ebp + xorl 48(%esp),%esi + xorl %edx,%ebp + xorl 4(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,16(%esp) + movl %eax,%edi + roll $5,%edi + leal 1859775393(%esi,%ebp,1),%esi + addl %edi,%esi + # 20_39 37 + movl %eax,%ebp + movl 20(%esp),%edi + rorl $2,%eax + xorl 28(%esp),%edi + xorl %ebx,%ebp + xorl 52(%esp),%edi + xorl %ecx,%ebp + xorl 8(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,20(%esp) + movl %esi,%edx + roll $5,%edx + leal 1859775393(%edi,%ebp,1),%edi + addl %edx,%edi + # 20_39 38 + movl %esi,%ebp + movl 24(%esp),%edx + rorl $2,%esi + xorl 32(%esp),%edx + xorl %eax,%ebp + xorl 56(%esp),%edx + xorl %ebx,%ebp + xorl 12(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,24(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + # 20_39 39 + movl %edi,%ebp + movl 28(%esp),%ecx + rorl $2,%edi + xorl 36(%esp),%ecx + xorl %esi,%ebp + xorl 60(%esp),%ecx + xorl %eax,%ebp + xorl 16(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,28(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + # 40_59 40 + movl 32(%esp),%ebx + movl 40(%esp),%ebp + xorl %ebp,%ebx + movl (%esp),%ebp + xorl %ebp,%ebx + movl 20(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,32(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + # 40_59 41 + movl 36(%esp),%eax + movl 44(%esp),%ebp + xorl %ebp,%eax + movl 4(%esp),%ebp + xorl %ebp,%eax + movl 24(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,36(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + # 40_59 42 + movl 40(%esp),%esi + movl 48(%esp),%ebp + xorl %ebp,%esi + movl 8(%esp),%ebp + xorl %ebp,%esi + movl 28(%esp),%ebp + xorl %ebp,%esi + movl %ebx,%ebp + roll $1,%esi + orl %ecx,%ebp + movl %esi,40(%esp) + andl %edx,%ebp + leal 2400959708(%esi,%edi,1),%esi + movl %ebx,%edi + rorl $2,%ebx + andl %ecx,%edi + orl %edi,%ebp + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + # 40_59 43 + movl 44(%esp),%edi + movl 52(%esp),%ebp + xorl %ebp,%edi + movl 12(%esp),%ebp + xorl %ebp,%edi + movl 32(%esp),%ebp + xorl %ebp,%edi + movl %eax,%ebp + roll $1,%edi + orl %ebx,%ebp + movl %edi,44(%esp) + andl %ecx,%ebp + leal 2400959708(%edi,%edx,1),%edi + movl %eax,%edx + rorl $2,%eax + andl %ebx,%edx + orl %edx,%ebp + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + # 40_59 44 + movl 48(%esp),%edx + movl 56(%esp),%ebp + xorl %ebp,%edx + movl 16(%esp),%ebp + xorl %ebp,%edx + movl 36(%esp),%ebp + xorl %ebp,%edx + movl %esi,%ebp + roll $1,%edx + orl %eax,%ebp + movl %edx,48(%esp) + andl %ebx,%ebp + leal 2400959708(%edx,%ecx,1),%edx + movl %esi,%ecx + rorl $2,%esi + andl %eax,%ecx + orl %ecx,%ebp + movl %edi,%ecx + roll $5,%ecx + addl %ebp,%edx + addl %ecx,%edx + # 40_59 45 + movl 52(%esp),%ecx + movl 60(%esp),%ebp + xorl %ebp,%ecx + movl 20(%esp),%ebp + xorl %ebp,%ecx + movl 40(%esp),%ebp + xorl %ebp,%ecx + movl %edi,%ebp + roll $1,%ecx + orl %esi,%ebp + movl %ecx,52(%esp) + andl %eax,%ebp + leal 2400959708(%ecx,%ebx,1),%ecx + movl %edi,%ebx + rorl $2,%edi + andl %esi,%ebx + orl %ebx,%ebp + movl %edx,%ebx + roll $5,%ebx + addl %ebp,%ecx + addl %ebx,%ecx + # 40_59 46 + movl 56(%esp),%ebx + movl (%esp),%ebp + xorl %ebp,%ebx + movl 24(%esp),%ebp + xorl %ebp,%ebx + movl 44(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,56(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + # 40_59 47 + movl 60(%esp),%eax + movl 4(%esp),%ebp + xorl %ebp,%eax + movl 28(%esp),%ebp + xorl %ebp,%eax + movl 48(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,60(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + # 40_59 48 + movl (%esp),%esi + movl 8(%esp),%ebp + xorl %ebp,%esi + movl 32(%esp),%ebp + xorl %ebp,%esi + movl 52(%esp),%ebp + xorl %ebp,%esi + movl %ebx,%ebp + roll $1,%esi + orl %ecx,%ebp + movl %esi,(%esp) + andl %edx,%ebp + leal 2400959708(%esi,%edi,1),%esi + movl %ebx,%edi + rorl $2,%ebx + andl %ecx,%edi + orl %edi,%ebp + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + # 40_59 49 + movl 4(%esp),%edi + movl 12(%esp),%ebp + xorl %ebp,%edi + movl 36(%esp),%ebp + xorl %ebp,%edi + movl 56(%esp),%ebp + xorl %ebp,%edi + movl %eax,%ebp + roll $1,%edi + orl %ebx,%ebp + movl %edi,4(%esp) + andl %ecx,%ebp + leal 2400959708(%edi,%edx,1),%edi + movl %eax,%edx + rorl $2,%eax + andl %ebx,%edx + orl %edx,%ebp + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + # 40_59 50 + movl 8(%esp),%edx + movl 16(%esp),%ebp + xorl %ebp,%edx + movl 40(%esp),%ebp + xorl %ebp,%edx + movl 60(%esp),%ebp + xorl %ebp,%edx + movl %esi,%ebp + roll $1,%edx + orl %eax,%ebp + movl %edx,8(%esp) + andl %ebx,%ebp + leal 2400959708(%edx,%ecx,1),%edx + movl %esi,%ecx + rorl $2,%esi + andl %eax,%ecx + orl %ecx,%ebp + movl %edi,%ecx + roll $5,%ecx + addl %ebp,%edx + addl %ecx,%edx + # 40_59 51 + movl 12(%esp),%ecx + movl 20(%esp),%ebp + xorl %ebp,%ecx + movl 44(%esp),%ebp + xorl %ebp,%ecx + movl (%esp),%ebp + xorl %ebp,%ecx + movl %edi,%ebp + roll $1,%ecx + orl %esi,%ebp + movl %ecx,12(%esp) + andl %eax,%ebp + leal 2400959708(%ecx,%ebx,1),%ecx + movl %edi,%ebx + rorl $2,%edi + andl %esi,%ebx + orl %ebx,%ebp + movl %edx,%ebx + roll $5,%ebx + addl %ebp,%ecx + addl %ebx,%ecx + # 40_59 52 + movl 16(%esp),%ebx + movl 24(%esp),%ebp + xorl %ebp,%ebx + movl 48(%esp),%ebp + xorl %ebp,%ebx + movl 4(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,16(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + # 40_59 53 + movl 20(%esp),%eax + movl 28(%esp),%ebp + xorl %ebp,%eax + movl 52(%esp),%ebp + xorl %ebp,%eax + movl 8(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,20(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + # 40_59 54 + movl 24(%esp),%esi + movl 32(%esp),%ebp + xorl %ebp,%esi + movl 56(%esp),%ebp + xorl %ebp,%esi + movl 12(%esp),%ebp + xorl %ebp,%esi + movl %ebx,%ebp + roll $1,%esi + orl %ecx,%ebp + movl %esi,24(%esp) + andl %edx,%ebp + leal 2400959708(%esi,%edi,1),%esi + movl %ebx,%edi + rorl $2,%ebx + andl %ecx,%edi + orl %edi,%ebp + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + # 40_59 55 + movl 28(%esp),%edi + movl 36(%esp),%ebp + xorl %ebp,%edi + movl 60(%esp),%ebp + xorl %ebp,%edi + movl 16(%esp),%ebp + xorl %ebp,%edi + movl %eax,%ebp + roll $1,%edi + orl %ebx,%ebp + movl %edi,28(%esp) + andl %ecx,%ebp + leal 2400959708(%edi,%edx,1),%edi + movl %eax,%edx + rorl $2,%eax + andl %ebx,%edx + orl %edx,%ebp + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + # 40_59 56 + movl 32(%esp),%edx + movl 40(%esp),%ebp + xorl %ebp,%edx + movl (%esp),%ebp + xorl %ebp,%edx + movl 20(%esp),%ebp + xorl %ebp,%edx + movl %esi,%ebp + roll $1,%edx + orl %eax,%ebp + movl %edx,32(%esp) + andl %ebx,%ebp + leal 2400959708(%edx,%ecx,1),%edx + movl %esi,%ecx + rorl $2,%esi + andl %eax,%ecx + orl %ecx,%ebp + movl %edi,%ecx + roll $5,%ecx + addl %ebp,%edx + addl %ecx,%edx + # 40_59 57 + movl 36(%esp),%ecx + movl 44(%esp),%ebp + xorl %ebp,%ecx + movl 4(%esp),%ebp + xorl %ebp,%ecx + movl 24(%esp),%ebp + xorl %ebp,%ecx + movl %edi,%ebp + roll $1,%ecx + orl %esi,%ebp + movl %ecx,36(%esp) + andl %eax,%ebp + leal 2400959708(%ecx,%ebx,1),%ecx + movl %edi,%ebx + rorl $2,%edi + andl %esi,%ebx + orl %ebx,%ebp + movl %edx,%ebx + roll $5,%ebx + addl %ebp,%ecx + addl %ebx,%ecx + # 40_59 58 + movl 40(%esp),%ebx + movl 48(%esp),%ebp + xorl %ebp,%ebx + movl 8(%esp),%ebp + xorl %ebp,%ebx + movl 28(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,40(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + # 40_59 59 + movl 44(%esp),%eax + movl 52(%esp),%ebp + xorl %ebp,%eax + movl 12(%esp),%ebp + xorl %ebp,%eax + movl 32(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,44(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + # 20_39 60 + movl %ebx,%ebp + movl 48(%esp),%esi + rorl $2,%ebx + xorl 56(%esp),%esi + xorl %ecx,%ebp + xorl 16(%esp),%esi + xorl %edx,%ebp + xorl 36(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,48(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + # 20_39 61 + movl %eax,%ebp + movl 52(%esp),%edi + rorl $2,%eax + xorl 60(%esp),%edi + xorl %ebx,%ebp + xorl 20(%esp),%edi + xorl %ecx,%ebp + xorl 40(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,52(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + # 20_39 62 + movl %esi,%ebp + movl 56(%esp),%edx + rorl $2,%esi + xorl (%esp),%edx + xorl %eax,%ebp + xorl 24(%esp),%edx + xorl %ebx,%ebp + xorl 44(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,56(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 3395469782(%edx,%ebp,1),%edx + addl %ecx,%edx + # 20_39 63 + movl %edi,%ebp + movl 60(%esp),%ecx + rorl $2,%edi + xorl 4(%esp),%ecx + xorl %esi,%ebp + xorl 28(%esp),%ecx + xorl %eax,%ebp + xorl 48(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,60(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 3395469782(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + # 20_39 64 + movl %edx,%ebp + movl (%esp),%ebx + rorl $2,%edx + xorl 8(%esp),%ebx + xorl %edi,%ebp + xorl 32(%esp),%ebx + xorl %esi,%ebp + xorl 52(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,(%esp) + movl %ecx,%eax + roll $5,%eax + leal 3395469782(%ebx,%ebp,1),%ebx + addl %eax,%ebx + # 20_39 65 + movl %ecx,%ebp + movl 4(%esp),%eax + rorl $2,%ecx + xorl 12(%esp),%eax + xorl %edx,%ebp + xorl 36(%esp),%eax + xorl %edi,%ebp + xorl 56(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,4(%esp) + movl %ebx,%esi + roll $5,%esi + leal 3395469782(%eax,%ebp,1),%eax + addl %esi,%eax + # 20_39 66 + movl %ebx,%ebp + movl 8(%esp),%esi + rorl $2,%ebx + xorl 16(%esp),%esi + xorl %ecx,%ebp + xorl 40(%esp),%esi + xorl %edx,%ebp + xorl 60(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,8(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + # 20_39 67 + movl %eax,%ebp + movl 12(%esp),%edi + rorl $2,%eax + xorl 20(%esp),%edi + xorl %ebx,%ebp + xorl 44(%esp),%edi + xorl %ecx,%ebp + xorl (%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,12(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + # 20_39 68 + movl %esi,%ebp + movl 16(%esp),%edx + rorl $2,%esi + xorl 24(%esp),%edx + xorl %eax,%ebp + xorl 48(%esp),%edx + xorl %ebx,%ebp + xorl 4(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,16(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 3395469782(%edx,%ebp,1),%edx + addl %ecx,%edx + # 20_39 69 + movl %edi,%ebp + movl 20(%esp),%ecx + rorl $2,%edi + xorl 28(%esp),%ecx + xorl %esi,%ebp + xorl 52(%esp),%ecx + xorl %eax,%ebp + xorl 8(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,20(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 3395469782(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + # 20_39 70 + movl %edx,%ebp + movl 24(%esp),%ebx + rorl $2,%edx + xorl 32(%esp),%ebx + xorl %edi,%ebp + xorl 56(%esp),%ebx + xorl %esi,%ebp + xorl 12(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,24(%esp) + movl %ecx,%eax + roll $5,%eax + leal 3395469782(%ebx,%ebp,1),%ebx + addl %eax,%ebx + # 20_39 71 + movl %ecx,%ebp + movl 28(%esp),%eax + rorl $2,%ecx + xorl 36(%esp),%eax + xorl %edx,%ebp + xorl 60(%esp),%eax + xorl %edi,%ebp + xorl 16(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,28(%esp) + movl %ebx,%esi + roll $5,%esi + leal 3395469782(%eax,%ebp,1),%eax + addl %esi,%eax + # 20_39 72 + movl %ebx,%ebp + movl 32(%esp),%esi + rorl $2,%ebx + xorl 40(%esp),%esi + xorl %ecx,%ebp + xorl (%esp),%esi + xorl %edx,%ebp + xorl 20(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,32(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + # 20_39 73 + movl %eax,%ebp + movl 36(%esp),%edi + rorl $2,%eax + xorl 44(%esp),%edi + xorl %ebx,%ebp + xorl 4(%esp),%edi + xorl %ecx,%ebp + xorl 24(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,36(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + # 20_39 74 + movl %esi,%ebp + movl 40(%esp),%edx + rorl $2,%esi + xorl 48(%esp),%edx + xorl %eax,%ebp + xorl 8(%esp),%edx + xorl %ebx,%ebp + xorl 28(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,40(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 3395469782(%edx,%ebp,1),%edx + addl %ecx,%edx + # 20_39 75 + movl %edi,%ebp + movl 44(%esp),%ecx + rorl $2,%edi + xorl 52(%esp),%ecx + xorl %esi,%ebp + xorl 12(%esp),%ecx + xorl %eax,%ebp + xorl 32(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,44(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 3395469782(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + # 20_39 76 + movl %edx,%ebp + movl 48(%esp),%ebx + rorl $2,%edx + xorl 56(%esp),%ebx + xorl %edi,%ebp + xorl 16(%esp),%ebx + xorl %esi,%ebp + xorl 36(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,48(%esp) + movl %ecx,%eax + roll $5,%eax + leal 3395469782(%ebx,%ebp,1),%ebx + addl %eax,%ebx + # 20_39 77 + movl %ecx,%ebp + movl 52(%esp),%eax + rorl $2,%ecx + xorl 60(%esp),%eax + xorl %edx,%ebp + xorl 20(%esp),%eax + xorl %edi,%ebp + xorl 40(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,52(%esp) + movl %ebx,%esi + roll $5,%esi + leal 3395469782(%eax,%ebp,1),%eax + addl %esi,%eax + # 20_39 78 + movl %ebx,%ebp + movl 56(%esp),%esi + rorl $2,%ebx + xorl (%esp),%esi + xorl %ecx,%ebp + xorl 24(%esp),%esi + xorl %edx,%ebp + xorl 44(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,56(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + # 20_39 79 + movl %eax,%ebp + movl 60(%esp),%edi + rorl $2,%eax + xorl 4(%esp),%edi + xorl %ebx,%ebp + xorl 28(%esp),%edi + xorl %ecx,%ebp + xorl 48(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,60(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + movl 84(%esp),%ebp + movl 88(%esp),%edx + addl (%ebp),%edi + addl 4(%ebp),%esi + addl 8(%ebp),%eax + addl 12(%ebp),%ebx + addl 16(%ebp),%ecx + movl %edi,(%ebp) + addl $64,%edx + movl %esi,4(%ebp) + cmpl 92(%esp),%edx + movl %eax,8(%ebp) + movl %ecx,%edi + movl %ebx,12(%ebp) + movl %edx,%esi + movl %ecx,16(%ebp) + jb .L000loop + addl $64,%esp + popl %edi + popl %esi + popl %ebx + popl %ebp + ret +.byte 83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115 +.byte 102,111,114,109,32,102,111,114,32,120,56,54,44,32,67,82 +.byte 89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112 +.byte 114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 diff --git a/project/src/crypto/sha1-586-elf.s b/project/src/crypto/sha1-586-elf.s new file mode 100644 index 0000000..15c7d36 --- /dev/null +++ b/project/src/crypto/sha1-586-elf.s @@ -0,0 +1,1442 @@ +.file "sha1-586-elf.s" +.text +.globl wit_sha1_block_data_order +.type wit_sha1_block_data_order,@function +.align 16 +wit_sha1_block_data_order: +.L_wit_sha1_block_data_order_begin: + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + movl 20(%esp),%ebp + movl 24(%esp),%esi + movl 28(%esp),%eax + subl $64,%esp + shll $6,%eax + addl %esi,%eax + movl %eax,92(%esp) + movl 16(%ebp),%edi +.align 16 +.L000loop: + movl (%esi),%eax + movl 4(%esi),%ebx + movl 8(%esi),%ecx + movl 12(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,(%esp) + movl %ebx,4(%esp) + movl %ecx,8(%esp) + movl %edx,12(%esp) + movl 16(%esi),%eax + movl 20(%esi),%ebx + movl 24(%esi),%ecx + movl 28(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,16(%esp) + movl %ebx,20(%esp) + movl %ecx,24(%esp) + movl %edx,28(%esp) + movl 32(%esi),%eax + movl 36(%esi),%ebx + movl 40(%esi),%ecx + movl 44(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,32(%esp) + movl %ebx,36(%esp) + movl %ecx,40(%esp) + movl %edx,44(%esp) + movl 48(%esi),%eax + movl 52(%esi),%ebx + movl 56(%esi),%ecx + movl 60(%esi),%edx + bswap %eax + bswap %ebx + bswap %ecx + bswap %edx + movl %eax,48(%esp) + movl %ebx,52(%esp) + movl %ecx,56(%esp) + movl %edx,60(%esp) + movl %esi,88(%esp) + movl (%ebp),%eax + movl 4(%ebp),%ebx + movl 8(%ebp),%ecx + movl 12(%ebp),%edx + + movl %ecx,%esi + movl %eax,%ebp + roll $5,%ebp + xorl %edx,%esi + addl %edi,%ebp + andl %ebx,%esi + movl (%esp),%edi + xorl %edx,%esi + rorl $2,%ebx + leal 1518500249(%ebp,%edi,1),%ebp + addl %esi,%ebp + + movl %ebx,%edi + movl %ebp,%esi + roll $5,%ebp + xorl %ecx,%edi + addl %edx,%ebp + andl %eax,%edi + movl 4(%esp),%edx + xorl %ecx,%edi + rorl $2,%eax + leal 1518500249(%ebp,%edx,1),%ebp + addl %edi,%ebp + + movl %eax,%edx + movl %ebp,%edi + roll $5,%ebp + xorl %ebx,%edx + addl %ecx,%ebp + andl %esi,%edx + movl 8(%esp),%ecx + xorl %ebx,%edx + rorl $2,%esi + leal 1518500249(%ebp,%ecx,1),%ebp + addl %edx,%ebp + + movl %esi,%ecx + movl %ebp,%edx + roll $5,%ebp + xorl %eax,%ecx + addl %ebx,%ebp + andl %edi,%ecx + movl 12(%esp),%ebx + xorl %eax,%ecx + rorl $2,%edi + leal 1518500249(%ebp,%ebx,1),%ebp + addl %ecx,%ebp + + movl %edi,%ebx + movl %ebp,%ecx + roll $5,%ebp + xorl %esi,%ebx + addl %eax,%ebp + andl %edx,%ebx + movl 16(%esp),%eax + xorl %esi,%ebx + rorl $2,%edx + leal 1518500249(%ebp,%eax,1),%ebp + addl %ebx,%ebp + + movl %edx,%eax + movl %ebp,%ebx + roll $5,%ebp + xorl %edi,%eax + addl %esi,%ebp + andl %ecx,%eax + movl 20(%esp),%esi + xorl %edi,%eax + rorl $2,%ecx + leal 1518500249(%ebp,%esi,1),%ebp + addl %eax,%ebp + + movl %ecx,%esi + movl %ebp,%eax + roll $5,%ebp + xorl %edx,%esi + addl %edi,%ebp + andl %ebx,%esi + movl 24(%esp),%edi + xorl %edx,%esi + rorl $2,%ebx + leal 1518500249(%ebp,%edi,1),%ebp + addl %esi,%ebp + + movl %ebx,%edi + movl %ebp,%esi + roll $5,%ebp + xorl %ecx,%edi + addl %edx,%ebp + andl %eax,%edi + movl 28(%esp),%edx + xorl %ecx,%edi + rorl $2,%eax + leal 1518500249(%ebp,%edx,1),%ebp + addl %edi,%ebp + + movl %eax,%edx + movl %ebp,%edi + roll $5,%ebp + xorl %ebx,%edx + addl %ecx,%ebp + andl %esi,%edx + movl 32(%esp),%ecx + xorl %ebx,%edx + rorl $2,%esi + leal 1518500249(%ebp,%ecx,1),%ebp + addl %edx,%ebp + + movl %esi,%ecx + movl %ebp,%edx + roll $5,%ebp + xorl %eax,%ecx + addl %ebx,%ebp + andl %edi,%ecx + movl 36(%esp),%ebx + xorl %eax,%ecx + rorl $2,%edi + leal 1518500249(%ebp,%ebx,1),%ebp + addl %ecx,%ebp + + movl %edi,%ebx + movl %ebp,%ecx + roll $5,%ebp + xorl %esi,%ebx + addl %eax,%ebp + andl %edx,%ebx + movl 40(%esp),%eax + xorl %esi,%ebx + rorl $2,%edx + leal 1518500249(%ebp,%eax,1),%ebp + addl %ebx,%ebp + + movl %edx,%eax + movl %ebp,%ebx + roll $5,%ebp + xorl %edi,%eax + addl %esi,%ebp + andl %ecx,%eax + movl 44(%esp),%esi + xorl %edi,%eax + rorl $2,%ecx + leal 1518500249(%ebp,%esi,1),%ebp + addl %eax,%ebp + + movl %ecx,%esi + movl %ebp,%eax + roll $5,%ebp + xorl %edx,%esi + addl %edi,%ebp + andl %ebx,%esi + movl 48(%esp),%edi + xorl %edx,%esi + rorl $2,%ebx + leal 1518500249(%ebp,%edi,1),%ebp + addl %esi,%ebp + + movl %ebx,%edi + movl %ebp,%esi + roll $5,%ebp + xorl %ecx,%edi + addl %edx,%ebp + andl %eax,%edi + movl 52(%esp),%edx + xorl %ecx,%edi + rorl $2,%eax + leal 1518500249(%ebp,%edx,1),%ebp + addl %edi,%ebp + + movl %eax,%edx + movl %ebp,%edi + roll $5,%ebp + xorl %ebx,%edx + addl %ecx,%ebp + andl %esi,%edx + movl 56(%esp),%ecx + xorl %ebx,%edx + rorl $2,%esi + leal 1518500249(%ebp,%ecx,1),%ebp + addl %edx,%ebp + + movl %esi,%ecx + movl %ebp,%edx + roll $5,%ebp + xorl %eax,%ecx + addl %ebx,%ebp + andl %edi,%ecx + movl 60(%esp),%ebx + xorl %eax,%ecx + rorl $2,%edi + leal 1518500249(%ebp,%ebx,1),%ebp + addl %ebp,%ecx + + movl (%esp),%ebx + movl %edi,%ebp + xorl 8(%esp),%ebx + xorl %esi,%ebp + xorl 32(%esp),%ebx + andl %edx,%ebp + rorl $2,%edx + xorl 52(%esp),%ebx + roll $1,%ebx + xorl %esi,%ebp + movl %ebx,(%esp) + leal 1518500249(%ebx,%eax,1),%ebx + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + + movl 4(%esp),%eax + movl %edx,%ebp + xorl 12(%esp),%eax + xorl %edi,%ebp + xorl 36(%esp),%eax + andl %ecx,%ebp + rorl $2,%ecx + xorl 56(%esp),%eax + roll $1,%eax + xorl %edi,%ebp + movl %eax,4(%esp) + leal 1518500249(%eax,%esi,1),%eax + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + + movl 8(%esp),%esi + movl %ecx,%ebp + xorl 16(%esp),%esi + xorl %edx,%ebp + xorl 40(%esp),%esi + andl %ebx,%ebp + rorl $2,%ebx + xorl 60(%esp),%esi + roll $1,%esi + xorl %edx,%ebp + movl %esi,8(%esp) + leal 1518500249(%esi,%edi,1),%esi + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + + movl 12(%esp),%edi + movl %ebx,%ebp + xorl 20(%esp),%edi + xorl %ecx,%ebp + xorl 44(%esp),%edi + andl %eax,%ebp + rorl $2,%eax + xorl (%esp),%edi + roll $1,%edi + xorl %ecx,%ebp + movl %edi,12(%esp) + leal 1518500249(%edi,%edx,1),%edi + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + + movl %esi,%ebp + movl 16(%esp),%edx + rorl $2,%esi + xorl 24(%esp),%edx + xorl %eax,%ebp + xorl 48(%esp),%edx + xorl %ebx,%ebp + xorl 4(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,16(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + + movl %edi,%ebp + movl 20(%esp),%ecx + rorl $2,%edi + xorl 28(%esp),%ecx + xorl %esi,%ebp + xorl 52(%esp),%ecx + xorl %eax,%ebp + xorl 8(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,20(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + + movl %edx,%ebp + movl 24(%esp),%ebx + rorl $2,%edx + xorl 32(%esp),%ebx + xorl %edi,%ebp + xorl 56(%esp),%ebx + xorl %esi,%ebp + xorl 12(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,24(%esp) + movl %ecx,%eax + roll $5,%eax + leal 1859775393(%ebx,%ebp,1),%ebx + addl %eax,%ebx + + movl %ecx,%ebp + movl 28(%esp),%eax + rorl $2,%ecx + xorl 36(%esp),%eax + xorl %edx,%ebp + xorl 60(%esp),%eax + xorl %edi,%ebp + xorl 16(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,28(%esp) + movl %ebx,%esi + roll $5,%esi + leal 1859775393(%eax,%ebp,1),%eax + addl %esi,%eax + + movl %ebx,%ebp + movl 32(%esp),%esi + rorl $2,%ebx + xorl 40(%esp),%esi + xorl %ecx,%ebp + xorl (%esp),%esi + xorl %edx,%ebp + xorl 20(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,32(%esp) + movl %eax,%edi + roll $5,%edi + leal 1859775393(%esi,%ebp,1),%esi + addl %edi,%esi + + movl %eax,%ebp + movl 36(%esp),%edi + rorl $2,%eax + xorl 44(%esp),%edi + xorl %ebx,%ebp + xorl 4(%esp),%edi + xorl %ecx,%ebp + xorl 24(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,36(%esp) + movl %esi,%edx + roll $5,%edx + leal 1859775393(%edi,%ebp,1),%edi + addl %edx,%edi + + movl %esi,%ebp + movl 40(%esp),%edx + rorl $2,%esi + xorl 48(%esp),%edx + xorl %eax,%ebp + xorl 8(%esp),%edx + xorl %ebx,%ebp + xorl 28(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,40(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + + movl %edi,%ebp + movl 44(%esp),%ecx + rorl $2,%edi + xorl 52(%esp),%ecx + xorl %esi,%ebp + xorl 12(%esp),%ecx + xorl %eax,%ebp + xorl 32(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,44(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + + movl %edx,%ebp + movl 48(%esp),%ebx + rorl $2,%edx + xorl 56(%esp),%ebx + xorl %edi,%ebp + xorl 16(%esp),%ebx + xorl %esi,%ebp + xorl 36(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,48(%esp) + movl %ecx,%eax + roll $5,%eax + leal 1859775393(%ebx,%ebp,1),%ebx + addl %eax,%ebx + + movl %ecx,%ebp + movl 52(%esp),%eax + rorl $2,%ecx + xorl 60(%esp),%eax + xorl %edx,%ebp + xorl 20(%esp),%eax + xorl %edi,%ebp + xorl 40(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,52(%esp) + movl %ebx,%esi + roll $5,%esi + leal 1859775393(%eax,%ebp,1),%eax + addl %esi,%eax + + movl %ebx,%ebp + movl 56(%esp),%esi + rorl $2,%ebx + xorl (%esp),%esi + xorl %ecx,%ebp + xorl 24(%esp),%esi + xorl %edx,%ebp + xorl 44(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,56(%esp) + movl %eax,%edi + roll $5,%edi + leal 1859775393(%esi,%ebp,1),%esi + addl %edi,%esi + + movl %eax,%ebp + movl 60(%esp),%edi + rorl $2,%eax + xorl 4(%esp),%edi + xorl %ebx,%ebp + xorl 28(%esp),%edi + xorl %ecx,%ebp + xorl 48(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,60(%esp) + movl %esi,%edx + roll $5,%edx + leal 1859775393(%edi,%ebp,1),%edi + addl %edx,%edi + + movl %esi,%ebp + movl (%esp),%edx + rorl $2,%esi + xorl 8(%esp),%edx + xorl %eax,%ebp + xorl 32(%esp),%edx + xorl %ebx,%ebp + xorl 52(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + + movl %edi,%ebp + movl 4(%esp),%ecx + rorl $2,%edi + xorl 12(%esp),%ecx + xorl %esi,%ebp + xorl 36(%esp),%ecx + xorl %eax,%ebp + xorl 56(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,4(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + + movl %edx,%ebp + movl 8(%esp),%ebx + rorl $2,%edx + xorl 16(%esp),%ebx + xorl %edi,%ebp + xorl 40(%esp),%ebx + xorl %esi,%ebp + xorl 60(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,8(%esp) + movl %ecx,%eax + roll $5,%eax + leal 1859775393(%ebx,%ebp,1),%ebx + addl %eax,%ebx + + movl %ecx,%ebp + movl 12(%esp),%eax + rorl $2,%ecx + xorl 20(%esp),%eax + xorl %edx,%ebp + xorl 44(%esp),%eax + xorl %edi,%ebp + xorl (%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,12(%esp) + movl %ebx,%esi + roll $5,%esi + leal 1859775393(%eax,%ebp,1),%eax + addl %esi,%eax + + movl %ebx,%ebp + movl 16(%esp),%esi + rorl $2,%ebx + xorl 24(%esp),%esi + xorl %ecx,%ebp + xorl 48(%esp),%esi + xorl %edx,%ebp + xorl 4(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,16(%esp) + movl %eax,%edi + roll $5,%edi + leal 1859775393(%esi,%ebp,1),%esi + addl %edi,%esi + + movl %eax,%ebp + movl 20(%esp),%edi + rorl $2,%eax + xorl 28(%esp),%edi + xorl %ebx,%ebp + xorl 52(%esp),%edi + xorl %ecx,%ebp + xorl 8(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,20(%esp) + movl %esi,%edx + roll $5,%edx + leal 1859775393(%edi,%ebp,1),%edi + addl %edx,%edi + + movl %esi,%ebp + movl 24(%esp),%edx + rorl $2,%esi + xorl 32(%esp),%edx + xorl %eax,%ebp + xorl 56(%esp),%edx + xorl %ebx,%ebp + xorl 12(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,24(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 1859775393(%edx,%ebp,1),%edx + addl %ecx,%edx + + movl %edi,%ebp + movl 28(%esp),%ecx + rorl $2,%edi + xorl 36(%esp),%ecx + xorl %esi,%ebp + xorl 60(%esp),%ecx + xorl %eax,%ebp + xorl 16(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,28(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 1859775393(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + + movl 32(%esp),%ebx + movl 40(%esp),%ebp + xorl %ebp,%ebx + movl (%esp),%ebp + xorl %ebp,%ebx + movl 20(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,32(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + + movl 36(%esp),%eax + movl 44(%esp),%ebp + xorl %ebp,%eax + movl 4(%esp),%ebp + xorl %ebp,%eax + movl 24(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,36(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + + movl 40(%esp),%esi + movl 48(%esp),%ebp + xorl %ebp,%esi + movl 8(%esp),%ebp + xorl %ebp,%esi + movl 28(%esp),%ebp + xorl %ebp,%esi + movl %ebx,%ebp + roll $1,%esi + orl %ecx,%ebp + movl %esi,40(%esp) + andl %edx,%ebp + leal 2400959708(%esi,%edi,1),%esi + movl %ebx,%edi + rorl $2,%ebx + andl %ecx,%edi + orl %edi,%ebp + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + + movl 44(%esp),%edi + movl 52(%esp),%ebp + xorl %ebp,%edi + movl 12(%esp),%ebp + xorl %ebp,%edi + movl 32(%esp),%ebp + xorl %ebp,%edi + movl %eax,%ebp + roll $1,%edi + orl %ebx,%ebp + movl %edi,44(%esp) + andl %ecx,%ebp + leal 2400959708(%edi,%edx,1),%edi + movl %eax,%edx + rorl $2,%eax + andl %ebx,%edx + orl %edx,%ebp + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + + movl 48(%esp),%edx + movl 56(%esp),%ebp + xorl %ebp,%edx + movl 16(%esp),%ebp + xorl %ebp,%edx + movl 36(%esp),%ebp + xorl %ebp,%edx + movl %esi,%ebp + roll $1,%edx + orl %eax,%ebp + movl %edx,48(%esp) + andl %ebx,%ebp + leal 2400959708(%edx,%ecx,1),%edx + movl %esi,%ecx + rorl $2,%esi + andl %eax,%ecx + orl %ecx,%ebp + movl %edi,%ecx + roll $5,%ecx + addl %ebp,%edx + addl %ecx,%edx + + movl 52(%esp),%ecx + movl 60(%esp),%ebp + xorl %ebp,%ecx + movl 20(%esp),%ebp + xorl %ebp,%ecx + movl 40(%esp),%ebp + xorl %ebp,%ecx + movl %edi,%ebp + roll $1,%ecx + orl %esi,%ebp + movl %ecx,52(%esp) + andl %eax,%ebp + leal 2400959708(%ecx,%ebx,1),%ecx + movl %edi,%ebx + rorl $2,%edi + andl %esi,%ebx + orl %ebx,%ebp + movl %edx,%ebx + roll $5,%ebx + addl %ebp,%ecx + addl %ebx,%ecx + + movl 56(%esp),%ebx + movl (%esp),%ebp + xorl %ebp,%ebx + movl 24(%esp),%ebp + xorl %ebp,%ebx + movl 44(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,56(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + + movl 60(%esp),%eax + movl 4(%esp),%ebp + xorl %ebp,%eax + movl 28(%esp),%ebp + xorl %ebp,%eax + movl 48(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,60(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + + movl (%esp),%esi + movl 8(%esp),%ebp + xorl %ebp,%esi + movl 32(%esp),%ebp + xorl %ebp,%esi + movl 52(%esp),%ebp + xorl %ebp,%esi + movl %ebx,%ebp + roll $1,%esi + orl %ecx,%ebp + movl %esi,(%esp) + andl %edx,%ebp + leal 2400959708(%esi,%edi,1),%esi + movl %ebx,%edi + rorl $2,%ebx + andl %ecx,%edi + orl %edi,%ebp + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + + movl 4(%esp),%edi + movl 12(%esp),%ebp + xorl %ebp,%edi + movl 36(%esp),%ebp + xorl %ebp,%edi + movl 56(%esp),%ebp + xorl %ebp,%edi + movl %eax,%ebp + roll $1,%edi + orl %ebx,%ebp + movl %edi,4(%esp) + andl %ecx,%ebp + leal 2400959708(%edi,%edx,1),%edi + movl %eax,%edx + rorl $2,%eax + andl %ebx,%edx + orl %edx,%ebp + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + + movl 8(%esp),%edx + movl 16(%esp),%ebp + xorl %ebp,%edx + movl 40(%esp),%ebp + xorl %ebp,%edx + movl 60(%esp),%ebp + xorl %ebp,%edx + movl %esi,%ebp + roll $1,%edx + orl %eax,%ebp + movl %edx,8(%esp) + andl %ebx,%ebp + leal 2400959708(%edx,%ecx,1),%edx + movl %esi,%ecx + rorl $2,%esi + andl %eax,%ecx + orl %ecx,%ebp + movl %edi,%ecx + roll $5,%ecx + addl %ebp,%edx + addl %ecx,%edx + + movl 12(%esp),%ecx + movl 20(%esp),%ebp + xorl %ebp,%ecx + movl 44(%esp),%ebp + xorl %ebp,%ecx + movl (%esp),%ebp + xorl %ebp,%ecx + movl %edi,%ebp + roll $1,%ecx + orl %esi,%ebp + movl %ecx,12(%esp) + andl %eax,%ebp + leal 2400959708(%ecx,%ebx,1),%ecx + movl %edi,%ebx + rorl $2,%edi + andl %esi,%ebx + orl %ebx,%ebp + movl %edx,%ebx + roll $5,%ebx + addl %ebp,%ecx + addl %ebx,%ecx + + movl 16(%esp),%ebx + movl 24(%esp),%ebp + xorl %ebp,%ebx + movl 48(%esp),%ebp + xorl %ebp,%ebx + movl 4(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,16(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + + movl 20(%esp),%eax + movl 28(%esp),%ebp + xorl %ebp,%eax + movl 52(%esp),%ebp + xorl %ebp,%eax + movl 8(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,20(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + + movl 24(%esp),%esi + movl 32(%esp),%ebp + xorl %ebp,%esi + movl 56(%esp),%ebp + xorl %ebp,%esi + movl 12(%esp),%ebp + xorl %ebp,%esi + movl %ebx,%ebp + roll $1,%esi + orl %ecx,%ebp + movl %esi,24(%esp) + andl %edx,%ebp + leal 2400959708(%esi,%edi,1),%esi + movl %ebx,%edi + rorl $2,%ebx + andl %ecx,%edi + orl %edi,%ebp + movl %eax,%edi + roll $5,%edi + addl %ebp,%esi + addl %edi,%esi + + movl 28(%esp),%edi + movl 36(%esp),%ebp + xorl %ebp,%edi + movl 60(%esp),%ebp + xorl %ebp,%edi + movl 16(%esp),%ebp + xorl %ebp,%edi + movl %eax,%ebp + roll $1,%edi + orl %ebx,%ebp + movl %edi,28(%esp) + andl %ecx,%ebp + leal 2400959708(%edi,%edx,1),%edi + movl %eax,%edx + rorl $2,%eax + andl %ebx,%edx + orl %edx,%ebp + movl %esi,%edx + roll $5,%edx + addl %ebp,%edi + addl %edx,%edi + + movl 32(%esp),%edx + movl 40(%esp),%ebp + xorl %ebp,%edx + movl (%esp),%ebp + xorl %ebp,%edx + movl 20(%esp),%ebp + xorl %ebp,%edx + movl %esi,%ebp + roll $1,%edx + orl %eax,%ebp + movl %edx,32(%esp) + andl %ebx,%ebp + leal 2400959708(%edx,%ecx,1),%edx + movl %esi,%ecx + rorl $2,%esi + andl %eax,%ecx + orl %ecx,%ebp + movl %edi,%ecx + roll $5,%ecx + addl %ebp,%edx + addl %ecx,%edx + + movl 36(%esp),%ecx + movl 44(%esp),%ebp + xorl %ebp,%ecx + movl 4(%esp),%ebp + xorl %ebp,%ecx + movl 24(%esp),%ebp + xorl %ebp,%ecx + movl %edi,%ebp + roll $1,%ecx + orl %esi,%ebp + movl %ecx,36(%esp) + andl %eax,%ebp + leal 2400959708(%ecx,%ebx,1),%ecx + movl %edi,%ebx + rorl $2,%edi + andl %esi,%ebx + orl %ebx,%ebp + movl %edx,%ebx + roll $5,%ebx + addl %ebp,%ecx + addl %ebx,%ecx + + movl 40(%esp),%ebx + movl 48(%esp),%ebp + xorl %ebp,%ebx + movl 8(%esp),%ebp + xorl %ebp,%ebx + movl 28(%esp),%ebp + xorl %ebp,%ebx + movl %edx,%ebp + roll $1,%ebx + orl %edi,%ebp + movl %ebx,40(%esp) + andl %esi,%ebp + leal 2400959708(%ebx,%eax,1),%ebx + movl %edx,%eax + rorl $2,%edx + andl %edi,%eax + orl %eax,%ebp + movl %ecx,%eax + roll $5,%eax + addl %ebp,%ebx + addl %eax,%ebx + + movl 44(%esp),%eax + movl 52(%esp),%ebp + xorl %ebp,%eax + movl 12(%esp),%ebp + xorl %ebp,%eax + movl 32(%esp),%ebp + xorl %ebp,%eax + movl %ecx,%ebp + roll $1,%eax + orl %edx,%ebp + movl %eax,44(%esp) + andl %edi,%ebp + leal 2400959708(%eax,%esi,1),%eax + movl %ecx,%esi + rorl $2,%ecx + andl %edx,%esi + orl %esi,%ebp + movl %ebx,%esi + roll $5,%esi + addl %ebp,%eax + addl %esi,%eax + + movl %ebx,%ebp + movl 48(%esp),%esi + rorl $2,%ebx + xorl 56(%esp),%esi + xorl %ecx,%ebp + xorl 16(%esp),%esi + xorl %edx,%ebp + xorl 36(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,48(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + + movl %eax,%ebp + movl 52(%esp),%edi + rorl $2,%eax + xorl 60(%esp),%edi + xorl %ebx,%ebp + xorl 20(%esp),%edi + xorl %ecx,%ebp + xorl 40(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,52(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + + movl %esi,%ebp + movl 56(%esp),%edx + rorl $2,%esi + xorl (%esp),%edx + xorl %eax,%ebp + xorl 24(%esp),%edx + xorl %ebx,%ebp + xorl 44(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,56(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 3395469782(%edx,%ebp,1),%edx + addl %ecx,%edx + + movl %edi,%ebp + movl 60(%esp),%ecx + rorl $2,%edi + xorl 4(%esp),%ecx + xorl %esi,%ebp + xorl 28(%esp),%ecx + xorl %eax,%ebp + xorl 48(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,60(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 3395469782(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + + movl %edx,%ebp + movl (%esp),%ebx + rorl $2,%edx + xorl 8(%esp),%ebx + xorl %edi,%ebp + xorl 32(%esp),%ebx + xorl %esi,%ebp + xorl 52(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,(%esp) + movl %ecx,%eax + roll $5,%eax + leal 3395469782(%ebx,%ebp,1),%ebx + addl %eax,%ebx + + movl %ecx,%ebp + movl 4(%esp),%eax + rorl $2,%ecx + xorl 12(%esp),%eax + xorl %edx,%ebp + xorl 36(%esp),%eax + xorl %edi,%ebp + xorl 56(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,4(%esp) + movl %ebx,%esi + roll $5,%esi + leal 3395469782(%eax,%ebp,1),%eax + addl %esi,%eax + + movl %ebx,%ebp + movl 8(%esp),%esi + rorl $2,%ebx + xorl 16(%esp),%esi + xorl %ecx,%ebp + xorl 40(%esp),%esi + xorl %edx,%ebp + xorl 60(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,8(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + + movl %eax,%ebp + movl 12(%esp),%edi + rorl $2,%eax + xorl 20(%esp),%edi + xorl %ebx,%ebp + xorl 44(%esp),%edi + xorl %ecx,%ebp + xorl (%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,12(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + + movl %esi,%ebp + movl 16(%esp),%edx + rorl $2,%esi + xorl 24(%esp),%edx + xorl %eax,%ebp + xorl 48(%esp),%edx + xorl %ebx,%ebp + xorl 4(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,16(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 3395469782(%edx,%ebp,1),%edx + addl %ecx,%edx + + movl %edi,%ebp + movl 20(%esp),%ecx + rorl $2,%edi + xorl 28(%esp),%ecx + xorl %esi,%ebp + xorl 52(%esp),%ecx + xorl %eax,%ebp + xorl 8(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,20(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 3395469782(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + + movl %edx,%ebp + movl 24(%esp),%ebx + rorl $2,%edx + xorl 32(%esp),%ebx + xorl %edi,%ebp + xorl 56(%esp),%ebx + xorl %esi,%ebp + xorl 12(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,24(%esp) + movl %ecx,%eax + roll $5,%eax + leal 3395469782(%ebx,%ebp,1),%ebx + addl %eax,%ebx + + movl %ecx,%ebp + movl 28(%esp),%eax + rorl $2,%ecx + xorl 36(%esp),%eax + xorl %edx,%ebp + xorl 60(%esp),%eax + xorl %edi,%ebp + xorl 16(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,28(%esp) + movl %ebx,%esi + roll $5,%esi + leal 3395469782(%eax,%ebp,1),%eax + addl %esi,%eax + + movl %ebx,%ebp + movl 32(%esp),%esi + rorl $2,%ebx + xorl 40(%esp),%esi + xorl %ecx,%ebp + xorl (%esp),%esi + xorl %edx,%ebp + xorl 20(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,32(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + + movl %eax,%ebp + movl 36(%esp),%edi + rorl $2,%eax + xorl 44(%esp),%edi + xorl %ebx,%ebp + xorl 4(%esp),%edi + xorl %ecx,%ebp + xorl 24(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,36(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + + movl %esi,%ebp + movl 40(%esp),%edx + rorl $2,%esi + xorl 48(%esp),%edx + xorl %eax,%ebp + xorl 8(%esp),%edx + xorl %ebx,%ebp + xorl 28(%esp),%edx + roll $1,%edx + addl %ecx,%ebp + movl %edx,40(%esp) + movl %edi,%ecx + roll $5,%ecx + leal 3395469782(%edx,%ebp,1),%edx + addl %ecx,%edx + + movl %edi,%ebp + movl 44(%esp),%ecx + rorl $2,%edi + xorl 52(%esp),%ecx + xorl %esi,%ebp + xorl 12(%esp),%ecx + xorl %eax,%ebp + xorl 32(%esp),%ecx + roll $1,%ecx + addl %ebx,%ebp + movl %ecx,44(%esp) + movl %edx,%ebx + roll $5,%ebx + leal 3395469782(%ecx,%ebp,1),%ecx + addl %ebx,%ecx + + movl %edx,%ebp + movl 48(%esp),%ebx + rorl $2,%edx + xorl 56(%esp),%ebx + xorl %edi,%ebp + xorl 16(%esp),%ebx + xorl %esi,%ebp + xorl 36(%esp),%ebx + roll $1,%ebx + addl %eax,%ebp + movl %ebx,48(%esp) + movl %ecx,%eax + roll $5,%eax + leal 3395469782(%ebx,%ebp,1),%ebx + addl %eax,%ebx + + movl %ecx,%ebp + movl 52(%esp),%eax + rorl $2,%ecx + xorl 60(%esp),%eax + xorl %edx,%ebp + xorl 20(%esp),%eax + xorl %edi,%ebp + xorl 40(%esp),%eax + roll $1,%eax + addl %esi,%ebp + movl %eax,52(%esp) + movl %ebx,%esi + roll $5,%esi + leal 3395469782(%eax,%ebp,1),%eax + addl %esi,%eax + + movl %ebx,%ebp + movl 56(%esp),%esi + rorl $2,%ebx + xorl (%esp),%esi + xorl %ecx,%ebp + xorl 24(%esp),%esi + xorl %edx,%ebp + xorl 44(%esp),%esi + roll $1,%esi + addl %edi,%ebp + movl %esi,56(%esp) + movl %eax,%edi + roll $5,%edi + leal 3395469782(%esi,%ebp,1),%esi + addl %edi,%esi + + movl %eax,%ebp + movl 60(%esp),%edi + rorl $2,%eax + xorl 4(%esp),%edi + xorl %ebx,%ebp + xorl 28(%esp),%edi + xorl %ecx,%ebp + xorl 48(%esp),%edi + roll $1,%edi + addl %edx,%ebp + movl %edi,60(%esp) + movl %esi,%edx + roll $5,%edx + leal 3395469782(%edi,%ebp,1),%edi + addl %edx,%edi + movl 84(%esp),%ebp + movl 88(%esp),%edx + addl (%ebp),%edi + addl 4(%ebp),%esi + addl 8(%ebp),%eax + addl 12(%ebp),%ebx + addl 16(%ebp),%ecx + movl %edi,(%ebp) + addl $64,%edx + movl %esi,4(%ebp) + cmpl 92(%esp),%edx + movl %eax,8(%ebp) + movl %ecx,%edi + movl %ebx,12(%ebp) + movl %edx,%esi + movl %ecx,16(%ebp) + jb .L000loop + addl $64,%esp + popl %edi + popl %esi + popl %ebx + popl %ebp + ret +.size wit_sha1_block_data_order,.-.L_wit_sha1_block_data_order_begin +.byte 83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115 +.byte 102,111,114,109,32,102,111,114,32,120,56,54,44,32,67,82 +.byte 89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112 +.byte 114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 diff --git a/project/src/crypto/sha1-x86_64.s b/project/src/crypto/sha1-x86_64.s new file mode 100644 index 0000000..f0ae885 --- /dev/null +++ b/project/src/crypto/sha1-x86_64.s @@ -0,0 +1,1282 @@ +.text +.globl wit_sha1_block_data_order +.type wit_sha1_block_data_order,@function +.align 16 +wit_sha1_block_data_order: + pushq %rbx + pushq %rbp + pushq %r12 + movq %rsp,%r11 + movq %rdi,%r8 + subq $72,%rsp + movq %rsi,%r9 + andq $-64,%rsp + movq %rdx,%r10 + movq %r11,64(%rsp) +.Lprologue: + + movl 0(%r8),%edx + movl 4(%r8),%esi + movl 8(%r8),%edi + movl 12(%r8),%ebp + movl 16(%r8),%r11d +.align 4 +.Lloop: + movl 0(%r9),%eax + bswapl %eax + movl %eax,0(%rsp) + leal 1518500249(%rax,%r11,1),%r12d + movl %edi,%ebx + movl 4(%r9),%eax + movl %edx,%r11d + xorl %ebp,%ebx + bswapl %eax + roll $5,%r11d + andl %esi,%ebx + movl %eax,4(%rsp) + addl %r11d,%r12d + xorl %ebp,%ebx + roll $30,%esi + addl %ebx,%r12d + leal 1518500249(%rax,%rbp,1),%r11d + movl %esi,%ebx + movl 8(%r9),%eax + movl %r12d,%ebp + xorl %edi,%ebx + bswapl %eax + roll $5,%ebp + andl %edx,%ebx + movl %eax,8(%rsp) + addl %ebp,%r11d + xorl %edi,%ebx + roll $30,%edx + addl %ebx,%r11d + leal 1518500249(%rax,%rdi,1),%ebp + movl %edx,%ebx + movl 12(%r9),%eax + movl %r11d,%edi + xorl %esi,%ebx + bswapl %eax + roll $5,%edi + andl %r12d,%ebx + movl %eax,12(%rsp) + addl %edi,%ebp + xorl %esi,%ebx + roll $30,%r12d + addl %ebx,%ebp + leal 1518500249(%rax,%rsi,1),%edi + movl %r12d,%ebx + movl 16(%r9),%eax + movl %ebp,%esi + xorl %edx,%ebx + bswapl %eax + roll $5,%esi + andl %r11d,%ebx + movl %eax,16(%rsp) + addl %esi,%edi + xorl %edx,%ebx + roll $30,%r11d + addl %ebx,%edi + leal 1518500249(%rax,%rdx,1),%esi + movl %r11d,%ebx + movl 20(%r9),%eax + movl %edi,%edx + xorl %r12d,%ebx + bswapl %eax + roll $5,%edx + andl %ebp,%ebx + movl %eax,20(%rsp) + addl %edx,%esi + xorl %r12d,%ebx + roll $30,%ebp + addl %ebx,%esi + leal 1518500249(%rax,%r12,1),%edx + movl %ebp,%ebx + movl 24(%r9),%eax + movl %esi,%r12d + xorl %r11d,%ebx + bswapl %eax + roll $5,%r12d + andl %edi,%ebx + movl %eax,24(%rsp) + addl %r12d,%edx + xorl %r11d,%ebx + roll $30,%edi + addl %ebx,%edx + leal 1518500249(%rax,%r11,1),%r12d + movl %edi,%ebx + movl 28(%r9),%eax + movl %edx,%r11d + xorl %ebp,%ebx + bswapl %eax + roll $5,%r11d + andl %esi,%ebx + movl %eax,28(%rsp) + addl %r11d,%r12d + xorl %ebp,%ebx + roll $30,%esi + addl %ebx,%r12d + leal 1518500249(%rax,%rbp,1),%r11d + movl %esi,%ebx + movl 32(%r9),%eax + movl %r12d,%ebp + xorl %edi,%ebx + bswapl %eax + roll $5,%ebp + andl %edx,%ebx + movl %eax,32(%rsp) + addl %ebp,%r11d + xorl %edi,%ebx + roll $30,%edx + addl %ebx,%r11d + leal 1518500249(%rax,%rdi,1),%ebp + movl %edx,%ebx + movl 36(%r9),%eax + movl %r11d,%edi + xorl %esi,%ebx + bswapl %eax + roll $5,%edi + andl %r12d,%ebx + movl %eax,36(%rsp) + addl %edi,%ebp + xorl %esi,%ebx + roll $30,%r12d + addl %ebx,%ebp + leal 1518500249(%rax,%rsi,1),%edi + movl %r12d,%ebx + movl 40(%r9),%eax + movl %ebp,%esi + xorl %edx,%ebx + bswapl %eax + roll $5,%esi + andl %r11d,%ebx + movl %eax,40(%rsp) + addl %esi,%edi + xorl %edx,%ebx + roll $30,%r11d + addl %ebx,%edi + leal 1518500249(%rax,%rdx,1),%esi + movl %r11d,%ebx + movl 44(%r9),%eax + movl %edi,%edx + xorl %r12d,%ebx + bswapl %eax + roll $5,%edx + andl %ebp,%ebx + movl %eax,44(%rsp) + addl %edx,%esi + xorl %r12d,%ebx + roll $30,%ebp + addl %ebx,%esi + leal 1518500249(%rax,%r12,1),%edx + movl %ebp,%ebx + movl 48(%r9),%eax + movl %esi,%r12d + xorl %r11d,%ebx + bswapl %eax + roll $5,%r12d + andl %edi,%ebx + movl %eax,48(%rsp) + addl %r12d,%edx + xorl %r11d,%ebx + roll $30,%edi + addl %ebx,%edx + leal 1518500249(%rax,%r11,1),%r12d + movl %edi,%ebx + movl 52(%r9),%eax + movl %edx,%r11d + xorl %ebp,%ebx + bswapl %eax + roll $5,%r11d + andl %esi,%ebx + movl %eax,52(%rsp) + addl %r11d,%r12d + xorl %ebp,%ebx + roll $30,%esi + addl %ebx,%r12d + leal 1518500249(%rax,%rbp,1),%r11d + movl %esi,%ebx + movl 56(%r9),%eax + movl %r12d,%ebp + xorl %edi,%ebx + bswapl %eax + roll $5,%ebp + andl %edx,%ebx + movl %eax,56(%rsp) + addl %ebp,%r11d + xorl %edi,%ebx + roll $30,%edx + addl %ebx,%r11d + leal 1518500249(%rax,%rdi,1),%ebp + movl %edx,%ebx + movl 60(%r9),%eax + movl %r11d,%edi + xorl %esi,%ebx + bswapl %eax + roll $5,%edi + andl %r12d,%ebx + movl %eax,60(%rsp) + addl %edi,%ebp + xorl %esi,%ebx + roll $30,%r12d + addl %ebx,%ebp + leal 1518500249(%rax,%rsi,1),%edi + movl 0(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 8(%rsp),%eax + xorl %edx,%ebx + roll $5,%esi + xorl 32(%rsp),%eax + andl %r11d,%ebx + addl %esi,%edi + xorl 52(%rsp),%eax + xorl %edx,%ebx + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,0(%rsp) + leal 1518500249(%rax,%rdx,1),%esi + movl 4(%rsp),%eax + movl %r11d,%ebx + movl %edi,%edx + xorl 12(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edx + xorl 36(%rsp),%eax + andl %ebp,%ebx + addl %edx,%esi + xorl 56(%rsp),%eax + xorl %r12d,%ebx + roll $30,%ebp + addl %ebx,%esi + roll $1,%eax + movl %eax,4(%rsp) + leal 1518500249(%rax,%r12,1),%edx + movl 8(%rsp),%eax + movl %ebp,%ebx + movl %esi,%r12d + xorl 16(%rsp),%eax + xorl %r11d,%ebx + roll $5,%r12d + xorl 40(%rsp),%eax + andl %edi,%ebx + addl %r12d,%edx + xorl 60(%rsp),%eax + xorl %r11d,%ebx + roll $30,%edi + addl %ebx,%edx + roll $1,%eax + movl %eax,8(%rsp) + leal 1518500249(%rax,%r11,1),%r12d + movl 12(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 20(%rsp),%eax + xorl %ebp,%ebx + roll $5,%r11d + xorl 44(%rsp),%eax + andl %esi,%ebx + addl %r11d,%r12d + xorl 0(%rsp),%eax + xorl %ebp,%ebx + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + movl %eax,12(%rsp) + leal 1518500249(%rax,%rbp,1),%r11d + movl 16(%rsp),%eax + movl %esi,%ebx + movl %r12d,%ebp + xorl 24(%rsp),%eax + xorl %edi,%ebx + roll $5,%ebp + xorl 48(%rsp),%eax + andl %edx,%ebx + addl %ebp,%r11d + xorl 4(%rsp),%eax + xorl %edi,%ebx + roll $30,%edx + addl %ebx,%r11d + roll $1,%eax + movl %eax,16(%rsp) + leal 1859775393(%rax,%rdi,1),%ebp + movl 20(%rsp),%eax + movl %edx,%ebx + movl %r11d,%edi + xorl 28(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edi + xorl 52(%rsp),%eax + xorl %esi,%ebx + addl %edi,%ebp + xorl 8(%rsp),%eax + roll $30,%r12d + addl %ebx,%ebp + roll $1,%eax + movl %eax,20(%rsp) + leal 1859775393(%rax,%rsi,1),%edi + movl 24(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 32(%rsp),%eax + xorl %r11d,%ebx + roll $5,%esi + xorl 56(%rsp),%eax + xorl %edx,%ebx + addl %esi,%edi + xorl 12(%rsp),%eax + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,24(%rsp) + leal 1859775393(%rax,%rdx,1),%esi + movl 28(%rsp),%eax + movl %r11d,%ebx + movl %edi,%edx + xorl 36(%rsp),%eax + xorl %ebp,%ebx + roll $5,%edx + xorl 60(%rsp),%eax + xorl %r12d,%ebx + addl %edx,%esi + xorl 16(%rsp),%eax + roll $30,%ebp + addl %ebx,%esi + roll $1,%eax + movl %eax,28(%rsp) + leal 1859775393(%rax,%r12,1),%edx + movl 32(%rsp),%eax + movl %ebp,%ebx + movl %esi,%r12d + xorl 40(%rsp),%eax + xorl %edi,%ebx + roll $5,%r12d + xorl 0(%rsp),%eax + xorl %r11d,%ebx + addl %r12d,%edx + xorl 20(%rsp),%eax + roll $30,%edi + addl %ebx,%edx + roll $1,%eax + movl %eax,32(%rsp) + leal 1859775393(%rax,%r11,1),%r12d + movl 36(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 44(%rsp),%eax + xorl %esi,%ebx + roll $5,%r11d + xorl 4(%rsp),%eax + xorl %ebp,%ebx + addl %r11d,%r12d + xorl 24(%rsp),%eax + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + movl %eax,36(%rsp) + leal 1859775393(%rax,%rbp,1),%r11d + movl 40(%rsp),%eax + movl %esi,%ebx + movl %r12d,%ebp + xorl 48(%rsp),%eax + xorl %edx,%ebx + roll $5,%ebp + xorl 8(%rsp),%eax + xorl %edi,%ebx + addl %ebp,%r11d + xorl 28(%rsp),%eax + roll $30,%edx + addl %ebx,%r11d + roll $1,%eax + movl %eax,40(%rsp) + leal 1859775393(%rax,%rdi,1),%ebp + movl 44(%rsp),%eax + movl %edx,%ebx + movl %r11d,%edi + xorl 52(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edi + xorl 12(%rsp),%eax + xorl %esi,%ebx + addl %edi,%ebp + xorl 32(%rsp),%eax + roll $30,%r12d + addl %ebx,%ebp + roll $1,%eax + movl %eax,44(%rsp) + leal 1859775393(%rax,%rsi,1),%edi + movl 48(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 56(%rsp),%eax + xorl %r11d,%ebx + roll $5,%esi + xorl 16(%rsp),%eax + xorl %edx,%ebx + addl %esi,%edi + xorl 36(%rsp),%eax + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,48(%rsp) + leal 1859775393(%rax,%rdx,1),%esi + movl 52(%rsp),%eax + movl %r11d,%ebx + movl %edi,%edx + xorl 60(%rsp),%eax + xorl %ebp,%ebx + roll $5,%edx + xorl 20(%rsp),%eax + xorl %r12d,%ebx + addl %edx,%esi + xorl 40(%rsp),%eax + roll $30,%ebp + addl %ebx,%esi + roll $1,%eax + movl %eax,52(%rsp) + leal 1859775393(%rax,%r12,1),%edx + movl 56(%rsp),%eax + movl %ebp,%ebx + movl %esi,%r12d + xorl 0(%rsp),%eax + xorl %edi,%ebx + roll $5,%r12d + xorl 24(%rsp),%eax + xorl %r11d,%ebx + addl %r12d,%edx + xorl 44(%rsp),%eax + roll $30,%edi + addl %ebx,%edx + roll $1,%eax + movl %eax,56(%rsp) + leal 1859775393(%rax,%r11,1),%r12d + movl 60(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 4(%rsp),%eax + xorl %esi,%ebx + roll $5,%r11d + xorl 28(%rsp),%eax + xorl %ebp,%ebx + addl %r11d,%r12d + xorl 48(%rsp),%eax + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + movl %eax,60(%rsp) + leal 1859775393(%rax,%rbp,1),%r11d + movl 0(%rsp),%eax + movl %esi,%ebx + movl %r12d,%ebp + xorl 8(%rsp),%eax + xorl %edx,%ebx + roll $5,%ebp + xorl 32(%rsp),%eax + xorl %edi,%ebx + addl %ebp,%r11d + xorl 52(%rsp),%eax + roll $30,%edx + addl %ebx,%r11d + roll $1,%eax + movl %eax,0(%rsp) + leal 1859775393(%rax,%rdi,1),%ebp + movl 4(%rsp),%eax + movl %edx,%ebx + movl %r11d,%edi + xorl 12(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edi + xorl 36(%rsp),%eax + xorl %esi,%ebx + addl %edi,%ebp + xorl 56(%rsp),%eax + roll $30,%r12d + addl %ebx,%ebp + roll $1,%eax + movl %eax,4(%rsp) + leal 1859775393(%rax,%rsi,1),%edi + movl 8(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 16(%rsp),%eax + xorl %r11d,%ebx + roll $5,%esi + xorl 40(%rsp),%eax + xorl %edx,%ebx + addl %esi,%edi + xorl 60(%rsp),%eax + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,8(%rsp) + leal 1859775393(%rax,%rdx,1),%esi + movl 12(%rsp),%eax + movl %r11d,%ebx + movl %edi,%edx + xorl 20(%rsp),%eax + xorl %ebp,%ebx + roll $5,%edx + xorl 44(%rsp),%eax + xorl %r12d,%ebx + addl %edx,%esi + xorl 0(%rsp),%eax + roll $30,%ebp + addl %ebx,%esi + roll $1,%eax + movl %eax,12(%rsp) + leal 1859775393(%rax,%r12,1),%edx + movl 16(%rsp),%eax + movl %ebp,%ebx + movl %esi,%r12d + xorl 24(%rsp),%eax + xorl %edi,%ebx + roll $5,%r12d + xorl 48(%rsp),%eax + xorl %r11d,%ebx + addl %r12d,%edx + xorl 4(%rsp),%eax + roll $30,%edi + addl %ebx,%edx + roll $1,%eax + movl %eax,16(%rsp) + leal 1859775393(%rax,%r11,1),%r12d + movl 20(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 28(%rsp),%eax + xorl %esi,%ebx + roll $5,%r11d + xorl 52(%rsp),%eax + xorl %ebp,%ebx + addl %r11d,%r12d + xorl 8(%rsp),%eax + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + movl %eax,20(%rsp) + leal 1859775393(%rax,%rbp,1),%r11d + movl 24(%rsp),%eax + movl %esi,%ebx + movl %r12d,%ebp + xorl 32(%rsp),%eax + xorl %edx,%ebx + roll $5,%ebp + xorl 56(%rsp),%eax + xorl %edi,%ebx + addl %ebp,%r11d + xorl 12(%rsp),%eax + roll $30,%edx + addl %ebx,%r11d + roll $1,%eax + movl %eax,24(%rsp) + leal 1859775393(%rax,%rdi,1),%ebp + movl 28(%rsp),%eax + movl %edx,%ebx + movl %r11d,%edi + xorl 36(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edi + xorl 60(%rsp),%eax + xorl %esi,%ebx + addl %edi,%ebp + xorl 16(%rsp),%eax + roll $30,%r12d + addl %ebx,%ebp + roll $1,%eax + movl %eax,28(%rsp) + leal 1859775393(%rax,%rsi,1),%edi + movl 32(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 40(%rsp),%eax + xorl %r11d,%ebx + roll $5,%esi + xorl 0(%rsp),%eax + xorl %edx,%ebx + addl %esi,%edi + xorl 20(%rsp),%eax + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,32(%rsp) + leal -1894007588(%rax,%rdx,1),%esi + movl 36(%rsp),%eax + movl %ebp,%ebx + movl %ebp,%ecx + xorl 44(%rsp),%eax + movl %edi,%edx + andl %r11d,%ebx + xorl 4(%rsp),%eax + orl %r11d,%ecx + roll $5,%edx + xorl 24(%rsp),%eax + andl %r12d,%ecx + addl %edx,%esi + roll $1,%eax + orl %ecx,%ebx + roll $30,%ebp + movl %eax,36(%rsp) + addl %ebx,%esi + leal -1894007588(%rax,%r12,1),%edx + movl 40(%rsp),%eax + movl %edi,%ebx + movl %edi,%ecx + xorl 48(%rsp),%eax + movl %esi,%r12d + andl %ebp,%ebx + xorl 8(%rsp),%eax + orl %ebp,%ecx + roll $5,%r12d + xorl 28(%rsp),%eax + andl %r11d,%ecx + addl %r12d,%edx + roll $1,%eax + orl %ecx,%ebx + roll $30,%edi + movl %eax,40(%rsp) + addl %ebx,%edx + leal -1894007588(%rax,%r11,1),%r12d + movl 44(%rsp),%eax + movl %esi,%ebx + movl %esi,%ecx + xorl 52(%rsp),%eax + movl %edx,%r11d + andl %edi,%ebx + xorl 12(%rsp),%eax + orl %edi,%ecx + roll $5,%r11d + xorl 32(%rsp),%eax + andl %ebp,%ecx + addl %r11d,%r12d + roll $1,%eax + orl %ecx,%ebx + roll $30,%esi + movl %eax,44(%rsp) + addl %ebx,%r12d + leal -1894007588(%rax,%rbp,1),%r11d + movl 48(%rsp),%eax + movl %edx,%ebx + movl %edx,%ecx + xorl 56(%rsp),%eax + movl %r12d,%ebp + andl %esi,%ebx + xorl 16(%rsp),%eax + orl %esi,%ecx + roll $5,%ebp + xorl 36(%rsp),%eax + andl %edi,%ecx + addl %ebp,%r11d + roll $1,%eax + orl %ecx,%ebx + roll $30,%edx + movl %eax,48(%rsp) + addl %ebx,%r11d + leal -1894007588(%rax,%rdi,1),%ebp + movl 52(%rsp),%eax + movl %r12d,%ebx + movl %r12d,%ecx + xorl 60(%rsp),%eax + movl %r11d,%edi + andl %edx,%ebx + xorl 20(%rsp),%eax + orl %edx,%ecx + roll $5,%edi + xorl 40(%rsp),%eax + andl %esi,%ecx + addl %edi,%ebp + roll $1,%eax + orl %ecx,%ebx + roll $30,%r12d + movl %eax,52(%rsp) + addl %ebx,%ebp + leal -1894007588(%rax,%rsi,1),%edi + movl 56(%rsp),%eax + movl %r11d,%ebx + movl %r11d,%ecx + xorl 0(%rsp),%eax + movl %ebp,%esi + andl %r12d,%ebx + xorl 24(%rsp),%eax + orl %r12d,%ecx + roll $5,%esi + xorl 44(%rsp),%eax + andl %edx,%ecx + addl %esi,%edi + roll $1,%eax + orl %ecx,%ebx + roll $30,%r11d + movl %eax,56(%rsp) + addl %ebx,%edi + leal -1894007588(%rax,%rdx,1),%esi + movl 60(%rsp),%eax + movl %ebp,%ebx + movl %ebp,%ecx + xorl 4(%rsp),%eax + movl %edi,%edx + andl %r11d,%ebx + xorl 28(%rsp),%eax + orl %r11d,%ecx + roll $5,%edx + xorl 48(%rsp),%eax + andl %r12d,%ecx + addl %edx,%esi + roll $1,%eax + orl %ecx,%ebx + roll $30,%ebp + movl %eax,60(%rsp) + addl %ebx,%esi + leal -1894007588(%rax,%r12,1),%edx + movl 0(%rsp),%eax + movl %edi,%ebx + movl %edi,%ecx + xorl 8(%rsp),%eax + movl %esi,%r12d + andl %ebp,%ebx + xorl 32(%rsp),%eax + orl %ebp,%ecx + roll $5,%r12d + xorl 52(%rsp),%eax + andl %r11d,%ecx + addl %r12d,%edx + roll $1,%eax + orl %ecx,%ebx + roll $30,%edi + movl %eax,0(%rsp) + addl %ebx,%edx + leal -1894007588(%rax,%r11,1),%r12d + movl 4(%rsp),%eax + movl %esi,%ebx + movl %esi,%ecx + xorl 12(%rsp),%eax + movl %edx,%r11d + andl %edi,%ebx + xorl 36(%rsp),%eax + orl %edi,%ecx + roll $5,%r11d + xorl 56(%rsp),%eax + andl %ebp,%ecx + addl %r11d,%r12d + roll $1,%eax + orl %ecx,%ebx + roll $30,%esi + movl %eax,4(%rsp) + addl %ebx,%r12d + leal -1894007588(%rax,%rbp,1),%r11d + movl 8(%rsp),%eax + movl %edx,%ebx + movl %edx,%ecx + xorl 16(%rsp),%eax + movl %r12d,%ebp + andl %esi,%ebx + xorl 40(%rsp),%eax + orl %esi,%ecx + roll $5,%ebp + xorl 60(%rsp),%eax + andl %edi,%ecx + addl %ebp,%r11d + roll $1,%eax + orl %ecx,%ebx + roll $30,%edx + movl %eax,8(%rsp) + addl %ebx,%r11d + leal -1894007588(%rax,%rdi,1),%ebp + movl 12(%rsp),%eax + movl %r12d,%ebx + movl %r12d,%ecx + xorl 20(%rsp),%eax + movl %r11d,%edi + andl %edx,%ebx + xorl 44(%rsp),%eax + orl %edx,%ecx + roll $5,%edi + xorl 0(%rsp),%eax + andl %esi,%ecx + addl %edi,%ebp + roll $1,%eax + orl %ecx,%ebx + roll $30,%r12d + movl %eax,12(%rsp) + addl %ebx,%ebp + leal -1894007588(%rax,%rsi,1),%edi + movl 16(%rsp),%eax + movl %r11d,%ebx + movl %r11d,%ecx + xorl 24(%rsp),%eax + movl %ebp,%esi + andl %r12d,%ebx + xorl 48(%rsp),%eax + orl %r12d,%ecx + roll $5,%esi + xorl 4(%rsp),%eax + andl %edx,%ecx + addl %esi,%edi + roll $1,%eax + orl %ecx,%ebx + roll $30,%r11d + movl %eax,16(%rsp) + addl %ebx,%edi + leal -1894007588(%rax,%rdx,1),%esi + movl 20(%rsp),%eax + movl %ebp,%ebx + movl %ebp,%ecx + xorl 28(%rsp),%eax + movl %edi,%edx + andl %r11d,%ebx + xorl 52(%rsp),%eax + orl %r11d,%ecx + roll $5,%edx + xorl 8(%rsp),%eax + andl %r12d,%ecx + addl %edx,%esi + roll $1,%eax + orl %ecx,%ebx + roll $30,%ebp + movl %eax,20(%rsp) + addl %ebx,%esi + leal -1894007588(%rax,%r12,1),%edx + movl 24(%rsp),%eax + movl %edi,%ebx + movl %edi,%ecx + xorl 32(%rsp),%eax + movl %esi,%r12d + andl %ebp,%ebx + xorl 56(%rsp),%eax + orl %ebp,%ecx + roll $5,%r12d + xorl 12(%rsp),%eax + andl %r11d,%ecx + addl %r12d,%edx + roll $1,%eax + orl %ecx,%ebx + roll $30,%edi + movl %eax,24(%rsp) + addl %ebx,%edx + leal -1894007588(%rax,%r11,1),%r12d + movl 28(%rsp),%eax + movl %esi,%ebx + movl %esi,%ecx + xorl 36(%rsp),%eax + movl %edx,%r11d + andl %edi,%ebx + xorl 60(%rsp),%eax + orl %edi,%ecx + roll $5,%r11d + xorl 16(%rsp),%eax + andl %ebp,%ecx + addl %r11d,%r12d + roll $1,%eax + orl %ecx,%ebx + roll $30,%esi + movl %eax,28(%rsp) + addl %ebx,%r12d + leal -1894007588(%rax,%rbp,1),%r11d + movl 32(%rsp),%eax + movl %edx,%ebx + movl %edx,%ecx + xorl 40(%rsp),%eax + movl %r12d,%ebp + andl %esi,%ebx + xorl 0(%rsp),%eax + orl %esi,%ecx + roll $5,%ebp + xorl 20(%rsp),%eax + andl %edi,%ecx + addl %ebp,%r11d + roll $1,%eax + orl %ecx,%ebx + roll $30,%edx + movl %eax,32(%rsp) + addl %ebx,%r11d + leal -1894007588(%rax,%rdi,1),%ebp + movl 36(%rsp),%eax + movl %r12d,%ebx + movl %r12d,%ecx + xorl 44(%rsp),%eax + movl %r11d,%edi + andl %edx,%ebx + xorl 4(%rsp),%eax + orl %edx,%ecx + roll $5,%edi + xorl 24(%rsp),%eax + andl %esi,%ecx + addl %edi,%ebp + roll $1,%eax + orl %ecx,%ebx + roll $30,%r12d + movl %eax,36(%rsp) + addl %ebx,%ebp + leal -1894007588(%rax,%rsi,1),%edi + movl 40(%rsp),%eax + movl %r11d,%ebx + movl %r11d,%ecx + xorl 48(%rsp),%eax + movl %ebp,%esi + andl %r12d,%ebx + xorl 8(%rsp),%eax + orl %r12d,%ecx + roll $5,%esi + xorl 28(%rsp),%eax + andl %edx,%ecx + addl %esi,%edi + roll $1,%eax + orl %ecx,%ebx + roll $30,%r11d + movl %eax,40(%rsp) + addl %ebx,%edi + leal -1894007588(%rax,%rdx,1),%esi + movl 44(%rsp),%eax + movl %ebp,%ebx + movl %ebp,%ecx + xorl 52(%rsp),%eax + movl %edi,%edx + andl %r11d,%ebx + xorl 12(%rsp),%eax + orl %r11d,%ecx + roll $5,%edx + xorl 32(%rsp),%eax + andl %r12d,%ecx + addl %edx,%esi + roll $1,%eax + orl %ecx,%ebx + roll $30,%ebp + movl %eax,44(%rsp) + addl %ebx,%esi + leal -1894007588(%rax,%r12,1),%edx + movl 48(%rsp),%eax + movl %edi,%ebx + movl %edi,%ecx + xorl 56(%rsp),%eax + movl %esi,%r12d + andl %ebp,%ebx + xorl 16(%rsp),%eax + orl %ebp,%ecx + roll $5,%r12d + xorl 36(%rsp),%eax + andl %r11d,%ecx + addl %r12d,%edx + roll $1,%eax + orl %ecx,%ebx + roll $30,%edi + movl %eax,48(%rsp) + addl %ebx,%edx + leal -899497514(%rax,%r11,1),%r12d + movl 52(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 60(%rsp),%eax + xorl %esi,%ebx + roll $5,%r11d + xorl 20(%rsp),%eax + xorl %ebp,%ebx + addl %r11d,%r12d + xorl 40(%rsp),%eax + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + movl %eax,52(%rsp) + leal -899497514(%rax,%rbp,1),%r11d + movl 56(%rsp),%eax + movl %esi,%ebx + movl %r12d,%ebp + xorl 0(%rsp),%eax + xorl %edx,%ebx + roll $5,%ebp + xorl 24(%rsp),%eax + xorl %edi,%ebx + addl %ebp,%r11d + xorl 44(%rsp),%eax + roll $30,%edx + addl %ebx,%r11d + roll $1,%eax + movl %eax,56(%rsp) + leal -899497514(%rax,%rdi,1),%ebp + movl 60(%rsp),%eax + movl %edx,%ebx + movl %r11d,%edi + xorl 4(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edi + xorl 28(%rsp),%eax + xorl %esi,%ebx + addl %edi,%ebp + xorl 48(%rsp),%eax + roll $30,%r12d + addl %ebx,%ebp + roll $1,%eax + movl %eax,60(%rsp) + leal -899497514(%rax,%rsi,1),%edi + movl 0(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 8(%rsp),%eax + xorl %r11d,%ebx + roll $5,%esi + xorl 32(%rsp),%eax + xorl %edx,%ebx + addl %esi,%edi + xorl 52(%rsp),%eax + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,0(%rsp) + leal -899497514(%rax,%rdx,1),%esi + movl 4(%rsp),%eax + movl %r11d,%ebx + movl %edi,%edx + xorl 12(%rsp),%eax + xorl %ebp,%ebx + roll $5,%edx + xorl 36(%rsp),%eax + xorl %r12d,%ebx + addl %edx,%esi + xorl 56(%rsp),%eax + roll $30,%ebp + addl %ebx,%esi + roll $1,%eax + movl %eax,4(%rsp) + leal -899497514(%rax,%r12,1),%edx + movl 8(%rsp),%eax + movl %ebp,%ebx + movl %esi,%r12d + xorl 16(%rsp),%eax + xorl %edi,%ebx + roll $5,%r12d + xorl 40(%rsp),%eax + xorl %r11d,%ebx + addl %r12d,%edx + xorl 60(%rsp),%eax + roll $30,%edi + addl %ebx,%edx + roll $1,%eax + movl %eax,8(%rsp) + leal -899497514(%rax,%r11,1),%r12d + movl 12(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 20(%rsp),%eax + xorl %esi,%ebx + roll $5,%r11d + xorl 44(%rsp),%eax + xorl %ebp,%ebx + addl %r11d,%r12d + xorl 0(%rsp),%eax + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + movl %eax,12(%rsp) + leal -899497514(%rax,%rbp,1),%r11d + movl 16(%rsp),%eax + movl %esi,%ebx + movl %r12d,%ebp + xorl 24(%rsp),%eax + xorl %edx,%ebx + roll $5,%ebp + xorl 48(%rsp),%eax + xorl %edi,%ebx + addl %ebp,%r11d + xorl 4(%rsp),%eax + roll $30,%edx + addl %ebx,%r11d + roll $1,%eax + movl %eax,16(%rsp) + leal -899497514(%rax,%rdi,1),%ebp + movl 20(%rsp),%eax + movl %edx,%ebx + movl %r11d,%edi + xorl 28(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edi + xorl 52(%rsp),%eax + xorl %esi,%ebx + addl %edi,%ebp + xorl 8(%rsp),%eax + roll $30,%r12d + addl %ebx,%ebp + roll $1,%eax + movl %eax,20(%rsp) + leal -899497514(%rax,%rsi,1),%edi + movl 24(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 32(%rsp),%eax + xorl %r11d,%ebx + roll $5,%esi + xorl 56(%rsp),%eax + xorl %edx,%ebx + addl %esi,%edi + xorl 12(%rsp),%eax + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,24(%rsp) + leal -899497514(%rax,%rdx,1),%esi + movl 28(%rsp),%eax + movl %r11d,%ebx + movl %edi,%edx + xorl 36(%rsp),%eax + xorl %ebp,%ebx + roll $5,%edx + xorl 60(%rsp),%eax + xorl %r12d,%ebx + addl %edx,%esi + xorl 16(%rsp),%eax + roll $30,%ebp + addl %ebx,%esi + roll $1,%eax + movl %eax,28(%rsp) + leal -899497514(%rax,%r12,1),%edx + movl 32(%rsp),%eax + movl %ebp,%ebx + movl %esi,%r12d + xorl 40(%rsp),%eax + xorl %edi,%ebx + roll $5,%r12d + xorl 0(%rsp),%eax + xorl %r11d,%ebx + addl %r12d,%edx + xorl 20(%rsp),%eax + roll $30,%edi + addl %ebx,%edx + roll $1,%eax + movl %eax,32(%rsp) + leal -899497514(%rax,%r11,1),%r12d + movl 36(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 44(%rsp),%eax + xorl %esi,%ebx + roll $5,%r11d + xorl 4(%rsp),%eax + xorl %ebp,%ebx + addl %r11d,%r12d + xorl 24(%rsp),%eax + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + movl %eax,36(%rsp) + leal -899497514(%rax,%rbp,1),%r11d + movl 40(%rsp),%eax + movl %esi,%ebx + movl %r12d,%ebp + xorl 48(%rsp),%eax + xorl %edx,%ebx + roll $5,%ebp + xorl 8(%rsp),%eax + xorl %edi,%ebx + addl %ebp,%r11d + xorl 28(%rsp),%eax + roll $30,%edx + addl %ebx,%r11d + roll $1,%eax + movl %eax,40(%rsp) + leal -899497514(%rax,%rdi,1),%ebp + movl 44(%rsp),%eax + movl %edx,%ebx + movl %r11d,%edi + xorl 52(%rsp),%eax + xorl %r12d,%ebx + roll $5,%edi + xorl 12(%rsp),%eax + xorl %esi,%ebx + addl %edi,%ebp + xorl 32(%rsp),%eax + roll $30,%r12d + addl %ebx,%ebp + roll $1,%eax + movl %eax,44(%rsp) + leal -899497514(%rax,%rsi,1),%edi + movl 48(%rsp),%eax + movl %r12d,%ebx + movl %ebp,%esi + xorl 56(%rsp),%eax + xorl %r11d,%ebx + roll $5,%esi + xorl 16(%rsp),%eax + xorl %edx,%ebx + addl %esi,%edi + xorl 36(%rsp),%eax + roll $30,%r11d + addl %ebx,%edi + roll $1,%eax + movl %eax,48(%rsp) + leal -899497514(%rax,%rdx,1),%esi + movl 52(%rsp),%eax + movl %r11d,%ebx + movl %edi,%edx + xorl 60(%rsp),%eax + xorl %ebp,%ebx + roll $5,%edx + xorl 20(%rsp),%eax + xorl %r12d,%ebx + addl %edx,%esi + xorl 40(%rsp),%eax + roll $30,%ebp + addl %ebx,%esi + roll $1,%eax + leal -899497514(%rax,%r12,1),%edx + movl 56(%rsp),%eax + movl %ebp,%ebx + movl %esi,%r12d + xorl 0(%rsp),%eax + xorl %edi,%ebx + roll $5,%r12d + xorl 24(%rsp),%eax + xorl %r11d,%ebx + addl %r12d,%edx + xorl 44(%rsp),%eax + roll $30,%edi + addl %ebx,%edx + roll $1,%eax + leal -899497514(%rax,%r11,1),%r12d + movl 60(%rsp),%eax + movl %edi,%ebx + movl %edx,%r11d + xorl 4(%rsp),%eax + xorl %esi,%ebx + roll $5,%r11d + xorl 28(%rsp),%eax + xorl %ebp,%ebx + addl %r11d,%r12d + xorl 48(%rsp),%eax + roll $30,%esi + addl %ebx,%r12d + roll $1,%eax + leal -899497514(%rax,%rbp,1),%r11d + movl %esi,%ebx + movl %r12d,%ebp + xorl %edx,%ebx + roll $5,%ebp + xorl %edi,%ebx + addl %ebp,%r11d + roll $30,%edx + addl %ebx,%r11d + addl 0(%r8),%r11d + addl 4(%r8),%r12d + addl 8(%r8),%edx + addl 12(%r8),%esi + addl 16(%r8),%edi + movl %r11d,0(%r8) + movl %r12d,4(%r8) + movl %edx,8(%r8) + movl %esi,12(%r8) + movl %edi,16(%r8) + + xchgl %r11d,%edx + xchgl %r12d,%esi + xchgl %r11d,%edi + xchgl %r12d,%ebp + + leaq 64(%r9),%r9 + subq $1,%r10 + jnz .Lloop + movq 64(%rsp),%rsi + movq (%rsi),%r12 + movq 8(%rsi),%rbp + movq 16(%rsi),%rbx + leaq 24(%rsi),%rsp +.Lepilogue: + .byte 0xf3,0xc3 +.size wit_sha1_block_data_order,.-wit_sha1_block_data_order +.byte 83,72,65,49,32,98,108,111,99,107,32,116,114,97,110,115,102,111,114,109,32,102,111,114,32,120,56,54,95,54,52,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 +.align 16 diff --git a/project/src/crypto/sha1_one.c b/project/src/crypto/sha1_one.c new file mode 100644 index 0000000..a47b78b --- /dev/null +++ b/project/src/crypto/sha1_one.c @@ -0,0 +1,74 @@ +/* crypto/sha/sha1_one.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include +#include +#include "wiimm-sha.h" + +unsigned char *WIIMM_SHA1(const unsigned char *d, size_t n, unsigned char *md) +{ + WIIMM_SHA_CTX c; + static unsigned char m[SHA_DIGEST_LENGTH]; + + if (md == NULL) md=m; + if (!WIIMM_SHA1_Init(&c)) + return NULL; + WIIMM_SHA1_Update(&c,d,n); + WIIMM_SHA1_Final(md,&c); + return(md); +} diff --git a/project/src/crypto/sha1dgst.c b/project/src/crypto/sha1dgst.c new file mode 100644 index 0000000..27561c3 --- /dev/null +++ b/project/src/crypto/sha1dgst.c @@ -0,0 +1,64 @@ +/* crypto/sha/sha1dgst.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#undef SHA_0 +#define SHA_1 + +#include "ssl-asm.h" +#include "sha_locl.h" + diff --git a/project/src/crypto/sha_locl.h b/project/src/crypto/sha_locl.h new file mode 100644 index 0000000..875af80 --- /dev/null +++ b/project/src/crypto/sha_locl.h @@ -0,0 +1,435 @@ +/* crypto/sha/sha_locl.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include +#include +#include "wiimm-sha.h" + +#define DATA_ORDER_IS_BIG_ENDIAN + +#define HASH_LONG SHA_LONG +#define HASH_CTX WIIMM_SHA_CTX +#define HASH_CBLOCK SHA_CBLOCK +#define HASH_MAKE_STRING(c,s) do { \ + unsigned long ll; \ + ll=(c)->h0; HOST_l2c(ll,(s)); \ + ll=(c)->h1; HOST_l2c(ll,(s)); \ + ll=(c)->h2; HOST_l2c(ll,(s)); \ + ll=(c)->h3; HOST_l2c(ll,(s)); \ + ll=(c)->h4; HOST_l2c(ll,(s)); \ + } while (0) + +#if defined(SHA_0) + +# define HASH_UPDATE SHA_Update +# define HASH_TRANSFORM SHA_Transform +# define HASH_FINAL SHA_Final +# define HASH_INIT SHA_Init +# define HASH_BLOCK_DATA_ORDER sha_block_data_order +# define Xupdate(a,ix,ia,ib,ic,id) (ix=(a)=(ia^ib^ic^id)) + +static void sha_block_data_order (WIIMM_SHA_CTX *c, const void *p,size_t num); + +#elif defined(SHA_1) + +# define HASH_UPDATE WIIMM_SHA1_Update +# define HASH_TRANSFORM WIIMM_SHA1_Transform +# define HASH_FINAL WIIMM_SHA1_Final +# define HASH_INIT WIIMM_SHA1_Init +# define HASH_BLOCK_DATA_ORDER wit_sha1_block_data_order +# if defined(__MWERKS__) && defined(__MC68K__) + /* Metrowerks for Motorola fails otherwise:-( */ +# define Xupdate(a,ix,ia,ib,ic,id) do { (a)=(ia^ib^ic^id); \ + ix=(a)=ROTATE((a),1); \ + } while (0) +# else +# define Xupdate(a,ix,ia,ib,ic,id) ( (a)=(ia^ib^ic^id), \ + ix=(a)=ROTATE((a),1) \ + ) +# endif + +#ifndef SHA1_ASM +static +#endif +void wit_sha1_block_data_order (WIIMM_SHA_CTX *c, const void *p,size_t num); + +#else +# error "Either SHA_0 or SHA_1 must be defined." +#endif + +#include "md32_common.h" + +#define INIT_DATA_h0 0x67452301UL +#define INIT_DATA_h1 0xefcdab89UL +#define INIT_DATA_h2 0x98badcfeUL +#define INIT_DATA_h3 0x10325476UL +#define INIT_DATA_h4 0xc3d2e1f0UL + +int HASH_INIT (WIIMM_SHA_CTX *c) + { + memset (c,0,sizeof(*c)); + c->h0=INIT_DATA_h0; + c->h1=INIT_DATA_h1; + c->h2=INIT_DATA_h2; + c->h3=INIT_DATA_h3; + c->h4=INIT_DATA_h4; + return 1; + } + +#define K_00_19 0x5a827999UL +#define K_20_39 0x6ed9eba1UL +#define K_40_59 0x8f1bbcdcUL +#define K_60_79 0xca62c1d6UL + +/* As pointed out by Wei Dai , F() below can be + * simplified to the code in F_00_19. Wei attributes these optimisations + * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel. + * #define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) + * I've just become aware of another tweak to be made, again from Wei Dai, + * in F_40_59, (x&a)|(y&a) -> (x|y)&a + */ +#define F_00_19(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) +#define F_20_39(b,c,d) ((b) ^ (c) ^ (d)) +#define F_40_59(b,c,d) (((b) & (c)) | (((b)|(c)) & (d))) +#define F_60_79(b,c,d) F_20_39(b,c,d) + +#ifndef OPENSSL_SMALL_FOOTPRINT + +#define BODY_00_15(i,a,b,c,d,e,f,xi) \ + (f)=xi+(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_16_19(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \ + Xupdate(f,xi,xa,xb,xc,xd); \ + (f)+=(e)+K_00_19+ROTATE((a),5)+F_00_19((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_20_31(i,a,b,c,d,e,f,xi,xa,xb,xc,xd) \ + Xupdate(f,xi,xa,xb,xc,xd); \ + (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_32_39(i,a,b,c,d,e,f,xa,xb,xc,xd) \ + Xupdate(f,xa,xa,xb,xc,xd); \ + (f)+=(e)+K_20_39+ROTATE((a),5)+F_20_39((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_40_59(i,a,b,c,d,e,f,xa,xb,xc,xd) \ + Xupdate(f,xa,xa,xb,xc,xd); \ + (f)+=(e)+K_40_59+ROTATE((a),5)+F_40_59((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#define BODY_60_79(i,a,b,c,d,e,f,xa,xb,xc,xd) \ + Xupdate(f,xa,xa,xb,xc,xd); \ + (f)=xa+(e)+K_60_79+ROTATE((a),5)+F_60_79((b),(c),(d)); \ + (b)=ROTATE((b),30); + +#ifdef X +#undef X +#endif +#ifndef MD32_XARRAY + /* + * Originally X was an array. As it's automatic it's natural + * to expect RISC compiler to accomodate at least part of it in + * the register bank, isn't it? Unfortunately not all compilers + * "find" this expectation reasonable:-( On order to make such + * compilers generate better code I replace X[] with a bunch of + * X0, X1, etc. See the function body below... + * + */ +# define X(i) XX##i +#else + /* + * However! Some compilers (most notably HP C) get overwhelmed by + * that many local variables so that we have to have the way to + * fall down to the original behavior. + */ +# define X(i) XX[i] +#endif + +#if !defined(SHA_1) || !defined(SHA1_ASM) +static void HASH_BLOCK_DATA_ORDER (WIIMM_SHA_CTX *c, const void *p, size_t num) + { + const unsigned char *data=p; + register unsigned MD32_REG_T A,B,C,D,E,T,l; +#ifndef MD32_XARRAY + unsigned MD32_REG_T XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, + XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15; +#else + SHA_LONG XX[16]; +#endif + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + for(;;) + { + const union { long one; char little; } is_endian = {1}; + + if (!is_endian.little && sizeof(SHA_LONG)==4 && ((size_t)p%4)==0) + { + const SHA_LONG *W=(const SHA_LONG *)data; + + X( 0) = W[0]; X( 1) = W[ 1]; + BODY_00_15( 0,A,B,C,D,E,T,X( 0)); X( 2) = W[ 2]; + BODY_00_15( 1,T,A,B,C,D,E,X( 1)); X( 3) = W[ 3]; + BODY_00_15( 2,E,T,A,B,C,D,X( 2)); X( 4) = W[ 4]; + BODY_00_15( 3,D,E,T,A,B,C,X( 3)); X( 5) = W[ 5]; + BODY_00_15( 4,C,D,E,T,A,B,X( 4)); X( 6) = W[ 6]; + BODY_00_15( 5,B,C,D,E,T,A,X( 5)); X( 7) = W[ 7]; + BODY_00_15( 6,A,B,C,D,E,T,X( 6)); X( 8) = W[ 8]; + BODY_00_15( 7,T,A,B,C,D,E,X( 7)); X( 9) = W[ 9]; + BODY_00_15( 8,E,T,A,B,C,D,X( 8)); X(10) = W[10]; + BODY_00_15( 9,D,E,T,A,B,C,X( 9)); X(11) = W[11]; + BODY_00_15(10,C,D,E,T,A,B,X(10)); X(12) = W[12]; + BODY_00_15(11,B,C,D,E,T,A,X(11)); X(13) = W[13]; + BODY_00_15(12,A,B,C,D,E,T,X(12)); X(14) = W[14]; + BODY_00_15(13,T,A,B,C,D,E,X(13)); X(15) = W[15]; + BODY_00_15(14,E,T,A,B,C,D,X(14)); + BODY_00_15(15,D,E,T,A,B,C,X(15)); + + data += SHA_CBLOCK; + } + else + { + HOST_c2l(data,l); X( 0)=l; HOST_c2l(data,l); X( 1)=l; + BODY_00_15( 0,A,B,C,D,E,T,X( 0)); HOST_c2l(data,l); X( 2)=l; + BODY_00_15( 1,T,A,B,C,D,E,X( 1)); HOST_c2l(data,l); X( 3)=l; + BODY_00_15( 2,E,T,A,B,C,D,X( 2)); HOST_c2l(data,l); X( 4)=l; + BODY_00_15( 3,D,E,T,A,B,C,X( 3)); HOST_c2l(data,l); X( 5)=l; + BODY_00_15( 4,C,D,E,T,A,B,X( 4)); HOST_c2l(data,l); X( 6)=l; + BODY_00_15( 5,B,C,D,E,T,A,X( 5)); HOST_c2l(data,l); X( 7)=l; + BODY_00_15( 6,A,B,C,D,E,T,X( 6)); HOST_c2l(data,l); X( 8)=l; + BODY_00_15( 7,T,A,B,C,D,E,X( 7)); HOST_c2l(data,l); X( 9)=l; + BODY_00_15( 8,E,T,A,B,C,D,X( 8)); HOST_c2l(data,l); X(10)=l; + BODY_00_15( 9,D,E,T,A,B,C,X( 9)); HOST_c2l(data,l); X(11)=l; + BODY_00_15(10,C,D,E,T,A,B,X(10)); HOST_c2l(data,l); X(12)=l; + BODY_00_15(11,B,C,D,E,T,A,X(11)); HOST_c2l(data,l); X(13)=l; + BODY_00_15(12,A,B,C,D,E,T,X(12)); HOST_c2l(data,l); X(14)=l; + BODY_00_15(13,T,A,B,C,D,E,X(13)); HOST_c2l(data,l); X(15)=l; + BODY_00_15(14,E,T,A,B,C,D,X(14)); + BODY_00_15(15,D,E,T,A,B,C,X(15)); + } + + BODY_16_19(16,C,D,E,T,A,B,X( 0),X( 0),X( 2),X( 8),X(13)); + BODY_16_19(17,B,C,D,E,T,A,X( 1),X( 1),X( 3),X( 9),X(14)); + BODY_16_19(18,A,B,C,D,E,T,X( 2),X( 2),X( 4),X(10),X(15)); + BODY_16_19(19,T,A,B,C,D,E,X( 3),X( 3),X( 5),X(11),X( 0)); + + BODY_20_31(20,E,T,A,B,C,D,X( 4),X( 4),X( 6),X(12),X( 1)); + BODY_20_31(21,D,E,T,A,B,C,X( 5),X( 5),X( 7),X(13),X( 2)); + BODY_20_31(22,C,D,E,T,A,B,X( 6),X( 6),X( 8),X(14),X( 3)); + BODY_20_31(23,B,C,D,E,T,A,X( 7),X( 7),X( 9),X(15),X( 4)); + BODY_20_31(24,A,B,C,D,E,T,X( 8),X( 8),X(10),X( 0),X( 5)); + BODY_20_31(25,T,A,B,C,D,E,X( 9),X( 9),X(11),X( 1),X( 6)); + BODY_20_31(26,E,T,A,B,C,D,X(10),X(10),X(12),X( 2),X( 7)); + BODY_20_31(27,D,E,T,A,B,C,X(11),X(11),X(13),X( 3),X( 8)); + BODY_20_31(28,C,D,E,T,A,B,X(12),X(12),X(14),X( 4),X( 9)); + BODY_20_31(29,B,C,D,E,T,A,X(13),X(13),X(15),X( 5),X(10)); + BODY_20_31(30,A,B,C,D,E,T,X(14),X(14),X( 0),X( 6),X(11)); + BODY_20_31(31,T,A,B,C,D,E,X(15),X(15),X( 1),X( 7),X(12)); + + BODY_32_39(32,E,T,A,B,C,D,X( 0),X( 2),X( 8),X(13)); + BODY_32_39(33,D,E,T,A,B,C,X( 1),X( 3),X( 9),X(14)); + BODY_32_39(34,C,D,E,T,A,B,X( 2),X( 4),X(10),X(15)); + BODY_32_39(35,B,C,D,E,T,A,X( 3),X( 5),X(11),X( 0)); + BODY_32_39(36,A,B,C,D,E,T,X( 4),X( 6),X(12),X( 1)); + BODY_32_39(37,T,A,B,C,D,E,X( 5),X( 7),X(13),X( 2)); + BODY_32_39(38,E,T,A,B,C,D,X( 6),X( 8),X(14),X( 3)); + BODY_32_39(39,D,E,T,A,B,C,X( 7),X( 9),X(15),X( 4)); + + BODY_40_59(40,C,D,E,T,A,B,X( 8),X(10),X( 0),X( 5)); + BODY_40_59(41,B,C,D,E,T,A,X( 9),X(11),X( 1),X( 6)); + BODY_40_59(42,A,B,C,D,E,T,X(10),X(12),X( 2),X( 7)); + BODY_40_59(43,T,A,B,C,D,E,X(11),X(13),X( 3),X( 8)); + BODY_40_59(44,E,T,A,B,C,D,X(12),X(14),X( 4),X( 9)); + BODY_40_59(45,D,E,T,A,B,C,X(13),X(15),X( 5),X(10)); + BODY_40_59(46,C,D,E,T,A,B,X(14),X( 0),X( 6),X(11)); + BODY_40_59(47,B,C,D,E,T,A,X(15),X( 1),X( 7),X(12)); + BODY_40_59(48,A,B,C,D,E,T,X( 0),X( 2),X( 8),X(13)); + BODY_40_59(49,T,A,B,C,D,E,X( 1),X( 3),X( 9),X(14)); + BODY_40_59(50,E,T,A,B,C,D,X( 2),X( 4),X(10),X(15)); + BODY_40_59(51,D,E,T,A,B,C,X( 3),X( 5),X(11),X( 0)); + BODY_40_59(52,C,D,E,T,A,B,X( 4),X( 6),X(12),X( 1)); + BODY_40_59(53,B,C,D,E,T,A,X( 5),X( 7),X(13),X( 2)); + BODY_40_59(54,A,B,C,D,E,T,X( 6),X( 8),X(14),X( 3)); + BODY_40_59(55,T,A,B,C,D,E,X( 7),X( 9),X(15),X( 4)); + BODY_40_59(56,E,T,A,B,C,D,X( 8),X(10),X( 0),X( 5)); + BODY_40_59(57,D,E,T,A,B,C,X( 9),X(11),X( 1),X( 6)); + BODY_40_59(58,C,D,E,T,A,B,X(10),X(12),X( 2),X( 7)); + BODY_40_59(59,B,C,D,E,T,A,X(11),X(13),X( 3),X( 8)); + + BODY_60_79(60,A,B,C,D,E,T,X(12),X(14),X( 4),X( 9)); + BODY_60_79(61,T,A,B,C,D,E,X(13),X(15),X( 5),X(10)); + BODY_60_79(62,E,T,A,B,C,D,X(14),X( 0),X( 6),X(11)); + BODY_60_79(63,D,E,T,A,B,C,X(15),X( 1),X( 7),X(12)); + BODY_60_79(64,C,D,E,T,A,B,X( 0),X( 2),X( 8),X(13)); + BODY_60_79(65,B,C,D,E,T,A,X( 1),X( 3),X( 9),X(14)); + BODY_60_79(66,A,B,C,D,E,T,X( 2),X( 4),X(10),X(15)); + BODY_60_79(67,T,A,B,C,D,E,X( 3),X( 5),X(11),X( 0)); + BODY_60_79(68,E,T,A,B,C,D,X( 4),X( 6),X(12),X( 1)); + BODY_60_79(69,D,E,T,A,B,C,X( 5),X( 7),X(13),X( 2)); + BODY_60_79(70,C,D,E,T,A,B,X( 6),X( 8),X(14),X( 3)); + BODY_60_79(71,B,C,D,E,T,A,X( 7),X( 9),X(15),X( 4)); + BODY_60_79(72,A,B,C,D,E,T,X( 8),X(10),X( 0),X( 5)); + BODY_60_79(73,T,A,B,C,D,E,X( 9),X(11),X( 1),X( 6)); + BODY_60_79(74,E,T,A,B,C,D,X(10),X(12),X( 2),X( 7)); + BODY_60_79(75,D,E,T,A,B,C,X(11),X(13),X( 3),X( 8)); + BODY_60_79(76,C,D,E,T,A,B,X(12),X(14),X( 4),X( 9)); + BODY_60_79(77,B,C,D,E,T,A,X(13),X(15),X( 5),X(10)); + BODY_60_79(78,A,B,C,D,E,T,X(14),X( 0),X( 6),X(11)); + BODY_60_79(79,T,A,B,C,D,E,X(15),X( 1),X( 7),X(12)); + + c->h0=(c->h0+E)&0xffffffffL; + c->h1=(c->h1+T)&0xffffffffL; + c->h2=(c->h2+A)&0xffffffffL; + c->h3=(c->h3+B)&0xffffffffL; + c->h4=(c->h4+C)&0xffffffffL; + + if (--num == 0) break; + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + } + } +#endif + +#else /* OPENSSL_SMALL_FOOTPRINT */ + +#define BODY_00_15(xi) do { \ + T=E+K_00_19+F_00_19(B,C,D); \ + E=D, D=C, C=ROTATE(B,30), B=A; \ + A=ROTATE(A,5)+T+xi; } while(0) + +#define BODY_16_19(xa,xb,xc,xd) do { \ + Xupdate(T,xa,xa,xb,xc,xd); \ + T+=E+K_00_19+F_00_19(B,C,D); \ + E=D, D=C, C=ROTATE(B,30), B=A; \ + A=ROTATE(A,5)+T; } while(0) + +#define BODY_20_39(xa,xb,xc,xd) do { \ + Xupdate(T,xa,xa,xb,xc,xd); \ + T+=E+K_20_39+F_20_39(B,C,D); \ + E=D, D=C, C=ROTATE(B,30), B=A; \ + A=ROTATE(A,5)+T; } while(0) + +#define BODY_40_59(xa,xb,xc,xd) do { \ + Xupdate(T,xa,xa,xb,xc,xd); \ + T+=E+K_40_59+F_40_59(B,C,D); \ + E=D, D=C, C=ROTATE(B,30), B=A; \ + A=ROTATE(A,5)+T; } while(0) + +#define BODY_60_79(xa,xb,xc,xd) do { \ + Xupdate(T,xa,xa,xb,xc,xd); \ + T=E+K_60_79+F_60_79(B,C,D); \ + E=D, D=C, C=ROTATE(B,30), B=A; \ + A=ROTATE(A,5)+T+xa; } while(0) + +#if !defined(SHA_1) || !defined(SHA1_ASM) +static void HASH_BLOCK_DATA_ORDER (WIIMM_SHA_CTX *c, const void *p, size_t num) + { + const unsigned char *data=p; + register unsigned MD32_REG_T A,B,C,D,E,T,l; + int i; + SHA_LONG X[16]; + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + for(;;) + { + for (i=0;i<16;i++) + { HOST_c2l(data,l); X[i]=l; BODY_00_15(X[i]); } + for (i=0;i<4;i++) + { BODY_16_19(X[i], X[i+2], X[i+8], X[(i+13)&15]); } + for (;i<24;i++) + { BODY_20_39(X[i&15], X[(i+2)&15], X[(i+8)&15],X[(i+13)&15]); } + for (i=0;i<20;i++) + { BODY_40_59(X[(i+8)&15],X[(i+10)&15],X[i&15], X[(i+5)&15]); } + for (i=4;i<24;i++) + { BODY_60_79(X[(i+8)&15],X[(i+10)&15],X[i&15], X[(i+5)&15]); } + + c->h0=(c->h0+A)&0xffffffffL; + c->h1=(c->h1+B)&0xffffffffL; + c->h2=(c->h2+C)&0xffffffffL; + c->h3=(c->h3+D)&0xffffffffL; + c->h4=(c->h4+E)&0xffffffffL; + + if (--num == 0) break; + + A=c->h0; + B=c->h1; + C=c->h2; + D=c->h3; + E=c->h4; + + } + } +#endif + +#endif diff --git a/project/src/crypto/ssl-asm.S b/project/src/crypto/ssl-asm.S new file mode 100644 index 0000000..0205a32 --- /dev/null +++ b/project/src/crypto/ssl-asm.S @@ -0,0 +1,39 @@ + +/*************************************************************************** + * __ __ _ ___________ * + * \ \ / /| |____ ____| * + * \ \ / / | | | | * + * \ \ /\ / / | | | | * + * \ \/ \/ / | | | | * + * \ /\ / | | | | * + * \/ \/ |_| |_| * + * * + * Wiimms ISO Tools * + * http://wit.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the WIT project. * + * Visit http://wit.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define WIIMM_INCLUDE_SSL_ASM +#include "ssl-asm.h" + diff --git a/project/src/crypto/ssl-asm.h b/project/src/crypto/ssl-asm.h new file mode 100644 index 0000000..aef71db --- /dev/null +++ b/project/src/crypto/ssl-asm.h @@ -0,0 +1,56 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifdef __CYGWIN__ + #define SHA1_ASM + #ifdef WIIMM_INCLUDE_SSL_ASM + #include "sha1-586-cygwin.s" + #endif +#elif defined(__linux__) + #if defined(__i386__) + #define SHA1_ASM + #ifdef WIIMM_INCLUDE_SSL_ASM + #include "sha1-586-elf.s" + #endif + #elif defined(__x86_64__) + #define SHA1_ASM + #ifdef WIIMM_INCLUDE_SSL_ASM + #include "sha1-x86_64.s" + #endif + #endif +#endif + diff --git a/project/src/crypto/wiimm-sha.h b/project/src/crypto/wiimm-sha.h new file mode 100644 index 0000000..b548057 --- /dev/null +++ b/project/src/crypto/wiimm-sha.h @@ -0,0 +1,204 @@ +/* crypto/sha/sha.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef WIIMM_HEADER_SHA_H +#define WIIMM_HEADER_SHA_H + +#define OPENSSL_NO_SHA0 +#define OPENSSL_NO_SHA256 +#define OPENSSL_NO_SHA512 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(OPENSSL_NO_SHA) || (defined(OPENSSL_NO_SHA0) && defined(OPENSSL_NO_SHA1)) +#error SHA is disabled. +#endif + +#if defined(OPENSSL_FIPS) +#define FIPS_SHA_SIZE_T size_t +#endif + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! SHA_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! SHA_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +#if defined(__LP32__) +#define SHA_LONG unsigned long +#elif defined(OPENSSL_SYS_CRAY) || defined(__ILP64__) +#define SHA_LONG unsigned long +#define SHA_LONG_LOG2 3 +#else +#define SHA_LONG unsigned int +#endif + +#define SHA_LBLOCK 16 +#define SHA_CBLOCK (SHA_LBLOCK*4) /* SHA treats input data as a + * contiguous array of 32 bit + * wide big-endian values. */ +#define SHA_LAST_BLOCK (SHA_CBLOCK-8) +#define SHA_DIGEST_LENGTH 20 + +typedef struct WIIMM_SHAstate_st + { + SHA_LONG h0,h1,h2,h3,h4; + SHA_LONG Nl,Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num; + } WIIMM_SHA_CTX; + +#ifndef OPENSSL_NO_SHA0 +int SHA_Init(WIIMM_SHA_CTX *c); +int SHA_Update(WIIMM_SHA_CTX *c, const void *data, size_t len); +int SHA_Final(unsigned char *md, WIIMM_SHA_CTX *c); +unsigned char *SHA(const unsigned char *d, size_t n, unsigned char *md); +void SHA_Transform(WIIMM_SHA_CTX *c, const unsigned char *data); +#endif + +#ifndef OPENSSL_NO_SHA1 +int WIIMM_SHA1_Init(WIIMM_SHA_CTX *c); +int WIIMM_SHA1_Update(WIIMM_SHA_CTX *c, const void *data, size_t len); +int WIIMM_SHA1_Final(unsigned char *md, WIIMM_SHA_CTX *c); +unsigned char *WIIMM_SHA1(const unsigned char *d, size_t n, unsigned char *md); +void WIIMM_SHA1_Transform(WIIMM_SHA_CTX *c, const unsigned char *data); +#endif + +#define SHA256_CBLOCK (SHA_LBLOCK*4) /* SHA-256 treats input data as a + * contiguous array of 32 bit + * wide big-endian values. */ +#define SHA224_DIGEST_LENGTH 28 +#define SHA256_DIGEST_LENGTH 32 + +typedef struct WIIMM_SHA256state_st + { + SHA_LONG h[8]; + SHA_LONG Nl,Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num,md_len; + } WIIMM_SHA256_CTX; + +#ifndef OPENSSL_NO_SHA256 +int SHA224_Init(WIIMM_SHA256_CTX *c); +int SHA224_Update(WIIMM_SHA256_CTX *c, const void *data, size_t len); +int SHA224_Final(unsigned char *md, WIIMM_SHA256_CTX *c); +unsigned char *SHA224(const unsigned char *d, size_t n,unsigned char *md); +int SHA256_Init(WIIMM_SHA256_CTX *c); +int SHA256_Update(WIIMM_SHA256_CTX *c, const void *data, size_t len); +int SHA256_Final(unsigned char *md, WIIMM_SHA256_CTX *c); +unsigned char *SHA256(const unsigned char *d, size_t n,unsigned char *md); +void SHA256_Transform(WIIMM_SHA256_CTX *c, const unsigned char *data); +#endif + +#define SHA384_DIGEST_LENGTH 48 +#define SHA512_DIGEST_LENGTH 64 + +#ifndef OPENSSL_NO_SHA512 +/* + * Unlike 32-bit digest algorithms, SHA-512 *relies* on SHA_LONG64 + * being exactly 64-bit wide. See Implementation Notes in sha512.c + * for further details. + */ +#define SHA512_CBLOCK (SHA_LBLOCK*8) /* SHA-512 treats input data as a + * contiguous array of 64 bit + * wide big-endian values. */ +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) +#define SHA_LONG64 unsigned __int64 +#define U64(C) C##UI64 +#elif defined(__arch64__) +#define SHA_LONG64 unsigned long +#define U64(C) C##UL +#else +#define SHA_LONG64 unsigned long long +#define U64(C) C##ULL +#endif + +typedef struct SHA512state_st + { + SHA_LONG64 h[8]; + SHA_LONG64 Nl,Nh; + union { + SHA_LONG64 d[SHA_LBLOCK]; + unsigned char p[SHA512_CBLOCK]; + } u; + unsigned int num,md_len; + } SHA512_CTX; +#endif + +#ifndef OPENSSL_NO_SHA512 +int SHA384_Init(SHA512_CTX *c); +int SHA384_Update(SHA512_CTX *c, const void *data, size_t len); +int SHA384_Final(unsigned char *md, SHA512_CTX *c); +unsigned char *SHA384(const unsigned char *d, size_t n,unsigned char *md); +int SHA512_Init(SHA512_CTX *c); +int SHA512_Update(SHA512_CTX *c, const void *data, size_t len); +int SHA512_Final(unsigned char *md, SHA512_CTX *c); +unsigned char *SHA512(const unsigned char *d, size_t n,unsigned char *md); +void SHA512_Transform(SHA512_CTX *c, const unsigned char *data); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/project/src/db-ctcode.c b/project/src/db-ctcode.c new file mode 100644 index 0000000..d0171fe --- /dev/null +++ b/project/src/db-ctcode.c @@ -0,0 +1,2758 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * +----------------------------------------+ * + * | This file is created automatically. | * + * | Edits are lost with the next update! | * + * +----------------------------------------+ * + * * + ***************************************************************************/ + +#include "db-ctcode.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// ctcode_boot_code_pal_bz2 (rmcp/boot_code.bin) + +const u8 ctcode_boot_code_pal_bz2[439] = +{ + 0x00, 0x00, 0x02, 0xac, // 684 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xc4,0x71, 0x6f,0x87,0x00,0x01, + 0x33,0xff,0xff,0xfe, 0xe4,0x46,0x5e,0x62, 0x54,0x4c,0x4e,0x74, 0x4b,0xc0,0x00,0xf8, + 0x44,0x02,0x77,0xdc, 0x64,0x64,0x43,0x47, 0x04,0x00,0x77,0x49, 0x20,0x60,0x11,0x43, + 0x00,0x54,0x07,0xb0, 0x01,0x53,0x15,0xac, 0x41,0x50,0x00,0x00, 0x0c,0x80,0x00,0x34, + 0x00,0x01,0x90,0x00, 0x00,0x0d,0x00,0x1a, 0x04,0xa6,0xa2,0xa6, 0xc9,0xe9,0x4c,0x46, + 0x8d,0x00,0x00,0x00, 0x00,0x00,0x69,0xa0, 0x1a,0x68,0x1a,0x1a, 0x64,0x01,0xa6,0x82, + 0x49,0x29,0xa9,0xe2, 0x4d,0x34,0x1a,0x1a, 0x68,0x68,0x34,0x1a, 0x03,0x40,0x1a,0x06, + 0x81,0xa1,0x91,0xa7, 0xa2,0x0d,0x1a,0x69, 0xa1,0xa3,0x4a,0x9c, 0x0e,0x34,0xd7,0xb2, + 0xce,0x1b,0x8c,0x84, 0x84,0x4d,0x4f,0xe6, 0x37,0x82,0x51,0xbb, 0x6a,0x9b,0x88,0x0a, + 0x93,0x46,0x4e,0x6f, 0x24,0x0f,0x0a,0x8a, 0xb6,0xa8,0x64,0x84, 0x39,0x7d,0x25,0xb3, + 0x4d,0x98,0x3a,0x04, 0x28,0x33,0x16,0xc2, 0x11,0x9a,0xb3,0x32, 0x1c,0xf4,0x9e,0xe0, + 0x8c,0x2f,0x62,0x7b, 0x37,0x13,0xaf,0x2b, 0x29,0xc1,0x59,0x81, 0x96,0x20,0x33,0x81, + 0x88,0x52,0x80,0xef, 0x9c,0x93,0xa8,0x0c, 0xe0,0xb5,0x5b,0xad, 0xf6,0x78,0x1f,0x2a, + 0xa8,0x52,0x85,0x38, 0x40,0xc0,0x17,0x98, 0x03,0xb1,0x95,0xd6, 0x11,0xb6,0xf6,0x9c, + 0x62,0xdc,0x4a,0x22, 0x36,0xc5,0x02,0x64, 0x60,0x3a,0x8a,0x40, 0x39,0x79,0xa5,0x21, + 0xf9,0x03,0xd3,0x16, 0x4c,0xdc,0x71,0xea, 0x73,0xaa,0xfb,0x05, 0x10,0xa2,0xa0,0x0b, + // 0x100: + 0x50,0x97,0xdd,0x2c, 0x73,0xf1,0xf2,0xd7, 0x7c,0x72,0x6b,0x00, 0xaf,0xd3,0x13,0xe6, + 0x79,0xfe,0x4b,0x94, 0xd6,0x62,0xaf,0xd3, 0xcd,0x81,0x5a,0xc4, 0x1c,0xa0,0xb0,0x9d, + 0x7c,0x84,0xca,0x83, 0x55,0xec,0x1f,0x62, 0xbe,0x50,0x16,0x15, 0x9a,0x5f,0x44,0x0e, + 0xe0,0x93,0x01,0xba, 0x36,0xac,0x56,0x18, 0x39,0x8f,0xcc,0x5b, 0x75,0xe4,0xeb,0x1f, + 0xa0,0xca,0x78,0x4d, 0x0b,0x90,0xbf,0x51, 0xeb,0xad,0x04,0x90, 0x48,0xa5,0x36,0x39, + 0x17,0x42,0x10,0x7c, 0x08,0x87,0x10,0x34, 0xa2,0x80,0x34,0x00, 0x45,0x2d,0x58,0xb4, + 0xa9,0x08,0xe6,0x77, 0x21,0xd6,0x46,0x9c, 0x62,0x7c,0xc4,0xbf, 0x78,0x8d,0xc5,0x29, + 0x12,0xa3,0xfc,0xcb, 0xce,0x30,0xdd,0xf7, 0x2d,0x29,0xf8,0xc9, 0x5e,0x90,0x6b,0xcb, + 0x24,0xaa,0x61,0x51, 0x90,0x30,0x1a,0x41, 0x05,0xc2,0x3e,0x0a, 0x93,0xef,0x62,0xcb, + 0xe6,0xcf,0x1c,0xbf, 0x9a,0x49,0x56,0x89, 0xef,0x8b,0x3d,0x3c, 0x1c,0x8b,0x48,0x0a, + 0x4c,0x9a,0x73,0x81, 0x55,0xb4,0xcc,0x7f, 0x8b,0xb9,0x22,0x9c, 0x28,0x48,0x62,0x38, + 0xb7,0xc3,0x80, + // 0x1b3 = 435 Bytes +}; // ctcode_boot_code_pal_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_boot_code_usa_bz2 (rmce/boot_code.bin) + +const u8 ctcode_boot_code_usa_bz2[438] = +{ + 0x00, 0x00, 0x02, 0xac, // 684 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x5d,0x69, 0x5c,0x70,0x00,0x01, + 0x33,0xff,0xff,0xfe, 0xe4,0x46,0x5e,0x62, 0x54,0x4c,0x4e,0x76, 0x4b,0xc0,0x00,0xf8, + 0x44,0x02,0x77,0xdc, 0x64,0x64,0x43,0x47, 0x04,0x00,0x77,0x49, 0x20,0x60,0x11,0x43, + 0x00,0x54,0x07,0xb0, 0x01,0x53,0x13,0x6c, 0x41,0x50,0x00,0x00, 0x0c,0x8d,0x00,0x03, + 0x40,0x00,0x00,0x00, 0x00,0x06,0x80,0x0d, 0x02,0x53,0x51,0x51, 0xe4,0xca,0x62,0x34, + 0x68,0x00,0x01,0xa6, 0x80,0x00,0x03,0x40, 0x06,0x9a,0x0c,0x86, 0x99,0x0d,0x1a,0x69, + 0xa0,0x92,0x48,0xa7, 0x89,0x3d,0x40,0x68, 0x7a,0x9a,0x00,0xd0, 0x00,0xd0,0x06,0x9a, + 0x00,0x7a,0x81,0xa3, 0x4d,0x00,0x34,0x34, 0x07,0xa9,0x4b,0x89, 0xc6,0xaa,0xf8,0x53, + 0xc6,0xa0,0x45,0x19, 0x03,0x51,0xb1,0x0b, 0xc1,0x28,0x5d,0xb9, 0x45,0xc4,0x42,0xe7, + 0x68,0x49,0x6d,0xe8, 0x81,0xe1,0x49,0x55, 0xd6,0x0c,0xc0,0x8b, 0xa3,0xda,0x43,0x34, + 0xa9,0x23,0xa2,0x22, 0x9a,0xcc,0x5c,0x08, 0x44,0xe5,0x9f,0x96, 0xe7,0xa4,0xfc,0x07, + 0x84,0x80,0x81,0xcc, 0xd5,0x1d,0x32,0xf3, 0x4e,0x0a,0xcc,0x0c, 0xae,0xc1,0x9c,0x0c, + 0x42,0x94,0x27,0x55, 0x14,0x9d,0x48,0x67, 0x05,0x8a,0xd2,0xc0, 0xcf,0x84,0x05,0x55, + 0x12,0x51,0x27,0x08, 0x18,0x02,0x66,0x00, 0xe6,0x65,0xf8,0xc2, 0x35,0x1f,0xcb,0x78, + 0xf7,0xae,0xab,0x8f, 0x68,0xd2,0x28,0x3c, 0x07,0x19,0xbe,0x03, 0x97,0x82,0x51,0xc1, + 0x18,0xfd,0x05,0x81, 0x87,0x7c,0x3f,0x2d, 0x1a,0xbe,0x81,0x4c, 0x29,0xa8,0x42,0xc4, + // 0x100: + 0x45,0xa7,0x13,0x1c, 0x15,0x37,0x7b,0x60, 0x1c,0x9a,0xb8,0x2a, 0x29,0x89,0x92,0x7d, + 0x5f,0x66,0x43,0x9a, 0xd4,0x15,0xbb,0x3a, 0x70,0xea,0xd7,0x89, 0xca,0x25,0x8a,0xec, + 0x14,0x26,0x53,0x5a, 0xa7,0x79,0x5e,0xb6, 0x68,0x14,0xda,0xad, 0x0f,0xb2,0x37,0x6e, + 0xca,0x84,0xda,0x14, 0xff,0x2a,0x68,0x39, 0x0b,0x37,0xdf,0x3e, 0x72,0xb4,0x4b,0x40, + 0x9a,0x70,0x63,0x05, 0x89,0x10,0x77,0x5c, 0x7e,0xa8,0x63,0x02, 0x25,0x1d,0xa1,0xe8, + 0x38,0x10,0x7d,0xe0, 0x44,0x38,0x81,0xa5, 0x08,0x01,0x90,0x02, 0x08,0xea,0xc5,0xa5, + 0x38,0x44,0x33,0xb9, 0x0e,0xb2,0x34,0xf3, 0x12,0xe6,0x25,0xbb, 0xc4,0x6c,0xa9,0xe8, + 0x9f,0x3b,0xe2,0x4c, 0x62,0xe9,0xdf,0x52, 0x25,0x99,0x7a,0x94, 0xd1,0x9a,0x12,0x16, + 0xea,0x50,0xe9,0x64, 0x0c,0x06,0x50,0x43, 0x64,0x9d,0x70,0xa3, 0xfc,0xd9,0xb4,0xf9, + 0x53,0x07,0x2f,0xe6, 0x8e,0xe6,0xd9,0x31, 0xf0,0x4f,0x4f,0x07, 0x22,0xda,0x03,0x99, + 0x93,0x4b,0x70,0x2a, 0x96,0xd9,0x8f,0xf1, 0x77,0x24,0x53,0x85, 0x09,0x05,0xd6,0x95, + 0xc7,0x00, + // 0x1b2 = 434 Bytes +}; // ctcode_boot_code_usa_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_boot_code_jap_bz2 (rmcj/boot_code.bin) + +const u8 ctcode_boot_code_jap_bz2[450] = +{ + 0x00, 0x00, 0x02, 0xac, // 684 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xb8,0xdf, 0xb5,0xb7,0x00,0x01, + 0x33,0xff,0xff,0xfe, 0xe4,0x46,0x5e,0x62, 0x54,0x4c,0x4e,0x74, 0x5b,0xc0,0x00,0xf8, + 0x44,0x02,0x77,0xdc, 0x64,0x64,0x43,0x47, 0x04,0x00,0x77,0x49, 0x20,0x60,0x11,0x43, + 0x00,0x54,0x07,0xb0, 0x01,0x53,0x18,0xb0, 0xd3,0x52,0xa3,0x64, 0x8c,0x10,0xd0,0x3d, + 0x26,0x9a,0x64,0xc2, 0x7a,0x8d,0x03,0x4c, 0x4c,0x43,0x11,0x91, 0x89,0x93,0xd3,0x50, + 0xc9,0xe5,0x0d,0x1e, 0xa7,0xa4,0xc2,0x53, 0x49,0x51,0xe4,0xca, 0x32,0x34,0x00,0x34, + 0x00,0x00,0x00,0x34, 0x34,0x00,0x69,0xa0, 0x00,0x00,0x1e,0xa0, 0x92,0x48,0xa7,0x89, + 0x3d,0x40,0x68,0x7a, 0x9a,0x00,0xd0,0x00, 0xd0,0x06,0x9a,0x00, 0x7a,0x81,0xa3,0x4d, + 0x00,0x34,0x34,0x07, 0xa9,0x5b,0x69,0xcc, 0xe5,0xea,0xb2,0x06, 0xa8,0x49,0x29,0x13, + 0x55,0xf8,0x8e,0x00, 0x94,0x6f,0xab,0x55, 0xc9,0x05,0xbe,0xd1, 0x94,0x1b,0xb9,0x04, + 0x02,0xb2,0xf1,0xcf, 0x0c,0x30,0x93,0x83, 0xc2,0x73,0x34,0xfb, 0x11,0xe4,0x24,0xb6, + 0x66,0x2b,0x84,0x23, 0x1d,0x63,0x61,0x3c, 0x12,0x83,0xb5,0xc2, 0x9c,0x11,0x3b,0x35, + 0x47,0x4c,0xbc,0xd3, 0x82,0xb3,0x03,0x2b, 0xb0,0x67,0x03,0x10, 0xa5,0x09,0xd5,0x45, + 0x27,0x52,0x19,0xc1, 0x62,0xb4,0xb0,0x33, 0xe1,0x01,0x55,0x44, 0x94,0x49,0xc2,0x06, + 0x00,0x99,0x80,0x39, 0x99,0x7e,0x30,0x8d, 0x47,0xf2,0xde,0x3d, 0xeb,0xaa,0xe3,0xda, + 0x34,0x8a,0x0f,0x01, 0xc6,0x6f,0x80,0xe5, 0xe0,0x94,0x70,0x46, 0x3f,0x41,0x60,0x61, + // 0x100: + 0xdf,0x0f,0xcb,0x46, 0xaf,0xa0,0x53,0x0a, 0x6a,0x10,0xb1,0x11, 0x69,0xc4,0xc7,0x05, + 0x4d,0xde,0xd8,0x05, 0x14,0x74,0x03,0x80, 0xa0,0xd7,0x87,0xee, 0xf4,0x18,0x08,0xfa, + 0xca,0x73,0xca,0xe3, 0x19,0x5d,0xd4,0x51, 0x5c,0xef,0x2b,0x70, 0x82,0x87,0x65,0x3d, + 0x7c,0xc3,0xae,0x4d, 0x00,0x36,0x99,0x6c, 0xfe,0x0d,0x55,0x55, 0x88,0xb8,0xea,0x05, + 0x96,0x9b,0x20,0x77, 0x07,0xa3,0xfd,0x63, 0x89,0xc6,0x1f,0x00, 0xa5,0x00,0x24,0x81, + 0x9a,0x0a,0xd7,0xf4, 0xff,0x8a,0xfc,0x43, 0x39,0x67,0x62,0x8b, 0x9b,0x68,0x38,0x33, + 0x22,0x0c,0x82,0x41, 0x9a,0x06,0xb0,0x0e, 0x67,0xf4,0xf1,0xc5, 0x00,0x4f,0x63,0xec, + 0xa3,0x46,0x45,0x16, 0x34,0xb0,0x18,0x5e, 0xc8,0x96,0x54,0xf4, 0x4f,0x9d,0xf1,0x26, + 0x31,0x74,0xef,0xa9, 0x12,0xcc,0xbd,0x4a, 0x68,0xcd,0x09,0x0b, 0x75,0x28,0x74,0xb2, + 0x06,0x03,0x28,0x21, 0xb2,0x4e,0xb8,0x52, 0xfd,0x69,0x7e,0xc2, 0x7d,0x11,0xd7,0xf3, + 0x4b,0x5f,0x31,0x46, 0x11,0x64,0xa8,0x03, 0xa2,0x64,0x06,0xf3, 0x26,0xa0,0xe0,0xbb, + 0x66,0x66,0x3f,0xc5, 0xdc,0x91,0x4e,0x14, 0x24,0x2e,0x37,0xed, 0x6d,0xc0, + // 0x1be = 446 Bytes +}; // ctcode_boot_code_jap_bz2[] + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// ctcode_boot_data_pal_bz2 (rmcp/boot_data.bin) + +const u8 ctcode_boot_data_pal_bz2[238] = +{ + 0x00, 0x00, 0x01, 0x30, // 304 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x55,0x20, 0xa3,0x2f,0x00,0x00, + 0x76,0xff,0xff,0xfc, 0xc4,0x44,0x00,0x24, 0x90,0x40,0x44,0x7e, 0xcf,0xc4,0x00,0xd8, + 0x00,0x00,0x16,0xe8, 0x40,0x44,0x00,0x01, 0x44,0x00,0x54,0x40, 0x00,0x40,0x10,0x00, + 0x10,0x00,0x08,0xb0, 0x00,0xb8,0x84,0x53, 0x26,0x41,0x36,0xa6, 0x13,0x10,0x30,0x68, + 0x11,0x91,0x93,0x6a, 0x18,0x64,0x30,0x00, 0x00,0x00,0x03,0x41, 0xa0,0x0d,0x00,0xd0, + 0x0d,0x35,0x11,0x92, 0x79,0x41,0xa0,0x00, 0xd0,0xd0,0x34,0xd0, 0xc4,0xd1,0xa0,0x30, + 0xc3,0xb2,0x3f,0x99, 0x80,0x9d,0xd0,0x44, 0x89,0x29,0x41,0xdc, 0xd7,0x6b,0x3a,0xc5, + 0x2d,0x9d,0x68,0x93, 0x14,0x71,0x40,0xf2, 0x02,0x46,0x49,0x12, 0x15,0x92,0x64,0x81, + 0x54,0x94,0xdc,0x40, 0xfc,0x59,0xea,0x45, 0x57,0x10,0x85,0xfa, 0x4c,0x0b,0x14,0x9b, + 0xf0,0xa8,0x35,0xaf, 0x2a,0x58,0x60,0x28, 0x94,0x70,0x57,0x88, 0x43,0xcb,0x66,0x0e, + 0x65,0xc5,0x64,0x5c, 0xb6,0x2a,0xa0,0x37, 0x8e,0x56,0xea,0x08, 0xf0,0x24,0x01,0xa0, + 0x3a,0x6b,0x2a,0x1d, 0x80,0x73,0x28,0x00, 0x40,0xb8,0x05,0xa0, 0x03,0xb8,0xe7,0xa9, + 0x25,0xe6,0xc2,0x1d, 0x52,0x27,0xfc,0xd2, 0x3d,0x9a,0x67,0x0b, 0x40,0xf5,0xd2,0x51, + 0x76,0x6e,0xbd,0x68, 0xfe,0x27,0x13,0xa0, 0x9a,0x21,0x0d,0xb2, 0x54,0x45,0x7a,0xdc, + 0x5d,0xc9,0x14,0xe1, 0x42,0x41,0x54,0x82, 0x8c,0xbc, + // 0xea = 234 Bytes +}; // ctcode_boot_data_pal_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_boot_data_usa_bz2 (rmce/boot_data.bin) + +const u8 ctcode_boot_data_usa_bz2[254] = +{ + 0x00, 0x00, 0x01, 0x30, // 304 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x66,0xc1, 0xb2,0xe2,0x00,0x00, + 0x76,0xff,0xff,0xfc, 0xc4,0x44,0x00,0x24, 0x90,0x44,0x44,0x7e, 0xcf,0xc4,0x00,0xd8, + 0x00,0x00,0x16,0xec, 0x40,0x44,0x00,0x01, 0x44,0x00,0x54,0x40, 0x00,0x40,0x10,0x00, + 0x10,0x00,0x08,0xb0, 0x00,0xbb,0x58,0x35, 0x4c,0x86,0x80,0x41, 0xa6,0x99,0x18,0x46, + 0x9a,0x18,0x99,0x30, 0x86,0x98,0xd3,0x42, 0x29,0xfa,0x48,0x9e, 0xa1,0xea,0x68,0x69, + 0xa0,0x18,0x26,0x9a, 0x0c,0x26,0x86,0xd4, 0x30,0x98,0x34,0xd4, 0x50,0xf5,0x36,0xa7, + 0xa8,0xda,0x86,0x83, 0x43,0x40,0x1a,0x31, 0x1a,0x34,0x00,0x22, 0xaf,0xa5,0xff,0x23, + 0x02,0x76,0xd6,0xb2, 0x89,0xac,0xa0,0xea, 0x61,0x82,0x35,0x87, 0xb3,0x82,0xd1,0x25, + 0x28,0xd2,0x82,0xd1, 0x01,0x1e,0x49,0x19, 0x8a,0x00,0x0f,0xb2, 0x74,0x86,0x23,0x04, + 0x57,0x8c,0x56,0x3c, 0x6b,0xc6,0x4d,0x2b, 0x40,0x65,0x05,0x48, 0x5b,0x46,0x49,0x4a, + 0x81,0xa0,0xa8,0x49, 0x1b,0x16,0x80,0x48, 0xa6,0x55,0x77,0x65, 0xb3,0x04,0xc1,0x4e, + 0xb1,0x0a,0xb1,0xaf, 0x27,0xa5,0x74,0x03, 0x68,0xf0,0xeb,0xc1, 0x1c,0x06,0x70,0x18, + 0x03,0x77,0xf9,0x9a, 0xa1,0x48,0x06,0xb8, 0x02,0x02,0x81,0x38, 0x05,0xf5,0x34,0xde, + 0x34,0x56,0x03,0x7d, 0xd8,0x5b,0xfa,0x7e, 0x5a,0xfc,0xda,0x91, 0x49,0x50,0xc9,0xf3, + 0x39,0x39,0xaf,0x97, 0x5c,0x48,0xd6,0x46, 0x4d,0x36,0x4f,0xa2, 0x14,0x45,0x5a,0x8b, + 0xb9,0x22,0x9c,0x28, 0x48,0x33,0x60,0xd9, 0x71,0x00, + // 0xfa = 250 Bytes +}; // ctcode_boot_data_usa_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_boot_data_jap_bz2 (rmcj/boot_data.bin) + +const u8 ctcode_boot_data_jap_bz2[245] = +{ + 0x00, 0x00, 0x01, 0x30, // 304 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xe4,0x08, 0xed,0x76,0x00,0x00, + 0x75,0xff,0xff,0xfc, 0xc4,0x44,0x00,0x24, 0x90,0x40,0x44,0x7e, 0xcf,0xc4,0x00,0xd8, + 0x00,0x04,0x16,0xe8, 0x40,0x44,0x00,0x01, 0x44,0x00,0x54,0x80, 0x04,0x40,0x10,0x00, + 0x10,0x00,0x08,0xb0, 0x00,0xb8,0x83,0x55, 0x36,0xa7,0xa8,0xf4, 0x9e,0xa3,0x4f,0x51, + 0x90,0xc4,0xd1,0xea, 0x3d,0x41,0x84,0x1a, 0x7a,0x99,0x34,0xda, 0x9e,0x50,0xc0,0x68, + 0x00,0x00,0x68,0x00, 0x00,0x00,0x00,0x0d, 0x35,0x13,0x22,0x7a, 0x40,0x00,0x1a,0x0d, + 0x03,0x4d,0x0c,0x46, 0x43,0x40,0xc0,0xcd, 0x77,0x7e,0x65,0x03, 0x9b,0x9e,0x81,0x12, + 0x32,0x83,0xb9,0xae, 0xc6,0x65,0x87,0x9b, 0x2a,0xd1,0x38,0x94, 0xb0,0x50,0x38,0x80, + 0x97,0x09,0x22,0x22, 0xb2,0x4c,0x4f,0x2a, 0x72,0x91,0x90,0x3f, 0x16,0xd7,0x12,0xa9, + 0x80,0x32,0x1c,0xe5, 0xe5,0x89,0xcd,0x2c, 0x50,0x7d,0xf8,0x4a, 0x96,0x1a,0x0a,0x24, + 0xdb,0xd5,0x53,0x21, 0x2f,0xc8,0x1c,0xdb, 0x4a,0xc8,0xb8,0xfd, 0x2a,0x80,0x37,0x08, + 0x21,0xa0,0x54,0x40, 0x58,0x1a,0x80,0x7c, 0x07,0x49,0xe6,0x5e, 0xc2,0x60,0x1b,0x38, + 0x0f,0x02,0x01,0x38, 0x04,0xa9,0x4a,0xe7, 0x2b,0x71,0xc8,0x6f, 0x68,0x22,0xd3,0x61, + 0x97,0x7e,0x64,0x97, 0x8b,0x8e,0xc6,0x09, 0x56,0xc5,0x5c,0xf1, 0xd1,0xf4,0xc5,0x29, + 0xd8,0xd0,0xd7,0x62, 0x77,0x8f,0xe2,0xee, 0x48,0xa7,0x0a,0x12, 0x1c,0x81,0x1d,0xae, + 0xc0, + // 0xf1 = 241 Bytes +}; // ctcode_boot_data_jap_bz2[] + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// ctcode_bad1code_pal_bz2 (rmcp/bad1code.bin) + +const u8 ctcode_bad1code_pal_bz2[999] = +{ + 0x00, 0x00, 0x07, 0x20, // 1824 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xd4,0x4a, 0x2f,0x5c,0x00,0x03, + 0x2b,0x7f,0xff,0xff, 0xe4,0x44,0x5e,0x7c, 0x44,0x3c,0x7e,0xfe, 0x4b,0xdb,0x32,0xec, + 0x06,0x0f,0xff,0xdc, 0x66,0x7f,0xc7,0xff, 0x22,0x07,0x77,0x6f, 0x60,0x2d,0x37,0x4b, + 0x38,0x7e,0xff,0xd0, 0x03,0x3e,0x00,0x2a, 0x40,0x01,0x92,0xa9, 0x88,0x0f,0x53,0x4d, + 0x00,0x00,0x00,0x06, 0x8d,0x00,0xd1,0xa0, 0x00,0x00,0x00,0x0d, 0x00,0x1a,0x00,0x00, + 0x00,0x0f,0x50,0x32, 0x1e,0x90,0xd0,0x10, 0x60,0x08,0xc1,0x31, 0x1a,0x64,0xc2,0x32, + 0x18,0x86,0x11,0x84, 0xc0,0x08,0xc2,0x32, 0x19,0x34,0xc8,0x68, 0xc8,0xc4,0x1a,0x18, + 0x09,0x89,0x81,0x31, 0x1a,0x30,0x11,0x93, 0x26,0x04,0x18,0x02, 0x30,0x4c,0x46,0x99, + 0x30,0x8c,0x86,0x21, 0x84,0x61,0x30,0x02, 0x30,0x8c,0x86,0x4d, 0x32,0x1a,0x32,0x31, + 0x06,0x86,0x02,0x62, 0x60,0x4c,0x46,0x8c, 0x04,0x64,0xc9,0x81, 0x06,0x00,0x8c,0x13, + 0x11,0xa6,0x4c,0x23, 0x21,0x88,0x61,0x18, 0x4c,0x00,0x8c,0x23, 0x21,0x93,0x4c,0x86, + 0x8c,0x8c,0x41,0xa1, 0x80,0x98,0x98,0x13, 0x11,0xa3,0x01,0x19, 0x32,0x60,0x12,0x48, + 0x4d,0x20,0x68,0x34, 0x8d,0x26,0xa7,0xa6, 0x6a,0x0c,0x93,0xca, 0x7a,0x13,0xca,0x68, + 0xc4,0x34,0xda,0x9e, 0x53,0x27,0xa4,0x3d, 0x43,0xd4,0x69,0x90, 0x69,0xe5,0x1e,0x53, + 0x6a,0x31,0x84,0x4f, 0x51,0xa6,0xd4,0x34, 0x36,0xa7,0x94,0x1a, 0x3d,0x4f,0x50,0xc9, + 0xe1,0x49,0xec,0x38, 0xdb,0x63,0x0f,0x63, 0xc9,0x70,0xb3,0x4c, 0xb2,0x4a,0x6a,0x65, + // 0x100: + 0x31,0xce,0x33,0xc5, 0x40,0xfd,0xb2,0x88, 0x2f,0x9c,0xa3,0x20, 0x69,0xc0,0xf9,0x3b, + 0x12,0x6a,0x19,0xe3, 0xca,0x19,0xf1,0x35, 0x4f,0xad,0xe6,0x87, 0x59,0x2f,0x88,0xac, + 0x48,0xf2,0x44,0xa9, 0x7d,0x14,0xa6,0x56, 0x86,0xcc,0x36,0x0b, 0x82,0xda,0x5d,0xdb, + 0xda,0xa3,0xd0,0x44, 0xdc,0x6d,0x11,0xa4, 0xfb,0x63,0x0c,0x74, 0x18,0xa9,0xc6,0x73, + 0x5c,0x5c,0x94,0xd5, 0x76,0xae,0x31,0xa7, 0x30,0xd5,0x2d,0x22, 0x56,0x49,0x35,0x29, + 0x25,0x50,0x49,0x91, 0x01,0xfc,0xc7,0xe0, 0xb2,0x4e,0x1d,0x44, 0x1e,0x03,0x20,0x65, + 0xa4,0xc8,0x95,0xc3, 0x38,0x70,0x9c,0xce, 0xdf,0x49,0xc3,0x83, 0x82,0x94,0x01,0x68, + 0xac,0xc0,0x9d,0x03, 0x9e,0x81,0x85,0x40, 0x03,0xe1,0x09,0x77, 0x59,0x07,0x66,0x63, + 0x94,0x21,0xe6,0x32, 0x72,0xf0,0x68,0xa2, 0x42,0x2b,0xba,0x82, 0x44,0x11,0x2a,0x20, + 0xed,0x14,0x52,0xa8, 0xc9,0x92,0x4c,0x02, 0x82,0x6e,0x5b,0x40, 0x66,0x49,0x0c,0x84, + 0xb8,0x4c,0x91,0x9d, 0xa4,0x19,0xef,0x40, 0x50,0x63,0x6b,0x54, 0xff,0x18,0x78,0x64, + 0x92,0x39,0xcc,0x9c, 0x37,0x45,0xe7,0xb0, 0x3d,0x6b,0xf0,0xce, 0xe0,0x98,0x66,0xed, + 0x5f,0xb9,0xc6,0x89, 0xa4,0x69,0xe4,0xd1, 0x2d,0xb3,0x86,0x40, 0x75,0x9c,0xc8,0xea, + 0x02,0xc8,0x83,0x00, 0xc7,0xa0,0x22,0xa9, 0x69,0x5e,0x87,0x44, 0x90,0xa3,0x29,0x60, + 0x1f,0x27,0x1f,0x83, 0xc2,0x2d,0x97,0x71, 0xeb,0xe6,0xe8,0x68, 0x62,0x5c,0xc4,0xef, + 0x11,0x39,0x5a,0x4b, 0x59,0x74,0xd0,0x7d, 0x86,0xe7,0x50,0xb0, 0xba,0xb7,0x52,0xc4, + // 0x200: + 0xd5,0x2e,0x4a,0xec, 0x5f,0x15,0xc2,0x55, 0x74,0xa3,0xc6,0x33, 0x03,0x38,0xa7,0x2a, + 0xbc,0x0e,0x5e,0xde, 0xde,0xcc,0x48,0x95, 0x09,0xcb,0x34,0x89, 0x8d,0xc3,0x38,0x28, + 0x3d,0x2b,0x9b,0x0d, 0x9f,0x8c,0xe5,0x50, 0x6a,0x91,0x70,0xc4, 0xa4,0x03,0xb2,0x10, + 0x01,0xd8,0x40,0x42, 0x1f,0x20,0x5e,0x80, 0x85,0x0d,0x4f,0xe4, 0xd1,0x2a,0x40,0x3d, + 0x8c,0xcf,0x97,0x4e, 0xdc,0xb2,0xd0,0x44, 0xe2,0x77,0xde,0xb4, 0x9c,0xaa,0xb4,0x55, + 0xb0,0x6c,0x3c,0x5d, 0xa5,0x66,0x46,0x4f, 0xa6,0xce,0x6f,0xcd, 0x87,0x5d,0xa6,0x2b, + 0x96,0x9c,0x0d,0xc9, 0x67,0x27,0x0f,0x05, 0x0f,0xb9,0x5e,0x1a, 0xcd,0xc9,0x76,0x27, + 0x7e,0x8f,0x78,0x45, 0x4c,0x41,0x3c,0xac, 0x0c,0x67,0x55,0x5f, 0xb0,0xc6,0x0c,0x82, + 0x89,0x80,0x02,0xe7, 0xf5,0xf4,0x20,0x2f, 0x00,0x34,0x0e,0x38, 0xc9,0xae,0x47,0x88, + 0x39,0x91,0x71,0x08, 0x16,0x25,0x08,0x11, 0x3f,0x0c,0x49,0xd5, 0xf3,0x8a,0x6c,0x1a, + 0x14,0x25,0xa9,0x94, 0x20,0x27,0x47,0x3b, 0x6a,0x80,0x02,0xce, 0x5e,0x2b,0xda,0xe2, + 0x95,0x40,0x5d,0x31, 0xb2,0xc7,0x31,0x0e, 0x29,0x38,0xff,0x5e, 0x2e,0x3c,0xbb,0xbe, + 0x2e,0xfc,0x75,0xf6, 0x54,0x86,0x28,0x60, 0x92,0x8b,0xce,0x64, 0xba,0x45,0x0a,0x33, + 0xa5,0x6d,0x86,0x18, 0xaa,0x55,0x75,0xe7, 0xf7,0xce,0xfc,0x32, 0x32,0x2d,0x20,0xf0, + 0x82,0x2c,0xa5,0xaf, 0x9a,0xee,0x7a,0x0a, 0xcb,0xa1,0x83,0x7c, 0x76,0xeb,0xc3,0xa5, + 0x6e,0xa2,0x0d,0x90, 0x7b,0x15,0xcc,0x61, 0x73,0xfb,0x93,0x5f, 0x01,0xc0,0x24,0x0f, + // 0x300: + 0xdd,0x35,0x34,0x7b, 0x24,0x12,0x66,0x89, 0xc6,0xfe,0x28,0x6d, 0x65,0x14,0x08,0x92, + 0xcb,0xe2,0x86,0xac, 0x92,0x3e,0xeb,0x77, 0xf1,0x44,0xe8,0xbc, 0x46,0xa5,0xb0,0xb6, + 0x29,0xa9,0x9c,0xdf, 0x2b,0x2c,0x1e,0x39, 0x1e,0xae,0xd2,0x79, 0xd5,0xe3,0xe0,0x08, + 0x9c,0x81,0x86,0xe3, 0x91,0xd0,0xf9,0x8d, 0xb2,0xf5,0x29,0xee, 0x12,0x18,0xc3,0x11, + 0x8a,0x23,0x89,0x85, 0x97,0x95,0x29,0xcd, 0x35,0x88,0x76,0x0f, 0x1e,0x07,0x34,0x9a, + 0x63,0xc8,0x70,0xe3, 0x60,0x34,0x0b,0xd2, 0xb5,0x6a,0xc2,0x95, 0x86,0x17,0xca,0x16, + 0x02,0x91,0x7e,0xa3, 0x16,0x6f,0x05,0x8f, 0x09,0x6c,0xa8,0x53, 0x30,0x0f,0xe9,0x5c, + 0xb0,0x30,0xc8,0x13, 0x08,0xe2,0x20,0xbd, 0xaa,0xf3,0xb7,0x68, 0xf1,0x1e,0x9a,0x09, + 0x39,0x86,0x07,0x00, 0x64,0xac,0xe3,0x83, 0x0c,0xef,0x82,0xff, 0xaf,0x24,0xf6,0x7a, + 0x78,0x54,0x2c,0xb4, 0x14,0x60,0xe7,0xf8, 0xd6,0x52,0x50,0x9c, 0xdf,0xa9,0x58,0xb0, + 0xfc,0x0a,0x63,0x4b, 0x36,0xa5,0x51,0x4d, 0x1c,0x16,0x77,0xd5, 0x19,0x59,0x72,0x23, + 0x85,0xfe,0xc0,0x37, 0xac,0x39,0x1f,0xf3, 0x24,0xb7,0x84,0xc4, 0xed,0x49,0x80,0x32, + 0x52,0x60,0xab,0x88, 0x3d,0x36,0xea,0x63, 0x86,0xc6,0xe8,0x83, 0x14,0xdc,0xde,0xef, + 0xc3,0x7d,0x90,0xf6, 0xda,0x1a,0x46,0x3f, 0xf1,0x77,0x24,0x53, 0x85,0x09,0x0d,0x44, + 0xa2,0xf5,0xc0, + // 0x3e3 = 995 Bytes +}; // ctcode_bad1code_pal_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_bad1code_usa_bz2 (rmce/bad1code.bin) + +const u8 ctcode_bad1code_usa_bz2[1001] = +{ + 0x00, 0x00, 0x07, 0x20, // 1824 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xaf,0x02, 0x62,0x73,0x00,0x03, + 0x2b,0x7f,0xff,0xff, 0xe4,0x44,0x5e,0x7c, 0x44,0x3c,0x7e,0xfe, 0x4b,0xdb,0x32,0xec, + 0x06,0x0f,0xff,0xdc, 0x66,0x7f,0x87,0xff, 0x22,0x07,0x77,0x6f, 0x60,0x2d,0x37,0x4b, + 0x38,0x7e,0xff,0xd0, 0x03,0x3e,0x00,0x29, 0x40,0x01,0xa4,0xa2, 0x61,0x0d,0x3d,0x26, + 0x86,0x80,0x69,0xa0, 0xd0,0x00,0x3d,0x41, 0xea,0x0c,0x8c,0x9a, 0x03,0x26,0x86,0x80, + 0x00,0x00,0x01,0xa0, 0x01,0x88,0x64,0x06, 0x9a,0x3c,0x90,0xd1, 0x88,0x34,0xd0,0xc4, + 0x64,0xc0,0x8d,0x30, 0x4d,0x31,0x18,0x23, 0x4d,0x06,0x98,0x04, 0xc4,0xc0,0x41,0x82, + 0x62,0x06,0x04,0x64, 0x06,0x21,0x84,0x60, 0x4c,0x98,0x00,0x02, 0x60,0x41,0xa6,0x86, + 0x23,0x26,0x04,0x69, 0x82,0x69,0x88,0xc1, 0x1a,0x68,0x34,0xc0, 0x26,0x26,0x02,0x0c, + 0x13,0x10,0x30,0x23, 0x20,0x31,0x0c,0x23, 0x02,0x64,0xc0,0x00, 0x13,0x02,0x0d,0x34, + 0x31,0x19,0x30,0x23, 0x4c,0x13,0x4c,0x46, 0x08,0xd3,0x41,0xa6, 0x01,0x31,0x30,0x10, + 0x60,0x98,0x81,0x81, 0x19,0x01,0x88,0x61, 0x18,0x13,0x26,0x00, 0x00,0x98,0x04,0x92, + 0x23,0x44,0x19,0x4c, 0x4d,0x35,0x4d,0xa9, 0xe1,0xa5,0x36,0x83, 0x29,0xe8,0x9a,0x79, + 0x43,0xca,0x68,0x3c, 0xa7,0xa8,0xf1,0x23, 0x4f,0x50,0xf5,0x1e, 0x90,0x62,0x7a,0x9e, + 0x99,0x4d,0xa9,0x8c, 0x51,0xe9,0x19,0x36, 0x93,0x1a,0x9b,0x4d, 0x4d,0x0d,0x19,0xa2, + 0x63,0x0a,0x56,0xaa, 0xe3,0x6d,0x8c,0x0d, 0xae,0x2b,0x85,0x9c, 0x66,0x11,0xd2,0x53, + // 0x100: + 0x29,0x8f,0x70,0xd0, 0x14,0xa7,0xf5,0x96, 0x44,0xb4,0x8a,0x11, 0x86,0xac,0x46,0x7f, + 0x4a,0x3f,0xc4,0xd1, 0x1e,0x4b,0xa3,0x03, 0xf3,0x3a,0xcf,0x34, 0xb9,0xe9,0x73,0x8b, + 0x42,0x37,0x91,0xa5, 0x47,0xf0,0xa3,0x32, 0xb0,0x36,0x71,0xb4, 0x57,0x2c,0xa5,0xd7, + 0xba,0x9c,0x7a,0x08, 0x1b,0x8d,0xa6,0x34, 0x7d,0x98,0x45,0x8e, 0x83,0x05,0x38,0xd0, + 0x6a,0xf6,0xc5,0x25, 0x51,0xaa,0x0c,0x6a, 0xcc,0x35,0x3b,0x08, 0x91,0x92,0x4d,0x46, + 0x39,0x14,0x49,0x32, 0x22,0x1f,0x88,0xfb, 0xe6,0x49,0xc3,0xa8, 0x03,0xc0,0x64,0x0c, + 0xb5,0x59,0x12,0x38, 0x67,0x0e,0x13,0x99, 0xdb,0xe9,0x38,0x70, 0x5c,0xa5,0x10,0x2d, + 0x45,0x9c,0x15,0x90, 0x39,0xe8,0x18,0x52, 0x80,0x73,0x42,0x4d, 0xd6,0x41,0xd4,0x98, + 0xf5,0x84,0x3c,0xc6, 0x4e,0x5d,0xdd,0x44, 0x46,0x41,0x75,0xd0, 0x46,0x82,0x05,0x34, + 0x1a,0x65,0x04,0xa9, 0xb2,0x64,0x93,0x00, 0xa2,0x4d,0x86,0xd1, 0x0c,0xc9,0x21,0x90, + 0x95,0xe3,0x24,0x68, 0x6a,0x86,0x8b,0xd0, 0x12,0xb1,0xe1,0xf2, 0x38,0x32,0x88,0x9c, + 0xe6,0x4e,0x1b,0x21, 0xe7,0x2c,0x7a,0xd9, 0xbc,0x35,0x84,0xc3, 0x37,0x52,0xf5,0xce, + 0x34,0xcd,0x43,0x57, 0x2a,0x81,0x76,0xce, 0x19,0x01,0xce,0x73, 0x23,0x98,0x0b,0x1e, + 0x26,0x01,0x8e,0x48, 0x89,0xcb,0x65,0x72, 0x19,0x04,0x65,0x09, 0x0a,0xa0,0xf8,0xf0, + 0x2e,0x2c,0x97,0x65, 0xbc,0x3e,0xfc,0xee, 0x8f,0x47,0x91,0x6d, 0x03,0xb0,0x40,0xc2, + 0xd4,0x5a,0xeb,0xe8, 0x41,0xb9,0x9a,0x54, 0x5c,0xcb,0x10,0xea, 0x95,0xcb,0x46,0x3b, + // 0x200: + 0x65,0xa0,0x41,0x58, 0x50,0xe2,0x1f,0x68, 0x67,0x94,0x7d,0x10, 0x95,0x7b,0x1b,0x7b, + 0x7f,0xc4,0x08,0xca, 0x65,0x52,0xb5,0x12, 0x63,0x70,0xe8,0x04, 0xaf,0x4a,0xbd,0x56, + 0xd0,0xc5,0x76,0x29, 0x30,0xe9,0xa4,0x88, 0x62,0x52,0x30,0xe9, 0x8c,0x05,0x84,0x01, + 0x88,0x2b,0x39,0x30, 0x13,0x01,0x02,0xfe, 0xa7,0x22,0x59,0x26, 0xa4,0x69,0x7d,0xfc, + 0x25,0x0a,0x57,0x58, 0xc6,0xe8,0x4a,0x0a, 0x34,0x68,0x41,0x3e, 0xed,0x72,0x8f,0x44, + 0xdb,0xf2,0xea,0x0c, 0x49,0xe9,0x33,0x9b, 0x65,0x87,0x5b,0xa6, 0x2a,0x16,0x1c,0x0d, + 0xea,0x33,0x93,0x87, 0x82,0xc2,0x36,0x08, 0x7e,0xca,0xe8,0xd7, 0x6c,0x17,0x61,0x76, + 0xe9,0x76,0x06,0xda, 0x5c,0x04,0xbb,0x31, 0xab,0x9d,0x8c,0x5f, 0x11,0xc6,0x2a,0x14, + 0xdb,0xe7,0x07,0xd1, 0x8b,0xb4,0x11,0x01, 0x84,0x74,0xb5,0xd2, 0xc7,0x6f,0x3b,0x1d, + 0xa6,0x7a,0x18,0xf1, 0xa2,0x31,0x01,0x35, 0x10,0xe2,0x2d,0xef, 0x59,0xbb,0xa0,0x79, + 0x18,0x8a,0xe0,0x99, 0x10,0x06,0x7c,0xf5, 0xa5,0xc8,0x01,0x1f, 0x7f,0xdf,0x28,0x1b, + 0x88,0x2c,0x05,0x08, 0xeb,0xf6,0xe0,0xf3, 0x20,0xac,0x3f,0xe4, 0x83,0x8f,0x56,0xde, + 0xf7,0x7e,0x1d,0xee, 0xfa,0x8c,0xc2,0x0b, 0xe2,0x41,0x7b,0x2c, 0x97,0x2c,0x96,0x85, + 0x64,0xac,0xb0,0xc3, 0x13,0x93,0xba,0xe3, 0xc7,0x8d,0xb1,0x93, 0x93,0x82,0x83,0xc0, + 0x08,0xb5,0x4b,0xbd, 0x9e,0xec,0x74,0x16, 0x8b,0x95,0x7f,0x64, 0xd6,0xa9,0x17,0xc7, + 0x66,0x9a,0x0e,0xf8, 0x3d,0x8a,0x87,0xbc, 0x2c,0x6e,0xac,0xd7, 0x60,0x70,0x08,0xc3, + // 0x300: + 0xfb,0x4d,0x49,0x19, 0x84,0x49,0x33,0x40, 0xe2,0xff,0x92,0xff, 0xbf,0x31,0x29,0x02, + 0x49,0x3c,0xb1,0x7e, 0x91,0xc6,0xff,0x43, 0x77,0xf7,0x45,0x64, 0x5c,0x23,0x5a,0xc8, + 0x59,0x14,0xd4,0x8e, 0x4f,0xa7,0x9a,0x0f, 0x1c,0x8e,0x57,0x3d, 0x3c,0xe6,0xf1,0xb8, + 0x62,0x38,0x81,0x6a, 0x46,0x60,0x38,0xe3, 0xe4,0xdc,0xa5,0x56, 0xd5,0x21,0x89,0x44, + 0x72,0x04,0x5e,0x6c, 0x85,0xf6,0x66,0x5c, 0xa6,0x21,0xae,0x45, 0xd3,0x3c,0xdc,0x2c, + 0x42,0x79,0xcb,0x77, 0x0e,0x3c,0x85,0xc1, 0x3c,0xf3,0x8a,0x0c, 0x30,0xba,0x01,0x54, + 0x28,0x1c,0x15,0x08, 0x33,0x77,0x6a,0x78, 0x0b,0xa2,0x99,0x48, 0xbd,0x3c,0x4a,0xbb, + 0x03,0x0c,0x81,0x30, 0x8b,0xc4,0x17,0x13, 0xbc,0xd6,0xb6,0x3c, 0xa6,0x19,0x8a,0x81, + 0x88,0x61,0x9c,0x2b, 0x71,0x92,0xaf,0x8c, 0x0c,0x33,0xbe,0xbf, 0xbb,0x4a,0x3b,0x5a, + 0xf9,0xb7,0xf2,0xd7, 0x68,0x94,0x22,0x73, 0xfc,0xcb,0x2d,0x28, 0xad,0x4d,0xfa,0x76, + 0x85,0x57,0xf0,0xe9, 0x0d,0x24,0xdf,0x94, 0xe2,0x9a,0x17,0xcc, 0xee,0xb4,0x24,0x65, + 0xc7,0x85,0xff,0xfd, 0x10,0x6f,0x55,0x72, 0x3c,0xec,0x92,0xde, 0x13,0x15,0x9a,0x8b, + 0x00,0x65,0x24,0xc1, 0x3e,0x08,0xf4,0xdb, 0xa9,0x8e,0x0b,0x1b, 0xa2,0x0c,0x13,0x73, + 0x7b,0xb9,0x16,0xfb, 0x21,0xed,0xe1,0x1a, 0x36,0x3f,0xf1,0x77, 0x24,0x53,0x85,0x09, + 0x0a,0xf0,0x26,0x27, 0x30, + // 0x3e5 = 997 Bytes +}; // ctcode_bad1code_usa_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_bad1code_jap_bz2 (rmcj/bad1code.bin) + +const u8 ctcode_bad1code_jap_bz2[1001] = +{ + 0x00, 0x00, 0x07, 0x20, // 1824 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xaa,0x33, 0x10,0x4f,0x00,0x03, + 0x2b,0x7f,0xff,0xff, 0xe4,0x44,0x5e,0x7c, 0x44,0x3c,0x7e,0xfe, 0x4b,0xdb,0x32,0xec, + 0x06,0x0f,0xff,0xdc, 0x6e,0x7f,0x87,0xff, 0x22,0x07,0x77,0x6f, 0x60,0x2d,0x37,0x4b, + 0x38,0x7e,0xff,0xd0, 0x03,0x3e,0x03,0xa1, 0x54,0x01,0x20,0xc0, 0x11,0x82,0x62,0x34, + 0xc9,0x84,0x64,0x31, 0x0c,0x23,0x09,0x80, 0x11,0x84,0x64,0x32, 0x69,0x90,0xd1,0x91, + 0x88,0x34,0x30,0x13, 0x13,0x02,0x62,0x34, 0x60,0x23,0x26,0x4c, 0x08,0x30,0x04,0x60, + 0x98,0x8d,0x32,0x61, 0x19,0x0c,0x43,0x08, 0xc2,0x60,0x04,0x61, 0x19,0x0c,0x9a,0x64, + 0x34,0x64,0x62,0x0d, 0x0c,0x04,0xc4,0xc0, 0x98,0x8d,0x18,0x08, 0xc9,0x93,0x00,0xd3, + 0x52,0x28,0x34,0x1a, 0x01,0xa0,0x00,0x00, 0x06,0x80,0x34,0x68, 0x34,0x00,0x00,0x00, + 0x00,0x03,0x40,0x00, 0x1a,0x06,0x80,0x34, 0x00,0x00,0x83,0x00, 0x46,0x09,0x88,0xd3, + 0x26,0x11,0x90,0xc4, 0x30,0x8c,0x26,0x00, 0x46,0x11,0x90,0xc9, 0xa6,0x43,0x46,0x46, + 0x20,0xd0,0xc0,0x4c, 0x4c,0x09,0x88,0xd1, 0x80,0x8c,0x99,0x30, 0x09,0x24,0x46,0x8a, + 0x63,0x40,0x9a,0x14, 0xf4,0xf5,0x34,0xf5, 0x27,0xa9,0x93,0xc1, 0x4d,0x34,0xf2,0x47, + 0xa9,0xfa,0x90,0x3c, 0x42,0x3d,0x20,0xc8, 0xc8,0x0c,0x8f,0x29, 0xe1,0x27,0xa6,0xa3, + 0xd3,0x29,0xea,0x1b, 0x53,0xd4,0xd1,0xa1, 0xea,0x6d,0x46,0x26, 0x6a,0x78,0x9a,0x36, + 0xa6,0xa5,0x5a,0x8e, 0x36,0xd8,0xc2,0xd9, 0xbe,0x70,0xb2,0xcf, 0x88,0x8e,0x7a,0x99, + // 0x100: + 0x4c,0x7b,0x06,0x70, 0xa5,0x3b,0xdf,0x01, 0x12,0xd0,0x2d,0xa3, 0x0d,0x38,0x8c,0xcd, + 0x18,0xf5,0x0c,0xf1, 0xe4,0xb9,0xf0,0x3f, 0x13,0xb2,0xf3,0x43, 0xe9,0x4b,0xaa,0x52, + 0x23,0x79,0x1a,0x53, 0xba,0xf3,0xa6,0x57, 0x23,0x65,0x1b,0x25, 0x62,0xc2,0x5a,0xd7, + 0x94,0x47,0xa0,0x81, 0xbe,0xda,0x43,0x47, 0xda,0x84,0x59,0x08, 0x30,0xd3,0x8c,0xd6, + 0xad,0x70,0x4f,0x54, 0xda,0x98,0xc6,0x9c, 0xc3,0x50,0xb9,0x44, 0x8c,0x92,0x69,0xd1, + 0xc8,0xa2,0x49,0x91, 0x10,0xff,0x55,0xf7, 0xec,0x93,0x87,0x5b, 0x03,0xc0,0x64,0x0c, + 0xb4,0xd9,0x12,0x38, 0x67,0x0e,0x13,0x99, 0xdc,0x09,0x38,0x70, 0x70,0xd2,0x88,0x17, + 0x61,0x65,0x05,0x54, 0x0e,0x7a,0x06,0x14, 0xa0,0x1d,0x40,0x92, 0xd3,0x20,0xfb,0x66, + 0x39,0x42,0x1e,0x63, 0x27,0x2d,0x8d,0x24, 0x46,0x41,0x6b,0x20, 0x8d,0x04,0x0a,0x08, + 0x3e,0xe2,0xd9,0x2a, 0x0c,0x99,0x24,0xc0, 0x28,0x93,0x62,0xb4, 0x43,0x32,0x48,0x64, + 0x25,0xc5,0x64,0x8c, 0xde,0xc0,0x67,0x3d, 0x01,0x2b,0x1b,0x5b, 0xae,0x0f,0x70,0x89, + 0xce,0x64,0xe1,0xb9, 0xcf,0x16,0x81,0x64, 0xe8,0x8e,0x1b,0x01, 0x08,0x25,0xbc,0x8a, + 0x94,0x34,0xc6,0xa0, 0xd5,0xb0,0x60,0x23, 0x0a,0x81,0x40,0x06, 0x22,0x8a,0x0b,0xe0, + 0x11,0xb2,0xe4,0x03, 0x1c,0xb1,0x14,0x4b, 0x85,0xc3,0x0e,0x71, 0x19,0x6d,0x21,0x50, + 0x1f,0x1e,0x15,0x7b, 0x07,0x10,0xbb,0x86, 0x7e,0x57,0x5b,0xad, 0xc9,0xb8,0x81,0xae, + 0x40,0xc4,0xc7,0x0d, 0x25,0xfa,0x2e,0x9a, 0x0d,0xff,0x8c,0xa8, 0xbe,0x4b,0xa8,0x6a, + // 0x200: + 0x97,0x05,0x36,0x3b, 0x65,0x30,0x82,0xba, 0x50,0xbe,0x3e,0xa0, 0xcc,0x27,0x48,0xab, + 0x86,0x2e,0xde,0xdf, 0xf7,0x02,0x05,0x02, 0xa1,0x56,0x71,0x31, 0xbe,0x66,0x04,0xaf, + 0x4a,0xb5,0x46,0xcd, 0xc6,0x76,0x79,0x30, 0xe9,0xa4,0x88,0x62, 0x52,0x30,0xd1,0x18, + 0x0b,0x94,0x0c,0x89, 0x68,0xb8,0x2b,0x16, 0x55,0xab,0xf4,0xa8, 0x4f,0x81,0x97,0xbd, + 0xe1,0xdd,0xf0,0x53, 0xce,0x66,0xec,0xee, 0xec,0xee,0x5e,0xd9, 0xd9,0xe5,0x63,0xd4, + 0x8f,0xba,0xed,0x2b, 0x78,0xca,0x2c,0xf6, 0x54,0x5c,0x90,0xa8, 0x74,0x21,0xfc,0x32, + 0x50,0x06,0x70,0xa9, 0x14,0x16,0x01,0x17, 0xe1,0x48,0x91,0xbe, 0x67,0x54,0xcd,0xc3, + 0x0d,0xe0,0x7a,0x91, 0x92,0x5a,0xf1,0xae, 0x17,0x6f,0xda,0xff, 0x8f,0xa0,0xa6,0x50, + 0x6e,0x98,0x3e,0x74, 0x58,0x81,0x10,0x18, 0x67,0x5f,0xf3,0x4b, 0x21,0xbc,0x8c,0x76, + 0x99,0xe8,0x63,0x71, 0x22,0x80,0x42,0xfb, 0xb1,0x44,0x6f,0xdb, 0x0c,0xd4,0x33,0x42, + 0x88,0xd1,0x24,0x10, 0x03,0xc6,0xe1,0xab, 0x40,0x07,0xab,0xaf, 0xae,0x97,0x3a,0x34, + 0x4f,0x81,0x1d,0x31, 0xaf,0x6c,0x19,0x8e, 0x61,0x54,0x7f,0xbf, 0x07,0x1e,0x95,0xdf, + 0x9d,0xc1,0x0d,0x8c, 0x95,0x19,0x88,0x17, 0xe4,0x82,0xc5,0x64, 0xb9,0xe4,0xb6,0xd5, + 0x52,0xb0,0xc3,0x0c, 0x51,0x28,0xba,0xbe, 0xe7,0xb3,0xfb,0x73, 0x79,0xb8,0x68,0x3b, + 0xc0,0x8b,0x74,0xbf, 0x9c,0xb7,0x63,0x20, 0xa4,0xbd,0xbc,0x0b, 0x06,0xa5,0x38,0xbd, + 0xdb,0x14,0x10,0x77, 0x01,0xec,0x53,0x31, 0xc5,0x8d,0xf7,0xcd, 0xc4,0x03,0xcc,0x23, + // 0x300: + 0x0e,0xea,0x69,0xe8, 0xe8,0x91,0x24,0xcd, 0x02,0xfb,0xbf,0x2f, 0x83,0xa0,0x4a,0x40, + 0x92,0x4d,0xe8,0xbf, 0x08,0xe3,0x7d,0xd5, 0xaf,0xdd,0x15,0x51, 0x5d,0x1a,0x96,0x02, + 0xc0,0xa6,0x9e,0x72, 0xf0,0x7a,0x40,0xf1, 0xc8,0xc9,0xce,0x36, 0x8f,0x0a,0x8c,0xea, + 0x7a,0x37,0xc2,0x2b, 0x11,0x1c,0x87,0x1c, 0x9c,0x9b,0xc4,0xaa, 0xd6,0x48,0x63,0x08, + 0x46,0x18,0x8b,0xdc, 0x0e,0x97,0x42,0x42, 0xc9,0xae,0x45,0xa2, 0x78,0xf8,0xf6,0x49, + 0xa6,0x38,0x4e,0x1c, 0x77,0x03,0x74,0xbc, 0x29,0x52,0xa4,0x29, 0x18,0x61,0x66,0x05, + 0x40,0x9c,0x71,0x94, 0x20,0xcd,0xb1,0x53, 0xbe,0x5e,0x14,0x09, 0xe7,0x1c,0xda,0x57, + 0x0c,0x0c,0x32,0x04, 0xc2,0x38,0xa8,0x2b, 0xd1,0x79,0xab,0x72, 0x6f,0x1e,0x23,0x21, + 0x03,0x10,0xb2,0x71, 0xae,0xc6,0x4a,0xb7, 0x30,0x18,0x67,0x75, 0x70,0x74,0x23,0xb7, + 0xad,0xf2,0xe0,0x4b, 0x59,0xa2,0x50,0x89, 0xcf,0xf1,0xae,0x8a, 0x51,0x5b,0x9c,0x14, + 0x29,0x15,0x1f,0xe7, 0xcf,0x1a,0x49,0xb5, 0x68,0x8a,0x68,0x60, 0x33,0xb5,0xa1,0x23, + 0x2e,0x44,0x30,0x7c, 0x51,0x07,0x96,0xa3, 0x91,0xe4,0x64,0x97, 0x94,0x4c,0x55,0x69, + 0xcc,0x01,0xcf,0x49, 0x82,0x8e,0x18,0xf4, 0xd6,0x93,0x17,0xac, 0x5a,0x10,0x62,0x1b, + 0xff,0xbc,0xcb,0xff, 0x14,0x0b,0x1f,0x30, 0x5d,0x88,0xff,0x17, 0x72,0x45,0x38,0x50, + 0x90,0xaa,0x33,0x10, 0x4f, + // 0x3e5 = 997 Bytes +}; // ctcode_bad1code_jap_bz2[] + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// ctcode_mod1_pal_bz2 (rmcp/mod1.bin) + +const u8 ctcode_mod1_pal_bz2[2187] = +{ + 0x00, 0x00, 0x0c, 0xfc, // 3324 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xfc,0xd3, 0x19,0x36,0x00,0x03, + 0xbb,0xff,0xff,0xff, 0x7f,0xee,0xff,0xfc, 0x7f,0x7e,0x7f,0xfe, 0x6f,0xf6,0xdd,0xef, + 0x65,0xcc,0x6f,0xff, 0x7d,0xef,0x5f,0xff, 0x47,0x58,0x5f,0xef, 0xe6,0x75,0xfd,0xef, + 0xd6,0x7e,0xdf,0xe0, 0x06,0xc6,0xed,0xb0, 0x02,0xb4,0x4e,0xeb, 0x76,0xeb,0x76,0xda, + 0xcb,0xb3,0x13,0xae, 0xe8,0x68,0x85,0x3d, 0x20,0x51,0x86,0x22, 0x66,0x53,0x4d,0xa8, + 0xcd,0x13,0xd4,0xf2, 0x9a,0x7a,0x9a,0x06, 0x9a,0x18,0x46,0x8c, 0x40,0x64,0xd0,0x03, + 0x43,0x4c,0x83,0x40, 0x69,0xa0,0xd0,0xc9, 0x88,0x34,0xd0,0x68, 0x69,0x88,0x00,0x68, + 0x68,0x34,0xd1,0xb5, 0x07,0xa9,0xa3,0xd4, 0x34,0xd3,0x26,0x83, 0x53,0x44,0x26,0x43, + 0xd4,0x09,0x88,0xc9, 0x3d,0x1a,0x34,0x34, 0xd1,0x3f,0x51,0xa0, 0x1a,0x4d,0x1a,0x60, + 0x8c,0x4d,0x00,0x00, 0x34,0xd3,0x43,0x40, 0x00,0xc8,0x64,0x00, 0x06,0x80,0x32,0x00, + 0x03,0x41,0xa0,0x00, 0x01,0xa1,0x93,0x43, 0x41,0xa1,0x02,0x21, 0xa0,0xa6,0xd1,0x0f, + 0x29,0xa7,0xa9,0xe9, 0x1b,0x50,0x3d,0x40, 0x03,0x40,0xf5,0x00, 0x68,0x34,0x00,0x00, + 0x00,0x03,0x40,0x00, 0x00,0x00,0x69,0xea, 0x00,0x00,0x00,0x00, 0x03,0x40,0x00,0x00, + 0x94,0xd2,0x10,0x40, 0x46,0xa7,0x94,0xf4, 0x9a,0x4c,0x99,0x1b, 0x53,0x68,0xd1,0x34, + 0xf5,0x3d,0x04,0x7a, 0x8f,0x53,0x46,0xd4, 0xc8,0x06,0x8f,0x48, 0xc8,0xd3,0x4d,0x1a, + 0x1a,0x64,0x68,0x0d, 0x00,0x19,0x00,0x0d, 0x1a,0x03,0x46,0x8d, 0x0d,0x0d,0x0d,0x0d, + // 0x100: + 0x00,0x06,0x86,0x8f, 0x53,0x40,0xd1,0x06, 0x80,0x1a,0x01,0xa6, 0x11,0x90,0xd0,0x00, + 0xc1,0x32,0x0d,0x34, 0x1a,0x64,0x1a,0x03, 0x26,0x80,0x0c,0x8c, 0x43,0x4c,0x80,0x30, + 0x43,0x21,0xa3,0x4d, 0x00,0x0d,0x0d,0x00, 0xd0,0xc9,0x90,0x00, 0x00,0x19,0x34,0x19, + 0x08,0x34,0x00,0xd0, 0x0d,0x30,0x8c,0x86, 0x80,0x06,0x09,0x90, 0x69,0xa0,0xd3,0x20, + 0xd0,0x19,0x34,0x00, 0x64,0x62,0x1a,0x64, 0x01,0x82,0x19,0x0d, 0x1a,0x68,0x00,0x68, + 0x68,0x06,0x86,0x4c, 0x80,0x00,0x00,0xc9, 0xa0,0xc8,0xac,0xfd, 0x65,0x30,0x8f,0x82, + 0x11,0x47,0x74,0xda, 0x6a,0x6e,0x73,0xad, 0x01,0xee,0x2c,0x9c, 0x68,0x20,0x20,0x9b, + 0xb8,0xa8,0x9d,0xa9, 0xb3,0x48,0x69,0x5a, 0x80,0x44,0xa6,0xdb, 0x69,0xef,0x64,0x83, + 0xa7,0x0a,0xc9,0x54, 0x5f,0xfa,0x1b,0x8f, 0x5c,0x93,0xfa,0xab, 0x9a,0x61,0xda,0xe8, + 0xbd,0x4c,0xab,0x5a, 0x24,0x90,0x41,0x90, 0x33,0x06,0x55,0xd7, 0x33,0x0a,0xb8,0xc7, + 0x5f,0x70,0xe0,0x8f, 0x92,0xe8,0x53,0x98, 0x31,0x08,0xc0,0xaa, 0xb9,0xe0,0x54,0x00, + 0xc3,0x93,0xd0,0xaf, 0xef,0xd4,0xc2,0x94, 0xc6,0x96,0x46,0x8d, 0x52,0x96,0x44,0x52, + 0x6e,0xcf,0xf6,0xdd, 0x4c,0xd0,0xd2,0x72, 0x04,0x20,0x10,0x22, 0x04,0x22,0xc2,0x7b, + 0x94,0xed,0x6e,0xda, 0x37,0xfb,0xfe,0x0d, 0x5a,0x4b,0x27,0x02, 0x87,0x68,0x87,0xe1, + 0x5c,0x99,0x29,0x3c, 0x81,0x41,0x2c,0xad, 0xd8,0xc5,0x57,0xb5, 0xeb,0x72,0xbf,0x0e, + 0x14,0x5e,0xfb,0xa6, 0xa4,0xe4,0xc6,0x41, 0x98,0x6d,0x1d,0x6f, 0xd4,0xac,0xb7,0x0c, + // 0x200: + 0xd2,0x8b,0xe4,0x03, 0x90,0x55,0x22,0xcc, 0x97,0x10,0x3b,0x36, 0x0c,0x1b,0x34,0x75, + 0xc4,0xd9,0xa8,0x28, 0xbc,0xf8,0x11,0xe2, 0x12,0x8b,0x74,0x69, 0x21,0x11,0x5d,0x68, + 0xf0,0x1a,0xcd,0x89, 0x4e,0xe9,0x01,0x6b, 0x2c,0xd2,0x19,0x01, 0x5d,0x31,0xab,0x80, + 0x22,0x62,0xa1,0x01, 0x84,0x20,0x8a,0x14, 0x20,0x45,0xcc,0xcd, 0xbd,0x56,0x6e,0x4d, + 0xe9,0x4a,0xc9,0x42, 0x8a,0x74,0xe9,0xd3, 0x88,0x62,0xd5,0x87, 0x42,0x60,0xab,0x11, + 0xc6,0x2a,0xbd,0x61, 0xc5,0x3a,0x64,0xe6, 0x77,0x85,0xa3,0x87, 0x52,0xc9,0x24,0x49, + 0xe7,0xa1,0xaa,0xc7, 0x12,0x31,0x90,0x87, 0x67,0x67,0x64,0x38, 0x9c,0x10,0xd4,0xa6, + 0x71,0x14,0x53,0x0e, 0x26,0xae,0x46,0x9d, 0x31,0xfd,0xa0,0x23, 0x21,0xf0,0xb4,0xa9, + 0x3c,0x05,0x08,0x40, 0xb7,0x59,0xda,0x41, 0x44,0xac,0xde,0xc4, 0xbd,0x49,0xdc,0x9f, + 0x4a,0xd4,0x10,0xc0, 0x12,0xa2,0x80,0x49, 0xad,0xc4,0x36,0xf8, 0x8e,0xdd,0xaa,0x66, + 0x2b,0x52,0xf7,0xce, 0xe9,0x2d,0x03,0x8e, 0x84,0x22,0xc0,0xe8, 0x4c,0xdb,0x3a,0xa4, + 0x42,0xb3,0xbc,0x55, 0x9a,0xe5,0xf5,0xf4, 0x42,0xb0,0x98,0x48, 0x38,0xe2,0x2e,0x4c, + 0x86,0xb9,0x52,0x9d, 0xf7,0x46,0x38,0x3b, 0x26,0x04,0x30,0x9a, 0xf0,0x77,0x49,0x21, + 0x59,0x09,0xdb,0x53, 0x5d,0xca,0xe2,0x19, 0x24,0x02,0x2f,0x90, 0xe2,0x66,0x12,0xfa, + 0x11,0x76,0x00,0xe8, 0x48,0x38,0x04,0xee, 0x68,0xaf,0x24,0xc6, 0xc7,0x99,0x66,0x12, + 0x3d,0x94,0x41,0xb2, 0xec,0x30,0xa1,0x38, 0xe6,0xa2,0x9b,0x84, 0x2c,0x53,0x48,0xc6, + // 0x300: + 0xf6,0x91,0xc8,0x23, 0x76,0x29,0xb3,0xd5, 0x9d,0x00,0xe5,0xc3, 0xf4,0xb0,0x4a,0x2e, + 0x0f,0x71,0x0e,0x21, 0xd9,0x4c,0x8c,0x65, 0x5d,0x9b,0x0d,0x5c, 0x6a,0x26,0x60,0x95, + 0x44,0x62,0xc5,0xe2, 0x84,0x26,0x37,0x4f, 0x01,0x99,0xaa,0xc5, 0x22,0xea,0x1b,0x2d, + 0x9c,0x76,0x06,0xb0, 0x80,0x98,0x08,0x6d, 0x88,0xb3,0x32,0xb2, 0xcd,0x42,0x28,0x46, + 0xd4,0x6a,0x88,0x06, 0xc6,0x16,0x3a,0xd6, 0x74,0x4e,0x73,0xac, 0x1a,0x18,0x8f,0xc5, + 0xf3,0x5d,0x7a,0x4b, 0x0b,0x86,0xbf,0x95, 0x9b,0xcf,0x26,0x79, 0xb8,0x94,0x71,0x8b, + 0x77,0xa5,0x7c,0x8b, 0x96,0xae,0xef,0x2b, 0x19,0x54,0x2a,0x5c, 0x74,0x9d,0xde,0x65, + 0x00,0x6b,0x46,0x50, 0x0c,0xa6,0xb8,0xcf, 0xaf,0x33,0x2d,0x59, 0xa2,0x2d,0x67,0xde, + 0xa4,0x84,0xa3,0xc9, 0x63,0xcd,0xa8,0x02, 0xdf,0xb2,0x40,0x95, 0x36,0xcc,0xdb,0x30, + 0x94,0x2a,0x94,0xc8, 0x73,0x71,0xb1,0x12, 0x51,0x75,0xd8,0x49, 0x22,0x0f,0xce,0xc5, + 0x4f,0xdb,0x26,0x82, 0x0a,0xd5,0xb3,0xd9, 0x30,0x23,0xa1,0x95, 0xfe,0x9a,0x2c,0x63, + 0x4d,0x82,0xa0,0x1b, 0x1d,0x9c,0xae,0xb2, 0x91,0x4a,0x34,0x18, 0x06,0xf9,0x18,0x1a, + 0xbd,0x96,0x9a,0xbb, 0x1d,0xf9,0x8b,0x85, 0x61,0x8c,0x0b,0x5c, 0x84,0x39,0xcb,0xb7, + 0x9f,0x35,0x53,0x74, 0x86,0x6a,0x53,0x33, 0x72,0xe8,0xe0,0x22, 0x28,0x3c,0x04,0xcc, + 0x7a,0x2d,0xe8,0x00, 0x4d,0x03,0x6c,0x01, 0x45,0x8c,0xa1,0x7f, 0x7e,0xf9,0xad,0xde, + 0x9e,0x96,0xb4,0x80, 0x49,0xd2,0xe2,0x0e, 0xd8,0xeb,0x5a,0x68, 0xf6,0xe1,0xa4,0xe2, + // 0x400: + 0xf7,0x9b,0x3c,0xc7, 0x7b,0xa0,0x03,0x4b, 0x71,0x29,0x0f,0x16, 0x94,0xe3,0x72,0x61, + 0xb5,0xb7,0x25,0x42, 0x47,0x2e,0xe0,0x07, 0x08,0x40,0x16,0x7a, 0xcb,0x66,0x16,0x72, + 0x9f,0x84,0x06,0x1a, 0x58,0x64,0x55,0x61, 0x54,0xbe,0x92,0x9f, 0x4f,0x55,0x9a,0x62, + 0x34,0x97,0x4d,0x3b, 0xe6,0x57,0x5e,0xe8, 0x4b,0x2c,0x43,0xcf, 0xcb,0x2d,0x23,0xd2, + 0x71,0x86,0xf2,0x74, 0x80,0x33,0x12,0x76, 0x41,0x2e,0x92,0xca, 0xbe,0x2d,0xf3,0x71, + 0x20,0x30,0xff,0x73, 0x0d,0xad,0x56,0x3a, 0xc9,0x9f,0x5a,0x62, 0x6f,0xf6,0x73,0xdd, + 0x62,0xf1,0xb9,0x4c, 0xbd,0xe8,0x78,0x99, 0x62,0x69,0x2d,0x39, 0xad,0x5d,0x85,0xbf, + 0x17,0x90,0x13,0x0a, 0x7b,0xdb,0x28,0x24, 0xc3,0x81,0x3e,0x20, 0x41,0x01,0x55,0xd0, + 0x98,0x2c,0xfb,0x66, 0x9a,0xdd,0x72,0x81, 0x6f,0x65,0x58,0xb6, 0x87,0x56,0xc6,0xa5, + 0x9d,0x16,0x8c,0x67, 0x9c,0x72,0x21,0xe5, 0x0c,0xa1,0x48,0x65, 0x41,0x95,0xaf,0xb5, + 0x0d,0x53,0x0e,0xac, 0x9e,0x45,0x24,0x92, 0x0f,0x7d,0xc1,0x38, 0x83,0x04,0xe7,0xd7, + 0xe2,0x5f,0xc6,0x92, 0x6b,0x29,0x7e,0x71, 0x4b,0x73,0x61,0x1d, 0x65,0x5d,0xc6,0x68, + 0x86,0x84,0xa5,0xe3, 0x99,0xe5,0xd5,0x10, 0xeb,0x4f,0x25,0xaa, 0xb2,0x62,0xf7,0x8a, + 0xb9,0x93,0x0f,0x26, 0xd0,0x45,0x11,0xbe, 0x02,0x45,0xdd,0xfb, 0x17,0x7c,0x2c,0x30, + 0xb0,0xf0,0xc1,0x3f, 0x14,0x88,0x81,0x32, 0x5b,0xc6,0x4a,0x10, 0xe1,0xa3,0xde,0x60, + 0x60,0xa6,0x92,0x49, 0xad,0x65,0xb4,0x90, 0x66,0xe0,0xf6,0x0e, 0xc7,0xd6,0x9b,0xe2, + // 0x500: + 0x85,0x99,0xe3,0x49, 0xd7,0x6d,0x75,0x35, 0x8c,0x59,0x67,0x2c, 0x24,0xfd,0x14,0xd3, + 0xe6,0xe3,0x82,0x93, 0xe0,0xa0,0xd6,0x04, 0x94,0x4f,0x9e,0xe6, 0x27,0x86,0x45,0x5b, + 0x67,0x6b,0xba,0x60, 0x6c,0x54,0xda,0x8c, 0x09,0x78,0x62,0xb0, 0xf6,0x84,0xf2,0x22, + 0x56,0x2a,0x72,0x57, 0x01,0xfc,0xde,0x8a, 0x96,0x03,0xf2,0x92, 0x0f,0xb0,0xa5,0xaa, + 0x25,0x15,0x0a,0x6a, 0x95,0x99,0x9a,0xd5, 0xc0,0xaf,0x52,0x8e, 0x51,0xcc,0xcb,0x5c, + 0x58,0xb3,0x33,0x4c, 0x34,0x65,0x45,0x0e, 0x41,0x13,0x4b,0x35, 0xf9,0xde,0x84,0x66, + 0x32,0xfb,0xf7,0xb0, 0x7d,0x68,0x73,0xaa, 0x32,0x28,0xc0,0x94, 0x53,0xb5,0xb2,0x6e, + 0x17,0x79,0x47,0x1f, 0x06,0x4f,0x1f,0x16, 0x85,0x86,0x14,0x55, 0x0b,0x65,0x37,0xe8, + 0x23,0x26,0x8c,0xbe, 0x88,0x9c,0x88,0x60, 0x63,0x6c,0x0c,0x36, 0xd4,0x43,0x1f,0x7b, + 0x0e,0x1c,0xba,0x79, 0x92,0x7f,0x85,0xd9, 0xd2,0xea,0x05,0xb8, 0x1d,0xc8,0x43,0xe9, + 0x71,0x88,0x63,0x28, 0xd2,0x6b,0xdf,0x8f, 0xbc,0x1f,0x85,0x8d, 0xa3,0xc7,0xb4,0x7e, + 0xb0,0xd4,0xa8,0xcc, 0x80,0xc1,0x7b,0x5c, 0xd6,0xd2,0xd2,0x0c, 0xc6,0x00,0x91,0x3a, + 0xbf,0xe8,0x6e,0xaf, 0x89,0xe9,0x96,0x23, 0xcd,0xa6,0x39,0xa2, 0xd6,0x9c,0x84,0x48, + 0xf4,0x53,0x4f,0x88, 0x4e,0x20,0xd9,0x98, 0x0a,0x55,0xc8,0x48, 0x0c,0x80,0x2c,0x41, + 0x2d,0x68,0x48,0x41, 0xd5,0xb7,0x49,0x94, 0x6a,0xa5,0xb9,0xdf, 0x7f,0x1c,0xbd,0xcf, + 0x43,0xa4,0xbd,0xba, 0x6c,0x1a,0x64,0x9b, 0xa1,0x17,0xb5,0xed, 0xe6,0xe5,0x3d,0x9c, + // 0x600: + 0x9a,0x2f,0x73,0x94, 0xda,0xb9,0x95,0xc1, 0xe9,0xba,0x1c,0x3b, 0xf3,0x0c,0x69,0x66, + 0x48,0x98,0x86,0x20, 0x38,0x81,0x4d,0x30, 0x81,0x00,0xb0,0x23, 0x28,0x1d,0xc9,0x23, + 0x55,0x45,0x4b,0x22, 0xc9,0xda,0xdd,0x39, 0xa9,0x50,0x9c,0x54, 0x79,0x10,0x14,0x37, + 0x9c,0x43,0xa8,0x04, 0x43,0xec,0x60,0x89, 0x64,0x3d,0x51,0x84, 0x87,0x06,0x38,0x98, + 0xaa,0xb9,0xb2,0xf8, 0x2e,0x4f,0xa0,0x82, 0x9e,0x0f,0x06,0x95, 0x10,0x85,0x8c,0x80, + 0xc0,0xa8,0xb5,0x88, 0x3c,0x74,0x75,0xf7, 0xf3,0x73,0xd5,0x2a, 0xde,0xaa,0x88,0x84, + 0x70,0x33,0x89,0x3d, 0xe0,0x68,0xc2,0x23, 0xbe,0x6a,0xd2,0xc9, 0x57,0xe3,0xb1,0x82, + 0x0e,0x60,0x89,0x7b, 0x8d,0xc6,0x99,0xfc, 0x56,0xce,0x1d,0xc0, 0xb0,0xe8,0xd1,0xcb, + 0x66,0x5a,0x3f,0x75, 0x65,0x30,0x26,0x21, 0x4c,0xa7,0xb7,0xe4, 0x91,0x09,0xbc,0x91, + 0xe5,0xa7,0x5f,0xea, 0x27,0xe9,0xd7,0x96, 0x93,0x77,0xbd,0x7c, 0xba,0x54,0x6d,0x4a, + 0x0e,0xc9,0x90,0x46, 0xb6,0x32,0x8a,0x20, 0x28,0x46,0x22,0x06, 0x22,0x09,0x92,0x51, + 0x07,0xe4,0xe0,0xc3, 0xa0,0x48,0x23,0xa8, 0x99,0xd5,0xe2,0x74, 0x3a,0x66,0xa1,0x32, + 0xc2,0xab,0xbc,0xa6, 0x6a,0xda,0x7d,0x14, 0xdb,0x0d,0xa1,0xde, 0xdf,0x5d,0xab,0x2e, + 0xc6,0x4b,0x86,0xd7, 0xeb,0x27,0xd7,0xae, 0x44,0xf4,0x12,0x54, 0x27,0x25,0x01,0x4a, + 0x8a,0x5d,0x63,0x44, 0xe9,0x8f,0xbd,0x50, 0xe7,0xca,0xda,0x72, 0xd8,0xf9,0xd5,0xa0, + 0x93,0x41,0xeb,0x62, 0xb0,0x10,0x07,0xe9, 0x53,0xb1,0x58,0x7f, 0x55,0xd9,0xe8,0x95, + // 0x700: + 0x23,0x00,0xd8,0x11, 0x84,0xaa,0x82,0xa1, 0xd3,0xca,0x4a,0x65, 0xad,0x29,0xa8,0x33, + 0x04,0xda,0xc5,0xe2, 0x65,0x68,0xa2,0xb8, 0x62,0xa6,0x72,0x76, 0x1d,0xbb,0xcc,0x2a, + 0x17,0x95,0x4e,0x44, 0xbd,0x9a,0xd9,0x06, 0x6a,0x2a,0xa0,0x44, 0x87,0xf5,0x4c,0x22, + 0x9a,0x51,0xc1,0x96, 0x7b,0x5e,0xc1,0x98, 0x54,0x2e,0x89,0x08, 0xf7,0xe1,0xc4,0x16, + 0xd6,0x7d,0x24,0x25, 0xaa,0x5a,0xc9,0xf8, 0xda,0xed,0x94,0x2b, 0xe1,0x06,0x77,0xdf, + 0x81,0xad,0x22,0x08, 0x91,0x01,0x43,0xd6, 0x70,0x82,0x49,0x99, 0x7e,0xd8,0x76,0x56, + 0x3e,0x43,0x93,0x26, 0x30,0x7c,0x97,0xec, 0x96,0x2c,0xe7,0x16, 0x04,0x34,0x20,0x22, + 0xf9,0x10,0x20,0x33, 0x8d,0x89,0x55,0xb3, 0x1a,0x60,0xad,0xa1, 0xdc,0x1b,0x82,0xf2, + 0x05,0x53,0x9a,0xa9, 0xb0,0xd4,0x46,0xbc, 0x76,0xb6,0xae,0xd7, 0x84,0xd6,0x10,0x54, + 0x9d,0xa8,0x59,0xa6, 0x18,0xcb,0x80,0x24, 0x58,0xa7,0x5f,0xfe, 0x76,0x99,0x32,0xb8, + 0x87,0x87,0x1c,0xce, 0x5d,0x57,0x54,0xd3, 0x47,0x7f,0x6c,0xeb, 0x84,0xe0,0x36,0x48, + 0xc4,0x4f,0xcd,0xf4, 0xac,0xc9,0x4d,0xdd, 0xe2,0xa2,0x12,0xf6, 0xaf,0x84,0xe0,0x9d, + 0x12,0xc6,0xd3,0x82, 0xb6,0x39,0x03,0x4a, 0xd0,0xa9,0x18,0x4f, 0x06,0xed,0xc8,0xcb, + 0xb2,0xa4,0x65,0x61, 0x90,0x8a,0x13,0x3b, 0xcf,0xee,0x6f,0xaf, 0x83,0x70,0x46,0x2c, + 0x63,0xba,0x20,0x19, 0x99,0x3d,0x4c,0xcb, 0x35,0x6d,0x05,0xe9, 0xbb,0xac,0xf6,0xec, + 0x68,0x93,0x08,0x68, 0x6b,0x7f,0xd4,0x3c, 0xa8,0x13,0x6c,0xd1, 0x89,0x78,0x12,0xfb, + // 0x800: + 0x15,0x89,0xa8,0x22, 0x6a,0x5d,0x19,0x5f, 0xe3,0x91,0x00,0x03, 0x30,0x6c,0x13,0x22, + 0xc6,0x41,0x50,0x47, 0x57,0x04,0x6c,0xe3, 0x53,0xcd,0x35,0x14, 0x24,0x1e,0xbd,0x6a, + 0x80,0x78,0xa3,0x8b, 0x6a,0xb5,0x79,0x2d, 0x8c,0x40,0xc2,0xcf, 0x3a,0xe6,0x0a,0x82, + 0x90,0x83,0x20,0x61, 0x33,0x40,0xa3,0x37, 0xa9,0x71,0x47,0x9e, 0xb6,0xae,0x91,0x20, + 0x50,0x81,0x45,0x48, 0x60,0x30,0xc4,0x43, 0x95,0x2a,0x9d,0x42, 0x02,0x32,0x09,0x40, + 0x4b,0x3e,0x48,0xdc, 0xb1,0x8f,0x20,0x0e, 0x37,0x64,0x49,0x0c, 0x71,0xab,0xe4,0x40, + 0x94,0x88,0x2e,0x1b, 0xc0,0xc9,0x9f,0x69, 0x30,0xc4,0xe4,0x56, 0x15,0x35,0x9d,0x8b, + 0x68,0x66,0xe4,0x65, 0x8d,0x06,0xad,0x70, 0x01,0x08,0x48,0xff, 0x8b,0xb9,0x22,0x9c, + 0x28,0x48,0x7e,0x69, 0x8c,0x9b,0x00, + // 0x887 = 2183 Bytes +}; // ctcode_mod1_pal_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_mod1_usa_bz2 (rmce/mod1.bin) + +const u8 ctcode_mod1_usa_bz2[2180] = +{ + 0x00, 0x00, 0x0c, 0xfc, // 3324 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xde,0xf4, 0x34,0xfc,0x00,0x03, + 0xb8,0x7f,0xff,0xff, 0x7f,0xee,0xff,0xfc, 0x7f,0x7e,0x7f,0xfe, 0x6f,0xf6,0xde,0xef, + 0x65,0xcc,0xef,0xff, 0x7c,0xff,0x5f,0xff, 0x47,0x40,0x5f,0xef, 0xe6,0x77,0x7d,0xef, + 0xd6,0x5e,0xdf,0xe0, 0x06,0xc6,0xed,0x00, 0x1a,0xdb,0x33,0xbb, 0x3b,0xbb,0x87,0x57, + 0x2e,0xee,0xe2,0x6b, 0xb8,0x34,0x12,0x68, 0x93,0xd1,0x04,0xfd, 0x12,0x63,0x48,0x64, + 0x4f,0xd4,0xd0,0xf4, 0x0d,0x4c,0x81,0x92, 0x6d,0x4f,0x23,0xd4, 0x68,0x6a,0x60,0xca, + 0x6d,0x46,0x03,0x43, 0x41,0x3d,0x46,0x00, 0x68,0x00,0x8c,0x11, 0x81,0xea,0x02,0x64, + 0x18,0x6a,0x0f,0x41, 0xa9,0xa6,0x9e,0x9a, 0x34,0x34,0xc9,0x34, 0x1a,0x9a,0x44,0xd1, + 0x89,0x94,0xc1,0xa2, 0x6a,0x69,0xe0,0x6a, 0x61,0xaa,0x7e,0xa2, 0x64,0xcd,0x46,0x43, + 0x35,0x1a,0x34,0x07, 0xa8,0x00,0x01,0xea, 0x34,0x07,0xa8,0x00, 0x00,0x1a,0x00,0x00, + 0xd0,0x01,0xa0,0x34, 0x00,0x00,0x06,0x8d, 0x06,0x86,0x83,0x44, 0x68,0x82,0x9e,0x82, + 0x9e,0x29,0xa1,0xfa, 0x90,0xf5,0x32,0x36, 0xa7,0xa4,0x7a,0x80, 0x34,0x7a,0x83,0x6a, + 0x1a,0x03,0x46,0x80, 0x68,0x00,0x03,0x40, 0x34,0x01,0x90,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x04,0xa6,0x91,0x02, 0x32,0x26,0x53,0x68, 0xd2,0x26,0x4d,0xa9, + 0xb2,0x8f,0x29,0xe5, 0x32,0x69,0xe2,0x4d, 0x3d,0x4c,0x83,0x27, 0xa8,0x0f,0x50,0x19, + 0x00,0x68,0x0d,0x01, 0xea,0x00,0x00,0x64, 0x34,0x34,0x34,0xd3, 0x40,0x00,0xd3,0x40, + // 0x100: + 0xd0,0xd0,0x06,0x80, 0x69,0xa3,0x41,0xea, 0x20,0x0d,0x03,0x4d, 0x06,0x83,0x13,0x20, + 0x06,0x86,0x41,0xa3, 0x00,0x80,0x03,0x4d, 0x19,0x01,0x84,0x06, 0x4c,0x83,0x26,0x99, + 0x34,0x31,0x19,0x32, 0x03,0x40,0x01,0xa3, 0x20,0x62,0x64,0xc8, 0x00,0x00,0x03,0x40, + 0x04,0x01,0xa0,0x69, 0xa0,0xd0,0x62,0x64, 0x00,0xd0,0xc8,0x34, 0x60,0x10,0x00,0x69, + 0xa3,0x20,0x30,0x80, 0xc9,0x90,0x64,0xd3, 0x26,0x86,0x23,0x26, 0x40,0x68,0x00,0x34, + 0x64,0x0c,0x4c,0x99, 0x00,0x00,0x00,0x68, 0x01,0xb4,0xf6,0x63, 0xb0,0x0f,0xec,0x09, + 0x67,0x31,0xfa,0xaa, 0xcc,0xd7,0xe3,0x4f, 0x4b,0x89,0x45,0x3a, 0x08,0x0a,0x83,0x8f, + 0x82,0x95,0xfa,0xee, 0x4a,0x88,0x57,0x48, 0x08,0x98,0xfe,0xae, 0xc3,0xf8,0x9f,0x8d, + 0x03,0x28,0x2d,0xaf, 0x7c,0x10,0xc7,0xef, 0x32,0x4e,0xc9,0x34, 0xe3,0xab,0xf7,0xd2, + 0xae,0x3a,0x14,0xb5, 0x20,0x86,0x40,0xcc, 0x19,0x6f,0x26,0x53, 0x33,0xa7,0xf1,0x6f, + 0x5c,0xea,0x07,0x9d, 0xb6,0x4b,0x24,0xc4, 0xa3,0x02,0x7d,0xe6, 0xad,0x60,0x0a,0xc2, + 0x75,0xfc,0x1b,0xd7, 0x07,0xe1,0x63,0x4a, 0xbb,0x93,0x3d,0x68, 0x44,0x52,0xaa,0x5e, + 0xed,0xb9,0x46,0xd0, 0xd2,0x71,0x41,0x44, 0x02,0x82,0x90,0x21, 0x16,0x13,0xdc,0xa3, + 0xb3,0xbf,0xb6,0x6e, 0xf7,0x7b,0xda,0xf4, 0x16,0x4f,0x8a,0x77, 0x68,0x86,0xfe,0xa2, + 0x54,0xa4,0x79,0x01, 0x41,0x2b,0xeb,0xf8, 0xc5,0x9d,0xfd,0xeb, 0x62,0xb7,0xbd,0x81, + 0x83,0xf9,0x46,0x9a, 0x28,0xa6,0x22,0x3c, 0x1f,0x9e,0xe8,0x73, 0xee,0x7a,0x01,0xe5, + // 0x200: + 0x4b,0xc4,0x07,0x3e, 0xcd,0x0b,0x4c,0x9b, 0x7e,0x23,0x9b,0xa6, 0x0d,0xe4,0xde,0x18, + 0x38,0x2e,0x6e,0xa7, 0xa0,0x2f,0x29,0x68, 0x36,0xd5,0x3d,0xa7, 0x78,0x7c,0x17,0x95, + 0x28,0xa9,0x1f,0x47, 0x35,0xa2,0x31,0x6d, 0x93,0xa6,0x0d,0xcf, 0x0f,0x2c,0x6a,0x11, + 0x29,0x58,0x80,0xc2, 0x10,0x45,0x0a,0x10, 0x22,0xe6,0x92,0xf7, 0x33,0x06,0x46,0xdd, + 0x49,0x64,0x9d,0x45, 0x3a,0x74,0xe9,0xc4, 0x31,0x6e,0xc3,0xa1, 0x30,0x57,0x88,0xe3, + 0x15,0xde,0x91,0xc5, 0x32,0x64,0xe7,0x15, 0xbe,0xc9,0x87,0x4a, 0xc9,0x24,0x48,0xf3, + 0x4e,0xd5,0xe3,0x91, 0x18,0xc8,0x21,0xd9, 0xd9,0xd9,0x0e,0x27, 0x04,0x35,0x09,0x9c, + 0x44,0xf4,0x43,0x81, 0xce,0xc8,0x6c,0x13, 0x1f,0xda,0x02,0x32, 0x0f,0x8d,0xaa,0x49, + 0xe0,0x28,0x42,0x05, 0xed,0x2e,0xd2,0x04, 0xf2,0x59,0xb5,0x12, 0xd2,0x4e,0xe4,0xda, + 0xa6,0x9c,0x86,0x18, 0x94,0xf3,0x82,0x4d, 0x7b,0x10,0xef,0x32, 0x1d,0xb3,0x93,0x31, + 0x4d,0x0f,0x75,0xdd, 0x25,0x8e,0xe3,0xa1, 0x08,0xb0,0x3a,0x13, 0x36,0xce,0xb9,0x10, + 0xa5,0xde,0x2a,0xcd, 0x45,0xdb,0xb1,0x0a, 0x44,0xc2,0x41,0xc3, 0x11,0x72,0x54,0x35, + 0xca,0xd4,0x7b,0xb9, 0xa3,0x83,0xb2,0x60, 0x43,0x09,0xaf,0x07, 0x74,0x92,0x15,0x90, + 0x99,0xb3,0x2a,0x72, 0xa1,0x0c,0x92,0x01, 0x17,0x50,0xe2,0x66, 0x12,0xfa,0x51,0x7f, + 0x00,0x74,0x24,0x1f, 0x21,0x33,0x9a,0x8b, 0xc9,0x18,0xd9,0x6b, 0x19,0x84,0x8f,0x69, + 0x10,0x6d,0x13,0x0c, 0x28,0x4c,0x39,0xae, 0xa2,0xe1,0x0b,0x14, 0x50,0x31,0xed,0xd0, + // 0x300: + 0x39,0x04,0x6e,0x45, 0x2e,0x2d,0x2e,0x80, 0x72,0xe1,0xfa,0xd8, 0x24,0x8b,0x83,0xdc, + 0x43,0x88,0x76,0x52, 0xa3,0x5a,0xaa,0x66, 0xd4,0xab,0x8d,0x3c, 0xac,0x12,0x56,0x46, + 0x5c,0x5e,0x28,0x42, 0x61,0x38,0xcc,0xd5, 0xa0,0x91,0x7c,0x86, 0xbb,0xa6,0x71,0x8d, + 0xc2,0x61,0xb7,0xe9, 0x19,0xc6,0x50,0x01, 0xd0,0x20,0x76,0x22, 0x19,0x94,0xba,0x65, + 0x11,0x40,0x6d,0x53, 0x56,0x40,0x36,0x38, 0x86,0xc1,0x9d,0x43, 0xb8,0xb9,0x26,0xbe, + 0x07,0xa2,0xf9,0xea, 0x7a,0xa8,0x46,0xbc, 0x7f,0x7c,0x1f,0xb6, 0xf6,0x69,0x78,0x13, + 0xe7,0x97,0xb6,0x8a, 0xba,0x2b,0x96,0xef, 0xfd,0x8a,0x4b,0xe4, 0x2a,0x1c,0x74,0x9d, + 0xde,0x55,0x00,0x6b, 0x66,0x80,0x08,0x2d, 0xc4,0x93,0xe6,0xd0, 0xc3,0x84,0x9c,0xbb, + 0x1c,0x82,0x90,0xc6, 0xd1,0xc8,0x3a,0xc6, 0x00,0x5b,0x77,0x10, 0x52,0x1b,0xef,0x3e, + 0xea,0xa3,0x34,0x66, 0x43,0x89,0xf0,0xf7, 0xa9,0x60,0xe9,0xfd, 0x4b,0x22,0x13,0x8f, + 0x70,0x8a,0xb0,0x32, 0x59,0x81,0x04,0x15, 0x1f,0x75,0x84,0xba, 0x32,0x19,0xe8,0x42, + 0x84,0x77,0x26,0x9a, 0x01,0x9d,0xaf,0x6d, 0xa0,0x8d,0x81,0xf3, 0x13,0xb0,0xcf,0x79, + 0x18,0x7c,0xff,0x5d, 0xab,0xa9,0x8f,0x08, 0xe4,0x71,0xac,0x0d, 0xae,0xbe,0xab,0x7d, + 0x39,0xb8,0x43,0x36, 0x22,0x66,0x6e,0x41, 0x1f,0x2a,0x22,0x83, 0xc3,0x4c,0xc6,0xe5, + 0xbd,0x10,0x09,0x60, 0x67,0x00,0x4f,0x8d, 0x6c,0xc0,0xc0,0x7c, 0x1f,0x4b,0x93,0xa1, + 0xad,0xa0,0x12,0x74, 0x93,0x7b,0xeb,0xa4, 0x32,0x3b,0xd0,0xc9, 0xe1,0x77,0xf7,0x34, + // 0x400: + 0x7b,0xfc,0xc0,0x16, 0x5d,0x3c,0x67,0xf9, 0x89,0xa1,0x39,0xda, 0x30,0xff,0x3b,0x2a, + 0xc1,0x3f,0x4d,0x00, 0x10,0x22,0x01,0xc9, 0xb4,0xe7,0x3a,0xd3, 0xc6,0x7a,0xd0,0x3b, + 0x0a,0xee,0x6d,0x97, 0x7b,0x16,0xf3,0x19, 0x23,0x39,0xb3,0x35, 0x7b,0x20,0x9a,0x5e, + 0xbd,0xed,0x53,0x0b, 0x0c,0xec,0x31,0xed, 0x51,0xe1,0x38,0x87, 0x45,0x9a,0x20,0x0c, + 0xc5,0x07,0x14,0x2c, 0x0a,0xe6,0xf3,0x06, 0xf2,0x18,0x8a,0x30, 0xbd,0xd1,0x95,0xd0, + 0x24,0x74,0x96,0x69, 0x28,0xcc,0xde,0x9c, 0xfb,0x6a,0x5c,0x3e, 0x43,0x70,0x39,0xfe, + 0x16,0xd3,0x92,0xb3, 0x9e,0x62,0x8f,0x42, 0x57,0x9b,0xa8,0x6b, 0x44,0x9b,0xec,0xa7, + 0x10,0x82,0xe1,0xa1, 0x0e,0xfc,0x02,0xf8, 0xaf,0xb0,0x5b,0xd8, 0xb6,0xdd,0x60,0x73, + 0x28,0x8b,0x60,0x6e, 0x9a,0x2c,0xaa,0x78, 0x9c,0x78,0xae,0xb8, 0x71,0xe8,0xab,0x8c, + 0x9f,0x8e,0xca,0x53, 0x29,0xbd,0xaa,0x25, 0xdb,0x4f,0x0c,0x72, 0x26,0x18,0x88,0xa7, + 0xd5,0xe2,0xa0,0xa1, 0xd3,0x22,0x6c,0xf0, 0xba,0xd9,0x38,0x26, 0xa8,0x3a,0xc8,0x2b, + 0x77,0x36,0x55,0x6d, 0x72,0x7b,0x26,0xea, 0xba,0x6c,0xe0,0xb9, 0x94,0x75,0xc1,0x49, + 0x60,0x90,0x6b,0xf8, 0x6e,0xf7,0xee,0x9b, 0x92,0x31,0x47,0x58, 0x84,0x5f,0xec,0x3f, + 0x22,0x89,0x97,0xae, 0xcc,0xdb,0xfb,0x83, 0x44,0x73,0x05,0x94, 0x1a,0x04,0xa7,0xc9, + 0x15,0xc3,0x8c,0x17, 0xd7,0xc9,0xe3,0x30, 0x2f,0x8c,0x9c,0x49, 0x35,0xbc,0x0c,0xa8, + 0x33,0x6f,0x7a,0xd7, 0x63,0xec,0x4d,0xf1, 0xc2,0xcc,0xd1,0xa0, 0xd9,0x76,0x39,0x94, + // 0x500: + 0x99,0x72,0x61,0x58, 0x49,0xf9,0xc9,0x66, 0xc1,0x8e,0x4a,0x4f, 0x89,0x06,0xe5,0x89, + 0x64,0x2a,0x8f,0xb3, 0x67,0xc1,0x6b,0x88, 0x55,0xeb,0xa7,0x89, 0x8d,0x5a,0x3a,0xe1, + 0x75,0x1a,0x38,0xb4, 0x3d,0x02,0x7c,0xca, 0xc9,0x15,0x81,0x38, 0xba,0x83,0x2b,0x3b, + 0x3a,0x1c,0x12,0x7e, 0x18,0xc7,0x1b,0x5e, 0x4b,0x2a,0x14,0x74, 0xe8,0x66,0x68,0xbd, + 0xdf,0xb9,0x6b,0x38, 0x4e,0x5e,0x63,0x0a, 0xc4,0x33,0x35,0x56, 0x46,0x5c,0x50,0xe2, + 0x84,0x1a,0x4a,0x57, 0xa6,0x7a,0x14,0xcc, 0x85,0x0a,0xb9,0x58, 0xc5,0x23,0x06,0x6d, + 0x7f,0x5c,0x5b,0x3b, 0x7b,0x67,0xc3,0xd3, 0xd2,0xaa,0xaf,0x27, 0xe4,0xe4,0x6a,0x61, + 0x42,0x63,0x8a,0xd9, 0x77,0x6b,0x11,0x0a, 0x52,0x5f,0xac,0x8c, 0x79,0x02,0x2d,0xa1, + 0x0d,0xab,0xcb,0x9d, 0x98,0xb7,0xee,0x64, 0x5a,0x66,0x9f,0xa2, 0x12,0x19,0xda,0x8d, + 0x48,0xf3,0xa2,0x93, 0x19,0xe3,0x40,0x41, 0x73,0x66,0xce,0x13, 0xe3,0x54,0xb4,0xbe, + 0x98,0x02,0xde,0xe7, 0x32,0xd6,0xa2,0x5f, 0x1e,0xa1,0xd1,0x0c, 0xb4,0x46,0x40,0x37, + 0xfb,0x91,0xba,0x7f, 0x8f,0x6f,0x04,0xa9, 0x3e,0x51,0x44,0x78, 0x62,0x22,0xdf,0x1c, + 0xb7,0x04,0x06,0xf7, 0xc4,0xd3,0x91,0x6a, 0x42,0xff,0x14,0x9a, 0x8b,0x21,0x5b,0x37, + 0x1f,0x44,0x40,0x66, 0x1e,0x31,0x5e,0x2a, 0xc0,0x16,0x60,0xdb, 0x68,0x6a,0xa8,0xe5, + 0xc4,0x42,0x64,0xd3, 0x05,0xc2,0x46,0xc4, 0x9c,0x8b,0x6d,0x49, 0x97,0xe6,0xef,0x9f, + 0x0e,0xfc,0xd0,0xe1, 0x0b,0x55,0x5e,0xe9, 0x64,0x9a,0xcf,0x06, 0x7b,0x58,0x54,0x5b, + // 0x600: + 0xb9,0x7d,0x8b,0xd0, 0xe6,0xe4,0x68,0x4c, 0x81,0xa4,0xd2,0x11, 0x32,0x8c,0xa0,0x71, + 0x02,0x96,0x51,0x02, 0x00,0x68,0x13,0x99, 0xe8,0xf4,0x29,0xa4, 0xb3,0xb4,0xc5,0xb9, + 0x1d,0xb1,0xa2,0xeb, 0xd8,0x2b,0xd9,0x8a, 0x22,0x02,0x5c,0xcd, 0xc1,0xfd,0x00,0x88, + 0x60,0x18,0x22,0x69, 0x0f,0x0c,0xe1,0x26, 0x81,0x0e,0x26,0x2b, 0xad,0x79,0x74,0x2e, + 0x4d,0x8f,0x05,0x34, 0x1e,0x0d,0x24,0xf0, 0x85,0x8d,0x80,0x61, 0xd6,0x5b,0x14,0x1b, + 0x54,0x75,0xfa,0x19, 0x79,0xba,0xd5,0xed, 0x12,0x88,0x44,0x38, 0x17,0x6a,0x1f,0x7a, + 0x1c,0xa6,0x85,0x6b, 0x0a,0x3c,0xa0,0xe2, 0x08,0xeb,0xf9,0x01, 0xcc,0x10,0x8d,0x4b, + 0x6b,0x45,0xe4,0xc2, 0x5d,0xec,0x3d,0xcd, 0xc5,0x74,0x5c,0xbb, 0x98,0x34,0xd9,0x09, + 0x02,0x44,0x49,0x2b, 0x5c,0xf8,0xec,0x62, 0x3b,0x88,0x78,0xb3, 0x61,0x6c,0x5b,0xc2, + 0x3a,0x8b,0x09,0xee, 0xf9,0x87,0x4b,0x22, 0xac,0xe0,0x7a,0xb3, 0x20,0x46,0xd3,0x99, + 0x14,0x50,0x1a,0x17, 0x07,0x31,0x10,0x32, 0x42,0x64,0x94,0x80, 0xfa,0x88,0x30,0xe8, + 0x12,0x09,0x35,0xf3, 0x3a,0xb4,0x9d,0x0e, 0x99,0xa8,0x4c,0xb1, 0xe9,0xf3,0x68,0x96, + 0xae,0x53,0x8d,0x6e, 0x7b,0x50,0x78,0x17, 0x92,0xa1,0x12,0x91, 0xf6,0x3b,0x5b,0x3b, + 0xe0,0x8f,0x4e,0x50, 0x93,0x20,0x4c,0x55, 0x01,0x29,0x69,0x5a, 0xd1,0x32,0x5b,0xaf, + 0x3b,0x4c,0xea,0x15, 0xcc,0xe1,0x63,0xc7, 0xa0,0x41,0x36,0xa9, 0x63,0x4d,0x08,0x28, + 0x1e,0xd7,0x3d,0x8b, 0x35,0x62,0x9e,0x63, 0xc4,0xc2,0xb7,0xad, 0x74,0xc8,0x09,0xb0, + // 0x700: + 0x64,0x31,0xae,0x8a, 0xe8,0x23,0x65,0xf3, 0x2d,0xd2,0x66,0x65, 0x69,0x8d,0x5c,0x8b, + 0x0b,0x45,0x9d,0x5e, 0xba,0x76,0x4d,0x36, 0xcd,0x27,0xd8,0xba, 0xe7,0xd3,0xe9,0x84, + 0x9a,0x09,0x59,0xd6, 0xe1,0xa7,0xc3,0x95, 0x1e,0xf1,0x68,0x85, 0x2d,0x15,0xcd,0xc0, + 0x72,0xc7,0xd9,0x43, 0x28,0x90,0x4d,0x0d, 0x64,0x36,0xf8,0xb8, 0x45,0xc5,0xc6,0x24, + 0x84,0xb9,0xa5,0xd1, 0xcd,0x9f,0xd3,0xec, 0xe1,0x56,0x38,0x62, 0x6f,0x20,0x74,0xc4, + 0x41,0x12,0x10,0x14, 0x3d,0x47,0x08,0x24, 0x99,0x96,0xa7,0x83, 0x65,0xf9,0x7d,0x8b, + 0x92,0xa6,0x31,0x76, 0xef,0xda,0xad,0x6c, 0xc7,0x0a,0x04,0x34, 0xe0,0x22,0xea,0x20, + 0x40,0x67,0x1b,0x26, 0xbb,0x68,0xf5,0x61, 0x4e,0x9f,0x6c,0x6d, 0x8b,0xc8,0x15,0xce, + 0x56,0xb7,0x57,0x99, 0x1a,0xa3,0x9d,0x7f, 0x4b,0xc9,0x52,0x0a, 0x25,0x69,0x96,0x95, + 0x86,0x34,0x40,0x09, 0x15,0x51,0xfd,0xa2, 0x5b,0x0a,0x51,0x92, 0xac,0x32,0xcb,0xda, + 0x58,0xce,0xc0,0xc0, 0xb6,0x5f,0x8a,0xa8, 0x55,0xd8,0xbd,0x5b, 0xe6,0xfd,0x98,0xe4, + 0xce,0xff,0xf8,0x85, 0x43,0xad,0x1b,0x02, 0xa4,0x4b,0x17,0xf3, 0x51,0x05,0x6c,0x72, + 0x06,0xa8,0xab,0x46, 0x60,0x9a,0x1d,0xbb, 0x23,0x3a,0x6d,0x19, 0x8e,0x23,0x90,0x94, + 0x16,0x97,0x71,0x73, 0xc1,0xe9,0x07,0x60, 0x8c,0x57,0xc7,0x78, 0x40,0x37,0x24,0xf6, + 0x12,0x5b,0xb2,0x74, 0x6f,0x8b,0xb4,0xf6, 0xb5,0xda,0xe5,0xab, 0x16,0xd7,0x2e,0x57, + 0x5d,0x49,0xca,0xbc, 0x98,0x79,0xd0,0xb2, 0x3e,0x0f,0xd4,0xd3, 0x8a,0x9f,0x91,0x29, + // 0x800: + 0x7d,0xf3,0xa2,0x48, 0x06,0x60,0xdd,0x27, 0x08,0xcd,0x66,0xf2, 0x78,0x42,0x04,0xba, + 0xf9,0x09,0x8a,0xa2, 0x0f,0xdf,0xa8,0x80, 0x3d,0x48,0xe2,0x67, 0xd2,0x9e,0x49,0x5f, + 0x10,0x30,0xb5,0x5e, 0x9d,0x01,0x58,0x41, 0xa0,0x2e,0xda,0xc0, 0x22,0xd7,0x0e,0x07, + 0x0a,0x34,0xd4,0x6a, 0x48,0x54,0x45,0x88, 0x9f,0x64,0x29,0x26, 0x03,0x4c,0x44,0x38, + 0x61,0xb5,0xac,0x45, 0x23,0x20,0xac,0x00, 0xa4,0x3e,0x58,0xdb, 0x31,0xd3,0x02,0x64, + 0xec,0x88,0xa1,0x8c, 0xfb,0x19,0xb0,0x24, 0xad,0x29,0x15,0xf8, 0xbc,0x4f,0xe2,0x7e, + 0xc2,0x51,0x89,0xc8, 0xae,0x2e,0xb5,0x2e, 0xc5,0xc4,0x33,0x69, 0xa4,0xcb,0x83,0x76, + 0xd3,0x00,0xec,0x5a, 0xff,0xc5,0xdc,0x91, 0x4e,0x14,0x24,0x37, 0xbd,0x0d,0x3f,0x00, + // 0x880 = 2176 Bytes +}; // ctcode_mod1_usa_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_mod1_jap_bz2 (rmcj/mod1.bin) + +const u8 ctcode_mod1_jap_bz2[2176] = +{ + 0x00, 0x00, 0x0c, 0xfc, // 3324 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x91,0xa7, 0x5c,0xb2,0x00,0x03, + 0xb8,0xff,0xff,0xff, 0x7f,0xee,0xff,0xfc, 0x7f,0x7e,0x7f,0xfe, 0x6f,0xf6,0xdd,0xef, + 0x65,0xec,0x6f,0xff, 0x7c,0xef,0x5f,0xff, 0x46,0x60,0x5f,0xef, 0xe6,0x75,0x7d,0xef, + 0xd6,0x5e,0xdf,0xe0, 0x06,0xc6,0xed,0x00, 0x1a,0xa3,0x3b,0xb3, 0xaa,0xad,0xbe,0xbd, + 0x3b,0x33,0x61,0xb4, 0x34,0x44,0x68,0x41, 0x26,0xc9,0x88,0x1a, 0x4d,0x3d,0x4c,0x99, + 0x3d,0x4c,0x4d,0xa9, 0xa6,0x80,0xd0,0xf4, 0x9e,0x93,0x47,0xa4, 0x68,0x34,0x00,0x01, + 0xa1,0xa3,0x4c,0x86, 0x9a,0x1a,0x34,0x34, 0x0d,0x34,0x34,0x03, 0xd4,0xd0,0x68,0x0f, + 0x48,0xc8,0x69,0xa3, 0x21,0xa6,0x8d,0x19, 0x32,0x34,0x1a,0x82, 0x98,0x8d,0x4d,0x8a, + 0x9f,0xaa,0x7e,0x49, 0xa9,0xfa,0xa3,0xd1, 0x3d,0xa4,0xc9,0x9a, 0x49,0xe1,0x13,0xd4, + 0xf4,0xd4,0x6d,0x43, 0xd2,0x06,0x81,0xa0, 0x00,0x68,0xf5,0x00, 0x00,0x34,0x0d,0x32, + 0x00,0x00,0xf5,0x1a, 0x01,0xa0,0x00,0x00, 0x1a,0x00,0x01,0xea, 0x69,0xa0,0xd1,0x01, + 0x4f,0x41,0x26,0x51, 0xfa,0x93,0x43,0xd4, 0x3c,0xa7,0xa9,0x9a, 0x86,0x9e,0xa0,0x68, + 0x1e,0xa0,0xd1,0xa6, 0x86,0x86,0x40,0x00, 0x00,0x00,0x68,0x00, 0x64,0x0d,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x06,0x80,0x00,0x04, 0xa6,0x91,0x02,0x1a, 0x9a,0x65,0x1e,0x86, + 0x91,0xa1,0xa9,0xa7, 0xa3,0x26,0x48,0xd3, 0xd3,0x4d,0x26,0x27, 0xa9,0x91,0x8d,0x46, + 0x8c,0x46,0x13,0x46, 0x46,0x10,0x64,0x34, 0xd0,0x34,0x00,0xd0, 0x00,0x0d,0x34,0xd0, + // 0x100: + 0x06,0x99,0x01,0xa1, 0xa0,0x00,0xd1,0xa3, 0x4d,0x06,0x88,0x34, 0x64,0x1a,0x34,0x34, + 0x0d,0x34,0xd0,0x19, 0x0d,0x00,0xd0,0x60, 0x40,0x01,0xa6,0x80, 0x68,0x00,0x30,0x43, + 0x43,0x00,0x20,0x68, 0x64,0xd0,0x06,0x20, 0x69,0xa6,0x9a,0x68, 0xc4,0xd0,0x00,0xc1, + 0x00,0x00,0x68,0x21, 0x45,0x34,0xd1,0xa6, 0x80,0x03,0x40,0x00, 0x01,0xa0,0x68,0x03, + 0x20,0x00,0x00,0x1a, 0x06,0x80,0x1a,0x34, 0x06,0x40,0x01,0xa0, 0x06,0x86,0x83,0x40, + 0x00,0x06,0x80,0x1a, 0x03,0x4c,0x80,0x05, 0x67,0xea,0x67,0x08, 0xeb,0xc2,0x22,0xc6, + 0x6f,0x43,0x9b,0xd0, 0x76,0xa7,0xe0,0x63, 0xcb,0x48,0x82,0x02, 0x80,0xe5,0xf0,0xd1, + 0x49,0xbb,0xe9,0x24, 0x35,0x5c,0x80,0x44, 0xb3,0x7d,0xc4,0x3f, 0x73,0xe7,0xb2,0x4b, + 0x4a,0x6c,0x7e,0x17, 0x4e,0x05,0xe4,0xbe, 0x62,0xae,0x35,0x50, 0xef,0xda,0xa9,0x33, + 0x2d,0x53,0xa4,0x82, 0x0c,0x81,0x98,0x32, 0xb1,0x8d,0x3a,0xb7, 0x77,0x21,0x5e,0x13, + 0xa0,0x18,0x0a,0x88, 0x99,0xe3,0x19,0xa3, 0x02,0x85,0x45,0x5a, 0x40,0x18,0xd2,0x8c, + 0xf5,0x53,0xa9,0x5e, 0xb6,0xad,0x85,0x96, 0xe5,0xd0,0x4a,0x88, 0x8a,0x2b,0xf3,0xbb, + 0xad,0xb5,0x30,0x30, 0x9a,0x80,0x82,0x01, 0x01,0x08,0x10,0x8b, 0x84,0xf6,0xa8,0xed, + 0x6e,0xec,0x9c,0x5e, 0x2f,0x1e,0xad,0x05, 0x73,0xe3,0x9d,0xda, 0x21,0xf8,0xd2,0x4a, + 0x94,0x8f,0x20,0x28, 0x25,0x75,0x77,0x18, 0xad,0x1f,0x92,0xa7, 0x8a,0xf3,0xe1,0x61, + 0xdc,0xbd,0x6c,0x4f, 0x0c,0x39,0x64,0x37, 0x75,0x6f,0xd9,0xe0, 0xdb,0x86,0x52,0x8b, + // 0x200: + 0xb4,0x03,0x7c,0xb5, 0x42,0x8c,0x95,0x82, 0x25,0xcd,0x83,0x06, 0xca,0x3e,0x12,0xfe, + 0xfd,0xce,0x14,0xf4, 0x05,0xcd,0x66,0x83, 0x79,0x09,0xec,0x3b, 0xc3,0x8d,0x73,0x4a, + 0x8a,0x91,0xef,0xa6, 0xb0,0x46,0x2d,0xb7, 0x74,0xc1,0xc1,0xcc, 0xcb,0x1a,0x44,0x4a, + 0x54,0x20,0x30,0x84, 0x11,0x42,0x84,0x08, 0xb9,0x98,0xca,0xea, 0xef,0xe4,0x6e,0x14, + 0x95,0xc9,0xd4,0x53, 0xa7,0x4e,0x9c,0x43, 0x16,0x6e,0x1d,0x09, 0x82,0xac,0x47,0x18, + 0xaa,0xf5,0x87,0x14, 0xc9,0x93,0x99,0xde, 0x46,0x34,0x3a,0xd6, 0x49,0x22,0x47,0x9a, + 0x76,0xab,0x1d,0x0c, 0x63,0x20,0x87,0x67, 0x67,0x64,0x38,0x9c, 0x10,0xd4,0x26,0x71, + 0x13,0xd1,0x0e,0x56, 0xba,0x43,0x64,0x98, 0xfe,0xd0,0x11,0x90, 0x7c,0x3c,0x74,0x9e, + 0x02,0x84,0x20,0x65, 0x6b,0x3b,0x48,0x13, 0xc9,0x5e,0xc4,0x4b, 0x09,0x3b,0x93,0x63, + 0xb4,0xe4,0x30,0x04, 0xa7,0x9c,0x12,0x6f, 0x32,0xd4,0x81,0xbf, 0xc6,0x76,0xdf,0x26, + 0x62,0x9a,0x8f,0x6d, 0xdd,0x25,0x9f,0x71, 0xd0,0x84,0x57,0x1d, 0x09,0x9b,0x77,0x58, + 0x88,0x52,0xef,0x15, 0x73,0x49,0x6e,0xdc, 0x42,0xb0,0x98,0x48, 0x2f,0x88,0xb9,0x2a, + 0x1a,0xea,0xa5,0x1c, 0x4d,0x70,0xe0,0xec, 0x98,0x10,0xc2,0x6b, 0x91,0xdd,0x24,0x85, + 0x5c,0x26,0x6c,0x9a, 0x5c,0xa4,0x43,0x24, 0x80,0x45,0xb4,0x38, 0x99,0x84,0xbe,0xa4, + 0x5e,0x40,0x1d,0x09, 0x07,0xca,0x4c,0xe6, 0x82,0xe6,0x46,0x37, 0x19,0x0c,0xc2,0x47, + 0xba,0x88,0x36,0x59, 0x86,0x14,0x26,0x1c, 0xd4,0xd1,0x68,0x85, 0xc5,0x14,0x0c,0x7b, + // 0x300: + 0xd4,0x0e,0x41,0x1c, 0x11,0x4b,0x9e,0xac, 0xe8,0x07,0x2d,0x1c, 0x9b,0x82,0x48,0xb8, + 0x3d,0xa4,0x38,0x87, 0x65,0x2a,0x3a,0x35, 0x4b,0x36,0x2a,0xb4, 0xd3,0xca,0xc1,0x25, + 0x44,0x69,0xa2,0xf1, 0x42,0x13,0x09,0xc6, 0x66,0xa9,0x04,0x8b, 0xa4,0x35,0xe6,0x6d, + 0xd8,0x1a,0x94,0x04, 0xa0,0x3c,0xc7,0xd4, 0x66,0x56,0xf9,0x88, 0x22,0x78,0xd8,0x86, + 0x24,0x80,0x64,0x71, 0x6b,0xd6,0xaf,0xac, 0x74,0xcd,0x93,0xa6, 0x81,0xd1,0xfd,0x17, + 0xde,0xba,0x82,0x9f, 0x57,0xce,0x5f,0xa6, 0x29,0xa5,0xe5,0x4f, 0xcd,0x32,0xb6,0x0a, + 0x76,0x16,0xac,0xdd, 0xfb,0x55,0x8b,0xa4, 0x2a,0x1c,0x74,0x9d, 0xde,0x55,0x00,0x6b, + 0x26,0x50,0x0c,0xa6, 0xd0,0xc2,0x5e,0x7d, 0xbb,0xc9,0x37,0x9a, 0x28,0x17,0xbf,0xaa, + 0x9a,0x92,0x12,0xdd, 0x4a,0x6e,0x50,0x05, 0xaa,0x5a,0x04,0xc3, 0x7e,0x33,0xed,0x4a, + 0x15,0x5a,0x64,0x37, 0x3f,0xcf,0xca,0x4b, 0xe9,0x92,0xf9,0x24, 0x88,0x53,0xae,0x04, + 0x5f,0x7c,0x9a,0x08, 0x2b,0x56,0xc2,0xe5, 0xc0,0x8e,0x7e,0x4c, 0xb6,0x3c,0x58,0xe8, + 0xa5,0xcb,0xa8,0x06, 0xdf,0xb4,0x92,0xed, 0x94,0x85,0x08,0xc4, 0xc0,0x38,0x88,0xc0, + 0xd7,0xee,0xb1,0xe9, 0x63,0xc1,0x34,0x98, 0x77,0x03,0x6a,0x2d, 0x53,0xc8,0x9c,0xf4, + 0xd0,0xcd,0x82,0x99, 0x9b,0x48,0x8f,0x99, 0x11,0x41,0xe1,0x26, 0x63,0xd5,0x6f,0x50, + 0x02,0x58,0x1b,0xd0, 0x09,0xf0,0xec,0x96, 0xed,0xbe,0x6b,0x83, 0xa7,0xa1,0xac,0xa0, + 0x12,0x74,0x93,0x6b, 0xd6,0xcc,0xd0,0xf7, 0x81,0x8d,0xcc,0xdf, 0xf6,0x79,0x7e,0xfb, + // 0x400: + 0x46,0x11,0x3b,0x93, 0x1f,0x4c,0x7a,0x83, 0x91,0x7e,0x1b,0xdb, 0x38,0x58,0x3d,0x9c, + 0x76,0x03,0xb4,0x20, 0x0e,0x8d,0x4d,0xb3, 0x0a,0x37,0xff,0x94, 0x06,0x1a,0xb2,0x90, + 0x5a,0xcb,0x7a,0xab, 0x55,0xad,0x6c,0xe7, 0x21,0xf0,0x0d,0x76, 0xd3,0x68,0xaa,0x8d, + 0x7b,0x09,0x62,0xc6, 0x3a,0x59,0xb5,0x24, 0x7b,0x0d,0xc9,0xbf, 0xd0,0x90,0x06,0x62, + 0x5b,0xa0,0x12,0xf5, 0x2b,0xd8,0xe1,0xdd, 0xb8,0x0f,0x06,0x2e, 0x24,0x06,0xfb,0x95, + 0x19,0x15,0x2b,0x91, 0x6e,0x25,0x33,0x1b, 0x75,0xeb,0xe3,0x72, 0xa4,0x33,0x77,0xe2, + 0x90,0xf2,0x12,0x89, 0x52,0x4d,0x70,0x8f, 0xde,0xe5,0xa5,0x2a, 0xa3,0x5e,0xe0,0x83, + 0xf0,0xa8,0x70,0x35, 0x83,0x00,0xf4,0x93, 0x58,0xb9,0x2d,0x2c, 0xbd,0x40,0xdc,0xc2, + 0x92,0xb0,0xca,0xf8, 0xf1,0x53,0x43,0xca, 0x8f,0x19,0xa7,0x21, 0xfc,0xb9,0x93,0xd2, + 0x17,0x71,0x32,0xba, 0x77,0x1f,0x4d,0xc0, 0xf1,0x47,0x68,0x9c, 0x70,0x9a,0x51,0xe0, + 0xb4,0x40,0x95,0xa4, 0x42,0x2b,0x31,0x63, 0x41,0x92,0x6d,0x29, 0x19,0x02,0x8f,0x91, + 0x4d,0x09,0x4b,0xba, 0x82,0xc2,0x58,0x35, 0xb9,0x96,0x13,0xcc, 0xae,0x20,0x44,0x43, + 0xcd,0xf7,0x3d,0x88, 0xfc,0x65,0x41,0x31, 0x31,0x46,0x82,0x2c, 0x17,0x17,0xc9,0x17, + 0x17,0xac,0x5c,0x57, 0x62,0x05,0x07,0xe6, 0x0a,0x78,0x45,0x45, 0x04,0x12,0xab,0x5d, + 0x61,0x81,0x80,0x8f, 0x89,0x81,0x7e,0x64, 0xb9,0x99,0x95,0x9b, 0x78,0xf0,0x66,0xe3, + 0xf6,0x2e,0xc7,0xda, 0x9b,0xe4,0x85,0x79, 0xa3,0x41,0xb8,0xde, 0x64,0xd6,0x34,0xd2, + // 0x500: + 0x73,0xb7,0x09,0x3e, 0xb6,0x59,0xaf,0xe3, 0xa2,0x49,0xf0,0x60, 0xda,0xb5,0x4c,0x31, + 0x11,0x63,0x35,0x75, 0x4f,0x4a,0xdd,0xd3, 0x32,0x2d,0x0a,0xd5, 0xb8,0xa3,0x6d,0x33, + 0x94,0x11,0x11,0x52, 0x6e,0xe6,0x9c,0xa8, 0xf3,0xc6,0xbc,0x77, 0xd9,0x6d,0xad,0x1c, + 0xab,0xe3,0x92,0x0f, 0x62,0x95,0x79,0x24, 0xa1,0xdd,0x60,0x45, 0x25,0x18,0x69,0x7f, + 0x48,0x41,0x5e,0xac, 0xce,0xd7,0x22,0x48, 0xa4,0x9f,0x59,0x5e, 0x17,0x04,0xce,0x56, + 0xde,0x5f,0xd1,0x9c, 0xfb,0x9f,0x97,0x2e, 0xc3,0x4f,0xaa,0xb5, 0xad,0x77,0x89,0xa3, + 0x4c,0x76,0x65,0x17, 0xb9,0xad,0x70,0x1a, 0x77,0x45,0x0a,0xf2, 0x4e,0x7e,0x76,0x85, + 0xa6,0x96,0x5d,0x2a, 0xd3,0xd9,0xa4,0xc9, 0xcd,0x11,0xe1,0x26, 0x72,0x68,0x81,0x17, + 0x0c,0x85,0x6b,0x29, 0xd3,0x5c,0x27,0x93, 0x26,0xca,0x54,0x9f, 0xe2,0x76,0x74,0x8d, + 0x99,0xe2,0x09,0xc8, 0x43,0xeb,0x71,0x88, 0x64,0x28,0xd0,0x6d, 0x9f,0x33,0x72,0x3f, + 0x23,0x23,0x1b,0x53, 0xe3,0xe7,0xa5,0x2a, 0x34,0x53,0x20,0x2f, 0xe0,0x22,0xc1,0xb5, + 0xa5,0xa6,0x66,0x76, 0xf8,0x49,0x9e,0x30, 0x69,0x3f,0x33,0xa6, 0x5c,0x3a,0x36,0x72, + 0x0d,0x1d,0x1a,0xa2, 0x1f,0x77,0x48,0xd7, 0x43,0x10,0xa4,0x25, 0x62,0x90,0x17,0xc8, + 0xc6,0x28,0x09,0xe0, 0x0a,0xc0,0x6c,0xad, 0x34,0xa5,0x0b,0xc4, 0xc2,0x4a,0x79,0x5a, + 0x24,0x51,0xfe,0xe8, 0xa2,0xc9,0xcb,0xa8, 0xe1,0xed,0x5f,0x0d, 0xc2,0x40,0x94,0xc9, + 0x86,0x29,0x8b,0x6d, 0x7e,0x5c,0xf6,0x33, 0x74,0x59,0xb5,0x75, 0x9d,0xd7,0xeb,0x34, + // 0x600: + 0x17,0xa6,0x80,0x69, 0x33,0x04,0x4c,0x53, 0x14,0x1c,0x40,0xa5, 0x94,0x40,0x80,0x94, + 0x2b,0xea,0x32,0xcd, 0x24,0xcb,0xc8,0xca, 0xb4,0x2c,0xb5,0x95, 0x66,0x65,0xa6,0x28, + 0x10,0xc5,0x18,0xc4, 0x40,0x23,0x75,0x2d, 0x88,0x9e,0x01,0x10, 0x52,0x40,0x08,0x95, + 0x10,0xe3,0x60,0x12, 0x59,0x08,0x02,0x40, 0x81,0x0a,0xab,0x4e, 0x5e,0x05,0xa9,0xb1, + 0x20,0xa6,0x83,0xc1, 0xa4,0x9e,0x10,0xb8, 0xea,0xc3,0x02,0xa2, 0xd8,0x20,0xf2,0x51, + 0xb9,0xbd,0x97,0x26, 0xa5,0x5b,0x0a,0xa4, 0x2f,0x9c,0x33,0x58, 0x53,0x5d,0x04,0x9e, + 0x1e,0x55,0x4a,0x6b, 0xe4,0x90,0x7e,0x2f, 0x3c,0x80,0xc4,0x07, 0x83,0xab,0xb7,0xd1, + 0xbf,0x5c,0x2a,0xbb, 0x5b,0xce,0x7d,0x7d, 0x5d,0xaa,0x55,0x2b, 0x5a,0x36,0x1b,0xaa, + 0x84,0xa7,0x67,0xc7, 0x63,0x7d,0x5a,0xf9, 0xf3,0x18,0x37,0x5b, 0xdc,0x3d,0x14,0xf9, + 0x8b,0xbb,0x1e,0x2d, 0xa9,0xe9,0x8d,0x5c, 0x01,0xf1,0x1a,0x09, 0xb7,0x2d,0x18,0xc0, + 0x8a,0x64,0x86,0x48, 0x4c,0x92,0x88,0x3e, 0x7e,0x0c,0x3a,0x04, 0x82,0x3d,0x24,0xae, + 0xae,0x53,0xa1,0xd3, 0x34,0xe9,0x96,0x7a, 0xaf,0x0e,0x89,0x6b, 0x74,0x7c,0xf4,0xbb, + 0x6d,0xe2,0xd2,0xed, 0x6a,0x49,0x84,0xb5, 0xca,0x7d,0x6e,0xae, 0x9f,0x66,0xa5,0xcf, + 0x40,0x8a,0xd2,0x27, 0x22,0xa4,0x1d,0x1a, 0x55,0x5c,0xc1,0x3a, 0x5b,0x9b,0x35,0xae, + 0x2b,0xb1,0xca,0x5b, 0x8e,0xb1,0x1e,0xd0, 0x74,0x8a,0x45,0x9c, 0x64,0xc1,0xc0,0xfd, + 0x20,0x64,0xe8,0x1b, 0xd8,0x5b,0xab,0x04, 0x0a,0x40,0x2a,0x01, 0x48,0x20,0x20,0x27, + // 0x700: + 0x2e,0xc4,0xc9,0xa8, 0xe5,0xa6,0xca,0x7d, 0x6a,0x67,0xb6,0x8c, 0x2b,0x02,0xc1,0xc5, + 0x9e,0x62,0xab,0x1c, 0x6f,0x52,0xc7,0xe2, 0x5b,0x13,0xd6,0x10, 0x72,0x88,0xd9,0x49, + 0x9d,0x3d,0xa8,0x6d, 0xfd,0x54,0xab,0x2c, 0x47,0x4a,0x91,0x16, 0x43,0xb2,0x3b,0x74, + 0x12,0x3c,0xe3,0xde, 0xfe,0xeb,0x03,0x05, 0x8e,0x92,0x12,0xc9, 0x5b,0x19,0xb9,0xbb, + 0x4d,0xd4,0x29,0xc3, 0x0c,0xe7,0x1a,0x07, 0x58,0x44,0x11,0xe5, 0xca,0x94,0x45,0x18, + 0x70,0xe0,0x11,0x49, 0x33,0x2e,0x56,0x2e, 0xaa,0xc3,0xbe,0xc5, 0xc9,0x53,0x18,0x34, + 0x3e,0xed,0x69,0x66, 0x39,0x90,0x21,0x88, 0x02,0x2f,0x11,0x02, 0x03,0x38,0xd8,0xb5, + 0x5a,0xfb,0x46,0x15, 0xb1,0x2c,0x85,0x90, 0x90,0x90,0xf8,0xc2, 0xbd,0x9d,0xab,0x54, + 0x2a,0xd4,0xd2,0x40, 0x85,0xc2,0x60,0x39, 0x84,0xd3,0x98,0x40, 0x84,0xa0,0x00,0x33, + 0x10,0xfd,0xde,0xce, 0x7e,0x2b,0xd7,0xad, 0x83,0x24,0x8a,0xad, 0x42,0xc7,0x0f,0x29, + 0xf5,0x15,0x78,0x6a, 0x0a,0x87,0xa3,0xf8, 0xa7,0xcd,0x98,0x6d, 0x96,0x1d,0xcd,0x33, + 0xc0,0x77,0xa5,0x7c, 0x22,0x08,0xa2,0x01, 0x2d,0x81,0x68,0x98, 0xa0,0x98,0x49,0x2e, + 0xa3,0x07,0x03,0xde, 0x4a,0x86,0x23,0xc9, 0x08,0x38,0x83,0x65, 0x11,0x42,0xb1,0xae, + 0xef,0xb1,0x36,0xfd, 0xce,0x0f,0x50,0x2c, 0x0a,0x42,0xdf,0x99, 0x92,0x81,0x2e,0xb4, + 0xd3,0xeb,0x65,0x50, 0x48,0x52,0x27,0x61, 0xdd,0x14,0xb1,0xa7, 0x98,0x31,0x7b,0x49, + 0x13,0x10,0x35,0x3b, 0xc0,0x78,0xb7,0xcd, 0x4b,0x92,0x25,0xc4, 0x34,0x2a,0x78,0x11, + // 0x800: + 0x20,0x02,0x94,0x15, 0x22,0x20,0x42,0x92, 0x84,0x25,0xec,0xc3, 0xad,0x9c,0x69,0xb0, + 0x93,0x12,0x4a,0x19, 0x65,0x33,0x83,0x44, 0x1c,0x5b,0x64,0x71, 0xc5,0x46,0xfc,0xa0, + 0x83,0x26,0xab,0x47, 0x15,0xe5,0x11,0xc0, 0xfb,0x47,0xae,0x75, 0x1f,0x98,0xf5,0x26, + 0xaf,0x85,0x19,0x63, 0x65,0xe9,0x81,0x4c, 0x1a,0xca,0x09,0x11, 0x59,0x5b,0xb7,0x97, + 0x81,0x4b,0x26,0x1f, 0x20,0xbd,0x5e,0xb9, 0x0e,0x04,0x93,0xa0, 0x99,0x39,0x18,0x59, + 0x82,0x1f,0xe8,0x72, 0x29,0x0b,0x58,0x49, 0xd9,0xa1,0x23,0xec, 0xdd,0x3b,0x01,0x06, + 0x85,0x1d,0x3b,0x9f, 0x41,0x08,0x8c,0x11, 0x4d,0x5e,0x0d,0x25, 0xac,0x98,0x07,0x62, + 0xc7,0xfe,0x2e,0xe4, 0x8a,0x70,0xa1,0x21, 0x23,0x4e,0xb9,0x64, + // 0x87c = 2172 Bytes +}; // ctcode_mod1_jap_bz2[] + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// ctcode_mod2_pal_bz2 (rmcp/mod2.bin) + +const u8 ctcode_mod2_pal_bz2[1517] = +{ + 0x00, 0x00, 0x0c, 0x38, // 3128 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x57,0x77, 0x75,0xc0,0x00,0x04, + 0xbc,0x7f,0xff,0xff, 0xff,0xe7,0x6f,0xff, 0x77,0x5f,0xff,0xff, 0xff,0xfe,0x7d,0xff, + 0x66,0x64,0x5f,0xff, 0x56,0x7f,0x7e,0xf7, 0x77,0x76,0x64,0x7f, 0x76,0x77,0xc4,0x47, + 0xfd,0xfe,0x77,0xd0, 0x04,0x1e,0x4d,0xbd, 0xb0,0x3d,0xe9,0xd8, 0xee,0xdb,0x34,0x88, + 0x9a,0xa6,0x4f,0x51, 0x84,0x6a,0x4f,0xd5, 0x1f,0xa9,0x9a,0x4c, 0xd2,0x19,0x43,0x26, + 0xd4,0xf3,0x54,0x0d, 0x34,0xc4,0x36,0x50, 0xc4,0x64,0x7a,0x8f, 0x50,0x00,0x3d,0x40, + 0x0c,0x6a,0x07,0xa9, 0xa6,0x8f,0x53,0x69, 0x3d,0x46,0xd4,0x0d, 0x0d,0x1a,0x34,0x34, + 0xd0,0x0d,0x00,0x66, 0xa1,0xea,0x69,0xa6, 0x8d,0x3d,0x4d,0x1a, 0x34,0x2a,0x9a,0x69, + 0xa9,0xa7,0x92,0x01, 0xa6,0x40,0xf5,0x34, 0xd3,0x40,0x34,0x06, 0x86,0x8c,0x86,0x6a, + 0x0d,0x0d,0x19,0x0c, 0x9b,0x48,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x84,0x8c,0x69,0x11, 0xa2,0x26,0x4f,0xd0, 0x28,0xf5,0x0f,0x53, + 0x65,0x0f,0x28,0x0f, 0x53,0xd2,0x6d,0x23, 0x23,0x4d,0x34,0xc8, 0x1e,0xa0,0x03,0x40, + 0x3d,0x40,0x00,0x0d, 0x03,0x41,0xa0,0x34, 0x00,0x68,0xd0,0x00, 0xf5,0x34,0x68,0x0d, + 0x00,0x00,0xd3,0xca, 0x0d,0x3f,0x54,0x40, 0xd0,0x69,0xa6,0x86, 0x43,0x23,0x40,0x34, + 0x06,0x9a,0x34,0x06, 0x83,0x09,0xa0,0xd3, 0x20,0x03,0x46,0x86, 0x80,0xc8,0x03,0x46, + 0x9a,0x31,0x00,0xc2, 0x0c,0x83,0x4d,0x32, 0x00,0x64,0x0d,0x00, 0x0c,0x10,0x68,0x62, + // 0x100: + 0x19,0x00,0x12,0x25, 0x10,0x12,0x9f,0x8a, 0x69,0x34,0xf5,0x3d, 0xa2,0x04,0xd3,0x27, + 0xa2,0x18,0x3d,0x46, 0xa6,0x4d,0x32,0x19, 0x34,0x69,0x82,0x30, 0x83,0x26,0x43,0x35, + 0x18,0x34,0x4c,0x08, 0xc8,0xc2,0x1a,0x32, 0x30,0x4c,0x26,0x1a, 0x68,0x23,0x09,0x91, + 0x82,0x60,0x4d,0x30, 0x46,0x8c,0x8a,0x35, 0xd3,0x72,0xad,0x57, 0x34,0x93,0x64,0xe8, + 0xb4,0x92,0xea,0xea, 0x61,0x61,0x04,0xc6, 0x13,0x5a,0x86,0x0c, 0x6a,0x22,0x00,0x20, + 0x12,0x69,0x04,0xb2, 0x42,0x4d,0x81,0x62, 0xca,0xe9,0xae,0x5d, 0x74,0x08,0x10,0x62, + 0xaf,0x1e,0x8c,0xfd, 0xd6,0xf3,0xf1,0xe0, 0x70,0xae,0x38,0xbf, 0x97,0x27,0x97,0xcd, + 0xe7,0xe0,0xe1,0x4c, 0x9b,0x89,0xf7,0x9d, 0x8f,0x90,0xab,0xa7, 0x2b,0x50,0xc0,0x63, + 0x5b,0x57,0x40,0xcd, 0x5e,0x1a,0x8a,0x2a, 0xab,0x75,0x5a,0x2c, 0x8d,0x62,0x7e,0x5a, + 0x17,0x06,0x6d,0x52, 0xf1,0x96,0xfd,0x86, 0x62,0x25,0xb6,0x66, 0xc1,0xd9,0xbc,0xb4, + 0x80,0xd7,0xfd,0x4a, 0x3b,0x29,0xfb,0x35, 0xe8,0xcf,0x6b,0x22, 0xde,0x4c,0xa9,0x6d, + 0x2e,0xbb,0x17,0x77, 0x97,0xb7,0xce,0x5d, 0x3a,0x5d,0x7a,0x79, 0xf8,0xfc,0x74,0x51, + 0x78,0xf5,0xca,0xc9, 0xa7,0xa2,0xcb,0x96, 0x54,0x96,0xed,0x99, 0x9f,0x9a,0xd0,0x3d, + 0x23,0x4a,0xb5,0x8d, 0xd0,0x71,0xdd,0xc4, 0x30,0x42,0x54,0x47, 0xc4,0x42,0xd8,0x69, + 0x22,0x21,0xf7,0x87, 0x11,0xd8,0x68,0x56, 0x11,0x91,0x39,0xbc, 0xa0,0x28,0x28,0x41, + 0x14,0x18,0xb8,0x45, 0x4a,0x84,0x1c,0x00, 0x20,0xf0,0x82,0x50, 0x88,0x12,0x40,0x7a, + // 0x200: + 0x53,0xea,0xd2,0xab, 0x63,0x53,0x1d,0x67, 0x7f,0xbe,0xe0,0xec, 0xfc,0xec,0x46,0x0c, + 0xb5,0xad,0x8c,0xc5, 0x5f,0x2e,0xf7,0x59, 0xd9,0x2e,0x5f,0x1f, 0xc3,0x59,0x7a,0xbc, + 0xcd,0x2e,0x74,0x16, 0xfc,0x3c,0xba,0xad, 0x50,0xee,0xf7,0x61, 0xa7,0x5f,0xe5,0x2d, + 0xa5,0xc2,0xc8,0x36, 0xf4,0xa0,0xe1,0xd5, 0xa2,0xca,0x50,0xce, 0x18,0x72,0x8d,0x01, + 0x66,0x4c,0x61,0x81, 0x54,0xf4,0x84,0x4b, 0x80,0x7f,0x78,0x5f, 0x25,0x4b,0x22,0x78, + 0x85,0xc6,0x75,0x4a, 0x48,0x0a,0x10,0xa8, 0xaa,0xd2,0x35,0x1b, 0x12,0x08,0xc2,0xd4, + 0xc4,0x2c,0x8c,0x37, 0x22,0x4b,0x69,0x27, 0x15,0x87,0x0c,0x74, 0xe2,0x65,0x89,0x6f, + 0xc5,0x70,0x25,0x5a, 0xf1,0xdb,0xdd,0xa6, 0x66,0xa6,0x03,0xda, 0xb3,0xbf,0x2b,0x1a, + 0x77,0x89,0x24,0xbe, 0xe4,0xb3,0x16,0x59, 0x4c,0xad,0xd3,0xe5, 0xa0,0xae,0xf4,0x21, + 0x05,0x66,0x19,0x96, 0x12,0x30,0x04,0x0d, 0x34,0x92,0xa2,0xf8, 0x4c,0x10,0x04,0x61, + 0xa0,0x44,0xdf,0x23, 0x4d,0x20,0x90,0x83, 0xca,0x74,0x9d,0x6f, 0x52,0x09,0x0d,0xec, + 0xe9,0x0b,0x9b,0x69, 0x92,0x09,0xb2,0x51, 0x90,0xcd,0x1e,0x46, 0x25,0x94,0x81,0x49, + 0xdb,0x8f,0xae,0x58, 0x48,0x43,0x68,0x48, 0x1b,0x06,0x26,0x80, 0x6c,0x60,0x63,0x22, + 0x67,0x12,0x23,0x3f, 0x11,0xa2,0xd2,0xc8, 0x09,0x41,0x5f,0xe4, 0xd0,0xbf,0xce,0x9a, + 0x0d,0xb6,0x70,0xda, 0x49,0x3d,0xd4,0xbe, 0x79,0x8b,0xbd,0x52, 0x9f,0x3d,0xa6,0x20, + 0x17,0x8f,0x6f,0xfd, 0xeb,0x2f,0x5f,0x7c, 0xe2,0xe1,0xcf,0x76, 0xf5,0xf2,0x4a,0x6e, + // 0x300: + 0x62,0x5b,0xdc,0x08, 0xc7,0x52,0x74,0xb6, 0xdf,0x64,0xdb,0x39, 0xca,0x1b,0x75,0xed, + 0x20,0x28,0x34,0x8f, 0x43,0x45,0x02,0x2a, 0xb0,0x4a,0xd9,0xa4, 0x16,0x0d,0x24,0xa9, + 0xd3,0xa7,0xef,0x77, 0xee,0x31,0x33,0x80, 0x03,0xbe,0xd2,0xee, 0x3d,0x1b,0xa0,0xfa, + 0x1a,0xeb,0x56,0x71, 0xc4,0xbe,0x74,0xbf, 0x01,0xda,0x2e,0x63, 0xce,0x9c,0xa6,0xdb, + 0x6e,0xc5,0x80,0x40, 0x34,0xd1,0x11,0x45, 0x3e,0x6a,0x84,0x56, 0x14,0xa5,0x20,0xcc, + 0x21,0x8a,0xed,0x74, 0x22,0x0f,0x40,0xe3, 0xa1,0x3d,0xbd,0x19, 0x3b,0x08,0xc8,0xf9, + 0x01,0x99,0xd3,0xde, 0x0f,0x37,0x78,0x32, 0xc4,0xb7,0x18,0x97, 0xf8,0x40,0x71,0xef, + 0xa0,0x1b,0x1f,0xc5, 0x62,0x56,0x49,0x57, 0xac,0xee,0x61,0xa7, 0xc7,0x6e,0xa0,0xea, + 0x26,0x21,0x49,0xa1, 0x33,0x98,0x11,0x89, 0x4a,0x83,0xe6,0xe4, 0xbc,0xf6,0x21,0x24, + 0x7c,0x1f,0x4d,0x9f, 0xaf,0xaa,0xf4,0x93, 0x18,0x4c,0x60,0xc5, 0x99,0xaa,0x53,0xbb, + 0x77,0xd0,0x5c,0x43, 0x0c,0x26,0xb6,0x8c, 0xf9,0xc5,0x56,0x55, 0x94,0xd3,0x56,0x55, + 0x99,0x60,0x5c,0xd0, 0x2c,0x86,0x24,0x1a, 0x3e,0x52,0x8a,0x8e, 0x4e,0x7a,0xff,0x66, + 0x28,0xff,0x23,0x4c, 0x94,0x53,0x1a,0x88, 0x90,0xc8,0x54,0x03, 0x00,0x09,0x55,0x88, + 0x40,0x04,0xe0,0xa7, 0x83,0x32,0xcb,0x96, 0x5a,0x68,0xcf,0xf2, 0x3e,0xd6,0xe4,0x8a, + 0x14,0x0c,0xf6,0xc0, 0x09,0x94,0x27,0x93, 0x80,0xfe,0x37,0x29, 0x70,0x14,0x2c,0x00, + 0x80,0x69,0x44,0x30, 0x3d,0xf0,0x7f,0x18, 0x2c,0xe9,0x04,0x92, 0xc4,0x84,0x11,0x07, + // 0x400: + 0x02,0x82,0x08,0x32, 0x4a,0xa1,0x6e,0x31, 0xd1,0xa9,0x90,0x21, 0x82,0x4c,0x01,0xd9, + 0x09,0x02,0xe7,0xbb, 0x15,0x7a,0x4f,0xd1, 0xec,0x5a,0x53,0xcc, 0xb6,0xfa,0xa3,0x31, + 0x1c,0x49,0xd1,0xa3, 0x5a,0x4d,0x0f,0xb5, 0x8e,0xfe,0x1b,0xfa, 0x67,0x7a,0x35,0x83, + 0x9e,0x21,0x8c,0x2f, 0x0f,0x97,0x71,0x6a, 0xf2,0xa3,0xe6,0xb4, 0x97,0x20,0xf9,0xf6, + 0x6e,0xaa,0xcb,0x3d, 0x9d,0xdd,0xdc,0xed, 0x30,0xcf,0x8b,0x87, 0x3b,0x73,0x45,0xf2, + 0xce,0x63,0x55,0x85, 0xc6,0x2b,0x48,0xe0, 0x1a,0x91,0x6c,0x95, 0x77,0x26,0x14,0x22, + 0x81,0x06,0xa9,0x81, 0x02,0xbc,0x42,0xa4, 0x40,0xe3,0xa9,0x79, 0x13,0xdc,0xde,0x03, + 0x0b,0x9e,0xcf,0xf3, 0x42,0xfd,0x47,0x2f, 0x6b,0x3c,0xcc,0x69, 0xea,0x24,0x15,0x43, + 0xe3,0x9f,0x46,0x8d, 0xa9,0x79,0x7c,0xd7, 0xe6,0x7d,0x57,0x34, 0xc6,0xbc,0x48,0xdb, + 0xc7,0xae,0xc9,0x8e, 0xc3,0x60,0x48,0xb9, 0x9e,0x4d,0xb2,0x38, 0x00,0x88,0xaf,0x45, + 0xef,0xb1,0xb6,0xf8, 0xa9,0x29,0xa5,0xa7, 0xc2,0x2e,0x7f,0x96, 0x9d,0xb6,0x86,0x2a, + 0xc9,0x4b,0x55,0x3e, 0xea,0xd5,0x27,0x19, 0x11,0xc6,0x71,0xdc, 0x19,0x42,0x05,0x91, + 0x95,0xb3,0x34,0x10, 0xa0,0xdd,0x44,0x30, 0x80,0x4f,0xce,0x7e, 0xb4,0xfc,0x74,0x5b, + 0x54,0xd2,0xa7,0x32, 0xef,0x17,0xaa,0xd6, 0xe0,0x45,0x84,0x73, 0x00,0x8a,0x1a,0x46, + 0x75,0xc0,0x52,0xa8, 0x6c,0x81,0x20,0x24, 0x2d,0xeb,0x42,0xac, 0x03,0x4a,0x63,0x04, + 0x60,0x1a,0xf7,0x5a, 0xbe,0x7c,0x37,0x49, 0x2d,0x2f,0x31,0xe6, 0xc2,0x25,0xf9,0xe9, + // 0x500: + 0xf8,0xb3,0x4d,0x6b, 0xab,0x65,0x28,0x80, 0x89,0x96,0x52,0xeb, 0xaf,0x77,0xfa,0xdb, + 0x21,0x08,0xbb,0x4e, 0xce,0x27,0x72,0x58, 0x36,0x60,0x95,0x61, 0x99,0xa1,0x3e,0xf6, + 0xf1,0xe4,0xf5,0x57, 0x9c,0xfb,0x29,0xf2, 0x5f,0xce,0x64,0x20, 0x65,0xab,0x96,0x01, + 0x10,0x65,0x97,0xf5, 0xb0,0x31,0x0e,0x88, 0xc6,0x85,0x1d,0x65, 0x31,0xd8,0xc2,0xc4, + 0xae,0x83,0x20,0x41, 0x42,0x18,0xd3,0x01, 0x64,0x99,0x67,0x1f, 0x6b,0xfd,0x7a,0x28, + 0xaa,0x31,0x14,0xbb, 0xe4,0xc0,0x90,0x69, 0x4b,0x92,0xaa,0x4c, 0x8a,0x5f,0xf4,0xdb, + 0xc9,0x4c,0x58,0xfa, 0x3a,0x06,0x57,0xad, 0xcc,0x30,0xa2,0x2b, 0xe9,0x9b,0xdd,0x41, + 0x0b,0x51,0x76,0x9c, 0x0f,0xe2,0x96,0x8f, 0x5a,0xb5,0x71,0x05, 0x30,0xb0,0xa2,0x4a, + 0x53,0x70,0xf4,0x04, 0x3c,0xaa,0xa9,0x22, 0x8d,0x23,0xaf,0x70, 0x84,0x3a,0xa8,0x63, + 0x79,0x1e,0x51,0x11, 0x48,0x5d,0x3d,0xdb, 0xce,0xc9,0xca,0xd5, 0xe1,0x1c,0xec,0x3e, + 0xbf,0x1c,0xe9,0x94, 0xd8,0x34,0x06,0x43, 0xd7,0xee,0x78,0x06, 0x9e,0x5c,0xd2,0x76, + 0xc5,0x55,0x35,0xe5, 0x22,0x53,0x26,0x90, 0xe5,0xac,0xb7,0x22, 0xb9,0xb7,0x86,0xc8, + 0x59,0xc3,0x25,0x46, 0x10,0x71,0x76,0x62, 0xb9,0x0a,0x1d,0x83, 0x57,0xc0,0x92,0xcf, + 0xd0,0x40,0xe8,0xb9, 0x35,0x87,0x90,0x84, 0x24,0x35,0x98,0x11, 0x96,0x93,0xfe,0x2e, + 0xe4,0x8a,0x70,0xa1, 0x20,0xae,0xee,0xeb, 0x80, + // 0x5e9 = 1513 Bytes +}; // ctcode_mod2_pal_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_mod2_usa_bz2 (rmce/mod2.bin) + +const u8 ctcode_mod2_usa_bz2[1531] = +{ + 0x00, 0x00, 0x0c, 0x38, // 3128 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x42,0xbb, 0x6e,0x44,0x00,0x04, + 0x86,0x7f,0xff,0xff, 0x7f,0x47,0x66,0x75, 0x67,0xd7,0x7f,0xf7, 0xff,0xff,0x5f,0xef, + 0xdf,0x77,0x57,0xff, 0xf7,0xdf,0x7f,0xff, 0x57,0x5f,0xff,0xff, 0xf5,0xdf,0xce,0x77, + 0xfe,0xef,0xfd,0xd0, 0x03,0xfe,0x76,0xee, 0xdb,0x06,0xeb,0xd3, 0x57,0x3b,0x77,0x6e, + 0xf1,0x11,0x4d,0x4f, 0x32,0x0a,0x9e,0x29, 0xa6,0xda,0x89,0x8f, 0x54,0x7a,0x8c,0x8d, + 0x32,0x67,0xa9,0xa4, 0x6d,0x10,0xd0,0x68, 0xda,0x83,0xd4,0x7a, 0x9a,0x03,0x21,0x90, + 0xfd,0x29,0x90,0x0d, 0xea,0x64,0x43,0x11, 0xa6,0x9b,0x53,0x43, 0x47,0xa8,0x69,0xa0, + 0x3d,0x40,0x1e,0xa3, 0x41,0xb5,0x0d,0x94, 0x00,0x68,0x1a,0x0d, 0x0d,0xa8,0xf5,0x31, + 0xa8,0x66,0x92,0x3d, 0x4c,0x46,0x83,0x4f, 0x51,0xa0,0x68,0x0d, 0x03,0x40,0x7a,0x83, + 0xd4,0x34,0x18,0x80, 0x03,0x10,0xd3,0x41, 0xa0,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x04, 0x24,0x8d,0x3c,0x4a, 0x79,0x53,0xd4,0xfd, + 0x24,0xf5,0x34,0xfd, 0x13,0x20,0x9a,0x7a, 0x13,0xd2,0x00,0xda, 0x11,0xe4,0x43,0x4d, + 0x3d,0x08,0x0d,0x06, 0x20,0x69,0xa0,0x00, 0x01,0x81,0x18,0x80, 0x68,0x61,0x06,0x8d, + 0x03,0x4d,0x0d,0x06, 0x20,0xc9,0xa3,0x43, 0x47,0xea,0x9e,0x89, 0xa6,0x09,0xa7,0xa8, + 0x7a,0x81,0x53,0x54, 0xf3,0x49,0xa9,0x93, 0x46,0x80,0x00,0x06, 0x80,0x00,0x34,0x03, + 0x40,0x00,0x00,0x00, 0x00,0x00,0x06,0x80, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + // 0x100: + 0x00,0x00,0x00,0x02, 0x45,0x24,0x6a,0x1a, 0x69,0xaa,0x7a,0x4d, 0xb5,0x4f,0x4d,0x26, + 0x9e,0x84,0x6c,0x53, 0xd1,0xa1,0xb4,0x4f, 0x52,0x64,0xc2,0x64, 0xc9,0x83,0x49,0xb2, + 0x68,0x13,0x40,0x62, 0x30,0x00,0x20,0xd1, 0x90,0xc4,0x60,0x26, 0x26,0x26,0x69,0xa9, + 0xa7,0xa0,0x99,0x03, 0x26,0x23,0x26,0xd4, 0x34,0x1a,0x61,0x0d, 0x18,0x05,0x14,0xa8, + 0xec,0xda,0x94,0xc4, 0x9b,0x54,0x10,0xa9, 0x8a,0x95,0xcc,0xac, 0x82,0x09,0x2c,0x23, + 0x6a,0x0c,0x18,0xd4, 0x21,0x00,0x08,0x02, 0x4d,0x20,0x91,0x14, 0x12,0x6c,0x09,0xac, + 0xa3,0x8d,0xc8,0xf6, 0xa0,0x20,0x41,0x5f, 0x51,0x63,0x43,0xb9, 0xdd,0xef,0x78,0x16, + 0xf7,0x1c,0x7f,0xab, 0xed,0xe5,0xf3,0x79, 0xf7,0xbf,0x1f,0x9c, 0x0c,0x1c,0x3c,0x68, + 0x30,0x96,0x71,0x12, 0xe6,0x2e,0x49,0x17, 0xd2,0x6d,0xcc,0xb5, 0x13,0x56,0xab,0x2d, + 0x94,0x59,0x47,0x29, 0xca,0xda,0xc5,0xb6, 0x6a,0x1a,0xa6,0x76, 0x8c,0xbb,0xc8,0x3a, + 0x9b,0xe2,0x47,0xcc, 0xc5,0x31,0x5d,0x90, 0x48,0x2b,0x92,0xb8, 0x59,0x3a,0xc6,0xd9, + 0x34,0x18,0xef,0xac, 0xb8,0x36,0xb6,0xe9, 0x5c,0xbf,0xba,0xbb, 0xe8,0xc0,0x97,0x51, + 0x46,0xcd,0xa8,0x93, 0xdd,0xee,0x12,0x4a, 0x62,0x66,0x5f,0x21, 0x23,0x23,0x9b,0xca, + 0x1d,0x67,0xc0,0x94, 0x93,0x54,0x6e,0x59, 0x6a,0x18,0xd1,0xb4, 0x05,0x4c,0x97,0x2e, + 0xce,0x22,0x61,0xc3, 0x6e,0x0c,0x82,0x0e, 0xb6,0xa6,0xe8,0xe0, 0x5e,0x1f,0x97,0xa1, + 0x29,0xa1,0x3c,0x99, 0xa3,0x23,0x35,0x80, 0x88,0x61,0x80,0xc1, 0x10,0xc8,0x10,0x0d, + // 0x200: + 0x20,0x80,0x02,0x0e, 0xf0,0x25,0x04,0x40, 0x49,0x01,0x4f,0x7a, 0xae,0x97,0x5f,0x89, + 0xa4,0xb0,0xa8,0xc1, 0xf9,0x53,0xa4,0x58, 0x55,0xe1,0x4a,0x9a, 0x7a,0x49,0xfe,0x36, + 0x2f,0xc9,0xad,0xe4, 0xe9,0xee,0x60,0xfb, 0xe3,0x55,0xbd,0x81, 0x7f,0x53,0x5f,0xd9, + 0x29,0x8b,0xd4,0x9a, 0x94,0x2a,0x8a,0xee, 0x65,0xee,0x22,0xef, 0xe4,0xb7,0x94,0x13, + 0xea,0x39,0xaa,0x71, 0x97,0xa2,0xa6,0x0a, 0xa0,0x75,0x40,0x86, 0xb7,0x5d,0x8c,0xa1, + 0x75,0x51,0x9c,0xa3, 0x3d,0x04,0xdd,0x12, 0x2a,0x81,0x01,0xa1, 0xce,0x40,0x25,0x29, + 0x27,0x1d,0x3c,0x49, 0x55,0x0a,0x13,0x6e, 0xc8,0xe2,0x0e,0x9c, 0x46,0x75,0x59,0xf4, + 0xb2,0xa0,0x85,0x85, 0x26,0xbc,0x81,0x84, 0xc3,0xdf,0x4d,0xd0, 0xb0,0xc2,0xb1,0xaf, + 0xa2,0x56,0xb5,0xae, 0x9b,0xe3,0xcd,0xcc, 0xd1,0x15,0x55,0x77, 0xaa,0xa7,0x65,0x55, + 0x7e,0xbe,0x5e,0x3d, 0x3c,0xc4,0x9c,0x1c, 0xb5,0x7b,0xcd,0xc1, 0x05,0x03,0x0b,0x56, + 0x11,0x30,0x04,0x0d, 0x34,0x92,0x9c,0xf7, 0xac,0x10,0x04,0x2f, 0xf0,0x11,0x1e,0x5f, + 0x4b,0x10,0x90,0x82, 0xb9,0xd8,0xba,0x6c, 0x6c,0x08,0x8d,0xc5, 0x04,0x47,0x03,0x3f, + 0x26,0x21,0x36,0x48, 0x19,0x06,0x69,0x1c, 0x51,0x10,0x17,0xf6, 0xf5,0xb1,0x41,0x00, + 0x9b,0x49,0x00,0xda, 0x10,0xd2,0x63,0x03, 0x01,0xf3,0x45,0xc1, 0x7e,0x93,0xd2,0xef, + 0xe0,0x09,0x40,0xba, 0x7b,0x14,0x5a,0x09, 0x21,0x7f,0x62,0x02, 0xe6,0x29,0xe5,0x85, + 0xe6,0x5c,0x69,0x00, 0xa9,0xf4,0xf0,0x94, 0xfa,0x37,0x30,0x48, 0xbe,0x46,0xe3,0xb6, + // 0x300: + 0xba,0xc9,0x45,0x13, 0x3e,0x06,0x09,0x13, 0xeb,0xe0,0x85,0xab, 0x60,0x91,0x31,0xa4, + 0x95,0x1d,0x1d,0x1f, 0x3a,0x77,0x8c,0x70, 0x84,0x2b,0xa1,0xf6, 0x2b,0x69,0xe3,0xd2, + 0x8a,0x20,0x7c,0x47, 0x0f,0xa7,0x20,0x29, 0xef,0xea,0x26,0x5b, 0x24,0x6d,0xe4,0xd4, + 0x99,0x13,0x4d,0x38, 0xba,0x0b,0x34,0x00, 0x81,0xe2,0x29,0x4b, 0x07,0x9b,0x16,0xd6, + 0x40,0xe6,0x31,0xac, 0x76,0x0b,0xe7,0x6e, 0x4a,0x5f,0xa6,0x33, 0xa9,0x00,0x68,0x8b, + 0x24,0xd7,0x91,0xc8, 0x48,0xbb,0x05,0x47, 0x76,0x45,0x15,0x4a, 0x19,0x86,0x1e,0xa5, + 0x83,0x15,0x17,0xf5, 0x9f,0x50,0x48,0xe2, 0x22,0x71,0x30,0x55, 0x6e,0xe6,0x10,0x60, + 0xbb,0x60,0x05,0x3c, 0xfe,0xda,0xd1,0x3e, 0xab,0xea,0x8e,0x21, 0x59,0x87,0xba,0x65, + 0x81,0xc3,0x38,0xea, 0xd3,0x04,0x1a,0xc1, 0x0d,0xf1,0xf2,0x52, 0x85,0x63,0x19,0xc1, + 0x69,0x82,0xde,0xd4, 0x14,0x31,0xcd,0x04, 0x41,0x79,0x94,0x19, 0x46,0x10,0xd2,0x32, + 0xc8,0xe3,0x18,0x29, 0x1a,0xdd,0xfb,0xda, 0x8c,0x61,0x37,0x09, 0xd4,0x91,0x1b,0x0c, + 0xb3,0xf8,0xe9,0xef, 0xb7,0xf8,0x25,0x91, 0x11,0x71,0x2f,0x8d, 0x1c,0xd3,0x68,0xcd, + 0xbe,0xab,0xbc,0x52, 0x72,0x6d,0xf9,0xbb, 0x09,0x86,0x6c,0xd1, 0x69,0x0a,0x70,0x02, + 0x1e,0x38,0x84,0x23, 0x4a,0xe3,0x78,0x6d, 0x5c,0x52,0xaa,0xbf, 0x53,0x2d,0x8a,0xd7, + 0xf8,0x7b,0xd6,0x2c, 0x7c,0x03,0x67,0xf4, 0xc2,0xc4,0x79,0x7d, 0xbd,0xc0,0xc2,0x0c, + 0x3f,0x1c,0x4b,0xfc, 0x0c,0xfa,0xcd,0xda, 0x83,0xa1,0x86,0x90, 0x73,0xd0,0xf9,0xd7, + // 0x400: + 0xae,0xde,0xf9,0x7a, 0xdc,0x98,0xdf,0x05, 0x72,0x33,0xf5,0x9a, 0x9b,0x51,0xce,0xb9, + 0xda,0xb7,0xe5,0x7b, 0x11,0x77,0x2b,0xe8, 0xa1,0xe8,0xf0,0x84, 0x42,0x6d,0xa1,0xa7, + 0xbb,0x10,0x33,0xc5, 0x00,0x24,0x9a,0x02, 0x1a,0x4b,0x95,0xd2, 0x4a,0x75,0xfc,0xd3, + 0x6f,0x83,0x62,0xb9, 0x35,0x5e,0xd9,0x53, 0x33,0x11,0x10,0x22, 0x25,0x2c,0x90,0x44, + 0x0f,0x9e,0x3f,0xc5, 0xbd,0x8a,0xc0,0x08, 0x80,0x98,0x0f,0x1a, 0xb0,0x18,0x09,0x2b, + 0x2d,0xcb,0x79,0x8b, 0x34,0x80,0x59,0x0c, 0x2e,0xc6,0x21,0x00, 0x09,0x93,0x44,0x5a, + 0x53,0xbe,0xfc,0xfd, 0xde,0x12,0xd8,0xa6, 0x6f,0x77,0x15,0x44, 0x51,0xed,0xb4,0x12, + 0x99,0xc2,0xa3,0x97, 0xd9,0x4d,0x32,0x65, 0x8f,0x2d,0x91,0x4f, 0xdc,0x91,0x7f,0xa9, + 0xcb,0x51,0xb1,0x6d, 0x2b,0xcb,0x7a,0x89, 0xe0,0x00,0x3a,0x20, 0xa0,0x20,0x38,0x92, + 0x98,0xca,0x1f,0x42, 0x5c,0xdf,0x86,0x96, 0xa7,0x2b,0x51,0x6d, 0x49,0xb3,0x6e,0x1a, + 0xc8,0x85,0xe1,0xc8, 0x54,0x3c,0x12,0x38, 0xee,0xb2,0x30,0x8d, 0x09,0x0b,0x7c,0xd0, + 0xa7,0x80,0xd0,0x8f, 0xd5,0xa8,0xd8,0xb2, 0xed,0x5a,0x5a,0x52, 0xc5,0x70,0xfc,0x5b, + 0xa6,0x06,0x4c,0x48, 0x88,0x14,0xc6,0x89, 0x23,0x08,0xe1,0xe2, 0x53,0xd9,0x57,0xce, + 0xa0,0x4b,0x15,0x33, 0xf4,0x71,0xc1,0x44, 0x02,0x8f,0xfc,0x7d, 0x46,0x24,0x23,0x00, + 0x82,0x3a,0x34,0xcf, 0x08,0x62,0x42,0x30, 0x0f,0x39,0x83,0x18, 0xc1,0x72,0xf3,0x8b, + 0x50,0x3d,0xa5,0x12, 0x4d,0x69,0xd7,0xe2, 0x08,0x5f,0x62,0x4d, 0x50,0x00,0x88,0x22, + // 0x500: + 0x8b,0xc6,0x21,0xf9, 0x55,0xeb,0xb3,0x62, 0x44,0x21,0xdf,0x4a, 0x19,0xf4,0x2c,0xdc, + 0x7c,0xee,0x68,0x40, 0xf8,0xe4,0x7c,0xfd, 0x01,0x17,0xe9,0xa7, 0x2e,0xde,0x0a,0xa2, + 0xa5,0x03,0x9f,0xc0, 0x1d,0x24,0x33,0xa3, 0x30,0xc7,0xd1,0xfa, 0xac,0xb0,0xd4,0x29, + 0x7d,0xb1,0xec,0x7e, 0xce,0x1a,0x80,0x5c, 0x1e,0x79,0xf0,0x4c, 0xa3,0x00,0x03,0x4d, + 0x7c,0x81,0xfd,0xf9, 0xb2,0x08,0x00,0xe4, 0x3a,0x51,0x60,0x1a, 0x02,0xc2,0x0c,0x4d, + 0x84,0x71,0xff,0x1e, 0x14,0x8b,0x21,0x8e, 0xb2,0x1c,0x86,0x4f, 0x78,0x06,0x8b,0xf4, + 0xa9,0x57,0x9b,0x79, 0xb5,0x94,0xd4,0xd3, 0xbd,0xba,0xdb,0xc1, 0x67,0x2e,0x5c,0xbb, + 0x0f,0x71,0xa8,0x08, 0x67,0x20,0xa9,0xa2, 0x39,0xff,0x51,0xca, 0xad,0x9f,0x7b,0xbe, + 0x5c,0x84,0x03,0x9d, 0x57,0x91,0x99,0x56, 0x7d,0xdf,0xd9,0x8c, 0x68,0x57,0xee,0xde, + 0x05,0x3b,0x40,0x71, 0xb4,0x40,0x05,0x24, 0xaa,0xd8,0x67,0x10, 0x42,0x01,0xb9,0xe4, + 0x80,0x08,0x4d,0xaa, 0x40,0xc3,0x00,0x6b, 0x4c,0x21,0x06,0x80, 0xa3,0xa7,0x56,0x8f, + 0x16,0x03,0x81,0x26, 0x51,0x5e,0x8c,0xfd, 0x9c,0x69,0xbd,0x28, 0xa7,0x16,0x35,0x89, + 0x91,0x84,0x25,0x7f, 0xb3,0x41,0x22,0x2d, 0xfa,0x44,0x71,0xc0, 0x10,0xe3,0x26,0x88, + 0x51,0xa6,0x9a,0xa4, 0x6f,0xe4,0x27,0xc2, 0xfe,0x7c,0x14,0xf8, 0x60,0x28,0x51,0xb5, + 0xd6,0x70,0xaa,0x68, 0x00,0x00,0x2f,0x86, 0x02,0x41,0x11,0xc9, 0xff,0x17,0x72,0x45, + 0x38,0x50,0x90,0x42, 0xbb,0x6e,0x44, + // 0x5f7 = 1527 Bytes +}; // ctcode_mod2_usa_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_mod2_jap_bz2 (rmcj/mod2.bin) + +const u8 ctcode_mod2_jap_bz2[1538] = +{ + 0x00, 0x00, 0x0c, 0x38, // 3128 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x83,0xe2, 0x98,0x91,0x00,0x04, + 0xb7,0x7f,0xff,0xff, 0xff,0x77,0xe6,0x4f, 0xff,0xf7,0xff,0xf6, 0xff,0xff,0xff,0xff, + 0x45,0x75,0x7f,0xff, 0x7f,0x7f,0xfe,0xfe, 0x6e,0x64,0xf5,0xed, 0xf5,0x65,0x6e,0xd5, + 0xfe,0x5f,0x7f,0xd0, 0x04,0x1e,0x4c,0x77, 0x02,0x76,0x8e,0xdb, 0xb6,0xc3,0x11,0x11, + 0xa2,0x9e,0x34,0x4d, 0x29,0xe1,0xa9,0xb5, 0x27,0xe8,0x6a,0x9e, 0xa7,0x92,0x3d,0x46, + 0x1a,0x41,0xea,0x68, 0x79,0x47,0xa8,0x0d, 0x0d,0xa8,0x1a,0x0f, 0x50,0xd3,0x41,0xa0, + 0xd0,0xf5,0x34,0x1a, 0x0d,0x1a,0x64,0x03, 0x40,0x00,0x68,0x68, 0x0d,0x1a,0x31,0x03, + 0xd4,0xcd,0x43,0x4d, 0x06,0x81,0xea,0x7a, 0x86,0x20,0x89,0x0a, 0x69,0xe9,0x1e,0x90, + 0x6a,0x60,0x0d,0x06, 0x9a,0x32,0x32,0x7a, 0x26,0x04,0xc8,0x9e, 0x9a,0x0d,0x26,0x23, + 0x13,0x1a,0x08,0xc2, 0x1a,0x18,0x68,0x00, 0x02,0x60,0x01,0x18, 0x26,0x04,0x69,0x93, + 0x23,0x04,0xd3,0x09, 0x89,0xa6,0x98,0x00, 0x26,0x26,0x01,0x11, 0x4c,0xc2,0x09,0xa1, + 0xa6,0x80,0x93,0xda, 0x93,0x66,0x84,0x64, 0x83,0x10,0x68,0xf5, 0x34,0x7a,0x9a,0x68, + 0x68,0x6d,0x4d,0x03, 0x40,0x00,0x7a,0x81, 0xa6,0x80,0x69,0x88, 0x06,0x81,0xa0,0x00, + 0x0f,0x48,0xd0,0x00, 0x00,0x68,0x00,0x34, 0x00,0xf4,0x81,0x00, 0x1a,0x68,0x19,0x0d, + 0x1a,0x1a,0x00,0x1a, 0x01,0xa6,0x83,0x10, 0x32,0x1a,0x00,0x0d, 0x00,0x00,0xc9,0xa0, + 0x00,0x64,0x01,0x93, 0x4c,0x8d,0x34,0x64, 0x68,0x34,0x00,0x00, 0x68,0x01,0x88,0x68, + // 0x100: + 0xd3,0x4c,0x26,0x86, 0x08,0x09,0x14,0x44, 0x26,0xd5,0x32,0x7a, 0x84,0xc6,0x99,0x1a, + 0x35,0x33,0x53,0x46, 0x02,0x31,0x1a,0x61, 0x0c,0x26,0x27,0xa2, 0x69,0x93,0x11,0x93, + 0x00,0x46,0x01,0x06, 0x98,0x46,0x8d,0x00, 0x60,0x9a,0x66,0x84, 0xf4,0x34,0x4c,0x00, + 0x09,0xa6,0x01,0x30, 0x26,0x04,0x64,0x3d, 0x4a,0x33,0x4e,0xec, 0xb5,0x33,0x49,0x36, + 0x4f,0x85,0x74,0x6d, 0x4e,0xd3,0x7e,0x82, 0x09,0x58,0x4e,0x6a, 0x0c,0x18,0xd4,0x21, + 0x00,0x08,0x02,0x4d, 0x20,0x92,0x30,0x49, 0xb0,0x2e,0x0c,0x9a, 0x73,0x93,0x5f,0x01, + 0x02,0x0f,0x4e,0xe5, 0x85,0xa1,0xba,0xb1, 0xde,0x59,0x7e,0xbc, 0x2e,0x2f,0x23,0x97, + 0xcd,0xe7,0x73,0xe5, 0xfb,0xfe,0x1f,0x2b, 0xff,0xb7,0xe6,0x6e, 0x02,0xee,0xd8,0x78, + 0xcd,0x19,0xd9,0xca, 0x5c,0x3a,0x0b,0xed, 0x9c,0xb9,0x5d,0x77, 0x4b,0xa8,0xf1,0x38, + 0xad,0xae,0x19,0x9e, 0x37,0x52,0xaf,0x98, 0xff,0x21,0x93,0xfa, 0x5a,0xfc,0x56,0x81, + 0xe9,0x2b,0xd5,0x02, 0x51,0x74,0x1f,0x56, 0x2d,0x26,0x13,0xe4, 0x9a,0x58,0x59,0x71, + 0xb9,0x5c,0xd5,0x3a, 0x7d,0x7b,0x4e,0xe7, 0x7e,0xd9,0x87,0x6e, 0xe1,0x42,0x86,0xb6, + 0xf2,0xb5,0x34,0xf1, 0x2f,0xd8,0x78,0xd9, 0xb6,0x4e,0x01,0xfc, 0x8e,0x2b,0xa9,0xd7, + 0x05,0x4e,0xa9,0x5f, 0xd6,0x41,0xb0,0x36, 0xfa,0x70,0xe1,0x66, 0x0a,0x5d,0x06,0xad, + 0x09,0x85,0x2e,0xed, 0xcb,0x55,0x9c,0x21, 0x11,0xa6,0x55,0x47, 0xcb,0x48,0x22,0x46, + 0x18,0x4f,0x04,0x8c, 0x38,0xe3,0x10,0x38, 0x00,0x00,0x80,0x3b, 0x40,0x04,0x21,0xc4, + // 0x200: + 0x03,0x80,0x84,0x20, 0x00,0x73,0x69,0x40, 0xd8,0x91,0x6a,0x33, 0x2f,0x27,0xde,0x13, + 0x35,0x26,0x7e,0xf0, 0x82,0xe3,0xac,0xe8, 0x5d,0x21,0x0d,0x93, 0xef,0x8f,0x63,0x73, + 0x11,0xab,0xc6,0xc2, 0xbb,0x23,0x6d,0x85, 0xa9,0x92,0x23,0xb7, 0x0c,0x1a,0xbf,0xda, + 0x41,0x47,0x7f,0xfc, 0xff,0xd1,0x36,0xf3, 0x3d,0x1e,0x50,0xbd, 0x02,0x95,0x0b,0x8e, + 0x81,0xe2,0x22,0x49, 0x42,0xb5,0x96,0x10, 0xf9,0x6b,0x56,0x25, 0x7e,0x86,0x5f,0x2d, + 0xdd,0x75,0x72,0x49, 0x54,0x3c,0x78,0x37, 0x15,0x2f,0xec,0x2b, 0xf1,0x0a,0x9f,0x0c, + 0x68,0x09,0xc2,0x7b, 0x8c,0x7d,0x31,0xf3, 0x2e,0x5d,0x4c,0x42, 0x51,0x4a,0xc4,0xbc, + 0x16,0xad,0x28,0x8a, 0xd5,0x37,0xac,0x61, 0xac,0xb3,0x88,0x62, 0xf0,0xfe,0xb9,0x74, + 0x9a,0xa7,0xcc,0x9a, 0xba,0x9e,0xa3,0xdc, 0xa1,0x51,0x7a,0xca, 0x7c,0x94,0xea,0xd5, + 0x91,0x82,0x0a,0xc6, 0x1e,0x93,0x08,0xb0, 0x04,0x0d,0x34,0x92, 0xaf,0x76,0x2c,0x10, + 0x04,0x2e,0xb0,0x11, 0x3a,0xd9,0xa4,0x88, 0x90,0x82,0xda,0xef, 0xaf,0x27,0x9b,0x81, + 0x12,0xc2,0x7c,0x4b, 0x2c,0xf4,0xb1,0x13, 0x64,0x83,0x20,0xef, 0xee,0xbd,0xc9,0x21, + 0x01,0x78,0x1f,0xd1, 0x18,0x24,0x21,0xb4, 0x24,0x0d,0x89,0x89, 0xa4,0x9b,0x18,0x1f, + 0xa5,0x6c,0x77,0xcf, 0x00,0xf4,0x3c,0xe8, 0x02,0x50,0x2d,0x78, 0x6a,0x6f,0x76,0x50, + 0xd1,0x69,0xa1,0x6e, 0x6d,0xb1,0xa4,0x97, 0xd7,0x2e,0x18,0xcb, 0xd6,0xd4,0x4e,0x48, + 0x05,0x55,0x46,0x13, 0x6e,0xe1,0xcf,0x97, 0x23,0x53,0x25,0x53, 0x11,0x9c,0x95,0xd3, + // 0x300: + 0x6d,0xed,0xf6,0x33, 0x91,0x4e,0xd0,0x83, 0x9a,0xd0,0x60,0x9c, 0x1a,0x43,0x68,0x1b, + 0x40,0xd8,0x86,0xc4, 0x36,0x95,0x27,0x06, 0x08,0x99,0x88,0x9a, 0x69,0xa2,0x01,0xc0, + 0xfc,0xaf,0x76,0x74, 0x05,0x76,0x19,0xa1, 0xf9,0xa5,0xa2,0x29, 0x0b,0x08,0x14,0x1a, + 0x16,0x6a,0x93,0xb4, 0x4a,0x38,0xad,0x8c, 0x23,0xae,0xba,0xfc, 0x2b,0x7d,0x8a,0xa0, + 0x60,0xb2,0xbb,0xa0, 0xd9,0x8c,0x14,0x51, 0x4d,0x1c,0x0f,0x24, 0x80,0x73,0xb5,0x8f, + 0xd4,0x70,0xa2,0x8a, 0x59,0x6e,0xb2,0x14, 0xf7,0x21,0x46,0x73, 0xfe,0xa0,0x6f,0xb9, + 0xd2,0x3a,0xe3,0x29, 0x96,0x6a,0x84,0x1d, 0x0a,0xd8,0x6e,0xca, 0x6a,0xab,0x8b,0xa0, + 0x41,0x14,0xde,0x1e, 0xb4,0x72,0xb1,0x91, 0x41,0xf8,0xd4,0x39, 0x04,0x52,0xc2,0x73, + 0xdb,0x82,0xfe,0xde, 0x9a,0x22,0x77,0x33, 0x4c,0x00,0xee,0x8c, 0x2b,0x06,0xd1,0x55, + 0x67,0x3a,0x44,0xa9, 0x21,0xc6,0xe5,0x9b, 0x85,0xd8,0xf7,0xa2, 0xef,0x0c,0x41,0x79, + 0x2b,0x09,0x29,0xa9, 0xb6,0x51,0x47,0xb4, 0x9f,0x8e,0xba,0xcb, 0x31,0x55,0xd0,0xa3, + 0xfd,0xff,0x9e,0x0a, 0x3a,0xb7,0xec,0x5f, 0x1d,0xb2,0xac,0x92, 0x03,0x96,0x10,0x67, + 0x94,0x33,0x4c,0xaf, 0x30,0x09,0xe5,0x8c, 0x09,0x49,0x20,0xb7, 0x60,0xe9,0x11,0x76, + 0xa2,0x3e,0xec,0xf3, 0xfe,0x93,0x87,0xc1, 0x19,0x33,0x23,0xd5, 0x0c,0x9f,0x33,0x3e, + 0x23,0x54,0x79,0xf9, 0x06,0x08,0x2b,0x87, 0xbb,0x2b,0x87,0x36, 0xbd,0x67,0x1a,0x18, + 0xe6,0x6e,0x60,0xe9, 0xaa,0x10,0x94,0xd0, 0x5a,0x8a,0xb7,0xc6, 0x8a,0xdb,0x22,0x65, + // 0x400: + 0x17,0x3e,0xf4,0xd0, 0x07,0x0e,0x13,0xe0, 0xdb,0xe3,0xfd,0x59, 0x8d,0x68,0x32,0x0e, + 0x92,0x49,0x26,0x17, 0x3a,0xd6,0x0a,0x3e, 0xe2,0x84,0x8a,0x7b, 0x16,0xd0,0xe3,0x40, + 0x96,0x5d,0x3b,0x56, 0xbb,0xf8,0xf7,0x1f, 0xc2,0xdd,0x4b,0x6f, 0x7f,0xfb,0x88,0x1e, + 0x8a,0x74,0xfe,0xb3, 0x7f,0xde,0xb8,0x8c, 0x85,0x28,0xc2,0x90, 0x74,0x29,0x6f,0xab, + 0x6c,0xeb,0xaf,0xaf, 0x2e,0xa5,0x50,0x96, 0x8d,0x2e,0xfb,0x91, 0x3b,0x44,0xaf,0x3c, + 0x9f,0xd0,0xec,0x8c, 0x87,0x11,0x82,0xa0, 0x01,0x2c,0x28,0xe6, 0xca,0x44,0x40,0x0b, + 0xfa,0x39,0x64,0x08, 0x01,0x8a,0x74,0x20, 0x05,0x02,0x61,0x91, 0x14,0x55,0x23,0xef, + 0x7d,0x5d,0x9f,0x83, 0xf8,0x9f,0x73,0x9f, 0xd7,0xa4,0x90,0x42, 0x49,0x2f,0x05,0x4b, + 0xb0,0x7e,0x75,0x60, 0x50,0xab,0x6a,0x2e, 0xd1,0x94,0x23,0x17, 0x37,0xb3,0x72,0xab, + 0xf8,0xe7,0x20,0xed, 0x8c,0x40,0x59,0x26, 0x00,0x84,0x20,0x43, 0x0d,0x51,0x30,0xd3, + 0x5b,0xf9,0x4a,0xf4, 0x87,0x59,0xdf,0x6e, 0x16,0x80,0xa0,0xac, 0x18,0x45,0x1b,0xc3, + 0xaa,0x49,0xe1,0x64, 0xad,0x70,0x54,0x92, 0xf8,0x40,0x85,0x8d, 0x44,0xf1,0xeb,0x08, + 0x0b,0x51,0x67,0xb0, 0x08,0x08,0x05,0xf5, 0xa6,0x94,0x9d,0xfd, 0xbb,0xeb,0xe3,0xab, + 0xc6,0x6f,0x27,0x37, 0xb8,0x18,0xdb,0x58, 0xa0,0x02,0x61,0x11, 0x1e,0x10,0x14,0xac, + 0x23,0x66,0x31,0x02, 0x42,0xb0,0x68,0x57, 0x80,0x1a,0x09,0x58, 0x83,0xb6,0xd6,0x39, + 0xac,0x6f,0xb7,0x4a, 0x37,0x16,0xd1,0x0a, 0x56,0x89,0x0b,0xb4, 0x48,0x5b,0x6d,0xd7, + // 0x500: + 0x32,0x00,0x85,0x56, 0x30,0x02,0x0c,0x40, 0x04,0x32,0xd2,0xf6, 0x7f,0x5f,0xfd,0xc0, + 0x42,0x6e,0x50,0x02, 0x61,0x4a,0xf7,0x2b, 0x33,0x06,0x83,0xf4, 0x58,0xc8,0xa3,0x73, + 0xb3,0xa0,0xc2,0x8e, 0xfe,0x9a,0x1a,0x28, 0xc6,0x66,0xc6,0x0c, 0x92,0x95,0xeb,0xb3, + 0x34,0x21,0x08,0x08, 0x44,0x10,0x7e,0x36, 0x07,0x30,0x08,0x5f, 0xdd,0x5b,0x59,0x59, + 0xf4,0xd4,0x95,0xaf, 0x84,0x16,0x0d,0xc4, 0x16,0xe3,0x07,0x20, 0xa2,0xcb,0x51,0x44, + 0x74,0xb8,0xa1,0x94, 0xc6,0xb4,0xc4,0xdd, 0x20,0x42,0x56,0x46, 0x22,0x84,0x58,0xcd, + 0xb9,0xbd,0xc9,0xa8, 0xa3,0x91,0x62,0x72, 0xe4,0x4e,0x83,0x4b, 0x43,0xb4,0x82,0x55, + 0x23,0x42,0xbd,0xee, 0xe1,0xfd,0x86,0x34, 0x7a,0x97,0x75,0x62, 0x57,0x3b,0x14,0xe7, + 0x66,0xbd,0x78,0xed, 0x6d,0x8c,0x63,0x44, 0xa5,0x87,0x05,0x97, 0x20,0x20,0x9a,0xdd, + 0xaa,0x75,0x55,0x67, 0x70,0x82,0x0b,0x70, 0x3f,0xc9,0x39,0x29, 0x4b,0x73,0x7d,0x8b, + 0xc5,0x29,0x58,0x1e, 0xdd,0x06,0xc7,0xb6, 0x3c,0x97,0x9b,0xd1, 0x18,0xe3,0x39,0x29, + 0x1d,0x3d,0xbd,0x4d, 0xf8,0x3c,0x01,0xa6, 0x9a,0x39,0xb8,0x50, 0xf5,0x33,0x4a,0xe1, + 0x29,0xd1,0xba,0x3c, 0xfe,0x4e,0x92,0x48, 0x24,0x95,0x2c,0xfb, 0xb5,0xd2,0x59,0x68, + 0x59,0xc6,0xea,0x8f, 0xfa,0xde,0x79,0xf2, 0x4f,0xe9,0xd6,0xe4, 0x3c,0xdb,0x3d,0xd1, + 0xf4,0x9b,0x2a,0xfb, 0x8c,0xd3,0x44,0xd1, 0xc9,0x4a,0x75,0x10, 0x4b,0x02,0xd4,0x8d, + 0x4f,0xc4,0xff,0xe2, 0xee,0x48,0xa7,0x0a, 0x12,0x10,0x7c,0x53, 0x12,0x20, + // 0x5fe = 1534 Bytes +}; // ctcode_mod2_jap_bz2[] + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// ctcode_ovr1_pal_bz2 (rmcp/ovr1.bin) + +const u8 ctcode_ovr1_pal_bz2[7364] = +{ + 0x00, 0x00, 0x53, 0x64, // 21348 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xbb,0xd2, 0x4b,0xe5,0x00,0x0e, + 0x7b,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe0, 0x19,0x30,0x7a,0xc7, 0xdd,0xf6,0x55,0x2f, 0x60,0x6a,0xfb,0x1a, + 0x4a,0x48,0x90,0x05, 0x4d,0xab,0x52,0x29, 0x16,0x43,0x66,0x00, 0x69,0xb6,0x76,0xee, + 0xe9,0xc5,0xce,0xc0, 0xe9,0x76,0xa9,0xab, 0xec,0x1a,0x0a,0xf3, 0x02,0x39,0xed,0xa9, + 0x7d,0x60,0x44,0x4a, 0x41,0x21,0xa9,0x02, 0x62,0x4c,0x25,0x3f, 0x13,0x49,0x8d,0x0c, + 0xa3,0x24,0xcc,0x4d, 0x35,0x3c,0x53,0xc4, 0xc9,0xa6,0x8d,0x47, 0x91,0x3d,0x34,0x69, + 0xa0,0x4d,0xa9,0xe4, 0x1a,0x34,0xca,0x7a, 0x9f,0xa5,0x36,0x49, 0x93,0x64,0x1a,0x6a, + 0x79,0x34,0xd3,0xd4, 0x1a,0x99,0xa8,0xfd, 0x4d,0x4f,0x53,0x4f, 0x12,0x3c,0x41,0xa9, + 0xa3,0xd4,0xd3,0x35, 0x36,0xa3,0x61,0x47, 0xa1,0x32,0x66,0xa7, 0xa1,0x1e,0x99,0x26, + 0x8c,0x64,0xd4,0xde, 0xa3,0x13,0x42,0x69, 0xe8,0xda,0x8c,0x93, 0x6a,0x0d,0x11,0x13, + 0x44,0xd8,0x0a,0x68, 0xd0,0xd3,0x50,0xf4, 0x3d,0x23,0x42,0x6d, 0x23,0x35,0x4f,0x12, + 0x6f,0x48,0x1a,0x46, 0x4d,0x1a,0x61,0xa8, 0xc8,0xd3,0x11,0xa6, 0x19,0x4c,0xd4,0xf4, + 0x86,0x9a,0x36,0xa0, 0x1a,0x7a,0x99,0x00, 0xd0,0xd0,0x64,0x00, 0xd0,0x32,0x68,0x03, + 0x21,0x84,0x32,0x03, 0x11,0x93,0x35,0x0c, 0x8c,0xd4,0x7a,0x80, 0x00,0xd3,0x40,0x68, + // 0x100: + 0xd8,0x90,0x4a,0x24, 0xc1,0xa4,0x42,0x69, 0x53,0x79,0x35,0x13, 0xf4,0x84,0x3d,0x3d, + 0x46,0xa6,0x06,0x93, 0x35,0x37,0xa4,0xd4, 0xfd,0x4f,0x54,0xc9, 0xe9,0xa0,0x43,0x09, + 0x89,0x93,0x4c,0x23, 0x46,0x00,0x34,0x8c, 0x04,0xc9,0xea,0x61, 0xa8,0x0c,0x8d,0xa6, + 0x88,0xd3,0x26,0x03, 0x24,0xf4,0x8c,0x27, 0xa3,0x29,0x80,0x23, 0x7a,0xa0,0xf5,0x30, + 0x34,0x04,0xc1,0x1e, 0x80,0x35,0x30,0xd0, 0x09,0x8d,0x35,0x02, 0x44,0x41,0x09,0x34, + 0x99,0x35,0x30,0x43, 0x2a,0x7e,0xa8,0xd9, 0x4f,0x53,0xf5,0x4f, 0x6a,0x9f,0x8a,0x9f, + 0xaa,0x3f,0x54,0xd9, 0x1e,0x8d,0x4f,0x4a, 0x3c,0x53,0xda,0x1a, 0xa6,0xca,0x68,0x69, + 0xbd,0x51,0xe5,0x3f, 0x54,0xf5,0x3c,0x8d, 0x4f,0xd5,0x3c,0xa7, 0xa8,0xc2,0x6f,0x54, + 0x7e,0x54,0x19,0x1e, 0xa6,0x9f,0xa5,0x34, 0x6d,0x43,0x26,0xd2, 0x6d,0x4f,0xd4,0x47, + 0xa6,0xa7,0xa2,0x7a, 0x4c,0x8f,0x53,0xcd, 0x53,0x65,0x3f,0x52, 0x68,0xf2,0x9a,0x68, + 0xf4,0xd4,0xd3,0xf5, 0x4d,0x31,0x0d,0x0d, 0x1a,0x32,0x64,0xf5, 0x06,0x9a,0x68,0xde, + 0xa4,0xc9,0x90,0x85, 0x12,0x0f,0x50,0x98, 0x98,0x0d,0x4c,0x4d, 0x03,0x04,0xc0,0x21, + 0x93,0x11,0x91,0xa6, 0x86,0x8c,0x08,0xc9, 0x82,0x30,0x02,0x0d, 0x34,0x32,0x7a,0x20, + 0x61,0x30,0x83,0x21, 0xa0,0xc9,0x82,0x69, 0x89,0xa6,0x00,0x00, 0x00,0x02,0x68,0xd3, + 0x11,0xa3,0x26,0x00, 0x86,0x46,0x98,0x81, 0x84,0x06,0x23,0x00, 0x91,0x44,0x09,0xa0, + 0xd2,0x32,0x26,0xd5, 0x33,0x4a,0x79,0x34, 0xda,0x26,0x8d,0x32, 0x6a,0x79,0x1e,0x94, + // 0x200: + 0xc2,0x7a,0x1a,0x9e, 0xa6,0x34,0x9e,0x93, 0x64,0x8f,0x51,0xea, 0x7a,0x87,0xa1,0x3d, + 0x26,0x9e,0xa7,0x94, 0x69,0xa0,0xf4,0x9e, 0x24,0xda,0x4d,0x31, 0x06,0x8f,0x53,0xd1, + 0xa4,0xd3,0x13,0xd2, 0x0c,0x40,0x7a,0x8d, 0x1a,0x32,0x64,0x1a, 0x64,0xcd,0x4c,0x4d, + 0xa4,0xf5,0x01,0xe1, 0x43,0x40,0x79,0x47, 0xa9,0xea,0x68,0xd0, 0x00,0x31,0x2c,0x62, + 0x34,0xf9,0x77,0x73, 0x2c,0xc0,0x86,0x4c, 0xb9,0x26,0x52,0xad, 0xed,0x46,0x90,0xf4, + 0x08,0xa7,0xc4,0x90, 0x7d,0x5a,0xbd,0x84, 0xf5,0x2d,0x6a,0x80, 0x51,0x98,0x9b,0x32, + 0xcb,0x5f,0xaf,0xe9, 0x2c,0x93,0xc4,0x32, 0x93,0xeb,0xc5,0x40, 0x29,0xb7,0x01,0x55, + 0xd7,0x75,0x56,0x1c, 0x72,0x72,0x0a,0x28, 0x82,0xcb,0x53,0x93, 0x8d,0x38,0x01,0x99, + 0x14,0xc4,0x7a,0x52, 0xe4,0xa3,0xb4,0x12, 0x63,0xb1,0x28,0xd1, 0x8e,0x21,0xc6,0x07, + 0x1d,0x5c,0x01,0x85, 0x83,0xc5,0x87,0x45, 0x41,0x81,0x3a,0xde, 0x82,0x31,0x49,0xbe, + 0xaf,0x3c,0x2d,0x5f, 0x9e,0x2c,0x4c,0x0c, 0xa4,0x02,0xb3,0x68, 0x45,0xab,0xb3,0x04, + 0xcf,0x12,0x51,0x52, 0x19,0x1c,0x20,0x02, 0xa1,0xd7,0xd5,0xd0, 0xf6,0x14,0x01,0x32, + 0xf0,0x72,0xda,0xa0, 0x6e,0x09,0x14,0x18, 0x02,0x6b,0x95,0xa8, 0x2e,0xc9,0xa0,0xf9, + 0xcc,0xac,0xe0,0xc2, 0xc3,0x28,0xea,0xb3, 0x30,0xfd,0xe8,0xd6, 0xa6,0x09,0xd0,0xac, + 0x5c,0xe4,0x9c,0xd6, 0x78,0x15,0xfa,0x68, 0x01,0x5b,0x8a,0x8f, 0x9a,0xad,0x8a,0xed, + 0xbd,0x66,0x97,0x4a, 0xa0,0x2e,0x7c,0xc8, 0x10,0xea,0x0c,0x10, 0x12,0xb5,0xe8,0xe2, + // 0x300: + 0x58,0x01,0x82,0x0f, 0x58,0x1d,0xe3,0x06, 0x62,0x02,0x21,0xc0, 0x75,0x24,0x54,0x8c, + 0x42,0x47,0x3f,0x98, 0x2a,0x44,0xa1,0x11, 0x38,0x69,0x0a,0x04, 0xa1,0x9b,0x33,0x10, + 0x10,0x9a,0x9e,0x40, 0x29,0x26,0x01,0x7a, 0xd9,0x09,0xd2,0x09, 0x22,0x29,0xe3,0x00, + 0x7b,0x4f,0x7b,0x8b, 0xb2,0xa9,0x10,0x50, 0x63,0x52,0xf0,0xde, 0x4a,0xcb,0xbe,0xc2, + 0xd7,0xe1,0xbe,0x08, 0x37,0x57,0x1b,0x54, 0x80,0x40,0xcf,0x77, 0x35,0xed,0x1e,0x75, + 0x41,0x40,0x47,0x94, 0x09,0xfc,0xf6,0x0b, 0xf5,0xb9,0xca,0xcc, 0x20,0x95,0x47,0x51, + 0x76,0x68,0x19,0x24, 0x9b,0xf3,0x08,0xf5, 0xd9,0xe8,0xde,0x60, 0x71,0xc0,0x0d,0xa7, + 0xbc,0x04,0xb8,0x16, 0x96,0xef,0x78,0x0d, 0x8b,0x2e,0x8f,0xd9, 0x23,0x3d,0xea,0x27, + 0xc3,0x34,0x34,0x84, 0x92,0xcf,0x33,0xcc, 0xe0,0xe4,0x80,0x12, 0x18,0xd6,0xbe,0x75, + 0x1e,0x66,0xa5,0x3b, 0x3b,0xe1,0x02,0x1e, 0x00,0xf0,0xb4,0xff, 0xa0,0x26,0x14,0xa9, + 0xc9,0xae,0x44,0xda, 0x7f,0x42,0xe4,0xa9, 0xce,0x04,0x06,0xe2, 0x6b,0xb4,0x24,0x20, + 0x4c,0x66,0x77,0x4d, 0x42,0x60,0x70,0x5b, 0x60,0x61,0xf7,0xb8, 0x86,0x29,0x30,0xbc, + 0xe8,0x00,0xab,0x81, 0xee,0x00,0xb7,0x1f, 0x0b,0x21,0x09,0x80, 0x5f,0x70,0x08,0x71, + 0x0e,0x40,0xc9,0x4f, 0x15,0xd8,0xf0,0x60, 0x73,0x5d,0x4f,0x13, 0x5b,0x56,0xed,0xcc, + 0x78,0x9a,0x48,0x77, 0xcf,0x29,0xf6,0x77, 0xd1,0xba,0x20,0xa6, 0x4f,0x69,0x62,0x48, + 0xd2,0x22,0x10,0x40, 0x8e,0xcf,0x2c,0x90, 0xd8,0x88,0x81,0x71, 0x02,0x61,0xe0,0x07, + // 0x400: + 0x6e,0x59,0x65,0xb8, 0x00,0xbc,0x90,0xca, 0xb7,0x08,0x08,0xe6, 0x9a,0x88,0x84,0xb7, + 0xb3,0xf5,0x59,0x95, 0x05,0xce,0xf6,0xd0, 0x2f,0x07,0xf5,0xe8, 0x1d,0xf9,0xc1,0x3a, + 0x04,0x8e,0x73,0xf9, 0x5e,0x09,0x79,0x53, 0xa2,0x88,0x83,0xe4, 0x91,0xd3,0x8e,0x21, + 0xc3,0x7c,0x62,0x95, 0x30,0x69,0x21,0xb1, 0x36,0x09,0x25,0x94, 0x4c,0x6d,0x24,0xac, + 0x09,0x08,0x3d,0xab, 0x47,0x9e,0x68,0x42, 0x53,0x31,0x7f,0x6e, 0x00,0xe0,0x8e,0x18, + 0xd7,0x90,0x49,0x25, 0x92,0x1a,0x0e,0x7f, 0xb5,0xd9,0x6c,0x76, 0x46,0xd4,0xe8,0x8d, + 0xa6,0xd3,0x2f,0x89, 0xd9,0x58,0x04,0xb6, 0xcd,0x20,0xe1,0x69, 0x47,0x30,0xc0,0x14, + 0x0c,0x4b,0x7c,0xd1, 0x47,0xf3,0xdf,0x21, 0xa5,0x46,0x21,0x24, 0x2f,0xd9,0x1a,0x12, + 0xb6,0x68,0xfc,0xd4, 0x41,0x52,0xce,0x75, 0xa4,0x05,0x4e,0xeb, 0x12,0xc3,0xc6,0xc0, + 0x45,0x29,0xa5,0x07, 0x86,0xed,0xb0,0x12, 0xeb,0x43,0x47,0xb8, 0x6d,0xb7,0x31,0x08, + 0x4d,0xbe,0x31,0x40, 0xa2,0xc5,0x9a,0x8c, 0x41,0x61,0xa4,0x8d, 0x9b,0x42,0xd1,0xd3, + 0x8e,0xfa,0xb6,0x01, 0x1d,0xdb,0x01,0x57, 0x63,0x62,0x0a,0x18, 0x82,0xb6,0x97,0x5b, + 0x11,0x23,0xed,0xb5, 0x44,0xa8,0x80,0x22, 0x0d,0x25,0x43,0x49, 0x45,0xa4,0x96,0x7b, + 0xdb,0x6b,0x35,0xce, 0x45,0xd6,0xb4,0x17, 0x1a,0x1e,0x36,0x09, 0x22,0x86,0x81,0x1e, + 0x4b,0x20,0xc4,0x23, 0xe4,0x7d,0x0f,0xe0, 0xdc,0x6f,0xf4,0xb5, 0xca,0xf8,0x11,0x04, + 0x33,0x6f,0xd9,0xc6, 0xbc,0x51,0x14,0x60, 0x33,0xef,0xe0,0x42, 0x50,0xd2,0x41,0xe9, + // 0x500: + 0x06,0x44,0x45,0x10, 0x19,0x10,0x20,0x40, 0x79,0xbc,0xad,0x03, 0xeb,0xe5,0x12,0xe2, + 0x50,0x54,0x28,0xa3, 0xf0,0x52,0xa5,0x4a, 0xa5,0xaa,0xaa,0x65, 0x99,0x22,0x50,0x94, + 0x24,0x05,0xc2,0x4b, 0xe6,0x81,0xf3,0xc6, 0x41,0x26,0xda,0x28, 0xdb,0x32,0x24,0xab, + 0x13,0x3a,0xbe,0x48, 0x3d,0x0f,0x2a,0x68, 0x0e,0xcd,0x78,0xdc, 0x48,0xd2,0x67,0x29, + 0x9f,0x1d,0x00,0x69, 0xc4,0x91,0xcb,0x17, 0x93,0x81,0x71,0x5b, 0x95,0xb8,0xaf,0x0b, + 0xf0,0xe2,0x11,0x12, 0x91,0x88,0x5e,0x04, 0xc2,0x2e,0xe9,0xdc, 0xa7,0x3d,0xdb,0x01, + 0xa2,0x2e,0xab,0x97, 0x1c,0xc8,0x36,0x28, 0x49,0xf2,0xf5,0xfb, 0x81,0x8a,0x4e,0x87, + 0x4b,0xe0,0x83,0xbe, 0x90,0x02,0xa6,0x0a, 0x60,0xf5,0x1b,0x36, 0xba,0x31,0xa1,0xf7, + 0x89,0xa9,0x31,0xb3, 0x1c,0xc9,0xaf,0xc8, 0x63,0x3d,0x4e,0x12, 0xba,0x8b,0x49,0x2a, + 0x50,0x7b,0xe6,0x20, 0xc1,0x23,0x01,0x03, 0x18,0xd9,0x72,0xe3, 0x5f,0xf6,0x21,0xb3, + 0x60,0x4a,0x91,0xb8, 0x20,0x43,0xeb,0xeb, 0x41,0xf3,0xae,0xee, 0x6d,0x37,0x2a,0x6b, + 0x06,0x76,0xcc,0x58, 0x23,0x08,0x98,0xc7, 0x2b,0xb7,0x30,0x72, 0xb0,0xbc,0x05,0xd2, + 0x07,0x23,0x15,0x60, 0x1b,0x5b,0x8b,0x8d, 0xb7,0x15,0xd8,0x0d, 0x94,0x07,0x9b,0x61, + 0xba,0xd4,0xa8,0x3f, 0xad,0xcc,0xae,0x26, 0x0d,0xb5,0x91,0x4a, 0x6d,0x98,0xfa,0xee, + 0x6f,0xff,0xb0,0x15, 0x50,0x64,0x79,0x05, 0xba,0x0e,0x8a,0xbf, 0x8d,0x81,0x63,0x58, + 0xc0,0x3e,0xe8,0x52, 0xb1,0xc9,0x78,0xb0, 0x5c,0x91,0x2d,0x90, 0x69,0x0d,0x67,0xb3, + // 0x600: + 0xd1,0x04,0x4e,0x1e, 0x09,0xf5,0xa0,0xb9, 0x70,0xa8,0x48,0x6e, 0x19,0x97,0x29,0xf1, + 0x9d,0x80,0xb0,0x01, 0x1e,0xf5,0x40,0xa4, 0x4c,0xf5,0xc1,0x9d, 0x84,0x6e,0x6c,0x77, + 0x5e,0x11,0xbb,0xae, 0x81,0x12,0x2d,0x6c, 0xc8,0x10,0xa9,0x7b, 0x38,0x18,0x5c,0x0b, + 0xe0,0x7d,0x89,0xd8, 0x84,0xd0,0x32,0x43, 0x39,0x30,0x9c,0xe9, 0xaa,0x0e,0xc1,0x5c, + 0xe2,0x23,0xcd,0x0e, 0x83,0xa5,0x3e,0x28, 0x99,0xd9,0x8a,0x3d, 0xad,0x81,0x05,0x65, + 0x3d,0x86,0xb4,0xa9, 0x92,0x20,0x2c,0x0e, 0xb8,0xd5,0x04,0xec, 0x73,0x0a,0x61,0x0c, + 0x4d,0x66,0x76,0x06, 0xe2,0x0a,0x11,0xe1, 0x86,0xb2,0x3a,0xb0, 0xeb,0x29,0xa4,0x7b, + 0x14,0x0d,0x30,0x6f, 0xa1,0xd5,0x37,0x1e, 0xc3,0x51,0x1a,0xfa, 0x82,0x76,0xd5,0x8e, + 0x78,0xae,0xd8,0x5c, 0x24,0x58,0x15,0xe7, 0x1a,0xc6,0x02,0xc2, 0x5d,0x70,0x25,0xcf, + 0x29,0x77,0xbc,0x1c, 0x8a,0x4b,0x40,0xa9, 0xb0,0x46,0xd5,0x9a, 0x2b,0x4c,0x0a,0x2c, + 0xea,0xc9,0xca,0x17, 0x56,0xe5,0x61,0x1d, 0x32,0xf1,0xd4,0x7f, 0xc3,0xc1,0x25,0x6e, + 0x6c,0xd8,0x86,0x5c, 0xac,0x80,0x86,0x2d, 0x02,0xe1,0xf1,0xf0, 0x94,0x6c,0x1e,0x13, + 0x81,0x2e,0x76,0xf8, 0x80,0xc6,0x64,0x64, 0x80,0x78,0xc6,0x4c, 0xd2,0x96,0x14,0x82, + 0x25,0xf7,0xf8,0x4d, 0x15,0x83,0xcc,0x2c, 0x67,0x28,0x46,0xa9, 0x06,0x4e,0x12,0x45, + 0x19,0xf6,0x18,0x64, 0x68,0x09,0xa0,0x64, 0x27,0x8d,0x5d,0x8c, 0x43,0x06,0x44,0x42, + 0x76,0x10,0xf2,0x32, 0x70,0x19,0x68,0xb4, 0x55,0xc3,0x6d,0xb4, 0x04,0x6b,0x0d,0x7b, + // 0x700: + 0x1b,0x4c,0x86,0x42, 0x71,0x10,0xa2,0x22, 0x21,0xe7,0x7f,0xbd, 0x8e,0x0b,0xac,0x33, + 0x23,0x30,0x9b,0xb1, 0xed,0x05,0xe3,0x7c, 0xa9,0xf9,0xff,0x36, 0x2c,0x4f,0xfb,0xf7, + 0x6b,0xad,0x0a,0xd1, 0xab,0x56,0x17,0x8e, 0x30,0x94,0xc8,0xea, 0x62,0x77,0xb6,0xf8, + 0x9f,0x57,0x68,0x37, 0x98,0x31,0x8b,0xc9, 0xde,0x58,0x11,0x0b, 0xcd,0x5e,0xad,0x3f, + 0x69,0x58,0xa0,0x89, 0xa3,0x2b,0x2f,0x46, 0x83,0x26,0x2c,0x71, 0xca,0x57,0xc0,0xc8, + 0xd6,0xa5,0xb4,0x1f, 0xab,0xee,0x3c,0x9f, 0xba,0x14,0x67,0xed, 0x52,0x4b,0xb0,0xd1, + 0xa8,0x4b,0xd1,0x88, 0xe1,0xe4,0x5d,0x24, 0xa4,0x66,0x2e,0xa9, 0xfe,0xe8,0xa2,0xa0, + 0x4f,0x91,0xe6,0xab, 0x74,0x25,0x61,0x8e, 0xc5,0x85,0x0e,0x9c, 0xdc,0x8a,0x1d,0x38, + 0x6e,0xd6,0x8f,0xa8, 0xa7,0xaa,0xea,0x9b, 0x8c,0x76,0x87,0x6e, 0x56,0x28,0xa2,0xc8, + 0x95,0xbb,0x42,0x5c, 0xd9,0xcb,0x6b,0x55, 0xf4,0x82,0x56,0xf1, 0xed,0x04,0x16,0x6e, + 0xb2,0x53,0x01,0x3e, 0x62,0x26,0xcc,0x9c, 0x97,0x9c,0xe1,0x3c, 0x79,0x42,0x44,0xa5, + 0x31,0x34,0x9b,0x1c, 0x95,0x24,0xc1,0xa7, 0x81,0x42,0x7d,0xb5, 0x62,0x39,0x61,0x70, + 0xc7,0x74,0x7c,0x13, 0xf2,0xf4,0xfc,0xe4, 0x44,0x60,0x68,0x18, 0xe4,0xe4,0x2b,0x9a, + 0x5f,0xbc,0x65,0x41, 0xfb,0x72,0xc2,0x92, 0xac,0x99,0x45,0x09, 0xda,0xc2,0x36,0x35, + 0x4b,0x75,0x25,0x8e, 0xe4,0x66,0x92,0xfc, 0xcb,0x70,0xcf,0x7a, 0xe0,0x33,0x37,0xdb, + 0x28,0xb9,0x10,0x9e, 0xff,0x3e,0x65,0xe2, 0xb0,0xf6,0x35,0x11, 0x17,0x8f,0xb5,0xd9, + // 0x800: + 0xc1,0x9d,0xa5,0xcc, 0x55,0x73,0xbb,0x35, 0xf6,0x87,0xad,0x3d, 0x0b,0x2b,0x7c,0xc8, + 0xed,0xf2,0x14,0x2a, 0x18,0x8f,0x8a,0xca, 0x07,0xa9,0x1b,0x7c, 0x7a,0x1a,0x99,0x13, + 0xfe,0xbd,0xf4,0xc0, 0x7c,0x6e,0x5f,0x4f, 0x8a,0x62,0x9c,0x51, 0x8a,0x30,0x9d,0x13, + 0x3f,0x54,0x0c,0x64, 0x12,0x96,0x56,0xe6, 0x3c,0x39,0xa5,0x4b, 0x72,0x69,0x96,0x40, + 0xb9,0x7c,0x12,0xa1, 0xb5,0xd2,0xb2,0x7a, 0x49,0x56,0x82,0x4d, 0x34,0xeb,0xae,0xa1, + 0x1b,0xc9,0xc3,0xbf, 0xc1,0xc7,0xc4,0x25, 0x6d,0x5f,0xdc,0x35, 0x70,0x7c,0x74,0x7b, + 0xec,0xf7,0x49,0xbb, 0xc4,0x8a,0xc1,0x3c, 0x42,0xf2,0x26,0x8e, 0x6e,0xa2,0x8c,0x30, + 0x95,0xe1,0xb2,0xd1, 0x1f,0xcd,0x46,0xe0, 0xb2,0xa2,0x7e,0x96, 0x06,0xff,0x06,0xa5, + 0x27,0x08,0x5b,0x0d, 0xad,0x66,0xe2,0x6c, 0x84,0x76,0x17,0x91, 0x4f,0x72,0xd6,0xe0, + 0x9b,0x2e,0xad,0xdd, 0x0c,0x11,0xfd,0x4b, 0xee,0xe2,0xf6,0x2a, 0x54,0x10,0xd5,0x0d, + 0xfc,0x02,0x39,0x24, 0x86,0xa3,0x2a,0x93, 0x1a,0x28,0xd7,0xf5, 0xc1,0x10,0xc9,0x47, + 0x47,0x22,0x8f,0xe9, 0x10,0x50,0x98,0x68, 0xda,0x14,0x10,0x43, 0x5a,0x98,0x45,0x45, + 0xd4,0x2f,0xf9,0x21, 0x58,0x01,0xb1,0x9f, 0x55,0x53,0xa2,0xf8, 0xf1,0xe6,0x1b,0x6d, + 0xcc,0x41,0xe6,0x86, 0x31,0x8c,0xea,0x48, 0xda,0xb7,0xbc,0x85, 0xbd,0xd5,0x46,0x3a, + 0x21,0xf0,0x4d,0x7a, 0x8d,0x14,0xc8,0xaa, 0xdc,0x3b,0xd8,0x20, 0xfd,0xcb,0x4a,0x81, + 0xc1,0x6e,0xc4,0xde, 0x2b,0x54,0x74,0xb4, 0x23,0xc6,0x62,0xf0, 0x31,0x8a,0x7a,0x45, + // 0x900: + 0x6e,0xed,0x18,0x3a, 0x34,0x12,0x38,0x49, 0x3c,0xd6,0x3b,0xec, 0x3d,0xb5,0x51,0x7f, + 0xda,0x78,0x4a,0x21, 0x5c,0x5c,0x21,0x73, 0xdf,0x0a,0x57,0x46, 0x2a,0xb6,0xea,0x86, + 0x9c,0x5b,0x79,0xc8, 0x17,0xb0,0xba,0xce, 0xee,0x86,0x75,0x58, 0xaf,0x74,0xba,0x1c, + 0x03,0xef,0x4a,0x17, 0x6d,0x1a,0x18,0x2e, 0x97,0x46,0x03,0x14, 0x7d,0x17,0x06,0x1e, + 0x68,0x31,0x4d,0x4e, 0x0d,0x00,0x75,0x7a, 0xbc,0x2b,0x44,0x01, 0xc2,0xf7,0xcd,0xaf, + 0x08,0x67,0x5f,0x04, 0x72,0x79,0x4c,0xae, 0xad,0x2d,0x8f,0x9c, 0xc8,0x22,0xc9,0x53, + 0xb2,0x31,0x9d,0xa1, 0x57,0x41,0x76,0x11, 0x24,0x21,0x71,0x4c, 0x44,0x7d,0x92,0xce, + 0x92,0xa1,0xf0,0x0a, 0x2b,0xb4,0x7c,0xda, 0x8a,0x39,0xfe,0xfb, 0xcf,0xdc,0xaa,0xbb, + 0x18,0xf7,0x67,0x49, 0x18,0x06,0xad,0xab, 0xa4,0xda,0xc1,0xa2, 0xfb,0x4c,0x4d,0x19, + 0x51,0x28,0x30,0x70, 0xd3,0x10,0x06,0x69, 0xf4,0x45,0xff,0x0b, 0xeb,0x02,0x60,0xa2, + 0x7d,0xe6,0x2e,0x1e, 0x19,0x45,0x5f,0x07, 0xed,0x79,0x93,0x77, 0x86,0x3b,0xa9,0xb5, + 0x67,0xd8,0x6a,0x6f, 0xef,0x95,0xf0,0xd5, 0xf9,0xca,0x33,0x84, 0x84,0x06,0xcb,0xaf, + 0x12,0x3a,0x9a,0xf4, 0x10,0x2b,0x1e,0x51, 0xd0,0x50,0x12,0xaa, 0xe9,0x74,0xf9,0x6c, + 0xcb,0x00,0xf5,0x22, 0x05,0x94,0xdb,0x75, 0xe7,0x99,0x8b,0x95, 0x54,0x55,0x55,0xac, + 0x56,0x03,0x29,0x8e, 0x29,0xc3,0xee,0x4e, 0x86,0x5e,0xe4,0xfb, 0xb7,0x3b,0x30,0x91, + 0x1d,0xbd,0xb9,0x43, 0x8b,0xb4,0x85,0xf8, 0xc6,0x0a,0xe4,0xaf, 0x8c,0xa5,0x2d,0x86, + // 0xa00: + 0xb2,0x80,0x4f,0x02, 0x37,0x6b,0x5d,0xd8, 0x73,0xd0,0xa8,0xb2, 0xac,0x04,0x28,0x8d, + 0x27,0x01,0xd1,0xb2, 0x3c,0xa8,0x0d,0x5d, 0x13,0x52,0xfb,0x4a, 0x3c,0x25,0xee,0xf1, + 0x73,0x1e,0xa2,0xbf, 0xce,0x2f,0x70,0x39, 0x96,0xce,0x62,0x6b, 0x28,0x65,0x2d,0xf0, + 0xfe,0x27,0x8d,0xa1, 0x10,0xdf,0x43,0x7a, 0x14,0x28,0x67,0x28, 0xac,0xde,0xa8,0xf2, + 0xdb,0x2d,0x3d,0xf5, 0x8d,0x23,0x8c,0xf0, 0x3d,0x77,0x0e,0x38, 0x03,0xb7,0x7d,0xbc, + 0xe9,0x77,0xb6,0xb0, 0xf9,0xc3,0x34,0x0f, 0x25,0x96,0x09,0x16, 0xb4,0xcd,0x54,0x50, + 0x48,0xb9,0x25,0x14, 0x40,0x33,0x10,0x50, 0xd8,0x60,0x20,0x22, 0x26,0xc9,0xa4,0x6d, + 0x48,0xa1,0x87,0x8b, 0xf4,0x94,0x09,0x5e, 0x3a,0xb2,0x70,0x10, 0x7a,0xb6,0x96,0xeb, + 0x98,0xef,0x73,0x1e, 0x79,0xd4,0xc7,0x43, 0x0c,0x57,0xb3,0xa4, 0xcd,0xb7,0x12,0x2e, + 0x7a,0x42,0x02,0xcd, 0x8a,0xb6,0x5b,0x6d, 0x22,0xbe,0x8c,0x2b, 0xce,0xe0,0x5e,0xae, + 0x05,0xbc,0x61,0xa4, 0x9e,0x62,0xba,0xac, 0xeb,0x06,0x5d,0xb5, 0xc2,0xd3,0xe1,0xc8, + 0x19,0x49,0x57,0x32, 0xd2,0x48,0x97,0x8e, 0xa1,0x44,0x40,0xb5, 0x26,0x83,0x4a,0x14, + 0x8c,0xeb,0xf9,0x15, 0x9c,0x4a,0x1c,0xcd, 0xe1,0x0a,0x90,0xb3, 0x67,0xd9,0x6f,0x34, + 0x40,0xd6,0xea,0xd9, 0xd5,0x44,0xb6,0x31, 0xd8,0x8d,0xe9,0xb2, 0xd7,0x65,0xc3,0xd4, + 0x3d,0x0b,0xf2,0xe6, 0xab,0x66,0x4e,0x82, 0xa3,0xf8,0x8a,0x4e, 0x0f,0x07,0xeb,0xf8, + 0xb6,0xd9,0x16,0x79, 0x62,0xf1,0x8a,0x44, 0x42,0x26,0x19,0x47, 0x40,0x7e,0x1d,0x66, + // 0xb00: + 0xb3,0x0a,0x35,0x35, 0x5d,0x47,0x63,0xe7, 0xbe,0xc3,0x7a,0xbb, 0x83,0x3e,0x59,0xc8, + 0x7a,0x00,0xf5,0xba, 0x1e,0x5e,0x9f,0xbe, 0xcb,0xb8,0xda,0xf8, 0x0d,0x3d,0xb7,0x9f, + 0x65,0x4c,0x95,0x97, 0x80,0x19,0x56,0x54, 0x7a,0x0e,0x74,0xf7, 0x77,0x5b,0x2e,0x54, + 0x62,0x72,0x74,0xed, 0xa9,0x21,0x5c,0x23, 0x76,0x35,0x9d,0x1d, 0xae,0x04,0xdb,0x57, + 0x2c,0x2d,0x75,0xb7, 0xb5,0x79,0x5d,0x17, 0x57,0xdc,0x59,0xae, 0xd5,0x2b,0x75,0x08, + 0xc4,0x6e,0x41,0x8f, 0x39,0xcb,0x4b,0xae, 0xf9,0xd2,0x84,0xe9, 0xf3,0x53,0x8b,0xfd, + 0x86,0xdc,0x9c,0x4d, 0x77,0x51,0x35,0x55, 0x1a,0xdf,0x2c,0x42, 0x03,0x5f,0xe1,0x07, + 0x65,0xd4,0xed,0x16, 0xee,0xc1,0x5c,0x9a, 0x5a,0xb3,0x60,0x52, 0x52,0xdc,0xa5,0x9b, + 0x60,0xfc,0x8a,0x04, 0x1f,0x7c,0x97,0xd3, 0x70,0x70,0x2a,0x51, 0x8e,0x0c,0xd0,0x97, + 0x83,0xb4,0x58,0x2f, 0x2d,0x72,0xa9,0x7b, 0x54,0xa4,0x5c,0x84, 0x43,0x8e,0x08,0xe3, + 0x40,0x0a,0x6e,0x94, 0x0a,0x69,0x1a,0x87, 0xe7,0xe2,0xf0,0xca, 0xdd,0x09,0xc9,0xd7, + 0x08,0xe2,0x41,0x54, 0xcf,0x91,0x20,0x42, 0xc2,0x82,0x6c,0xb0, 0x10,0x81,0x68,0xc5, + 0x17,0x4d,0x24,0xc0, 0x18,0x4d,0x25,0x3d, 0xd5,0x93,0xad,0x36, 0x46,0x6b,0x0e,0x23, + 0xfb,0x08,0x64,0x29, 0x2b,0x85,0x4c,0xa1, 0x71,0x0e,0xc0,0x9b, 0x04,0x51,0xbb,0x20, + 0x28,0xd0,0xdd,0x4b, 0x3e,0x9d,0x7b,0x4c, 0xbd,0x2c,0x08,0xd6, 0x55,0xbb,0x9b,0xe7, + 0xbd,0x46,0xda,0x90, 0x19,0xec,0xaf,0x87, 0x32,0xf1,0x1e,0x9e, 0x52,0x6e,0x31,0x6b, + // 0xc00: + 0x2c,0xbf,0x0c,0xb5, 0x87,0x66,0x09,0x60, 0x49,0x00,0x89,0xab, 0xa0,0x87,0x64,0x89, + 0x9c,0xd6,0xde,0x47, 0x1d,0x9d,0x9b,0x96, 0xc8,0x14,0x40,0x7e, 0x44,0xbc,0xb0,0xc3, + 0x8c,0x35,0x8a,0xef, 0xaf,0x09,0x2f,0x7f, 0x69,0xf3,0xb3,0x80, 0x45,0x9e,0xa8,0x5b, + 0x78,0xf7,0xbc,0x7b, 0xcd,0x9a,0xdd,0xb8, 0xa3,0x0b,0xdc,0xa2, 0xb9,0xb2,0xfb,0x07, + 0x15,0xf1,0x43,0x16, 0x3f,0x8d,0xa9,0x07, 0x9b,0x19,0xaf,0xe8, 0x4b,0x46,0xb3,0x68, + 0xf3,0x1f,0xe9,0x91, 0x9a,0x84,0x6d,0xf3, 0x4d,0xe8,0x3e,0x48, 0xfa,0xd1,0xed,0xf6, + 0xf8,0xde,0xcd,0x5e, 0xd3,0xfc,0xea,0xb8, 0x9a,0x4e,0x88,0xb1, 0xdf,0xd6,0x51,0xc2, + 0x5f,0x20,0xcb,0x16, 0x92,0xf8,0x7e,0x73, 0x1a,0x4a,0x4b,0x0f, 0xc4,0x6a,0xd8,0xed, + 0xe2,0xcd,0xcf,0x73, 0x19,0xe9,0x44,0x93, 0x37,0x3b,0xa8,0xeb, 0xb3,0x23,0x02,0x7d, + 0x89,0x30,0xcb,0xcf, 0x26,0x71,0xf7,0x07, 0x4e,0xc7,0xa1,0x81, 0xb1,0xca,0x0c,0x53, + 0x31,0xc6,0xcc,0xa1, 0x24,0x57,0x11,0x7f, 0x99,0x72,0x8d,0x50, 0x76,0x76,0x4e,0x68, + 0xa9,0x05,0x3d,0xe7, 0x1e,0xb6,0xa8,0xd5, 0xba,0xee,0x2e,0xda, 0x98,0x0d,0x62,0x45, + 0x8e,0x52,0xc2,0x27, 0x41,0x07,0xa3,0x3b, 0x8f,0x4e,0x71,0xb0, 0x89,0x35,0x3c,0x5d, + 0x72,0x1c,0x23,0xa4, 0x63,0xb1,0xf5,0x73, 0x26,0x6e,0x24,0x93, 0xb7,0xe2,0xd5,0x97, + 0x0d,0x78,0x79,0x1a, 0x20,0x6a,0x65,0xc2, 0xd0,0x59,0x0a,0x05, 0xa2,0xfc,0xfe,0x68, + 0x81,0x27,0xa4,0x55, 0x6f,0xa3,0xe8,0xcc, 0xc6,0xab,0xf9,0x54, 0xe5,0x46,0x3d,0x34, + // 0xd00: + 0x14,0xa2,0xb4,0x28, 0x5d,0x2e,0x62,0xb2, 0x00,0xf1,0x48,0x22, 0xc9,0x91,0xdf,0xdc, + 0x7c,0xf2,0x65,0x51, 0x92,0x01,0x0d,0xae, 0x48,0x9b,0xc2,0xb4, 0x84,0x74,0xa2,0x87, + 0x7e,0xe2,0x1b,0x86, 0xfe,0x62,0x74,0x0a, 0x08,0x65,0x30,0x2d, 0xd8,0xc2,0x59,0x1d, + 0x34,0x68,0x63,0x98, 0x24,0x7a,0x56,0xc4, 0xc4,0xef,0x20,0x75, 0x87,0x47,0x16,0x58, + 0x51,0x53,0xdb,0x45, 0x02,0x46,0xec,0x32, 0x4f,0xc9,0x99,0x96, 0xf1,0xc8,0xed,0xd7, + 0x6f,0x43,0x02,0xcc, 0x3c,0x5f,0x85,0x8a, 0x3c,0x84,0xea,0xdb, 0x4d,0xeb,0xc0,0xeb, + 0x2e,0x79,0x46,0xb9, 0x77,0x08,0x4b,0xe3, 0x6a,0xb1,0xc6,0xb4, 0x21,0x90,0xc7,0x0e, + 0x3e,0x9f,0x4e,0xd7, 0xaa,0x4d,0x15,0x47, 0xba,0x18,0x4e,0x11, 0x34,0x1f,0x67,0x1f, + 0xa4,0xe4,0x7f,0x6e, 0x33,0x5d,0x3d,0xdf, 0x43,0x9b,0x46,0x6e, 0xeb,0x94,0x38,0xfa, + 0xcf,0x0c,0xd7,0x66, 0x28,0xd0,0x77,0xff, 0x85,0x8a,0x09,0x1e, 0x30,0x84,0x47,0x93, + 0x84,0x52,0x54,0x31, 0x04,0x40,0x5d,0xa7, 0x53,0xb4,0x1c,0x27, 0x27,0x06,0xb2,0x16, + 0x1a,0x2b,0xfe,0xb2, 0x31,0xc4,0x11,0x6a, 0xba,0xb4,0x21,0x90, 0x1d,0x3c,0x72,0x77, + 0x7e,0x0d,0xc2,0x88, 0xc1,0x46,0x0f,0x09, 0x21,0x93,0x33,0x2e, 0x11,0x43,0x5b,0x28, + 0x85,0xad,0x64,0xd5, 0x2e,0x9a,0x53,0x4c, 0xe0,0xd8,0xa1,0x44, 0xc9,0x1a,0x9e,0x8e, + 0x29,0xf6,0x76,0x52, 0xa6,0x79,0x74,0x52, 0x36,0x5e,0x0f,0x81, 0xcd,0x32,0x85,0x2f, + 0x8b,0x2a,0x73,0x97, 0x91,0xd9,0x8a,0xc2, 0x65,0xd7,0x7a,0x5b, 0x6f,0x52,0xcd,0xd4, + // 0xe00: + 0x70,0x5d,0xe1,0xf8, 0x01,0x8d,0x46,0xa8, 0x34,0x6d,0x6e,0x88, 0xca,0x39,0xa0,0x43, + 0xb3,0x56,0x74,0xb9, 0x27,0x3f,0x07,0xbf, 0xcb,0x15,0x76,0x5d, 0x98,0x8c,0x8f,0x4a, + 0x52,0xe1,0x8c,0x96, 0x22,0xba,0xe9,0xed, 0x85,0xe7,0x7e,0xc7, 0xb0,0x37,0x9b,0x4b, + 0x06,0x8e,0x88,0x91, 0x91,0x28,0xf0,0x75, 0x41,0x22,0x30,0xdc, 0x89,0x55,0xec,0xc2, + 0xd0,0x71,0xb5,0x5c, 0x98,0xaf,0x08,0x26, 0x88,0x31,0x28,0x1c, 0xa9,0x41,0x58,0x94, + 0x70,0x2b,0x90,0xa1, 0x6c,0xa1,0x6c,0xfd, 0x7c,0x67,0x0e,0xf0, 0x7e,0xfc,0xc7,0x09, + 0x88,0x82,0x22,0x16, 0x6d,0x5d,0x6e,0x1f, 0x70,0xce,0xb6,0xe9, 0x42,0xb9,0xf4,0x36, + 0x9e,0x74,0xc1,0x51, 0xab,0x64,0x9b,0x64, 0x9b,0x25,0x04,0x60, 0x9a,0x78,0x5a,0x0f, + 0x5a,0x46,0xb1,0xd5, 0x48,0x5d,0x32,0x95, 0x2a,0x48,0x7e,0xbd, 0xcb,0x0f,0xf8,0xc4, + 0x36,0x85,0x8a,0x60, 0x30,0x39,0x29,0xf3, 0xec,0xa8,0xb1,0x57, 0xaf,0x7d,0xd2,0x46, + 0x1f,0x65,0xfd,0xaf, 0x0c,0x79,0x4b,0x61, 0x23,0xf2,0x78,0x35, 0x19,0xd7,0x20,0xcf, + 0xc4,0x40,0xd0,0xeb, 0xfb,0xed,0x92,0xf8, 0x85,0x57,0x38,0x3c, 0xc7,0xf5,0xdc,0x93, + 0x63,0xef,0x5c,0xaa, 0x15,0x44,0x8c,0x21, 0x89,0xee,0xaf,0xc9, 0x06,0x8e,0x0c,0xc8, + 0x80,0xf5,0x02,0x0d, 0xc4,0x56,0x87,0x20, 0x88,0x53,0xe5,0x41, 0xfb,0x45,0x8f,0x01, + 0x8e,0x7f,0x4f,0x24, 0x5c,0x93,0x73,0xd2, 0x50,0x91,0x9b,0xed, 0x0c,0xf9,0xcd,0x29, + 0xc6,0x0b,0xcc,0xc3, 0x54,0xe7,0x69,0x26, 0x15,0x27,0xbf,0x90, 0x99,0x0a,0xa7,0x22, + // 0xf00: + 0x3a,0xe8,0x3c,0xcb, 0xab,0x84,0x1d,0xae, 0xb0,0x0a,0x9a,0x47, 0x7c,0xc0,0x82,0xf9, + 0xfa,0x03,0xfb,0x3b, 0x5c,0x22,0xc5,0x0a, 0x90,0xb3,0x7a,0x41, 0x33,0xbf,0x84,0x95, + 0xc6,0xbc,0x26,0x4f, 0x05,0x18,0xd5,0x44, 0x14,0xb6,0x0c,0x24, 0x44,0x2e,0x64,0x05, + 0xfa,0xe8,0x97,0x02, 0x8d,0xfd,0xfa,0xc1, 0xba,0x52,0xf4,0x39, 0xc8,0x49,0x25,0xab, + 0x24,0x63,0x87,0x13, 0xa2,0xa6,0xbc,0xc2, 0x26,0xb7,0x7a,0x17, 0x63,0x4f,0x88,0x41, + 0x4b,0x48,0xfa,0x52, 0x9e,0x5e,0x2f,0x31, 0x53,0x9c,0xeb,0xb6, 0x98,0x15,0xae,0x20, + 0x9b,0xa9,0x7e,0x12, 0x14,0x8e,0xd9,0x51, 0x50,0x28,0xa3,0xc3, 0x32,0x38,0xa8,0x3f, + 0x88,0xd3,0x59,0x19, 0x1d,0x24,0xc0,0x65, 0xec,0xa5,0x42,0x6b, 0xb7,0x1f,0x9b,0xc7, + 0x85,0x7a,0xea,0xa9, 0xe9,0xb6,0xdf,0xdb, 0x3b,0x1d,0xad,0x29, 0x4d,0x2a,0x9a,0x48, + 0xc4,0x64,0xa0,0xf2, 0xd6,0xb3,0xec,0xdd, 0x58,0xab,0x31,0x66, 0x70,0x84,0xc1,0x19, + 0x08,0x20,0x2a,0x02, 0x01,0x9a,0x21,0xc3, 0xa7,0x8f,0xfe,0xb7, 0x01,0x68,0x06,0x47, + 0x90,0xaa,0x51,0xb0, 0x2d,0x27,0x90,0x2f, 0x08,0x63,0x18,0xc6, 0x35,0xc7,0x43,0x83, + 0x8a,0xb7,0x93,0x25, 0xab,0xc7,0xc0,0xf5, 0x87,0x79,0x01,0xac, 0xd5,0x06,0x12,0xbd, + 0x41,0xdd,0x47,0x33, 0xc6,0x8b,0xe9,0xef, 0xb7,0x61,0x39,0x1d, 0xd4,0x1e,0x8b,0x5c, + 0xb5,0xc4,0xb5,0x18, 0xf6,0x6f,0x57,0x50, 0xe2,0xd4,0xc6,0xf1, 0x6d,0x31,0x4e,0xb6, + 0x28,0x7c,0xcf,0xf1, 0x0c,0x4a,0x3c,0x73, 0x14,0xb1,0x3d,0x3b, 0xe8,0x85,0xc8,0x85, + // 0x1000: + 0xc5,0xf4,0xab,0x85, 0xe9,0x32,0xf3,0x5f, 0x71,0xaf,0x61,0xe4, 0xcd,0x64,0x63,0x72, + 0x5c,0xba,0xd9,0x10, 0xe1,0xca,0x82,0xcd, 0x94,0x39,0x0a,0x19, 0x0b,0x9c,0x64,0x99, + 0x87,0x99,0x14,0xa5, 0x8e,0x2a,0x25,0x39, 0x81,0xa2,0xb8,0xf2, 0xeb,0xf9,0xcf,0xdc, + 0x99,0x53,0x58,0x74, 0xbc,0x87,0xe9,0xee, 0xf8,0xcd,0xd5,0x05, 0x05,0x07,0xf1,0xb5, + 0x7f,0x6b,0x19,0x02, 0x3a,0x35,0x52,0x0b, 0xe2,0xfe,0x48,0x0a, 0x74,0xef,0xd6,0xdd, + 0x45,0x5d,0x52,0xb5, 0x4c,0x22,0xab,0x1b, 0xf5,0xf9,0x47,0xa2, 0xf1,0x32,0x0b,0xb5, + 0x74,0xf2,0xc3,0x46, 0x8a,0x6a,0xb3,0xf0, 0x79,0x5a,0x9e,0xd7, 0x83,0x98,0xf3,0x2f, + 0x26,0x8c,0x91,0xf2, 0x4c,0xc4,0x51,0x83, 0x9e,0x06,0x0e,0x8d, 0xd1,0x03,0xd4,0x15, + 0x12,0x02,0x91,0x1d, 0xf6,0xe1,0x81,0xb6, 0xb8,0xde,0x89,0x8b, 0x33,0x8d,0xd9,0xe0, + 0x41,0x4b,0x6b,0xd8, 0x48,0x3c,0xfe,0x39, 0xdb,0xec,0x8a,0x54, 0x17,0xc3,0x63,0xe8, + 0x4d,0xb0,0xaa,0x43, 0x5c,0x33,0xea,0x41, 0x51,0x59,0x8e,0xa9, 0x54,0xe7,0x6e,0x99, + 0x6c,0x5d,0xbf,0x3d, 0x61,0x6c,0x31,0x9c, 0xb9,0x61,0x30,0x40, 0x41,0x2b,0xb6,0x9a, + 0x7a,0xb9,0x0e,0x3e, 0x59,0x6c,0xfe,0x30, 0x6b,0x8d,0xae,0x50, 0x64,0xbf,0x7e,0x68, + 0x12,0x62,0xc0,0x04, 0xe5,0xdc,0x46,0x00, 0xce,0x28,0x98,0x00, 0x43,0xb6,0x96,0xa4, + 0xaa,0x86,0x9e,0xa2, 0x85,0xd4,0xf3,0xf9, 0x89,0x66,0x5c,0xcd, 0xd1,0x24,0xe1,0x38, + 0x91,0x64,0x81,0xba, 0xb2,0xc0,0x33,0x7a, 0x5d,0xd6,0xa9,0xa4, 0x15,0x9b,0xdc,0x9f, + // 0x1100: + 0x53,0x4c,0x10,0x61, 0x48,0x4e,0x60,0x64, 0x3f,0x4d,0x40,0xc5, 0xcb,0x54,0xf1,0xc3, + 0x39,0x18,0xe0,0x54, 0x98,0x06,0xcb,0x18, 0xa3,0xdb,0x89,0x14, 0x2a,0x3a,0x0e,0xda, + 0xd3,0xc4,0x2c,0x53, 0x00,0xd6,0x3c,0x98, 0x50,0xe2,0x88,0xcd, 0x06,0x41,0xb5,0xdd, + 0x67,0xa5,0x39,0x1b, 0x67,0xec,0x3b,0xd7, 0xcc,0xe8,0x09,0x19, 0x86,0x34,0xc3,0xf8, + 0xf1,0x20,0x3b,0x35, 0x0d,0x8c,0x16,0x8d, 0x73,0x24,0x11,0x14, 0x94,0xe7,0xd5,0x75, + 0x5c,0x4e,0x26,0x22, 0x2c,0x9a,0xb0,0xf1, 0x6a,0x36,0x19,0x2f, 0x94,0x31,0x56,0x91, + 0x31,0xb7,0xe3,0xad, 0x7f,0x59,0x1b,0x1e, 0xd5,0x99,0x92,0x5a, 0x0f,0x06,0x19,0x8c, + 0xa0,0xed,0x24,0xa8, 0xab,0xb9,0x82,0x8c, 0x43,0xa1,0x9e,0xaa, 0xd4,0x6b,0x4f,0xe4, + 0x68,0x94,0xf0,0x37, 0x39,0x23,0xa7,0x9e, 0x52,0xff,0x9d,0x89, 0x49,0x0a,0x22,0x01, + 0x1d,0x82,0xc0,0x65, 0x99,0x64,0xa1,0xc8, 0x0a,0xfb,0xba,0xad, 0x72,0xc9,0x45,0x9a, + 0xed,0x7f,0x26,0x4a, 0x01,0xe2,0x95,0x1e, 0x44,0xac,0x2b,0xd3, 0x58,0x03,0x29,0x55, + 0x06,0x7c,0x36,0xbe, 0x2a,0xeb,0xed,0x55, 0x56,0x38,0xe3,0x6b, 0x2f,0xa0,0x3a,0xb5, + 0xd2,0xff,0x91,0x95, 0x6b,0x9f,0xaf,0xf7, 0x34,0x54,0xc0,0xb1, 0x72,0x12,0x5d,0x90, + 0x94,0xb7,0x80,0x19, 0x58,0xae,0xbd,0x93, 0xcf,0x04,0x43,0xf6, 0x26,0xca,0x3a,0x37, + 0xde,0x68,0x69,0xdd, 0x62,0x4a,0x92,0xac, 0xba,0x87,0x5c,0x69, 0x26,0x6f,0xca,0xf6, + 0x20,0x0a,0x25,0xd8, 0xa1,0xd0,0x84,0x0f, 0x70,0x75,0xd4,0x80, 0x89,0x78,0x44,0x22, + // 0x1200: + 0x1e,0xbc,0xdd,0x05, 0x4b,0xb3,0x24,0x7b, 0xd3,0x27,0x43,0xb0, 0xbf,0xaa,0x8c,0xc2, + 0x8d,0x3c,0x1a,0x2c, 0x94,0x65,0x27,0xcb, 0x28,0x3a,0x67,0x03, 0x80,0xbf,0x56,0xc7, + 0x8d,0xee,0x7c,0x42, 0x7d,0x64,0x7f,0x0b, 0x23,0x18,0xfb,0x6a, 0xc3,0xdf,0xd7,0xe2, + 0x8c,0xd1,0x86,0x4e, 0xdd,0xd2,0x69,0xa1, 0x9a,0x24,0xcb,0x84, 0x0b,0x58,0x6f,0xa5, + 0xea,0xbe,0x99,0x7d, 0xdf,0x1e,0xe7,0x2a, 0xa1,0x68,0x6e,0x75, 0x1e,0x0e,0x94,0xe6, + 0x6f,0x72,0x0d,0x06, 0x60,0x3f,0x4f,0x0b, 0x08,0x7a,0xa7,0x37, 0x40,0x15,0xf5,0x0b, + 0xa0,0xed,0x0a,0x52, 0xaf,0xe5,0xe3,0x0f, 0xca,0x3a,0x76,0x51, 0x4a,0x72,0xfd,0x7d, + 0xa5,0xd0,0xb7,0xa0, 0x89,0x15,0x8a,0x94, 0xe4,0x64,0x19,0xa8, 0xcd,0x14,0xd2,0xd6, + 0xcc,0xfd,0x32,0x63, 0x93,0x5d,0xb1,0xe9, 0x5e,0xd1,0x1f,0x7d, 0x49,0xbf,0x9c,0x7c, + 0x61,0x2c,0x2e,0x7e, 0x5a,0x1c,0xd4,0x5d, 0x2d,0x57,0x44,0xc0, 0xe9,0x49,0x4f,0x5a, + 0x5f,0x02,0xa3,0xdf, 0xd5,0x9f,0xb6,0xdb, 0xe1,0x00,0xe6,0xd6, 0xf1,0xc4,0x97,0x13, + 0x12,0x19,0x25,0x1e, 0xbf,0x5b,0x35,0x24, 0x62,0xc9,0x6f,0x26, 0xb7,0x9e,0x26,0xdc, + 0xdb,0xe9,0x7a,0xcb, 0x21,0x80,0x3b,0x84, 0x06,0xeb,0xed,0x3f, 0x34,0xaa,0x4d,0x56, + 0x86,0x33,0x92,0x5d, 0xda,0xc3,0x1d,0x6f, 0xbd,0x9a,0x75,0x37, 0x7f,0x08,0x13,0xdf, + 0x55,0xd9,0xdb,0xf8, 0x60,0x16,0x34,0x56, 0xa8,0x72,0x46,0x55, 0x48,0x48,0xc9,0x32, + 0x5c,0x5f,0x97,0x38, 0x6e,0x3b,0x30,0x3b, 0x47,0xae,0xa5,0x07, 0x44,0xf4,0x79,0x08, + // 0x1300: + 0x96,0xda,0x27,0x37, 0xd3,0xd8,0x6d,0xc4, 0xca,0x15,0x4c,0x74, 0x9d,0xda,0x20,0x4b, + 0x63,0x67,0x1d,0x28, 0x7c,0x7d,0x0e,0xab, 0x61,0x39,0xcd,0x3e, 0xaf,0x8f,0x81,0x96, + 0xe4,0xab,0xda,0xdc, 0x60,0xeb,0xab,0x2f, 0xe3,0xab,0x5b,0x0d, 0xb7,0x64,0xf0,0x90, + 0x16,0x5d,0x38,0x72, 0x8a,0x4b,0xac,0x6d, 0x13,0xe5,0x25,0xc0, 0x4c,0x5f,0x4a,0xcd, + 0x4c,0xb0,0x28,0xf0, 0x90,0xa4,0x87,0x92, 0x65,0xc1,0x14,0x87, 0x6d,0x49,0x38,0x51, + 0x5c,0x4a,0x30,0x67, 0xe9,0x19,0x29,0x5e, 0x9f,0x23,0x2d,0xe6, 0x10,0xd9,0x5e,0x63, + 0x43,0x50,0xd7,0x61, 0x07,0x9a,0x89,0xf2, 0x5d,0xc6,0x93,0xe4, 0xf4,0x70,0x64,0x66, + 0x4c,0xdd,0xab,0xd7, 0xd9,0x14,0x48,0x1b, 0x89,0x29,0x45,0x22, 0x3b,0x9e,0x92,0x61, + 0xe3,0x38,0x9a,0x30, 0x49,0x99,0x6b,0xd2, 0xac,0x90,0xf7,0x3e, 0x24,0xfe,0x0d,0x21, + 0x4f,0x9d,0x12,0x3f, 0xa6,0x16,0x54,0xf4, 0xdf,0x2e,0x37,0xc3, 0x3a,0xc9,0xa6,0x57, + 0xe9,0xb1,0x0b,0x44, 0x23,0x6e,0x10,0x80, 0xf7,0xa6,0xc5,0x8a, 0x99,0xed,0x82,0x62, + 0x7b,0x5a,0x7a,0x3a, 0xdd,0xed,0x77,0x96, 0x72,0xd9,0x55,0x55, 0x52,0x8d,0xb1,0x57, + 0xf0,0xa5,0x94,0x20, 0xd9,0x09,0x84,0x7b, 0x69,0x47,0x45,0x28, 0xa1,0xac,0x1b,0x3c, + 0xe3,0x24,0xae,0x41, 0x8a,0x1a,0x54,0x70, 0xc9,0x59,0xa9,0x49, 0x4a,0xa3,0x49,0x86, + 0x64,0x74,0xd3,0x49, 0xdc,0xe7,0xee,0x0f, 0x41,0xed,0x05,0x1d, 0x84,0xf6,0x53,0x45, + 0xe6,0xf0,0x5f,0xf8, 0xd4,0x54,0x85,0xee, 0x58,0xc4,0xe6,0xf7, 0x93,0x73,0x05,0xa8, + // 0x1400: + 0xde,0xa8,0x1b,0xd3, 0xfe,0x2c,0x0f,0x2d, 0x44,0x24,0x18,0x31, 0x9c,0x95,0x1b,0xa6, + 0x5c,0xb6,0x6e,0xef, 0xa9,0xb7,0x28,0x5f, 0xca,0x3b,0xd3,0x10, 0xed,0xf9,0x88,0x83, + 0x48,0xc6,0x95,0x58, 0xe3,0xe8,0xe0,0x2e, 0x41,0x55,0x4c,0x06, 0x11,0x96,0x4e,0x66, + 0x2a,0x6b,0x2a,0x5a, 0x50,0x0e,0x68,0x66, 0x54,0x64,0xd4,0xf7, 0xd9,0xfa,0x8b,0x16, + 0xba,0x14,0x13,0xe3, 0x82,0x29,0xb8,0x53, 0x4d,0xf0,0x24,0x50, 0xde,0xdf,0x67,0xb7, + 0xa4,0xbd,0xb5,0x23, 0x07,0x83,0xcc,0x0c, 0x65,0x95,0x99,0x3b, 0xb2,0xb2,0x97,0xda, + 0xe5,0x93,0x22,0x66, 0x77,0xf3,0xa6,0x93, 0x4b,0x29,0x9b,0xf6, 0x1d,0x44,0x2d,0x50, + 0xf3,0xda,0xa6,0x4d, 0xb3,0x5b,0x54,0x55, 0xda,0x78,0x1e,0xfa, 0x81,0x83,0x40,0xbd, + 0x6b,0xec,0x17,0x3b, 0x3d,0xf1,0xbf,0x76, 0x4b,0x14,0xf6,0x6c, 0x0f,0x10,0xf7,0xaf, + 0xe9,0x7c,0x64,0xff, 0x15,0x88,0x84,0xd0, 0x91,0xfd,0x4b,0xa5, 0xe0,0xa4,0xfe,0xc9, + 0x24,0x35,0xa2,0x67, 0xa9,0x33,0xcc,0xb9, 0x70,0x91,0x14,0x81, 0x07,0xd2,0xfc,0xff, + 0x5e,0x1c,0x80,0xcd, 0xcc,0x9e,0x83,0x4f, 0x51,0x63,0x5e,0xb9, 0xd5,0x55,0xef,0xdb, + 0xa0,0x48,0xf6,0xe9, 0x72,0x74,0x6e,0x46, 0xe5,0x3b,0xed,0x6b, 0x7b,0x68,0x7d,0x0c, + 0x3a,0x82,0xfd,0xdb, 0x2d,0xf9,0x64,0x86, 0xfe,0x4b,0x76,0xe4, 0xb3,0x8b,0x99,0x20, + 0x72,0x6b,0x7e,0x16, 0x11,0x33,0x56,0x29, 0x19,0xb3,0x1c,0x64, 0x20,0xeb,0x30,0x0a, + 0xaa,0xaf,0x55,0x63, 0x38,0xa5,0x77,0x52, 0xb6,0xa3,0x51,0x6f, 0x93,0x43,0xb5,0xe3, + // 0x1500: + 0x56,0x97,0x95,0xfb, 0x06,0x8c,0x63,0x54, 0x88,0x10,0x20,0xa9, 0x6c,0x88,0x6c,0x9f, + 0xb4,0xda,0xc9,0x33, 0x68,0x39,0xce,0xae, 0xb9,0x61,0xa4,0x77, 0x69,0x64,0x06,0x59, + 0xb7,0x6c,0x08,0x2d, 0x34,0x78,0x41,0xf9, 0xeb,0x78,0xc0,0x80, 0x7e,0x48,0xf6,0xc6, + 0xdc,0x04,0x8d,0x34, 0x77,0x01,0x07,0x1c, 0x71,0xc9,0xe9,0xc7, 0x0e,0x65,0x29,0x40, + 0x59,0xd4,0x25,0x10, 0xe9,0xb3,0xab,0xa0, 0x92,0x86,0x5a,0x49, 0x24,0x80,0x64,0x32, + 0xc8,0xe0,0x11,0xae, 0xda,0xb2,0xec,0xfe, 0x44,0x67,0xe9,0x87, 0xb4,0x21,0x65,0x96, + 0x56,0x48,0x64,0x85, 0xa4,0x90,0xad,0x08, 0x15,0xa2,0xd4,0x2c, 0xb0,0x0b,0x4c,0x40, + 0x5a,0xb5,0xb7,0xde, 0x78,0x1c,0xdd,0x56, 0xf5,0x2c,0xde,0x54, 0x6f,0xee,0x12,0x27, + 0xb2,0x84,0x13,0x07, 0xbd,0x82,0x8e,0xd8, 0x8c,0x42,0x43,0x66, 0x4f,0x48,0x9b,0x4b, + 0x74,0xc0,0x32,0x91, 0x4b,0x19,0x8c,0x3c, 0xc5,0xb9,0x25,0xc2, 0xbd,0x7b,0x0a,0x30, + 0xa3,0x1f,0x5f,0xf0, 0x54,0x72,0xa1,0xfb, 0x8e,0x62,0xd8,0xaa, 0x2a,0x0e,0x02,0xbe, + 0xaa,0xd3,0x2a,0x2a, 0x2d,0x56,0x44,0xe8, 0xcc,0x8b,0xfd,0xa2, 0x79,0xab,0x36,0xf5, + 0xd9,0x3e,0x98,0xcf, 0x6b,0x33,0xf6,0x05, 0xce,0x66,0x2c,0x07, 0x64,0x08,0x2e,0x16, + 0x2c,0xeb,0x9a,0x87, 0x95,0x9d,0xb3,0xdc, 0x5e,0xb2,0x04,0xe5, 0xeb,0xcc,0x83,0x28, + 0xc2,0x03,0x76,0xce, 0xcd,0x99,0x06,0xcd, 0xa6,0xd6,0x91,0x50, 0x2e,0x7f,0x23,0x3a, + 0xaa,0xf4,0xc8,0x33, 0xd8,0x05,0x55,0x91, 0x02,0x71,0x66,0xb5, 0x24,0x3a,0x33,0x48, + // 0x1600: + 0xcf,0xe8,0xfa,0x4a, 0xe0,0x83,0x50,0xab, 0x25,0x43,0x20,0x44, 0xfe,0xe0,0xc1,0x44, + 0x22,0x6c,0xcc,0x64, 0x78,0x90,0x7f,0x68, 0x21,0xd5,0xec,0x91, 0x43,0xb5,0xf3,0x0b, + 0x96,0x58,0x12,0xc3, 0x9f,0x14,0x61,0xd1, 0x01,0x40,0x9b,0xcb, 0xc9,0x98,0xc0,0xfe, + 0xeb,0x4c,0xc2,0xea, 0x9b,0x81,0x8d,0x22, 0x03,0x10,0x00,0x9f, 0xbf,0xea,0xbe,0x64, + 0x74,0x8a,0xfd,0xe7, 0x53,0x98,0x95,0x7d, 0x6c,0xb3,0x6e,0x5c, 0xad,0xd2,0x24,0xa7, + 0x9d,0x3f,0x56,0x71, 0x80,0xf1,0x09,0x6e, 0x56,0x9a,0x80,0x87, 0xec,0x7d,0xed,0x02, + 0xce,0x10,0x06,0x64, 0x63,0x2a,0xef,0xd4, 0xb8,0xf9,0x9c,0xe1, 0x3b,0xc8,0xe0,0x2f, + 0x5a,0x26,0x00,0xb6, 0xaf,0x94,0x81,0xd3, 0xfe,0x0b,0xd9,0x0a, 0x4e,0xf5,0xdb,0x47, + 0x64,0x5b,0x41,0x2d, 0xf1,0x43,0x5a,0x3c, 0x12,0xbe,0x20,0x7b, 0x42,0x77,0xc7,0x6e, + 0x18,0xb1,0x34,0xac, 0x05,0x60,0x39,0x8c, 0x2f,0x3d,0x8f,0x89, 0x5d,0x53,0x8b,0xcb, + 0x57,0x7f,0x22,0xa4, 0x3b,0xbb,0xce,0x22, 0xc9,0xfe,0x83,0x59, 0x7b,0xd4,0xde,0x0a, + 0x0b,0x48,0x96,0x37, 0x94,0x9d,0x4b,0xfd, 0x97,0xba,0x0a,0x2a, 0x6c,0xa2,0x9e,0x73, + 0xc5,0x74,0xcb,0xd2, 0xca,0x1b,0x05,0x9d, 0x31,0x29,0x71,0x07, 0xc1,0x60,0xaf,0x46, + 0x29,0x7c,0x30,0x21, 0x29,0x84,0x12,0xfa, 0x2a,0xc1,0xc4,0x61, 0xcd,0x7d,0x44,0x1a, + 0x71,0x1c,0xfe,0x8e, 0x69,0x49,0x25,0xbf, 0xe8,0x63,0xac,0x68, 0x11,0xd7,0xf9,0x3c, + 0xb9,0x05,0xf3,0x1a, 0x20,0x5a,0xcb,0xa7, 0xf3,0x93,0xf4,0x5a, 0xc6,0x3e,0x88,0x88, + // 0x1700: + 0xdb,0x4b,0x2d,0xc5, 0xf3,0x81,0xfc,0x50, 0xa5,0xee,0x4e,0x6e, 0x68,0x65,0xe6,0xca, + 0xfb,0x16,0x9a,0x6f, 0x5f,0x45,0xae,0xeb, 0xe6,0xa1,0x10,0x98, 0x13,0xb2,0xf2,0x5f, + 0xf3,0xaf,0x5c,0x06, 0x41,0x8b,0x22,0xfe, 0xca,0xc8,0x3d,0xcf, 0x33,0x7b,0x3c,0x11, + 0xcd,0xc7,0xe5,0x1a, 0x81,0x8d,0x5b,0x6f, 0x34,0x6b,0x80,0x95, 0xca,0xd4,0x78,0xd0, + 0x26,0x82,0x9b,0xc7, 0x73,0x16,0x75,0xd9, 0x53,0xcc,0x98,0x99, 0xd5,0x35,0xeb,0xae, + 0x75,0x52,0x38,0x9b, 0x47,0xbf,0x4b,0x4d, 0xcc,0x46,0xff,0xbc, 0xce,0x01,0xef,0x4b, + 0x11,0xfe,0x6c,0x5f, 0x7c,0x9c,0x6b,0xfe, 0x37,0x52,0xdb,0xff, 0xba,0xa6,0xcb,0x0e, + 0xd7,0x0f,0xab,0xa3, 0xe8,0x60,0x47,0x82, 0xce,0x7a,0x67,0x27, 0x8f,0xc7,0x7f,0xd2, + 0xe2,0xab,0xe0,0x8c, 0x40,0x0c,0xd7,0xfa, 0x5a,0x97,0x0a,0xfd, 0x3c,0x81,0x96,0xa6, + 0xae,0xea,0xe1,0xac, 0x4d,0x0b,0x0e,0x5b, 0x5f,0x2a,0xf6,0x27, 0x45,0xc7,0xca,0x63, + 0x1b,0x9d,0x90,0xe6, 0xc0,0xbd,0x39,0x4c, 0xfa,0x40,0x17,0x00, 0xac,0xcb,0x18,0x4a, + 0x21,0x06,0xfb,0xf5, 0xf3,0x52,0x12,0x5a, 0xec,0x4f,0x19,0x0b, 0x17,0xfe,0x2b,0x50, + 0xb8,0xb1,0x77,0x6a, 0x58,0xf5,0xd8,0xfc, 0x13,0xab,0xb5,0x69, 0xad,0x72,0xa5,0x52, + 0x79,0xe9,0x0e,0x9d, 0x1b,0x75,0x01,0xbf, 0x95,0x37,0xcb,0x83, 0x54,0x43,0x19,0x83, + 0x13,0x66,0x00,0xd0, 0x95,0x59,0xa3,0x09, 0x10,0x37,0xe6,0x49, 0xdb,0xea,0xf8,0x10, + 0x58,0xdb,0x51,0x2e, 0xc2,0x93,0x0a,0x13, 0x47,0x72,0x79,0x70, 0x65,0x5a,0xfc,0xc2, + // 0x1800: + 0x15,0x58,0xe9,0x02, 0x99,0xb8,0x08,0x9e, 0xa5,0x23,0x15,0x02, 0xeb,0xae,0x2c,0x37, + 0x95,0x66,0xa2,0x1a, 0x97,0x00,0x24,0x82, 0x09,0x3c,0xa3,0x2f, 0x66,0x80,0x2a,0xbe, + 0x21,0xa2,0xe5,0x07, 0x5a,0x2c,0x19,0xb0, 0x4e,0xb9,0x23,0x7d, 0x02,0x37,0x67,0xea, + 0x75,0x15,0x75,0x96, 0x34,0xe9,0xe3,0xe5, 0xd6,0xad,0x65,0xba, 0x87,0xe1,0x78,0x95, + 0xed,0x54,0x3a,0x35, 0xf8,0xf7,0xbf,0xaf, 0x7e,0x4e,0xa1,0xc0, 0x80,0xde,0xc7,0x87, + 0xdb,0xe0,0xde,0xe2, 0xe7,0x76,0x7b,0x30, 0xe9,0x67,0xf9,0x14, 0xf6,0x04,0xa9,0x27, + 0x4e,0x06,0x39,0x4e, 0xb2,0xfe,0x55,0x45, 0x6f,0xb1,0xb7,0xe4, 0xaf,0xce,0x4e,0x39, + 0xaa,0x98,0x9d,0x15, 0x18,0x4b,0x41,0xc4, 0x03,0x97,0xcf,0xc1, 0x7c,0x74,0x56,0x90, + 0x01,0x71,0x41,0xf9, 0x99,0xaa,0x72,0xb3, 0x84,0xb7,0x7a,0x8f, 0x3d,0x5f,0x72,0xc3, + 0xd9,0xc7,0xd7,0x6a, 0xf6,0x69,0xe2,0xab, 0x0e,0x80,0xda,0x27, 0xa8,0xe7,0xec,0x37, + 0x0f,0x70,0xd9,0x3c, 0x8f,0x58,0x31,0x30, 0x5a,0xd9,0x17,0xb4, 0x6b,0x3c,0xa7,0x7c, + 0x4e,0x29,0x78,0x8e, 0x3d,0x69,0x69,0x90, 0xae,0xc4,0xfc,0x0b, 0xde,0x1a,0x62,0x12, + 0x08,0x9b,0xe2,0x16, 0x4f,0x78,0x9b,0x57, 0xbd,0x91,0x0d,0x1c, 0x52,0x89,0xf7,0xc9, + 0xe5,0xfa,0x0e,0x02, 0x00,0xd0,0xe7,0x44, 0xaf,0x2f,0xaf,0xe3, 0x61,0xef,0xa8,0x92, + 0x08,0xf4,0x91,0x2c, 0x0b,0x55,0x4d,0xce, 0xd6,0xe3,0x2a,0xf9, 0x2b,0x85,0x7d,0x07, + 0x30,0xb0,0x96,0xbb, 0xac,0xc3,0x86,0x0d, 0x1c,0x82,0x70,0x9a, 0x65,0x0e,0xc6,0x17, + // 0x1900: + 0x45,0xa4,0x83,0xcf, 0xd6,0xb9,0xcd,0x53, 0xe7,0x82,0x9a,0xa2, 0xaf,0xe9,0x76,0x32, + 0x4b,0x30,0x74,0x7d, 0x3e,0x32,0xf3,0x89, 0x49,0xee,0x70,0x4c, 0x1a,0xac,0x38,0x2c, + 0xbe,0x4d,0x6c,0xd9, 0xe2,0x8f,0x79,0x41, 0xc0,0x2c,0xb0,0x53, 0xe1,0x40,0x95,0x69, + 0x4b,0x98,0xbc,0x46, 0x11,0x89,0xdb,0x64, 0xf7,0x5a,0x13,0x1d, 0xfa,0x27,0x24,0x67, + 0x79,0xba,0xa6,0x35, 0xf5,0x9f,0x11,0x06, 0xca,0x90,0x5a,0x4e, 0xd7,0x96,0x72,0xe5, + 0xe0,0xef,0x02,0x3a, 0xc2,0xba,0xac,0xb1, 0xeb,0xdb,0xd0,0x35, 0xd2,0x42,0xda,0x29, + 0x24,0xfc,0x09,0x13, 0x98,0x81,0x4a,0xd8, 0xf6,0x96,0xbc,0xcb, 0xa4,0xdc,0x6a,0x73, + 0x3c,0x91,0x60,0x89, 0x69,0x06,0xd9,0xc5, 0x92,0x94,0x90,0xd3, 0x54,0xa0,0xa7,0x0e, + 0x29,0xa3,0xf1,0xb6, 0x43,0x75,0xea,0xf8, 0x17,0xe4,0x68,0xcd, 0xa9,0xe7,0xf4,0x28, + 0x51,0x6a,0xb3,0xb0, 0x8d,0x73,0x73,0xc2, 0x56,0x8d,0xba,0xee, 0x8d,0xda,0x8b,0x11, + 0x49,0x43,0x6f,0x23, 0x37,0x3b,0x0a,0x58, 0xc1,0xab,0x62,0x34, 0x72,0x35,0x71,0xe8, + 0xd4,0x7c,0x21,0x0f, 0xd3,0x44,0xee,0xf5, 0xca,0x25,0xa9,0x37, 0x74,0xdc,0x18,0x9c, + 0xe3,0xc1,0x60,0x23, 0xea,0x91,0xb9,0x30, 0xc6,0x21,0x1c,0x81, 0x82,0x7a,0xaa,0xff, + 0xe6,0xef,0xb8,0xa7, 0x87,0x58,0x4f,0x76, 0x8a,0x48,0x66,0xf9, 0xe9,0xbe,0xf2,0xf6, + 0x87,0x48,0xbd,0x22, 0xa3,0xa9,0x82,0x21, 0xee,0xeb,0xc5,0x4c, 0x35,0xd4,0x2a,0xbe, + 0x69,0x7a,0x36,0xf4, 0x1f,0x99,0xb7,0x58, 0xbb,0x2f,0xd7,0xfe, 0x53,0x2f,0xec,0xa8, + // 0x1a00: + 0x13,0xd2,0xde,0xd4, 0xc0,0xba,0x1e,0x69, 0x1c,0xac,0x2d,0x63, 0x54,0x54,0x44,0xad, + 0x66,0x98,0x89,0xca, 0xa5,0x85,0x7c,0x9c, 0x0c,0xf9,0xd8,0x79, 0x64,0x05,0xe6,0x51, + 0x51,0x91,0xd1,0x84, 0xd0,0xf4,0x15,0x48, 0x5c,0x30,0xf9,0x27, 0x2e,0x98,0x5e,0x44, + 0x68,0xb5,0x0e,0x8a, 0x95,0x59,0xf6,0x68, 0xf2,0x2c,0x33,0x0f, 0xb2,0x60,0x5e,0xdf, + 0x5d,0x7e,0xfa,0x5d, 0x70,0xf8,0xa1,0x77, 0x6e,0xb2,0x30,0xe9, 0x1c,0xb7,0x1d,0x77, + 0xf4,0xef,0xd1,0xb2, 0x2e,0xc1,0x87,0x63, 0x3e,0x88,0x4c,0x4c, 0xdc,0xd0,0xb4,0xaa, + 0xee,0xe7,0x35,0x3b, 0x3b,0x24,0x32,0xc4, 0x52,0x2f,0xa3,0x74, 0x93,0x2c,0xa1,0x02, + 0x8d,0x3b,0xca,0x35, 0x3d,0x70,0xe9,0x4c, 0x3e,0xfa,0x96,0x30, 0x0a,0x31,0x38,0xc8, + 0x18,0xec,0x5d,0xc5, 0xca,0x84,0x40,0x56, 0x6c,0xac,0x38,0x2f, 0x31,0x97,0x03,0x36, + 0x9e,0x78,0x62,0xb7, 0xe6,0xf9,0x87,0xc2, 0x5d,0x8c,0x09,0xcf, 0x8a,0xf1,0x20,0x3a, + 0xfc,0x41,0xd0,0x28, 0x30,0x38,0xf4,0x13, 0x09,0x09,0xab,0x7c, 0xa0,0xc9,0xa4,0x29, + 0xef,0x13,0x7d,0xb7, 0x9f,0xf2,0xde,0x74, 0xf1,0x64,0x29,0xc5, 0x60,0xa7,0x5c,0x21, + 0xbd,0xb7,0x5b,0x4f, 0x7b,0xd2,0x88,0x33, 0x54,0x01,0xb2,0x8d, 0x59,0x0b,0x25,0x0e, + 0xb4,0x2a,0x81,0xe9, 0x16,0xeb,0x59,0x70, 0x9f,0xa3,0x1a,0x58, 0x42,0x57,0x68,0x68, + 0x76,0x17,0x4b,0x00, 0xb3,0x28,0x0c,0xc0, 0xbc,0x8c,0x86,0xc5, 0xbc,0xda,0x46,0x2c, + 0xe3,0xe9,0x24,0x71, 0xb4,0xfd,0xc6,0x3a, 0x10,0xa7,0xd9,0x55, 0xd5,0x65,0xbf,0xba, + // 0x1b00: + 0x15,0x88,0x3f,0xb3, 0xde,0x08,0x4a,0x0d, 0xd4,0x85,0xe1,0xfa, 0x3c,0x45,0x8b,0x4f, + 0xe3,0x39,0x8a,0xc0, 0xdb,0x7b,0x2b,0x16, 0x4e,0xda,0xb9,0x22, 0x7e,0x15,0x06,0x9f, + 0xa1,0xcc,0x49,0xac, 0x87,0xa8,0xc3,0x6b, 0xf0,0x5b,0x1d,0x0f, 0xa7,0x7d,0xb4,0xc0, + 0xde,0xe0,0x6f,0x71, 0x98,0xca,0x7d,0xc6, 0xc3,0x75,0x4f,0x53, 0xf1,0xe8,0x58,0x7f, + 0xee,0x3d,0xad,0x99, 0x8f,0x91,0x51,0x63, 0xdf,0x7f,0x3d,0x7e, 0x3d,0xf3,0x9f,0x4c, + 0xce,0x19,0xa6,0x32, 0x56,0xd4,0xdc,0x7b, 0x6b,0xe4,0xa6,0xe2, 0x2d,0xf3,0x3a,0xdf, + 0x3f,0xf2,0x98,0x2e, 0x4a,0xaa,0x5e,0x88, 0xe4,0xb9,0x19,0x7b, 0x68,0xac,0x2d,0x1d, + 0x38,0xb3,0xb6,0xb8, 0x94,0xc7,0x27,0x58, 0xe1,0x22,0x57,0x0c, 0x67,0xd1,0x43,0xbd, + 0x92,0x91,0xf2,0x32, 0xd3,0x71,0xb9,0x9a, 0x36,0x2e,0xf4,0xf0, 0xd9,0xa8,0x16,0x74, + 0x30,0xd2,0x6a,0x8f, 0x80,0xc7,0x6f,0x60, 0xbc,0x37,0xd6,0x05, 0xd9,0x6e,0x9f,0xca, + 0x3e,0xf9,0xa6,0xff, 0xd1,0x38,0xb1,0xeb, 0xd5,0x89,0x7e,0x58, 0xf6,0x63,0x44,0x56, + 0xcf,0x7d,0xd7,0xbf, 0xb6,0x2a,0x74,0x2e, 0xcd,0x43,0xa3,0x0f, 0x8a,0x78,0x65,0x37, + 0x29,0xa9,0xb5,0xab, 0xdc,0xcc,0x3a,0xa3, 0xed,0xf7,0x29,0xa0, 0xb0,0xe1,0x78,0x51, + 0xa7,0x63,0x9f,0x0a, 0x2e,0xe6,0xe7,0x41, 0x95,0xd6,0x72,0xfb, 0x55,0x1f,0x3a,0x85, + 0x6e,0x95,0xc6,0xf8, 0x7b,0x78,0x9f,0xc3, 0x3f,0x96,0xe9,0xf9, 0xab,0x9b,0x0d,0xb1, + 0xd9,0x0a,0xf9,0xa4, 0xb6,0x5d,0x88,0x76, 0x95,0x7a,0x28,0x8a, 0xba,0x1c,0xca,0x12, + // 0x1c00: + 0x6b,0xe2,0x0f,0xc8, 0x63,0xd4,0x53,0xd2, 0xd6,0xa8,0x3a,0x61, 0xb9,0xc7,0xcb,0x5f, + 0xfd,0x72,0x0f,0xc1, 0xfa,0x6d,0x1c,0xd0, 0x8c,0x2d,0x19,0x27, 0x37,0xa4,0x9f,0xa6, + 0xc3,0x7a,0x87,0x50, 0xf5,0x1b,0xaa,0xa4, 0xd1,0x72,0x56,0x2f, 0x28,0x0c,0x5d,0x37, + 0xfb,0xaa,0x4c,0x14, 0xa3,0x13,0x1c,0x68, 0x44,0xa0,0x03,0x71, 0x43,0x63,0x24,0x66, + 0xe1,0x3c,0xb1,0x12, 0x2b,0xd5,0xe8,0xb6, 0xe4,0xa0,0x05,0x4c, 0x6b,0x1d,0x83,0xda, + 0x6b,0xb5,0x3f,0x3a, 0x9c,0x4d,0x98,0xa0, 0x10,0xcf,0xf2,0xa1, 0xa0,0xa4,0xa8,0x7d, + 0xd3,0x2f,0xa0,0xbb, 0x44,0x02,0x85,0x75, 0x1e,0xe2,0x04,0xdb, 0x82,0x33,0x17,0x60, + 0xcd,0x21,0xcc,0x44, 0xfb,0x2a,0x6a,0x8e, 0x49,0xad,0x43,0x90, 0x71,0x46,0x4c,0x2a, + 0x62,0x97,0xbc,0x55, 0x20,0x63,0x29,0x68, 0x92,0x1f,0x98,0xec, 0x8c,0x07,0x4c,0x07, + 0x5b,0xbe,0x37,0xb7, 0x61,0x83,0x5b,0x50, 0xfc,0x43,0x69,0xd3, 0xf8,0x69,0x8e,0xb2, + 0xae,0x52,0xce,0xe5, 0xc7,0xed,0x04,0x09, 0x41,0x40,0x81,0xb3, 0xbf,0xcf,0x7d,0x14, + 0xbf,0x55,0x82,0x85, 0x3f,0xf8,0xbb,0x92, 0x29,0xc2,0x84,0x85, 0xde,0x92,0x5f,0x28, + // 0x1cc0 = 7360 Bytes +}; // ctcode_ovr1_pal_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_ovr1_usa_bz2 (rmce/ovr1.bin) + +const u8 ctcode_ovr1_usa_bz2[7335] = +{ + 0x00, 0x00, 0x53, 0x64, // 21348 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x3d,0xa1, 0xf5,0x64,0x00,0x0e, + 0x7b,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe0, 0x19,0x30,0x6b,0x2f, 0xbb,0xd8,0x4a,0xec, 0x03,0x76,0xd0,0x96, + 0xd9,0x12,0x02,0x29, 0x2a,0xa4,0x52,0x2c, 0xa9,0x9b,0x00,0x06, 0x4e,0xed,0xdd,0x69, + 0xbb,0xbb,0xb1,0xa1, 0xdb,0x35,0x2b,0xa0, 0xe4,0x55,0x66,0x02, 0x79,0xed,0xa5,0x54, + 0x08,0x89,0x5f,0x60, 0xc8,0x6a,0x40,0x98, 0x93,0x09,0x4f,0xc4, 0x13,0x4d,0x93,0x28, + 0xc9,0x3d,0x19,0x34, 0xd3,0x14,0xf1,0x32, 0x69,0xa3,0x51,0xe4, 0x4f,0x4d,0x1a,0x6a, + 0x61,0x32,0x7a,0x35, 0x1e,0xa6,0xd3,0x49, 0xe9,0xe9,0x4f,0x12, 0x64,0x79,0x34,0x1a, + 0x9e,0x43,0x13,0x53, 0x6a,0x69,0xea,0x3f, 0x53,0x4d,0x4d,0x3c, 0x44,0xf1,0x46,0x69, + 0x34,0xd3,0x4f,0x24, 0xda,0x8d,0x93,0x51, 0xe8,0x47,0xa1,0xa0, 0x87,0x93,0x53,0xd4, + 0xd3,0x33,0x4a,0x3c, 0x99,0x3d,0x29,0xea, 0x7a,0x4d,0xa9,0xe2, 0x9e,0x6a,0x98,0x83, + 0x44,0x40,0x86,0x02, 0x9a,0x64,0x34,0xd4, 0x9e,0x68,0xca,0x6d, 0x1a,0xa7,0xb4,0x87, + 0x94,0xc5,0x36,0xd4, 0x26,0x6a,0x1a,0x68, 0x1b,0x29,0xea,0x62, 0x34,0x60,0x99,0xa9, + 0xb5,0x32,0x3d,0x41, 0xa0,0xf2,0x80,0x1a, 0x00,0xd3,0x43,0x40, 0x00,0x34,0x0d,0x06, + 0x9a,0x19,0x1a,0x69, 0xea,0x06,0x81,0xa6, 0x9a,0x06,0x4d,0x34, 0x31,0x1a,0x00,0x1a, + // 0x100: + 0x34,0xd0,0x64,0x18, 0xd4,0x19,0x24,0xc0, 0x44,0xd2,0x4f,0x54, 0xf2,0x62,0x9b,0x49, + 0x93,0x4f,0x53,0x27, 0xa4,0x34,0xf4,0x46, 0x46,0x9b,0x44,0xd0, 0x32,0x68,0xc9,0xea, + 0x01,0x88,0x36,0x90, 0xc8,0x1a,0x1a,0x36, 0xa0,0x69,0xa0,0x69, 0xa1,0xe8,0x80,0x69, + 0xa6,0x40,0x68,0x03, 0xd4,0x68,0x1a,0x34, 0x32,0x1a,0x00,0x7a, 0x83,0x41,0xa6,0x4d, + 0x00,0x1a,0x64,0x00, 0x0c,0x99,0x0d,0x34, 0xc4,0x09,0x11,0x04, 0x24,0xd0,0x4d,0x53, + 0xd9,0x13,0x53,0xca, 0x9f,0xaa,0x3c,0xd5, 0x34,0xfd,0x53,0xf4, 0x8f,0x2a,0x7a,0x9e, + 0xa3,0xc6,0xa6,0x99, 0xa4,0x87,0xb4,0x6a, 0x27,0xea,0x8f,0x24, 0xd3,0xd4,0x3f,0x54, + 0x7a,0x4f,0xd5,0x3d, 0x4f,0xd5,0x36,0x29, 0x89,0xfa,0xa7,0xa4, 0xf5,0x34,0xf5,0x3f, + 0x44,0x8d,0xa8,0x7a, 0x47,0x92,0x68,0x68, 0x69,0xe9,0x1e,0x49, 0xe8,0x81,0xe5,0x1e, + 0xa3,0xd4,0x1f,0xa2, 0x8f,0x28,0xf2,0x4d, 0xa6,0x51,0x89,0xea, 0x7e,0xa8,0xd3,0xd4, + 0x3c,0xa3,0x43,0x26, 0x8f,0x49,0xa6,0x9e, 0xa3,0x21,0xa7,0xa7, 0xaa,0x32,0x34,0x40, + 0x00,0x1a,0x13,0x68, 0x00,0x4c,0x00,0x34, 0x00,0x13,0x02,0x7a, 0x00,0x13,0x00,0x00, + 0x00,0x00,0x08,0x6d, 0x00,0x01,0x30,0x13, 0x00,0x26,0x4c,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x04,0xc9,0x80, 0x00,0x13,0x00,0x09, 0x80,0xd0,0x24,0x50, 0x80,0x9a,0x04,0xd1, + 0x1b,0x55,0x3c,0xd2, 0x62,0x1e,0x88,0x62, 0x9b,0x53,0x34,0x62, 0x8c,0x69,0xa9,0xe9, + 0xa8,0xc1,0xa4,0xd3, 0xd4,0x32,0x69,0xea, 0x69,0x84,0xf4,0x26, 0x08,0xd0,0x3d,0x26, + // 0x200: + 0xd2,0x19,0x1a,0x68, 0x06,0xd4,0xf5,0x30, 0x8f,0x44,0xda,0x83, 0x68,0x81,0xa6,0x81, + 0x88,0x01,0xa1,0xa6, 0xd4,0x31,0x34,0x32, 0x7a,0x69,0x19,0x34, 0x36,0x91,0x9a,0x83, + 0x40,0x01,0x84,0xb8, 0x8c,0xe2,0xcc,0xbf, 0x98,0x6e,0x04,0x52, 0x6d,0xe9,0xb3,0xd5, + 0x77,0x54,0x3a,0x63, 0xce,0xa2,0x67,0x00, 0x41,0x6b,0x26,0x6d, 0x69,0xab,0x6c,0x94, + 0x03,0x02,0x62,0x21, 0x96,0x4e,0xa3,0x7d, 0x49,0x64,0x24,0x90, 0xc9,0x53,0xb3,0x1d, + 0x00,0xa2,0x3c,0x0a, 0x75,0x9f,0x45,0x6b, 0xc0,0xa6,0xdd,0x45, 0x10,0x59,0x39,0x84, + 0xf3,0x8f,0x00,0x66, 0x45,0x06,0x55,0x69, 0x70,0xd2,0x9c,0x09, 0x31,0xd6,0x15,0x4d, + 0x49,0xc6,0x3a,0x90, 0x72,0x95,0xc1,0x18, 0x5c,0x49,0x2c,0x52, 0x2b,0xcc,0x0a,0x57, + 0x73,0xb5,0x25,0x37, 0x7c,0x66,0x98,0x5d, 0xb0,0x7c,0x18,0x82, 0x32,0x50,0x0a,0xe9, + 0xc1,0x1e,0x75,0xd0, 0x26,0xff,0xe9,0xe5, 0x58,0x64,0x70,0xc0, 0x0a,0x87,0xd6,0xb3, + 0x8b,0xa9,0x90,0x13, 0x42,0xd2,0x83,0x92, 0x1d,0x84,0x45,0x0a, 0x00,0xa3,0xc7,0xd7, + 0x96,0x7d,0xc0,0xb3, 0xdc,0x75,0x20,0xc4, 0xc5,0x29,0x4a,0xb7, 0x05,0x89,0x35,0x37, + 0x66,0x09,0xf0,0xac, 0x7e,0x42,0x6e,0x67, 0x38,0x0a,0xa2,0x8c, 0x01,0x65,0x8c,0x95, + 0x99,0xb2,0x8e,0xfe, 0x7a,0xc7,0xa7,0xe9, 0xd4,0x05,0xcb,0x19, 0x02,0x1d,0x01,0x82, + 0x05,0xf7,0x4a,0xd3, 0x20,0x18,0x5c,0xad, 0xc8,0x65,0xec,0xd8, 0x41,0x23,0x80,0xe4, + 0x94,0x6b,0xec,0xf2, 0xd9,0xbe,0x8a,0xcd, 0xfd,0xcb,0xc9,0x32, 0xe2,0x07,0x8d,0x76, + // 0x300: + 0x3e,0x13,0x08,0x23, 0xc1,0xce,0xa0,0x14, 0xd3,0x00,0xbd,0x3b, 0x7a,0x52,0x09,0x22, + 0x2a,0x63,0x00,0x7b, 0x12,0x7d,0xa6,0x5b, 0x54,0x88,0x28,0x38, 0x74,0xc9,0x1a,0xe9, + 0xf9,0x45,0xb1,0x38, 0x7d,0xff,0xe0,0x85, 0xb5,0xd7,0xea,0x92, 0x01,0x03,0x2e,0x04, + 0xc7,0xa4,0x79,0xd5, 0x05,0x01,0x1a,0x50, 0x27,0x6f,0xba,0x5f, 0x9b,0x66,0xac,0xc2, + 0x09,0x24,0x75,0xff, 0xb9,0xa0,0x67,0x12, 0x71,0x0c,0x23,0xcd, 0xcf,0x62,0xff,0x0e, + 0xb4,0x00,0xda,0x7e, 0x60,0x25,0xc3,0xb4, 0xed,0x3d,0x61,0xb6, 0x2c,0x66,0x7e,0xe1, + 0x19,0xcc,0xda,0x76, 0xa6,0x8b,0xd8,0x39, 0x0d,0x3b,0x2d,0x1e, 0x2e,0x70,0x01,0xad, + 0xcb,0x63,0x7f,0xca, 0x31,0xf1,0x8a,0x75, 0x76,0xc8,0x04,0x3c, 0x01,0x85,0x7b,0xbc, + 0xc2,0xf2,0x8e,0x39, 0x69,0x4f,0x01,0x4e, 0xef,0x7b,0x4f,0x05, 0xa5,0x41,0x05,0x2f, + 0xf9,0x9c,0x79,0x10, 0x65,0x36,0xf9,0x5c, 0xfa,0x45,0x2c,0x4b, 0xe4,0xaf,0xf8,0xdf, + 0xa5,0xc4,0xa2,0xdc, 0x00,0x7f,0x18,0xe8, 0x80,0xbd,0x1d,0xae, 0x66,0x13,0x80,0xbf, + 0x10,0x10,0xde,0x9c, 0x83,0x75,0xa2,0x2b, 0xa9,0xdc,0xc0,0xe2, 0xfa,0x3c,0xf5,0x5c, + 0xcb,0xbd,0x39,0xf8, 0x35,0x70,0xed,0x1e, 0x73,0xee,0x6e,0x23, 0xa6,0x47,0xf0,0x5f, + 0x3c,0x8a,0x4f,0xf9, 0x32,0xc4,0x27,0x51, 0xca,0xa8,0x57,0x12, 0x2a,0x88,0x56,0x63, + 0x26,0x6c,0x24,0xbf, 0xd9,0x01,0x55,0xdd, 0x9b,0x5b,0x22,0x09, 0xd7,0x38,0x91,0xdf, + 0x7e,0xaf,0xb3,0xcc, 0xa8,0x2d,0xc7,0x16, 0x05,0xde,0xfe,0xa5, 0x03,0xbb,0x38,0x67, + // 0x400: + 0x36,0x91,0xcb,0x72, 0x1e,0x09,0x73,0x47, 0xa0,0x88,0x83,0xfe, 0x08,0xdf,0x8e,0x21, + 0xc3,0x7b,0xf2,0x99, 0x30,0x69,0x21,0xb1, 0x36,0x09,0x25,0x9c, 0x4c,0x6d,0x24,0xac, + 0x12,0x10,0x7b,0x06, 0x8e,0x11,0xa1,0x09, 0x4e,0xc5,0xfd,0xfc, 0x41,0xc1,0x1b,0xb1, + 0xaf,0x20,0x92,0x4b, 0x75,0x1a,0x0e,0x53, 0x69,0xb0,0xe8,0x36, 0x07,0x56,0x7a,0xb3, + 0xa6,0xe9,0xb3,0xf9, 0x3d,0x55,0x80,0x96, 0x81,0xa4,0x1c,0xc5, 0xe8,0x62,0xb0,0x05, + 0x01,0x89,0x70,0xcd, 0x12,0x7e,0x4b,0xfa, 0x4d,0x29,0x31,0x09, 0x21,0x7c,0xf1,0xa1, + 0x2b,0xc7,0x9f,0xdd, 0x22,0x0a,0xb6,0x72, 0x4d,0x20,0x2e,0xbc, 0x56,0x25,0x91,0x96, + 0x80,0x8a,0xd3,0x4a, 0x0f,0x21,0xde,0x60, 0x25,0xd2,0x86,0x8f, 0x68,0xdb,0x6e,0x62, + 0x10,0x9b,0x7d,0x99, 0x40,0xa2,0xc5,0x9a, 0xac,0x41,0x61,0xa4, 0x8d,0xbb,0x42,0xd1, + 0xd4,0x8e,0xe7,0x1d, 0x89,0x3e,0x43,0x01, 0x66,0x63,0x62,0x0c, 0x18,0x83,0x1e,0x9b, + 0xd6,0x48,0x91,0xef, 0x5a,0xc2,0xa8,0x80, 0x44,0x34,0x95,0x2d, 0x25,0x16,0x92,0x5a, + 0x6f,0x65,0xcd,0x73, 0xce,0x4b,0xa5,0x68, 0x30,0x34,0x3c,0xb4, 0x12,0x45,0x2d,0x02, + 0x3c,0x66,0x41,0x88, 0x47,0xc7,0xf9,0x9e, 0x1e,0xf3,0x83,0xa7, 0xae,0xaf,0x81,0x10, + 0x43,0x37,0x7d,0x54, 0x6c,0x85,0x11,0x46, 0x03,0x3a,0xc8,0x10, 0x94,0x34,0x90,0x76, + 0x23,0x22,0x22,0x8c, 0x0c,0x88,0x10,0x20, 0x34,0xb8,0xf9,0x0b, 0x60,0x2a,0xd9,0x13, + 0xc2,0xa1,0x45,0x18, 0x05,0x3e,0x7c,0xfa, 0xdc,0x26,0x11,0xb6, 0xe6,0x89,0xe2,0x78, + // 0x500: + 0x96,0x19,0x09,0x2f, 0x98,0x07,0xce,0x19, 0x04,0x9b,0x68,0xa7, 0xaf,0x64,0x49,0x96, + 0x4e,0x91,0x7c,0x80, 0x7c,0x3f,0x9d,0x35, 0x26,0xcd,0x77,0x39, 0xe1,0xa4,0xcf,0x37, + 0xa8,0x1d,0x20,0x6b, 0x04,0x91,0xc2,0x98, 0x53,0x81,0x81,0x5e, 0x99,0xb8,0xad,0xaf, + 0x79,0x92,0x44,0x4a, 0x4c,0x42,0xfb,0xb3, 0x88,0xc5,0xd6,0x39, 0x8e,0x4f,0xad,0x03, + 0x54,0x62,0xac,0x18, 0x1c,0xe8,0x3a,0x04, 0x24,0xf8,0x6b,0x5f, 0x40,0x62,0x97,0xaa, + 0xe3,0x3b,0x50,0x77, 0xe4,0x01,0x75,0x82, 0x9c,0x38,0xfd,0x8b, 0x5c,0xe8,0xd0,0xfe, + 0xa2,0x6a,0x59,0x69, 0xce,0x28,0xd7,0xe6, 0x72,0xbe,0x9b,0x1d, 0x62,0xa2,0xe2,0x4a, + 0xb4,0x1d,0x23,0x10, 0x65,0x08,0xc0,0x40, 0xc6,0x36,0x60,0xc0, 0xd7,0xfe,0x88,0x6c, + 0x58,0x13,0x24,0x77, 0x04,0x08,0x7d,0x8d, 0x70,0x3e,0x49,0xe2, 0xef,0xa9,0xb9,0x93, + 0x59,0x4a,0x1b,0x34, 0x70,0x46,0x39,0x39, 0x97,0x58,0xb8,0x32, 0x99,0xd8,0x61,0x03, + 0x14,0x81,0xc0,0xc5, 0x58,0x0d,0xae,0xe3, 0x03,0x6d,0xc5,0x6c, 0x86,0xca,0x43,0x8d, + 0x61,0xa2,0xc1,0x30, 0x3f,0x37,0x69,0x50, 0x26,0x0d,0xb5,0x59, 0x29,0x68,0x18,0xf8, + 0xfc,0xbf,0x91,0x48, 0xa6,0x41,0x59,0xdc, 0x93,0xe8,0xf8,0x09, 0xbf,0x3b,0x02,0xce, + 0x69,0x80,0x7e,0x30, 0xad,0x65,0xd2,0xed, 0xe0,0xb8,0x32,0x6b, 0x60,0xd2,0x1a,0xd3, + 0x69,0xa2,0x08,0xa0, 0x3b,0x53,0xeb,0xc1, 0x72,0x61,0x50,0x90, 0xde,0x33,0x2f,0x7c, + 0xfb,0x37,0x60,0x2c, 0x00,0x47,0x8e,0xa0, 0x52,0x26,0x77,0x01, 0x9d,0x84,0x73,0x16, + // 0x600: + 0x3d,0xa7,0x7c,0x30, 0xe9,0xe3,0x92,0xd8, 0x4e,0x2b,0x43,0x24, 0x4d,0x0b,0x05,0xd7, + 0x90,0x5b,0x07,0x18, 0xf9,0x88,0x79,0x02, 0xf2,0x20,0xa3,0x75, 0x52,0x9e,0x84,0x2a, + 0xb1,0x33,0x24,0xbf, 0x30,0x3a,0x4f,0x66, 0x7c,0x51,0x33,0x66, 0x28,0xed,0x2c,0x10, + 0x54,0xa8,0xb1,0xae, 0x31,0x32,0x48,0x0b, 0x07,0x68,0x6a,0x92, 0x86,0x3b,0x0a,0xc2, + 0x18,0x9a,0xde,0x75, 0xe7,0x8d,0x0a,0x27, 0x96,0x1a,0xf9,0xbf, 0x50,0x1e,0xcb,0x2c, + 0x87,0xf7,0xa8,0x1a, 0x60,0xdf,0x17,0xab, 0x6e,0x76,0x5c,0x84, 0x6d,0x72,0x05,0x0d, + 0xab,0x39,0x32,0xd3, 0x61,0x80,0x92,0xc6, 0xb5,0x40,0xd6,0x54, 0x16,0x3a,0xe9,0xc1, + 0x2e,0x4d,0x4d,0xdf, 0x6e,0x73,0x49,0x2d, 0x4a,0x96,0x14,0x67, 0x99,0x71,0x5f,0x02, + 0x4a,0xda,0x9d,0x38, 0xa1,0x71,0x2e,0x2a, 0x98,0x6a,0x57,0x6e, 0xa1,0xe3,0x3a,0x52, + 0x53,0xe5,0xcc,0x80, 0xaf,0xf2,0x56,0x04, 0x34,0x74,0x8b,0x78, 0xcc,0x42,0x61,0xb0, + 0x78,0xef,0xa4,0x89, 0x3e,0x97,0x10,0x80, 0xc6,0x66,0xe5,0x00, 0xee,0x4c,0x93,0x8d, + 0x2d,0xa9,0x08,0x9b, 0xdf,0x61,0x68,0xa8, 0x3c,0xa2,0xde,0xfd, 0x4a,0x39,0x84,0x19, + 0x30,0x94,0x51,0xc2, 0x30,0xcc,0x11,0xa4, 0x27,0x81,0x9a,0xa2, 0x37,0x7a,0x98,0x86, + 0x56,0x48,0x84,0xec, 0x21,0xe4,0x64,0xe0, 0x32,0xd1,0x68,0xab, 0x86,0xdb,0x68,0x08, + 0xd6,0x36,0x6c,0x6d, 0x32,0x19,0x09,0xc4, 0x42,0x88,0x88,0x87, 0x9d,0xfb,0x8c,0x70, + 0x5d,0x29,0x99,0x19, 0x84,0xdd,0x8e,0xf0, 0x5e,0x27,0xc8,0x9f, 0xfe,0xf9,0x51,0x62, + // 0x700: + 0x7f,0xdb,0xb7,0x5e, 0x90,0x2b,0x46,0xa7, 0xa8,0x5e,0x00,0xc2, 0x63,0x35,0xca,0x44, + 0xa3,0xec,0xde,0xcf, 0xf2,0xd7,0x03,0x6f, 0x94,0x8c,0x5e,0xef, 0x85,0x63,0x44,0x30, + 0xb5,0x86,0xaa,0x3a, 0xca,0x8a,0x48,0x9a, 0xb2,0x07,0x22,0x40, 0x8a,0x03,0x08,0x46, + 0x43,0xf1,0x44,0x54, 0x93,0x5c,0x13,0xb2, 0xea,0x7f,0x3c,0x68, 0x2a,0xa1,0x8f,0x24, + 0x9c,0x64,0xec,0x02, 0x44,0x9d,0x4a,0xe7, 0x62,0x2e,0x92,0x52, 0x33,0x17,0x54,0xff, + 0x78,0x51,0x50,0x26, 0x6a,0x84,0xa8,0x31, 0xd0,0xa0,0xa1,0xd3, 0x69,0x22,0x87,0x4e, + 0xb9,0xd9,0xc5,0xfb, 0x6f,0x45,0xed,0xfd, 0xbb,0x71,0x8f,0x4c, 0x76,0x05,0x45,0x34, + 0xdb,0x12,0xbb,0x70, 0x4b,0xd0,0x1c,0x2e, 0xb9,0x5f,0x90,0x95, 0xec,0x86,0x82,0x0b, + 0x7c,0xa9,0x29,0xc0, 0xa3,0x41,0x13,0x62, 0x50,0x4d,0xc8,0xe1, 0xf0,0x26,0x09,0x25, + 0x31,0xbb,0xea,0xfa, 0x0c,0xdd,0x64,0xe1, 0xac,0x81,0x4a,0x7b, 0x4a,0x88,0xef,0x02, + 0xdd,0x4e,0xc8,0xf7, 0xe7,0xb0,0xd4,0xfa, 0xe8,0x88,0xc0,0xd0, 0x31,0xc9,0xc8,0x57, + 0x34,0xbe,0x43,0x2a, 0x0f,0xc1,0x2c,0x29, 0x2a,0xc9,0x94,0x50, 0x9d,0xd4,0x23,0x6b, + 0x54,0xb9,0x99,0x2c, 0x7e,0x30,0xcd,0x35, 0xec,0x6d,0xd7,0x1e, 0x3b,0x80,0xcc,0xdf, + 0x15,0x45,0xc8,0x84, 0xfe,0x9f,0x3e,0x65, 0xe1,0xb0,0xee,0xaa, 0x22,0x2f,0x1f,0x5b, + 0xb7,0x83,0x3b,0x4b, 0xcd,0x55,0x6e,0x36, 0xeb,0xa6,0x3b,0x63, 0xad,0x65,0x6f,0x99, + 0x1d,0x96,0x42,0x85, 0x4e,0x3b,0xc8,0x7e, 0xb5,0x95,0x0f,0x66, 0x36,0xfc,0xd2,0x1a, + // 0x800: + 0xa1,0x14,0xf8,0x5b, 0x89,0xc0,0xeb,0x38, 0x7d,0x66,0x90,0xd2, 0x1e,0x5c,0xd2,0x0c, + 0x28,0x44,0xef,0x5a, 0x0c,0x64,0x12,0x9a, 0x66,0xe7,0x36,0xb3, 0xcc,0x97,0xe2,0x38, + 0xe5,0x9a,0x30,0x5f, 0x09,0x90,0xda,0xe8, 0x19,0x45,0x64,0xcb, 0x53,0x2e,0x3a,0x85, + 0xd4,0x52,0x8d,0xbd, 0x01,0xda,0x65,0x37, 0x48,0x84,0xcd,0xab, 0xfb,0x86,0xae,0x0f, + 0xb8,0x47,0xf4,0x67, 0xba,0x4d,0xde,0x24, 0x56,0x09,0xec,0x0b, 0xc8,0x9a,0x3c,0xdd, + 0x45,0x18,0x61,0x2b, 0xc0,0x65,0xa2,0x3f, 0x92,0x8d,0xc1,0x65, 0x44,0xfe,0xfe,0x06, + 0xf8,0xd5,0x29,0x38, 0x42,0xd9,0xf9,0xda, 0xcd,0xc4,0xd9,0x08, 0xea,0x2f,0x22,0x9f, + 0x64,0xd7,0xd0,0x13, 0x65,0xc1,0x77,0x43, 0x04,0x7f,0x52,0xea, 0xe2,0xf6,0x2a,0x54, + 0x10,0xd5,0x0f,0xa8, 0x80,0x47,0x82,0x48, 0x6a,0xb2,0xa9,0x31, 0xa2,0x8d,0x7f,0x5c, + 0x11,0x0c,0x94,0x73, 0xf2,0x28,0xf2,0x44, 0x14,0x26,0x1a,0x37, 0x25,0x04,0x10,0xd6, + 0xae,0x11,0x51,0x70, 0x17,0xfb,0x90,0xac, 0x00,0xd8,0xce,0x66, 0xa7,0x3d,0xfe,0x71, + 0xd0,0x36,0xdb,0x98, 0x83,0xd2,0x8c,0x63, 0x19,0xe8,0xc8,0xe5, 0xdb,0xdf,0x42,0xf4, + 0x3a,0x51,0x8e,0x88, 0x7b,0xf3,0x64,0xa3, 0x45,0x32,0x2a,0xb7, 0x8e,0xf6,0x08,0x3b, + 0xfb,0x4a,0x81,0xc1, 0x6e,0x21,0xbe,0x56, 0xa8,0xe9,0x68,0x47, 0x3e,0xc5,0xfb,0x58, + 0xc5,0x3b,0x15,0x6e, 0xdd,0x18,0x3a,0x34, 0x12,0x38,0x49,0x3c, 0xd6,0x3b,0x9c,0x3b, + 0xfa,0xa2,0xff,0xb2, 0xf0,0x94,0x42,0xb8, 0xb9,0x71,0x67,0x3c, 0x18,0xae,0x04,0x53, + // 0x900: + 0x36,0xe6,0x43,0x4e, 0x13,0xf0,0x5f,0x48, 0x5d,0x6d,0x0a,0xdb, 0xa1,0x0c,0xe2,0x2a, + 0x5b,0x55,0xa4,0xa4, 0x39,0xb2,0x45,0x0d, 0xa2,0xca,0x57,0x09, 0x72,0x01,0x52,0x37, + 0x4e,0x46,0x0e,0xd4, 0x0a,0x93,0x51,0xa5, 0xa0,0x0e,0x27,0x12, 0x99,0xa4,0x80,0x39, + 0x7e,0xf1,0xb5,0xdb, 0x0c,0xf8,0x10,0x46, 0x9f,0x39,0x9d,0xe6, 0x52,0xf6,0x7a,0x5c, + 0xca,0x2d,0x97,0x5d, 0xb1,0x8c,0xeb,0x0b, 0xbc,0xb6,0x2c,0x22, 0x48,0x86,0x05,0x39, + 0x11,0xf5,0x4b,0x9c, 0x25,0x43,0xe1,0x14, 0x57,0x68,0xf9,0x55, 0x14,0x6e,0xbb,0x9f, + 0xec,0xb9,0x55,0x76, 0x31,0xf3,0xa3,0xb8, 0x6a,0x35,0x64,0x9b, 0x57,0x68,0xbd,0xc7, + 0x13,0xc6,0x64,0x4c, 0x0c,0x1c,0x38,0xe2, 0x00,0xcd,0x66,0xa8, 0xbf,0x9a,0xf5,0x01, + 0x38,0x53,0x46,0xdf, 0x7f,0xcb,0xe5,0xca, 0x6e,0xec,0xbb,0x6f, 0x2a,0x7f,0xaa,0x65, + 0xba,0x3b,0x96,0xf9, 0xcd,0x6e,0x25,0xf5, 0x7c,0x6b,0x10,0xd3, 0xb3,0x8a,0x84,0x06, + 0xcc,0x57,0x93,0x1d, 0x6d,0xaa,0x48,0x15, 0x1e,0x41,0xcb,0x52, 0x13,0x2b,0x49,0x74, + 0x5b,0xcf,0x63,0x88, 0x14,0x65,0x80,0xd0, 0xcb,0xa3,0xe5,0xe8, 0x75,0x9a,0x53,0x24, + 0xce,0x7d,0xf6,0x20, 0x6f,0x75,0xcc,0xbd, 0x57,0x49,0x15,0xdf, 0xe9,0x61,0xe3,0x44, + 0x75,0x8a,0xc4,0x74, 0x17,0x28,0x76,0x1b, 0x98,0x5e,0xb8,0xc1, 0x5c,0x95,0xf1,0x14, + 0xa5,0xb3,0xd8,0xd0, 0x09,0xe1,0x47,0x3a, 0xb5,0xfa,0x8f,0x39, 0x0a,0x8b,0x2a,0xc0, + 0x42,0x88,0xd3,0x80, 0x1f,0x16,0xa5,0x22, 0x20,0x66,0x12,0xf2, 0x65,0x6d,0x92,0x0a, + // 0xa00: + 0x3f,0xcf,0xac,0xa4, 0x93,0x1c,0xb7,0x06, 0x26,0x3f,0x49,0x57, 0x51,0xc0,0xe6,0x51, + 0xa5,0xb9,0xcd,0xed, 0xb6,0xfa,0x11,0x0d, 0xe9,0xde,0x85,0x0a, 0x19,0xca,0x2b,0x37, + 0xa4,0x6f,0x76,0xba, 0x9c,0x0b,0x1a,0x67, 0x66,0xf0,0x3d,0xb7, 0x5d,0x1c,0x21,0xdb, + 0xba,0xe7,0xbe,0x9b, 0x7d,0x6b,0x0f,0x95, 0x34,0x20,0xf3,0x7b, 0xc0,0x49,0x6b,0x8d, + 0x0d,0xd2,0x92,0x4b, 0x4c,0xa2,0x88,0x06, 0x82,0x0a,0x1c,0xfe, 0x32,0x02,0x22,0x6c, + 0x9e,0x4d,0xa9,0x28, 0x61,0xe2,0x7c,0xe5, 0x02,0x57,0x8c,0xac, 0x9c,0x04,0x1d,0xab, + 0x4b,0x99,0xf3,0x5f, 0xa5,0x98,0xea,0x5d, 0x4c,0x74,0x30,0xc5, 0x7e,0xb5,0x26,0x6d, + 0xbb,0x91,0x79,0xc9, 0x10,0x16,0xf8,0x2a, 0xb7,0x9f,0x99,0x25, 0x7e,0x30,0xb5,0x43, + 0x81,0x86,0xd0,0x17, 0xb2,0xa6,0xae,0x89, 0xcb,0x4a,0xa7,0x50, 0x67,0xdb,0x59,0xba, + 0xff,0x04,0x83,0x39, 0x32,0xe2,0x9a,0x49, 0x13,0x69,0x69,0x51, 0x10,0x2d,0x69,0xc2, + 0x71,0x81,0x58,0xce, 0x9f,0x80,0x5a,0x24, 0xa1,0xaa,0xc2,0x21, 0x56,0x16,0xed,0xfa, + 0xdd,0xbe,0x88,0x1a, 0xdb,0x06,0x7a,0x28, 0x96,0xc6,0x3b,0x11, 0xbe,0x36,0xda,0xf9, + 0x75,0xda,0xa7,0x5a, 0xfc,0xf9,0xa5,0xb7, 0x27,0x41,0x51,0xf9, 0x4a,0x4e,0x1f,0x0f, + 0x7f,0xd8,0x5b,0x6c, 0x59,0xe5,0x8b,0xc6, 0x29,0x11,0x08,0x98, 0x65,0x1d,0x01,0xfe, + 0xed,0x66,0xb3,0x0a, 0x35,0x74,0xba,0x2e, 0x27,0x53,0xc1,0xe8, 0x17,0x68,0x67,0xcb, + 0x39,0x0f,0x40,0x1e, 0xb6,0x87,0x9f,0xa7, 0x86,0xcb,0xb8,0xe5, 0xf8,0xed,0x3f,0x3d, + // 0xb00: + 0xea,0xd9,0x53,0x25, 0x65,0xc7,0x0c,0xab, 0x2a,0x3e,0xe3,0x70, 0x7f,0x1d,0xd6,0xdb, + 0xdf,0x46,0x27,0x85, 0xa9,0x6d,0x59,0x16, 0x82,0x38,0xb1,0xa9, 0xd3,0xb4,0xc6,0x9f, + 0xab,0x73,0x42,0xe7, 0x4b,0x87,0x99,0xce, 0xea,0xbd,0xe7,0xd1, 0xb7,0x69,0xaa,0xd6, + 0xda,0x11,0x88,0xdc, 0x86,0x3d,0x17,0x0b, 0x37,0x3d,0xf3,0x66, 0x09,0xd4,0xda,0x4e, + 0x2f,0xbc,0x6d,0xc9, 0xd7,0xeb,0xf4,0x73, 0x55,0x51,0xae,0x02, 0xc4,0x20,0x36,0x5f, + 0xb6,0x1c,0x4f,0x45, 0xb9,0x5c,0xed,0x82, 0xb9,0x34,0xb6,0x06, 0xcc,0xa4,0xa5,0xcc, + 0x25,0x9b,0x66,0xf9, 0x14,0x08,0x38,0xa9, 0x7c,0xf7,0x4f,0x81, 0x52,0x8c,0x70,0x66, + 0x84,0xbb,0xed,0xca, 0xc1,0x6f,0x6e,0x55, 0x2f,0xd8,0x4a,0x4b, 0x80,0x88,0x79,0x90, + 0x8e,0x58,0x00,0xaf, 0x14,0xa4,0x53,0xc9, 0xa8,0x78,0x3a,0x1c, 0x82,0xaf,0x52,0x69, + 0xed,0x04,0x72,0x60, 0xae,0xb3,0xe3,0xc8, 0x21,0x62,0x82,0x6c, 0xb0,0x21,0x02,0xe1, + 0xa3,0x17,0x43,0x29, 0xc0,0x31,0xda,0x4a, 0x8c,0x55,0xbb,0xd5, 0x3e,0x6a,0x7b,0x1c, + 0x47,0xda,0x21,0x90, 0xac,0xb4,0x17,0x59, 0x4a,0xde,0x9d,0x82, 0x6c,0x11,0x4f,0x7a, + 0x40,0x51,0xa5,0xbb, 0xab,0x50,0x9d,0xab, 0x8c,0xc3,0x34,0x08, 0xd4,0xaa,0xec,0xef, + 0x9e,0xf5,0x1b,0x6a, 0x40,0x67,0xe9,0xdf, 0x0e,0x51,0xe2,0x3d, 0x4c,0xa4,0xde,0x62, + 0xd6,0x59,0x7a,0xd2, 0xd6,0x1d,0x98,0x25, 0x81,0x24,0x00,0x4e, 0xd0,0x41,0x1a,0x12, + 0x73,0x37,0x65,0x86, 0xd4,0xda,0xbd,0x49, 0x33,0x04,0x48,0x0b, 0xae,0x14,0xd3,0x0b, + // 0xc00: + 0xeb,0x59,0x72,0x96, 0xa2,0x98,0x39,0x34, 0x94,0x16,0xf9,0xb8, 0xf2,0x8f,0xa7,0x13, + 0xac,0x2d,0x6b,0x0b, 0x58,0xdd,0x27,0x9d, 0xa6,0x01,0x3d,0x32, 0x8a,0x86,0x0e,0xf1, + 0xcb,0x6c,0x59,0x81, 0x6f,0x4f,0x08,0xb1, 0x62,0xf4,0x36,0xfc, 0xf8,0xa0,0x2e,0x76, + 0x8d,0xf7,0xc5,0xc8, 0xcd,0x42,0x37,0x79, 0xa6,0xf4,0x1f,0x84, 0x3e,0x94,0x7b,0xbd, + 0xde,0x37,0xb3,0x57, 0xb4,0xf8,0xea,0xb8, 0x9a,0x6e,0x88,0xb1, 0xdd,0xd6,0x51,0x8e, + 0x3a,0xa1,0x18,0x21, 0x27,0x53,0xc3,0xc1, 0x72,0x12,0x5f,0xd0, 0x21,0x6c,0x37,0x05, + 0x35,0x12,0x4c,0xba, 0xa8,0x2d,0x64,0x94, 0x17,0xab,0xda,0xb1, 0x52,0x90,0xf1,0x27, + 0x74,0x4c,0x32,0xf3, 0xe1,0x4e,0x3e,0xe4, 0xdf,0xb1,0xe8,0x60, 0x6d,0x72,0x83,0x14, + 0xcc,0x71,0xb3,0x28, 0x49,0x15,0xc4,0x5f, 0xe4,0x5c,0xa3,0x54, 0x1d,0x9d,0x93,0x9a, + 0x2a,0x41,0x4f,0xe5, 0xfc,0xda,0xda,0xa3, 0x56,0xe9,0xfb,0x0f, 0x3d,0x4c,0x06,0xb1, + 0x22,0xc7,0xf3,0x2c, 0x22,0x74,0x10,0x7d, 0xd1,0xd9,0xfb,0x03, 0x7f,0x84,0x49,0xeb, + 0xd0,0xda,0x21,0xe5, 0x9d,0x63,0x1d,0x9f, 0x5b,0x7b,0x37,0xd8, 0x92,0xa1,0xbe,0xe2, + 0xee,0x7c,0x35,0xe1, 0xfc,0x35,0x40,0xd4, 0xeb,0x37,0x49,0x6c, 0x2b,0x0b,0x66,0x50, + 0xed,0x52,0x0a,0x24, 0xa7,0x6f,0x1a,0x7f, 0xdd,0x49,0xdb,0x47, 0xee,0x46,0x0d,0x54, + 0x78,0xa8,0x65,0x6e, 0xa9,0xab,0x2f,0x97, 0x74,0xd5,0x20,0x05, 0x84,0x0c,0x28,0xe8, + 0x15,0x68,0x82,0xdf, 0x7c,0x12,0x35,0xc4, 0x00,0xcc,0x13,0x89, 0xc4,0xa4,0x1c,0x0a, + // 0xd00: + 0x9c,0xd2,0x34,0x8a, 0x0c,0xc2,0xe3,0x06, 0x03,0x9b,0x50,0x43, 0x29,0x81,0x6e,0x24, + 0x25,0x91,0xd0,0xc6, 0x86,0x39,0x82,0x47, 0xa7,0x6c,0x4c,0x4f, 0xcf,0x81,0xd6,0x1d, + 0x1c,0x59,0x61,0x45, 0x4e,0xfe,0x28,0x12, 0x37,0x61,0x92,0x7a, 0xf9,0x99,0x6f,0x1c, + 0x8e,0xc9,0x76,0x54, 0x30,0x2c,0xc3,0xdd, 0x7c,0x0c,0x51,0xc8, 0x4e,0xad,0xb4,0xde, + 0xc8,0x0e,0x92,0xe7, 0x2a,0xd7,0x26,0xe1, 0x09,0x7c,0x4d,0x2c, 0x71,0xad,0x08,0x64, + 0x31,0xc3,0x8f,0x9f, 0xd4,0xb5,0xea,0x93, 0x45,0x51,0xee,0xc6, 0x13,0x84,0x4d,0x07, + 0xd5,0x47,0xe5,0xb9, 0x1f,0xd8,0x0c,0xd7, 0x9e,0xdb,0x9c,0xcd, 0xa3,0x37,0x75,0xca, + 0x1c,0x7d,0x1f,0x80, 0x6b,0xe6,0x28,0xd0, 0x77,0x7c,0x6c,0x50, 0x48,0xf1,0x84,0x22, + 0x3f,0xd3,0x08,0xa4, 0xa8,0x62,0x08,0x80, 0xbb,0x4e,0xa7,0xa9, 0x1c,0x27,0x27,0x0e, + 0xb2,0x16,0x1a,0x2b, 0xe5,0xc8,0xc7,0x10, 0x45,0xaa,0xea,0xd0, 0x86,0x40,0x6f,0xe3, + 0xc1,0xe7,0x7b,0xdb, 0x85,0x11,0x82,0x8c, 0x1e,0x12,0x43,0x26, 0x66,0x5c,0x22,0x86, + 0xb4,0xa2,0x16,0xb3, 0x26,0xa9,0x74,0x32, 0x9a,0x67,0x0e,0xc5, 0x0a,0x26,0x48,0xd4, + 0xf3,0xd1,0x4f,0xab, 0xb2,0x95,0x33,0xc9, 0xa2,0x91,0xb2,0xf0, 0x79,0x3b,0x46,0x50, + 0xa5,0xf1,0x65,0x4e, 0x5a,0xf2,0x3b,0x31, 0x58,0x4c,0xba,0xfd, 0x02,0xdb,0xba,0x96, + 0x6e,0xa3,0x82,0xef, 0x0f,0x58,0x18,0xd4, 0x6a,0x83,0x47,0x2f, 0x74,0x46,0x51,0xb4, + 0x04,0x3b,0x35,0x67, 0x4b,0x92,0x6e,0xa0, 0xf2,0x32,0xc5,0x5d, 0x97,0x66,0x23,0x23, + // 0xe00: + 0xef,0x8a,0x5c,0x31, 0x92,0xc4,0x57,0x5e, 0x78,0xa2,0xf4,0xfe, 0x93,0xa8,0x39,0xfd, + 0xcd,0x83,0x47,0x44, 0x48,0xc8,0x94,0x78, 0x9d,0x58,0x51,0x1b, 0x57,0x44,0xb4,0xba, + 0xd0,0xd1,0x0e,0x6b, 0x4b,0x56,0xc6,0x68, 0x84,0xd1,0x0c,0x4a, 0x0e,0x88,0xc0,0xc4, + 0xa8,0xe0,0xcc,0x42, 0x85,0xb6,0x85,0xb7, 0xf6,0xd1,0x9c,0x3f, 0x38,0x7e,0x41,0x8e, + 0x13,0x11,0x04,0x44, 0x2c,0xdb,0x0a,0xdc, 0x3e,0xc5,0x9d,0x2d, 0xd2,0x85,0x73,0xe6, + 0x6e,0x7d,0x39,0x82, 0xa3,0x56,0xc9,0x36, 0xc9,0x36,0xca,0x08, 0xc1,0x34,0xf0,0xb4, + 0x1d,0xb1,0x1b,0x17, 0x55,0x21,0x74,0xca, 0x54,0xa9,0x21,0xd5, 0x71,0x7a,0x4f,0xa7, + 0x38,0x70,0x4e,0x75, 0x8f,0x50,0x41,0x57, 0xc1,0xd9,0x12,0xc6, 0xfb,0x19,0x5f,0x28, + 0x34,0x9f,0x57,0xef, 0xde,0x32,0x0a,0xee, 0x05,0x07,0xbb,0xee, 0xe2,0x75,0xce,0x43, + 0x36,0xc4,0x0e,0x1f, 0x5f,0xda,0x6c,0x17, 0xc4,0x2e,0xe0,0xdc, 0xf1,0x1f,0xdb,0xba, + 0x27,0xcc,0x61,0xc1, 0x76,0x17,0x62,0x46, 0x10,0xc9,0xf7,0x38, 0x84,0x86,0x8d,0xcc, + 0xe8,0x80,0xf5,0x42, 0x0d,0xe4,0x56,0x87, 0x7c,0x44,0x29,0xe6, 0x81,0xf7,0x8b,0x1e, + 0x13,0x1c,0xfb,0x5c, 0x91,0x72,0x4e,0x63, 0xd0,0xd0,0x91,0x9c, 0x0d,0x0c,0xf9,0xcd, + 0x39,0xc6,0x0b,0xcc, 0xc3,0x54,0xdc,0x52, 0x4c,0x2a,0x4f,0x77, 0x21,0x32,0x15,0x4e, + 0x44,0x74,0xf0,0x7d, 0x4b,0xab,0x84,0x1e, 0xab,0x62,0x05,0x5d, 0x2f,0xba,0x68,0x42, + 0x59,0x84,0x06,0x2e, 0xae,0xf1,0x0b,0x94, 0x3a,0xc2,0xb9,0xe9, 0x85,0x16,0xce,0x1a, + // 0xf00: + 0x59,0x1a,0x70,0x99, 0xbb,0x74,0x70,0xea, 0x88,0x49,0x74,0x18, 0x48,0x8c,0x59,0x50, + 0x05,0x23,0x90,0x3f, 0x83,0xfe,0xea,0x46, 0xdf,0x4e,0x22,0x9b, 0xf5,0xed,0x4b,0x5c, + 0xf4,0x04,0x92,0x5c, 0x64,0x19,0x08,0xca, 0xd0,0x43,0x69,0xaa, 0xd5,0x4f,0x24,0x7d, + 0x90,0xa2,0xc8,0x8a, 0x65,0x96,0x5f,0x03, 0x7d,0xd9,0x4c,0x44, 0x6a,0x67,0xc4,0x1e, + 0x3a,0x48,0x5e,0x94, 0xd6,0x0b,0x8c,0xb7, 0x45,0x24,0x83,0x19, 0x40,0xf5,0xc4,0x12, + 0x1b,0x52,0xfa,0xac, 0xac,0xd3,0xc6,0x01, 0x5f,0xca,0x8a,0x82, 0x6b,0xa3,0x1f,0x1b, + 0x57,0x31,0x44,0xd7, 0x65,0xee,0x34,0x1f, 0xa6,0x39,0x19,0xe9, 0x4a,0x57,0xa6,0x2f, + 0x22,0xa1,0xb2,0x85, 0xc5,0xdd,0xd3,0xb7, 0xf4,0xdc,0x41,0xba, 0x38,0x62,0x09,0x19, + 0x08,0x40,0x41,0x01, 0x00,0xcd,0x11,0x62, 0xcc,0x94,0xfa,0xee, 0xc0,0x61,0x26,0x47, + 0x6d,0x83,0x51,0xd0, 0x33,0x91,0xec,0xc5, 0xd9,0x0c,0x63,0x18, 0xc6,0xb6,0x68,0x70, + 0x39,0xf5,0xc0,0xc6, 0x24,0xf3,0x6c,0xe9, 0x36,0x47,0x59,0x00, 0xc6,0xba,0x14,0xc5, + 0x6c,0x0e,0xd6,0x39, 0x4f,0xc2,0x8b,0xea, 0x6f,0x42,0x3a,0x3c, 0x8e,0xd6,0x4e,0xb7, + 0x5d,0x6b,0x92,0xd4, 0x63,0xd4,0xbd,0x85, 0x43,0xaf,0xa9,0x8d, 0xe2,0xda,0x82,0x9d, + 0x68,0xa1,0xc9,0xff, 0x00,0xc9,0xa7,0xbf, 0x37,0xf2,0xca,0x2b, 0xdc,0x44,0x31,0x22, + 0x18,0x17,0xd1,0xb4, 0x18,0x65,0x9b,0x9e, 0xfb,0x7a,0xff,0xe5, 0x35,0x83,0x1b,0x96, + 0x26,0x06,0xc8,0x86, 0x70,0xb8,0x16,0x58, 0x87,0x22,0x96,0x43, 0x13,0x7d,0x96,0x7d, + // 0x1000: + 0xe8,0x05,0x31,0x66, 0x7a,0x25,0x79,0xf1, 0xa2,0xd0,0xf7,0xa5, 0xfc,0xe8,0xec,0x4c, + 0xe1,0xcc,0x9e,0xcb, 0x0d,0xb5,0xf3,0xa7, 0x6c,0x18,0x0c,0x06, 0x07,0xe8,0xdb,0xed, + 0xc5,0x40,0x4d,0xb1, 0x38,0x40,0x3c,0x1f, 0x51,0x00,0x97,0x23, 0x7c,0xbd,0x72,0x49, + 0x43,0x25,0x4c,0xa0, 0xa1,0x24,0xc5,0x61, 0x8e,0x54,0x73,0x3e, 0x64,0x40,0x72,0x17, + 0x14,0x8b,0xe7,0x59, 0x91,0x86,0x86,0x2f, 0xde,0xd9,0xb3,0xb8, 0xb9,0x45,0x8a,0x45, + 0x08,0x89,0x1a,0xb1, 0x28,0x52,0xd0,0x11, 0x40,0x3c,0xdf,0xbb, 0x8b,0x01,0xe5,0x8d, + 0x2a,0x8c,0xb1,0x1c, 0x4a,0xec,0xb7,0xf0, 0x70,0x8c,0x07,0x81, 0xf7,0x3b,0xda,0x95, + 0x66,0x8b,0xe3,0xdc, 0x37,0x1a,0xe8,0x7a, 0x6a,0x0a,0xe2,0x18, 0xc5,0x59,0x15,0x94, + 0xd5,0x93,0x14,0x67, 0x4f,0x92,0xc9,0x9a, 0x75,0x54,0x7c,0xa6, 0xce,0x9e,0xf9,0xa8, + 0xe0,0xb8,0x6e,0xba, 0xc2,0xe0,0x62,0xe7, 0x66,0xd2,0x61,0x00, 0x82,0x57,0x57,0x47, + 0x26,0xcd,0x0f,0x2c, 0xba,0xf7,0x78,0xd1, 0xa6,0x37,0x2e,0x07, 0x15,0xb8,0xca,0x82, + 0x4c,0x78,0x00,0x9e, 0xdd,0xc6,0x68,0x0b, 0x95,0x13,0x00,0x08, 0x76,0xd3,0xd5,0x95, + 0x50,0xd4,0xd5,0x50, 0xb8,0x1b,0xac,0xc4, 0xb3,0x2e,0x52,0xe8, 0x92,0x70,0x9c,0x48, + 0xb2,0x40,0xdd,0x59, 0x60,0x19,0xd0,0x17, 0x75,0xaa,0x69,0x05, 0x66,0xf7,0x27,0xf2, + 0x29,0x82,0x0c,0x29, 0x09,0xcc,0x0c,0x87, 0xeb,0xe8,0x18,0xb9, 0x6a,0x9e,0xf4,0x33, + 0x91,0x8e,0x05,0x49, 0x80,0x6c,0xb1,0x8a, 0x3c,0x11,0x22,0x85, 0x47,0x41,0xdb,0x58, + // 0x1100: + 0xf0,0x0b,0x14,0xc0, 0x36,0x2f,0x26,0x12, 0x39,0xf1,0x16,0xa0, 0x56,0x19,0xed,0x15, + 0xb9,0x4b,0xb3,0xd0, 0x3d,0xa7,0x5a,0xfc, 0xb5,0x82,0x45,0xa1, 0x8d,0x30,0xfb,0xf5, + 0x10,0x0e,0x55,0x41, 0xb1,0x82,0xd1,0xae, 0x64,0x82,0x22,0x92, 0x9c,0xfe,0x4f,0xa3, + 0xe3,0x71,0xb1,0x11, 0x64,0xd5,0x87,0x8b, 0x51,0xb3,0xc9,0x7f, 0xc8,0xc5,0x5a,0x44, + 0xc6,0xef,0xda,0xad, 0x97,0x49,0x1b,0x5f, 0x50,0xcc,0xc9,0x2d, 0x07,0x83,0x0c,0xc6, + 0x50,0x7d,0xac,0x95, 0x15,0x79,0x88,0x28, 0xc4,0x3a,0x19,0xea, 0xad,0x46,0xb8,0x5e, + 0x5f,0x05,0x5e,0x0e, 0x47,0x2a,0x76,0x37, 0xa9,0xb0,0xe1,0xc9, 0x91,0x18,0x48,0x04, + 0xf1,0x96,0xc5,0x99, 0xeb,0x4c,0x1d,0x01, 0x6a,0x77,0xb8,0xea, 0xe7,0x28,0xb3,0x5d, + 0xaf,0x79,0x92,0x80, 0x78,0xa5,0x47,0x91, 0x2b,0x0a,0xf4,0x36, 0x00,0xca,0x55,0x41, + 0x9f,0xde,0xd7,0x94, 0xbd,0x35,0xaa,0xaa, 0xc4,0x56,0x5a,0xa1, 0xd3,0x04,0x62,0x1d, + 0xef,0xeb,0x11,0x63, 0x28,0x59,0x5b,0x2c, 0xad,0xe1,0xa1,0xc1, 0x44,0x9e,0x38,0x13, + 0x22,0x23,0xc0,0x11, 0x68,0x65,0x52,0x14, 0x51,0x04,0x43,0xc3, 0x4d,0x94,0xeb,0x9f, + 0xd3,0xe1,0xeb,0xef, 0x32,0x66,0x49,0x54, 0xba,0x27,0x68,0x69, 0x26,0x6e,0x0b,0x56, + 0x40,0x14,0x4c,0x58, 0xa2,0x2b,0x5a,0x9f, 0x20,0x8d,0x45,0x51, 0x2d,0x32,0xc1,0x60, + 0xf8,0xd8,0x45,0x5a, 0xd4,0xc0,0xb9,0xe6, 0x60,0x61,0x91,0xc7, 0xd6,0x9c,0xb1,0xc4, + 0x66,0x38,0x60,0xd1, 0x64,0xa3,0x29,0x3c, 0xc2,0x83,0xa6,0x70, 0x38,0x4b,0xdb,0xd8, + // 0x1200: + 0xf7,0x5e,0x2f,0x80, 0x4f,0xb4,0x8f,0x11, 0x91,0x8c,0x7d,0x7d, 0x61,0xf0,0x6b,0xe5, + 0x0c,0xd1,0x65,0xf2, 0x7b,0x70,0x79,0xf2, 0x5e,0x4b,0xc5,0x10, 0xc1,0x42,0xfa,0xb8, + 0xd2,0x45,0xc7,0x07, 0x87,0xb8,0xa9,0x48, 0x84,0x50,0x10,0xcc, 0x95,0x80,0xa7,0x3d, + 0xe3,0x14,0xe0,0x68, 0x33,0x01,0xf6,0x90, 0xb0,0x87,0xa4,0xe6, 0xe8,0x02,0xbe,0xc5, + 0x73,0x7e,0xa4,0xa5, 0x2a,0xfc,0xcc,0x61, 0xf2,0xae,0x9f,0x69, 0x14,0xa7,0xc9,0xee, + 0x2d,0x2e,0x84,0xf9, 0xe6,0x86,0xa3,0x50, 0x47,0x24,0xe0,0x3a, 0x66,0xbc,0xa0,0xd3, + 0xcb,0x08,0x2f,0xf1, 0x35,0x27,0x6d,0x99, 0x66,0x5d,0xd9,0xba, 0xe4,0x0f,0x5e,0xbd, + 0x6d,0x63,0xae,0xbd, 0x9b,0xb3,0xf6,0x8d, 0x53,0xaa,0xf4,0x10, 0x01,0x4e,0x72,0x0f, + 0xd9,0xda,0xf4,0x2b, 0x5f,0x88,0x33,0x93, 0xad,0x8c,0x03,0x2e, 0x94,0xca,0x0e,0x4c, + 0x06,0x01,0x97,0x94, 0x0b,0x4e,0x22,0xd1, 0xc6,0x06,0x73,0x4f, 0x70,0xbc,0x9f,0x79, + 0xe2,0x9e,0x2f,0x4d, 0xed,0xf3,0x86,0xc4, 0x7a,0xa4,0x0d,0xe6, 0xfe,0xea,0x29,0xea, + 0xb8,0x13,0x62,0x9a, 0xbd,0xbc,0x6d,0x34, 0xbc,0x2b,0x5d,0x5b, 0xdc,0xc4,0x0f,0xb2, + 0x45,0x5a,0xd4,0x32, 0xa4,0x4d,0x06,0xa4, 0x8c,0xc9,0x0b,0xf2, 0x06,0x38,0x57,0xc0, + 0xd1,0x4a,0xe7,0xb2, 0xc7,0x73,0x01,0x5a, 0x2b,0xb9,0x88,0x53, 0x47,0xdf,0x91,0x68, + 0x9d,0x08,0x2e,0x3e, 0xb5,0xd9,0x33,0x34, 0x89,0x41,0xc5,0x87, 0x3b,0xa8,0x20,0x80, + 0xab,0x3d,0x72,0x71, 0xe0,0x78,0x58,0xdb, 0xae,0x71,0x71,0x8e, 0x3e,0xaf,0x0c,0x81, + // 0x1300: + 0x2f,0x13,0x09,0xbd, 0x6c,0x4b,0xcd,0x27, 0xe9,0x88,0xf2,0x25, 0x5f,0xc1,0x69,0x77, + 0x68,0x52,0xb3,0x62, 0xe1,0x84,0x8e,0x42, 0x98,0x65,0x12,0x3a, 0xc9,0x95,0xa3,0xf4, + 0xa8,0xe3,0x82,0x38, 0x35,0x1a,0xe3,0xcd, 0x55,0xc1,0xac,0xe9, 0x0f,0x40,0x89,0x8f, + 0x30,0x03,0xc0,0x8b, 0x5c,0x82,0x5d,0x55, 0x73,0xb1,0x2c,0x65, 0xed,0x62,0xd9,0x9b, + 0x08,0x5b,0x98,0xc5, 0x8d,0x83,0xf2,0x5e, 0x8e,0x96,0xe9,0xdb, 0xb2,0x06,0x10,0x3d, + 0x75,0x0a,0x4b,0x99, 0xa4,0xb0,0x58,0x87, 0x39,0xa5,0xa1,0x59, 0xa7,0x1b,0xed,0xb2, + 0x83,0xcc,0x00,0x74, 0x20,0x92,0xea,0x11, 0xc4,0x7f,0x3b,0x13, 0x9e,0x83,0x82,0x0b, + 0xfb,0x43,0x7b,0x06, 0x4d,0x22,0xce,0x2a, 0x8c,0x3f,0xd7,0xca, 0x1e,0x6c,0x2c,0x60, + 0x19,0x40,0x31,0xb3, 0xb1,0x8c,0x0b,0x59, 0xaf,0xc0,0x9b,0x5b, 0xc3,0xc1,0xd6,0x7e, + 0xf1,0x54,0xf4,0xa5, 0xa6,0x9b,0x29,0x60, 0x88,0x88,0x23,0x0a, 0xf9,0x75,0xc6,0x68, + 0x86,0x42,0xb5,0xe0, 0x37,0xcf,0x73,0x73, 0xee,0xb4,0x71,0x17, 0x31,0x61,0x07,0x3c, + 0xcc,0xf0,0x37,0x70, 0x35,0x05,0x42,0x06, 0x61,0xb2,0x54,0x51, 0x13,0xf2,0xe9,0x4d, + 0xe7,0x73,0x4a,0xa6, 0xee,0xca,0xe9,0x24, 0x4a,0xa9,0x99,0x0c, 0xe4,0xaa,0x67,0x67, + 0xe1,0x7a,0x8f,0x28, 0xf1,0xe2,0x8c,0x4d, 0x49,0xc1,0x6c,0x7d, 0x21,0xab,0x05,0xc8, + 0xf7,0xca,0x07,0x7c, 0x7f,0xd5,0x8d,0xe4, 0xa8,0x84,0x86,0x0c, 0x66,0x9e,0x9d,0xb3, + 0x30,0x5e,0x3b,0xdb, 0xf5,0xde,0x98,0x31, 0x26,0x1e,0x19,0xc8, 0x76,0x1c,0x4b,0x40, + // 0x1400: + 0x83,0x85,0xb3,0xa4, 0xa9,0x1b,0xfd,0x52, 0x56,0xa2,0x20,0xaa, 0x80,0x30,0x47,0x2a, + 0x10,0x12,0x42,0x69, 0x12,0x31,0x00,0x75, 0x82,0x10,0x43,0x03, 0xb2,0xd1,0xe7,0x24, + 0x13,0x24,0xb1,0x11, 0x06,0xe0,0x01,0x41, 0x86,0x8f,0xe5,0xaf, 0x2d,0x22,0xc9,0x73, + 0x6b,0x74,0xb4,0xa8, 0x38,0x56,0x63,0xac, 0xc4,0x06,0x09,0x21, 0x03,0xed,0x89,0x44, + 0x6a,0x36,0xc9,0x9e, 0x1a,0x32,0x78,0xd1, 0xdd,0x3a,0x0e,0xbf, 0x66,0x35,0x6a,0x89, + 0x14,0x22,0x3e,0xd8, 0x49,0x03,0x84,0xbb, 0x21,0x44,0xde,0x79, 0x1d,0x1c,0x40,0xac, + 0x88,0x14,0xa1,0x51, 0x58,0xa8,0x75,0xd8, 0x3e,0x5e,0x87,0x4c, 0x7f,0x5b,0x03,0x6c, + 0x7f,0x9c,0xa5,0x78, 0xc6,0xfd,0xee,0x6c, 0x48,0xa5,0x14,0x1d, 0xb1,0x7c,0xbc,0x32, + 0x9f,0x4e,0x50,0x8d, 0xdc,0xa4,0xfe,0x14, 0x9a,0x9a,0x34,0x43, 0x83,0x4b,0x03,0x17, + 0x1b,0x78,0xd3,0xe9, 0x9c,0x00,0xcc,0x3c, 0x3f,0x3d,0xaa,0x90, 0x4d,0x0a,0x4a,0x54, + 0x92,0x7f,0x93,0xc4, 0x07,0x0d,0x6c,0x68, 0xbe,0x30,0xe7,0xb0, 0x57,0xb8,0xe7,0x5b, + 0xeb,0xe1,0xf3,0xf2, 0x2e,0x86,0x23,0xbc, 0x5e,0xf2,0x49,0x1e, 0x1c,0xaf,0x5e,0x95, + 0xbd,0x1e,0xf6,0x40, 0xd3,0xaf,0x0c,0x2c, 0x44,0xed,0x59,0x58, 0xce,0xfc,0x28,0x56, + 0xb1,0x53,0x55,0x09, 0x24,0x96,0xae,0x03, 0x34,0x82,0x5b,0x71, 0x96,0x11,0x84,0x52, + 0xa0,0xbf,0x41,0x82, 0xb6,0x90,0x53,0x4b, 0xab,0x1a,0x21,0x01, 0x4e,0xd0,0x03,0x02, + 0xaa,0x95,0xf1,0x0c, 0xb3,0x0e,0x3a,0xba, 0x4c,0xdc,0x0f,0x72, 0x6c,0xb2,0x5b,0xfc, + // 0x1500: + 0xbe,0xdd,0x2d,0x80, 0xdb,0x79,0xe7,0x40, 0x84,0xe3,0x87,0x88, 0x1b,0x6f,0x1f,0x0a, + 0x10,0x0f,0xc6,0x1e, 0xc8,0xd6,0x00,0x91, 0xa0,0x94,0xf0,0x20, 0xf3,0xcf,0x3d,0x4d, + 0x48,0xf1,0xd1,0x25, 0x28,0x0b,0xbe,0x84, 0xa2,0x2c,0x8a,0xec, 0xdb,0x48,0x25,0x10, + 0xe3,0xa9,0x24,0x90, 0x0e,0x07,0x2e,0x9e, 0x21,0x1b,0x1b,0x57, 0x1d,0xcf,0xd0,0x8d, + 0x45,0x7e,0xbc,0x76, 0xd9,0x6d,0x6e,0xc3, 0x24,0x4f,0x71,0x24, 0x2b,0x82,0x05,0x70, + 0xb9,0x0b,0x6c,0x02, 0xe3,0x10,0x17,0x2e, 0x7d,0xed,0xbf,0xdd, 0xf4,0x17,0xaf,0xeb, + 0x59,0xb7,0xba,0x78, 0x78,0xa4,0x93,0xd8, 0x42,0x09,0x83,0xef, 0xa0,0xa3,0xd7,0x91, + 0x88,0x48,0xd8,0x94, 0x56,0x26,0xd2,0xdb, 0x30,0x0c,0xec,0x52, 0xca,0xe5,0x4f,0x29, + 0x77,0x44,0xd8,0xf8, 0x70,0xe3,0xc6,0x14, 0xe6,0x2d,0x77,0x77, 0x4f,0x3a,0x1e,0x29, + 0xc4,0x5e,0x15,0xd2, 0xe8,0x78,0x8a,0xfd, 0xdb,0x8c,0xba,0x5d, 0x2e,0x54,0x8a,0x11, + 0xa1,0x17,0xfc,0x45, 0x13,0xd4,0xdb,0xe7, 0xb7,0x5e,0x84,0xd3, 0x73,0x5a,0x8b,0x05, + 0xc8,0xef,0x76,0x07, 0x54,0x08,0x30,0x16, 0x5b,0xe7,0x9a,0x87, 0x91,0xa5,0xb7,0xf4, + 0x30,0xdb,0x02,0x83, 0x0e,0x16,0x41,0x94, 0xe3,0x81,0xde,0xb3, 0x66,0xcc,0xcb,0x67, + 0x4d,0xd5,0xd6,0x2a, 0x45,0xca,0x66,0xa8, 0x57,0x70,0xce,0x83, 0x83,0xc6,0x2e,0xd4, + 0x88,0x14,0x0b,0xcc, 0x6b,0x48,0x73,0xa6, 0xad,0x9f,0xd5,0xfb, 0x1b,0x40,0x83,0x90, + 0x55,0x25,0x4b,0x42, 0x54,0xf7,0x6c,0x59, 0x64,0xaf,0x36,0x6a, 0xeb,0xf1,0xab,0x2f, + // 0x1600: + 0xda,0xe5,0x61,0x9b, 0xfb,0x7d,0x82,0xe3, 0x68,0x15,0x07,0x38, 0x64,0x97,0xca,0xa4, + 0x47,0xc8,0xf1,0xf1, 0x2a,0x01,0xfd,0xd9, 0xcc,0xb3,0x2a,0x9b, 0xc1,0xae,0x95,0x01, + 0xa8,0x00,0x46,0x99, 0xdf,0xed,0x11,0x7a, 0xaf,0xde,0x73,0x72, 0xf3,0xb0,0x0e,0x96, + 0x65,0xed,0x9a,0xf7, 0xa9,0x2a,0x67,0xcf, 0xd1,0xa4,0x68,0x55, 0x90,0xb4,0x7a,0xc4, + 0xd4,0x04,0x36,0xe7, 0xdc,0x48,0x5d,0xe2, 0x00,0xcc,0x8c,0x71, 0xcf,0xfd,0x4c,0x8d, + 0xa3,0xdf,0xb3,0xfc, 0x25,0x86,0xf5,0xc2, 0x68,0x0b,0xca,0x13, 0x90,0x39,0xef,0xc1, + 0x9c,0xd0,0x77,0x69, 0x77,0x78,0xea,0x8b, 0x68,0x25,0xb3,0x88, 0xda,0x8c,0x19,0x2f, + 0x00,0x3e,0xf0,0x9f, 0xc8,0xfe,0x21,0x4b, 0x13,0x0a,0xc0,0x56, 0x02,0xd1,0x8e,0xd6, + 0xc3,0xc0,0xae,0xa5, 0xc9,0xe3,0x15,0xdb, 0xfc,0x54,0x87,0x7b, 0x81,0xc8,0x59,0x2f, + 0xd4,0xc5,0x94,0xbf, 0x4d,0x83,0xbd,0xea, 0xc4,0x6c,0xaf,0xb8, 0x56,0xb8,0x1b,0x79, + 0x30,0x51,0x5b,0x82, 0x8a,0x78,0xd3,0x95, 0xd9,0x2e,0x3a,0x44, 0xd8,0x1f,0x40,0xc4, + 0xa5,0xc3,0x9f,0x03, 0x88,0xbd,0x18,0x95, 0xf0,0xa0,0x8a,0x98, 0x41,0x2f,0xd0,0x56, + 0x0e,0x33,0x0d,0xa7, 0x9d,0x83,0x52,0x23, 0x75,0xa3,0x9a,0x52, 0x49,0x70,0x79,0xc8, + 0xe9,0x1a,0x04,0x7a, 0x6f,0x8b,0xf2,0x64, 0x17,0x25,0x91,0x02, 0xd1,0xfd,0x0c,0x52, + 0x53,0xd5,0xe8,0xda, 0xda,0xc6,0x46,0xae, 0x81,0x6b,0x30,0x1b, 0x2f,0xde,0xa9,0x32, + 0x78,0x88,0x94,0x63, 0x29,0x10,0xb7,0xc8, 0xf5,0xd1,0x34,0xf5, 0x7a,0x6e,0xa6,0x8e, + // 0x1700: + 0x19,0x08,0x22,0x96, 0x86,0x43,0xfc,0x54, 0xf3,0x55,0x3c,0x18, 0xcf,0x17,0xca,0xf9, + 0xe0,0xa8,0x3a,0x2c, 0xa6,0xc2,0x3a,0x58, 0xdc,0xf6,0x28,0x19, 0x66,0xfb,0x69,0x83, + 0x72,0x42,0x4b,0x3f, 0xf1,0xe1,0xa0,0x4d, 0x4d,0x2e,0x1d,0xcc, 0x51,0xd7,0x65,0x4d, + 0xcf,0x31,0x32,0xdc, 0xd9,0x9d,0x43,0xea, 0x91,0xc6,0xf5,0x24, 0xed,0xb9,0x2f,0x76, + 0x33,0xbf,0xd6,0x63, 0x05,0x27,0xcd,0xc5, 0xee,0x75,0x2b,0x7c, 0x98,0x66,0x30,0xdd, + 0xab,0xac,0x7d,0xd6, 0x19,0xac,0x53,0x9b, 0xae,0x93,0x03,0xcc, 0x40,0x95,0x09,0xbc, + 0xe5,0x16,0x46,0xdb, 0x1d,0xfe,0x50,0x8f, 0x45,0xe0,0x8b,0xf0, 0x13,0x78,0xf6,0x66, + 0x97,0x53,0x97,0xe3, 0x53,0xad,0x52,0x9f, 0xe3,0xff,0x61,0xb3, 0x43,0x14,0x8d,0xce, + 0xea,0xa3,0x36,0xaf, 0x0b,0x92,0xe4,0x2a, 0xa8,0xea,0xd8,0xf6, 0x2c,0x66,0x46,0xe6, + 0x9e,0x01,0x18,0x5a, 0x24,0xd8,0x41,0xd1, 0x08,0x27,0xce,0x4f, 0xde,0x90,0x92,0x77, + 0x62,0x70,0xc8,0x48, 0xac,0x25,0x6a,0x1c, 0xcb,0x17,0x7d,0x62, 0xc7,0x9c,0xc7,0xdb, + 0x9b,0x73,0xd9,0xa6, 0xeb,0x67,0xca,0xa4, 0x6b,0x48,0x74,0xd8, 0xdf,0x28,0x0e,0x16, + 0x5b,0x9e,0x86,0xfc, 0xa8,0x8a,0x32,0xe6, 0x22,0x18,0x03,0x99, 0x29,0xc6,0x8c,0x44, + 0x60,0xee,0x92,0x6f, 0xb7,0xe2,0x6c,0xa1, 0x35,0xab,0xab,0x65, 0xa5,0x20,0xa8,0x4e, + 0x1e,0xcc,0xf2,0x80, 0xca,0xc9,0x83,0x08, 0x3a,0x0a,0x64,0x66, 0xd2,0x82,0x11,0x57, + 0x96,0xb9,0x09,0xcd, 0x93,0x03,0x09,0xda, 0xaf,0x3c,0x81,0xb6, 0x3c,0x16,0x51,0x45, + // 0x1800: + 0xf9,0x55,0xae,0x36, 0x6d,0xc4,0x7f,0xe2, 0x92,0xb3,0xd7,0x13, 0xb4,0x83,0x53,0xdb, + 0x50,0xc4,0x9b,0x18, 0x15,0x3d,0x6e,0xa3, 0xa0,0x9d,0x63,0x71, 0x31,0x3c,0x15,0x0b, + 0x25,0xf3,0x5e,0xf4, 0x5e,0xfb,0xc0,0xc3, 0xb9,0x5e,0xf8,0xd3, 0xdb,0x49,0xfe,0x75, + 0xb3,0x7b,0x57,0x82, 0x03,0xba,0x9d,0xd7, 0x63,0x69,0x27,0x7b, 0x9b,0xbe,0xbe,0x0f, + 0x97,0x29,0x14,0x9e, 0xbc,0x95,0x24,0xec, 0xc0,0x6b,0x14,0xed, 0x30,0x24,0x94,0x73, + 0x3c,0xfd,0xf7,0x39, 0x7e,0x9a,0x69,0x6a, 0xa9,0x69,0xb1,0x51, 0x8e,0xde,0x0e,0x20, + 0x1d,0x0e,0x9e,0x23, 0xda,0xe5,0x69,0x00, 0x1d,0xa9,0xde,0x91, 0x92,0x99,0x3b,0x28, + 0x3b,0x67,0x98,0xd3, 0xd5,0xde,0xd8,0x7a, 0x39,0x7a,0xed,0x9e, 0xe9,0x3c,0x4f,0x28, + 0xe7,0xfd,0xda,0x7a, 0x8e,0x9c,0xcd,0x03, 0xd4,0x2e,0x77,0x35, 0x70,0x18,0x98,0x3a, + 0xd6,0x25,0xee,0xf1, 0x67,0x14,0xf0,0xc9, 0xcb,0x2f,0xc6,0xe5, 0x71,0x27,0x2e,0x15, + 0xf3,0xcf,0xf0,0xdf, 0xf1,0xd3,0x10,0x90, 0x24,0xe1,0x90,0xb2, 0x7b,0x04,0xde,0xbd, + 0x6e,0x08,0x68,0xe5, 0x94,0x4f,0xae,0x47, 0x43,0xd5,0xc9,0x07, 0xf3,0x42,0xda,0x25, + 0x7a,0x1d,0xb5,0x66, 0x47,0x0e,0x89,0x20, 0x8d,0x49,0x10,0xc0, 0xb5,0x54,0xd6,0x7b, + 0x5c,0xad,0x5f,0x39, 0xcc,0x57,0xce,0xf4, 0x8b,0x09,0x6b,0xae, 0x18,0x71,0xc1,0xa3, + 0x9a,0x4e,0x35,0xe6, 0x78,0xec,0xa9,0x74, 0x51,0xd0,0x5a,0x8e, 0x62,0xd9,0x8a,0x7d, + 0x38,0x53,0x54,0x56, 0x05,0x2e,0xde,0x41, 0x66,0x26,0xbb,0xf1, 0xac,0x5e,0x71,0x29, + // 0x1900: + 0x36,0x5f,0x29,0x82, 0x53,0x22,0x07,0x43, 0x9d,0x6f,0xdc,0x9e, 0x28,0xfd,0xe9,0xdf, + 0x90,0xb2,0xc1,0x4f, 0x65,0x02,0x55,0xa5, 0x2e,0x5a,0xf9,0x18, 0x36,0x27,0x4f,0x27, + 0xba,0xd5,0x18,0xed, 0xd8,0x39,0x23,0x2b, 0xed,0xfd,0x31,0xaf, 0x8c,0xbe,0x64,0x1b, + 0x2a,0x41,0x69,0x19, 0xe3,0x4d,0x5c,0xe6, 0x0e,0xc1,0x47,0x68, 0x57,0xe9,0x3f,0x97, + 0x5e,0xe0,0x81,0xab, 0xb5,0xb6,0xb1,0x29, 0x37,0x90,0x04,0x4e, 0x62,0x05,0x2b,0x62, + 0xcd,0xbd,0xe2,0x55, 0x6f,0x73,0xb6,0xcc, 0x70,0xc5,0xbf,0x12, 0xf2,0xcd,0xd3,0x8f, + 0x35,0x29,0x21,0xa0, 0xb0,0x41,0x52,0x1c, 0x73,0x47,0xe3,0x7a, 0x87,0x70,0xea,0xf7, + 0x95,0x08,0xe8,0xcd, 0xca,0x66,0x2a,0x90, 0xa2,0xf3,0x8e,0xd6, 0xa6,0xff,0x69,0xb8, + 0x57,0x02,0xed,0x9f, 0x36,0xfd,0x7d,0xc0, 0xac,0xaa,0xcf,0x4b, 0xb9,0xa5,0x87,0x40, + 0x5a,0x2b,0x70,0x3a, 0x39,0x7a,0x39,0x58, 0x15,0x16,0x10,0xc7, 0xe9,0x78,0xfe,0xc5, + 0xea,0xb5,0xeb,0x3e, 0x1a,0xed,0xbc,0x6e, 0x46,0x48,0x5c,0x09, 0x5a,0x24,0x7b,0xc6, + 0x1a,0xc5,0xa3,0x1a, 0x60,0xa4,0xe1,0x37, 0xdd,0x2f,0xc9,0x7e, 0x78,0xa5,0xc5,0x37, + 0x5c,0xa4,0xb6,0xf0, 0x1a,0x85,0xb4,0x9e, 0xa9,0xd6,0x33,0x58, 0xa8,0xe8,0x61,0x08, + 0xbf,0x0e,0x1c,0x58, 0x38,0x39,0xd0,0xaa, 0xf8,0xa5,0x68,0xdc, 0x10,0x7d,0x64,0xdd, + 0xcf,0x75,0x5f,0xb6, 0xfb,0xa5,0xdf,0xa7, 0xe7,0x53,0xd2,0xf7, 0xea,0x60,0x17,0x1e, + 0x69,0x32,0x76,0x1d, 0x78,0xc5,0x15,0x11, 0x0d,0xec,0x93,0x11, 0x39,0x54,0xb0,0x8f, + // 0x1a00: + 0x73,0x41,0x95,0x9b, 0x5d,0x02,0x02,0xff, 0x3c,0xa8,0xcd,0x64, 0x59,0x35,0x5d,0x55, + 0x5a,0xdb,0x76,0x1c, 0x29,0xa6,0x6c,0x2f, 0xe1,0xf5,0x7a,0xf5, 0xca,0x95,0x59,0x5c, + 0xd1,0xe6,0x98,0x68, 0x9f,0x24,0x40,0xef, 0xb4,0x67,0xf9,0xfa, 0x7e,0xd0,0xf7,0xe1, + 0x38,0x0e,0x91,0xf0, 0xc9,0x32,0x74,0x1d, 0xc7,0xe4,0xef,0xb1, 0x91,0xee,0xa1,0x87, + 0x77,0x50,0x88,0x4c, 0x4d,0x2c,0xc0,0xb4, 0xaa,0xbe,0xd3,0x6c, 0x37,0x56,0x28,0x68, + 0x08,0xa4,0x34,0x8c, 0x8e,0x97,0x9f,0x83, 0x0a,0x3d,0x57,0x84, 0x6a,0x7b,0x81,0x75, + 0x30,0xfc,0x2c,0x1a, 0xc1,0x28,0xd5,0x23, 0x60,0x63,0xb8,0x3d, 0x7e,0xd1,0x42,0x20, + 0x2c,0x6f,0xad,0x76, 0xf2,0x5a,0xca,0x01, 0x72,0x9e,0x54,0x63, 0x36,0x46,0xb3,0x4b, + 0x0a,0x17,0x10,0x29, 0x36,0x1b,0xc4,0x80, 0xfb,0x02,0x16,0x75, 0x41,0x83,0xb6,0x41, + 0x34,0x90,0x99,0xcb, 0x28,0x32,0x29,0x0a, 0x6b,0x49,0xde,0xc3, 0xcb,0xf9,0xb7,0x9c, + 0xfc,0x79,0x6a,0x5f, 0xb4,0x54,0xaf,0x10, 0xd8,0xe7,0x97,0xd0, 0xef,0x79,0xd1,0x0a, + 0x8e,0x40,0x6c,0xa3, 0x56,0x42,0xc9,0x43, 0xb5,0x0a,0xa0,0x3b, 0x25,0xba,0x72,0xad, + 0xd3,0xf6,0x23,0x0b, 0x08,0x49,0x6e,0xcd, 0x0e,0xa1,0x9a,0xc0, 0x2c,0x8a,0x03,0x44, + 0x2f,0xe2,0xe1,0x72, 0x8f,0x16,0x91,0x6b, 0x2b,0x63,0xa3,0xe5, 0x4c,0xec,0xb1,0xd0, + 0xc4,0xcb,0xe9,0xd8, 0x4c,0x9e,0xe6,0xa9, 0x72,0x0c,0x5d,0x6b, 0x84,0x35,0x07,0xbf, + 0x2d,0x98,0xba,0x6f, 0xf9,0x72,0xd0,0xf8, 0x4f,0x63,0x30,0x77, 0xbe,0xba,0xe5,0x91, + // 0x1b00: + 0xbd,0xb3,0x48,0xa7, 0x87,0x23,0x43,0xcc, 0x33,0x1b,0x9f,0x90, 0xfe,0x81,0xd9,0x20, + 0xdb,0x84,0xb4,0xf8, 0xb8,0x9d,0x05,0x3a, 0x0a,0xa0,0xa0,0x0b, 0x1e,0xdb,0x2c,0x11, + 0xc7,0xf4,0x20,0x01, 0x5c,0x32,0xea,0x80, 0x25,0xea,0xe2,0x22, 0x81,0xda,0x5d,0xb1, + 0x2a,0xa2,0x7f,0x0d, 0xc0,0x20,0xc6,0xc0, 0x2d,0xcc,0xd3,0x56, 0xda,0xb4,0x4a,0x6f, + 0x91,0xbf,0x1d,0xd1, 0xa3,0xf7,0x41,0x83, 0x9a,0x55,0x52,0xb4, 0x47,0x25,0xcd,0x4a, + 0xda,0xc4,0xe4,0x6b, 0xa9,0xc5,0x95,0xad, 0xbe,0xc5,0xdd,0x3a, 0xcc,0x94,0x79,0x5c, + 0x71,0xa8,0x45,0x0c, 0x0c,0xe4,0x7e,0x24, 0xff,0xa9,0x59,0xd2, 0xd7,0x31,0x76,0xa7, + 0x85,0xd2,0x40,0x32, 0xa1,0x85,0x91,0x54, 0x7b,0xfb,0xbf,0x06, 0x07,0x09,0xa5,0x81, + 0x76,0x3b,0xf7,0xe2, 0x8f,0x69,0x7b,0xc4, 0xf1,0xcd,0x2c,0x79, 0xf3,0x22,0x1f,0x56, + 0x3d,0x19,0x7a,0x8b, 0x7c,0xe7,0xd5,0xdf, 0xfc,0x9c,0x66,0xc3, 0x9e,0x92,0x19,0x18, + 0x67,0x1c,0xc4,0xfe, 0xe7,0x63,0xb0,0xeb, 0xd5,0xfb,0x92,0xce, 0x88,0xfa,0xdd,0xea, + 0x68,0x16,0xb0,0x9c, 0x68,0xc3,0x9d,0xde, 0xca,0x2e,0xea,0xeb, 0x53,0x9f,0xda,0x74, + 0x6e,0xea,0x38,0xaa, 0x15,0xae,0xae,0x37, 0x0a,0xf3,0xc9,0x70, 0xcb,0xe1,0x66,0xfb, + 0x8c,0xb5,0x84,0xf1, 0xd9,0x9a,0xf9,0x84, 0xb6,0x3d,0xd8,0x6b, 0xda,0xbd,0x5c,0x3d, + 0x5d,0x0e,0x8d,0x09, 0x15,0xf0,0xe7,0xe6, 0x72,0xea,0x29,0xe9, 0x79,0x8a,0x0e,0x96, + 0x6e,0x72,0xff,0x6b, 0xff,0x6e,0x69,0xf8, 0xbc,0x8b,0x45,0xa8, 0x45,0x96,0x8c,0x82, + // 0x1c00: + 0xd7,0x94,0x9d,0x39, 0x9e,0x0a,0x16,0xc7, 0xa8,0xdf,0xd5,0x26, 0x8a,0x90,0xe7,0xbc, + 0x20,0x32,0x94,0xde, 0x19,0x44,0xc1,0x4a, 0x31,0x1a,0xc3,0x42, 0x25,0x00,0x1b,0x8a, + 0x1b,0x79,0x03,0x28, 0x13,0xc9,0x91,0x25, 0x16,0x8e,0xf3,0x58, 0x4a,0x00,0x55,0xc6, + 0xb9,0xda,0xc9,0xd0, 0x6e,0xd4,0xdb,0x58, 0x70,0x17,0x42,0x40, 0x8a,0x7f,0x9d,0x54, + 0x8a,0xca,0xf5,0xb9, 0xe6,0x73,0xbb,0xb4, 0x40,0x28,0x7b,0x59, 0x5a,0xf8,0x11,0x1e, + 0x15,0x2d,0x6e,0xc1, 0x9a,0x43,0xd8,0xba, 0x76,0xd4,0xd1,0x1c, 0xd3,0x5e,0xaa,0xdd, + 0xe5,0x1b,0x30,0xa9, 0x8a,0xde,0xe1,0x54, 0x81,0xc2,0x56,0xd5, 0xa4,0x30,0x63,0x3e, + 0x60,0x3e,0x60,0x3e, 0xea,0xe7,0x06,0xe6, 0xa0,0xd6,0xd4,0x3e, 0xd0,0xda,0x76,0xf1, + 0xaa,0x63,0xac,0x6b, 0x94,0xb2,0xba,0x72, 0xfa,0x41,0x02,0x50, 0x50,0x20,0x6c,0xc1, + 0xea,0x3e,0x0a,0x5e, 0x4d,0x82,0x85,0x3f, 0xf8,0xbb,0x92,0x29, 0xc2,0x84,0x81,0xed, + 0x0f,0xab,0x20, + // 0x1ca3 = 7331 Bytes +}; // ctcode_ovr1_usa_bz2[] + +//----------------------------------------------------------------------------- +// ctcode_ovr1_jap_bz2 (rmcj/ovr1.bin) + +const u8 ctcode_ovr1_jap_bz2[7364] = +{ + 0x00, 0x00, 0x53, 0x64, // 21348 Bytes decompressed + // 0x00: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x22,0xcf, 0xc1,0x5b,0x00,0x0e, + 0x7b,0x7f,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe0, 0x19,0x30,0x7a,0xfb, 0x9f,0x7a,0x71,0x4b, 0xd8,0x68,0xbe,0xda, + 0x12,0xda,0xd9,0x10, 0x0d,0xb4,0x9a,0x5a, 0xc1,0x95,0x61,0x35, 0xa0,0x0b,0x65,0x1d, + 0xd9,0x76,0x98,0xbb, 0x8d,0x2e,0x6a,0xaa, 0xda,0xec,0x32,0x2a, 0xb3,0x02,0x3e,0x7b, + 0x6a,0x6f,0xbb,0x81, 0x11,0x29,0x04,0x0d, 0x48,0x13,0x12,0x9e, 0xd1,0x53,0x7b,0x49, + 0xa8,0xd3,0x64,0x65, 0x19,0x26,0x32,0x69, 0xa9,0xb2,0x9e,0x26, 0x4d,0x34,0x6a,0x3c, + 0x89,0xe9,0xa3,0x4d, 0x02,0x64,0xf2,0x6a, 0x6d,0x27,0xa6,0x83, 0xd2,0x9e,0x24,0xf4, + 0x9b,0x20,0x35,0x3c, 0x53,0x62,0x6a,0x6d, 0x4d,0x3d,0x47,0xea, 0x68,0x9a,0x78,0xa1, + 0xe2,0x4c,0xd2,0x1a, 0x69,0x88,0xd1,0xb2, 0x6a,0x3d,0x04,0xc9, 0xb2,0x10,0xf2,0x6a, + 0x7a,0x86,0x1e,0x94, 0x79,0x31,0xa5,0x34, 0xf4,0x9b,0x53,0xda, 0x53,0xc5,0x33,0x50, + 0x68,0x88,0x20,0xc0, 0x53,0x46,0x86,0xa7, 0xa4,0x6d,0x19,0x4c, 0x35,0x36,0x93,0x4d, + 0xaa,0x78,0xa6,0x7a, 0x93,0x0d,0x23,0x27, 0xa9,0xa6,0x0d,0x4c, 0xd4,0x0c,0x9a,0x32, + 0x7a,0x98,0x26,0x43, 0x11,0xb5,0x18,0x83, 0xd4,0x68,0x1a,0x68, 0x68,0x34,0x68,0x06, + 0x81,0xa3,0x4d,0x34, 0x33,0x53,0x4d,0x3d, 0x46,0x80,0x68,0xc8, 0xc9,0xa6,0x86,0x46, + // 0x100: + 0x9e,0xa6,0x4d,0x03, 0x41,0xa6,0x99,0x34, 0xc8,0x63,0x48,0x24, 0x90,0x02,0x46,0xa1, + 0xaa,0x9f,0xa3,0x45, 0x1e,0x4d,0x27,0xa9, 0xea,0x79,0x4f,0x50, 0x6c,0xa6,0x4f,0x53, + 0x4f,0x4c,0xa7,0xa6, 0xa0,0xd9,0x10,0xf4, 0xca,0x6d,0x43,0x4c, 0x87,0x94,0xf5,0x30, + 0x26,0x46,0x9e,0xa3, 0xd4,0x19,0x3d,0x40, 0x34,0x68,0x7a,0x80, 0x3d,0x4f,0x42,0x00, + 0x07,0xa8,0x00,0x3d, 0x47,0xa4,0x00,0xd0, 0x6d,0x47,0xa4,0x1e, 0xa6,0x9a,0x32,0x01, + 0x86,0xa0,0x32,0x0d, 0xa8,0xf4,0x83,0x32, 0x81,0x22,0x21,0x04, 0x20,0x9a,0x32,0x4c, + 0xa9,0xed,0x53,0xd2, 0x7b,0x49,0xea,0x9e, 0x9e,0x51,0xe5,0x34, 0xd4,0xd3,0xf4,0xa7, + 0xb4,0xca,0x7e,0xaa, 0x68,0xf2,0x9f,0xa9, 0xe8,0xd5,0x1f,0xa9, 0x0d,0x1a,0x7e,0xa4, + 0x7a,0x9e,0x49,0x91, 0xe5,0x3f,0x48,0x4f, 0x50,0x7e,0x89,0xb4, 0x91,0xe5,0x0f,0x6a, + 0x9b,0xd1,0x43,0xd4, 0xd0,0xd0,0xfd,0x53, 0xd2,0x6c,0xa3,0xd4, 0x79,0x4c,0x34,0x8c, + 0x9b,0x46,0x53,0xda, 0xa7,0xa3,0x29,0xa7, 0x92,0x3f,0x54,0xf2, 0x9e,0xa6,0x9b,0x50, + 0x64,0x68,0x1a,0x7a, 0x35,0x00,0x34,0x7b, 0x54,0x7a,0x86,0x84, 0x28,0x90,0x7a,0x84, + 0xc4,0xc0,0x6a,0x62, 0x68,0x18,0x26,0x01, 0x0c,0x98,0x8c,0x8d, 0x34,0x34,0x60,0x46, + 0x4c,0x11,0x80,0x10, 0x69,0xa1,0x93,0xd1, 0x03,0x09,0x84,0x19, 0x0d,0x06,0x4c,0x13, + 0x4c,0x4d,0x30,0x00, 0x00,0x00,0x13,0x46, 0x98,0x8d,0x19,0x30, 0x04,0x32,0x34,0xc4, + 0x0c,0x20,0x31,0x18, 0x04,0x8a,0x20,0x4c, 0x80,0xa7,0xa1,0x36, 0xa9,0x9a,0x53,0xc9, + // 0x200: + 0xa9,0xf9,0x09,0x3c, 0xd4,0x11,0xea,0x6f, 0x28,0x9a,0x6d,0x34, 0xd3,0x53,0x35,0x0f, + 0x48,0xc4,0xda,0x19, 0x4d,0x3d,0x1a,0x9e, 0x93,0x69,0x19,0x34, 0x06,0xd4,0xda,0x02, + 0x68,0x00,0x7a,0x26, 0xd4,0x19,0xa9,0x93, 0x00,0x46,0xd4,0x34, 0xd1,0xea,0x7a,0x26, + 0x83,0x46,0x4d,0xa4, 0xda,0x9e,0x93,0x46, 0x87,0x91,0xa9,0xea, 0x06,0x6a,0x31,0x34, + 0x68,0x68,0x0d,0x1b, 0x29,0x63,0x11,0xb5, 0x98,0x7b,0x20,0xd3, 0xf8,0x64,0xd3,0xb2, + 0xa5,0xaa,0xe7,0xb8, 0x3b,0x23,0xcc,0xa2, 0x4e,0x08,0x82,0xd5, 0xac,0x57,0xcf,0xd3, + 0x56,0xa8,0x05,0xdc, 0xc4,0xe1,0x96,0x36, 0xed,0x78,0x49,0x63, 0x23,0x90,0xe7,0x6e, + 0x8c,0x45,0x40,0x29, 0xc7,0x41,0x55,0xd7, 0x75,0xd5,0xfc,0xaa, 0x71,0x2a,0x28,0x82, + 0xc6,0xc9,0x27,0x5b, 0x74,0x03,0x32,0x28, 0x12,0x29,0x8b,0x0b, 0x21,0xb0,0x93,0x1d, + 0xb9,0x50,0xd0,0x9c, 0x43,0xa1,0x07,0x21, 0x5b,0xc9,0x85,0xc4, 0x72,0xc0,0x22,0xa0, + 0xc0,0x9e,0x73,0x33, 0x42,0x52,0xae,0xac, 0x4f,0x8c,0xbb,0xe3, 0xc2,0x08,0x03,0x9d, + 0x7e,0x59,0x26,0xc4, 0x5a,0xbc,0x90,0x26, 0xb8,0xb2,0xca,0x90, 0xc8,0xe1,0x00,0x15, + 0x0f,0x2d,0x5d,0x0f, 0x5d,0x18,0x26,0x65, 0xf2,0x63,0x71,0x9c, 0x82,0x45,0x05,0xf8, + 0x9b,0xc5,0xd4,0x16, 0x85,0xb0,0xb3,0xbc, 0xfc,0xe8,0xbf,0x43, 0x29,0x0a,0xb5,0x01, + 0xf4,0x7a,0x1c,0xb9, 0x82,0x78,0x2b,0x16, 0xce,0x56,0x47,0x2a, 0x0a,0xed,0x36,0x00, + 0xad,0xc1,0xc8,0xc8, 0xd6,0xc5,0x7a,0xd6, 0xb3,0x41,0xa0,0x50, 0x17,0x4e,0x64,0x08, + // 0x300: + 0x67,0xcc,0x10,0x12, 0xb5,0xe8,0xe2,0x58, 0x01,0x82,0x0f,0x58, 0x1d,0xe3,0x06,0x62, + 0x02,0x21,0xc0,0x75, 0x24,0x54,0x8c,0x42, 0x47,0x3f,0x98,0x2a, 0x44,0xa1,0x11,0x38, + 0x69,0x0a,0x04,0xa1, 0x9b,0x33,0x10,0x10, 0x9a,0x9e,0x40,0x29, 0x46,0x01,0x6b,0xf1, + 0x33,0xc4,0x12,0x44, 0x53,0xe6,0x00,0xf7, 0xe3,0xec,0x18,0x69, 0x52,0x20,0xa0,0xe6, + 0x53,0x1c,0x7c,0xd2, 0xf1,0xcb,0x5f,0xaf, 0xb7,0xff,0x96,0x0e, 0xef,0x6d,0xee,0x24, + 0x11,0xae,0xd6,0xea, 0xf9,0x29,0xe5,0xeb, 0x98,0x69,0x58,0xbc, 0x5d,0xcc,0x38,0x7d, + 0x9d,0xd8,0xa3,0x55, 0x5b,0x51,0xfb,0xa4, 0x0c,0x79,0xbe,0x7c, 0x56,0x6f,0xf5,0x78, + 0x3e,0x47,0x9b,0xf5, 0x80,0xb7,0x3e,0x50, 0x29,0xbe,0xb0,0xec, 0xbd,0xdf,0x62,0x97, + 0x36,0x3f,0x70,0x8c, 0xe7,0x94,0x96,0x19, 0xa2,0xe2,0x0e,0x47, 0x48,0xd7,0x3e,0xe3, + 0x30,0x00,0x8e,0xc8, 0xe0,0x7f,0xca,0xf0, 0x70,0x0a,0x77,0x78, 0xc2,0x04,0x3c,0x01, + 0x7b,0x71,0xe2,0x7f, 0x96,0x58,0xb3,0x32, 0xc1,0x13,0x69,0xfc, 0x6c,0x14,0xa4,0x70, + 0x20,0x37,0x13,0x69, 0xa7,0x21,0x02,0xa3, 0x34,0x7a,0xba,0x23, 0x03,0x9a,0xfa,0xd3, + 0x0f,0xbb,0xc4,0x31, 0x61,0x85,0xe8,0xc0, 0x05,0x5a,0x0f,0xc2, 0x03,0x42,0x7c,0x4e, + 0x62,0x2c,0x02,0xf1, 0x80,0x8d,0xab,0xa0, 0x73,0x37,0x4a,0xe3, 0xf9,0x70,0x70,0x3b, + 0x2d,0xb6,0x1c,0x27, 0xa1,0x63,0xe2,0xfd, 0x6c,0x7c,0x27,0xb4, 0xf0,0xbf,0xb6,0x6e, + 0x88,0x29,0x93,0xda, 0x58,0x92,0x34,0x88, 0x84,0x10,0x23,0xb3, 0xcb,0x24,0x36,0x22, + // 0x400: + 0x20,0x5c,0x40,0x98, 0x78,0x01,0xdb,0x96, 0x59,0x6e,0x00,0x2f, 0x24,0x32,0xad,0xc2, + 0x02,0x39,0xa6,0xa2, 0x21,0x2d,0xee,0xfd, 0xae,0x45,0x41,0x73, 0x5e,0xc2,0x05,0xef, + 0xfd,0xcd,0x03,0xdd, 0x1d,0x31,0xb9,0x48, 0xd9,0xf8,0xce,0xf4, 0xbe,0x94,0xe8,0x22, + 0x20,0xff,0x62,0x3a, 0x21,0xc4,0x38,0x6f, 0xd1,0xaa,0xa6,0x0d, 0x24,0x36,0x26,0xc1, + 0x24,0xb6,0x89,0x8d, 0xa4,0x96,0x51,0x21, 0x07,0x5a,0xd1,0xf5, 0x2d,0x08,0x4a,0x66, + 0x2f,0xef,0xe4,0x0e, 0x08,0xf4,0x26,0xb0, 0x82,0x49,0x2c,0xb8, 0xd0,0x72,0xdd,0x86, + 0xb7,0xa2,0xd6,0x9d, 0x51,0xed,0xcd,0x76, 0xbb,0x39,0x89,0xaf, 0xae,0x09,0x62,0xb4, + 0x83,0xd9,0xdb,0x85, 0xf3,0x00,0x50,0x18, 0x96,0x89,0xa2,0x47, 0xbb,0x7b,0x26,0x94, + 0x8c,0x42,0x48,0x5f, 0x54,0x68,0x4a,0xd9, 0xc6,0x7a,0x08,0x82, 0xa5,0x9c,0xab,0x48, + 0x0b,0x4e,0xf9,0x89, 0x61,0x64,0xa0,0x22, 0x94,0xd2,0x83,0xc2, 0x76,0xd8,0x09,0x74, + 0xe1,0x9d,0xd9,0xb6, 0xdb,0x98,0x84,0x26, 0xdf,0x18,0xa0,0x51, 0x60,0xcd,0x26,0x20, + 0xb0,0xd2,0x47,0x30, 0xd0,0xb3,0xb4,0x63, 0xbb,0xad,0x7e,0x47, 0x6c,0xc0,0x55,0x31, + 0xb1,0x05,0x0c,0x41, 0x5b,0x49,0xce,0x44, 0x48,0xf0,0x1a,0xa2, 0x54,0x40,0x11,0x06, + 0x92,0xa1,0xa4,0xa2, 0xd2,0x4b,0x3d,0xd0, 0x73,0x3c,0xeb,0x91, 0x74,0xed,0x05,0xd6, + 0x87,0x92,0x82,0x48, 0xa1,0xa0,0x47,0x98, 0xc8,0x31,0x08,0xff, 0x4f,0x9f,0xe1,0x6e, + 0x37,0xfa,0x1a,0x8a, 0xeb,0xc8,0x82,0x19, 0xb7,0xea,0x23,0x5e, 0x28,0x8a,0x30,0x19, + // 0x500: + 0xd5,0xc0,0x84,0xa1, 0xa4,0x83,0xb1,0x19, 0x11,0x14,0x40,0x64, 0x40,0x81,0x01,0xaa, + 0xc5,0xc6,0x5a,0x82, 0x8d,0x81,0x2c,0x2a, 0x14,0x51,0xf0,0x29, 0x72,0xe5,0xd3,0x5e, + 0xaf,0x4d,0x35,0x28, 0x4b,0x12,0xc5,0x28, 0x60,0x24,0xbe,0x90, 0x1d,0xb0,0xc8,0x24, + 0xdb,0x45,0x1b,0x06, 0x44,0x95,0x62,0x63, 0xae,0xd0,0x1f,0xa9, 0xe2,0x4d,0x09,0xd5, + 0xad,0xae,0x6c,0x69, 0x33,0x87,0xd0,0x0e, 0x80,0x34,0xc2,0x48, 0xe2,0x8b,0xc9,0xc0, + 0xba,0xad,0xca,0xdc, 0x56,0xcf,0xf3,0x62, 0x11,0x12,0x91,0x88, 0x5d,0xe4,0xc2,0x2f, + 0xb4,0xce,0x53,0x51, 0xd8,0x81,0xa3,0x2f, 0x95,0xdb,0xae,0x64, 0x1d,0x12,0x12,0x7a, + 0x2a,0xbb,0x71,0x8a, 0x4e,0x6f,0x49,0xde, 0x83,0xb9,0x20,0x05, 0xa6,0x0a,0x60,0xe4, + 0xfa,0x66,0xb5,0x63, 0x43,0xee,0x53,0x52, 0x64,0xa6,0x38,0xb3, 0x59,0x95,0xc8,0xfb, + 0x0c,0x15,0x7c,0x8b, 0x29,0x2a,0x50,0x7c, 0x86,0x20,0xc0,0x23, 0x01,0x03,0x18,0xd9, + 0xa5,0xa4,0xd7,0x9b, 0x21,0xf9,0x0c,0x0a, 0xa4,0x72,0x48,0x23, 0x91,0xc3,0x07,0xd3, + 0xbd,0x6f,0x3a,0x9b, 0xaa,0x6b,0x4e,0xe6, 0xcc,0x68,0x23,0x04, 0x98,0xc9,0xab,0xeb, + 0xb8,0x19,0x98,0x5e, 0x02,0xf8,0x81,0xe9, 0xe2,0xab,0x83,0x6b, 0xf1,0x5d,0x6d,0xb8, + 0xae,0xa0,0x6c,0xa0, 0x39,0x06,0x19,0xdb, 0xb2,0x83,0xe1,0xea, 0x53,0x89,0x83,0x6d, + 0x52,0x49,0x26,0x2b, 0x1f,0x27,0x93,0xf3, 0xe8,0x14,0xa8,0x29, 0x36,0xa4,0xd8,0xdc, + 0x24,0xbe,0x2b,0x02, 0xbf,0x32,0xc0,0x3c, 0x20,0xa5,0x64,0xd2, 0xda,0x41,0x70,0xc4, + // 0x600: + 0xb6,0x01,0xa4,0x35, 0x9e,0xcf,0x44,0x11, 0x38,0x77,0xa7,0xda, 0x82,0xe5,0x42,0xa1, + 0x21,0xb8,0x66,0x3e, 0x43,0xe3,0x3b,0x01, 0x60,0x02,0x3e,0x32, 0x81,0x48,0x99,0xdb, + 0x86,0x66,0x11,0xce, 0x58,0xfc,0xee,0xf4, 0xe5,0x5b,0x74,0x93, 0xa5,0x5b,0xcc,0xf1, + 0x27,0xb7,0xd3,0x89, 0x9f,0xe7,0x61,0x13, 0xca,0x53,0x02,0x28, 0x83,0xbf,0x95,0x72, + 0xcf,0xb9,0xd4,0x64, 0x3b,0x34,0xdb,0x84, 0x9f,0x00,0x3a,0x0e, 0x84,0xeb,0x44,0xce, + 0xac,0x51,0xec,0x2b, 0x88,0x2b,0x29,0xeb, 0xb5,0xa4,0x4c,0x91, 0x01,0x5c,0x75,0x0d, + 0x50,0x4e,0xc7,0x61, 0x58,0x43,0x13,0x5b, 0x8e,0x21,0xc9,0x85, 0x13,0xb3,0x1a,0xf3, + 0x9c,0x20,0xf6,0x98, 0xe2,0x3f,0xc0,0x40, 0xd3,0x06,0xfd,0x87, 0x52,0xdc,0x7a,0x8d, + 0x3c,0x6a,0xd3,0x93, 0xb6,0xab,0xea,0x0a, 0x9b,0x0b,0xa4,0x8a, 0xfe,0xa9,0xc6,0xb2, + 0x20,0xb0,0x57,0xca, 0x04,0xb5,0x0a,0x5f, 0x13,0xca,0xcb,0x24, 0xb4,0x2a,0x4b,0xc8, + 0xcd,0xb2,0xca,0xb8, 0x04,0x8a,0xc2,0x99, 0x38,0xa1,0x7a,0xb7, 0x15,0x44,0x34,0x2b, + 0x68,0xa1,0xe6,0x3a, 0x12,0x53,0x64,0xcc, 0x20,0xa7,0xf2,0x52, 0x04,0x31,0xa8,0x16, + 0x6b,0x29,0x09,0x46, 0xc1,0xe0,0xb8,0x12, 0xe3,0xdc,0x20,0x31, 0x99,0x69,0x20,0x1b, + 0x53,0x0c,0xd2,0x15, 0xd4,0x82,0x25,0xe9, 0x70,0x5a,0x2b,0x06, 0xf0,0xaf,0x9d,0xa1, + 0x1c,0xc2,0x0c,0x38, 0x49,0x14,0x68,0x18, 0x61,0x11,0xa0,0x26, 0x81,0x95,0x9e,0x36, + 0xba,0x98,0x86,0x04, 0x88,0x84,0xec,0x21, 0xe2,0x62,0xe0,0x31, 0xce,0x68,0xab,0x86, + // 0x700: + 0xdb,0x68,0x08,0xd7, 0x1a,0xf6,0x36,0x99, 0x0c,0x84,0xe2,0x21, 0x44,0x44,0x43,0xcc, + 0xfc,0x1c,0x2f,0x5c, 0x03,0x22,0x32,0x09, 0xbb,0x1e,0xf0,0x5f, 0xb7,0xf3,0xa7,0xff, + 0xbc,0xd8,0xb1,0x3f, 0xe5,0xed,0x97,0xa7, 0x0a,0xd1,0xa9,0xab, 0x0b,0x72,0x30,0x94, + 0xcb,0x6a,0x62,0x7d, 0xcb,0x79,0xbf,0x65, 0x64,0x36,0xf8,0x11, 0x8b,0xc3,0xbc,0xaf, + 0xe2,0x17,0x9a,0xbd, 0x5a,0x7e,0xbe,0xb1, 0x41,0x13,0x8c,0x2b, 0x2e,0x85,0x05,0x98, + 0x55,0xad,0x9e,0xba, 0xeb,0x19,0x74,0xb4, 0x23,0x74,0xfe,0xb3, 0xb4,0xe6,0x44,0xec, + 0x3d,0x02,0x49,0x73, 0x17,0xc4,0x1e,0xd7, 0xb9,0xdd,0x15,0x89, 0xa4,0x94,0x8c,0xc1, + 0xd5,0x3f,0x00,0x51, 0x50,0x26,0x6a,0x84, 0xa8,0x31,0xd0,0xa0, 0xa1,0xd3,0x61,0x22, + 0x87,0x4e,0xb1,0xe0, 0xd7,0x7a,0x7f,0x19, 0xd2,0x74,0x8c,0xcf, 0x7e,0xdc,0xf9,0x25, + 0x4a,0x53,0x08,0x96, 0x35,0xe2,0x5a,0xf3, 0x91,0xf3,0xab,0x22, 0x02,0x58,0xf9,0xb6, + 0x40,0xe5,0xc6,0x55, 0x29,0x01,0x3e,0x2c, 0x4e,0x94,0x9c,0x97, 0x4f,0x7b,0x73,0x28, + 0x48,0x94,0xa6,0x1f, 0x19,0xd0,0xef,0x74, 0x93,0x06,0x96,0x05, 0x09,0xf6,0x15,0x88, + 0xe6,0x85,0x97,0x36, 0x26,0xe0,0xec,0xf4, 0x7d,0x4c,0x44,0x5e, 0x65,0x30,0xc5,0xc8, + 0x57,0x24,0xbf,0x19, 0x95,0x07,0xc8,0x2c, 0x29,0x2a,0xc9,0x94, 0x50,0x9d,0xac,0x23, + 0x97,0xaa,0x5c,0xec, 0x96,0x3b,0x51,0x9a, 0x0b,0xf1,0x6d,0xd6, 0x1f,0x19,0xc0,0x64, + 0x6f,0xd7,0xa8,0xb8, 0x88,0x4f,0xa4,0xcd, 0x91,0x78,0x4c,0x3b, 0xba,0x88,0x8b,0xa1, + // 0x800: + 0xc8,0x65,0x60,0x58, 0x69,0x5f,0x4a,0xb0, 0xf2,0xab,0x8f,0x35, 0xe7,0xb9,0x64,0xb3, + 0xd4,0x8f,0x7f,0x48, 0xa0,0xa4,0x2b,0x0f, 0x9e,0x64,0x81,0xae, 0x1b,0x7c,0x1a,0x1a, + 0x99,0x13,0xf8,0xde, 0x3c,0xc0,0x75,0xbc, 0x56,0x97,0x18,0xc6, 0x31,0x4c,0x61,0x84, + 0xe8,0x99,0xea,0x01, 0x8c,0x82,0x52,0xca, 0xdc,0xc6,0xce,0x69, 0x52,0xf0,0x4e,0x45, + 0x65,0x4b,0xb7,0x02, 0x54,0x36,0xba,0x06, 0x4f,0x49,0x2a,0xe2, 0x24,0xe4,0x67,0x5a, + 0xea,0x11,0xb7,0x9c, 0x3b,0xbc,0x0c,0xa4, 0x42,0x56,0xd5,0xde, 0x0b,0x57,0x03,0xfd, + 0x04,0x7c,0x7c,0xd7, 0x24,0xdd,0xd1,0x22, 0xb0,0x4f,0x5c,0x5d, 0x22,0x68,0xfa,0x0a, + 0x8a,0x2f,0xbe,0x57, 0x7c,0xcb,0x44,0x78, 0xf4,0x6e,0x0b,0x2a, 0x27,0xd8,0xf6,0xb2, + 0x43,0xf6,0x36,0x2b, 0x38,0x42,0xd8,0x79, 0xfa,0xcd,0xc2,0x6c, 0x84,0x7a,0xbb,0xa4, + 0x53,0x83,0x5b,0x82, 0x6c,0xba,0x47,0x72, 0x18,0x23,0xf9,0x57, 0x51,0x17,0x58,0xa9, + 0x50,0x43,0x54,0x37, 0xf0,0x08,0xf0,0x09, 0x0d,0x26,0x55,0x26, 0x34,0x51,0xaf,0xe6, + 0x82,0x21,0x92,0x8d, 0xdc,0x8a,0x3f,0x90, 0x41,0x42,0x61,0xa3, 0x9b,0x28,0x20,0x86, + 0xb4,0xaf,0x8a,0x8b, 0x7a,0xbf,0xd4,0x85, 0x60,0x06,0xc6,0x7a, 0x2a,0x9d,0x07,0xcd, + 0x8f,0x48,0xdb,0x6e, 0x62,0x0f,0xae,0x18, 0xc6,0x33,0x7c,0x46, 0xd9,0xbd,0xe4,0x2d, + 0x5c,0xf8,0xc3,0x38, 0x3e,0x41,0xaf,0x51, 0x9c,0x99,0x15,0x5b, 0x87,0x75,0x82,0x0f, + 0x81,0x69,0x50,0x38, 0x2d,0xf6,0xc6,0xf1, 0x5a,0xa3,0xa5,0xa1, 0x1e,0x1b,0x17,0xbd, + // 0x900: + 0xc2,0x29,0xd8,0x2b, 0x76,0xc8,0xbd,0xd1, 0xa0,0x91,0xc2,0x49, 0xe4,0xb1,0xdd,0x5f, + 0xf0,0x6a,0x8b,0xbf, 0x5d,0xdf,0x28,0x85, 0x70,0xba,0x91,0x6d, 0x3e,0x54,0xad,0xd8, + 0xaa,0xdb,0xaa,0x1a, 0x71,0x6d,0xe7,0x7a, 0x2e,0xe6,0xe5,0x99, 0xdc,0x86,0x74,0x78, + 0x2f,0x11,0x73,0xd7, 0x87,0xae,0x28,0x4d, 0x99,0x18,0x28,0xb9, 0x0b,0xdc,0x15,0x46, + 0x73,0x64,0xe6,0xb9, 0xc5,0x53,0x27,0xd1, 0x90,0x06,0xb3,0x15, 0x23,0x04,0x01,0xcf, + 0x75,0xac,0xcb,0xbe, 0x19,0xf2,0xe0,0x8c, 0xfe,0x63,0x33,0xed, 0x12,0xe8,0x78,0x2c, + 0xaa,0x2c,0x16,0x9d, 0x81,0x8c,0xeb,0x8b, 0x5c,0xbd,0xf4,0x22, 0x48,0x42,0xea,0x98, + 0x88,0xfa,0x95,0x8e, 0x4a,0x87,0xc1,0x28, 0xae,0x68,0xf3,0x6a, 0x28,0xda,0xf7,0x5f, + 0x26,0xe2,0xaa,0xe6, 0x31,0xee,0x87,0x70, 0x68,0xb5,0x64,0x9b, 0x57,0x34,0x5b,0xe4, + 0x89,0xa3,0x2a,0x25, 0x06,0x0e,0x1c,0x91, 0x00,0x66,0x9b,0x46, 0x5c,0xcb,0x6a,0x82, + 0x60,0xa2,0x7d,0xbf, 0x03,0x93,0xc9,0x94, 0x5a,0xf9,0xbd,0xf6, 0xf2,0x6e,0xe4,0xc9, + 0x7c,0x0b,0x36,0x3d, 0xb6,0xa7,0x21,0x71, 0x5c,0x1a,0xc8,0x19, 0xf6,0x71,0x70,0x80, + 0xd9,0x7c,0xf1,0x23, 0xa9,0xaa,0x82,0x05, 0x63,0xcf,0x39,0x7a, 0x02,0x55,0x52,0x5d, + 0x26,0xf9,0xd1,0xe0, 0x05,0x19,0x60,0x32, 0xe3,0xbd,0xdb,0x50, 0xea,0xf3,0xe6,0x49, + 0x9c,0xde,0x93,0x00, 0x3e,0x9b,0x50,0xc7, 0xd7,0x70,0x22,0xbd, 0x16,0x7d,0xfc,0x98, + 0x8e,0xad,0x58,0x8d, 0x5b,0x8a,0x1d,0x7f, 0x37,0x0b,0xb0,0x2f, 0x57,0x12,0xbe,0x62, + // 0xa00: + 0x94,0xb9,0x6d,0x75, 0x00,0x9e,0x0c,0x6e, 0x96,0xa7,0xab,0xda, 0x42,0xa2,0xc6,0xb0, + 0x10,0xa2,0x34,0x20, 0x0f,0x79,0x55,0x01, 0xe0,0x71,0x09,0x70, 0xe1,0x85,0x88,0x04, + 0xfb,0x7d,0xa5,0x08, 0x30,0xd0,0xdf,0x98, 0xb3,0x1e,0xbe,0xbb, 0x8d,0x47,0x12,0x8e, + 0x3a,0xfe,0x83,0xf3, 0x75,0xf8,0x22,0x1b, 0xfa,0xcb,0xa8,0x50, 0xa1,0x98,0xa2,0xb3, + 0x79,0xe7,0x3f,0xb2, 0xd1,0xdf,0x58,0xd0, 0x38,0xce,0xf3,0xb7, 0xeb,0x63,0x82,0x3b, + 0x77,0x7b,0xce,0x87, 0x56,0xd6,0x1e,0xcc, 0xe7,0x01,0xf3,0x3e, 0x78,0x28,0xb8,0x67, + 0x39,0x9e,0x5e,0x51, 0x6a,0xa9,0x44,0x06, 0xde,0x14,0x71,0x35, 0xc8,0x09,0x13,0x65, + 0xa8,0xda,0xa2,0x73, 0x07,0xda,0xce,0xc0, 0x1e,0xba,0xb5,0x24, 0xce,0x07,0x1d,0x3b, + 0x25,0xa4,0xc9,0xdc, 0xdc,0x73,0xad,0x12, 0xb8,0x29,0x55,0xf2, 0x20,0xf7,0xcb,0x46, + 0xf1,0x68,0x20,0x40, 0x5e,0x90,0x55,0xb7, 0xcd,0x84,0x8a,0xe4, 0x61,0x54,0xee,0x05, + 0xea,0x80,0xb7,0x91, 0x38,0xe9,0xe6,0x2a, 0x55,0x9d,0x60,0xce, 0x36,0xb7,0xba,0x7b, + 0xf9,0x03,0x31,0x15, 0xae,0x64,0x92,0x23, 0xa6,0xa2,0x78,0x81, 0x73,0x46,0xab,0xc7, + 0x85,0x86,0x3a,0x6d, 0x42,0xd2,0x24,0xed, 0x6e,0x64,0x42,0xb0, 0x61,0xc3,0xe6,0xfa, + 0xfb,0xc0,0xd3,0xd6, 0x33,0xa3,0x89,0x6c, 0x63,0xb1,0x1a,0xa6, 0xcb,0x53,0x1e,0xb7, + 0x48,0xfb,0xd7,0xcf, 0x99,0xfc,0xc1,0x39, 0x55,0x1f,0xcb,0x52, 0x74,0xfd,0x3f,0xd5, + 0x75,0xf6,0xd9,0x16, 0x78,0xe0,0xf0,0x8a, 0x44,0x42,0x26,0x19, 0x47,0x40,0x7d,0xfd, + // 0xb00: + 0x66,0xb3,0x0a,0x34, 0xb3,0xf7,0xbe,0xb7, 0xea,0x37,0xfa,0xab, 0xb3,0x33,0x63,0x98, + 0x87,0x94,0x1e,0x9e, 0x5e,0x7e,0x9f,0x0d, 0x97,0x38,0xdb,0x7b, 0xe6,0x9f,0xa0,0xfb, + 0xb6,0x54,0xc5,0x59, 0x7b,0xe0,0xc6,0xb2, 0xa3,0xef,0x39,0xa3, 0x93,0x72,0xd9,0x79, + 0x11,0x81,0xc8,0xd1, 0xb6,0x95,0x0c,0x81, 0x3a,0xd9,0xc1,0xdf, 0xdc,0xeb,0xad,0xed, + 0x9d,0x63,0x3b,0xf2, 0x79,0x6e,0x17,0xd1, 0xef,0xfb,0x4f,0x7b, 0x9b,0x23,0x58,0xaf, + 0x26,0x26,0x46,0xe8, 0x31,0xfd,0x26,0xf6, 0xbe,0xc7,0xbc,0xa8, 0x4e,0x8e,0xc6,0x70, + 0x7d,0xe3,0x6e,0x4e, 0x26,0xa6,0xf6,0x6a, 0xaa,0x35,0xe9,0x96, 0x01,0x01,0xaf,0xef, + 0x43,0xd6,0x6f,0xb9, 0xb5,0xba,0xb0,0x57, 0x16,0x96,0xb0,0xbc, 0x49,0x14,0xb8,0x04, + 0xaa,0xbc,0xf6,0xb2, 0x04,0x0e,0x6d,0x2c, 0xce,0x9e,0x82,0x52, 0x46,0x38,0x15,0x41, + 0x2d,0x8e,0x69,0x50, 0xb8,0x29,0xc9,0x52, 0xed,0x92,0x91,0x70, 0x91,0x0e,0x0c,0x23, + 0x92,0x00,0x29,0xbe, 0x28,0x14,0xd2,0x35, 0x0f,0xd7,0xbf,0xe1, 0x15,0xbd,0xa9,0x9f, + 0xa8,0x23,0x89,0x05, 0x69,0x9d,0x9c,0x81, 0x19,0x54,0x26,0xcc, 0xa1,0x10,0x67,0x1b, + 0x91,0x76,0x14,0xb0, 0x06,0xbd,0xa4,0xae, 0xd6,0xae,0x6b,0x0b, 0x79,0xcb,0x65,0x72, + 0x3f,0x05,0x0c,0x8a, 0x4a,0x82,0xd3,0x28, 0x5e,0x89,0xd7,0x13, 0x60,0x8a,0x36,0xc4, + 0x05,0x1a,0x1b,0xb4, 0xb4,0x09,0xd5,0x65, 0x97,0xa5,0x81,0x1a, 0xca,0xb6,0xc6,0x7c, + 0xd7,0x54,0x6d,0xa9, 0x01,0x9e,0xe2,0xeb, 0xf6,0x0f,0x01,0xe8, 0xe3,0x26,0xe3,0x06, + // 0xc00: + 0xb1,0xc7,0xd9,0x16, 0xb0,0xec,0xc1,0x2b, 0xc9,0x20,0x12,0xe2, 0x10,0x8f,0xa4,0x96, + 0xe3,0x41,0x02,0x59, 0x5b,0x9a,0x39,0xb0, 0x44,0xb2,0x08,0x68, 0x4c,0x7c,0x06,0x96, + 0x51,0xce,0x5d,0x96, 0xc7,0x26,0xdb,0xec, 0x30,0xa6,0x0e,0x71, 0x35,0x32,0x4b,0xe0, + 0x42,0x10,0x21,0x03, 0xc1,0x5f,0x7c,0x95, 0x46,0xea,0x53,0x3a, 0xba,0xbe,0x2b,0x67, + 0xc2,0x8e,0xa6,0x5b, 0x6a,0xda,0x30,0x38, 0xb3,0xbf,0x6e,0xcc, 0x27,0x13,0x27,0x6a, + 0x7f,0x9d,0x8b,0xa0, 0x3b,0x45,0x73,0xf3, 0xb4,0x88,0xdf,0x54, 0x6e,0x50,0x6d,0x16, + 0x8a,0xd4,0x93,0x29, 0xc9,0xfd,0x62,0x8d, 0x8c,0x86,0x82,0x24, 0x7c,0x78,0xbd,0x1c, + 0xfa,0xfe,0xe5,0xaa, 0xc9,0x2e,0xcb,0x97, 0xac,0x1e,0x92,0xa7, 0xa2,0x65,0x2a,0xe8, + 0x9d,0x26,0x67,0xfb, 0x27,0x61,0x84,0x12, 0x4c,0x71,0x7a,0x47, 0x6c,0x2e,0x45,0x07, + 0xfe,0xa9,0x30,0xcb, 0xa7,0x91,0x38,0x7c, 0x23,0xa2,0x63,0xcb, 0x79,0xb2,0xc6,0x0c, + 0x13,0x30,0xc2,0xcc, 0xa1,0x24,0x57,0x01, 0x79,0x45,0xc5,0x1a, 0xa0,0xec,0xec,0x9c, + 0xd1,0x52,0x0a,0x78, 0xfe,0xde,0xb6,0xa8, 0xd5,0xbe,0xd3,0xaf, 0xdb,0xd2,0xf1,0xac, + 0x08,0xb1,0xe4,0x2b, 0xe2,0x72,0xa0,0xeb, 0x0e,0x37,0x68,0x63, 0x42,0x24,0xd4,0xf0, + 0x15,0x10,0xde,0x9d, 0x23,0x1d,0x7e,0xeb, 0x7f,0x33,0xb1,0x24, 0x9d,0xbd,0xa5,0xac, + 0x50,0xd6,0x07,0x97, 0xa3,0x06,0xa6,0x5b, 0xdd,0x05,0x80,0xc4, 0x33,0x1a,0x67,0x86, + 0x90,0x5d,0x45,0x66, 0xf5,0xd6,0xf2,0xaf, 0x3e,0x24,0xf8,0x53, 0x0d,0x60,0x7c,0xd4, + // 0xd00: + 0x33,0x17,0x85,0x71, 0x34,0x4c,0xfd,0x5c, 0x10,0x10,0x18,0x8a, 0x69,0xd5,0x3b,0xef, + 0x10,0xa7,0x1a,0x3e, 0x53,0x40,0x47,0x57, 0x34,0xb9,0x66,0xac, 0xc4,0xe9,0xc9,0x1b, + 0xe7,0x11,0xe3,0xb9, 0x93,0x51,0xb9,0x50, 0x43,0x29,0x79,0x6f, 0x59,0x09,0x62,0x74, + 0x31,0x97,0x0c,0x81, 0x23,0xd0,0xb6,0x06, 0x07,0x73,0x03,0xac, 0x3a,0x38,0xb2,0xbe, + 0x8a,0x9d,0xf4,0x50, 0x24,0x6e,0xc3,0x24, 0xe2,0xcc,0xcb,0x78, 0x62,0x76,0x4b,0xb2, + 0xa1,0x79,0x66,0x1e, 0x1f,0xca,0xc1,0x1f, 0xbe,0x9d,0x5b,0x69, 0xbd,0x78,0x1c,0x0b, + 0x8e,0x65,0xae,0x55, 0xc1,0x09,0x7c,0xcc, 0xfc,0x30,0xad,0x08, 0x64,0x31,0xc3,0x8f, + 0x3d,0xa3,0x6b,0xaa, 0x93,0x45,0x51,0xe2, 0x8c,0x26,0xf8,0x9a, 0x0f,0xa8,0x8e,0x3b, + 0x91,0xfd,0x98,0xcd, 0x49,0xed,0xb9,0xec, 0x99,0xd3,0x73,0xae, 0x30,0xe3,0xd1,0xf7, + 0xe6,0xa6,0x42,0x8d, 0x07,0xba,0xeb,0xb0, 0x41,0x23,0xc2,0x10, 0x88,0xfe,0xfb,0xe2, + 0x92,0xa1,0x88,0x22, 0x02,0xe6,0x9d,0x4e, 0xa8,0x70,0x9c,0x9d, 0x35,0x64,0x2c,0x34, + 0x57,0xfc,0x24,0x63, 0x88,0x22,0xd5,0x75, 0x68,0x43,0x20,0x3a, 0x28,0xe4,0x6e,0xbd, + 0xfd,0xc1,0x44,0x5e, 0xa2,0xf7,0x7c,0x90, 0xc9,0x99,0x97,0x08, 0xa1,0xa7,0x28,0x85, + 0xa6,0xc9,0xaa,0x5d, 0x0c,0xa6,0x99,0xd3, 0x58,0xa1,0x44,0xc9, 0x1a,0x9d,0xdc,0x53, + 0xd4,0x59,0x4a,0x99, 0xe5,0x51,0x48,0xd9, 0x74,0x1f,0xc7,0xb1, 0x65,0x0a,0x5d,0x83, + 0x2a,0x6c,0xee,0x91, 0xd9,0x8a,0xc2,0x65, 0xcb,0xf4,0xcb,0x6d, 0xea,0x59,0xba,0x8e, + // 0xe00: + 0x0b,0x9d,0xfc,0x30, 0xc2,0xa3,0x54,0x1a, 0x36,0xd7,0x22,0x31, 0x8d,0x88,0x21,0xd9, + 0xab,0x3a,0x5c,0x49, 0xb5,0x83,0xc9,0xc7, 0x05,0x36,0x26,0xc5, 0x46,0x1d,0xd1,0x90, + 0x98,0x55,0xe4,0x87, 0x47,0x29,0xfb,0x31, 0x73,0x9a,0xfe,0x60, 0xe3,0xf4,0x32,0x0b, + 0xef,0x12,0x2c,0x3d, 0x1d,0xef,0x30,0x12, 0x23,0x09,0xc8,0x95, 0xaf,0x74,0x16,0x43, + 0x81,0xb5,0x76,0x62, 0xa8,0x41,0x34,0x41, 0x89,0x40,0xe2,0x4a, 0x0a,0xc4,0xa3,0x81, + 0x51,0x0a,0x16,0xca, 0x17,0x31,0xdc,0x46, 0x60,0xfd,0x21,0xf9, 0x26,0x17,0xcc,0x44, + 0x11,0x10,0xb2,0x6b, 0x2b,0x70,0x7d,0xab, 0x3d,0x3d,0xc9,0x42, 0xb8,0xf9,0xfc,0xdf, + 0xdb,0x97,0xaa,0x35, 0x6c,0x53,0x6c,0x93, 0x64,0xa0,0x8b,0xd3, 0x4e,0xfb,0x41,0xdb, + 0x11,0xae,0x75,0x52, 0x17,0x26,0x52,0xa5, 0x49,0x0d,0x7f,0xad, 0xc7,0xee,0x26,0x0e, + 0x15,0xcc,0xb0,0x6b, 0x04,0x15,0x3c,0x35, 0x78,0x95,0xdb,0xfa, 0xf2,0x5c,0x27,0x31, + 0xfe,0xf7,0x75,0xdf, 0xdc,0x32,0x85,0x56, 0x82,0x83,0xe3,0x78, 0x71,0x3b,0xa7,0x41, + 0x9f,0x1c,0x83,0xa3, 0xe2,0x7c,0x3e,0xd5, 0x77,0x06,0x7e,0xb7, 0xcb,0xe9,0x3f,0xdf, + 0xc7,0x2d,0xf4,0x3b, 0x0d,0x2c,0xf8,0xcf, 0x92,0x62,0x36,0x9c, 0x6d,0x61,0x41,0xa3, + 0xcb,0xb2,0x20,0x7a, 0x41,0x07,0xa1,0x8a, 0xd0,0xf8,0x04,0x42, 0x9d,0xc8,0x3f,0xd6, + 0x58,0x7a,0xa6,0x39, 0xf6,0xf8,0xa2,0xe2, 0x4f,0x45,0xe9,0x68, 0x48,0xcd,0xf6,0x5c, + 0xd9,0x8d,0x09,0xc2, 0x0b,0xa6,0x61,0xaa, 0x79,0xda,0x49,0x7d, 0x49,0xf7,0x72,0x13, + // 0xf00: + 0x21,0x54,0xe4,0x47, 0x4b,0x07,0x01,0xd5, 0xc2,0x0f,0x5f,0xae, 0x01,0x49,0xa6,0xf1, + 0x19,0x10,0x56,0x7c, 0x80,0xfb,0x27,0x98, 0xbf,0x2e,0x50,0xa9, 0x0a,0xcf,0x40,0x26, + 0xb7,0x30,0x92,0xc0, 0xb6,0x09,0x95,0xbf, 0x46,0x19,0x51,0x05, 0x2e,0x03,0x09,0x11, + 0x0b,0x1e,0x06,0x25, 0xb4,0x5f,0xc6,0x9f, 0xbb,0x12,0xcb,0xe4, 0x96,0x6f,0x15,0xa4, + 0x74,0x21,0x7e,0x11, 0xef,0xcd,0xbc,0xda, 0x5a,0xe9,0x08,0x96, 0x3e,0x65,0xd9,0x2f, + 0xb6,0xf4,0x72,0x8d, 0x8a,0x63,0x8e,0x3f, 0xd1,0xab,0xc5,0x98, 0x88,0xd1,0xcd,0x80, + 0x3c,0x33,0xd0,0xbd, 0x41,0xa6,0x17,0x0c, 0xb6,0xf6,0x49,0x06, 0x32,0x81,0xd8,0x90, + 0x48,0x78,0xa6,0x8a, 0xc4,0xc4,0xf4,0xb3, 0x01,0x8f,0xb9,0x95, 0x09,0xae,0x28,0xfd, + 0x4e,0x1c,0x2b,0xab, 0x9f,0x4f,0x67,0xe8, 0x3f,0x9e,0x79,0x7d, 0xb5,0x29,0x4d,0x0a, + 0x9a,0x08,0xc0,0xbd, 0x5d,0xcb,0xec,0xf3, 0x57,0xf6,0x10,0xf1, 0xb6,0xe7,0x9b,0x24, + 0xb2,0x64,0x5c,0x12, 0x07,0x0c,0xce,0xca, 0xca,0xef,0xb9,0xfe, 0x1e,0x3d,0x67,0x56, + 0x4d,0xaf,0xe3,0x21, 0x8e,0x1c,0xd4,0x7b, 0x81,0x6c,0x86,0x31, 0x8c,0x63,0x5d,0x62, + 0x1c,0x0e,0x81,0x70, 0xb1,0x89,0x34,0xbd, 0x65,0x06,0xbc,0xec, 0x60,0x17,0xf6,0x82, + 0x88,0xae,0x98,0xe3, 0xc6,0xc3,0xb3,0x8b, 0xb4,0x7d,0x36,0xe8, 0x27,0x13,0x8f,0x07, + 0x0f,0x51,0x6a,0x12, 0xd4,0x61,0xd4,0xbd, 0x65,0x43,0xd9,0x54, 0xc2,0xe8,0xb6,0x88, + 0xa7,0x4e,0x28,0x7f, 0xd7,0x8c,0x18,0x94, 0x6e,0x4c,0x62,0xbc, 0xf4,0xf8,0xf1,0x0b, + // 0x1000: + 0xb1,0x0b,0xab,0x63, 0x50,0x5e,0x93,0x39, 0x35,0xcb,0xcf,0x9f, 0xf3,0x26,0xb0,0x31, + 0xb9,0x2e,0xdf,0x36, 0x44,0x33,0x25,0xa0, 0xb1,0x61,0x0e,0x42, 0x86,0x42,0xee,0x76, + 0x4c,0x57,0xbe,0x8a, 0x52,0xbe,0x72,0x25, 0x38,0xa3,0x45,0x43, 0xcd,0xaf,0xeb,0x3f, + 0xd8,0x33,0x07,0x34, 0x7a,0x4d,0x3f,0x63, 0xf4,0x74,0x9d,0x75, 0x0a,0x14,0x3f,0xad, + 0xf8,0xaf,0xca,0x70, 0xfe,0x51,0x63,0x20, 0xc8,0x31,0x41,0xc2, 0x9a,0x6c,0x4b,0xaa, + 0x4f,0x59,0x2a,0x2c, 0xac,0xe7,0xa8,0xbb, 0x93,0x5b,0x63,0xcf, 0x7d,0x4b,0x04,0xd9, + 0x4d,0x35,0xa9,0x7c, 0x13,0xe2,0xb0,0xec, 0xba,0xce,0xe9,0xf3, 0x36,0x57,0x1a,0xe6, + 0xb3,0x22,0xc8,0xe0, 0x17,0x0e,0x83,0x03, 0xa0,0x06,0x6b,0x7a, 0xe9,0x01,0xda,0x8c, + 0x94,0x46,0x24,0x3b, 0x9b,0x8e,0x63,0x91, 0x71,0xe4,0x4a,0x0d, 0x43,0xa1,0xd3,0x44, + 0x8b,0x17,0xb6,0xee, 0x61,0xba,0xd5,0xc3, 0x4f,0x39,0x4c,0x42, 0xfc,0xad,0x85,0x49, + 0x45,0x6c,0x48,0xa3, 0x32,0x76,0xac,0x95, 0xa7,0x5a,0xb1,0xb0, 0x6c,0xf9,0x97,0x0e, + 0x23,0x3d,0xa2,0xed, 0x2b,0x96,0x41,0x8b, 0x3b,0x86,0x53,0x04, 0x04,0x12,0xbb,0x19, + 0xb8,0xf5,0xc8,0x75, 0x65,0xd7,0xcc,0x58, 0x0b,0x63,0x6e,0xc4, 0x62,0xf8,0x38,0xf0, + 0x49,0x8a,0xfc,0x13, 0xbc,0x78,0x8c,0x81, 0x66,0xa2,0x5f,0x82, 0x1d,0xb4,0x34,0xa5, + 0x54,0x34,0x74,0x94, 0x2d,0xf6,0xd7,0x21, 0x2c,0xc7,0x61,0x72, 0x24,0x9b,0xe7,0x02, + 0x2c,0x90,0x37,0x56, 0x58,0x06,0x6a,0x97, 0x3a,0xd5,0x34,0x82, 0xb3,0x75,0xc4,0xfb, + // 0x1100: + 0x5a,0x5e,0x82,0xfa, 0x42,0x73,0x03,0x21, 0xfb,0x3a,0x06,0x0e, 0x5a,0xa7,0x24,0x33, + 0x11,0x85,0xe5,0x49, 0x80,0x6c,0xb1,0x82, 0x3c,0x11,0x22,0x85, 0x47,0x41,0xdb,0x4c, + 0xf0,0x0b,0x14,0xbc, 0x35,0xcf,0x16,0x12, 0x1c,0xf8,0x8a,0xa0, 0x52,0x19,0xbc,0xed, + 0x89,0x24,0xee,0x31, 0x5f,0x65,0xd9,0xbd, 0xea,0xb8,0x91,0x50, 0xc6,0x98,0x78,0x15, + 0x88,0x07,0x2e,0xa0, 0xd8,0xc1,0x67,0x57, 0x22,0x41,0x11,0x49, 0x4e,0x7f,0x33,0xa3, + 0xe2,0x71,0x30,0x11, 0x64,0xd5,0x87,0x83, 0x51,0xcb,0x62,0xbe, 0x70,0xc5,0x5a,0x44, + 0xc6,0xdf,0xb7,0x5a, 0xfe,0x04,0x72,0xfd, 0x53,0x32,0x24,0xb2, 0xbb,0xd8,0x64,0x31, + 0x83,0x85,0x25,0x45, 0x5e,0x72,0x0a,0x31, 0x0e,0x86,0x6a,0xab, 0x48,0xd6,0x9b,0xcb, + 0xd1,0xa9,0xe0,0x78, 0x59,0x73,0xa4,0x9e, 0x53,0x21,0xca,0xc4, 0xa4,0x85,0x11,0x00, + 0x8f,0x3c,0xaf,0xd9, 0x62,0x59,0x28,0x72, 0x02,0xb9,0xb2,0xad, 0x76,0xc1,0x05,0x74, + 0xd9,0x75,0x76,0x4e, 0x06,0xaa,0x50,0x6b, 0x0f,0x54,0x8e,0xaa, 0x40,0x16,0x7a,0x88, + 0x31,0xd9,0x32,0xfd, 0xeb,0x63,0x28,0xa8, 0xb0,0xce,0xdb,0x65, 0xa7,0x80,0x68,0xb2, + 0xe4,0x37,0xe5,0xa3, 0x1c,0x3b,0x1d,0xe4, 0x14,0x28,0x48,0x98, 0xe4,0x97,0x36,0x24, + 0xf6,0x6a,0x01,0x69, 0x11,0xcc,0x32,0xeb, 0xa1,0x11,0xfd,0xc9, 0xb2,0xfe,0x1b,0xef, + 0xf7,0xd8,0xf9,0x3e, 0x76,0xa9,0x2c,0x17, 0xe1,0xbc,0x83,0x49, 0x33,0xe5,0x99,0x32, + 0xc0,0x29,0x35,0xb2, 0x87,0x46,0x31,0x38, 0x23,0xb2,0x14,0x44, 0xb1,0xc9,0x04,0x83, + // 0x1200: + 0xfb,0x51,0xd1,0x65, 0x91,0x42,0x67,0xfa, 0xa1,0x4b,0x1b,0xbc, 0xbb,0xed,0x5a,0x8c, + 0x56,0xfb,0xda,0x2c, 0x94,0x63,0x27,0x98, 0x50,0x74,0xcc,0x07, 0x05,0x77,0x36,0x3e, + 0x27,0x89,0xe0,0x13, 0xc7,0x8f,0xdc,0x64, 0x61,0x1e,0xaa,0xb0, 0xf7,0xf5,0xf2,0x86, + 0x5f,0x1f,0x03,0x7e, 0x41,0x45,0xd4,0x51, 0x2a,0x18,0xc4,0x4c, 0x3a,0x58,0x5b,0x13, + 0x42,0xda,0xbe,0x57, 0x36,0xe7,0x33,0x23, 0x09,0x5e,0x95,0x40, 0x1d,0x3a,0x50,0xd8, + 0x4c,0x32,0xb2,0xf1, 0xf6,0x90,0xaf,0x87, 0x9e,0xe6,0xe4,0x01, 0x5f,0xc7,0x5b,0x9e, + 0xa8,0xa5,0x2a,0xfc, 0xcc,0x21,0xf3,0x2e, 0x9e,0xb6,0x29,0x4e, 0x57,0x71,0x69,0x74, + 0x2f,0xf9,0xd2,0x24, 0xd2,0x62,0xdb,0x59, 0x87,0xa9,0x95,0x13, 0x1d,0x8d,0xd5,0xac, + 0x3b,0x45,0x94,0xbb, 0xe6,0x08,0x2b,0xb8, 0xf9,0x7f,0xc6,0x29, 0x0e,0xe2,0x12,0x8c, + 0xf3,0xfe,0xde,0x7f, 0x07,0x8b,0x68,0xbb, 0x96,0xef,0xb0,0x08, 0xba,0xe6,0x7f,0x9d, + 0xde,0x6b,0x54,0xa7, 0xbc,0x3d,0xc6,0x75, 0x2b,0x50,0xf6,0x8f, 0x42,0x0b,0x9e,0xef, + 0x77,0x5f,0x42,0xca, 0x3e,0x0f,0x16,0xe5, 0x58,0xb0,0x5b,0xc3, 0x5e,0x2e,0xd3,0x66, + 0x6c,0xf4,0x9d,0x7d, 0x80,0xbf,0x1d,0xd2, 0x03,0x75,0x7e,0xc9, 0x14,0x75,0x5a,0x28, + 0xcc,0x49,0x77,0xaa, 0x86,0x4a,0xd7,0x77, 0x34,0xea,0x6e,0xf2, 0x10,0x29,0xb1,0x67, + 0x7c,0x2c,0xb4,0x72, 0x8c,0x15,0x93,0x3e, 0x3b,0x53,0x4c,0xc4, 0x66,0x68,0xea,0xb1, + 0x9b,0x43,0x48,0xeb, 0x78,0xd0,0x77,0xdf, 0xef,0x6a,0xc3,0xa4, 0x7f,0x2e,0x8a,0x45, + // 0x1300: + 0xed,0x13,0x9b,0xa9, 0xeb,0xb6,0xe2,0x62, 0x16,0xa6,0x39,0xee, 0xe5,0x10,0x25,0xaf, + 0xd2,0xc7,0x8f,0x0e, 0xcb,0x45,0xaa,0xd6, 0x4e,0x7a,0xc7,0xf1, 0x78,0x38,0x19,0xac, + 0xf5,0x56,0x7c,0x1c, 0x0f,0x79,0xe8,0xff, 0x16,0x5a,0xc8,0x2a, 0x6d,0xf4,0x14,0xcb, + 0x04,0x23,0xc3,0x11, 0x30,0x90,0x3a,0x77, 0x07,0x60,0x70,0x38, 0x4c,0x23,0x6b,0xee, + 0xb7,0x94,0x06,0xb8, 0x58,0xd6,0x5c,0xb3, 0x73,0xcf,0xd6,0x56, 0xd4,0xb3,0xc6,0x7e, + 0x61,0x54,0x3e,0x35, 0xa5,0x36,0x2e,0xed, 0xb4,0x33,0xe5,0xa3, 0x1d,0x24,0x0d,0xe4, + 0x78,0x55,0xc8,0x8c, 0x60,0x70,0xa9,0xb5, 0xea,0x7a,0xdc,0xef, 0xbb,0x8d,0x55,0x6a, + 0xbd,0x9e,0x1c,0x50, 0xc1,0x24,0xa2,0x6b, 0x49,0xce,0x4a,0x43, 0xba,0x59,0xad,0x2f, + 0x54,0xe2,0x8a,0xa1, 0x3a,0xd5,0xae,0xec, 0xda,0x68,0xed,0xf8, 0x6b,0x75,0x59,0x8c, + 0x78,0x39,0x12,0xe9, 0xa3,0x81,0xf0,0x5b, 0x0c,0x6a,0xf7,0x34, 0xb8,0xa2,0xd3,0xeb, + 0x6a,0x47,0x09,0x19, 0x5f,0x18,0xc4,0x84, 0x16,0x8f,0x52,0xb7, 0x3c,0x55,0x05,0xc1, + 0xed,0xac,0xeb,0xf1, 0x5d,0x8f,0x83,0xa3, 0xd3,0xb3,0x33,0x16, 0xae,0x8e,0xee,0xe5, + 0x60,0xb1,0x1a,0xe7, 0xa8,0x12,0xfa,0xf3, 0x97,0x51,0x39,0x23, 0x38,0x69,0xa9,0x29, + 0xab,0xa6,0x1a,0x90, 0xec,0xd3,0x8a,0xab, 0x8f,0xb0,0xa7,0x9e, 0x27,0x87,0x8e,0x66, + 0xbf,0x8d,0x0a,0x8b, 0x5d,0x45,0xa3,0xca, 0x95,0x12,0xd3,0x33, 0x21,0x98,0x92,0x96, + 0x6c,0xb7,0x5a,0xad, 0xe1,0xff,0xa2,0x8b, 0xba,0x13,0x85,0xf9, 0x3a,0x53,0x8d,0x05, + // 0x1400: + 0xca,0x6e,0x14,0x0d, 0xc1,0xe7,0x2c,0x0d, 0xda,0x88,0x48,0x30, 0x63,0x33,0xf4,0x6d, + 0x99,0x76,0xd9,0xb7, 0xb9,0x4d,0xb9,0x43, 0x21,0x28,0xef,0x4c, 0x43,0xea,0x6b,0x24, + 0x15,0x99,0xbc,0x9b, 0xf2,0x8e,0x4f,0x30, 0xdd,0xe5,0x99,0x8c, 0xc5,0x45,0x56,0x9b, + 0xab,0x51,0xf5,0xc0, 0xf6,0xb5,0x80,0xf6, 0x05,0x6a,0xca,0xab, 0x87,0xbe,0xf8,0xaf, + 0x30,0x35,0xd6,0x2c, 0x2e,0xec,0x09,0x8d, 0x65,0xbb,0x5e,0xe1, 0x49,0x1b,0x18,0x60, + 0xe0,0xfe,0x4c,0x58, 0x66,0x66,0xb9,0xfd, 0xa1,0x53,0x54,0xd5, 0xaa,0xfc,0x25,0xc5, + 0xb2,0xdd,0xeb,0x05, 0x09,0x1e,0x97,0x9a, 0x3d,0xfc,0xeb,0x3d, 0x36,0xdf,0x80,0xeb, + 0x23,0x0d,0x8f,0xe1, 0x57,0xd5,0x72,0x5a, 0x06,0x4c,0xde,0x1b, 0xca,0xfe,0x3b,0x06, + 0x6a,0xc1,0x8a,0xb9, 0x6c,0xd4,0x21,0xd9, 0xdd,0xd8,0x6f,0xfc, 0x31,0xe6,0xd7,0x0d, + 0xb1,0xfe,0xb0,0x29, 0xbf,0x3d,0x2f,0xc3, 0xae,0x91,0x42,0x27, 0x36,0x65,0xc2,0xd8, + 0xca,0x3d,0x89,0x3a, 0x30,0xca,0x0f,0xe7, 0x41,0xbf,0xc6,0xc6, 0x26,0x49,0x44,0x8c, + 0x2d,0x97,0x3b,0x0f, 0xcb,0x30,0x36,0xf4, 0x29,0xba,0xe0,0x3c, 0xc1,0x5c,0x4e,0x73, + 0xdf,0xff,0x6f,0xb0, 0x4c,0xe4,0xad,0x8d, 0x4b,0x57,0x59,0x76, 0x9f,0x1f,0x9c,0x6f, + 0xb5,0x87,0x6d,0x85, 0x68,0x32,0x0e,0xd9, 0x6f,0x76,0x48,0x6e, 0xa4,0xb7,0x6e,0x4b, + 0x18,0xdb,0xf1,0x03, 0x3e,0xb7,0x41,0x5d, 0x13,0x35,0x5e,0x91, 0x9f,0xac,0x71,0x9e, + 0x8b,0xb0,0x66,0x07, 0xbe,0xec,0xcd,0x56, 0xe1,0x8b,0xb2,0x15, 0xd5,0xb5,0x6c,0xd9, + // 0x1500: + 0x65,0xbb,0xd5,0x7d, 0xcb,0x8c,0x7b,0xb8, 0x06,0xf4,0xa9,0x93, 0x20,0x22,0x2a,0xa9, + 0x5b,0x90,0xb7,0x7c, 0xdb,0x8b,0xa4,0xcd, 0xb0,0xef,0x4a,0xc3, 0x05,0x7f,0xa5,0xf0, + 0x52,0xd0,0x0d,0x35, 0x6a,0xe0,0x10,0x5b, 0x6c,0xf9,0x41,0xbe, 0xd4,0x72,0x41,0x00, + 0xfc,0xf1,0xf0,0x0d, 0x88,0x09,0x1a,0x29, 0x0e,0x82,0x0e,0xba, 0xeb,0xb3,0xf3,0xae, + 0x9c,0xd2,0x52,0x80, 0xbb,0xc8,0x4a,0x21, 0xd3,0xe5,0x18,0x41, 0x25,0x0d,0x36,0x92, + 0x49,0x00,0xd0,0x69, 0xa3,0xc9,0x23,0x53, 0x6a,0xc3,0xb1,0xb8, 0x46,0x82,0x98,0x7b, + 0xb2,0x16,0x19,0x61, 0x65,0xc6,0x48,0x59, 0x49,0x0a,0xc8,0x81, 0x59,0x2c,0xc2,0xc3, + 0x00,0xb2,0xc4,0x05, 0x9b,0x3f,0x83,0x6f, 0xde,0x71,0xd6,0xad, 0xea,0x59,0xb7,0xb4, + 0x6e,0xae,0x92,0x27, 0xad,0x84,0x13,0x07, 0xe2,0x41,0x47,0x60, 0x46,0x21,0x21,0xd3, + 0x13,0xd2,0x26,0xd2, 0xfd,0x0c,0x03,0x31, 0x14,0xb2,0x39,0x13, 0x78,0xbf,0x19,0x2e, + 0x0d,0xeb,0xd8,0x31, 0x85,0x19,0x4a,0xbc, 0x3b,0x47,0x12,0x1e, 0x49,0xea,0xad,0x8a, + 0xd1,0x68,0x3c,0x85, 0x72,0xd5,0x96,0x5a, 0x2d,0x16,0x6b,0x22, 0x74,0x6f,0xc2,0xf3, + 0x91,0x3c,0xd5,0x9b, 0x7c,0xee,0x1f,0x46, 0x67,0xb9,0x9d,0x05, 0x71,0x72,0x9b,0xed, + 0x70,0xd7,0x82,0x0b, 0xa5,0x7b,0x1c,0xeb, 0x50,0xdd,0xe3,0xd8, 0xed,0xef,0x58,0x02, + 0x72,0xf5,0xe6,0x41, 0x94,0x60,0x81,0xb6, 0x67,0x56,0xcc,0xab, 0x66,0xbb,0xaa,0xa4, + 0x54,0x0b,0x96,0xcb, 0x4e,0xad,0x5e,0x99, 0x06,0x7a,0xfc,0xb5, 0x59,0x10,0x27,0x17, + // 0x1600: + 0x01,0xa9,0x21,0xab, 0x38,0xd6,0x79,0xaf, 0x9f,0xa8,0x10,0x69, 0xd5,0x64,0xa8,0x32, + 0x04,0x4f,0xb6,0xc6, 0x0a,0x21,0x13,0x86, 0x63,0x15,0x03,0xf9, 0x80,0x1d,0x83,0x66, + 0x88,0xde,0xde,0xa4, 0xac,0x97,0x04,0xb8, 0xe9,0x84,0x80,0xe8, 0x7e,0xa0,0x4d,0xcf, + 0x1d,0xeb,0xb0,0x3f, 0xcb,0x2b,0x8f,0x61, 0x53,0x74,0x33,0xd8, 0xa0,0x32,0xfc,0x08, + 0x92,0x7c,0x8b,0x84, 0x5a,0x2b,0xf9,0x9c, 0xac,0x7d,0x5d,0x03, 0x85,0x91,0x77,0xec, + 0x5e,0xd1,0x25,0x3e, 0xf1,0xfb,0x13,0xac, 0x8a,0x32,0x17,0xc7, 0x6b,0x0d,0x40,0x43, + 0x7e,0x7a,0x58,0xcb, 0xba,0x40,0x19,0x91, 0x8e,0x79,0xed,0xe3, 0x02,0xe1,0xde,0x03, + 0xd6,0x15,0x3c,0x06, 0xc9,0x90,0x33,0x93, 0x2a,0xd0,0x33,0xbb, 0xd6,0x2c,0xc5,0x23, + 0x7b,0x6b,0x77,0x74, 0x7c,0x80,0x4b,0x3c, 0x68,0x97,0x1d,0xfd, 0x7f,0x80,0x1f,0x6e, + 0x4f,0xe4,0x7f,0x10, 0xa5,0x89,0x84,0xc0, 0x4c,0x06,0x23,0x07, 0x0e,0xb7,0xc0,0x9d, + 0x4e,0x39,0xe5,0xc3, 0xc7,0xf8,0xb5,0x0e, 0xf6,0xfb,0x8c,0xba, 0x77,0xef,0x71,0x29, + 0x74,0xab,0xbf,0xbb, 0xea,0xc4,0x6c,0x6e, 0x78,0x16,0x57,0x9b, 0x79,0x30,0x51,0x5b, + 0x82,0x8a,0x79,0xd2, 0x15,0xd8,0x2e,0x3e, 0x48,0xd8,0x1d,0x19, 0x8a,0x4b,0x87,0x3e, + 0x06,0xfd,0x92,0x31, 0x2c,0xa1,0x41,0x09, 0x4b,0xe0,0x97,0xd0, 0x56,0x0e,0xb9,0x86, + 0xc7,0xcf,0xc1,0xa3, 0x11,0xb5,0xce,0xc9, 0x29,0x24,0xb7,0xfc, 0xf4,0x70,0x1a,0x04, + 0x74,0xbe,0x5f,0x99, 0x20,0xb9,0x4d,0x10, 0x2d,0x3e,0xe9,0xf4, 0xed,0xd2,0x8f,0x4e, + // 0x1700: + 0xce,0xee,0x22,0x3e, 0x19,0x85,0xb1,0xa0, 0xdc,0xf0,0x68,0x93, 0x1f,0x0d,0x39,0x36, + 0x31,0xd3,0x85,0x75, 0x8b,0x4f,0x39,0xac, 0xa3,0xd5,0xf6,0x93, 0x70,0x88,0x40,0x13, + 0xd3,0x31,0x9f,0xeb, 0xcf,0x55,0x53,0xe1, 0xa4,0xf9,0x7c,0x7f, 0x96,0x0a,0x87,0xa0, + 0xd6,0x6c,0x23,0xa1, 0x8d,0xcd,0x38,0x03, 0x22,0xbe,0xce,0x64, 0xd7,0x31,0xb0,0xcd, + 0xfc,0x37,0xc8,0x13, 0x4d,0x4b,0x7d,0xdc, 0x8a,0x3a,0xd8,0xa9, 0xb9,0xf6,0x86,0x5a, + 0x1a,0xf5,0xd9,0xbc, 0xa9,0x1c,0x4f,0x6a, 0x3f,0xef,0x96,0xf9, 0x22,0x39,0xfd,0xe4, + 0x2f,0x31,0xed,0xb0, 0x3c,0x3d,0x72,0xdb, 0x8b,0xdb,0xeb,0xde, 0x91,0xc7,0xdf,0x95, + 0x4f,0x49,0x80,0x6f, 0x89,0xd8,0x5d,0xf3, 0x4f,0xe4,0x41,0x6b, 0x2b,0x35,0x65,0x88, + 0xc3,0xff,0xb3,0x22, 0xa7,0xc3,0x18,0x80, 0x19,0xb5,0xc5,0xeb, 0x5c,0xf9,0xba,0xc8, + 0xe3,0x2c,0x4d,0x61, 0x69,0x84,0xb9,0x54, 0x24,0x31,0x6e,0x30, 0x19,0x34,0x3a,0x2a, + 0x37,0x5d,0x90,0x72, 0x77,0x43,0xa5,0x00, 0xf4,0xe9,0x31,0x8b, 0x00,0xb7,0xf4,0xcc, + 0xad,0x83,0xa3,0x10, 0x59,0x7f,0x93,0xa4, 0xa0,0x92,0x76,0xc2, 0x70,0xc8,0x48,0xab, + 0xd4,0xd5,0xb0,0x2c, 0x5b,0x58,0x96,0x3c, 0xd6,0x9e,0xb4,0xdb, 0x0d,0x92,0xab,0x1c, + 0xd9,0x5c,0x1c,0x3e, 0x09,0xd3,0x63,0x78, 0xac,0x63,0x2b,0xf7, 0x3c,0xf8,0x2a,0x9a, + 0x87,0x4e,0xcd,0x36, 0x07,0xdf,0x2e,0x75, 0xc6,0xf3,0x62,0x68, 0x77,0x9b,0x7f,0x8b, + 0xdd,0xfc,0x8c,0x99, 0xbc,0x5d,0x9e,0x5c, 0xd4,0xc6,0x8b,0x3d, 0xfc,0x97,0xd2,0x8d, + // 0x1800: + 0x6e,0x6f,0x64,0x55, 0x63,0xa4,0x0a,0x66, 0xe0,0x22,0x7a,0x94, 0x8c,0x54,0x0b,0xae, + 0xb8,0xb0,0xdd,0xd5, 0x1a,0x88,0x6a,0x5c, 0x00,0x92,0x08,0x24, 0xf2,0x88,0xbf,0x9d, + 0x00,0x55,0x7c,0x43, 0x43,0xca,0x0e,0xb4, 0x58,0x33,0x60,0x1d, 0x72,0x46,0xf5,0xfd, + 0x0f,0x71,0xad,0xcf, 0xd5,0xd6,0x58,0xc9, 0x4f,0x2d,0x32,0xb5, 0x7e,0x93,0x67,0x0f, + 0xc7,0xf2,0x6f,0xad, 0xd4,0x3c,0x35,0x98, 0x88,0xfc,0x2f,0x8e, 0x56,0x91,0xd0,0x80, + 0xe6,0xbb,0x89,0xde, 0x5f,0x23,0xdc,0xe5, 0x2d,0xed,0xc3,0xc5, 0x98,0xe6,0x12,0xac, + 0x25,0x49,0x3b,0x10, 0x1a,0x95,0x92,0xf7, 0x9b,0x05,0x7c,0xbf, 0x3b,0x79,0xcd,0x65, + 0xa2,0x9a,0x62,0xb6, 0x5a,0x6c,0x54,0x60, 0xaf,0x07,0x10,0x0e, 0x7f,0x52,0xfd,0xf1, + 0xd5,0x35,0x00,0x0e, 0xcc,0xf7,0x44,0xc9, 0x47,0x4a,0xf8,0x3b, 0x37,0xa8,0xd3,0xd3, + 0xb8,0xad,0xf3,0xf2, 0x7c,0xcd,0x96,0xe9, 0x28,0x9e,0x39,0xcf, 0xf3,0xa9,0x54,0x75, + 0x36,0xd4,0x2f,0x70, 0xb9,0x9c,0xb5,0xa8, 0x68,0x60,0xb1,0xae, 0x2e,0x75,0xc4,0xe2, + 0xce,0x11,0x39,0x05, 0xf8,0xf1,0xd6,0xb2, 0x73,0x01,0x3e,0x71, 0xfe,0x1b,0xae,0x2a, + 0xa2,0x12,0x04,0x9c, 0x22,0x16,0x4f,0x50, 0x9b,0xb7,0xbd,0xc1, 0x0d,0x1c,0x82,0x89, + 0xf5,0x49,0x67,0x7a, 0xae,0x61,0xfc,0xd0, 0xc6,0x8d,0x3c,0xee, 0xd7,0x93,0x85,0xc2, + 0xa3,0x50,0x23,0x54, 0x44,0x33,0x2d,0x6d, 0x37,0x4f,0x67,0x90, 0xab,0xe6,0xb0,0x15, + 0x93,0xdd,0x12,0xc2, 0x9e,0x65,0xab,0x3e, 0x28,0x34,0x7d,0x24, 0xe2,0x5b,0xe6,0x8e, + // 0x1900: + 0xc8,0x17,0x47,0x1f, 0x05,0xa5,0xe5,0xb1, 0x99,0xa7,0xd2,0x05, 0x95,0x45,0x5e,0x52, + 0xed,0xe4,0x57,0x5f, 0xea,0xbf,0x0e,0x4b, 0x23,0x89,0xc1,0xd8, 0xfc,0x46,0x09,0x4c, + 0x28,0x1c,0xee,0x65, 0x7e,0xe4,0xf1,0x49, 0xed,0x4f,0x70,0xcb, 0x2c,0x14,0xf8,0x50, + 0x25,0x5a,0x92,0xe5, 0xae,0x51,0x83,0x68, 0x74,0xfa,0x5d,0xdd, 0x39,0x8f,0x1d,0x73, + 0x94,0x35,0xb9,0xde, 0xd3,0x1b,0x97,0x6b, 0xf1,0xa1,0x8a,0xa9, 0x05,0xa8,0x6d,0x81, + 0x35,0xdc,0xca,0x1d, 0xdf,0x46,0x5c,0xaf, 0xce,0x83,0x26,0xcb, 0x18,0x81,0xae,0xb1, + 0xd6,0x51,0x2a,0x3d, 0x78,0x02,0x24,0x62, 0x05,0x26,0xd1,0xb2, 0xf7,0x99,0x55,0x1b, + 0x2f,0x03,0x21,0x85, 0x2e,0x4c,0x97,0xa5, 0x37,0x0e,0x2c,0xa4, 0xa4,0x86,0x8a,0xa5, + 0x05,0x3a,0x71,0x4d, 0x1f,0xa5,0xa2,0x1c, 0xbe,0xab,0xe2,0xdd, 0x91,0xd7,0x9b,0x73, + 0xef,0xa8,0x90,0xa2, 0xf5,0x67,0x5f,0x43, 0xc8,0xb8,0xe0,0x2b, 0x77,0x72,0xbb,0xaa, + 0x7a,0xa2,0xc4,0x52, 0x51,0x5a,0xd2,0xf4, 0x73,0xd0,0xa6,0x0b, 0xe2,0xb6,0x23,0xaf, + 0xa5,0xd3,0xc8,0xbb, 0xa8,0xb0,0x84,0x37, 0xd9,0xc7,0xb6,0xee, 0xd1,0xaf,0x49,0xb5, + 0xa7,0xdf,0xc4,0xc9, 0xc7,0x0b,0x81,0x23, 0x4c,0x8d,0x99,0x86, 0x70,0x28,0xe5,0xcc, + 0x14,0x7b,0xd5,0xe3, 0x55,0xb8,0xe4,0x1e, 0x01,0x71,0x3f,0xdc, 0xa9,0x4a,0xd5,0x06, + 0xb5,0x6d,0x4f,0xb6, 0x74,0x8c,0x52,0x2a, 0x3a,0xd8,0x22,0x1e, 0xd6,0xfa,0x2a,0x61, + 0xb0,0xa2,0x5b,0xef, 0x4a,0xd2,0x63,0x10, 0x7d,0x6a,0xe5,0xce, 0x77,0x65,0xb5,0xfb, + // 0x1a00: + 0x66,0x1f,0xa8,0x27, 0x92,0xa5,0xbb,0xa9, 0x80,0x75,0x3c,0xd2, 0x3a,0x56,0xf5,0xa3, + 0x15,0xd4,0x44,0x2f, 0x6a,0xa8,0x89,0x15, 0x4b,0x08,0xf9,0x34, 0x1a,0xf4,0xf0,0xf3, + 0x88,0x0b,0xac,0xd2, 0xd3,0x35,0x31,0x64, 0xd3,0xf5,0x56,0xc7, 0x5a,0x33,0xe0,0x4d, + 0x36,0x67,0x75,0x0f, 0xa8,0xf4,0x9d,0x56, 0xad,0xaf,0xee,0x52, 0x65,0x99,0xe7,0xb2, + 0xf2,0x40,0x5d,0xb7, 0x6d,0xf9,0x7a,0x3b, 0x41,0xed,0xc2,0x6f, 0x9d,0xa4,0x21,0x94, + 0x3a,0x50,0xf6,0x9f, 0x92,0x7d,0x8c,0x90, 0x77,0x0c,0xfb,0x7a, 0x54,0x42,0xa2,0x68, + 0x66,0x45,0x85,0x55, 0xce,0x8b,0x5b,0xba, 0xae,0x43,0x38,0x45, 0x81,0xbc,0x64,0x7c, + 0xc5,0x04,0x18,0x57, 0xac,0x79,0x46,0xa7, 0xb4,0x1d,0x69,0x86, 0xf6,0xa5,0x9b,0xca, + 0x8c,0xce,0xb4,0x06, 0x1f,0x09,0xb6,0xb8, 0x50,0x88,0x0a,0xcb, 0x7a,0xfd,0xfc,0x76, + 0x71,0xc0,0x74,0x69, 0xe9,0x86,0x0f,0x72, 0x6b,0x32,0xb0,0x99, 0x62,0xfe,0x77,0xe8, + 0xe3,0xa4,0x07,0x9f, 0x08,0x39,0x95,0x05, 0x56,0x21,0x04,0xca, 0x42,0x6a,0xd6,0x50, + 0x59,0x24,0x29,0xf1, 0x93,0x9d,0xdf,0xa7, 0xf5,0xf1,0xf3,0xd1, 0x69,0x54,0xe4,0x32, + 0x53,0xce,0x90,0xdb, 0xda,0xaf,0xa3,0xb9, 0xeb,0x04,0x19,0xb8, 0xc3,0x15,0x48,0x99, + 0x0b,0x25,0x16,0xcc, 0x2d,0x80,0xec,0x16, 0xed,0xc7,0xb4,0x4b, 0xaf,0x18,0x58,0x43, + 0x61,0x3a,0x68,0x77, 0x0d,0x97,0x01,0x5e, 0x50,0x19,0xe1,0x75, 0x17,0x0b,0x8f,0x79, + 0xb0,0x8b,0x5d,0xca, 0x8f,0x90,0xc8,0xc9, 0xef,0x70,0xf0,0x84, 0x9b,0x7a,0xbb,0xd6, + // 0x1b00: + 0x37,0x87,0x44,0xb9, 0x07,0xd9,0x2f,0x98, 0x42,0x50,0x6d,0x29, 0x58,0x87,0xab,0xe2, + 0xae,0x5a,0x3f,0x2d, 0xdc,0x1d,0x55,0xa7, 0xbc,0xb9,0x59,0x5a, 0x57,0x24,0x5d,0x21, + 0x46,0xd1,0xe6,0xba, 0x19,0x55,0x90,0xf4, 0x97,0xfb,0x6e,0x4f, 0xd7,0xcd,0xeb,0x6e, + 0xb9,0x6a,0xab,0x9a, 0xab,0x9b,0x0b,0x09, 0x3b,0x2d,0x76,0xd2, 0x4d,0x4f,0xd3,0x9b, + 0x65,0xf6,0xc2,0x3e, 0xa2,0x6a,0x46,0x29, 0x45,0xcf,0x71,0xc2, 0xed,0x71,0x0b,0x3b, + 0xbc,0x9a,0xbf,0xb6, 0xce,0x32,0xd2,0x9b, 0x95,0x64,0xdd,0x49, 0xb8,0x8b,0xf0,0x5d, + 0x9b,0xbf,0x73,0xd9, 0xb0,0x52,0xb6,0x56, 0x8c,0xe5,0x39,0x69, 0x5b,0x28,0x9c,0x2d, + 0x55,0x38,0xaf,0xb2, 0xb4,0xd7,0x64,0x92, 0x5c,0xe7,0x20,0x57, 0x14,0x69,0x51,0x42, + 0xf3,0x31,0x21,0x7f, 0x41,0xab,0xe4,0xf4, 0x75,0x4d,0x1e,0x29, 0xe1,0x74,0x10,0x0d, + 0x68,0xa1,0x64,0x96, 0x9e,0xff,0x92,0xdf, 0xc0,0xde,0xb7,0xad, 0x2e,0xbb,0x7a,0xfc, + 0x51,0xed,0xee,0x3d, 0xff,0x1c,0xd2,0xe7, 0xaf,0x2e,0x21,0xf5, 0x73,0xd9,0x97,0x08, + 0xaf,0x9c,0xfa,0x2e, 0xff,0x1c,0x6c,0xd8, 0x61,0xa0,0x86,0x46, 0x1b,0x1b,0x94,0xa0, + 0xdc,0xeb,0xb5,0xbd, 0x6a,0xbf,0x62,0x59, 0xd9,0x1d,0x7f,0x7a, 0x9a,0x07,0x0e,0x13, + 0x89,0x18,0x76,0x49, 0xf0,0xa2,0xfb,0xbd, 0xdd,0x36,0x6e,0x5f, 0xa1,0x6d,0x51,0xf0, + 0x2b,0x4d,0xd7,0x98, 0x6d,0xed,0xbe,0x25, 0xab,0x5f,0x75,0xb3, 0xee,0x03,0x16,0x73, + 0xe7,0x65,0x6b,0x26, 0x54,0xd7,0x76,0xe1, 0xae,0x2a,0xf5,0x10, 0xf5,0x74,0x59,0xf4, + // 0x1c00: + 0x24,0x99,0x43,0x9f, 0x95,0xc9,0xab,0xa7, 0xa5,0xe5,0xab,0x3a, 0x59,0xcb,0x94,0xe9, + 0xfe,0xd6,0x7e,0xc7, 0xb8,0xe3,0x58,0x31, 0x3e,0x2c,0xb4,0x24, 0x58,0xbd,0x28,0xea, + 0x6d,0xb7,0xe8,0x59, 0x9e,0xaf,0x7b,0x54, 0xaa,0x2a,0x47,0x9c, 0xf2,0x80,0xc7,0xd3, + 0x78,0x65,0x15,0x05, 0x88,0xc4,0x6a,0x4d, 0x08,0x94,0x00,0x72, 0x28,0x6d,0xe4,0x4c, + 0xa1,0x4a,0x4c,0x8a, 0x13,0xd3,0xe7,0x36, 0x24,0xa0,0x05,0x4e, 0x6b,0x9d,0x7d,0x1e, + 0x8b,0x8c,0xa7,0xef, 0x53,0x82,0xc9,0x08, 0xc2,0x19,0xfe,0xd4, 0x51,0xa9,0x2a,0x16, + 0xce,0xb1,0x99,0xe3, 0x21,0xf9,0x42,0xdd, 0xc8,0xdb,0x3f,0x9c, 0x74,0x50,0xb3,0xc6, + 0x06,0x69,0x0e,0xe0, 0x6e,0x8d,0x29,0xa6, 0x39,0x46,0xbd,0x16, 0x25,0xd5,0x1a,0x30, + 0xa9,0x8a,0x6d,0x2a, 0xa9,0x02,0xc2,0x9a, 0x8d,0x21,0xf1,0x8d, 0x09,0x80,0xf1,0x80, + 0xf3,0x94,0x10,0xe6, 0xf8,0xa1,0x47,0x1f, 0x6b,0xc3,0x8a,0xda, 0xf1,0x58,0x57,0xd9, + 0xf5,0x5b,0xef,0x37, 0x53,0xfe,0x90,0x21, 0x00,0x18,0x21,0x07, 0x7d,0xf1,0x55,0x0f, + 0x0b,0x51,0x6e,0xd3, 0x7f,0xf8,0xbb,0x92, 0x29,0xc2,0x84,0x81, 0x16,0x7e,0x0a,0xd8, + // 0x1cc0 = 7360 Bytes +}; // ctcode_ovr1_jap_bz2[] + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// END diff --git a/project/src/db-ctcode.h b/project/src/db-ctcode.h new file mode 100644 index 0000000..eda97d2 --- /dev/null +++ b/project/src/db-ctcode.h @@ -0,0 +1,64 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * +----------------------------------------+ * + * | This file is created automatically. | * + * | Edits are lost with the next update! | * + * +----------------------------------------+ * + * * + ***************************************************************************/ + +#ifndef SZS_DB_CTCODE_H +#define SZS_DB_CTCODE_H 1 + +#include "dclib/dclib-types.h" + +/////////////////////////////////////////////////////////////////////////////// + +extern const u8 ctcode_boot_code_pal_bz2[439]; // 684 bytes decompressed +extern const u8 ctcode_boot_code_usa_bz2[438]; // 684 bytes decompressed +extern const u8 ctcode_boot_code_jap_bz2[450]; // 684 bytes decompressed + +extern const u8 ctcode_boot_data_pal_bz2[238]; // 304 bytes decompressed +extern const u8 ctcode_boot_data_usa_bz2[254]; // 304 bytes decompressed +extern const u8 ctcode_boot_data_jap_bz2[245]; // 304 bytes decompressed + +extern const u8 ctcode_bad1code_pal_bz2[999]; // 1824 bytes decompressed +extern const u8 ctcode_bad1code_usa_bz2[1001]; // 1824 bytes decompressed +extern const u8 ctcode_bad1code_jap_bz2[1001]; // 1824 bytes decompressed + +extern const u8 ctcode_mod1_pal_bz2[2187]; // 3324 bytes decompressed +extern const u8 ctcode_mod1_usa_bz2[2180]; // 3324 bytes decompressed +extern const u8 ctcode_mod1_jap_bz2[2176]; // 3324 bytes decompressed + +extern const u8 ctcode_mod2_pal_bz2[1517]; // 3128 bytes decompressed +extern const u8 ctcode_mod2_usa_bz2[1531]; // 3128 bytes decompressed +extern const u8 ctcode_mod2_jap_bz2[1538]; // 3128 bytes decompressed + +extern const u8 ctcode_ovr1_pal_bz2[7364]; // 21348 bytes decompressed +extern const u8 ctcode_ovr1_usa_bz2[7335]; // 21348 bytes decompressed +extern const u8 ctcode_ovr1_jap_bz2[7364]; // 21348 bytes decompressed + +/////////////////////////////////////////////////////////////////////////////// + +#endif // !SZS_DB_CTCODE_H diff --git a/project/src/db-dol.c b/project/src/db-dol.c new file mode 100644 index 0000000..f077b0a --- /dev/null +++ b/project/src/db-dol.c @@ -0,0 +1,545 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + + +#include "db-dol.h" +#include "lib-staticr.h" + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////// struct DolSectionMap_t ////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const DolSectionMap_t DolSectionMap[N_DOL_SECTION_MAP] = +{ + // 0 + { STR_M_JAP, 0, "D3", 0x20, + "Section D3 of original MKWii/JAP.", + { 0x40,0x65,0xb7,0x71,0x65,0x6e,0xbc,0x7f,0x04,0x1c, + 0xe3,0x44,0xb5,0x32,0x42,0x0d,0x43,0x05,0x76,0xa8 }}, + // 1 + { STR_M_KOR, 0, "D3", 0x20, + "Section D3 of original MKWii/KOR.", + { 0xcd,0x27,0xcf,0xe9,0xce,0x3f,0x28,0xbc,0x64,0xb3, + 0xb2,0x05,0x70,0x10,0xaf,0x9a,0x5c,0xe6,0x04,0xf7 }}, + // 2 + { STR_M_USA, 0, "D3", 0x20, + "Section D3 of original MKWii/USA.", + { 0xe1,0x05,0x64,0x2d,0x86,0x59,0xc9,0xac,0x76,0xde, + 0x8c,0x84,0xd0,0x67,0xc6,0x9a,0x37,0x2f,0xd5,0xab }}, + // 3 + { STR_M_PAL, 0, "D3", 0x20, + "Section D3 of original MKWii/PAL.", + { 0xeb,0x28,0xbc,0x20,0x23,0x0a,0xce,0xa4,0xc9,0xe0, + 0x6d,0x67,0xee,0x25,0x20,0x1c,0xa1,0x55,0x39,0x7d }}, + // 4 + { STR_M_USA, 0, "D2", 0xc0, + "Section D2 of original MKWii/USA.", + { 0x4a,0x87,0x49,0xbd,0xa1,0x78,0x9f,0x8c,0x9c,0x9e, + 0x25,0x6a,0x6b,0xda,0x19,0x7f,0x7f,0xc7,0xf5,0x7f }}, + // 5 + { STR_M_KOR, 0, "D2", 0xc0, + "Section D2 of original MKWii/KOR.", + { 0x7f,0xcd,0xfb,0xdd,0x3e,0x2b,0xf4,0xf2,0x35,0xd0, + 0x4a,0x57,0xcb,0x9a,0xb0,0xa2,0x2a,0x86,0xf0,0xa5 }}, + // 6 + { STR_M_JAP, 0, "D2", 0xc0, + "Section D2 of original MKWii/JAP.", + { 0xb4,0x63,0xae,0x32,0x03,0x5d,0xcc,0xa7,0xb0,0x64, + 0xee,0x4a,0x1c,0x24,0xd3,0x10,0x84,0xd9,0xf7,0xe1 }}, + // 7 + { STR_M_PAL, 0, "D2", 0xc0, + "Section D2 of original MKWii/PAL.", + { 0xf6,0x7f,0x2b,0x81,0x56,0x65,0x9d,0xac,0xd2,0x56, + 0xb0,0xcd,0x6c,0xca,0xfd,0x72,0x39,0xe5,0x3d,0x2e }}, + // 8 + { STR_M_PAL, 0, "D8", 0x130, + "Section D8 of Revolution.alpha/PAL.", + { 0x3c,0x9c,0xba,0x13,0x93,0x33,0x3d,0xee,0x66,0x36, + 0xed,0x7e,0xb1,0xdf,0xeb,0xf9,0x96,0xf4,0x99,0xba }}, + // 9 + { STR_M_USA, 0, "D8", 0x130, + "Section D8 of Revolution.alpha/USA.", + { 0x40,0x37,0x79,0x85,0xab,0xc0,0x6d,0xc2,0xe8,0x6a, + 0xc6,0xbb,0xbd,0xbf,0x9e,0xb1,0x0e,0xda,0x4b,0x1a }}, + // 10 + { STR_M_PAL, 0, "D8", 0x130, + "Section D8 of CT-CODE 2015-10/PAL.", + { 0x79,0x0b,0xe8,0xe8,0x09,0x4e,0x3f,0xf0,0xe3,0xd6, + 0xd2,0x5c,0x15,0x6e,0x06,0x8c,0x24,0x3c,0x36,0xf5 }}, + // 11 + { STR_M_PAL, 0, "D8", 0x130, + "Section D8 of Revolution.Tock64/PAL.", + { 0x9a,0x7e,0x74,0x67,0x74,0xc2,0x9a,0x2b,0x1f,0xda, + 0x34,0xd3,0x11,0x63,0xe8,0x36,0x12,0x1b,0x98,0xf4 }}, + // 12 + { STR_M_JAP, 0, "D8", 0x130, + "Section D8 of Revolution.alpha/JAP.", + { 0x9f,0xa3,0xe3,0xb1,0xa3,0xfc,0xaf,0x8d,0x68,0x5a, + 0x0b,0x1b,0xa4,0xb2,0x26,0x6b,0x42,0x00,0xeb,0xb2 }}, + // 13 + { STR_M_JAP, 0, "D8", 0x130, + "Section D8 of Revolution.Tock64/JAP.", + { 0xa7,0x2a,0x72,0xe6,0x05,0xd8,0x86,0xaf,0xac,0x17, + 0x84,0x71,0xcf,0x10,0xca,0x27,0x86,0xe5,0x7c,0x63 }}, + // 14 + { STR_M_JAP, 0, "D8", 0x130, + "Section D8 of CT-CODE 2015-10/JAP.", + { 0xc8,0xc4,0x66,0x2f,0xd4,0x91,0x30,0xd5,0xbf,0x44, + 0xf5,0x91,0xf5,0x1d,0x5d,0x18,0xbf,0x0c,0xfb,0xbc }}, + // 15 + { STR_M_USA, 0, "D8", 0x130, + "Section D8 of Revolution.Tock64/USA.", + { 0xce,0x00,0xe1,0xca,0xef,0x38,0x2c,0x65,0xc3,0x1d, + 0x30,0xbc,0x3c,0xf1,0x89,0x0c,0xdd,0xe0,0x1f,0x5a }}, + // 16 + { STR_M_USA, 0, "D8", 0x130, + "Section D8 of CT-CODE 2015-10/USA.", + { 0xdf,0xcb,0xe7,0x9d,0xf5,0x04,0x3b,0xac,0xf5,0xc7, + 0xa8,0xd7,0x3c,0xaa,0xaa,0xfa,0x5f,0x6e,0xe0,0xd2 }}, + // 17 + { STR_M_PAL, 0, "T2", 0x2ac, + "Section T2 of CT-CODE 2015-10/PAL (D8 moved).", + { 0x38,0xde,0x8a,0x97,0xe3,0x2b,0x73,0x3d,0xb5,0xc9, + 0xb5,0x6f,0xa4,0x50,0xbe,0xf2,0xd2,0x95,0x97,0x62 }}, + // 18 + { STR_M_PAL, 0, "T2", 0x2ac, + "Section T2 of CT-CODE 2015-10/PAL.", + { 0x5a,0x45,0x51,0xe2,0x39,0x61,0x27,0x05,0xe4,0xb0, + 0x1d,0xe9,0x88,0x0f,0x04,0xed,0x75,0xb0,0xfa,0xe4 }}, + // 19 + { STR_M_JAP, 0, "T2", 0x2ac, + "Section T2 of CT-CODE 2015-10/JAP (D8 moved).", + { 0x5c,0x6d,0x69,0x3a,0x11,0xa0,0xa5,0x27,0x6b,0x1a, + 0xb1,0xa2,0xce,0xa9,0x32,0xf1,0x3a,0x05,0x20,0x59 }}, + // 20 + { STR_M_USA, 0, "T2", 0x2ac, + "Section T2 of CT-CODE 2015-10/USA (D8 moved).", + { 0x8f,0xb0,0xb9,0x74,0x8f,0xd6,0xd1,0x06,0x84,0x4e, + 0xd2,0x50,0xfc,0x9b,0xea,0x7d,0x86,0xb9,0x5b,0x9e }}, + // 21 + { STR_M_JAP, 0, "T2", 0x2ac, + "Section T2 of CT-CODE 2015-10/JAP.", + { 0x92,0x4a,0x14,0x8b,0x00,0x16,0xd0,0xa0,0x71,0x8b, + 0x81,0x73,0x98,0x56,0x81,0x38,0x5b,0x40,0x10,0x95 }}, + // 22 + { STR_M_USA, 0, "T2", 0x2ac, + "Section T2 of CT-CODE 2015-10/USA.", + { 0xc5,0x0d,0x38,0xd7,0xba,0x4f,0x35,0x9b,0x4f,0x2b, + 0x22,0xf9,0x34,0x92,0x65,0xd6,0x11,0x5b,0x00,0x47 }}, + // 23 + { STR_M_USA, 0, "T2", 0x3a0, + "Section T2 of Revolution.alpha/USA (D8 moved).", + { 0x17,0x87,0x71,0x5e,0x56,0x06,0x7c,0xda,0xc2,0x41, + 0xfa,0xc7,0xdd,0x7d,0xc0,0xef,0x2e,0xed,0x43,0x53 }}, + // 24 + { STR_M_USA, 0, "T2", 0x3a0, + "Section T2 of Revolution.alpha/USA.", + { 0x2a,0x7b,0xe1,0x25,0xc0,0x7c,0x47,0x33,0x96,0x95, + 0x23,0x49,0x46,0x4f,0xa0,0xc2,0x33,0x72,0x9b,0x99 }}, + // 25 + { STR_M_PAL, 0, "T2", 0x3a0, + "Section T2 of Revolution.alpha/PAL.", + { 0x81,0x5d,0x23,0xd7,0xd9,0xbe,0x26,0xf6,0xa3,0x7d, + 0xd5,0x8b,0x85,0x08,0xeb,0x77,0x4b,0x70,0x77,0xa7 }}, + // 26 + { STR_M_PAL, 0, "T2", 0x3a0, + "Section T2 of Revolution.alpha/PAL (D8 moved).", + { 0x8e,0xb3,0x50,0x53,0xc4,0xf8,0x28,0x0e,0xe4,0x81, + 0xae,0xfd,0xed,0x97,0xfd,0x72,0xd8,0xc9,0xbe,0x3c }}, + // 27 + { STR_M_JAP, 0, "T2", 0x3a0, + "Section T2 of Revolution.alpha/JAP.", + { 0x99,0x71,0xef,0x50,0x08,0x73,0xbf,0xa3,0xc7,0xcb, + 0x98,0x56,0x59,0x88,0xae,0xbf,0x52,0x52,0x0f,0x1f }}, + // 28 + { STR_M_JAP, 0, "T2", 0x3a0, + "Section T2 of Revolution.alpha/JAP (D8 moved).", + { 0xbf,0xb6,0x75,0x1e,0x2a,0x99,0x4d,0x50,0x69,0xa5, + 0x87,0xef,0x01,0x39,0xa1,0x6b,0x15,0x99,0xd4,0xb7 }}, + // 29 + { STR_M_KOR, 0, "D0", 0x5c0, + "Section D0 of original MKWii/KOR.", + { 0x2d,0x62,0xc1,0xdb,0x2a,0x82,0x64,0x0a,0xa7,0x61, + 0xdd,0x6d,0xfc,0x4a,0x4d,0x59,0xb4,0x51,0x6c,0x27 }}, + // 30 + { STR_M_USA, 0, "D0", 0x5c0, + "Section D0 of original MKWii/USA.", + { 0x47,0x6d,0x32,0x6c,0x21,0xaa,0x65,0xf2,0x9e,0x69, + 0xf9,0xde,0xd2,0xaa,0xb6,0xc2,0x76,0x94,0x19,0x09 }}, + // 31 + { STR_M_JAP, 0, "D0", 0x5c0, + "Section D0 of original MKWii/JAP.", + { 0x7f,0xcd,0xe8,0xc8,0x06,0xfb,0x10,0xbc,0x81,0x9c, + 0xb8,0xbb,0xa0,0x72,0x6a,0xd8,0x75,0xb2,0xb7,0x6a }}, + // 32 + { STR_M_PAL, 0, "D0", 0x5c0, + "Section D0 of original MKWii/PAL.", + { 0xc8,0xbf,0xf1,0x5d,0x1d,0x5a,0x05,0x77,0xbc,0xec, + 0x42,0x05,0xe7,0x11,0x76,0x64,0xa1,0x87,0xfd,0x3d }}, + // 33 + { STR_M_JAP, 0, "D1", 0x8a0, + "Section D1 of original MKWii/JAP.", + { 0x5a,0x2c,0x70,0x39,0x11,0x58,0xa0,0x71,0xc0,0xb9, + 0x09,0xac,0x3e,0xde,0x55,0x64,0xb0,0x70,0xb7,0x52 }}, + // 34 + { STR_M_PAL, 0, "D1", 0x8a0, + "Section D1 of original MKWii/PAL.", + { 0x6c,0xe1,0x67,0x91,0x92,0x92,0x44,0x05,0x12,0x12, + 0x64,0xff,0x57,0x7f,0xae,0x81,0xe5,0xa8,0xf8,0x7a }}, + // 35 + { STR_M_USA, 0, "D1", 0x8a0, + "Section D1 of original MKWii/USA.", + { 0x73,0x5b,0x90,0x85,0x58,0x13,0x3f,0x1e,0x56,0x67, + 0xeb,0x61,0x3a,0xa9,0xe9,0x13,0xc4,0xd3,0xd5,0x6f }}, + // 36 + { STR_M_KOR, 0, "D1", 0x8a0, + "Section D1 of original MKWii/KOR.", + { 0x8b,0x99,0x7f,0x81,0xdf,0x8b,0x0e,0x1d,0x96,0x3e, + 0xbf,0xe7,0xa3,0xb2,0x0e,0x92,0x7d,0xcc,0x5f,0x5b }}, + // 37 + { STR_M_USA, 0, "D6", 0x13c0, + "Section D6 of original MKWii/USA.", + { 0x98,0x55,0xd3,0xa2,0x15,0x10,0xe6,0xd4,0x78,0xa8, + 0x40,0x51,0xe5,0x51,0x77,0x04,0x10,0x18,0x29,0x63 }}, + // 38 + { STR_M_PAL, 0, "D6", 0x13c0, + "Section D6 of original MKWii/PAL.", + { 0xaa,0x9a,0x25,0x46,0xb8,0xd9,0x57,0x5e,0x22,0x6d, + 0x72,0x2f,0x71,0xff,0x86,0x49,0xc6,0x12,0x2d,0xcf }}, + // 39 + { STR_M_JAP, 0, "D6", 0x13c0, + "Section D6 of original MKWii/JAP.", + { 0xb7,0x6b,0x13,0x92,0x1c,0xad,0x77,0x78,0x92,0xe8, + 0xff,0xe4,0x5d,0x45,0xe6,0x1c,0x06,0xb8,0x4d,0xdb }}, + // 40 + { STR_M_KOR, 0, "D6", 0x13e0, + "Section D6 of original MKWii/KOR.", + { 0xad,0x8f,0x5f,0x3d,0xf0,0x9d,0xa5,0x26,0x9f,0x71, + 0xfb,0xeb,0x7b,0x68,0x99,0x72,0x71,0xdc,0x65,0xcc }}, + // 41 + { STR_M_PAL, 0, "D7", 0x21a0, + "Section D7 of original MKWii/PAL.", + { 0x43,0x0d,0x6f,0xd3,0xe0,0x12,0x72,0x2f,0x44,0x5b, + 0x9e,0xdc,0x92,0x1b,0xe0,0x62,0x8e,0x72,0xe8,0x07 }}, + // 42 + { STR_M_JAP, 0, "D7", 0x21a0, + "Section D7 of original MKWii/JAP.", + { 0x4d,0xc4,0xf5,0x81,0x3b,0x8b,0x9e,0x5d,0xc3,0x36, + 0xfb,0x91,0xa9,0xc0,0x5b,0xd7,0x59,0x11,0xee,0x25 }}, + // 43 + { STR_M_USA, 0, "D7", 0x21a0, + "Section D7 of original MKWii/USA.", + { 0x9b,0x2c,0x27,0x2b,0x6b,0x43,0xff,0xce,0x11,0x05, + 0x92,0x29,0x42,0x26,0x6f,0x9c,0x59,0xd6,0x55,0xe8 }}, + // 44 + { STR_M_KOR, 0, "D7", 0x21a0, + "Section D7 of original MKWii/KOR.", + { 0xc3,0x04,0xfc,0x7f,0xf8,0x6f,0xf0,0x39,0xa9,0xe7, + 0xda,0x78,0xf3,0x1b,0xfa,0x41,0x91,0x7d,0xf1,0x3c }}, + // 45 + { STR_M_USA, 0, "T0", 0x2460, + "Section T0 of original MKWii/USA.", + { 0x5e,0x16,0x94,0x9f,0x50,0x5f,0xc6,0xc3,0x6e,0xca, + 0x76,0x5b,0x8a,0x07,0x6e,0x23,0x68,0x77,0x84,0x67 }}, + // 46 + { STR_M_PAL, 0, "T0", 0x2460, + "Section T0 of original MKWii/PAL.", + { 0x67,0xa0,0xfd,0x48,0x8f,0x9b,0xe2,0xb8,0xa1,0x35, + 0xed,0x61,0x48,0xe7,0x41,0x0b,0xb6,0x5b,0xdf,0x2f }}, + // 47 + { STR_M_KOR, 0, "T0", 0x2460, + "Section T0 of original MKWii/KOR.", + { 0x8e,0x71,0x21,0xdf,0x02,0x9f,0xff,0x15,0x45,0x5b, + 0xc9,0x30,0xdd,0xda,0x1a,0x84,0x35,0x41,0xe8,0xc3 }}, + // 48 + { STR_M_JAP, 0, "T0", 0x2460, + "Section T0 of original MKWii/JAP.", + { 0xa6,0x86,0x2e,0xbc,0x13,0xdd,0x9e,0x7e,0xfa,0xa9, + 0x0d,0x49,0x19,0x5f,0x9b,0xe6,0x30,0x19,0x01,0x4e }}, + // 49 + { STR_M_USA, 0, "T0", 0x2460, + "Section T0 of Revolution.alpha/USA.", + { 0xce,0x3f,0x05,0xb9,0x3d,0x39,0xa0,0x44,0xea,0x87, + 0x3c,0xe9,0x1d,0xff,0x8e,0xa8,0x93,0x98,0x05,0xe4 }}, + // 50 + { STR_M_KOR, 0, "D4", 0x13100, + "Section D4 of original MKWii/KOR.", + { 0x23,0x8c,0xc9,0x84,0xd1,0x07,0x8a,0x1b,0xc4,0x56, + 0x1b,0x56,0x30,0xe0,0x1a,0x4d,0xbc,0xa1,0x48,0x0c }}, + // 51 + { STR_M_JAP, 0, "D4", 0x13120, + "Section D4 of original MKWii/JAP.", + { 0xba,0x43,0xcc,0x17,0x8b,0x01,0x44,0xc0,0x5b,0x38, + 0x59,0x9d,0xfc,0x2c,0x14,0xcd,0xa6,0x9b,0xc1,0x8a }}, + // 52 + { STR_M_USA, 0, "D4", 0x13420, + "Section D4 of original MKWii/USA.", + { 0xbd,0x62,0x9a,0x5e,0x1b,0xda,0xf5,0x46,0xe8,0x04, + 0xcb,0xa1,0xfa,0xca,0xac,0x38,0xac,0x7a,0xe6,0xaf }}, + // 53 + { STR_M_PAL, 0, "D4", 0x136c0, + "Section D4 of original MKWii/PAL.", + { 0x4f,0xd6,0xcf,0x0f,0x65,0x76,0xc1,0x23,0x96,0x86, + 0xc8,0x37,0x1b,0x95,0x93,0x9f,0xba,0x72,0xf6,0xc5 }}, + // 54 + { STR_M_KOR, 0, "D5", 0x39d00, + "Section D5 of original MKWii/KOR.", + { 0x4a,0x10,0xfb,0x98,0x57,0xb0,0xa5,0x96,0xb7,0xf6, + 0x92,0x06,0xed,0xf2,0xdf,0x04,0x5e,0x6d,0x63,0x55 }}, + // 55 + { STR_M_USA, 0, "D5", 0x47aa0, + "Section D5 of original MKWii/USA.", + { 0x9b,0x08,0x11,0xf2,0xb5,0xed,0x17,0x69,0x7e,0x4b, + 0xb3,0xef,0xb4,0x65,0x23,0x78,0x21,0x24,0x27,0xc1 }}, + // 56 + { STR_M_JAP, 0, "D5", 0x4bac0, + "Section D5 of original MKWii/JAP.", + { 0x16,0x24,0x5d,0xad,0xb9,0x7f,0xe9,0x5f,0x00,0x7f, + 0xef,0x6f,0xec,0xf6,0x24,0xe5,0x35,0xc1,0x7c,0x38 }}, + // 57 + { STR_M_PAL, 0, "D5", 0x4bac0, + "Section D5 of original MKWii/PAL.", + { 0x4a,0x9a,0x76,0x06,0x69,0x35,0x0b,0xd2,0x54,0xd2, + 0xbf,0x22,0xac,0xd9,0xf9,0xc8,0x13,0xcf,0x95,0xd8 }}, + // 58 + { STR_M_JAP, 0, "T1", 0x23da40, + "Section T1 of original MKWii/JAP.", + { 0x93,0xb4,0x6b,0xbe,0xab,0xf5,0x7d,0x98,0x93,0xf2, + 0x16,0x07,0x4f,0xc0,0x44,0x82,0x01,0xd1,0x42,0x40 }}, + // 59 + { STR_M_USA, 0, "T1", 0x23da80, + "Section T1 of original MKWii/USA.", + { 0x3a,0x6d,0x28,0xc3,0xdd,0xc8,0x10,0xcc,0xf0,0xb2, + 0x7f,0xe7,0xd8,0x7d,0x12,0x8f,0x62,0x72,0xd0,0xed }}, + // 60 + { STR_M_PAL, 0, "T1", 0x23db20, + "Section T1 of original MKWii/PAL.", + { 0x1c,0xbe,0x24,0xaf,0x98,0x6a,0x6e,0x3e,0xbe,0xa1, + 0x5c,0xc9,0x66,0x49,0xce,0xc7,0x7d,0x73,0x75,0x7b }}, + // 61 + { STR_M_KOR, 0, "T1", 0x23dea0, + "Section T1 of original MKWii/KOR.", + { 0xfc,0xf6,0x28,0x91,0x85,0x47,0x11,0xc3,0x4e,0xac, + 0x2e,0x61,0x47,0x53,0x5e,0xe1,0x7c,0x3c,0xb8,0xe6 }}, + // 62 + { STR_M_KOR, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI, "", 0x2916c0, + "Original KOR file with Wiimmfi support.", + { 0xd3,0x9e,0x00,0xa6,0xcb,0x15,0x5d,0xbd,0x86,0x60, + 0xc2,0xd2,0x1e,0x91,0x83,0x29,0x75,0x06,0x42,0x42 }}, + // 63 + { STR_M_KOR, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_ORIG, "", 0x2916c0, + "Original KOR file.", + { 0xd8,0x86,0x46,0x75,0x0f,0x67,0x63,0x5e,0x17,0x91, + 0x1c,0xf9,0x69,0x20,0xfb,0x28,0xc9,0x8e,0x6d,0xfe }}, + // 64 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_ORIG, "", 0x29f340, + "Original USA file.", + { 0x1b,0x36,0x72,0x09,0x91,0x9f,0xcc,0x22,0x5c,0x74, + 0xa1,0x55,0xdc,0x46,0xf2,0x9c,0x5d,0x64,0xb9,0xa2 }}, + // 65 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI, "", 0x29f340, + "Original USA file with Wiimmfi support.", + { 0xb7,0x69,0x39,0x0c,0x2a,0x37,0x20,0x6e,0xad,0x2c, + 0xcd,0xd5,0x56,0xc8,0x81,0x4c,0x05,0xf2,0x9e,0x36 }}, + // 66 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x29f71c, + "Original USA file with Wiimmfi support and CT-CODE (2015-10).", + { 0x10,0xa8,0x82,0x21,0xc8,0xbd,0xb2,0x80,0xa5,0x9a, + 0xa5,0xa1,0x91,0xcc,0x17,0x34,0x08,0xdb,0x32,0x5f }}, + // 67 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x29f71c, + "Original USA file with CT-CODE (2015-10, D8 moved).", + { 0x7d,0x24,0x63,0xf1,0x27,0x1f,0xee,0x81,0xe1,0xbc, + 0x01,0xdd,0xf7,0xc7,0x85,0x95,0x88,0x74,0x4c,0x79 }}, + // 68 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x29f71c, + "Original USA file with CT-CODE (2015-10).", + { 0x81,0x45,0xcb,0xa4,0x67,0x29,0xe2,0x8d,0x59,0xcc, + 0x45,0xb7,0x68,0x86,0x43,0xb1,0x65,0x54,0xd1,0xe6 }}, + // 69 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x29f71c, + "Original USA file with Wiimmfi support and CT-CODE (2015-10, D8 moved).", + { 0x8b,0x2f,0xb1,0x69,0xe5,0x3f,0x1e,0xe5,0x4b,0xec, + 0x16,0x1a,0x31,0xf4,0x19,0x5b,0xfe,0xbd,0xe4,0xe0 }}, + // 70 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x29f810, + "Original USA file with Wiimmfi support and CT-CODE (2013-10).", + { 0x32,0x29,0xbf,0xf9,0x82,0x8c,0xb5,0x2d,0xbb,0x95, + 0x6a,0x23,0x3c,0x13,0xa1,0x90,0xae,0xb7,0xc9,0x29 }}, + // 71 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x29f810, + "CTGPR USA file with CT-CODE (2013-10).", + { 0x3a,0x70,0x4f,0xa6,0xff,0xaa,0x78,0x1d,0xcc,0x17, + 0x31,0xa4,0xee,0x69,0x4e,0x33,0x39,0x93,0xc1,0x0d }}, + // 72 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x29f810, + "Original USA file with Wiimmfi support and CT-CODE (2013-10, D8 moved).", + { 0x48,0xde,0x3d,0xc8,0xab,0x44,0xb6,0x51,0x8a,0x20, + 0xc9,0xd4,0x11,0x90,0x68,0x4c,0xc9,0x90,0xf4,0x3c }}, + // 73 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x29f810, + "Original USA file with CT-CODE (2013-10).", + { 0x9b,0x05,0x02,0xcd,0x74,0x9b,0xa4,0x2b,0x8d,0x72, + 0xd2,0xe1,0x05,0x54,0xc7,0xa1,0x88,0xe8,0x65,0x2f }}, + // 74 + { STR_M_USA, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x29f810, + "Original USA file with CT-CODE (2013-10, D8 moved).", + { 0xd4,0xcd,0x5f,0x41,0x71,0xb7,0x39,0xb1,0x68,0x48, + 0xff,0xae,0xe9,0x5a,0x16,0x4c,0xa9,0x56,0x30,0x05 }}, + // 75 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_ORIG, "", 0x2a3020, + "Original JAP file.", + { 0x3b,0xf9,0xbe,0x2e,0x65,0x0e,0xae,0xe2,0xda,0xb3, + 0xb3,0xde,0xbc,0x2d,0xc6,0xdf,0x7f,0x84,0x0c,0x47 }}, + // 76 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI, "", 0x2a3020, + "Original JAP file with Wiimmfi support.", + { 0x98,0x64,0xc3,0xb5,0x5f,0xeb,0x5a,0x47,0xb4,0xdf, + 0xba,0x8d,0xfc,0x61,0x72,0x4b,0xe0,0x0c,0xd4,0x9f }}, + // 77 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a33fc, + "Original JAP file with CT-CODE (2015-10).", + { 0x0f,0x35,0x0f,0x5d,0xd1,0x86,0x97,0xb6,0xff,0xc5, + 0x26,0xee,0x08,0x57,0x88,0xf8,0x6d,0x1a,0x11,0x0e }}, + // 78 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a33fc, + "Original JAP file with Wiimmfi support and CT-CODE (2015-10, D8 moved).", + { 0x87,0x91,0x52,0xcf,0x0b,0xb8,0x72,0x0b,0xaf,0x37, + 0xf3,0xe0,0xa8,0x9d,0x50,0x26,0x06,0x25,0x54,0x94 }}, + // 79 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a33fc, + "Original JAP file with CT-CODE (2015-10, D8 moved).", + { 0xe4,0xbd,0x64,0x34,0xf8,0x4c,0x2f,0x6a,0x74,0xa7, + 0xab,0x06,0x46,0x07,0x49,0xea,0x91,0x52,0x13,0x42 }}, + // 80 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a33fc, + "Original JAP file with Wiimmfi support and CT-CODE (2015-10).", + { 0xec,0x12,0xe9,0x5e,0x0a,0x23,0x3d,0x8a,0xeb,0x8a, + 0xb4,0x2e,0xc6,0x5f,0x8c,0x4a,0x56,0x1d,0x05,0x4f }}, + // 81 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a34f0, + "Original JAP file with CT-CODE (2013-10).", + { 0x29,0x3c,0x59,0xbf,0x2c,0xd2,0x2f,0xcc,0x3f,0x2c, + 0x0b,0x8f,0xc7,0xc2,0xcd,0x85,0xb3,0xfc,0x5e,0x88 }}, + // 82 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a34f0, + "Original JAP file with CT-CODE (2013-10, D8 moved).", + { 0xa3,0xfb,0xc3,0x4a,0xf7,0x6e,0x4c,0x65,0x7d,0xa7, + 0x31,0x5e,0xb3,0xc4,0x42,0x86,0xef,0x0b,0x62,0x3a }}, + // 83 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a34f0, + "Original JAP file with Wiimmfi support and CT-CODE (2013-10).", + { 0xaa,0x63,0xac,0x04,0x39,0xaf,0xe5,0xa6,0xaf,0xf1, + 0x36,0xb8,0x93,0x00,0x30,0x25,0x37,0x8f,0x8e,0x8b }}, + // 84 + { STR_M_JAP, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a34f0, + "Original JAP file with Wiimmfi support and CT-CODE (2013-10, D8 moved).", + { 0xbe,0x31,0xc1,0x67,0x29,0xdc,0x47,0xdc,0x9f,0x66, + 0x1f,0xf7,0x56,0x11,0x33,0x32,0x00,0xc2,0x44,0x14 }}, + // 85 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI, "", 0x2a36a0, + "Original PAL file with Wiimmfi support.", + { 0xa5,0x5b,0x4e,0xfa,0x05,0x86,0xfa,0x9d,0xbd,0xe3, + 0xc9,0xc4,0x86,0x54,0xef,0x7a,0xfd,0xde,0xf1,0x95 }}, + // 86 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_ORIG, "", 0x2a36a0, + "Original PAL file.", + { 0xac,0x7d,0x72,0x44,0x86,0x30,0xad,0xe7,0x65,0x5f, + 0xc8,0xbc,0x5f,0xd7,0xa6,0x54,0x3c,0xb5,0x3a,0x49 }}, + // 87 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a3a7c, + "Original PAL file with Wiimmfi support and CT-CODE (2015-10, D8 moved).", + { 0x94,0x66,0xc7,0x26,0xa4,0x91,0x58,0xe1,0xc0,0xd5, + 0x20,0x6f,0xf4,0xde,0x17,0x7c,0xc6,0xe0,0x3f,0x13 }}, + // 88 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a3a7c, + "Original PAL file with Wiimmfi support and CT-CODE (2015-10).", + { 0xad,0x41,0x54,0x79,0xc5,0xda,0x9c,0x0d,0x2f,0x45, + 0xed,0xf4,0xfd,0x7b,0xe3,0x22,0xd8,0xad,0x6b,0xed }}, + // 89 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a3a7c, + "Original PAL file with CT-CODE (2015-10, D8 moved).", + { 0xd4,0x55,0x35,0x2e,0x5f,0x8a,0xf0,0xfc,0xb4,0x51, + 0x98,0x77,0xe6,0x74,0x29,0xbb,0xd5,0x75,0xcf,0x11 }}, + // 90 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a3a7c, + "Original PAL file with CT-CODE (2015-10).", + { 0xec,0xc2,0x1a,0xd8,0x82,0x4f,0x56,0x16,0x5f,0xa4, + 0x20,0xc1,0x52,0xc5,0x3e,0xaa,0x98,0x85,0x8b,0x19 }}, + // 91 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a3b70, + "Original PAL file with Wiimmfi support and CT-CODE (2013-10).", + { 0x2d,0x07,0x07,0xdc,0x84,0x1a,0x33,0x72,0x93,0xe0, + 0x33,0x4f,0x5f,0x9a,0xb3,0x44,0x07,0xd7,0xd5,0x62 }}, + // 92 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_WIIMMFI|DOL_S_CTCODE, "", 0x2a3b70, + "Original PAL file with Wiimmfi support and CT-CODE (2013-10, D8 moved).", + { 0x2d,0x6c,0x00,0x55,0x70,0x43,0x45,0x5c,0xc9,0x8d, + 0x1b,0x4d,0x2b,0x13,0x48,0x43,0x30,0x7d,0x93,0xb9 }}, + // 93 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a3b70, + "Original PAL file with CT-CODE (2013-10).", + { 0x32,0xf8,0xf1,0xa1,0xa3,0x45,0xf9,0x5d,0x4f,0x0c, + 0x33,0x6b,0x7c,0x4f,0x6d,0xfd,0xda,0x48,0x5c,0x4a }}, + // 94 + { STR_M_PAL, DOL_S_ANALYZED|DOL_S_KNOWN|DOL_S_CTCODE, "", 0x2a3b70, + "Original PAL file with CT-CODE (2013-10, D8 moved).", + { 0xfa,0x52,0x60,0x34,0x4a,0x82,0x6b,0x30,0x56,0x36, + 0x29,0xda,0x5f,0x67,0x9b,0xe5,0xa2,0xbe,0x87,0xdd }}, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////// struct DolAddressMap_t ////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +const DolAddressMap_t DolAddressMapPAL = +{ + 0x800060a4, // entry_point + 0x802a4080, // bss_addr + 0xe50fc, // bss_size + 0x802a4080, // shared_bss_addr + 0xceb80, // shared_bss_size + 0x80006210, // f_setup_register +}; + +const DolAddressMap_t DolAddressMapUSA = +{ + 0x800060a4, // entry_point + 0x8029fd00, // bss_addr + 0xe50fc, // bss_size + 0x802a4080, // shared_bss_addr + 0xceb80, // shared_bss_size + 0x80006210, // f_setup_register +}; + +const DolAddressMap_t DolAddressMapJAP = +{ + 0x800060a4, // entry_point + 0x802a3a00, // bss_addr + 0xe50fc, // bss_size + 0x802a4080, // shared_bss_addr + 0xceb80, // shared_bss_size + 0x80006210, // f_setup_register +}; + +const DolAddressMap_t DolAddressMapKOR = +{ + 0x800060a4, // entry_point + 0x80292080, // bss_addr + 0xe511c, // bss_size + 0x802a4080, // shared_bss_addr + 0xceb80, // shared_bss_size + 0x80006210, // f_setup_register +}; + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/db-dol.h b/project/src/db-dol.h new file mode 100644 index 0000000..efbd6bd --- /dev/null +++ b/project/src/db-dol.h @@ -0,0 +1,69 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_DB_DOL_H +#define SZS_DB_DOL_H 1 + +#include "lib-std.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// definitions /////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define N_DOL_SECTION_MAP 95 + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////// struct DolSectionMap_t ////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DolSectionMap_t]] + +typedef struct DolSectionMap_t +{ + uint mode; // str_mode_t + uint dol_stat; // dol_status_t for complete dol files + char sect[4]; // section name, emtpy for non sections + uint size; // size of data + ccp info; // info string + sha1_hash_t hash; // SHA1 hash + +} DolSectionMap_t; + +extern const DolSectionMap_t DolSectionMap[N_DOL_SECTION_MAP]; + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////// struct DolAddressMap_t ////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DolAddressMap_t]] + +typedef struct DolAddressMap_t +{ + u32 entry_point; // 800060a4 800060a4 800060a4 800060a4 + u32 bss_addr; // 802a4080 8029fd00 802a3a00 80292080 + u32 bss_size; // e50fc e50fc e50fc e511c + u32 shared_bss_addr; // 802a4080 802a4080 802a4080 802a4080 + u32 shared_bss_size; // ceb80 ceb80 ceb80 ceb80 + u32 f_setup_register; // 80006210 80006210 80006210 80006210 +} +DolAddressMap_t; + +extern const DolAddressMap_t DolAddressMapPAL; +extern const DolAddressMap_t DolAddressMapUSA; +extern const DolAddressMap_t DolAddressMapJAP; +extern const DolAddressMap_t DolAddressMapKOR; + +const DolAddressMap_t * GetDolAddressMap ( uint mode ); + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_DB_DOL_H diff --git a/project/src/db-file.c b/project/src/db-file.c new file mode 100644 index 0000000..0b1007a --- /dev/null +++ b/project/src/db-file.c @@ -0,0 +1,12064 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#include "db-file.h" +#include "db-kcl.h" + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// draw data //////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const s16 DbFileDraw[175] = +{ + // 0: empty list at index 0 + 0, + + // 1: castletree1.brres + 8, DFD_TREE, 370, 20, 920, 1820, 300, 900, + 0, + + // 10: castletree2.brres + 8, DFD_TREE, 370, 0, 920, 1820, 300, 900, + 0, + + // 19: coin.brres + 2, DFD_COIN, + 0, + + // 22: DKtreeA64.brres + 8, DFD_TREE, 240, 0, 1300, 2000, 300, 700, + 0, + + // 31: DKtreeB64.brres + 8, DFD_TREE, 190, 0, 1600, 1190, 300, 280, + 0, + + // 40: DKturibashiGC.brres + 8, DFD_RECTANGLE, KCL_FLAG_BRIDGE-N_KCL_FLAG, 915, 7250, -9, 0, 0, + 0, + + // 49: dokan_sfc.brres + 5, DFD_CYLINDER_Y, KCL_FLAG_DOKAN-N_KCL_FLAG, 220, 161, + 0, + + // 55: donkytree1GC.brres + 8, DFD_TREE, 380, 310, 1300, 2050, 300, 400, + 0, + + // 64: donkytree2GC.brres + 8, DFD_TREE, 350, 0, 1100, 1600, 300, 400, + 0, + + // 73: gardentreeDS.brres + 8, DFD_TREE, 240, 0, 540, 620, 300, 1800, + 0, + + // 82: HeyhoTreeGBA.brres + 8, DFD_TREE, 100, 0, 330, 530, 100, 200, + 0, + + // 91: itembox.brres + 2, DFD_ITEMBOX, + 0, + + // 94: karehayama.brres + 7, DFD_PYRAMID, KCL_FLAG_KAREHAYAMA-N_KCL_FLAG, 5, 300, 360, -100, + 0, + + // 102: kinoko.brres + 2, DFD_KINOKO, + 0, + + // 105: mariotreeGC.brres + 8, DFD_TREE, 340, 120, 900, 1700, 300, 900, + 0, + + // 114: obakeblockSFC.brres + 4, DFD_CUBE, KCL_FLAG_OBAKEBLOCK-N_KCL_FLAG, 250, + 0, + + // 119: pakkun_dokan.brres + 6, DFD_CYLINDER_Y, KCL_FLAG_DOKAN-N_KCL_FLAG, 288, 182, -69, + 0, + + // 126: PalmTree.brres + 8, DFD_TREE, 330, 0, 1850, 2200, 300, 1050, + 0, + + // 135: peachtreeGC.brres + 8, DFD_TREE, 370, 175, 1250, 2050, 300, 450, + 0, + + // 144: TownTreeDS.brres + 8, DFD_TREE, 420, 0, 800, 1620, 300, 1630, + 0, + + // 153: WLdokanGC.brres + 5, DFD_CYLINDER_Z, KCL_FLAG_DOKAN-N_KCL_FLAG, 1600, 200, + 0, + + // 159: WLscreenGC.brres + 9, DFD_CUBOID, KCL_FLAG_DECORATION-N_KCL_FLAG, 12920, 10906, 3100, 0, 3505, -1550, + 0, + + // 169: woodbox.brres + 5, DFD_CUBE, KCL_FLAG_WOODBOX-N_KCL_FLAG, 200, 100, + 0, +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// data base //////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const DbFile_t DbFile[N_DB_FILE+1] = +{ + { 0, 112, 236 }, // 0 + { 0, 305, 237 }, // 1 + { 0, 771, 238 }, // 2 + { 0, 636, 239 }, // 3 + { 0, 147, 240 }, // 4 + { 0, 646, 243 }, // 5 + { 0, 74, 254 }, // 6 + { 0, 367, 32 }, // 7 + { 0, 379, 420 }, // 8 + { 0, 257, 433 }, // 9 + { 0, 393, 64 }, // 10 + { 0, 494, 65 }, // 11 + { 0, 59, 66 }, // 12 + { 0, 690, 67 }, // 13 + { 0, 351, 81 }, // 14 + { 0, 893, 476 }, // 15 + { 0, 321, 480 }, // 16 + { 0, 660, 492 }, // 17 + { 0, 311, 304 }, // 18 + { 0, 287, 305 }, // 19 + { 0, 259, 312 }, // 20 + { 0, 332, 313 }, // 21 + { 0, 657, 453 }, // 22 + { 0, 916, 455 }, // 23 + { 0, 300, 457 }, // 24 + { 0, 714, 458 }, // 25 + { 1, 112, 236 }, // 26 + { 1, 305, 237 }, // 27 + { 1, 771, 238 }, // 28 + { 1, 636, 239 }, // 29 + { 1, 147, 240 }, // 30 + { 1, 829, 242 }, // 31 + { 1, 74, 254 }, // 32 + { 1, 367, 32 }, // 33 + { 1, 379, 420 }, // 34 + { 1, 257, 433 }, // 35 + { 1, 393, 64 }, // 36 + { 1, 494, 65 }, // 37 + { 1, 59, 66 }, // 38 + { 1, 690, 67 }, // 39 + { 1, 351, 81 }, // 40 + { 1, 893, 476 }, // 41 + { 1, 321, 480 }, // 42 + { 1, 660, 492 }, // 43 + { 1, 311, 304 }, // 44 + { 1, 287, 305 }, // 45 + { 1, 259, 312 }, // 46 + { 1, 332, 313 }, // 47 + { 1, 657, 453 }, // 48 + { 1, 916, 455 }, // 49 + { 1, 300, 457 }, // 50 + { 1, 714, 458 }, // 51 + { 2, 766, 231 }, // 52 + { 2, 389, 232 }, // 53 + { 2, 292, 233 }, // 54 + { 2, 371, 239 }, // 55 + { 2, 139, 240 }, // 56 + { 2, 814, 243 }, // 57 + { 2, 215, 244 }, // 58 + { 2, 729, 28 }, // 59 + { 2, 379, 420 }, // 60 + { 2, 895, 433 }, // 61 + { 2, 321, 480 }, // 62 + { 2, 840, 492 }, // 63 + { 2, 166, 494 }, // 64 + { 2, 534, 198 }, // 65 + { 2, 608, 199 }, // 66 + { 2, 744, 200 }, // 67 + { 2, 14, 201 }, // 68 + { 2, 194, 202 }, // 69 + { 2, 521, 340 }, // 70 + { 2, 238, 341 }, // 71 + { 2, 903, 453 }, // 72 + { 2, 812, 455 }, // 73 + { 2, 145, 456 }, // 74 + { 2, 474, 457 }, // 75 + { 2, 28, 462 }, // 76 + { 2, 713, 466 }, // 77 + { 3, 766, 231 }, // 78 + { 3, 389, 232 }, // 79 + { 3, 292, 233 }, // 80 + { 3, 371, 239 }, // 81 + { 3, 139, 240 }, // 82 + { 3, 579, 242 }, // 83 + { 3, 215, 244 }, // 84 + { 3, 729, 28 }, // 85 + { 3, 379, 420 }, // 86 + { 3, 895, 433 }, // 87 + { 3, 321, 480 }, // 88 + { 3, 840, 492 }, // 89 + { 3, 166, 494 }, // 90 + { 3, 534, 198 }, // 91 + { 3, 608, 199 }, // 92 + { 3, 744, 200 }, // 93 + { 3, 14, 201 }, // 94 + { 3, 194, 202 }, // 95 + { 3, 521, 340 }, // 96 + { 3, 238, 341 }, // 97 + { 3, 903, 453 }, // 98 + { 3, 812, 455 }, // 99 + { 3, 145, 456 }, // 100 + { 3, 474, 457 }, // 101 + { 3, 28, 462 }, // 102 + { 3, 713, 466 }, // 103 + { 4, 802, 239 }, // 104 + { 4, 469, 240 }, // 105 + { 4, 553, 243 }, // 106 + { 4, 379, 420 }, // 107 + { 4, 465, 422 }, // 108 + { 4, 80, 423 }, // 109 + { 4, 394, 424 }, // 110 + { 4, 493, 425 }, // 111 + { 4, 35, 426 }, // 112 + { 4, 184, 427 }, // 113 + { 4, 89, 431 }, // 114 + { 4, 887, 433 }, // 115 + { 4, 321, 480 }, // 116 + { 4, 674, 492 }, // 117 + { 4, 716, 210 }, // 118 + { 4, 716, 211 }, // 119 + { 4, 545, 453 }, // 120 + { 4, 93, 454 }, // 121 + { 4, 892, 455 }, // 122 + { 4, 392, 456 }, // 123 + { 4, 565, 457 }, // 124 + { 4, 841, 458 }, // 125 + { 4, 340, 463 }, // 126 + { 4, 15, 464 }, // 127 + { 5, 802, 239 }, // 128 + { 5, 469, 240 }, // 129 + { 5, 754, 242 }, // 130 + { 5, 379, 420 }, // 131 + { 5, 465, 422 }, // 132 + { 5, 80, 423 }, // 133 + { 5, 394, 424 }, // 134 + { 5, 493, 425 }, // 135 + { 5, 35, 426 }, // 136 + { 5, 184, 427 }, // 137 + { 5, 89, 431 }, // 138 + { 5, 887, 433 }, // 139 + { 5, 321, 480 }, // 140 + { 5, 674, 492 }, // 141 + { 5, 716, 210 }, // 142 + { 5, 716, 211 }, // 143 + { 5, 545, 453 }, // 144 + { 5, 93, 454 }, // 145 + { 5, 892, 455 }, // 146 + { 5, 392, 456 }, // 147 + { 5, 565, 457 }, // 148 + { 5, 841, 458 }, // 149 + { 5, 340, 463 }, // 150 + { 5, 15, 464 }, // 151 + { 6, 676, 3 }, // 152 + { 6, 679, 4 }, // 153 + { 6, 759, 5 }, // 154 + { 6, 33, 6 }, // 155 + { 6, 109, 223 }, // 156 + { 6, 153, 224 }, // 157 + { 6, 272, 225 }, // 158 + { 6, 126, 226 }, // 159 + { 6, 106, 239 }, // 160 + { 6, 197, 240 }, // 161 + { 6, 160, 243 }, // 162 + { 6, 628, 11 }, // 163 + { 6, 391, 12 }, // 164 + { 6, 526, 409 }, // 165 + { 6, 379, 420 }, // 166 + { 6, 290, 433 }, // 167 + { 6, 348, 71 }, // 168 + { 6, 897, 87 }, // 169 + { 6, 270, 450 }, // 170 + { 6, 196, 88 }, // 171 + { 6, 632, 475 }, // 172 + { 6, 321, 480 }, // 173 + { 6, 406, 492 }, // 174 + { 6, 659, 495 }, // 175 + { 6, 378, 258 }, // 176 + { 6, 278, 259 }, // 177 + { 6, 251, 260 }, // 178 + { 6, 501, 261 }, // 179 + { 6, 347, 372 }, // 180 + { 6, 732, 373 }, // 181 + { 6, 749, 326 }, // 182 + { 6, 731, 327 }, // 183 + { 6, 187, 330 }, // 184 + { 6, 670, 331 }, // 185 + { 6, 883, 400 }, // 186 + { 6, 29, 401 }, // 187 + { 6, 538, 453 }, // 188 + { 6, 777, 455 }, // 189 + { 6, 768, 456 }, // 190 + { 6, 304, 457 }, // 191 + { 6, 832, 458 }, // 192 + { 7, 676, 3 }, // 193 + { 7, 679, 4 }, // 194 + { 7, 759, 5 }, // 195 + { 7, 33, 6 }, // 196 + { 7, 109, 223 }, // 197 + { 7, 153, 224 }, // 198 + { 7, 272, 225 }, // 199 + { 7, 126, 226 }, // 200 + { 7, 106, 239 }, // 201 + { 7, 197, 240 }, // 202 + { 7, 555, 242 }, // 203 + { 7, 628, 11 }, // 204 + { 7, 391, 12 }, // 205 + { 7, 526, 409 }, // 206 + { 7, 379, 420 }, // 207 + { 7, 290, 433 }, // 208 + { 7, 348, 71 }, // 209 + { 7, 897, 87 }, // 210 + { 7, 270, 450 }, // 211 + { 7, 196, 88 }, // 212 + { 7, 632, 475 }, // 213 + { 7, 321, 480 }, // 214 + { 7, 406, 492 }, // 215 + { 7, 659, 495 }, // 216 + { 7, 378, 258 }, // 217 + { 7, 278, 259 }, // 218 + { 7, 251, 260 }, // 219 + { 7, 501, 261 }, // 220 + { 7, 347, 372 }, // 221 + { 7, 732, 373 }, // 222 + { 7, 749, 326 }, // 223 + { 7, 731, 327 }, // 224 + { 7, 187, 330 }, // 225 + { 7, 670, 331 }, // 226 + { 7, 883, 400 }, // 227 + { 7, 29, 401 }, // 228 + { 7, 538, 453 }, // 229 + { 7, 777, 455 }, // 230 + { 7, 768, 456 }, // 231 + { 7, 304, 457 }, // 232 + { 7, 832, 458 }, // 233 + { 8, 32, 229 }, // 234 + { 8, 797, 230 }, // 235 + { 8, 766, 231 }, // 236 + { 8, 389, 232 }, // 237 + { 8, 694, 239 }, // 238 + { 8, 700, 240 }, // 239 + { 8, 790, 243 }, // 240 + { 8, 358, 410 }, // 241 + { 8, 2, 411 }, // 242 + { 8, 379, 420 }, // 243 + { 8, 89, 431 }, // 244 + { 8, 337, 433 }, // 245 + { 8, 176, 62 }, // 246 + { 8, 502, 63 }, // 247 + { 8, 321, 480 }, // 248 + { 8, 226, 492 }, // 249 + { 8, 131, 493 }, // 250 + { 8, 716, 210 }, // 251 + { 8, 716, 211 }, // 252 + { 8, 259, 312 }, // 253 + { 8, 332, 313 }, // 254 + { 8, 151, 398 }, // 255 + { 8, 1, 399 }, // 256 + { 8, 85, 453 }, // 257 + { 8, 56, 455 }, // 258 + { 8, 615, 457 }, // 259 + { 9, 32, 229 }, // 260 + { 9, 797, 230 }, // 261 + { 9, 766, 231 }, // 262 + { 9, 389, 232 }, // 263 + { 9, 694, 239 }, // 264 + { 9, 700, 240 }, // 265 + { 9, 22, 242 }, // 266 + { 9, 358, 410 }, // 267 + { 9, 2, 411 }, // 268 + { 9, 379, 420 }, // 269 + { 9, 89, 431 }, // 270 + { 9, 337, 433 }, // 271 + { 9, 176, 62 }, // 272 + { 9, 502, 63 }, // 273 + { 9, 321, 480 }, // 274 + { 9, 226, 492 }, // 275 + { 9, 131, 493 }, // 276 + { 9, 716, 210 }, // 277 + { 9, 716, 211 }, // 278 + { 9, 259, 312 }, // 279 + { 9, 332, 313 }, // 280 + { 9, 151, 398 }, // 281 + { 9, 1, 399 }, // 282 + { 9, 85, 453 }, // 283 + { 9, 56, 455 }, // 284 + { 9, 615, 457 }, // 285 + { 10, 220, 7 }, // 286 + { 10, 141, 8 }, // 287 + { 10, 397, 9 }, // 288 + { 10, 262, 10 }, // 289 + { 10, 70, 239 }, // 290 + { 10, 214, 240 }, // 291 + { 10, 275, 243 }, // 292 + { 10, 191, 402 }, // 293 + { 10, 418, 403 }, // 294 + { 10, 701, 412 }, // 295 + { 10, 379, 420 }, // 296 + { 10, 598, 433 }, // 297 + { 10, 642, 438 }, // 298 + { 10, 177, 437 }, // 299 + { 10, 312, 439 }, // 300 + { 10, 603, 85 }, // 301 + { 10, 588, 445 }, // 302 + { 10, 779, 101 }, // 303 + { 10, 835, 102 }, // 304 + { 10, 443, 103 }, // 305 + { 10, 321, 480 }, // 306 + { 10, 491, 492 }, // 307 + { 10, 346, 453 }, // 308 + { 10, 96, 455 }, // 309 + { 10, 383, 457 }, // 310 + { 10, 873, 458 }, // 311 + { 11, 220, 7 }, // 312 + { 11, 141, 8 }, // 313 + { 11, 397, 9 }, // 314 + { 11, 262, 10 }, // 315 + { 11, 70, 239 }, // 316 + { 11, 214, 240 }, // 317 + { 11, 803, 242 }, // 318 + { 11, 191, 402 }, // 319 + { 11, 418, 403 }, // 320 + { 11, 701, 412 }, // 321 + { 11, 379, 420 }, // 322 + { 11, 598, 433 }, // 323 + { 11, 642, 438 }, // 324 + { 11, 177, 437 }, // 325 + { 11, 312, 439 }, // 326 + { 11, 603, 85 }, // 327 + { 11, 588, 445 }, // 328 + { 11, 779, 101 }, // 329 + { 11, 835, 102 }, // 330 + { 11, 443, 103 }, // 331 + { 11, 321, 480 }, // 332 + { 11, 491, 492 }, // 333 + { 11, 346, 453 }, // 334 + { 11, 96, 455 }, // 335 + { 11, 383, 457 }, // 336 + { 11, 873, 458 }, // 337 + { 12, 627, 236 }, // 338 + { 12, 514, 237 }, // 339 + { 12, 97, 238 }, // 340 + { 12, 746, 239 }, // 341 + { 12, 285, 240 }, // 342 + { 12, 711, 243 }, // 343 + { 12, 685, 253 }, // 344 + { 12, 298, 252 }, // 345 + { 12, 43, 23 }, // 346 + { 12, 281, 415 }, // 347 + { 12, 132, 416 }, // 348 + { 12, 379, 420 }, // 349 + { 12, 216, 46 }, // 350 + { 12, 329, 47 }, // 351 + { 12, 664, 433 }, // 352 + { 12, 192, 83 }, // 353 + { 12, 321, 480 }, // 354 + { 12, 560, 492 }, // 355 + { 12, 567, 197 }, // 356 + { 12, 881, 274 }, // 357 + { 12, 848, 275 }, // 358 + { 12, 229, 282 }, // 359 + { 12, 678, 283 }, // 360 + { 12, 761, 302 }, // 361 + { 12, 907, 303 }, // 362 + { 12, 552, 453 }, // 363 + { 12, 698, 455 }, // 364 + { 12, 758, 456 }, // 365 + { 12, 165, 457 }, // 366 + { 12, 755, 458 }, // 367 + { 13, 627, 236 }, // 368 + { 13, 514, 237 }, // 369 + { 13, 97, 238 }, // 370 + { 13, 746, 239 }, // 371 + { 13, 285, 240 }, // 372 + { 13, 711, 243 }, // 373 + { 13, 685, 253 }, // 374 + { 13, 298, 252 }, // 375 + { 13, 43, 23 }, // 376 + { 13, 281, 415 }, // 377 + { 13, 132, 416 }, // 378 + { 13, 379, 420 }, // 379 + { 13, 216, 46 }, // 380 + { 13, 329, 47 }, // 381 + { 13, 664, 433 }, // 382 + { 13, 192, 83 }, // 383 + { 13, 321, 480 }, // 384 + { 13, 560, 492 }, // 385 + { 13, 567, 197 }, // 386 + { 13, 881, 274 }, // 387 + { 13, 848, 275 }, // 388 + { 13, 229, 282 }, // 389 + { 13, 678, 283 }, // 390 + { 13, 761, 302 }, // 391 + { 13, 907, 303 }, // 392 + { 13, 552, 453 }, // 393 + { 13, 698, 455 }, // 394 + { 13, 758, 456 }, // 395 + { 13, 165, 457 }, // 396 + { 13, 755, 458 }, // 397 + { 14, 901, 173 }, // 398 + { 14, 505, 239 }, // 399 + { 14, 124, 240 }, // 400 + { 14, 863, 243 }, // 401 + { 14, 357, 27 }, // 402 + { 14, 479, 31 }, // 403 + { 14, 379, 420 }, // 404 + { 14, 500, 433 }, // 405 + { 14, 204, 73 }, // 406 + { 14, 321, 480 }, // 407 + { 14, 242, 483 }, // 408 + { 14, 113, 112 }, // 409 + { 14, 5, 492 }, // 410 + { 14, 908, 390 }, // 411 + { 14, 174, 391 }, // 412 + { 14, 316, 332 }, // 413 + { 14, 805, 333 }, // 414 + { 14, 386, 453 }, // 415 + { 14, 794, 455 }, // 416 + { 14, 135, 456 }, // 417 + { 14, 609, 457 }, // 418 + { 14, 276, 458 }, // 419 + { 15, 901, 173 }, // 420 + { 15, 505, 239 }, // 421 + { 15, 124, 240 }, // 422 + { 15, 396, 242 }, // 423 + { 15, 357, 27 }, // 424 + { 15, 479, 31 }, // 425 + { 15, 379, 420 }, // 426 + { 15, 500, 433 }, // 427 + { 15, 204, 73 }, // 428 + { 15, 321, 480 }, // 429 + { 15, 242, 483 }, // 430 + { 15, 113, 112 }, // 431 + { 15, 5, 492 }, // 432 + { 15, 908, 390 }, // 433 + { 15, 174, 391 }, // 434 + { 15, 316, 332 }, // 435 + { 15, 805, 333 }, // 436 + { 15, 386, 453 }, // 437 + { 15, 794, 455 }, // 438 + { 15, 135, 456 }, // 439 + { 15, 609, 457 }, // 440 + { 15, 276, 458 }, // 441 + { 16, 593, 236 }, // 442 + { 16, 825, 237 }, // 443 + { 16, 720, 238 }, // 444 + { 16, 243, 239 }, // 445 + { 16, 327, 240 }, // 446 + { 16, 770, 243 }, // 447 + { 16, 30, 246 }, // 448 + { 16, 860, 406 }, // 449 + { 16, 205, 408 }, // 450 + { 16, 526, 409 }, // 451 + { 16, 701, 412 }, // 452 + { 16, 379, 420 }, // 453 + { 16, 154, 432 }, // 454 + { 16, 318, 433 }, // 455 + { 16, 121, 68 }, // 456 + { 16, 375, 69 }, // 457 + { 16, 629, 70 }, // 458 + { 16, 687, 75 }, // 459 + { 16, 277, 76 }, // 460 + { 16, 748, 77 }, // 461 + { 16, 246, 79 }, // 462 + { 16, 822, 469 }, // 463 + { 16, 632, 475 }, // 464 + { 16, 321, 480 }, // 465 + { 16, 589, 492 }, // 466 + { 16, 677, 346 }, // 467 + { 16, 454, 347 }, // 468 + { 16, 259, 312 }, // 469 + { 16, 332, 313 }, // 470 + { 16, 159, 453 }, // 471 + { 16, 233, 455 }, // 472 + { 16, 813, 457 }, // 473 + { 16, 449, 458 }, // 474 + { 17, 593, 236 }, // 475 + { 17, 825, 237 }, // 476 + { 17, 720, 238 }, // 477 + { 17, 243, 239 }, // 478 + { 17, 327, 240 }, // 479 + { 17, 3, 242 }, // 480 + { 17, 30, 246 }, // 481 + { 17, 860, 406 }, // 482 + { 17, 205, 408 }, // 483 + { 17, 526, 409 }, // 484 + { 17, 701, 412 }, // 485 + { 17, 379, 420 }, // 486 + { 17, 154, 432 }, // 487 + { 17, 318, 433 }, // 488 + { 17, 121, 68 }, // 489 + { 17, 375, 69 }, // 490 + { 17, 629, 70 }, // 491 + { 17, 687, 75 }, // 492 + { 17, 277, 76 }, // 493 + { 17, 748, 77 }, // 494 + { 17, 246, 79 }, // 495 + { 17, 822, 469 }, // 496 + { 17, 632, 475 }, // 497 + { 17, 321, 480 }, // 498 + { 17, 589, 492 }, // 499 + { 17, 677, 346 }, // 500 + { 17, 454, 347 }, // 501 + { 17, 259, 312 }, // 502 + { 17, 332, 313 }, // 503 + { 17, 159, 453 }, // 504 + { 17, 233, 455 }, // 505 + { 17, 813, 457 }, // 506 + { 17, 449, 458 }, // 507 + { 18, 203, 239 }, // 508 + { 18, 45, 240 }, // 509 + { 18, 374, 243 }, // 510 + { 18, 30, 246 }, // 511 + { 18, 529, 25 }, // 512 + { 18, 765, 30 }, // 513 + { 18, 834, 33 }, // 514 + { 18, 705, 34 }, // 515 + { 18, 379, 420 }, // 516 + { 18, 89, 431 }, // 517 + { 18, 118, 433 }, // 518 + { 18, 195, 72 }, // 519 + { 18, 798, 440 }, // 520 + { 18, 270, 450 }, // 521 + { 18, 492, 468 }, // 522 + { 18, 632, 475 }, // 523 + { 18, 321, 480 }, // 524 + { 18, 75, 115 }, // 525 + { 18, 792, 492 }, // 526 + { 18, 716, 210 }, // 527 + { 18, 716, 211 }, // 528 + { 18, 677, 346 }, // 529 + { 18, 454, 347 }, // 530 + { 18, 633, 284 }, // 531 + { 18, 658, 285 }, // 532 + { 18, 146, 286 }, // 533 + { 18, 774, 287 }, // 534 + { 18, 146, 288 }, // 535 + { 18, 774, 289 }, // 536 + { 18, 347, 372 }, // 537 + { 18, 732, 373 }, // 538 + { 18, 105, 453 }, // 539 + { 18, 280, 454 }, // 540 + { 18, 600, 455 }, // 541 + { 18, 385, 456 }, // 542 + { 18, 110, 457 }, // 543 + { 18, 849, 458 }, // 544 + { 19, 203, 239 }, // 545 + { 19, 45, 240 }, // 546 + { 19, 673, 242 }, // 547 + { 19, 30, 246 }, // 548 + { 19, 529, 25 }, // 549 + { 19, 765, 30 }, // 550 + { 19, 834, 33 }, // 551 + { 19, 705, 34 }, // 552 + { 19, 379, 420 }, // 553 + { 19, 89, 431 }, // 554 + { 19, 118, 433 }, // 555 + { 19, 195, 72 }, // 556 + { 19, 798, 440 }, // 557 + { 19, 270, 450 }, // 558 + { 19, 492, 468 }, // 559 + { 19, 632, 475 }, // 560 + { 19, 321, 480 }, // 561 + { 19, 75, 115 }, // 562 + { 19, 792, 492 }, // 563 + { 19, 716, 210 }, // 564 + { 19, 716, 211 }, // 565 + { 19, 677, 346 }, // 566 + { 19, 454, 347 }, // 567 + { 19, 633, 284 }, // 568 + { 19, 658, 285 }, // 569 + { 19, 146, 286 }, // 570 + { 19, 774, 287 }, // 571 + { 19, 146, 288 }, // 572 + { 19, 774, 289 }, // 573 + { 19, 347, 372 }, // 574 + { 19, 732, 373 }, // 575 + { 19, 105, 453 }, // 576 + { 19, 280, 454 }, // 577 + { 19, 600, 455 }, // 578 + { 19, 385, 456 }, // 579 + { 19, 110, 457 }, // 580 + { 19, 849, 458 }, // 581 + { 20, 827, 170 }, // 582 + { 20, 818, 239 }, // 583 + { 20, 665, 240 }, // 584 + { 20, 223, 243 }, // 585 + { 20, 376, 414 }, // 586 + { 20, 379, 420 }, // 587 + { 20, 562, 421 }, // 588 + { 20, 727, 433 }, // 589 + { 20, 270, 450 }, // 590 + { 20, 866, 482 }, // 591 + { 20, 242, 483 }, // 592 + { 20, 453, 492 }, // 593 + { 20, 128, 206 }, // 594 + { 20, 435, 207 }, // 595 + { 20, 92, 222 }, // 596 + { 20, 90, 278 }, // 597 + { 20, 201, 279 }, // 598 + { 20, 127, 280 }, // 599 + { 20, 424, 281 }, // 600 + { 20, 784, 356 }, // 601 + { 20, 468, 357 }, // 602 + { 20, 578, 360 }, // 603 + { 20, 87, 361 }, // 604 + { 20, 525, 364 }, // 605 + { 20, 899, 365 }, // 606 + { 20, 347, 372 }, // 607 + { 20, 732, 373 }, // 608 + { 20, 881, 388 }, // 609 + { 20, 848, 389 }, // 610 + { 20, 908, 390 }, // 611 + { 20, 174, 391 }, // 612 + { 20, 723, 453 }, // 613 + { 20, 833, 455 }, // 614 + { 20, 844, 457 }, // 615 + { 20, 319, 458 }, // 616 + { 21, 827, 170 }, // 617 + { 21, 818, 239 }, // 618 + { 21, 665, 240 }, // 619 + { 21, 223, 243 }, // 620 + { 21, 376, 414 }, // 621 + { 21, 379, 420 }, // 622 + { 21, 562, 421 }, // 623 + { 21, 727, 433 }, // 624 + { 21, 270, 450 }, // 625 + { 21, 866, 482 }, // 626 + { 21, 242, 483 }, // 627 + { 21, 453, 492 }, // 628 + { 21, 128, 206 }, // 629 + { 21, 435, 207 }, // 630 + { 21, 92, 222 }, // 631 + { 21, 90, 278 }, // 632 + { 21, 201, 279 }, // 633 + { 21, 127, 280 }, // 634 + { 21, 424, 281 }, // 635 + { 21, 784, 356 }, // 636 + { 21, 468, 357 }, // 637 + { 21, 578, 360 }, // 638 + { 21, 87, 361 }, // 639 + { 21, 525, 364 }, // 640 + { 21, 899, 365 }, // 641 + { 21, 347, 372 }, // 642 + { 21, 732, 373 }, // 643 + { 21, 881, 388 }, // 644 + { 21, 848, 389 }, // 645 + { 21, 908, 390 }, // 646 + { 21, 174, 391 }, // 647 + { 21, 723, 453 }, // 648 + { 21, 833, 455 }, // 649 + { 21, 844, 457 }, // 650 + { 21, 319, 458 }, // 651 + { 22, 260, 239 }, // 652 + { 22, 366, 240 }, // 653 + { 22, 183, 243 }, // 654 + { 22, 23, 26 }, // 655 + { 22, 434, 37 }, // 656 + { 22, 379, 420 }, // 657 + { 22, 914, 433 }, // 658 + { 22, 699, 451 }, // 659 + { 22, 190, 116 }, // 660 + { 22, 228, 117 }, // 661 + { 22, 535, 118 }, // 662 + { 22, 283, 119 }, // 663 + { 22, 88, 120 }, // 664 + { 22, 254, 121 }, // 665 + { 22, 57, 122 }, // 666 + { 22, 730, 123 }, // 667 + { 22, 530, 124 }, // 668 + { 22, 231, 125 }, // 669 + { 22, 271, 126 }, // 670 + { 22, 245, 127 }, // 671 + { 22, 611, 128 }, // 672 + { 22, 888, 129 }, // 673 + { 22, 8, 130 }, // 674 + { 22, 20, 131 }, // 675 + { 22, 574, 132 }, // 676 + { 22, 733, 133 }, // 677 + { 22, 55, 134 }, // 678 + { 22, 817, 135 }, // 679 + { 22, 846, 136 }, // 680 + { 22, 762, 137 }, // 681 + { 22, 752, 138 }, // 682 + { 22, 477, 139 }, // 683 + { 22, 83, 140 }, // 684 + { 22, 212, 141 }, // 685 + { 22, 531, 142 }, // 686 + { 22, 767, 143 }, // 687 + { 22, 21, 144 }, // 688 + { 22, 401, 145 }, // 689 + { 22, 836, 146 }, // 690 + { 22, 382, 147 }, // 691 + { 22, 407, 148 }, // 692 + { 22, 532, 149 }, // 693 + { 22, 364, 150 }, // 694 + { 22, 879, 151 }, // 695 + { 22, 168, 152 }, // 696 + { 22, 810, 153 }, // 697 + { 22, 618, 154 }, // 698 + { 22, 76, 155 }, // 699 + { 22, 404, 156 }, // 700 + { 22, 268, 157 }, // 701 + { 22, 898, 158 }, // 702 + { 22, 328, 159 }, // 703 + { 22, 917, 160 }, // 704 + { 22, 419, 161 }, // 705 + { 22, 289, 162 }, // 706 + { 22, 178, 492 }, // 707 + { 22, 539, 276 }, // 708 + { 22, 775, 277 }, // 709 + { 22, 171, 294 }, // 710 + { 22, 681, 295 }, // 711 + { 22, 372, 298 }, // 712 + { 22, 569, 299 }, // 713 + { 22, 431, 376 }, // 714 + { 22, 356, 377 }, // 715 + { 22, 232, 386 }, // 716 + { 22, 185, 387 }, // 717 + { 22, 447, 334 }, // 718 + { 22, 669, 335 }, // 719 + { 22, 524, 396 }, // 720 + { 22, 400, 397 }, // 721 + { 22, 273, 453 }, // 722 + { 22, 606, 454 }, // 723 + { 22, 786, 455 }, // 724 + { 22, 738, 456 }, // 725 + { 22, 689, 457 }, // 726 + { 22, 585, 458 }, // 727 + { 22, 403, 461 }, // 728 + { 23, 260, 239 }, // 729 + { 23, 366, 240 }, // 730 + { 23, 522, 242 }, // 731 + { 23, 23, 26 }, // 732 + { 23, 434, 37 }, // 733 + { 23, 379, 420 }, // 734 + { 23, 914, 433 }, // 735 + { 23, 699, 451 }, // 736 + { 23, 190, 116 }, // 737 + { 23, 228, 117 }, // 738 + { 23, 535, 118 }, // 739 + { 23, 283, 119 }, // 740 + { 23, 88, 120 }, // 741 + { 23, 254, 121 }, // 742 + { 23, 57, 122 }, // 743 + { 23, 730, 123 }, // 744 + { 23, 530, 124 }, // 745 + { 23, 231, 125 }, // 746 + { 23, 271, 126 }, // 747 + { 23, 245, 127 }, // 748 + { 23, 611, 128 }, // 749 + { 23, 888, 129 }, // 750 + { 23, 8, 130 }, // 751 + { 23, 20, 131 }, // 752 + { 23, 574, 132 }, // 753 + { 23, 733, 133 }, // 754 + { 23, 55, 134 }, // 755 + { 23, 817, 135 }, // 756 + { 23, 846, 136 }, // 757 + { 23, 762, 137 }, // 758 + { 23, 752, 138 }, // 759 + { 23, 477, 139 }, // 760 + { 23, 83, 140 }, // 761 + { 23, 212, 141 }, // 762 + { 23, 531, 142 }, // 763 + { 23, 767, 143 }, // 764 + { 23, 21, 144 }, // 765 + { 23, 401, 145 }, // 766 + { 23, 836, 146 }, // 767 + { 23, 382, 147 }, // 768 + { 23, 407, 148 }, // 769 + { 23, 532, 149 }, // 770 + { 23, 364, 150 }, // 771 + { 23, 879, 151 }, // 772 + { 23, 168, 152 }, // 773 + { 23, 810, 153 }, // 774 + { 23, 618, 154 }, // 775 + { 23, 76, 155 }, // 776 + { 23, 404, 156 }, // 777 + { 23, 268, 157 }, // 778 + { 23, 898, 158 }, // 779 + { 23, 328, 159 }, // 780 + { 23, 917, 160 }, // 781 + { 23, 419, 161 }, // 782 + { 23, 289, 162 }, // 783 + { 23, 178, 492 }, // 784 + { 23, 539, 276 }, // 785 + { 23, 775, 277 }, // 786 + { 23, 171, 294 }, // 787 + { 23, 681, 295 }, // 788 + { 23, 372, 298 }, // 789 + { 23, 569, 299 }, // 790 + { 23, 431, 376 }, // 791 + { 23, 356, 377 }, // 792 + { 23, 232, 386 }, // 793 + { 23, 185, 387 }, // 794 + { 23, 447, 334 }, // 795 + { 23, 669, 335 }, // 796 + { 23, 524, 396 }, // 797 + { 23, 400, 397 }, // 798 + { 23, 273, 453 }, // 799 + { 23, 606, 454 }, // 800 + { 23, 786, 455 }, // 801 + { 23, 738, 456 }, // 802 + { 23, 689, 457 }, // 803 + { 23, 585, 458 }, // 804 + { 23, 403, 461 }, // 805 + { 24, 901, 173 }, // 806 + { 24, 218, 236 }, // 807 + { 24, 718, 237 }, // 808 + { 24, 858, 238 }, // 809 + { 24, 566, 239 }, // 810 + { 24, 221, 240 }, // 811 + { 24, 595, 243 }, // 812 + { 24, 793, 247 }, // 813 + { 24, 831, 248 }, // 814 + { 24, 180, 249 }, // 815 + { 24, 98, 250 }, // 816 + { 24, 256, 251 }, // 817 + { 24, 379, 420 }, // 818 + { 24, 26, 433 }, // 819 + { 24, 594, 74 }, // 820 + { 24, 270, 450 }, // 821 + { 24, 842, 474 }, // 822 + { 24, 321, 480 }, // 823 + { 24, 843, 492 }, // 824 + { 24, 854, 221 }, // 825 + { 24, 780, 348 }, // 826 + { 24, 697, 349 }, // 827 + { 24, 489, 350 }, // 828 + { 24, 31, 351 }, // 829 + { 24, 259, 312 }, // 830 + { 24, 332, 313 }, // 831 + { 24, 347, 372 }, // 832 + { 24, 732, 373 }, // 833 + { 24, 709, 382 }, // 834 + { 24, 411, 383 }, // 835 + { 24, 232, 386 }, // 836 + { 24, 185, 387 }, // 837 + { 24, 441, 453 }, // 838 + { 24, 422, 455 }, // 839 + { 24, 648, 456 }, // 840 + { 24, 856, 457 }, // 841 + { 24, 623, 458 }, // 842 + { 24, 403, 461 }, // 843 + { 25, 901, 173 }, // 844 + { 25, 218, 236 }, // 845 + { 25, 718, 237 }, // 846 + { 25, 858, 238 }, // 847 + { 25, 566, 239 }, // 848 + { 25, 221, 240 }, // 849 + { 25, 324, 242 }, // 850 + { 25, 793, 247 }, // 851 + { 25, 831, 248 }, // 852 + { 25, 180, 249 }, // 853 + { 25, 98, 250 }, // 854 + { 25, 256, 251 }, // 855 + { 25, 379, 420 }, // 856 + { 25, 26, 433 }, // 857 + { 25, 594, 74 }, // 858 + { 25, 270, 450 }, // 859 + { 25, 842, 474 }, // 860 + { 25, 321, 480 }, // 861 + { 25, 843, 492 }, // 862 + { 25, 854, 221 }, // 863 + { 25, 780, 348 }, // 864 + { 25, 697, 349 }, // 865 + { 25, 489, 350 }, // 866 + { 25, 31, 351 }, // 867 + { 25, 259, 312 }, // 868 + { 25, 332, 313 }, // 869 + { 25, 347, 372 }, // 870 + { 25, 732, 373 }, // 871 + { 25, 709, 382 }, // 872 + { 25, 411, 383 }, // 873 + { 25, 232, 386 }, // 874 + { 25, 185, 387 }, // 875 + { 25, 441, 453 }, // 876 + { 25, 422, 455 }, // 877 + { 25, 648, 456 }, // 878 + { 25, 856, 457 }, // 879 + { 25, 623, 458 }, // 880 + { 25, 403, 461 }, // 881 + { 26, 19, 239 }, // 882 + { 26, 631, 240 }, // 883 + { 26, 886, 243 }, // 884 + { 26, 379, 420 }, // 885 + { 26, 583, 51 }, // 886 + { 26, 864, 43 }, // 887 + { 26, 241, 45 }, // 888 + { 26, 263, 48 }, // 889 + { 26, 604, 433 }, // 890 + { 26, 270, 450 }, // 891 + { 26, 101, 99 }, // 892 + { 26, 751, 472 }, // 893 + { 26, 791, 492 }, // 894 + { 26, 58, 290 }, // 895 + { 26, 683, 291 }, // 896 + { 26, 597, 316 }, // 897 + { 26, 188, 317 }, // 898 + { 26, 347, 372 }, // 899 + { 26, 732, 373 }, // 900 + { 26, 808, 374 }, // 901 + { 26, 341, 375 }, // 902 + { 26, 475, 453 }, // 903 + { 26, 807, 455 }, // 904 + { 26, 156, 457 }, // 905 + { 26, 78, 458 }, // 906 + { 27, 19, 239 }, // 907 + { 27, 631, 240 }, // 908 + { 27, 745, 242 }, // 909 + { 27, 379, 420 }, // 910 + { 27, 583, 51 }, // 911 + { 27, 864, 43 }, // 912 + { 27, 241, 45 }, // 913 + { 27, 263, 48 }, // 914 + { 27, 604, 433 }, // 915 + { 27, 270, 450 }, // 916 + { 27, 101, 99 }, // 917 + { 27, 751, 472 }, // 918 + { 27, 791, 492 }, // 919 + { 27, 58, 290 }, // 920 + { 27, 683, 291 }, // 921 + { 27, 597, 316 }, // 922 + { 27, 188, 317 }, // 923 + { 27, 347, 372 }, // 924 + { 27, 732, 373 }, // 925 + { 27, 808, 374 }, // 926 + { 27, 341, 375 }, // 927 + { 27, 475, 453 }, // 928 + { 27, 807, 455 }, // 929 + { 27, 156, 457 }, // 930 + { 27, 78, 458 }, // 931 + { 28, 134, 235 }, // 932 + { 28, 448, 239 }, // 933 + { 28, 450, 240 }, // 934 + { 28, 764, 243 }, // 935 + { 28, 144, 257 }, // 936 + { 28, 114, 35 }, // 937 + { 28, 787, 36 }, // 938 + { 28, 434, 37 }, // 939 + { 28, 379, 420 }, // 940 + { 28, 157, 428 }, // 941 + { 28, 510, 429 }, // 942 + { 28, 591, 430 }, // 943 + { 28, 362, 433 }, // 944 + { 28, 699, 451 }, // 945 + { 28, 650, 114 }, // 946 + { 28, 433, 492 }, // 947 + { 28, 303, 166 }, // 948 + { 28, 540, 208 }, // 949 + { 28, 77, 209 }, // 950 + { 28, 148, 352 }, // 951 + { 28, 297, 353 }, // 952 + { 28, 539, 276 }, // 953 + { 28, 775, 277 }, // 954 + { 28, 372, 296 }, // 955 + { 28, 569, 297 }, // 956 + { 28, 372, 298 }, // 957 + { 28, 569, 299 }, // 958 + { 28, 550, 362 }, // 959 + { 28, 725, 363 }, // 960 + { 28, 431, 376 }, // 961 + { 28, 356, 377 }, // 962 + { 28, 232, 386 }, // 963 + { 28, 185, 387 }, // 964 + { 28, 538, 453 }, // 965 + { 28, 345, 455 }, // 966 + { 28, 225, 456 }, // 967 + { 28, 616, 457 }, // 968 + { 28, 541, 458 }, // 969 + { 29, 134, 235 }, // 970 + { 29, 448, 239 }, // 971 + { 29, 450, 240 }, // 972 + { 29, 302, 242 }, // 973 + { 29, 144, 257 }, // 974 + { 29, 114, 35 }, // 975 + { 29, 787, 36 }, // 976 + { 29, 434, 37 }, // 977 + { 29, 379, 420 }, // 978 + { 29, 157, 428 }, // 979 + { 29, 510, 429 }, // 980 + { 29, 591, 430 }, // 981 + { 29, 362, 433 }, // 982 + { 29, 699, 451 }, // 983 + { 29, 650, 114 }, // 984 + { 29, 433, 492 }, // 985 + { 29, 303, 166 }, // 986 + { 29, 540, 208 }, // 987 + { 29, 77, 209 }, // 988 + { 29, 148, 352 }, // 989 + { 29, 297, 353 }, // 990 + { 29, 539, 276 }, // 991 + { 29, 775, 277 }, // 992 + { 29, 372, 296 }, // 993 + { 29, 569, 297 }, // 994 + { 29, 372, 298 }, // 995 + { 29, 569, 299 }, // 996 + { 29, 550, 362 }, // 997 + { 29, 725, 363 }, // 998 + { 29, 431, 376 }, // 999 + { 29, 356, 377 }, // 1000 + { 29, 232, 386 }, // 1001 + { 29, 185, 387 }, // 1002 + { 29, 538, 453 }, // 1003 + { 29, 345, 455 }, // 1004 + { 29, 225, 456 }, // 1005 + { 29, 616, 457 }, // 1006 + { 29, 541, 458 }, // 1007 + { 30, 691, 171 }, // 1008 + { 30, 427, 239 }, // 1009 + { 30, 736, 240 }, // 1010 + { 30, 306, 243 }, // 1011 + { 30, 575, 24 }, // 1012 + { 30, 158, 41 }, // 1013 + { 30, 740, 42 }, // 1014 + { 30, 379, 420 }, // 1015 + { 30, 473, 49 }, // 1016 + { 30, 548, 433 }, // 1017 + { 30, 590, 104 }, // 1018 + { 30, 309, 106 }, // 1019 + { 30, 130, 492 }, // 1020 + { 30, 186, 354 }, // 1021 + { 30, 662, 355 }, // 1022 + { 30, 338, 328 }, // 1023 + { 30, 915, 329 }, // 1024 + { 30, 649, 453 }, // 1025 + { 30, 482, 455 }, // 1026 + { 30, 136, 457 }, // 1027 + { 30, 726, 458 }, // 1028 + { 31, 691, 171 }, // 1029 + { 31, 427, 239 }, // 1030 + { 31, 736, 240 }, // 1031 + { 31, 772, 242 }, // 1032 + { 31, 575, 24 }, // 1033 + { 31, 158, 41 }, // 1034 + { 31, 740, 42 }, // 1035 + { 31, 379, 420 }, // 1036 + { 31, 473, 49 }, // 1037 + { 31, 548, 433 }, // 1038 + { 31, 590, 104 }, // 1039 + { 31, 309, 106 }, // 1040 + { 31, 130, 492 }, // 1041 + { 31, 186, 354 }, // 1042 + { 31, 662, 355 }, // 1043 + { 31, 338, 328 }, // 1044 + { 31, 915, 329 }, // 1045 + { 31, 649, 453 }, // 1046 + { 31, 482, 455 }, // 1047 + { 31, 136, 457 }, // 1048 + { 31, 726, 458 }, // 1049 + { 32, 349, 239 }, // 1050 + { 32, 706, 240 }, // 1051 + { 32, 460, 243 }, // 1052 + { 32, 860, 406 }, // 1053 + { 32, 701, 412 }, // 1054 + { 32, 379, 420 }, // 1055 + { 32, 811, 433 }, // 1056 + { 32, 264, 434 }, // 1057 + { 32, 445, 435 }, // 1058 + { 32, 312, 439 }, // 1059 + { 32, 253, 86 }, // 1060 + { 32, 692, 446 }, // 1061 + { 32, 270, 450 }, // 1062 + { 32, 885, 452 }, // 1063 + { 32, 143, 89 }, // 1064 + { 32, 632, 475 }, // 1065 + { 32, 321, 480 }, // 1066 + { 32, 857, 492 }, // 1067 + { 32, 544, 218 }, // 1068 + { 32, 919, 219 }, // 1069 + { 32, 259, 312 }, // 1070 + { 32, 332, 313 }, // 1071 + { 32, 592, 324 }, // 1072 + { 32, 651, 325 }, // 1073 + { 32, 347, 372 }, // 1074 + { 32, 732, 373 }, // 1075 + { 32, 739, 378 }, // 1076 + { 32, 415, 379 }, // 1077 + { 32, 639, 453 }, // 1078 + { 32, 554, 455 }, // 1079 + { 32, 607, 456 }, // 1080 + { 32, 81, 457 }, // 1081 + { 33, 349, 239 }, // 1082 + { 33, 706, 240 }, // 1083 + { 33, 460, 243 }, // 1084 + { 33, 860, 406 }, // 1085 + { 33, 701, 412 }, // 1086 + { 33, 379, 420 }, // 1087 + { 33, 811, 433 }, // 1088 + { 33, 264, 434 }, // 1089 + { 33, 445, 435 }, // 1090 + { 33, 312, 439 }, // 1091 + { 33, 253, 86 }, // 1092 + { 33, 692, 446 }, // 1093 + { 33, 270, 450 }, // 1094 + { 33, 885, 452 }, // 1095 + { 33, 143, 89 }, // 1096 + { 33, 632, 475 }, // 1097 + { 33, 321, 480 }, // 1098 + { 33, 857, 492 }, // 1099 + { 33, 544, 218 }, // 1100 + { 33, 919, 219 }, // 1101 + { 33, 259, 312 }, // 1102 + { 33, 332, 313 }, // 1103 + { 33, 592, 324 }, // 1104 + { 33, 651, 325 }, // 1105 + { 33, 347, 372 }, // 1106 + { 33, 732, 373 }, // 1107 + { 33, 739, 378 }, // 1108 + { 33, 415, 379 }, // 1109 + { 33, 639, 453 }, // 1110 + { 33, 554, 455 }, // 1111 + { 33, 607, 456 }, // 1112 + { 33, 81, 457 }, // 1113 + { 34, 172, 239 }, // 1114 + { 34, 61, 240 }, // 1115 + { 34, 430, 243 }, // 1116 + { 34, 379, 420 }, // 1117 + { 34, 586, 433 }, // 1118 + { 34, 270, 450 }, // 1119 + { 34, 321, 480 }, // 1120 + { 34, 377, 492 }, // 1121 + { 34, 95, 292 }, // 1122 + { 34, 459, 293 }, // 1123 + { 34, 347, 372 }, // 1124 + { 34, 732, 373 }, // 1125 + { 34, 737, 453 }, // 1126 + { 34, 62, 455 }, // 1127 + { 34, 806, 456 }, // 1128 + { 34, 211, 457 }, // 1129 + { 35, 172, 239 }, // 1130 + { 35, 61, 240 }, // 1131 + { 35, 430, 243 }, // 1132 + { 35, 379, 420 }, // 1133 + { 35, 586, 433 }, // 1134 + { 35, 270, 450 }, // 1135 + { 35, 321, 480 }, // 1136 + { 35, 377, 492 }, // 1137 + { 35, 95, 292 }, // 1138 + { 35, 459, 293 }, // 1139 + { 35, 347, 372 }, // 1140 + { 35, 732, 373 }, // 1141 + { 35, 737, 453 }, // 1142 + { 35, 62, 455 }, // 1143 + { 35, 806, 456 }, // 1144 + { 35, 211, 457 }, // 1145 + { 36, 227, 2 }, // 1146 + { 36, 24, 172 }, // 1147 + { 36, 344, 239 }, // 1148 + { 36, 417, 240 }, // 1149 + { 36, 37, 243 }, // 1150 + { 36, 379, 420 }, // 1151 + { 36, 247, 433 }, // 1152 + { 36, 852, 441 }, // 1153 + { 36, 155, 492 }, // 1154 + { 36, 149, 453 }, // 1155 + { 36, 516, 455 }, // 1156 + { 36, 876, 456 }, // 1157 + { 36, 230, 457 }, // 1158 + { 36, 760, 458 }, // 1159 + { 37, 227, 2 }, // 1160 + { 37, 24, 172 }, // 1161 + { 37, 344, 239 }, // 1162 + { 37, 417, 240 }, // 1163 + { 37, 37, 243 }, // 1164 + { 37, 379, 420 }, // 1165 + { 37, 247, 433 }, // 1166 + { 37, 852, 441 }, // 1167 + { 37, 155, 492 }, // 1168 + { 37, 149, 453 }, // 1169 + { 37, 516, 455 }, // 1170 + { 37, 876, 456 }, // 1171 + { 37, 230, 457 }, // 1172 + { 37, 760, 458 }, // 1173 + { 38, 395, 239 }, // 1174 + { 38, 258, 240 }, // 1175 + { 38, 99, 243 }, // 1176 + { 38, 379, 420 }, // 1177 + { 38, 163, 433 }, // 1178 + { 38, 485, 60 }, // 1179 + { 38, 440, 467 }, // 1180 + { 38, 321, 480 }, // 1181 + { 38, 181, 492 }, // 1182 + { 38, 295, 220 }, // 1183 + { 38, 702, 306 }, // 1184 + { 38, 235, 307 }, // 1185 + { 38, 446, 453 }, // 1186 + { 38, 62, 455 }, // 1187 + { 38, 428, 456 }, // 1188 + { 38, 313, 457 }, // 1189 + { 39, 395, 239 }, // 1190 + { 39, 258, 240 }, // 1191 + { 39, 99, 243 }, // 1192 + { 39, 379, 420 }, // 1193 + { 39, 163, 433 }, // 1194 + { 39, 485, 60 }, // 1195 + { 39, 440, 467 }, // 1196 + { 39, 321, 480 }, // 1197 + { 39, 181, 492 }, // 1198 + { 39, 295, 220 }, // 1199 + { 39, 702, 306 }, // 1200 + { 39, 235, 307 }, // 1201 + { 39, 446, 453 }, // 1202 + { 39, 62, 455 }, // 1203 + { 39, 428, 456 }, // 1204 + { 39, 313, 457 }, // 1205 + { 40, 282, 239 }, // 1206 + { 40, 769, 240 }, // 1207 + { 40, 179, 243 }, // 1208 + { 40, 788, 418 }, // 1209 + { 40, 379, 420 }, // 1210 + { 40, 451, 433 }, // 1211 + { 40, 747, 447 }, // 1212 + { 40, 354, 448 }, // 1213 + { 40, 568, 449 }, // 1214 + { 40, 270, 450 }, // 1215 + { 40, 321, 480 }, // 1216 + { 40, 722, 492 }, // 1217 + { 40, 693, 214 }, // 1218 + { 40, 851, 215 }, // 1219 + { 40, 250, 216 }, // 1220 + { 40, 513, 217 }, // 1221 + { 40, 259, 312 }, // 1222 + { 40, 332, 313 }, // 1223 + { 40, 140, 366 }, // 1224 + { 40, 684, 367 }, // 1225 + { 40, 783, 368 }, // 1226 + { 40, 471, 369 }, // 1227 + { 40, 570, 370 }, // 1228 + { 40, 10, 371 }, // 1229 + { 40, 347, 372 }, // 1230 + { 40, 732, 373 }, // 1231 + { 40, 902, 453 }, // 1232 + { 40, 62, 455 }, // 1233 + { 40, 13, 456 }, // 1234 + { 40, 373, 457 }, // 1235 + { 41, 282, 239 }, // 1236 + { 41, 769, 240 }, // 1237 + { 41, 179, 243 }, // 1238 + { 41, 788, 418 }, // 1239 + { 41, 379, 420 }, // 1240 + { 41, 451, 433 }, // 1241 + { 41, 747, 447 }, // 1242 + { 41, 354, 448 }, // 1243 + { 41, 568, 449 }, // 1244 + { 41, 270, 450 }, // 1245 + { 41, 321, 480 }, // 1246 + { 41, 722, 492 }, // 1247 + { 41, 693, 214 }, // 1248 + { 41, 851, 215 }, // 1249 + { 41, 250, 216 }, // 1250 + { 41, 513, 217 }, // 1251 + { 41, 259, 312 }, // 1252 + { 41, 332, 313 }, // 1253 + { 41, 140, 366 }, // 1254 + { 41, 684, 367 }, // 1255 + { 41, 783, 368 }, // 1256 + { 41, 471, 369 }, // 1257 + { 41, 570, 370 }, // 1258 + { 41, 10, 371 }, // 1259 + { 41, 347, 372 }, // 1260 + { 41, 732, 373 }, // 1261 + { 41, 902, 453 }, // 1262 + { 41, 62, 455 }, // 1263 + { 41, 13, 456 }, // 1264 + { 41, 373, 457 }, // 1265 + { 42, 208, 239 }, // 1266 + { 42, 734, 240 }, // 1267 + { 42, 820, 243 }, // 1268 + { 42, 508, 245 }, // 1269 + { 42, 17, 38 }, // 1270 + { 42, 25, 39 }, // 1271 + { 42, 688, 40 }, // 1272 + { 42, 379, 420 }, // 1273 + { 42, 323, 433 }, // 1274 + { 42, 270, 450 }, // 1275 + { 42, 776, 471 }, // 1276 + { 42, 321, 480 }, // 1277 + { 42, 605, 492 }, // 1278 + { 42, 667, 203 }, // 1279 + { 42, 865, 204 }, // 1280 + { 42, 416, 205 }, // 1281 + { 42, 274, 344 }, // 1282 + { 42, 0, 345 }, // 1283 + { 42, 299, 314 }, // 1284 + { 42, 511, 315 }, // 1285 + { 42, 347, 372 }, // 1286 + { 42, 732, 373 }, // 1287 + { 42, 728, 453 }, // 1288 + { 42, 104, 455 }, // 1289 + { 42, 314, 457 }, // 1290 + { 43, 208, 239 }, // 1291 + { 43, 734, 240 }, // 1292 + { 43, 820, 243 }, // 1293 + { 43, 508, 245 }, // 1294 + { 43, 17, 38 }, // 1295 + { 43, 25, 39 }, // 1296 + { 43, 688, 40 }, // 1297 + { 43, 379, 420 }, // 1298 + { 43, 323, 433 }, // 1299 + { 43, 270, 450 }, // 1300 + { 43, 776, 471 }, // 1301 + { 43, 321, 480 }, // 1302 + { 43, 605, 492 }, // 1303 + { 43, 667, 203 }, // 1304 + { 43, 865, 204 }, // 1305 + { 43, 416, 205 }, // 1306 + { 43, 274, 344 }, // 1307 + { 43, 0, 345 }, // 1308 + { 43, 299, 314 }, // 1309 + { 43, 511, 315 }, // 1310 + { 43, 347, 372 }, // 1311 + { 43, 732, 373 }, // 1312 + { 43, 728, 453 }, // 1313 + { 43, 104, 455 }, // 1314 + { 43, 314, 457 }, // 1315 + { 44, 210, 239 }, // 1316 + { 44, 49, 240 }, // 1317 + { 44, 523, 243 }, // 1318 + { 44, 336, 29 }, // 1319 + { 44, 701, 412 }, // 1320 + { 44, 379, 420 }, // 1321 + { 44, 637, 433 }, // 1322 + { 44, 312, 439 }, // 1323 + { 44, 270, 450 }, // 1324 + { 44, 632, 475 }, // 1325 + { 44, 573, 100 }, // 1326 + { 44, 321, 480 }, // 1327 + { 44, 789, 107 }, // 1328 + { 44, 497, 108 }, // 1329 + { 44, 252, 109 }, // 1330 + { 44, 859, 110 }, // 1331 + { 44, 399, 111 }, // 1332 + { 44, 167, 492 }, // 1333 + { 44, 659, 495 }, // 1334 + { 44, 347, 372 }, // 1335 + { 44, 732, 373 }, // 1336 + { 44, 883, 400 }, // 1337 + { 44, 29, 401 }, // 1338 + { 44, 116, 453 }, // 1339 + { 44, 581, 455 }, // 1340 + { 44, 117, 456 }, // 1341 + { 44, 308, 457 }, // 1342 + { 44, 757, 458 }, // 1343 + { 45, 210, 239 }, // 1344 + { 45, 49, 240 }, // 1345 + { 45, 523, 243 }, // 1346 + { 45, 336, 29 }, // 1347 + { 45, 701, 412 }, // 1348 + { 45, 379, 420 }, // 1349 + { 45, 637, 433 }, // 1350 + { 45, 312, 439 }, // 1351 + { 45, 270, 450 }, // 1352 + { 45, 632, 475 }, // 1353 + { 45, 573, 100 }, // 1354 + { 45, 321, 480 }, // 1355 + { 45, 789, 107 }, // 1356 + { 45, 497, 108 }, // 1357 + { 45, 252, 109 }, // 1358 + { 45, 859, 110 }, // 1359 + { 45, 399, 111 }, // 1360 + { 45, 167, 492 }, // 1361 + { 45, 659, 495 }, // 1362 + { 45, 347, 372 }, // 1363 + { 45, 732, 373 }, // 1364 + { 45, 883, 400 }, // 1365 + { 45, 29, 401 }, // 1366 + { 45, 116, 453 }, // 1367 + { 45, 581, 455 }, // 1368 + { 45, 117, 456 }, // 1369 + { 45, 308, 457 }, // 1370 + { 45, 757, 458 }, // 1371 + { 46, 884, 239 }, // 1372 + { 46, 743, 240 }, // 1373 + { 46, 619, 243 }, // 1374 + { 46, 379, 420 }, // 1375 + { 46, 369, 433 }, // 1376 + { 46, 199, 492 }, // 1377 + { 46, 773, 163 }, // 1378 + { 46, 484, 164 }, // 1379 + { 46, 707, 165 }, // 1380 + { 46, 303, 166 }, // 1381 + { 46, 910, 167 }, // 1382 + { 46, 267, 168 }, // 1383 + { 46, 869, 169 }, // 1384 + { 46, 536, 310 }, // 1385 + { 46, 34, 311 }, // 1386 + { 46, 334, 453 }, // 1387 + { 46, 94, 455 }, // 1388 + { 46, 599, 457 }, // 1389 + { 47, 884, 239 }, // 1390 + { 47, 743, 240 }, // 1391 + { 47, 619, 243 }, // 1392 + { 47, 379, 420 }, // 1393 + { 47, 369, 433 }, // 1394 + { 47, 199, 492 }, // 1395 + { 47, 773, 163 }, // 1396 + { 47, 484, 164 }, // 1397 + { 47, 707, 165 }, // 1398 + { 47, 303, 166 }, // 1399 + { 47, 910, 167 }, // 1400 + { 47, 267, 168 }, // 1401 + { 47, 869, 169 }, // 1402 + { 47, 536, 310 }, // 1403 + { 47, 34, 311 }, // 1404 + { 47, 334, 453 }, // 1405 + { 47, 94, 455 }, // 1406 + { 47, 599, 457 }, // 1407 + { 48, 142, 239 }, // 1408 + { 48, 219, 240 }, // 1409 + { 48, 44, 243 }, // 1410 + { 48, 23, 26 }, // 1411 + { 48, 379, 420 }, // 1412 + { 48, 704, 433 }, // 1413 + { 48, 270, 450 }, // 1414 + { 48, 842, 474 }, // 1415 + { 48, 741, 481 }, // 1416 + { 48, 66, 492 }, // 1417 + { 48, 854, 221 }, // 1418 + { 48, 171, 294 }, // 1419 + { 48, 681, 295 }, // 1420 + { 48, 259, 312 }, // 1421 + { 48, 332, 313 }, // 1422 + { 48, 347, 372 }, // 1423 + { 48, 732, 373 }, // 1424 + { 48, 709, 382 }, // 1425 + { 48, 411, 383 }, // 1426 + { 48, 781, 453 }, // 1427 + { 48, 909, 455 }, // 1428 + { 48, 487, 456 }, // 1429 + { 48, 458, 457 }, // 1430 + { 48, 182, 458 }, // 1431 + { 48, 403, 461 }, // 1432 + { 49, 142, 239 }, // 1433 + { 49, 219, 240 }, // 1434 + { 49, 44, 243 }, // 1435 + { 49, 23, 26 }, // 1436 + { 49, 379, 420 }, // 1437 + { 49, 704, 433 }, // 1438 + { 49, 270, 450 }, // 1439 + { 49, 842, 474 }, // 1440 + { 49, 741, 481 }, // 1441 + { 49, 66, 492 }, // 1442 + { 49, 854, 221 }, // 1443 + { 49, 171, 294 }, // 1444 + { 49, 681, 295 }, // 1445 + { 49, 259, 312 }, // 1446 + { 49, 332, 313 }, // 1447 + { 49, 347, 372 }, // 1448 + { 49, 732, 373 }, // 1449 + { 49, 709, 382 }, // 1450 + { 49, 411, 383 }, // 1451 + { 49, 781, 453 }, // 1452 + { 49, 909, 455 }, // 1453 + { 49, 487, 456 }, // 1454 + { 49, 458, 457 }, // 1455 + { 49, 182, 458 }, // 1456 + { 49, 403, 461 }, // 1457 + { 50, 67, 195 }, // 1458 + { 50, 426, 239 }, // 1459 + { 50, 79, 240 }, // 1460 + { 50, 596, 243 }, // 1461 + { 50, 144, 257 }, // 1462 + { 50, 379, 420 }, // 1463 + { 50, 742, 433 }, // 1464 + { 50, 699, 451 }, // 1465 + { 50, 571, 492 }, // 1466 + { 50, 9, 338 }, // 1467 + { 50, 390, 339 }, // 1468 + { 50, 148, 352 }, // 1469 + { 50, 297, 353 }, // 1470 + { 50, 539, 276 }, // 1471 + { 50, 775, 277 }, // 1472 + { 50, 431, 376 }, // 1473 + { 50, 356, 377 }, // 1474 + { 50, 517, 453 }, // 1475 + { 50, 104, 455 }, // 1476 + { 50, 826, 456 }, // 1477 + { 50, 129, 457 }, // 1478 + { 51, 67, 195 }, // 1479 + { 51, 426, 239 }, // 1480 + { 51, 79, 240 }, // 1481 + { 51, 596, 243 }, // 1482 + { 51, 144, 257 }, // 1483 + { 51, 379, 420 }, // 1484 + { 51, 742, 433 }, // 1485 + { 51, 699, 451 }, // 1486 + { 51, 571, 492 }, // 1487 + { 51, 9, 338 }, // 1488 + { 51, 390, 339 }, // 1489 + { 51, 148, 352 }, // 1490 + { 51, 297, 353 }, // 1491 + { 51, 539, 276 }, // 1492 + { 51, 775, 277 }, // 1493 + { 51, 431, 376 }, // 1494 + { 51, 356, 377 }, // 1495 + { 51, 517, 453 }, // 1496 + { 51, 104, 455 }, // 1497 + { 51, 826, 456 }, // 1498 + { 51, 129, 457 }, // 1499 + { 52, 68, 239 }, // 1500 + { 52, 73, 240 }, // 1501 + { 52, 7, 243 }, // 1502 + { 52, 6, 15 }, // 1503 + { 52, 198, 16 }, // 1504 + { 52, 564, 17 }, // 1505 + { 52, 860, 406 }, // 1506 + { 52, 205, 408 }, // 1507 + { 52, 379, 420 }, // 1508 + { 52, 785, 433 }, // 1509 + { 52, 270, 450 }, // 1510 + { 52, 710, 492 }, // 1511 + { 52, 240, 262 }, // 1512 + { 52, 456, 263 }, // 1513 + { 52, 894, 266 }, // 1514 + { 52, 162, 267 }, // 1515 + { 52, 347, 372 }, // 1516 + { 52, 732, 373 }, // 1517 + { 52, 232, 386 }, // 1518 + { 52, 185, 387 }, // 1519 + { 52, 549, 453 }, // 1520 + { 52, 48, 455 }, // 1521 + { 52, 719, 456 }, // 1522 + { 52, 875, 457 }, // 1523 + { 52, 763, 458 }, // 1524 + { 53, 68, 239 }, // 1525 + { 53, 73, 240 }, // 1526 + { 53, 7, 243 }, // 1527 + { 53, 6, 15 }, // 1528 + { 53, 198, 16 }, // 1529 + { 53, 564, 17 }, // 1530 + { 53, 860, 406 }, // 1531 + { 53, 205, 408 }, // 1532 + { 53, 379, 420 }, // 1533 + { 53, 785, 433 }, // 1534 + { 53, 270, 450 }, // 1535 + { 53, 710, 492 }, // 1536 + { 53, 240, 262 }, // 1537 + { 53, 456, 263 }, // 1538 + { 53, 894, 266 }, // 1539 + { 53, 162, 267 }, // 1540 + { 53, 347, 372 }, // 1541 + { 53, 732, 373 }, // 1542 + { 53, 232, 386 }, // 1543 + { 53, 185, 387 }, // 1544 + { 53, 549, 453 }, // 1545 + { 53, 48, 455 }, // 1546 + { 53, 719, 456 }, // 1547 + { 53, 875, 457 }, // 1548 + { 53, 763, 458 }, // 1549 + { 54, 284, 239 }, // 1550 + { 54, 882, 240 }, // 1551 + { 54, 119, 243 }, // 1552 + { 54, 379, 420 }, // 1553 + { 54, 89, 431 }, // 1554 + { 54, 137, 433 }, // 1555 + { 54, 652, 436 }, // 1556 + { 54, 617, 61 }, // 1557 + { 54, 801, 443 }, // 1558 + { 54, 294, 444 }, // 1559 + { 54, 321, 480 }, // 1560 + { 54, 402, 492 }, // 1561 + { 54, 131, 493 }, // 1562 + { 54, 716, 210 }, // 1563 + { 54, 716, 211 }, // 1564 + { 54, 11, 212 }, // 1565 + { 54, 420, 213 }, // 1566 + { 54, 151, 398 }, // 1567 + { 54, 1, 399 }, // 1568 + { 54, 173, 453 }, // 1569 + { 54, 624, 455 }, // 1570 + { 54, 36, 456 }, // 1571 + { 54, 46, 457 }, // 1572 + { 55, 284, 239 }, // 1573 + { 55, 882, 240 }, // 1574 + { 55, 119, 243 }, // 1575 + { 55, 379, 420 }, // 1576 + { 55, 89, 431 }, // 1577 + { 55, 137, 433 }, // 1578 + { 55, 652, 436 }, // 1579 + { 55, 617, 61 }, // 1580 + { 55, 801, 443 }, // 1581 + { 55, 294, 444 }, // 1582 + { 55, 321, 480 }, // 1583 + { 55, 402, 492 }, // 1584 + { 55, 131, 493 }, // 1585 + { 55, 716, 210 }, // 1586 + { 55, 716, 211 }, // 1587 + { 55, 11, 212 }, // 1588 + { 55, 420, 213 }, // 1589 + { 55, 151, 398 }, // 1590 + { 55, 1, 399 }, // 1591 + { 55, 173, 453 }, // 1592 + { 55, 624, 455 }, // 1593 + { 55, 36, 456 }, // 1594 + { 55, 46, 457 }, // 1595 + { 56, 69, 239 }, // 1596 + { 56, 331, 240 }, // 1597 + { 56, 360, 243 }, // 1598 + { 56, 74, 254 }, // 1599 + { 56, 379, 420 }, // 1600 + { 56, 712, 433 }, // 1601 + { 56, 580, 442 }, // 1602 + { 56, 16, 492 }, // 1603 + { 56, 259, 312 }, // 1604 + { 56, 332, 313 }, // 1605 + { 56, 816, 453 }, // 1606 + { 56, 62, 455 }, // 1607 + { 57, 69, 239 }, // 1608 + { 57, 331, 240 }, // 1609 + { 57, 360, 243 }, // 1610 + { 57, 74, 254 }, // 1611 + { 57, 379, 420 }, // 1612 + { 57, 712, 433 }, // 1613 + { 57, 580, 442 }, // 1614 + { 57, 16, 492 }, // 1615 + { 57, 259, 312 }, // 1616 + { 57, 332, 313 }, // 1617 + { 57, 816, 453 }, // 1618 + { 57, 62, 455 }, // 1619 + { 58, 292, 233 }, // 1620 + { 58, 115, 239 }, // 1621 + { 58, 438, 240 }, // 1622 + { 58, 310, 243 }, // 1623 + { 58, 296, 405 }, // 1624 + { 58, 379, 420 }, // 1625 + { 58, 668, 433 }, // 1626 + { 58, 270, 450 }, // 1627 + { 58, 321, 480 }, // 1628 + { 58, 735, 492 }, // 1629 + { 58, 131, 493 }, // 1630 + { 58, 521, 340 }, // 1631 + { 58, 238, 341 }, // 1632 + { 58, 347, 372 }, // 1633 + { 58, 732, 373 }, // 1634 + { 58, 151, 398 }, // 1635 + { 58, 1, 399 }, // 1636 + { 58, 398, 453 }, // 1637 + { 58, 62, 455 }, // 1638 + { 58, 682, 456 }, // 1639 + { 58, 715, 457 }, // 1640 + { 58, 413, 458 }, // 1641 + { 59, 292, 233 }, // 1642 + { 59, 115, 239 }, // 1643 + { 59, 438, 240 }, // 1644 + { 59, 310, 243 }, // 1645 + { 59, 296, 405 }, // 1646 + { 59, 379, 420 }, // 1647 + { 59, 668, 433 }, // 1648 + { 59, 270, 450 }, // 1649 + { 59, 321, 480 }, // 1650 + { 59, 735, 492 }, // 1651 + { 59, 131, 493 }, // 1652 + { 59, 521, 340 }, // 1653 + { 59, 238, 341 }, // 1654 + { 59, 347, 372 }, // 1655 + { 59, 732, 373 }, // 1656 + { 59, 151, 398 }, // 1657 + { 59, 1, 399 }, // 1658 + { 59, 398, 453 }, // 1659 + { 59, 62, 455 }, // 1660 + { 59, 682, 456 }, // 1661 + { 59, 715, 457 }, // 1662 + { 59, 413, 458 }, // 1663 + { 60, 557, 194 }, // 1664 + { 60, 50, 239 }, // 1665 + { 60, 206, 240 }, // 1666 + { 60, 635, 243 }, // 1667 + { 60, 152, 14 }, // 1668 + { 60, 65, 18 }, // 1669 + { 60, 60, 22 }, // 1670 + { 60, 213, 255 }, // 1671 + { 60, 672, 256 }, // 1672 + { 60, 379, 420 }, // 1673 + { 60, 823, 433 }, // 1674 + { 60, 270, 450 }, // 1675 + { 60, 321, 480 }, // 1676 + { 60, 655, 492 }, // 1677 + { 60, 567, 196 }, // 1678 + { 60, 519, 264 }, // 1679 + { 60, 542, 265 }, // 1680 + { 60, 881, 272 }, // 1681 + { 60, 848, 273 }, // 1682 + { 60, 347, 372 }, // 1683 + { 60, 732, 373 }, // 1684 + { 60, 63, 453 }, // 1685 + { 60, 56, 455 }, // 1686 + { 60, 339, 456 }, // 1687 + { 60, 211, 457 }, // 1688 + { 61, 557, 194 }, // 1689 + { 61, 50, 239 }, // 1690 + { 61, 206, 240 }, // 1691 + { 61, 635, 243 }, // 1692 + { 61, 152, 14 }, // 1693 + { 61, 65, 18 }, // 1694 + { 61, 60, 22 }, // 1695 + { 61, 213, 255 }, // 1696 + { 61, 672, 256 }, // 1697 + { 61, 379, 420 }, // 1698 + { 61, 823, 433 }, // 1699 + { 61, 270, 450 }, // 1700 + { 61, 321, 480 }, // 1701 + { 61, 655, 492 }, // 1702 + { 61, 567, 196 }, // 1703 + { 61, 519, 264 }, // 1704 + { 61, 542, 265 }, // 1705 + { 61, 881, 272 }, // 1706 + { 61, 848, 273 }, // 1707 + { 61, 347, 372 }, // 1708 + { 61, 732, 373 }, // 1709 + { 61, 63, 453 }, // 1710 + { 61, 56, 455 }, // 1711 + { 61, 339, 456 }, // 1712 + { 61, 211, 457 }, // 1713 + { 62, 123, 239 }, // 1714 + { 62, 839, 240 }, // 1715 + { 62, 423, 243 }, // 1716 + { 62, 144, 257 }, // 1717 + { 62, 434, 37 }, // 1718 + { 62, 379, 420 }, // 1719 + { 62, 457, 50 }, // 1720 + { 62, 466, 433 }, // 1721 + { 62, 699, 451 }, // 1722 + { 62, 537, 492 }, // 1723 + { 62, 148, 352 }, // 1724 + { 62, 297, 353 }, // 1725 + { 62, 539, 276 }, // 1726 + { 62, 775, 277 }, // 1727 + { 62, 372, 298 }, // 1728 + { 62, 569, 299 }, // 1729 + { 62, 481, 300 }, // 1730 + { 62, 193, 301 }, // 1731 + { 62, 576, 322 }, // 1732 + { 62, 384, 323 }, // 1733 + { 62, 431, 376 }, // 1734 + { 62, 356, 377 }, // 1735 + { 62, 325, 453 }, // 1736 + { 62, 125, 455 }, // 1737 + { 62, 904, 457 }, // 1738 + { 63, 123, 239 }, // 1739 + { 63, 839, 240 }, // 1740 + { 63, 423, 243 }, // 1741 + { 63, 144, 257 }, // 1742 + { 63, 434, 37 }, // 1743 + { 63, 379, 420 }, // 1744 + { 63, 457, 50 }, // 1745 + { 63, 466, 433 }, // 1746 + { 63, 699, 451 }, // 1747 + { 63, 537, 492 }, // 1748 + { 63, 148, 352 }, // 1749 + { 63, 297, 353 }, // 1750 + { 63, 539, 276 }, // 1751 + { 63, 775, 277 }, // 1752 + { 63, 372, 298 }, // 1753 + { 63, 569, 299 }, // 1754 + { 63, 481, 300 }, // 1755 + { 63, 193, 301 }, // 1756 + { 63, 576, 322 }, // 1757 + { 63, 384, 323 }, // 1758 + { 63, 431, 376 }, // 1759 + { 63, 356, 377 }, // 1760 + { 63, 325, 453 }, // 1761 + { 63, 125, 455 }, // 1762 + { 63, 904, 457 }, // 1763 + { 64, 561, 174 }, // 1764 + { 64, 108, 175 }, // 1765 + { 64, 265, 176 }, // 1766 + { 64, 103, 177 }, // 1767 + { 64, 463, 178 }, // 1768 + { 64, 663, 179 }, // 1769 + { 64, 577, 180 }, // 1770 + { 64, 819, 181 }, // 1771 + { 64, 896, 182 }, // 1772 + { 64, 72, 183 }, // 1773 + { 64, 462, 184 }, // 1774 + { 64, 515, 185 }, // 1775 + { 64, 317, 186 }, // 1776 + { 64, 850, 187 }, // 1777 + { 64, 880, 188 }, // 1778 + { 64, 455, 189 }, // 1779 + { 64, 84, 190 }, // 1780 + { 64, 12, 191 }, // 1781 + { 64, 584, 192 }, // 1782 + { 64, 363, 234 }, // 1783 + { 64, 54, 236 }, // 1784 + { 64, 305, 237 }, // 1785 + { 64, 771, 238 }, // 1786 + { 64, 189, 239 }, // 1787 + { 64, 425, 240 }, // 1788 + { 64, 293, 243 }, // 1789 + { 64, 379, 420 }, // 1790 + { 64, 71, 433 }, // 1791 + { 64, 750, 78 }, // 1792 + { 64, 612, 80 }, // 1793 + { 64, 912, 82 }, // 1794 + { 64, 572, 84 }, // 1795 + { 64, 867, 492 }, // 1796 + { 64, 855, 342 }, // 1797 + { 64, 483, 343 }, // 1798 + { 64, 217, 453 }, // 1799 + { 64, 906, 455 }, // 1800 + { 64, 626, 456 }, // 1801 + { 64, 286, 457 }, // 1802 + { 64, 847, 458 }, // 1803 + { 65, 561, 174 }, // 1804 + { 65, 108, 175 }, // 1805 + { 65, 265, 176 }, // 1806 + { 65, 103, 177 }, // 1807 + { 65, 463, 178 }, // 1808 + { 65, 663, 179 }, // 1809 + { 65, 577, 180 }, // 1810 + { 65, 819, 181 }, // 1811 + { 65, 896, 182 }, // 1812 + { 65, 72, 183 }, // 1813 + { 65, 462, 184 }, // 1814 + { 65, 515, 185 }, // 1815 + { 65, 317, 186 }, // 1816 + { 65, 850, 187 }, // 1817 + { 65, 880, 188 }, // 1818 + { 65, 455, 189 }, // 1819 + { 65, 84, 190 }, // 1820 + { 65, 12, 191 }, // 1821 + { 65, 584, 192 }, // 1822 + { 65, 363, 234 }, // 1823 + { 65, 54, 236 }, // 1824 + { 65, 305, 237 }, // 1825 + { 65, 771, 238 }, // 1826 + { 65, 189, 239 }, // 1827 + { 65, 425, 240 }, // 1828 + { 65, 293, 243 }, // 1829 + { 65, 379, 420 }, // 1830 + { 65, 71, 433 }, // 1831 + { 65, 750, 78 }, // 1832 + { 65, 612, 80 }, // 1833 + { 65, 912, 82 }, // 1834 + { 65, 572, 84 }, // 1835 + { 65, 867, 492 }, // 1836 + { 65, 855, 342 }, // 1837 + { 65, 483, 343 }, // 1838 + { 65, 217, 453 }, // 1839 + { 65, 906, 455 }, // 1840 + { 65, 626, 456 }, // 1841 + { 65, 286, 457 }, // 1842 + { 65, 847, 458 }, // 1843 + { 66, 363, 234 }, // 1844 + { 66, 359, 239 }, // 1845 + { 66, 795, 240 }, // 1846 + { 66, 343, 243 }, // 1847 + { 66, 701, 412 }, // 1848 + { 66, 379, 420 }, // 1849 + { 66, 27, 433 }, // 1850 + { 66, 270, 450 }, // 1851 + { 66, 321, 480 }, // 1852 + { 66, 488, 484 }, // 1853 + { 66, 551, 485 }, // 1854 + { 66, 4, 486 }, // 1855 + { 66, 799, 487 }, // 1856 + { 66, 291, 488 }, // 1857 + { 66, 645, 489 }, // 1858 + { 66, 387, 490 }, // 1859 + { 66, 630, 491 }, // 1860 + { 66, 518, 492 }, // 1861 + { 66, 855, 342 }, // 1862 + { 66, 483, 343 }, // 1863 + { 66, 347, 372 }, // 1864 + { 66, 732, 373 }, // 1865 + { 66, 845, 392 }, // 1866 + { 66, 200, 393 }, // 1867 + { 66, 333, 394 }, // 1868 + { 66, 380, 395 }, // 1869 + { 66, 412, 453 }, // 1870 + { 66, 429, 455 }, // 1871 + { 66, 686, 456 }, // 1872 + { 66, 442, 457 }, // 1873 + { 66, 499, 458 }, // 1874 + { 67, 363, 234 }, // 1875 + { 67, 359, 239 }, // 1876 + { 67, 795, 240 }, // 1877 + { 67, 507, 242 }, // 1878 + { 67, 701, 412 }, // 1879 + { 67, 379, 420 }, // 1880 + { 67, 27, 433 }, // 1881 + { 67, 270, 450 }, // 1882 + { 67, 321, 480 }, // 1883 + { 67, 488, 484 }, // 1884 + { 67, 551, 485 }, // 1885 + { 67, 4, 486 }, // 1886 + { 67, 799, 487 }, // 1887 + { 67, 291, 488 }, // 1888 + { 67, 645, 489 }, // 1889 + { 67, 387, 490 }, // 1890 + { 67, 630, 491 }, // 1891 + { 67, 518, 492 }, // 1892 + { 67, 855, 342 }, // 1893 + { 67, 483, 343 }, // 1894 + { 67, 347, 372 }, // 1895 + { 67, 732, 373 }, // 1896 + { 67, 845, 392 }, // 1897 + { 67, 200, 393 }, // 1898 + { 67, 333, 394 }, // 1899 + { 67, 380, 395 }, // 1900 + { 67, 412, 453 }, // 1901 + { 67, 429, 455 }, // 1902 + { 67, 686, 456 }, // 1903 + { 67, 442, 457 }, // 1904 + { 67, 499, 458 }, // 1905 + { 68, 363, 234 }, // 1906 + { 68, 476, 239 }, // 1907 + { 68, 207, 240 }, // 1908 + { 68, 861, 243 }, // 1909 + { 68, 870, 404 }, // 1910 + { 68, 379, 420 }, // 1911 + { 68, 703, 433 }, // 1912 + { 68, 563, 492 }, // 1913 + { 68, 855, 342 }, // 1914 + { 68, 483, 343 }, // 1915 + { 68, 796, 308 }, // 1916 + { 68, 326, 309 }, // 1917 + { 68, 353, 453 }, // 1918 + { 68, 100, 455 }, // 1919 + { 68, 350, 456 }, // 1920 + { 68, 654, 457 }, // 1921 + { 68, 266, 458 }, // 1922 + { 69, 363, 234 }, // 1923 + { 69, 476, 239 }, // 1924 + { 69, 207, 240 }, // 1925 + { 69, 861, 243 }, // 1926 + { 69, 870, 404 }, // 1927 + { 69, 379, 420 }, // 1928 + { 69, 703, 433 }, // 1929 + { 69, 563, 492 }, // 1930 + { 69, 855, 342 }, // 1931 + { 69, 483, 343 }, // 1932 + { 69, 796, 308 }, // 1933 + { 69, 326, 309 }, // 1934 + { 69, 353, 453 }, // 1935 + { 69, 100, 455 }, // 1936 + { 69, 350, 456 }, // 1937 + { 69, 654, 457 }, // 1938 + { 69, 266, 458 }, // 1939 + { 70, 42, 227 }, // 1940 + { 70, 120, 228 }, // 1941 + { 70, 363, 234 }, // 1942 + { 70, 122, 239 }, // 1943 + { 70, 236, 240 }, // 1944 + { 70, 900, 243 }, // 1945 + { 70, 379, 420 }, // 1946 + { 70, 724, 433 }, // 1947 + { 70, 798, 440 }, // 1948 + { 70, 492, 468 }, // 1949 + { 70, 721, 477 }, // 1950 + { 70, 82, 113 }, // 1951 + { 70, 40, 492 }, // 1952 + { 70, 855, 342 }, // 1953 + { 70, 483, 343 }, // 1954 + { 70, 170, 453 }, // 1955 + { 70, 824, 455 }, // 1956 + { 70, 891, 457 }, // 1957 + { 70, 874, 458 }, // 1958 + { 71, 42, 227 }, // 1959 + { 71, 120, 228 }, // 1960 + { 71, 363, 234 }, // 1961 + { 71, 122, 239 }, // 1962 + { 71, 236, 240 }, // 1963 + { 71, 582, 242 }, // 1964 + { 71, 379, 420 }, // 1965 + { 71, 724, 433 }, // 1966 + { 71, 798, 440 }, // 1967 + { 71, 492, 468 }, // 1968 + { 71, 721, 477 }, // 1969 + { 71, 82, 113 }, // 1970 + { 71, 40, 492 }, // 1971 + { 71, 855, 342 }, // 1972 + { 71, 483, 343 }, // 1973 + { 71, 170, 453 }, // 1974 + { 71, 824, 455 }, // 1975 + { 71, 891, 457 }, // 1976 + { 71, 874, 458 }, // 1977 + { 72, 363, 234 }, // 1978 + { 72, 175, 239 }, // 1979 + { 72, 410, 240 }, // 1980 + { 72, 640, 243 }, // 1981 + { 72, 144, 257 }, // 1982 + { 72, 379, 420 }, // 1983 + { 72, 169, 433 }, // 1984 + { 72, 255, 470 }, // 1985 + { 72, 321, 480 }, // 1986 + { 72, 408, 492 }, // 1987 + { 72, 855, 342 }, // 1988 + { 72, 483, 343 }, // 1989 + { 72, 148, 352 }, // 1990 + { 72, 297, 353 }, // 1991 + { 72, 138, 380 }, // 1992 + { 72, 464, 381 }, // 1993 + { 72, 547, 453 }, // 1994 + { 72, 647, 455 }, // 1995 + { 72, 815, 457 }, // 1996 + { 72, 388, 458 }, // 1997 + { 73, 363, 234 }, // 1998 + { 73, 175, 239 }, // 1999 + { 73, 410, 240 }, // 2000 + { 73, 307, 242 }, // 2001 + { 73, 144, 257 }, // 2002 + { 73, 379, 420 }, // 2003 + { 73, 169, 433 }, // 2004 + { 73, 255, 470 }, // 2005 + { 73, 321, 480 }, // 2006 + { 73, 408, 492 }, // 2007 + { 73, 855, 342 }, // 2008 + { 73, 483, 343 }, // 2009 + { 73, 148, 352 }, // 2010 + { 73, 297, 353 }, // 2011 + { 73, 138, 380 }, // 2012 + { 73, 464, 381 }, // 2013 + { 73, 547, 453 }, // 2014 + { 73, 647, 455 }, // 2015 + { 73, 815, 457 }, // 2016 + { 73, 388, 458 }, // 2017 + { 74, 363, 234 }, // 2018 + { 74, 809, 239 }, // 2019 + { 74, 653, 240 }, // 2020 + { 74, 821, 243 }, // 2021 + { 74, 379, 420 }, // 2022 + { 74, 261, 433 }, // 2023 + { 74, 558, 492 }, // 2024 + { 74, 855, 342 }, // 2025 + { 74, 483, 343 }, // 2026 + { 74, 361, 453 }, // 2027 + { 74, 509, 455 }, // 2028 + { 75, 363, 234 }, // 2029 + { 75, 809, 239 }, // 2030 + { 75, 653, 240 }, // 2031 + { 75, 821, 243 }, // 2032 + { 75, 379, 420 }, // 2033 + { 75, 261, 433 }, // 2034 + { 75, 558, 492 }, // 2035 + { 75, 855, 342 }, // 2036 + { 75, 483, 343 }, // 2037 + { 75, 361, 453 }, // 2038 + { 75, 509, 455 }, // 2039 + { 76, 363, 234 }, // 2040 + { 76, 461, 239 }, // 2041 + { 76, 643, 240 }, // 2042 + { 76, 601, 243 }, // 2043 + { 76, 379, 420 }, // 2044 + { 76, 237, 433 }, // 2045 + { 76, 39, 492 }, // 2046 + { 76, 855, 342 }, // 2047 + { 76, 483, 343 }, // 2048 + { 76, 911, 453 }, // 2049 + { 76, 224, 455 }, // 2050 + { 77, 363, 234 }, // 2051 + { 77, 461, 239 }, // 2052 + { 77, 643, 240 }, // 2053 + { 77, 601, 243 }, // 2054 + { 77, 379, 420 }, // 2055 + { 77, 237, 433 }, // 2056 + { 77, 39, 492 }, // 2057 + { 77, 855, 342 }, // 2058 + { 77, 483, 343 }, // 2059 + { 77, 911, 453 }, // 2060 + { 77, 224, 455 }, // 2061 + { 78, 363, 234 }, // 2062 + { 78, 889, 239 }, // 2063 + { 78, 222, 240 }, // 2064 + { 78, 871, 243 }, // 2065 + { 78, 853, 13 }, // 2066 + { 78, 379, 420 }, // 2067 + { 78, 368, 433 }, // 2068 + { 78, 432, 105 }, // 2069 + { 78, 661, 492 }, // 2070 + { 78, 855, 342 }, // 2071 + { 78, 483, 343 }, // 2072 + { 78, 342, 453 }, // 2073 + { 78, 62, 455 }, // 2074 + { 78, 38, 457 }, // 2075 + { 78, 756, 458 }, // 2076 + { 79, 363, 234 }, // 2077 + { 79, 889, 239 }, // 2078 + { 79, 222, 240 }, // 2079 + { 79, 871, 243 }, // 2080 + { 79, 853, 13 }, // 2081 + { 79, 379, 420 }, // 2082 + { 79, 368, 433 }, // 2083 + { 79, 432, 105 }, // 2084 + { 79, 661, 492 }, // 2085 + { 79, 855, 342 }, // 2086 + { 79, 483, 343 }, // 2087 + { 79, 342, 453 }, // 2088 + { 79, 62, 455 }, // 2089 + { 79, 38, 457 }, // 2090 + { 79, 756, 458 }, // 2091 + { 80, 363, 234 }, // 2092 + { 80, 666, 239 }, // 2093 + { 80, 804, 240 }, // 2094 + { 80, 708, 243 }, // 2095 + { 80, 379, 420 }, // 2096 + { 80, 249, 433 }, // 2097 + { 80, 405, 492 }, // 2098 + { 80, 855, 342 }, // 2099 + { 80, 483, 343 }, // 2100 + { 80, 361, 453 }, // 2101 + { 80, 104, 455 }, // 2102 + { 81, 363, 234 }, // 2103 + { 81, 666, 239 }, // 2104 + { 81, 804, 240 }, // 2105 + { 81, 708, 243 }, // 2106 + { 81, 379, 420 }, // 2107 + { 81, 249, 433 }, // 2108 + { 81, 405, 492 }, // 2109 + { 81, 855, 342 }, // 2110 + { 81, 483, 343 }, // 2111 + { 81, 361, 453 }, // 2112 + { 81, 104, 455 }, // 2113 + { 82, 363, 234 }, // 2114 + { 82, 506, 239 }, // 2115 + { 82, 878, 240 }, // 2116 + { 82, 778, 243 }, // 2117 + { 82, 379, 420 }, // 2118 + { 82, 656, 433 }, // 2119 + { 82, 587, 492 }, // 2120 + { 82, 855, 342 }, // 2121 + { 82, 483, 343 }, // 2122 + { 82, 808, 374 }, // 2123 + { 82, 341, 375 }, // 2124 + { 82, 782, 453 }, // 2125 + { 82, 104, 455 }, // 2126 + { 82, 467, 457 }, // 2127 + { 82, 288, 458 }, // 2128 + { 83, 363, 234 }, // 2129 + { 83, 506, 239 }, // 2130 + { 83, 878, 240 }, // 2131 + { 83, 778, 243 }, // 2132 + { 83, 379, 420 }, // 2133 + { 83, 656, 433 }, // 2134 + { 83, 587, 492 }, // 2135 + { 83, 855, 342 }, // 2136 + { 83, 483, 343 }, // 2137 + { 83, 808, 374 }, // 2138 + { 83, 341, 375 }, // 2139 + { 83, 782, 453 }, // 2140 + { 83, 104, 455 }, // 2141 + { 83, 467, 457 }, // 2142 + { 83, 288, 458 }, // 2143 + { 84, 32, 229 }, // 2144 + { 84, 797, 230 }, // 2145 + { 84, 766, 231 }, // 2146 + { 84, 389, 232 }, // 2147 + { 84, 694, 239 }, // 2148 + { 84, 91, 240 }, // 2149 + { 84, 790, 243 }, // 2150 + { 84, 64, 21 }, // 2151 + { 84, 860, 406 }, // 2152 + { 84, 436, 407 }, // 2153 + { 84, 205, 408 }, // 2154 + { 84, 526, 409 }, // 2155 + { 84, 701, 412 }, // 2156 + { 84, 421, 413 }, // 2157 + { 84, 337, 433 }, // 2158 + { 84, 226, 492 }, // 2159 + { 84, 478, 268 }, // 2160 + { 84, 838, 269 }, // 2161 + { 84, 370, 270 }, // 2162 + { 84, 610, 271 }, // 2163 + { 84, 602, 320 }, // 2164 + { 84, 877, 321 }, // 2165 + { 84, 480, 453 }, // 2166 + { 84, 800, 457 }, // 2167 + { 84, 634, 458 }, // 2168 + { 84, 918, 459 }, // 2169 + { 84, 913, 460 }, // 2170 + { 84, 414, 465 }, // 2171 + { 85, 32, 229 }, // 2172 + { 85, 797, 230 }, // 2173 + { 85, 766, 231 }, // 2174 + { 85, 389, 232 }, // 2175 + { 85, 694, 239 }, // 2176 + { 85, 91, 240 }, // 2177 + { 85, 22, 242 }, // 2178 + { 85, 64, 21 }, // 2179 + { 85, 860, 406 }, // 2180 + { 85, 436, 407 }, // 2181 + { 85, 205, 408 }, // 2182 + { 85, 526, 409 }, // 2183 + { 85, 701, 412 }, // 2184 + { 85, 421, 413 }, // 2185 + { 85, 337, 433 }, // 2186 + { 85, 226, 492 }, // 2187 + { 85, 478, 268 }, // 2188 + { 85, 838, 269 }, // 2189 + { 85, 370, 270 }, // 2190 + { 85, 610, 271 }, // 2191 + { 85, 602, 320 }, // 2192 + { 85, 877, 321 }, // 2193 + { 85, 480, 453 }, // 2194 + { 85, 800, 457 }, // 2195 + { 85, 634, 458 }, // 2196 + { 85, 918, 459 }, // 2197 + { 85, 913, 460 }, // 2198 + { 85, 414, 465 }, // 2199 + { 86, 248, 230 }, // 2200 + { 86, 133, 231 }, // 2201 + { 86, 352, 232 }, // 2202 + { 86, 150, 239 }, // 2203 + { 86, 365, 240 }, // 2204 + { 86, 52, 243 }, // 2205 + { 86, 202, 19 }, // 2206 + { 86, 680, 20 }, // 2207 + { 86, 337, 433 }, // 2208 + { 86, 452, 492 }, // 2209 + { 86, 638, 453 }, // 2210 + { 86, 527, 457 }, // 2211 + { 86, 634, 458 }, // 2212 + { 87, 248, 230 }, // 2213 + { 87, 133, 231 }, // 2214 + { 87, 352, 232 }, // 2215 + { 87, 150, 239 }, // 2216 + { 87, 365, 240 }, // 2217 + { 87, 503, 242 }, // 2218 + { 87, 202, 19 }, // 2219 + { 87, 680, 20 }, // 2220 + { 87, 337, 433 }, // 2221 + { 87, 452, 492 }, // 2222 + { 87, 638, 453 }, // 2223 + { 87, 527, 457 }, // 2224 + { 87, 634, 458 }, // 2225 + { 88, 112, 236 }, // 2226 + { 88, 305, 237 }, // 2227 + { 88, 771, 238 }, // 2228 + { 88, 209, 239 }, // 2229 + { 88, 335, 240 }, // 2230 + { 88, 498, 243 }, // 2231 + { 88, 367, 32 }, // 2232 + { 88, 379, 420 }, // 2233 + { 88, 257, 433 }, // 2234 + { 88, 393, 64 }, // 2235 + { 88, 494, 65 }, // 2236 + { 88, 59, 66 }, // 2237 + { 88, 690, 67 }, // 2238 + { 88, 351, 81 }, // 2239 + { 88, 893, 476 }, // 2240 + { 88, 330, 492 }, // 2241 + { 88, 504, 453 }, // 2242 + { 88, 868, 455 }, // 2243 + { 88, 239, 457 }, // 2244 + { 88, 714, 458 }, // 2245 + { 88, 403, 461 }, // 2246 + { 89, 112, 236 }, // 2247 + { 89, 305, 237 }, // 2248 + { 89, 771, 238 }, // 2249 + { 89, 209, 239 }, // 2250 + { 89, 335, 240 }, // 2251 + { 89, 498, 243 }, // 2252 + { 89, 367, 32 }, // 2253 + { 89, 379, 420 }, // 2254 + { 89, 257, 433 }, // 2255 + { 89, 393, 64 }, // 2256 + { 89, 494, 65 }, // 2257 + { 89, 59, 66 }, // 2258 + { 89, 690, 67 }, // 2259 + { 89, 351, 81 }, // 2260 + { 89, 893, 476 }, // 2261 + { 89, 330, 492 }, // 2262 + { 89, 504, 453 }, // 2263 + { 89, 868, 455 }, // 2264 + { 89, 239, 457 }, // 2265 + { 89, 714, 458 }, // 2266 + { 89, 403, 461 }, // 2267 + { 90, 301, 239 }, // 2268 + { 90, 444, 240 }, // 2269 + { 90, 533, 243 }, // 2270 + { 90, 89, 431 }, // 2271 + { 90, 53, 433 }, // 2272 + { 90, 652, 436 }, // 2273 + { 90, 617, 61 }, // 2274 + { 90, 294, 444 }, // 2275 + { 90, 543, 492 }, // 2276 + { 90, 716, 210 }, // 2277 + { 90, 716, 211 }, // 2278 + { 90, 11, 212 }, // 2279 + { 90, 420, 213 }, // 2280 + { 91, 301, 239 }, // 2281 + { 91, 444, 240 }, // 2282 + { 91, 533, 243 }, // 2283 + { 91, 89, 431 }, // 2284 + { 91, 53, 433 }, // 2285 + { 91, 652, 436 }, // 2286 + { 91, 617, 61 }, // 2287 + { 91, 294, 444 }, // 2288 + { 91, 543, 492 }, // 2289 + { 91, 716, 210 }, // 2290 + { 91, 716, 211 }, // 2291 + { 91, 11, 212 }, // 2292 + { 91, 420, 213 }, // 2293 + { 92, 695, 193 }, // 2294 + { 92, 675, 239 }, // 2295 + { 92, 439, 240 }, // 2296 + { 92, 622, 243 }, // 2297 + { 92, 379, 420 }, // 2298 + { 92, 47, 433 }, // 2299 + { 92, 621, 473 }, // 2300 + { 92, 620, 90 }, // 2301 + { 92, 18, 91 }, // 2302 + { 92, 161, 92 }, // 2303 + { 92, 51, 93 }, // 2304 + { 92, 546, 94 }, // 2305 + { 92, 644, 95 }, // 2306 + { 92, 862, 96 }, // 2307 + { 92, 520, 97 }, // 2308 + { 92, 625, 98 }, // 2309 + { 92, 409, 492 }, // 2310 + { 92, 320, 336 }, // 2311 + { 92, 905, 337 }, // 2312 + { 92, 186, 354 }, // 2313 + { 92, 662, 355 }, // 2314 + { 92, 315, 457 }, // 2315 + { 92, 890, 458 }, // 2316 + { 93, 695, 193 }, // 2317 + { 93, 675, 239 }, // 2318 + { 93, 439, 240 }, // 2319 + { 93, 622, 243 }, // 2320 + { 93, 379, 420 }, // 2321 + { 93, 47, 433 }, // 2322 + { 93, 621, 473 }, // 2323 + { 93, 620, 90 }, // 2324 + { 93, 18, 91 }, // 2325 + { 93, 161, 92 }, // 2326 + { 93, 51, 93 }, // 2327 + { 93, 546, 94 }, // 2328 + { 93, 644, 95 }, // 2329 + { 93, 862, 96 }, // 2330 + { 93, 520, 97 }, // 2331 + { 93, 625, 98 }, // 2332 + { 93, 409, 492 }, // 2333 + { 93, 320, 336 }, // 2334 + { 93, 905, 337 }, // 2335 + { 93, 186, 354 }, // 2336 + { 93, 662, 355 }, // 2337 + { 93, 315, 457 }, // 2338 + { 93, 890, 458 }, // 2339 + { 94, 696, 230 }, // 2340 + { 94, 111, 231 }, // 2341 + { 94, 244, 232 }, // 2342 + { 94, 828, 239 }, // 2343 + { 94, 830, 240 }, // 2344 + { 94, 269, 243 }, // 2345 + { 94, 337, 433 }, // 2346 + { 94, 164, 492 }, // 2347 + { 94, 638, 453 }, // 2348 + { 94, 527, 457 }, // 2349 + { 94, 634, 458 }, // 2350 + { 95, 696, 230 }, // 2351 + { 95, 111, 231 }, // 2352 + { 95, 244, 232 }, // 2353 + { 95, 828, 239 }, // 2354 + { 95, 830, 240 }, // 2355 + { 95, 470, 242 }, // 2356 + { 95, 337, 433 }, // 2357 + { 95, 164, 492 }, // 2358 + { 95, 638, 453 }, // 2359 + { 95, 527, 457 }, // 2360 + { 95, 634, 458 }, // 2361 + { 96, 559, 239 }, // 2362 + { 96, 41, 240 }, // 2363 + { 96, 486, 243 }, // 2364 + { 96, 355, 492 }, // 2365 + { 97, 559, 239 }, // 2366 + { 97, 528, 240 }, // 2367 + { 97, 86, 243 }, // 2368 + { 97, 381, 492 }, // 2369 + { 98, 495, 239 }, // 2370 + { 98, 753, 240 }, // 2371 + { 98, 86, 243 }, // 2372 + { 98, 381, 492 }, // 2373 + { 99, 837, 385 }, // 2374 + { 99, 556, 384 }, // 2375 + { 99, 641, 478 }, // 2376 + { 99, 671, 479 }, // 2377 + { 100, 855, 342 }, // 2378 + { 100, 483, 343 }, // 2379 + { 100, 363, 234 }, // 2380 + { 101, 872, 52 }, // 2381 + { 101, 107, 58 }, // 2382 + { 101, 496, 55 }, // 2383 + { 101, 613, 56 }, // 2384 + { 101, 614, 59 }, // 2385 + { 101, 490, 57 }, // 2386 + { 101, 437, 54 }, // 2387 + { 101, 717, 53 }, // 2388 + { 102, 35, 426 }, // 2389 + { 102, 872, 52 }, // 2390 + { 102, 465, 422 }, // 2391 + { 102, 613, 56 }, // 2392 + { 102, 394, 424 }, // 2393 + { 102, 490, 57 }, // 2394 + { 102, 184, 427 }, // 2395 + { 102, 717, 53 }, // 2396 + { 102, 493, 425 }, // 2397 + { 103, 883, 400 }, // 2398 + { 103, 29, 401 }, // 2399 + { 103, 89, 431 }, // 2400 + { 103, 659, 495 }, // 2401 + { 103, 822, 469 }, // 2402 + { 103, 74, 254 }, // 2403 + { 103, 580, 442 }, // 2404 + { 103, 716, 210 }, // 2405 + { 103, 716, 211 }, // 2406 + { 104, 1, 399 }, // 2407 + { 104, 151, 398 }, // 2408 + { 104, 82, 113 }, // 2409 + { 104, 131, 493 }, // 2410 + { 105, 784, 356 }, // 2411 + { 105, 468, 357 }, // 2412 + { 105, 376, 414 }, // 2413 + { 105, 128, 206 }, // 2414 + { 105, 435, 207 }, // 2415 + { 106, 102, 359 }, // 2416 + { 106, 322, 358 }, // 2417 + { 106, 512, 417 }, // 2418 + { 107, 709, 382 }, // 2419 + { 107, 411, 383 }, // 2420 + { 107, 842, 474 }, // 2421 + { 107, 854, 221 }, // 2422 + { 108, 148, 352 }, // 2423 + { 108, 297, 353 }, // 2424 + { 108, 144, 257 }, // 2425 + { 109, 188, 319 }, // 2426 + { 109, 597, 318 }, // 2427 + { 109, 188, 317 }, // 2428 + { 109, 597, 316 }, // 2429 + { 109, 864, 43 }, // 2430 + { 109, 472, 44 }, // 2431 + { 110, 519, 264 }, // 2432 + { 110, 542, 265 }, // 2433 + { 110, 739, 378 }, // 2434 + { 110, 415, 379 }, // 2435 + { 110, 152, 14 }, // 2436 + { 110, 901, 173 }, // 2437 + { 110, 885, 452 }, // 2438 + { 110, 544, 218 }, // 2439 + { 110, 919, 219 }, // 2440 + { 0, 234, 0 }, // 2441 + { 0, 279, 1 }, // 2442 + { 0, 0, 419 }, // 2443 + { 0, 0, 241 }, // 2444 + + { -1, -1, -1 } // END +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// szs /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const DbFileSZS_t DbFileSZS[N_DB_FILE_SZS+1] = +{ + { 0, "T11", "beginner_course", 0, FLT_TRACK | FLT_STD }, // 0 + { 27, "T11d", "beginner_course_d", 0, FLT_TRACK | FLT_D }, // 1 + { 54, "T12", "farm_course", 1, FLT_TRACK | FLT_STD }, // 2 + { 81, "T12d", "farm_course_d", 1, FLT_TRACK | FLT_D }, // 3 + { 108, "T13", "kinoko_course", 2, FLT_TRACK | FLT_STD }, // 4 + { 133, "T13d", "kinoko_course_d", 2, FLT_TRACK | FLT_D }, // 5 + { 158, "T14", "factory_course", 3, FLT_TRACK | FLT_STD }, // 6 + { 200, "T14d", "factory_course_d", 3, FLT_TRACK | FLT_D }, // 7 + { 242, "T21", "castle_course", 4, FLT_TRACK | FLT_STD }, // 8 + { 269, "T21d", "castle_course_d", 4, FLT_TRACK | FLT_D }, // 9 + { 296, "T22", "shopping_course", 5, FLT_TRACK | FLT_STD }, // 10 + { 323, "T22d", "shopping_course_d", 5, FLT_TRACK | FLT_D }, // 11 + { 350, "T23", "boardcross_course", 6, FLT_TRACK | FLT_STD }, // 12 + { 381, "T23d", "boardcross_course_d", 6, FLT_TRACK | FLT_D }, // 13 + { 412, "T24", "truck_course", 7, FLT_TRACK | FLT_STD }, // 14 + { 435, "T24d", "truck_course_d", 7, FLT_TRACK | FLT_D }, // 15 + { 458, "T31", "senior_course", 8, FLT_TRACK | FLT_STD }, // 16 + { 492, "T31d", "senior_course_d", 8, FLT_TRACK | FLT_D }, // 17 + { 526, "T32", "water_course", 9, FLT_TRACK | FLT_STD }, // 18 + { 564, "T32d", "water_course_d", 9, FLT_TRACK | FLT_D }, // 19 + { 602, "T33", "treehouse_course", 10, FLT_TRACK | FLT_STD }, // 20 + { 638, "T33d", "treehouse_course_d", 10, FLT_TRACK | FLT_D }, // 21 + { 674, "T34", "volcano_course", 11, FLT_TRACK | FLT_STD }, // 22 + { 752, "T34d", "volcano_course_d", 11, FLT_TRACK | FLT_D }, // 23 + { 830, "T41", "desert_course", 12, FLT_TRACK | FLT_STD }, // 24 + { 869, "T41d", "desert_course_d", 12, FLT_TRACK | FLT_D }, // 25 + { 908, "T42", "ridgehighway_course", 13, FLT_TRACK | FLT_STD }, // 26 + { 934, "T42d", "ridgehighway_course_d", 13, FLT_TRACK | FLT_D }, // 27 + { 960, "T43", "koopa_course", 14, FLT_TRACK | FLT_STD }, // 28 + { 999, "T43d", "koopa_course_d", 14, FLT_TRACK | FLT_D }, // 29 + { 1038, "T44", "rainbow_course", 15, FLT_TRACK | FLT_STD }, // 30 + { 1060, "T44d", "rainbow_course_d", 15, FLT_TRACK | FLT_D }, // 31 + { 1082, "T51", "old_peach_gc", 16, FLT_TRACK | FLT_STD }, // 32 + { 1115, "T51d", "old_peach_gc_d", 16, FLT_TRACK | FLT_D }, // 33 + { 1148, "T52", "old_falls_ds", 17, FLT_TRACK | FLT_STD }, // 34 + { 1165, "T52d", "old_falls_ds_d", 17, FLT_TRACK | FLT_D }, // 35 + { 1182, "T53", "old_obake_sfc", 18, FLT_TRACK | FLT_STD }, // 36 + { 1197, "T53d", "old_obake_sfc_d", 18, FLT_TRACK | FLT_D }, // 37 + { 1212, "T54", "old_mario_64", 19, FLT_TRACK | FLT_STD }, // 38 + { 1229, "T54d", "old_mario_64_d", 19, FLT_TRACK | FLT_D }, // 39 + { 1246, "T61", "old_sherbet_64", 20, FLT_TRACK | FLT_STD }, // 40 + { 1277, "T61d", "old_sherbet_64_d", 20, FLT_TRACK | FLT_D }, // 41 + { 1308, "T62", "old_heyho_gba", 21, FLT_TRACK | FLT_STD }, // 42 + { 1334, "T62d", "old_heyho_gba_d", 21, FLT_TRACK | FLT_D }, // 43 + { 1360, "T63", "old_town_ds", 22, FLT_TRACK | FLT_STD }, // 44 + { 1389, "T63d", "old_town_ds_d", 22, FLT_TRACK | FLT_D }, // 45 + { 1418, "T64", "old_waluigi_gc", 23, FLT_TRACK | FLT_STD }, // 46 + { 1437, "T64d", "old_waluigi_gc_d", 23, FLT_TRACK | FLT_D }, // 47 + { 1456, "T71", "old_desert_ds", 24, FLT_TRACK | FLT_STD }, // 48 + { 1482, "T71d", "old_desert_ds_d", 24, FLT_TRACK | FLT_D }, // 49 + { 1508, "T72", "old_koopa_gba", 25, FLT_TRACK | FLT_STD }, // 50 + { 1530, "T72d", "old_koopa_gba_d", 25, FLT_TRACK | FLT_D }, // 51 + { 1552, "T73", "old_donkey_64", 26, FLT_TRACK | FLT_STD }, // 52 + { 1578, "T73d", "old_donkey_64_d", 26, FLT_TRACK | FLT_D }, // 53 + { 1604, "T74", "old_mario_gc", 27, FLT_TRACK | FLT_STD }, // 54 + { 1628, "T74d", "old_mario_gc_d", 27, FLT_TRACK | FLT_D }, // 55 + { 1652, "T81", "old_mario_sfc", 28, FLT_TRACK | FLT_STD }, // 56 + { 1665, "T81d", "old_mario_sfc_d", 28, FLT_TRACK | FLT_D }, // 57 + { 1678, "T82", "old_garden_ds", 29, FLT_TRACK | FLT_STD }, // 58 + { 1701, "T82d", "old_garden_ds_d", 29, FLT_TRACK | FLT_D }, // 59 + { 1724, "T83", "old_donkey_gc", 30, FLT_TRACK | FLT_STD }, // 60 + { 1750, "T83d", "old_donkey_gc_d", 30, FLT_TRACK | FLT_D }, // 61 + { 1776, "T84", "old_koopa_64", 31, FLT_TRACK | FLT_STD }, // 62 + { 1802, "T84d", "old_koopa_64_d", 31, FLT_TRACK | FLT_D }, // 63 + { 1828, "A11", "block_battle", 0, FLT_ARENA | FLT_STD }, // 64 + { 1869, "A11d", "block_battle_d", 0, FLT_ARENA | FLT_D }, // 65 + { 1910, "A12", "venice_battle", 1, FLT_ARENA | FLT_STD }, // 66 + { 1942, "A12d", "venice_battle_d", 1, FLT_ARENA | FLT_D }, // 67 + { 1974, "A13", "skate_battle", 2, FLT_ARENA | FLT_STD }, // 68 + { 1992, "A13d", "skate_battle_d", 2, FLT_ARENA | FLT_D }, // 69 + { 2010, "A14", "casino_battle", 3, FLT_ARENA | FLT_STD }, // 70 + { 2030, "A14d", "casino_battle_d", 3, FLT_ARENA | FLT_D }, // 71 + { 2050, "A15", "sand_battle", 4, FLT_ARENA | FLT_STD }, // 72 + { 2071, "A15d", "sand_battle_d", 4, FLT_ARENA | FLT_D }, // 73 + { 2092, "A21", "old_battle4_sfc", 5, FLT_ARENA | FLT_STD }, // 74 + { 2104, "A21d", "old_battle4_sfc_d", 5, FLT_ARENA | FLT_D }, // 75 + { 2116, "A22", "old_battle3_gba", 6, FLT_ARENA | FLT_STD }, // 76 + { 2128, "A22d", "old_battle3_gba_d", 6, FLT_ARENA | FLT_D }, // 77 + { 2140, "A23", "old_matenro_64", 7, FLT_ARENA | FLT_STD }, // 78 + { 2156, "A23d", "old_matenro_64_d", 7, FLT_ARENA | FLT_D }, // 79 + { 2172, "A24", "old_CookieLand_gc", 8, FLT_ARENA | FLT_STD }, // 80 + { 2184, "A24d", "old_CookieLand_gc_d", 8, FLT_ARENA | FLT_D }, // 81 + { 2196, "A25", "old_House_ds", 9, FLT_ARENA | FLT_STD }, // 82 + { 2212, "A25d", "old_House_ds_d", 9, FLT_ARENA | FLT_D }, // 83 + { 2228, "WIN", "winningrun_demo", -1, FLT_OTHER | FLT_STD }, // 84 + { 2257, "WINd", "winningrun_demo_d", -1, FLT_OTHER | FLT_D }, // 85 + { 2286, "LOS", "loser_demo", -1, FLT_OTHER | FLT_STD }, // 86 + { 2300, "LOSd", "loser_demo_d", -1, FLT_OTHER | FLT_D }, // 87 + { 2314, "END", "ending_demo", -1, FLT_OTHER | FLT_STD }, // 88 + { 2336, "ENDd", "ending_demo_d", -1, FLT_OTHER | FLT_D }, // 89 + { 2358, "MAR", "old_mario_gc_b", -1, FLT_OTHER | FLT_STD }, // 90 + { 2372, "MARd", "old_mario_gc_b_d", -1, FLT_OTHER | FLT_D }, // 91 + { 2386, "RNG", "ring_mission", -1, FLT_OTHER | FLT_STD }, // 92 + { 2410, "RNGd", "ring_mission_d", -1, FLT_OTHER | FLT_D }, // 93 + { 2434, "DRW", "draw_demo", -1, FLT_OLD | FLT_STD }, // 94 + { 2446, "DRWd", "draw_demo_d", -1, FLT_OLD | FLT_D }, // 95 + { 2458, "HAY", "old_mario_gc_hayasi", -1, FLT_OLD | FLT_STD }, // 96 + { 2463, "NAR", "old_mario_gc_narita", -1, FLT_OLD | FLT_STD }, // 97 + { 2468, "YAB", "old_mario_gc_yabuki", -1, FLT_OLD | FLT_STD }, // 98 + { 2473, "CO01", "CommonObj01", -1, FLT_OTHER | FLT_STD }, // 99 + { 2478, "CO02", "CommonObj02", -1, FLT_OTHER | FLT_STD }, // 100 + { 2482, "CO03", "CommonObj03", -1, FLT_OTHER | FLT_STD }, // 101 + { 2491, "CO04", "CommonObj04", -1, FLT_OTHER | FLT_STD }, // 102 + { 2501, "CO05", "CommonObj05", -1, FLT_OTHER | FLT_STD }, // 103 + { 2511, "CO06", "CommonObj06", -1, FLT_OTHER | FLT_STD }, // 104 + { 2516, "CO07", "CommonObj07", -1, FLT_OTHER | FLT_STD }, // 105 + { 2522, "CO08", "CommonObj08", -1, FLT_OTHER | FLT_STD }, // 106 + { 2526, "CO09", "CommonObj09", -1, FLT_OTHER | FLT_STD }, // 107 + { 2531, "CO10", "CommonObj10", -1, FLT_OTHER | FLT_STD }, // 108 + { 2535, "CO11", "CommonObj11", -1, FLT_OTHER | FLT_STD }, // 109 + { 2542, "CO12", "CommonObj12", -1, FLT_OTHER | FLT_STD }, // 110 + + { 2552, "",0,0,-1 } // END +}; + +// +//-------------------------------------------------- + +const s16 DbFileRefSZS[N_DB_FILE_REF_SZS+1] = +{ + + // 0, T11, N=26+1 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, -1, + + // 27, T11d, N=26+1 + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, -1, + + // 54, T12, N=26+1 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, -1, + + // 81, T12d, N=26+1 + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, -1, + + // 108, T13, N=24+1 + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, -1, + + // 133, T13d, N=24+1 + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, -1, + + // 158, T14, N=41+1 + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, -1, + + // 200, T14d, N=41+1 + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, -1, + + // 242, T21, N=26+1 + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, -1, + + // 269, T21d, N=26+1 + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, -1, + + // 296, T22, N=26+1 + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, -1, + + // 323, T22d, N=26+1 + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 335, 336, 337, -1, + + // 350, T23, N=30+1 + 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, + 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, + -1, + + // 381, T23d, N=30+1 + 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, + 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, + -1, + + // 412, T24, N=22+1 + 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, + 418, 419, -1, + + // 435, T24d, N=22+1 + 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, + 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, + 440, 441, -1, + + // 458, T31, N=33+1 + 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, + 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, + 472, 473, 474, -1, + + // 492, T31d, N=33+1 + 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, + 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, + 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, + 505, 506, 507, -1, + + // 526, T32, N=37+1 + 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, + 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, + 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, + 538, 539, 540, 541, 542, 543, 544, -1, + + // 564, T32d, N=37+1 + 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, + 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, + 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, + 575, 576, 577, 578, 579, 580, 581, -1, + + // 602, T33, N=35+1 + 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, + 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, + 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, + 612, 613, 614, 615, 616, -1, + + // 638, T33d, N=35+1 + 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, + 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, + 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, + 647, 648, 649, 650, 651, -1, + + // 674, T34, N=77+1 + 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, + 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, + 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, + 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, + 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, + 722, 723, 724, 725, 726, 727, 728, -1, + + // 752, T34d, N=77+1 + 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, + 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, + 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, + 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, + 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, + 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, + 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, + 799, 800, 801, 802, 803, 804, 805, -1, + + // 830, T41, N=38+1 + 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, + 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, + 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, + 836, 837, 838, 839, 840, 841, 842, 843, -1, + + // 869, T41d, N=38+1 + 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, + 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, + 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, + 874, 875, 876, 877, 878, 879, 880, 881, -1, + + // 908, T42, N=25+1 + 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, + 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, + 902, 903, 904, 905, 906, -1, + + // 934, T42d, N=25+1 + 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, + 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, + 927, 928, 929, 930, 931, -1, + + // 960, T43, N=38+1 + 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, + 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, + 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, + 962, 963, 964, 965, 966, 967, 968, 969, -1, + + // 999, T43d, N=38+1 + 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, + 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, + 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, + 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, -1, + + // 1038, T44, N=21+1 + 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, + 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, + 1028, -1, + + // 1060, T44d, N=21+1 + 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, + 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, + 1049, -1, + + // 1082, T51, N=32+1 + 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, + 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, + 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, + 1080, 1081, -1, + + // 1115, T51d, N=32+1 + 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, + 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, + 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, + 1112, 1113, -1, + + // 1148, T52, N=16+1 + 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, + 1124, 1125, 1126, 1127, 1128, 1129, -1, + + // 1165, T52d, N=16+1 + 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, + 1140, 1141, 1142, 1143, 1144, 1145, -1, + + // 1182, T53, N=14+1 + 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, + 1156, 1157, 1158, 1159, -1, + + // 1197, T53d, N=14+1 + 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, + 1170, 1171, 1172, 1173, -1, + + // 1212, T54, N=16+1 + 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, + 1184, 1185, 1186, 1187, 1188, 1189, -1, + + // 1229, T54d, N=16+1 + 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, + 1200, 1201, 1202, 1203, 1204, 1205, -1, + + // 1246, T61, N=30+1 + 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215, + 1216, 1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, + 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, + -1, + + // 1277, T61d, N=30+1 + 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, + 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, + 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, + -1, + + // 1308, T62, N=25+1 + 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, + 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, + 1286, 1287, 1288, 1289, 1290, -1, + + // 1334, T62d, N=25+1 + 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, 1300, + 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, + 1311, 1312, 1313, 1314, 1315, -1, + + // 1360, T63, N=28+1 + 1316, 1317, 1318, 1319, 1320, 1321, 1322, 1323, 1324, 1325, + 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, + 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, -1, + + // 1389, T63d, N=28+1 + 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, 1352, 1353, + 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, + 1364, 1365, 1366, 1367, 1368, 1369, 1370, 1371, -1, + + // 1418, T64, N=18+1 + 1372, 1373, 1374, 1375, 1376, 1377, 1378, 1379, 1380, 1381, + 1382, 1383, 1384, 1385, 1386, 1387, 1388, 1389, -1, + + // 1437, T64d, N=18+1 + 1390, 1391, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, + 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407, -1, + + // 1456, T71, N=25+1 + 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1417, + 1418, 1419, 1420, 1421, 1422, 1423, 1424, 1425, 1426, 1427, + 1428, 1429, 1430, 1431, 1432, -1, + + // 1482, T71d, N=25+1 + 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, + 1443, 1444, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, + 1453, 1454, 1455, 1456, 1457, -1, + + // 1508, T72, N=21+1 + 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, + 1468, 1469, 1470, 1471, 1472, 1473, 1474, 1475, 1476, 1477, + 1478, -1, + + // 1530, T72d, N=21+1 + 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, + 1489, 1490, 1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, + 1499, -1, + + // 1552, T73, N=25+1 + 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, + 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, + 1520, 1521, 1522, 1523, 1524, -1, + + // 1578, T73d, N=25+1 + 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, 1534, + 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1542, 1543, 1544, + 1545, 1546, 1547, 1548, 1549, -1, + + // 1604, T74, N=23+1 + 1550, 1551, 1552, 1553, 1554, 1555, 1556, 1557, 1558, 1559, + 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, + 1570, 1571, 1572, -1, + + // 1628, T74d, N=23+1 + 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, + 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, + 1593, 1594, 1595, -1, + + // 1652, T81, N=12+1 + 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, + 1606, 1607, -1, + + // 1665, T81d, N=12+1 + 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 1616, 1617, + 1618, 1619, -1, + + // 1678, T82, N=22+1 + 1620, 1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, 1629, + 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, 1638, 1639, + 1640, 1641, -1, + + // 1701, T82d, N=22+1 + 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, + 1652, 1653, 1654, 1655, 1656, 1657, 1658, 1659, 1660, 1661, + 1662, 1663, -1, + + // 1724, T83, N=25+1 + 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, + 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, + 1684, 1685, 1686, 1687, 1688, -1, + + // 1750, T83d, N=25+1 + 1689, 1690, 1691, 1692, 1693, 1694, 1695, 1696, 1697, 1698, + 1699, 1700, 1701, 1702, 1703, 1704, 1705, 1706, 1707, 1708, + 1709, 1710, 1711, 1712, 1713, -1, + + // 1776, T84, N=25+1 + 1714, 1715, 1716, 1717, 1718, 1719, 1720, 1721, 1722, 1723, + 1724, 1725, 1726, 1727, 1728, 1729, 1730, 1731, 1732, 1733, + 1734, 1735, 1736, 1737, 1738, -1, + + // 1802, T84d, N=25+1 + 1739, 1740, 1741, 1742, 1743, 1744, 1745, 1746, 1747, 1748, + 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, + 1759, 1760, 1761, 1762, 1763, -1, + + // 1828, A11, N=40+1 + 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1773, + 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, 1783, + 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, + 1794, 1795, 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, + -1, + + // 1869, A11d, N=40+1 + 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, 1812, 1813, + 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, + 1824, 1825, 1826, 1827, 1828, 1829, 1830, 1831, 1832, 1833, + 1834, 1835, 1836, 1837, 1838, 1839, 1840, 1841, 1842, 1843, + -1, + + // 1910, A12, N=31+1 + 1844, 1845, 1846, 1847, 1848, 1849, 1850, 1851, 1852, 1853, + 1854, 1855, 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, + 1864, 1865, 1866, 1867, 1868, 1869, 1870, 1871, 1872, 1873, + 1874, -1, + + // 1942, A12d, N=31+1 + 1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, + 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, + 1895, 1896, 1897, 1898, 1899, 1900, 1901, 1902, 1903, 1904, + 1905, -1, + + // 1974, A13, N=17+1 + 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1915, + 1916, 1917, 1918, 1919, 1920, 1921, 1922, -1, + + // 1992, A13d, N=17+1 + 1923, 1924, 1925, 1926, 1927, 1928, 1929, 1930, 1931, 1932, + 1933, 1934, 1935, 1936, 1937, 1938, 1939, -1, + + // 2010, A14, N=19+1 + 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, + 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, -1, + + // 2030, A14d, N=19+1 + 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, + 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, -1, + + // 2050, A15, N=20+1 + 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, + 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, + -1, + + // 2071, A15d, N=20+1 + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, + 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + -1, + + // 2092, A21, N=11+1 + 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, + 2028, -1, + + // 2104, A21d, N=11+1 + 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, + 2039, -1, + + // 2116, A22, N=11+1 + 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, + 2050, -1, + + // 2128, A22d, N=11+1 + 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, + 2061, -1, + + // 2140, A23, N=15+1 + 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, + 2072, 2073, 2074, 2075, 2076, -1, + + // 2156, A23d, N=15+1 + 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, + 2087, 2088, 2089, 2090, 2091, -1, + + // 2172, A24, N=11+1 + 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, + 2102, -1, + + // 2184, A24d, N=11+1 + 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, + 2113, -1, + + // 2196, A25, N=15+1 + 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, 2122, 2123, + 2124, 2125, 2126, 2127, 2128, -1, + + // 2212, A25d, N=15+1 + 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137, 2138, + 2139, 2140, 2141, 2142, 2143, -1, + + // 2228, WIN, N=28+1 + 2144, 2145, 2146, 2147, 2148, 2149, 2150, 2151, 2152, 2153, + 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, + 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171, -1, + + // 2257, WINd, N=28+1 + 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2181, + 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, + 2192, 2193, 2194, 2195, 2196, 2197, 2198, 2199, -1, + + // 2286, LOS, N=13+1 + 2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, + 2210, 2211, 2212, -1, + + // 2300, LOSd, N=13+1 + 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, + 2223, 2224, 2225, -1, + + // 2314, END, N=21+1 + 2226, 2227, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, + 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, + 2246, -1, + + // 2336, ENDd, N=21+1 + 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, + 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, + 2267, -1, + + // 2358, MAR, N=13+1 + 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, + 2278, 2279, 2280, -1, + + // 2372, MARd, N=13+1 + 2281, 2282, 2283, 2284, 2285, 2286, 2287, 2288, 2289, 2290, + 2291, 2292, 2293, -1, + + // 2386, RNG, N=23+1 + 2294, 2295, 2296, 2297, 2298, 2299, 2300, 2301, 2302, 2303, + 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, + 2314, 2315, 2316, -1, + + // 2410, RNGd, N=23+1 + 2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, + 2327, 2328, 2329, 2330, 2331, 2332, 2333, 2334, 2335, 2336, + 2337, 2338, 2339, -1, + + // 2434, DRW, N=11+1 + 2340, 2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349, + 2350, -1, + + // 2446, DRWd, N=11+1 + 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, 2360, + 2361, -1, + + // 2458, HAY, N=4+1 + 2362, 2363, 2364, 2365, -1, + + // 2463, NAR, N=4+1 + 2366, 2367, 2368, 2369, -1, + + // 2468, YAB, N=4+1 + 2370, 2371, 2372, 2373, -1, + + // 2473, CO01, N=4+1 + 2374, 2375, 2376, 2377, -1, + + // 2478, CO02, N=3+1 + 2378, 2379, 2380, -1, + + // 2482, CO03, N=8+1 + 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, -1, + + // 2491, CO04, N=9+1 + 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, -1, + + // 2501, CO05, N=9+1 + 2398, 2399, 2400, 2401, 2402, 2403, 2404, 2405, 2406, -1, + + // 2511, CO06, N=4+1 + 2407, 2408, 2409, 2410, -1, + + // 2516, CO07, N=5+1 + 2411, 2412, 2413, 2414, 2415, -1, + + // 2522, CO08, N=3+1 + 2416, 2417, 2418, -1, + + // 2526, CO09, N=4+1 + 2419, 2420, 2421, 2422, -1, + + // 2531, CO10, N=3+1 + 2423, 2424, 2425, -1, + + // 2535, CO11, N=6+1 + 2426, 2427, 2428, 2429, 2430, 2431, -1, + + // 2542, CO12, N=9+1 + 2432, 2433, 2434, 2435, 2436, 2437, 2438, 2439, 2440, -1, + + // END + -2 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// sha1 /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const DbFileSHA1_t DbFileSHA1[N_DB_FILE_SHA1+1] = +{ + { 0, // 0 + { 0x00, 0x20, 0x2c, 0x25, 0xcf, 0xd2, 0x81, 0xa9, 0xae, 0x80, + 0x99, 0x39, 0xa5, 0xc8, 0x4a, 0xba, 0x61, 0x93, 0x5d, 0x89 }}, + { 3, // 1 + { 0x00, 0x66, 0x20, 0xd3, 0xbd, 0xe3, 0x77, 0x8d, 0x6e, 0x72, + 0x68, 0x53, 0xf6, 0xb8, 0xcd, 0xb4, 0xfa, 0xbb, 0x30, 0x65 }}, + { 11, // 2 + { 0x00, 0xb1, 0x6d, 0xf3, 0x81, 0xb6, 0x77, 0x1d, 0x5f, 0x80, + 0xcb, 0x07, 0x85, 0xfa, 0x78, 0xb9, 0x79, 0x3b, 0x7a, 0x47 }}, + { 14, // 3 + { 0x00, 0xe6, 0x8c, 0x9e, 0xf0, 0x5a, 0xcc, 0xa5, 0x60, 0x6a, + 0xd4, 0x7f, 0x13, 0x45, 0x1c, 0x0d, 0xf9, 0xd5, 0x56, 0x9b }}, + { 16, // 4 + { 0x01, 0xe5, 0x12, 0x09, 0xf0, 0x91, 0x66, 0x2c, 0x9f, 0x12, + 0xe4, 0x0f, 0x17, 0xb6, 0x71, 0x39, 0x0f, 0xf2, 0xfd, 0x9c }}, + { 19, // 5 + { 0x01, 0xe8, 0x01, 0x37, 0xaf, 0xd1, 0xb5, 0x26, 0xd0, 0x36, + 0x78, 0x0c, 0xb9, 0xf4, 0xb5, 0xa2, 0xdf, 0x71, 0xbd, 0xec }}, + { 22, // 6 + { 0x02, 0x20, 0x1a, 0xfa, 0x07, 0xc9, 0xab, 0x84, 0x35, 0x96, + 0x2a, 0x97, 0x3c, 0x4b, 0x02, 0xa0, 0xa3, 0x1f, 0x6f, 0x2e }}, + { 25, // 7 + { 0x02, 0x3b, 0x37, 0x6a, 0xcc, 0xfb, 0x0b, 0x53, 0x5c, 0x9b, + 0x85, 0xc9, 0xba, 0x79, 0x2a, 0xda, 0x84, 0x55, 0xfb, 0xb0 }}, + { 28, // 8 + { 0x02, 0xa9, 0xf3, 0x58, 0xc5, 0xfc, 0x0a, 0x9e, 0x13, 0x08, + 0xdf, 0x1a, 0x90, 0x54, 0x23, 0x92, 0x91, 0x83, 0xc2, 0xd0 }}, + { 31, // 9 + { 0x02, 0xc0, 0x3f, 0x24, 0xd2, 0x9c, 0x3e, 0x6d, 0x4d, 0x38, + 0x89, 0x1f, 0xc5, 0xa5, 0xcb, 0xee, 0xa2, 0x88, 0x5d, 0x37 }}, + { 34, // 10 + { 0x03, 0xf5, 0xc2, 0xb9, 0x37, 0x11, 0xf4, 0xcf, 0xd6, 0x40, + 0x91, 0x5c, 0x35, 0xb3, 0xbc, 0x4a, 0x02, 0x45, 0x30, 0xca }}, + { 37, // 11 + { 0x04, 0x5e, 0x51, 0xe1, 0xf1, 0x11, 0xeb, 0x48, 0xee, 0xe5, + 0xa9, 0x0b, 0xd6, 0xe9, 0x82, 0x15, 0xf6, 0x1f, 0xc1, 0xe5 }}, + { 42, // 12 + { 0x04, 0x89, 0xdd, 0x38, 0x20, 0x39, 0x87, 0x1a, 0xd8, 0x5c, + 0xd8, 0x37, 0xb9, 0x8f, 0x83, 0xac, 0x6b, 0xf5, 0x42, 0xe3 }}, + { 45, // 13 + { 0x05, 0x0c, 0xc1, 0x3a, 0x77, 0xad, 0xa8, 0x31, 0x53, 0x04, + 0xf4, 0xb6, 0x80, 0x97, 0xb9, 0xd8, 0x38, 0x76, 0x97, 0x05 }}, + { 48, // 14 + { 0x05, 0x6b, 0x68, 0xae, 0xd0, 0x21, 0x54, 0x3c, 0xaa, 0xf2, + 0xba, 0x9c, 0x1f, 0xec, 0xa1, 0xcb, 0xc0, 0xe6, 0xa2, 0x3f }}, + { 51, // 15 + { 0x05, 0x70, 0x02, 0x80, 0xe5, 0x5b, 0x54, 0x05, 0xf4, 0x18, + 0xc3, 0x64, 0x10, 0x37, 0xcf, 0x65, 0x32, 0x41, 0x94, 0x28 }}, + { 54, // 16 + { 0x06, 0x8d, 0x11, 0xe1, 0x5b, 0xa3, 0xf6, 0xcb, 0xc3, 0x00, + 0x17, 0xc1, 0x13, 0x2c, 0xdf, 0xcc, 0x43, 0xc5, 0x54, 0x5c }}, + { 57, // 17 + { 0x06, 0x98, 0x2c, 0x71, 0x87, 0x44, 0x2c, 0x97, 0xa6, 0x09, + 0x19, 0xef, 0x75, 0xbd, 0xd0, 0x97, 0x45, 0xf4, 0xa9, 0x66 }}, + { 60, // 18 + { 0x07, 0x60, 0x29, 0x44, 0xb8, 0x3f, 0xe4, 0xef, 0x40, 0x7f, + 0x46, 0xd7, 0xdf, 0x8e, 0x8c, 0xbc, 0xc6, 0x70, 0x92, 0x69 }}, + { 63, // 19 + { 0x08, 0x03, 0x65, 0x99, 0x5e, 0x09, 0x83, 0xee, 0x05, 0x98, + 0x82, 0x9b, 0x39, 0xdb, 0x8a, 0x00, 0xa1, 0xfa, 0x3b, 0xc1 }}, + { 66, // 20 + { 0x08, 0x16, 0x83, 0xd0, 0xbd, 0xd6, 0xe0, 0xa2, 0x78, 0x3d, + 0xf1, 0x16, 0x56, 0x84, 0x47, 0xe1, 0xfa, 0x35, 0xb3, 0xa7 }}, + { 69, // 21 + { 0x09, 0x9d, 0x54, 0xc8, 0xf2, 0xe0, 0xea, 0x26, 0x13, 0x67, + 0xb7, 0x63, 0x11, 0xac, 0x91, 0x18, 0x3a, 0xd6, 0x97, 0x31 }}, + { 72, // 22 + { 0x0a, 0x89, 0xde, 0xe4, 0x63, 0x5a, 0x91, 0x6b, 0xb1, 0xb5, + 0xc3, 0x48, 0x6a, 0x1b, 0xce, 0x67, 0x6b, 0xda, 0x6c, 0x65 }}, + { 75, // 23 + { 0x0a, 0x8c, 0x35, 0xac, 0x46, 0xa4, 0x3d, 0xa6, 0x22, 0xa1, + 0x36, 0x22, 0x41, 0x9e, 0x38, 0x16, 0x7c, 0xad, 0x12, 0x88 }}, + { 80, // 24 + { 0x0a, 0x95, 0xfe, 0xb1, 0x84, 0x38, 0x40, 0x4d, 0x1a, 0x1a, + 0x66, 0x2f, 0xe0, 0xbf, 0xcf, 0x2c, 0x83, 0xd3, 0xe9, 0xed }}, + { 83, // 25 + { 0x0b, 0x1a, 0x17, 0x34, 0xe5, 0xbd, 0x40, 0x73, 0x17, 0x8c, + 0xeb, 0xe3, 0x5d, 0x6c, 0x28, 0xb1, 0xe5, 0xb7, 0x94, 0x77 }}, + { 86, // 26 + { 0x0b, 0x1f, 0x3d, 0x97, 0x5f, 0x51, 0x5a, 0x9d, 0xe9, 0x88, + 0x1d, 0xbf, 0x3d, 0x43, 0x56, 0x01, 0xff, 0xf7, 0x59, 0x9b }}, + { 89, // 27 + { 0x0b, 0x59, 0xc9, 0x96, 0x59, 0xa1, 0x81, 0x1a, 0xfd, 0xc1, + 0x89, 0xae, 0x5c, 0x37, 0x75, 0x42, 0xdb, 0x0d, 0xd8, 0x2b }}, + { 92, // 28 + { 0x0b, 0x70, 0x20, 0x43, 0x43, 0xb8, 0xfe, 0x2c, 0x77, 0x61, + 0x4b, 0x87, 0x56, 0xf3, 0x66, 0x23, 0x7d, 0x22, 0xb6, 0x4a }}, + { 95, // 29 + { 0x0b, 0xf3, 0x8b, 0x9c, 0x51, 0x0a, 0x23, 0x66, 0xf0, 0x31, + 0x53, 0x69, 0xc1, 0x60, 0x32, 0x7c, 0x1c, 0x42, 0xeb, 0x18 }}, + { 101, // 30 + { 0x0c, 0x2f, 0x3a, 0xc6, 0xbb, 0xc4, 0x81, 0xe3, 0x12, 0xd8, + 0xb4, 0x13, 0x8c, 0xeb, 0xc4, 0x4c, 0xcc, 0x5e, 0xa3, 0x4e }}, + { 106, // 31 + { 0x0c, 0x4b, 0x8d, 0xd9, 0xdb, 0xea, 0x8e, 0x68, 0x93, 0xed, + 0xc4, 0xbe, 0x62, 0x74, 0x59, 0x3b, 0x42, 0x75, 0x3c, 0xef }}, + { 109, // 32 + { 0x0c, 0x77, 0xd8, 0xb6, 0x76, 0x32, 0xbb, 0x1f, 0xc9, 0x2a, + 0x88, 0xeb, 0xa0, 0xe7, 0x22, 0x28, 0xa9, 0x42, 0xa4, 0x4d }}, + { 114, // 33 + { 0x0c, 0x93, 0xd9, 0x6f, 0xed, 0xc2, 0xd8, 0xfb, 0x6a, 0x3c, + 0x90, 0x4f, 0x86, 0xa5, 0x12, 0x2e, 0x6d, 0x80, 0x5e, 0xd1 }}, + { 117, // 34 + { 0x0c, 0xb6, 0x37, 0x69, 0x24, 0x71, 0x50, 0x03, 0xed, 0x9f, + 0xec, 0x84, 0xbb, 0xd8, 0x2c, 0x98, 0x02, 0x26, 0x2c, 0x6c }}, + { 120, // 35 + { 0x0c, 0xe1, 0x87, 0x82, 0x4d, 0x3d, 0xda, 0xf1, 0x0a, 0x5a, + 0x35, 0x90, 0x9e, 0x69, 0x42, 0x4a, 0xcb, 0xd3, 0x66, 0x91 }}, + { 124, // 36 + { 0x0c, 0xe8, 0xe6, 0xbb, 0xe7, 0x53, 0x5b, 0x50, 0x2c, 0x4d, + 0xea, 0xc2, 0xf3, 0x47, 0x98, 0xd7, 0xc6, 0x38, 0xbb, 0xf4 }}, + { 127, // 37 + { 0x0c, 0xe9, 0xd4, 0x22, 0x6d, 0xb5, 0x11, 0xa0, 0x3e, 0xc7, + 0x51, 0x44, 0x8e, 0xf9, 0x24, 0x84, 0x05, 0x4c, 0xac, 0x2d }}, + { 130, // 38 + { 0x0d, 0x1c, 0xd7, 0xed, 0x30, 0x7e, 0xdc, 0x58, 0x97, 0x98, + 0xb5, 0xc6, 0xac, 0xe3, 0x8d, 0x67, 0x3d, 0x12, 0x76, 0x64 }}, + { 133, // 39 + { 0x0d, 0x72, 0x0e, 0x7c, 0xab, 0x91, 0x99, 0xf6, 0x34, 0xf4, + 0xa9, 0x87, 0x3e, 0x64, 0xa6, 0x06, 0xa7, 0xdd, 0xee, 0xb9 }}, + { 136, // 40 + { 0x0d, 0x92, 0xa7, 0x94, 0x43, 0x64, 0xb1, 0xb0, 0x0e, 0x0a, + 0x00, 0xd0, 0x65, 0x65, 0x09, 0xf4, 0xdc, 0x4c, 0xd5, 0x80 }}, + { 139, // 41 + { 0x0d, 0xef, 0x20, 0xe3, 0xd9, 0xc7, 0x9c, 0x56, 0x37, 0x17, + 0x4d, 0x45, 0x09, 0x07, 0x0d, 0x1b, 0x37, 0x79, 0xf4, 0xf0 }}, + { 141, // 42 + { 0x0e, 0x62, 0x89, 0x67, 0x00, 0x4e, 0x2d, 0x5d, 0xc6, 0x35, + 0x6e, 0x50, 0xed, 0x72, 0x13, 0xe9, 0x62, 0x83, 0x36, 0x60 }}, + { 144, // 43 + { 0x0e, 0x6b, 0x3e, 0xa6, 0xdd, 0x75, 0xa4, 0xe4, 0x51, 0xd1, + 0xe4, 0x16, 0x24, 0xef, 0x86, 0x8a, 0x7a, 0x7a, 0x13, 0x3d }}, + { 147, // 44 + { 0x0e, 0x8e, 0x52, 0x99, 0x4f, 0x02, 0xe7, 0xee, 0x8b, 0xa3, + 0x81, 0x5b, 0x3e, 0xcf, 0x47, 0x00, 0x24, 0xb9, 0x74, 0x71 }}, + { 150, // 45 + { 0x0e, 0xf3, 0x19, 0x31, 0xa5, 0x3d, 0x5f, 0xab, 0x3f, 0x6b, + 0x8d, 0xae, 0xdd, 0x4b, 0xa9, 0xe0, 0xd8, 0xbb, 0xca, 0x96 }}, + { 153, // 46 + { 0x0e, 0xfa, 0xb0, 0x39, 0x91, 0xbc, 0xca, 0xa4, 0xb0, 0xbd, + 0x45, 0xf6, 0x20, 0x70, 0x4e, 0x4e, 0xfb, 0x7a, 0x2f, 0x22 }}, + { 156, // 47 + { 0x0f, 0x42, 0xdf, 0xb1, 0xa9, 0xd6, 0x76, 0x9f, 0x29, 0xb8, + 0xd6, 0xae, 0xe4, 0x51, 0xd1, 0xcd, 0xeb, 0x95, 0x33, 0xe8 }}, + { 159, // 48 + { 0x0f, 0x50, 0x9f, 0x83, 0x3b, 0x24, 0xce, 0x65, 0xf2, 0xca, + 0x3f, 0xc4, 0x3a, 0xce, 0x52, 0xcc, 0x79, 0xe3, 0xf5, 0xca }}, + { 162, // 49 + { 0x0f, 0xd5, 0xfd, 0x1d, 0x0f, 0x46, 0x38, 0x10, 0x6c, 0xb6, + 0x7c, 0xfc, 0xe7, 0xee, 0x07, 0x08, 0x5b, 0x4c, 0x70, 0xd1 }}, + { 165, // 50 + { 0x10, 0x1b, 0x2a, 0x69, 0xb1, 0x30, 0xd0, 0x1f, 0xb3, 0xe2, + 0x98, 0x1b, 0xcb, 0x2d, 0xd2, 0xc4, 0xc9, 0x56, 0xcf, 0x05 }}, + { 168, // 51 + { 0x10, 0x27, 0xad, 0x2b, 0x3b, 0xf5, 0x96, 0x9e, 0x5b, 0x22, + 0x6f, 0xbb, 0x61, 0xd3, 0x25, 0x65, 0x03, 0xc4, 0x63, 0xda }}, + { 171, // 52 + { 0x10, 0x32, 0x9e, 0x6e, 0x64, 0xf6, 0x15, 0xbc, 0x4c, 0x72, + 0xa6, 0xa7, 0xa7, 0x83, 0xe9, 0xd2, 0x1d, 0x2f, 0x24, 0x4e }}, + { 173, // 53 + { 0x10, 0x86, 0x01, 0xa2, 0x33, 0x14, 0x05, 0x93, 0xf9, 0xae, + 0xb7, 0x7d, 0xed, 0xf4, 0x57, 0x3f, 0xae, 0x05, 0x22, 0xc7 }}, + { 176, // 54 + { 0x12, 0x06, 0x89, 0x9d, 0xf7, 0xa3, 0xe3, 0xf3, 0xf5, 0x42, + 0xba, 0xe7, 0x1e, 0x19, 0xf6, 0xc6, 0x00, 0x7c, 0x2f, 0x7e }}, + { 179, // 55 + { 0x12, 0x07, 0xf7, 0xc4, 0x0c, 0x7e, 0xe3, 0xb3, 0xc0, 0x43, + 0x28, 0x3a, 0x75, 0x9b, 0x85, 0x66, 0x78, 0x11, 0x15, 0x5c }}, + { 182, // 56 + { 0x12, 0x15, 0x4e, 0x55, 0x66, 0x27, 0xe8, 0x8d, 0xdd, 0x92, + 0x3d, 0xf1, 0xdf, 0xef, 0x5c, 0x0a, 0x3d, 0xea, 0x6c, 0xcd }}, + { 187, // 57 + { 0x12, 0x31, 0x24, 0x24, 0x23, 0x16, 0x7f, 0xa1, 0xea, 0x80, + 0x62, 0xdf, 0x65, 0xe5, 0x03, 0xd8, 0x68, 0xa7, 0xda, 0x6e }}, + { 190, // 58 + { 0x12, 0xf6, 0xf9, 0x76, 0xc5, 0xc7, 0x62, 0xd0, 0xa1, 0x75, + 0xa3, 0x6d, 0x49, 0xa7, 0xb5, 0xca, 0xc2, 0x55, 0x98, 0x89 }}, + { 193, // 59 + { 0x13, 0x0e, 0x82, 0x2c, 0x99, 0x4c, 0x7f, 0x17, 0xc2, 0xa7, + 0x51, 0xfe, 0x0f, 0x35, 0xa0, 0xa2, 0xfd, 0x58, 0x85, 0xb3 }}, + { 198, // 60 + { 0x13, 0x9c, 0x0c, 0xf1, 0x0c, 0x2f, 0x40, 0x1d, 0x9e, 0x10, + 0xa6, 0x5d, 0x40, 0x9c, 0x9c, 0xe3, 0x67, 0x93, 0xbe, 0xca }}, + { 201, // 61 + { 0x13, 0xa8, 0x0d, 0x1b, 0x86, 0x3e, 0x8e, 0x33, 0x55, 0xf5, + 0xa5, 0x5c, 0x02, 0x5f, 0x95, 0x9a, 0x6c, 0xf8, 0x8c, 0x4f }}, + { 204, // 62 + { 0x13, 0xb8, 0x53, 0x67, 0xbf, 0x04, 0x4a, 0x3a, 0xf2, 0x50, + 0xfe, 0x18, 0x7c, 0x54, 0x8a, 0x4a, 0xb5, 0xd4, 0xa8, 0x77 }}, + { 217, // 63 + { 0x13, 0xea, 0x9b, 0x23, 0xcc, 0xe3, 0x90, 0x8f, 0x4e, 0x56, + 0xc5, 0xba, 0x63, 0x33, 0x64, 0xf4, 0x48, 0x6a, 0x4e, 0xce }}, + { 220, // 64 + { 0x14, 0x70, 0xc7, 0x7d, 0xf7, 0x03, 0xcf, 0xec, 0x07, 0xe2, + 0x62, 0x0b, 0x9b, 0x4f, 0xcd, 0x1b, 0x4a, 0x25, 0x7a, 0x50 }}, + { 223, // 65 + { 0x14, 0xc9, 0x8d, 0x4d, 0x9f, 0x5d, 0xd9, 0x00, 0x40, 0x4b, + 0x8d, 0xcc, 0x7b, 0x1a, 0x21, 0x9b, 0xa7, 0xbd, 0x29, 0xac }}, + { 226, // 66 + { 0x15, 0x22, 0xa6, 0x72, 0x4b, 0x26, 0x44, 0x1b, 0x82, 0xac, + 0x1e, 0xa4, 0x46, 0x90, 0xdc, 0x1f, 0x93, 0x22, 0x24, 0xae }}, + { 229, // 67 + { 0x15, 0x60, 0x9f, 0xce, 0x96, 0x3a, 0xf5, 0x68, 0x7c, 0xc8, + 0xc8, 0xc1, 0xa4, 0x16, 0x80, 0x3f, 0x01, 0xb6, 0xe8, 0xe2 }}, + { 232, // 68 + { 0x15, 0x9b, 0x27, 0x3f, 0xdb, 0x4c, 0x5b, 0x7a, 0x23, 0x24, + 0x8a, 0x34, 0x81, 0xc0, 0xee, 0x8a, 0xad, 0x92, 0x44, 0xfb }}, + { 235, // 69 + { 0x15, 0xbb, 0xf2, 0x69, 0x8f, 0x5e, 0x89, 0x8b, 0x47, 0xa0, + 0xea, 0x8e, 0x62, 0x97, 0x68, 0xf8, 0x7e, 0x99, 0x8c, 0x92 }}, + { 238, // 70 + { 0x15, 0xf7, 0x0e, 0xe6, 0xd3, 0x9b, 0x5e, 0xba, 0xa8, 0xee, + 0xf0, 0x32, 0xbd, 0xb8, 0xe0, 0x5d, 0x33, 0x90, 0xe1, 0x33 }}, + { 241, // 71 + { 0x16, 0x14, 0x9a, 0xa7, 0x86, 0xa3, 0x08, 0x3b, 0x62, 0x10, + 0xdb, 0xc5, 0x03, 0x87, 0xf4, 0x94, 0xe0, 0xf6, 0xed, 0xff }}, + { 244, // 72 + { 0x16, 0x51, 0x9b, 0x9f, 0xcb, 0x2e, 0x6d, 0xd0, 0x41, 0xec, + 0x9f, 0xf6, 0xac, 0xd7, 0x92, 0xac, 0xf3, 0x8c, 0x38, 0x78 }}, + { 247, // 73 + { 0x16, 0x68, 0xac, 0x4a, 0x38, 0x0b, 0xf4, 0x8b, 0xd2, 0x7b, + 0xee, 0x2a, 0xeb, 0x7f, 0xbb, 0xd3, 0xec, 0x7b, 0x13, 0x06 }}, + { 250, // 74 + { 0x16, 0x77, 0x7d, 0xa5, 0x5d, 0xfc, 0x58, 0x1a, 0xa9, 0x9e, + 0x0a, 0xd9, 0x50, 0xf7, 0xfd, 0xf6, 0xda, 0x55, 0xde, 0xbe }}, + { 256, // 75 + { 0x16, 0x82, 0xcb, 0x2f, 0x30, 0xff, 0x3b, 0xfd, 0x44, 0x18, + 0x7a, 0xa2, 0x9c, 0xc0, 0x23, 0x39, 0xfa, 0x8f, 0xdb, 0x58 }}, + { 259, // 76 + { 0x17, 0x17, 0x6d, 0x62, 0x47, 0x49, 0xf6, 0x4b, 0xce, 0xe6, + 0x05, 0xf6, 0xf7, 0xff, 0x16, 0x04, 0x50, 0x53, 0x34, 0x09 }}, + { 262, // 77 + { 0x17, 0x98, 0x08, 0x67, 0x20, 0x28, 0x2a, 0xd1, 0x79, 0x24, + 0xb4, 0xb4, 0x3a, 0xda, 0x40, 0xb9, 0x9e, 0xf4, 0xf4, 0x8a }}, + { 265, // 78 + { 0x17, 0x9d, 0x8a, 0xb4, 0x20, 0x17, 0xaa, 0xcc, 0x9f, 0x9d, + 0x97, 0xf3, 0x58, 0x55, 0xb8, 0xba, 0x65, 0xe8, 0x91, 0x73 }}, + { 268, // 79 + { 0x18, 0x12, 0x97, 0x31, 0xcb, 0xca, 0xe5, 0xf4, 0xa8, 0xc7, + 0x2f, 0x0f, 0xbe, 0x3c, 0x53, 0x36, 0x67, 0xd7, 0x61, 0x42 }}, + { 271, // 80 + { 0x18, 0xb7, 0x96, 0x8f, 0x06, 0xcb, 0x29, 0x3a, 0x69, 0x30, + 0x27, 0x62, 0x57, 0xdf, 0x2b, 0xe6, 0x5d, 0xea, 0xa1, 0x58 }}, + { 274, // 81 + { 0x19, 0x1b, 0x54, 0x64, 0xc4, 0x3c, 0x5f, 0x78, 0x2e, 0x6b, + 0x11, 0x6d, 0x73, 0xbb, 0xa0, 0x16, 0x6e, 0x5c, 0x32, 0x5f }}, + { 277, // 82 + { 0x19, 0xe5, 0xab, 0xf6, 0x76, 0xd2, 0x5c, 0x2c, 0x76, 0x17, + 0xe0, 0x8c, 0x27, 0x47, 0x72, 0xf6, 0x20, 0x43, 0x24, 0xb6 }}, + { 281, // 83 + { 0x19, 0xeb, 0x9d, 0x50, 0x60, 0xf4, 0x82, 0x2d, 0x00, 0x80, + 0x2e, 0xaa, 0x82, 0xfe, 0xe7, 0x83, 0x27, 0x3b, 0x97, 0x5e }}, + { 284, // 84 + { 0x1a, 0x68, 0xfa, 0xbe, 0x50, 0x9a, 0x18, 0x96, 0xfe, 0xd4, + 0xea, 0xf8, 0xa4, 0xaf, 0xda, 0x62, 0xe5, 0x7c, 0xd5, 0xd4 }}, + { 287, // 85 + { 0x1a, 0xde, 0x58, 0xb7, 0x28, 0x86, 0xfe, 0xbd, 0xa0, 0x62, + 0xe2, 0xb3, 0xd1, 0xba, 0x80, 0xc3, 0x84, 0x39, 0xce, 0x7c }}, + { 290, // 86 + { 0x1a, 0xf2, 0x96, 0x16, 0x1e, 0xd4, 0xbb, 0xa8, 0x0e, 0x43, + 0x26, 0x12, 0xe7, 0x02, 0x19, 0x0e, 0x86, 0xb2, 0x81, 0x09 }}, + { 293, // 87 + { 0x1a, 0xf9, 0x6a, 0x78, 0x80, 0xa5, 0x7b, 0xec, 0x04, 0x43, + 0x04, 0x61, 0x84, 0x59, 0xfc, 0x8c, 0x0c, 0x67, 0xc2, 0x4a }}, + { 296, // 88 + { 0x1b, 0x01, 0x47, 0x64, 0x97, 0x5c, 0x9b, 0x7e, 0xbf, 0x52, + 0x6c, 0xbf, 0xe3, 0x0a, 0xe4, 0x39, 0xc4, 0x59, 0x3c, 0xf2 }}, + { 299, // 89 + { 0x1b, 0x6c, 0x55, 0x35, 0x6b, 0xb2, 0xbc, 0x84, 0x80, 0xa0, + 0x8b, 0xb2, 0x5e, 0x92, 0x41, 0x48, 0xd0, 0xd3, 0x91, 0x14 }}, + { 311, // 90 + { 0x1b, 0xc3, 0x40, 0xf8, 0x6f, 0xc8, 0x72, 0xec, 0x0a, 0x36, + 0xf2, 0xb6, 0x38, 0xd8, 0x6c, 0xc2, 0xfe, 0x49, 0xc2, 0x0a }}, + { 314, // 91 + { 0x1c, 0xb3, 0x5f, 0xd5, 0x02, 0x42, 0xbb, 0x10, 0xd3, 0x7f, + 0x55, 0x02, 0x71, 0x62, 0xad, 0xc9, 0x08, 0x24, 0xbd, 0x92 }}, + { 317, // 92 + { 0x1c, 0xfa, 0x11, 0x1b, 0xeb, 0xb7, 0x76, 0x63, 0x20, 0xfb, + 0xf2, 0x0d, 0x81, 0x71, 0x0e, 0x06, 0x35, 0x6c, 0xa5, 0xc6 }}, + { 320, // 93 + { 0x1d, 0x3b, 0xaa, 0xc6, 0xcd, 0xed, 0x6e, 0x3f, 0x88, 0xd0, + 0xdb, 0x8c, 0x9c, 0xb3, 0x00, 0x8c, 0xa7, 0xa9, 0x07, 0xc6 }}, + { 323, // 94 + { 0x1d, 0x41, 0x7b, 0xac, 0x87, 0x40, 0x70, 0xed, 0x89, 0x56, + 0xa0, 0xf3, 0x9e, 0x76, 0xd9, 0xd5, 0xe4, 0xc2, 0xde, 0x27 }}, + { 326, // 95 + { 0x1d, 0xc1, 0x5d, 0x20, 0x10, 0x76, 0x0b, 0xce, 0x76, 0x19, + 0x77, 0x29, 0xc3, 0x8c, 0xbd, 0x9d, 0xc2, 0xa8, 0x8e, 0x7a }}, + { 329, // 96 + { 0x1e, 0x05, 0xa1, 0x1c, 0x5a, 0x72, 0xff, 0x5f, 0x0d, 0xe2, + 0xfa, 0x39, 0x02, 0x32, 0xe7, 0x21, 0xd7, 0x67, 0x78, 0xf9 }}, + { 332, // 97 + { 0x1e, 0x07, 0xc5, 0x53, 0xba, 0xea, 0xd2, 0xe2, 0xc8, 0xbe, + 0x8d, 0xee, 0x08, 0x60, 0x1d, 0xc5, 0x4a, 0x00, 0x94, 0x5d }}, + { 335, // 98 + { 0x1e, 0x75, 0x53, 0xfb, 0x35, 0xd1, 0xeb, 0xd0, 0x04, 0xa5, + 0xcd, 0xa1, 0x76, 0xba, 0xe7, 0x0e, 0x01, 0xcc, 0x23, 0x16 }}, + { 338, // 99 + { 0x1e, 0x86, 0x7e, 0xd7, 0x7c, 0xe6, 0xdb, 0x22, 0x7e, 0x97, + 0xc9, 0x5e, 0x44, 0x29, 0xf1, 0x35, 0xb6, 0x36, 0x34, 0xb2 }}, + { 341, // 100 + { 0x1e, 0x95, 0x31, 0xbb, 0xa7, 0xb2, 0xce, 0x97, 0xae, 0xee, + 0xd8, 0x61, 0x11, 0xce, 0x5e, 0xf4, 0x71, 0x94, 0x23, 0xbd }}, + { 344, // 101 + { 0x1f, 0x06, 0x2b, 0x4c, 0xbd, 0x91, 0x8c, 0x78, 0x5c, 0x3f, + 0x2d, 0xcb, 0x93, 0xfa, 0xf5, 0xc7, 0xae, 0x79, 0x23, 0x17 }}, + { 347, // 102 + { 0x1f, 0x56, 0x9e, 0xf9, 0x45, 0xa3, 0xad, 0xda, 0x38, 0x89, + 0xcf, 0xfa, 0x93, 0x44, 0x7f, 0x7b, 0x8e, 0x23, 0x95, 0x6c }}, + { 349, // 103 + { 0x1f, 0x66, 0xdc, 0x9c, 0x1a, 0x59, 0x1f, 0x1f, 0x3d, 0x27, + 0x65, 0x6c, 0xac, 0x19, 0xf4, 0xae, 0x4e, 0x52, 0xab, 0x43 }}, + { 352, // 104 + { 0x1f, 0x68, 0xfb, 0x84, 0x43, 0x5d, 0xd3, 0xe1, 0xa5, 0xb7, + 0xc8, 0x7a, 0x77, 0xed, 0x1a, 0x36, 0xff, 0x58, 0xf2, 0xa1 }}, + { 361, // 105 + { 0x1f, 0xb4, 0x36, 0x8a, 0x14, 0xb6, 0xa4, 0x18, 0xd9, 0x27, + 0xd0, 0xd3, 0xb9, 0x28, 0xea, 0xdf, 0x33, 0x6e, 0xfe, 0xa5 }}, + { 364, // 106 + { 0x1f, 0xc9, 0xf3, 0xb4, 0xa5, 0x3f, 0x5c, 0x7a, 0x5d, 0x7e, + 0x26, 0x70, 0x83, 0x61, 0x8d, 0x0d, 0x22, 0x08, 0x7d, 0x5e }}, + { 367, // 107 + { 0x1f, 0xe1, 0x16, 0x1c, 0x9e, 0x0d, 0x9b, 0xcd, 0xec, 0x1a, + 0xc3, 0xde, 0xef, 0x03, 0xc0, 0x52, 0x8f, 0x1e, 0xa5, 0xea }}, + { 369, // 108 + { 0x20, 0x26, 0x3d, 0x11, 0x07, 0x3b, 0x5a, 0x55, 0xf3, 0x38, + 0xd6, 0x8c, 0xe0, 0xab, 0x85, 0x34, 0x29, 0x71, 0x49, 0x74 }}, + { 372, // 109 + { 0x20, 0x87, 0x76, 0x67, 0x6a, 0xc8, 0xfa, 0xf6, 0xd0, 0xe5, + 0x3f, 0xbb, 0x8a, 0x76, 0xc8, 0x5a, 0x09, 0x11, 0x97, 0x82 }}, + { 375, // 110 + { 0x20, 0x8c, 0xaf, 0x4a, 0xaa, 0x2b, 0xc6, 0x38, 0x29, 0xad, + 0x2a, 0xc5, 0xf6, 0x92, 0x8d, 0xb1, 0x01, 0x8c, 0x1c, 0x74 }}, + { 378, // 111 + { 0x20, 0xda, 0xf1, 0xae, 0xc6, 0x48, 0xe0, 0xa4, 0x81, 0xd4, + 0x31, 0x91, 0xcf, 0x0f, 0xb4, 0xae, 0x04, 0xef, 0xe5, 0x93 }}, + { 381, // 112 + { 0x20, 0xfc, 0x98, 0xab, 0xce, 0x5f, 0x08, 0x52, 0x2a, 0x1b, + 0x53, 0xa0, 0x38, 0x37, 0x48, 0xf8, 0xa8, 0x49, 0xfa, 0x94 }}, + { 386, // 113 + { 0x21, 0x23, 0x3d, 0x8d, 0xd0, 0x67, 0x94, 0x65, 0x8c, 0xe4, + 0x84, 0x33, 0x4f, 0xc2, 0xd4, 0x8e, 0x76, 0xd2, 0x8b, 0xd6 }}, + { 389, // 114 + { 0x21, 0x2c, 0x52, 0x37, 0xc6, 0x99, 0x64, 0x01, 0x44, 0x43, + 0xfd, 0xb6, 0x85, 0xe8, 0xe9, 0xb5, 0xa0, 0xfe, 0x8d, 0x8a }}, + { 392, // 115 + { 0x22, 0xb7, 0x2d, 0x9b, 0x55, 0xda, 0x71, 0xc4, 0x6b, 0x04, + 0x4c, 0x5c, 0x3a, 0xcf, 0x76, 0x59, 0x72, 0x3d, 0x36, 0xdc }}, + { 395, // 116 + { 0x22, 0xe9, 0x08, 0x8e, 0x81, 0x7e, 0xd8, 0xad, 0x28, 0xe0, + 0x5b, 0xd5, 0x51, 0x07, 0xd4, 0x75, 0x7a, 0x4a, 0x1e, 0x57 }}, + { 398, // 117 + { 0x23, 0x21, 0x9d, 0x8a, 0x13, 0x9c, 0xf6, 0x29, 0xe1, 0x6e, + 0x7c, 0x60, 0x99, 0xd3, 0x03, 0x54, 0x58, 0x76, 0x3f, 0x58 }}, + { 401, // 118 + { 0x23, 0x24, 0x53, 0xd9, 0x1f, 0x29, 0xfd, 0x85, 0xac, 0xf6, + 0x2c, 0xdf, 0x1b, 0x5a, 0x19, 0xa5, 0xd0, 0x9b, 0xf8, 0x31 }}, + { 404, // 119 + { 0x23, 0x2d, 0x23, 0x12, 0x92, 0xa8, 0x4b, 0x6b, 0x2f, 0xfa, + 0xf2, 0xaf, 0x7d, 0x07, 0x18, 0x33, 0x62, 0x4c, 0x55, 0xe4 }}, + { 407, // 120 + { 0x23, 0x75, 0x9d, 0x08, 0x4e, 0x04, 0xcd, 0x12, 0xb6, 0xd0, + 0xad, 0xd8, 0xab, 0x2d, 0x5d, 0xcf, 0x12, 0xdb, 0xc3, 0xf3 }}, + { 410, // 121 + { 0x23, 0xcc, 0x64, 0xbb, 0x9f, 0x5c, 0x31, 0xe7, 0xe4, 0x73, + 0x60, 0x2c, 0x19, 0x61, 0xe4, 0x05, 0xee, 0xbf, 0x53, 0xc2 }}, + { 413, // 122 + { 0x23, 0xdf, 0xea, 0xd5, 0xbf, 0xe9, 0xa6, 0x6d, 0xff, 0x23, + 0x33, 0x70, 0x08, 0x5c, 0xfc, 0xd0, 0x38, 0x2f, 0x8a, 0x57 }}, + { 416, // 123 + { 0x24, 0xaf, 0x97, 0x6c, 0x68, 0x60, 0x0f, 0x11, 0x6c, 0xac, + 0x0f, 0x7d, 0xfd, 0x39, 0x92, 0x67, 0xb2, 0x55, 0x87, 0x6c }}, + { 419, // 124 + { 0x25, 0x31, 0x7e, 0xfc, 0xa7, 0x28, 0x4c, 0x3f, 0xf2, 0x6b, + 0xa2, 0x26, 0x7f, 0x55, 0xd9, 0x68, 0x56, 0xc9, 0x9b, 0x27 }}, + { 422, // 125 + { 0x25, 0xfe, 0xba, 0xde, 0x6f, 0x60, 0x14, 0xbf, 0x6f, 0xaa, + 0x65, 0x19, 0x85, 0xba, 0xd8, 0xfc, 0x8b, 0xf0, 0x48, 0x02 }}, + { 425, // 126 + { 0x26, 0x27, 0x88, 0x03, 0x43, 0xc2, 0x71, 0xbb, 0x62, 0x94, + 0xd6, 0x88, 0xb0, 0x0e, 0xd4, 0xff, 0xdc, 0x09, 0xeb, 0x19 }}, + { 428, // 127 + { 0x26, 0x8c, 0x64, 0xf0, 0x90, 0x41, 0x97, 0x8f, 0x93, 0x36, + 0xef, 0x1b, 0x5b, 0xf1, 0xfa, 0xf0, 0xe3, 0x80, 0xa6, 0x7e }}, + { 431, // 128 + { 0x26, 0x97, 0xc1, 0x43, 0x1d, 0x3a, 0x73, 0xbf, 0x6b, 0x25, + 0x89, 0xbb, 0x32, 0x7a, 0xb5, 0x5d, 0x46, 0xef, 0xeb, 0x42 }}, + { 435, // 129 + { 0x26, 0xdf, 0x5c, 0x65, 0xd4, 0x35, 0x82, 0x9f, 0x9f, 0x1a, + 0xd1, 0xc8, 0xd6, 0xfe, 0x4e, 0x83, 0x11, 0x64, 0x14, 0x63 }}, + { 438, // 130 + { 0x27, 0x60, 0x75, 0x42, 0x65, 0xc0, 0xe2, 0xe3, 0x4f, 0x69, + 0xd8, 0xcd, 0x63, 0xe2, 0x49, 0x4c, 0xc8, 0xa3, 0x38, 0x3f }}, + { 441, // 131 + { 0x27, 0x7b, 0xac, 0xbf, 0x7a, 0xe0, 0xb6, 0xfd, 0xc8, 0xe4, + 0x13, 0x75, 0x86, 0x68, 0x71, 0xee, 0xc1, 0x2d, 0xad, 0xa9 }}, + { 449, // 132 + { 0x27, 0x89, 0x95, 0xc4, 0xd0, 0xf7, 0xf9, 0xc8, 0x45, 0xe4, + 0xef, 0x4d, 0x8e, 0xb5, 0x93, 0xf3, 0xa6, 0x82, 0xfb, 0x25 }}, + { 452, // 133 + { 0x27, 0xac, 0xfa, 0x5f, 0x00, 0x47, 0x29, 0x0c, 0xe3, 0xc2, + 0xee, 0xcc, 0x89, 0xf3, 0x7f, 0x0e, 0x7b, 0xd2, 0x80, 0x4b }}, + { 455, // 134 + { 0x28, 0x5f, 0xb3, 0x1a, 0x6c, 0xfb, 0xcd, 0xd5, 0xc9, 0x3f, + 0x91, 0xd7, 0x30, 0x37, 0xea, 0x6c, 0xcd, 0x73, 0x0e, 0x36 }}, + { 458, // 135 + { 0x28, 0x79, 0x26, 0xd0, 0x6c, 0xe6, 0xca, 0x6d, 0xb9, 0xd5, + 0xfc, 0x84, 0x27, 0x24, 0xfa, 0x3c, 0x80, 0xc6, 0xf7, 0x0c }}, + { 461, // 136 + { 0x28, 0xda, 0x14, 0x42, 0xeb, 0xf1, 0x37, 0x07, 0x03, 0xcb, + 0xcc, 0x42, 0x6c, 0x26, 0x15, 0x79, 0x4a, 0xe1, 0xff, 0x81 }}, + { 464, // 137 + { 0x29, 0x3d, 0x4f, 0xe3, 0xcf, 0x9e, 0x0f, 0x45, 0xfb, 0xc6, + 0x21, 0x3d, 0xbf, 0xc6, 0xa2, 0xd5, 0x8a, 0xa5, 0x65, 0x5e }}, + { 467, // 138 + { 0x29, 0x98, 0x4e, 0x23, 0x21, 0x92, 0x59, 0xd3, 0xaf, 0x3b, + 0x49, 0x17, 0xb4, 0xea, 0xc2, 0xd5, 0xa5, 0x02, 0x28, 0xc0 }}, + { 470, // 139 + { 0x29, 0x98, 0x7d, 0xc8, 0x44, 0x6c, 0xb1, 0xc3, 0x4c, 0xd2, + 0xab, 0xd7, 0xda, 0xd7, 0x03, 0x86, 0x4d, 0x6c, 0x29, 0x3c }}, + { 473, // 140 + { 0x29, 0xbd, 0x6d, 0x3e, 0x2f, 0x7b, 0xab, 0xdf, 0x21, 0xa2, + 0x46, 0xf4, 0x23, 0xc5, 0x92, 0x1f, 0xd9, 0xc6, 0xbb, 0x3d }}, + { 476, // 141 + { 0x2a, 0x43, 0x79, 0x8f, 0x46, 0xb8, 0x11, 0x05, 0x44, 0xf5, + 0xc9, 0x37, 0x41, 0x50, 0xff, 0x3a, 0xab, 0x09, 0xbc, 0xa5 }}, + { 479, // 142 + { 0x2a, 0x6d, 0xc7, 0xea, 0xb3, 0x89, 0x25, 0xca, 0x86, 0xb6, + 0xec, 0x5d, 0x8d, 0x19, 0x9d, 0x89, 0x77, 0x9d, 0xd6, 0xaa }}, + { 482, // 143 + { 0x2b, 0x3d, 0xc8, 0xcb, 0x5a, 0xb3, 0x7c, 0xb9, 0x0f, 0x8c, + 0x0a, 0x3b, 0x16, 0x31, 0xff, 0x51, 0xe8, 0xde, 0x27, 0x6a }}, + { 485, // 144 + { 0x2b, 0x41, 0xf7, 0x18, 0x7d, 0x3c, 0x6b, 0xb7, 0xe7, 0x38, + 0x49, 0xd0, 0x84, 0x74, 0x31, 0x8c, 0x43, 0x73, 0x1a, 0x97 }}, + { 495, // 145 + { 0x2b, 0xd4, 0x5e, 0x0c, 0xe6, 0xb2, 0xb8, 0xed, 0xbd, 0x6d, + 0xdb, 0xbc, 0x1f, 0x9f, 0x35, 0x84, 0xf9, 0x8b, 0x7b, 0x7a }}, + { 498, // 146 + { 0x2c, 0x54, 0x2d, 0xb8, 0x04, 0xf9, 0x21, 0x0d, 0xd6, 0x9b, + 0x5f, 0x46, 0x4b, 0x7f, 0xa8, 0xdc, 0x15, 0x99, 0xc9, 0xd8 }}, + { 503, // 147 + { 0x2c, 0x61, 0xd7, 0x78, 0x88, 0xe3, 0x29, 0x33, 0xb1, 0x4d, + 0x6e, 0x75, 0x0c, 0x65, 0x8c, 0xb5, 0x9d, 0x92, 0xdb, 0x96 }}, + { 506, // 148 + { 0x2c, 0x6e, 0xe4, 0xc5, 0x1a, 0x50, 0x4d, 0xb7, 0x8b, 0x46, + 0x3d, 0x9f, 0x29, 0x18, 0x53, 0x6e, 0xab, 0xe5, 0x52, 0xc5 }}, + { 516, // 149 + { 0x2c, 0xb9, 0x4b, 0xd1, 0xa9, 0xfb, 0x39, 0x45, 0x4f, 0x91, + 0x3e, 0x42, 0xbb, 0x4d, 0xb1, 0x5f, 0xb9, 0xbb, 0xfb, 0x9d }}, + { 519, // 150 + { 0x2c, 0xdf, 0x4e, 0x00, 0xc2, 0xad, 0x7a, 0xa6, 0x55, 0x85, + 0x5f, 0x48, 0x40, 0xb4, 0xb3, 0x21, 0x3d, 0xa4, 0x2c, 0xbc }}, + { 522, // 151 + { 0x2d, 0x29, 0x09, 0x2f, 0x90, 0x56, 0x34, 0x71, 0xa4, 0xe0, + 0xce, 0xf2, 0x25, 0xd3, 0xd5, 0x10, 0x7a, 0xa3, 0x9d, 0x6f }}, + { 530, // 152 + { 0x2d, 0xc4, 0xe7, 0x4a, 0x3f, 0x89, 0x49, 0x09, 0x4f, 0x91, + 0xc6, 0xcd, 0x42, 0x24, 0x95, 0xe7, 0x91, 0x18, 0x39, 0xee }}, + { 534, // 153 + { 0x2d, 0xdc, 0xf7, 0xde, 0x5e, 0x73, 0xef, 0x6b, 0x9f, 0x11, + 0xa0, 0x94, 0xb9, 0x46, 0x87, 0x38, 0x84, 0x22, 0x04, 0x39 }}, + { 537, // 154 + { 0x2e, 0x11, 0x35, 0x84, 0x7b, 0x42, 0xd8, 0x8e, 0x36, 0x00, + 0xc0, 0x63, 0x27, 0xf5, 0xe3, 0x86, 0x68, 0x1b, 0xf0, 0x22 }}, + { 540, // 155 + { 0x2e, 0xb1, 0xd6, 0x85, 0x31, 0x17, 0x71, 0xcf, 0x59, 0xe2, + 0xbb, 0xdd, 0x4c, 0x39, 0x76, 0x7e, 0x8d, 0xbb, 0x9b, 0x07 }}, + { 543, // 156 + { 0x2e, 0xd5, 0x51, 0xbd, 0xe5, 0xc7, 0x2c, 0x05, 0x76, 0xbf, + 0x54, 0xeb, 0x49, 0xb1, 0xde, 0x9b, 0xa3, 0x59, 0xbe, 0x71 }}, + { 546, // 157 + { 0x2e, 0xfe, 0xd2, 0x10, 0x87, 0xc8, 0x5a, 0x5b, 0x79, 0x67, + 0x81, 0xf6, 0xa9, 0x5d, 0x70, 0x5e, 0x5b, 0xf9, 0xa6, 0xd1 }}, + { 549, // 158 + { 0x2f, 0x72, 0xd9, 0x2e, 0x2f, 0x51, 0xda, 0x72, 0xa1, 0xcf, + 0xb9, 0xdf, 0x13, 0x27, 0xf7, 0xce, 0x28, 0xa6, 0x75, 0x56 }}, + { 552, // 159 + { 0x2f, 0x8f, 0xed, 0x49, 0x3c, 0x08, 0x3e, 0xc9, 0xec, 0x4d, + 0xdc, 0xf2, 0xae, 0x81, 0xc7, 0x8e, 0x2a, 0xf4, 0x00, 0x61 }}, + { 555, // 160 + { 0x30, 0x29, 0xc4, 0xeb, 0x82, 0xbc, 0x30, 0x16, 0x90, 0xb0, + 0x91, 0x08, 0x65, 0xe0, 0x33, 0xbc, 0x03, 0x39, 0xa1, 0xaa }}, + { 557, // 161 + { 0x30, 0x73, 0x6c, 0xaa, 0xf3, 0x0d, 0x2b, 0xb2, 0x49, 0x51, + 0x8d, 0x22, 0x5b, 0x78, 0xb1, 0xb8, 0xd4, 0x70, 0xf9, 0x0a }}, + { 560, // 162 + { 0x31, 0x30, 0x52, 0x2f, 0xbd, 0xb4, 0x82, 0xc8, 0x33, 0x90, + 0xfc, 0x92, 0x2f, 0x08, 0xaa, 0xd3, 0x7e, 0xe5, 0x21, 0xf4 }}, + { 563, // 163 + { 0x31, 0x4a, 0x81, 0x45, 0xf2, 0x70, 0xc2, 0xd7, 0xcd, 0x3b, + 0x3e, 0xd1, 0x57, 0xea, 0x4d, 0x99, 0x09, 0xb4, 0x5e, 0xe1 }}, + { 566, // 164 + { 0x31, 0xa3, 0xb9, 0xcb, 0x80, 0x19, 0x33, 0xa7, 0x98, 0x92, + 0xc9, 0xe9, 0x0e, 0x1c, 0x1d, 0x0e, 0x04, 0x95, 0x7f, 0xc1 }}, + { 569, // 165 + { 0x31, 0xee, 0x53, 0x98, 0x13, 0x38, 0x81, 0x79, 0xda, 0x58, + 0xd8, 0x07, 0xc0, 0x06, 0x70, 0x88, 0x5d, 0x04, 0x55, 0xb7 }}, + { 572, // 166 + { 0x32, 0x15, 0x16, 0x90, 0x4d, 0x64, 0x81, 0x0f, 0xb0, 0x79, + 0x0a, 0x55, 0x32, 0xab, 0xc0, 0x24, 0x84, 0x66, 0x19, 0xcf }}, + { 575, // 167 + { 0x32, 0x50, 0x8a, 0x29, 0x22, 0x0b, 0xb8, 0xcb, 0x1c, 0x56, + 0x23, 0xc1, 0x61, 0x16, 0x0f, 0x94, 0x97, 0x89, 0xd8, 0xf7 }}, + { 578, // 168 + { 0x32, 0x69, 0xfd, 0x44, 0xd3, 0x6c, 0x4f, 0xd1, 0xbe, 0x53, + 0x76, 0x52, 0x75, 0x49, 0xf4, 0x23, 0x3b, 0xa0, 0xd0, 0x97 }}, + { 581, // 169 + { 0x32, 0x72, 0x0c, 0xdf, 0x04, 0xd4, 0x35, 0x45, 0xc2, 0xbc, + 0xd5, 0x00, 0x1a, 0x0b, 0xb7, 0xc6, 0x58, 0xaa, 0x30, 0x10 }}, + { 584, // 170 + { 0x32, 0x91, 0x95, 0xf3, 0x07, 0x80, 0x94, 0x84, 0x1e, 0x99, + 0x90, 0x57, 0x47, 0xba, 0x97, 0x4d, 0xdd, 0x7c, 0x8a, 0x15 }}, + { 587, // 171 + { 0x32, 0x95, 0x67, 0x31, 0x8c, 0x15, 0xa0, 0xf2, 0x72, 0xc4, + 0xa9, 0x2e, 0x02, 0x30, 0xf0, 0x04, 0xde, 0x0e, 0x42, 0xb4 }}, + { 592, // 172 + { 0x32, 0xe0, 0xef, 0xba, 0x32, 0xde, 0x3f, 0xe4, 0x9a, 0x29, + 0x07, 0x24, 0x89, 0xbd, 0x9c, 0xec, 0x5f, 0x43, 0xa2, 0x91 }}, + { 595, // 173 + { 0x32, 0xfa, 0x5c, 0x25, 0x23, 0x51, 0xd2, 0x73, 0x9d, 0x8e, + 0x28, 0xf3, 0x92, 0xbe, 0x99, 0xef, 0xc4, 0x8c, 0xba, 0x59 }}, + { 598, // 174 + { 0x33, 0x5b, 0x5d, 0x21, 0xf5, 0xcb, 0xa0, 0x5f, 0x10, 0xd6, + 0xb9, 0x76, 0x82, 0xbb, 0xb7, 0xff, 0xda, 0x33, 0xc3, 0x2d }}, + { 603, // 175 + { 0x33, 0x8b, 0xb8, 0x00, 0x56, 0x89, 0xae, 0xae, 0x34, 0x53, + 0xa7, 0xf6, 0xc8, 0x3a, 0x3a, 0x97, 0xbb, 0xe4, 0xce, 0x5a }}, + { 606, // 176 + { 0x33, 0xe9, 0xd4, 0x29, 0xd0, 0x2b, 0xb3, 0x21, 0xbf, 0x81, + 0xfc, 0x9b, 0x7c, 0x31, 0x3b, 0x35, 0x6c, 0x5f, 0x5f, 0xd7 }}, + { 609, // 177 + { 0x34, 0x45, 0x18, 0x59, 0x19, 0x33, 0x28, 0x48, 0xd6, 0xf2, + 0x1a, 0x72, 0x75, 0x9a, 0x43, 0x87, 0x69, 0xd3, 0xbe, 0xf3 }}, + { 612, // 178 + { 0x34, 0x6a, 0x4d, 0xd3, 0x51, 0x9e, 0xc8, 0x4d, 0x63, 0xe4, + 0x8f, 0xb2, 0x2e, 0x72, 0x33, 0xb2, 0x6c, 0x51, 0xd6, 0x28 }}, + { 615, // 179 + { 0x34, 0xb7, 0x0b, 0xaf, 0x9f, 0x5d, 0xe0, 0xe9, 0x3b, 0x67, + 0x2e, 0x98, 0x6e, 0x01, 0x42, 0x3f, 0xbf, 0x35, 0xce, 0xd4 }}, + { 618, // 180 + { 0x34, 0xfd, 0x55, 0xe2, 0xd6, 0x83, 0x35, 0xd7, 0xea, 0xab, + 0x75, 0x4b, 0xa5, 0x1d, 0x29, 0x20, 0x92, 0x12, 0x78, 0xfa }}, + { 621, // 181 + { 0x35, 0x04, 0x3a, 0xec, 0x14, 0x40, 0xbb, 0x0f, 0x1e, 0x12, + 0x48, 0x73, 0xb0, 0x67, 0x07, 0x5e, 0xc7, 0x23, 0x87, 0xa3 }}, + { 624, // 182 + { 0x35, 0x99, 0x6c, 0x00, 0x94, 0xd4, 0xcf, 0xf0, 0x1d, 0x20, + 0xe4, 0xf0, 0x83, 0x3d, 0x78, 0xf1, 0x1e, 0xa8, 0x7d, 0x0a }}, + { 627, // 183 + { 0x35, 0x9f, 0x40, 0x98, 0xba, 0xb3, 0x4b, 0x23, 0x89, 0xf6, + 0xa8, 0x66, 0x60, 0x82, 0xdc, 0x41, 0xd1, 0x28, 0x82, 0x81 }}, + { 629, // 184 + { 0x35, 0xb3, 0xec, 0x69, 0x8b, 0x52, 0x7f, 0x4c, 0x46, 0x19, + 0xa4, 0xbd, 0x1f, 0xe6, 0x27, 0x22, 0xf5, 0x00, 0x1a, 0xfd }}, + { 633, // 185 + { 0x36, 0x1d, 0x1b, 0x5a, 0x64, 0xbb, 0x51, 0xc5, 0xbc, 0xca, + 0x11, 0xbc, 0x45, 0xae, 0x2e, 0x16, 0xc7, 0x3c, 0xf0, 0x79 }}, + { 642, // 186 + { 0x36, 0x1e, 0x3e, 0xbd, 0xb4, 0x4d, 0x6e, 0xcc, 0x0a, 0x80, + 0x02, 0xee, 0xee, 0xb8, 0x4b, 0x38, 0xc4, 0x7f, 0x42, 0x1b }}, + { 647, // 187 + { 0x36, 0x62, 0xd8, 0x45, 0x7f, 0xda, 0x88, 0x04, 0xe9, 0x33, + 0x9c, 0x5f, 0x7d, 0x9d, 0xc6, 0x0f, 0x05, 0x23, 0x34, 0x72 }}, + { 650, // 188 + { 0x36, 0xfe, 0x2f, 0x80, 0xc4, 0x7f, 0xb8, 0xf4, 0x9d, 0x38, + 0x3e, 0x22, 0xca, 0xcc, 0xb6, 0xaa, 0x92, 0x30, 0x0f, 0x1d }}, + { 655, // 189 + { 0x37, 0x65, 0x0b, 0x14, 0x7a, 0x0c, 0x12, 0xf3, 0xce, 0xc2, + 0xd7, 0x95, 0x8a, 0x02, 0xb6, 0x29, 0x8b, 0xe1, 0x4f, 0xca }}, + { 658, // 190 + { 0x38, 0x42, 0xb8, 0xee, 0x27, 0xa4, 0xfe, 0x94, 0xc0, 0xae, + 0x73, 0x7b, 0x1a, 0x40, 0x06, 0x79, 0x65, 0xa3, 0xd8, 0x25 }}, + { 661, // 191 + { 0x38, 0x4a, 0x79, 0xac, 0xc7, 0xdd, 0xc5, 0x62, 0xfc, 0xfe, + 0x8f, 0x44, 0x54, 0x41, 0x56, 0x0e, 0xc0, 0xe2, 0xb1, 0x6f }}, + { 664, // 192 + { 0x38, 0x8c, 0x2f, 0xca, 0x44, 0x62, 0x38, 0xef, 0x1f, 0xce, + 0x4c, 0xf6, 0xcc, 0x43, 0x8c, 0x2e, 0x2b, 0xfd, 0x4d, 0xeb }}, + { 667, // 193 + { 0x38, 0x98, 0xc4, 0xf2, 0xfc, 0xc5, 0x4d, 0x0a, 0xdd, 0x34, + 0x59, 0x55, 0x2c, 0x36, 0xb3, 0xe3, 0x5c, 0x91, 0xc9, 0x67 }}, + { 670, // 194 + { 0x38, 0xb3, 0xf3, 0xc9, 0x3d, 0x77, 0x7a, 0xfb, 0x24, 0x6a, + 0x18, 0x63, 0x56, 0xf9, 0x6e, 0x5c, 0xc9, 0x60, 0x09, 0x68 }}, + { 673, // 195 + { 0x38, 0xb7, 0x74, 0xd4, 0xaa, 0xbd, 0x6d, 0xc2, 0x93, 0x7b, + 0xe0, 0x07, 0x6d, 0xdb, 0x08, 0xce, 0x4a, 0x95, 0x1c, 0x2b }}, + { 676, // 196 + { 0x38, 0xd3, 0xcb, 0xb2, 0xab, 0xb2, 0x44, 0xc8, 0x18, 0xbc, + 0x33, 0x9b, 0x2e, 0x50, 0xfd, 0xf3, 0xd6, 0x6f, 0x79, 0xf3 }}, + { 679, // 197 + { 0x39, 0x03, 0x71, 0x52, 0xb3, 0x95, 0xcb, 0x69, 0x08, 0x3f, + 0x4d, 0x27, 0x66, 0x1c, 0x0a, 0x29, 0x28, 0x7e, 0x8c, 0xe2 }}, + { 682, // 198 + { 0x39, 0x0f, 0xf0, 0xfa, 0x10, 0xa8, 0xdb, 0xc3, 0xa7, 0x2f, + 0x35, 0x0f, 0x8d, 0x4b, 0xe6, 0xa3, 0x1b, 0x1b, 0x01, 0x29 }}, + { 685, // 199 + { 0x39, 0x29, 0x1d, 0x15, 0x97, 0x7a, 0x69, 0x6f, 0xdb, 0xd1, + 0xb2, 0x6c, 0x23, 0x11, 0x14, 0x24, 0x96, 0xfa, 0x50, 0x74 }}, + { 688, // 200 + { 0x39, 0x46, 0x96, 0x03, 0xdf, 0xd5, 0x6f, 0x16, 0xf9, 0x5f, + 0x34, 0x65, 0xea, 0x59, 0xa4, 0x3a, 0x3f, 0x72, 0xec, 0xbd }}, + { 691, // 201 + { 0x39, 0x57, 0x14, 0xcd, 0x62, 0x49, 0x29, 0x23, 0x5c, 0x7b, + 0x82, 0x11, 0x15, 0x74, 0x80, 0xec, 0xfb, 0x90, 0x76, 0x50 }}, + { 694, // 202 + { 0x39, 0xbc, 0x1f, 0xd5, 0xae, 0xc0, 0x68, 0x01, 0x24, 0x2b, + 0x49, 0xc4, 0xa6, 0x63, 0x89, 0x31, 0x73, 0x11, 0xfb, 0xce }}, + { 697, // 203 + { 0x39, 0xd2, 0x5f, 0xf7, 0xd8, 0x3b, 0x75, 0xef, 0xf9, 0x1c, + 0x25, 0x83, 0x68, 0xe6, 0xab, 0x2a, 0x53, 0x41, 0x2b, 0x82 }}, + { 700, // 204 + { 0x39, 0xdc, 0x69, 0x60, 0x4f, 0x8a, 0x2b, 0x31, 0x0c, 0x8d, + 0xce, 0xd0, 0x29, 0xad, 0xae, 0x2d, 0x17, 0xbe, 0x98, 0x75 }}, + { 703, // 205 + { 0x39, 0xef, 0xda, 0x91, 0xb6, 0x8b, 0x10, 0x1d, 0x02, 0x0d, + 0x27, 0x60, 0x79, 0xf1, 0x5d, 0x2f, 0xd6, 0x79, 0x21, 0x47 }}, + { 710, // 206 + { 0x3a, 0x43, 0x25, 0x80, 0x17, 0x5f, 0x9c, 0xed, 0xfc, 0x6b, + 0x8e, 0xd6, 0xec, 0xd7, 0x80, 0x5a, 0xdf, 0x7b, 0x39, 0x9c }}, + { 713, // 207 + { 0x3a, 0x65, 0x84, 0xa4, 0xe4, 0x86, 0xcf, 0x30, 0xba, 0x1c, + 0xcf, 0xd5, 0x1b, 0x16, 0x8d, 0x7e, 0x97, 0xe2, 0xda, 0xdb }}, + { 716, // 208 + { 0x3a, 0x6c, 0x02, 0x5f, 0xed, 0x84, 0x7c, 0x0e, 0xc5, 0x4a, + 0x72, 0x18, 0x31, 0x60, 0xe9, 0x60, 0x12, 0x2b, 0x78, 0x50 }}, + { 719, // 209 + { 0x3a, 0x6e, 0xca, 0xeb, 0x8e, 0x1a, 0x7a, 0x75, 0x57, 0xda, + 0x40, 0x10, 0x07, 0x77, 0x7a, 0x0d, 0xc4, 0xac, 0xa9, 0xe7 }}, + { 722, // 210 + { 0x3a, 0xc2, 0x6a, 0x56, 0x17, 0x02, 0x10, 0x4a, 0xa6, 0x85, + 0xf9, 0x69, 0xb9, 0x27, 0x50, 0xc0, 0x60, 0x49, 0x90, 0x2e }}, + { 725, // 211 + { 0x3b, 0x25, 0x0d, 0x30, 0x5d, 0x17, 0xef, 0x00, 0x6e, 0x59, + 0x8a, 0x2a, 0x46, 0x9a, 0xbc, 0xf9, 0xdd, 0xc4, 0x06, 0x27 }}, + { 730, // 212 + { 0x3b, 0x55, 0xae, 0xf1, 0x05, 0x0f, 0x15, 0x38, 0x4d, 0x85, + 0xd2, 0x05, 0x83, 0x52, 0xc8, 0xd7, 0x30, 0x13, 0x39, 0x60 }}, + { 733, // 213 + { 0x3b, 0x6f, 0xe1, 0x72, 0x83, 0xb4, 0xc1, 0x80, 0xf7, 0xea, + 0x15, 0xad, 0xfe, 0xe7, 0xf2, 0x86, 0xa1, 0x2a, 0x5a, 0x64 }}, + { 736, // 214 + { 0x3b, 0x89, 0x34, 0xf0, 0xb7, 0x96, 0x10, 0x41, 0xc9, 0x5a, + 0x78, 0x72, 0xaa, 0xb3, 0x7a, 0x70, 0xc4, 0x79, 0xb9, 0xdb }}, + { 739, // 215 + { 0x3b, 0xa9, 0xd6, 0x90, 0x2b, 0xdb, 0x29, 0x64, 0x93, 0x69, + 0x12, 0x0f, 0x79, 0x06, 0xdf, 0x0f, 0x35, 0xeb, 0x9a, 0x23 }}, + { 742, // 216 + { 0x3b, 0xca, 0x90, 0xa3, 0xec, 0xeb, 0xbd, 0x92, 0xa4, 0x37, + 0x6c, 0x22, 0x6b, 0xe1, 0xfc, 0xa6, 0x30, 0x53, 0x2c, 0xc9 }}, + { 745, // 217 + { 0x3c, 0x16, 0x00, 0x9b, 0x77, 0x8f, 0x27, 0xbe, 0x49, 0x31, + 0x00, 0x08, 0x9c, 0xaa, 0x6a, 0xcb, 0x46, 0x5e, 0x68, 0x85 }}, + { 748, // 218 + { 0x3c, 0x5a, 0x84, 0xbd, 0xce, 0xf5, 0xbb, 0x5d, 0x2c, 0xf3, + 0xf9, 0x43, 0x7f, 0x3f, 0x59, 0x77, 0xb0, 0x7c, 0xaf, 0x52 }}, + { 751, // 219 + { 0x3c, 0x69, 0x78, 0x18, 0x76, 0xfa, 0xc6, 0x27, 0x7d, 0x6d, + 0xd3, 0xaa, 0x06, 0x1f, 0xbf, 0x72, 0xed, 0x18, 0x13, 0x53 }}, + { 754, // 220 + { 0x3d, 0x14, 0x76, 0x85, 0x9b, 0x31, 0x81, 0x69, 0xcf, 0x6d, + 0x63, 0x89, 0x8d, 0xff, 0xfd, 0xc1, 0x0d, 0x81, 0xc7, 0x6a }}, + { 757, // 221 + { 0x3d, 0x2f, 0xf8, 0xd4, 0xe1, 0x1a, 0x9a, 0x28, 0x96, 0x3d, + 0x8c, 0x72, 0x55, 0x2d, 0x42, 0x84, 0x2c, 0x57, 0x16, 0xae }}, + { 760, // 222 + { 0x3d, 0x33, 0x23, 0x63, 0x3c, 0x80, 0xeb, 0x82, 0x1d, 0x23, + 0x32, 0xfd, 0x92, 0x65, 0x0a, 0x0a, 0x9b, 0x57, 0xe7, 0x2b }}, + { 763, // 223 + { 0x3d, 0xc5, 0xfe, 0x86, 0xf2, 0x82, 0xe1, 0x16, 0xfa, 0xef, + 0xc5, 0x12, 0x48, 0xfc, 0x09, 0x39, 0x5f, 0x8a, 0xd5, 0x8b }}, + { 766, // 224 + { 0x3d, 0xfe, 0x43, 0x30, 0x70, 0xab, 0x48, 0x04, 0xbf, 0x12, + 0x14, 0x12, 0xb1, 0xd8, 0x33, 0x87, 0x27, 0xe8, 0xf1, 0xb9 }}, + { 769, // 225 + { 0x3e, 0x2b, 0x78, 0x33, 0x40, 0xa7, 0xc0, 0x3c, 0xa9, 0x68, + 0x85, 0xec, 0xb9, 0x10, 0x9d, 0xb3, 0x23, 0x67, 0x20, 0xed }}, + { 772, // 226 + { 0x3e, 0xd5, 0xa4, 0xe0, 0x51, 0xc4, 0x5d, 0xb2, 0xca, 0xce, + 0x2c, 0xc2, 0x4b, 0xfb, 0x40, 0x47, 0xbe, 0xcf, 0x61, 0x78 }}, + { 777, // 227 + { 0x3f, 0x28, 0x01, 0x53, 0xbf, 0xa9, 0x1b, 0xf8, 0xc1, 0x0e, + 0x68, 0x30, 0xb1, 0xa3, 0xec, 0x93, 0x9f, 0x58, 0x6f, 0x37 }}, + { 780, // 228 + { 0x3f, 0x29, 0x04, 0x45, 0x57, 0xa3, 0xda, 0x0b, 0x30, 0x94, + 0xa7, 0xbf, 0xe7, 0x3c, 0xd1, 0x26, 0x4a, 0x87, 0xb8, 0x07 }}, + { 783, // 229 + { 0x3f, 0xc6, 0x0c, 0xc4, 0x48, 0x94, 0xc1, 0x58, 0x05, 0xdd, + 0x69, 0xae, 0x14, 0x27, 0xed, 0xc2, 0x67, 0xbe, 0xba, 0x4c }}, + { 786, // 230 + { 0x3f, 0xd9, 0xd1, 0xff, 0x3e, 0xc0, 0x46, 0x3c, 0xc6, 0x03, + 0x2e, 0xd0, 0x86, 0xbf, 0x29, 0x5f, 0x04, 0x2d, 0x74, 0x8a }}, + { 789, // 231 + { 0x40, 0x24, 0x1c, 0x41, 0x5a, 0x11, 0x8d, 0xf9, 0x92, 0xbb, + 0xb4, 0xe9, 0x23, 0xac, 0x26, 0x3e, 0x2b, 0x7b, 0xa4, 0x02 }}, + { 792, // 232 + { 0x40, 0x44, 0x84, 0x5c, 0x01, 0xad, 0xbb, 0xc8, 0x78, 0xec, + 0x11, 0x7d, 0x70, 0xbe, 0x72, 0x0f, 0x97, 0xe0, 0xea, 0x44 }}, + { 801, // 233 + { 0x41, 0x32, 0xdb, 0x18, 0x30, 0xc6, 0x08, 0xa3, 0xf3, 0x15, + 0xf1, 0xd2, 0xc3, 0x97, 0x03, 0xc1, 0x96, 0x81, 0x6b, 0xe3 }}, + { 804, // 234 + { 0x41, 0x43, 0x38, 0xda, 0x47, 0xd1, 0x54, 0x8c, 0xe5, 0x5b, + 0xdd, 0x46, 0x48, 0xb7, 0xc6, 0x8e, 0x10, 0x0d, 0x70, 0x6c }}, + { 806, // 235 + { 0x42, 0x10, 0x9e, 0x43, 0x5d, 0x0f, 0x53, 0x08, 0x15, 0x06, + 0xce, 0x47, 0xc4, 0xe0, 0x55, 0xb4, 0x0a, 0x9d, 0x0f, 0xe5 }}, + { 809, // 236 + { 0x42, 0x76, 0xbf, 0xb3, 0x76, 0x31, 0x04, 0x81, 0x02, 0x8e, + 0x87, 0xea, 0x38, 0x5e, 0x53, 0xbe, 0x5d, 0xce, 0x2c, 0x8a }}, + { 812, // 237 + { 0x42, 0x78, 0x0d, 0xbb, 0xa5, 0x4c, 0x45, 0x01, 0xfc, 0xae, + 0xc6, 0x88, 0x62, 0x09, 0x9d, 0x92, 0xf8, 0xf5, 0xf0, 0xaa }}, + { 815, // 238 + { 0x42, 0xf5, 0x41, 0xc1, 0xd9, 0xf8, 0x3a, 0x0d, 0x7a, 0x93, + 0x2a, 0xa3, 0xc5, 0x5a, 0xa8, 0x9f, 0x7c, 0x0d, 0xe7, 0x4d }}, + { 820, // 239 + { 0x43, 0x10, 0xca, 0xeb, 0x82, 0x11, 0x80, 0x85, 0xed, 0xf9, + 0xf7, 0xb9, 0xe7, 0x93, 0xdd, 0x96, 0x62, 0x50, 0x26, 0xf4 }}, + { 823, // 240 + { 0x43, 0x68, 0x2e, 0x94, 0xc6, 0x44, 0xb2, 0x08, 0x31, 0xfb, + 0x83, 0x0c, 0x8f, 0xc0, 0xb7, 0xc6, 0x4d, 0x18, 0xdf, 0xf0 }}, + { 826, // 241 + { 0x44, 0x34, 0x33, 0xb2, 0xf7, 0x1b, 0xbc, 0x3f, 0x0e, 0x32, + 0x13, 0x79, 0x8f, 0x1a, 0x44, 0x25, 0x2c, 0x98, 0x9e, 0xf2 }}, + { 829, // 242 + { 0x44, 0xa1, 0xf0, 0x28, 0xba, 0x25, 0x65, 0xc3, 0x5a, 0x8c, + 0x7b, 0x1f, 0xe6, 0x41, 0x58, 0xa2, 0x6a, 0xa8, 0xf6, 0x30 }}, + { 834, // 243 + { 0x44, 0xa7, 0x3b, 0x15, 0xa1, 0x88, 0x10, 0xe1, 0xdd, 0x89, + 0xd6, 0x19, 0xad, 0x57, 0x19, 0xf0, 0x56, 0x8a, 0x32, 0x3c }}, + { 837, // 244 + { 0x44, 0xf2, 0xa4, 0xfd, 0x14, 0x93, 0xa1, 0xd7, 0x0d, 0xed, + 0x58, 0x46, 0x06, 0xba, 0xed, 0x55, 0x9a, 0x9d, 0x09, 0x2d }}, + { 840, // 245 + { 0x45, 0x7e, 0xae, 0xf8, 0xce, 0x2f, 0x5b, 0x35, 0x9e, 0x40, + 0x4f, 0x08, 0xb4, 0x1f, 0x1d, 0xb0, 0x74, 0x4f, 0xb7, 0xa1 }}, + { 843, // 246 + { 0x45, 0x84, 0x12, 0xc8, 0x36, 0xbf, 0xbf, 0x04, 0x1c, 0x47, + 0x41, 0x8b, 0x0a, 0x61, 0x14, 0x83, 0xa8, 0x4e, 0xef, 0xac }}, + { 846, // 247 + { 0x45, 0xac, 0x46, 0xbf, 0xe7, 0x5d, 0xf4, 0xca, 0x03, 0xad, + 0x64, 0x5e, 0x8a, 0x6a, 0xd3, 0x80, 0xf1, 0x8d, 0xd1, 0xd5 }}, + { 849, // 248 + { 0x45, 0xba, 0xf4, 0xb8, 0x3c, 0xde, 0x20, 0xa5, 0xb0, 0xfa, + 0x2a, 0x9f, 0xaf, 0x7b, 0xbd, 0x78, 0x1a, 0xa8, 0xec, 0xe9 }}, + { 852, // 249 + { 0x45, 0xfa, 0x09, 0xd1, 0x8c, 0x6b, 0x85, 0xe9, 0x9b, 0x82, + 0x6e, 0xf8, 0xf8, 0x10, 0xce, 0x84, 0xcb, 0xf2, 0x82, 0x46 }}, + { 855, // 250 + { 0x46, 0x02, 0x2c, 0xb8, 0x48, 0xf0, 0xe9, 0x90, 0x1f, 0x62, + 0x3d, 0x3e, 0xe8, 0x74, 0x3e, 0x9f, 0x7a, 0x92, 0x6c, 0x18 }}, + { 858, // 251 + { 0x46, 0x18, 0x70, 0xa7, 0xdd, 0xa5, 0x15, 0x2f, 0x69, 0x7f, + 0x00, 0x61, 0x88, 0x96, 0x5d, 0x7a, 0x34, 0x6b, 0x2c, 0x5a }}, + { 861, // 252 + { 0x46, 0x20, 0xdb, 0x8c, 0xa5, 0x7b, 0xa3, 0x0a, 0x57, 0xe7, + 0x26, 0x0c, 0x45, 0xf4, 0x0f, 0x48, 0x88, 0xb5, 0xbc, 0xbc }}, + { 864, // 253 + { 0x46, 0x2b, 0xfb, 0xf0, 0x75, 0xcb, 0xe6, 0xbe, 0x9d, 0x4c, + 0x4e, 0x75, 0x7f, 0x62, 0xd0, 0x72, 0xfa, 0x06, 0xbb, 0x04 }}, + { 867, // 254 + { 0x46, 0x50, 0x40, 0x9b, 0xd0, 0x97, 0x71, 0x97, 0xc8, 0x5c, + 0xf6, 0xa7, 0x8a, 0xd8, 0xd5, 0xe9, 0x9f, 0x79, 0x97, 0x61 }}, + { 870, // 255 + { 0x46, 0x51, 0x12, 0x93, 0x3d, 0x6c, 0x5a, 0x4f, 0x2b, 0x2f, + 0x12, 0xce, 0x86, 0xc9, 0xd0, 0xa9, 0xff, 0x1c, 0xd5, 0x15 }}, + { 873, // 256 + { 0x46, 0x85, 0xb2, 0x75, 0x61, 0x7c, 0x46, 0xfa, 0x28, 0x98, + 0x4f, 0x99, 0x6f, 0xd3, 0xbc, 0xe7, 0xd4, 0x1e, 0x7d, 0xcc }}, + { 876, // 257 + { 0x46, 0xb0, 0x89, 0xea, 0xd9, 0x0d, 0x1f, 0x39, 0x4f, 0x06, + 0xd1, 0xb5, 0x00, 0x04, 0x2e, 0x1e, 0xf0, 0xa6, 0xdc, 0x1d }}, + { 881, // 258 + { 0x47, 0x1a, 0xa3, 0xbb, 0x66, 0xc8, 0xbd, 0xb2, 0x80, 0xe6, + 0x6b, 0x55, 0x35, 0x44, 0x15, 0x66, 0xa8, 0x47, 0x84, 0x07 }}, + { 884, // 259 + { 0x47, 0x28, 0xf4, 0xae, 0xf6, 0x83, 0x8f, 0x0e, 0xd7, 0xa2, + 0x73, 0xa6, 0x9d, 0xe1, 0xf5, 0x71, 0x1f, 0x02, 0xc5, 0x5f }}, + { 901, // 260 + { 0x47, 0x71, 0x9c, 0xc7, 0x5d, 0x15, 0x62, 0x93, 0x87, 0xf0, + 0xdd, 0x86, 0xc3, 0x24, 0xbd, 0x87, 0x25, 0x1b, 0x4b, 0x99 }}, + { 904, // 261 + { 0x47, 0x82, 0x0c, 0x96, 0xb2, 0x42, 0x38, 0xe9, 0x11, 0x98, + 0xbb, 0x4b, 0x54, 0xdc, 0x3f, 0x28, 0x20, 0x17, 0x71, 0x73 }}, + { 907, // 262 + { 0x47, 0xac, 0x59, 0xa4, 0xe2, 0x7a, 0x59, 0xb9, 0x5c, 0xd6, + 0xa2, 0xe5, 0x5d, 0xa2, 0x32, 0x3e, 0x39, 0xef, 0x34, 0x04 }}, + { 910, // 263 + { 0x47, 0xb7, 0x35, 0x8c, 0x06, 0xc4, 0xfa, 0xf5, 0x91, 0x20, + 0xbf, 0x6b, 0xf1, 0x25, 0x4e, 0x2a, 0xae, 0x02, 0xff, 0x51 }}, + { 913, // 264 + { 0x47, 0xfc, 0x57, 0x99, 0x65, 0x41, 0x0c, 0xb3, 0xab, 0xcf, + 0x6a, 0x9e, 0xbe, 0x25, 0xf6, 0x51, 0x62, 0x7a, 0xc7, 0x1e }}, + { 916, // 265 + { 0x48, 0x7a, 0xae, 0x12, 0xe8, 0x10, 0xbb, 0xc5, 0xa5, 0x61, + 0xbf, 0xc1, 0x81, 0x39, 0x16, 0xcc, 0x36, 0x36, 0x47, 0x40 }}, + { 919, // 266 + { 0x48, 0x96, 0x23, 0x3d, 0x58, 0x4e, 0x80, 0xa1, 0x89, 0xeb, + 0xe9, 0x9c, 0xe6, 0xd6, 0x82, 0x9b, 0x7d, 0x95, 0xc1, 0xa0 }}, + { 922, // 267 + { 0x49, 0x18, 0x28, 0x1d, 0x9d, 0x95, 0x98, 0x2b, 0x9a, 0xc2, + 0xbe, 0x79, 0xac, 0x8a, 0x4d, 0x41, 0x01, 0x1a, 0xe2, 0x23 }}, + { 925, // 268 + { 0x49, 0x4d, 0x69, 0xcd, 0x26, 0xe3, 0x4d, 0xd9, 0x5b, 0xe6, + 0x81, 0x8e, 0x7b, 0x1a, 0xe6, 0x0e, 0x93, 0x8f, 0x9a, 0x7d }}, + { 928, // 269 + { 0x49, 0x5d, 0x5b, 0x2b, 0x99, 0xcc, 0x1b, 0x52, 0xdc, 0x12, + 0xe1, 0x64, 0x46, 0xa9, 0x04, 0xcb, 0x1f, 0xff, 0x46, 0x57 }}, + { 930, // 270 + { 0x49, 0xa1, 0x30, 0x19, 0x83, 0x25, 0xce, 0x00, 0xc6, 0x9b, + 0xd0, 0x51, 0x58, 0xad, 0x3b, 0xdd, 0x8b, 0xa4, 0xfa, 0x15 }}, + { 961, // 271 + { 0x49, 0xcb, 0xc3, 0xe3, 0xd1, 0x7f, 0x44, 0xd7, 0xa3, 0x25, + 0xf4, 0xec, 0xfe, 0x84, 0x47, 0x5a, 0xc4, 0x9d, 0x32, 0x69 }}, + { 964, // 272 + { 0x4a, 0x08, 0xb2, 0xa1, 0x02, 0x76, 0xc6, 0x93, 0xaf, 0xf5, + 0xa6, 0xc6, 0x15, 0x85, 0x19, 0xc0, 0x81, 0xa8, 0xc4, 0x95 }}, + { 967, // 273 + { 0x4a, 0x84, 0x2a, 0x1e, 0x66, 0x65, 0x8e, 0x7c, 0x2a, 0xd7, + 0xc4, 0xdf, 0xd1, 0xf4, 0x09, 0x29, 0x32, 0x3c, 0xb5, 0xe8 }}, + { 970, // 274 + { 0x4a, 0x8e, 0x40, 0x8b, 0xbd, 0x1f, 0x3f, 0x23, 0xb4, 0x03, + 0x66, 0x2e, 0x71, 0xd2, 0x83, 0x79, 0x47, 0xdd, 0x22, 0x3f }}, + { 973, // 275 + { 0x4b, 0xff, 0xee, 0x12, 0x6d, 0x66, 0x25, 0xb1, 0xa6, 0xf8, + 0xae, 0xf4, 0x86, 0x0e, 0x64, 0x5b, 0x9c, 0x82, 0x1d, 0x39 }}, + { 975, // 276 + { 0x4c, 0x35, 0x41, 0xd4, 0x77, 0xd2, 0xa2, 0xd0, 0x51, 0x00, + 0x06, 0x4a, 0x94, 0x07, 0x05, 0x70, 0x99, 0x20, 0xcc, 0x2b }}, + { 978, // 277 + { 0x4d, 0x4b, 0x5d, 0x76, 0x4d, 0xa8, 0x14, 0xd4, 0xa1, 0x28, + 0x11, 0x08, 0xa0, 0x09, 0xc0, 0xaa, 0x6f, 0xd7, 0xae, 0xe7 }}, + { 981, // 278 + { 0x4d, 0x93, 0xd8, 0xd4, 0x76, 0x0a, 0x9f, 0x06, 0x0f, 0x57, + 0xc1, 0x2b, 0x58, 0x9a, 0xba, 0xab, 0x19, 0xed, 0x4e, 0x61 }}, + { 984, // 279 + { 0x4d, 0xe6, 0x9c, 0xa9, 0xe2, 0x84, 0x0d, 0x5e, 0x00, 0x49, + 0x59, 0xe1, 0x15, 0x3b, 0x3a, 0x65, 0xd8, 0x7c, 0x82, 0xaf }}, + { 986, // 280 + { 0x4d, 0xef, 0x6a, 0x20, 0xcb, 0xa1, 0x6d, 0xea, 0x60, 0xc6, + 0x66, 0xca, 0x4e, 0x23, 0x2b, 0xac, 0x3e, 0xb1, 0x49, 0xf3 }}, + { 989, // 281 + { 0x4e, 0x37, 0x5f, 0xf1, 0x28, 0x2a, 0xe7, 0x38, 0x42, 0x90, + 0xa1, 0x11, 0x19, 0x69, 0x35, 0x10, 0x61, 0xdf, 0xf9, 0x02 }}, + { 992, // 282 + { 0x4e, 0xa8, 0x0f, 0xc8, 0xe3, 0x82, 0x7e, 0x8e, 0x4a, 0x17, + 0x59, 0xe8, 0x8d, 0x87, 0x0b, 0x5a, 0xe1, 0x28, 0x65, 0x71 }}, + { 995, // 283 + { 0x4f, 0x6f, 0xf4, 0x8a, 0x22, 0x34, 0x81, 0xc7, 0xa7, 0x27, + 0x2c, 0x80, 0x48, 0x52, 0x7d, 0x69, 0xa8, 0x01, 0x85, 0x88 }}, + { 998, // 284 + { 0x4f, 0x85, 0xe6, 0xf3, 0x26, 0x6b, 0xa3, 0x85, 0xb3, 0x4b, + 0x0f, 0xa8, 0x6c, 0x4b, 0x61, 0xc1, 0xd8, 0xc7, 0xa8, 0x64 }}, + { 1001, // 285 + { 0x4f, 0xb2, 0x19, 0x23, 0x5f, 0x16, 0x68, 0x2d, 0x4c, 0xcd, + 0x72, 0x48, 0x0b, 0x05, 0x6e, 0x20, 0x29, 0x6c, 0x2b, 0x2e }}, + { 1004, // 286 + { 0x4f, 0xca, 0x22, 0xea, 0x91, 0x24, 0xa5, 0x8d, 0x3c, 0xfa, + 0x0d, 0xb0, 0x0c, 0x81, 0x8b, 0xb1, 0x19, 0xfa, 0xae, 0x03 }}, + { 1007, // 287 + { 0x4f, 0xca, 0xbd, 0x46, 0x4e, 0x8b, 0x97, 0x86, 0x9e, 0xfb, + 0x30, 0xfb, 0xcb, 0x84, 0x60, 0x79, 0x5e, 0x94, 0x2e, 0x26 }}, + { 1010, // 288 + { 0x4f, 0xd6, 0xd9, 0xdf, 0xce, 0x48, 0x4c, 0xb5, 0x7c, 0x16, + 0x6d, 0x38, 0xc3, 0xbf, 0xaa, 0x8a, 0xbf, 0x3c, 0x32, 0x9d }}, + { 1013, // 289 + { 0x50, 0x3e, 0x35, 0xbf, 0xd5, 0x7d, 0xc1, 0x50, 0x1d, 0x86, + 0x32, 0x1b, 0x1c, 0x5e, 0xa0, 0x70, 0xab, 0xe2, 0x5e, 0x48 }}, + { 1016, // 290 + { 0x50, 0x63, 0xb0, 0xf5, 0x93, 0x93, 0x77, 0xcc, 0x8a, 0x92, + 0x9b, 0xaf, 0x9d, 0xa8, 0x85, 0x2d, 0x97, 0x35, 0xc0, 0x9c }}, + { 1019, // 291 + { 0x50, 0x63, 0xe6, 0xc2, 0xd9, 0x75, 0xd1, 0x4a, 0xc3, 0x3e, + 0x9f, 0x64, 0x66, 0xbd, 0x24, 0xb8, 0x03, 0x42, 0xef, 0xc1 }}, + { 1022, // 292 + { 0x50, 0xf1, 0x0b, 0xae, 0x24, 0x98, 0x95, 0x52, 0x1a, 0x8e, + 0x05, 0xe7, 0x96, 0x2b, 0x7f, 0x36, 0x86, 0xe8, 0xe0, 0x78 }}, + { 1027, // 293 + { 0x51, 0x02, 0x52, 0xb8, 0x71, 0x78, 0x9f, 0x0a, 0x60, 0x3e, + 0x6a, 0xd5, 0x75, 0xf2, 0xee, 0x75, 0xf9, 0xbf, 0x83, 0x5f }}, + { 1030, // 294 + { 0x51, 0xa7, 0xf4, 0x3e, 0x64, 0x39, 0xd3, 0x2f, 0x82, 0x54, + 0xf6, 0x6a, 0x3d, 0x6d, 0x1c, 0xa7, 0x40, 0x92, 0xa2, 0x14 }}, + { 1035, // 295 + { 0x52, 0x1d, 0x8c, 0x42, 0xe5, 0x9f, 0x94, 0x49, 0x3a, 0x02, + 0x60, 0x56, 0xa4, 0xf5, 0x9c, 0x93, 0x26, 0x6a, 0x05, 0xf1 }}, + { 1038, // 296 + { 0x52, 0x50, 0x7d, 0x47, 0x19, 0xed, 0xa5, 0x0c, 0x31, 0xea, + 0x69, 0xc5, 0x55, 0x36, 0x49, 0x13, 0xfe, 0x3a, 0x42, 0xe4 }}, + { 1041, // 297 + { 0x52, 0x80, 0x89, 0x0b, 0xff, 0x9b, 0xec, 0x5f, 0x0f, 0xa8, + 0x59, 0x26, 0x47, 0xd7, 0xa6, 0xa6, 0xc1, 0x51, 0xdc, 0x00 }}, + { 1051, // 298 + { 0x52, 0xaf, 0xea, 0x35, 0x5c, 0xae, 0x00, 0x78, 0x67, 0x09, + 0x73, 0xb6, 0x13, 0xb2, 0x0f, 0xd2, 0x2f, 0xd0, 0x3a, 0x5c }}, + { 1054, // 299 + { 0x53, 0xdd, 0x99, 0x1c, 0x9f, 0x94, 0x95, 0x8a, 0xe0, 0x76, + 0xe4, 0xf2, 0x94, 0xfc, 0x2d, 0x63, 0x8c, 0x10, 0x81, 0xb5 }}, + { 1057, // 300 + { 0x54, 0x7e, 0xfc, 0x4f, 0xd8, 0xea, 0xa3, 0x07, 0x7b, 0xc4, + 0x17, 0x10, 0x36, 0xa0, 0xed, 0xcc, 0x4e, 0x65, 0x83, 0x40 }}, + { 1060, // 301 + { 0x55, 0x14, 0x6f, 0x01, 0x1c, 0x24, 0x2e, 0x36, 0xa0, 0xfd, + 0x51, 0x37, 0x97, 0x66, 0x12, 0x32, 0xe9, 0x63, 0x64, 0x78 }}, + { 1063, // 302 + { 0x55, 0x98, 0x6f, 0x12, 0x7b, 0x3e, 0x33, 0xcb, 0x45, 0x13, + 0xe6, 0x41, 0x05, 0xbc, 0x22, 0xed, 0xc7, 0x0b, 0x42, 0x8e }}, + { 1065, // 303 + { 0x56, 0x0e, 0xa5, 0xd7, 0x52, 0xe7, 0xfb, 0x7a, 0x8a, 0xda, + 0x52, 0xed, 0xf9, 0x8b, 0x24, 0x7c, 0x15, 0xf3, 0xd1, 0x05 }}, + { 1070, // 304 + { 0x56, 0x4c, 0x79, 0xed, 0x06, 0xda, 0x42, 0x48, 0xd2, 0x71, + 0x50, 0xa4, 0xae, 0x62, 0xb1, 0x7c, 0xcc, 0xa9, 0x2c, 0xb1 }}, + { 1073, // 305 + { 0x56, 0x61, 0x87, 0xa2, 0x78, 0xa6, 0xe0, 0x20, 0x4a, 0x31, + 0xf7, 0x54, 0x71, 0x06, 0xc7, 0x5b, 0x04, 0x6e, 0x86, 0x56 }}, + { 1080, // 306 + { 0x56, 0xc9, 0x9c, 0x4c, 0xac, 0x21, 0x32, 0xe4, 0x94, 0xbd, + 0xb1, 0xd8, 0x90, 0x2a, 0x25, 0xbc, 0x74, 0x16, 0x7b, 0xbd }}, + { 1082, // 307 + { 0x56, 0xd4, 0xd3, 0x7d, 0x1e, 0x55, 0xdb, 0x6c, 0x77, 0x09, + 0x09, 0x06, 0x88, 0x06, 0x14, 0x51, 0x5a, 0x40, 0xe6, 0xe4 }}, + { 1084, // 308 + { 0x56, 0xd8, 0x7d, 0x56, 0x1d, 0xf2, 0x67, 0x86, 0x8e, 0xba, + 0x93, 0x9d, 0x9b, 0x27, 0xf5, 0x04, 0x95, 0x01, 0x0b, 0x5b }}, + { 1087, // 309 + { 0x57, 0x5d, 0x91, 0xff, 0x6e, 0x25, 0x06, 0xa9, 0xd6, 0xc2, + 0x59, 0x57, 0x0e, 0x40, 0xc4, 0x4b, 0x55, 0xd9, 0x47, 0x99 }}, + { 1090, // 310 + { 0x57, 0xb5, 0x4d, 0xf3, 0x61, 0x5b, 0x45, 0x51, 0x68, 0x7e, + 0x49, 0xff, 0x79, 0xcb, 0x70, 0x8e, 0xbe, 0x1a, 0x4a, 0x59 }}, + { 1093, // 311 + { 0x58, 0x6b, 0x36, 0x5c, 0x20, 0x9f, 0x7f, 0x2c, 0x60, 0xd7, + 0x46, 0xcf, 0x6a, 0x50, 0x63, 0x0f, 0xab, 0xfc, 0xbe, 0x13 }}, + { 1096, // 312 + { 0x58, 0x78, 0xe1, 0xd0, 0x62, 0xa8, 0x5f, 0x1f, 0xca, 0x6c, + 0x56, 0x1a, 0xd3, 0x1c, 0x05, 0xc7, 0x07, 0x03, 0x56, 0x23 }}, + { 1103, // 313 + { 0x58, 0x97, 0x33, 0xfc, 0xce, 0xf7, 0xdb, 0x3e, 0x81, 0xa0, + 0x4d, 0x1a, 0xd4, 0xd9, 0x28, 0x33, 0xfa, 0xb0, 0x67, 0x52 }}, + { 1106, // 314 + { 0x58, 0xbd, 0xcb, 0xe0, 0x99, 0xab, 0x13, 0xfc, 0xf5, 0x1d, + 0xce, 0xeb, 0x8b, 0xd4, 0x13, 0xc7, 0x1d, 0x88, 0x6d, 0x42 }}, + { 1109, // 315 + { 0x58, 0xc6, 0x56, 0x5f, 0x4d, 0xb3, 0x61, 0xaa, 0x71, 0x9f, + 0x2c, 0xac, 0x39, 0xa3, 0xee, 0x39, 0x84, 0x1f, 0x5f, 0xf3 }}, + { 1112, // 316 + { 0x59, 0x28, 0x53, 0xa7, 0x11, 0x97, 0x98, 0x94, 0x00, 0x8f, + 0xb4, 0x49, 0x82, 0x11, 0x0d, 0x95, 0x97, 0xf2, 0x11, 0xbd }}, + { 1115, // 317 + { 0x5a, 0x20, 0xdd, 0x49, 0x23, 0x69, 0x3c, 0xc5, 0xb5, 0x4c, + 0x0c, 0x12, 0xe5, 0x43, 0x20, 0xf1, 0x0a, 0x26, 0x18, 0x42 }}, + { 1118, // 318 + { 0x5a, 0x8f, 0x83, 0xe3, 0x93, 0x0f, 0x26, 0x2c, 0xc9, 0x32, + 0x22, 0x0d, 0xc4, 0xca, 0xd1, 0xbc, 0x3b, 0xbf, 0x57, 0x55 }}, + { 1121, // 319 + { 0x5a, 0xab, 0x9c, 0x04, 0x3a, 0xc0, 0xf1, 0x80, 0x92, 0xbc, + 0x5d, 0xe7, 0x03, 0x93, 0x02, 0x26, 0x1c, 0x98, 0x8a, 0xbf }}, + { 1124, // 320 + { 0x5b, 0x35, 0x50, 0x33, 0xcf, 0x56, 0xaf, 0x4d, 0x4a, 0xf8, + 0x18, 0x12, 0x10, 0xe6, 0xc6, 0x59, 0x78, 0x98, 0x8e, 0xb5 }}, + { 1127, // 321 + { 0x5b, 0x45, 0x94, 0x0b, 0x88, 0xf0, 0xe4, 0xd1, 0x1a, 0xb9, + 0x94, 0x12, 0xad, 0x4a, 0xa1, 0xe6, 0xd8, 0xa4, 0x66, 0xde }}, + { 1172, // 322 + { 0x5b, 0x4f, 0xe3, 0xa8, 0xfc, 0xeb, 0x04, 0x30, 0x9d, 0xf9, + 0xa3, 0x4f, 0xe0, 0x71, 0x88, 0xda, 0x17, 0x84, 0x76, 0xa9 }}, + { 1174, // 323 + { 0x5b, 0xa3, 0x8e, 0x92, 0xa4, 0x45, 0x9a, 0xab, 0x8c, 0x61, + 0x22, 0xfa, 0x63, 0xe5, 0x80, 0x9b, 0x22, 0x0f, 0xc7, 0x9f }}, + { 1177, // 324 + { 0x5b, 0xab, 0x3b, 0x8b, 0x2a, 0xb3, 0x5f, 0xf1, 0xa1, 0xaa, + 0x60, 0xe4, 0x04, 0xee, 0x9d, 0xa2, 0x30, 0x4c, 0xee, 0xa2 }}, + { 1179, // 325 + { 0x5e, 0x2e, 0xc4, 0x79, 0x40, 0xbe, 0x4b, 0xef, 0x17, 0x7e, + 0x15, 0x90, 0xbc, 0x34, 0xc8, 0x83, 0x71, 0x8c, 0xf6, 0xb9 }}, + { 1182, // 326 + { 0x5e, 0x7a, 0x70, 0x88, 0x34, 0x06, 0x1a, 0xa0, 0xac, 0xe3, + 0xcf, 0x8e, 0xd7, 0x18, 0xb5, 0xb5, 0x2e, 0x62, 0x68, 0xbb }}, + { 1185, // 327 + { 0x5e, 0xaf, 0x27, 0xe3, 0xd7, 0xea, 0x73, 0x8e, 0x6f, 0x96, + 0x0c, 0x12, 0xfd, 0xd0, 0x4c, 0x35, 0xff, 0x27, 0xc8, 0xb2 }}, + { 1188, // 328 + { 0x5e, 0xc4, 0x6a, 0x09, 0xc9, 0xa3, 0x7f, 0xcb, 0xba, 0xa9, + 0x95, 0xd6, 0x62, 0xf2, 0x66, 0x9d, 0x84, 0xab, 0x59, 0x88 }}, + { 1191, // 329 + { 0x5e, 0xdf, 0x89, 0x89, 0x43, 0xc2, 0x49, 0x72, 0x11, 0x75, + 0xb6, 0x5a, 0x31, 0x22, 0xc2, 0x68, 0x62, 0x8e, 0x48, 0xed }}, + { 1194, // 330 + { 0x5f, 0x5b, 0xe3, 0x3d, 0x91, 0x63, 0x07, 0x84, 0x83, 0x55, + 0xae, 0x9f, 0x27, 0x76, 0xd8, 0x98, 0x94, 0x5c, 0x60, 0xab }}, + { 1197, // 331 + { 0x5f, 0x74, 0xd7, 0xc4, 0x12, 0x60, 0x03, 0xb3, 0x6b, 0x89, + 0x2d, 0x12, 0x4c, 0xb9, 0xfd, 0xc1, 0x7f, 0x55, 0xf7, 0x84 }}, + { 1200, // 332 + { 0x5f, 0xa7, 0x47, 0xc2, 0x12, 0xda, 0x99, 0x8a, 0x2c, 0x96, + 0x0e, 0x93, 0xed, 0x3a, 0x41, 0x7c, 0x11, 0x7c, 0x19, 0x40 }}, + { 1217, // 333 + { 0x60, 0x38, 0x0a, 0x34, 0xd3, 0x47, 0x78, 0xd2, 0x54, 0xc3, + 0x5b, 0x70, 0x0a, 0xcf, 0x02, 0xb7, 0x93, 0x0f, 0x11, 0xe1 }}, + { 1220, // 334 + { 0x60, 0x8f, 0x35, 0x1a, 0xb5, 0xa6, 0x5a, 0xe9, 0xf7, 0x8b, + 0x33, 0xe5, 0xea, 0x52, 0xe5, 0xe6, 0xbf, 0xd1, 0x24, 0xc8 }}, + { 1223, // 335 + { 0x60, 0xbc, 0xa0, 0xcf, 0xc9, 0xf5, 0x1f, 0xeb, 0xc5, 0xde, + 0xd3, 0xc1, 0xb3, 0xf7, 0xef, 0x04, 0x28, 0x75, 0xe9, 0x2c }}, + { 1226, // 336 + { 0x61, 0x0d, 0xc0, 0x41, 0xc3, 0x5a, 0x21, 0xe5, 0x44, 0x3e, + 0xa9, 0x80, 0x8b, 0x8a, 0x08, 0x4c, 0x7c, 0x1d, 0x0e, 0x70 }}, + { 1229, // 337 + { 0x61, 0x23, 0x1d, 0x00, 0x53, 0xe0, 0x5d, 0x3a, 0xd8, 0x68, + 0x98, 0x63, 0xdb, 0x66, 0x00, 0x06, 0x51, 0x0b, 0x40, 0x45 }}, + { 1238, // 338 + { 0x61, 0x40, 0x71, 0xcd, 0xed, 0xd0, 0xba, 0x25, 0x71, 0x0c, + 0x4c, 0xf5, 0x5d, 0xd9, 0x40, 0xde, 0x27, 0x8b, 0xb2, 0x02 }}, + { 1241, // 339 + { 0x61, 0x91, 0x02, 0x7e, 0xe3, 0x54, 0xc1, 0xf6, 0x8b, 0xd8, + 0x41, 0x18, 0xb1, 0x1a, 0x4b, 0x28, 0x1d, 0x72, 0xd2, 0x7c }}, + { 1244, // 340 + { 0x61, 0x91, 0xd3, 0x1e, 0x48, 0xc2, 0x75, 0xcb, 0x84, 0x4b, + 0xa3, 0x69, 0xb8, 0x75, 0x8d, 0x2b, 0xae, 0x8f, 0xd5, 0x11 }}, + { 1247, // 341 + { 0x61, 0x9a, 0x17, 0x20, 0x2a, 0x5b, 0xe8, 0xf1, 0xab, 0x38, + 0xd8, 0xf3, 0x89, 0x7d, 0xfc, 0x03, 0xe7, 0xbe, 0xc3, 0x88 }}, + { 1252, // 342 + { 0x61, 0xed, 0x5a, 0xf4, 0xfa, 0xfb, 0xe0, 0xb4, 0x06, 0x24, + 0x08, 0x5d, 0x80, 0xc5, 0xab, 0x97, 0xa6, 0x28, 0x82, 0xb1 }}, + { 1255, // 343 + { 0x62, 0x45, 0x1c, 0xee, 0x08, 0x45, 0xdd, 0x9b, 0xa4, 0x64, + 0xd2, 0x59, 0x28, 0x6e, 0x71, 0x0a, 0x5e, 0x53, 0x18, 0xbd }}, + { 1257, // 344 + { 0x62, 0x6b, 0xc4, 0x77, 0x87, 0x9e, 0xa5, 0xfa, 0x71, 0xa4, + 0x78, 0x49, 0xdc, 0xed, 0xd0, 0xd7, 0x09, 0x06, 0xa5, 0x94 }}, + { 1260, // 345 + { 0x63, 0x61, 0x22, 0x3a, 0x9e, 0x23, 0x52, 0x87, 0x05, 0x3c, + 0xb1, 0x7a, 0xef, 0xa3, 0xa9, 0x33, 0x19, 0x6c, 0x0c, 0xfe }}, + { 1263, // 346 + { 0x63, 0x7d, 0x7c, 0xf1, 0x4d, 0x00, 0xeb, 0xdf, 0xa2, 0xb2, + 0x35, 0x09, 0x4f, 0x72, 0xfb, 0xad, 0x37, 0x98, 0x92, 0xd3 }}, + { 1266, // 347 + { 0x63, 0x88, 0xbb, 0xd0, 0x12, 0x92, 0xff, 0x74, 0xf2, 0x94, + 0x73, 0x58, 0x9d, 0x5a, 0xef, 0x7d, 0x16, 0x3a, 0x6d, 0xca }}, + { 1297, // 348 + { 0x63, 0xe0, 0xb8, 0x63, 0x13, 0x49, 0x2b, 0xab, 0x54, 0xa3, + 0x66, 0x8c, 0x00, 0x8a, 0x25, 0xc3, 0xd7, 0x73, 0x7d, 0x6d }}, + { 1300, // 349 + { 0x64, 0x32, 0x02, 0x7f, 0x4d, 0x56, 0xcd, 0xb1, 0x72, 0xfd, + 0x99, 0x49, 0xe0, 0xd2, 0x71, 0x57, 0x64, 0x23, 0x0f, 0x28 }}, + { 1303, // 350 + { 0x64, 0x48, 0xfe, 0x8c, 0x7f, 0x4c, 0x25, 0x49, 0x65, 0x05, + 0xa1, 0x32, 0x70, 0x82, 0xaf, 0xd4, 0x7c, 0xb2, 0x76, 0x0c }}, + { 1306, // 351 + { 0x64, 0x4e, 0x8b, 0x59, 0x32, 0x2c, 0x10, 0x79, 0xe2, 0x4b, + 0xce, 0x20, 0x91, 0x8c, 0xfd, 0x03, 0x29, 0x0a, 0x15, 0x57 }}, + { 1311, // 352 + { 0x64, 0x73, 0x16, 0x0d, 0x2b, 0x13, 0xa2, 0x64, 0x96, 0xa3, + 0xf3, 0x28, 0x8b, 0x35, 0x14, 0x63, 0x05, 0x40, 0xaa, 0xa6 }}, + { 1314, // 353 + { 0x64, 0xe4, 0x01, 0xdb, 0x0d, 0x32, 0x54, 0x10, 0x94, 0x3e, + 0xa4, 0xb3, 0x5d, 0x5b, 0x9d, 0x0e, 0x62, 0x90, 0x7a, 0xd8 }}, + { 1317, // 354 + { 0x65, 0x27, 0x8a, 0x37, 0x28, 0x31, 0x55, 0x4e, 0x46, 0x08, + 0x81, 0x92, 0xc6, 0x86, 0xe8, 0xde, 0xfa, 0x76, 0x14, 0x13 }}, + { 1320, // 355 + { 0x65, 0x44, 0xeb, 0x6a, 0xb2, 0x36, 0x47, 0x0a, 0x79, 0xf1, + 0x6b, 0x4f, 0xc1, 0x06, 0xc7, 0x29, 0x29, 0x06, 0x86, 0x50 }}, + { 1322, // 356 + { 0x65, 0x68, 0xa7, 0xe4, 0x0d, 0xa2, 0x78, 0xf2, 0xdd, 0x1c, + 0xb5, 0xbd, 0xe3, 0xca, 0xa9, 0x92, 0x77, 0x78, 0xc2, 0xd8 }}, + { 1331, // 357 + { 0x65, 0x86, 0xca, 0x1f, 0xc3, 0xa9, 0x5e, 0xf0, 0xc6, 0x10, + 0x2c, 0x37, 0xf4, 0xa1, 0xcf, 0xb2, 0xf8, 0xd6, 0xdc, 0xd8 }}, + { 1334, // 358 + { 0x65, 0xa7, 0xf2, 0xef, 0xe8, 0xc0, 0xeb, 0x8e, 0xe9, 0x82, + 0x55, 0x37, 0x9f, 0x91, 0x52, 0xa0, 0x6a, 0x95, 0xaf, 0x82 }}, + { 1337, // 359 + { 0x65, 0xd6, 0xbf, 0x99, 0xca, 0xc0, 0x44, 0xc0, 0x80, 0x67, + 0x06, 0x7e, 0x73, 0x93, 0x7d, 0xfd, 0x3e, 0x47, 0x65, 0x9c }}, + { 1340, // 360 + { 0x66, 0x2c, 0x1e, 0x0d, 0x0a, 0x23, 0xf8, 0xd2, 0x68, 0x4b, + 0xfb, 0x98, 0x2a, 0x05, 0x39, 0xfc, 0xfe, 0x77, 0x01, 0xd8 }}, + { 1343, // 361 + { 0x66, 0x2f, 0xcb, 0x38, 0x61, 0x62, 0x87, 0xe8, 0x7c, 0xfe, + 0x0a, 0x06, 0x01, 0x07, 0xd6, 0xca, 0xf5, 0x84, 0x60, 0x95 }}, + { 1348, // 362 + { 0x66, 0x75, 0x6e, 0xba, 0xf5, 0x96, 0xe1, 0x54, 0x4d, 0xbf, + 0x90, 0x1f, 0xc2, 0x29, 0x0f, 0x33, 0x39, 0x36, 0xaf, 0x92 }}, + { 1351, // 363 + { 0x67, 0x80, 0x4c, 0x72, 0x28, 0xdd, 0x96, 0x28, 0x38, 0xbf, + 0x1c, 0x3f, 0x33, 0x39, 0x57, 0x92, 0x2b, 0xb2, 0xfa, 0x5b }}, + { 1373, // 364 + { 0x67, 0x8e, 0x96, 0x77, 0x9b, 0xc3, 0xb4, 0x61, 0x63, 0x89, + 0xf5, 0xf6, 0x59, 0xef, 0xf9, 0xdb, 0x8f, 0x0f, 0x94, 0x4e }}, + { 1376, // 365 + { 0x67, 0xc5, 0xaa, 0xf9, 0x3c, 0xab, 0x0f, 0xbb, 0x0b, 0xce, + 0x4e, 0x53, 0x4d, 0x7f, 0x51, 0x97, 0x60, 0x60, 0xab, 0x81 }}, + { 1379, // 366 + { 0x68, 0x2e, 0xf8, 0xe1, 0x10, 0x7d, 0x29, 0x44, 0x42, 0xf8, + 0xd4, 0xa4, 0x83, 0xac, 0xbc, 0xe6, 0x99, 0xa0, 0x6f, 0x86 }}, + { 1382, // 367 + { 0x68, 0xba, 0x63, 0x00, 0xfe, 0x68, 0xe4, 0x01, 0x07, 0x85, + 0x02, 0xc6, 0xfb, 0xdd, 0x30, 0x2e, 0x95, 0xec, 0x19, 0x9c }}, + { 1387, // 368 + { 0x68, 0xe7, 0xff, 0xd8, 0x98, 0x63, 0x0b, 0xe3, 0x07, 0x07, + 0x18, 0xc9, 0xf3, 0xdc, 0xfe, 0x8f, 0x3d, 0x26, 0x6c, 0xbf }}, + { 1390, // 369 + { 0x69, 0x06, 0x2d, 0xe2, 0xfc, 0xd0, 0x16, 0x73, 0x38, 0x6c, + 0x34, 0x3e, 0xe4, 0x5e, 0x5c, 0x14, 0x19, 0x20, 0x12, 0xbc }}, + { 1393, // 370 + { 0x69, 0x10, 0x9e, 0x99, 0x22, 0xf6, 0x8d, 0xe6, 0xf9, 0x3c, + 0x9b, 0xdd, 0x45, 0x90, 0xed, 0xb0, 0x79, 0xaa, 0xfc, 0xb2 }}, + { 1396, // 371 + { 0x69, 0x49, 0x35, 0x89, 0x22, 0x73, 0xb1, 0x42, 0x8d, 0xb3, + 0x2b, 0x00, 0xf8, 0xc9, 0x5f, 0x70, 0x3e, 0x8e, 0x8a, 0x99 }}, + { 1399, // 372 + { 0x69, 0x92, 0x3a, 0x2f, 0x43, 0xb6, 0x7e, 0xdf, 0xe5, 0x88, + 0xd6, 0xd4, 0x36, 0x88, 0x40, 0x16, 0x0c, 0xfb, 0x6b, 0x21 }}, + { 1408, // 373 + { 0x6a, 0x77, 0x87, 0x72, 0x3f, 0x33, 0x14, 0x83, 0x2f, 0xc4, + 0x2a, 0x31, 0xe8, 0x04, 0x61, 0x42, 0x62, 0x99, 0x51, 0xb0 }}, + { 1411, // 374 + { 0x6a, 0x8f, 0x24, 0x46, 0xe6, 0xb0, 0xc7, 0x44, 0x2e, 0x38, + 0xde, 0x67, 0xf1, 0x91, 0xa8, 0x6b, 0x27, 0x93, 0x41, 0x51 }}, + { 1413, // 375 + { 0x6b, 0x91, 0xb3, 0x32, 0x54, 0x41, 0x7d, 0x11, 0xd2, 0x58, + 0x3e, 0x92, 0xc7, 0x1f, 0x5c, 0x52, 0x55, 0xca, 0x81, 0x46 }}, + { 1416, // 376 + { 0x6b, 0xdc, 0x92, 0xbd, 0xe7, 0x87, 0xeb, 0x11, 0xba, 0xd5, + 0x57, 0xf0, 0xc2, 0xe5, 0x0a, 0xa7, 0x6e, 0xf7, 0x7b, 0xd4 }}, + { 1420, // 377 + { 0x6c, 0x59, 0xb5, 0x63, 0xa0, 0xec, 0xc0, 0x56, 0xc5, 0x32, + 0xc5, 0xbc, 0x8d, 0x2b, 0x99, 0xb9, 0x7e, 0x1d, 0xb8, 0xa3 }}, + { 1423, // 378 + { 0x6c, 0xa0, 0x66, 0x39, 0x56, 0xd9, 0xec, 0x33, 0xad, 0x6b, + 0x19, 0x61, 0xaa, 0x86, 0x33, 0xd3, 0x6c, 0x08, 0x6a, 0x13 }}, + { 1426, // 379 + { 0x6c, 0xf7, 0x3f, 0x15, 0x1a, 0x17, 0x17, 0xbd, 0x99, 0x77, + 0x78, 0x25, 0xbd, 0x4d, 0x49, 0x92, 0x96, 0xac, 0x29, 0xee }}, + { 1515, // 380 + { 0x6d, 0x2b, 0x7d, 0xb0, 0xac, 0x20, 0x73, 0xd0, 0x46, 0xf6, + 0x23, 0xb1, 0x25, 0x06, 0xae, 0xfd, 0x48, 0x27, 0x10, 0x55 }}, + { 1518, // 381 + { 0x6d, 0x63, 0x54, 0x83, 0xb9, 0xc7, 0xbe, 0x1d, 0x61, 0x0b, + 0x93, 0x02, 0xd7, 0x35, 0x9a, 0x22, 0xc0, 0xa0, 0x12, 0xca }}, + { 1521, // 382 + { 0x6d, 0xb3, 0xf3, 0x1c, 0xc0, 0x3f, 0xf8, 0x71, 0x10, 0x5c, + 0xef, 0x88, 0x03, 0xbb, 0xc3, 0xe6, 0x24, 0x05, 0x1d, 0xfd }}, + { 1524, // 383 + { 0x6d, 0xe1, 0x63, 0x66, 0xd1, 0xa5, 0xef, 0xa0, 0x92, 0xcc, + 0x3c, 0x80, 0x3a, 0x32, 0x04, 0xd6, 0xd5, 0xe6, 0x73, 0x8b }}, + { 1527, // 384 + { 0x6e, 0x26, 0xc3, 0x56, 0x60, 0x69, 0xf1, 0xc6, 0xa0, 0xe6, + 0x43, 0xda, 0x6f, 0xf8, 0x97, 0x13, 0xc3, 0x24, 0x8c, 0x25 }}, + { 1530, // 385 + { 0x6e, 0x47, 0x7f, 0xb2, 0xe9, 0xeb, 0xdd, 0x4a, 0xde, 0x46, + 0x6f, 0xc7, 0x5f, 0x8c, 0x40, 0x00, 0xf1, 0xc9, 0xa3, 0xd2 }}, + { 1533, // 386 + { 0x6e, 0x4a, 0xfc, 0x0c, 0x81, 0xf2, 0xe4, 0x1c, 0xb2, 0xd8, + 0x24, 0x7f, 0xd5, 0x82, 0x37, 0xd0, 0xf5, 0xd8, 0xc9, 0xd0 }}, + { 1536, // 387 + { 0x6e, 0xa2, 0xa9, 0x60, 0xe6, 0x31, 0x0e, 0x78, 0x1c, 0x38, + 0x48, 0x39, 0x15, 0x0e, 0xb6, 0x5b, 0x72, 0xee, 0x08, 0xd5 }}, + { 1539, // 388 + { 0x6e, 0xa6, 0x59, 0x76, 0xcd, 0x1c, 0xa8, 0xf9, 0x20, 0x86, + 0xf0, 0xf5, 0xed, 0x33, 0x41, 0xb4, 0xd5, 0x62, 0x39, 0xee }}, + { 1542, // 389 + { 0x6e, 0xba, 0x4a, 0xc5, 0x60, 0xa9, 0x50, 0x5c, 0x12, 0xec, + 0xeb, 0x38, 0x12, 0xf3, 0x9d, 0xfd, 0xae, 0x5f, 0x85, 0xa2 }}, + { 1549, // 390 + { 0x6e, 0xe3, 0x15, 0x80, 0xcc, 0xa8, 0x61, 0xd8, 0x7b, 0xdd, + 0xdd, 0xcc, 0x3b, 0x17, 0x2e, 0x76, 0x80, 0xb7, 0x85, 0x6b }}, + { 1552, // 391 + { 0x6f, 0x11, 0xec, 0x5d, 0xc2, 0x0c, 0x2f, 0xe9, 0xe1, 0x7e, + 0x38, 0x32, 0x19, 0xc9, 0xf5, 0x99, 0xfc, 0x1d, 0xfa, 0x4b }}, + { 1555, // 392 + { 0x6f, 0xd2, 0xf2, 0x96, 0x0c, 0xae, 0xfc, 0x20, 0x67, 0xd9, + 0x57, 0xca, 0x04, 0xac, 0x4b, 0xd8, 0x69, 0x46, 0xcf, 0x2e }}, + { 1558, // 393 + { 0x70, 0x09, 0xf4, 0x7a, 0xdb, 0xf0, 0x14, 0xc9, 0x74, 0x72, + 0xe9, 0x77, 0xce, 0xaa, 0xb4, 0x1d, 0xd7, 0x30, 0x6f, 0xab }}, + { 1563, // 394 + { 0x70, 0x0c, 0x20, 0xf6, 0x38, 0x06, 0x7e, 0x6c, 0xaf, 0xbc, + 0x7f, 0x49, 0x1e, 0xd6, 0x99, 0xc1, 0x52, 0xde, 0x68, 0x05 }}, + { 1567, // 395 + { 0x70, 0x0c, 0xb9, 0xe4, 0x59, 0xca, 0x15, 0xf4, 0x92, 0x14, + 0x37, 0x5d, 0x35, 0xeb, 0x32, 0xc4, 0x72, 0x19, 0x7e, 0x86 }}, + { 1570, // 396 + { 0x70, 0x91, 0x0a, 0x7e, 0x80, 0x2f, 0x4e, 0x9e, 0x16, 0x80, + 0x75, 0x43, 0x94, 0x5d, 0x45, 0x57, 0xc5, 0x27, 0x8d, 0x17 }}, + { 1572, // 397 + { 0x70, 0xa2, 0x0d, 0xbe, 0xcc, 0xf2, 0x34, 0x4b, 0xd5, 0xee, + 0x2e, 0xc1, 0x8f, 0x07, 0x6c, 0xc9, 0x9c, 0x7c, 0x61, 0x1e }}, + { 1575, // 398 + { 0x70, 0xb0, 0xe9, 0xcc, 0x01, 0x3b, 0xc4, 0xef, 0xf6, 0xa5, + 0x88, 0x82, 0x2b, 0x82, 0xe7, 0xd6, 0xd5, 0xf4, 0xb4, 0x8d }}, + { 1578, // 399 + { 0x70, 0xc7, 0x36, 0x3a, 0x61, 0x5f, 0xda, 0x69, 0x6c, 0x72, + 0x9d, 0x33, 0xac, 0x81, 0xd1, 0x0d, 0xec, 0xa0, 0xb1, 0x4d }}, + { 1581, // 400 + { 0x70, 0xfa, 0xbf, 0xa0, 0xd9, 0x78, 0x23, 0xd4, 0xe4, 0x36, + 0xd0, 0x67, 0x52, 0x93, 0xf9, 0x68, 0x82, 0x6e, 0xa8, 0xa0 }}, + { 1584, // 401 + { 0x71, 0x34, 0x60, 0xf6, 0x91, 0xc1, 0x7e, 0x0e, 0x74, 0x82, + 0xea, 0xe8, 0xbb, 0xa6, 0xb2, 0xa9, 0x56, 0x6b, 0xaa, 0xcf }}, + { 1587, // 402 + { 0x71, 0x39, 0x2a, 0xb2, 0x3f, 0xdd, 0x31, 0xd2, 0x9c, 0xb9, + 0xb6, 0x1d, 0x60, 0xcd, 0x93, 0x02, 0x7c, 0x4a, 0xa0, 0xbd }}, + { 1590, // 403 + { 0x71, 0x55, 0x65, 0x9a, 0xff, 0xfd, 0x98, 0x91, 0x7f, 0xe7, + 0x81, 0x22, 0xb2, 0xfe, 0xc7, 0xc6, 0x5e, 0x25, 0x80, 0x7a }}, + { 1599, // 404 + { 0x71, 0xa3, 0x44, 0xcb, 0x80, 0x51, 0xf5, 0xa7, 0x88, 0x69, + 0x01, 0x42, 0x03, 0x84, 0x7f, 0x96, 0xb7, 0xb9, 0x0b, 0xe4 }}, + { 1602, // 405 + { 0x71, 0xe9, 0x8e, 0x09, 0x6e, 0x08, 0x7e, 0x7b, 0x14, 0xf2, + 0xd9, 0x83, 0xd1, 0x16, 0x73, 0x46, 0x69, 0xa4, 0x6b, 0x0c }}, + { 1605, // 406 + { 0x72, 0xa6, 0x86, 0x18, 0xa1, 0x90, 0x0b, 0x12, 0x97, 0xa8, + 0xd9, 0x95, 0xe0, 0x33, 0xf7, 0x11, 0x57, 0x6b, 0x46, 0xf0 }}, + { 1608, // 407 + { 0x73, 0x63, 0x31, 0x91, 0x1b, 0x03, 0xe8, 0x73, 0x42, 0x0a, + 0xab, 0x36, 0x87, 0xb1, 0xa0, 0xa3, 0xa8, 0x5d, 0x09, 0xcf }}, + { 1611, // 408 + { 0x73, 0xf0, 0x8f, 0x73, 0xef, 0x28, 0x61, 0x96, 0x91, 0x38, + 0xc7, 0x9d, 0x9c, 0xb3, 0x4a, 0x7a, 0xc1, 0x6c, 0xd4, 0xa0 }}, + { 1614, // 409 + { 0x74, 0x43, 0xd2, 0x5d, 0xa0, 0xce, 0x1d, 0xd4, 0x34, 0x7f, + 0x1d, 0x51, 0x77, 0x26, 0x49, 0xdb, 0x33, 0x9c, 0x5b, 0x86 }}, + { 1617, // 410 + { 0x74, 0x53, 0x50, 0x5d, 0x10, 0xdd, 0x70, 0x03, 0x7e, 0xd4, + 0x8a, 0x68, 0xf5, 0x1f, 0xc0, 0xb3, 0xe5, 0xa4, 0xa0, 0x92 }}, + { 1620, // 411 + { 0x74, 0x76, 0xfb, 0xf5, 0xa3, 0xff, 0x70, 0x4f, 0x97, 0x65, + 0xb4, 0xd3, 0x79, 0x07, 0x37, 0x88, 0xfc, 0x1e, 0x60, 0xd2 }}, + { 1626, // 412 + { 0x74, 0x86, 0x2f, 0xf4, 0xa5, 0x6a, 0x9c, 0xac, 0x0d, 0x98, + 0xb1, 0x93, 0x5c, 0x3c, 0xde, 0x10, 0xda, 0xd3, 0x88, 0x4b }}, + { 1629, // 413 + { 0x74, 0xb4, 0x78, 0x55, 0x46, 0xc8, 0x5e, 0x61, 0x5e, 0x49, + 0xc0, 0x9f, 0xb2, 0xfd, 0xb8, 0x9c, 0x53, 0x01, 0xd7, 0x9b }}, + { 1632, // 414 + { 0x75, 0x78, 0x55, 0x7b, 0xfd, 0x48, 0x54, 0xfe, 0xc5, 0x63, + 0xce, 0x92, 0xf4, 0xb1, 0x37, 0x56, 0x16, 0xb7, 0x70, 0x43 }}, + { 1635, // 415 + { 0x75, 0xb9, 0x47, 0x55, 0x21, 0xe7, 0x84, 0x96, 0x80, 0xe0, + 0x50, 0x25, 0xc4, 0x81, 0x9c, 0xe4, 0x9b, 0xd0, 0xf2, 0x60 }}, + { 1639, // 416 + { 0x75, 0xca, 0x3e, 0x92, 0xc0, 0x6c, 0x36, 0x55, 0x5d, 0xea, + 0x9b, 0x4c, 0x89, 0xf2, 0x98, 0x0a, 0xee, 0x99, 0xfa, 0x82 }}, + { 1642, // 417 + { 0x75, 0xe8, 0x2d, 0x75, 0x14, 0x3f, 0x9c, 0x2b, 0xf0, 0x2c, + 0xaf, 0x74, 0x57, 0xbd, 0x5d, 0xec, 0x0e, 0x0f, 0x41, 0xee }}, + { 1645, // 418 + { 0x76, 0x22, 0x02, 0x93, 0x65, 0xdf, 0xbb, 0xdc, 0x1e, 0x6e, + 0x18, 0xf8, 0x4a, 0x18, 0xd6, 0x38, 0x48, 0x8f, 0xd3, 0x99 }}, + { 1648, // 419 + { 0x76, 0xae, 0x3f, 0xeb, 0x17, 0x5f, 0x55, 0x06, 0x6b, 0xb6, + 0xe2, 0x5b, 0xad, 0x5e, 0x3f, 0xe5, 0x97, 0xd0, 0x57, 0x1b }}, + { 1651, // 420 + { 0x76, 0xba, 0x0f, 0x96, 0x0c, 0xa8, 0x26, 0x4e, 0xbc, 0x2b, + 0x06, 0x26, 0xaf, 0xcb, 0xee, 0x42, 0x02, 0xfb, 0xe1, 0xdb }}, + { 1656, // 421 + { 0x76, 0xde, 0x93, 0xc0, 0xd7, 0x0c, 0x8a, 0x9a, 0x51, 0xbf, + 0xa8, 0x0e, 0x91, 0x21, 0xb0, 0x8b, 0xe4, 0x25, 0x61, 0xa7 }}, + { 1659, // 422 + { 0x77, 0x2c, 0x84, 0x17, 0xda, 0xb1, 0xf6, 0xe4, 0x93, 0x44, + 0x3a, 0x99, 0xe4, 0x9e, 0xde, 0x34, 0x29, 0xec, 0x93, 0x12 }}, + { 1662, // 423 + { 0x77, 0x48, 0x49, 0xde, 0xba, 0x83, 0xfc, 0x7e, 0xaf, 0xf4, + 0xf4, 0x6c, 0xc3, 0xe7, 0x0a, 0xbb, 0xaf, 0x41, 0x5c, 0xb0 }}, + { 1665, // 424 + { 0x77, 0x87, 0x32, 0x78, 0x7e, 0x6f, 0x1b, 0x4b, 0x21, 0xff, + 0xc5, 0xd8, 0x4c, 0x99, 0xa1, 0x1b, 0xf3, 0x69, 0x8a, 0xef }}, + { 1668, // 425 + { 0x77, 0xc9, 0x71, 0xef, 0x67, 0x75, 0xb6, 0x63, 0x94, 0x00, + 0xe1, 0x1d, 0x48, 0xf7, 0xe0, 0xa3, 0xb3, 0x1a, 0xef, 0x96 }}, + { 1671, // 426 + { 0x78, 0x3a, 0xa9, 0x55, 0xcb, 0x39, 0x55, 0xb7, 0x1c, 0x63, + 0x0f, 0x98, 0xdf, 0xaf, 0x97, 0x9c, 0x60, 0x1b, 0x71, 0x69 }}, + { 1674, // 427 + { 0x78, 0x67, 0x50, 0x28, 0xf6, 0xcb, 0x81, 0x2a, 0xa6, 0x8c, + 0x1a, 0xd4, 0x8f, 0x3e, 0x25, 0xcd, 0x0d, 0xd1, 0xcc, 0xd6 }}, + { 1677, // 428 + { 0x78, 0xfd, 0x1a, 0x98, 0xce, 0xf1, 0xeb, 0x20, 0x1b, 0xff, + 0x63, 0xd7, 0x95, 0x7c, 0xd1, 0x83, 0x7b, 0x34, 0xa6, 0x3e }}, + { 1680, // 429 + { 0x79, 0x05, 0x40, 0x9b, 0x91, 0x66, 0x95, 0xe8, 0x02, 0xb0, + 0x59, 0x48, 0x81, 0xad, 0x73, 0xcd, 0x2f, 0x23, 0x69, 0x9a }}, + { 1683, // 430 + { 0x79, 0x15, 0xd1, 0x8d, 0xb3, 0xdc, 0x45, 0x23, 0xa7, 0xbc, + 0xea, 0x90, 0xe2, 0x27, 0x51, 0x62, 0x0c, 0x49, 0xd8, 0x3c }}, + { 1686, // 431 + { 0x79, 0x3e, 0xa1, 0xed, 0x06, 0xeb, 0xa8, 0x02, 0xee, 0x6d, + 0x7d, 0x4e, 0xe2, 0x31, 0x57, 0xeb, 0x11, 0x32, 0x2e, 0xf4 }}, + { 1695, // 432 + { 0x79, 0xca, 0x0f, 0x7c, 0x8e, 0x01, 0xb5, 0x8b, 0x41, 0x2e, + 0xfa, 0x49, 0x56, 0x76, 0xa0, 0xde, 0x20, 0x02, 0x74, 0x87 }}, + { 1698, // 433 + { 0x79, 0xe6, 0x81, 0x70, 0xa6, 0x31, 0x58, 0x81, 0xe1, 0x60, + 0xeb, 0x4b, 0xb3, 0x0b, 0x25, 0x3c, 0x6c, 0x40, 0xcf, 0x76 }}, + { 1701, // 434 + { 0x7a, 0xb7, 0x37, 0x18, 0x8a, 0xc7, 0xfe, 0x50, 0xa2, 0xd3, + 0x15, 0x0b, 0xf5, 0xc7, 0xbc, 0xf3, 0xa1, 0x3b, 0x4a, 0xa6 }}, + { 1708, // 435 + { 0x7b, 0x60, 0x66, 0xb2, 0x07, 0x66, 0x10, 0x80, 0xe9, 0x9d, + 0xd7, 0x73, 0xec, 0xa6, 0xa2, 0x99, 0x83, 0x05, 0xc6, 0x70 }}, + { 1712, // 436 + { 0x7b, 0x96, 0x4d, 0x76, 0xe1, 0xf4, 0xe9, 0x5b, 0x5e, 0xa3, + 0xd0, 0x43, 0x26, 0xc9, 0x3f, 0xfa, 0xbb, 0x69, 0x90, 0x95 }}, + { 1715, // 437 + { 0x7c, 0x4b, 0x6d, 0x44, 0xe3, 0x90, 0x07, 0xee, 0xe6, 0x08, + 0x5c, 0x68, 0x8f, 0x3b, 0x25, 0x0e, 0xf3, 0xdc, 0x3b, 0xc9 }}, + { 1717, // 438 + { 0x7c, 0x79, 0x28, 0x40, 0x53, 0xd3, 0x8f, 0xb3, 0x69, 0x59, + 0x5f, 0xea, 0xbf, 0x73, 0x41, 0x66, 0xfc, 0xf3, 0xca, 0xd6 }}, + { 1720, // 439 + { 0x7c, 0xf7, 0x0f, 0xf3, 0xb1, 0x54, 0x54, 0xd5, 0x5e, 0x91, + 0x47, 0xd3, 0xd5, 0xe5, 0xce, 0xb4, 0x0b, 0x76, 0x83, 0x52 }}, + { 1723, // 440 + { 0x7c, 0xfb, 0xe1, 0x21, 0x06, 0x4b, 0x14, 0xfd, 0x5f, 0x9e, + 0x37, 0x37, 0xb7, 0x1a, 0x42, 0x20, 0x51, 0x2e, 0xe4, 0xf9 }}, + { 1726, // 441 + { 0x7c, 0xff, 0x86, 0xcc, 0x27, 0x89, 0x9b, 0x39, 0xb4, 0x2e, + 0x92, 0x22, 0xad, 0x8f, 0x49, 0x3c, 0x64, 0x16, 0x77, 0x80 }}, + { 1729, // 442 + { 0x7d, 0x14, 0xaf, 0x09, 0x7d, 0x66, 0xa4, 0x70, 0xde, 0x23, + 0xfb, 0x3d, 0x92, 0x8f, 0xdb, 0xfd, 0x9e, 0xe0, 0x47, 0x3b }}, + { 1732, // 443 + { 0x7e, 0x68, 0x62, 0xf9, 0x1f, 0x26, 0x0a, 0xdb, 0x49, 0x45, + 0x9d, 0x2a, 0x24, 0x4f, 0xff, 0x06, 0x62, 0xcb, 0xe9, 0x27 }}, + { 1735, // 444 + { 0x7e, 0x7d, 0xb1, 0x6e, 0x9c, 0x79, 0x57, 0xf4, 0xdd, 0x82, + 0x44, 0xf7, 0x62, 0x5a, 0xc8, 0x7c, 0xfc, 0x56, 0x6c, 0xeb }}, + { 1738, // 445 + { 0x7e, 0x84, 0x31, 0xa2, 0xe2, 0xe3, 0xd8, 0x6c, 0xc4, 0xda, + 0x22, 0x79, 0x7b, 0xfb, 0xba, 0x46, 0xa6, 0xb8, 0x40, 0xa8 }}, + { 1741, // 446 + { 0x7e, 0x8a, 0x10, 0x9d, 0x0c, 0x74, 0x57, 0x74, 0xac, 0xc4, + 0x4e, 0x72, 0xec, 0x6d, 0xdc, 0xd1, 0x1e, 0xdb, 0xf7, 0xb8 }}, + { 1744, // 447 + { 0x7e, 0xb3, 0x18, 0x7e, 0xaf, 0x66, 0x75, 0xfe, 0xda, 0x4f, + 0x3a, 0x1a, 0xe1, 0xf2, 0x93, 0x2a, 0x88, 0x03, 0xb5, 0x01 }}, + { 1747, // 448 + { 0x7e, 0xd7, 0xcd, 0x51, 0xa1, 0xd6, 0xf6, 0xe7, 0xb5, 0x9d, + 0x6b, 0xcb, 0x83, 0xe7, 0xf1, 0xaf, 0x7f, 0xa2, 0x7e, 0xca }}, + { 1750, // 449 + { 0x7f, 0x05, 0xad, 0x10, 0xef, 0x59, 0xad, 0xc2, 0x35, 0x32, + 0x5c, 0x6d, 0x31, 0x0c, 0xe4, 0x1c, 0x25, 0xbc, 0x82, 0xf5 }}, + { 1753, // 450 + { 0x7f, 0x61, 0x5a, 0xb4, 0xa6, 0xf9, 0xe6, 0x6a, 0x2f, 0xe9, + 0x0b, 0x1e, 0x4c, 0x70, 0x2f, 0xc2, 0x5a, 0xd6, 0x75, 0x7e }}, + { 1756, // 451 + { 0x7f, 0x92, 0x9b, 0x2d, 0xf6, 0x4e, 0x5b, 0x66, 0xfc, 0xf5, + 0x0f, 0x21, 0x5e, 0x58, 0xdf, 0x6e, 0x3e, 0xcc, 0x2e, 0xe3 }}, + { 1759, // 452 + { 0x7f, 0xd8, 0xda, 0x77, 0x86, 0x01, 0x35, 0x20, 0xdb, 0xc9, + 0xcb, 0xd5, 0xd2, 0x47, 0x13, 0x13, 0x27, 0x34, 0x5a, 0xec }}, + { 1762, // 453 + { 0x80, 0x28, 0x81, 0xc2, 0xa3, 0xab, 0x04, 0x24, 0xa0, 0xe6, + 0x56, 0xc2, 0x03, 0x3e, 0xeb, 0xb3, 0xea, 0xa2, 0xe0, 0x31 }}, + { 1765, // 454 + { 0x80, 0x34, 0x58, 0x41, 0x40, 0x7f, 0xbf, 0xb9, 0x1a, 0xe4, + 0x2b, 0x5b, 0x98, 0xf0, 0xa6, 0x34, 0xf0, 0xc1, 0xd0, 0x46 }}, + { 1770, // 455 + { 0x80, 0x4d, 0xbd, 0x68, 0x1e, 0x55, 0x0a, 0x21, 0x29, 0xdc, + 0x85, 0x12, 0xe6, 0x50, 0xd7, 0x4a, 0x70, 0xc5, 0xd6, 0x2f }}, + { 1773, // 456 + { 0x80, 0x67, 0x57, 0xdf, 0x13, 0xc1, 0x5b, 0x6b, 0xd2, 0x09, + 0xaa, 0x14, 0x5c, 0xbc, 0xf0, 0x12, 0xe2, 0xa2, 0x29, 0xcf }}, + { 1776, // 457 + { 0x80, 0xa0, 0x41, 0x00, 0xa2, 0xf3, 0xcf, 0xce, 0x1c, 0xee, + 0x07, 0xc7, 0xaf, 0xf4, 0xb8, 0x8e, 0xbd, 0xd1, 0xf3, 0xe3 }}, + { 1779, // 458 + { 0x80, 0xbd, 0x3c, 0xc2, 0x1c, 0xe8, 0xb2, 0xb3, 0xed, 0x62, + 0x5c, 0x96, 0x5c, 0xb5, 0x42, 0x36, 0x93, 0x25, 0xca, 0x23 }}, + { 1782, // 459 + { 0x80, 0xfd, 0x4c, 0xcd, 0xfb, 0x05, 0xf5, 0xca, 0x01, 0x18, + 0xa2, 0xab, 0x2d, 0x1f, 0x84, 0x0e, 0x95, 0xcd, 0x01, 0x84 }}, + { 1785, // 460 + { 0x81, 0xc9, 0x38, 0xdf, 0x9e, 0xc0, 0x32, 0xb3, 0x70, 0x8f, + 0x36, 0x1f, 0x7e, 0x33, 0x74, 0x95, 0xd9, 0xe6, 0xaf, 0x21 }}, + { 1788, // 461 + { 0x82, 0xd1, 0xfb, 0x9e, 0x63, 0x9f, 0xe7, 0x1b, 0x84, 0x8b, + 0xdd, 0x60, 0x81, 0xf1, 0x53, 0xaf, 0x8b, 0x09, 0x4f, 0xf2 }}, + { 1791, // 462 + { 0x83, 0x75, 0x9d, 0x02, 0x53, 0xd2, 0xb3, 0x0f, 0xd2, 0x89, + 0x69, 0x69, 0x74, 0xd9, 0x03, 0x31, 0x44, 0x5d, 0x20, 0x50 }}, + { 1794, // 463 + { 0x83, 0xa9, 0x8d, 0x64, 0x3c, 0x3a, 0x6b, 0x40, 0x57, 0x15, + 0x72, 0xfa, 0x42, 0x91, 0x35, 0x38, 0xd9, 0x41, 0xd8, 0x1c }}, + { 1797, // 464 + { 0x83, 0xf5, 0x00, 0x29, 0x7f, 0xfd, 0x47, 0xc3, 0xbd, 0x63, + 0xad, 0xb4, 0xe2, 0x3c, 0xef, 0xb0, 0x9a, 0x62, 0xfb, 0xbc }}, + { 1800, // 465 + { 0x84, 0x04, 0x10, 0xe1, 0x56, 0x7e, 0x8d, 0x0b, 0x56, 0x02, + 0x04, 0x83, 0x15, 0xa8, 0x8a, 0x0a, 0x42, 0xfc, 0x41, 0x4f }}, + { 1804, // 466 + { 0x84, 0x2b, 0x21, 0x9c, 0x7a, 0x23, 0xc0, 0x5f, 0xd0, 0x40, + 0xa7, 0x4f, 0x93, 0x7e, 0x8b, 0xbd, 0x2f, 0xea, 0xc3, 0xd9 }}, + { 1807, // 467 + { 0x84, 0x50, 0x1b, 0x2a, 0x89, 0x79, 0xb5, 0x09, 0xab, 0x06, + 0xb2, 0x8b, 0xbd, 0x1c, 0xd8, 0x45, 0x48, 0xa9, 0x3b, 0x2a }}, + { 1810, // 468 + { 0x85, 0x11, 0x7b, 0xfc, 0xe1, 0x37, 0xdf, 0x4e, 0xcf, 0x1b, + 0x66, 0x93, 0xfd, 0x8e, 0x6a, 0x94, 0x35, 0xee, 0xce, 0x3a }}, + { 1814, // 469 + { 0x85, 0x24, 0x65, 0x85, 0xf3, 0x4b, 0xcb, 0x90, 0xcd, 0xb8, + 0xd8, 0x39, 0x28, 0x09, 0xb8, 0x94, 0x2d, 0xa7, 0x7b, 0x75 }}, + { 1817, // 470 + { 0x85, 0xde, 0x19, 0x88, 0x1f, 0x41, 0xc2, 0x69, 0xe3, 0x46, + 0x8b, 0xbf, 0x1b, 0x09, 0xa8, 0xa7, 0x93, 0xa5, 0xe9, 0x1a }}, + { 1819, // 471 + { 0x86, 0x1d, 0x05, 0xe6, 0xfa, 0x3c, 0x1c, 0x71, 0x1b, 0x57, + 0x0c, 0xd1, 0xf3, 0xce, 0x0a, 0xb5, 0xe8, 0xdd, 0x2c, 0xaf }}, + { 1822, // 472 + { 0x86, 0xae, 0x0f, 0x55, 0xe1, 0xdd, 0xb0, 0xbb, 0x67, 0xa9, + 0x3d, 0x4a, 0x27, 0x45, 0x34, 0x02, 0x37, 0xe0, 0xff, 0x27 }}, + { 1824, // 473 + { 0x86, 0xff, 0x66, 0xcc, 0x5e, 0x40, 0x9a, 0xdc, 0x51, 0x2f, + 0x47, 0x26, 0x93, 0xe0, 0x96, 0xfe, 0xb8, 0x75, 0xeb, 0x60 }}, + { 1827, // 474 + { 0x87, 0x01, 0x11, 0x12, 0x4b, 0x00, 0xc9, 0x50, 0x9a, 0x2e, + 0x1c, 0xd4, 0xdf, 0xcf, 0x57, 0xe5, 0xf0, 0x92, 0x7e, 0x3a }}, + { 1830, // 475 + { 0x87, 0x19, 0xa1, 0xa8, 0xdb, 0xc1, 0x9b, 0xb1, 0x9e, 0x54, + 0xef, 0xa6, 0x7a, 0x49, 0xa6, 0x03, 0x11, 0x21, 0xb7, 0xe8 }}, + { 1833, // 476 + { 0x87, 0x6e, 0xe7, 0x9a, 0xac, 0xdd, 0x17, 0xf8, 0x3a, 0x41, + 0x71, 0x33, 0x01, 0x33, 0xeb, 0x4e, 0xce, 0x96, 0x24, 0x8f }}, + { 1836, // 477 + { 0x89, 0x5f, 0xa7, 0x9c, 0x69, 0x22, 0xb5, 0xc7, 0x56, 0x88, + 0xb1, 0xf9, 0xcd, 0x1c, 0x79, 0x2a, 0x34, 0xee, 0xb7, 0x48 }}, + { 1839, // 478 + { 0x89, 0x87, 0x32, 0x4a, 0x3a, 0x3f, 0x33, 0x69, 0x63, 0x05, + 0x4c, 0x5b, 0x79, 0xe5, 0xdd, 0x59, 0x0e, 0xf8, 0x62, 0x77 }}, + { 1842, // 479 + { 0x89, 0xe9, 0x5b, 0x35, 0x1b, 0xb9, 0x24, 0x0e, 0xb7, 0xfe, + 0xd9, 0x8c, 0x4e, 0x8d, 0xb6, 0x88, 0x44, 0x6f, 0x5a, 0xc1 }}, + { 1845, // 480 + { 0x8a, 0x3b, 0x03, 0xbf, 0x1a, 0x73, 0xee, 0x4d, 0xfc, 0xfb, + 0xc1, 0x1a, 0xbe, 0x99, 0xe8, 0x9c, 0x85, 0x09, 0x96, 0x7a }}, + { 1848, // 481 + { 0x8a, 0x72, 0xba, 0x73, 0x6a, 0x7f, 0xc5, 0x03, 0x41, 0xae, + 0xb9, 0x26, 0xfe, 0xe3, 0x24, 0x3e, 0x6b, 0x43, 0xfb, 0x38 }}, + { 1851, // 482 + { 0x8a, 0xa0, 0x8b, 0xd6, 0xc2, 0x89, 0xa9, 0xe3, 0x92, 0xfc, + 0x13, 0x71, 0xba, 0xfc, 0x3c, 0xc3, 0x6f, 0xf8, 0xd6, 0x23 }}, + { 1854, // 483 + { 0x8a, 0xcc, 0x24, 0x21, 0xa4, 0xf9, 0x31, 0xb7, 0x65, 0xd6, + 0x08, 0x91, 0xc7, 0xc3, 0xd1, 0x46, 0x88, 0xf9, 0x7b, 0x65 }}, + { 1876, // 484 + { 0x8a, 0xcd, 0xb8, 0x7f, 0xb6, 0x88, 0x45, 0xfb, 0x6f, 0xec, + 0xe7, 0x95, 0x3d, 0x21, 0x5a, 0xd9, 0x5b, 0x88, 0x6d, 0xf1 }}, + { 1879, // 485 + { 0x8a, 0xde, 0x83, 0x41, 0x80, 0x69, 0x1e, 0x21, 0xc7, 0xeb, + 0x8a, 0x00, 0xee, 0x97, 0x2c, 0xd1, 0xc8, 0x46, 0x68, 0xce }}, + { 1882, // 486 + { 0x8a, 0xe1, 0x68, 0xda, 0xb6, 0x3b, 0xa7, 0x08, 0x78, 0x58, + 0x6f, 0x82, 0xee, 0x1e, 0xce, 0xb7, 0x6e, 0x28, 0xec, 0x57 }}, + { 1884, // 487 + { 0x8a, 0xe2, 0xca, 0x41, 0xa9, 0x2c, 0xa6, 0x2b, 0x7f, 0x31, + 0x79, 0x3c, 0x8e, 0x54, 0xaa, 0xf2, 0x7f, 0xb1, 0xa0, 0xaa }}, + { 1887, // 488 + { 0x8b, 0x4a, 0x17, 0x6d, 0x70, 0x4d, 0x15, 0xdb, 0x60, 0x1b, + 0x70, 0x8e, 0xbd, 0xc7, 0x26, 0x29, 0xe3, 0x00, 0x91, 0x79 }}, + { 1890, // 489 + { 0x8b, 0x8a, 0xa0, 0x1c, 0xfe, 0x80, 0xdb, 0x76, 0xfa, 0xed, + 0x84, 0x10, 0xd1, 0x27, 0x21, 0x6c, 0x6b, 0xbf, 0x74, 0xd2 }}, + { 1893, // 490 + { 0x8b, 0x8b, 0xf6, 0x1e, 0x55, 0x06, 0x6b, 0x71, 0x1e, 0xc6, + 0x32, 0x8f, 0x79, 0x65, 0x5d, 0x0a, 0x5c, 0x37, 0x8a, 0xee }}, + { 1896, // 491 + { 0x8c, 0x02, 0x5c, 0x22, 0xcd, 0x51, 0x6f, 0xf8, 0x2d, 0x3e, + 0x60, 0xfb, 0x53, 0xcb, 0xa0, 0xef, 0x39, 0x42, 0x52, 0x50 }}, + { 1899, // 492 + { 0x8c, 0x05, 0x83, 0xd6, 0xbf, 0x30, 0xbe, 0xd0, 0x1b, 0xdc, + 0xb0, 0xdd, 0xb6, 0xd1, 0xd1, 0x17, 0x06, 0x7c, 0xbe, 0xc9 }}, + { 1904, // 493 + { 0x8c, 0x17, 0x61, 0xac, 0x0c, 0xc6, 0x33, 0x79, 0x1b, 0x7d, + 0x41, 0xca, 0xa9, 0x0f, 0xe5, 0x6b, 0x11, 0xf1, 0x0d, 0x06 }}, + { 1908, // 494 + { 0x8c, 0x23, 0x6f, 0xe1, 0xc1, 0xe3, 0xcb, 0xcf, 0xe4, 0x0b, + 0xac, 0x09, 0x8b, 0x14, 0xc9, 0x73, 0x74, 0x3a, 0x21, 0xb0 }}, + { 1913, // 495 + { 0x8c, 0x81, 0x11, 0x53, 0xf9, 0xdd, 0xdc, 0xb2, 0xf0, 0xa6, + 0x3e, 0xca, 0x4b, 0x08, 0xcf, 0xe5, 0xa3, 0x33, 0xda, 0xd1 }}, + { 1915, // 496 + { 0x8d, 0x13, 0x1f, 0xaa, 0xdd, 0x23, 0x43, 0xc2, 0x7b, 0x5c, + 0xfb, 0x2d, 0x42, 0x18, 0xee, 0xd2, 0x8b, 0x29, 0x9c, 0x2e }}, + { 1917, // 497 + { 0x8d, 0x1f, 0xe0, 0xe5, 0x41, 0x2b, 0xf4, 0x2d, 0xdd, 0xde, + 0x5d, 0x77, 0x97, 0xa1, 0xb9, 0x15, 0x7a, 0xe0, 0x0c, 0xaa }}, + { 1920, // 498 + { 0x8d, 0x2a, 0xf2, 0xe4, 0x25, 0x4f, 0x80, 0xc9, 0x0b, 0xae, + 0x64, 0xac, 0xf0, 0xad, 0xc7, 0x13, 0xab, 0x87, 0x60, 0xd6 }}, + { 1923, // 499 + { 0x8d, 0x8a, 0x2b, 0x2c, 0x9f, 0x18, 0x79, 0x71, 0xd7, 0xdc, + 0xf1, 0x18, 0xa2, 0xed, 0xe6, 0x49, 0xa2, 0xab, 0xd0, 0x14 }}, + { 1926, // 500 + { 0x8d, 0x90, 0x03, 0xa1, 0xdd, 0x20, 0x1e, 0xc1, 0x52, 0xcc, + 0xd3, 0xdc, 0x05, 0x8e, 0xed, 0x88, 0x57, 0xf5, 0x1f, 0xe8 }}, + { 1929, // 501 + { 0x8d, 0xef, 0x18, 0xe2, 0xcf, 0x49, 0xc1, 0x61, 0x7e, 0x67, + 0x0b, 0xcb, 0x87, 0x20, 0x88, 0xbe, 0xec, 0xc4, 0x39, 0xe1 }}, + { 1932, // 502 + { 0x8e, 0x0a, 0xfc, 0xcc, 0x5d, 0xb9, 0xf6, 0x72, 0x7a, 0xd0, + 0xbb, 0x8d, 0x33, 0xbb, 0xc2, 0x30, 0x8b, 0x43, 0x32, 0x17 }}, + { 1935, // 503 + { 0x8e, 0xc1, 0x10, 0xed, 0x66, 0xb5, 0x61, 0xac, 0xb7, 0xf0, + 0xc5, 0xb7, 0x1f, 0x41, 0xb7, 0x89, 0x0b, 0x93, 0x8e, 0x99 }}, + { 1937, // 504 + { 0x8e, 0xc3, 0xec, 0x3e, 0xd7, 0x30, 0x4f, 0x38, 0x61, 0x50, + 0x36, 0xfa, 0xe6, 0xe8, 0x94, 0xfe, 0x25, 0x62, 0x67, 0x10 }}, + { 1940, // 505 + { 0x8e, 0xd5, 0xb4, 0x57, 0x8f, 0x4c, 0xe0, 0x3b, 0xe4, 0x31, + 0xbb, 0xd0, 0xb8, 0x69, 0xf9, 0x79, 0x7c, 0xed, 0x08, 0x2c }}, + { 1943, // 506 + { 0x8f, 0x81, 0x5a, 0x88, 0xfa, 0x20, 0x76, 0xac, 0x0e, 0xda, + 0xb7, 0x2a, 0x47, 0x4d, 0x78, 0x6d, 0xba, 0xcb, 0xb7, 0x16 }}, + { 1946, // 507 + { 0x8f, 0xa6, 0xaf, 0x15, 0xe9, 0x7b, 0x9b, 0xc5, 0x3a, 0xcd, + 0x26, 0xc3, 0xad, 0x3b, 0xe1, 0xc5, 0x60, 0xa2, 0xbc, 0x56 }}, + { 1948, // 508 + { 0x90, 0x31, 0x70, 0x10, 0x83, 0x41, 0x63, 0x08, 0x64, 0x44, + 0x77, 0x6e, 0xad, 0xda, 0x88, 0xac, 0x7a, 0x18, 0xad, 0x5a }}, + { 1951, // 509 + { 0x90, 0x37, 0x5b, 0xef, 0xcb, 0xaf, 0xaf, 0x37, 0x14, 0xe3, + 0x33, 0x86, 0xbf, 0xdc, 0x26, 0xe2, 0x80, 0xd0, 0x0d, 0xe4 }}, + { 1954, // 510 + { 0x90, 0x5d, 0x22, 0xd3, 0x58, 0xf8, 0x5e, 0x1d, 0x4d, 0xb1, + 0x20, 0x05, 0x1c, 0x10, 0xc1, 0xc8, 0x00, 0x17, 0x2d, 0x6e }}, + { 1957, // 511 + { 0x90, 0x63, 0xea, 0x2b, 0xdf, 0xbf, 0xc8, 0xcc, 0xc8, 0x04, + 0x42, 0x88, 0x8c, 0xdc, 0x68, 0x2b, 0x64, 0xe5, 0x9d, 0x59 }}, + { 1960, // 512 + { 0x90, 0xb0, 0x74, 0xc6, 0x9a, 0x62, 0x5c, 0xa7, 0x55, 0xb6, + 0x5d, 0x74, 0xf2, 0xf9, 0xa6, 0xbf, 0x87, 0x7e, 0xb3, 0xa4 }}, + { 1962, // 513 + { 0x90, 0xf5, 0xe1, 0xe4, 0x34, 0x6e, 0x5a, 0x0d, 0x9c, 0x2c, + 0x32, 0xfb, 0x16, 0x71, 0xec, 0x21, 0x6e, 0xae, 0x7a, 0x8a }}, + { 1965, // 514 + { 0x91, 0xb8, 0xba, 0x3f, 0x46, 0x0c, 0xb7, 0x9b, 0x2b, 0xd6, + 0x2b, 0x36, 0x64, 0xda, 0x2a, 0xd1, 0xde, 0x6f, 0x9e, 0xd6 }}, + { 1968, // 515 + { 0x92, 0xb0, 0xa4, 0xb5, 0x5e, 0xc7, 0x4e, 0x42, 0x59, 0xfb, + 0xc2, 0x1f, 0x80, 0x33, 0x26, 0x49, 0xd5, 0x52, 0xbb, 0xbb }}, + { 1971, // 516 + { 0x92, 0xba, 0xd4, 0x78, 0x3d, 0xe0, 0x24, 0x7b, 0xaf, 0x89, + 0xed, 0x2d, 0xf5, 0xef, 0x96, 0xc0, 0x87, 0x0c, 0xd9, 0x75 }}, + { 1974, // 517 + { 0x92, 0xc8, 0x48, 0xca, 0x44, 0xff, 0xb9, 0x6a, 0x08, 0x02, + 0xbc, 0x42, 0x7e, 0xf7, 0xb6, 0x15, 0xed, 0x13, 0x76, 0x76 }}, + { 1977, // 518 + { 0x93, 0x16, 0x1d, 0x05, 0xf7, 0x60, 0xe0, 0x73, 0xe0, 0x61, + 0xcd, 0x7f, 0xb8, 0x3d, 0x67, 0xea, 0x57, 0x8d, 0x5f, 0x7b }}, + { 1980, // 519 + { 0x93, 0x29, 0x5b, 0x69, 0x58, 0xfa, 0x71, 0x96, 0x2d, 0x83, + 0x39, 0x8e, 0x12, 0xf7, 0xca, 0xa6, 0xaa, 0x72, 0x94, 0x75 }}, + { 1984, // 520 + { 0x93, 0x7a, 0xbc, 0xd4, 0x6d, 0xa2, 0x86, 0x03, 0x02, 0xa2, + 0xdf, 0x4d, 0xb0, 0x63, 0xb5, 0xa3, 0xef, 0x4a, 0x7d, 0xbc }}, + { 1987, // 521 + { 0x93, 0x7c, 0x3a, 0x80, 0x27, 0xe4, 0x68, 0xb6, 0xb0, 0x6a, + 0x3d, 0xc1, 0x70, 0x36, 0x71, 0x52, 0x25, 0x60, 0xf5, 0xb6 }}, + { 1992, // 522 + { 0x94, 0x15, 0x35, 0x73, 0xb3, 0x97, 0xb3, 0x80, 0xfc, 0x06, + 0x1d, 0xbe, 0xe7, 0x3c, 0x2c, 0x29, 0xc6, 0x24, 0xce, 0x47 }}, + { 1994, // 523 + { 0x94, 0x4f, 0xfc, 0x01, 0xdd, 0xb9, 0x38, 0x1d, 0xc3, 0x23, + 0x1d, 0xd3, 0x29, 0x03, 0xf8, 0x4a, 0x97, 0xee, 0x5e, 0x47 }}, + { 1997, // 524 + { 0x94, 0xac, 0x2e, 0x2d, 0x55, 0x5e, 0x74, 0x12, 0xff, 0x89, + 0xf9, 0x89, 0xc2, 0xbf, 0x0e, 0xe8, 0x5f, 0x6e, 0x66, 0x94 }}, + { 2000, // 525 + { 0x94, 0xc9, 0x4e, 0x4e, 0xcb, 0xea, 0x2c, 0x51, 0xd7, 0x5a, + 0x4b, 0xbb, 0xb8, 0xb5, 0x74, 0x32, 0xbc, 0x9b, 0x53, 0xaa }}, + { 2003, // 526 + { 0x94, 0xf6, 0x6e, 0x7d, 0x6a, 0x21, 0x82, 0x4e, 0x29, 0xfd, + 0x73, 0x17, 0x27, 0x65, 0x5e, 0x8b, 0xd1, 0x05, 0x7c, 0x29 }}, + { 2010, // 527 + { 0x94, 0xfe, 0x82, 0xc0, 0xb7, 0x76, 0xd5, 0x5c, 0xfd, 0x3a, + 0x86, 0xcd, 0x33, 0xef, 0x42, 0xd6, 0x8d, 0xb0, 0x1f, 0xd1 }}, + { 2015, // 528 + { 0x95, 0x04, 0x09, 0xc0, 0xf4, 0xcb, 0xcd, 0x65, 0x89, 0x10, + 0x7f, 0xdf, 0x08, 0x58, 0x49, 0x10, 0x08, 0xd1, 0xe5, 0x03 }}, + { 2017, // 529 + { 0x95, 0x8e, 0x22, 0x83, 0xec, 0x7e, 0x2c, 0x1e, 0x6d, 0xee, + 0xc2, 0x3e, 0xf4, 0x6c, 0xef, 0x3e, 0x28, 0x40, 0x71, 0x67 }}, + { 2020, // 530 + { 0x95, 0xc5, 0x34, 0x54, 0x1d, 0x69, 0x7a, 0xd5, 0x84, 0x5c, + 0xc8, 0x7f, 0xf5, 0x4d, 0xff, 0xff, 0x3f, 0x8c, 0xd0, 0x93 }}, + { 2023, // 531 + { 0x96, 0x4b, 0xd2, 0xcd, 0x78, 0x9e, 0xed, 0x28, 0x4b, 0xdb, + 0x1f, 0xaa, 0x93, 0xf1, 0x72, 0x7b, 0x80, 0x72, 0xbe, 0x8e }}, + { 2026, // 532 + { 0x96, 0x55, 0x32, 0xf9, 0xee, 0x46, 0x6c, 0xcf, 0x28, 0xbd, + 0x38, 0x9e, 0xab, 0xe0, 0x29, 0xff, 0x5b, 0x3b, 0x5c, 0x69 }}, + { 2029, // 533 + { 0x97, 0x42, 0xa4, 0xd1, 0x96, 0xda, 0xd8, 0xe6, 0x2a, 0xdc, + 0xd4, 0x8a, 0x70, 0x36, 0x68, 0x39, 0x4c, 0x27, 0xce, 0xc2 }}, + { 2032, // 534 + { 0x97, 0x5d, 0xd9, 0x1b, 0x07, 0x3e, 0x5e, 0xc9, 0xda, 0x84, + 0xa5, 0x45, 0x53, 0xc9, 0xf1, 0x71, 0xb4, 0xbd, 0xc0, 0x1a }}, + { 2035, // 535 + { 0x97, 0x68, 0x51, 0x78, 0x26, 0x52, 0xb5, 0x04, 0x9a, 0x76, + 0xf8, 0x8c, 0xe7, 0x39, 0x34, 0x86, 0xf0, 0x55, 0xbd, 0x32 }}, + { 2038, // 536 + { 0x97, 0xda, 0xfe, 0x1f, 0x55, 0x48, 0x53, 0x77, 0x21, 0x5e, + 0x5b, 0x49, 0xf0, 0x04, 0x8c, 0x57, 0x82, 0x7d, 0x98, 0xa0 }}, + { 2041, // 537 + { 0x97, 0xf7, 0x36, 0x82, 0xe6, 0xf1, 0x97, 0x9f, 0x07, 0xb4, + 0x34, 0xd9, 0x64, 0x96, 0xfb, 0xdb, 0xc0, 0xa6, 0x84, 0xb8 }}, + { 2044, // 538 + { 0x98, 0x15, 0x1c, 0x72, 0x85, 0x39, 0xb3, 0x59, 0xfc, 0x78, + 0x06, 0xcf, 0x4b, 0xb9, 0x70, 0x75, 0xfe, 0xd7, 0x60, 0x46 }}, + { 2049, // 539 + { 0x98, 0x35, 0x15, 0x03, 0xea, 0x51, 0x96, 0xda, 0x7f, 0xab, + 0x18, 0xcf, 0xfa, 0x1b, 0xfe, 0x52, 0x48, 0x9c, 0xc5, 0xdc }}, + { 2058, // 540 + { 0x98, 0x62, 0x20, 0xf9, 0xe3, 0x65, 0xd2, 0xff, 0x43, 0xe2, + 0x56, 0x8d, 0x6b, 0x38, 0xf8, 0xcb, 0xc2, 0x00, 0xb2, 0xbc }}, + { 2061, // 541 + { 0x98, 0x95, 0x6a, 0xae, 0x61, 0x07, 0xe7, 0x1e, 0xbf, 0xfe, + 0x09, 0xa6, 0x98, 0x70, 0x9b, 0xf6, 0x96, 0x21, 0xe7, 0xa8 }}, + { 2064, // 542 + { 0x98, 0xe6, 0x01, 0x0d, 0xcd, 0xc0, 0x70, 0x43, 0x11, 0x5d, + 0xbf, 0x9b, 0x3a, 0x6a, 0x1f, 0x00, 0xb6, 0xe9, 0xd4, 0x96 }}, + { 2068, // 543 + { 0x98, 0xe6, 0xa4, 0x11, 0x4f, 0x7b, 0x57, 0x29, 0xa8, 0xf6, + 0x6a, 0x4a, 0x2d, 0xd3, 0x56, 0x5b, 0xf7, 0xc3, 0x10, 0x7e }}, + { 2071, // 544 + { 0x99, 0xcb, 0xb1, 0x6d, 0x2b, 0x24, 0x22, 0x37, 0x1e, 0x3d, + 0x09, 0x23, 0xbc, 0x0f, 0xa0, 0xc3, 0xe2, 0xeb, 0xfb, 0x55 }}, + { 2075, // 545 + { 0x9a, 0x92, 0x42, 0x36, 0xf6, 0xbc, 0xb4, 0x63, 0xd7, 0x2e, + 0x50, 0x88, 0x3d, 0x2b, 0x0a, 0x76, 0x11, 0xd0, 0xbc, 0x96 }}, + { 2078, // 546 + { 0x9a, 0xac, 0x1a, 0xda, 0x3e, 0x6f, 0xf2, 0x54, 0xb2, 0x3a, + 0x87, 0xa5, 0xda, 0xe2, 0x85, 0x37, 0x79, 0xe9, 0x92, 0xf9 }}, + { 2081, // 547 + { 0x9a, 0xcf, 0x45, 0xe2, 0x52, 0xa0, 0x9d, 0x87, 0xc5, 0xab, + 0xd5, 0xd1, 0xae, 0x35, 0x5a, 0xd5, 0xa7, 0xa9, 0xcb, 0x23 }}, + { 2084, // 548 + { 0x9a, 0xf9, 0xc0, 0xea, 0x16, 0xd8, 0x5a, 0x79, 0x87, 0xfc, + 0x97, 0xea, 0x31, 0xcb, 0x0a, 0xdd, 0x17, 0x05, 0x9a, 0x9d }}, + { 2087, // 549 + { 0x9b, 0x2b, 0xb0, 0xc1, 0xee, 0x7e, 0xb6, 0x39, 0x6f, 0x04, + 0x90, 0xaf, 0x23, 0x3a, 0xbf, 0x65, 0x47, 0x2d, 0x71, 0x15 }}, + { 2090, // 550 + { 0x9b, 0xd0, 0xee, 0x2f, 0xd0, 0xa6, 0x2d, 0x06, 0x1a, 0x65, + 0xa4, 0xed, 0x74, 0x7c, 0x2d, 0x4a, 0x26, 0x45, 0x59, 0x61 }}, + { 2093, // 551 + { 0x9c, 0x1b, 0x8e, 0xc9, 0x6f, 0xb4, 0xb6, 0xc0, 0xc9, 0x11, + 0xb5, 0x30, 0x09, 0x51, 0x61, 0x35, 0x8a, 0xe8, 0x17, 0xa9 }}, + { 2096, // 552 + { 0x9d, 0x27, 0xb8, 0x80, 0x34, 0xc0, 0x8f, 0x30, 0x11, 0xec, + 0x74, 0xe2, 0x1f, 0x1d, 0x53, 0x57, 0xfd, 0x27, 0x54, 0x26 }}, + { 2099, // 553 + { 0x9d, 0xa1, 0x99, 0x99, 0x51, 0x18, 0x58, 0xbc, 0xf2, 0xcd, + 0x4c, 0x77, 0x4f, 0x80, 0xe1, 0xe2, 0xdd, 0x6b, 0x54, 0x7f }}, + { 2101, // 554 + { 0x9d, 0xbd, 0x44, 0x11, 0x5d, 0xfd, 0xfd, 0x3c, 0x06, 0x99, + 0x2c, 0x5f, 0x7c, 0xaa, 0xf7, 0xe7, 0xe0, 0x5d, 0xb3, 0x09 }}, + { 2104, // 555 + { 0x9d, 0xf5, 0x79, 0xac, 0xd3, 0x81, 0x04, 0xea, 0x0a, 0xf7, + 0xca, 0xe2, 0x0f, 0x7b, 0xc5, 0x4a, 0x6d, 0x3c, 0x86, 0xa0 }}, + { 2106, // 556 + { 0x9e, 0x12, 0xcd, 0xa1, 0xdc, 0xb3, 0x0e, 0xf7, 0x57, 0x5a, + 0xdb, 0x58, 0x1a, 0xbb, 0x2e, 0x6b, 0x39, 0x98, 0xe9, 0x76 }}, + { 2108, // 557 + { 0x9e, 0x2f, 0x41, 0x9b, 0x1c, 0xab, 0x50, 0x70, 0x0d, 0x55, + 0x1c, 0x81, 0xf2, 0xaf, 0x8a, 0xa1, 0x63, 0xfb, 0x00, 0x5a }}, + { 2111, // 558 + { 0x9e, 0x87, 0x76, 0x6d, 0x4c, 0x8c, 0xa4, 0x26, 0x0e, 0x40, + 0xca, 0x45, 0xba, 0x59, 0x48, 0x87, 0x0a, 0x94, 0xf9, 0xd0 }}, + { 2114, // 559 + { 0x9e, 0xf0, 0xa5, 0x17, 0xa6, 0x63, 0xc7, 0x58, 0xdd, 0x26, + 0x8b, 0xb0, 0x99, 0xa9, 0x14, 0x7c, 0x57, 0x70, 0xb8, 0xfd }}, + { 2117, // 560 + { 0x9f, 0x1b, 0x26, 0x3c, 0xc3, 0x36, 0x33, 0x38, 0xd7, 0xc7, + 0x57, 0x2c, 0x3b, 0x81, 0xfc, 0x91, 0xda, 0xee, 0xde, 0x46 }}, + { 2120, // 561 + { 0xa0, 0x09, 0x33, 0x55, 0xb1, 0x16, 0xd4, 0x74, 0x0e, 0xcd, + 0x54, 0xda, 0x73, 0xce, 0x91, 0x16, 0x19, 0x6f, 0x3b, 0xe7 }}, + { 2123, // 562 + { 0xa0, 0x0a, 0x90, 0x06, 0xac, 0x3e, 0x0d, 0x3a, 0x4b, 0x0e, + 0x00, 0x41, 0x32, 0x0d, 0x0c, 0x9a, 0xb1, 0x65, 0xf9, 0x52 }}, + { 2126, // 563 + { 0xa0, 0xad, 0x8c, 0x51, 0xd2, 0x1b, 0x42, 0x0c, 0x6d, 0x06, + 0x53, 0xb5, 0xb3, 0x0a, 0x30, 0x67, 0x0c, 0x66, 0x9b, 0x18 }}, + { 2129, // 564 + { 0xa0, 0xbb, 0x50, 0x45, 0x79, 0x91, 0x29, 0xec, 0x48, 0x66, + 0x81, 0x0c, 0xf2, 0x58, 0x0d, 0xf0, 0x9c, 0x65, 0xda, 0x07 }}, + { 2132, // 565 + { 0xa0, 0xfc, 0x8c, 0x71, 0x16, 0x54, 0xdd, 0xd0, 0x0b, 0x83, + 0x3e, 0x67, 0x12, 0x43, 0x85, 0xa9, 0xff, 0x55, 0x3d, 0xb3 }}, + { 2135, // 566 + { 0xa1, 0x22, 0xa5, 0x22, 0x6a, 0x82, 0xc4, 0x51, 0x93, 0x7a, + 0x81, 0xca, 0xa2, 0xbd, 0x08, 0xf5, 0xf7, 0xb4, 0x8c, 0x0c }}, + { 2138, // 567 + { 0xa1, 0x2b, 0x14, 0x93, 0xf5, 0x11, 0xa5, 0xa5, 0x86, 0xd1, + 0x71, 0x74, 0x00, 0xc9, 0x09, 0xc8, 0x23, 0xac, 0x3e, 0x4f }}, + { 2143, // 568 + { 0xa1, 0x2e, 0xcf, 0x4e, 0x29, 0xb0, 0x5b, 0xa9, 0xb3, 0xf9, + 0xbb, 0x79, 0x94, 0x30, 0x1a, 0xc7, 0x2a, 0xf1, 0x29, 0x24 }}, + { 2146, // 569 + { 0xa1, 0x4f, 0xd7, 0x78, 0xfe, 0x16, 0xbd, 0x47, 0x0f, 0xee, + 0xfb, 0x5f, 0x32, 0x84, 0x15, 0x81, 0x8e, 0x3d, 0xe7, 0x78 }}, + { 2155, // 570 + { 0xa1, 0x5a, 0xa9, 0xaa, 0xa6, 0x33, 0x53, 0xfb, 0x28, 0xb2, + 0xaa, 0x52, 0xb1, 0x6d, 0xe9, 0xef, 0xb9, 0xe1, 0x23, 0xf3 }}, + { 2158, // 571 + { 0xa1, 0x96, 0xb4, 0xa1, 0xdb, 0x9f, 0x25, 0x5c, 0xa7, 0xcd, + 0xbc, 0x4a, 0x75, 0xa8, 0xa0, 0x48, 0x54, 0x73, 0x13, 0x92 }}, + { 2161, // 572 + { 0xa1, 0xe6, 0x32, 0x0f, 0x03, 0x1e, 0xab, 0xf2, 0x0f, 0x10, + 0x53, 0x28, 0xec, 0xd5, 0x1b, 0xe6, 0xc9, 0xde, 0xad, 0xbf }}, + { 2164, // 573 + { 0xa1, 0xf5, 0x99, 0x0c, 0x52, 0x53, 0x92, 0x58, 0x4b, 0x73, + 0x9a, 0x97, 0x2b, 0x49, 0x92, 0x65, 0xe7, 0xb5, 0x59, 0x3a }}, + { 2167, // 574 + { 0xa2, 0x08, 0xed, 0x95, 0x84, 0xf3, 0x26, 0x9e, 0x8c, 0xc5, + 0x20, 0x66, 0x92, 0xc8, 0x50, 0x33, 0x61, 0x67, 0xf6, 0x7d }}, + { 2170, // 575 + { 0xa2, 0xf0, 0x7a, 0xab, 0x6b, 0x3f, 0x3e, 0x15, 0x3e, 0xcd, + 0x38, 0x55, 0x29, 0x3e, 0x88, 0x63, 0xc0, 0x71, 0xf0, 0x79 }}, + { 2173, // 576 + { 0xa2, 0xf8, 0x52, 0x60, 0x27, 0xe6, 0x44, 0x7a, 0xcb, 0xe2, + 0x4e, 0x6d, 0x30, 0xa7, 0xf2, 0x03, 0x44, 0x42, 0x68, 0x30 }}, + { 2176, // 577 + { 0xa3, 0x60, 0x51, 0xa5, 0x53, 0x6f, 0xf0, 0xf7, 0xbd, 0x77, + 0x7f, 0xcb, 0x66, 0x9e, 0x46, 0x07, 0x2c, 0xc3, 0xc2, 0x34 }}, + { 2179, // 578 + { 0xa3, 0xce, 0x3f, 0x0c, 0x17, 0xe0, 0xa4, 0x85, 0x76, 0xb5, + 0xc3, 0x76, 0x5f, 0x4f, 0x78, 0x09, 0x76, 0x32, 0x92, 0x17 }}, + { 2182, // 579 + { 0xa3, 0xed, 0x00, 0xc7, 0xcd, 0x96, 0x85, 0xd6, 0xf6, 0xd3, + 0x98, 0x52, 0xd5, 0x82, 0x5a, 0xc6, 0x0e, 0xeb, 0xb3, 0x52 }}, + { 2184, // 580 + { 0xa4, 0x1f, 0x20, 0xf8, 0x4c, 0xca, 0xf6, 0x64, 0xb0, 0xf2, + 0x76, 0xdf, 0x98, 0xba, 0xdf, 0x70, 0x58, 0x98, 0x8b, 0x05 }}, + { 2188, // 581 + { 0xa4, 0x26, 0x96, 0xe8, 0x7b, 0x02, 0x7c, 0x81, 0xca, 0xad, + 0xb5, 0xb3, 0x9e, 0x42, 0xcc, 0xcf, 0xf9, 0xb1, 0x4e, 0x94 }}, + { 2191, // 582 + { 0xa4, 0x59, 0x08, 0xde, 0x85, 0x7a, 0x5a, 0xb9, 0x37, 0x2c, + 0xbc, 0x10, 0x69, 0xde, 0xa4, 0x25, 0x4a, 0x98, 0xec, 0x01 }}, + { 2193, // 583 + { 0xa4, 0x9d, 0x82, 0x24, 0x53, 0xcf, 0xcc, 0x1a, 0xa8, 0xf7, + 0x65, 0x9b, 0xae, 0xf3, 0x9b, 0x0c, 0x73, 0x8d, 0xfc, 0xcb }}, + { 2196, // 584 + { 0xa4, 0xb6, 0x48, 0x48, 0x85, 0x72, 0xd3, 0x4c, 0x5c, 0x30, + 0xa5, 0x05, 0x0c, 0xe0, 0x3b, 0x29, 0x87, 0x24, 0x7f, 0x6a }}, + { 2199, // 585 + { 0xa4, 0xc5, 0x63, 0x71, 0xa0, 0xe8, 0x7f, 0xde, 0xf7, 0x47, + 0x07, 0x93, 0xd7, 0x29, 0x49, 0xd0, 0xa3, 0xed, 0x1e, 0xe0 }}, + { 2202, // 586 + { 0xa5, 0x13, 0xfa, 0x26, 0x86, 0x06, 0x9d, 0x10, 0xb8, 0x7b, + 0xec, 0x37, 0x98, 0x4b, 0x05, 0x04, 0x78, 0x81, 0x15, 0x87 }}, + { 2205, // 587 + { 0xa5, 0x2a, 0x6b, 0x05, 0x96, 0xa0, 0xb4, 0x94, 0xf5, 0x07, + 0xbb, 0x42, 0xe1, 0x49, 0x1d, 0xe8, 0x72, 0x42, 0x1f, 0xfc }}, + { 2208, // 588 + { 0xa5, 0x55, 0xce, 0x7f, 0xaa, 0xbb, 0x7f, 0x34, 0x5a, 0xc3, + 0x0a, 0x5f, 0x40, 0xcc, 0x96, 0xa5, 0xe3, 0x42, 0xda, 0x9d }}, + { 2211, // 589 + { 0xa5, 0xbc, 0x1d, 0xd3, 0x64, 0x78, 0x0c, 0x3b, 0x53, 0xbc, + 0xce, 0x05, 0x43, 0xf7, 0x06, 0xe1, 0xb4, 0x6f, 0x97, 0x14 }}, + { 2214, // 590 + { 0xa6, 0x03, 0x0f, 0x0e, 0xd9, 0xb1, 0x27, 0xc9, 0x07, 0x7a, + 0xbc, 0x63, 0x77, 0xb6, 0xf5, 0x37, 0x6c, 0xfb, 0x0c, 0x7e }}, + { 2217, // 591 + { 0xa6, 0x7f, 0x38, 0x2b, 0x3d, 0x7f, 0x6f, 0x09, 0xf9, 0xcd, + 0xfd, 0x2d, 0x0e, 0x8f, 0x89, 0x58, 0x9e, 0x67, 0x8f, 0x37 }}, + { 2220, // 592 + { 0xa6, 0xda, 0x1f, 0xe0, 0x53, 0x8a, 0x9a, 0x5c, 0xc6, 0x5c, + 0x21, 0x74, 0x06, 0xb6, 0xa0, 0x45, 0x7b, 0x1f, 0xcf, 0xd9 }}, + { 2223, // 593 + { 0xa7, 0x41, 0xd1, 0xe0, 0xf7, 0x47, 0x12, 0x5b, 0xc1, 0x55, + 0x3c, 0x1f, 0x21, 0xaf, 0xb4, 0x3d, 0xeb, 0xe0, 0xec, 0x25 }}, + { 2226, // 594 + { 0xa7, 0x48, 0xe2, 0x9f, 0x0d, 0x74, 0x3f, 0x07, 0xfe, 0x53, + 0x0a, 0x67, 0x52, 0xcb, 0x6a, 0x1c, 0x1a, 0xb5, 0x82, 0xd3 }}, + { 2229, // 595 + { 0xa7, 0xe4, 0x69, 0x6b, 0x6c, 0x55, 0xe4, 0xf1, 0xcd, 0x38, + 0x6c, 0xab, 0xd9, 0x59, 0xd7, 0xde, 0xb2, 0x45, 0xf5, 0x15 }}, + { 2231, // 596 + { 0xa7, 0xf4, 0x1b, 0x27, 0xbc, 0x53, 0xc0, 0x92, 0xdc, 0xe0, + 0x7e, 0x5f, 0xc2, 0x02, 0x4c, 0x18, 0x23, 0x96, 0x87, 0x95 }}, + { 2234, // 597 + { 0xa7, 0xf5, 0xca, 0xb4, 0x34, 0x1a, 0x79, 0xde, 0x84, 0xd5, + 0x0b, 0xcc, 0x53, 0xbb, 0xf0, 0x1a, 0xa0, 0x51, 0xa2, 0xb0 }}, + { 2239, // 598 + { 0xa8, 0x03, 0x5c, 0x7f, 0x5e, 0x0a, 0xc0, 0x79, 0xf9, 0xf6, + 0x5d, 0xd6, 0xab, 0x0e, 0x30, 0x8a, 0xd3, 0x53, 0x84, 0x8a }}, + { 2242, // 599 + { 0xa8, 0x16, 0xd7, 0x4a, 0xb0, 0x7f, 0xc5, 0x95, 0x19, 0xc5, + 0xd5, 0x2b, 0x3a, 0x19, 0x6b, 0xf7, 0x9b, 0xa5, 0x3d, 0xa9 }}, + { 2245, // 600 + { 0xa8, 0x3f, 0xc4, 0xc9, 0xba, 0x26, 0xb4, 0xa5, 0xc0, 0x58, + 0x6f, 0xc6, 0x82, 0x2d, 0x7c, 0x3d, 0xf1, 0xbb, 0x18, 0xcc }}, + { 2248, // 601 + { 0xa8, 0x56, 0x35, 0xc0, 0x83, 0xa5, 0xc6, 0x41, 0xfb, 0xa4, + 0x90, 0xf9, 0xaa, 0x0c, 0x3e, 0x2c, 0x10, 0xc3, 0x75, 0x10 }}, + { 2251, // 602 + { 0xa8, 0x80, 0x30, 0x7e, 0x3a, 0xfa, 0xd2, 0x4f, 0x45, 0xdd, + 0xb2, 0xf5, 0x8c, 0xe4, 0xe2, 0x22, 0x3d, 0x2b, 0x9e, 0x19 }}, + { 2254, // 603 + { 0xa8, 0x8f, 0xbe, 0x6d, 0xb5, 0x01, 0xaf, 0xfe, 0x36, 0x94, + 0xad, 0x3b, 0xc8, 0x5e, 0x26, 0x01, 0x9c, 0xc9, 0x8f, 0x9b }}, + { 2257, // 604 + { 0xa9, 0x5a, 0x20, 0x6c, 0x39, 0xd8, 0x8e, 0x60, 0xd7, 0xe5, + 0xfa, 0x0b, 0x92, 0x1f, 0x60, 0xb9, 0xcc, 0x08, 0x38, 0x67 }}, + { 2260, // 605 + { 0xa9, 0x6b, 0xa9, 0xa7, 0x9a, 0x5c, 0x56, 0x1d, 0x17, 0x11, + 0xd6, 0x64, 0xd5, 0x94, 0xc3, 0xec, 0x97, 0x28, 0xa4, 0xdf }}, + { 2263, // 606 + { 0xaa, 0x16, 0x13, 0x9b, 0x17, 0x60, 0x23, 0xdd, 0x44, 0xaa, + 0x4e, 0x1f, 0xd2, 0xd1, 0xc2, 0x23, 0xdb, 0xb0, 0x1a, 0x6a }}, + { 2266, // 607 + { 0xaa, 0x16, 0xf1, 0xf5, 0x29, 0xe9, 0xa3, 0x49, 0x48, 0x64, + 0xc5, 0x56, 0xd6, 0x42, 0x17, 0x8b, 0xb3, 0x0b, 0xf0, 0x9d }}, + { 2269, // 608 + { 0xaa, 0x6b, 0xdb, 0xaf, 0xe2, 0xe2, 0x0a, 0x3f, 0x8b, 0xb0, + 0xe7, 0x71, 0xf4, 0xb3, 0x87, 0x03, 0xf9, 0x24, 0x4e, 0x85 }}, + { 2272, // 609 + { 0xaa, 0x7b, 0x16, 0x2d, 0xef, 0x9c, 0xf0, 0xa0, 0x21, 0xc3, + 0xc4, 0x87, 0xd3, 0xf1, 0x88, 0x4e, 0x7e, 0x79, 0x9b, 0xb6 }}, + { 2275, // 610 + { 0xab, 0x1b, 0xc0, 0x1e, 0x2d, 0x44, 0xc7, 0xe9, 0x8d, 0xf3, + 0xad, 0xce, 0xc4, 0xa5, 0x15, 0x7f, 0x41, 0x61, 0x0b, 0xbf }}, + { 2278, // 611 + { 0xab, 0x56, 0x89, 0xd6, 0xb6, 0x07, 0xcb, 0x68, 0x6d, 0x65, + 0x12, 0xe2, 0x9b, 0xb6, 0x67, 0x75, 0x44, 0xc1, 0x1a, 0xe8 }}, + { 2281, // 612 + { 0xab, 0xb0, 0x8f, 0x2a, 0xfe, 0xc0, 0x6c, 0x8a, 0x6a, 0xf8, + 0xc9, 0xbb, 0xdf, 0x09, 0xdc, 0x81, 0x33, 0x24, 0x78, 0xd5 }}, + { 2284, // 613 + { 0xab, 0xd8, 0x73, 0x88, 0xe7, 0x5c, 0xb5, 0xc9, 0xf8, 0x78, + 0x7c, 0xa5, 0xb8, 0x66, 0x74, 0x24, 0x31, 0xe9, 0x72, 0x62 }}, + { 2287, // 614 + { 0xac, 0x16, 0x76, 0xdf, 0x73, 0x5b, 0xdc, 0x88, 0x75, 0x5e, + 0x9e, 0x06, 0xe9, 0x15, 0x76, 0x75, 0xc6, 0xcd, 0xd3, 0x9a }}, + { 2289, // 615 + { 0xac, 0xf5, 0x20, 0x80, 0xae, 0x20, 0x5b, 0x79, 0xbd, 0x5b, + 0x01, 0x14, 0x73, 0xd1, 0xfa, 0x3c, 0x56, 0xb4, 0xe8, 0x6e }}, + { 2292, // 616 + { 0xad, 0x11, 0xf6, 0x69, 0xbb, 0x43, 0xf3, 0x3d, 0x10, 0x30, + 0x84, 0x09, 0xd3, 0x88, 0xdc, 0x05, 0xc0, 0x73, 0x6f, 0x71 }}, + { 2295, // 617 + { 0xad, 0x4a, 0x77, 0x5b, 0xf7, 0x70, 0x55, 0x95, 0x05, 0xdd, + 0x3c, 0x55, 0xc5, 0x0f, 0xf0, 0x4d, 0xcb, 0x8f, 0xfd, 0x67 }}, + { 2300, // 618 + { 0xad, 0x69, 0x99, 0x71, 0xba, 0x8e, 0xbd, 0x53, 0xe3, 0xff, + 0x8c, 0x6d, 0x05, 0x00, 0xe4, 0xf9, 0x1d, 0xad, 0xc1, 0xf8 }}, + { 2303, // 619 + { 0xad, 0xf6, 0x8a, 0x97, 0x3a, 0xdd, 0x78, 0x2f, 0xbc, 0x6a, + 0x24, 0xd6, 0xbc, 0x36, 0xaf, 0x1d, 0xd1, 0x19, 0x11, 0x89 }}, + { 2306, // 620 + { 0xae, 0x83, 0x9a, 0xf3, 0x6f, 0x2b, 0x6e, 0x1e, 0x69, 0xa1, + 0x6e, 0x27, 0x3b, 0x4b, 0xa8, 0x71, 0xe2, 0x5c, 0x74, 0x73 }}, + { 2309, // 621 + { 0xae, 0xd0, 0x4a, 0xdf, 0x14, 0xdb, 0xb5, 0x4b, 0xc2, 0xf6, + 0xb7, 0xc3, 0x97, 0x7e, 0xcb, 0x0a, 0xdc, 0xc3, 0x6d, 0xad }}, + { 2312, // 622 + { 0xaf, 0x1f, 0x9e, 0xa6, 0x53, 0x38, 0x39, 0xa7, 0x12, 0x60, + 0xa1, 0x7e, 0x85, 0x8c, 0x9b, 0x51, 0x91, 0xde, 0xc2, 0x1d }}, + { 2315, // 623 + { 0xaf, 0x49, 0xcd, 0xff, 0xb1, 0x46, 0x22, 0xbf, 0x95, 0x4e, + 0x26, 0xfa, 0xa5, 0x73, 0x84, 0x01, 0xd9, 0xdf, 0x06, 0xc7 }}, + { 2318, // 624 + { 0xaf, 0x5b, 0x5c, 0x66, 0x90, 0x68, 0xf2, 0xbb, 0xfd, 0xc1, + 0x2b, 0x67, 0x78, 0xd7, 0x6e, 0xec, 0x85, 0x00, 0x91, 0xa2 }}, + { 2321, // 625 + { 0xaf, 0xb8, 0x56, 0x8a, 0x31, 0x90, 0x38, 0x23, 0x2e, 0x75, + 0xf6, 0xcf, 0x7f, 0x2c, 0x96, 0x65, 0x32, 0x8b, 0x20, 0x89 }}, + { 2324, // 626 + { 0xb0, 0x03, 0xda, 0x5b, 0x38, 0x57, 0xc4, 0x04, 0xe4, 0x31, + 0xe0, 0x5f, 0xe0, 0xad, 0xe1, 0xba, 0xe0, 0x9a, 0xc0, 0xd6 }}, + { 2327, // 627 + { 0xb0, 0xc8, 0x9f, 0xde, 0xc6, 0xfe, 0xda, 0xa3, 0xea, 0xe9, + 0x60, 0xde, 0x53, 0x91, 0x77, 0x02, 0x53, 0xd4, 0xeb, 0x09 }}, + { 2330, // 628 + { 0xb0, 0xdc, 0x47, 0x21, 0xb8, 0xa8, 0x2a, 0x97, 0x03, 0x6c, + 0x1c, 0x6e, 0x81, 0x5a, 0x14, 0x8b, 0xb9, 0x1b, 0x78, 0x81 }}, + { 2333, // 629 + { 0xb0, 0xfa, 0xbc, 0x09, 0x32, 0x10, 0x40, 0xc5, 0x91, 0x94, + 0x18, 0xf5, 0x43, 0xc3, 0x77, 0x78, 0x57, 0xd8, 0xde, 0x82 }}, + { 2336, // 630 + { 0xb1, 0x4e, 0xb7, 0xec, 0x80, 0xd9, 0xf8, 0xbc, 0x8e, 0xa1, + 0x9a, 0x24, 0xfd, 0x2c, 0x0f, 0xf1, 0xae, 0xa2, 0x26, 0x0c }}, + { 2339, // 631 + { 0xb1, 0x5c, 0x53, 0x40, 0xb1, 0x4b, 0xfc, 0xbb, 0x21, 0x44, + 0x2c, 0x21, 0x7e, 0x57, 0x04, 0x23, 0x99, 0x75, 0x9a, 0x67 }}, + { 2342, // 632 + { 0xb1, 0x69, 0xe8, 0x42, 0x50, 0x3e, 0x28, 0x86, 0xc1, 0xce, + 0xf6, 0x19, 0x93, 0xbf, 0x8b, 0xd6, 0x4e, 0xfb, 0x45, 0xa3 }}, + { 2353, // 633 + { 0xb1, 0xa7, 0xbe, 0x66, 0x1f, 0x77, 0x7a, 0xa9, 0x34, 0x9e, + 0x70, 0xbe, 0x58, 0xc1, 0xe3, 0xbb, 0xa5, 0x55, 0x69, 0x3c }}, + { 2356, // 634 + { 0xb2, 0x06, 0xbf, 0x64, 0x23, 0xf9, 0x9c, 0xd6, 0xd1, 0xd0, + 0x66, 0x32, 0x7a, 0xcb, 0xb7, 0xa3, 0x97, 0xbc, 0xc3, 0x7a }}, + { 2363, // 635 + { 0xb2, 0x0d, 0x7f, 0x6b, 0x40, 0x9e, 0x7b, 0x45, 0x5f, 0x18, + 0x39, 0xe6, 0x49, 0x03, 0xe6, 0x37, 0xc1, 0x6f, 0x69, 0x61 }}, + { 2366, // 636 + { 0xb2, 0x1f, 0x3e, 0x47, 0x82, 0x4c, 0xbe, 0xfa, 0xa0, 0x34, + 0x28, 0x9b, 0xc9, 0xca, 0x13, 0x3f, 0x67, 0xf0, 0xcb, 0x7d }}, + { 2369, // 637 + { 0xb2, 0xb6, 0xa8, 0xe8, 0x89, 0xd8, 0xcd, 0x00, 0x89, 0xc0, + 0xec, 0x66, 0x2e, 0xdc, 0x51, 0xfd, 0x76, 0x97, 0x8c, 0x47 }}, + { 2372, // 638 + { 0xb2, 0xc5, 0x51, 0x3f, 0xe3, 0x8c, 0xd8, 0x80, 0xa0, 0xf0, + 0x24, 0xb8, 0x7c, 0x96, 0x6b, 0xa7, 0xda, 0x15, 0xed, 0x66 }}, + { 2377, // 639 + { 0xb3, 0x1e, 0x48, 0x13, 0x02, 0xf6, 0xdb, 0x28, 0xfa, 0xb5, + 0x64, 0xe7, 0x59, 0x5e, 0x3a, 0x77, 0x5c, 0x06, 0xe9, 0xb0 }}, + { 2380, // 640 + { 0xb3, 0x69, 0x94, 0x7e, 0xfb, 0xeb, 0xef, 0x97, 0xa1, 0x33, + 0x26, 0xe7, 0xff, 0x17, 0xd0, 0x9d, 0x9b, 0xf1, 0xc9, 0xe2 }}, + { 2382, // 641 + { 0xb3, 0x86, 0x55, 0xf4, 0xdd, 0xa9, 0xae, 0x3e, 0x5d, 0x3e, + 0x78, 0xed, 0xdb, 0x67, 0xbe, 0x80, 0xfe, 0x9e, 0xd2, 0x9b }}, + { 2384, // 642 + { 0xb3, 0x97, 0x1a, 0x12, 0x4b, 0x4a, 0xbe, 0x5c, 0x5f, 0xb7, + 0x0e, 0x35, 0xd9, 0x51, 0x2d, 0x51, 0xda, 0xc7, 0x89, 0x0b }}, + { 2387, // 643 + { 0xb3, 0xbf, 0xdf, 0x6c, 0xeb, 0xc9, 0xa0, 0xdb, 0xb5, 0x4b, + 0x7e, 0x36, 0xf2, 0xbd, 0xd8, 0xdc, 0x4d, 0x77, 0x82, 0xd9 }}, + { 2390, // 644 + { 0xb3, 0xce, 0xd6, 0x9a, 0x75, 0xc5, 0x31, 0x0c, 0x69, 0xcd, + 0x85, 0xbd, 0x2b, 0xa8, 0x3f, 0x62, 0x92, 0x9c, 0xc1, 0x1b }}, + { 2393, // 645 + { 0xb4, 0x67, 0x3c, 0xe5, 0xda, 0xfc, 0xc7, 0x55, 0xd3, 0xca, + 0x92, 0x34, 0x6e, 0xb9, 0xf0, 0x76, 0x88, 0xff, 0x7e, 0xe0 }}, + { 2396, // 646 + { 0xb4, 0xc4, 0x80, 0xdd, 0x2a, 0x1d, 0x66, 0x34, 0x76, 0x9f, + 0x90, 0x0d, 0x93, 0xef, 0x43, 0x27, 0xca, 0x51, 0x2f, 0xb2 }}, + { 2398, // 647 + { 0xb4, 0xd8, 0xb8, 0x3b, 0x22, 0x2a, 0x66, 0x30, 0x48, 0xe9, + 0xbf, 0x58, 0xd5, 0x37, 0xdf, 0x4c, 0x03, 0x10, 0xd9, 0x15 }}, + { 2401, // 648 + { 0xb5, 0x6d, 0x55, 0xa5, 0x21, 0x9c, 0x73, 0x17, 0x67, 0xa3, + 0x70, 0x46, 0x05, 0x31, 0x7b, 0xbe, 0xa7, 0xe5, 0x07, 0x96 }}, + { 2404, // 649 + { 0xb5, 0xdb, 0xca, 0x20, 0xaa, 0x1c, 0xc1, 0xb8, 0xd9, 0x23, + 0x96, 0x56, 0xd4, 0x84, 0x45, 0x08, 0x2e, 0x72, 0x2c, 0xdb }}, + { 2407, // 650 + { 0xb5, 0xe6, 0xc6, 0x56, 0x3e, 0x95, 0x60, 0x7c, 0xfc, 0x10, + 0xe9, 0xa1, 0x79, 0xda, 0xd6, 0xb4, 0x39, 0x91, 0x0c, 0x5d }}, + { 2410, // 651 + { 0xb6, 0x1c, 0xb3, 0xae, 0xd5, 0xa3, 0xc1, 0xa2, 0xf9, 0x2d, + 0x68, 0x07, 0xca, 0x51, 0x8a, 0xb7, 0x7f, 0xe8, 0x45, 0xbb }}, + { 2413, // 652 + { 0xb6, 0xde, 0x6c, 0x62, 0x6c, 0x55, 0x1e, 0x8d, 0x0a, 0xf3, + 0x4c, 0x88, 0xa6, 0xf2, 0x68, 0xb6, 0x81, 0x2a, 0x78, 0xdb }}, + { 2418, // 653 + { 0xb7, 0x38, 0x71, 0xc6, 0xca, 0xbf, 0x0a, 0xf2, 0x83, 0xa9, + 0x49, 0x5a, 0x69, 0x3b, 0xce, 0x32, 0x22, 0x50, 0x9c, 0xed }}, + { 2421, // 654 + { 0xb7, 0xc4, 0x91, 0xc8, 0x26, 0x4d, 0x66, 0x60, 0xed, 0x72, + 0x5b, 0xb2, 0x89, 0xd1, 0xfd, 0x3d, 0xf6, 0xc8, 0x33, 0x5f }}, + { 2424, // 655 + { 0xb8, 0x30, 0x4b, 0x54, 0xec, 0x8b, 0x5c, 0x74, 0x29, 0x0a, + 0xb4, 0xcc, 0xdc, 0xd2, 0x2e, 0x4b, 0x30, 0x73, 0x3b, 0x43 }}, + { 2427, // 656 + { 0xb8, 0x90, 0x01, 0xe5, 0xb0, 0x3f, 0xa3, 0x75, 0x97, 0x53, + 0xaa, 0xe5, 0xcc, 0x51, 0xb4, 0xa8, 0x6b, 0x7e, 0xe3, 0x9c }}, + { 2430, // 657 + { 0xb9, 0x34, 0x1c, 0xec, 0xed, 0x4b, 0xab, 0xf4, 0xaa, 0xeb, + 0xa8, 0x3a, 0x60, 0xe7, 0x98, 0xc7, 0xc4, 0xd8, 0xd0, 0xb5 }}, + { 2433, // 658 + { 0xb9, 0xec, 0xa0, 0x2b, 0xbb, 0xab, 0xf9, 0x0d, 0x17, 0xf0, + 0xf7, 0xfe, 0xc7, 0x8c, 0x19, 0xab, 0x39, 0x6b, 0xf6, 0xb1 }}, + { 2436, // 659 + { 0xba, 0xc3, 0x5e, 0xab, 0xa0, 0x09, 0xa6, 0xba, 0xcc, 0x0d, + 0x5d, 0xfc, 0x48, 0x90, 0xe9, 0x70, 0x35, 0x8b, 0x6c, 0xe6 }}, + { 2442, // 660 + { 0xbb, 0x56, 0xca, 0x4a, 0x61, 0x34, 0x53, 0xea, 0x7f, 0xf1, + 0xf0, 0x29, 0x51, 0x6f, 0x9a, 0xd5, 0x5a, 0x75, 0x59, 0x69 }}, + { 2445, // 661 + { 0xbc, 0x12, 0xb2, 0x13, 0x95, 0x08, 0x85, 0x82, 0x38, 0x56, + 0x71, 0x5e, 0xf3, 0xbe, 0xc9, 0x50, 0x8f, 0xed, 0x43, 0x30 }}, + { 2448, // 662 + { 0xbc, 0x5b, 0x15, 0xd4, 0xa4, 0x63, 0x6f, 0x16, 0x90, 0x02, + 0x5a, 0xd7, 0xda, 0x8d, 0xc1, 0xc6, 0xba, 0x32, 0x3e, 0xfa }}, + { 2453, // 663 + { 0xbd, 0x04, 0x42, 0x3e, 0xf9, 0xa0, 0x38, 0x43, 0xcd, 0x33, + 0xf8, 0xe8, 0x0b, 0x5a, 0xc6, 0x7a, 0xa0, 0x4a, 0xa7, 0x25 }}, + { 2456, // 664 + { 0xbd, 0x11, 0x3d, 0xbb, 0xd7, 0xd6, 0x95, 0x01, 0x5d, 0xe5, + 0x2a, 0x07, 0x06, 0x37, 0xee, 0x0a, 0xb9, 0xfc, 0x18, 0x69 }}, + { 2459, // 665 + { 0xbd, 0x6f, 0x76, 0xeb, 0x42, 0x63, 0x1b, 0x7f, 0x18, 0x50, + 0x51, 0xd6, 0xd4, 0x76, 0x44, 0x39, 0xab, 0xe0, 0x80, 0x1a }}, + { 2462, // 666 + { 0xbd, 0xa8, 0x68, 0xaf, 0x13, 0xe5, 0x9f, 0x94, 0x3a, 0xbb, + 0xe6, 0xaa, 0xcc, 0x48, 0x99, 0x5c, 0x61, 0x06, 0xbe, 0x46 }}, + { 2465, // 667 + { 0xbd, 0xd2, 0xb7, 0xb7, 0xd3, 0x8d, 0xe7, 0xbc, 0xb7, 0xa2, + 0xbe, 0x69, 0x8d, 0xc9, 0x2b, 0x7d, 0x20, 0x38, 0x81, 0x20 }}, + { 2468, // 668 + { 0xbd, 0xfc, 0x9e, 0x1c, 0xd2, 0xa6, 0x87, 0x5e, 0x62, 0xbb, + 0xc4, 0xa3, 0x39, 0xf9, 0x1d, 0xef, 0xa0, 0xf5, 0x28, 0xfb }}, + { 2471, // 669 + { 0xbe, 0x0a, 0x3b, 0xd2, 0x3d, 0xa3, 0xfd, 0x05, 0x2f, 0x61, + 0xb4, 0xfb, 0xdd, 0x1f, 0x6e, 0x4f, 0x80, 0x9f, 0xa4, 0xfb }}, + { 2474, // 670 + { 0xbe, 0x42, 0xcc, 0xb5, 0xda, 0x04, 0x5b, 0x2a, 0x10, 0xf0, + 0x59, 0xc2, 0x7d, 0x6f, 0x7b, 0xf7, 0xa6, 0x69, 0x1e, 0xa8 }}, + { 2477, // 671 + { 0xbe, 0x60, 0xde, 0x1c, 0xc4, 0x30, 0x4b, 0x7a, 0x22, 0xd4, + 0x9a, 0x24, 0x41, 0xa5, 0xde, 0x19, 0xd9, 0x67, 0xcb, 0xb7 }}, + { 2479, // 672 + { 0xbe, 0x63, 0x8b, 0xc0, 0x41, 0x2a, 0x1f, 0xd3, 0xc6, 0xc8, + 0x2d, 0x1d, 0xea, 0x20, 0x16, 0x74, 0xba, 0xdb, 0x98, 0x04 }}, + { 2482, // 673 + { 0xbe, 0x95, 0xe3, 0xfb, 0xe5, 0xdc, 0x51, 0x81, 0xdc, 0x27, + 0x89, 0x02, 0xae, 0x6a, 0x0a, 0xf3, 0x56, 0xa4, 0x21, 0x0d }}, + { 2484, // 674 + { 0xbe, 0xa9, 0xae, 0xf2, 0xd7, 0xda, 0xdf, 0xe1, 0x2a, 0xf2, + 0xf6, 0x41, 0x3b, 0x21, 0x0f, 0x22, 0x2d, 0x34, 0x0b, 0x9b }}, + { 2487, // 675 + { 0xbe, 0xee, 0xd8, 0x3c, 0xdd, 0xe0, 0xc4, 0x05, 0xb2, 0x71, + 0xee, 0x76, 0x7a, 0x27, 0x46, 0xb0, 0xed, 0xa3, 0x4b, 0x78 }}, + { 2490, // 676 + { 0xbf, 0x12, 0xc2, 0x2a, 0x56, 0x5e, 0x64, 0x93, 0x81, 0x9e, + 0x91, 0x2b, 0x31, 0x24, 0xc7, 0x68, 0x31, 0xd2, 0xa9, 0x66 }}, + { 2493, // 677 + { 0xbf, 0x39, 0xcd, 0x22, 0x9e, 0xcb, 0x53, 0xab, 0xb7, 0x15, + 0x5f, 0x49, 0x08, 0x47, 0xba, 0x87, 0x93, 0xfd, 0xcd, 0xc4 }}, + { 2498, // 678 + { 0xbf, 0x4f, 0x7c, 0x49, 0x2d, 0xe5, 0xcf, 0xf6, 0x3f, 0x0d, + 0xcf, 0x87, 0x65, 0x5a, 0x3b, 0x69, 0xb8, 0xec, 0x4a, 0x31 }}, + { 2501, // 679 + { 0xbf, 0x81, 0xb7, 0x4f, 0x66, 0xad, 0x3f, 0xae, 0x14, 0x81, + 0xf4, 0x4a, 0x56, 0x02, 0xa3, 0x96, 0x58, 0xb8, 0x8e, 0xfe }}, + { 2504, // 680 + { 0xbf, 0x84, 0xfc, 0x63, 0x42, 0xa7, 0x20, 0x50, 0x6c, 0xf9, + 0x5d, 0x3a, 0xb3, 0x17, 0xce, 0xa3, 0xf2, 0xcd, 0x89, 0xf4 }}, + { 2507, // 681 + { 0xbf, 0xc9, 0x6a, 0x97, 0x4c, 0xb9, 0x63, 0xe0, 0x06, 0x21, + 0x1a, 0xde, 0x4b, 0xca, 0xdd, 0x14, 0xaa, 0xec, 0x18, 0xc6 }}, + { 2512, // 682 + { 0xc1, 0x2d, 0x18, 0x6f, 0xb5, 0x19, 0x83, 0x28, 0x0c, 0x5d, + 0x35, 0x96, 0x4f, 0x81, 0x68, 0x02, 0xe7, 0x11, 0x4d, 0x1c }}, + { 2515, // 683 + { 0xc2, 0x02, 0x06, 0x3d, 0x09, 0x6e, 0x69, 0xb6, 0x85, 0xe5, + 0x4b, 0xb4, 0x95, 0x3c, 0xa0, 0x65, 0xa9, 0x40, 0x48, 0xc5 }}, + { 2518, // 684 + { 0xc2, 0x2b, 0xf4, 0x56, 0x6b, 0xef, 0x99, 0x3d, 0x1e, 0xa3, + 0x8b, 0xcc, 0x7f, 0x65, 0xfa, 0x9c, 0x6e, 0xca, 0x82, 0xb6 }}, + { 2521, // 685 + { 0xc2, 0x30, 0xd2, 0x0d, 0x3d, 0x03, 0x9d, 0x00, 0x43, 0xe6, + 0x8d, 0xd3, 0xce, 0xed, 0x13, 0x8a, 0xb1, 0x09, 0xa0, 0xb3 }}, + { 2524, // 686 + { 0xc2, 0x61, 0xc7, 0x44, 0x71, 0x1d, 0xe3, 0x07, 0x27, 0x82, + 0x11, 0x80, 0xc4, 0x3f, 0x0c, 0xe4, 0xb5, 0x60, 0xa5, 0x3e }}, + { 2527, // 687 + { 0xc2, 0x87, 0x5c, 0x7a, 0x4e, 0x08, 0xb6, 0xc9, 0x81, 0x72, + 0xce, 0xc1, 0xbe, 0x70, 0xa3, 0xdb, 0x21, 0xe9, 0x99, 0x64 }}, + { 2530, // 688 + { 0xc2, 0x89, 0x53, 0x8b, 0x5a, 0x5e, 0xfc, 0x7d, 0x66, 0x0a, + 0x7a, 0x4c, 0x8d, 0x32, 0x5e, 0xcf, 0x50, 0xcb, 0x62, 0xb5 }}, + { 2533, // 689 + { 0xc2, 0xb3, 0xa0, 0x5e, 0x1a, 0x44, 0xb6, 0x3c, 0x62, 0x82, + 0x86, 0xb3, 0x84, 0xd0, 0xbe, 0x31, 0x45, 0x89, 0x5a, 0x7e }}, + { 2536, // 690 + { 0xc2, 0xbe, 0x6e, 0xd1, 0x07, 0xd8, 0x95, 0x7c, 0xf2, 0xf2, + 0x17, 0x0a, 0xcb, 0x89, 0x3f, 0x53, 0x5a, 0x52, 0x94, 0x68 }}, + { 2541, // 691 + { 0xc3, 0x0d, 0x10, 0x33, 0x14, 0x60, 0x5a, 0xa9, 0x07, 0x53, + 0xa3, 0x18, 0x66, 0xda, 0x8b, 0x11, 0x98, 0xd6, 0x5f, 0x28 }}, + { 2544, // 692 + { 0xc3, 0x24, 0x15, 0x8f, 0xa1, 0x71, 0x5a, 0xbc, 0x08, 0xf9, + 0x79, 0xe3, 0x92, 0xca, 0xbf, 0x9c, 0xa4, 0xd0, 0x66, 0xf8 }}, + { 2547, // 693 + { 0xc3, 0x6a, 0x79, 0xa2, 0x84, 0x27, 0x29, 0x51, 0x2b, 0xc5, + 0xf0, 0x1c, 0x44, 0x66, 0x9d, 0xf6, 0xc1, 0xb3, 0x2d, 0x99 }}, + { 2550, // 694 + { 0xc3, 0x6c, 0x7b, 0xa2, 0x76, 0xbd, 0x80, 0x59, 0xf7, 0xaa, + 0xff, 0xec, 0xa5, 0x80, 0x52, 0x00, 0xba, 0x8d, 0xb1, 0x73 }}, + { 2555, // 695 + { 0xc3, 0x7c, 0x2a, 0x7c, 0xfb, 0x95, 0x1d, 0x93, 0x52, 0x41, + 0x3c, 0xd7, 0xc0, 0x36, 0xe6, 0xa0, 0xbb, 0x99, 0xc0, 0xd6 }}, + { 2558, // 696 + { 0xc3, 0xc4, 0xbb, 0x66, 0xc8, 0x1a, 0x01, 0x90, 0x88, 0x03, + 0x5b, 0x8a, 0x36, 0xcc, 0xaa, 0x04, 0x82, 0xfd, 0x80, 0x9f }}, + { 2561, // 697 + { 0xc3, 0xe3, 0xdb, 0x20, 0x99, 0x56, 0xbd, 0x60, 0x08, 0xd8, + 0x5e, 0xe4, 0x74, 0x35, 0xcb, 0xa4, 0x50, 0xed, 0x18, 0xed }}, + { 2564, // 698 + { 0xc4, 0xa3, 0xaf, 0x06, 0x83, 0x1e, 0x43, 0x1d, 0xe9, 0xeb, + 0xff, 0x34, 0x25, 0xfa, 0xed, 0x3a, 0x68, 0x74, 0xff, 0xb3 }}, + { 2567, // 699 + { 0xc4, 0xb6, 0x28, 0xc3, 0xce, 0x7b, 0x28, 0xc6, 0x8a, 0x09, + 0xb5, 0x32, 0xa2, 0x00, 0x2c, 0xf5, 0xb3, 0xc6, 0x30, 0xbc }}, + { 2576, // 700 + { 0xc4, 0xb7, 0x85, 0x28, 0x76, 0x25, 0x55, 0xdd, 0x14, 0x70, + 0xd8, 0x89, 0x02, 0xcb, 0xfd, 0xf9, 0xad, 0x14, 0xc9, 0x1a }}, + { 2579, // 701 + { 0xc4, 0xef, 0x76, 0x5c, 0x29, 0x0c, 0xa4, 0x28, 0x87, 0xc9, + 0xe6, 0x9e, 0x82, 0x1b, 0x6c, 0x07, 0xec, 0x54, 0xac, 0xbe }}, + { 2592, // 702 + { 0xc5, 0x3f, 0xa0, 0xa5, 0x03, 0x56, 0x13, 0xb0, 0xba, 0xcd, + 0x2a, 0x0f, 0x59, 0x87, 0x90, 0x92, 0xd2, 0x2b, 0x23, 0x83 }}, + { 2595, // 703 + { 0xc7, 0x15, 0x38, 0xc1, 0xb8, 0xaf, 0x66, 0xe7, 0x5f, 0x9d, + 0x33, 0x5c, 0xe9, 0x1a, 0xaa, 0xe3, 0x34, 0x9c, 0x41, 0x20 }}, + { 2598, // 704 + { 0xc7, 0x22, 0x65, 0xad, 0x6d, 0x70, 0xcd, 0x16, 0xde, 0x49, + 0xa2, 0xcd, 0xa1, 0x69, 0xe2, 0x1d, 0xa8, 0x1c, 0xe3, 0xb2 }}, + { 2601, // 705 + { 0xc7, 0x8f, 0x6a, 0xc2, 0x77, 0x80, 0x48, 0x37, 0x86, 0xa0, + 0x29, 0x73, 0xde, 0xed, 0x49, 0xe8, 0x8b, 0x32, 0x5e, 0x68 }}, + { 2604, // 706 + { 0xc8, 0x52, 0x32, 0x82, 0x3f, 0x07, 0xf4, 0x48, 0x31, 0xf9, + 0xa8, 0x5d, 0xd9, 0x47, 0xf1, 0xef, 0x44, 0xb5, 0xb8, 0xde }}, + { 2607, // 707 + { 0xc8, 0xa2, 0x18, 0xa8, 0x89, 0xaf, 0x7a, 0x16, 0x83, 0x97, + 0x89, 0xb5, 0x60, 0xf8, 0x6b, 0xf9, 0x14, 0xa0, 0x62, 0x2a }}, + { 2610, // 708 + { 0xc9, 0x7c, 0x88, 0xda, 0xd0, 0xd2, 0x50, 0xa3, 0xa2, 0xf5, + 0x98, 0xa1, 0xc5, 0x35, 0xa7, 0x69, 0xd6, 0xee, 0xab, 0xc0 }}, + { 2613, // 709 + { 0xc9, 0x90, 0x40, 0x14, 0x44, 0x10, 0x4d, 0xd8, 0x72, 0xac, + 0x58, 0x7f, 0x18, 0x24, 0xe6, 0xd7, 0x6f, 0xbf, 0xce, 0xe5 }}, + { 2619, // 710 + { 0xc9, 0xa3, 0x46, 0x41, 0xa1, 0x46, 0x84, 0x6e, 0x8b, 0x83, + 0x46, 0x35, 0x15, 0x0c, 0x36, 0x20, 0x9c, 0xcd, 0xf7, 0x40 }}, + { 2622, // 711 + { 0xc9, 0xa5, 0x05, 0x2c, 0xd0, 0x4e, 0x63, 0x8e, 0xff, 0xdd, + 0x0c, 0x9f, 0x04, 0x2e, 0x96, 0x25, 0x04, 0x1a, 0x6a, 0xa9 }}, + { 2625, // 712 + { 0xca, 0x0b, 0x6f, 0xf3, 0x4c, 0x4d, 0x99, 0x67, 0x75, 0x09, + 0x54, 0x61, 0x16, 0x6a, 0xf1, 0xf4, 0x1d, 0xc0, 0x5a, 0x99 }}, + { 2628, // 713 + { 0xca, 0xf8, 0xa4, 0x6b, 0x27, 0xa2, 0x34, 0xfa, 0x1c, 0x3d, + 0x99, 0x71, 0x85, 0x1b, 0x0e, 0xd2, 0x45, 0x07, 0xba, 0x39 }}, + { 2631, // 714 + { 0xca, 0xfa, 0x81, 0x84, 0x92, 0x8a, 0x30, 0x10, 0x93, 0x92, + 0xc5, 0x15, 0x66, 0xa8, 0xc9, 0x5d, 0xe9, 0x41, 0xa6, 0x9b }}, + { 2636, // 715 + { 0xca, 0xfc, 0x6e, 0xc9, 0x5a, 0xe4, 0x40, 0xc8, 0x38, 0x2c, + 0xa2, 0xf4, 0x22, 0xca, 0xd8, 0xa6, 0x4a, 0x75, 0x7a, 0x91 }}, + { 2639, // 716 + { 0xcb, 0x44, 0xa5, 0x87, 0x8a, 0x05, 0x75, 0xbc, 0x75, 0xf1, + 0xc4, 0xe9, 0x91, 0xca, 0x52, 0x48, 0xec, 0xe6, 0x68, 0x03 }}, + { 2662, // 717 + { 0xcb, 0xaa, 0xb7, 0x4a, 0x6c, 0x54, 0x3b, 0xc0, 0x78, 0x7a, + 0xee, 0x89, 0x23, 0xf3, 0xa4, 0x57, 0xd3, 0xf1, 0x52, 0xfb }}, + { 2665, // 718 + { 0xcb, 0xc3, 0xcd, 0x35, 0x26, 0x85, 0x7a, 0xbc, 0x85, 0x3f, + 0x76, 0xc2, 0x6a, 0x6a, 0x6d, 0x75, 0x40, 0x01, 0x1e, 0x47 }}, + { 2668, // 719 + { 0xcc, 0x25, 0xc1, 0x3f, 0x5f, 0xe0, 0x5a, 0xc6, 0x4a, 0x4b, + 0x4d, 0x31, 0x51, 0xd7, 0xa0, 0x5e, 0x4d, 0x40, 0x0c, 0xb0 }}, + { 2671, // 720 + { 0xcc, 0x81, 0x5b, 0xa3, 0x04, 0xaf, 0x90, 0xef, 0x58, 0x65, + 0x73, 0x36, 0x87, 0x7b, 0xea, 0x29, 0x22, 0x3e, 0x32, 0xee }}, + { 2674, // 721 + { 0xcc, 0x92, 0xf6, 0xd2, 0x12, 0x2b, 0xf0, 0x04, 0xb6, 0xb6, + 0x2a, 0x6f, 0x84, 0xc9, 0xd0, 0xcf, 0xf5, 0x01, 0x3f, 0xdd }}, + { 2677, // 722 + { 0xcd, 0x29, 0xcb, 0x60, 0xe3, 0x0d, 0xba, 0x7e, 0x93, 0x91, + 0x81, 0x40, 0xcc, 0xb6, 0xff, 0x58, 0x22, 0x5e, 0x08, 0x42 }}, + { 2680, // 723 + { 0xcd, 0x2c, 0x15, 0xd7, 0xa0, 0x7a, 0x16, 0xe3, 0xed, 0x8c, + 0x82, 0x71, 0x9f, 0x5f, 0xb4, 0xb6, 0xb3, 0x06, 0xb1, 0xc9 }}, + { 2683, // 724 + { 0xcd, 0x47, 0xb6, 0x83, 0xa0, 0x6f, 0x4e, 0xed, 0x02, 0x01, + 0xf1, 0x5c, 0x9a, 0x6b, 0x99, 0xf0, 0xd4, 0x4b, 0xd5, 0x90 }}, + { 2686, // 725 + { 0xcd, 0x70, 0x1c, 0x2d, 0xd6, 0x7f, 0x43, 0x47, 0x67, 0x69, + 0xab, 0x97, 0x43, 0xdf, 0x3a, 0x68, 0xb9, 0x12, 0x45, 0x06 }}, + { 2689, // 726 + { 0xcd, 0x79, 0x0a, 0xbc, 0x80, 0xee, 0xb4, 0x99, 0x6c, 0x1a, + 0x95, 0xb4, 0xbd, 0xf5, 0x3d, 0x48, 0x86, 0x9d, 0xd0, 0x7d }}, + { 2692, // 727 + { 0xcd, 0x84, 0xa4, 0x81, 0x85, 0x0a, 0x00, 0xf6, 0xfe, 0x9e, + 0x31, 0x07, 0xf2, 0x13, 0x5d, 0x68, 0x4c, 0x52, 0x80, 0x03 }}, + { 2695, // 728 + { 0xcd, 0x8f, 0x12, 0xf3, 0xe7, 0x91, 0x18, 0x86, 0xf6, 0x9a, + 0xdc, 0x08, 0x7d, 0x3a, 0x64, 0x2a, 0x2c, 0xec, 0xa0, 0x61 }}, + { 2698, // 729 + { 0xce, 0x39, 0x35, 0x17, 0xcb, 0x7b, 0x62, 0x5d, 0x4a, 0xfa, + 0xec, 0x94, 0x03, 0x32, 0xe8, 0xff, 0x2d, 0xc8, 0xfc, 0xc4 }}, + { 2701, // 730 + { 0xce, 0x6a, 0xe0, 0xf4, 0x68, 0xad, 0x92, 0x97, 0xeb, 0x4e, + 0x90, 0x72, 0x33, 0x3b, 0xd0, 0x3b, 0x4e, 0xdd, 0x44, 0xcb }}, + { 2704, // 731 + { 0xce, 0x8e, 0xfd, 0x66, 0x46, 0x3c, 0xa0, 0xbb, 0x89, 0x69, + 0x26, 0x2e, 0x69, 0x91, 0xec, 0x0d, 0x1f, 0x5f, 0x1e, 0x9c }}, + { 2707, // 732 + { 0xce, 0xaf, 0x83, 0xb3, 0xbd, 0x2b, 0xb7, 0xa0, 0x5c, 0xcf, + 0xe6, 0x77, 0x5b, 0x0d, 0xd0, 0x4d, 0x48, 0x27, 0xc2, 0xc0 }}, + { 2738, // 733 + { 0xce, 0xda, 0x2a, 0x6c, 0xd4, 0x12, 0x20, 0x86, 0x4b, 0xc2, + 0xab, 0xb3, 0x7c, 0x03, 0x9a, 0xae, 0x3c, 0x8e, 0x40, 0xf0 }}, + { 2741, // 734 + { 0xcf, 0x8c, 0x19, 0x75, 0x00, 0x3f, 0x44, 0x8a, 0x22, 0x87, + 0x27, 0x0e, 0xdb, 0x9e, 0x69, 0x5f, 0x7f, 0x28, 0xdf, 0xd9 }}, + { 2744, // 735 + { 0xd1, 0x0a, 0x9b, 0xc0, 0xe0, 0x23, 0xed, 0x28, 0xe6, 0x00, + 0xa4, 0x40, 0x36, 0x3c, 0xa2, 0x4c, 0x5b, 0xf0, 0x33, 0xc7 }}, + { 2747, // 736 + { 0xd1, 0x4b, 0xbd, 0x07, 0x0b, 0x67, 0xbf, 0xa2, 0x09, 0xa7, + 0x75, 0x4f, 0xbf, 0xda, 0xf0, 0x05, 0xcc, 0x89, 0xca, 0x6d }}, + { 2750, // 737 + { 0xd1, 0x4f, 0x81, 0x5b, 0xde, 0x26, 0x10, 0x96, 0x0d, 0xf0, + 0x75, 0xb9, 0xb9, 0xc3, 0x69, 0x52, 0xdc, 0x96, 0x96, 0x9c }}, + { 2753, // 738 + { 0xd1, 0x6a, 0x9f, 0x9e, 0xaf, 0x54, 0xa3, 0x49, 0x67, 0xdf, + 0xbc, 0x2b, 0xeb, 0xa2, 0xf0, 0xc5, 0x1c, 0x5f, 0xcb, 0x43 }}, + { 2756, // 739 + { 0xd1, 0xf4, 0x64, 0xcf, 0x3d, 0xc2, 0xbd, 0xe8, 0xc4, 0x0e, + 0xbb, 0x21, 0xb4, 0x76, 0xd4, 0xa2, 0xa6, 0xf9, 0x2e, 0x22 }}, + { 2760, // 740 + { 0xd1, 0xff, 0xe1, 0x3e, 0x57, 0x55, 0x08, 0xe9, 0x28, 0xa7, + 0xdd, 0x8c, 0x04, 0x3e, 0xc4, 0xbb, 0xf2, 0x36, 0x62, 0x5f }}, + { 2763, // 741 + { 0xd2, 0x5c, 0x3a, 0x1a, 0x66, 0x18, 0xe0, 0x6d, 0xe1, 0xb5, + 0x1e, 0x4c, 0x17, 0xa3, 0x50, 0x90, 0xdd, 0xdb, 0x33, 0x7b }}, + { 2766, // 742 + { 0xd2, 0xc6, 0x90, 0x8f, 0x17, 0x48, 0x2b, 0x41, 0x5e, 0x2b, + 0xe7, 0xe4, 0x25, 0x8c, 0xc3, 0xd2, 0xba, 0xdc, 0x54, 0x09 }}, + { 2769, // 743 + { 0xd2, 0xce, 0x72, 0x52, 0x1e, 0x0e, 0x85, 0x0a, 0x5a, 0x59, + 0x7f, 0xaa, 0x90, 0x0b, 0x07, 0x71, 0x85, 0xc8, 0xe4, 0x0b }}, + { 2772, // 744 + { 0xd2, 0xe1, 0x0f, 0xcc, 0x18, 0x06, 0x8d, 0x6c, 0xe9, 0x27, + 0x79, 0x17, 0x0e, 0x85, 0x63, 0xfd, 0xbd, 0x4a, 0x15, 0x11 }}, + { 2775, // 745 + { 0xd3, 0x09, 0x41, 0xd1, 0xe2, 0x5f, 0x5d, 0x3d, 0x67, 0x0f, + 0x8c, 0xe9, 0xc7, 0x97, 0x99, 0x20, 0x9c, 0xca, 0x19, 0x79 }}, + { 2777, // 746 + { 0xd3, 0x28, 0xd3, 0x5d, 0xfb, 0xe0, 0x05, 0x74, 0x7c, 0xbd, + 0x01, 0xd5, 0x4b, 0xdd, 0xa4, 0xa6, 0xa1, 0x7a, 0x68, 0x7d }}, + { 2780, // 747 + { 0xd3, 0x7a, 0x62, 0x9d, 0xc6, 0xd6, 0x93, 0x98, 0xc7, 0x6e, + 0x1d, 0x09, 0x4e, 0x57, 0x20, 0xea, 0xb5, 0x81, 0x3d, 0xcc }}, + { 2783, // 748 + { 0xd4, 0x4d, 0x7c, 0x71, 0xdb, 0xf2, 0xe3, 0x23, 0x11, 0xc4, + 0xe4, 0xd0, 0x6c, 0x79, 0x06, 0x34, 0x5d, 0x45, 0x30, 0x7a }}, + { 2786, // 749 + { 0xd4, 0xaf, 0xfd, 0x18, 0x56, 0x4e, 0xcc, 0x73, 0xbc, 0x82, + 0x92, 0x98, 0xfe, 0x2b, 0xaa, 0xbc, 0xb5, 0x87, 0x0f, 0x77 }}, + { 2789, // 750 + { 0xd5, 0x0f, 0xa4, 0x76, 0xcd, 0xa3, 0xe3, 0x01, 0xcf, 0xc0, + 0xfe, 0x43, 0xec, 0x29, 0x04, 0xf8, 0xdc, 0x53, 0x1c, 0x80 }}, + { 2792, // 751 + { 0xd5, 0x1a, 0xf2, 0x1f, 0x93, 0x04, 0x82, 0x6b, 0x2d, 0x92, + 0x21, 0x81, 0xe2, 0x75, 0x3a, 0x47, 0xb0, 0x25, 0x13, 0x25 }}, + { 2795, // 752 + { 0xd5, 0x45, 0xba, 0x90, 0x84, 0x87, 0x1b, 0x76, 0x04, 0x7b, + 0x5e, 0xda, 0xcc, 0x9f, 0x3f, 0x91, 0x88, 0xdd, 0xe8, 0xb1 }}, + { 2798, // 753 + { 0xd5, 0x63, 0x42, 0x03, 0xb3, 0x24, 0xb6, 0x21, 0x8d, 0xb1, + 0x70, 0x69, 0xb3, 0x88, 0x54, 0xb8, 0x5c, 0xde, 0x3d, 0xae }}, + { 2800, // 754 + { 0xd5, 0x8a, 0x89, 0x47, 0xbc, 0x3b, 0x3e, 0xb4, 0xce, 0x7f, + 0x33, 0x4f, 0xbb, 0x41, 0x08, 0x7f, 0x20, 0x33, 0xb1, 0x00 }}, + { 2802, // 755 + { 0xd6, 0x29, 0x4d, 0xaf, 0xa1, 0xf7, 0x36, 0x9f, 0xa1, 0xd6, + 0x7a, 0x61, 0x32, 0xda, 0xe9, 0x18, 0xa6, 0x73, 0x35, 0x9b }}, + { 2805, // 756 + { 0xd6, 0x40, 0xc2, 0xd3, 0x6c, 0x73, 0xef, 0x69, 0x64, 0x8b, + 0x8c, 0x1f, 0xd5, 0x3d, 0xd0, 0xec, 0x9b, 0x84, 0xbf, 0x89 }}, + { 2808, // 757 + { 0xd6, 0x59, 0x6d, 0xa4, 0x3e, 0x57, 0xe6, 0xf8, 0x10, 0x12, + 0x83, 0x98, 0x44, 0x97, 0x3a, 0xfb, 0x4b, 0xb4, 0x74, 0x4a }}, + { 2811, // 758 + { 0xd6, 0x6f, 0x8a, 0x61, 0x32, 0x83, 0x30, 0xff, 0xc6, 0xbb, + 0x43, 0xa1, 0x2b, 0x2b, 0x6e, 0x3c, 0x34, 0x9c, 0x75, 0x7e }}, + { 2814, // 759 + { 0xd6, 0x75, 0x98, 0x99, 0x36, 0xa7, 0x32, 0x22, 0xc7, 0xd5, + 0x8a, 0xfd, 0x8c, 0xc4, 0x80, 0x2a, 0x41, 0xa1, 0x26, 0x0b }}, + { 2817, // 760 + { 0xd7, 0x88, 0x2c, 0xd2, 0x38, 0x79, 0x54, 0xee, 0xe4, 0xf3, + 0xba, 0x4d, 0x1a, 0x7a, 0x31, 0x50, 0xc0, 0xc7, 0x1f, 0x35 }}, + { 2820, // 761 + { 0xd7, 0xad, 0xdc, 0xb9, 0x57, 0xf5, 0x17, 0x18, 0x44, 0x27, + 0xf9, 0x7b, 0x7c, 0x5d, 0x33, 0x08, 0x8e, 0x92, 0x81, 0xe4 }}, + { 2823, // 762 + { 0xd7, 0xbd, 0x4e, 0x79, 0xf4, 0xd0, 0xfe, 0xf2, 0xc5, 0x2e, + 0x2f, 0xc5, 0xf1, 0x50, 0x4b, 0x3b, 0xd4, 0x35, 0x91, 0x3f }}, + { 2826, // 763 + { 0xd7, 0xf0, 0x75, 0xe3, 0x4b, 0xca, 0x33, 0x75, 0x86, 0xb9, + 0x1c, 0xf7, 0xed, 0xde, 0x45, 0xca, 0x9b, 0xfd, 0x5e, 0x2b }}, + { 2829, // 764 + { 0xd8, 0x7a, 0xd0, 0x79, 0xb5, 0x67, 0xf7, 0x54, 0x39, 0x34, + 0xa4, 0x01, 0x5c, 0x6a, 0xd1, 0x3f, 0x7d, 0x77, 0xa6, 0x5a }}, + { 2831, // 765 + { 0xd8, 0x7f, 0x4f, 0x19, 0xec, 0x4b, 0x49, 0xfa, 0x05, 0xd7, + 0xb2, 0x81, 0x59, 0x0e, 0xb3, 0x55, 0x8e, 0x4d, 0x13, 0x8b }}, + { 2834, // 766 + { 0xd8, 0x98, 0x0c, 0xd9, 0xfc, 0xa8, 0x59, 0x8f, 0x3d, 0x93, + 0x52, 0xa7, 0xa2, 0x99, 0x85, 0xed, 0xe9, 0x8c, 0x5f, 0x45 }}, + { 2841, // 767 + { 0xd8, 0xe8, 0x51, 0x06, 0xd3, 0xd4, 0x27, 0x42, 0xcf, 0xc4, + 0x27, 0xdb, 0xee, 0xce, 0xa5, 0x5c, 0xd6, 0x9b, 0xb2, 0x9f }}, + { 2844, // 768 + { 0xda, 0xd4, 0xaf, 0xfb, 0x30, 0x56, 0x7c, 0x8d, 0x02, 0x49, + 0xfc, 0x53, 0x3f, 0x12, 0x2e, 0xaf, 0x8b, 0xf6, 0x47, 0x17 }}, + { 2847, // 769 + { 0xdb, 0x10, 0x59, 0x9d, 0x8a, 0x06, 0x71, 0x69, 0x12, 0xff, + 0xd9, 0x8d, 0x40, 0xe2, 0x5b, 0x64, 0x2b, 0x80, 0xf7, 0x30 }}, + { 2850, // 770 + { 0xdb, 0x5a, 0x76, 0x89, 0xf0, 0x6d, 0x65, 0xb5, 0xce, 0x1d, + 0xcd, 0x05, 0x1f, 0xb6, 0x0e, 0x35, 0x33, 0xb8, 0x01, 0x42 }}, + { 2852, // 771 + { 0xdb, 0x61, 0x86, 0xe4, 0x07, 0x9a, 0x34, 0x2a, 0x1b, 0x4a, + 0x9d, 0xe6, 0xb3, 0xcb, 0xee, 0x26, 0x96, 0x48, 0xbf, 0x15 }}, + { 2859, // 772 + { 0xdb, 0x8e, 0x50, 0x91, 0xde, 0x8c, 0xdb, 0x70, 0x4b, 0x4d, + 0xca, 0x02, 0xea, 0x02, 0xcc, 0xd9, 0x88, 0x28, 0x88, 0x3a }}, + { 2861, // 773 + { 0xdb, 0xfd, 0x55, 0xe5, 0xf1, 0xcc, 0x34, 0xce, 0x40, 0xc6, + 0x82, 0x91, 0x52, 0x68, 0x5a, 0x2b, 0x8e, 0xfe, 0xe5, 0x10 }}, + { 2864, // 774 + { 0xdc, 0x01, 0x50, 0x2c, 0x04, 0xa8, 0x4a, 0x64, 0xa8, 0x61, + 0xd7, 0x72, 0xdd, 0x40, 0x39, 0x29, 0x47, 0xdc, 0x68, 0x09 }}, + { 2869, // 775 + { 0xdc, 0x7e, 0x2e, 0x64, 0x55, 0xed, 0x81, 0x1b, 0x39, 0xf3, + 0x97, 0xb3, 0x0a, 0x97, 0xe9, 0x08, 0x07, 0x03, 0xc8, 0x97 }}, + { 2878, // 776 + { 0xdc, 0xd4, 0xeb, 0x9a, 0xf2, 0xf4, 0x7f, 0x81, 0x4c, 0xfd, + 0x38, 0xcb, 0x2b, 0x30, 0xd4, 0x63, 0x48, 0xc8, 0xa8, 0xeb }}, + { 2881, // 777 + { 0xdc, 0xe1, 0x09, 0xc5, 0x7e, 0x9f, 0xca, 0xfd, 0xb8, 0xeb, + 0x3b, 0x8f, 0x45, 0xdc, 0x19, 0x80, 0x54, 0x39, 0xf3, 0x25 }}, + { 2884, // 778 + { 0xdd, 0x4e, 0x02, 0xeb, 0x4a, 0x90, 0xf3, 0xf0, 0xae, 0x7e, + 0xce, 0xf2, 0x6c, 0xba, 0xb2, 0x54, 0xe8, 0x03, 0xf9, 0xcd }}, + { 2887, // 779 + { 0xdd, 0x69, 0xca, 0x70, 0xc0, 0xad, 0x1e, 0xe9, 0x72, 0xfd, + 0x8e, 0x88, 0x42, 0xe1, 0xd6, 0x15, 0x0a, 0xbd, 0xac, 0x21 }}, + { 2890, // 780 + { 0xdd, 0x87, 0x94, 0x5b, 0xdb, 0xa4, 0xa2, 0x36, 0x07, 0xaf, + 0x55, 0x72, 0x97, 0xae, 0x04, 0x68, 0x60, 0x6e, 0x01, 0xb0 }}, + { 2893, // 781 + { 0xde, 0x84, 0x9b, 0xf2, 0x4c, 0xd3, 0xe9, 0xdb, 0xf4, 0x42, + 0xa5, 0x3d, 0xb1, 0x96, 0xd8, 0x2e, 0x00, 0x7c, 0xdb, 0x18 }}, + { 2896, // 782 + { 0xde, 0xb0, 0x99, 0x9e, 0xa1, 0x49, 0x99, 0xb1, 0xe8, 0x4f, + 0xcc, 0x7e, 0xf3, 0x9a, 0xca, 0xbd, 0xdd, 0x50, 0x85, 0x57 }}, + { 2899, // 783 + { 0xdf, 0x3a, 0xa8, 0x77, 0xa7, 0x9d, 0x54, 0xff, 0xc1, 0x65, + 0xd0, 0x24, 0x9a, 0x1f, 0xeb, 0xce, 0x74, 0x32, 0xe5, 0x3d }}, + { 2902, // 784 + { 0xdf, 0x4e, 0xe5, 0x3a, 0xe0, 0x53, 0x00, 0x5a, 0x93, 0xdf, + 0x5a, 0xbe, 0x3e, 0x14, 0xa6, 0x16, 0xf8, 0xdd, 0x1d, 0x74 }}, + { 2906, // 785 + { 0xdf, 0x5b, 0xea, 0xdf, 0x55, 0xe1, 0x7b, 0x2c, 0x84, 0x8b, + 0x75, 0x3a, 0x64, 0x64, 0x29, 0xe5, 0xd8, 0xea, 0x1a, 0x35 }}, + { 2909, // 786 + { 0xdf, 0xb1, 0x75, 0xb7, 0xe1, 0x3f, 0xd8, 0x78, 0x01, 0x13, + 0xc3, 0x14, 0x8b, 0x5e, 0x40, 0xac, 0xd9, 0x5a, 0x78, 0x7b }}, + { 2912, // 787 + { 0xe0, 0x31, 0x05, 0x7b, 0x48, 0x44, 0x2b, 0x45, 0xdc, 0xcc, + 0xec, 0xd3, 0x26, 0xc8, 0xdc, 0x1e, 0x90, 0xb9, 0x9a, 0x05 }}, + { 2915, // 788 + { 0xe0, 0xe4, 0x79, 0x5f, 0x49, 0x2b, 0xa5, 0x85, 0xf3, 0xeb, + 0xc6, 0x80, 0xca, 0x4c, 0x4e, 0x70, 0x65, 0xc2, 0xe9, 0x4b }}, + { 2918, // 789 + { 0xe0, 0xed, 0x51, 0x97, 0xda, 0x88, 0xe2, 0xda, 0x1b, 0xf0, + 0x66, 0xd5, 0x8e, 0x21, 0x80, 0x8f, 0x04, 0x33, 0x90, 0x48 }}, + { 2921, // 790 + { 0xe1, 0x23, 0x0d, 0xa2, 0x3a, 0xaa, 0x39, 0xa8, 0x33, 0x3b, + 0xc2, 0x50, 0xfc, 0x83, 0xec, 0xff, 0x64, 0x6c, 0xaa, 0xd0 }}, + { 2924, // 791 + { 0xe1, 0x27, 0xc1, 0x8f, 0x8c, 0x2b, 0x64, 0x80, 0xaa, 0xe4, + 0xec, 0xe0, 0xd6, 0xe7, 0x84, 0x2c, 0x81, 0x8d, 0x8a, 0xc2 }}, + { 2927, // 792 + { 0xe1, 0x43, 0x57, 0x85, 0x80, 0xc2, 0xbf, 0x12, 0x18, 0x21, + 0xaf, 0xa5, 0x11, 0x61, 0x7b, 0xd3, 0xec, 0xf4, 0xcd, 0x71 }}, + { 2930, // 793 + { 0xe1, 0x63, 0xaa, 0x7e, 0x03, 0x93, 0xe0, 0x1f, 0xee, 0x3a, + 0x38, 0x94, 0xe6, 0x3d, 0x77, 0x74, 0xc1, 0x8f, 0xed, 0xef }}, + { 2933, // 794 + { 0xe1, 0xbe, 0x59, 0xde, 0xc6, 0x09, 0xd6, 0x77, 0xf6, 0xfc, + 0x25, 0xd9, 0x8e, 0xfd, 0xac, 0xc6, 0xa2, 0xb7, 0x74, 0xf7 }}, + { 2936, // 795 + { 0xe1, 0xc5, 0x7d, 0x1a, 0xf0, 0xdb, 0xe4, 0xa3, 0x72, 0x21, + 0x93, 0x1b, 0xec, 0xbb, 0xd1, 0xb5, 0xb6, 0x37, 0x91, 0xdc }}, + { 2939, // 796 + { 0xe1, 0xd8, 0xa7, 0x90, 0x40, 0x89, 0x5f, 0xf7, 0x6d, 0x72, + 0xa3, 0xfa, 0xb0, 0x21, 0xb4, 0xb4, 0x5c, 0xa2, 0x6b, 0x6d }}, + { 2942, // 797 + { 0xe1, 0xe4, 0xa9, 0x66, 0x5d, 0x1d, 0xc4, 0xca, 0x0c, 0x2b, + 0x50, 0xa6, 0x16, 0x79, 0x26, 0x9f, 0xb8, 0xaa, 0x92, 0xcb }}, + { 2947, // 798 + { 0xe2, 0x3d, 0xd7, 0x34, 0x72, 0x9c, 0x59, 0xa5, 0x56, 0x3a, + 0xb2, 0x6b, 0x9e, 0xab, 0xbf, 0xb9, 0xfe, 0x79, 0xdb, 0x94 }}, + { 2952, // 799 + { 0xe2, 0x6a, 0x8a, 0x99, 0x1b, 0x4b, 0x9c, 0x51, 0x8c, 0xb0, + 0x86, 0xf9, 0x24, 0xe8, 0xd2, 0x7b, 0x24, 0x22, 0xef, 0xee }}, + { 2955, // 800 + { 0xe2, 0x81, 0x03, 0xac, 0x09, 0xc3, 0xa7, 0x75, 0x43, 0xb2, + 0xfe, 0x29, 0xa4, 0xdd, 0x86, 0x51, 0x3c, 0x01, 0x08, 0x28 }}, + { 2958, // 801 + { 0xe2, 0xf0, 0xfb, 0x04, 0x81, 0x61, 0xc4, 0x74, 0xc5, 0x9a, + 0x30, 0x2a, 0xe0, 0xac, 0xc4, 0x5e, 0x0d, 0x8b, 0x28, 0x09 }}, + { 2961, // 802 + { 0xe2, 0xf6, 0x33, 0x7c, 0xae, 0xe9, 0x2e, 0x15, 0x56, 0x70, + 0x0b, 0x8b, 0xcd, 0x23, 0x2d, 0xd7, 0x16, 0x17, 0x6f, 0xcb }}, + { 2964, // 803 + { 0xe3, 0x48, 0x42, 0x05, 0xbd, 0x80, 0xd4, 0x5d, 0x4f, 0xb0, + 0x7c, 0xc9, 0x0b, 0xeb, 0xea, 0x6b, 0x79, 0xd1, 0xbd, 0xaa }}, + { 2966, // 804 + { 0xe3, 0x4e, 0xea, 0x45, 0xd6, 0xb5, 0xe6, 0x7d, 0x02, 0x64, + 0x1f, 0x25, 0xad, 0x4e, 0x7e, 0x4c, 0xa3, 0xa3, 0x4d, 0xf1 }}, + { 2969, // 805 + { 0xe3, 0x84, 0x17, 0x19, 0xc5, 0x38, 0x4b, 0x71, 0xb6, 0xb6, + 0x23, 0x36, 0x11, 0x21, 0x5b, 0xd8, 0x9a, 0x41, 0x72, 0x2d }}, + { 2972, // 806 + { 0xe4, 0x4e, 0x3c, 0x24, 0x4d, 0xce, 0x92, 0xee, 0x5d, 0x58, + 0x6b, 0x09, 0x85, 0x6d, 0x00, 0x63, 0x1c, 0x2d, 0x2f, 0x4d }}, + { 2975, // 807 + { 0xe4, 0x5c, 0xe9, 0xc4, 0x3b, 0xf9, 0xf4, 0x08, 0x24, 0x26, + 0x86, 0xbc, 0x8c, 0x8e, 0x7d, 0x33, 0xa9, 0x4d, 0x62, 0x5e }}, + { 2978, // 808 + { 0xe4, 0x8c, 0xc8, 0x6c, 0xca, 0xd0, 0x07, 0x11, 0x1e, 0x11, + 0x66, 0x6d, 0x33, 0x50, 0x29, 0x64, 0x49, 0x27, 0x0d, 0x2f }}, + { 2983, // 809 + { 0xe4, 0xa9, 0x93, 0xef, 0x2c, 0x24, 0x60, 0x4e, 0xbc, 0x08, + 0x91, 0x53, 0x19, 0x40, 0x07, 0xd0, 0x5f, 0x96, 0x26, 0xae }}, + { 2986, // 810 + { 0xe4, 0xd7, 0x76, 0xcf, 0xcf, 0x2c, 0x02, 0x9b, 0xe6, 0x51, + 0xf8, 0x61, 0xc5, 0x42, 0x4a, 0xe9, 0xab, 0x9e, 0xab, 0x02 }}, + { 2989, // 811 + { 0xe4, 0xef, 0xee, 0x86, 0x53, 0x44, 0x89, 0x59, 0x11, 0xff, + 0xd6, 0xef, 0xa1, 0x7e, 0x66, 0x80, 0x08, 0x18, 0x68, 0x1c }}, + { 2992, // 812 + { 0xe4, 0xf2, 0x91, 0xe2, 0x10, 0x35, 0xb5, 0xc3, 0xb8, 0x09, + 0xfb, 0xc8, 0x37, 0xdd, 0x76, 0xcf, 0x30, 0xba, 0x21, 0xf7 }}, + { 2995, // 813 + { 0xe5, 0x66, 0x17, 0xd6, 0xc0, 0xfe, 0x6f, 0x5b, 0x3c, 0x79, + 0xca, 0xe2, 0x92, 0x02, 0x31, 0x82, 0x71, 0x4a, 0x85, 0x5c }}, + { 2998, // 814 + { 0xe5, 0x7d, 0x30, 0x27, 0xe8, 0xef, 0xc6, 0xa4, 0x43, 0xfd, + 0x6a, 0x81, 0x12, 0xc2, 0xdf, 0xfc, 0x79, 0x32, 0xf7, 0x2e }}, + { 3000, // 815 + { 0xe5, 0x99, 0xa9, 0x0a, 0xe6, 0x7f, 0x0e, 0x1f, 0x7f, 0x8a, + 0xa7, 0x54, 0x65, 0xc7, 0x55, 0xcb, 0x95, 0x71, 0xe9, 0xc9 }}, + { 3003, // 816 + { 0xe5, 0xaa, 0x65, 0x1a, 0x58, 0x57, 0x55, 0xb6, 0x86, 0x68, + 0x46, 0x36, 0xd3, 0x2d, 0xb0, 0x88, 0x87, 0xc6, 0x1c, 0x21 }}, + { 3006, // 817 + { 0xe7, 0x2d, 0x1e, 0x19, 0x88, 0x61, 0xb9, 0xac, 0xf5, 0x84, + 0x5e, 0xee, 0xc2, 0xfa, 0xc6, 0x9f, 0x97, 0x65, 0x95, 0x68 }}, + { 3009, // 818 + { 0xe7, 0x9e, 0x8f, 0x0f, 0xcd, 0x3b, 0x5b, 0x04, 0xff, 0x62, + 0x8e, 0xbc, 0x50, 0xdc, 0x54, 0x65, 0x46, 0xd6, 0x0c, 0xe6 }}, + { 3012, // 819 + { 0xe8, 0x00, 0x03, 0x87, 0x2b, 0xc0, 0x97, 0xcd, 0xd1, 0x48, + 0xd5, 0x8d, 0xca, 0xf4, 0x84, 0xeb, 0xc9, 0x76, 0xbf, 0xd8 }}, + { 3015, // 820 + { 0xe8, 0x2f, 0x49, 0x77, 0xda, 0x47, 0x43, 0x24, 0x2d, 0x3d, + 0x16, 0xae, 0x73, 0xb6, 0xd1, 0x27, 0xe1, 0x4d, 0xe0, 0x22 }}, + { 3018, // 821 + { 0xe8, 0x73, 0x87, 0x07, 0x87, 0x27, 0x62, 0x1d, 0xbb, 0xf7, + 0xf6, 0x17, 0x01, 0x6c, 0x17, 0x09, 0x75, 0x0c, 0xb6, 0x82 }}, + { 3021, // 822 + { 0xe8, 0xd1, 0xd8, 0xef, 0x2d, 0x14, 0xf1, 0xcd, 0xe9, 0x8d, + 0xfc, 0xa0, 0x0f, 0xad, 0xad, 0xdf, 0xb4, 0x0b, 0xa8, 0x9e }}, + { 3025, // 823 + { 0xe8, 0xdb, 0x6f, 0x04, 0xd8, 0x49, 0x40, 0x61, 0x63, 0x87, + 0x2e, 0x77, 0x95, 0x2d, 0xe8, 0x4c, 0xee, 0x0e, 0x33, 0x27 }}, + { 3028, // 824 + { 0xe9, 0x1b, 0xee, 0x0e, 0xd4, 0xc3, 0x49, 0x19, 0x99, 0x37, + 0xbb, 0xe8, 0x33, 0xbc, 0xd2, 0xb2, 0x07, 0x7d, 0x79, 0x83 }}, + { 3031, // 825 + { 0xe9, 0x2e, 0x94, 0xbb, 0x09, 0xeb, 0x61, 0x76, 0x46, 0xe7, + 0xf5, 0x15, 0xb9, 0x3e, 0xf2, 0xdd, 0xe2, 0x67, 0xf5, 0x69 }}, + { 3034, // 826 + { 0xe9, 0x66, 0xc1, 0xed, 0x57, 0x7c, 0xf8, 0x4d, 0xda, 0x73, + 0x3b, 0xe2, 0xc8, 0x35, 0xcd, 0x4c, 0x43, 0x90, 0x70, 0x34 }}, + { 3037, // 827 + { 0xe9, 0x69, 0xcb, 0x8e, 0x40, 0x4a, 0x60, 0x61, 0x2c, 0xa4, + 0x30, 0x41, 0x00, 0x8f, 0x12, 0xc3, 0x4b, 0x2a, 0xb1, 0x4e }}, + { 3040, // 828 + { 0xea, 0x21, 0xa4, 0x8d, 0xe8, 0xe3, 0xa9, 0xe8, 0x90, 0xb2, + 0x66, 0xe4, 0x46, 0xe3, 0x25, 0xf3, 0xd1, 0xf2, 0x7c, 0x0f }}, + { 3043, // 829 + { 0xeb, 0x55, 0xaf, 0x72, 0x17, 0xd8, 0xf9, 0x0f, 0xc9, 0xe9, + 0x60, 0x93, 0x06, 0x8f, 0x6a, 0x31, 0x72, 0xbf, 0x94, 0xd6 }}, + { 3045, // 830 + { 0xec, 0x90, 0x47, 0xc9, 0xcc, 0xe7, 0x24, 0xa5, 0xbf, 0xac, + 0x57, 0xfe, 0xc9, 0xe1, 0x09, 0x29, 0x85, 0xde, 0x51, 0xaa }}, + { 3048, // 831 + { 0xec, 0x95, 0xdf, 0xdb, 0x30, 0xf1, 0x23, 0xf5, 0xe8, 0xdd, + 0x6b, 0x0e, 0x3d, 0x15, 0x10, 0x1a, 0x42, 0x17, 0xb6, 0xc2 }}, + { 3051, // 832 + { 0xec, 0xad, 0x5c, 0x0d, 0xf4, 0xd1, 0x62, 0x03, 0xd8, 0x8b, + 0x4c, 0x65, 0xd5, 0x06, 0x28, 0xc7, 0x8f, 0x2b, 0x14, 0x40 }}, + { 3054, // 833 + { 0xec, 0xce, 0xe6, 0x39, 0x90, 0x2f, 0x90, 0x4c, 0x32, 0x8c, + 0x7e, 0x64, 0x1f, 0x87, 0x77, 0x61, 0x18, 0x22, 0x71, 0x6d }}, + { 3057, // 834 + { 0xed, 0x07, 0xcd, 0x4d, 0x4e, 0x93, 0xe0, 0x8b, 0x89, 0x3e, + 0xe0, 0xba, 0x9d, 0xf3, 0xbe, 0x92, 0x45, 0x5c, 0x6c, 0x88 }}, + { 3060, // 835 + { 0xed, 0x12, 0x88, 0xc5, 0xd6, 0xaf, 0xb0, 0xd4, 0xc9, 0xf2, + 0x08, 0x1c, 0xe6, 0xc0, 0x59, 0xbf, 0xc6, 0xb1, 0x57, 0x34 }}, + { 3063, // 836 + { 0xed, 0x99, 0x31, 0xf7, 0xc1, 0xb7, 0x8e, 0xa4, 0xf9, 0xb4, + 0x71, 0xdd, 0xd0, 0x02, 0x26, 0xbe, 0xbc, 0xfb, 0x07, 0xdf }}, + { 3066, // 837 + { 0xed, 0xb9, 0x52, 0xf8, 0xce, 0x55, 0x4f, 0xba, 0x8c, 0x04, + 0x9e, 0x68, 0xbc, 0x15, 0x9e, 0x53, 0xf3, 0x61, 0x5c, 0x6e }}, + { 3068, // 838 + { 0xed, 0xd8, 0x89, 0x01, 0x5a, 0xc0, 0x06, 0x79, 0xa2, 0xfd, + 0x7d, 0x2d, 0xa3, 0x17, 0xf4, 0x7a, 0x10, 0x46, 0xfb, 0x81 }}, + { 3071, // 839 + { 0xed, 0xde, 0x92, 0xb1, 0xd1, 0xb7, 0xea, 0x76, 0xf3, 0xf6, + 0x41, 0xbf, 0xef, 0x9c, 0x56, 0xd2, 0x29, 0x45, 0x83, 0x5b }}, + { 3074, // 840 + { 0xed, 0xe3, 0x6e, 0x5f, 0x21, 0x7c, 0xb8, 0xdd, 0x83, 0x84, + 0x01, 0x5f, 0x9b, 0xe3, 0x90, 0xd0, 0x94, 0x5b, 0xed, 0x43 }}, + { 3077, // 841 + { 0xee, 0xe5, 0xe8, 0xc5, 0x73, 0xa1, 0x01, 0x4c, 0x11, 0x7e, + 0x98, 0x23, 0x27, 0x86, 0x94, 0xe6, 0x8b, 0x32, 0xb6, 0xe7 }}, + { 3080, // 842 + { 0xef, 0x1c, 0x80, 0x23, 0xba, 0x2b, 0xb9, 0xed, 0x81, 0x8a, + 0x7e, 0xc5, 0xfb, 0xbd, 0x7d, 0x5e, 0x8c, 0x33, 0x7f, 0x63 }}, + { 3086, // 843 + { 0xef, 0x50, 0x09, 0x44, 0x92, 0xba, 0xd6, 0x0c, 0xf9, 0x93, + 0xe0, 0x46, 0x25, 0xaa, 0x61, 0xec, 0x24, 0xf6, 0xfb, 0x37 }}, + { 3089, // 844 + { 0xef, 0xda, 0x6b, 0xc0, 0xce, 0x35, 0x1f, 0xa4, 0xb1, 0xdd, + 0xcf, 0x34, 0x0a, 0xd2, 0x30, 0x13, 0x02, 0x1a, 0xfb, 0x0a }}, + { 3092, // 845 + { 0xf0, 0x22, 0xb5, 0x7f, 0x4c, 0xb3, 0xc9, 0x78, 0x29, 0x82, + 0x82, 0xe1, 0xa3, 0x77, 0x05, 0x20, 0x4f, 0x33, 0xea, 0x97 }}, + { 3095, // 846 + { 0xf0, 0x52, 0x61, 0x16, 0x36, 0x14, 0x39, 0x3d, 0x8c, 0xca, + 0x11, 0x4d, 0x84, 0x76, 0xc9, 0xd1, 0x55, 0xaf, 0x56, 0x98 }}, + { 3098, // 847 + { 0xf0, 0x76, 0x11, 0x9f, 0x33, 0x7b, 0x7b, 0xab, 0x17, 0x02, + 0xb1, 0x49, 0x75, 0x98, 0x32, 0xbc, 0xca, 0x0c, 0xf5, 0xa1 }}, + { 3101, // 848 + { 0xf1, 0x75, 0x07, 0x89, 0x7e, 0x4c, 0x41, 0x5f, 0x11, 0xb0, + 0x84, 0xbe, 0x88, 0x7c, 0x59, 0xf7, 0x07, 0x6c, 0x69, 0x01 }}, + { 3108, // 849 + { 0xf1, 0xb0, 0xe3, 0x9b, 0x7a, 0x6b, 0xc4, 0x7b, 0xfa, 0xf3, + 0x9a, 0xc8, 0xce, 0x61, 0x73, 0xbe, 0xbf, 0x17, 0x69, 0x1c }}, + { 3111, // 850 + { 0xf1, 0xc4, 0xb9, 0x3a, 0x0d, 0xa0, 0x4e, 0x26, 0xc0, 0x21, + 0x25, 0xeb, 0x77, 0x93, 0x60, 0x1c, 0x12, 0x72, 0x18, 0xe0 }}, + { 3114, // 851 + { 0xf2, 0x20, 0x76, 0x65, 0x7c, 0x75, 0x66, 0xa9, 0xbf, 0x08, + 0x49, 0xee, 0x62, 0x12, 0x72, 0x98, 0x37, 0x69, 0x1e, 0x5d }}, + { 3117, // 852 + { 0xf2, 0x43, 0x54, 0x39, 0x0d, 0xd0, 0xb1, 0x74, 0x2f, 0xbe, + 0x96, 0xd7, 0x1c, 0x2c, 0xf0, 0x62, 0xa8, 0xa4, 0xa0, 0x24 }}, + { 3120, // 853 + { 0xf2, 0x5e, 0x76, 0xda, 0xdb, 0x4c, 0xc5, 0xca, 0x38, 0xcf, + 0x8a, 0x4d, 0x5d, 0x24, 0xf4, 0xef, 0x6a, 0x01, 0x30, 0x4f }}, + { 3123, // 854 + { 0xf2, 0x7f, 0xfe, 0xfb, 0xf6, 0xb8, 0xa8, 0xa7, 0x8a, 0xdb, + 0x91, 0x30, 0x44, 0xbc, 0x2a, 0xf1, 0x5d, 0x15, 0x8c, 0xc4 }}, + { 3129, // 855 + { 0xf2, 0x9f, 0x53, 0xb5, 0x0a, 0xc1, 0xcf, 0xa6, 0x7d, 0xa0, + 0xf3, 0x96, 0xb8, 0xa0, 0x6a, 0x17, 0x00, 0x2e, 0x06, 0x92 }}, + { 3151, // 856 + { 0xf2, 0xb3, 0xda, 0x4b, 0x27, 0x2a, 0x1a, 0xe8, 0x1b, 0x40, + 0x66, 0xf0, 0x3a, 0x12, 0xea, 0x45, 0x11, 0xe5, 0x31, 0x30 }}, + { 3154, // 857 + { 0xf2, 0xc0, 0x3d, 0x62, 0xc7, 0xdc, 0x91, 0x03, 0xc2, 0x99, + 0x92, 0x98, 0x71, 0xd3, 0x55, 0xde, 0x20, 0xe7, 0xd4, 0x09 }}, + { 3157, // 858 + { 0xf3, 0x07, 0x5d, 0x07, 0x17, 0x3d, 0x8d, 0x13, 0x83, 0x9d, + 0x99, 0xac, 0x4e, 0x40, 0x7a, 0x6a, 0xaa, 0x83, 0xbc, 0x51 }}, + { 3160, // 859 + { 0xf3, 0x70, 0xf4, 0x04, 0x07, 0xf5, 0x29, 0x5c, 0xb7, 0x45, + 0x6b, 0x7f, 0x99, 0x4c, 0xed, 0x1c, 0xfc, 0x08, 0x87, 0xd4 }}, + { 3163, // 860 + { 0xf3, 0x8c, 0xf2, 0xd2, 0x5e, 0x92, 0xeb, 0x65, 0xc6, 0xdd, + 0x5e, 0xda, 0x4d, 0x6b, 0x24, 0xbe, 0xc3, 0xb4, 0x36, 0xf6 }}, + { 3172, // 861 + { 0xf4, 0x4e, 0xb9, 0x30, 0xa1, 0xa2, 0x39, 0xd8, 0xfa, 0x11, + 0xa8, 0x59, 0x1b, 0xd4, 0xfe, 0x45, 0x5a, 0x09, 0x31, 0x5d }}, + { 3175, // 862 + { 0xf4, 0x85, 0xdb, 0x66, 0xa4, 0xa2, 0xfa, 0xd0, 0xef, 0xac, + 0xec, 0xb7, 0x05, 0x55, 0xe9, 0x89, 0xab, 0x24, 0xc0, 0xfd }}, + { 3178, // 863 + { 0xf4, 0xf8, 0x0a, 0x95, 0xdc, 0x80, 0x66, 0xa5, 0x1b, 0x36, + 0x68, 0x88, 0x21, 0x8f, 0x3e, 0x78, 0x25, 0xe5, 0xb1, 0x08 }}, + { 3180, // 864 + { 0xf5, 0x81, 0x7b, 0x09, 0x3a, 0x24, 0xf8, 0x2f, 0x33, 0x91, + 0x4b, 0x1f, 0xa9, 0x19, 0x03, 0x3f, 0xbb, 0x0e, 0x7e, 0xca }}, + { 3184, // 865 + { 0xf5, 0x86, 0x83, 0x1f, 0x85, 0x05, 0x54, 0x9b, 0x20, 0xd5, + 0xb0, 0x20, 0x94, 0x13, 0xe8, 0x1e, 0xa8, 0x42, 0xf4, 0x58 }}, + { 3187, // 866 + { 0xf5, 0x88, 0xa1, 0x97, 0x36, 0xad, 0x65, 0xaf, 0x6a, 0x18, + 0xf0, 0x5b, 0x24, 0xac, 0x2a, 0x05, 0x8a, 0x77, 0xed, 0xcc }}, + { 3190, // 867 + { 0xf5, 0x89, 0x90, 0x82, 0x56, 0x0b, 0xc1, 0xf7, 0xb6, 0x7d, + 0x4b, 0xeb, 0xf1, 0x82, 0x0b, 0xf1, 0x95, 0x7f, 0x80, 0xa6 }}, + { 3193, // 868 + { 0xf5, 0xa9, 0xf0, 0xc6, 0x76, 0x22, 0x39, 0x3b, 0x51, 0x22, + 0x40, 0x92, 0x74, 0xe2, 0x22, 0x56, 0x27, 0x10, 0x5b, 0xc5 }}, + { 3196, // 869 + { 0xf5, 0xed, 0x71, 0x73, 0x1e, 0x51, 0xf0, 0xf9, 0x16, 0x96, + 0xac, 0x8f, 0xc9, 0xbd, 0x6c, 0xf8, 0x9f, 0xef, 0x86, 0x54 }}, + { 3199, // 870 + { 0xf6, 0x40, 0x62, 0x2c, 0x79, 0x2b, 0x12, 0xc6, 0xe2, 0x80, + 0x52, 0x43, 0x7e, 0x44, 0xd5, 0x66, 0x98, 0x50, 0x66, 0x6d }}, + { 3202, // 871 + { 0xf6, 0x4f, 0x2a, 0x1d, 0x86, 0xf7, 0xf6, 0x3a, 0x8f, 0x37, + 0x6f, 0x76, 0x7a, 0xce, 0x68, 0x71, 0x0e, 0x38, 0xe3, 0x6b }}, + { 3205, // 872 + { 0xf6, 0x59, 0x4b, 0xce, 0xe7, 0x74, 0x32, 0x3e, 0xc8, 0x79, + 0x92, 0x97, 0x45, 0x3c, 0xed, 0x79, 0xba, 0x57, 0xa6, 0xe5 }}, + { 3208, // 873 + { 0xf6, 0xc4, 0x86, 0x4f, 0xb7, 0x49, 0x09, 0xc4, 0x57, 0x9c, + 0x2b, 0xc8, 0xff, 0x43, 0x50, 0xd6, 0x4c, 0x50, 0xdb, 0x2f }}, + { 3211, // 874 + { 0xf6, 0xd7, 0xea, 0x61, 0xb7, 0x50, 0xb0, 0x58, 0xdd, 0xa0, + 0xf9, 0x29, 0x96, 0x27, 0x04, 0x8c, 0x29, 0xba, 0xfa, 0x5d }}, + { 3214, // 875 + { 0xf6, 0xe7, 0xe0, 0x36, 0xe3, 0x8c, 0x6e, 0x86, 0x43, 0x7d, + 0x4c, 0x0a, 0x47, 0xf6, 0xea, 0x79, 0xab, 0x12, 0xfd, 0xaf }}, + { 3217, // 876 + { 0xf7, 0x25, 0x77, 0x53, 0x3b, 0x4d, 0xda, 0x10, 0xe1, 0xf5, + 0x1f, 0x25, 0x0d, 0x92, 0xdc, 0xf0, 0x0b, 0x54, 0xed, 0x12 }}, + { 3220, // 877 + { 0xf7, 0x48, 0x91, 0xa2, 0xa5, 0x45, 0xb8, 0x7c, 0xb1, 0xbc, + 0x09, 0xed, 0xf0, 0xb6, 0x17, 0x17, 0xd8, 0x4f, 0x44, 0x10 }}, + { 3223, // 878 + { 0xf7, 0x92, 0x3b, 0x4a, 0xba, 0x82, 0xe9, 0xbb, 0xfe, 0x6f, + 0x5a, 0xe9, 0x4e, 0x17, 0x1b, 0x74, 0xa3, 0x4b, 0x6a, 0xb7 }}, + { 3226, // 879 + { 0xf7, 0x94, 0x74, 0xdd, 0x66, 0x1d, 0x46, 0x74, 0x46, 0x81, + 0x0d, 0xa8, 0xa9, 0xa8, 0x7a, 0x15, 0x6c, 0xf6, 0x60, 0xd7 }}, + { 3229, // 880 + { 0xf7, 0x96, 0x46, 0xf2, 0x33, 0xc3, 0x0f, 0x84, 0x80, 0xbb, + 0x15, 0x5a, 0xd3, 0xa7, 0x7f, 0xce, 0xfb, 0xe0, 0xa7, 0x9f }}, + { 3232, // 881 + { 0xf8, 0x22, 0xd4, 0xc1, 0x6c, 0xdb, 0x8e, 0x1d, 0xd8, 0x3a, + 0x36, 0xdd, 0xdf, 0xdf, 0xf1, 0xa0, 0x58, 0x9f, 0xd0, 0x0c }}, + { 3239, // 882 + { 0xf8, 0x39, 0x9b, 0x7a, 0x71, 0xed, 0x66, 0x03, 0x77, 0x37, + 0x4a, 0x91, 0x70, 0x9d, 0x52, 0xf6, 0x7f, 0x9a, 0x20, 0x4c }}, + { 3242, // 883 + { 0xf8, 0x7d, 0xdc, 0x35, 0x94, 0x30, 0x49, 0xfe, 0xfe, 0xaf, + 0x79, 0x9c, 0x9b, 0xde, 0x55, 0xd9, 0xee, 0xe8, 0xcf, 0x26 }}, + { 3248, // 884 + { 0xf8, 0x9e, 0xd9, 0xa9, 0x0f, 0xfa, 0x6b, 0x67, 0x8c, 0xda, + 0xf4, 0x53, 0x53, 0x57, 0x8f, 0x0e, 0x91, 0x5d, 0x1d, 0x32 }}, + { 3251, // 885 + { 0xf8, 0xbb, 0x50, 0x26, 0x6a, 0xdb, 0x29, 0xc0, 0x4d, 0xcd, + 0x1b, 0x72, 0xaf, 0x0f, 0x17, 0x8f, 0x57, 0xef, 0xe6, 0x14 }}, + { 3255, // 886 + { 0xf8, 0xf9, 0xea, 0x1c, 0xf3, 0x18, 0xbd, 0xd6, 0xbb, 0xe6, + 0x92, 0x4e, 0x13, 0x9c, 0x43, 0x0b, 0xe2, 0x4e, 0x77, 0x19 }}, + { 3257, // 887 + { 0xf9, 0x40, 0xce, 0x1f, 0x66, 0xb6, 0x7c, 0x49, 0xfa, 0x5c, + 0x79, 0x04, 0xc5, 0x7a, 0x4a, 0x33, 0x46, 0x91, 0x4c, 0x48 }}, + { 3260, // 888 + { 0xf9, 0xb9, 0x1b, 0xe5, 0xe5, 0x39, 0x2f, 0x5d, 0xe8, 0x82, + 0xfa, 0xbe, 0x63, 0x20, 0x2e, 0x36, 0x66, 0x71, 0x4a, 0x74 }}, + { 3263, // 889 + { 0xf9, 0xc3, 0x74, 0x07, 0x13, 0xf6, 0xb5, 0x87, 0x0e, 0x6c, + 0x47, 0x44, 0x2b, 0xcc, 0xb7, 0x65, 0x92, 0xcf, 0x40, 0x59 }}, + { 3266, // 890 + { 0xf9, 0xc4, 0x10, 0x41, 0xdc, 0xa4, 0xa2, 0xc4, 0x44, 0xad, + 0x3b, 0xb1, 0x65, 0x9e, 0x32, 0x88, 0xf2, 0xeb, 0x2f, 0x95 }}, + { 3269, // 891 + { 0xf9, 0xfb, 0xbb, 0xd2, 0x85, 0x84, 0xf9, 0xa7, 0xfa, 0x1b, + 0x3d, 0xbd, 0x05, 0x65, 0x20, 0x43, 0x76, 0x30, 0x88, 0x11 }}, + { 3272, // 892 + { 0xfa, 0x6a, 0x80, 0xc8, 0x24, 0x7d, 0xd6, 0x79, 0x06, 0x0e, + 0x1a, 0x30, 0xc8, 0x85, 0xbf, 0x4f, 0xc3, 0x3d, 0xc7, 0x5a }}, + { 3275, // 893 + { 0xfa, 0x71, 0x27, 0x56, 0x9d, 0x5e, 0x52, 0x52, 0xbb, 0x20, + 0x4b, 0x61, 0x8b, 0xd7, 0xe6, 0x1f, 0x4f, 0xfa, 0x76, 0xd1 }}, + { 3280, // 894 + { 0xfa, 0xa5, 0x54, 0x27, 0x69, 0x5b, 0x65, 0x1e, 0x92, 0x87, + 0x87, 0xa2, 0x84, 0x41, 0xdc, 0x6e, 0x1a, 0x6e, 0x79, 0xd7 }}, + { 3283, // 895 + { 0xfa, 0xcd, 0xfa, 0x60, 0xdb, 0x5f, 0x44, 0xc8, 0x08, 0x1e, + 0xca, 0x4f, 0xf3, 0x76, 0x66, 0xe7, 0x75, 0x40, 0x1e, 0xd1 }}, + { 3286, // 896 + { 0xfb, 0x0d, 0x6b, 0xff, 0xb5, 0x06, 0xb9, 0xc2, 0x92, 0x11, + 0xcd, 0xd6, 0x9c, 0x93, 0xfe, 0x6f, 0x29, 0xe9, 0x79, 0xcf }}, + { 3289, // 897 + { 0xfb, 0x58, 0x0d, 0x51, 0x6e, 0xfa, 0x69, 0x7d, 0x71, 0x29, + 0x5c, 0x3b, 0x66, 0x6c, 0x1f, 0x5d, 0xb8, 0x1e, 0xef, 0x05 }}, + { 3292, // 898 + { 0xfb, 0xd8, 0x82, 0x48, 0xa1, 0xc9, 0x69, 0x0e, 0x87, 0xbd, + 0x8b, 0xa1, 0x8b, 0x99, 0x56, 0xfd, 0xe0, 0xee, 0x4b, 0x03 }}, + { 3295, // 899 + { 0xfc, 0x90, 0x60, 0x29, 0xdf, 0x54, 0xde, 0x30, 0xd4, 0xf6, + 0x35, 0xd8, 0x2d, 0xbc, 0x40, 0x8a, 0xfd, 0xdc, 0x96, 0xcd }}, + { 3298, // 900 + { 0xfc, 0xa7, 0xff, 0xdd, 0xa7, 0x38, 0x30, 0x73, 0xce, 0x91, + 0x1d, 0x23, 0xa1, 0x04, 0x60, 0x29, 0xe3, 0xd0, 0x1b, 0x22 }}, + { 3300, // 901 + { 0xfc, 0xd9, 0x32, 0xbe, 0x2b, 0xd2, 0x1a, 0x3a, 0xc0, 0x41, + 0xfc, 0x46, 0xa4, 0x3d, 0xcc, 0x99, 0xf4, 0xbc, 0x30, 0x41 }}, + { 3306, // 902 + { 0xfc, 0xea, 0xb4, 0x09, 0xa5, 0x92, 0x0a, 0xf8, 0xb7, 0x0e, + 0xc6, 0xb1, 0x9b, 0xf6, 0xf2, 0x1e, 0xb7, 0x2b, 0x13, 0x90 }}, + { 3309, // 903 + { 0xfd, 0x19, 0x66, 0x1b, 0xfc, 0x3b, 0x22, 0x90, 0xaf, 0xf1, + 0x01, 0x44, 0xb8, 0xc8, 0x24, 0x4f, 0x4d, 0x66, 0xf0, 0xee }}, + { 3312, // 904 + { 0xfd, 0x1a, 0x63, 0xbd, 0xd5, 0x10, 0x79, 0x7f, 0x72, 0x97, + 0xb2, 0x2e, 0xbb, 0x23, 0xe0, 0xd8, 0x99, 0x75, 0x62, 0x82 }}, + { 3315, // 905 + { 0xfd, 0x1b, 0x98, 0xf1, 0x5c, 0x80, 0x38, 0x16, 0x8c, 0x5c, + 0x05, 0x23, 0xfa, 0x31, 0x19, 0x62, 0xcb, 0xd0, 0x87, 0x53 }}, + { 3318, // 906 + { 0xfd, 0x20, 0x0d, 0xab, 0x62, 0xd2, 0x99, 0x31, 0xc5, 0xfe, + 0xde, 0xb1, 0xa0, 0x45, 0x71, 0x4f, 0x71, 0x5f, 0xe9, 0x9b }}, + { 3321, // 907 + { 0xfd, 0x56, 0x31, 0x5b, 0xdb, 0x75, 0xfb, 0xca, 0xf5, 0xaa, + 0x40, 0x69, 0x44, 0x32, 0xa5, 0xdf, 0x87, 0xfe, 0xa9, 0x4b }}, + { 3324, // 908 + { 0xfd, 0x9f, 0xbe, 0xc2, 0xa0, 0x0c, 0x1d, 0x56, 0x67, 0xb0, + 0x68, 0x0e, 0xb3, 0x7a, 0xc5, 0x2e, 0x4a, 0xc9, 0x20, 0xef }}, + { 3329, // 909 + { 0xfd, 0xb3, 0x45, 0x34, 0xb8, 0x60, 0x8a, 0x9d, 0x64, 0xd9, + 0x22, 0xcd, 0xc2, 0x77, 0xe9, 0x73, 0xab, 0x33, 0xdb, 0x99 }}, + { 3332, // 910 + { 0xfd, 0xbe, 0x57, 0xa5, 0x0d, 0x17, 0x2b, 0x5a, 0x68, 0x9b, + 0xba, 0x36, 0x87, 0xec, 0x7e, 0x84, 0xa9, 0x62, 0x5d, 0x27 }}, + { 3335, // 911 + { 0xfd, 0xcf, 0x17, 0x5c, 0x9a, 0x38, 0xa9, 0xd4, 0xb8, 0x63, + 0x22, 0x5a, 0x59, 0xbb, 0x68, 0xc7, 0x43, 0x14, 0x45, 0x31 }}, + { 3338, // 912 + { 0xfd, 0xd0, 0x04, 0x7a, 0xa4, 0x48, 0x54, 0x01, 0xf5, 0xd5, + 0xca, 0x2f, 0x1a, 0xdf, 0xf3, 0x58, 0x52, 0x3e, 0x24, 0x42 }}, + { 3341, // 913 + { 0xfe, 0x1c, 0x63, 0x08, 0x8f, 0xe8, 0xd2, 0x5d, 0x60, 0xc3, + 0x4c, 0x97, 0x40, 0x33, 0x59, 0x03, 0xc7, 0x5f, 0x1d, 0xc5 }}, + { 3344, // 914 + { 0xfe, 0x36, 0xb8, 0x3f, 0x6d, 0xc0, 0x63, 0x61, 0xbc, 0x7e, + 0xbe, 0x6d, 0x28, 0x02, 0x77, 0x38, 0x94, 0x94, 0xa7, 0xa3 }}, + { 3347, // 915 + { 0xfe, 0x68, 0xd1, 0x7e, 0x65, 0xbb, 0x67, 0x40, 0x23, 0x83, + 0x0d, 0x66, 0x69, 0xc1, 0xd5, 0x54, 0x61, 0xea, 0xf9, 0xb4 }}, + { 3350, // 916 + { 0xfe, 0xd6, 0xad, 0x1f, 0xf5, 0x61, 0xfb, 0xf1, 0xab, 0x1a, + 0xe0, 0x4b, 0x98, 0x38, 0x13, 0xa8, 0x6d, 0x02, 0xf6, 0x6c }}, + { 3353, // 917 + { 0xff, 0x81, 0x4d, 0x15, 0xa7, 0x7f, 0x0e, 0x5a, 0x53, 0x39, + 0x95, 0x52, 0x70, 0x7c, 0x51, 0x2a, 0x22, 0xef, 0xf0, 0xb0 }}, + { 3356, // 918 + { 0xff, 0x91, 0xc6, 0x62, 0x7f, 0xe0, 0xa8, 0xc6, 0x0c, 0x4e, + 0x7a, 0x76, 0x4e, 0x03, 0x59, 0x38, 0xb8, 0x68, 0xf9, 0x40 }}, + { 3359, // 919 + { 0xff, 0xf7, 0xea, 0x41, 0x70, 0x7b, 0xf6, 0x3f, 0x4d, 0xbf, + 0xd4, 0x56, 0x2d, 0xef, 0x75, 0x39, 0x4a, 0x81, 0x51, 0x3f }}, + + {-1,""} +}; + +// +//-------------------------------------------------- + +const s16 DbFileRefSHA1[N_DB_FILE_REF_SHA1+1] = +{ + + // 0, 0, N=2+1 + 1283, 1308, -1, + + // 3, 1, N=7+1 + 256, 282, 1568, 1591, 1636, 1658, 2407, -1, + + // 11, 2, N=2+1 + 242, 268, -1, + + // 14, 3, N=1+1 + 480, -1, + + // 16, 4, N=2+1 + 1855, 1886, -1, + + // 19, 5, N=2+1 + 410, 432, -1, + + // 22, 6, N=2+1 + 1503, 1528, -1, + + // 25, 7, N=2+1 + 1502, 1527, -1, + + // 28, 8, N=2+1 + 674, 751, -1, + + // 31, 9, N=2+1 + 1467, 1488, -1, + + // 34, 10, N=2+1 + 1229, 1259, -1, + + // 37, 11, N=4+1 + 1565, 1588, 2279, 2292, -1, + + // 42, 12, N=2+1 + 1781, 1821, -1, + + // 45, 13, N=2+1 + 1234, 1264, -1, + + // 48, 14, N=2+1 + 68, 94, -1, + + // 51, 15, N=2+1 + 127, 151, -1, + + // 54, 16, N=2+1 + 1603, 1615, -1, + + // 57, 17, N=2+1 + 1270, 1295, -1, + + // 60, 18, N=2+1 + 2302, 2325, -1, + + // 63, 19, N=2+1 + 882, 907, -1, + + // 66, 20, N=2+1 + 675, 752, -1, + + // 69, 21, N=2+1 + 688, 765, -1, + + // 72, 22, N=2+1 + 266, 2178, -1, + + // 75, 23, N=4+1 + 655, 732, 1411, 1436, -1, + + // 80, 24, N=2+1 + 1147, 1161, -1, + + // 83, 25, N=2+1 + 1271, 1296, -1, + + // 86, 26, N=2+1 + 819, 857, -1, + + // 89, 27, N=2+1 + 1850, 1881, -1, + + // 92, 28, N=2+1 + 76, 102, -1, + + // 95, 29, N=5+1 + 187, 228, 1338, 1366, 2399, -1, + + // 101, 30, N=4+1 + 448, 481, 511, 548, -1, + + // 106, 31, N=2+1 + 829, 867, -1, + + // 109, 32, N=4+1 + 234, 260, 2144, 2172, -1, + + // 114, 33, N=2+1 + 155, 196, -1, + + // 117, 34, N=2+1 + 1386, 1404, -1, + + // 120, 35, N=3+1 + 112, 136, 2389, -1, + + // 124, 36, N=2+1 + 1571, 1594, -1, + + // 127, 37, N=2+1 + 1150, 1164, -1, + + // 130, 38, N=2+1 + 2075, 2090, -1, + + // 133, 39, N=2+1 + 2046, 2057, -1, + + // 136, 40, N=2+1 + 1952, 1971, -1, + + // 139, 41, N=1+1 + 2363, -1, + + // 141, 42, N=2+1 + 1940, 1959, -1, + + // 144, 43, N=2+1 + 346, 376, -1, + + // 147, 44, N=2+1 + 1410, 1435, -1, + + // 150, 45, N=2+1 + 509, 546, -1, + + // 153, 46, N=2+1 + 1572, 1595, -1, + + // 156, 47, N=2+1 + 2299, 2322, -1, + + // 159, 48, N=2+1 + 1521, 1546, -1, + + // 162, 49, N=2+1 + 1317, 1345, -1, + + // 165, 50, N=2+1 + 1665, 1690, -1, + + // 168, 51, N=2+1 + 2304, 2327, -1, + + // 171, 52, N=1+1 + 2205, -1, + + // 173, 53, N=2+1 + 2272, 2285, -1, + + // 176, 54, N=2+1 + 1784, 1824, -1, + + // 179, 55, N=2+1 + 678, 755, -1, + + // 182, 56, N=4+1 + 258, 284, 1686, 1711, -1, + + // 187, 57, N=2+1 + 666, 743, -1, + + // 190, 58, N=2+1 + 895, 920, -1, + + // 193, 59, N=4+1 + 12, 38, 2237, 2258, -1, + + // 198, 60, N=2+1 + 1670, 1695, -1, + + // 201, 61, N=2+1 + 1115, 1131, -1, + + // 204, 62, N=12+1 + 1127, 1143, 1187, 1203, 1233, 1263, 1607, 1619, 1638, 1660, + 2074, 2089, -1, + + // 217, 63, N=2+1 + 1685, 1710, -1, + + // 220, 64, N=2+1 + 2151, 2179, -1, + + // 223, 65, N=2+1 + 1669, 1694, -1, + + // 226, 66, N=2+1 + 1417, 1442, -1, + + // 229, 67, N=2+1 + 1458, 1479, -1, + + // 232, 68, N=2+1 + 1500, 1525, -1, + + // 235, 69, N=2+1 + 1596, 1608, -1, + + // 238, 70, N=2+1 + 290, 316, -1, + + // 241, 71, N=2+1 + 1791, 1831, -1, + + // 244, 72, N=2+1 + 1773, 1813, -1, + + // 247, 73, N=2+1 + 1501, 1526, -1, + + // 250, 74, N=5+1 + 6, 32, 1599, 1611, 2403, -1, + + // 256, 75, N=2+1 + 525, 562, -1, + + // 259, 76, N=2+1 + 699, 776, -1, + + // 262, 77, N=2+1 + 950, 988, -1, + + // 265, 78, N=2+1 + 906, 931, -1, + + // 268, 79, N=2+1 + 1460, 1481, -1, + + // 271, 80, N=2+1 + 109, 133, -1, + + // 274, 81, N=2+1 + 1081, 1113, -1, + + // 277, 82, N=3+1 + 1951, 1970, 2409, -1, + + // 281, 83, N=2+1 + 684, 761, -1, + + // 284, 84, N=2+1 + 1780, 1820, -1, + + // 287, 85, N=2+1 + 257, 283, -1, + + // 290, 86, N=2+1 + 2368, 2372, -1, + + // 293, 87, N=2+1 + 604, 639, -1, + + // 296, 88, N=2+1 + 664, 741, -1, + + // 299, 89, N=11+1 + 114, 138, 244, 270, 517, 554, 1554, 1577, 2271, 2284, + 2400, -1, + + // 311, 90, N=2+1 + 597, 632, -1, + + // 314, 91, N=2+1 + 2149, 2177, -1, + + // 317, 92, N=2+1 + 596, 631, -1, + + // 320, 93, N=2+1 + 121, 145, -1, + + // 323, 94, N=2+1 + 1388, 1406, -1, + + // 326, 95, N=2+1 + 1122, 1138, -1, + + // 329, 96, N=2+1 + 309, 335, -1, + + // 332, 97, N=2+1 + 340, 370, -1, + + // 335, 98, N=2+1 + 816, 854, -1, + + // 338, 99, N=2+1 + 1176, 1192, -1, + + // 341, 100, N=2+1 + 1919, 1936, -1, + + // 344, 101, N=2+1 + 892, 917, -1, + + // 347, 102, N=1+1 + 2416, -1, + + // 349, 103, N=2+1 + 1767, 1807, -1, + + // 352, 104, N=8+1 + 1289, 1314, 1476, 1497, 2102, 2113, 2126, 2141, -1, + + // 361, 105, N=2+1 + 539, 576, -1, + + // 364, 106, N=2+1 + 160, 201, -1, + + // 367, 107, N=1+1 + 2382, -1, + + // 369, 108, N=2+1 + 1765, 1805, -1, + + // 372, 109, N=2+1 + 156, 197, -1, + + // 375, 110, N=2+1 + 543, 580, -1, + + // 378, 111, N=2+1 + 2341, 2352, -1, + + // 381, 112, N=4+1 + 0, 26, 2226, 2247, -1, + + // 386, 113, N=2+1 + 409, 431, -1, + + // 389, 114, N=2+1 + 937, 975, -1, + + // 392, 115, N=2+1 + 1621, 1643, -1, + + // 395, 116, N=2+1 + 1339, 1367, -1, + + // 398, 117, N=2+1 + 1341, 1369, -1, + + // 401, 118, N=2+1 + 518, 555, -1, + + // 404, 119, N=2+1 + 1552, 1575, -1, + + // 407, 120, N=2+1 + 1941, 1960, -1, + + // 410, 121, N=2+1 + 456, 489, -1, + + // 413, 122, N=2+1 + 1943, 1962, -1, + + // 416, 123, N=2+1 + 1714, 1739, -1, + + // 419, 124, N=2+1 + 400, 422, -1, + + // 422, 125, N=2+1 + 1737, 1762, -1, + + // 425, 126, N=2+1 + 159, 200, -1, + + // 428, 127, N=2+1 + 599, 634, -1, + + // 431, 128, N=3+1 + 594, 629, 2414, -1, + + // 435, 129, N=2+1 + 1478, 1499, -1, + + // 438, 130, N=2+1 + 1020, 1041, -1, + + // 441, 131, N=7+1 + 250, 276, 1562, 1585, 1630, 1652, 2410, -1, + + // 449, 132, N=2+1 + 348, 378, -1, + + // 452, 133, N=2+1 + 2201, 2214, -1, + + // 455, 134, N=2+1 + 932, 970, -1, + + // 458, 135, N=2+1 + 417, 439, -1, + + // 461, 136, N=2+1 + 1027, 1048, -1, + + // 464, 137, N=2+1 + 1555, 1578, -1, + + // 467, 138, N=2+1 + 1992, 2012, -1, + + // 470, 139, N=2+1 + 56, 82, -1, + + // 473, 140, N=2+1 + 1224, 1254, -1, + + // 476, 141, N=2+1 + 287, 313, -1, + + // 479, 142, N=2+1 + 1408, 1433, -1, + + // 482, 143, N=2+1 + 1064, 1096, -1, + + // 485, 144, N=9+1 + 936, 974, 1462, 1483, 1717, 1742, 1982, 2002, 2425, -1, + + // 495, 145, N=2+1 + 74, 100, -1, + + // 498, 146, N=4+1 + 533, 535, 570, 572, -1, + + // 503, 147, N=2+1 + 4, 30, -1, + + // 506, 148, N=9+1 + 951, 989, 1469, 1490, 1724, 1749, 1990, 2010, 2423, -1, + + // 516, 149, N=2+1 + 1155, 1169, -1, + + // 519, 150, N=2+1 + 2203, 2216, -1, + + // 522, 151, N=7+1 + 255, 281, 1567, 1590, 1635, 1657, 2408, -1, + + // 530, 152, N=3+1 + 1668, 1693, 2436, -1, + + // 534, 153, N=2+1 + 157, 198, -1, + + // 537, 154, N=2+1 + 454, 487, -1, + + // 540, 155, N=2+1 + 1154, 1168, -1, + + // 543, 156, N=2+1 + 905, 930, -1, + + // 546, 157, N=2+1 + 941, 979, -1, + + // 549, 158, N=2+1 + 1013, 1034, -1, + + // 552, 159, N=2+1 + 471, 504, -1, + + // 555, 160, N=1+1 + 162, -1, + + // 557, 161, N=2+1 + 2303, 2326, -1, + + // 560, 162, N=2+1 + 1515, 1540, -1, + + // 563, 163, N=2+1 + 1178, 1194, -1, + + // 566, 164, N=2+1 + 2347, 2358, -1, + + // 569, 165, N=2+1 + 366, 396, -1, + + // 572, 166, N=2+1 + 64, 90, -1, + + // 575, 167, N=2+1 + 1333, 1361, -1, + + // 578, 168, N=2+1 + 696, 773, -1, + + // 581, 169, N=2+1 + 1984, 2004, -1, + + // 584, 170, N=2+1 + 1955, 1974, -1, + + // 587, 171, N=4+1 + 710, 787, 1419, 1444, -1, + + // 592, 172, N=2+1 + 1114, 1130, -1, + + // 595, 173, N=2+1 + 1569, 1592, -1, + + // 598, 174, N=4+1 + 412, 434, 612, 647, -1, + + // 603, 175, N=2+1 + 1979, 1999, -1, + + // 606, 176, N=2+1 + 246, 272, -1, + + // 609, 177, N=2+1 + 299, 325, -1, + + // 612, 178, N=2+1 + 707, 784, -1, + + // 615, 179, N=2+1 + 1208, 1238, -1, + + // 618, 180, N=2+1 + 815, 853, -1, + + // 621, 181, N=2+1 + 1182, 1198, -1, + + // 624, 182, N=2+1 + 1431, 1456, -1, + + // 627, 183, N=1+1 + 654, -1, + + // 629, 184, N=3+1 + 113, 137, 2395, -1, + + // 633, 185, N=8+1 + 717, 794, 837, 875, 964, 1002, 1519, 1544, -1, + + // 642, 186, N=4+1 + 1021, 1042, 2313, 2336, -1, + + // 647, 187, N=2+1 + 184, 225, -1, + + // 650, 188, N=4+1 + 898, 923, 2426, 2428, -1, + + // 655, 189, N=2+1 + 1787, 1827, -1, + + // 658, 190, N=2+1 + 660, 737, -1, + + // 661, 191, N=2+1 + 293, 319, -1, + + // 664, 192, N=2+1 + 353, 383, -1, + + // 667, 193, N=2+1 + 1731, 1756, -1, + + // 670, 194, N=2+1 + 69, 95, -1, + + // 673, 195, N=2+1 + 519, 556, -1, + + // 676, 196, N=2+1 + 171, 212, -1, + + // 679, 197, N=2+1 + 161, 202, -1, + + // 682, 198, N=2+1 + 1504, 1529, -1, + + // 685, 199, N=2+1 + 1377, 1395, -1, + + // 688, 200, N=2+1 + 1867, 1898, -1, + + // 691, 201, N=2+1 + 598, 633, -1, + + // 694, 202, N=2+1 + 2206, 2219, -1, + + // 697, 203, N=2+1 + 508, 545, -1, + + // 700, 204, N=2+1 + 406, 428, -1, + + // 703, 205, N=6+1 + 450, 483, 1507, 1532, 2154, 2182, -1, + + // 710, 206, N=2+1 + 1666, 1691, -1, + + // 713, 207, N=2+1 + 1908, 1925, -1, + + // 716, 208, N=2+1 + 1266, 1291, -1, + + // 719, 209, N=2+1 + 2229, 2250, -1, + + // 722, 210, N=2+1 + 1316, 1344, -1, + + // 725, 211, N=4+1 + 1129, 1145, 1688, 1713, -1, + + // 730, 212, N=2+1 + 685, 762, -1, + + // 733, 213, N=2+1 + 1671, 1696, -1, + + // 736, 214, N=2+1 + 291, 317, -1, + + // 739, 215, N=2+1 + 58, 84, -1, + + // 742, 216, N=2+1 + 350, 380, -1, + + // 745, 217, N=2+1 + 1799, 1839, -1, + + // 748, 218, N=2+1 + 807, 845, -1, + + // 751, 219, N=2+1 + 1409, 1434, -1, + + // 754, 220, N=2+1 + 286, 312, -1, + + // 757, 221, N=2+1 + 811, 849, -1, + + // 760, 222, N=2+1 + 2064, 2079, -1, + + // 763, 223, N=2+1 + 585, 620, -1, + + // 766, 224, N=2+1 + 2050, 2061, -1, + + // 769, 225, N=2+1 + 967, 1005, -1, + + // 772, 226, N=4+1 + 249, 275, 2159, 2187, -1, + + // 777, 227, N=2+1 + 1146, 1160, -1, + + // 780, 228, N=2+1 + 661, 738, -1, + + // 783, 229, N=2+1 + 359, 389, -1, + + // 786, 230, N=2+1 + 1158, 1172, -1, + + // 789, 231, N=2+1 + 669, 746, -1, + + // 792, 232, N=8+1 + 716, 793, 836, 874, 963, 1001, 1518, 1543, -1, + + // 801, 233, N=2+1 + 472, 505, -1, + + // 804, 234, N=1+1 + 2441, -1, + + // 806, 235, N=2+1 + 1185, 1201, -1, + + // 809, 236, N=2+1 + 1944, 1963, -1, + + // 812, 237, N=2+1 + 2045, 2056, -1, + + // 815, 238, N=4+1 + 71, 97, 1632, 1654, -1, + + // 820, 239, N=2+1 + 2244, 2265, -1, + + // 823, 240, N=2+1 + 1512, 1537, -1, + + // 826, 241, N=2+1 + 888, 913, -1, + + // 829, 242, N=4+1 + 408, 430, 592, 627, -1, + + // 834, 243, N=2+1 + 445, 478, -1, + + // 837, 244, N=2+1 + 2342, 2353, -1, + + // 840, 245, N=2+1 + 671, 748, -1, + + // 843, 246, N=2+1 + 462, 495, -1, + + // 846, 247, N=2+1 + 1152, 1166, -1, + + // 849, 248, N=2+1 + 2200, 2213, -1, + + // 852, 249, N=2+1 + 2097, 2108, -1, + + // 855, 250, N=2+1 + 1220, 1250, -1, + + // 858, 251, N=2+1 + 178, 219, -1, + + // 861, 252, N=2+1 + 1330, 1358, -1, + + // 864, 253, N=2+1 + 1060, 1092, -1, + + // 867, 254, N=2+1 + 665, 742, -1, + + // 870, 255, N=2+1 + 1985, 2005, -1, + + // 873, 256, N=2+1 + 817, 855, -1, + + // 876, 257, N=4+1 + 9, 35, 2234, 2255, -1, + + // 881, 258, N=2+1 + 1175, 1191, -1, + + // 884, 259, N=16+1 + 20, 46, 253, 279, 469, 502, 830, 868, 1070, 1102, + 1222, 1252, 1421, 1446, 1604, 1616, -1, + + // 901, 260, N=2+1 + 652, 729, -1, + + // 904, 261, N=2+1 + 2023, 2034, -1, + + // 907, 262, N=2+1 + 289, 315, -1, + + // 910, 263, N=2+1 + 889, 914, -1, + + // 913, 264, N=2+1 + 1057, 1089, -1, + + // 916, 265, N=2+1 + 1766, 1806, -1, + + // 919, 266, N=2+1 + 1922, 1939, -1, + + // 922, 267, N=2+1 + 1383, 1401, -1, + + // 925, 268, N=2+1 + 701, 778, -1, + + // 928, 269, N=1+1 + 2345, -1, + + // 930, 270, N=30+1 + 170, 211, 521, 558, 590, 625, 821, 859, 891, 916, + 1062, 1094, 1119, 1135, 1215, 1245, 1275, 1300, 1324, 1352, + 1414, 1439, 1510, 1535, 1627, 1649, 1675, 1700, 1851, 1882, + -1, + + // 961, 271, N=2+1 + 670, 747, -1, + + // 964, 272, N=2+1 + 158, 199, -1, + + // 967, 273, N=2+1 + 722, 799, -1, + + // 970, 274, N=2+1 + 1282, 1307, -1, + + // 973, 275, N=1+1 + 292, -1, + + // 975, 276, N=2+1 + 419, 441, -1, + + // 978, 277, N=2+1 + 460, 493, -1, + + // 981, 278, N=2+1 + 177, 218, -1, + + // 984, 279, N=1+1 + 2442, -1, + + // 986, 280, N=2+1 + 540, 577, -1, + + // 989, 281, N=2+1 + 347, 377, -1, + + // 992, 282, N=2+1 + 1206, 1236, -1, + + // 995, 283, N=2+1 + 663, 740, -1, + + // 998, 284, N=2+1 + 1550, 1573, -1, + + // 1001, 285, N=2+1 + 342, 372, -1, + + // 1004, 286, N=2+1 + 1802, 1842, -1, + + // 1007, 287, N=2+1 + 19, 45, -1, + + // 1010, 288, N=2+1 + 2128, 2143, -1, + + // 1013, 289, N=2+1 + 706, 783, -1, + + // 1016, 290, N=2+1 + 167, 208, -1, + + // 1019, 291, N=2+1 + 1857, 1888, -1, + + // 1022, 292, N=4+1 + 54, 80, 1620, 1642, -1, + + // 1027, 293, N=2+1 + 1789, 1829, -1, + + // 1030, 294, N=4+1 + 1559, 1582, 2275, 2288, -1, + + // 1035, 295, N=2+1 + 1183, 1199, -1, + + // 1038, 296, N=2+1 + 1624, 1646, -1, + + // 1041, 297, N=9+1 + 952, 990, 1470, 1491, 1725, 1750, 1991, 2011, 2424, -1, + + // 1051, 298, N=2+1 + 345, 375, -1, + + // 1054, 299, N=2+1 + 1284, 1309, -1, + + // 1057, 300, N=2+1 + 24, 50, -1, + + // 1060, 301, N=2+1 + 2268, 2281, -1, + + // 1063, 302, N=1+1 + 973, -1, + + // 1065, 303, N=4+1 + 948, 986, 1381, 1399, -1, + + // 1070, 304, N=2+1 + 191, 232, -1, + + // 1073, 305, N=6+1 + 1, 27, 1785, 1825, 2227, 2248, -1, + + // 1080, 306, N=1+1 + 1011, -1, + + // 1082, 307, N=1+1 + 2001, -1, + + // 1084, 308, N=2+1 + 1342, 1370, -1, + + // 1087, 309, N=2+1 + 1019, 1040, -1, + + // 1090, 310, N=2+1 + 1623, 1645, -1, + + // 1093, 311, N=2+1 + 18, 44, -1, + + // 1096, 312, N=6+1 + 300, 326, 1059, 1091, 1323, 1351, -1, + + // 1103, 313, N=2+1 + 1189, 1205, -1, + + // 1106, 314, N=2+1 + 1290, 1315, -1, + + // 1109, 315, N=2+1 + 2315, 2338, -1, + + // 1112, 316, N=2+1 + 413, 435, -1, + + // 1115, 317, N=2+1 + 1776, 1816, -1, + + // 1118, 318, N=2+1 + 455, 488, -1, + + // 1121, 319, N=2+1 + 616, 651, -1, + + // 1124, 320, N=2+1 + 2311, 2334, -1, + + // 1127, 321, N=44+1 + 16, 42, 62, 88, 116, 140, 173, 214, 248, 274, + 306, 332, 354, 384, 407, 429, 465, 498, 524, 561, + 823, 861, 1066, 1098, 1120, 1136, 1181, 1197, 1216, 1246, + 1277, 1302, 1327, 1355, 1560, 1583, 1628, 1650, 1676, 1701, + 1852, 1883, 1986, 2006, -1, + + // 1172, 322, N=1+1 + 2417, -1, + + // 1174, 323, N=2+1 + 1274, 1299, -1, + + // 1177, 324, N=1+1 + 850, -1, + + // 1179, 325, N=2+1 + 1736, 1761, -1, + + // 1182, 326, N=2+1 + 1917, 1934, -1, + + // 1185, 327, N=2+1 + 446, 479, -1, + + // 1188, 328, N=2+1 + 703, 780, -1, + + // 1191, 329, N=2+1 + 351, 381, -1, + + // 1194, 330, N=2+1 + 2241, 2262, -1, + + // 1197, 331, N=2+1 + 1597, 1609, -1, + + // 1200, 332, N=16+1 + 21, 47, 254, 280, 470, 503, 831, 869, 1071, 1103, + 1223, 1253, 1422, 1447, 1605, 1617, -1, + + // 1217, 333, N=2+1 + 1868, 1899, -1, + + // 1220, 334, N=2+1 + 1387, 1405, -1, + + // 1223, 335, N=2+1 + 2230, 2251, -1, + + // 1226, 336, N=2+1 + 1319, 1347, -1, + + // 1229, 337, N=8+1 + 245, 271, 2158, 2186, 2208, 2221, 2346, 2357, -1, + + // 1238, 338, N=2+1 + 1023, 1044, -1, + + // 1241, 339, N=2+1 + 1687, 1712, -1, + + // 1244, 340, N=2+1 + 126, 150, -1, + + // 1247, 341, N=4+1 + 902, 927, 2124, 2139, -1, + + // 1252, 342, N=2+1 + 2073, 2088, -1, + + // 1255, 343, N=1+1 + 1847, -1, + + // 1257, 344, N=2+1 + 1148, 1162, -1, + + // 1260, 345, N=2+1 + 966, 1004, -1, + + // 1263, 346, N=2+1 + 308, 334, -1, + + // 1266, 347, N=30+1 + 180, 221, 537, 574, 607, 642, 832, 870, 899, 924, + 1074, 1106, 1124, 1140, 1230, 1260, 1286, 1311, 1335, 1363, + 1423, 1448, 1516, 1541, 1633, 1655, 1683, 1708, 1864, 1895, + -1, + + // 1297, 348, N=2+1 + 168, 209, -1, + + // 1300, 349, N=2+1 + 1050, 1082, -1, + + // 1303, 350, N=2+1 + 1920, 1937, -1, + + // 1306, 351, N=4+1 + 14, 40, 2239, 2260, -1, + + // 1311, 352, N=2+1 + 2202, 2215, -1, + + // 1314, 353, N=2+1 + 1918, 1935, -1, + + // 1317, 354, N=2+1 + 1213, 1243, -1, + + // 1320, 355, N=1+1 + 2365, -1, + + // 1322, 356, N=8+1 + 715, 792, 962, 1000, 1474, 1495, 1735, 1760, -1, + + // 1331, 357, N=2+1 + 402, 424, -1, + + // 1334, 358, N=2+1 + 241, 267, -1, + + // 1337, 359, N=2+1 + 1845, 1876, -1, + + // 1340, 360, N=2+1 + 1598, 1610, -1, + + // 1343, 361, N=4+1 + 2027, 2038, 2101, 2112, -1, + + // 1348, 362, N=2+1 + 944, 982, -1, + + // 1351, 363, N=21+1 + 1783, 1823, 1844, 1875, 1906, 1923, 1942, 1961, 1978, 1998, + 2018, 2029, 2040, 2051, 2062, 2077, 2092, 2103, 2114, 2129, + 2380, -1, + + // 1373, 364, N=2+1 + 694, 771, -1, + + // 1376, 365, N=2+1 + 2204, 2217, -1, + + // 1379, 366, N=2+1 + 653, 730, -1, + + // 1382, 367, N=4+1 + 7, 33, 2232, 2253, -1, + + // 1387, 368, N=2+1 + 2068, 2083, -1, + + // 1390, 369, N=2+1 + 1376, 1394, -1, + + // 1393, 370, N=2+1 + 2162, 2190, -1, + + // 1396, 371, N=2+1 + 55, 81, -1, + + // 1399, 372, N=8+1 + 712, 789, 955, 957, 993, 995, 1728, 1753, -1, + + // 1408, 373, N=2+1 + 1235, 1265, -1, + + // 1411, 374, N=1+1 + 510, -1, + + // 1413, 375, N=2+1 + 457, 490, -1, + + // 1416, 376, N=3+1 + 586, 621, 2413, -1, + + // 1420, 377, N=2+1 + 1121, 1137, -1, + + // 1423, 378, N=2+1 + 176, 217, -1, + + // 1426, 379, N=88+1 + 8, 34, 60, 86, 107, 131, 166, 207, 243, 269, + 296, 322, 349, 379, 404, 426, 453, 486, 516, 553, + 587, 622, 657, 734, 818, 856, 885, 910, 940, 978, + 1015, 1036, 1055, 1087, 1117, 1133, 1151, 1165, 1177, 1193, + 1210, 1240, 1273, 1298, 1321, 1349, 1375, 1393, 1412, 1437, + 1463, 1484, 1508, 1533, 1553, 1576, 1600, 1612, 1625, 1647, + 1673, 1698, 1719, 1744, 1790, 1830, 1849, 1880, 1911, 1928, + 1946, 1965, 1983, 2003, 2022, 2033, 2044, 2055, 2067, 2082, + 2096, 2107, 2118, 2133, 2233, 2254, 2298, 2321, -1, + + // 1515, 380, N=2+1 + 1869, 1900, -1, + + // 1518, 381, N=2+1 + 2369, 2373, -1, + + // 1521, 382, N=2+1 + 691, 768, -1, + + // 1524, 383, N=2+1 + 310, 336, -1, + + // 1527, 384, N=2+1 + 1733, 1758, -1, + + // 1530, 385, N=2+1 + 542, 579, -1, + + // 1533, 386, N=2+1 + 415, 437, -1, + + // 1536, 387, N=2+1 + 1859, 1890, -1, + + // 1539, 388, N=2+1 + 1997, 2017, -1, + + // 1542, 389, N=6+1 + 53, 79, 237, 263, 2147, 2175, -1, + + // 1549, 390, N=2+1 + 1468, 1489, -1, + + // 1552, 391, N=2+1 + 164, 205, -1, + + // 1555, 392, N=2+1 + 123, 147, -1, + + // 1558, 393, N=4+1 + 10, 36, 2235, 2256, -1, + + // 1563, 394, N=3+1 + 110, 134, 2393, -1, + + // 1567, 395, N=2+1 + 1174, 1190, -1, + + // 1570, 396, N=1+1 + 423, -1, + + // 1572, 397, N=2+1 + 288, 314, -1, + + // 1575, 398, N=2+1 + 1637, 1659, -1, + + // 1578, 399, N=2+1 + 1332, 1360, -1, + + // 1581, 400, N=2+1 + 721, 798, -1, + + // 1584, 401, N=2+1 + 689, 766, -1, + + // 1587, 402, N=2+1 + 1561, 1584, -1, + + // 1590, 403, N=8+1 + 728, 805, 843, 881, 1432, 1457, 2246, 2267, -1, + + // 1599, 404, N=2+1 + 700, 777, -1, + + // 1602, 405, N=2+1 + 2098, 2109, -1, + + // 1605, 406, N=2+1 + 174, 215, -1, + + // 1608, 407, N=2+1 + 692, 769, -1, + + // 1611, 408, N=2+1 + 1987, 2007, -1, + + // 1614, 409, N=2+1 + 2310, 2333, -1, + + // 1617, 410, N=2+1 + 1980, 2000, -1, + + // 1620, 411, N=5+1 + 835, 873, 1426, 1451, 2420, -1, + + // 1626, 412, N=2+1 + 1870, 1901, -1, + + // 1629, 413, N=2+1 + 1641, 1663, -1, + + // 1632, 414, N=2+1 + 2171, 2199, -1, + + // 1635, 415, N=3+1 + 1077, 1109, 2435, -1, + + // 1639, 416, N=2+1 + 1281, 1306, -1, + + // 1642, 417, N=2+1 + 1149, 1163, -1, + + // 1645, 418, N=2+1 + 294, 320, -1, + + // 1648, 419, N=2+1 + 705, 782, -1, + + // 1651, 420, N=4+1 + 1566, 1589, 2280, 2293, -1, + + // 1656, 421, N=2+1 + 2157, 2185, -1, + + // 1659, 422, N=2+1 + 839, 877, -1, + + // 1662, 423, N=2+1 + 1716, 1741, -1, + + // 1665, 424, N=2+1 + 600, 635, -1, + + // 1668, 425, N=2+1 + 1788, 1828, -1, + + // 1671, 426, N=2+1 + 1459, 1480, -1, + + // 1674, 427, N=2+1 + 1009, 1030, -1, + + // 1677, 428, N=2+1 + 1188, 1204, -1, + + // 1680, 429, N=2+1 + 1871, 1902, -1, + + // 1683, 430, N=2+1 + 1116, 1132, -1, + + // 1686, 431, N=8+1 + 714, 791, 961, 999, 1473, 1494, 1734, 1759, -1, + + // 1695, 432, N=2+1 + 2069, 2084, -1, + + // 1698, 433, N=2+1 + 947, 985, -1, + + // 1701, 434, N=6+1 + 656, 733, 939, 977, 1718, 1743, -1, + + // 1708, 435, N=3+1 + 595, 630, 2415, -1, + + // 1712, 436, N=2+1 + 2153, 2181, -1, + + // 1715, 437, N=1+1 + 2387, -1, + + // 1717, 438, N=2+1 + 1622, 1644, -1, + + // 1720, 439, N=2+1 + 2296, 2319, -1, + + // 1723, 440, N=2+1 + 1180, 1196, -1, + + // 1726, 441, N=2+1 + 838, 876, -1, + + // 1729, 442, N=2+1 + 1873, 1904, -1, + + // 1732, 443, N=2+1 + 305, 331, -1, + + // 1735, 444, N=2+1 + 2269, 2282, -1, + + // 1738, 445, N=2+1 + 1058, 1090, -1, + + // 1741, 446, N=2+1 + 1186, 1202, -1, + + // 1744, 447, N=2+1 + 718, 795, -1, + + // 1747, 448, N=2+1 + 933, 971, -1, + + // 1750, 449, N=2+1 + 474, 507, -1, + + // 1753, 450, N=2+1 + 934, 972, -1, + + // 1756, 451, N=2+1 + 1211, 1241, -1, + + // 1759, 452, N=2+1 + 2209, 2222, -1, + + // 1762, 453, N=2+1 + 593, 628, -1, + + // 1765, 454, N=4+1 + 468, 501, 530, 567, -1, + + // 1770, 455, N=2+1 + 1779, 1819, -1, + + // 1773, 456, N=2+1 + 1513, 1538, -1, + + // 1776, 457, N=2+1 + 1720, 1745, -1, + + // 1779, 458, N=2+1 + 1430, 1455, -1, + + // 1782, 459, N=2+1 + 1123, 1139, -1, + + // 1785, 460, N=2+1 + 1052, 1084, -1, + + // 1788, 461, N=2+1 + 2041, 2052, -1, + + // 1791, 462, N=2+1 + 1774, 1814, -1, + + // 1794, 463, N=2+1 + 1768, 1808, -1, + + // 1797, 464, N=2+1 + 1993, 2013, -1, + + // 1800, 465, N=3+1 + 108, 132, 2391, -1, + + // 1804, 466, N=2+1 + 1721, 1746, -1, + + // 1807, 467, N=2+1 + 2127, 2142, -1, + + // 1810, 468, N=3+1 + 602, 637, 2412, -1, + + // 1814, 469, N=2+1 + 105, 129, -1, + + // 1817, 470, N=1+1 + 2356, -1, + + // 1819, 471, N=2+1 + 1227, 1257, -1, + + // 1822, 472, N=1+1 + 2431, -1, + + // 1824, 473, N=2+1 + 1016, 1037, -1, + + // 1827, 474, N=2+1 + 75, 101, -1, + + // 1830, 475, N=2+1 + 903, 928, -1, + + // 1833, 476, N=2+1 + 1907, 1924, -1, + + // 1836, 477, N=2+1 + 683, 760, -1, + + // 1839, 478, N=2+1 + 2160, 2188, -1, + + // 1842, 479, N=2+1 + 403, 425, -1, + + // 1845, 480, N=2+1 + 2166, 2194, -1, + + // 1848, 481, N=2+1 + 1730, 1755, -1, + + // 1851, 482, N=2+1 + 1026, 1047, -1, + + // 1854, 483, N=21+1 + 1798, 1838, 1863, 1894, 1915, 1932, 1954, 1973, 1989, 2009, + 2026, 2037, 2048, 2059, 2072, 2087, 2100, 2111, 2122, 2137, + 2379, -1, + + // 1876, 484, N=2+1 + 1379, 1397, -1, + + // 1879, 485, N=2+1 + 1179, 1195, -1, + + // 1882, 486, N=1+1 + 2364, -1, + + // 1884, 487, N=2+1 + 1429, 1454, -1, + + // 1887, 488, N=2+1 + 1853, 1884, -1, + + // 1890, 489, N=2+1 + 828, 866, -1, + + // 1893, 490, N=2+1 + 2386, 2394, -1, + + // 1896, 491, N=2+1 + 307, 333, -1, + + // 1899, 492, N=4+1 + 522, 559, 1949, 1968, -1, + + // 1904, 493, N=3+1 + 111, 135, 2397, -1, + + // 1908, 494, N=4+1 + 11, 37, 2236, 2257, -1, + + // 1913, 495, N=1+1 + 2370, -1, + + // 1915, 496, N=1+1 + 2383, -1, + + // 1917, 497, N=2+1 + 1329, 1357, -1, + + // 1920, 498, N=2+1 + 2231, 2252, -1, + + // 1923, 499, N=2+1 + 1874, 1905, -1, + + // 1926, 500, N=2+1 + 405, 427, -1, + + // 1929, 501, N=2+1 + 179, 220, -1, + + // 1932, 502, N=2+1 + 247, 273, -1, + + // 1935, 503, N=1+1 + 2218, -1, + + // 1937, 504, N=2+1 + 2242, 2263, -1, + + // 1940, 505, N=2+1 + 399, 421, -1, + + // 1943, 506, N=2+1 + 2115, 2130, -1, + + // 1946, 507, N=1+1 + 1878, -1, + + // 1948, 508, N=2+1 + 1269, 1294, -1, + + // 1951, 509, N=2+1 + 2028, 2039, -1, + + // 1954, 510, N=2+1 + 942, 980, -1, + + // 1957, 511, N=2+1 + 1285, 1310, -1, + + // 1960, 512, N=1+1 + 2418, -1, + + // 1962, 513, N=2+1 + 1221, 1251, -1, + + // 1965, 514, N=2+1 + 339, 369, -1, + + // 1968, 515, N=2+1 + 1775, 1815, -1, + + // 1971, 516, N=2+1 + 1156, 1170, -1, + + // 1974, 517, N=2+1 + 1475, 1496, -1, + + // 1977, 518, N=2+1 + 1861, 1892, -1, + + // 1980, 519, N=3+1 + 1679, 1704, 2432, -1, + + // 1984, 520, N=2+1 + 2308, 2331, -1, + + // 1987, 521, N=4+1 + 70, 96, 1631, 1653, -1, + + // 1992, 522, N=1+1 + 731, -1, + + // 1994, 523, N=2+1 + 1318, 1346, -1, + + // 1997, 524, N=2+1 + 720, 797, -1, + + // 2000, 525, N=2+1 + 605, 640, -1, + + // 2003, 526, N=6+1 + 165, 206, 451, 484, 2155, 2183, -1, + + // 2010, 527, N=4+1 + 2211, 2224, 2349, 2360, -1, + + // 2015, 528, N=1+1 + 2367, -1, + + // 2017, 529, N=2+1 + 512, 549, -1, + + // 2020, 530, N=2+1 + 668, 745, -1, + + // 2023, 531, N=2+1 + 686, 763, -1, + + // 2026, 532, N=2+1 + 693, 770, -1, + + // 2029, 533, N=2+1 + 2270, 2283, -1, + + // 2032, 534, N=2+1 + 65, 91, -1, + + // 2035, 535, N=2+1 + 662, 739, -1, + + // 2038, 536, N=2+1 + 1385, 1403, -1, + + // 2041, 537, N=2+1 + 1723, 1748, -1, + + // 2044, 538, N=4+1 + 188, 229, 965, 1003, -1, + + // 2049, 539, N=8+1 + 708, 785, 953, 991, 1471, 1492, 1726, 1751, -1, + + // 2058, 540, N=2+1 + 949, 987, -1, + + // 2061, 541, N=2+1 + 969, 1007, -1, + + // 2064, 542, N=3+1 + 1680, 1705, 2433, -1, + + // 2068, 543, N=2+1 + 2276, 2289, -1, + + // 2071, 544, N=3+1 + 1068, 1100, 2439, -1, + + // 2075, 545, N=2+1 + 120, 144, -1, + + // 2078, 546, N=2+1 + 2305, 2328, -1, + + // 2081, 547, N=2+1 + 1994, 2014, -1, + + // 2084, 548, N=2+1 + 1017, 1038, -1, + + // 2087, 549, N=2+1 + 1520, 1545, -1, + + // 2090, 550, N=2+1 + 959, 997, -1, + + // 2093, 551, N=2+1 + 1854, 1885, -1, + + // 2096, 552, N=2+1 + 363, 393, -1, + + // 2099, 553, N=1+1 + 106, -1, + + // 2101, 554, N=2+1 + 1079, 1111, -1, + + // 2104, 555, N=1+1 + 203, -1, + + // 2106, 556, N=1+1 + 2375, -1, + + // 2108, 557, N=2+1 + 1664, 1689, -1, + + // 2111, 558, N=2+1 + 2024, 2035, -1, + + // 2114, 559, N=2+1 + 2362, 2366, -1, + + // 2117, 560, N=2+1 + 355, 385, -1, + + // 2120, 561, N=2+1 + 1764, 1804, -1, + + // 2123, 562, N=2+1 + 588, 623, -1, + + // 2126, 563, N=2+1 + 1913, 1930, -1, + + // 2129, 564, N=2+1 + 1505, 1530, -1, + + // 2132, 565, N=2+1 + 124, 148, -1, + + // 2135, 566, N=2+1 + 810, 848, -1, + + // 2138, 567, N=4+1 + 356, 386, 1678, 1703, -1, + + // 2143, 568, N=2+1 + 1214, 1244, -1, + + // 2146, 569, N=8+1 + 713, 790, 956, 958, 994, 996, 1729, 1754, -1, + + // 2155, 570, N=2+1 + 1228, 1258, -1, + + // 2158, 571, N=2+1 + 1466, 1487, -1, + + // 2161, 572, N=2+1 + 1795, 1835, -1, + + // 2164, 573, N=2+1 + 1326, 1354, -1, + + // 2167, 574, N=2+1 + 676, 753, -1, + + // 2170, 575, N=2+1 + 1012, 1033, -1, + + // 2173, 576, N=2+1 + 1732, 1757, -1, + + // 2176, 577, N=2+1 + 1770, 1810, -1, + + // 2179, 578, N=2+1 + 603, 638, -1, + + // 2182, 579, N=1+1 + 83, -1, + + // 2184, 580, N=3+1 + 1602, 1614, 2404, -1, + + // 2188, 581, N=2+1 + 1340, 1368, -1, + + // 2191, 582, N=1+1 + 1964, -1, + + // 2193, 583, N=2+1 + 886, 911, -1, + + // 2196, 584, N=2+1 + 1782, 1822, -1, + + // 2199, 585, N=2+1 + 727, 804, -1, + + // 2202, 586, N=2+1 + 1118, 1134, -1, + + // 2205, 587, N=2+1 + 2120, 2135, -1, + + // 2208, 588, N=2+1 + 302, 328, -1, + + // 2211, 589, N=2+1 + 466, 499, -1, + + // 2214, 590, N=2+1 + 1018, 1039, -1, + + // 2217, 591, N=2+1 + 943, 981, -1, + + // 2220, 592, N=2+1 + 1072, 1104, -1, + + // 2223, 593, N=2+1 + 442, 475, -1, + + // 2226, 594, N=2+1 + 820, 858, -1, + + // 2229, 595, N=1+1 + 812, -1, + + // 2231, 596, N=2+1 + 1461, 1482, -1, + + // 2234, 597, N=4+1 + 897, 922, 2427, 2429, -1, + + // 2239, 598, N=2+1 + 297, 323, -1, + + // 2242, 599, N=2+1 + 1389, 1407, -1, + + // 2245, 600, N=2+1 + 541, 578, -1, + + // 2248, 601, N=2+1 + 2043, 2054, -1, + + // 2251, 602, N=2+1 + 2164, 2192, -1, + + // 2254, 603, N=2+1 + 301, 327, -1, + + // 2257, 604, N=2+1 + 890, 915, -1, + + // 2260, 605, N=2+1 + 1278, 1303, -1, + + // 2263, 606, N=2+1 + 723, 800, -1, + + // 2266, 607, N=2+1 + 1080, 1112, -1, + + // 2269, 608, N=2+1 + 66, 92, -1, + + // 2272, 609, N=2+1 + 418, 440, -1, + + // 2275, 610, N=2+1 + 2163, 2191, -1, + + // 2278, 611, N=2+1 + 672, 749, -1, + + // 2281, 612, N=2+1 + 1793, 1833, -1, + + // 2284, 613, N=2+1 + 2384, 2392, -1, + + // 2287, 614, N=1+1 + 2385, -1, + + // 2289, 615, N=2+1 + 259, 285, -1, + + // 2292, 616, N=2+1 + 968, 1006, -1, + + // 2295, 617, N=4+1 + 1557, 1580, 2274, 2287, -1, + + // 2300, 618, N=2+1 + 698, 775, -1, + + // 2303, 619, N=2+1 + 1374, 1392, -1, + + // 2306, 620, N=2+1 + 2301, 2324, -1, + + // 2309, 621, N=2+1 + 2300, 2323, -1, + + // 2312, 622, N=2+1 + 2297, 2320, -1, + + // 2315, 623, N=2+1 + 842, 880, -1, + + // 2318, 624, N=2+1 + 1570, 1593, -1, + + // 2321, 625, N=2+1 + 2309, 2332, -1, + + // 2324, 626, N=2+1 + 1801, 1841, -1, + + // 2327, 627, N=2+1 + 338, 368, -1, + + // 2330, 628, N=2+1 + 163, 204, -1, + + // 2333, 629, N=2+1 + 458, 491, -1, + + // 2336, 630, N=2+1 + 1860, 1891, -1, + + // 2339, 631, N=2+1 + 883, 908, -1, + + // 2342, 632, N=10+1 + 172, 213, 464, 497, 523, 560, 1065, 1097, 1325, 1353, + -1, + + // 2353, 633, N=2+1 + 531, 568, -1, + + // 2356, 634, N=6+1 + 2168, 2196, 2212, 2225, 2350, 2361, -1, + + // 2363, 635, N=2+1 + 1667, 1692, -1, + + // 2366, 636, N=2+1 + 3, 29, -1, + + // 2369, 637, N=2+1 + 1322, 1350, -1, + + // 2372, 638, N=4+1 + 2210, 2223, 2348, 2359, -1, + + // 2377, 639, N=2+1 + 1078, 1110, -1, + + // 2380, 640, N=1+1 + 1981, -1, + + // 2382, 641, N=1+1 + 2376, -1, + + // 2384, 642, N=2+1 + 298, 324, -1, + + // 2387, 643, N=2+1 + 2042, 2053, -1, + + // 2390, 644, N=2+1 + 2306, 2329, -1, + + // 2393, 645, N=2+1 + 1858, 1889, -1, + + // 2396, 646, N=1+1 + 5, -1, + + // 2398, 647, N=2+1 + 1995, 2015, -1, + + // 2401, 648, N=2+1 + 840, 878, -1, + + // 2404, 649, N=2+1 + 1025, 1046, -1, + + // 2407, 650, N=2+1 + 946, 984, -1, + + // 2410, 651, N=2+1 + 1073, 1105, -1, + + // 2413, 652, N=4+1 + 1556, 1579, 2273, 2286, -1, + + // 2418, 653, N=2+1 + 2020, 2031, -1, + + // 2421, 654, N=2+1 + 1921, 1938, -1, + + // 2424, 655, N=2+1 + 1677, 1702, -1, + + // 2427, 656, N=2+1 + 2119, 2134, -1, + + // 2430, 657, N=2+1 + 22, 48, -1, + + // 2433, 658, N=2+1 + 532, 569, -1, + + // 2436, 659, N=5+1 + 175, 216, 1334, 1362, 2401, -1, + + // 2442, 660, N=2+1 + 17, 43, -1, + + // 2445, 661, N=2+1 + 2070, 2085, -1, + + // 2448, 662, N=4+1 + 1022, 1043, 2314, 2337, -1, + + // 2453, 663, N=2+1 + 1769, 1809, -1, + + // 2456, 664, N=2+1 + 352, 382, -1, + + // 2459, 665, N=2+1 + 584, 619, -1, + + // 2462, 666, N=2+1 + 2093, 2104, -1, + + // 2465, 667, N=2+1 + 1279, 1304, -1, + + // 2468, 668, N=2+1 + 1626, 1648, -1, + + // 2471, 669, N=2+1 + 719, 796, -1, + + // 2474, 670, N=2+1 + 185, 226, -1, + + // 2477, 671, N=1+1 + 2377, -1, + + // 2479, 672, N=2+1 + 1672, 1697, -1, + + // 2482, 673, N=1+1 + 547, -1, + + // 2484, 674, N=2+1 + 117, 141, -1, + + // 2487, 675, N=2+1 + 2295, 2318, -1, + + // 2490, 676, N=2+1 + 152, 193, -1, + + // 2493, 677, N=4+1 + 467, 500, 529, 566, -1, + + // 2498, 678, N=2+1 + 360, 390, -1, + + // 2501, 679, N=2+1 + 153, 194, -1, + + // 2504, 680, N=2+1 + 2207, 2220, -1, + + // 2507, 681, N=4+1 + 711, 788, 1420, 1445, -1, + + // 2512, 682, N=2+1 + 1639, 1661, -1, + + // 2515, 683, N=2+1 + 896, 921, -1, + + // 2518, 684, N=2+1 + 1225, 1255, -1, + + // 2521, 685, N=2+1 + 344, 374, -1, + + // 2524, 686, N=2+1 + 1872, 1903, -1, + + // 2527, 687, N=2+1 + 459, 492, -1, + + // 2530, 688, N=2+1 + 1272, 1297, -1, + + // 2533, 689, N=2+1 + 726, 803, -1, + + // 2536, 690, N=4+1 + 13, 39, 2238, 2259, -1, + + // 2541, 691, N=2+1 + 1008, 1029, -1, + + // 2544, 692, N=2+1 + 1061, 1093, -1, + + // 2547, 693, N=2+1 + 1218, 1248, -1, + + // 2550, 694, N=4+1 + 238, 264, 2148, 2176, -1, + + // 2555, 695, N=2+1 + 2294, 2317, -1, + + // 2558, 696, N=2+1 + 2340, 2351, -1, + + // 2561, 697, N=2+1 + 827, 865, -1, + + // 2564, 698, N=2+1 + 364, 394, -1, + + // 2567, 699, N=8+1 + 659, 736, 945, 983, 1465, 1486, 1722, 1747, -1, + + // 2576, 700, N=2+1 + 239, 265, -1, + + // 2579, 701, N=12+1 + 295, 321, 452, 485, 1054, 1086, 1320, 1348, 1848, 1879, + 2156, 2184, -1, + + // 2592, 702, N=2+1 + 1184, 1200, -1, + + // 2595, 703, N=2+1 + 1912, 1929, -1, + + // 2598, 704, N=2+1 + 1413, 1438, -1, + + // 2601, 705, N=2+1 + 515, 552, -1, + + // 2604, 706, N=2+1 + 1051, 1083, -1, + + // 2607, 707, N=2+1 + 1380, 1398, -1, + + // 2610, 708, N=2+1 + 2095, 2106, -1, + + // 2613, 709, N=5+1 + 834, 872, 1425, 1450, 2419, -1, + + // 2619, 710, N=2+1 + 1511, 1536, -1, + + // 2622, 711, N=2+1 + 343, 373, -1, + + // 2625, 712, N=2+1 + 1601, 1613, -1, + + // 2628, 713, N=2+1 + 77, 103, -1, + + // 2631, 714, N=4+1 + 25, 51, 2245, 2266, -1, + + // 2636, 715, N=2+1 + 1640, 1662, -1, + + // 2639, 716, N=22+1 + 118, 119, 142, 143, 251, 252, 277, 278, 527, 528, + 564, 565, 1563, 1564, 1586, 1587, 2277, 2278, 2290, 2291, + 2405, 2406, -1, + + // 2662, 717, N=2+1 + 2388, 2396, -1, + + // 2665, 718, N=2+1 + 808, 846, -1, + + // 2668, 719, N=2+1 + 1522, 1547, -1, + + // 2671, 720, N=2+1 + 444, 477, -1, + + // 2674, 721, N=2+1 + 1950, 1969, -1, + + // 2677, 722, N=2+1 + 1217, 1247, -1, + + // 2680, 723, N=2+1 + 613, 648, -1, + + // 2683, 724, N=2+1 + 1947, 1966, -1, + + // 2686, 725, N=2+1 + 960, 998, -1, + + // 2689, 726, N=2+1 + 1028, 1049, -1, + + // 2692, 727, N=2+1 + 589, 624, -1, + + // 2695, 728, N=2+1 + 1288, 1313, -1, + + // 2698, 729, N=2+1 + 59, 85, -1, + + // 2701, 730, N=2+1 + 667, 744, -1, + + // 2704, 731, N=2+1 + 183, 224, -1, + + // 2707, 732, N=30+1 + 181, 222, 538, 575, 608, 643, 833, 871, 900, 925, + 1075, 1107, 1125, 1141, 1231, 1261, 1287, 1312, 1336, 1364, + 1424, 1449, 1517, 1542, 1634, 1656, 1684, 1709, 1865, 1896, + -1, + + // 2738, 733, N=2+1 + 677, 754, -1, + + // 2741, 734, N=2+1 + 1267, 1292, -1, + + // 2744, 735, N=2+1 + 1629, 1651, -1, + + // 2747, 736, N=2+1 + 1010, 1031, -1, + + // 2750, 737, N=2+1 + 1126, 1142, -1, + + // 2753, 738, N=2+1 + 725, 802, -1, + + // 2756, 739, N=3+1 + 1076, 1108, 2434, -1, + + // 2760, 740, N=2+1 + 1014, 1035, -1, + + // 2763, 741, N=2+1 + 1416, 1441, -1, + + // 2766, 742, N=2+1 + 1464, 1485, -1, + + // 2769, 743, N=2+1 + 1373, 1391, -1, + + // 2772, 744, N=2+1 + 67, 93, -1, + + // 2775, 745, N=1+1 + 909, -1, + + // 2777, 746, N=2+1 + 341, 371, -1, + + // 2780, 747, N=2+1 + 1212, 1242, -1, + + // 2783, 748, N=2+1 + 461, 494, -1, + + // 2786, 749, N=2+1 + 182, 223, -1, + + // 2789, 750, N=2+1 + 1792, 1832, -1, + + // 2792, 751, N=2+1 + 893, 918, -1, + + // 2795, 752, N=2+1 + 682, 759, -1, + + // 2798, 753, N=1+1 + 2371, -1, + + // 2800, 754, N=1+1 + 130, -1, + + // 2802, 755, N=2+1 + 367, 397, -1, + + // 2805, 756, N=2+1 + 2076, 2091, -1, + + // 2808, 757, N=2+1 + 1343, 1371, -1, + + // 2811, 758, N=2+1 + 365, 395, -1, + + // 2814, 759, N=2+1 + 154, 195, -1, + + // 2817, 760, N=2+1 + 1159, 1173, -1, + + // 2820, 761, N=2+1 + 361, 391, -1, + + // 2823, 762, N=2+1 + 681, 758, -1, + + // 2826, 763, N=2+1 + 1524, 1549, -1, + + // 2829, 764, N=1+1 + 935, -1, + + // 2831, 765, N=2+1 + 513, 550, -1, + + // 2834, 766, N=6+1 + 52, 78, 236, 262, 2146, 2174, -1, + + // 2841, 767, N=2+1 + 687, 764, -1, + + // 2844, 768, N=2+1 + 190, 231, -1, + + // 2847, 769, N=2+1 + 1207, 1237, -1, + + // 2850, 770, N=1+1 + 447, -1, + + // 2852, 771, N=6+1 + 2, 28, 1786, 1826, 2228, 2249, -1, + + // 2859, 772, N=1+1 + 1032, -1, + + // 2861, 773, N=2+1 + 1378, 1396, -1, + + // 2864, 774, N=4+1 + 534, 536, 571, 573, -1, + + // 2869, 775, N=8+1 + 709, 786, 954, 992, 1472, 1493, 1727, 1752, -1, + + // 2878, 776, N=2+1 + 1276, 1301, -1, + + // 2881, 777, N=2+1 + 189, 230, -1, + + // 2884, 778, N=2+1 + 2117, 2132, -1, + + // 2887, 779, N=2+1 + 303, 329, -1, + + // 2890, 780, N=2+1 + 826, 864, -1, + + // 2893, 781, N=2+1 + 1427, 1452, -1, + + // 2896, 782, N=2+1 + 2125, 2140, -1, + + // 2899, 783, N=2+1 + 1226, 1256, -1, + + // 2902, 784, N=3+1 + 601, 636, 2411, -1, + + // 2906, 785, N=2+1 + 1509, 1534, -1, + + // 2909, 786, N=2+1 + 724, 801, -1, + + // 2912, 787, N=2+1 + 938, 976, -1, + + // 2915, 788, N=2+1 + 1209, 1239, -1, + + // 2918, 789, N=2+1 + 1328, 1356, -1, + + // 2921, 790, N=2+1 + 240, 2150, -1, + + // 2924, 791, N=2+1 + 894, 919, -1, + + // 2927, 792, N=2+1 + 526, 563, -1, + + // 2930, 793, N=2+1 + 813, 851, -1, + + // 2933, 794, N=2+1 + 416, 438, -1, + + // 2936, 795, N=2+1 + 1846, 1877, -1, + + // 2939, 796, N=2+1 + 1916, 1933, -1, + + // 2942, 797, N=4+1 + 235, 261, 2145, 2173, -1, + + // 2947, 798, N=4+1 + 520, 557, 1948, 1967, -1, + + // 2952, 799, N=2+1 + 1856, 1887, -1, + + // 2955, 800, N=2+1 + 2167, 2195, -1, + + // 2958, 801, N=2+1 + 1558, 1581, -1, + + // 2961, 802, N=2+1 + 104, 128, -1, + + // 2964, 803, N=1+1 + 318, -1, + + // 2966, 804, N=2+1 + 2094, 2105, -1, + + // 2969, 805, N=2+1 + 414, 436, -1, + + // 2972, 806, N=2+1 + 1128, 1144, -1, + + // 2975, 807, N=2+1 + 904, 929, -1, + + // 2978, 808, N=4+1 + 901, 926, 2123, 2138, -1, + + // 2983, 809, N=2+1 + 2019, 2030, -1, + + // 2986, 810, N=2+1 + 697, 774, -1, + + // 2989, 811, N=2+1 + 1056, 1088, -1, + + // 2992, 812, N=2+1 + 73, 99, -1, + + // 2995, 813, N=2+1 + 473, 506, -1, + + // 2998, 814, N=1+1 + 57, -1, + + // 3000, 815, N=2+1 + 1996, 2016, -1, + + // 3003, 816, N=2+1 + 1606, 1618, -1, + + // 3006, 817, N=2+1 + 679, 756, -1, + + // 3009, 818, N=2+1 + 583, 618, -1, + + // 3012, 819, N=2+1 + 1771, 1811, -1, + + // 3015, 820, N=2+1 + 1268, 1293, -1, + + // 3018, 821, N=2+1 + 2021, 2032, -1, + + // 3021, 822, N=3+1 + 463, 496, 2402, -1, + + // 3025, 823, N=2+1 + 1674, 1699, -1, + + // 3028, 824, N=2+1 + 1956, 1975, -1, + + // 3031, 825, N=2+1 + 443, 476, -1, + + // 3034, 826, N=2+1 + 1477, 1498, -1, + + // 3037, 827, N=2+1 + 582, 617, -1, + + // 3040, 828, N=2+1 + 2343, 2354, -1, + + // 3043, 829, N=1+1 + 31, -1, + + // 3045, 830, N=2+1 + 2344, 2355, -1, + + // 3048, 831, N=2+1 + 814, 852, -1, + + // 3051, 832, N=2+1 + 192, 233, -1, + + // 3054, 833, N=2+1 + 614, 649, -1, + + // 3057, 834, N=2+1 + 514, 551, -1, + + // 3060, 835, N=2+1 + 304, 330, -1, + + // 3063, 836, N=2+1 + 690, 767, -1, + + // 3066, 837, N=1+1 + 2374, -1, + + // 3068, 838, N=2+1 + 2161, 2189, -1, + + // 3071, 839, N=2+1 + 1715, 1740, -1, + + // 3074, 840, N=2+1 + 63, 89, -1, + + // 3077, 841, N=2+1 + 125, 149, -1, + + // 3080, 842, N=5+1 + 822, 860, 1415, 1440, 2421, -1, + + // 3086, 843, N=2+1 + 824, 862, -1, + + // 3089, 844, N=2+1 + 615, 650, -1, + + // 3092, 845, N=2+1 + 1866, 1897, -1, + + // 3095, 846, N=2+1 + 680, 757, -1, + + // 3098, 847, N=2+1 + 1803, 1843, -1, + + // 3101, 848, N=6+1 + 358, 388, 610, 645, 1682, 1707, -1, + + // 3108, 849, N=2+1 + 544, 581, -1, + + // 3111, 850, N=2+1 + 1777, 1817, -1, + + // 3114, 851, N=2+1 + 1219, 1249, -1, + + // 3117, 852, N=2+1 + 1153, 1167, -1, + + // 3120, 853, N=2+1 + 2066, 2081, -1, + + // 3123, 854, N=5+1 + 825, 863, 1418, 1443, 2422, -1, + + // 3129, 855, N=21+1 + 1797, 1837, 1862, 1893, 1914, 1931, 1953, 1972, 1988, 2008, + 2025, 2036, 2047, 2058, 2071, 2086, 2099, 2110, 2121, 2136, + 2378, -1, + + // 3151, 856, N=2+1 + 841, 879, -1, + + // 3154, 857, N=2+1 + 1067, 1099, -1, + + // 3157, 858, N=2+1 + 809, 847, -1, + + // 3160, 859, N=2+1 + 1331, 1359, -1, + + // 3163, 860, N=8+1 + 449, 482, 1053, 1085, 1506, 1531, 2152, 2180, -1, + + // 3172, 861, N=2+1 + 1909, 1926, -1, + + // 3175, 862, N=2+1 + 2307, 2330, -1, + + // 3178, 863, N=1+1 + 401, -1, + + // 3180, 864, N=3+1 + 887, 912, 2430, -1, + + // 3184, 865, N=2+1 + 1280, 1305, -1, + + // 3187, 866, N=2+1 + 591, 626, -1, + + // 3190, 867, N=2+1 + 1796, 1836, -1, + + // 3193, 868, N=2+1 + 2243, 2264, -1, + + // 3196, 869, N=2+1 + 1384, 1402, -1, + + // 3199, 870, N=2+1 + 1910, 1927, -1, + + // 3202, 871, N=2+1 + 2065, 2080, -1, + + // 3205, 872, N=2+1 + 2381, 2390, -1, + + // 3208, 873, N=2+1 + 311, 337, -1, + + // 3211, 874, N=2+1 + 1958, 1977, -1, + + // 3214, 875, N=2+1 + 1523, 1548, -1, + + // 3217, 876, N=2+1 + 1157, 1171, -1, + + // 3220, 877, N=2+1 + 2165, 2193, -1, + + // 3223, 878, N=2+1 + 2116, 2131, -1, + + // 3226, 879, N=2+1 + 695, 772, -1, + + // 3229, 880, N=2+1 + 1778, 1818, -1, + + // 3232, 881, N=6+1 + 357, 387, 609, 644, 1681, 1706, -1, + + // 3239, 882, N=2+1 + 1551, 1574, -1, + + // 3242, 883, N=5+1 + 186, 227, 1337, 1365, 2398, -1, + + // 3248, 884, N=2+1 + 1372, 1390, -1, + + // 3251, 885, N=3+1 + 1063, 1095, 2438, -1, + + // 3255, 886, N=1+1 + 884, -1, + + // 3257, 887, N=2+1 + 115, 139, -1, + + // 3260, 888, N=2+1 + 673, 750, -1, + + // 3263, 889, N=2+1 + 2063, 2078, -1, + + // 3266, 890, N=2+1 + 2316, 2339, -1, + + // 3269, 891, N=2+1 + 1957, 1976, -1, + + // 3272, 892, N=2+1 + 122, 146, -1, + + // 3275, 893, N=4+1 + 15, 41, 2240, 2261, -1, + + // 3280, 894, N=2+1 + 1514, 1539, -1, + + // 3283, 895, N=2+1 + 61, 87, -1, + + // 3286, 896, N=2+1 + 1772, 1812, -1, + + // 3289, 897, N=2+1 + 169, 210, -1, + + // 3292, 898, N=2+1 + 702, 779, -1, + + // 3295, 899, N=2+1 + 606, 641, -1, + + // 3298, 900, N=1+1 + 1945, -1, + + // 3300, 901, N=5+1 + 398, 420, 806, 844, 2437, -1, + + // 3306, 902, N=2+1 + 1232, 1262, -1, + + // 3309, 903, N=2+1 + 72, 98, -1, + + // 3312, 904, N=2+1 + 1738, 1763, -1, + + // 3315, 905, N=2+1 + 2312, 2335, -1, + + // 3318, 906, N=2+1 + 1800, 1840, -1, + + // 3321, 907, N=2+1 + 362, 392, -1, + + // 3324, 908, N=4+1 + 411, 433, 611, 646, -1, + + // 3329, 909, N=2+1 + 1428, 1453, -1, + + // 3332, 910, N=2+1 + 1382, 1400, -1, + + // 3335, 911, N=2+1 + 2049, 2060, -1, + + // 3338, 912, N=2+1 + 1794, 1834, -1, + + // 3341, 913, N=2+1 + 2170, 2198, -1, + + // 3344, 914, N=2+1 + 658, 735, -1, + + // 3347, 915, N=2+1 + 1024, 1045, -1, + + // 3350, 916, N=2+1 + 23, 49, -1, + + // 3353, 917, N=2+1 + 704, 781, -1, + + // 3356, 918, N=2+1 + 2169, 2197, -1, + + // 3359, 919, N=3+1 + 1069, 1101, 2440, -1, + + // END + -2 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// file /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const DbFileFILE_t DbFileFILE[N_DB_FILE_FILE+1] = +{ + // 0 + { 0, 260, 0x00830, 0, FF_UNKNOWN, "AIParam/AIParam.baa" }, + { 2, 260, 0x00830, 0, FF_UNKNOWN, "AIParam/AIParam.bas" }, + { 4, 1, 0x20068, 0, FF_BRRES, "BGteresaSFC.brres" }, + { 7, 2, 0x20068, 0, FF_BRRES, "BeltCrossing.brres" }, + { 10, 3, 0x20068, 0, FF_BRRES, "BeltCurveA.brres" }, + { 13, 5, 0x20068, 0, FF_BRRES, "BeltEasy.brres" }, + { 16, 7, 0x20068, 0, FF_BRRES, "Block.brres" }, + { 19, 9, 0x20068, 0, FF_BRRES, "CarA1.brres" }, + { 22, 10, 0x20068, 0, FF_BRRES, "CarA2.brres" }, + { 25, 11, 0x20068, 0, FF_BRRES, "CarA3.brres" }, + // 10 + { 28, 12, 0x20068, 0, FF_BRRES, "CarB.brres" }, + { 31, 13, 0x20068, 0, FF_BRRES, "Crane.brres" }, + { 34, 13, 0x20228, 0, FF_KCL, "Crane.kcl" }, + { 37, 14, 0x20068, 0, FF_BRRES, "Crescent64.brres" }, + { 40, 17, 0x20068, 0, FF_BRRES, "DKrockGC.brres" }, + { 44, 19, 0x20068, 0, FF_BRRES, "DKship64.brres" }, + { 47, 20, 0x26068, 22, FF_BRRES, "DKtreeA64.brres" }, + { 50, 21, 0x26068, 31, FF_BRRES, "DKtreeB64.brres" }, + { 53, 22, 0x2e068, 40, FF_BRRES, "DKturibashiGC.brres" }, + { 56, 23, 0x20068, 0, FF_BRRES, "DemoCol.brres" }, + // 20 + { 59, 23, 0x20228, 0, FF_KCL, "DemoCol.kcl" }, + { 62, 25, 0x20068, 0, FF_BRRES, "DemoJugemu.brres" }, + { 65, 26, 0x20068, 0, FF_BRRES, "DonkyCannonGC.brres" }, + { 68, 27, 0x20068, 0, FF_BRRES, "DonkyCannon_wii.brres" }, + { 71, 28, 0x20068, 0, FF_BRRES, "EarthRing.brres" }, + { 74, 33, 0x20068, 0, FF_BRRES, "Epropeller.brres" }, + { 77, 39, 0x20068, 0, FF_BRRES, "FireSnake.brres" }, + { 82, 40, 0x20068, 0, FF_BRRES, "FlagA1.brres" }, + { 85, 41, 0x20068, 0, FF_BRRES, "FlagA2.brres" }, + { 88, 42, 0x20068, 0, FF_BRRES, "FlagA3.brres" }, + // 30 + { 91, 44, 0x20068, 0, FF_BRRES, "FlagA5.brres" }, + { 94, 45, 0x20068, 0, FF_BRRES, "FlagB1.brres" }, + { 97, 46, 0x20068, 0, FF_BRRES, "FlagB2.brres" }, + { 102, 47, 0x20068, 0, FF_BRRES, "FlagB3.brres" }, + { 105, 48, 0x20068, 0, FF_BRRES, "FlagB4.brres" }, + { 108, 49, 0x20068, 0, FF_BRRES, "FlamePole.brres" }, + { 111, 49, 0x20228, 0, FF_KCL, "FlamePole.kcl" }, + { 114, 51, 0x20068, 0, FF_BRRES, "FlamePole_v.brres" }, + { 121, 60, 0x20068, 0, FF_BRRES, "HeyhoBallGBA.brres" }, + { 124, 61, 0x20068, 0, FF_BRRES, "HeyhoShipGBA.brres" }, + // 40 + { 127, 62, 0x26068, 82, FF_BRRES, "HeyhoTreeGBA.brres" }, + { 130, 63, 0x20068, 0, FF_BRRES, "InsekiA.brres" }, + { 133, 64, 0x20068, 0, FF_BRRES, "InsekiB.brres" }, + { 136, 65, 0x20068, 0, FF_BRRES, "K_bomb_car.brres" }, + { 140, 66, 0x50068, 0, FF_BRRES, "K_bomb_car1.brres" }, + { 142, 68, 0x20068, 0, FF_BRRES, "K_car_body.brres" }, + { 145, 69, 0x20068, 0, FF_BRRES, "K_chairlift00.brres" }, + { 148, 70, 0x20068, 0, FF_BRRES, "K_sticklift00.brres" }, + { 151, 71, 0x20068, 0, FF_BRRES, "K_truck.brres" }, + { 154, 73, 0x20068, 0, FF_BRRES, "KmoonZ.brres" }, + // 50 + { 157, 74, 0x20068, 0, FF_BRRES, "KoopaFigure64.brres" }, + { 160, 75, 0x20068, 0, FF_BRRES, "Ksticketc.brres" }, + { 163, 77, 0x50068, 0, FF_BRRES, "M_obj_jump.brres" }, + { 166, 77, 0x50228, 0, FF_KCL, "M_obj_jump.kcl" }, + { 169, 78, 0x50068, 0, FF_BRRES, "M_obj_jump2.brres" }, + { 171, 78, 0x50228, 0, FF_KCL, "M_obj_jump2.kcl" }, + { 173, 81, 0x50068, 0, FF_BRRES, "M_obj_s_jump.brres" }, + { 176, 81, 0x50228, 0, FF_KCL, "M_obj_s_jump.kcl" }, + { 179, 82, 0x50068, 0, FF_BRRES, "M_obj_s_jump2.brres" }, + { 181, 82, 0x50228, 0, FF_KCL, "M_obj_s_jump2.kcl" }, + // 60 + { 183, 84, 0x20068, 0, FF_BRRES, "MarioGo64.brres" }, + { 186, 85, 0x20068, 0, FF_BRRES, "MashBalloonGC.brres" }, + { 191, 86, 0x20068, 0, FF_BRRES, "Mdush.brres" }, + { 194, 86, 0x20228, 0, FF_KCL, "Mdush.kcl" }, + { 197, 87, 0x20068, 0, FF_BRRES, "MiiKanban.brres" }, + { 202, 88, 0x20068, 0, FF_BRRES, "MiiObj01.brres" }, + { 207, 89, 0x20068, 0, FF_BRRES, "MiiObj02.brres" }, + { 212, 90, 0x20068, 0, FF_BRRES, "MiiObj03.brres" }, + { 217, 91, 0x20068, 0, FF_BRRES, "MiiObjD01.brres" }, + { 220, 92, 0x20068, 0, FF_BRRES, "MiiObjD02.brres" }, + // 70 + { 223, 93, 0x20068, 0, FF_BRRES, "MiiObjD03.brres" }, + { 226, 94, 0x20068, 0, FF_BRRES, "MiiSighKino.brres" }, + { 229, 95, 0x20068, 0, FF_BRRES, "MiiSignNoko.brres" }, + { 232, 96, 0x20068, 0, FF_BRRES, "MiiSignWario.brres" }, + { 235, 98, 0x20068, 0, FF_BRRES, "MiiSphinxY2.brres" }, + { 238, 100, 0x20068, 0, FF_BRRES, "MiiStatueBD1.brres" }, + { 241, 101, 0x20068, 0, FF_BRRES, "MiiStatueBL1.brres" }, + { 244, 102, 0x20068, 0, FF_BRRES, "MiiStatueD1.brres" }, + { 247, 103, 0x20068, 0, FF_BRRES, "MiiStatueD2.brres" }, + { 250, 105, 0x20068, 0, FF_BRRES, "MiiStatueL1.brres" }, + // 80 + { 253, 106, 0x20068, 0, FF_BRRES, "MiiStatueL2.brres" }, + { 256, 107, 0x20068, 0, FF_BRRES, "MiiStatueL3.brres" }, + { 261, 108, 0x20068, 0, FF_BRRES, "MiiStatueM1.brres" }, + { 264, 109, 0x20068, 0, FF_BRRES, "MiiStatueM2.brres" }, + { 267, 110, 0x20068, 0, FF_BRRES, "MiiStatueP1.brres" }, + { 270, 112, 0x26068, 126, FF_BRRES, "PalmTree.brres" }, + { 273, 113, 0x20068, 0, FF_BRRES, "PeachHunsuiGC.brres" }, + { 276, 114, 0x20068, 0, FF_BRRES, "Piston.brres" }, + { 279, 115, 0x20068, 0, FF_BRRES, "Press.brres" }, + { 282, 116, 0x20068, 0, FF_BRRES, "Psea.brres" }, + // 90 + { 285, 117, 0x20068, 0, FF_BRRES, "RM_ring1.brres" }, + { 288, 117, 0x2022c, 0, FF_KCL, "RM_ring1.kcl" }, + { 291, 117, 0x2022c, 0, FF_KCL, "RM_ring1b.kcl" }, + { 294, 117, 0x2022c, 0, FF_KCL, "RM_ring2.kcl" }, + { 297, 117, 0x2022c, 0, FF_KCL, "RM_ring2b.kcl" }, + { 300, 117, 0x2022c, 0, FF_KCL, "RM_ring2c.kcl" }, + { 303, 117, 0x2022c, 0, FF_KCL, "RM_ring3.kcl" }, + { 306, 117, 0x2022c, 0, FF_KCL, "RM_ring3b.kcl" }, + { 309, 117, 0x2022c, 0, FF_KCL, "RM_ring3c.kcl" }, + { 312, 118, 0x20068, 0, FF_BRRES, "RhMiiKanban.brres" }, + // 100 + { 315, 119, 0x20068, 0, FF_BRRES, "SentakuDS.brres" }, + { 318, 120, 0x20068, 0, FF_BRRES, "ShMiiObj01.brres" }, + { 321, 121, 0x20068, 0, FF_BRRES, "ShMiiObj02.brres" }, + { 324, 122, 0x20068, 0, FF_BRRES, "ShMiiObj03.brres" }, + { 327, 123, 0x20068, 0, FF_BRRES, "SpaceSun.brres" }, + { 330, 124, 0x20068, 0, FF_BRRES, "Spot64.brres" }, + { 333, 125, 0x20068, 0, FF_BRRES, "StarRing.brres" }, + { 336, 127, 0x20068, 0, FF_BRRES, "TownBridgeDS.brres" }, + { 339, 127, 0x20228, 0, FF_KCL, "TownBridgeDS.kcl" }, + { 342, 127, 0x20228, 0, FF_KCL, "TownBridgeDS2.kcl" }, + // 110 + { 345, 127, 0x20228, 0, FF_KCL, "TownBridgeDS3.kcl" }, + { 348, 128, 0x26068, 144, FF_BRRES, "TownTreeDS.brres" }, + { 351, 129, 0x20068, 0, FF_BRRES, "TruckWagon.brres" }, + { 354, 130, 0x20068, 0, FF_BRRES, "Twanwan.brres" }, + { 358, 132, 0x20068, 0, FF_BRRES, "TwistedWay.brres" }, + { 361, 133, 0x20068, 0, FF_BRRES, "UtsuboDokan.brres" }, + { 364, 134, 0x20068, 0, FF_BRRES, "VolcanoBall1.brres" }, + { 367, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece0.kcl" }, + { 370, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece0b.kcl" }, + { 373, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece0c.kcl" }, + // 120 + { 376, 135, 0x20068, 0, FF_BRRES, "VolcanoPiece1.brres" }, + { 379, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece1.kcl" }, + { 382, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece11.kcl" }, + { 385, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece11b.kcl" }, + { 388, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece11c.kcl" }, + { 391, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece13.kcl" }, + { 394, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece13b.kcl" }, + { 397, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece13c.kcl" }, + { 400, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece14.kcl" }, + { 403, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece14b.kcl" }, + // 130 + { 406, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece14c.kcl" }, + { 409, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece16.kcl" }, + { 412, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece16b.kcl" }, + { 415, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece16c.kcl" }, + { 418, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece18.kcl" }, + { 421, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece18b.kcl" }, + { 424, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece18c.kcl" }, + { 427, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece1b.kcl" }, + { 430, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece1c.kcl" }, + { 433, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece2.kcl" }, + // 140 + { 436, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece2b.kcl" }, + { 439, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece2c.kcl" }, + { 442, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece3.kcl" }, + { 445, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece3b.kcl" }, + { 448, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece3c.kcl" }, + { 451, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece4.kcl" }, + { 454, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece4b.kcl" }, + { 457, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece4c.kcl" }, + { 460, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece5.kcl" }, + { 463, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece5b.kcl" }, + // 150 + { 466, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece5c.kcl" }, + { 469, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece7.kcl" }, + { 472, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece7b.kcl" }, + { 475, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece7c.kcl" }, + { 478, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece8.kcl" }, + { 481, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece8b.kcl" }, + { 484, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece8c.kcl" }, + { 487, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece9.kcl" }, + { 490, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece9b.kcl" }, + { 493, 135, 0x2022c, 0, FF_KCL, "VolcanoPiece9c.kcl" }, + // 160 + { 496, 136, 0x20068, 0, FF_BRRES, "VolcanoRock1.brres" }, + { 499, 136, 0x20228, 0, FF_KCL, "VolcanoRock1.kcl" }, + { 502, 136, 0x20228, 0, FF_KCL, "VolcanoRock2.kcl" }, + { 505, 137, 0x20068, 0, FF_BRRES, "WLarrowGC.brres" }, + { 508, 138, 0x26068, 153, FF_BRRES, "WLdokanGC.brres" }, + { 511, 138, 0x20228, 0, FF_KCL, "WLdokanGC.kcl" }, + { 514, 139, 0x20068, 0, FF_BRRES, "WLfirebarGC.brres" }, + { 519, 140, 0x26068, 159, FF_BRRES, "WLscreenGC.brres" }, + { 522, 141, 0x20068, 0, FF_BRRES, "WLwallGC.brres" }, + { 525, 141, 0x20228, 0, FF_KCL, "WLwallGC.kcl" }, + // 170 + { 528, 142, 0x20068, 0, FF_BRRES, "ami.brres" }, + { 531, 143, 0x20068, 0, FF_BRRES, "aurora.brres" }, + { 534, 144, 0x20068, 0, FF_BRRES, "b_teresa.brres" }, + { 537, 145, 0x20068, 0, FF_BRRES, "basabasa.brres" }, + { 543, 146, 0x20068, 0, FF_BRRES, "bblock1.brres" }, + { 546, 146, 0x2022c, 0, FF_KCL, "bblock1.kcl" }, + { 549, 146, 0x2022c, 0, FF_KCL, "bblock1b.kcl" }, + { 552, 146, 0x2022c, 0, FF_KCL, "bblock2.kcl" }, + { 555, 146, 0x2022c, 0, FF_KCL, "bblock2b.kcl" }, + { 558, 146, 0x2022c, 0, FF_KCL, "bblock3.kcl" }, + // 180 + { 561, 146, 0x2022c, 0, FF_KCL, "bblock3b.kcl" }, + { 564, 146, 0x2022c, 0, FF_KCL, "bblock4.kcl" }, + { 567, 146, 0x2022c, 0, FF_KCL, "bblock4b.kcl" }, + { 570, 146, 0x2022c, 0, FF_KCL, "bblock5.kcl" }, + { 573, 146, 0x2022c, 0, FF_KCL, "bblock5b.kcl" }, + { 576, 146, 0x2022c, 0, FF_KCL, "bblock6.kcl" }, + { 579, 146, 0x2022c, 0, FF_KCL, "bblock6b.kcl" }, + { 582, 146, 0x2022c, 0, FF_KCL, "bblock7.kcl" }, + { 585, 146, 0x2022c, 0, FF_KCL, "bblock7b.kcl" }, + { 588, 146, 0x2022c, 0, FF_KCL, "bblock8.kcl" }, + // 190 + { 591, 146, 0x2022c, 0, FF_KCL, "bblock8b.kcl" }, + { 594, 146, 0x2022c, 0, FF_KCL, "bblock9.kcl" }, + { 597, 146, 0x2022c, 0, FF_KCL, "bblock9b.kcl" }, + { 600, 147, 0x20068, 0, FF_BRRES, "begoman_spike.brres" }, + { 603, 148, 0x20068, 0, FF_BRRES, "bird.brres" }, + { 606, 149, 0x20068, 0, FF_BRRES, "boble.brres" }, + { 609, 26, 0x20128, 0, FF_BRASD, "brasd/DonkyCannonGC/DonkyCannonGC.brasd" }, + { 612, 27, 0x20128, 0, FF_BRASD, "brasd/DonkyCannon_wii/DonkyCannon_wii.brasd" }, + { 615, 160, 0x20128, 0, FF_BRASD, "brasd/cow/eat.brasd" }, + { 618, 160, 0x20128, 0, FF_BRASD, "brasd/cow/eat_ed.brasd" }, + // 200 + { 621, 160, 0x20128, 0, FF_BRASD, "brasd/cow/run.brasd" }, + { 624, 160, 0x20128, 0, FF_BRASD, "brasd/cow/surprise.brasd" }, + { 627, 160, 0x20128, 0, FF_BRASD, "brasd/cow/walk.brasd" }, + { 630, 161, 0x20128, 0, FF_BRASD, "brasd/crab/wait.brasd" }, + { 633, 161, 0x20128, 0, FF_BRASD, "brasd/crab/walk_l.brasd" }, + { 636, 161, 0x20128, 0, FF_BRASD, "brasd/crab/walk_r.brasd" }, + { 639, 187, 0x20128, 0, FF_BRASD, "brasd/hanachan/head_wait2.brasd" }, + { 643, 187, 0x20128, 0, FF_BRASD, "brasd/hanachan/walk.brasd" }, + { 647, 199, 0x20128, 0, FF_BRASD, "brasd/koopaFigure/vomit.brasd" }, + { 650, 199, 0x20128, 0, FF_BRASD, "brasd/koopaFigure/wait.brasd" }, + // 210 + { 653, 201, 0x20128, 0, FF_BRASD, "brasd/kuribo/walk_l.brasd" }, + { 665, 201, 0x20128, 0, FF_BRASD, "brasd/kuribo/walk_r.brasd" }, + { 677, 218, 0x20128, 0, FF_BRASD, "brasd/pakkun_f/attack.brasd" }, + { 682, 218, 0x20128, 0, FF_BRASD, "brasd/pakkun_f/wait.brasd" }, + { 687, 222, 0x20128, 0, FF_BRASD, "brasd/penguin_m/walk.brasd" }, + { 690, 223, 0x20128, 0, FF_BRASD, "brasd/penguin_s/dive.brasd" }, + { 693, 223, 0x20128, 0, FF_BRASD, "brasd/penguin_s/stand_up.brasd" }, + { 696, 223, 0x20128, 0, FF_BRASD, "brasd/penguin_s/walk.brasd" }, + { 699, 228, 0x20128, 0, FF_BRASD, "brasd/poihana/throw.brasd" }, + { 703, 228, 0x20128, 0, FF_BRASD, "brasd/poihana/walk.brasd" }, + // 220 + { 707, 229, 0x20128, 0, FF_BRASD, "brasd/puchi_pakkun/puchi_pakkun.brasd" }, + { 710, 235, 0x20128, 0, FF_BRASD, "brasd/sanbo/revive.brasd" }, + { 716, 243, 0x20128, 0, FF_BRASD, "brasd/tree_cannon/tree_cannon.brasd" }, + { 719, 150, 0x20068, 0, FF_BRRES, "bulldozer_left.brres" }, + { 722, 150, 0x20228, 0, FF_KCL, "bulldozer_left.kcl" }, + { 725, 151, 0x20068, 0, FF_BRRES, "bulldozer_right.brres" }, + { 728, 151, 0x20228, 0, FF_KCL, "bulldozer_right.kcl" }, + { 731, 153, 0x20068, 0, FF_BRRES, "casino_roulette.brres" }, + { 734, 153, 0x20228, 0, FF_KCL, "casino_roulette.kcl" }, + { 737, 154, 0x20068, 0, FF_BRRES, "castleballoon1.brres" }, + // 230 + { 742, 155, 0x20048, 0, FF_BRRES, "castleflower1.brres" }, + { 751, 156, 0x26048, 1, FF_BRRES, "castletree1.brres" }, + { 762, 157, 0x26048, 10, FF_BRRES, "castletree2.brres" }, + { 773, 158, 0x20068, 0, FF_BRRES, "choropu.brres" }, + { 778, 159, 0x26068, 19, FF_BRRES, "coin.brres" }, + { 800, 259, 0x20820, 0, FF_UNKNOWN, "course.0" }, + { 803, 257, 0x00800, 0, FF_UNKNOWN, "course.bmm" }, + { 816, 257, 0x00800, 0, FF_UNKNOWN, "course.btiEnv" }, + { 829, 257, 0x00800, 0, FF_UNKNOWN, "course.btiMat" }, + { 842, 257, 0x00201, 0, FF_KCL, "course.kcl" }, + // 240 + { 942, 257, 0x00401, 0, FF_KMP, "course.kmp" }, + { 1042, 260, 0x00834, 0, FF_UNKNOWN, "course.lex" }, + { 1044, 257, 0x00042, 0, FF_BRRES, "course_d_model.brres" }, + { 1065, 257, 0x00042, 0, FF_BRRES, "course_model.brres" }, + { 1145, 160, 0x20068, 0, FF_BRRES, "cow.brres" }, + { 1148, 161, 0x20068, 0, FF_BRRES, "crab.brres" }, + { 1151, 162, 0x20068, 0, FF_BRRES, "cruiser.brres" }, + { 1156, 163, 0x20068, 0, FF_BRRES, "dc_pillar.brres" }, + { 1159, 163, 0x20228, 0, FF_KCL, "dc_pillar.kcl" }, + { 1162, 260, 0x20220, 0, FF_KCL, "dc_pillar_base.kcl" }, + // 250 + { 1165, 164, 0x20068, 0, FF_BRRES, "dc_sandcone.brres" }, + { 1168, 164, 0x20228, 0, FF_KCL, "dc_sandcone.kcl" }, + { 1171, 165, 0x20068, 0, FF_BRRES, "dk_miiobj00.brres" }, + { 1174, 166, 0x20068, 0, FF_BRRES, "dkmonitor.brres" }, + { 1177, 167, 0x26068, 49, FF_BRRES, "dokan_sfc.brres" }, + { 1183, 168, 0x26068, 55, FF_BRRES, "donkytree1GC.brres" }, + { 1186, 169, 0x26068, 64, FF_BRRES, "donkytree2GC.brres" }, + { 1189, 170, 0x20068, 0, FF_BRRES, "dossun.brres" }, + { 1199, 0, 0x200a8, 0, FF_BREFF, "effect/Alarm/rk_alarm.breff" }, + { 1202, 0, 0x200a8, 0, FF_BREFT, "effect/Alarm/rk_alarm.breft" }, + // 260 + { 1205, 7, 0x200a8, 0, FF_BREFF, "effect/Block/rk_block.breff" }, + { 1208, 7, 0x200a8, 0, FF_BREFT, "effect/Block/rk_block.breft" }, + { 1211, 15, 0x200a8, 0, FF_BREFF, "effect/DKfalls/rk_64DKfalls.breff" }, + { 1214, 15, 0x200a8, 0, FF_BREFT, "effect/DKfalls/rk_64DKfalls.breft" }, + { 1217, 17, 0x200a8, 0, FF_BREFF, "effect/DKrockGC/rk_DKrockGC.breff" }, + { 1221, 17, 0x200a8, 0, FF_BREFT, "effect/DKrockGC/rk_DKrockGC.breft" }, + { 1225, 19, 0x200a8, 0, FF_BREFF, "effect/DKship64/rk_DKship64.breff" }, + { 1228, 19, 0x200a8, 0, FF_BREFT, "effect/DKship64/rk_DKship64.breft" }, + { 1231, 24, 0x200a8, 0, FF_BREFF, "effect/DemoEf/rk_demo.breff" }, + { 1234, 24, 0x200a8, 0, FF_BREFT, "effect/DemoEf/rk_demo.breft" }, + // 270 + { 1237, 25, 0x200a8, 0, FF_BREFF, "effect/DemoJugemu/rk_DemoJugemu.breff" }, + { 1240, 25, 0x200a8, 0, FF_BREFT, "effect/DemoJugemu/rk_DemoJugemu.breft" }, + { 1243, 26, 0x200a8, 0, FF_BREFF, "effect/DonkyCannonGC/rk_dkCannon.breff" }, + { 1246, 26, 0x200a8, 0, FF_BREFT, "effect/DonkyCannonGC/rk_dkCannon.breft" }, + { 1249, 27, 0x200a8, 0, FF_BREFF, "effect/DonkyCannon_wii/rk_dkCannon.breff" }, + { 1252, 27, 0x200a8, 0, FF_BREFT, "effect/DonkyCannon_wii/rk_dkCannon.breft" }, + { 1255, 29, 0x200a8, 0, FF_BREFF, "effect/EnvFire/rk_EnvFire.breff" }, + { 1264, 29, 0x200a8, 0, FF_BREFT, "effect/EnvFire/rk_EnvFire.breft" }, + { 1273, 30, 0x200a8, 0, FF_BREFF, "effect/EnvKareha/rk_EnvKareha.breff" }, + { 1276, 30, 0x200a8, 0, FF_BREFT, "effect/EnvKareha/rk_EnvKareha.breft" }, + // 280 + { 1279, 31, 0x200a8, 0, FF_BREFF, "effect/EnvKarehaUp/rk_EnvKarehaUp.breff" }, + { 1282, 31, 0x200a8, 0, FF_BREFT, "effect/EnvKarehaUp/rk_EnvKarehaUp.breft" }, + { 1285, 32, 0x200a8, 0, FF_BREFF, "effect/EnvSnow/rk_EnvSnow.breff" }, + { 1288, 32, 0x200a8, 0, FF_BREFT, "effect/EnvSnow/rk_EnvSnow.breft" }, + { 1291, 33, 0x200a8, 0, FF_BREFF, "effect/Epropeller/rk_epropeller.breff" }, + { 1294, 33, 0x200a8, 0, FF_BREFT, "effect/Epropeller/rk_epropeller.breft" }, + { 1297, 34, 0x200a8, 0, FF_BREFF, "effect/FallBsA/rk_fallbasin.breff" }, + { 1300, 34, 0x200a8, 0, FF_BREFT, "effect/FallBsA/rk_fallbasin.breft" }, + { 1303, 35, 0x200a8, 0, FF_BREFF, "effect/FallBsB/rk_fallbasin.breff" }, + { 1306, 35, 0x200a8, 0, FF_BREFT, "effect/FallBsB/rk_fallbasin.breft" }, + // 290 + { 1309, 37, 0x200a8, 0, FF_BREFF, "effect/Fall_MH/rk_ridgeFall.breff" }, + { 1312, 37, 0x200a8, 0, FF_BREFT, "effect/Fall_MH/rk_ridgeFall.breft" }, + { 1315, 38, 0x200a8, 0, FF_BREFF, "effect/Fall_Y/rk_yoshiFalls.breff" }, + { 1318, 38, 0x200a8, 0, FF_BREFT, "effect/Fall_Y/rk_yoshiFalls.breft" }, + { 1321, 39, 0x200a8, 0, FF_BREFF, "effect/FireSnake/rk_fireSnake.breff" }, + { 1326, 39, 0x200a8, 0, FF_BREFT, "effect/FireSnake/rk_fireSnake.breft" }, + { 1331, 49, 0x200a8, 0, FF_BREFF, "effect/FlamePole/rk_flamePole.breff" }, + { 1334, 49, 0x200a8, 0, FF_BREFT, "effect/FlamePole/rk_flamePole.breft" }, + { 1337, 51, 0x200a8, 0, FF_BREFF, "effect/FlamePole_v/rk_flamePole.breff" }, + { 1344, 51, 0x200a8, 0, FF_BREFT, "effect/FlamePole_v/rk_flamePole.breft" }, + // 300 + { 1351, 52, 0x200a8, 0, FF_BREFF, "effect/FlamePole_v_big/rk_flamePoleL.breff" }, + { 1354, 52, 0x200a8, 0, FF_BREFT, "effect/FlamePole_v_big/rk_flamePoleL.breft" }, + { 1357, 53, 0x200a8, 0, FF_BREFF, "effect/Flash_B/rk_flashBCross.breff" }, + { 1360, 53, 0x200a8, 0, FF_BREFT, "effect/Flash_B/rk_flashBCross.breft" }, + { 1363, 54, 0x200a8, 0, FF_BREFF, "effect/Flash_L/rk_flashBeginner.breff" }, + { 1366, 54, 0x200a8, 0, FF_BREFT, "effect/Flash_L/rk_flashBeginner.breft" }, + { 1369, 55, 0x200a8, 0, FF_BREFF, "effect/Flash_M/rk_flash64mario.breff" }, + { 1372, 55, 0x200a8, 0, FF_BREFT, "effect/Flash_M/rk_flash64mario.breft" }, + { 1375, 56, 0x200a8, 0, FF_BREFF, "effect/Flash_S/rk_flashSkate.breff" }, + { 1378, 56, 0x200a8, 0, FF_BREFT, "effect/Flash_S/rk_flashSkate.breft" }, + // 310 + { 1381, 57, 0x200a8, 0, FF_BREFF, "effect/Flash_W/rk_flashWaluigi.breff" }, + { 1384, 57, 0x200a8, 0, FF_BREFT, "effect/Flash_W/rk_flashWaluigi.breft" }, + { 1387, 59, 0x200a8, 0, FF_BREFF, "effect/Hanabi/rk_stHanabi.breff" }, + { 1404, 59, 0x200a8, 0, FF_BREFT, "effect/Hanabi/rk_stHanabi.breft" }, + { 1421, 60, 0x200a8, 0, FF_BREFF, "effect/HeyhoBallGBA/rk_HeyhoBallGBA.breff" }, + { 1424, 60, 0x200a8, 0, FF_BREFT, "effect/HeyhoBallGBA/rk_HeyhoBallGBA.breft" }, + { 1427, 65, 0x200a8, 0, FF_BREFF, "effect/K_bomb_car/rk_K_bomb_car.breff" }, + { 1431, 65, 0x200a8, 0, FF_BREFT, "effect/K_bomb_car/rk_K_bomb_car.breft" }, + { 1435, 66, 0x500a8, 0, FF_BREFF, "effect/K_bomb_car1/rk_K_bomb_car.breff" }, + { 1437, 66, 0x500a8, 0, FF_BREFT, "effect/K_bomb_car1/rk_K_bomb_car.breft" }, + // 320 + { 1439, 72, 0x200a8, 0, FF_BREFF, "effect/Kamifubuki/rk_kamifubuki.breff" }, + { 1442, 72, 0x200a8, 0, FF_BREFT, "effect/Kamifubuki/rk_kamifubuki.breft" }, + { 1445, 74, 0x200a8, 0, FF_BREFF, "effect/KoopaFigure64/rk_koopaFire.breff" }, + { 1448, 74, 0x200a8, 0, FF_BREFT, "effect/KoopaFigure64/rk_koopaFire.breft" }, + { 1451, 113, 0x200a8, 0, FF_BREFF, "effect/PeachHunsuiGC/rk_hunsui.breff" }, + { 1454, 113, 0x200a8, 0, FF_BREFT, "effect/PeachHunsuiGC/rk_hunsui.breft" }, + { 1457, 115, 0x200a8, 0, FF_BREFF, "effect/Press/rk_press.breff" }, + { 1460, 115, 0x200a8, 0, FF_BREFT, "effect/Press/rk_press.breft" }, + { 1463, 125, 0x200a8, 0, FF_BREFF, "effect/StarRing/rk_StarRing.breff" }, + { 1466, 125, 0x200a8, 0, FF_BREFT, "effect/StarRing/rk_StarRing.breft" }, + // 330 + { 1469, 126, 0x200a8, 0, FF_BREFF, "effect/Steam/rk_steam.breff" }, + { 1472, 126, 0x200a8, 0, FF_BREFT, "effect/Steam/rk_steam.breft" }, + { 1475, 129, 0x200a8, 0, FF_BREFF, "effect/TruckWagon/rk_truck.breff" }, + { 1478, 129, 0x200a8, 0, FF_BREFT, "effect/TruckWagon/rk_truck.breft" }, + { 1481, 134, 0x200a8, 0, FF_BREFF, "effect/VolcanoBall1/rk_volc.breff" }, + { 1484, 134, 0x200a8, 0, FF_BREFT, "effect/VolcanoBall1/rk_volc.breft" }, + { 1487, 147, 0x200a8, 0, FF_BREFF, "effect/begoman_spike/rk_begoman_spike.breff" }, + { 1490, 147, 0x200a8, 0, FF_BREFT, "effect/begoman_spike/rk_begoman_spike.breft" }, + { 1493, 149, 0x200a8, 0, FF_BREFF, "effect/boble/rk_boble.breff" }, + { 1496, 149, 0x200a8, 0, FF_BREFT, "effect/boble/rk_boble.breft" }, + // 340 + { 1499, 158, 0x200a8, 0, FF_BREFF, "effect/choropu/rk_choropuMoko.breff" }, + { 1504, 158, 0x200a8, 0, FF_BREFT, "effect/choropu/rk_choropuMoko.breft" }, + { 1509, 159, 0x200a8, 0, FF_BREFF, "effect/coin/rk_coin.breff" }, + { 1531, 159, 0x200a8, 0, FF_BREFT, "effect/coin/rk_coin.breft" }, + { 1553, 161, 0x200a8, 0, FF_BREFF, "effect/crab/rk_crab.breff" }, + { 1556, 161, 0x200a8, 0, FF_BREFT, "effect/crab/rk_crab.breft" }, + { 1559, 162, 0x200a8, 0, FF_BREFF, "effect/cruiser/rk_cruiser.breff" }, + { 1564, 162, 0x200a8, 0, FF_BREFT, "effect/cruiser/rk_cruiser.breft" }, + { 1569, 163, 0x200a8, 0, FF_BREFF, "effect/dc_pillar/rk_dc_pillar.breff" }, + { 1572, 163, 0x200a8, 0, FF_BREFT, "effect/dc_pillar/rk_dc_pillar.breft" }, + // 350 + { 1575, 164, 0x200a8, 0, FF_BREFF, "effect/dc_sandcone/rk_dc_sandcone.breff" }, + { 1578, 164, 0x200a8, 0, FF_BREFT, "effect/dc_sandcone/rk_dc_sandcone.breft" }, + { 1581, 170, 0x200a8, 0, FF_BREFF, "effect/dossun/rk_dossun.breff" }, + { 1591, 170, 0x200a8, 0, FF_BREFT, "effect/dossun/rk_dossun.breft" }, + { 1601, 172, 0x200a8, 0, FF_BREFF, "effect/entry/rk_entry.breff" }, + { 1606, 172, 0x200a8, 0, FF_BREFT, "effect/entry/rk_entry.breft" }, + { 1611, 187, 0x200a8, 0, FF_BREFF, "effect/hanachan/rk_hana.breff" }, + { 1615, 187, 0x200a8, 0, FF_BREFT, "effect/hanachan/rk_hana.breft" }, + { 1619, 190, 0x500a8, 0, FF_BREFF, "effect/honeBall/rk_honeBall.breff" }, + { 1621, 190, 0x500a8, 0, FF_BREFT, "effect/honeBall/rk_honeBall.breft" }, + // 360 + { 1623, 193, 0x200a8, 0, FF_BREFF, "effect/karehayama/rk_karehayama.breff" }, + { 1626, 193, 0x200a8, 0, FF_BREFT, "effect/karehayama/rk_karehayama.breft" }, + { 1629, 198, 0x200a8, 0, FF_BREFF, "effect/koopaBall/rk_koopaBall.breff" }, + { 1632, 198, 0x200a8, 0, FF_BREFT, "effect/koopaBall/rk_koopaBall.breft" }, + { 1635, 202, 0x200a8, 0, FF_BREFF, "effect/leaf_effect/rk_leaf_effect.breff" }, + { 1638, 202, 0x200a8, 0, FF_BREFT, "effect/leaf_effect/rk_leaf_effect.breft" }, + { 1641, 221, 0x200a8, 0, FF_BREFF, "effect/penguin_l/rk_penguin_l.breff" }, + { 1644, 221, 0x200a8, 0, FF_BREFT, "effect/penguin_l/rk_penguin_l.breft" }, + { 1647, 222, 0x200a8, 0, FF_BREFF, "effect/penguin_m/rk_penguin_m.breff" }, + { 1650, 222, 0x200a8, 0, FF_BREFT, "effect/penguin_m/rk_penguin_m.breft" }, + // 370 + { 1653, 223, 0x200a8, 0, FF_BREFF, "effect/penguin_s/rk_penguin_s.breff" }, + { 1656, 223, 0x200a8, 0, FF_BREFT, "effect/penguin_s/rk_penguin_s.breft" }, + { 1659, 225, 0x200a8, 0, FF_BREFF, "effect/pocha/rk_pocha.breff" }, + { 1690, 225, 0x200a8, 0, FF_BREFT, "effect/pocha/rk_pocha.breft" }, + { 1721, 226, 0x200a8, 0, FF_BREFF, "effect/pochaMori/rk_pochaLeaf.breff" }, + { 1726, 226, 0x200a8, 0, FF_BREFT, "effect/pochaMori/rk_pochaLeaf.breft" }, + { 1731, 227, 0x200a8, 0, FF_BREFF, "effect/pochaYogan/rk_pochaYogan.breff" }, + { 1740, 227, 0x200a8, 0, FF_BREFT, "effect/pochaYogan/rk_pochaYogan.breft" }, + { 1749, 228, 0x200a8, 0, FF_BREFF, "effect/poihana/rk_poihana.breff" }, + { 1753, 228, 0x200a8, 0, FF_BREFT, "effect/poihana/rk_poihana.breft" }, + // 380 + { 1757, 232, 0x200a8, 0, FF_BREFF, "effect/quicksand/rk_qsandDossun.breff" }, + { 1760, 232, 0x200a8, 0, FF_BREFT, "effect/quicksand/rk_qsandDossun.breft" }, + { 1763, 235, 0x200a8, 0, FF_BREFF, "effect/sanbo/rk_sanbo.breff" }, + { 1769, 235, 0x200a8, 0, FF_BREFT, "effect/sanbo/rk_sanbo.breft" }, + { 1775, 239, 0x500a8, 0, FF_BREFF, "effect/starGate/rk_starGate.breff" }, + { 1777, 239, 0x500a8, 0, FF_BREFT, "effect/starGate/rk_starGate.breft" }, + { 1779, 242, 0x200a8, 0, FF_BREFF, "effect/taimatsu/rk_taimatsu.breff" }, + { 1788, 242, 0x200a8, 0, FF_BREFT, "effect/taimatsu/rk_taimatsu.breft" }, + { 1797, 243, 0x200a8, 0, FF_BREFF, "effect/tree_cannon/rk_dkCannon.breff" }, + { 1800, 243, 0x200a8, 0, FF_BREFT, "effect/tree_cannon/rk_dkCannon.breft" }, + // 390 + { 1803, 244, 0x200a8, 0, FF_BREFF, "effect/truckChimSmk/rk_truckChimSmk.breff" }, + { 1808, 244, 0x200a8, 0, FF_BREFT, "effect/truckChimSmk/rk_truckChimSmk.breft" }, + { 1813, 245, 0x200a8, 0, FF_BREFF, "effect/truckChimSmkW/rk_truckChimSmkW.breff" }, + { 1816, 245, 0x200a8, 0, FF_BREFT, "effect/truckChimSmkW/rk_truckChimSmkW.breft" }, + { 1819, 249, 0x200a8, 0, FF_BREFF, "effect/venice_hasi/rk_venice.breff" }, + { 1822, 249, 0x200a8, 0, FF_BREFT, "effect/venice_hasi/rk_venice.breft" }, + { 1825, 253, 0x200a8, 0, FF_BREFF, "effect/volsmk/rk_volsmk.breff" }, + { 1828, 253, 0x200a8, 0, FF_BREFT, "effect/volsmk/rk_volsmk.breft" }, + { 1831, 254, 0x200a8, 0, FF_BREFF, "effect/wanwan/rk_wanwan.breff" }, + { 1839, 254, 0x200a8, 0, FF_BREFT, "effect/wanwan/rk_wanwan.breft" }, + // 400 + { 1847, 256, 0x200a8, 0, FF_BREFF, "effect/woodbox/rk_woodbox.breff" }, + { 1853, 256, 0x200a8, 0, FF_BREFT, "effect/woodbox/rk_woodbox.breft" }, + { 1859, 173, 0x20068, 0, FF_BRRES, "escalator.brres" }, + { 1862, 173, 0x20228, 0, FF_KCL, "escalator.kcl" }, + { 1865, 174, 0x20068, 0, FF_BRRES, "fks_screen_wii.brres" }, + { 1868, 177, 0x26068, 73, FF_BRRES, "gardentreeDS.brres" }, + { 1871, 178, 0x20068, 0, FF_BRRES, "group_enemy_a.brres" }, + { 1880, 179, 0x20068, 0, FF_BRRES, "group_enemy_a_demo.brres" }, + { 1883, 180, 0x20068, 0, FF_BRRES, "group_enemy_b.brres" }, + { 1890, 181, 0x20068, 0, FF_BRRES, "group_enemy_c.brres" }, + // 410 + { 1897, 183, 0x20068, 0, FF_BRRES, "group_enemy_e.brres" }, + { 1900, 184, 0x20068, 0, FF_BRRES, "group_enemy_f.brres" }, + { 1903, 185, 0x20068, 0, FF_BRRES, "group_monte_a.brres" }, + { 1916, 186, 0x20068, 0, FF_BRRES, "group_monte_a_demo.brres" }, + { 1919, 187, 0x20068, 0, FF_BRRES, "hanachan.brres" }, + { 1923, 188, 0x20068, 0, FF_BRRES, "heyho.brres" }, + { 1926, 189, 0x20068, 0, FF_BRRES, "heyho2.brres" }, + { 1929, 190, 0x50068, 0, FF_BRRES, "honeBall.brres" }, + { 1931, 191, 0x2006c, 0, FF_BRRES, "ice.brres" }, + { 1934, 260, 0x00834, 0, FF_UNKNOWN, "info.txt" }, + // 420 + { 1936, 192, 0x20068, 91, FF_BRRES, "itembox.brres" }, + { 2025, 193, 0x26068, 94, FF_BRRES, "karehayama.brres" }, + { 2028, 195, 0x2e068, 102, FF_BRRES, "kinoko.brres" }, + { 2032, 196, 0x20068, 0, FF_BRRES, "kinokoT1.brres" }, + { 2035, 195, 0x20228, 0, FF_KCL, "kinoko_d_g.kcl" }, + { 2039, 195, 0x20228, 0, FF_KCL, "kinoko_d_r.kcl" }, + { 2043, 195, 0x20228, 0, FF_KCL, "kinoko_g.kcl" }, + { 2047, 195, 0x20228, 0, FF_KCL, "kinoko_r.kcl" }, + { 2051, 198, 0x20068, 0, FF_BRRES, "koopaBall.brres" }, + { 2054, 199, 0x20068, 0, FF_BRRES, "koopaFigure.brres" }, + // 430 + { 2057, 200, 0x20068, 0, FF_BRRES, "koopaFirebar.brres" }, + { 2060, 201, 0x20068, 0, FF_BRRES, "kuribo.brres" }, + { 2072, 204, 0x20068, 0, FF_BRRES, "light_house.brres" }, + { 2075, 257, 0x00041, 0, FF_BRRES, "map_model.brres" }, + { 2172, 205, 0x20068, 0, FF_BRRES, "mare_a.brres" }, + { 2175, 206, 0x20068, 0, FF_BRRES, "mare_b.brres" }, + { 2178, 207, 0x26068, 105, FF_BRRES, "mariotreeGC.brres" }, + { 2183, 208, 0x20068, 0, FF_BRRES, "mii_balloon.brres" }, + { 2186, 209, 0x20068, 0, FF_BRRES, "miiposter.brres" }, + { 2189, 210, 0x20068, 0, FF_BRRES, "monte_a.brres" }, + // 440 + { 2196, 211, 0x20068, 0, FF_BRRES, "moray.brres" }, + { 2201, 215, 0x26068, 114, FF_BRRES, "obakeblockSFC.brres" }, + { 2204, 216, 0x20068, 0, FF_BRRES, "oilSFC.brres" }, + { 2208, 217, 0x26068, 119, FF_BRRES, "pakkun_dokan.brres" }, + { 2211, 218, 0x20068, 0, FF_BRRES, "pakkun_f.brres" }, + { 2216, 219, 0x20068, 0, FF_BRRES, "parasol.brres" }, + { 2219, 220, 0x26068, 135, FF_BRRES, "peachtreeGC.brres" }, + { 2222, 221, 0x20068, 0, FF_BRRES, "penguin_l.brres" }, + { 2225, 222, 0x20068, 0, FF_BRRES, "penguin_m.brres" }, + { 2228, 223, 0x20068, 0, FF_BRRES, "penguin_s.brres" }, + // 450 + { 2231, 225, 0x20068, 0, FF_BRRES, "pocha.brres" }, + { 2262, 227, 0x20068, 0, FF_BRRES, "pochaYogan.brres" }, + { 2271, 228, 0x20068, 0, FF_BRRES, "poihana.brres" }, + { 2275, 258, 0x00804, 0, FF_UNKNOWN, "posteffect/posteffect.bblm" }, + { 2368, 258, 0x00804, 0, FF_UNKNOWN, "posteffect/posteffect.bblm1" }, + { 2375, 258, 0x00804, 0, FF_UNKNOWN, "posteffect/posteffect.bdof" }, + { 2462, 258, 0x00804, 0, FF_UNKNOWN, "posteffect/posteffect.bfg" }, + { 2511, 258, 0x00804, 0, FF_UNKNOWN, "posteffect/posteffect.blight" }, + { 2598, 258, 0x00804, 0, FF_UNKNOWN, "posteffect/posteffect.blmap" }, + { 2661, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect.blobj" }, + // 460 + { 2664, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect.bltex" }, + { 2667, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect.bti" }, + { 2676, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect2.bblm" }, + { 2679, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect2.blight" }, + { 2682, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect2.blmap" }, + { 2685, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect2.blobj" }, + { 2688, 258, 0x20824, 0, FF_UNKNOWN, "posteffect/posteffect3.bblm" }, + { 2691, 229, 0x20068, 0, FF_BRRES, "puchi_pakkun.brres" }, + { 2694, 230, 0x20068, 0, FF_BRRES, "pukupuku.brres" }, + { 2699, 231, 0x20068, 0, FF_BRRES, "pylon01.brres" }, + // 470 + { 2703, 232, 0x20068, 0, FF_BRRES, "quicksand.brres" }, + { 2706, 233, 0x20068, 0, FF_BRRES, "r_parasol.brres" }, + { 2709, 234, 0x20068, 0, FF_BRRES, "ridgemii00.brres" }, + { 2712, 260, 0x20220, 0, FF_KCL, "ring_mission_b.kcl" }, + { 2715, 235, 0x20068, 0, FF_BRRES, "sanbo.brres" }, + { 2721, 236, 0x20068, 0, FF_BRRES, "seagull.brres" }, + { 2732, 237, 0x20068, 0, FF_BRRES, "skyship.brres" }, + { 2737, 238, 0x20068, 0, FF_BRRES, "spot.brres" }, + { 2740, 239, 0x50068, 0, FF_BRRES, "starGate.brres" }, + { 2742, 239, 0x50228, 0, FF_KCL, "starGate.kcl" }, + // 480 + { 2744, 240, 0x20068, 0, FF_BRRES, "sun.brres" }, + { 2789, 241, 0x20068, 0, FF_BRRES, "sunDS.brres" }, + { 2792, 243, 0x20068, 0, FF_BRRES, "tree_cannon.brres" }, + { 2795, 244, 0x20068, 0, FF_BRRES, "truckChimSmk.brres" }, + { 2800, 245, 0x20068, 0, FF_BRRES, "truckChimSmkW.brres" }, + { 2803, 249, 0x20068, 0, FF_BRRES, "venice_hasi.brres" }, + { 2806, 249, 0x20228, 0, FF_KCL, "venice_hasi.kcl" }, + { 2809, 260, 0x20220, 0, FF_KCL, "venice_hasi_b.kcl" }, + { 2812, 260, 0x20220, 0, FF_KCL, "venice_hasi_wall.kcl" }, + { 2815, 250, 0x20068, 0, FF_BRRES, "venice_nami.brres" }, + // 490 + { 2818, 251, 0x20068, 0, FF_BRRES, "venice_saku.brres" }, + { 2821, 251, 0x20228, 0, FF_KCL, "venice_saku.kcl" }, + { 2824, 257, 0x00041, 0, FF_BRRES, "vrcorn_model.brres" }, + { 2924, 254, 0x20068, 0, FF_BRRES, "wanwan.brres" }, + { 2932, 255, 0x20068, 0, FF_BRRES, "windmill.brres" }, + { 2935, 256, 0x26068, 169, FF_BRRES, "woodbox.brres" }, + // 496 = END + {-1,-1,0,0,0} +}; + +// +//-------------------------------------------------- + +const s16 DbFileRefFILE[N_DB_FILE_REF_FILE+1] = +{ + + // 0, AIParam/AIParam.baa, N=1+1 + 2441, -1, + + // 2, AIParam/AIParam.bas, N=1+1 + 2442, -1, + + // 4, BGteresaSFC.brres, N=2+1 + 1146, 1160, -1, + + // 7, BeltCrossing.brres, N=2+1 + 152, 193, -1, + + // 10, BeltCurveA.brres, N=2+1 + 153, 194, -1, + + // 13, BeltEasy.brres, N=2+1 + 154, 195, -1, + + // 16, Block.brres, N=2+1 + 155, 196, -1, + + // 19, CarA1.brres, N=2+1 + 286, 312, -1, + + // 22, CarA2.brres, N=2+1 + 287, 313, -1, + + // 25, CarA3.brres, N=2+1 + 288, 314, -1, + + // 28, CarB.brres, N=2+1 + 289, 315, -1, + + // 31, Crane.brres, N=2+1 + 163, 204, -1, + + // 34, Crane.kcl, N=2+1 + 164, 205, -1, + + // 37, Crescent64.brres, N=2+1 + 2066, 2081, -1, + + // 40, DKrockGC.brres, N=3+1 + 1668, 1693, 2436, -1, + + // 44, DKship64.brres, N=2+1 + 1503, 1528, -1, + + // 47, DKtreeA64.brres, N=2+1 + 1504, 1529, -1, + + // 50, DKtreeB64.brres, N=2+1 + 1505, 1530, -1, + + // 53, DKturibashiGC.brres, N=2+1 + 1669, 1694, -1, + + // 56, DemoCol.brres, N=2+1 + 2206, 2219, -1, + + // 59, DemoCol.kcl, N=2+1 + 2207, 2220, -1, + + // 62, DemoJugemu.brres, N=2+1 + 2151, 2179, -1, + + // 65, DonkyCannonGC.brres, N=2+1 + 1670, 1695, -1, + + // 68, DonkyCannon_wii.brres, N=2+1 + 346, 376, -1, + + // 71, EarthRing.brres, N=2+1 + 1012, 1033, -1, + + // 74, Epropeller.brres, N=2+1 + 512, 549, -1, + + // 77, FireSnake.brres, N=4+1 + 655, 732, 1411, 1436, -1, + + // 82, FlagA1.brres, N=2+1 + 402, 424, -1, + + // 85, FlagA2.brres, N=2+1 + 59, 85, -1, + + // 88, FlagA3.brres, N=2+1 + 1319, 1347, -1, + + // 91, FlagA5.brres, N=2+1 + 513, 550, -1, + + // 94, FlagB1.brres, N=2+1 + 403, 425, -1, + + // 97, FlagB2.brres, N=4+1 + 7, 33, 2232, 2253, -1, + + // 102, FlagB3.brres, N=2+1 + 514, 551, -1, + + // 105, FlagB4.brres, N=2+1 + 515, 552, -1, + + // 108, FlamePole.brres, N=2+1 + 937, 975, -1, + + // 111, FlamePole.kcl, N=2+1 + 938, 976, -1, + + // 114, FlamePole_v.brres, N=6+1 + 656, 733, 939, 977, 1718, 1743, -1, + + // 121, HeyhoBallGBA.brres, N=2+1 + 1270, 1295, -1, + + // 124, HeyhoShipGBA.brres, N=2+1 + 1271, 1296, -1, + + // 127, HeyhoTreeGBA.brres, N=2+1 + 1272, 1297, -1, + + // 130, InsekiA.brres, N=2+1 + 1013, 1034, -1, + + // 133, InsekiB.brres, N=2+1 + 1014, 1035, -1, + + // 136, K_bomb_car.brres, N=3+1 + 887, 912, 2430, -1, + + // 140, K_bomb_car1.brres, N=1+1 + 2431, -1, + + // 142, K_car_body.brres, N=2+1 + 888, 913, -1, + + // 145, K_chairlift00.brres, N=2+1 + 350, 380, -1, + + // 148, K_sticklift00.brres, N=2+1 + 351, 381, -1, + + // 151, K_truck.brres, N=2+1 + 889, 914, -1, + + // 154, KmoonZ.brres, N=2+1 + 1016, 1037, -1, + + // 157, KoopaFigure64.brres, N=2+1 + 1720, 1745, -1, + + // 160, Ksticketc.brres, N=2+1 + 886, 911, -1, + + // 163, M_obj_jump.brres, N=2+1 + 2381, 2390, -1, + + // 166, M_obj_jump.kcl, N=2+1 + 2388, 2396, -1, + + // 169, M_obj_jump2.brres, N=1+1 + 2387, -1, + + // 171, M_obj_jump2.kcl, N=1+1 + 2383, -1, + + // 173, M_obj_s_jump.brres, N=2+1 + 2384, 2392, -1, + + // 176, M_obj_s_jump.kcl, N=2+1 + 2386, 2394, -1, + + // 179, M_obj_s_jump2.brres, N=1+1 + 2382, -1, + + // 181, M_obj_s_jump2.kcl, N=1+1 + 2385, -1, + + // 183, MarioGo64.brres, N=2+1 + 1179, 1195, -1, + + // 186, MashBalloonGC.brres, N=4+1 + 1557, 1580, 2274, 2287, -1, + + // 191, Mdush.brres, N=2+1 + 246, 272, -1, + + // 194, Mdush.kcl, N=2+1 + 247, 273, -1, + + // 197, MiiKanban.brres, N=4+1 + 10, 36, 2235, 2256, -1, + + // 202, MiiObj01.brres, N=4+1 + 11, 37, 2236, 2257, -1, + + // 207, MiiObj02.brres, N=4+1 + 12, 38, 2237, 2258, -1, + + // 212, MiiObj03.brres, N=4+1 + 13, 39, 2238, 2259, -1, + + // 217, MiiObjD01.brres, N=2+1 + 456, 489, -1, + + // 220, MiiObjD02.brres, N=2+1 + 457, 490, -1, + + // 223, MiiObjD03.brres, N=2+1 + 458, 491, -1, + + // 226, MiiSighKino.brres, N=2+1 + 168, 209, -1, + + // 229, MiiSignNoko.brres, N=2+1 + 519, 556, -1, + + // 232, MiiSignWario.brres, N=2+1 + 406, 428, -1, + + // 235, MiiSphinxY2.brres, N=2+1 + 820, 858, -1, + + // 238, MiiStatueBD1.brres, N=2+1 + 459, 492, -1, + + // 241, MiiStatueBL1.brres, N=2+1 + 460, 493, -1, + + // 244, MiiStatueD1.brres, N=2+1 + 461, 494, -1, + + // 247, MiiStatueD2.brres, N=2+1 + 1792, 1832, -1, + + // 250, MiiStatueL1.brres, N=2+1 + 462, 495, -1, + + // 253, MiiStatueL2.brres, N=2+1 + 1793, 1833, -1, + + // 256, MiiStatueL3.brres, N=4+1 + 14, 40, 2239, 2260, -1, + + // 261, MiiStatueM1.brres, N=2+1 + 1794, 1834, -1, + + // 264, MiiStatueM2.brres, N=2+1 + 353, 383, -1, + + // 267, MiiStatueP1.brres, N=2+1 + 1795, 1835, -1, + + // 270, PalmTree.brres, N=2+1 + 301, 327, -1, + + // 273, PeachHunsuiGC.brres, N=2+1 + 1060, 1092, -1, + + // 276, Piston.brres, N=2+1 + 169, 210, -1, + + // 279, Press.brres, N=2+1 + 171, 212, -1, + + // 282, Psea.brres, N=2+1 + 1064, 1096, -1, + + // 285, RM_ring1.brres, N=2+1 + 2301, 2324, -1, + + // 288, RM_ring1.kcl, N=2+1 + 2302, 2325, -1, + + // 291, RM_ring1b.kcl, N=2+1 + 2303, 2326, -1, + + // 294, RM_ring2.kcl, N=2+1 + 2304, 2327, -1, + + // 297, RM_ring2b.kcl, N=2+1 + 2305, 2328, -1, + + // 300, RM_ring2c.kcl, N=2+1 + 2306, 2329, -1, + + // 303, RM_ring3.kcl, N=2+1 + 2307, 2330, -1, + + // 306, RM_ring3b.kcl, N=2+1 + 2308, 2331, -1, + + // 309, RM_ring3c.kcl, N=2+1 + 2309, 2332, -1, + + // 312, RhMiiKanban.brres, N=2+1 + 892, 917, -1, + + // 315, SentakuDS.brres, N=2+1 + 1326, 1354, -1, + + // 318, ShMiiObj01.brres, N=2+1 + 303, 329, -1, + + // 321, ShMiiObj02.brres, N=2+1 + 304, 330, -1, + + // 324, ShMiiObj03.brres, N=2+1 + 305, 331, -1, + + // 327, SpaceSun.brres, N=2+1 + 1018, 1039, -1, + + // 330, Spot64.brres, N=2+1 + 2069, 2084, -1, + + // 333, StarRing.brres, N=2+1 + 1019, 1040, -1, + + // 336, TownBridgeDS.brres, N=2+1 + 1328, 1356, -1, + + // 339, TownBridgeDS.kcl, N=2+1 + 1329, 1357, -1, + + // 342, TownBridgeDS2.kcl, N=2+1 + 1330, 1358, -1, + + // 345, TownBridgeDS3.kcl, N=2+1 + 1331, 1359, -1, + + // 348, TownTreeDS.brres, N=2+1 + 1332, 1360, -1, + + // 351, TruckWagon.brres, N=2+1 + 409, 431, -1, + + // 354, Twanwan.brres, N=3+1 + 1951, 1970, 2409, -1, + + // 358, TwistedWay.brres, N=2+1 + 946, 984, -1, + + // 361, UtsuboDokan.brres, N=2+1 + 525, 562, -1, + + // 364, VolcanoBall1.brres, N=2+1 + 660, 737, -1, + + // 367, VolcanoPiece0.kcl, N=2+1 + 661, 738, -1, + + // 370, VolcanoPiece0b.kcl, N=2+1 + 662, 739, -1, + + // 373, VolcanoPiece0c.kcl, N=2+1 + 663, 740, -1, + + // 376, VolcanoPiece1.brres, N=2+1 + 664, 741, -1, + + // 379, VolcanoPiece1.kcl, N=2+1 + 665, 742, -1, + + // 382, VolcanoPiece11.kcl, N=2+1 + 666, 743, -1, + + // 385, VolcanoPiece11b.kcl, N=2+1 + 667, 744, -1, + + // 388, VolcanoPiece11c.kcl, N=2+1 + 668, 745, -1, + + // 391, VolcanoPiece13.kcl, N=2+1 + 669, 746, -1, + + // 394, VolcanoPiece13b.kcl, N=2+1 + 670, 747, -1, + + // 397, VolcanoPiece13c.kcl, N=2+1 + 671, 748, -1, + + // 400, VolcanoPiece14.kcl, N=2+1 + 672, 749, -1, + + // 403, VolcanoPiece14b.kcl, N=2+1 + 673, 750, -1, + + // 406, VolcanoPiece14c.kcl, N=2+1 + 674, 751, -1, + + // 409, VolcanoPiece16.kcl, N=2+1 + 675, 752, -1, + + // 412, VolcanoPiece16b.kcl, N=2+1 + 676, 753, -1, + + // 415, VolcanoPiece16c.kcl, N=2+1 + 677, 754, -1, + + // 418, VolcanoPiece18.kcl, N=2+1 + 678, 755, -1, + + // 421, VolcanoPiece18b.kcl, N=2+1 + 679, 756, -1, + + // 424, VolcanoPiece18c.kcl, N=2+1 + 680, 757, -1, + + // 427, VolcanoPiece1b.kcl, N=2+1 + 681, 758, -1, + + // 430, VolcanoPiece1c.kcl, N=2+1 + 682, 759, -1, + + // 433, VolcanoPiece2.kcl, N=2+1 + 683, 760, -1, + + // 436, VolcanoPiece2b.kcl, N=2+1 + 684, 761, -1, + + // 439, VolcanoPiece2c.kcl, N=2+1 + 685, 762, -1, + + // 442, VolcanoPiece3.kcl, N=2+1 + 686, 763, -1, + + // 445, VolcanoPiece3b.kcl, N=2+1 + 687, 764, -1, + + // 448, VolcanoPiece3c.kcl, N=2+1 + 688, 765, -1, + + // 451, VolcanoPiece4.kcl, N=2+1 + 689, 766, -1, + + // 454, VolcanoPiece4b.kcl, N=2+1 + 690, 767, -1, + + // 457, VolcanoPiece4c.kcl, N=2+1 + 691, 768, -1, + + // 460, VolcanoPiece5.kcl, N=2+1 + 692, 769, -1, + + // 463, VolcanoPiece5b.kcl, N=2+1 + 693, 770, -1, + + // 466, VolcanoPiece5c.kcl, N=2+1 + 694, 771, -1, + + // 469, VolcanoPiece7.kcl, N=2+1 + 695, 772, -1, + + // 472, VolcanoPiece7b.kcl, N=2+1 + 696, 773, -1, + + // 475, VolcanoPiece7c.kcl, N=2+1 + 697, 774, -1, + + // 478, VolcanoPiece8.kcl, N=2+1 + 698, 775, -1, + + // 481, VolcanoPiece8b.kcl, N=2+1 + 699, 776, -1, + + // 484, VolcanoPiece8c.kcl, N=2+1 + 700, 777, -1, + + // 487, VolcanoPiece9.kcl, N=2+1 + 701, 778, -1, + + // 490, VolcanoPiece9b.kcl, N=2+1 + 702, 779, -1, + + // 493, VolcanoPiece9c.kcl, N=2+1 + 703, 780, -1, + + // 496, VolcanoRock1.brres, N=2+1 + 704, 781, -1, + + // 499, VolcanoRock1.kcl, N=2+1 + 705, 782, -1, + + // 502, VolcanoRock2.kcl, N=2+1 + 706, 783, -1, + + // 505, WLarrowGC.brres, N=2+1 + 1378, 1396, -1, + + // 508, WLdokanGC.brres, N=2+1 + 1379, 1397, -1, + + // 511, WLdokanGC.kcl, N=2+1 + 1380, 1398, -1, + + // 514, WLfirebarGC.brres, N=4+1 + 948, 986, 1381, 1399, -1, + + // 519, WLscreenGC.brres, N=2+1 + 1382, 1400, -1, + + // 522, WLwallGC.brres, N=2+1 + 1383, 1401, -1, + + // 525, WLwallGC.kcl, N=2+1 + 1384, 1402, -1, + + // 528, ami.brres, N=2+1 + 582, 617, -1, + + // 531, aurora.brres, N=2+1 + 1008, 1029, -1, + + // 534, b_teresa.brres, N=2+1 + 1147, 1161, -1, + + // 537, basabasa.brres, N=5+1 + 398, 420, 806, 844, 2437, -1, + + // 543, bblock1.brres, N=2+1 + 1764, 1804, -1, + + // 546, bblock1.kcl, N=2+1 + 1765, 1805, -1, + + // 549, bblock1b.kcl, N=2+1 + 1766, 1806, -1, + + // 552, bblock2.kcl, N=2+1 + 1767, 1807, -1, + + // 555, bblock2b.kcl, N=2+1 + 1768, 1808, -1, + + // 558, bblock3.kcl, N=2+1 + 1769, 1809, -1, + + // 561, bblock3b.kcl, N=2+1 + 1770, 1810, -1, + + // 564, bblock4.kcl, N=2+1 + 1771, 1811, -1, + + // 567, bblock4b.kcl, N=2+1 + 1772, 1812, -1, + + // 570, bblock5.kcl, N=2+1 + 1773, 1813, -1, + + // 573, bblock5b.kcl, N=2+1 + 1774, 1814, -1, + + // 576, bblock6.kcl, N=2+1 + 1775, 1815, -1, + + // 579, bblock6b.kcl, N=2+1 + 1776, 1816, -1, + + // 582, bblock7.kcl, N=2+1 + 1777, 1817, -1, + + // 585, bblock7b.kcl, N=2+1 + 1778, 1818, -1, + + // 588, bblock8.kcl, N=2+1 + 1779, 1819, -1, + + // 591, bblock8b.kcl, N=2+1 + 1780, 1820, -1, + + // 594, bblock9.kcl, N=2+1 + 1781, 1821, -1, + + // 597, bblock9b.kcl, N=2+1 + 1782, 1822, -1, + + // 600, begoman_spike.brres, N=2+1 + 2294, 2317, -1, + + // 603, bird.brres, N=2+1 + 1664, 1689, -1, + + // 606, boble.brres, N=2+1 + 1458, 1479, -1, + + // 609, brasd/DonkyCannonGC/DonkyCannonGC.brasd, N=2+1 + 1678, 1703, -1, + + // 612, brasd/DonkyCannon_wii/DonkyCannon_wii.brasd, N=2+1 + 356, 386, -1, + + // 615, brasd/cow/eat.brasd, N=2+1 + 65, 91, -1, + + // 618, brasd/cow/eat_ed.brasd, N=2+1 + 66, 92, -1, + + // 621, brasd/cow/run.brasd, N=2+1 + 67, 93, -1, + + // 624, brasd/cow/surprise.brasd, N=2+1 + 68, 94, -1, + + // 627, brasd/cow/walk.brasd, N=2+1 + 69, 95, -1, + + // 630, brasd/crab/wait.brasd, N=2+1 + 1279, 1304, -1, + + // 633, brasd/crab/walk_l.brasd, N=2+1 + 1280, 1305, -1, + + // 636, brasd/crab/walk_r.brasd, N=2+1 + 1281, 1306, -1, + + // 639, brasd/hanachan/head_wait2.brasd, N=3+1 + 594, 629, 2414, -1, + + // 643, brasd/hanachan/walk.brasd, N=3+1 + 595, 630, 2415, -1, + + // 647, brasd/koopaFigure/vomit.brasd, N=2+1 + 949, 987, -1, + + // 650, brasd/koopaFigure/wait.brasd, N=2+1 + 950, 988, -1, + + // 653, brasd/kuribo/walk_l.brasd, N=11+1 + 118, 142, 251, 277, 527, 564, 1563, 1586, 2277, 2290, + 2405, -1, + + // 665, brasd/kuribo/walk_r.brasd, N=11+1 + 119, 143, 252, 278, 528, 565, 1564, 1587, 2278, 2291, + 2406, -1, + + // 677, brasd/pakkun_f/attack.brasd, N=4+1 + 1565, 1588, 2279, 2292, -1, + + // 682, brasd/pakkun_f/wait.brasd, N=4+1 + 1566, 1589, 2280, 2293, -1, + + // 687, brasd/penguin_m/walk.brasd, N=2+1 + 1218, 1248, -1, + + // 690, brasd/penguin_s/dive.brasd, N=2+1 + 1219, 1249, -1, + + // 693, brasd/penguin_s/stand_up.brasd, N=2+1 + 1220, 1250, -1, + + // 696, brasd/penguin_s/walk.brasd, N=2+1 + 1221, 1251, -1, + + // 699, brasd/poihana/throw.brasd, N=3+1 + 1068, 1100, 2439, -1, + + // 703, brasd/poihana/walk.brasd, N=3+1 + 1069, 1101, 2440, -1, + + // 707, brasd/puchi_pakkun/puchi_pakkun.brasd, N=2+1 + 1183, 1199, -1, + + // 710, brasd/sanbo/revive.brasd, N=5+1 + 825, 863, 1418, 1443, 2422, -1, + + // 716, brasd/tree_cannon/tree_cannon.brasd, N=2+1 + 596, 631, -1, + + // 719, bulldozer_left.brres, N=2+1 + 156, 197, -1, + + // 722, bulldozer_left.kcl, N=2+1 + 157, 198, -1, + + // 725, bulldozer_right.brres, N=2+1 + 158, 199, -1, + + // 728, bulldozer_right.kcl, N=2+1 + 159, 200, -1, + + // 731, casino_roulette.brres, N=2+1 + 1940, 1959, -1, + + // 734, casino_roulette.kcl, N=2+1 + 1941, 1960, -1, + + // 737, castleballoon1.brres, N=4+1 + 234, 260, 2144, 2172, -1, + + // 742, castleflower1.brres, N=8+1 + 235, 261, 2145, 2173, 2200, 2213, 2340, 2351, -1, + + // 751, castletree1.brres, N=10+1 + 52, 78, 236, 262, 2146, 2174, 2201, 2214, 2341, 2352, + -1, + + // 762, castletree2.brres, N=10+1 + 53, 79, 237, 263, 2147, 2175, 2202, 2215, 2342, 2353, + -1, + + // 773, choropu.brres, N=4+1 + 54, 80, 1620, 1642, -1, + + // 778, coin.brres, N=21+1 + 1783, 1823, 1844, 1875, 1906, 1923, 1942, 1961, 1978, 1998, + 2018, 2029, 2040, 2051, 2062, 2077, 2092, 2103, 2114, 2129, + 2380, -1, + + // 800, course.0, N=2+1 + 932, 970, -1, + + // 803, course.bmm, N=12+1 + 0, 26, 338, 368, 442, 475, 807, 845, 1784, 1824, + 2226, 2247, -1, + + // 816, course.btiEnv, N=12+1 + 1, 27, 339, 369, 443, 476, 808, 846, 1785, 1825, + 2227, 2248, -1, + + // 829, course.btiMat, N=12+1 + 2, 28, 340, 370, 444, 477, 809, 847, 1786, 1826, + 2228, 2249, -1, + + // 842, course.kcl, N=99+1 + 3, 29, 55, 81, 104, 128, 160, 201, 238, 264, + 290, 316, 341, 371, 399, 421, 445, 478, 508, 545, + 583, 618, 652, 729, 810, 848, 882, 907, 933, 971, + 1009, 1030, 1050, 1082, 1114, 1130, 1148, 1162, 1174, 1190, + 1206, 1236, 1266, 1291, 1316, 1344, 1372, 1390, 1408, 1433, + 1459, 1480, 1500, 1525, 1550, 1573, 1596, 1608, 1621, 1643, + 1665, 1690, 1714, 1739, 1787, 1827, 1845, 1876, 1907, 1924, + 1943, 1962, 1979, 1999, 2019, 2030, 2041, 2052, 2063, 2078, + 2093, 2104, 2115, 2130, 2148, 2176, 2203, 2216, 2229, 2250, + 2268, 2281, 2295, 2318, 2343, 2354, 2362, 2366, 2370, -1, + + // 942, course.kmp, N=99+1 + 4, 30, 56, 82, 105, 129, 161, 202, 239, 265, + 291, 317, 342, 372, 400, 422, 446, 479, 509, 546, + 584, 619, 653, 730, 811, 849, 883, 908, 934, 972, + 1010, 1031, 1051, 1083, 1115, 1131, 1149, 1163, 1175, 1191, + 1207, 1237, 1267, 1292, 1317, 1345, 1373, 1391, 1409, 1434, + 1460, 1481, 1501, 1526, 1551, 1574, 1597, 1609, 1622, 1644, + 1666, 1691, 1715, 1740, 1788, 1828, 1846, 1877, 1908, 1925, + 1944, 1963, 1980, 2000, 2020, 2031, 2042, 2053, 2064, 2079, + 2094, 2105, 2116, 2131, 2149, 2177, 2204, 2217, 2230, 2251, + 2269, 2282, 2296, 2319, 2344, 2355, 2363, 2367, 2371, -1, + + // 1042, course.lex, N=1+1 + 2444, -1, + + // 1044, course_d_model.brres, N=20+1 + 31, 83, 130, 203, 266, 318, 423, 480, 547, 731, + 850, 909, 973, 1032, 1878, 1964, 2001, 2178, 2218, 2356, + -1, + + // 1065, course_model.brres, N=79+1 + 5, 57, 106, 162, 240, 292, 343, 373, 401, 447, + 510, 585, 620, 654, 812, 884, 935, 1011, 1052, 1084, + 1116, 1132, 1150, 1164, 1176, 1192, 1208, 1238, 1268, 1293, + 1318, 1346, 1374, 1392, 1410, 1435, 1461, 1482, 1502, 1527, + 1552, 1575, 1598, 1610, 1623, 1645, 1667, 1692, 1716, 1741, + 1789, 1829, 1847, 1909, 1926, 1945, 1981, 2021, 2032, 2043, + 2054, 2065, 2080, 2095, 2106, 2117, 2132, 2150, 2205, 2231, + 2252, 2270, 2283, 2297, 2320, 2345, 2364, 2368, 2372, -1, + + // 1145, cow.brres, N=2+1 + 58, 84, -1, + + // 1148, crab.brres, N=2+1 + 1269, 1294, -1, + + // 1151, cruiser.brres, N=4+1 + 448, 481, 511, 548, -1, + + // 1156, dc_pillar.brres, N=2+1 + 813, 851, -1, + + // 1159, dc_pillar.kcl, N=2+1 + 814, 852, -1, + + // 1162, dc_pillar_base.kcl, N=2+1 + 815, 853, -1, + + // 1165, dc_sandcone.brres, N=2+1 + 816, 854, -1, + + // 1168, dc_sandcone.kcl, N=2+1 + 817, 855, -1, + + // 1171, dk_miiobj00.brres, N=2+1 + 345, 375, -1, + + // 1174, dkmonitor.brres, N=2+1 + 344, 374, -1, + + // 1177, dokan_sfc.brres, N=5+1 + 6, 32, 1599, 1611, 2403, -1, + + // 1183, donkytree1GC.brres, N=2+1 + 1671, 1696, -1, + + // 1186, donkytree2GC.brres, N=2+1 + 1672, 1697, -1, + + // 1189, dossun.brres, N=9+1 + 936, 974, 1462, 1483, 1717, 1742, 1982, 2002, 2425, -1, + + // 1199, effect/Alarm/rk_alarm.breff, N=2+1 + 176, 217, -1, + + // 1202, effect/Alarm/rk_alarm.breft, N=2+1 + 177, 218, -1, + + // 1205, effect/Block/rk_block.breff, N=2+1 + 178, 219, -1, + + // 1208, effect/Block/rk_block.breft, N=2+1 + 179, 220, -1, + + // 1211, effect/DKfalls/rk_64DKfalls.breff, N=2+1 + 1512, 1537, -1, + + // 1214, effect/DKfalls/rk_64DKfalls.breft, N=2+1 + 1513, 1538, -1, + + // 1217, effect/DKrockGC/rk_DKrockGC.breff, N=3+1 + 1679, 1704, 2432, -1, + + // 1221, effect/DKrockGC/rk_DKrockGC.breft, N=3+1 + 1680, 1705, 2433, -1, + + // 1225, effect/DKship64/rk_DKship64.breff, N=2+1 + 1514, 1539, -1, + + // 1228, effect/DKship64/rk_DKship64.breft, N=2+1 + 1515, 1540, -1, + + // 1231, effect/DemoEf/rk_demo.breff, N=2+1 + 2160, 2188, -1, + + // 1234, effect/DemoEf/rk_demo.breft, N=2+1 + 2161, 2189, -1, + + // 1237, effect/DemoJugemu/rk_DemoJugemu.breff, N=2+1 + 2162, 2190, -1, + + // 1240, effect/DemoJugemu/rk_DemoJugemu.breft, N=2+1 + 2163, 2191, -1, + + // 1243, effect/DonkyCannonGC/rk_dkCannon.breff, N=2+1 + 1681, 1706, -1, + + // 1246, effect/DonkyCannonGC/rk_dkCannon.breft, N=2+1 + 1682, 1707, -1, + + // 1249, effect/DonkyCannon_wii/rk_dkCannon.breff, N=2+1 + 357, 387, -1, + + // 1252, effect/DonkyCannon_wii/rk_dkCannon.breft, N=2+1 + 358, 388, -1, + + // 1255, effect/EnvFire/rk_EnvFire.breff, N=8+1 + 708, 785, 953, 991, 1471, 1492, 1726, 1751, -1, + + // 1264, effect/EnvFire/rk_EnvFire.breft, N=8+1 + 709, 786, 954, 992, 1472, 1493, 1727, 1752, -1, + + // 1273, effect/EnvKareha/rk_EnvKareha.breff, N=2+1 + 597, 632, -1, + + // 1276, effect/EnvKareha/rk_EnvKareha.breft, N=2+1 + 598, 633, -1, + + // 1279, effect/EnvKarehaUp/rk_EnvKarehaUp.breff, N=2+1 + 599, 634, -1, + + // 1282, effect/EnvKarehaUp/rk_EnvKarehaUp.breft, N=2+1 + 600, 635, -1, + + // 1285, effect/EnvSnow/rk_EnvSnow.breff, N=2+1 + 359, 389, -1, + + // 1288, effect/EnvSnow/rk_EnvSnow.breft, N=2+1 + 360, 390, -1, + + // 1291, effect/Epropeller/rk_epropeller.breff, N=2+1 + 531, 568, -1, + + // 1294, effect/Epropeller/rk_epropeller.breft, N=2+1 + 532, 569, -1, + + // 1297, effect/FallBsA/rk_fallbasin.breff, N=2+1 + 533, 570, -1, + + // 1300, effect/FallBsA/rk_fallbasin.breft, N=2+1 + 534, 571, -1, + + // 1303, effect/FallBsB/rk_fallbasin.breff, N=2+1 + 535, 572, -1, + + // 1306, effect/FallBsB/rk_fallbasin.breft, N=2+1 + 536, 573, -1, + + // 1309, effect/Fall_MH/rk_ridgeFall.breff, N=2+1 + 895, 920, -1, + + // 1312, effect/Fall_MH/rk_ridgeFall.breft, N=2+1 + 896, 921, -1, + + // 1315, effect/Fall_Y/rk_yoshiFalls.breff, N=2+1 + 1122, 1138, -1, + + // 1318, effect/Fall_Y/rk_yoshiFalls.breft, N=2+1 + 1123, 1139, -1, + + // 1321, effect/FireSnake/rk_fireSnake.breff, N=4+1 + 710, 787, 1419, 1444, -1, + + // 1326, effect/FireSnake/rk_fireSnake.breft, N=4+1 + 711, 788, 1420, 1445, -1, + + // 1331, effect/FlamePole/rk_flamePole.breff, N=2+1 + 955, 993, -1, + + // 1334, effect/FlamePole/rk_flamePole.breft, N=2+1 + 956, 994, -1, + + // 1337, effect/FlamePole_v/rk_flamePole.breff, N=6+1 + 712, 789, 957, 995, 1728, 1753, -1, + + // 1344, effect/FlamePole_v/rk_flamePole.breft, N=6+1 + 713, 790, 958, 996, 1729, 1754, -1, + + // 1351, effect/FlamePole_v_big/rk_flamePoleL.breff, N=2+1 + 1730, 1755, -1, + + // 1354, effect/FlamePole_v_big/rk_flamePoleL.breft, N=2+1 + 1731, 1756, -1, + + // 1357, effect/Flash_B/rk_flashBCross.breff, N=2+1 + 361, 391, -1, + + // 1360, effect/Flash_B/rk_flashBCross.breft, N=2+1 + 362, 392, -1, + + // 1363, effect/Flash_L/rk_flashBeginner.breff, N=2+1 + 18, 44, -1, + + // 1366, effect/Flash_L/rk_flashBeginner.breft, N=2+1 + 19, 45, -1, + + // 1369, effect/Flash_M/rk_flash64mario.breff, N=2+1 + 1184, 1200, -1, + + // 1372, effect/Flash_M/rk_flash64mario.breft, N=2+1 + 1185, 1201, -1, + + // 1375, effect/Flash_S/rk_flashSkate.breff, N=2+1 + 1916, 1933, -1, + + // 1378, effect/Flash_S/rk_flashSkate.breft, N=2+1 + 1917, 1934, -1, + + // 1381, effect/Flash_W/rk_flashWaluigi.breff, N=2+1 + 1385, 1403, -1, + + // 1384, effect/Flash_W/rk_flashWaluigi.breft, N=2+1 + 1386, 1404, -1, + + // 1387, effect/Hanabi/rk_stHanabi.breff, N=16+1 + 20, 46, 253, 279, 469, 502, 830, 868, 1070, 1102, + 1222, 1252, 1421, 1446, 1604, 1616, -1, + + // 1404, effect/Hanabi/rk_stHanabi.breft, N=16+1 + 21, 47, 254, 280, 470, 503, 831, 869, 1071, 1103, + 1223, 1253, 1422, 1447, 1605, 1617, -1, + + // 1421, effect/HeyhoBallGBA/rk_HeyhoBallGBA.breff, N=2+1 + 1284, 1309, -1, + + // 1424, effect/HeyhoBallGBA/rk_HeyhoBallGBA.breft, N=2+1 + 1285, 1310, -1, + + // 1427, effect/K_bomb_car/rk_K_bomb_car.breff, N=3+1 + 897, 922, 2429, -1, + + // 1431, effect/K_bomb_car/rk_K_bomb_car.breft, N=3+1 + 898, 923, 2428, -1, + + // 1435, effect/K_bomb_car1/rk_K_bomb_car.breff, N=1+1 + 2427, -1, + + // 1437, effect/K_bomb_car1/rk_K_bomb_car.breft, N=1+1 + 2426, -1, + + // 1439, effect/Kamifubuki/rk_kamifubuki.breff, N=2+1 + 2164, 2192, -1, + + // 1442, effect/Kamifubuki/rk_kamifubuki.breft, N=2+1 + 2165, 2193, -1, + + // 1445, effect/KoopaFigure64/rk_koopaFire.breff, N=2+1 + 1732, 1757, -1, + + // 1448, effect/KoopaFigure64/rk_koopaFire.breft, N=2+1 + 1733, 1758, -1, + + // 1451, effect/PeachHunsuiGC/rk_hunsui.breff, N=2+1 + 1072, 1104, -1, + + // 1454, effect/PeachHunsuiGC/rk_hunsui.breft, N=2+1 + 1073, 1105, -1, + + // 1457, effect/Press/rk_press.breff, N=2+1 + 182, 223, -1, + + // 1460, effect/Press/rk_press.breft, N=2+1 + 183, 224, -1, + + // 1463, effect/StarRing/rk_StarRing.breff, N=2+1 + 1023, 1044, -1, + + // 1466, effect/StarRing/rk_StarRing.breft, N=2+1 + 1024, 1045, -1, + + // 1469, effect/Steam/rk_steam.breff, N=2+1 + 184, 225, -1, + + // 1472, effect/Steam/rk_steam.breft, N=2+1 + 185, 226, -1, + + // 1475, effect/TruckWagon/rk_truck.breff, N=2+1 + 413, 435, -1, + + // 1478, effect/TruckWagon/rk_truck.breft, N=2+1 + 414, 436, -1, + + // 1481, effect/VolcanoBall1/rk_volc.breff, N=2+1 + 718, 795, -1, + + // 1484, effect/VolcanoBall1/rk_volc.breft, N=2+1 + 719, 796, -1, + + // 1487, effect/begoman_spike/rk_begoman_spike.breff, N=2+1 + 2311, 2334, -1, + + // 1490, effect/begoman_spike/rk_begoman_spike.breft, N=2+1 + 2312, 2335, -1, + + // 1493, effect/boble/rk_boble.breff, N=2+1 + 1467, 1488, -1, + + // 1496, effect/boble/rk_boble.breft, N=2+1 + 1468, 1489, -1, + + // 1499, effect/choropu/rk_choropuMoko.breff, N=4+1 + 70, 96, 1631, 1653, -1, + + // 1504, effect/choropu/rk_choropuMoko.breft, N=4+1 + 71, 97, 1632, 1654, -1, + + // 1509, effect/coin/rk_coin.breff, N=21+1 + 1797, 1837, 1862, 1893, 1914, 1931, 1953, 1972, 1988, 2008, + 2025, 2036, 2047, 2058, 2071, 2086, 2099, 2110, 2121, 2136, + 2378, -1, + + // 1531, effect/coin/rk_coin.breft, N=21+1 + 1798, 1838, 1863, 1894, 1915, 1932, 1954, 1973, 1989, 2009, + 2026, 2037, 2048, 2059, 2072, 2087, 2100, 2111, 2122, 2137, + 2379, -1, + + // 1553, effect/crab/rk_crab.breff, N=2+1 + 1282, 1307, -1, + + // 1556, effect/crab/rk_crab.breft, N=2+1 + 1283, 1308, -1, + + // 1559, effect/cruiser/rk_cruiser.breff, N=4+1 + 467, 500, 529, 566, -1, + + // 1564, effect/cruiser/rk_cruiser.breft, N=4+1 + 468, 501, 530, 567, -1, + + // 1569, effect/dc_pillar/rk_dc_pillar.breff, N=2+1 + 826, 864, -1, + + // 1572, effect/dc_pillar/rk_dc_pillar.breft, N=2+1 + 827, 865, -1, + + // 1575, effect/dc_sandcone/rk_dc_sandcone.breff, N=2+1 + 828, 866, -1, + + // 1578, effect/dc_sandcone/rk_dc_sandcone.breft, N=2+1 + 829, 867, -1, + + // 1581, effect/dossun/rk_dossun.breff, N=9+1 + 951, 989, 1469, 1490, 1724, 1749, 1990, 2010, 2423, -1, + + // 1591, effect/dossun/rk_dossun.breft, N=9+1 + 952, 990, 1470, 1491, 1725, 1750, 1991, 2011, 2424, -1, + + // 1601, effect/entry/rk_entry.breff, N=4+1 + 1021, 1042, 2313, 2336, -1, + + // 1606, effect/entry/rk_entry.breft, N=4+1 + 1022, 1043, 2314, 2337, -1, + + // 1611, effect/hanachan/rk_hana.breff, N=3+1 + 601, 636, 2411, -1, + + // 1615, effect/hanachan/rk_hana.breft, N=3+1 + 602, 637, 2412, -1, + + // 1619, effect/honeBall/rk_honeBall.breff, N=1+1 + 2417, -1, + + // 1621, effect/honeBall/rk_honeBall.breft, N=1+1 + 2416, -1, + + // 1623, effect/karehayama/rk_karehayama.breff, N=2+1 + 603, 638, -1, + + // 1626, effect/karehayama/rk_karehayama.breft, N=2+1 + 604, 639, -1, + + // 1629, effect/koopaBall/rk_koopaBall.breff, N=2+1 + 959, 997, -1, + + // 1632, effect/koopaBall/rk_koopaBall.breft, N=2+1 + 960, 998, -1, + + // 1635, effect/leaf_effect/rk_leaf_effect.breff, N=2+1 + 605, 640, -1, + + // 1638, effect/leaf_effect/rk_leaf_effect.breft, N=2+1 + 606, 641, -1, + + // 1641, effect/penguin_l/rk_penguin_l.breff, N=2+1 + 1224, 1254, -1, + + // 1644, effect/penguin_l/rk_penguin_l.breft, N=2+1 + 1225, 1255, -1, + + // 1647, effect/penguin_m/rk_penguin_m.breff, N=2+1 + 1226, 1256, -1, + + // 1650, effect/penguin_m/rk_penguin_m.breft, N=2+1 + 1227, 1257, -1, + + // 1653, effect/penguin_s/rk_penguin_s.breff, N=2+1 + 1228, 1258, -1, + + // 1656, effect/penguin_s/rk_penguin_s.breft, N=2+1 + 1229, 1259, -1, + + // 1659, effect/pocha/rk_pocha.breff, N=30+1 + 180, 221, 537, 574, 607, 642, 832, 870, 899, 924, + 1074, 1106, 1124, 1140, 1230, 1260, 1286, 1311, 1335, 1363, + 1423, 1448, 1516, 1541, 1633, 1655, 1683, 1708, 1864, 1895, + -1, + + // 1690, effect/pocha/rk_pocha.breft, N=30+1 + 181, 222, 538, 575, 608, 643, 833, 871, 900, 925, + 1075, 1107, 1125, 1141, 1231, 1261, 1287, 1312, 1336, 1364, + 1424, 1449, 1517, 1542, 1634, 1656, 1684, 1709, 1865, 1896, + -1, + + // 1721, effect/pochaMori/rk_pochaLeaf.breff, N=4+1 + 901, 926, 2123, 2138, -1, + + // 1726, effect/pochaMori/rk_pochaLeaf.breft, N=4+1 + 902, 927, 2124, 2139, -1, + + // 1731, effect/pochaYogan/rk_pochaYogan.breff, N=8+1 + 714, 791, 961, 999, 1473, 1494, 1734, 1759, -1, + + // 1740, effect/pochaYogan/rk_pochaYogan.breft, N=8+1 + 715, 792, 962, 1000, 1474, 1495, 1735, 1760, -1, + + // 1749, effect/poihana/rk_poihana.breff, N=3+1 + 1076, 1108, 2434, -1, + + // 1753, effect/poihana/rk_poihana.breft, N=3+1 + 1077, 1109, 2435, -1, + + // 1757, effect/quicksand/rk_qsandDossun.breff, N=2+1 + 1992, 2012, -1, + + // 1760, effect/quicksand/rk_qsandDossun.breft, N=2+1 + 1993, 2013, -1, + + // 1763, effect/sanbo/rk_sanbo.breff, N=5+1 + 834, 872, 1425, 1450, 2419, -1, + + // 1769, effect/sanbo/rk_sanbo.breft, N=5+1 + 835, 873, 1426, 1451, 2420, -1, + + // 1775, effect/starGate/rk_starGate.breff, N=1+1 + 2375, -1, + + // 1777, effect/starGate/rk_starGate.breft, N=1+1 + 2374, -1, + + // 1779, effect/taimatsu/rk_taimatsu.breff, N=8+1 + 716, 793, 836, 874, 963, 1001, 1518, 1543, -1, + + // 1788, effect/taimatsu/rk_taimatsu.breft, N=8+1 + 717, 794, 837, 875, 964, 1002, 1519, 1544, -1, + + // 1797, effect/tree_cannon/rk_dkCannon.breff, N=2+1 + 609, 644, -1, + + // 1800, effect/tree_cannon/rk_dkCannon.breft, N=2+1 + 610, 645, -1, + + // 1803, effect/truckChimSmk/rk_truckChimSmk.breff, N=4+1 + 411, 433, 611, 646, -1, + + // 1808, effect/truckChimSmk/rk_truckChimSmk.breft, N=4+1 + 412, 434, 612, 647, -1, + + // 1813, effect/truckChimSmkW/rk_truckChimSmkW.breff, N=2+1 + 1866, 1897, -1, + + // 1816, effect/truckChimSmkW/rk_truckChimSmkW.breft, N=2+1 + 1867, 1898, -1, + + // 1819, effect/venice_hasi/rk_venice.breff, N=2+1 + 1868, 1899, -1, + + // 1822, effect/venice_hasi/rk_venice.breft, N=2+1 + 1869, 1900, -1, + + // 1825, effect/volsmk/rk_volsmk.breff, N=2+1 + 720, 797, -1, + + // 1828, effect/volsmk/rk_volsmk.breft, N=2+1 + 721, 798, -1, + + // 1831, effect/wanwan/rk_wanwan.breff, N=7+1 + 255, 281, 1567, 1590, 1635, 1657, 2408, -1, + + // 1839, effect/wanwan/rk_wanwan.breft, N=7+1 + 256, 282, 1568, 1591, 1636, 1658, 2407, -1, + + // 1847, effect/woodbox/rk_woodbox.breff, N=5+1 + 186, 227, 1337, 1365, 2398, -1, + + // 1853, effect/woodbox/rk_woodbox.breft, N=5+1 + 187, 228, 1338, 1366, 2399, -1, + + // 1859, escalator.brres, N=2+1 + 293, 319, -1, + + // 1862, escalator.kcl, N=2+1 + 294, 320, -1, + + // 1865, fks_screen_wii.brres, N=2+1 + 1910, 1927, -1, + + // 1868, gardentreeDS.brres, N=2+1 + 1624, 1646, -1, + + // 1871, group_enemy_a.brres, N=8+1 + 449, 482, 1053, 1085, 1506, 1531, 2152, 2180, -1, + + // 1880, group_enemy_a_demo.brres, N=2+1 + 2153, 2181, -1, + + // 1883, group_enemy_b.brres, N=6+1 + 450, 483, 1507, 1532, 2154, 2182, -1, + + // 1890, group_enemy_c.brres, N=6+1 + 165, 206, 451, 484, 2155, 2183, -1, + + // 1897, group_enemy_e.brres, N=2+1 + 241, 267, -1, + + // 1900, group_enemy_f.brres, N=2+1 + 242, 268, -1, + + // 1903, group_monte_a.brres, N=12+1 + 295, 321, 452, 485, 1054, 1086, 1320, 1348, 1848, 1879, + 2156, 2184, -1, + + // 1916, group_monte_a_demo.brres, N=2+1 + 2157, 2185, -1, + + // 1919, hanachan.brres, N=3+1 + 586, 621, 2413, -1, + + // 1923, heyho.brres, N=2+1 + 347, 377, -1, + + // 1926, heyho2.brres, N=2+1 + 348, 378, -1, + + // 1929, honeBall.brres, N=1+1 + 2418, -1, + + // 1931, ice.brres, N=2+1 + 1209, 1239, -1, + + // 1934, info.txt, N=1+1 + 2443, -1, + + // 1936, itembox.brres, N=88+1 + 8, 34, 60, 86, 107, 131, 166, 207, 243, 269, + 296, 322, 349, 379, 404, 426, 453, 486, 516, 553, + 587, 622, 657, 734, 818, 856, 885, 910, 940, 978, + 1015, 1036, 1055, 1087, 1117, 1133, 1151, 1165, 1177, 1193, + 1210, 1240, 1273, 1298, 1321, 1349, 1375, 1393, 1412, 1437, + 1463, 1484, 1508, 1533, 1553, 1576, 1600, 1612, 1625, 1647, + 1673, 1698, 1719, 1744, 1790, 1830, 1849, 1880, 1911, 1928, + 1946, 1965, 1983, 2003, 2022, 2033, 2044, 2055, 2067, 2082, + 2096, 2107, 2118, 2133, 2233, 2254, 2298, 2321, -1, + + // 2025, karehayama.brres, N=2+1 + 588, 623, -1, + + // 2028, kinoko.brres, N=3+1 + 108, 132, 2391, -1, + + // 2032, kinokoT1.brres, N=2+1 + 109, 133, -1, + + // 2035, kinoko_d_g.kcl, N=3+1 + 110, 134, 2393, -1, + + // 2039, kinoko_d_r.kcl, N=3+1 + 111, 135, 2397, -1, + + // 2043, kinoko_g.kcl, N=3+1 + 112, 136, 2389, -1, + + // 2047, kinoko_r.kcl, N=3+1 + 113, 137, 2395, -1, + + // 2051, koopaBall.brres, N=2+1 + 941, 979, -1, + + // 2054, koopaFigure.brres, N=2+1 + 942, 980, -1, + + // 2057, koopaFirebar.brres, N=2+1 + 943, 981, -1, + + // 2060, kuribo.brres, N=11+1 + 114, 138, 244, 270, 517, 554, 1554, 1577, 2271, 2284, + 2400, -1, + + // 2072, light_house.brres, N=2+1 + 454, 487, -1, + + // 2075, map_model.brres, N=96+1 + 9, 35, 61, 87, 115, 139, 167, 208, 245, 271, + 297, 323, 352, 382, 405, 427, 455, 488, 518, 555, + 589, 624, 658, 735, 819, 857, 890, 915, 944, 982, + 1017, 1038, 1056, 1088, 1118, 1134, 1152, 1166, 1178, 1194, + 1211, 1241, 1274, 1299, 1322, 1350, 1376, 1394, 1413, 1438, + 1464, 1485, 1509, 1534, 1555, 1578, 1601, 1613, 1626, 1648, + 1674, 1699, 1721, 1746, 1791, 1831, 1850, 1881, 1912, 1929, + 1947, 1966, 1984, 2004, 2023, 2034, 2045, 2056, 2068, 2083, + 2097, 2108, 2119, 2134, 2158, 2186, 2208, 2221, 2234, 2255, + 2272, 2285, 2299, 2322, 2346, 2357, -1, + + // 2172, mare_a.brres, N=2+1 + 1057, 1089, -1, + + // 2175, mare_b.brres, N=2+1 + 1058, 1090, -1, + + // 2178, mariotreeGC.brres, N=4+1 + 1556, 1579, 2273, 2286, -1, + + // 2183, mii_balloon.brres, N=2+1 + 299, 325, -1, + + // 2186, miiposter.brres, N=2+1 + 298, 324, -1, + + // 2189, monte_a.brres, N=6+1 + 300, 326, 1059, 1091, 1323, 1351, -1, + + // 2196, moray.brres, N=4+1 + 520, 557, 1948, 1967, -1, + + // 2201, obakeblockSFC.brres, N=2+1 + 1153, 1167, -1, + + // 2204, oilSFC.brres, N=3+1 + 1602, 1614, 2404, -1, + + // 2208, pakkun_dokan.brres, N=2+1 + 1558, 1581, -1, + + // 2211, pakkun_f.brres, N=4+1 + 1559, 1582, 2275, 2288, -1, + + // 2216, parasol.brres, N=2+1 + 302, 328, -1, + + // 2219, peachtreeGC.brres, N=2+1 + 1061, 1093, -1, + + // 2222, penguin_l.brres, N=2+1 + 1212, 1242, -1, + + // 2225, penguin_m.brres, N=2+1 + 1213, 1243, -1, + + // 2228, penguin_s.brres, N=2+1 + 1214, 1244, -1, + + // 2231, pocha.brres, N=30+1 + 170, 211, 521, 558, 590, 625, 821, 859, 891, 916, + 1062, 1094, 1119, 1135, 1215, 1245, 1275, 1300, 1324, 1352, + 1414, 1439, 1510, 1535, 1627, 1649, 1675, 1700, 1851, 1882, + -1, + + // 2262, pochaYogan.brres, N=8+1 + 659, 736, 945, 983, 1465, 1486, 1722, 1747, -1, + + // 2271, poihana.brres, N=3+1 + 1063, 1095, 2438, -1, + + // 2275, posteffect/posteffect.bblm, N=92+1 + 22, 48, 72, 98, 120, 144, 188, 229, 257, 283, + 308, 334, 363, 393, 415, 437, 471, 504, 539, 576, + 613, 648, 722, 799, 838, 876, 903, 928, 965, 1003, + 1025, 1046, 1078, 1110, 1126, 1142, 1155, 1169, 1186, 1202, + 1232, 1262, 1288, 1313, 1339, 1367, 1387, 1405, 1427, 1452, + 1475, 1496, 1520, 1545, 1569, 1592, 1606, 1618, 1637, 1659, + 1685, 1710, 1736, 1761, 1799, 1839, 1870, 1901, 1918, 1935, + 1955, 1974, 1994, 2014, 2027, 2038, 2049, 2060, 2073, 2088, + 2101, 2112, 2125, 2140, 2166, 2194, 2210, 2223, 2242, 2263, + 2348, 2359, -1, + + // 2368, posteffect/posteffect.bblm1, N=6+1 + 121, 145, 540, 577, 723, 800, -1, + + // 2375, posteffect/posteffect.bdof, N=86+1 + 23, 49, 73, 99, 122, 146, 189, 230, 258, 284, + 309, 335, 364, 394, 416, 438, 472, 505, 541, 578, + 614, 649, 724, 801, 839, 877, 904, 929, 966, 1004, + 1026, 1047, 1079, 1111, 1127, 1143, 1156, 1170, 1187, 1203, + 1233, 1263, 1289, 1314, 1340, 1368, 1388, 1406, 1428, 1453, + 1476, 1497, 1521, 1546, 1570, 1593, 1607, 1619, 1638, 1660, + 1686, 1711, 1737, 1762, 1800, 1840, 1871, 1902, 1919, 1936, + 1956, 1975, 1995, 2015, 2028, 2039, 2050, 2061, 2074, 2089, + 2102, 2113, 2126, 2141, 2243, 2264, -1, + + // 2462, posteffect/posteffect.bfg, N=48+1 + 74, 100, 123, 147, 190, 231, 365, 395, 417, 439, + 542, 579, 725, 802, 840, 878, 967, 1005, 1080, 1112, + 1128, 1144, 1157, 1171, 1188, 1204, 1234, 1264, 1341, 1369, + 1429, 1454, 1477, 1498, 1522, 1547, 1571, 1594, 1639, 1661, + 1687, 1712, 1801, 1841, 1872, 1903, 1920, 1937, -1, + + // 2511, posteffect/posteffect.blight, N=86+1 + 24, 50, 75, 101, 124, 148, 191, 232, 259, 285, + 310, 336, 366, 396, 418, 440, 473, 506, 543, 580, + 615, 650, 726, 803, 841, 879, 905, 930, 968, 1006, + 1027, 1048, 1081, 1113, 1129, 1145, 1158, 1172, 1189, 1205, + 1235, 1265, 1290, 1315, 1342, 1370, 1389, 1407, 1430, 1455, + 1478, 1499, 1523, 1548, 1572, 1595, 1640, 1662, 1688, 1713, + 1738, 1763, 1802, 1842, 1873, 1904, 1921, 1938, 1957, 1976, + 1996, 2016, 2075, 2090, 2127, 2142, 2167, 2195, 2211, 2224, + 2244, 2265, 2315, 2338, 2349, 2360, -1, + + // 2598, posteffect/posteffect.blmap, N=62+1 + 25, 51, 125, 149, 192, 233, 311, 337, 367, 397, + 419, 441, 474, 507, 544, 581, 616, 651, 727, 804, + 842, 880, 906, 931, 969, 1007, 1028, 1049, 1159, 1173, + 1343, 1371, 1431, 1456, 1524, 1549, 1641, 1663, 1803, 1843, + 1874, 1905, 1922, 1939, 1958, 1977, 1997, 2017, 2076, 2091, + 2128, 2143, 2168, 2196, 2212, 2225, 2245, 2266, 2316, 2339, + 2350, 2361, -1, + + // 2661, posteffect/posteffect.blobj, N=2+1 + 2169, 2197, -1, + + // 2664, posteffect/posteffect.bltex, N=2+1 + 2170, 2198, -1, + + // 2667, posteffect/posteffect.bti, N=8+1 + 728, 805, 843, 881, 1432, 1457, 2246, 2267, -1, + + // 2676, posteffect/posteffect2.bblm, N=2+1 + 76, 102, -1, + + // 2679, posteffect/posteffect2.blight, N=2+1 + 126, 150, -1, + + // 2682, posteffect/posteffect2.blmap, N=2+1 + 127, 151, -1, + + // 2685, posteffect/posteffect2.blobj, N=2+1 + 2171, 2199, -1, + + // 2688, posteffect/posteffect3.bblm, N=2+1 + 77, 103, -1, + + // 2691, puchi_pakkun.brres, N=2+1 + 1180, 1196, -1, + + // 2694, pukupuku.brres, N=4+1 + 522, 559, 1949, 1968, -1, + + // 2699, pylon01.brres, N=3+1 + 463, 496, 2402, -1, + + // 2703, quicksand.brres, N=2+1 + 1985, 2005, -1, + + // 2706, r_parasol.brres, N=2+1 + 1276, 1301, -1, + + // 2709, ridgemii00.brres, N=2+1 + 893, 918, -1, + + // 2712, ring_mission_b.kcl, N=2+1 + 2300, 2323, -1, + + // 2715, sanbo.brres, N=5+1 + 822, 860, 1415, 1440, 2421, -1, + + // 2721, seagull.brres, N=10+1 + 172, 213, 464, 497, 523, 560, 1065, 1097, 1325, 1353, + -1, + + // 2732, skyship.brres, N=4+1 + 15, 41, 2240, 2261, -1, + + // 2737, spot.brres, N=2+1 + 1950, 1969, -1, + + // 2740, starGate.brres, N=1+1 + 2376, -1, + + // 2742, starGate.kcl, N=1+1 + 2377, -1, + + // 2744, sun.brres, N=44+1 + 16, 42, 62, 88, 116, 140, 173, 214, 248, 274, + 306, 332, 354, 384, 407, 429, 465, 498, 524, 561, + 823, 861, 1066, 1098, 1120, 1136, 1181, 1197, 1216, 1246, + 1277, 1302, 1327, 1355, 1560, 1583, 1628, 1650, 1676, 1701, + 1852, 1883, 1986, 2006, -1, + + // 2789, sunDS.brres, N=2+1 + 1416, 1441, -1, + + // 2792, tree_cannon.brres, N=2+1 + 591, 626, -1, + + // 2795, truckChimSmk.brres, N=4+1 + 408, 430, 592, 627, -1, + + // 2800, truckChimSmkW.brres, N=2+1 + 1853, 1884, -1, + + // 2803, venice_hasi.brres, N=2+1 + 1854, 1885, -1, + + // 2806, venice_hasi.kcl, N=2+1 + 1855, 1886, -1, + + // 2809, venice_hasi_b.kcl, N=2+1 + 1856, 1887, -1, + + // 2812, venice_hasi_wall.kcl, N=2+1 + 1857, 1888, -1, + + // 2815, venice_nami.brres, N=2+1 + 1858, 1889, -1, + + // 2818, venice_saku.brres, N=2+1 + 1859, 1890, -1, + + // 2821, venice_saku.kcl, N=2+1 + 1860, 1891, -1, + + // 2824, vrcorn_model.brres, N=99+1 + 17, 43, 63, 89, 117, 141, 174, 215, 249, 275, + 307, 333, 355, 385, 410, 432, 466, 499, 526, 563, + 593, 628, 707, 784, 824, 862, 894, 919, 947, 985, + 1020, 1041, 1067, 1099, 1121, 1137, 1154, 1168, 1182, 1198, + 1217, 1247, 1278, 1303, 1333, 1361, 1377, 1395, 1417, 1442, + 1466, 1487, 1511, 1536, 1561, 1584, 1603, 1615, 1629, 1651, + 1677, 1702, 1723, 1748, 1796, 1836, 1861, 1892, 1913, 1930, + 1952, 1971, 1987, 2007, 2024, 2035, 2046, 2057, 2070, 2085, + 2098, 2109, 2120, 2135, 2159, 2187, 2209, 2222, 2241, 2262, + 2276, 2289, 2310, 2333, 2347, 2358, 2365, 2369, 2373, -1, + + // 2924, wanwan.brres, N=7+1 + 250, 276, 1562, 1585, 1630, 1652, 2410, -1, + + // 2932, windmill.brres, N=2+1 + 64, 90, -1, + + // 2935, woodbox.brres, N=5+1 + 175, 216, 1334, 1362, 2401, -1, + + // END + -1 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// group ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const DbFileGROUP_t DbFileGROUP[N_DB_FILE_GROUP+1] = +{ + { 0x000a8, 0, "E", "Alarm" }, // 0 + { 0x00068, 3, "B", "BGteresaSFC" }, // 1 + { 0x00068, 5, "B", "BeltCrossing" }, // 2 + { 0x00068, 7, "B", "BeltCurveA" }, // 3 + { 0x01008, 9, "", "BeltCurveB" }, // 4 + { 0x00068, 10, "B", "BeltEasy" }, // 5 + { 0x01008, 12, "", "BlackHole" }, // 6 + { 0x000e8, 13, "BE", "Block" }, // 7 + { 0x01008, 17, "", "BossField01_OBJ1" }, // 8 + { 0x00068, 18, "B", "CarA1" }, // 9 + { 0x00068, 20, "B", "CarA2" }, // 10 + { 0x00068, 22, "B", "CarA3" }, // 11 + { 0x00068, 24, "B", "CarB" }, // 12 + { 0x00268, 26, "BK", "Crane" }, // 13 + { 0x00068, 29, "B", "Crescent64" }, // 14 + { 0x000a8, 31, "E", "DKfalls" }, // 15 + { 0x01008, 34, "", "DKmarutaGC" }, // 16 + { 0x000e8, 35, "BE", "DKrockGC" }, // 17 + { 0x01008, 39, "", "DKropeGC" }, // 18 + { 0x000e8, 40, "BE", "DKship64" }, // 19 + { 0x06068, 44, "B", "DKtreeA64" }, // 20 + { 0x06068, 46, "B", "DKtreeB64" }, // 21 + { 0x0e068, 48, "B", "DKturibashiGC" }, // 22 + { 0x00268, 50, "BK", "DemoCol" }, // 23 + { 0x000a8, 53, "E", "DemoEf" }, // 24 + { 0x000e8, 56, "BE", "DemoJugemu" }, // 25 + { 0x001e8, 60, "BED", "DonkyCannonGC" }, // 26 + { 0x001e8, 65, "BED", "DonkyCannon_wii" }, // 27 + { 0x00068, 70, "B", "EarthRing" }, // 28 + { 0x000a8, 72, "E", "EnvFire" }, // 29 + { 0x000a8, 75, "E", "EnvKareha" }, // 30 + { 0x000a8, 78, "E", "EnvKarehaUp" }, // 31 + { 0x000a8, 81, "E", "EnvSnow" }, // 32 + { 0x000e8, 84, "BE", "Epropeller" }, // 33 + { 0x000a8, 88, "E", "FallBsA" }, // 34 + { 0x000a8, 91, "E", "FallBsB" }, // 35 + { 0x01008, 94, "", "FallBsC" }, // 36 + { 0x000a8, 95, "E", "Fall_MH" }, // 37 + { 0x000a8, 98, "E", "Fall_Y" }, // 38 + { 0x000e8, 101, "BE", "FireSnake" }, // 39 + { 0x00068, 105, "B", "FlagA1" }, // 40 + { 0x00068, 107, "B", "FlagA2" }, // 41 + { 0x00068, 109, "B", "FlagA3" }, // 42 + { 0x01008, 111, "", "FlagA4" }, // 43 + { 0x00068, 112, "B", "FlagA5" }, // 44 + { 0x00068, 114, "B", "FlagB1" }, // 45 + { 0x00068, 116, "B", "FlagB2" }, // 46 + { 0x00068, 118, "B", "FlagB3" }, // 47 + { 0x00068, 120, "B", "FlagB4" }, // 48 + { 0x002e8, 122, "BEK", "FlamePole" }, // 49 + { 0x01008, 127, "", "FlamePoleEff" }, // 50 + { 0x000e8, 128, "BE", "FlamePole_v" }, // 51 + { 0x000a8, 132, "E", "FlamePole_v_big" }, // 52 + { 0x000a8, 135, "E", "Flash_B" }, // 53 + { 0x000a8, 138, "E", "Flash_L" }, // 54 + { 0x000a8, 141, "E", "Flash_M" }, // 55 + { 0x000a8, 144, "E", "Flash_S" }, // 56 + { 0x000a8, 147, "E", "Flash_W" }, // 57 + { 0x01008, 150, "", "HP_pipe" }, // 58 + { 0x000a8, 151, "E", "Hanabi" }, // 59 + { 0x000e8, 154, "BE", "HeyhoBallGBA" }, // 60 + { 0x00068, 158, "B", "HeyhoShipGBA" }, // 61 + { 0x06068, 160, "B", "HeyhoTreeGBA" }, // 62 + { 0x00068, 162, "B", "InsekiA" }, // 63 + { 0x00068, 164, "B", "InsekiB" }, // 64 + { 0x000e8, 166, "BE", "K_bomb_car" }, // 65 + { 0x100e8, 170, "BE", "K_bomb_car1" }, // 66 + { 0x01008, 174, "", "K_bomb_car_dummy" }, // 67 + { 0x00068, 175, "B", "K_car_body" }, // 68 + { 0x00068, 177, "B", "K_chairlift00" }, // 69 + { 0x00068, 179, "B", "K_sticklift00" }, // 70 + { 0x00068, 181, "B", "K_truck" }, // 71 + { 0x000a8, 183, "E", "Kamifubuki" }, // 72 + { 0x00068, 186, "B", "KmoonZ" }, // 73 + { 0x000e8, 188, "BE", "KoopaFigure64" }, // 74 + { 0x00068, 192, "B", "Ksticketc" }, // 75 + { 0x01008, 194, "", "MFaceBill" }, // 76 + { 0x10268, 195, "BK", "M_obj_jump" }, // 77 + { 0x10268, 198, "BK", "M_obj_jump2" }, // 78 + { 0x01008, 201, "", "M_obj_kanban" }, // 79 + { 0x01008, 202, "", "M_obj_kanban_y" }, // 80 + { 0x10268, 203, "BK", "M_obj_s_jump" }, // 81 + { 0x10268, 206, "BK", "M_obj_s_jump2" }, // 82 + { 0x01008, 209, "", "M_obj_start" }, // 83 + { 0x00068, 210, "B", "MarioGo64" }, // 84 + { 0x00068, 212, "B", "MashBalloonGC" }, // 85 + { 0x00268, 214, "BK", "Mdush" }, // 86 + { 0x00068, 217, "B", "MiiKanban" }, // 87 + { 0x00068, 219, "B", "MiiObj01" }, // 88 + { 0x00068, 221, "B", "MiiObj02" }, // 89 + { 0x00068, 223, "B", "MiiObj03" }, // 90 + { 0x00068, 225, "B", "MiiObjD01" }, // 91 + { 0x00068, 227, "B", "MiiObjD02" }, // 92 + { 0x00068, 229, "B", "MiiObjD03" }, // 93 + { 0x00068, 231, "B", "MiiSighKino" }, // 94 + { 0x00068, 233, "B", "MiiSignNoko" }, // 95 + { 0x00068, 235, "B", "MiiSignWario" }, // 96 + { 0x01008, 237, "", "MiiSphinxY1" }, // 97 + { 0x00068, 238, "B", "MiiSphinxY2" }, // 98 + { 0x01008, 240, "", "MiiStatue1" }, // 99 + { 0x00068, 241, "B", "MiiStatueBD1" }, // 100 + { 0x00068, 243, "B", "MiiStatueBL1" }, // 101 + { 0x00068, 245, "B", "MiiStatueD1" }, // 102 + { 0x00068, 247, "B", "MiiStatueD2" }, // 103 + { 0x01008, 249, "", "MiiStatueDK1" }, // 104 + { 0x00068, 250, "B", "MiiStatueL1" }, // 105 + { 0x00068, 252, "B", "MiiStatueL2" }, // 106 + { 0x00068, 254, "B", "MiiStatueL3" }, // 107 + { 0x00068, 256, "B", "MiiStatueM1" }, // 108 + { 0x00068, 258, "B", "MiiStatueM2" }, // 109 + { 0x00068, 260, "B", "MiiStatueP1" }, // 110 + { 0x01008, 262, "", "Mstand" }, // 111 + { 0x06068, 263, "B", "PalmTree" }, // 112 + { 0x000e8, 265, "BE", "PeachHunsuiGC" }, // 113 + { 0x00068, 269, "B", "Piston" }, // 114 + { 0x000e8, 271, "BE", "Press" }, // 115 + { 0x00068, 275, "B", "Psea" }, // 116 + { 0x0026c, 277, "B8K", "RM_ring1" }, // 117 + { 0x00068, 287, "B", "RhMiiKanban" }, // 118 + { 0x00068, 289, "B", "SentakuDS" }, // 119 + { 0x00068, 291, "B", "ShMiiObj01" }, // 120 + { 0x00068, 293, "B", "ShMiiObj02" }, // 121 + { 0x00068, 295, "B", "ShMiiObj03" }, // 122 + { 0x00068, 297, "B", "SpaceSun" }, // 123 + { 0x00068, 299, "B", "Spot64" }, // 124 + { 0x000e8, 301, "BE", "StarRing" }, // 125 + { 0x000a8, 305, "E", "Steam" }, // 126 + { 0x00268, 308, "B3K", "TownBridgeDS" }, // 127 + { 0x06068, 313, "B", "TownTreeDS" }, // 128 + { 0x000e8, 315, "BE", "TruckWagon" }, // 129 + { 0x00068, 319, "B", "Twanwan" }, // 130 + { 0x01008, 321, "", "Twanwan_ue" }, // 131 + { 0x00068, 322, "B", "TwistedWay" }, // 132 + { 0x00068, 324, "B", "UtsuboDokan" }, // 133 + { 0x000e8, 326, "BE", "VolcanoBall1" }, // 134 + { 0x0026c, 330, "B42K", "VolcanoPiece1" }, // 135 + { 0x00268, 374, "B2K", "VolcanoRock1" }, // 136 + { 0x00068, 378, "B", "WLarrowGC" }, // 137 + { 0x06268, 380, "BK", "WLdokanGC" }, // 138 + { 0x00068, 383, "B", "WLfirebarGC" }, // 139 + { 0x06068, 385, "B", "WLscreenGC" }, // 140 + { 0x00268, 387, "BK", "WLwallGC" }, // 141 + { 0x00068, 390, "B", "ami" }, // 142 + { 0x00068, 392, "B", "aurora" }, // 143 + { 0x00068, 394, "B", "b_teresa" }, // 144 + { 0x00068, 396, "B", "basabasa" }, // 145 + { 0x0026c, 398, "B18K", "bblock1" }, // 146 + { 0x000e8, 418, "BE", "begoman_spike" }, // 147 + { 0x00068, 422, "B", "bird" }, // 148 + { 0x000e8, 424, "BE", "boble" }, // 149 + { 0x00268, 428, "BK", "bulldozer_left" }, // 150 + { 0x00268, 431, "BK", "bulldozer_right" }, // 151 + { 0x01008, 434, "", "car_body_dummy" }, // 152 + { 0x00268, 435, "BK", "casino_roulette" }, // 153 + { 0x00068, 438, "B", "castleballoon1" }, // 154 + { 0x00048, 440, "B", "castleflower1" }, // 155 + { 0x06048, 442, "B", "castletree1" }, // 156 + { 0x06048, 444, "B", "castletree2" }, // 157 + { 0x000e8, 446, "BE", "choropu" }, // 158 + { 0x060e8, 450, "BE", "coin" }, // 159 + { 0x00168, 454, "B5D", "cow" }, // 160 + { 0x001e8, 461, "BE3D", "crab" }, // 161 + { 0x000e8, 468, "BE", "cruiser" }, // 162 + { 0x002e8, 472, "BEK", "dc_pillar" }, // 163 + { 0x002e8, 477, "BEK", "dc_sandcone" }, // 164 + { 0x00068, 482, "B", "dk_miiobj00" }, // 165 + { 0x00068, 484, "B", "dkmonitor" }, // 166 + { 0x06068, 486, "B", "dokan_sfc" }, // 167 + { 0x06068, 488, "B", "donkytree1GC" }, // 168 + { 0x06068, 490, "B", "donkytree2GC" }, // 169 + { 0x000e8, 492, "BE", "dossun" }, // 170 + { 0x01008, 496, "", "dummy" }, // 171 + { 0x000a8, 497, "E", "entry" }, // 172 + { 0x00268, 500, "BK", "escalator" }, // 173 + { 0x00068, 503, "B", "fks_screen_wii" }, // 174 + { 0x01008, 505, "", "flag" }, // 175 + { 0x01008, 506, "", "flagBlend" }, // 176 + { 0x06068, 507, "B", "gardentreeDS" }, // 177 + { 0x00068, 509, "B", "group_enemy_a" }, // 178 + { 0x00068, 511, "B", "group_enemy_a_demo" }, // 179 + { 0x00068, 513, "B", "group_enemy_b" }, // 180 + { 0x00068, 515, "B", "group_enemy_c" }, // 181 + { 0x01008, 517, "", "group_enemy_d" }, // 182 + { 0x00068, 518, "B", "group_enemy_e" }, // 183 + { 0x00068, 520, "B", "group_enemy_f" }, // 184 + { 0x00068, 522, "B", "group_monte_a" }, // 185 + { 0x00068, 524, "B", "group_monte_a_demo" }, // 186 + { 0x001e8, 526, "BE2D", "hanachan" }, // 187 + { 0x00068, 532, "B", "heyho" }, // 188 + { 0x00068, 534, "B", "heyho2" }, // 189 + { 0x100e8, 536, "BE", "honeBall" }, // 190 + { 0x0006c, 540, "B", "ice" }, // 191 + { 0x00068, 542, "B", "itembox" }, // 192 + { 0x060e8, 544, "BE", "karehayama" }, // 193 + { 0x01008, 548, "", "kart_truck_dummy" }, // 194 + { 0x0e268, 549, "B4K", "kinoko" }, // 195 + { 0x00068, 555, "B", "kinokoT1" }, // 196 + { 0x01008, 557, "", "kinokoT2" }, // 197 + { 0x000e8, 558, "BE", "koopaBall" }, // 198 + { 0x00168, 562, "B2D", "koopaFigure" }, // 199 + { 0x00068, 566, "B", "koopaFirebar" }, // 200 + { 0x00168, 568, "B2D", "kuribo" }, // 201 + { 0x000a8, 572, "E", "leaf_effect" }, // 202 + { 0x01008, 575, "", "lensFX" }, // 203 + { 0x00068, 576, "B", "light_house" }, // 204 + { 0x00068, 578, "B", "mare_a" }, // 205 + { 0x00068, 580, "B", "mare_b" }, // 206 + { 0x06068, 582, "B", "mariotreeGC" }, // 207 + { 0x00068, 584, "B", "mii_balloon" }, // 208 + { 0x00068, 586, "B", "miiposter" }, // 209 + { 0x00068, 588, "B", "monte_a" }, // 210 + { 0x00068, 590, "B", "moray" }, // 211 + { 0x01008, 592, "", "npc_mii_a" }, // 212 + { 0x01008, 593, "", "npc_mii_b" }, // 213 + { 0x01008, 594, "", "npc_mii_c" }, // 214 + { 0x06068, 595, "B", "obakeblockSFC" }, // 215 + { 0x00068, 597, "B", "oilSFC" }, // 216 + { 0x06068, 599, "B", "pakkun_dokan" }, // 217 + { 0x00168, 601, "B2D", "pakkun_f" }, // 218 + { 0x00068, 605, "B", "parasol" }, // 219 + { 0x06068, 607, "B", "peachtreeGC" }, // 220 + { 0x000e8, 609, "BE", "penguin_l" }, // 221 + { 0x001e8, 613, "BED", "penguin_m" }, // 222 + { 0x001e8, 618, "BE3D", "penguin_s" }, // 223 + { 0x01008, 625, "", "pile" }, // 224 + { 0x000e8, 626, "BE", "pocha" }, // 225 + { 0x000a8, 630, "E", "pochaMori" }, // 226 + { 0x000e8, 633, "BE", "pochaYogan" }, // 227 + { 0x001e8, 637, "BE2D", "poihana" }, // 228 + { 0x00168, 643, "BD", "puchi_pakkun" }, // 229 + { 0x00068, 646, "B", "pukupuku" }, // 230 + { 0x00068, 648, "B", "pylon01" }, // 231 + { 0x000e8, 650, "BE", "quicksand" }, // 232 + { 0x00068, 654, "B", "r_parasol" }, // 233 + { 0x00068, 656, "B", "ridgemii00" }, // 234 + { 0x001e8, 658, "BED", "sanbo" }, // 235 + { 0x00068, 663, "B", "seagull" }, // 236 + { 0x00068, 665, "B", "skyship" }, // 237 + { 0x00068, 667, "B", "spot" }, // 238 + { 0x102e8, 669, "BEK", "starGate" }, // 239 + { 0x00068, 674, "B", "sun" }, // 240 + { 0x00068, 676, "B", "sunDS" }, // 241 + { 0x000a8, 678, "E", "taimatsu" }, // 242 + { 0x001e8, 681, "BED", "tree_cannon" }, // 243 + { 0x000e8, 686, "BE", "truckChimSmk" }, // 244 + { 0x000e8, 690, "BE", "truckChimSmkW" }, // 245 + { 0x01008, 694, "", "v_stand1" }, // 246 + { 0x01008, 695, "", "v_stand2" }, // 247 + { 0x01008, 696, "", "venice_gondola" }, // 248 + { 0x002e8, 697, "BEK", "venice_hasi" }, // 249 + { 0x00068, 702, "B", "venice_nami" }, // 250 + { 0x00268, 704, "BK", "venice_saku" }, // 251 + { 0x01008, 707, "", "volfall" }, // 252 + { 0x000a8, 708, "E", "volsmk" }, // 253 + { 0x000e8, 711, "BE", "wanwan" }, // 254 + { 0x00068, 715, "B", "windmill" }, // 255 + { 0x060e8, 717, "BE", "woodbox" }, // 256 + { 0x00e43, 721, "4BKP3x", "~course" }, // 257 + { 0x00824, 731, "14x", "~posteffect" }, // 258 + { 0x00820, 746, "x", "~useless" }, // 259 + { 0x00a34, 748, "8x", "~~unknown" }, // 260 + + {-1,0,"",""} +}; + +// +//-------------------------------------------------- + +const s16 DbFileRefGROUP[N_DB_FILE_REF_GROUP+1] = +{ + + // 0, Alarm, N=2+1 + 258, 259, -1, + + // 3, BGteresaSFC, N=1+1 + 2, -1, + + // 5, BeltCrossing, N=1+1 + 3, -1, + + // 7, BeltCurveA, N=1+1 + 4, -1, + + // 9, BeltCurveB, N=0+1 + -1, + + // 10, BeltEasy, N=1+1 + 5, -1, + + // 12, BlackHole, N=0+1 + -1, + + // 13, Block, N=3+1 + 6, 260, 261, -1, + + // 17, BossField01_OBJ1, N=0+1 + -1, + + // 18, CarA1, N=1+1 + 7, -1, + + // 20, CarA2, N=1+1 + 8, -1, + + // 22, CarA3, N=1+1 + 9, -1, + + // 24, CarB, N=1+1 + 10, -1, + + // 26, Crane, N=2+1 + 11, 12, -1, + + // 29, Crescent64, N=1+1 + 13, -1, + + // 31, DKfalls, N=2+1 + 262, 263, -1, + + // 34, DKmarutaGC, N=0+1 + -1, + + // 35, DKrockGC, N=3+1 + 14, 264, 265, -1, + + // 39, DKropeGC, N=0+1 + -1, + + // 40, DKship64, N=3+1 + 15, 266, 267, -1, + + // 44, DKtreeA64, N=1+1 + 16, -1, + + // 46, DKtreeB64, N=1+1 + 17, -1, + + // 48, DKturibashiGC, N=1+1 + 18, -1, + + // 50, DemoCol, N=2+1 + 19, 20, -1, + + // 53, DemoEf, N=2+1 + 268, 269, -1, + + // 56, DemoJugemu, N=3+1 + 21, 270, 271, -1, + + // 60, DonkyCannonGC, N=4+1 + 22, 196, 272, 273, -1, + + // 65, DonkyCannon_wii, N=4+1 + 23, 197, 274, 275, -1, + + // 70, EarthRing, N=1+1 + 24, -1, + + // 72, EnvFire, N=2+1 + 276, 277, -1, + + // 75, EnvKareha, N=2+1 + 278, 279, -1, + + // 78, EnvKarehaUp, N=2+1 + 280, 281, -1, + + // 81, EnvSnow, N=2+1 + 282, 283, -1, + + // 84, Epropeller, N=3+1 + 25, 284, 285, -1, + + // 88, FallBsA, N=2+1 + 286, 287, -1, + + // 91, FallBsB, N=2+1 + 288, 289, -1, + + // 94, FallBsC, N=0+1 + -1, + + // 95, Fall_MH, N=2+1 + 290, 291, -1, + + // 98, Fall_Y, N=2+1 + 292, 293, -1, + + // 101, FireSnake, N=3+1 + 26, 294, 295, -1, + + // 105, FlagA1, N=1+1 + 27, -1, + + // 107, FlagA2, N=1+1 + 28, -1, + + // 109, FlagA3, N=1+1 + 29, -1, + + // 111, FlagA4, N=0+1 + -1, + + // 112, FlagA5, N=1+1 + 30, -1, + + // 114, FlagB1, N=1+1 + 31, -1, + + // 116, FlagB2, N=1+1 + 32, -1, + + // 118, FlagB3, N=1+1 + 33, -1, + + // 120, FlagB4, N=1+1 + 34, -1, + + // 122, FlamePole, N=4+1 + 35, 36, 296, 297, -1, + + // 127, FlamePoleEff, N=0+1 + -1, + + // 128, FlamePole_v, N=3+1 + 37, 298, 299, -1, + + // 132, FlamePole_v_big, N=2+1 + 300, 301, -1, + + // 135, Flash_B, N=2+1 + 302, 303, -1, + + // 138, Flash_L, N=2+1 + 304, 305, -1, + + // 141, Flash_M, N=2+1 + 306, 307, -1, + + // 144, Flash_S, N=2+1 + 308, 309, -1, + + // 147, Flash_W, N=2+1 + 310, 311, -1, + + // 150, HP_pipe, N=0+1 + -1, + + // 151, Hanabi, N=2+1 + 312, 313, -1, + + // 154, HeyhoBallGBA, N=3+1 + 38, 314, 315, -1, + + // 158, HeyhoShipGBA, N=1+1 + 39, -1, + + // 160, HeyhoTreeGBA, N=1+1 + 40, -1, + + // 162, InsekiA, N=1+1 + 41, -1, + + // 164, InsekiB, N=1+1 + 42, -1, + + // 166, K_bomb_car, N=3+1 + 43, 316, 317, -1, + + // 170, K_bomb_car1, N=3+1 + 44, 318, 319, -1, + + // 174, K_bomb_car_dummy, N=0+1 + -1, + + // 175, K_car_body, N=1+1 + 45, -1, + + // 177, K_chairlift00, N=1+1 + 46, -1, + + // 179, K_sticklift00, N=1+1 + 47, -1, + + // 181, K_truck, N=1+1 + 48, -1, + + // 183, Kamifubuki, N=2+1 + 320, 321, -1, + + // 186, KmoonZ, N=1+1 + 49, -1, + + // 188, KoopaFigure64, N=3+1 + 50, 322, 323, -1, + + // 192, Ksticketc, N=1+1 + 51, -1, + + // 194, MFaceBill, N=0+1 + -1, + + // 195, M_obj_jump, N=2+1 + 52, 53, -1, + + // 198, M_obj_jump2, N=2+1 + 54, 55, -1, + + // 201, M_obj_kanban, N=0+1 + -1, + + // 202, M_obj_kanban_y, N=0+1 + -1, + + // 203, M_obj_s_jump, N=2+1 + 56, 57, -1, + + // 206, M_obj_s_jump2, N=2+1 + 58, 59, -1, + + // 209, M_obj_start, N=0+1 + -1, + + // 210, MarioGo64, N=1+1 + 60, -1, + + // 212, MashBalloonGC, N=1+1 + 61, -1, + + // 214, Mdush, N=2+1 + 62, 63, -1, + + // 217, MiiKanban, N=1+1 + 64, -1, + + // 219, MiiObj01, N=1+1 + 65, -1, + + // 221, MiiObj02, N=1+1 + 66, -1, + + // 223, MiiObj03, N=1+1 + 67, -1, + + // 225, MiiObjD01, N=1+1 + 68, -1, + + // 227, MiiObjD02, N=1+1 + 69, -1, + + // 229, MiiObjD03, N=1+1 + 70, -1, + + // 231, MiiSighKino, N=1+1 + 71, -1, + + // 233, MiiSignNoko, N=1+1 + 72, -1, + + // 235, MiiSignWario, N=1+1 + 73, -1, + + // 237, MiiSphinxY1, N=0+1 + -1, + + // 238, MiiSphinxY2, N=1+1 + 74, -1, + + // 240, MiiStatue1, N=0+1 + -1, + + // 241, MiiStatueBD1, N=1+1 + 75, -1, + + // 243, MiiStatueBL1, N=1+1 + 76, -1, + + // 245, MiiStatueD1, N=1+1 + 77, -1, + + // 247, MiiStatueD2, N=1+1 + 78, -1, + + // 249, MiiStatueDK1, N=0+1 + -1, + + // 250, MiiStatueL1, N=1+1 + 79, -1, + + // 252, MiiStatueL2, N=1+1 + 80, -1, + + // 254, MiiStatueL3, N=1+1 + 81, -1, + + // 256, MiiStatueM1, N=1+1 + 82, -1, + + // 258, MiiStatueM2, N=1+1 + 83, -1, + + // 260, MiiStatueP1, N=1+1 + 84, -1, + + // 262, Mstand, N=0+1 + -1, + + // 263, PalmTree, N=1+1 + 85, -1, + + // 265, PeachHunsuiGC, N=3+1 + 86, 324, 325, -1, + + // 269, Piston, N=1+1 + 87, -1, + + // 271, Press, N=3+1 + 88, 326, 327, -1, + + // 275, Psea, N=1+1 + 89, -1, + + // 277, RM_ring1, N=9+1 + 90, 91, 92, 93, 94, 95, 96, 97, 98, -1, + + // 287, RhMiiKanban, N=1+1 + 99, -1, + + // 289, SentakuDS, N=1+1 + 100, -1, + + // 291, ShMiiObj01, N=1+1 + 101, -1, + + // 293, ShMiiObj02, N=1+1 + 102, -1, + + // 295, ShMiiObj03, N=1+1 + 103, -1, + + // 297, SpaceSun, N=1+1 + 104, -1, + + // 299, Spot64, N=1+1 + 105, -1, + + // 301, StarRing, N=3+1 + 106, 328, 329, -1, + + // 305, Steam, N=2+1 + 330, 331, -1, + + // 308, TownBridgeDS, N=4+1 + 107, 108, 109, 110, -1, + + // 313, TownTreeDS, N=1+1 + 111, -1, + + // 315, TruckWagon, N=3+1 + 112, 332, 333, -1, + + // 319, Twanwan, N=1+1 + 113, -1, + + // 321, Twanwan_ue, N=0+1 + -1, + + // 322, TwistedWay, N=1+1 + 114, -1, + + // 324, UtsuboDokan, N=1+1 + 115, -1, + + // 326, VolcanoBall1, N=3+1 + 116, 334, 335, -1, + + // 330, VolcanoPiece1, N=43+1 + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, -1, + + // 374, VolcanoRock1, N=3+1 + 160, 161, 162, -1, + + // 378, WLarrowGC, N=1+1 + 163, -1, + + // 380, WLdokanGC, N=2+1 + 164, 165, -1, + + // 383, WLfirebarGC, N=1+1 + 166, -1, + + // 385, WLscreenGC, N=1+1 + 167, -1, + + // 387, WLwallGC, N=2+1 + 168, 169, -1, + + // 390, ami, N=1+1 + 170, -1, + + // 392, aurora, N=1+1 + 171, -1, + + // 394, b_teresa, N=1+1 + 172, -1, + + // 396, basabasa, N=1+1 + 173, -1, + + // 398, bblock1, N=19+1 + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, -1, + + // 418, begoman_spike, N=3+1 + 193, 336, 337, -1, + + // 422, bird, N=1+1 + 194, -1, + + // 424, boble, N=3+1 + 195, 338, 339, -1, + + // 428, bulldozer_left, N=2+1 + 223, 224, -1, + + // 431, bulldozer_right, N=2+1 + 225, 226, -1, + + // 434, car_body_dummy, N=0+1 + -1, + + // 435, casino_roulette, N=2+1 + 227, 228, -1, + + // 438, castleballoon1, N=1+1 + 229, -1, + + // 440, castleflower1, N=1+1 + 230, -1, + + // 442, castletree1, N=1+1 + 231, -1, + + // 444, castletree2, N=1+1 + 232, -1, + + // 446, choropu, N=3+1 + 233, 340, 341, -1, + + // 450, coin, N=3+1 + 234, 342, 343, -1, + + // 454, cow, N=6+1 + 198, 199, 200, 201, 202, 244, -1, + + // 461, crab, N=6+1 + 203, 204, 205, 245, 344, 345, -1, + + // 468, cruiser, N=3+1 + 246, 346, 347, -1, + + // 472, dc_pillar, N=4+1 + 247, 248, 348, 349, -1, + + // 477, dc_sandcone, N=4+1 + 250, 251, 350, 351, -1, + + // 482, dk_miiobj00, N=1+1 + 252, -1, + + // 484, dkmonitor, N=1+1 + 253, -1, + + // 486, dokan_sfc, N=1+1 + 254, -1, + + // 488, donkytree1GC, N=1+1 + 255, -1, + + // 490, donkytree2GC, N=1+1 + 256, -1, + + // 492, dossun, N=3+1 + 257, 352, 353, -1, + + // 496, dummy, N=0+1 + -1, + + // 497, entry, N=2+1 + 354, 355, -1, + + // 500, escalator, N=2+1 + 402, 403, -1, + + // 503, fks_screen_wii, N=1+1 + 404, -1, + + // 505, flag, N=0+1 + -1, + + // 506, flagBlend, N=0+1 + -1, + + // 507, gardentreeDS, N=1+1 + 405, -1, + + // 509, group_enemy_a, N=1+1 + 406, -1, + + // 511, group_enemy_a_demo, N=1+1 + 407, -1, + + // 513, group_enemy_b, N=1+1 + 408, -1, + + // 515, group_enemy_c, N=1+1 + 409, -1, + + // 517, group_enemy_d, N=0+1 + -1, + + // 518, group_enemy_e, N=1+1 + 410, -1, + + // 520, group_enemy_f, N=1+1 + 411, -1, + + // 522, group_monte_a, N=1+1 + 412, -1, + + // 524, group_monte_a_demo, N=1+1 + 413, -1, + + // 526, hanachan, N=5+1 + 206, 207, 356, 357, 414, -1, + + // 532, heyho, N=1+1 + 415, -1, + + // 534, heyho2, N=1+1 + 416, -1, + + // 536, honeBall, N=3+1 + 358, 359, 417, -1, + + // 540, ice, N=1+1 + 418, -1, + + // 542, itembox, N=1+1 + 420, -1, + + // 544, karehayama, N=3+1 + 360, 361, 421, -1, + + // 548, kart_truck_dummy, N=0+1 + -1, + + // 549, kinoko, N=5+1 + 422, 424, 425, 426, 427, -1, + + // 555, kinokoT1, N=1+1 + 423, -1, + + // 557, kinokoT2, N=0+1 + -1, + + // 558, koopaBall, N=3+1 + 362, 363, 428, -1, + + // 562, koopaFigure, N=3+1 + 208, 209, 429, -1, + + // 566, koopaFirebar, N=1+1 + 430, -1, + + // 568, kuribo, N=3+1 + 210, 211, 431, -1, + + // 572, leaf_effect, N=2+1 + 364, 365, -1, + + // 575, lensFX, N=0+1 + -1, + + // 576, light_house, N=1+1 + 432, -1, + + // 578, mare_a, N=1+1 + 434, -1, + + // 580, mare_b, N=1+1 + 435, -1, + + // 582, mariotreeGC, N=1+1 + 436, -1, + + // 584, mii_balloon, N=1+1 + 437, -1, + + // 586, miiposter, N=1+1 + 438, -1, + + // 588, monte_a, N=1+1 + 439, -1, + + // 590, moray, N=1+1 + 440, -1, + + // 592, npc_mii_a, N=0+1 + -1, + + // 593, npc_mii_b, N=0+1 + -1, + + // 594, npc_mii_c, N=0+1 + -1, + + // 595, obakeblockSFC, N=1+1 + 441, -1, + + // 597, oilSFC, N=1+1 + 442, -1, + + // 599, pakkun_dokan, N=1+1 + 443, -1, + + // 601, pakkun_f, N=3+1 + 212, 213, 444, -1, + + // 605, parasol, N=1+1 + 445, -1, + + // 607, peachtreeGC, N=1+1 + 446, -1, + + // 609, penguin_l, N=3+1 + 366, 367, 447, -1, + + // 613, penguin_m, N=4+1 + 214, 368, 369, 448, -1, + + // 618, penguin_s, N=6+1 + 215, 216, 217, 370, 371, 449, -1, + + // 625, pile, N=0+1 + -1, + + // 626, pocha, N=3+1 + 372, 373, 450, -1, + + // 630, pochaMori, N=2+1 + 374, 375, -1, + + // 633, pochaYogan, N=3+1 + 376, 377, 451, -1, + + // 637, poihana, N=5+1 + 218, 219, 378, 379, 452, -1, + + // 643, puchi_pakkun, N=2+1 + 220, 467, -1, + + // 646, pukupuku, N=1+1 + 468, -1, + + // 648, pylon01, N=1+1 + 469, -1, + + // 650, quicksand, N=3+1 + 380, 381, 470, -1, + + // 654, r_parasol, N=1+1 + 471, -1, + + // 656, ridgemii00, N=1+1 + 472, -1, + + // 658, sanbo, N=4+1 + 221, 382, 383, 474, -1, + + // 663, seagull, N=1+1 + 475, -1, + + // 665, skyship, N=1+1 + 476, -1, + + // 667, spot, N=1+1 + 477, -1, + + // 669, starGate, N=4+1 + 384, 385, 478, 479, -1, + + // 674, sun, N=1+1 + 480, -1, + + // 676, sunDS, N=1+1 + 481, -1, + + // 678, taimatsu, N=2+1 + 386, 387, -1, + + // 681, tree_cannon, N=4+1 + 222, 388, 389, 482, -1, + + // 686, truckChimSmk, N=3+1 + 390, 391, 483, -1, + + // 690, truckChimSmkW, N=3+1 + 392, 393, 484, -1, + + // 694, v_stand1, N=0+1 + -1, + + // 695, v_stand2, N=0+1 + -1, + + // 696, venice_gondola, N=0+1 + -1, + + // 697, venice_hasi, N=4+1 + 394, 395, 485, 486, -1, + + // 702, venice_nami, N=1+1 + 489, -1, + + // 704, venice_saku, N=2+1 + 490, 491, -1, + + // 707, volfall, N=0+1 + -1, + + // 708, volsmk, N=2+1 + 396, 397, -1, + + // 711, wanwan, N=3+1 + 398, 399, 493, -1, + + // 715, windmill, N=1+1 + 494, -1, + + // 717, woodbox, N=3+1 + 400, 401, 495, -1, + + // 721, ~course, N=9+1 + 236, 237, 238, 239, 240, 242, 243, 433, 492, -1, + + // 731, ~posteffect, N=14+1 + 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, + 463, 464, 465, 466, -1, + + // 746, ~useless, N=1+1 + 235, -1, + + // 748, ~~unknown, N=8+1 + 0, 1, 241, 249, 419, 473, 487, 488, -1, + + // END + -2 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/db-file.h b/project/src/db-file.h new file mode 100644 index 0000000..4208d54 --- /dev/null +++ b/project/src/db-file.h @@ -0,0 +1,212 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_DB_FILE_H +#define SZS_DB_FILE_H 1 + +#include "lib-std.h" + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// const ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define N_DB_FILE 2445 +#define N_DB_FILE_SZS 111 +#define N_DB_FILE_SHA1 920 +#define N_DB_FILE_FILE 496 +#define N_DB_FILE_GROUP 261 + +#define N_DB_FILE_REF_SZS 2552 +#define N_DB_FILE_REF_SHA1 3363 +#define N_DB_FILE_REF_FILE 2941 +#define N_DB_FILE_REF_GROUP 757 + +#define MAX_DB_FILE_REF_SZS 77 +#define MAX_DB_FILE_REF_SHA1 88 +#define MAX_DB_FILE_REF_FILE 99 +#define MAX_DB_FILE_REF_GROUP 43 + +#define MAX_DB_FILE_SIZE 3402114 + + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////// enum DbFlags_t ////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DbFlags_t]] +typedef enum DbFlags_t +{ + DBF_REQUIRED = 0x00001, // for track files required + DBF_REQUIRED2 = 0x00002, // alternativley required + DBF_OPTIONAL = 0x00004, // optional file + DBF_OBJECT = 0x00008, // file is object relevant + DBF_EXTERNAL = 0x00010, // external file + DBF_UNIQUE = 0x00020, // all instances are identical + DBF_BRRES = 0x00040, // B: at least one BRRES file exists + DBF_EFFECT = 0x00080, // E: at least one effect pair (BREFF+BREFT) exists + DBF_BRASD = 0x00100, // D: at least one BRASD file exists + DBF_KCL = 0x00200, // K: at least one KCL file exists + DBF_KMP = 0x00400, // P: at least one KMP file exists + DBF_OTHER = 0x00800, // x: at least one unknown file exists + DBF_NO_FILE = 0x01000, // ?: no file found for group + DBF_SCALE = 0x02000, // is known as scalable + DBF_ROTATE = 0x04000, // is known as rotatable + DBF_FALLDOWN = 0x08000, // relevant for fall-down + DBF_COMMONOBJ = 0x10000, // found in CommonObj*.szs + DBF_ARCH_REQ = 0x20000, // required for archive => used for WU8 encoding + DBF_ARCH_OPT = 0x40000, // optional in archive + + DBF_NONE = 0x00000, // none set + DBF_ALL = 0x7ffff // all 19 flags of above + +} DbFlags_t; + +#define DBF_ARCH_REQUIRED(f) ( (f&DBF_ARCH_REQ) != 0 ) +#define DBF_ARCH_OPTIONAL(f) ( (f&DBF_ARCH_OPT) != 0 ) +#define DBF_ARCH_SUPPORT(f) ( (f&(DBF_ARCH_REQ|DBF_ARCH_OPT)) != 0 ) + +#define OLD_DBF_IN_ARCHIVE(f) ( !(f&DBF_COMMONOBJ) && ( (f&DBF_OBJECT) \ + || (f&(DBF_EXTERNAL|DBF_UNIQUE)) == DBF_UNIQUE )) + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// draw data //////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DbFileDraw_t]] +typedef enum DbFileDraw_t +{ + DFD_TERM, + DFD_COIN, + DFD_CUBE, + DFD_CUBOID, + DFD_CYLINDER_Y, + DFD_CYLINDER_Z, + DFD_ITEMBOX, + DFD_KINOKO, + DFD_PYRAMID, + DFD_RECTANGLE, + DFD_TREE, + DFD__N +} +DbFileDraw_t; + +extern const s16 DbFileDraw[175]; + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// data base //////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DbFile_t]] +typedef struct DbFile_t +{ + s16 szs; // index into table 'DbFileSZS' + s16 sha1; // index into table 'DbFileSHA1' + s16 file; // index into table 'DbFileFILE' + +} +__attribute__ ((packed)) DbFile_t; + +extern const DbFile_t DbFile[N_DB_FILE+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// szs /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DbFileSZS_t]] +typedef struct DbFileSZS_t +{ + s16 ref; // index into table 'DbFileRefSZS' + char id[6]; // alpha numeric ID of track + ccp szs_name; // szs filename + s16 idx; // -1 or index + u16 type; // type of file +} +__attribute__ ((packed)) DbFileSZS_t; + +extern const DbFileSZS_t DbFileSZS[N_DB_FILE_SZS+1]; +extern const s16 DbFileRefSZS[N_DB_FILE_REF_SZS+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// sha1 /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DbFileSHA1_t]] +typedef struct DbFileSHA1_t +{ + s16 ref; // index into table 'DbFileRefSHA1' + sha1_hash_t sha1; // SHA1 check sum +} +__attribute__ ((packed)) DbFileSHA1_t; + +extern const DbFileSHA1_t DbFileSHA1[N_DB_FILE_SHA1+1]; +extern const s16 DbFileRefSHA1[N_DB_FILE_REF_SHA1+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// file /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DbFileFILE_t]] +typedef struct DbFileFILE_t +{ + s16 ref; // index into table 'DbFileRefFILE' + u16 group; // -1:none, >=0:index into 'DbFileGROUP' + u32 flags; // enum DbFlags_t + u16 draw_index; // index into draw list + u16 fform; // file format + ccp file; // internal filename without './' prefix +} +__attribute__ ((packed)) DbFileFILE_t; + +extern const DbFileFILE_t DbFileFILE[N_DB_FILE_FILE+1]; +extern const s16 DbFileRefFILE[N_DB_FILE_REF_FILE+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// group ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[DbFileGROUP_t]] +typedef struct DbFileGROUP_t +{ + u32 flags; // enum DbFlags_t + s16 ref; // index into table 'DbFileRefGROUP' + char status[6+1]; // flags as text + char name[18+1]; // group name +} +__attribute__ ((packed)) DbFileGROUP_t; + +extern const DbFileGROUP_t DbFileGROUP[N_DB_FILE_GROUP+1]; +extern const s16 DbFileRefGROUP[N_DB_FILE_REF_GROUP+1]; + +enum // group index of specialgroups +{ + DB_GROUP_COURSE = 257, + DB_GROUP_POSTEFFECT = 258, + DB_GROUP_USELESS = 259, + DB_GROUP__UNKNOWN = 260, +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_DB_FILE_H diff --git a/project/src/db-kcl.c b/project/src/db-kcl.c new file mode 100644 index 0000000..220a8b1 --- /dev/null +++ b/project/src/db-kcl.c @@ -0,0 +1,881 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#include "db-kcl.h" + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////// kcl_attrib_name[] //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const kcl_attrib_name_t kcl_attrib_name[N_KCL_ATTRIB+1] = +{ + { KCLT_C_ROAD, "C_ROAD" }, + { KCLT_C_OFFROAD, "C_OFFROAD" }, + { KCLT_C_FAST, "C_FAST" }, + { KCLT_C_HALFPIPE, "C_HALFPIPE" }, + { KCLT_C_MOVING, "C_MOVING" }, + { KCLT_C_WATER, "C_WATER" }, + { KCLT_C_CANNON, "C_CANNON" }, + { KCLT_C_WALL, "C_WALL" }, + { KCLT_C_FALL, "C_FALL" }, + { KCLT_C_EFFECT, "C_EFFECT" }, + { KCLT_C_ITEM, "C_ITEM" }, + { KCLT_C_UNKNOWN, "C_UNKNOWN" }, + { KCLT_BORDER, "BORDER" }, + { KCLT_DRIVE, "DRIVE" }, + { KCLT_BOOST, "BOOST" }, + { KCLT_TRICK, "TRICK" }, + { KCLT_SOUND, "SOUND" }, + { KCLT_ITEM, "ITEM" }, + { KCLT_UNKNOWN, "UNKNOWN" }, + {0,0} +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// kcl_class[] /////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const kcl_class_t kcl_class[N_KCL_CLASS+1] = +{ + { 0, 16, 0x0a0, "road", "normal road", + KCLT_C_ROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 1, 16, 0x0b0, "offroad", "offroad", + KCLT_C_OFFROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 2, 6, 0x0c0, "fast", "boost and trick", + KCLT_C_FAST | KCLT_BORDER | KCLT_DRIVE }, + + { 3, 5, 0x0c6, "halfpipe", "halfpipe", + KCLT_C_HALFPIPE | KCLT_BORDER | KCLT_DRIVE }, + + { 4, 5, 0x0cb, "moving", "moving terrain", + KCLT_C_MOVING | KCLT_BORDER | KCLT_DRIVE }, + + { 5, 8, 0x0d0, "water", "shallow water", + KCLT_C_WATER | KCLT_BORDER | KCLT_DRIVE }, + + { 6, 5, 0x0d8, "cannon", "cannon activator", + KCLT_C_CANNON }, + + { 7, 16, 0x0dd, "wall", "wall", + KCLT_C_WALL | KCLT_BORDER }, + + { 8, 10, 0x0ed, "fall", "fall or out of bounds", + KCLT_C_FALL }, + + { 9, 10, 0x0f7, "effect", "sound trigger and other effects", + KCLT_C_EFFECT }, + + { 10, 5, 0x101, "item", "for items only", + KCLT_C_ITEM | KCLT_ITEM }, + + { 11, 10, 0x106, "unknown", "unknown type", + KCLT_C_UNKNOWN }, + + {0,0,0} +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// kcl_color[] /////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const u8 kcl_color[N_KCL_COLORS][4] = +{ + {0,0,0,0}, // 0x000 unknown + {0xc0,0x00,0x00,0xff}, // 0x001 kmp_ckpt_warning + {0xc0,0x00,0x00,0xb0}, // 0x002 kmp_ckpt_warning_t + {0xc0,0x00,0x00,0x70}, // 0x003 kmp_ckpt_warning_tt + {0x20,0x00,0x80,0xff}, // 0x004 kmp_ckpt_lap + {0x20,0x00,0x80,0xb0}, // 0x005 kmp_ckpt_lap_t + {0x20,0x00,0x80,0x70}, // 0x006 kmp_ckpt_lap_tt + {0x00,0x00,0xc0,0xff}, // 0x007 kmp_ckpt_mandatory + {0x00,0x00,0xc0,0xb0}, // 0x008 kmp_ckpt_mandatory_t + {0x00,0x00,0xc0,0x70}, // 0x009 kmp_ckpt_mandatory_tt + {0x00,0x00,0x40,0xff}, // 0x00a kmp_ckpt_mandatory5 + {0x00,0x00,0x40,0xb0}, // 0x00b kmp_ckpt_mandatory5_t + {0x00,0x00,0x40,0x70}, // 0x00c kmp_ckpt_mandatory5_tt + {0x00,0xc0,0xc0,0xff}, // 0x00d kmp_ckpt_etc + {0x00,0xc0,0xc0,0xb0}, // 0x00e kmp_ckpt_etc_t + {0x00,0xc0,0xc0,0x70}, // 0x00f kmp_ckpt_etc_tt + {0x80,0x00,0x00,0xff}, // 0x010 kmp_ckpt_tie_lt + {0x00,0x80,0x00,0xff}, // 0x011 kmp_ckpt_tie_rt + {0x40,0x00,0x00,0xff}, // 0x012 kmp_ckpt_tie_group_lt + {0x00,0x40,0x00,0xff}, // 0x013 kmp_ckpt_tie_group_rt + {0x50,0x00,0x30,0xff}, // 0x014 kmp_ckpt_tie_next_lt + {0x00,0x50,0x50,0xff}, // 0x015 kmp_ckpt_tie_next_rt + {0x00,0x80,0x00,0xff}, // 0x016 kmp_ckpt_jgpt + {0x00,0x40,0x00,0xff}, // 0x017 kmp_jgpt + {0x20,0xa0,0x20,0x80}, // 0x018 kmp_jgpt_area + {0x00,0x00,0x00,0xff}, // 0x019 kmp_ktpt + {0x80,0x80,0x80,0x80}, // 0x01a kmp_ktpt_area + {0x00,0x00,0x80,0xc0}, // 0x01b kmp_enpt + {0x00,0x00,0x80,0xc0}, // 0x01c kmp_enpt_single + {0x00,0x40,0xa0,0xc0}, // 0x01d kmp_enpt_tie_bi + {0x00,0x60,0xb0,0xc0}, // 0x01e kmp_enpt_tie_next + {0x00,0xb0,0xb0,0xc0}, // 0x01f kmp_enpt_tie_prev + {0x00,0x80,0xc0,0xff}, // 0x020 kmp_enpt_dispatch + {0x40,0x00,0x00,0xc0}, // 0x021 kmp_itpt + {0x40,0x00,0x00,0xc0}, // 0x022 kmp_itpt_single + {0x60,0x00,0x40,0xc0}, // 0x023 kmp_itpt_tie_bi + {0x80,0x00,0x50,0xc0}, // 0x024 kmp_itpt_tie_next + {0xa0,0x00,0xa0,0xc0}, // 0x025 kmp_itpt_tie_prev + {0xe0,0x00,0x80,0xff}, // 0x026 kmp_itpt_dispatch + {0xff,0x80,0xff,0xff}, // 0x027 kmp_cnpt_landing + {0xff,0xa0,0xff,0xff}, // 0x028 kmp_cnpt_way + {0x80,0x80,0x00,0xff}, // 0x029 kmp_area_arrow + {0x80,0x80,0x00,0x40}, // 0x02a kmp_area_border + {0x00,0x80,0x00,0xc0}, // 0x02b kmp_poti0 + {0x00,0x92,0x49,0xc0}, // 0x02c kmp_poti1 + {0x24,0xa4,0x12,0xc0}, // 0x02d kmp_poti2 + {0x24,0xa4,0x12,0xc0}, // 0x02e kmp_poti3 + {0x70,0x70,0x70,0xff}, // 0x02f kmp_itembox + {0x40,0x40,0x40,0xff}, // 0x030 kmp_itembox_enemy + {0x40,0x10,0x10,0xff}, // 0x031 kmp_itembox_player + {0x60,0xa0,0x60,0xe0}, // 0x032 kmp_itemroute + {0x30,0x70,0x30,0xe0}, // 0x033 kmp_itemroute_enemy + {0x50,0x10,0x10,0xe0}, // 0x034 kmp_itemroute_player + {0x00,0x70,0x00,0xff}, // 0x035 kmp_kinoko_green + {0x90,0x18,0x00,0xff}, // 0x036 kmp_kinoko_red + {0x70,0x40,0x60,0xff}, // 0x037 kmp_bridge + {0x10,0x7b,0x18,0xff}, // 0x038 kmp_dokan + {0xde,0x82,0x31,0xff}, // 0x039 kmp_karehayama + {0x70,0x5e,0x36,0xff}, // 0x03a kmp_obakeblock + {0x00,0x80,0x00,0x40}, // 0x03b kmp_tree_crown + {0x58,0x32,0x10,0xff}, // 0x03c kmp_tree_trunk + {0x9c,0x69,0x21,0xff}, // 0x03d kmp_woodbox + {0xe0,0xb0,0x00,0xff}, // 0x03e kmp_coin_start + {0xff,0xd7,0x00,0xff}, // 0x03f kmp_coin_respawn + {0xff,0xb0,0x00,0xff}, // 0x040 kmp_coin_invalid + {0x40,0x40,0x80,0xff}, // 0x041 kmp_solid_object + {0x20,0x80,0x80,0xe0}, // 0x042 kmp_solid_route + {0x80,0x00,0xff,0xc0}, // 0x043 kmp_deco_object + {0x10,0x10,0x10,0xff}, // 0x044 kmp_zero_ground_black + {0xf0,0xf0,0xf0,0xff}, // 0x045 kmp_zero_ground_white + {0xff,0x20,0x20,0xff}, // 0x046 red + {0xff,0x20,0x20,0xb0}, // 0x047 red_t + {0xff,0x20,0x20,0x70}, // 0x048 red_tt + {0xff,0x60,0x10,0xff}, // 0x049 red_orange + {0xff,0x60,0x10,0xb0}, // 0x04a red_orange_t + {0xff,0x60,0x10,0x70}, // 0x04b red_orange_tt + {0xff,0xa0,0x00,0xff}, // 0x04c orange + {0xff,0xa0,0x00,0xb0}, // 0x04d orange_t + {0xff,0xa0,0x00,0x70}, // 0x04e orange_tt + {0xf8,0xc8,0x00,0xff}, // 0x04f orange_yellow + {0xf8,0xc8,0x00,0xb0}, // 0x050 orange_yellow_t + {0xf8,0xc8,0x00,0x70}, // 0x051 orange_yellow_tt + {0xf0,0xf0,0x00,0xff}, // 0x052 yellow + {0xf0,0xf0,0x00,0xb0}, // 0x053 yellow_t + {0xf0,0xf0,0x00,0x70}, // 0x054 yellow_tt + {0xa0,0xf0,0x00,0xff}, // 0x055 yellow_green + {0xa0,0xf0,0x00,0xb0}, // 0x056 yellow_green_t + {0xa0,0xf0,0x00,0x70}, // 0x057 yellow_green_tt + {0x00,0xff,0x00,0xff}, // 0x058 green + {0x00,0xff,0x00,0xb0}, // 0x059 green_t + {0x00,0xff,0x00,0x70}, // 0x05a green_tt + {0x00,0xff,0xa0,0xff}, // 0x05b green_cyan + {0x00,0xff,0xa0,0xb0}, // 0x05c green_cyan_t + {0x00,0xff,0xa0,0x70}, // 0x05d green_cyan_tt + {0x00,0xff,0xff,0xff}, // 0x05e cyan + {0x00,0xff,0xff,0xb0}, // 0x05f cyan_t + {0x00,0xff,0xff,0x70}, // 0x060 cyan_tt + {0x10,0x90,0xff,0xff}, // 0x061 cyan_blue + {0x10,0x90,0xff,0xb0}, // 0x062 cyan_blue_t + {0x10,0x90,0xff,0x70}, // 0x063 cyan_blue_tt + {0x20,0x20,0xff,0xff}, // 0x064 blue + {0x20,0x20,0xff,0xb0}, // 0x065 blue_t + {0x20,0x20,0xff,0x70}, // 0x066 blue_tt + {0x80,0x00,0xff,0xff}, // 0x067 blue_magenta + {0x80,0x00,0xff,0xb0}, // 0x068 blue_magenta_t + {0x80,0x00,0xff,0x70}, // 0x069 blue_magenta_tt + {0xff,0x00,0xff,0xff}, // 0x06a magenta + {0xff,0x00,0xff,0xb0}, // 0x06b magenta_t + {0xff,0x00,0xff,0x70}, // 0x06c magenta_tt + {0xff,0x00,0x80,0xff}, // 0x06d magenta_red + {0xff,0x00,0x80,0xb0}, // 0x06e magenta_red_t + {0xff,0x00,0x80,0x70}, // 0x06f magenta_red_tt + {0x80,0x50,0x10,0xff}, // 0x070 brown + {0x80,0x50,0x10,0xb0}, // 0x071 brown_t + {0x80,0x50,0x10,0x70}, // 0x072 brown_tt + {0xff,0xff,0xff,0xff}, // 0x073 white + {0xff,0xff,0xff,0xb0}, // 0x074 white_t + {0xff,0xff,0xff,0x70}, // 0x075 white_tt + {0xa0,0xa0,0xa0,0xff}, // 0x076 grey + {0xa0,0xa0,0xa0,0xb0}, // 0x077 grey_t + {0xa0,0xa0,0xa0,0x70}, // 0x078 grey_tt + {0x01,0x01,0x01,0xff}, // 0x079 black + {0x01,0x01,0x01,0xb0}, // 0x07a black_t + {0x01,0x01,0x01,0x70}, // 0x07b black_tt + {0xff,0xd7,0x00,0xff}, // 0x07c gold + {0xff,0xd7,0x00,0xb0}, // 0x07d gold_t + {0xff,0xd7,0x00,0x70}, // 0x07e gold_tt + {0xc0,0xc0,0xc0,0xff}, // 0x07f silver + {0xc0,0xc0,0xc0,0xb0}, // 0x080 silver_t + {0xc0,0xc0,0xc0,0x70}, // 0x081 silver_tt + {0xd8,0xb0,0x80,0xff}, // 0x082 bronze + {0xd8,0xb0,0x80,0xb0}, // 0x083 bronze_t + {0xd8,0xb0,0x80,0x70}, // 0x084 bronze_tt + {0x00,0xd0,0xff,0xff}, // 0x085 test0 + {0x00,0xd0,0xff,0xb0}, // 0x086 test0_t + {0x00,0xd0,0xff,0x70}, // 0x087 test0_tt + {0x00,0xff,0xd0,0xff}, // 0x088 test1 + {0x00,0xff,0xd0,0xb0}, // 0x089 test1_t + {0x00,0xff,0xd0,0x70}, // 0x08a test1_tt + {0x60,0xff,0x00,0xff}, // 0x08b test2 + {0x60,0xff,0x00,0xb0}, // 0x08c test2_t + {0x60,0xff,0x00,0x70}, // 0x08d test2_tt + {0xf0,0xf0,0x00,0xff}, // 0x08e test3 + {0xf0,0xf0,0x00,0xb0}, // 0x08f test3_t + {0xf0,0xf0,0x00,0x70}, // 0x090 test3_tt + {0xff,0xa0,0x00,0xff}, // 0x091 test4 + {0xff,0xa0,0x00,0xb0}, // 0x092 test4_t + {0xff,0xa0,0x00,0x70}, // 0x093 test4_tt + {0xff,0x60,0x10,0xff}, // 0x094 test5 + {0xff,0x60,0x10,0xb0}, // 0x095 test5_t + {0xff,0x60,0x10,0x70}, // 0x096 test5_tt + {0xff,0x20,0x20,0xff}, // 0x097 test6 + {0xff,0x20,0x20,0xb0}, // 0x098 test6_t + {0xff,0x20,0x20,0x70}, // 0x099 test6_tt + {0xff,0x00,0x80,0xff}, // 0x09a test7 + {0xff,0x00,0x80,0xb0}, // 0x09b test7_t + {0xff,0x00,0x80,0x70}, // 0x09c test7_tt + {0xff,0x00,0xff,0xff}, // 0x09d test8 + {0xff,0x00,0xff,0xb0}, // 0x09e test8_t + {0xff,0x00,0xff,0x70}, // 0x09f test8_tt + + {0xff,0x00,0x00,0xff}, // 0x0a0 road_1 + {0xe8,0x00,0xe8,0xff}, // 0x0a1 road_2 + {0xda,0x00,0x00,0xff}, // 0x0a2 road_3 + {0xba,0x00,0xba,0xff}, // 0x0a3 road_4 + {0xb6,0x00,0x00,0xff}, // 0x0a4 road_5 + {0x8d,0x00,0x8d,0xff}, // 0x0a5 road_6 + {0x92,0x00,0x00,0xff}, // 0x0a6 road_7 + {0x60,0x00,0x60,0xff}, // 0x0a7 road_8 + {0xff,0x00,0xff,0xff}, // 0x0a8 road_9 + {0xec,0x00,0x00,0xff}, // 0x0a9 road_10 + {0xd1,0x00,0xd1,0xff}, // 0x0aa road_11 + {0xc8,0x00,0x00,0xff}, // 0x0ab road_12 + {0xa4,0x00,0xa4,0xff}, // 0x0ac road_13 + {0xa4,0x00,0x00,0xff}, // 0x0ad road_14 + {0x76,0x00,0x76,0xff}, // 0x0ae road_15 + {0x80,0x00,0x00,0xff}, // 0x0af road_16 + {0xc0,0x60,0x60,0xff}, // 0x0b0 offroad_1 + {0xf1,0x40,0x00,0xff}, // 0x0b1 offroad_2 + {0xa4,0x4d,0x44,0xff}, // 0x0b2 offroad_3 + {0xd6,0x40,0x00,0xff}, // 0x0b3 offroad_4 + {0x89,0x3b,0x29,0xff}, // 0x0b4 offroad_5 + {0xbb,0x40,0x00,0xff}, // 0x0b5 offroad_6 + {0x6d,0x29,0x0d,0xff}, // 0x0b6 offroad_7 + {0xa0,0x40,0x00,0xff}, // 0x0b7 offroad_8 + {0xff,0x40,0x00,0xff}, // 0x0b8 offroad_9 + {0xb2,0x56,0x52,0xff}, // 0x0b9 offroad_10 + {0xe3,0x40,0x00,0xff}, // 0x0ba offroad_11 + {0x96,0x44,0x36,0xff}, // 0x0bb offroad_12 + {0xc8,0x40,0x00,0xff}, // 0x0bc offroad_13 + {0x7b,0x32,0x1b,0xff}, // 0x0bd offroad_14 + {0xad,0x40,0x00,0xff}, // 0x0be offroad_15 + {0x60,0x20,0x00,0xff}, // 0x0bf offroad_16 + {0xff,0xa0,0x30,0xff}, // 0x0c0 fast_1 + {0xe6,0x79,0x23,0xff}, // 0x0c1 fast_2 + {0xf2,0x8c,0x29,0xff}, // 0x0c2 fast_3 + {0xf8,0x96,0x2c,0xff}, // 0x0c3 fast_4 + {0xe0,0x70,0x20,0xff}, // 0x0c4 fast_5 + {0xec,0x83,0x26,0xff}, // 0x0c5 fast_6 + {0x00,0x80,0xff,0xff}, // 0x0c6 halfpipe_1 + {0x00,0x68,0xcf,0xff}, // 0x0c7 halfpipe_2 + {0x00,0x78,0xef,0xff}, // 0x0c8 halfpipe_3 + {0x00,0x60,0xc0,0xff}, // 0x0c9 halfpipe_4 + {0x00,0x70,0xdf,0xff}, // 0x0ca halfpipe_5 + {0xff,0x00,0x60,0xff}, // 0x0cb moving_1 + {0xcf,0x00,0x60,0xff}, // 0x0cc moving_2 + {0xef,0x00,0x60,0xff}, // 0x0cd moving_3 + {0xc0,0x00,0x60,0xff}, // 0x0ce moving_4 + {0xdf,0x00,0x60,0xff}, // 0x0cf moving_5 + {0x00,0x00,0xff,0xff}, // 0x0d0 water_1 + {0x00,0x00,0xa4,0xff}, // 0x0d1 water_2 + {0x00,0x00,0xda,0xff}, // 0x0d2 water_3 + {0x00,0x00,0x80,0xff}, // 0x0d3 water_4 + {0x00,0x00,0xb6,0xff}, // 0x0d4 water_5 + {0x00,0x00,0xec,0xff}, // 0x0d5 water_6 + {0x00,0x00,0x92,0xff}, // 0x0d6 water_7 + {0x00,0x00,0xc8,0xff}, // 0x0d7 water_8 + {0xff,0x80,0xff,0xff}, // 0x0d8 cannon_1 + {0x9f,0x50,0x9f,0xff}, // 0x0d9 cannon_2 + {0xdf,0x70,0xdf,0xff}, // 0x0da cannon_3 + {0x80,0x40,0x80,0xff}, // 0x0db cannon_4 + {0xbf,0x60,0xbf,0xff}, // 0x0dc cannon_5 + {0x00,0x80,0x00,0xa0}, // 0x0dd wall_1 + {0x00,0x92,0x49,0xa0}, // 0x0de wall_2 + {0x24,0xa4,0x12,0xa0}, // 0x0df wall_3 + {0x00,0xb6,0x5b,0xa0}, // 0x0e0 wall_4 + {0x49,0xc8,0x24,0xa0}, // 0x0e1 wall_5 + {0x00,0xda,0x6d,0xa0}, // 0x0e2 wall_6 + {0x6d,0xec,0x36,0xa0}, // 0x0e3 wall_7 + {0x00,0xff,0x80,0xa0}, // 0x0e4 wall_8 + {0x00,0x80,0x40,0xa0}, // 0x0e5 wall_9 + {0x12,0x92,0x09,0xa0}, // 0x0e6 wall_10 + {0x00,0xa4,0x52,0xa0}, // 0x0e7 wall_11 + {0x36,0xb6,0x1b,0xa0}, // 0x0e8 wall_12 + {0x00,0xc8,0x64,0xa0}, // 0x0e9 wall_13 + {0x5b,0xda,0x2d,0xa0}, // 0x0ea wall_14 + {0x00,0xec,0x76,0xa0}, // 0x0eb wall_15 + {0x80,0xff,0x40,0xa0}, // 0x0ec wall_16 + {0x00,0x00,0x00,0xff}, // 0x0ed fall_1 + {0x30,0x10,0x10,0xff}, // 0x0ee fall_2 + {0x20,0x20,0x20,0xff}, // 0x0ef fall_3 + {0x50,0x30,0x30,0xff}, // 0x0f0 fall_4 + {0x40,0x40,0x40,0xff}, // 0x0f1 fall_5 + {0x10,0x10,0x10,0xff}, // 0x0f2 fall_6 + {0x40,0x20,0x20,0xff}, // 0x0f3 fall_7 + {0x30,0x30,0x30,0xff}, // 0x0f4 fall_8 + {0x60,0x40,0x40,0xff}, // 0x0f5 fall_9 + {0x20,0x00,0x00,0xff}, // 0x0f6 fall_10 + {0x00,0xff,0xff,0xa0}, // 0x0f7 effect_1 + {0x15,0xaa,0xaa,0xa0}, // 0x0f8 effect_2 + {0x07,0xe2,0xe2,0xa0}, // 0x0f9 effect_3 + {0x1c,0x8e,0x8e,0xa0}, // 0x0fa effect_4 + {0x0e,0xc6,0xc6,0xa0}, // 0x0fb effect_5 + {0x03,0xf0,0xf0,0xa0}, // 0x0fc effect_6 + {0x18,0x9c,0x9c,0xa0}, // 0x0fd effect_7 + {0x0a,0xd4,0xd4,0xa0}, // 0x0fe effect_8 + {0x20,0x80,0x80,0xa0}, // 0x0ff effect_9 + {0x11,0xb8,0xb8,0xa0}, // 0x100 effect_10 + {0x30,0xff,0x80,0xff}, // 0x101 item_1 + {0x1e,0xcf,0x68,0xff}, // 0x102 item_2 + {0x2a,0xef,0x78,0xff}, // 0x103 item_3 + {0x18,0xc0,0x60,0xff}, // 0x104 item_4 + {0x24,0xdf,0x70,0xff}, // 0x105 item_5 + {0xff,0xff,0xff,0xa0}, // 0x106 unknown_1 + {0xd0,0xd0,0xef,0xa0}, // 0x107 unknown_2 + {0xdf,0xdf,0xdf,0xa0}, // 0x108 unknown_3 + {0xb0,0xb0,0xcf,0xa0}, // 0x109 unknown_4 + {0xbf,0xbf,0xbf,0xa0}, // 0x10a unknown_5 + {0xef,0xef,0xef,0xa0}, // 0x10b unknown_6 + {0xc0,0xc0,0xdf,0xa0}, // 0x10c unknown_7 + {0xcf,0xcf,0xcf,0xa0}, // 0x10d unknown_8 + {0xa0,0xa0,0xbf,0xa0}, // 0x10e unknown_9 + {0xe0,0xe0,0xff,0xa0}, // 0x10f unknown_10 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// kcl_type[] //////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const kcl_type_t kcl_type[N_KCL_TYPE+1] = +{ + { 0x00, 0, "Road", + KCLT_C_ROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x01, 1, "Slippery Road 1", + KCLT_C_OFFROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x02, 1, "Weak Off-road", + KCLT_C_OFFROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x03, 1, "Off-road", + KCLT_C_OFFROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x04, 1, "Heavy Off-road", + KCLT_C_OFFROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x05, 1, "Slippery Road 2", + KCLT_C_OFFROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x06, 2, "Boost Pad", + KCLT_C_FAST | KCLT_BORDER | KCLT_DRIVE | KCLT_BOOST }, + + { 0x07, 2, "Boost Ramp", + KCLT_C_FAST | KCLT_BORDER | KCLT_DRIVE | KCLT_BOOST | KCLT_TRICK }, + + { 0x08, 2, "Jump Pad", + KCLT_C_FAST | KCLT_BORDER | KCLT_DRIVE | KCLT_TRICK }, + + { 0x09, 10, "Item Road", + KCLT_C_ITEM | KCLT_ITEM }, + + { 0x0a, 8, "Solid Fall", + KCLT_C_FALL }, + + { 0x0b, 5, "Moving Water", + KCLT_C_WATER | KCLT_BORDER | KCLT_DRIVE }, + + { 0x0c, 7, "Wall", + KCLT_C_WALL | KCLT_BORDER }, + + { 0x0d, 7, "Invisible Wall", + KCLT_C_WALL | KCLT_BORDER | KCLT_ITEM }, + + { 0x0e, 10, "Item Wall", + KCLT_C_ITEM | KCLT_ITEM }, + + { 0x0f, 7, "Wall", + KCLT_C_WALL | KCLT_BORDER }, + + { 0x10, 8, "Fall Boundary", + KCLT_C_FALL }, + + { 0x11, 6, "Cannon Activator", + KCLT_C_CANNON }, + + { 0x12, 9, "Recalculation", + KCLT_C_EFFECT }, + + { 0x13, 3, "Half-pipe Ramp", + KCLT_C_HALFPIPE | KCLT_BORDER | KCLT_DRIVE }, + + { 0x14, 7, "Wall", + KCLT_C_WALL | KCLT_BORDER }, + + { 0x15, 4, "Moving Road", + KCLT_C_MOVING | KCLT_BORDER | KCLT_DRIVE }, + + { 0x16, 0, "Gravity Road", + KCLT_C_ROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x17, 0, "Road", + KCLT_C_ROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x18, 9, "Sound Trigger", + KCLT_C_EFFECT | KCLT_SOUND }, + + { 0x19, 11, "Unknown", + KCLT_C_UNKNOWN | KCLT_UNKNOWN }, + + { 0x1a, 9, "Effect Trigger", + KCLT_C_EFFECT }, + + { 0x1b, 11, "Unknown", + KCLT_C_UNKNOWN | KCLT_UNKNOWN }, + + { 0x1c, 11, "Unknown", + KCLT_C_UNKNOWN | KCLT_UNKNOWN }, + + { 0x1d, 0, "Moving Road", + KCLT_C_ROAD | KCLT_BORDER | KCLT_DRIVE }, + + { 0x1e, 7, "Special Wall", + KCLT_C_WALL | KCLT_BORDER }, + + { 0x1f, 7, "Wall", + KCLT_C_WALL | KCLT_BORDER }, + + {0,0,0,0} +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////// GetFlagName() ////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFlagName ( uint kcl_flag ) +{ + static ccp tab[] = + { + "UNKNOWN", + "CKPT_WARNING", + "CKPT_WARNING_T", + "CKPT_WARNING_TT", + "CKPT_LC", + "CKPT_LC_T", + "CKPT_LC_TT", + "CKPT_MD", + "CKPT_MD_T", + "CKPT_MD_TT", + "CKPT_MD5", + "CKPT_MD5_T", + "CKPT_MD5_TT", + "CKPT_ETC", + "CKPT_ETC_T", + "CKPT_ETC_TT", + "CKPT_TIE_LT", + "CKPT_TIE_RT", + "CKPT_TIE_GROUP_LT", + "CKPT_TIE_GROUP_RT", + "CKPT_TIE_NEXT_LT", + "CKPT_TIE_NEXT_RT", + "CKPT_JGPT", + "JGPT", + "JGPT_AREA", + "KTPT", + "KTPT_AREA", + "ENPT", + "ENPT_SINGLE", + "ENPT_TIE_BI", + "ENPT_TIE_NEXT", + "ENPT_TIE_PREV", + "ENPT_DISPATCH", + "ITPT", + "ITPT_SINGLE", + "ITPT_TIE_BI", + "ITPT_TIE_NEXT", + "ITPT_TIE_PREV", + "ITPT_DISPATCH", + "CNPT_LANDING", + "CNPT_WAY", + "AREA_ARROW", + "AREA_BORDER", + "POTI0", + "POTI1", + "POTI2", + "POTI3", + "ITEMBOX", + "ITEMBOX_ENEMY", + "ITEMBOX_PLAYER", + "ITEMROUTE", + "ITEMROUTE_ENEMY", + "ITEMROUTE_PLAYER", + "KINOKO_GREEN", + "KINOKO_RED", + "BRIDGE", + "DOKAN", + "KAREHAYAMA", + "OBAKEBLOCK", + "TREE_CROWN", + "TREE_TRUNK", + "WOODBOX", + "COIN_START", + "COIN_RESPAWN", + "COIN_INVALID", + "SOLIDOBJ", + "SOLID_ROUTE", + "DECORATION", + "ZERO_GROUND_BLACK", + "ZERO_GROUND_WHITE", + "RED", + "RED_T", + "RED_TT", + "RED_ORANGE", + "RED_ORANGE_T", + "RED_ORANGE_TT", + "ORANGE", + "ORANGE_T", + "ORANGE_TT", + "ORANGE_YELLOW", + "ORANGE_YELLOW_T", + "ORANGE_YELLOW_TT", + "YELLOW", + "YELLOW_T", + "YELLOW_TT", + "YELLOW_GREEN", + "YELLOW_GREEN_T", + "YELLOW_GREEN_TT", + "GREEN", + "GREEN_T", + "GREEN_TT", + "GREEN_CYAN", + "GREEN_CYAN_T", + "GREEN_CYAN_TT", + "CYAN", + "CYAN_T", + "CYAN_TT", + "CYAN_BLUE", + "CYAN_BLUE_T", + "CYAN_BLUE_TT", + "BLUE", + "BLUE_T", + "BLUE_TT", + "BLUE_MAGENTA", + "BLUE_MAGENTA_T", + "BLUE_MAGENTA_TT", + "MAGENTA", + "MAGENTA_T", + "MAGENTA_TT", + "MAGENTA_RED", + "MAGENTA_RED_T", + "MAGENTA_RED_TT", + "BROWN", + "BROWN_T", + "BROWN_TT", + "WHITE", + "WHITE_T", + "WHITE_TT", + "GREY", + "GREY_T", + "GREY_TT", + "BLACK", + "BLACK_T", + "BLACK_TT", + "GOLD", + "GOLD_T", + "GOLD_TT", + "SILVER", + "SILVER_T", + "SILVER_TT", + "BRONZE", + "BRONZE_T", + "BRONZE_TT", + "TEST0", + "TEST0_T", + "TEST0_TT", + "TEST1", + "TEST1_T", + "TEST1_TT", + "TEST2", + "TEST2_T", + "TEST2_TT", + "TEST3", + "TEST3_T", + "TEST3_TT", + "TEST4", + "TEST4_T", + "TEST4_TT", + "TEST5", + "TEST5_T", + "TEST5_TT", + "TEST6", + "TEST6_T", + "TEST6_TT", + "TEST7", + "TEST7_T", + "TEST7_TT", + "TEST8", + "TEST8_T", + "TEST8_TT", + }; + + return tab[ kcl_flag > N_KCL_FLAG && kcl_flag < NN_KCL_FLAG + ? kcl_flag - N_KCL_FLAG : 0 ]; +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// GetFlagGroupName() //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFlagGroupName ( uint kcl_flag ) +{ + static ccp tab[] = + { + "unknown", + "kmp_ckpt_warning", + "kmp_ckpt_warning_t", + "kmp_ckpt_warning_tt", + "kmp_ckpt_lap", + "kmp_ckpt_lap_t", + "kmp_ckpt_lap_tt", + "kmp_ckpt_mandatory", + "kmp_ckpt_mandatory_t", + "kmp_ckpt_mandatory_tt", + "kmp_ckpt_mandatory5", + "kmp_ckpt_mandatory5_t", + "kmp_ckpt_mandatory5_tt", + "kmp_ckpt_etc", + "kmp_ckpt_etc_t", + "kmp_ckpt_etc_tt", + "kmp_ckpt_tie_lt", + "kmp_ckpt_tie_rt", + "kmp_ckpt_tie_group_lt", + "kmp_ckpt_tie_group_rt", + "kmp_ckpt_tie_next_lt", + "kmp_ckpt_tie_next_rt", + "kmp_ckpt_jgpt", + "kmp_jgpt", + "kmp_jgpt_area", + "kmp_ktpt", + "kmp_ktpt_area", + "kmp_enpt", + "kmp_enpt_single", + "kmp_enpt_tie_bi", + "kmp_enpt_tie_next", + "kmp_enpt_tie_prev", + "kmp_enpt_dispatch", + "kmp_itpt", + "kmp_itpt_single", + "kmp_itpt_tie_bi", + "kmp_itpt_tie_next", + "kmp_itpt_tie_prev", + "kmp_itpt_dispatch", + "kmp_cnpt_landing", + "kmp_cnpt_way", + "kmp_area_arrow", + "kmp_area_border", + "kmp_poti0", + "kmp_poti1", + "kmp_poti2", + "kmp_poti3", + "kmp_itembox", + "kmp_itembox_enemy", + "kmp_itembox_player", + "kmp_itemroute", + "kmp_itemroute_enemy", + "kmp_itemroute_player", + "kmp_kinoko_green", + "kmp_kinoko_red", + "kmp_bridge", + "kmp_dokan", + "kmp_karehayama", + "kmp_obakeblock", + "kmp_tree_crown", + "kmp_tree_trunk", + "kmp_woodbox", + "kmp_coin_start", + "kmp_coin_respawn", + "kmp_coin_invalid", + "kmp_solid_object", + "kmp_solid_route", + "kmp_deco_object", + "kmp_zero_ground_black", + "kmp_zero_ground_white", + "red", + "red_t", + "red_tt", + "red_orange", + "red_orange_t", + "red_orange_tt", + "orange", + "orange_t", + "orange_tt", + "orange_yellow", + "orange_yellow_t", + "orange_yellow_tt", + "yellow", + "yellow_t", + "yellow_tt", + "yellow_green", + "yellow_green_t", + "yellow_green_tt", + "green", + "green_t", + "green_tt", + "green_cyan", + "green_cyan_t", + "green_cyan_tt", + "cyan", + "cyan_t", + "cyan_tt", + "cyan_blue", + "cyan_blue_t", + "cyan_blue_tt", + "blue", + "blue_t", + "blue_tt", + "blue_magenta", + "blue_magenta_t", + "blue_magenta_tt", + "magenta", + "magenta_t", + "magenta_tt", + "magenta_red", + "magenta_red_t", + "magenta_red_tt", + "brown", + "brown_t", + "brown_tt", + "white", + "white_t", + "white_tt", + "grey", + "grey_t", + "grey_tt", + "black", + "black_t", + "black_tt", + "gold", + "gold_t", + "gold_tt", + "silver", + "silver_t", + "silver_tt", + "bronze", + "bronze_t", + "bronze_tt", + "test0", + "test0_t", + "test0_tt", + "test1", + "test1_t", + "test1_tt", + "test2", + "test2_t", + "test2_tt", + "test3", + "test3_t", + "test3_tt", + "test4", + "test4_t", + "test4_tt", + "test5", + "test5_t", + "test5_tt", + "test6", + "test6_t", + "test6_tt", + "test7", + "test7_t", + "test7_tt", + "test8", + "test8_t", + "test8_tt", + }; + + return tab[ kcl_flag > N_KCL_FLAG && kcl_flag < NN_KCL_FLAG + ? kcl_flag - N_KCL_FLAG : 0 ]; +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// kcl_color_key_tab[] /////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t kcl_color_key_tab[] = +{ + { KCL_FLAG_BLACK, "BLACK", 0, 1 }, + { KCL_FLAG_BLUE, "BLUE", 0, 1 }, + { KCL_FLAG_BLUE_MAGENTA, "BLUE-MAGENTA", "BLUEMAGENTA", 1 }, + { KCL_FLAG_BLUE_MAGENTA, "MAGENTA-BLUE", "MAGENTABLUE", 1 }, + { KCL_FLAG_BRONZE, "BRONZE", 0, 1 }, + { KCL_FLAG_BROWN, "BROWN", 0, 1 }, + { KCL_FLAG_CYAN, "CYAN", 0, 1 }, + { KCL_FLAG_CYAN_BLUE, "CYAN-BLUE", "CYANBLUE", 1 }, + { KCL_FLAG_CYAN_BLUE, "BLUE-CYAN", "BLUECYAN", 1 }, + { KCL_FLAG_GOLD, "GOLD", 0, 1 }, + { KCL_FLAG_GREEN, "GREEN", 0, 1 }, + { KCL_FLAG_GREEN_CYAN, "GREEN-CYAN", "GREENCYAN", 1 }, + { KCL_FLAG_GREEN_CYAN, "CYAN-GREEN", "CYANGREEN", 1 }, + { KCL_FLAG_GREY, "GREY", 0, 1 }, + { KCL_FLAG_MAGENTA, "MAGENTA", 0, 1 }, + { KCL_FLAG_MAGENTA_RED, "MAGENTA-RED", "MAGENTARED", 1 }, + { KCL_FLAG_MAGENTA_RED, "RED-MAGENTA", "REDMAGENTA", 1 }, + { KCL_FLAG_ORANGE, "ORANGE", 0, 1 }, + { KCL_FLAG_ORANGE_YELLOW, "ORANGE-YELLOW", "ORANGEYELLOW", 1 }, + { KCL_FLAG_ORANGE_YELLOW, "YELLOW-ORANGE", "YELLOWORANGE", 1 }, + { KCL_FLAG_RED, "RED", 0, 1 }, + { KCL_FLAG_RED_ORANGE, "RED-ORANGE", "REDORANGE", 1 }, + { KCL_FLAG_RED_ORANGE, "ORANGE-RED", "ORANGERED", 1 }, + { KCL_FLAG_SILVER, "SILVER", 0, 1 }, + { KCL_FLAG_TEST0, "TEST0", 0, 1 }, + { KCL_FLAG_TEST1, "TEST1", 0, 1 }, + { KCL_FLAG_TEST2, "TEST2", 0, 1 }, + { KCL_FLAG_TEST3, "TEST3", 0, 1 }, + { KCL_FLAG_TEST4, "TEST4", 0, 1 }, + { KCL_FLAG_TEST5, "TEST5", 0, 1 }, + { KCL_FLAG_TEST6, "TEST6", 0, 1 }, + { KCL_FLAG_TEST7, "TEST7", 0, 1 }, + { KCL_FLAG_TEST8, "TEST8", 0, 1 }, + { KCL_FLAG_WHITE, "WHITE", 0, 1 }, + { KCL_FLAG_YELLOW, "YELLOW", 0, 1 }, + { KCL_FLAG_YELLOW_GREEN, "YELLOW-GREEN", "YELLOWGREEN", 1 }, + { KCL_FLAG_YELLOW_GREEN, "GREEN-YELLOW", "GREENYELLOW", 1 }, + {0,0,0,0} +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/db-kcl.h b/project/src/db-kcl.h new file mode 100644 index 0000000..ff8f7ff --- /dev/null +++ b/project/src/db-kcl.h @@ -0,0 +1,310 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_DB_KCL_H +#define SZS_DB_KCL_H 1 + +#include "types.h" + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////// enum kcl_attrib_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum kcl_attrib_t +{ + + KCLT_C_ROAD = 0x00001, // normal road + KCLT_C_OFFROAD = 0x00002, // offroad + KCLT_C_FAST = 0x00004, // boost and trick + KCLT_C_HALFPIPE = 0x00008, // halfpipe + KCLT_C_MOVING = 0x00010, // moving terrain + KCLT_C_WATER = 0x00020, // shallow water + KCLT_C_CANNON = 0x00040, // cannon activator + KCLT_C_WALL = 0x00080, // wall + KCLT_C_FALL = 0x00100, // fall or out of bounds + KCLT_C_EFFECT = 0x00200, // sound trigger and other effects + KCLT_C_ITEM = 0x00400, // for items only + KCLT_C_UNKNOWN = 0x00800, // unknown type + KCLT_M_CLASS = 0x00fff, // mask of classes + + KCLT_BORDER = 0x01000, // road or wall + KCLT_DRIVE = 0x02000, // is driveable + KCLT_BOOST = 0x04000, // boost + KCLT_TRICK = 0x08000, // trickable + KCLT_SOUND = 0x10000, // sound effect + KCLT_ITEM = 0x20000, // special for items + KCLT_UNKNOWN = 0x40000, // unknown + KCLT_M_FEATURES = 0x7f000, // mask of base features + + KCLT_NONE = 0x00000, // none set + KCLT_M_ALL = 0x7ffff // all flags of above + +} kcl_attrib_t; + +/////////////////////////////////////////////////////////////////////////////// + +#define N_KCL_CLASS 12 +#define N_KCL_TYPE 0x20 +#define N_KCL_ATTRIB 19 +#define N_KCL_FLAG 0x10000 +#define N_KCL_AUTO_FLAGS 200 +#define N_KCL_USER_FLAGS 200 +#define N_KCL_COLORS 272 + +#define IS_KCL_TYPE(f,t) ( (f) < N_KCL_FLAG && ((f)&0x1f) == (t) ) +#define GET_KCL_TYPE(f) ( (f) < N_KCL_FLAG ? (f)&0x1f : -1 ) + +enum +{ + KCL_FLAG_UNKNOWN = N_KCL_FLAG, + + KCL_FLAG_CKPT_WARNING, + KCL_FLAG_CKPT_WARNING_T, + KCL_FLAG_CKPT_WARNING_TT, + KCL_FLAG_CKPT_LC, + KCL_FLAG_CKPT_LC_T, + KCL_FLAG_CKPT_LC_TT, + KCL_FLAG_CKPT_MD, + KCL_FLAG_CKPT_MD_T, + KCL_FLAG_CKPT_MD_TT, + KCL_FLAG_CKPT_MD5, + KCL_FLAG_CKPT_MD5_T, + KCL_FLAG_CKPT_MD5_TT, + KCL_FLAG_CKPT_ETC, + KCL_FLAG_CKPT_ETC_T, + KCL_FLAG_CKPT_ETC_TT, + KCL_FLAG_CKPT_TIE_LT, + KCL_FLAG_CKPT_TIE_RT, + KCL_FLAG_CKPT_TIE_GROUP_LT, + KCL_FLAG_CKPT_TIE_GROUP_RT, + KCL_FLAG_CKPT_TIE_NEXT_LT, + KCL_FLAG_CKPT_TIE_NEXT_RT, + KCL_FLAG_CKPT_JGPT, + KCL_FLAG_JGPT, + KCL_FLAG_JGPT_AREA, + KCL_FLAG_KTPT, + KCL_FLAG_KTPT_AREA, + KCL_FLAG_ENPT, + KCL_FLAG_ENPT_SINGLE, + KCL_FLAG_ENPT_TIE_BI, + KCL_FLAG_ENPT_TIE_NEXT, + KCL_FLAG_ENPT_TIE_PREV, + KCL_FLAG_ENPT_DISPATCH, + KCL_FLAG_ITPT, + KCL_FLAG_ITPT_SINGLE, + KCL_FLAG_ITPT_TIE_BI, + KCL_FLAG_ITPT_TIE_NEXT, + KCL_FLAG_ITPT_TIE_PREV, + KCL_FLAG_ITPT_DISPATCH, + KCL_FLAG_CNPT_LANDING, + KCL_FLAG_CNPT_WAY, + KCL_FLAG_AREA_ARROW, + KCL_FLAG_AREA_BORDER, + KCL_FLAG_POTI0, + KCL_FLAG_POTI1, + KCL_FLAG_POTI2, + KCL_FLAG_POTI3, + KCL_FLAG_ITEMBOX, + KCL_FLAG_ITEMBOX_ENEMY, + KCL_FLAG_ITEMBOX_PLAYER, + KCL_FLAG_ITEMROUTE, + KCL_FLAG_ITEMROUTE_ENEMY, + KCL_FLAG_ITEMROUTE_PLAYER, + KCL_FLAG_KINOKO_GREEN, + KCL_FLAG_KINOKO_RED, + KCL_FLAG_BRIDGE, + KCL_FLAG_DOKAN, + KCL_FLAG_KAREHAYAMA, + KCL_FLAG_OBAKEBLOCK, + KCL_FLAG_TREE_CROWN, + KCL_FLAG_TREE_TRUNK, + KCL_FLAG_WOODBOX, + KCL_FLAG_COIN_START, + KCL_FLAG_COIN_RESPAWN, + KCL_FLAG_COIN_INVALID, + KCL_FLAG_SOLIDOBJ, + KCL_FLAG_SOLID_ROUTE, + KCL_FLAG_DECORATION, + KCL_FLAG_ZERO_GROUND_BLACK, + KCL_FLAG_ZERO_GROUND_WHITE, + KCL_FLAG_RED, + KCL_FLAG_RED_T, + KCL_FLAG_RED_TT, + KCL_FLAG_RED_ORANGE, + KCL_FLAG_RED_ORANGE_T, + KCL_FLAG_RED_ORANGE_TT, + KCL_FLAG_ORANGE, + KCL_FLAG_ORANGE_T, + KCL_FLAG_ORANGE_TT, + KCL_FLAG_ORANGE_YELLOW, + KCL_FLAG_ORANGE_YELLOW_T, + KCL_FLAG_ORANGE_YELLOW_TT, + KCL_FLAG_YELLOW, + KCL_FLAG_YELLOW_T, + KCL_FLAG_YELLOW_TT, + KCL_FLAG_YELLOW_GREEN, + KCL_FLAG_YELLOW_GREEN_T, + KCL_FLAG_YELLOW_GREEN_TT, + KCL_FLAG_GREEN, + KCL_FLAG_GREEN_T, + KCL_FLAG_GREEN_TT, + KCL_FLAG_GREEN_CYAN, + KCL_FLAG_GREEN_CYAN_T, + KCL_FLAG_GREEN_CYAN_TT, + KCL_FLAG_CYAN, + KCL_FLAG_CYAN_T, + KCL_FLAG_CYAN_TT, + KCL_FLAG_CYAN_BLUE, + KCL_FLAG_CYAN_BLUE_T, + KCL_FLAG_CYAN_BLUE_TT, + KCL_FLAG_BLUE, + KCL_FLAG_BLUE_T, + KCL_FLAG_BLUE_TT, + KCL_FLAG_BLUE_MAGENTA, + KCL_FLAG_BLUE_MAGENTA_T, + KCL_FLAG_BLUE_MAGENTA_TT, + KCL_FLAG_MAGENTA, + KCL_FLAG_MAGENTA_T, + KCL_FLAG_MAGENTA_TT, + KCL_FLAG_MAGENTA_RED, + KCL_FLAG_MAGENTA_RED_T, + KCL_FLAG_MAGENTA_RED_TT, + KCL_FLAG_BROWN, + KCL_FLAG_BROWN_T, + KCL_FLAG_BROWN_TT, + KCL_FLAG_WHITE, + KCL_FLAG_WHITE_T, + KCL_FLAG_WHITE_TT, + KCL_FLAG_GREY, + KCL_FLAG_GREY_T, + KCL_FLAG_GREY_TT, + KCL_FLAG_BLACK, + KCL_FLAG_BLACK_T, + KCL_FLAG_BLACK_TT, + KCL_FLAG_GOLD, + KCL_FLAG_GOLD_T, + KCL_FLAG_GOLD_TT, + KCL_FLAG_SILVER, + KCL_FLAG_SILVER_T, + KCL_FLAG_SILVER_TT, + KCL_FLAG_BRONZE, + KCL_FLAG_BRONZE_T, + KCL_FLAG_BRONZE_TT, + KCL_FLAG_TEST0, + KCL_FLAG_TEST0_T, + KCL_FLAG_TEST0_TT, + KCL_FLAG_TEST1, + KCL_FLAG_TEST1_T, + KCL_FLAG_TEST1_TT, + KCL_FLAG_TEST2, + KCL_FLAG_TEST2_T, + KCL_FLAG_TEST2_TT, + KCL_FLAG_TEST3, + KCL_FLAG_TEST3_T, + KCL_FLAG_TEST3_TT, + KCL_FLAG_TEST4, + KCL_FLAG_TEST4_T, + KCL_FLAG_TEST4_TT, + KCL_FLAG_TEST5, + KCL_FLAG_TEST5_T, + KCL_FLAG_TEST5_TT, + KCL_FLAG_TEST6, + KCL_FLAG_TEST6_T, + KCL_FLAG_TEST6_TT, + KCL_FLAG_TEST7, + KCL_FLAG_TEST7_T, + KCL_FLAG_TEST7_TT, + KCL_FLAG_TEST8, + KCL_FLAG_TEST8_T, + KCL_FLAG_TEST8_TT, + + NN_KCL_FLAG, + + KCL_FLAG_AUTO_0 = NN_KCL_FLAG, + KCL_FLAG_AUTO_MAX = KCL_FLAG_AUTO_0 + N_KCL_AUTO_FLAGS - 1, + KCL_FLAG_USER_0 = KCL_FLAG_AUTO_MAX + 1, + KCL_FLAG_USER_MAX = KCL_FLAG_USER_0 + N_KCL_USER_FLAGS - 1, + NNN_KCL_FLAG +}; + +ccp GetFlagName ( uint kcl_flag ); +ccp GetFlagGroupName ( uint kcl_flag ); + +static inline bool IsAutoFlagKCL ( uint flag ) + { return flag >= KCL_FLAG_AUTO_0 && flag <= KCL_FLAG_AUTO_MAX; } + +static inline bool IsUserFlagKCL ( uint flag ) + { return flag >= KCL_FLAG_USER_0 && flag <= KCL_FLAG_USER_MAX; } + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////// struct kcl_attrib_name_t ///////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kcl_attrib_name_t +{ + kcl_attrib_t attrib; // attribute + ccp name; // name of attribute + +} kcl_attrib_name_t; + +extern const kcl_attrib_name_t kcl_attrib_name[N_KCL_ATTRIB+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct kcl_class_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kcl_class_t +{ + u16 cls; // class id, index to self + u16 ncol; // number of colors in mtl + u16 color_index; // first index into kcl_colors[] + ccp name; // short name + ccp info; // description + kcl_attrib_t attrib; // attributes + +} kcl_class_t; + +extern const kcl_class_t kcl_class[N_KCL_CLASS+1]; +extern const u8 kcl_color[N_KCL_COLORS][4]; + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////// struct kcl_type_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kcl_type_t +{ + u16 type; // type id, index to self + u16 cls; // class index + ccp info; // description + kcl_attrib_t attrib; // attributes + +} kcl_type_t; + +extern const kcl_type_t kcl_type[N_KCL_TYPE+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// kcl_color_key_tab[] /////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const KeywordTab_t kcl_color_key_tab[]; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_DB_KCL_H diff --git a/project/src/db-mkw.c b/project/src/db-mkw.c new file mode 100644 index 0000000..7c7c519 --- /dev/null +++ b/project/src/db-mkw.c @@ -0,0 +1,917 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + + +#include "db-mkw.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct TrackInfo_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const TrackInfo_t InvalidTrack = { 0, 99, 99, 99, 0x00, "-", "?", "?", "-", "-" }; + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// tracks ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// track positions + +const u32 track_pos_default[32] = +{ + 8, 1, 2, 4, 0, 5, 6, 7, 9,15,11, 3, 14,10,12,13, + 16,20,25,26, 27,31,23,18, 21,30,29,17, 24,22,19,28 +}; + +u32 track_pos[32] = +{ + 8, 1, 2, 4, 0, 5, 6, 7, 9,15,11, 3, 14,10,12,13, + 16,20,25,26, 27,31,23,18, 21,30,29,17, 24,22,19,28 +}; + +u32 track_pos_r[32] = +{ + 4, 1, 2,11, 3, 5, 6, 7, 0, 8,13,10, 14,15,12, 9, + 16,27,23,30, 17,24,29,22, 28,18,19,20, 31,26,25,21 +}; + +u32 track_pos_swap[32] = +{ + 8, 1, 2, 4, 16,20,25,26, 0, 5, 6, 7, 27,31,23,18, + 9,15,11, 3, 21,30,29,17, 14,10,12,13, 24,22,19,28, +}; + +//----------------------------------------------------------------------------- +// track info + +const TrackInfo_t track_info[32] = +{ + { + 0x2490, 0, 4, 21, 0x7d, // bmg, track, index, slot, music_id + "MC", + "Mario Circuit", + "Marios Piste", + "castle_course", + "n_Circuit32_n", + "n_Circuit32_f" + }, + { + 0x2491, 1, 1, 12, 0x77, // bmg, track, index, slot, music_id + "MMM", + "Moo Moo Meadows", + "Kuhmuh-Weide", + "farm_course", + "n_Farm_n", + "n_Farm_F" + }, + { + 0x2492, 2, 2, 13, 0x79, // bmg, track, index, slot, music_id + "MG", + "Mushroom Gorge", + "Pilz-Schlucht", + "kinoko_course", + "n_Kinoko_n", + "n_Kinoko_F" + }, + { + 0x2493, 3, 11, 34, 0x8b, // bmg, track, index, slot, music_id + "GV", + "Grumble Volcano", + "Vulkangrollen", + "volcano_course", + "n_Volcano32_n", + "n_Volcano32_f" + }, + { + 0x2494, 4, 3, 14, 0x7b, // bmg, track, index, slot, music_id + "TF", + "Toad's Factory", + "Toads Fabrik", + "factory_course", + "STRM_N_FACTORY_N", + "STRM_N_FACTORY_F" + }, + { + 0x2495, 5, 5, 22, 0x7f, // bmg, track, index, slot, music_id + "CM", + "Coconut Mall", + "Kokos-Promenade", + "shopping_course", + "n_Shopping32_n", + "n_Shopping32_f" + }, + { + 0x2496, 6, 6, 23, 0x81, // bmg, track, index, slot, music_id + "DKS", + "DK Summit", + "DK Skikane", + "boardcross_course", + "n_Snowboard32_n", + "n_Snowboard32_F" + }, + { + 0x2497, 7, 7, 24, 0x83, // bmg, track, index, slot, music_id + "WGM", + "Wario's Gold Mine", + "Warios Goldmine", + "truck_course", + "STRM_N_TRUCK_N", + "STRM_N_TRUCK_F" + }, + { + 0x2498, 8, 0, 11, 0x7d, // bmg, track, index, slot, music_id + "LC", + "Luigi Circuit", + "Luigis Piste", + "beginner_course", + "n_Circuit32_n", + "n_Circuit32_f" + }, + { + 0x2499, 9, 8, 31, 0x87, // bmg, track, index, slot, music_id + "DC", + "Daisy Circuit", + "Daisys Piste", + "senior_course", + "n_Daisy32_n", + "n_Daisy32_f" + }, + { + 0x249a, 10, 13, 42, 0x8d, // bmg, track, index, slot, music_id + "MH", + "Moonview Highway", + "Mondblickstraße", + "ridgehighway_course", + "STRM_N_RIDGEHIGHWAY_N", + "STRM_N_RIDGEHIGHWAY_F" + }, + { + 0x249b, 11, 10, 33, 0x8f, // bmg, track, index, slot, music_id + "MT", + "Maple Treeway", + "Blätterwald", + "treehouse_course", + "n_maple_n", + "n_maple_F" + }, + { + 0x249c, 12, 14, 43, 0x91, // bmg, track, index, slot, music_id + "BC", + "Bowser's Castle", + "Bowsers Festung", + "koopa_course", + "STRM_N_KOOPA_N", + "STRM_N_KOOPA_F" + }, + { + 0x249d, 13, 15, 44, 0x93, // bmg, track, index, slot, music_id + "RR", + "Rainbow Road", + "Regenbogen-Boulevard", + "rainbow_course", + "n_Rainbow32_n", + "n_Rainbow32_f" + }, + { + 0x249e, 14, 12, 41, 0x89, // bmg, track, index, slot, music_id + "DDR", + "Dry Dry Ruins", + "Staubtrockene Ruinen", + "desert_course", + "STRM_N_DESERT_N", + "STRM_N_DESERT_F" + }, + { + 0x249f, 15, 9, 32, 0x85, // bmg, track, index, slot, music_id + "KC", + "Koopa Cape", + "Koopa-Kap", + "water_course", + "STRM_N_WATER_N", + "STRM_N_WATER_F" + }, + { + 0x24a0, 16, 16, 51, 0xa5, // bmg, track, index, slot, music_id + "gPB", + "GCN Peach Beach", + "GCN Peach Beach", + "old_peach_gc", + "r_GC_Beach32_n", + "r_GC_Beach32_f" + }, + { + 0x24a1, 17, 27, 74, 0xa7, // bmg, track, index, slot, music_id + "gMC", + "GCN Mario Circuit", + "GCN Marios Piste", + "old_mario_gc", + "r_GC_Circuit32_n", + "r_GC_Circuit32_f" + }, + { + 0x24a2, 18, 23, 64, 0xa9, // bmg, track, index, slot, music_id + "gWS", + "GCN Waluigi Stadium", + "GCN Waluigi-Arena", + "old_waluigi_gc", + "r_GC_Stadium32_n", + "r_GC_Stadium32_f" + }, + { + 0x24a3, 19, 30, 83, 0xab, // bmg, track, index, slot, music_id + "gDKM", + "GCN DK Mountain", + "GCN DK Bergland", + "old_donkey_gc", + "r_GC_Mountain32_n", + "r_GC_Mountain32_f" + }, + { + 0x24a4, 20, 17, 52, 0xad, // bmg, track, index, slot, music_id + "dYF", + "DS Yoshi Falls", + "DS Yoshi-Kaskaden", + "old_falls_ds", + "r_DS_Jungle32_n", + "r_DS_Jungle32_f" + }, + { + 0x24a5, 21, 24, 71, 0xb1, // bmg, track, index, slot, music_id + "dDH", + "DS Desert Hills", + "DS Glühheiße Wüste", + "old_desert_ds", + "r_DS_Desert32_n", + "r_DS_Desert32_f" + }, + { + 0x24a6, 22, 29, 82, 0xb3, // bmg, track, index, slot, music_id + "dPG", + "DS Peach Gardens", + "DS Peachs Schlossgarten", + "old_garden_ds", + "r_DS_Garden32_n", + "r_DS_Garden32_f" + }, + { + 0x24a7, 23, 22, 63, 0xaf, // bmg, track, index, slot, music_id + "dDS", + "DS Delfino Square", + "DS Piazzale Delfino", + "old_town_ds", + "r_DS_Town32_n", + "r_DS_Town32_f" + }, + { + 0x24a8, 24, 28, 81, 0x99, // bmg, track, index, slot, music_id + "sMC3", + "SNES Mario Circuit 3", + "SNES Marios Piste 3", + "old_mario_sfc", + "r_SFC_Circuit32_n", + "r_SFC_Circuit32_f" + }, + { + 0x24a9, 25, 18, 53, 0x97, // bmg, track, index, slot, music_id + "sGV2", + "SNES Ghost Valley 2", + "SNES Geistertal 2", + "old_obake_sfc", + "r_SFC_Obake32_n", + "r_SFC_Obake32_f" + }, + { + 0x24aa, 26, 19, 54, 0x9f, // bmg, track, index, slot, music_id + "nMR", + "N64 Mario Raceway", + "N64 Marios Rennpiste", + "old_mario_64", + "r_64_Circuit32_n", + "r_64_Circuit32_f" + }, + { + 0x24ab, 27, 20, 61, 0x9d, // bmg, track, index, slot, music_id + "nSL", + "N64 Sherbet Land", + "N64 Sorbet-Land", + "old_sherbet_64", + "r_64_Sherbet32_n", + "r_64_Sherbet32_f" + }, + { + 0x24ac, 28, 31, 84, 0xa3, // bmg, track, index, slot, music_id + "nBC", + "N64 Bowser's Castle", + "N64 Bowsers Festung", + "old_koopa_64", + "r_64_Kuppa32_n", + "r_64_Kuppa32_f" + }, + { + 0x24ad, 29, 26, 73, 0xa1, // bmg, track, index, slot, music_id + "nDKJP", + "N64 DK's Jungle Parkway", + "N64 DKs Dschungelpark", + "old_donkey_64", + "r_64_Jungle32_n", + "r_64_Jungle32_f" + }, + { + 0x24ae, 30, 25, 72, 0x9b, // bmg, track, index, slot, music_id + "gBC3", + "GBA Bowser Castle 3", + "GBA Bowsers Festung 3", + "old_koopa_gba", + "r_AGB_Kuppa32_n", + "r_AGB_Kuppa32_f" + }, + { + 0x24af, 31, 21, 62, 0x95, // bmg, track, index, slot, music_id + "gSGB", + "GBA Shy Guy Beach", + "GBA Shy Guy-Strand", + "old_heyho_gba", + "r_AGB_Beach32_n", + "r_AGB_Beach32_f" + }, +}; + +//----------------------------------------------------------------------------- +// track name table + +const KeywordTab_t track_name_tab[] = +{ + { 0, "2.1", "21", 0 }, + { 0, "CASTLECOURSE.SZS", "MARIOCIRCUIT", 0 }, + { 0, "MARIOSPISTE", "MC", 0 }, + { 0, "T2.1", "T21", 0 }, + + { 1, "1.2", "12", 0 }, + { 1, "FARMCOURSE.SZS", "KUHMUH", 0 }, + { 1, "KUHMUHWEIDE", "MEADOWS", 0 }, + { 1, "MMM", "MOO", 0 }, + { 1, "MOOMEADOWS", "MOOMOO", 0 }, + { 1, "MOOMOOMEADOWS", "NFARMF.BRSTM", 0 }, + { 1, "NFARMN.BRSTM", "T1.2", 0 }, + { 1, "T12", "WEIDE", 0 }, + + { 2, "1.3", "13", 0 }, + { 2, "GORGE", "KINOKOCOURSE.SZS", 0 }, + { 2, "MG", "MUSHROOM", 0 }, + { 2, "MUSHROOMGORGE", "NKINOKOF.BRSTM", 0 }, + { 2, "NKINOKON.BRSTM", "PILZ", 0 }, + { 2, "PILZSCHLUCHT", "SCHLUCHT", 0 }, + { 2, "T1.3", "T13", 0 }, + + { 3, "3.4", "34", 0 }, + { 3, "GRUMBLE", "GRUMBLEVOLCANO", 0 }, + { 3, "GV", "NVOLCANO32F.BRSTM", 0 }, + { 3, "NVOLCANO32N.BRSTM", "T3.4", 0 }, + { 3, "T34", "VOLCANO", 0 }, + { 3, "VOLCANOCOURSE.SZS", "VULKANGROLLEN", 0 }, + + { 4, "1.4", "14", 0 }, + { 4, "FABRIK", "FACTORY", 0 }, + { 4, "FACTORYCOURSE.SZS", "STRMNFACTORYF.BRSTM", 0 }, + { 4, "STRMNFACTORYN.BRSTM", "T1.4", 0 }, + { 4, "T14", "TF", 0 }, + { 4, "TOADS", "TOADSFABRIK", 0 }, + { 4, "TOADSFACTORY", 0, 0 }, + + { 5, "2.2", "22", 0 }, + { 5, "CM", "COCONUT", 0 }, + { 5, "COCONUTMALL", "KOKOS", 0 }, + { 5, "KOKOSPROMENADE", "MALL", 0 }, + { 5, "NSHOPPING32F.BRSTM", "NSHOPPING32N.BRSTM", 0 }, + { 5, "PROMENADE", "SHOPPINGCOURSE.SZS", 0 }, + { 5, "T2.2", "T22", 0 }, + + { 6, "2.3", "23", 0 }, + { 6, "BOARDCROSSCOURSE.SZS", "DKS", 0 }, + { 6, "DKSKIKANE", "DKSUMMIT", 0 }, + { 6, "DS", "NSNOWBOARD32F.BRSTM", 0 }, + { 6, "NSNOWBOARD32N.BRSTM", "SKIKANE", 0 }, + { 6, "SUMMIT", "T2.3", 0 }, + { 6, "T23", 0, 0 }, + + { 7, "2.4", "24", 0 }, + { 7, "GOLD", "GOLDMINE", 0 }, + { 7, "MINE", "STRMNTRUCKF.BRSTM", 0 }, + { 7, "STRMNTRUCKN.BRSTM", "T2.4", 0 }, + { 7, "T24", "TRUCKCOURSE.SZS", 0 }, + { 7, "WARIOS", "WARIOSGOLD", 0 }, + { 7, "WARIOSGOLDMINE", "WGM", 0 }, + + { 8, "1.1", "11", 0 }, + { 8, "BEGINNERCOURSE.SZS", "LC", 0 }, + { 8, "LUIGI", "LUIGICIRCUIT", 0 }, + { 8, "LUIGIS", "LUIGISPISTE", 0 }, + { 8, "T1.1", "T11", 0 }, + + { 9, "3.1", "31", 0 }, + { 9, "DAISY", "DAISYCIRCUIT", 0 }, + { 9, "DAISYS", "DAISYSPISTE", 0 }, + { 9, "DC", "NDAISY32F.BRSTM", 0 }, + { 9, "NDAISY32N.BRSTM", "SENIORCOURSE.SZS", 0 }, + { 9, "T3.1", "T31", 0 }, + + { 10, "4.2", "42", 0 }, + { 10, "HIGHWAY", "MH", 0 }, + { 10, "MONDBLICKSTRASSE", "MOONVIEW", 0 }, + { 10, "MOONVIEWHIGHWAY", "MVH", 0 }, + { 10, "RIDGEHIGHWAYCOURSE.SZS", "STRMNRIDGEHIGHWAYF.BRSTM", 0 }, + { 10, "STRMNRIDGEHIGHWAYN.BRSTM", "T4.2", 0 }, + { 10, "T42", 0, 0 }, + + { 11, "3.3", "33", 0 }, + { 11, "BLAETTERWALD", "MAPLE", 0 }, + { 11, "MAPLETREEWAY", "MT", 0 }, + { 11, "NMAPLEF.BRSTM", "NMAPLEN.BRSTM", 0 }, + { 11, "T3.3", "T33", 0 }, + { 11, "TREEHOUSECOURSE.SZS", "TREEWAY", 0 }, + + { 12, "4.3", "43", 0 }, + { 12, "BC", "BOW.FESTUNG", 0 }, + { 12, "BOWSERSCASTLE", "BOWSERSFESTUNG", 0 }, + { 12, "KOOPACOURSE.SZS", "STRMNKOOPAF.BRSTM", 0 }, + { 12, "STRMNKOOPAN.BRSTM", "T4.3", 0 }, + { 12, "T43", 0, 0 }, + + { 13, "4.4", "44", 0 }, + { 13, "BLVD", "NRAINBOW32F.BRSTM", 0 }, + { 13, "NRAINBOW32N.BRSTM", "RAINBOW", 0 }, + { 13, "RAINBOWCOURSE.SZS", "RAINBOWROAD", 0 }, + { 13, "REGENBOGEN", "REGENBOGENBLVD", 0 }, + { 13, "REGENBOGENBOULEVARD", "ROAD", 0 }, + { 13, "RR", "T4.4", 0 }, + { 13, "T44", 0, 0 }, + + { 14, "4.1", "41", 0 }, + { 14, "DDR", "DESERTCOURSE.SZS", 0 }, + { 14, "DRY", "DRYDRY", 0 }, + { 14, "DRYDRYRUINS", "DRYRUINS", 0 }, + { 14, "RUINEN", "RUINS", 0 }, + { 14, "STAUBTROCKENE", "STAUBTROCKENERUINEN", 0 }, + { 14, "STRMNDESERTF.BRSTM", "STRMNDESERTN.BRSTM", 0 }, + { 14, "T4.1", "T41", 0 }, + { 14, "TROCKENE", "TROCKENERUINEN", 0 }, + + { 15, "3.2", "32", 0 }, + { 15, "CAPE", "KAP", 0 }, + { 15, "KC", "KOOPA", 0 }, + { 15, "KOOPACAPE", "KOOPAKAP", 0 }, + { 15, "STRMNWATERF.BRSTM", "STRMNWATERN.BRSTM", 0 }, + { 15, "T3.2", "T32", 0 }, + { 15, "WATERCOURSE.SZS", 0, 0 }, + + { 16, "5.1", "51", 0 }, + { 16, "GCNPEACH", "GCNPEACHBEACH", 0 }, + { 16, "GPB", "OLDPEACHGC.SZS", 0 }, + { 16, "PEACHBEACH", "RGCBEACH32F.BRSTM", 0 }, + { 16, "RGCBEACH32N.BRSTM", "RPB", 0 }, + { 16, "T5.1", "T51", 0 }, + + { 17, "7.4", "74", 0 }, + { 17, "GC", "GCMARIOS", 0 }, + { 17, "GCMARIOSPISTE", "GCNMARIO", 0 }, + { 17, "GCNMARIOCIRCUIT", "GCNMARIOS", 0 }, + { 17, "GCNMARIOSPISTE", "GMC", 0 }, + { 17, "OLDMARIOGC.SZS", "RGCCIRCUIT32F.BRSTM", 0 }, + { 17, "RGCCIRCUIT32N.BRSTM", "RMC", 0 }, + { 17, "T7.4", "T74", 0 }, + + { 18, "6.4", "64", 0 }, + { 18, "ARENA", "GCNWALUIGI", 0 }, + { 18, "GCNWALUIGIARENA", "GCNWALUIGISTADIUM", 0 }, + { 18, "GWS", "OLDWALUIGIGC.SZS", 0 }, + { 18, "RGCSTADIUM32F.BRSTM", "RGCSTADIUM32N.BRSTM", 0 }, + { 18, "RWS", "STADIUM", 0 }, + { 18, "T6.4", "T64", 0 }, + { 18, "WALUIGI", "WALUIGIARENA", 0 }, + { 18, "WALUIGISTADIUM", 0, 0 }, + + { 19, "8.3", "83", 0 }, + { 19, "BERGLAND", "DKBERGLAND", 0 }, + { 19, "DKMOUNTAIN", "GCNDK", 0 }, + { 19, "GCNDKBERGLAND", "GCNDKMOUNTAIN", 0 }, + { 19, "GDKM", "MOUNTAIN", 0 }, + { 19, "OLDDONKEYGC.SZS", "RDKM", 0 }, + { 19, "RGCMOUNTAIN32F.BRSTM", "RGCMOUNTAIN32N.BRSTM", 0 }, + { 19, "T8.3", "T83", 0 }, + + { 20, "5.2", "52", 0 }, + { 20, "DSYOSHI", "DSYOSHIFALLS", 0 }, + { 20, "DSYOSHIKASKADEN", "DYF", 0 }, + { 20, "FALLS", "KASKADEN", 0 }, + { 20, "OLDFALLSDS.SZS", "RDSJUNGLE32F.BRSTM", 0 }, + { 20, "RDSJUNGLE32N.BRSTM", "RYF", 0 }, + { 20, "T5.2", "T52", 0 }, + { 20, "YOSHI", "YOSHIFALLS", 0 }, + { 20, "YOSHIKASKADEN", 0, 0 }, + + { 21, "7.1", "71", 0 }, + { 21, "DDH", "DESERT", 0 }, + { 21, "DESERTHILLS", "DSDESERT", 0 }, + { 21, "DSDESERTHILLS", "DSGLUEHHEISSE", 0 }, + { 21, "DSGLUEHHEISSEWUESTE", "GLUEHHEISSE", 0 }, + { 21, "GLUEHHEISSEWUESTE", "HEISSE", 0 }, + { 21, "HEISSEWUESTE", "HILLS", 0 }, + { 21, "OLDDESERTDS.SZS", "RDH", 0 }, + { 21, "RDSDESERT32F.BRSTM", "RDSDESERT32N.BRSTM", 0 }, + { 21, "T7.1", "T71", 0 }, + { 21, "WUESTE", 0, 0 }, + + { 22, "8.2", "82", 0 }, + { 22, "DPG", "DSPEACH", 0 }, + { 22, "DSPEACHGARDENS", "DSPEACHS", 0 }, + { 22, "DSPEACHSSCHLOSSGARTEN", "GARDENS", 0 }, + { 22, "OLDGARDENDS.SZS", "PEACHGARDENS", 0 }, + { 22, "PEACHS", "PEACHSSCHLOSSGARTEN", 0 }, + { 22, "RDSGARDEN32F.BRSTM", "RDSGARDEN32N.BRSTM", 0 }, + { 22, "RPG", "SCHLOSSGARTEN", 0 }, + { 22, "T8.2", "T82", 0 }, + + { 23, "6.3", "63", 0 }, + { 23, "DDS", "DELFINO", 0 }, + { 23, "DELFINOSQUARE", "DSDELFINO", 0 }, + { 23, "DSDELFINOSQUARE", "DSPIAZZALE", 0 }, + { 23, "DSPIAZZALEDELFINO", "OLDTOWNDS.SZS", 0 }, + { 23, "PIAZ", "PIAZ.DELFINO", 0 }, + { 23, "PIAZZALE", "PIAZZALEDELFINO", 0 }, + { 23, "RDS", "RDSTOWN32F.BRSTM", 0 }, + { 23, "RDSTOWN32N.BRSTM", "SQUARE", 0 }, + { 23, "T6.3", "T63", 0 }, + + { 24, "8.1", "81", 0 }, + { 24, "CIRCUIT3", "MARIOCIRCUIT3", 0 }, + { 24, "MARIOSPISTE3", "OLDMARIOSFC.SZS", 0 }, + { 24, "PISTE3", "RMC3", 0 }, + { 24, "RSFCCIRCUIT32F.BRSTM", "RSFCCIRCUIT32N.BRSTM", 0 }, + { 24, "SMC3", "SNESMARIO", 0 }, + { 24, "SNESMARIOCIRCUIT", "SNESMARIOCIRCUIT3", 0 }, + { 24, "SNESMARIOS", "SNESMARIOSPISTE", 0 }, + { 24, "SNESMARIOSPISTE3", "T8.1", 0 }, + { 24, "T81", 0, 0 }, + + { 25, "5.3", "53", 0 }, + { 25, "GEISTERTAL", "GEISTERTAL2", 0 }, + { 25, "GHOST", "GHOSTVALLEY", 0 }, + { 25, "GHOSTVALLEY2", "OLDOBAKESFC.SZS", 0 }, + { 25, "RGV2", "RSFCOBAKE32F.BRSTM", 0 }, + { 25, "RSFCOBAKE32N.BRSTM", "SGV2", 0 }, + { 25, "SNESGEISTERTAL", "SNESGEISTERTAL2", 0 }, + { 25, "SNESGHOST", "SNESGHOSTVALLEY", 0 }, + { 25, "SNESGHOSTVALLEY2", "T5.3", 0 }, + { 25, "T53", "VALLEY", 0 }, + { 25, "VALLEY2", 0, 0 }, + + { 26, "5.4", "54", 0 }, + { 26, "MAR", "MAR.RENNPISTE", 0 }, + { 26, "MARIORACEWAY", "MARIOSRENNPISTE", 0 }, + { 26, "N64MARIO", "N64MARIORACEWAY", 0 }, + { 26, "N64MARIOS", "N64MARIOSRENNPISTE", 0 }, + { 26, "NMR", "OLDMARIO64.SZS", 0 }, + { 26, "R64CIRCUIT32F.BRSTM", "R64CIRCUIT32N.BRSTM", 0 }, + { 26, "RACEWAY", "RENNPISTE", 0 }, + { 26, "RMR", "T5.4", 0 }, + { 26, "T54", 0, 0 }, + + { 27, "6.1", "61", 0 }, + { 27, "LAND", "N64SHERBET", 0 }, + { 27, "N64SHERBETLAND", "N64SORBETLAND", 0 }, + { 27, "NSL", "OLDSHERBET64.SZS", 0 }, + { 27, "R64SHERBET32F.BRSTM", "R64SHERBET32N.BRSTM", 0 }, + { 27, "RSL", "SHERBET", 0 }, + { 27, "SHERBETLAND", "SORBET", 0 }, + { 27, "SORBETLAND", "T6.1", 0 }, + { 27, "T61", 0, 0 }, + + { 28, "8.4", "84", 0 }, + { 28, "BOW.FESTUNG", "N64BOW.FESTUNG", 0 }, + { 28, "N64BOWSERS", "N64BOWSERSCASTLE", 0 }, + { 28, "N64BOWSERSFESTUNG", "NBC", 0 }, + { 28, "OLDKOOPA64.SZS", "R64KUPPA32F.BRSTM", 0 }, + { 28, "R64KUPPA32N.BRSTM", "RBC", 0 }, + { 28, "T8.4", "T84", 0 }, + + { 29, "7.3", "73", 0 }, + { 29, "DKS", "DKSDSCHUNGELPARK", 0 }, + { 29, "DKSJUNGLE", "DKSJUNGLEPARKWAY", 0 }, + { 29, "DSCHUNGELPARK", "JUNGLE", 0 }, + { 29, "JUNGLEPARKWAY", "N64DKS", 0 }, + { 29, "N64DKSDSCHUNGELPARK", "N64DKSJUNGLE", 0 }, + { 29, "N64DKSJUNGLEPARKWAY", "NDKJP", 0 }, + { 29, "OLDDONKEY64.SZS", "PARKWAY", 0 }, + { 29, "R64JUNGLE32F.BRSTM", "R64JUNGLE32N.BRSTM", 0 }, + { 29, "RDJP", "RDKJP", 0 }, + { 29, "T7.3", "T73", 0 }, + + { 30, "7.2", "72", 0 }, + { 30, "BOW.FESTUNG3", "BOWSER", 0 }, + { 30, "BOWSERCASTLE", "BOWSERCASTLE3", 0 }, + { 30, "BOWSERSFESTUNG3", "CASTLE3", 0 }, + { 30, "FESTUNG3", "GBABOWSER", 0 }, + { 30, "GBABOWSERCASTLE", "GBABOWSERCASTLE3", 0 }, + { 30, "GBABOWSERS", "GBABOWSERSFESTUNG", 0 }, + { 30, "GBABOWSERSFESTUNG3", "GBC3", 0 }, + { 30, "OLDKOOPAGBA.SZS", "RAGBKUPPA32F.BRSTM", 0 }, + { 30, "RAGBKUPPA32N.BRSTM", "RBC3", 0 }, + { 30, "T7.2", "T72", 0 }, + + { 31, "6.2", "62", 0 }, + { 31, "GBASHY", "GBASHYGUY", 0 }, + { 31, "GBASHYGUYBEACH", "GBASHYGUYSTRAND", 0 }, + { 31, "GSGB", "GUY", 0 }, + { 31, "GUYBEACH", "GUYSTRAND", 0 }, + { 31, "OLDHEYHOGBA.SZS", "RAGBBEACH32F.BRSTM", 0 }, + { 31, "RAGBBEACH32N.BRSTM", "RSGB", 0 }, + { 31, "SHY", "SHYGUY", 0 }, + { 31, "SHYGUYBEACH", "SHYGUYSTRAND", 0 }, + { 31, "STRAND", "T6.2", 0 }, + { 31, "T62", 0, 0 }, + + {0,0,0,0} +}; + +//----------------------------------------------------------------------------- + +int ScanTrack ( ccp name ) +{ + const KeywordTab_t * cmd = ScanKeyword(0,name,track_name_tab); + return cmd ? cmd->id : -1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// arenas ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// arena positions + +const u32 arena_pos_default[10] = +{ + 1,0,3,2,4, 7,8,9,5,6 +}; + +u32 arena_pos[10] = +{ + 1,0,3,2,4, 7,8,9,5,6 +}; + +u32 arena_pos_r[10] = +{ + 1,0,3,2,4, 8,9,5,6,7 +}; + +//----------------------------------------------------------------------------- +// arena info + +const TrackInfo_t arena_info[10] = +{ + { + 0x24b8, 32, 1, 12, 0xb5, // bmg, track, index, slot, music_id + "aDP", + "Delfino Pier", + "Delfino-Pier", + "venice_battle", + "n_venice_n", + "n_venice_F" + }, + { + 0x24b9, 33, 0, 11, 0xb7, // bmg, track, index, slot, music_id + "aBP", + "Block Plaza", + "Block-Plaza", + "block_battle", + "n_block_n", + "n_block_F" + }, + { + 0x24ba, 34, 3, 14, 0xbb, // bmg, track, index, slot, music_id + "aCCW", + "Chain Chomp Wheel", + "Kettenhund-Roulette", + "casino_battle", + "n_casino_n", + "n_casino_F" + }, + { + 0x24bb, 35, 2, 13, 0xb9, // bmg, track, index, slot, music_id + "aFS", + "Funky Stadium", + "Funky-Stadion", + "skate_battle", + "n_skate_n", + "n_skate_F" + }, + { + 0x24bc, 36, 4, 15, 0xbd, // bmg, track, index, slot, music_id + "aTD", + "Thwomp Desert", + "Steinwüste", + "sand_battle", + "n_ryuusa_n", + "n_ryuusa_F" + }, + { + 0x24bd, 37, 8, 24, 0xbf, // bmg, track, index, slot, music_id + "agCL", + "GCN Cookie Land", + "GCN Keks-Land", + "old_CookieLand_gc", + "r_GC_Battle32_n", + "r_GC_Battle32_F" + }, + { + 0x24be, 38, 9, 25, 0xc1, // bmg, track, index, slot, music_id + "adTH", + "DS Twilight House", + "DS Finstervilla", + "old_House_ds", + "r_ds_battle_n", + "r_ds_battle_F" + }, + { + 0x24bf, 39, 5, 21, 0xc3, // bmg, track, index, slot, music_id + "asBC4", + "SNES Battle Course 4", + "SNES Kampfkurs 4", + "old_battle4_sfc", + "r_sfc_battle_n", + "r_sfc_battle_F" + }, + { + 0x24c0, 40, 6, 22, 0xc5, // bmg, track, index, slot, music_id + "agBC3", + "GBA Battle Course 3", + "GBA Kampfkurs 3", + "old_battle3_gba", + "r_agb_battle_n", + "r_agb_battle_F" + }, + { + 0x24c1, 41, 7, 23, 0xc7, // bmg, track, index, slot, music_id + "anSS", + "N64 Skyscraper", + "N64 Wolkenkratzer", + "old_matenro_64", + "r_64_battle_n", + "r_64_battle_F" + }, +}; + +//----------------------------------------------------------------------------- +// arena name table + +const KeywordTab_t arena_name_tab[] = +{ + { 0, "1.2", "12", 0 }, + { 0, "A1.2", "A12", 0 }, + { 0, "ADP", "DELFINOPIER", 0 }, + { 0, "NVENICEF.BRSTM", "NVENICEN.BRSTM", 0 }, + { 0, "U1.2", "U12", 0 }, + { 0, "VENICEBATTLE.SZS", 0, 0 }, + + { 1, "1.1", "11", 0 }, + { 1, "A1.1", "A11", 0 }, + { 1, "ABP", "BLOCKBATTLE.SZS", 0 }, + { 1, "BLOCKPLAZA", "NBLOCKF.BRSTM", 0 }, + { 1, "NBLOCKN.BRSTM", "U1.1", 0 }, + { 1, "U11", 0, 0 }, + + { 2, "1.4", "14", 0 }, + { 2, "A1.4", "A14", 0 }, + { 2, "ACCW", "CASINOBATTLE.SZS", 0 }, + { 2, "CHAINCHOMPWHEEL", "KETTENHUNDRLT", 0 }, + { 2, "KETTENHUNDROULETTE", "NCASINOF.BRSTM", 0 }, + { 2, "NCASINON.BRSTM", "U1.4", 0 }, + { 2, "U14", 0, 0 }, + + { 3, "1.3", "13", 0 }, + { 3, "A1.3", "A13", 0 }, + { 3, "AFS", "FUNKYSTADION", 0 }, + { 3, "FUNKYSTADIUM", "NSKATEF.BRSTM", 0 }, + { 3, "NSKATEN.BRSTM", "SKATEBATTLE.SZS", 0 }, + { 3, "U1.3", "U13", 0 }, + + { 4, "1.5", "15", 0 }, + { 4, "A1.5", "A15", 0 }, + { 4, "ATD", "NRYUUSAF.BRSTM", 0 }, + { 4, "NRYUUSAN.BRSTM", "SANDBATTLE.SZS", 0 }, + { 4, "STEINWUESTE", "THWOMPDESERT", 0 }, + { 4, "U1.5", "U15", 0 }, + + { 5, "2.4", "24", 0 }, + { 5, "A2.4", "A24", 0 }, + { 5, "AGCL", "ARCL", 0 }, + { 5, "GCNCOOKIELAND", "GCNKEKSLAND", 0 }, + { 5, "OLDCOOKIELANDGC.SZS", "RGCBATTLE32F.BRSTM", 0 }, + { 5, "RGCBATTLE32N.BRSTM", "U2.4", 0 }, + { 5, "U24", 0, 0 }, + + { 6, "2.5", "25", 0 }, + { 6, "A2.5", "A25", 0 }, + { 6, "ADTH", "ARTH", 0 }, + { 6, "DSFINSTERVILLA", "DSTWILIGHTHOUSE", 0 }, + { 6, "OLDHOUSEDS.SZS", "RDSBATTLEF.BRSTM", 0 }, + { 6, "RDSBATTLEN.BRSTM", "U2.5", 0 }, + { 6, "U25", 0, 0 }, + + { 7, "2.1", "21", 0 }, + { 7, "A2.1", "A21", 0 }, + { 7, "ARBC4", "ASBC4", 0 }, + { 7, "OLDBATTLE4SFC.SZS", "RSFCBATTLEF.BRSTM", 0 }, + { 7, "RSFCBATTLEN.BRSTM", "SNESBATTLECOURSE4", 0 }, + { 7, "SNESKAMPFK.4", "SNESKAMPFKURS4", 0 }, + { 7, "U2.1", "U21", 0 }, + + { 8, "2.2", "22", 0 }, + { 8, "A2.2", "A22", 0 }, + { 8, "AGBC3", "ARBC3", 0 }, + { 8, "GBABATTLECOURSE3", "GBAKAMPFKURS3", 0 }, + { 8, "OLDBATTLE3GBA.SZS", "RAGBBATTLEF.BRSTM", 0 }, + { 8, "RAGBBATTLEN.BRSTM", "U2.2", 0 }, + { 8, "U22", 0, 0 }, + + { 9, "2.3", "23", 0 }, + { 9, "A2.3", "A23", 0 }, + { 9, "ANSS", "ARSS", 0 }, + { 9, "N64SKYSCRAPER", "N64WOLKENKRATZER", 0 }, + { 9, "OLDMATENRO64.SZS", "R64BATTLEF.BRSTM", 0 }, + { 9, "R64BATTLEN.BRSTM", "U2.3", 0 }, + { 9, "U23", "WOLKENKRATZER", 0 }, + + {0,0,0,0} +}; + +//----------------------------------------------------------------------------- + +int ScanArena ( ccp name ) +{ + const KeywordTab_t * cmd = ScanKeyword(0,name,arena_name_tab); + return cmd ? cmd->id : -1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct MusicInfo_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const MusicInfo_t music_info[MKW_N_MUSIC+1] = +{ + { 0x75, 0, -1, "xLC" }, + { 0x77, 1, 1, "MMM" }, + { 0x79, 1, 2, "MG" }, + { 0x7b, 1, 4, "TF" }, + { 0x7d, 1, 0, "MC" }, + { 0x7f, 1, 5, "CM" }, + { 0x81, 1, 6, "DKS" }, + { 0x83, 1, 7, "WGM" }, + { 0x85, 1, 15, "KC" }, + { 0x87, 1, 9, "DC" }, + { 0x89, 1, 14, "DDR" }, + { 0x8b, 1, 3, "GV" }, + { 0x8d, 1, 10, "MH" }, + { 0x8f, 1, 11, "MT" }, + { 0x91, 1, 12, "BC" }, + { 0x93, 1, 13, "RR" }, + { 0x95, 5, 31, "gSGB" }, + { 0x97, 5, 25, "sGV2" }, + { 0x99, 5, 24, "sMC3" }, + { 0x9b, 5, 30, "gBC3" }, + { 0x9d, 5, 27, "nSL" }, + { 0x9f, 5, 26, "nMR" }, + { 0xa1, 5, 29, "nDKJP" }, + { 0xa3, 5, 28, "nBC" }, + { 0xa5, 5, 16, "gPB" }, + { 0xa7, 5, 17, "gMC" }, + { 0xa9, 5, 18, "gWS" }, + { 0xab, 5, 19, "gDKM" }, + { 0xad, 5, 20, "dYF" }, + { 0xaf, 5, 23, "dDS" }, + { 0xb1, 5, 21, "dDH" }, + { 0xb3, 5, 22, "dPG" }, + { 0xb5, 2, 32, "aDP" }, + { 0xb7, 2, 33, "aBP" }, + { 0xb9, 2, 35, "aFS" }, + { 0xbb, 2, 34, "aCCW" }, + { 0xbd, 2, 36, "aTD" }, + { 0xbf, 6, 37, "agCL" }, + { 0xc1, 6, 38, "adTH" }, + { 0xc3, 6, 39, "asBC4" }, + { 0xc5, 6, 40, "agBC3" }, + { 0xc7, 6, 41, "anSS" }, + {0,0,-1,""} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/db-mkw.h b/project/src/db-mkw.h new file mode 100644 index 0000000..6c49f91 --- /dev/null +++ b/project/src/db-mkw.h @@ -0,0 +1,127 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_DB_MKW_H +#define SZS_DB_MKW_H 1 + +#include "lib-std.h" +#include "lib-mkw.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct TrackInfo_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[TrackInfo_t]] (generic code) + +typedef struct TrackInfo_t +{ + u16 bmg_mid; // standard message id for BMG files + u8 track_id; // track id (0..41) + u8 def_index; // default index (0..31) + u8 def_slot; // default slot (11..84/25) + u8 music_id; // music ID + + ccp abbrev; // short name + ccp name_en; // english name + ccp name_de; // german name + ccp track_fname; // track file name + ccp sound_n_fname; // file name of normal sound + ccp sound_f_fname; // file name of fast sound + +} TrackInfo_t; + + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// tracks ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum // some constants +{ + FW_TRACK_NAME_EN = 23, + FW_TRACK_NAME_DE = 23, + FW_TRACK_FNAME = 19, + FW_TRACK_SOUND_N = 21, + FW_TRACK_SOUND_F = 21, +}; + +//----------------------------------------------------------------------------- + +extern const u32 track_pos_default[32]; +extern u32 track_pos[32]; +extern u32 track_pos_r[32]; + +extern u32 track_pos_swap[32]; +extern const KeywordTab_t track_name_tab[]; +int ScanTrack ( ccp name ); + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// arenas ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum // some constants +{ + FW_ARENA_NAME_EN = 20, + FW_ARENA_NAME_DE = 19, + FW_ARENA_FNAME = 17, + FW_ARENA_SOUND_N = 15, + FW_ARENA_SOUND_F = 15, +}; + +//----------------------------------------------------------------------------- + +extern const u32 arena_pos_default[10]; +extern u32 arena_pos[10]; +extern u32 arena_pos_r[10]; + +extern const KeywordTab_t arena_name_tab[]; +int ScanArena ( ccp name ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// vars and functtions /////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const TrackInfo_t InvalidTrack; +extern const TrackInfo_t track_info[MKW_N_TRACKS]; +extern const TrackInfo_t arena_info[MKW_N_ARENAS]; + +static inline const TrackInfo_t * GetTrackInfo ( uint tidx ) +{ + return tidx < MKW_N_TRACKS + ? track_info + tidx + : tidx < MKW_N_TRACKS + MKW_N_ARENAS + ? arena_info + tidx - MKW_N_TRACKS + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct MusicInfo_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[MusicInfo_t]] (generic code) + +typedef struct MusicInfo_t +{ + uint id; // ID + uint flags; // 1:track, 2:arena, 4:retro + int track; // -1:no, 0-31:track, 32-41:arena + char name[8]; // short name + +} MusicInfo_t; + +extern const MusicInfo_t music_info[MKW_N_MUSIC+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_DB_MKW_H diff --git a/project/src/db-object.c b/project/src/db-object.c new file mode 100644 index 0000000..2e61e59 --- /dev/null +++ b/project/src/db-object.c @@ -0,0 +1,3108 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + + +#include "db-object.h" + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////// characteristics strings ///////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const char obj_charact[] = ""; +const char obj_charact_a[] = "no route, found in arenas"; +const char obj_charact_o[] = "no route, found in other files"; +const char obj_charact_or_s_t[] = "optional route, solid, found in tracks"; +const char obj_charact_r_s_a[] = "route needed, solid, found in arenas"; +const char obj_charact_r_s_t[] = "route needed, solid, found in tracks"; +const char obj_charact_r_s_to[] = "route needed, solid, found in tracks + other files"; +const char obj_charact_r_t[] = "route needed, found in tracks"; +const char obj_charact_r_ta[] = "route needed, found in tracks + arenas"; +const char obj_charact_r_to[] = "route needed, found in tracks + other files"; +const char obj_charact_s[] = "solid"; +const char obj_charact_s_a[] = "no route, solid, found in arenas"; +const char obj_charact_s_o[] = "no route, solid, found in other files"; +const char obj_charact_s_t[] = "no route, solid, found in tracks"; +const char obj_charact_s_ta[] = "no route, solid, found in tracks + arenas"; +const char obj_charact_s_tao[] = "no route, solid, found in tracks + arenas + other files"; +const char obj_charact_s_to[] = "no route, solid, found in tracks + other files"; +const char obj_charact_t[] = "no route, found in tracks"; +const char obj_charact_ta[] = "no route, found in tracks + arenas"; +const char obj_charact_to[] = "no route, found in tracks + other files"; + + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////// format strings ////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const ObjSettingFormat_t settings_format_uuuu + = { "%6u %6u %6u %6u", "%s%6u %s%6u %s%6u %s%6u" }; +const ObjSettingFormat_t settings_format_uxuu + = { "%6u %#6x %6u %6u", "%s%6u %s%#6x %s%6u %s%6u" }; +const ObjSettingFormat_t settings_format_uxxu + = { "%6u %#6x %#6x %6u", "%s%6u %s%#6x %s%#6x %s%6u" }; +const ObjSettingFormat_t settings_format_xxxx + = { "%#6x %#6x %#6x %#6x", "%s%#6x %s%#6x %s%#6x %s%#6x" }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct ObjectInfo_t /////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const ObjectInfo_t ObjectInfo[N_KMP_GOBJ] = +{ + // names and infos are copied from + // http://wiki.tockdom.de/index.php?title=Object + + /* 0x000 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x001 */ + { "airblock", "Object not used in regular tracks. Used in competitions as \"invisible wall\" above barriers (so you can't fly/jump over them). They are boxes of 200x200x200 units with a pivot in the bottom face.", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x002 */ + { "Psea", "The sea on GCN Peach Beach; for details see Object/Psea.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {116,-1,-1}, "Psea(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x003 */ + { "lensFX", "Generates a lens flare object when looking at the sun; it's also highly recommended to place this object near or coincident to `sun'.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_M_ARENA, + obj_charact_ta, + &settings_format_uuuu, &settings_format_uuuu, + {203,-1,-1}, "lensFX", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x004 */ + { "venice_nami", "The rising and dropping sea on Delfino Pier. This object goes through five stages before it ends at its final height given in setting 2. Stages 1, 3 and 5 are active until a target height is reached, stages 2 and 4 are active for a given time period. The object has an ongoing additional cyclus of rising and dropping between -80 and 80 added to its current height. All target heights are relative to the placement height, the rising speed is in units per frame.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {250,-1,-1}, "venice_nami(B)", + { "Rising speed in odd stages", + "Stage 5 target height", + "Stage 1 target height", + "Stage 2 frame count", + "Stage 3 target height", + "Stage 4 frame count", + "Rising speed in even stages", + 0 }}, + /* 0x005 */ + { "sound_river", "Generates a river sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Only works on slots 3.2, 4.2, 5.2 and 7.3.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x006 */ + { "sound_water_fall", "Generates a waterfall sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Only works on slots 3.2, 4.2, 5.2 and 7.3.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x007 */ + { "pocha", "Generates a water splash effect when a player touches KCL flag 0x10 with variants 1 (water) or 3 (icy water that also needs the `0x17|Ice' object). Also works for KCL flag 0x1A with variant 2, but cannot be triggered again until the player respawns. Only Y coordinate is used.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_M_ARENA, + obj_charact_ta, + &settings_format_uuuu, &settings_format_uuuu, + {225,-1,-1}, "pocha(BE)", + { "Group Y KCL Index", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x008 */ + { "sound_lake", "Generates a lake sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Only works on slot 8.2.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x009 */ + { "sound_big_fall", "Generates a louder waterfall sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Only works on slots 3.2 and 7.3.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x00a */ + { "sound_sea", "Generates a sea sound effect, the range where the sound comes from is controlled by the route, so makes use of it.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x00b */ + { "sound_fountain", "Generates a fountain sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Only works on slots 2.2 and 3.1.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x00c */ + { "sound_volcano", "Generates a volcano sound effect, the range where the sound comes from is controlled by the route, so makes use of it.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x00d */ + { "sound_audience", "Generates an audience sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Works on slots 1.1, 1.4, 2.3, 3.1 and 5.4; slot 3.1 doesn't feature slot-based audience ambience.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x00e */ + { "sound_big_river", "Generates a louder river sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Only works on slot 5.2.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x00f */ + { "sound_sand_fall", "Object not used in any track. Generates a sand fall sound effect, the range where the sound comes from is controlled by the route. This object must have a route.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x010 */ + { "sound_lift", "Generates a lift sound effect, the range where the sound comes from is controlled by the route, so makes use of it. Route Setting 1 is always 1 in all Nintendo tracks.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x011 */ + { "pochaYogan", "Generates a lava splash effect when a player touches KCL flag 0x10 with variant 2. Only Y coordinate is used.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {227,-1,-1}, "pochaYogan(BE)", + { "Group Y KCL Index", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x012 */ + { "entry", "Generates a fire around the player when falls through a burning air fall boundary flag.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {172,-1,-1}, "entry(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x013 */ + { "pochaMori", "Generates a leaf splash effect when a player touches KCL flag 0x1A with variant 2. Only Y coordinate is used.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_M_ARENA, + obj_charact_ta, + &settings_format_uuuu, &settings_format_uuuu, + {226,-1,-1}, "pochaMori(E)", + { "Group Y KCL Index", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x014 */ + { "eline_control", "Enemy line controller. Multiple controllers can be chained together. The timer of the next controller in a chain begins after the end of the previous. Controller ID 0 is reserved to indicate the end of a controller chain. Enemies can choose to not obey a controller unless the next EHPH Section is set to -1. Found in slots 1.4, 2.2, 3.4 and 4.1 and in Arena 1.2.", + OBF_OBJFLOW | OBF_NO_ROUTE | OBF_M_TRACK | OBF_M_ARENA, + obj_charact_ta, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { "Entry Flag: 0 = Do Not Enter 1 = Enter", + "Time to Next Controller (seconds)", + "Controller ID", + "Next Controller ID", + 0, 0, 0, + "ENPH Section; if -1, use ENPH of previous controller" }}, + /* 0x015 */ + { "sound_Mii", "Generates a mii crowd sound effect, the range where the sound comes from is controlled by the route, so makes use of it.", + OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x016 */ + { "begoman_manager", "Galaxy Colosseum Topmen manager; object not used in any track. Once the topmen controlled by the first manager are defeated, the topmen controlled by the second manager will spawn, etc...", + OBF_OBJFLOW | OBF_NO_ROUTE | OBF_F_OTHER, + obj_charact_o, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { "Manager ID (1-based)", + "[?] Number of Topmen to control", + "First Topman ID to control", + "[?] Topman behavior: 1: Coward, they will ignore the player 2: Aggressive, they will attack the player", + "[?] Number of Topmen under control + Number of Topmen controlled by the previous manager", + 0, 0, 0 }}, + /* 0x017 */ + { "ice", "Generates ice around the player when respawned after falling in an icy water fall boundary flag; the location doesn't matter, the ice effect is always gotten. This object can be used to enable tracks for slot 6.1. But is has only impact with in combination with an icy boundary flag. In this case it works only at slot 6.1.", + OBF_OPTIONAL | OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {191,-1,-1}, "ice(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x018 */ + { "startline2D", "Generates a finish line at given location, this object is used in competitions where you have to drive laps on a battle course and some others; object not used in any track.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x019 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x01a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x01b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x01c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x01d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x01e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x01f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x020 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x021 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x022 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x023 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x024 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x025 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x026 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x027 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x028 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x029 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x02a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x02b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x02c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x02d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x02e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x02f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x030 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x031 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x032 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x033 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x034 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x035 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x036 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x037 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x038 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x039 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x03a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x03b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x03c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x03d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x03e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x03f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x040 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x041 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x042 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x043 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x044 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x045 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x046 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x047 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x048 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x049 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x04a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x04b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x04c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x04d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x04e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x04f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x050 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x051 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x052 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x053 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x054 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x055 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x056 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x057 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x058 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x059 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x05a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x05b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x05c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x05d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x05e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x05f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x060 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x061 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x062 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x063 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x064 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x065 */ + { "itembox", "Spinning box with a question mark in it. If touched it snaps and an item lottery begins; after the lottery the item shown is gotten.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_FOUND | OBF_ITEMBOX, + obj_charact_s_tao, + &settings_format_uxxu, &settings_format_uuuu, + {192,-1,-1}, "itembox(B)", + { "Unknown", + "Item type (player)", + "Item type (enemy)", + "Disables shadow if set", + 0, + "Timing lottery", + "[?] Additional height, multiplied with unknown number (always 1?).", + "Unknown" }}, + /* 0x066 */ + { "DummyPole", "Solidity of the poles of different courses. Deleting this object will cause the Dummy pole itself to be unsolid.", + OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_s_to, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { "Wall KCL Variant", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x067 */ + { "flag", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {175,-1,-1}, "flag", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x068 */ + { "flagBlend", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {176,-1,-1}, "flagBlend", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x069 */ + { "gnd_sphere", "Object not used in any track. Green sphere with animation, solid.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x06a */ + { "gnd_trapezoid", "Object not used in any track. Red trapezoid with animation, solid.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x06b */ + { "gnd_wave1", "Object not used in any track. Blue object that makes wave animation. If CPUs touch down the object, they dissapear.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x06c */ + { "gnd_wave2", "Object not used in any track. Yellow object that makes wave animation. If CPUs touch down the object, they dissapear.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x06d */ + { "gnd_wave3", "Object not used in any track. Orange object that makes wave animation. If CPUs touch down the object, they dissapear.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x06e */ + { "gnd_wave4", "Object not used in any track. Blue object that makes wave animation. If CPUs touch down the object, they dissapear.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x06f */ + { "sun", "The sun in the vrcorn, often used with `lensFX'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_M_ARENA, + obj_charact_ta, + &settings_format_uuuu, &settings_format_uuuu, + {240,-1,-1}, "sun(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x070 */ + { "woodbox", "Wooden box. When touched the player slows down and the box drops an item. The wooden boxes respawn after 5 seconds, drop from 5000 units above the origin and drop onto the first face they touch. In Time Trial it becomes an unbreakable `ironbox'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {256,-1,-1}, "woodbox(BE)", + { 0, + "Item to give", + "Chance to give an item", + 0, 0, 0, 0, + "LE-CODE only: Replace the standard drop height by this value." }}, + /* 0x071 */ + { "KmoonZ", "The moon of Rainbow Road.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {73,-1,-1}, "KmoonZ(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x072 */ + { "sunDS", "The Angry Sun from DS Desert Hills. It changes point of view and rotation of the crown according to player's position. Route Setting 2 controls the behavior depending on the value X: X < 30 (1E in hex) → Stays for X/60 seconds X ≥ 30 (1E in hex) → Drops a `FireSnake' staying for X/60 seconds. In both cases the dropping animation is viewed; if the route start point is set to drop, it will drop at the second \"lap\" of the route. Only slots 3.1 and 7.1 support this object.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {241,-1,-1}, "sunDS(B)", + { "Speed", + "Delay before starting in 1/60 seconds after starting of countdown. 240 (F0 in hex) = Starts immediately after the \"GO!\"", + 0, 0, 0, 0, 0, 0 }}, + /* 0x073 */ + { "coin", "Present in all battle courses in Coin Runners mode and in some tournaments, when touched it disappears and the player gets one coin. Some coins don't appear at the start of the battle but show up later. See Object/coin for details.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_s_a, + &settings_format_uuuu, &settings_format_uuuu, + {159,-1,-1}, "coin(BE)", + { "[?] Mode. 0 = Battle 1 = Tournament or Mission Mode.", + "Start or respawn place. See Coin for details.", + "Spawn behavior. See Coin for details.", + 0, 0, 0, 0, 0 }}, + /* 0x074 */ + { "ironbox", "Object not used in any track. It seems to be born as an object separate from `woodbox'. ", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x075 */ + { "ItemDirect", "Object used in tournaments (SNES Battle Course 4 and Daisy Circuit). Can be used to place an item directly onto the track. They will not show up in time trial. You must not place more than 11 of these in a track otherwise your track will freeze or hang in online play.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { "0 = Green Shell 1 = Red Shell 2 = Banana 3 = Mushroom 4 = Star", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x076 */ + { "s_itembox", "Item box that acts like a kart, it moves with moving KCL. When placed high, it falls until it reaches the ground, and when placed on a steep surface, it falls.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_M_ARENA | OBF_ITEMBOX, + obj_charact_s_ta, + &settings_format_uxxu, &settings_format_uuuu, + {192,-1,-1}, "itembox(B)", + { "[?] Unknown", + "[?] Item type (player)", + "[?] Item type (enemy)", + "[?] Disables shadow if set", + 0, + "[?] Timing lottery", + "?", + "[?] Unknown" }}, + /* 0x077 */ + { "pile_coin", "Object not used in any track. This is the pile of coins above the players used in battle mode, static and without collision.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {159,-1,-1}, "coin(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x078 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x079 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x07a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x07b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x07c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x07d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x07e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x07f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x080 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x081 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x082 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x083 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x084 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x085 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x086 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x087 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x088 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x089 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x08a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x08b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x08c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x08d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x08e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x08f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x090 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x091 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x092 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x093 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x094 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x095 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x096 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x097 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x098 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x099 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x09a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x09b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x09c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x09d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x09e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x09f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0a9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0aa */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0ab */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0ac */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0ad */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0ae */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0af */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0b9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0ba */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0bb */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0bc */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0bd */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0be */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0bf */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0c9 */ + { "f_itembox", "Route controlled item box. If any Route Setting 2 = 1, the box breaks at that point.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER | OBF_ITEMBOX, + obj_charact_r_s_to, + &settings_format_uxxu, &settings_format_uuuu, + {192,-1,-1}, "itembox(B)", + { "Start speed in units per 1/60 seconds; if a Route Setting 1 > 0, then a new speed value is defined", + "Item type (player)", + "Item type (enemy)", + "Disables shadow if set", + "Route start point", + 0, 0, 0 }}, + /* 0x0ca */ + { "MashBalloonGC", "Airballoon as seen on GCN Mario Circuit. It has an assigned route but also floats from Y to Y.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_r_to, + &settings_format_uuuu, &settings_format_uuuu, + {85,-1,-1}, "MashBalloonGC(B)", + { "Speed", + "Max Y difference", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0cb */ + { "WLwallGC", "Horizontal moving Piranha Plant wall from GCN Waluigi Stadium. It moves like a sine function (inside → outside → inside); normally combined with `WLdokanGC'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {141,-1,-1}, "WLwallGC(BK)", + { "Time inside the pipe", + "Time outside the pipe", + "Speed", + "Length of going", + "Going out first time", + 0, 0, 0 }}, + /* 0x0cc */ + { "CarA1", "Moving Car from Coconut Mall. It moves exactly like `WLwallGC' but instead left → right.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {9,-1,-1}, "CarA1(B)", + { "Speed", + "Acceleration", + "Time of pause", + 0, 0, 0, 0, 0 }}, + /* 0x0cd */ + { "basabasa", "Bats following a route, when touched they act like a wall. The bats are released as groups which can be edited in any way.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {145,-1,-1}, "basabasa(B)", + { "Speed in units per 1/60 seconds", + "Delay until start of the next group in 1/60 seconds. Multiple groups can be on the way", + "Number of bats per group", + "How far they can get off the route (unknown values)", + "Maximum height", + "Distance between the bats in the same group", + "Start delay in 1/60 seconds", + "?" }}, + /* 0x0ce */ + { "HeyhoShipGBA", "Shy Guy ship which shoots exploding cannonballs; cannonball object is `HeyhoBallGBA'. This object is made for counterclockwise directed tracks, so if a track is clockwise directed this object must be modified with BrawlBox in the following ways: Cullmode from Materials must be set on Cull_Outside and Z scale in KMP must be -1. Only slot 6.2 supports this object and it's necessary to allow tracks to work on this slot.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {61,-1,-1}, "HeyhoShipGBA(B)", + { "Speed", + "How strong it moves up and down", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0cf */ + { "koopaBall", "Gigantic exploding fireball, used with `koopaFigure'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {198,-1,-1}, "koopaBall(BE)", + { "Unknown", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0d0 */ + { "kart_truck", "Truck from Moonview Highway. Route Setting 1 is used for blinking (1 = Left, 2 = Right). Route Setting 2 selects the speed (0 or 1).", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {71,-1,-1}, "K_truck(B)", + { "Route start point", + "Speed 0 in 1/60 seconds", + "Speed 1 in 1/60 seconds", + "Texturing: 0 = MooMoo 1 = Fruit 2 = Factory", + 0, 0, 0, 0 }}, + /* 0x0d1 */ + { "car_body", "Car from Moonview Highway. Route Setting 1 is used for blinking (1 = Left, 2 = Right) Route Setting 2 selects the speed (0 or 1)", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {68,-1,-1}, "K_car_body(B)", + { "Route start point", + "Speed 0 in 1/60 seconds", + "Speed 1 in 1/60 seconds", + "Color: 0 = Blue 1 = Red 2 = Yellow", + "Unknown", + 0, 0, 0 }}, + /* 0x0d2 */ + { "skyship", "Blimp from Luigi Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_r_to, + &settings_format_uuuu, &settings_format_uuuu, + {237,-1,-1}, "skyship(B)", + { "Speed", + "[?] Max Y difference", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0d3 */ + { "w_woodbox", "Box following route. Route Setting 1 sets the speed (gets added to S1).", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {256,-1,-1}, "woodbox(BE)", + { "Speed", + "Item to give", + "Chance to give an item", + "Unknown", + "Start delay", + "Time between 2 boxes", + "Max amount of boxes", + 0 }}, + /* 0x0d4 */ + { "w_itembox", "Itemboxes following route.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_ITEMBOX, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {192,-1,-1}, "itembox(B)", + { "Speed", + "[?] Item type (player)", + "[?] Item type (enemy)", + "[?] Disables shadow if set", + "Start delay", + "Time between 2 boxes", + "Number of itemboxes on the route (default 5)", + 0 }}, + /* 0x0d5 */ + { "w_itemboxline", "`Press', block.brres and `itembox' combination from Toad's Factory. Each block follows a route until it reach the press and the press converts it to an itembox which continues the route. Route setting 1 is the speed it slows down to (if 0 it uses the speed setting in S1). Route setting 2: 0x1: press activates when a block reaches this point 0x2: determines where the itembox glows", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_TRACK | OBF_ITEMBOX, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {7,192,115}, "Block(BE);itembox(B);Press(BE)", + { "Speed", + 0, 0, 0, + "Delay until the first block spawns (time in frames)", + "Delay between block spawns (time in frames)", + "Number of Blocks/Item Boxes that can exist at any one time", + 0 }}, + /* 0x0d6 */ + { "VolcanoBall1", "Firery rock from Grumble Volcano. When created a volcano explosion occurs and it follows the route until it touches a solid part, then it becomes a fire; slot 3.4 enables its SFX.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {134,-1,-1}, "VolcanoBall1(BE)", + { "Fireball speed", + "Delay before starting", + "Time between 2 fireballs", + "Time until the fire goes out", + 0, 0, 0, 0 }}, + /* 0x0d7 */ + { "penguin_s", "Small penguin from N64 Sherbet Land, creates a symmetrical copy of itself under the ground to simulate ice reflection. Route Setting 1: 0 = Default; 1 = Glide; 2 = Animation faster; 3 = Default. Route Setting 2 describes the speed when Route Setting 1 ≠ 0, but is always required a value ≠ 0. Slot 6.1 enables its SFX.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {223,-1,-1}, "penguin_s(BE3D)", + { "Speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0d8 */ + { "penguin_m", "Medium penguin from N64 Sherbet Land. Slot 6.1 enables its SFX.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {222,-1,-1}, "penguin_m(BED)", + { "Speed", + "LE-CODE or cheat code required: Route start point.", + "LE-CODE or cheat code required: If S8=1: Item to drop (same as ItemDirect).", + 0, 0, 0, 0, + "LE-CODE or special cheat code required: 0: normal behaviour 1: custom \"mushroom car\" behaviour developed by KHacker." }}, + /* 0x0d9 */ + { "penguin_l", "Large penguin from N64 Sherbet Land.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {221,-1,-1}, "penguin_l(BE)", + { "Speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0da */ + { "castleballoon1", "Balloon from Mario Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_r_to, + &settings_format_uuuu, &settings_format_uuuu, + {154,-1,-1}, "castleballoon1(B)", + { "Speed", + "Max Y difference", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0db */ + { "dossunc", "Thwomp with optional route; without route, S1 and S2 must be 0. If S2 = 2, a 3-point route is needed to make effect: 1 → Start point, Route Setting 2 = 0 2 → Rotation point, Route Setting 2 = 1 3 → Going down point, Route Setting 2 = 0. The couple starts, at the second point turns of 180° and goes left and right, going down around the third point according to player's direction, then goes back to the first point. To make effect the object needs a rotation of ±90°, route points must have the same Y and Z coordinate and no other object must be near the start point. In GP and VS Race the object is activated by the proximity of a main player (not a CPU) and the speed is proportional to the player's; in Time Trial speed and start delay are the same of respective settings in KMP file. If S2 = 3, Route Setting 2 (\"cycle\") must be 1, or else the Thwomp will spin endlessly when hit by an item.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_OPT_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_or_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {170,-1,-1}, "dossun(BE)", + { "Route speed", + "Behaviour with route: 1 = Stomp on every route point 2 = Couple of Thwomps, goes left and right before going down; read the description 3 = Grounded Thwomp, goes left and right", + "Start delay in 1/60 seconds after start of countdown. 240 (F0 in hex) = Starts immediately after the \"GO!\"", + "Sleep time in 1/60 seconds after start of countdown", + 0, 0, 0, 0 }}, + /* 0x0dc */ + { "dossunc_soko", "Object not used in any track.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0dd */ + { "boble", "Podoboo from GBA Bowser Castle 3. Route Setting 1 controls the speed (gets added to S1), Route Setting 2 = 1 means that the model flips horizontally.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {149,-1,-1}, "boble(BE)", + { "Speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0de */ + { "K_bomb_car", "Bomb car from Moonview Highway. Route Setting 1 is used for blinking (1 = Left, 2 = Right). Route Setting 2 selects the speed (0 or 1).", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {65,-1,-1}, "K_bomb_car(BE)", + { "Route start point", + "Speed 0 in 1/60 seconds", + "Speed 1 in 1/60 seconds", + 0, 0, 0, 0, 0 }}, + /* 0x0df */ + { "K_bomb_car_dummy", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {67,-1,-1}, "K_bomb_car_dummy", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0e0 */ + { "car_body_dummy", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {152,-1,-1}, "car_body_dummy", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0e1 */ + { "kart_truck_dummy", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {194,-1,-1}, "kart_truck_dummy", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0e2 */ + { "hanachan", "Wiggler from Maple Treeway. Route Setting 1 sets the time in 1/60 seconds of stop in that point. Define the route heights carfefully. If the Wiggler has issues with the body segments, move the route to a more flat area.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {187,-1,-1}, "hanachan(BE2D)", + { "Speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0e3 */ + { "seagull", "Seagull route following from tracks with marine setting.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {236,-1,-1}, "seagull(B)", + { "Speed", + "Number of seagulls", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0e4 */ + { "moray", "Unagi (eel) from Koopa Cape and Chain Chomp Wheel.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_M_ARENA, + obj_charact_r_ta, + &settings_format_uuuu, &settings_format_uuuu, + {211,-1,-1}, "moray(B)", + { "Speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0e5 */ + { "crab", "Crab from GBA Shy Guy Beach. Route Setting 1 sets the time in 1/60 seconds of stop in that point.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {161,-1,-1}, "crab(BE3D)", + { "Speed in units per 1/60 seconds", + "Crab view direction: 0 = Left 1 = Right", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0e6 */ + { "basabasa_dummy", "Object not used in any track.", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0e7 */ + { "CarA2", "Moving Car from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {10,-1,-1}, "CarA2(B)", + { "Speed", + "Acceleration", + "Time of pause", + 0, 0, 0, 0, 0 }}, + /* 0x0e8 */ + { "CarA3", "Moving Car from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {11,-1,-1}, "CarA3(B)", + { "Speed", + "Acceleration", + "Time of pause", + 0, 0, 0, 0, 0 }}, + /* 0x0e9 */ + { "Hwanwan", "Route-following Chain Chomp from DS Peach Gardens. Hwanwan and wanwan only work together if the effect files are absent. Route Setting 2 = 1 plays a rollover animation, originally used on a Competition in Rainbow Road.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {254,-1,-1}, "wanwan(BE)", + { "Speed", + "Amount of item boxes carried", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0ea */ + { "HeyhoBallGBA", "Cannonball landing area, which explodes after 3 seconds, shot by `HeyhoShipGBA'; in GP and VS Race it explodes if touched. The object's scale doesn't affect the explosion effect's dimension. Generally CPUs don't go against this object as they try to avoid them. This objects requires `HeyhoShipGBA' and therefor it can only be used on 6.2. Note that no more than 59 of this object can be used.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {60,-1,-1}, "HeyhoBallGBA(BE)", + { "Route id from where `HeyhoShipGBA' shoots", + "Time between shot and arrival in 1/60 seconds", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0eb */ + { "Twanwan", "Rolling Chain Chomp from Chain Chomp Wheel. The object has two different kind of movements; one being a route and one relative to it's current location based off a sine wave, whereby the wave is controlled by s3 and s4.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_r_s_a, + &settings_format_uuuu, &settings_format_uuuu, + {130,-1,-1}, "Twanwan(B)", + { "Route speed", + "0 = start and wait on first route point 1 = start at object location and enable release action", + "Amplitude", + "Period length", + "Start delay in 1/60 seconds", + "Time until it starts following route in 1/60 seconds (s5 has priority)", + 0, 0 }}, + /* 0x0ec */ + { "cruiserR", "Route-following boat from Daisy Circuit and Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {162,-1,-1}, "cruiser(BE)", + { "Start speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0ed */ + { "bird", "Eagle route following from GCN DK Mountain.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {148,-1,-1}, "bird(B)", + { "Speed", + "Number of eagles", + 0, 0, 0, 0, 0, 0 }}, + /* 0x0ee */ + { "sin_itembox", "Item box following a route back and forth; speed is like sine function. Route Setting 2: 0 = Asymetric; 1 = Symetric.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_ITEMBOX, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {192,-1,-1}, "itembox(B)", + { "Cycle time in 1/60 seconds", + 0, 0, 0, + "Start delay in 1/60 seconds", + 0, 0, 0 }}, + /* 0x0ef */ + { "Twanwan_ue", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {131,-1,-1}, "Twanwan_ue", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0f0 */ + { "BossHanachan", "Big Wiggler boss used in the Wario's Gold Mine competition; follows a route. If S2 of the route point = 0002 (S1 needs to be 0000 otherwise it will try to do different things at the same time and Wiggler's body will glitch out), it will play Wiggler's trick animation once Wiggler gets to the point. See BossHanachan for more information about his route settings.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {187,-1,-1}, "hanachan(BE2D)", + { "Primary Wiggler Speed", + "Unknown", + "Secondary Wiggler Speed", + "Point where wiggler changes to speed two. If passed before wiggler stops, it will not change to speed two. Route settings must be 0000 0001", + "Angry Wiggler Speed", + "Point where wiggler changes to angry speed. It will function as long as the second speed change has been added, and route settings are 0000 0001", + "Time Wiggler stop to being angry (in decimals)", + 0 }}, + /* 0x0f1 */ + { "Kdossunc", "Object not used in any track.", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0f2 */ + { "BossHanachanHead", "Object used in the Wario's Gold Mine competition to show the Wiggler's head on the map. It is not found in the KMP, but adding BossHanachan will automatically generate it in game.", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0f3 */ + { "K_bomb_car1", "Object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_COMMONOBJ | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {66,-1,-1}, "K_bomb_car1(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x0f4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0f5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0f6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0f7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0f8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0f9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0fa */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0fb */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0fc */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0fd */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0fe */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x0ff */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x100 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x101 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x102 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x103 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x104 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x105 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x106 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x107 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x108 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x109 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x10a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x10b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x10c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x10d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x10e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x10f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x110 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x111 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x112 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x113 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x114 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x115 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x116 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x117 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x118 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x119 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x11a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x11b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x11c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x11d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x11e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x11f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x120 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x121 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x122 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x123 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x124 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x125 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x126 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x127 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x128 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x129 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x12a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x12b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x12c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x12d */ + { "dummy", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {171,-1,-1}, "dummy", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x12e */ + { "dokan_sfc", "Pipe from Luigi Circuit and SNES Mario Circuit 3.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {167,-1,-1}, "dokan_sfc(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x12f */ + { "castletree1", "Unsolid swaying tree from Moo Moo Meadows and Mario Circuit.", + OBF_OBJECT | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {156,-1,-1}, "castletree1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x130 */ + { "castletree1c", "Solid swaying tree from Moo Moo Meadows and Mario Circuit.", + OBF_OBJECT | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_s_to, + &settings_format_uuuu, &settings_format_uuuu, + {156,-1,-1}, "castletree1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x131 */ + { "castletree2", "Unsolid swaying tree from Moo Moo Meadows and Mario Circuit. The tree turns to look always to the player.", + OBF_OBJECT | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {157,-1,-1}, "castletree2(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x132 */ + { "castleflower1", "Flower from Mario Circuit.", + OBF_OBJECT | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {155,-1,-1}, "castleflower1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x133 */ + { "mariotreeGC", "Unsolid swaying tree from GCN Mario Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {207,-1,-1}, "mariotreeGC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x134 */ + { "mariotreeGCc", "Solid swaying tree from GCN Mario Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_s_to, + &settings_format_uuuu, &settings_format_uuuu, + {207,-1,-1}, "mariotreeGC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x135 */ + { "donkytree1GC", "Unsolid tree from GCN DK Mountain.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {168,-1,-1}, "donkytree1GC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x136 */ + { "donkytree2GC", "Unsolid tree from GCN DK Mountain.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {169,-1,-1}, "donkytree2GC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x137 */ + { "peachtreeGC", "Unsolid swaying tree from GCN Peach Beach.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {220,-1,-1}, "peachtreeGC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x138 */ + { "peachtreeGCc", "Solid swaying tree from GCN Peach Beach.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {220,-1,-1}, "peachtreeGC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x139 */ + { "npc_mii_a", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {212,-1,-1}, "npc_mii_a", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x13a */ + { "npc_mii_b", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {213,-1,-1}, "npc_mii_b", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x13b */ + { "npc_mii_c", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {214,-1,-1}, "npc_mii_c", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x13c */ + { "obakeblockSFCc", + "Falling block from SNES Ghost Valley 2. In Grand Prix and Offline VS Race falls when touched, in Time Trial and Online VS Race fall is controlled by S2 and S3. This object is solid and animated only on slot 5.3, but isn't scalable (fixed scale 1.3). In other slots it is scalable, but unsolid and doesn't fall. Blocks are solid only if X is between -30985 and 8690 and Z between -16295 and 21430; Y (height) doesn't seem to be important.\n" + "For slot 5.3, the Y angle indicates the fall's direction and not the block's rotation, which remains the same. Block's side measures 250 units (325 on slot 5.3) and the center coincides with geometrical center. If many blocks are used slot 5.3 is recommended. sGV2 uses 602 blocks.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {215,-1,-1}, "obakeblockSFC(B)", + { 0, + "Fall time controller in Time Trial and Online VS Race, controls the delay before starting in seconds after starting of countdown, depending on the value T: T = 0 → Block doesn't fall T > 0 → Block falls after T seconds T = 4 → Block falls immediately after the \"GO!\"", + "Additional delay of X/60 seconds, goes added to S2", + 0, 0, 0, 0, 0 }}, + /* 0x13d */ + { "WLarrowGC", "Arrow with eyes from GCN Waluigi Stadium.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {137,-1,-1}, "WLarrowGC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x13e */ + { "WLscreenGC", "TV from GCN Waluigi Stadium. Nintendo disables it in Multiplayer mode (PFlag = 9).", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {140,-1,-1}, "WLscreenGC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x13f */ + { "WLdokanGC", "Big pipe from GCN Waluigi Stadium, normally combined with `WLwallGC'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {138,-1,-1}, "WLdokanGC(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x140 */ + { "MarioGo64c", "The \"GO\" sign from N64 Mario Raceway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {84,-1,-1}, "MarioGo64(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x141 */ + { "PeachHunsuiGC", "Fountain from GCN Peach Beach.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {113,-1,-1}, "PeachHunsuiGC(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x142 */ + { "kinokoT1", "Moving bouncy mushroom decoration from Mushroom Gorge.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {196,-1,-1}, "kinokoT1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x143 */ + { "kinokoT2", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {197,-1,-1}, "kinokoT2", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x144 */ + { "pylon01", "Street cone from Daisy Circuit. Only slots 3.1 and 7.1 support this object with a limit of 30. Colors can be changed in the CLR0 (maybe even added).", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {231,-1,-1}, "pylon01(B)", + { "Color: 0 = Red 1 = Blue 2 = Yellow", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x145 */ + { "PalmTree", "Solid palm from Coconut Mall. It's the largest tree of Mario Kart Wii.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {112,-1,-1}, "PalmTree(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x146 */ + { "parasol", "Parasol from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {219,-1,-1}, "parasol(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x147 */ + { "cruiser", "Boat from Daisy Circuit and Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {162,-1,-1}, "cruiser(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x148 */ + { "K_sticklift00", "Barrier gate from DK Summit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {70,-1,-1}, "K_sticklift00(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x149 */ + { "heyho2", "Manager of the Shy Guy Ski Lift from DK Summit. Spawns 30 Chairlifts that travel on a route. Route setting 0 removes the Shy Guys. Route setting 1 randomly makes either 0, 1 or 2 randomly coloured Shy Guys spawn on the chair.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_TRACK, + obj_charact_r_t, + &settings_format_uuuu, &settings_format_uuuu, + {189,69,-1}, "heyho2(B);K_chairlift00(B)", + { "Which MDL0 from K_chairlift00.brres is loaded: 0 = K_chairlift00 1 = K_chairlift01", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x14a */ + { "HeyhoTreeGBAc", "Small solid tree from GBA Shy Guy Beach.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {62,-1,-1}, "HeyhoTreeGBA(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x14b */ + { "MFaceBill", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {76,-1,-1}, "MFaceBill", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x14c */ + { "truckChimSmk", "Smoke generator on Wario's Gold Mine and Maple Treeway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {244,-1,-1}, "truckChimSmk(BE)", + { "Time to start smoking", + "0 = Default smoke variant, doesnt vanish when touched. 1 = Different smoke variant (fluffy), doesnt vanish when touched. 2 = 1 but vanishes when touched. 3 = Different smoke variant (thick), doesnt vanish when touched. 4 = Same as 2?. 5+ = No smoke but \"explode\" effect", + 0, 0, 0, 0, 0, 0 }}, + /* 0x14d */ + { "MiiObj01", "Mii audience from Luigi Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {88,-1,-1}, "MiiObj01(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x14e */ + { "MiiObj02", "Mii audience from Luigi Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {89,-1,-1}, "MiiObj02(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x14f */ + { "MiiObj03", "Mii audience from Luigi Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {90,-1,-1}, "MiiObj03(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x150 */ + { "gardentreeDS", "Unsolid and thin tree from DS Peach Gardens.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {177,-1,-1}, "gardentreeDS(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x151 */ + { "gardentreeDSc", "Solid and thin tree from DS Peach Gardens.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {177,-1,-1}, "gardentreeDS(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x152 */ + { "FlagA1", "Starting line flag from Wario's Gold Mine.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {40,-1,-1}, "FlagA1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x153 */ + { "FlagA2", "Starting line flag from Moo Moo Meadows.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {41,-1,-1}, "FlagA2(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x154 */ + { "FlagB1", "\"W\" flag from Wario's Gold Mine.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {45,-1,-1}, "FlagB1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x155 */ + { "FlagB2", "\"L\" Flag from Luigi Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {46,-1,-1}, "FlagB2(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x156 */ + { "FlagA3", "\"Mario Kart\" flag from DS Delfino Square. Strangely, scales must follow a precise proportion: Xscale < Yscale < Zscale, or game freezes. It's a bit buggy, so it's better use another \"FlagA\" object and import the original texture.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {42,-1,-1}, "FlagA3(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x157 */ + { "DKtreeA64", "Unsolid tree from N64 DK's Jungle Parkway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {20,-1,-1}, "DKtreeA64(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x158 */ + { "DKtreeA64c", "Solid tree from N64 DK's Jungle Parkway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {20,-1,-1}, "DKtreeA64(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x159 */ + { "DKtreeB64", "Usolid tree from N64 DK's Jungle Parkway with a small crown.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {21,-1,-1}, "DKtreeB64(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x15a */ + { "DKtreeB64c", "Solid tree from N64 DK's Jungle Parkway with a small crown.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {21,-1,-1}, "DKtreeB64(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x15b */ + { "TownTreeDSc", "Solid tree from DS Delfino Square with orange leaves.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {128,-1,-1}, "TownTreeDS(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x15c */ + { "Piston", "Piston machine decoration from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {114,-1,-1}, "Piston(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x15d */ + { "oilSFC", "Oil puddle from SNES Mario Circuit 3.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {216,-1,-1}, "oilSFC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x15e */ + { "DKmarutaGCc", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {16,-1,-1}, "DKmarutaGC", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x15f */ + { "DKropeGCc", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {18,-1,-1}, "DKropeGC", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x160 */ + { "mii_balloon", "Floating balloon from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {208,-1,-1}, "mii_balloon(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x161 */ + { "windmill", "Rotating windmill decoration from Moo Moo Meadows.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {255,-1,-1}, "windmill(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x162 */ + { "dossun", "Thwomp caged in N64 Bowser's Castle growling when a player approaches; it's an unsolid decoration.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {170,-1,-1}, "dossun(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x163 */ + { "TownTreeDS", "Unsolid tree from DS Delfino Square with orange leaves.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {128,-1,-1}, "TownTreeDS(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x164 */ + { "Ksticketc", "Port, opens when approaching.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {75,-1,-1}, "Ksticketc(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x165 */ + { "monte_a", "Pianta alone.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {210,-1,-1}, "monte_a(B)", + { 0, + "1 = Enable player rotation and clapping.", + "Distance at which it rotates to face the player.", + "Distance at which it loads the clapping chr0.", + 0, 0, 0, 0 }}, + /* 0x166 */ + { "MiiStatueM1", "Mario statue from Block Plaza. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {108,-1,-1}, "MiiStatueM1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x167 */ + { "ShMiiObj01", "Mii audience from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {120,-1,-1}, "ShMiiObj01(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x168 */ + { "ShMiiObj02", "Mii audience from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {121,-1,-1}, "ShMiiObj02(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x169 */ + { "ShMiiObj03", "Mii audience from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {122,-1,-1}, "ShMiiObj03(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x16a */ + { "Hanabi", "Fireworks used in cutscene tracks. Only loads in GP Mode.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {59,-1,-1}, "Hanabi(E)", + { "Frames until the firework \"pops\" in the intro camera sequence.", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x16b */ + { "miiposter", "Mii posters from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {209,-1,-1}, "miiposter(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x16c */ + { "dk_miiobj00", "Mii audience from DK Summit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {165,-1,-1}, "dk_miiobj00(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x16d */ + { "light_house", "Lighthouse from Daisy Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {204,-1,-1}, "light_house(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x16e */ + { "r_parasol", "Parasol from GBA Shy Guy Beach; it opens or closes itself when is hit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {233,-1,-1}, "r_parasol(B)", + { "Starting state: 0 = Open 1 = Closed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x16f */ + { "obakeblock2SFCc", + "Falling block from SNES Ghost Valley 2. In Grand Prix and Offline VS Race falls when touched, in Time Trial and Online VS Race fall is controlled by S2 and S3. This object is solid and animated only on slot 5.3, but isn't scalable (fixed scale 1.3). In other slots it is scalable, but unsolid and doesn't fall. Blocks are solid only if X is between -30985 and 8690 and Z between -16295 and 21430; Y (height) doesn't seem to be important.\n" + "For slot 5.3, the Y angle indicates the fall's direction and not the block's rotation, which remains the same. Block's side measures 250 units (325 on slot 5.3) and the center coincides with geometrical center. If many blocks are used slot 5.3 is recommended. sGV2 uses 602 blocks.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {215,-1,-1}, "obakeblockSFC(B)", + { 0, + "Fall time controller in Time Trial and Online VS Race, controls the delay before starting in seconds after starting of countdown, depending on the value T: T = 0 → Block doesn't fall T > 0 → Block falls after T seconds T = 4 → Block falls immediately after the \"GO!\"", + "Additional delay of X/60 seconds, goes added to S2", + 0, 0, 0, 0, 0 }}, + /* 0x170 */ + { "obakeblock3SFCc", + "Falling block from SNES Ghost Valley 2. In Grand Prix and Offline VS Race falls when touched, in Time Trial and Online VS Race fall is controlled by S2 and S3. This object is solid and animated only on slot 5.3, but isn't scalable (fixed scale 1.3). In other slots it is scalable, but unsolid and doesn't fall. Blocks are solid only if X is between -30985 and 8690 and Z between -16295 and 21430; Y (height) doesn't seem to be important.\n" + "For slot 5.3, the Y angle indicates the fall's direction and not the block's rotation, which remains the same. Block's side measures 250 units (325 on slot 5.3) and the center coincides with geometrical center. If many blocks are used slot 5.3 is recommended. sGV2 uses 602 blocks.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {215,-1,-1}, "obakeblockSFC(B)", + { "?", + "Fall time controller in Time Trial and Online VS Race, controls the delay before starting in seconds after starting of countdown, depending on the value T: T = 0 → Block doesn't fall T > 0 → Block falls after T seconds T = 4 → Block falls immediately after the \"GO!\"", + "Additional delay of X/60 seconds, goes added to S2", + 0, 0, 0, 0, 0 }}, + /* 0x171 */ + { "koopaFigure", "Bowser statue from Bowser's Castle.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_BRASD | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {199,-1,-1}, "koopaFigure(B2D)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x172 */ + { "pukupuku", "Cheep Cheep fish.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_M_ARENA, + obj_charact_ta, + &settings_format_uuuu, &settings_format_uuuu, + {230,-1,-1}, "pukupuku(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x173 */ + { "v_stand1", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {246,-1,-1}, "v_stand1", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x174 */ + { "v_stand2", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {247,-1,-1}, "v_stand2", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x175 */ + { "leaf_effect", "Leaf effect when player drives or flies through tree leafs.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {202,-1,-1}, "leaf_effect(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x176 */ + { "karehayama", "Leaf pile from Maple Treeway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uxuu, &settings_format_uuuu, + {193,-1,-1}, "karehayama(BE)", + { 0, + "Item to give", + "Chance to give an item", + 0, 0, 0, 0, 0 }}, + /* 0x177 */ + { "EarthRing", "Earth luminous ring from Rainbow Road.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {28,-1,-1}, "EarthRing(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x178 */ + { "SpaceSun", "The sun in Rainbow Road.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {123,-1,-1}, "SpaceSun(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x179 */ + { "BlackHole", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {6,-1,-1}, "BlackHole", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x17a */ + { "StarRing", "Launch Star from Rainbow Road.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {125,-1,-1}, "StarRing(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x17b */ + { "M_obj_kanban", "Yellow/black fence which can be found in competitions; object not used in any track.", + OBF_OBJECT | OBF_BRRES | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {79,-1,-1}, "M_obj_kanban", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x17c */ + { "MiiStatueL1", "Luigi statue from Daisy Circuit. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {105,-1,-1}, "MiiStatueL1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x17d */ + { "MiiStatueD1", "Daisy statue from Daisy Circuit. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {102,-1,-1}, "MiiStatueD1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x17e */ + { "MiiSphinxY1", "Mii Sphinx without head, unsolid. Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {97,-1,-1}, "MiiSphinxY1", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x17f */ + { "MiiSphinxY2", "Yoshi Sphinx from Dry Dry Ruins. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {98,-1,-1}, "MiiSphinxY2(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x180 */ + { "FlagA5", "Start line flag from Koopa Cape. It's less buggy than `FlagA3'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {44,-1,-1}, "FlagA5(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x181 */ + { "CarB", "Unsolid car decoration from Coconut Mall.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {12,-1,-1}, "CarB(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x182 */ + { "FlagA4", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {43,-1,-1}, "FlagA4", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x183 */ + { "Steam", "Steam effect from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {126,-1,-1}, "Steam(E)", + { 0, + "Delay before starting in 1/60 seconds.", + "Length of steam effect.", + "Time between two steam effects.", + 0, 0, 0, 0 }}, + /* 0x184 */ + { "Alarm", "Alarm effect from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {0,-1,-1}, "Alarm(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x185 */ + { "group_monte_a", "Pianta audience.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {185,-1,-1}, "group_monte_a(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x186 */ + { "MiiStatueL2", "Luigi statue from Block Plaza. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {106,-1,-1}, "MiiStatueL2(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x187 */ + { "MiiStatueD2", "Daisy statue from Block Plaza. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {103,-1,-1}, "MiiStatueD2(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x188 */ + { "MiiStatueP1", "Peach statue from Block Plaza. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {110,-1,-1}, "MiiStatueP1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x189 */ + { "SentakuDS", "Hanging clothes decoration.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {119,-1,-1}, "SentakuDS(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x18a */ + { "fks_screen_wii", "TV from Funky Stadium.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {174,-1,-1}, "fks_screen_wii(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x18b */ + { "KoopaFigure64", "Bowser statue from N64 Bowser's Castle; it spits fire for 5 seconds, then doesn't nothing for 3 seconds, then spits fire again. In GP and VS Race the object is activated by the proximity of a player.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {74,-1,-1}, "KoopaFigure64(BE)", + { "Units of distance from where KoopaFigure64 starts spitting fire", + "Fire function and effect: 0 = Decoration with default effect 1 = Obstacle with L effect", + "Spitting fire start delay in 1/60 seconds after start of countdown. 0 = Freeze of the game 240 (F0 in hex) = Starts spitting fire immediately after the \"GO!\"", + 0, 0, 0, 0, 0 }}, + /* 0x18c */ + { "b_teresa", "Flying Boos from SNES Ghost Valley 2 controlled by AREA type 07, using exactly one object. Object's location doesn't matter, Boos are always viewed when players enter in related AREA.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {144,-1,-1}, "b_teresa(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x18d */ + { "MiiStatueDK1", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {104,-1,-1}, "MiiStatueDK1", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x18e */ + { "MiiKanban", "Luigi face sign from Luigi Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {87,-1,-1}, "MiiKanban(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x18f */ + { "BGteresaSFC", "Flying Boo from SNES Ghost Valley 2; optimal scale values for this object are values around 3 and 4.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {1,-1,-1}, "BGteresaSFC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x190 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x191 */ + { "kuribo", "Goomba.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_r_s_to, + &settings_format_uuuu, &settings_format_uuuu, + {201,-1,-1}, "kuribo(B2D)", + { "Respawn time", + "Speed", + "Animation speed", + 0, 0, 0, 0, 0 }}, + /* 0x192 */ + { "choropu", "Moving Monty Mole from Moo Moo Meadows. Can't be used together with `choropu2'. Route Setting 2: 0 = Burrow, 1 = Pop out.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {158,-1,-1}, "choropu(BE)", + { "Speed", + "Start delay in 1/60 seconds", + 0, 0, 0, 0, 0, 0 }}, + /* 0x193 */ + { "cow", "Master cow following a route followed by some more cows (from Moo Moo Meadows). The master cow starts at the first point of the route, all other cows around the object position.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {160,-1,-1}, "cow(B5D)", + { "Number of slave cows following the master cow unordered", + "Unknown", + "Start delay of the slave cows in 1/60 seconds. The master cow starts a few seconds earlier", + 0, 0, 0, 0, 0 }}, + /* 0x194 */ + { "pakkun_f", "Piranha Plant from GCN Mario Circuit, normally combined with `pakkun_dokan'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_s_to, + &settings_format_uuuu, &settings_format_uuuu, + {218,-1,-1}, "pakkun_f(B2D)", + { "Idle time in 1/60 seconds between 2 attacks in time trial and online", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x195 */ + { "WLfirebarGC", "Rotating fire bars from GCN Waluigi Stadium. Can't be used together with `koopaFirebar'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {139,-1,-1}, "WLfirebarGC(B)", + { "Fireballs per arm", + "Rotations X per 360 seconds. Counterclockwise = X Clockwise = 65536 - X", + "Fireball distance", + "Number or arms", + 0, 0, 0, 0 }}, + /* 0x196 */ + { "wanwan", "Chain Chomp. Snaps in a wedge shape that is directed to a given point. Hwanwan and wanwan only work together if the effect files are absent.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {254,-1,-1}, "wanwan(BE)", + { "Length of chain in game units", + "Unknown", + 0, + "Let (X,Y) be the the center of the wedge points to, then (X/10) if X positive, 65536+(X/10) if X negative", + "Let (X,Y) be the the center of the wedge points to, then (Y/10) if Y positive, 65536+(Y/10) if Y negative", + "Time to wander around (in 1/60 seconds); if 0, about 5 seconds is used. Add 3,5 seconds for full cycle time", + "(Width of wedge in degrees-35)/2", + 0 }}, + /* 0x197 */ + { "poihana", "Cataquack from GCN Peach Beach. It flings players into the air. In GP and VS race, it looks for players. In Time Trials, it flings only if touched. It is likely possible to add further colors by modifying the Pat0.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {228,-1,-1}, "poihana(BE2D)", + { "Color. Normal settings: 1 = Red 2 = Violet 3 = Green Other settings: 0 = Violet ≥4 = Green", + "Units of distance from where `poihana' starts looking for players", + 0, 0, 0, 0, 0, 0 }}, + /* 0x198 */ + { "DKrockGC", "Falling rock from GCN DK Mountain, it respawns only if its Route's header has Cycle set to 1. Slot 8.3 enables its SFX.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {17,-1,-1}, "DKrockGC(BE)", + { "Not 0: Delay of ~1.5 seconds for the second rock", + "Minimal cycle time in 1/60 seconds", + "Speed", + "Bouncing factor", + 0, 0, 0, 0 }}, + /* 0x199 */ + { "sanbo", "Pokey from Dry Dry Desert and DS Desert Hills. Route Setting 1 sets the time in 1/60 seconds of stop in that point. Its SFX is exclusive to certain slots.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {235,-1,-1}, "sanbo(BED)", + { "Speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x19a */ + { "choropu2", "Monty Mole from DS Peach Gardens. Can't be used together with `choropu'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {158,-1,-1}, "choropu(BE)", + { "Hide time in 1/60 seconds", + "Start delay in 1/60 seconds", + 0, 0, 0, 0, 0, 0 }}, + /* 0x19b */ + { "TruckWagon", "Truck from Wario's Gold Mine. Route Setting 1 changes the speed (if 0 it uses the speed setting in S1). Route Setting 2: 0x0: Normal behavior 0x1: Truck sways left and right harder and wont change back until the end of the route. 0x2: Truck sways left and right.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {129,-1,-1}, "TruckWagon(BE)", + { "Speed", + "Delay before the second (groups) truck spawns (time in frames)", + "Delay until the first (groups) truck spawns (time in frames)", + "0 = collision, allows for effects/animations and spawns 12 groups of trucks. 1 = No collision, loads _LOD model instead and limits to 3 TruckWagons on a route.", + 0, 0, 0, 0 }}, + /* 0x19c */ + { "heyho", "Shy Guy obstacle from DK Summit. Additional colors can probably be added by expanding the Pat0.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {188,-1,-1}, "heyho(B)", + { "Speed", + "Color. Normal settings: 0 = Red 1 = Yellow 2 = Green", + 0, 0, 0, 0, 0, 0 }}, + /* 0x19d */ + { "Press", "Press from Toad's Factory (over the belts). The entered position is the sleeping position, so must be placed over the ground (600 is the default value); the total cycle time depends on the sleep time and the falling height.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {115,-1,-1}, "Press(BE)", + { 0, + "Start delay in 1/60 seconds", + "Sleep time in 1/60 seconds; Special case 0 = One time shoot", + 0, 0, 0, 0, 0 }}, + /* 0x19e */ + { "Press_soko", "Object not used in any track.", + OBF_OBJFLOW, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x19f */ + { "pile", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {224,-1,-1}, "pile", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1a0 */ + { "choropu_ground", "Object not used in any track.", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1a1 */ + { "WLfireringGC", "Rotating fire ring from GCN Waluigi Stadium. Scale settings refer to fireballs' dimensions and position coordinates to center's coordinates.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {139,-1,-1}, "WLfirebarGC(B)", + { "Number of fireballs", + "Rotations X in 1/60 seconds. Counterclockwise = X Clockwise = 65536 - X", + "Pulsation factor, shrinks and enlarges the radius. Special cases: S3 = 0 or S4 = 0 no pulsation; S3 ≥ S4 negative pulsation", + "Radius", + 0, 0, 0, 0 }}, + /* 0x1a2 */ + { "pakkun_dokan", "Pipe of potted Piranha Plant, normally combined with `pakkun_f'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {217,-1,-1}, "pakkun_dokan(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1a3 */ + { "begoman_spike", "Topman from Galaxy Colosseum; object not used in any track. To appear, it (or they) must be the first object of section GOBJ. Note that the rings are hardcoded to have their centre at (0,0)", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_s_o, + &settings_format_uuuu, &settings_format_uuuu, + {147,-1,-1}, "begoman_spike(BE)", + { "ID (1-based), used in begoman_manager to control when they appear", + "The radius of the 1st ring in which the topman is allowed to roam in.", + "Time in frames from the start of the race until the topman will switch from the 1st ring to the 2nd ring.", + "The radius of the 2nd ring in which the topman is allowed to roam in.", + "Time in frames from the start of the race until the topman will switch from the 2nd ring to the 3rd ring.", + "The radius of the 3rd ring in which the topman is allowed to roam in.", + "Time in frames from the start of the race until the topman will switch from the 3rd ring to the 4th ring.", + "The radius of the 3rd ring in which the topman is allowed to roam in." }}, + /* 0x1a4 */ + { "FireSnake", "Grounded Podoboo from DS Desert Hills. It bounces casually around its position point. It works with the object `sunDS', so it only works on slots 3.1 and 7.1.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {39,-1,-1}, "FireSnake(BE)", + { "Route id from where `sunDS' drops", + "Time before vanishing in 1/60 seconds", + 0, 0, 0, 0, 0, 0 }}, + /* 0x1a5 */ + { "koopaFirebar", "Rotating firebar from Bowser's Castle. Specific object rotation values cause strange behavior, such as 0,90,0. Can't be used together with `WLfirebarGC'.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {200,139,-1}, "koopaFirebar(B);WLfirebarGC(B)", + { "Number of fireballs per hand", + "Rotations X per 1/360 seconds. Counterclockwise = X Clockwise = 65536 - X", + "Distance between fireballs", + "Number of hands", + 0, 0, 0, 0 }}, + /* 0x1a6 */ + { "Epropeller", "Electric propeller from Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {33,-1,-1}, "Epropeller(BE)", + { "Rotations per 12 seconds", + "Direction: 0 = Clockwise 1 = Counterclockwise", + 0, 0, 0, 0, 0, + "[?] Unused in the normal game. With this cheat code this setting can be used to change the speed to S1/(30*S8) instead of S1/30, allowing for slower Epropellers." }}, + /* 0x1a7 */ + { "dc_pillar_c", "Object not used in any track.", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1a8 */ + { "FireSnake_v", "Grounded Podoboo from Grumble Volcano. It jump from its position point describing a parabola and after landing bounces casually around the arrival point.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {39,-1,-1}, "FireSnake(BE)", + { "Start delay in 1/60 seconds after starting of countdown. 240 (F0 in hex) = Starts immediately after the \"GO!\"", + "Duration and respawn factor, K reference value is 600 (258 in hex): X < K → Duration time in 1/60 seconds, then respawn without loading vanishing animation X = K → 10 seconds of duration, then respawn after other 10 seconds X > K → 10 seconds of duration, then respawn after (X - 600)/60 seconds. Normally is used the third option", + "Units of distance from position point to landing point following the Y angulation", + 0, 0, 0, 0, 0 }}, + /* 0x1a9 */ + { "honeBall", "Object not used in regular tracks. Used in the N64 Bowser castle competition with Dry Bowser.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_COMMONOBJ | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {190,-1,-1}, "honeBall(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1aa */ + { "puchi_pakkun", "Small Piranha Plant from N64 Mario Raceway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {229,-1,-1}, "puchi_pakkun(BD)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1ab */ + { "sanbo_big", "Giant Pokey used in the Thwomp Desert Competition, where its scale is 7 on the X, Y, and Z axis KMP KMP cloud seems to slightly corrupt the giant pokey, as its sound will not be loaded and its head will not appear on the minimap.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {235,-1,-1}, "sanbo(BED)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1ac */ + { "sanbo_big.2", "Object not used in any track. Seems to be identical to sanbo_big.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {235,-1,-1}, "sanbo(BED)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1ad */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ae */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1af */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1b9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ba */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1bb */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1bc */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1bd */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1be */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1bf */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1c9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ca */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1cb */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1cc */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1cd */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ce */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1cf */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1d9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1da */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1db */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1dc */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1dd */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1de */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1df */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1e9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ea */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1eb */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ec */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ed */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ee */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1ef */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1f0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1f1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1f2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1f3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1f4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x1f5 */ + { "kinoko_ud", "Moving decoration mushroom from Mushroom Gorge.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_SCALE | OBF_ROTATE | OBF_FALLDOWN | OBF_OBJFLOW | OBF_SOLID | OBF_ROAD | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {195,-1,-1}, "kinoko(B4K)", + { "[?] Texturing: 0 = Light 1 = Dark", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1f6 */ + { "kinoko_bend", "Bouncy mushroom.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_SCALE | OBF_ROTATE | OBF_FALLDOWN | OBF_OBJFLOW | OBF_SOLID | OBF_ROAD | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {195,-1,-1}, "kinoko(B4K)", + { "Texturing: 0 = Light 1 = Dark", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1f7 */ + { "VolcanoRock1", "Floating drivable rock (at Grumble Volcano in the lava), it moves like a cosine on the x-axis and the y-axis.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {136,-1,-1}, "VolcanoRock1(B2K)", + { "Model/Effect/KCL file to use (0 or 1)", + "Time to go from x1 to x2 and back in 1/60 seconds", + "Max X movement in both directions", + "Offsets starting X and Y position within the specified movement in 1/60 seconds", + "Time of one period on the y-axis in 1/60 secons", + "The amplitude of the cosine on the y-axis", + 0, 0 }}, + /* 0x1f8 */ + { "bulldozer_left", "The moving bulldozer from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {150,-1,-1}, "bulldozer_left(BK)", + { 0, + "Movement Distance", + "Movement Time", + "Time before starting", + "Time of standing still", + 0, 0, 0 }}, + /* 0x1f9 */ + { "bulldozer_right", "Moving bulldozer from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {151,-1,-1}, "bulldozer_right(BK)", + { 0, + "Movement distance", + "Movement time", + "Time before starting", + "Time of standing still", + 0, 0, 0 }}, + /* 0x1fa */ + { "kinoko_nm", "Green mushroom.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_SCALE | OBF_ROTATE | OBF_FALLDOWN | OBF_OBJFLOW | OBF_SOLID | OBF_ROAD | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {195,-1,-1}, "kinoko(B4K)", + { "Texturing: 0 = Light 1 = Dark", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1fb */ + { "Crane", "Crane platform Toad's Factory. Slot 1.4 enables its SFX.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {13,-1,-1}, "Crane(BK)", + { 0, + "Movement Distance", + "Movement Time", + "Time before starting", + 0, 0, 0, 0 }}, + /* 0x1fc */ + { "VolcanoPiece", "Floating, shaking and sinking driveable rock from Grumble Volcano. Slot 3.4 enables its SFX The normal KCL file is always active, KCL b activates when the rock starts shaking and falls with the rock, KCL c activates when the rock starts to fall (It stays in place while the rock falls)", + OBF_OPTIONAL | OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_SPECIAL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {135,-1,-1}, "VolcanoPiece1(B42K)", + { "Model/KCL reference", + "Time until it shakes in seconds at 150cc and mirror mode (already starts 4 seconds before in-game timer)", + "Time it shakes in seconds at 150cc and mirror mode", + "Time until it shakes in seconds at 100cc (already starts 4 seconds before in-game timer)", + "Time it shakes in seconds at 100cc", + "Time until it shakes in seconds at 50cc (already starts 4 seconds before in-game timer)", + "Time it shakes in seconds at 50cc", + "Time in frames between shaking and falling (always 30 frames on GV)" }}, + /* 0x1fd */ + { "FlamePole", "Ground fire geyser from Bowser's Castle.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {49,-1,-1}, "FlamePole(BEK)", + { "Cycle time in 1/60 seconds + 9 seconds", + "Start delay in 1/60 seconds", + "Size factor", + 0, 0, 0, 0, 0 }}, + /* 0x1fe */ + { "TwistedWay", "Moving section from Bowser's Castle.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {132,-1,-1}, "TwistedWay(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x1ff */ + { "TownBridgeDSc", "Drawbridge from DS Delfino Square; the bridge in DS Delfino Square is created using two of these objects face to face.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {127,-1,-1}, "TownBridgeDS(B3K)", + { "Angle α of inclination in degrees, with 0°≤α<45°; if α ≥ 45° causes beauty flaws and stops the bridge at 45°", + "Time in 1/60 seconds used to lift and lower", + "Time in 1/60 seconds of stop after lift", + "Time in 1/60 seconds of stop after lowering", + 0, 0, 0, 0 }}, + /* 0x200 */ + { "DKship64", "Ferry-boat and audience from N64 DK's Jungle Parkway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_ALWAYS_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_TRACK, + obj_charact_r_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {19,178,180}, "DKship64(BE);group_enemy_a(B);group_enemy_b(B)", + { "Speed", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x201 */ + { "kinoko_kuki", "Object not used in any track. It is the stem of both green and bouncy mushrooms.", + OBF_OBJFLOW | OBF_SOLID, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {-1,-1,-1}, 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x202 */ + { "DKturibashiGCc", "Wooden, Swaying bridge from GCN DK Mountain. The object collision is not affected by rotation, so all rotation values need to be 0 in order to work properly.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_SCALE | OBF_ROTATE | OBF_FALLDOWN | OBF_OBJFLOW | OBF_SOLID | OBF_ROAD | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {22,-1,-1}, "DKturibashiGC(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x203 */ + { "FlamePoleEff", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {50,-1,-1}, "FlamePoleEff", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x204 */ + { "aurora", "Moving section from Rainbow Road. The object collision is not affected by rotation, so all rotation values need to be 0 in order to work properly.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {143,-1,-1}, "aurora(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x205 */ + { "venice_saku", "Fence gate from Delfino Pier.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_s_a, + &settings_format_uuuu, &settings_format_uuuu, + {251,-1,-1}, "venice_saku(BK)", + { "Time until it goes up (in frames)", + "How far up it goes", + "Speed that it moves up", + 0, 0, 0, 0, 0 }}, + /* 0x206 */ + { "casino_roulette", "The rotating parts of Chain Chomp Wheel; when used with KCL flag 0x1D, the road slowly around the object counterclockwise. X, Y and Z positions and rotations need to be 0 and scales need to be 1 in order to work properly. Arena slot 1.4 enables functionality that causes players to additionally rotate the direction they are facing on both the object by itself and on KCL flag 0x1D.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_s_a, + &settings_format_uuuu, &settings_format_uuuu, + {153,-1,-1}, "casino_roulette(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x207 */ + { "BossField01_OBJ1", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {8,-1,-1}, "BossField01_OBJ1", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x208 */ + { "dc_pillar", "Falling pillar from Dry Dry Ruins.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {163,-1,-1}, "dc_pillar(BEK)", + { "Time before falling in 1/60 seconds in 150cc and mirror mode", + "Final angle in degrees (0° = vertical, 90° = horizontal), normally ≥ 90°", + "Time before falling in 1/60 seconds in 100cc mode", + "Time before falling in 1/60 seconds in 50cc mode", + 0, 0, 0, 0 }}, + /* 0x209 */ + { "dc_sandcone", "Pile of sand from Dry Dry Ruins.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {164,-1,-1}, "dc_sandcone(BEK)", + { "Growth speed", + "Final size", + "Start delay before it starts to grow in 1/60 seconds (starts 4 seconds before the timer in-game)", + 0, 0, 0, 0, 0 }}, + /* 0x20a */ + { "venice_hasi", "Bridge from Delfino Pier. Animation capped at 30 frames. S3 might be a relic of old animation length.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_s_a, + &settings_format_uuuu, &settings_format_uuuu, + {249,-1,-1}, "venice_hasi(BEK)", + { "Time until the bridge goes up (and animation starts playing)", + "Animation / Bridge opening speed (0x3 normal, 0x1 faster, 0x10 slow)", + "Unknown", + 0, 0, 0, 0, 0 }}, + /* 0x20b */ + { "venice_gondola", "Object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME | OBF_MULTI_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {248,210,-1}, "venice_gondola;monte_a(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x20c */ + { "quicksand", "Thwomp Desert controller. Arena slot 1.5 enables its SFX. `s_itembox' is affected by the push/pull factor on slots other than arena slot 1.5, causing buggy interactions. ", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {232,170,-1}, "quicksand(BE);dossun(BE)", + { "[?] 0 = Use Thwomp 1 = Don't use Thwomp", + "[?] Sand shake wave length. 0 by default if s1 = 0", + "[?] Time until sand wave reaches the end. 0 by default if s1 = 0. Must not be 0 if s1 = 1", + "[?] Sand push and pull strength.", + 0, 0, 0, 0 }}, + /* 0x20d */ + { "bblock", "Block parts from Block Plaza, with statues.", + OBF_OPTIONAL | OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_NO_FILE | OBF_SPECIAL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_ARENA, + obj_charact_s_a, + &settings_format_uuuu, &settings_format_uuuu, + {146,99,-1}, "bblock1(B18K);MiiStatue1", + { "reference to both KCL and BRRES (1 to 9)", + "Time when it falls down", + "Time it shakes before falling down (always 3 in original)", + 0, 0, 0, 0, 0 }}, + /* 0x20e */ + { "ami", "Bouncing bridge from Maple Treeway. The collision's rotation and scaling are hard-coded, so it is recommended to keep the object's rotation at 0,180,0 and scale at 1,1,1.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {142,-1,-1}, "ami(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x20f */ + { "M_obj_jump", "Trick ramp used on backwards competitions; object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_COMMONOBJ | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {77,-1,-1}, "M_obj_jump(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x210 */ + { "starGate", "Gates used in some competitions; object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_KCL | OBF_COMMONOBJ | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {239,-1,-1}, "starGate(BEK)", + { "Number (ordered)", + "Trigger", + 0, 0, 0, 0, 0, 0 }}, + /* 0x211 */ + { "RM_ring1", "Driveable ring zones from Galaxy Colosseum; object not used in any track.", + OBF_OPTIONAL | OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_SPECIAL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_s_o, + &settings_format_uuuu, &settings_format_uuuu, + {117,-1,-1}, "RM_ring1(B8K)", + { "Ring ID; 1 = Outer ring 2 = ring in the middle 3 = Inner ring", + "Time after course start when the ring starts shaking (seconds)", + "Time that ring shakes (seconds)", + "[?] Delay after ring shakes before falling down (1/60 seconds)", + 0, 0, 0, 0 }}, + /* 0x212 */ + { "FlamePole_v", "Fire geyser without a base.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {51,-1,-1}, "FlamePole_v(BE)", + { "Time outside the lava, K reference value is 500 (1F4 in hex): X < K → Stays outside the lava for 5 seconds X ≥ K → Stays outside the lava for X/100 seconds", + "Start delay in 1/60 seconds; it starts 3 seconds before the \"GO!\"", + "Scale factor; values up to 3 shrink the size, 4 and above grow the size", + "Cycle time in 1/60 seconds + 8.25 seconds", + 0, 0, 0, 0 }}, + /* 0x213 */ + { "M_obj_s_jump", "Ramp used on competitions; object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_COMMONOBJ | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {81,-1,-1}, "M_obj_s_jump(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x214 */ + { "InsekiA", "Asteroids from Rainbow Road.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {63,-1,-1}, "InsekiA(B)", + { 0, + "0 = Clockwise, 1 = Counterclockwise", + "Rotation speed.", + "Rotation offset.", + 0, 0, 0, 0 }}, + /* 0x215 */ + { "InsekiB", "Star bits from Rainbow Road.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {64,-1,-1}, "InsekiB(B)", + { 0, + "0 = Clockwise, 1 = Counterclockwise", + "Rotation speed.", + "Rotation offset.", + 0, 0, 0, 0 }}, + /* 0x216 */ + { "FlamePole_v_big", "Really, really big fire geyser without a base from N64 Bowser's Castle. Time outside the lava is regulated by S1 if S3 = 1, if not it stays outside the lava for 5 seconds independently form the valuse of S1.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {52,51,-1}, "FlamePole_v_big(E);FlamePole_v(BE)", + { "If S3 = 1, time outside the lava in 1/100 s, K reference value is 500 (1F4 in hex): X < K → Stays outside the lava for 5 seconds X ≥ K → Stays outside the lava for X/100 seconds.", + "Start delay in 1/60 seconds, it starts 3 seconds before the \"GO!\" if S3 = 0, if not 2 seconds before the \"GO!\"", + "S1 activator and S2 conditioner: 0 = S1 Deactivated 1 = S1 Activated", + 0, 0, 0, 0, 0 }}, + /* 0x217 */ + { "Mdush", "Small jump ramp from Mario Circuit and Moo Moo Meadows.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {86,-1,-1}, "Mdush(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x218 */ + { "HP_pipe", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {58,-1,-1}, "HP_pipe", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x219 */ + { "DemoCol", "Collision zone from the losing demo screen, located in a grass part from Mario Circuit; object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_s_o, + &settings_format_uuuu, &settings_format_uuuu, + {23,-1,-1}, "DemoCol(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x21a */ + { "M_obj_s_jump2", "Ramp used on competitions; object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_COMMONOBJ | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {82,-1,-1}, "M_obj_s_jump2(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x21b */ + { "M_obj_jump2", "Ramp used on competitions; object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_COMMONOBJ | OBF_OBJFLOW | OBF_SOLID | OBF_FNAME, + obj_charact_s, + &settings_format_uuuu, &settings_format_uuuu, + {78,-1,-1}, "M_obj_jump2(BK)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x21c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x21d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x21e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x21f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x220 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x221 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x222 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x223 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x224 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x225 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x226 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x227 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x228 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x229 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x22a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x22b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x22c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x22d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x22e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x22f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x230 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x231 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x232 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x233 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x234 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x235 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x236 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x237 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x238 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x239 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x23a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x23b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x23c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x23d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x23e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x23f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x240 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x241 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x242 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x243 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x244 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x245 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x246 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x247 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x248 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x249 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x24a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x24b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x24c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x24d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x24e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x24f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x250 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x251 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x252 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x253 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x254 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x255 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x256 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x257 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x258 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x259 */ + { "DonkyCannonGC", "DK cannon from GCN DK Mountain.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {26,-1,-1}, "DonkyCannonGC(BED)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x25a */ + { "BeltEasy", "Second set of belts from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {5,-1,-1}, "BeltEasy(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x25b */ + { "BeltCrossing", "First set of belts from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {2,-1,-1}, "BeltCrossing(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x25c */ + { "BeltCurveA", "Third set of belts from Toad's Factory. Object is not scalable.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {3,-1,-1}, "BeltCurveA(B)", + { 0, 0, + "Time until it switches", + "Time until it switches back", + 0, 0, 0, 0 }}, + /* 0x25d */ + { "BeltCurveB", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {4,-1,-1}, "BeltCurveB", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x25e */ + { "escalator", "Coconut Mall escalator.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {173,-1,-1}, "escalator(BK)", + { 0, + "Speed 1 (units per second, signed)", + "Time until it switches to Speed 2", + "Speed 2 (units per second, signed)", + "(Total) time until it switches to Speed 3", + "Speed 3 (units per second, signed)", + 0, 0 }}, + /* 0x25f */ + { "DonkyCannon_wii", "DK cannon from DK Summit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {27,-1,-1}, "DonkyCannon_wii(BED)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x260 */ + { "escalator_group", "2 Time Escalators from Coconut Mall. One of the two escalators uses the given speed, the other uses multiplies the speed with -1 (negative speed), so they always run in opposite directions.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_KCL | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_MULTI_FNAME | OBF_M_TRACK, + obj_charact_s_t, + &settings_format_uuuu, &settings_format_uuuu, + {173,210,-1}, "escalator(BK);monte_a(B)", + { 0, + "Speed 1 (units per second, signed)", + "Time until it switches to Speed 2", + "Speed 2 (units per second, signed)", + "(Total) time until it switches to Speed 3", + "Speed 3 (units per second, signed)", + 0, 0 }}, + /* 0x261 */ + { "tree_cannon", "Cannon from Maple Treeway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_BRASD | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {243,-1,-1}, "tree_cannon(BED)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x262 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x263 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x264 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x265 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x266 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x267 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x268 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x269 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x26a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x26b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x26c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x26d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x26e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x26f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x270 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x271 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x272 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x273 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x274 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x275 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x276 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x277 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x278 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x279 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x27a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x27b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x27c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x27d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x27e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x27f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x280 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x281 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x282 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x283 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x284 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x285 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x286 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x287 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x288 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x289 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x28a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x28b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x28c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x28d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x28e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x28f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x290 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x291 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x292 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x293 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x294 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x295 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x296 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x297 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x298 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x299 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x29a */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x29b */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x29c */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x29d */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x29e */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x29f */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2a9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2aa */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2ab */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2ac */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2ad */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2ae */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2af */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b0 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b1 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b2 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b3 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b4 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b5 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b6 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b7 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b8 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2b9 */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2ba */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2bb */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2bc */ { 0,0,0,0,0,0, {-1,-1,-1},0, {0,0,0,0,0,0,0,0} }, + /* 0x2bd */ + { "group_enemy_b", "Enemy audience.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {180,-1,-1}, "group_enemy_b(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2be */ + { "group_enemy_c", "Toads audience.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {181,-1,-1}, "group_enemy_c(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2bf */ + { "taimatsu", "Flame of the torches from N64 DK's Jungle Parkway (torches inside the cave) and Bowser Castle (on the wall).", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {242,-1,-1}, "taimatsu(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c0 */ + { "truckChimSmkW", "Houses' smoke from Delfino Pier.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_SOLID | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_s_a, + &settings_format_uuuu, &settings_format_uuuu, + {245,-1,-1}, "truckChimSmkW(BE)", + { 0, + "0 = Light 1 = Dark smoke and it interacts with the Player. 2 = Same as 1.", + 0, 0, 0, 0, 0, 0 }}, + /* 0x2c1 */ + { "Mstand", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {111,-1,-1}, "Mstand", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c2 */ + { "dkmonitor", "TV screen from DK Summit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {166,-1,-1}, "dkmonitor(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c3 */ + { "group_enemy_a", "Enemy audience.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {178,-1,-1}, "group_enemy_a(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c4 */ + { "FlagB3", "Bright Shell Flag from Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {47,-1,-1}, "FlagB3(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c5 */ + { "spot", "Spotlights from Chain Chomp Wheel.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {238,-1,-1}, "spot(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c6 */ + { "group_enemy_d", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {182,-1,-1}, "group_enemy_d", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c7 */ + { "FlagB4", "Dark Shell Flag from Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {48,-1,-1}, "FlagB4(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c8 */ + { "group_enemy_e", "Enemy audience from Mario Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {183,-1,-1}, "group_enemy_e(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2c9 */ + { "group_monte_L", "Enemy audience from Delfino Pier.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {185,-1,-1}, "group_monte_a(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2ca */ + { "group_enemy_f", "Enemy audience from Mario Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {184,-1,-1}, "group_enemy_f(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2cb */ + { "FallBsA", "Waterfall from Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {34,-1,-1}, "FallBsA(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2cc */ + { "FallBsB", "Waterfall from Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {35,-1,-1}, "FallBsB(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2cd */ + { "FallBsC", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {36,-1,-1}, "FallBsC", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2ce */ + { "volsmk", "Smoke used by the trembling rocks from Grumble Volcano.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {253,-1,-1}, "volsmk(E)", + { 0, + "Time until it starts emitting smoke in seconds at 150cc and mirror mode (already starts 4 seconds before in-game timer)", + "Time it emits smoke in seconds at 150cc and mirror mode", + "Time until it starts emitting smoke in seconds at 100cc (already starts 4 seconds before in-game timer)", + "Time it emits smoke in seconds at 100cc", + "Time until it starts emitting smoke in seconds at 50cc (already starts 4 seconds before in-game timer)", + "Time it emits smoke in seconds at 50cc", + 0 }}, + /* 0x2cf */ + { "ridgemii00", "Mii audience from Moonview Highway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {234,-1,-1}, "ridgemii00(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d0 */ + { "Flash_L", "Audience camera flashes on Luigi Circuit.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {54,-1,-1}, "Flash_L(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d1 */ + { "Flash_B", "Audience camera flashes on DK Summit.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {53,-1,-1}, "Flash_B(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d2 */ + { "Flash_W", "Audience camera flashes on GCN Waluigi Stadium. Active whenever the player performs a trick.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {57,-1,-1}, "Flash_W(E)", + { "Unknown", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d3 */ + { "Flash_M", "Audience camera flashes on N64 Mario Raceway. Active whenever the audience ambience is playing.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {55,-1,-1}, "Flash_M(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d4 */ + { "Flash_S", "Audience camera flashes on Funky Stadium. Active whenever the player performs a trick.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {56,-1,-1}, "Flash_S(E)", + { "Unknown", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d5 */ + { "MiiSignNoko", "Koopa face sign from Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {95,-1,-1}, "MiiSignNoko(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d6 */ + { "UtsuboDokan", "Piped Unagis (eels) from Koopa Cape.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {133,-1,-1}, "UtsuboDokan(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d7 */ + { "Spot64", "Spotlights from N64 Skyscraper.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {124,-1,-1}, "Spot64(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d8 */ + { "DemoEf", "Object not used in any track.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_o, + &settings_format_uuuu, &settings_format_uuuu, + {24,-1,-1}, "DemoEf(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2d9 */ + { "Fall_MH", "Part of the waterfall effect from Moonview Highway.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {37,-1,-1}, "Fall_MH(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2da */ + { "Fall_Y", "Part of the waterfall effects from Koopa Cape and DS Yoshi Falls.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {38,-1,-1}, "Fall_Y(E)", + { "0 = A, 1 = B or 2 = C breff animation", + 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2db */ + { "DemoJugemu", "Lakitu model from the Mario Circuit winning demo.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_o, + &settings_format_uuuu, &settings_format_uuuu, + {25,-1,-1}, "DemoJugemu(BE)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2dc */ + { "group_enemy_a_demo", "Enemy audience from the Mario Circuit winning demo.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_o, + &settings_format_uuuu, &settings_format_uuuu, + {179,-1,-1}, "group_enemy_a_demo(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2dd */ + { "group_monte_a_demo", "Pianta audience from the Mario Circuit winning demo.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_o, + &settings_format_uuuu, &settings_format_uuuu, + {186,-1,-1}, "group_monte_a_demo(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2de */ + { "volfall", "Object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {252,-1,-1}, "volfall", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2df */ + { "MiiStatueM2", "Mario statue from DK Summit. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {109,-1,-1}, "MiiStatueM2(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e0 */ + { "RhMiiKanban", "Peach face poster from Moonview Highway.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {118,-1,-1}, "RhMiiKanban(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e1 */ + { "MiiStatueL3", "Luigi statue from Luigi Circuit. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK | OBF_F_OTHER, + obj_charact_to, + &settings_format_uuuu, &settings_format_uuuu, + {107,-1,-1}, "MiiStatueL3(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e2 */ + { "MiiSignWario", "Wario Face Sign in Wario's Gold Mine.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {96,-1,-1}, "MiiSignWario(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e3 */ + { "MiiStatueBL1", "Baby Luigi statue from Daisy Circuit. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {101,-1,-1}, "MiiStatueBL1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e4 */ + { "MiiStatueBD1", "Baby Daisy statue from Daisy Circuit. The following subfiles are used by the environment map for Mii heads: course.bmm, course.btiEnv, course.btiMat", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {100,-1,-1}, "MiiStatueBD1(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e5 */ + { "Kamifubuki", "Confetti from Winning Scene.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_F_OTHER, + obj_charact_o, + &settings_format_uuuu, &settings_format_uuuu, + {72,-1,-1}, "Kamifubuki(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e6 */ + { "Crescent64", "Crescent Moon from N64 Skyscraper.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_ARENA, + obj_charact_a, + &settings_format_uuuu, &settings_format_uuuu, + {14,-1,-1}, "Crescent64(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e7 */ + { "MiiSighKino", "Toad face sign from Toad's Factory.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {94,-1,-1}, "MiiSighKino(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e8 */ + { "MiiObjD01", "Mii audience from Daisy Circuit", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {91,-1,-1}, "MiiObjD01(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2e9 */ + { "MiiObjD02", "Mii audience from Daisy Circuit", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {92,-1,-1}, "MiiObjD02(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2ea */ + { "MiiObjD03", "Mii audience from Daisy Circuit", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {93,-1,-1}, "MiiObjD03(B)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2eb */ + { "mare_a", "Toad from GCN Peach Peach.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {205,-1,-1}, "mare_a(B)", + { 0, + "1 = Enable player rotation and clapping.", + "Distance at which it rotates to face the player.", + "Distance at which it loads the clapping chr0.", + 0, 0, 0, 0 }}, + /* 0x2ec */ + { "mare_b", "Toad from GCN Peach Peach.", + OBF_OBJECT | OBF_UNIQUE | OBF_BRRES | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {206,-1,-1}, "mare_b(B)", + { 0, + "1 = Enable player rotation and clapping.", + "Distance at which it rotates to face the player.", + "Distance at which it loads the clapping chr0.", + 0, 0, 0, 0 }}, + /* 0x2ed */ + { "EnvKareha", "Downward Leaf effect on screen from Maple Treeway; AREA type 0x01 activates this object.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {30,-1,-1}, "EnvKareha(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2ee */ + { "EnvFire", "Lava particle effect from Bowser's Castle; AREA type 0x01 deactivates this object.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {29,-1,-1}, "EnvFire(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2ef */ + { "EnvSnow", "Snow effect from DK Summit; AREA type 0x01 deactivates this object.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {32,-1,-1}, "EnvSnow(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2f0 */ + { "M_obj_start", "Start line used in competitions; object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {83,-1,-1}, "M_obj_start", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2f1 */ + { "EnvKarehaUp", "Upward Leaf effect on screen from Maple Treeway; AREA type 0x01 activates this object.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {31,-1,-1}, "EnvKarehaUp(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2f2 */ + { "M_obj_kanban_y", "Yellow/black fence with an arrow on it, can be found in competitions; object not used in any track.", + OBF_OBJECT | OBF_NO_FILE | OBF_OBJFLOW | OBF_FNAME, + obj_charact, + &settings_format_uuuu, &settings_format_uuuu, + {80,-1,-1}, "M_obj_kanban_y", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, + /* 0x2f3 */ + { "DKfalls", "The waterfall splash effect found on N64 DK's Jungle Parkway.", + OBF_OBJECT | OBF_UNIQUE | OBF_EFFECT | OBF_OBJFLOW | OBF_NO_ROUTE | OBF_FNAME | OBF_M_TRACK, + obj_charact_t, + &settings_format_uuuu, &settings_format_uuuu, + {15,-1,-1}, "DKfalls(E)", + { 0, 0, 0, 0, 0, 0, 0, 0 }}, +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// ObjSpec[] //////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const ObjSpec_t ObjSpec[N_OBJ_SPEC+1] = +{ + { 0x1fc, 1, 0, "VolcanoPiece%u.kcl" }, // VolcanoPiece, FILE_BY_SETTING + { 0x1fc, 1, 0, "VolcanoPiece%ub.kcl" }, // VolcanoPiece, FILE_BY_SETTING + { 0x1fc, 1, 0, "VolcanoPiece%uc.kcl" }, // VolcanoPiece, FILE_BY_SETTING + + { 0x20d, 1, 0, "bblock%u.kcl" }, // bblock, FILE_BY_SETTING + { 0x20d, 1, 0, "bblock%ub.kcl" }, // bblock, FILE_BY_SETTING + + { 0x211, 1, 0, "RM_ring%u.kcl" }, // RM_ring1, FILE_BY_SETTING + { 0x211, 1, 0, "RM_ring%ub.kcl" }, // RM_ring1, FILE_BY_SETTING + { 0x211, 1, 10, "RM_ring%uc.kcl" }, // RM_ring1, FILE_BY_SETTING + + {0,0,0,0} +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/db-object.h b/project/src/db-object.h new file mode 100644 index 0000000..7055eb9 --- /dev/null +++ b/project/src/db-object.h @@ -0,0 +1,186 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_DB_OBJECT_H +#define SZS_DB_OBJECT_H 1 + +#include "lib-std.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// enum ObjectFlags_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[ObjectFlags_t]] +typedef enum ObjectFlags_t +{ + OBF_REQUIRED = 0x000000001, // for track files required + OBF_REQUIRED2 = 0x000000002, // alternativley required + OBF_OPTIONAL = 0x000000004, // optional file + OBF_OBJECT = 0x000000008, // file is object relevant + OBF_EXTERNAL = 0x000000010, // external file + OBF_UNIQUE = 0x000000020, // all instances are identical + OBF_BRRES = 0x000000040, // B: at least one BRRES file exists + OBF_EFFECT = 0x000000080, // E: at least one effect pair (BREFF+BREFT) exists + OBF_BRASD = 0x000000100, // D: at least one BRASD file exists + OBF_KCL = 0x000000200, // K: at least one KCL file exists + OBF_KMP = 0x000000400, // P: at least one KMP file exists + OBF_OTHER = 0x000000800, // x: at least one unknown file exists + OBF_NO_FILE = 0x000001000, // ?: no file found for group + OBF_SCALE = 0x000002000, // is known as scalable + OBF_ROTATE = 0x000004000, // is known as rotatable + OBF_FALLDOWN = 0x000008000, // relevant for fall-down + OBF_COMMONOBJ = 0x000010000, // found in CommonObj*.szs + OBF_ARCH_REQ = 0x000020000, // required for archive => used for WU8 encoding + OBF_ARCH_OPT = 0x000040000, // optional in archive + OBF_SPECIAL = 0x000080000, // special settings -> ObjectSpecial* + OBF_OBJFLOW = 0x000100000, // defined in ObjFlow.bin + OBF_SOLID = 0x000200000, // object is solid + OBF_ROAD = 0x000400000, // object acts like a road + OBF_NO_ROUTE = 0x000800000, // object needs no route + OBF_OPT_ROUTE = 0x001000000, // object has an optional route + OBF_ALWAYS_ROUTE = 0x002000000, // object has always a route + OBF_FNAME = 0x004000000, // object need at least 1 file + OBF_MULTI_FNAME = 0x008000000, // object needs 2 or more files + OBF_F_TRACK_S = 0x010000000, // object found in standard track file(s) + OBF_F_TRACK_D = 0x020000000, // object found in _d track file(s) + OBF_F_ARENA_S = 0x040000000, // object found in standard arena file(s) + OBF_F_ARENA_D = 0x080000000, // object found in _d arena file(s) + OBF_F_OTHER = 0x100000000, // object found in other file(s) + OBF_ITEMBOX = 0x200000000, // object is an itembox + + OBF_M_ROUTE = OBF_NO_ROUTE | OBF_OPT_ROUTE | OBF_ALWAYS_ROUTE, + OBF_M_TRACK = OBF_F_TRACK_S | OBF_F_TRACK_D, + OBF_M_ARENA = OBF_F_ARENA_S | OBF_F_ARENA_D, + OBF_M_FOUND = OBF_M_TRACK | OBF_M_ARENA | OBF_F_OTHER, + + OBF_NONE = 0x000000000, // none set + OBF_ALL = 0x3ffffffff // all 34 flags of above + +} ObjectFlags_t; + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////// struct ObjSettingFormat_t //////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[ObjSettingFormat_t]] +typedef struct ObjSettingFormat_t +{ + char std[4*5+2]; // standard like "%6u %#6x %#6x %6u" + char col[4*7+2]; // with color support like "%s%6u %s%#6x %s%#6x %s%6u" +} +ObjSettingFormat_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct ObjectInfo_t /////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[ObjectInfo_t]] +typedef struct ObjectInfo_t +{ + ccp name; // NULL or name of object + ccp info; // NULL or info for object + ObjectFlags_t flags; // flags of object + ccp charact; // characteristics based on 'flags' + const ObjSettingFormat_t + *format1; // formats for first 4 settings + const ObjSettingFormat_t + *format2; // formats for second 4 settings + int group_idx[3]; // >=0: index into 'DbFileGROUP' + ccp fname; // NULL or filename list + ccp param[8]; // NULL or parameter info +} +ObjectInfo_t; + +#define N_KMP_GOBJ 756 +extern const ObjectInfo_t ObjectInfo[N_KMP_GOBJ]; + +static inline u16 GetValidObjectId ( u16 obj_id ) +{ + if ( obj_id < 0x2000 ) + { + obj_id &= 0x03ff; + if ( obj_id < N_KMP_GOBJ ) + return obj_id; + } + return 0; +} + +static inline const ObjectInfo_t * GetObjectInfo ( u16 obj_id ) +{ + if ( obj_id < 0x2000 ) + { + obj_id &= 0x03ff; + if ( obj_id < N_KMP_GOBJ ) + return ObjectInfo + obj_id; + } + return ObjectInfo; +} + + +// +/////////////////////////////////////////////////////////////////////////////// +///////////////////////////// format strings ////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const ObjSettingFormat_t settings_format_uuuu; +extern const ObjSettingFormat_t settings_format_uxuu; +extern const ObjSettingFormat_t settings_format_uxxu; +extern const ObjSettingFormat_t settings_format_xxxx; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// enum ObjSpecType_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[ObjSpecType_t]] +typedef enum ObjSpecType_t +{ + OSP_NONE, // not used + OSP_FILE_BY_SETTING, // select file by setting +} +__attribute__ ((packed)) ObjSpecType_t; + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////// struct ObjSpec_t ///////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[ObjSpec_t]] +typedef struct ObjSpec_t +{ + u16 obj_id; // id of related object, 0=term + ObjSpecType_t type; // type of special + u8 mode; // type dependent mode + ccp text; // NULL or type dependent text +} +ObjSpec_t; + +#define N_OBJ_SPEC 8 +extern const ObjSpec_t ObjSpec[N_OBJ_SPEC+1]; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// statistics //////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define OBJ_FW_NAME 18 +#define OBJ_FW_INFO 897 +#define OBJ_FW_FNAME 46 +#define OBJ_FW_CHAR 55 +#define OBJ_FW_SETTING 330 + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_DB_OBJECT_H diff --git a/project/src/file-type.c b/project/src/file-type.c new file mode 100644 index 0000000..ead9730 --- /dev/null +++ b/project/src/file-type.c @@ -0,0 +1,1224 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#include "file-type.h" + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////// FileTypeTab[] ////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const char filetype_info_unknown[] = "?"; +const char filetype_info_not_supported[] = "not supported"; + +const file_type_t FileTypeTab[FF_N+1] = +{ + + // FF_UNKNOWN = 0 + { + FF_UNKNOWN, 0, 0, "?", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + filetype_info_not_supported, + filetype_info_not_supported, + "Unknown file" + }, + + // FF_YAZ0 = 1 + { + FF_YAZ0, 0, 0, "YAZ0", + ".szs", ".szs", ".szs", + FFT_VALID | FFT_COMPRESS, + 4, {0x59,0x61,0x7a,0x30}, // "Yaz0" + 0, + "0", + "0,1", + "YAZ compression" + }, + + // FF_YAZ1 = 2 + { + FF_YAZ1, 0, 0, "YAZ1", + ".szs", ".szs", ".szs", + FFT_VALID | FFT_COMPRESS, + 4, {0x59,0x61,0x7a,0x31}, // "Yaz1" + 0, + "1", + "0,1", + "YAZ compression" + }, + + // FF_XYZ = 3 + { + FF_XYZ, 0, 0, "XYZ", + ".xyz", ".szs", ".xyz", + FFT_VALID | FFT_COMPRESS, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "disguise YAZ compression" + }, + + // FF_BZ = 4 + { + FF_BZ, 0, 0, "BZ", + ".bz", ".szs", ".bz", + FFT_VALID | FFT_COMPRESS, + 4, {0x57,0x42,0x5a,0x61}, // "WBZa" + 0, + MinusString, + MinusString, + "bzip2 compression used" + }, + + // FF_BZ2 = 5 + { + FF_BZ2, 0, 0, "BZ2", + ".bz2", ".szs", ".bz2", + FFT_VALID | FFT_COMPRESS, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "bzip2 compressed file" + }, + + // FF_U8 = 6 + { + FF_U8, 0, 0, "U8", + ".u8", ".szs", ".u8", + FFT_VALID | FFT_ARCHIVE | FFT_CREATE | FFT_EXTRACT | FFT_CUT | FFT_LINK, + 4, {0x55,0xaa,0x38,0x2d}, // "Uª8-" + 0, + MinusString, + MinusString, + "Nintendos archive format" + }, + + // FF_WU8 = 7 + { + FF_WU8, 0, 0, "WU8", + ".wu8", ".wu8", ".wu8", + FFT_VALID | FFT_ARCHIVE | FFT_CREATE | FFT_EXTRACT | FFT_CUT | FFT_LINK, + 4, {0x57,0x55,0x38,0x61}, // "WU8a" + 0, + MinusString, + MinusString, + "Wiimms differential U8" + }, + + // FF_RARC = 8 + { + FF_RARC, 0, 0, "RARC", + ".rarc", ".arc", ".rarc", + FFT_VALID | FFT_ARCHIVE | FFT_EXTRACT | FFT_CUT, + 4, {0x52,0x41,0x52,0x43}, // "RARC" + 0, + MinusString, + filetype_info_not_supported, + "Archive format for objects" + }, + + // FF_BRRES = 9 + { + FF_BRRES, 0, 0, "BRRES", + ".brres", ".szs", ".bres", + FFT_VALID | FFT_ARCHIVE | FFT_CREATE | FFT_EXTRACT | FFT_CUT, + 4, {0x62,0x72,0x65,0x73}, // "bres" + 0, + MinusString, + MinusString, + "Archive format for objects" + }, + + // FF_BREFF = 10 + { + FF_BREFF, 0, 0, "BREFF", + ".breff", ".szs", ".reff", + FFT_VALID | FFT_ARCHIVE | FFT_CREATE | FFT_EXTRACT, + 4, {0x52,0x45,0x46,0x46}, // "REFF" + 0, + "9,11", + "9,11", + "Main file of an effect pair" + }, + + // FF_BREFT = 11 + { + FF_BREFT, 0, 0, "BREFT", + ".breft", ".szs", ".reft", + FFT_VALID | FFT_ARCHIVE | FFT_CREATE | FFT_EXTRACT | FFT_CUT, + 4, {0x52,0x45,0x46,0x54}, // "REFT" + 0, + "9,11", + "9,11", + "Image file of an effect pair" + }, + + // FF_RKC = 12 + { + FF_RKC, 0, 0, "RKC", + ".rkc", ".szs", ".rkc", + FFT_VALID | FFT_ARCHIVE | FFT_CREATE | FFT_EXTRACT | FFT_CUT, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_PACK = 13 + { + FF_PACK, 0, 0, "PACK", + ".pack", ".szs", ".pack", + FFT_VALID | FFT_ARCHIVE | FFT_CREATE | FFT_EXTRACT | FFT_CUT, + 4, {0x50,0x41,0x43,0x4b}, // "PACK" + 0, + MinusString, + MinusString, + "Simple archive format" + }, + + // FF_CHR = 14 + { + FF_CHR, 0, 0, "CHR", + ".chr", ".szs", ".chr0", + FFT_VALID | FFT_BRSUB | FFT_BRSUB2 | FFT_CUT, + 4, {0x43,0x48,0x52,0x30}, // "CHR0" + "AnmChr(NW4R)", + "5,(*)", + filetype_info_not_supported, + "Model movement animations" + }, + + // FF_CLR = 15 + { + FF_CLR, 0, 0, "CLR", + ".clr", ".szs", ".clr0", + FFT_VALID | FFT_BRSUB | FFT_BRSUB2 | FFT_CUT, + 4, {0x43,0x4c,0x52,0x30}, // "CLR0" + "AnmClr(NW4R)", + "4,(*)", + filetype_info_not_supported, + "Colour changing animations" + }, + + // FF_MDL = 16 + { + FF_MDL, FF_MDL, FF_MDL_TXT, "MDL", + ".mdl", ".szs", ".mdl0", + FFT_VALID | FFT_BRSUB | FFT_BRSUB2 | FFT_CUT | FFT_DECODE | FFT_PATCH, + 4, {0x4d,0x44,0x4c,0x30}, // "MDL0" + "3DModels(NW4R)", + "11,(*)", + filetype_info_not_supported, + "Model files" + }, + + // FF_PAT = 17 + { + FF_PAT, FF_PAT, FF_PAT_TXT, "PAT", + ".pat", ".szs", ".pat0", + FFT_VALID | FFT_BRSUB | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 4, {0x50,0x41,0x54,0x30}, // "PAT0" + "AnmTexPat(NW4R)", + "4,(*)", + "4", + "Texture swapping animations" + }, + + // FF_SCN = 18 + { + FF_SCN, 0, 0, "SCN", + ".scn", ".szs", ".scn0", + FFT_VALID | FFT_BRSUB | FFT_BRSUB2 | FFT_CUT, + 4, {0x53,0x43,0x4e,0x30}, // "SCN0" + "AnmScn(NW4R)", + "5,(*)", + filetype_info_not_supported, + "Polygon morphing animations" + }, + + // FF_SHP = 19 + { + FF_SHP, 0, 0, "SHP", + ".shp", ".szs", ".shp0", + FFT_VALID | FFT_BRSUB | FFT_BRSUB2 | FFT_CUT, + 4, {0x53,0x48,0x50,0x30}, // "SHP0" + "AnmShp(NW4R)", + "4,(*)", + filetype_info_not_supported, + filetype_info_unknown + }, + + // FF_SRT = 20 + { + FF_SRT, 0, 0, "SRT", + ".srt", ".szs", ".srt0", + FFT_VALID | FFT_BRSUB | FFT_BRSUB2 | FFT_CUT, + 4, {0x53,0x52,0x54,0x30}, // "SRT0" + "AnmTexSrt(NW4R)", + "5,(*)", + filetype_info_not_supported, + "Texture movement animations" + }, + + // FF_TEX = 21 + { + FF_TEX, 0, 0, "TEX", + ".tex", ".szs", ".tex0", + FFT_VALID | FFT_GRAPHIC | FFT_BRSUB | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 4, {0x54,0x45,0x58,0x30}, // "TEX0" + "Textures(NW4R)", + "3,(*)", + "3", + "Texture file" + }, + + // FF_TEX_CT = 22 + { + FF_TEX_CT, 0, 0, "TEX+CT", + ".tex", ".szs", ".tex0", + FFT_VALID | FFT_GRAPHIC | FFT_BRSUB | FFT_CUT | FFT_DECODE | FFT_ENCODE | FFT_PATCH, + 0, {0}, // no magic + 0, + "3,(*)", + "3", + "Texture file with CTGP-CODE" + }, + + // FF_CT_TEXT = 23 + { + FF_CT_TEXT, 0, 0, "CTTEXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 8, {0x23,0x43,0x54,0x2d,0x43,0x4f,0x44,0x45}, // "#CT-CODE" + 0, + MinusString, + MinusString, + "CTGP-CODE text file" + }, + + // FF_CT0_CODE = 24 + { + FF_CT0_CODE, 0, 0, "C0CODE", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-CODE for main.dol section T2" + }, + + // FF_CT0_DATA = 25 + { + FF_CT0_DATA, 0, 0, "C0DATA", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-DATA for main.dol section D8" + }, + + // FF_CT1_CODE = 26 + { + FF_CT1_CODE, 0, 0, "C1CODE", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-CODE for strap TEX0 file" + }, + + // FF_CT1_DATA = 27 + { + FF_CT1_DATA, 0, 0, "C1DATA", + ".ctcode", ".szs", ".ctcode", + FFT_VALID | FFT_CUT | FFT_DECODE | FFT_PATCH, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-DATA for strap TEX0 file" + }, + + // FF_CUP1 = 28 + { + FF_CUP1, 0, 0, "CUP1", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-DATA section: Cups" + }, + + // FF_CRS1 = 29 + { + FF_CRS1, 0, 0, "CRS1", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-DATA section: Tracks" + }, + + // FF_MOD1 = 30 + { + FF_MOD1, 0, 0, "MOD1", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-DATA section: main.dol patches" + }, + + // FF_MOD2 = 31 + { + FF_MOD2, 0, 0, "MOD2", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-DATA section: StaticR.rel patches" + }, + + // FF_OVR1 = 32 + { + FF_OVR1, 0, 0, "OVR1", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "CT-DATA section: Speedometer" + }, + + // FF_LE_BIN = 33 + { + FF_LE_BIN, 0, 0, "LE-BIN", + ".bin", ".szs", ".bin", + FFT_VALID | FFT_CUT | FFT_DECODE | FFT_PATCH, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "LE-CODE binary" + }, + + // FF_LEX = 34 + { + FF_LEX, FF_LEX, FF_LEX_TXT, "LEX", + ".lex", ".szs", ".lex", + FFT_VALID | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 4, {0x4c,0x45,0x2d,0x58}, // "LE-X" + 0, + MinusString, + MinusString, + "LECODE extension file" + }, + + // FF_LEX_TXT = 35 + { + FF_LEX_TXT, FF_LEX, FF_LEX_TXT, "LEXTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 4, {0x23,0x4c,0x45,0x58}, // "#LEX" + 0, + MinusString, + MinusString, + "Text version of LEX" + }, + + // FF_LPAR = 36 + { + FF_LPAR, 0, 0, "LPAR", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 8, {0x23,0x4c,0x45,0x2d,0x4c,0x50,0x41,0x52}, // "#LE-LPAR" + 0, + MinusString, + MinusString, + "Text file with LE-CODE parameters" + }, + + // FF_MDL_TXT = 37 + { + FF_MDL_TXT, FF_MDL, FF_MDL_TXT, "MDLTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT, + 4, {0x23,0x4d,0x44,0x4c}, // "#MDL" + 0, + filetype_info_not_supported, + MinusString, + "Text version of MDL" + }, + + // FF_PAT_TXT = 38 + { + FF_PAT_TXT, FF_PAT, FF_PAT_TXT, "PATTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 4, {0x23,0x50,0x41,0x54}, // "#PAT" + 0, + MinusString, + MinusString, + "Text version of PAT" + }, + + // FF_TPL = 39 + { + FF_TPL, 0, 0, "TPL", + ".tpl", ".szs", ".tpl", + FFT_VALID | FFT_GRAPHIC | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 4, {0x00,0x20,0xaf,0x30}, // "\000 ¯0" + 0, + MinusString, + MinusString, + "Image container" + }, + + // FF_BTI = 40 + { + FF_BTI, 0, 0, "BTI", + ".bti", ".szs", ".bti", + FFT_VALID | FFT_GRAPHIC | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Image container" + }, + + // FF_BREFT_IMG = 41 + { + FF_BREFT_IMG, 0, 0, "BT-IMG", + ".bt-img", ".szs", ".bt-img", + FFT_VALID | FFT_GRAPHIC | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Raw image of BREFT file" + }, + + // FF_BMG = 42 + { + FF_BMG, FF_BMG, FF_BMG_TXT, "BMG", + ".bmg", ".szs", ".bmg", + FFT_VALID | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Message file" + }, + + // FF_BMG_TXT = 43 + { + FF_BMG_TXT, FF_BMG, FF_BMG_TXT, "BMGTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Text version of BMG" + }, + + // FF_KCL = 44 + { + FF_KCL, FF_KCL, FF_KCL_TXT, "KCL", + ".kcl", ".szs", ".kcl", + FFT_VALID | FFT_CUT | FFT_DECODE | FFT_ENCODE | FFT_PATCH, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Collision file" + }, + + // FF_KCL_TXT = 45 + { + FF_KCL_TXT, FF_KCL, FF_KCL_TXT, "KCLTXT", + ".obj", ".szs", ".obj", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 4, {0x23,0x4b,0x43,0x4c}, // "#KCL" + 0, + MinusString, + MinusString, + "Wavefront OBJ by WSZST" + }, + + // FF_WAV_OBJ = 46 + { + FF_WAV_OBJ, FF_KCL, FF_WAV_OBJ, "WAVOBJ", + ".obj", ".szs", ".obj", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Wavefront OBJ" + }, + + // FF_SKP_OBJ = 47 + { + FF_SKP_OBJ, FF_KCL, FF_SKP_OBJ, "SKPOBJ", + ".obj", ".szs", ".obj", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Wavefront OBJ by Sketchup" + }, + + // FF_KMP = 48 + { + FF_KMP, FF_KMP, FF_KMP_TXT, "KMP", + ".kmp", ".szs", ".kmp", + FFT_VALID | FFT_CUT | FFT_DECODE | FFT_ENCODE, + 4, {0x52,0x4b,0x4d,0x44}, // "RKMD" + 0, + MinusString, + MinusString, + "Track information file" + }, + + // FF_KMP_TXT = 49 + { + FF_KMP_TXT, FF_KMP, FF_KMP_TXT, "KMPTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 4, {0x23,0x4b,0x4d,0x50}, // "#KMP" + 0, + MinusString, + MinusString, + "Text version of KMP" + }, + + // FF_ITEMSLT = 50 + { + FF_ITEMSLT, FF_ITEMSLT, FF_ITEMSLT_TXT, "ITEMSLT", + ".bin", ".szs", ".slt", + FFT_VALID, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_ITEMSLT_TXT = 51 + { + FF_ITEMSLT_TXT, FF_ITEMSLT, FF_ITEMSLT_TXT, "ITEMSLTTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_KMG = 52 + { + FF_KMG, FF_KMG, FF_KMG_TXT, "KMG", + ".kmg", ".szs", ".kmg", + FFT_VALID | FFT_DECODE | FFT_ENCODE, + 4, {0x52,0x4b,0x4d,0x47}, // "RKMG" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_KMG_TXT = 53 + { + FF_KMG_TXT, FF_KMG, FF_KMG_TXT, "KMGTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 4, {0x23,0x4b,0x4d,0x47}, // "#KMG" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_KRM = 54 + { + FF_KRM, FF_KRM, FF_KRM_TXT, "KRM", + ".krm", ".szs", ".krm", + FFT_VALID, + 4, {0x52,0x4b,0x52,0x4d}, // "RKRM" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_KRM_TXT = 55 + { + FF_KRM_TXT, FF_KRM, FF_KRM_TXT, "KRMTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT, + 4, {0x23,0x4b,0x52,0x4d}, // "#KRM" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_KRT = 56 + { + FF_KRT, FF_KRT, FF_KRT_TXT, "KRT", + ".krt", ".szs", ".krt", + FFT_VALID, + 4, {0x52,0x4b,0x47,0x54}, // "RKGT" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_KRT_TXT = 57 + { + FF_KRT_TXT, FF_KRT, FF_KRT_TXT, "KRTTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT, + 4, {0x23,0x4b,0x52,0x54}, // "#KRT" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_OBJFLOW = 58 + { + FF_OBJFLOW, FF_OBJFLOW, FF_OBJFLOW_TXT, "OBFLOW", + ".bin", ".szs", ".bin", + FFT_VALID | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_OBJFLOW_TXT = 59 + { + FF_OBJFLOW_TXT, FF_OBJFLOW, FF_OBJFLOW_TXT, "OF-TXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 8, {0x23,0x4f,0x42,0x4a,0x46,0x4c,0x4f,0x57}, // "#OBJFLOW" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_ITEM = 60 + { + FF_GH_ITEM, FF_GH_ITEM, FF_GH_ITEM_TXT, "GHITEM", + ".bin", ".szs", ".bin", + FFT_VALID | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_ITEM_TXT = 61 + { + FF_GH_ITEM_TXT, FF_GH_ITEM, FF_GH_ITEM_TXT, "GI-TXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 8, {0x23,0x47,0x48,0x2d,0x49,0x54,0x45,0x4d}, // "#GH-ITEM" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_IOBJ = 62 + { + FF_GH_IOBJ, FF_GH_IOBJ, FF_GH_IOBJ_TXT, "GHIOBJ", + ".bin", ".szs", ".bin", + FFT_VALID | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_IOBJ_TXT = 63 + { + FF_GH_IOBJ_TXT, FF_GH_IOBJ, FF_GH_IOBJ_TXT, "GIOTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 8, {0x23,0x47,0x48,0x2d,0x49,0x4f,0x42,0x4a}, // "#GH-IOBJ" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_KART = 64 + { + FF_GH_KART, FF_GH_KART, FF_GH_KART_TXT, "GHKART", + ".bin", ".szs", ".bin", + FFT_VALID | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_KART_TXT = 65 + { + FF_GH_KART_TXT, FF_GH_KART, FF_GH_KART_TXT, "GK-TXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 8, {0x23,0x47,0x48,0x2d,0x4b,0x41,0x52,0x54}, // "#GH-KART" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_KOBJ = 66 + { + FF_GH_KOBJ, FF_GH_KOBJ, FF_GH_KOBJ_TXT, "GHKOBJ", + ".bin", ".szs", ".bin", + FFT_VALID | FFT_DECODE | FFT_ENCODE, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_GH_KOBJ_TXT = 67 + { + FF_GH_KOBJ_TXT, FF_GH_KOBJ, FF_GH_KOBJ_TXT, "GKOTXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_DECODE | FFT_ENCODE | FFT_PARSER, + 8, {0x23,0x47,0x48,0x2d,0x4b,0x4f,0x42,0x4a}, // "#GH-KOBJ" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_DRIVER = 68 + { + FF_DRIVER, 0, 0, "DRV", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_VEHICLE = 69 + { + FF_VEHICLE, 0, 0, "VEH", + ".bin", ".szs", ".bin", + FFT_VALID, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_BRASD = 70 + { + FF_BRASD, 0, 0, "BRASD", + ".brasd", ".szs", ".brasd", + FFT_VALID, + 4, {0x52,0x41,0x53,0x44}, // "RASD" + 0, + MinusString, + MinusString, + filetype_info_unknown + }, + + // FF_RKG = 71 + { + FF_RKG, 0, 0, "RKG", + ".rkg", ".szs", ".rkg", + FFT_VALID, + 4, {0x52,0x4b,0x47,0x44}, // "RKGD" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_RKCO = 72 + { + FF_RKCO, 0, 0, "RKCO", + ".rkco", ".szs", ".rkco", + FFT_VALID, + 4, {0x52,0x4b,0x43,0x4f}, // "RKCO" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_STATICR = 73 + { + FF_STATICR, 0, 0, "STATR", + ".rel", ".szs", ".rel", + FFT_VALID | FFT_PATCH, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "A 'StaticR.rel' file" + }, + + // FF_DOL = 74 + { + FF_DOL, 0, 0, "DOL", + ".dol", ".szs", ".dol", + FFT_VALID | FFT_EXTRACT | FFT_PATCH, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "An executable DOL file" + }, + + // FF_GCT = 75 + { + FF_GCT, 0, 0, "GCT", + ".gct", ".szs", ".gct", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Gecko Cheat Code" + }, + + // FF_GCH = 76 + { + FF_GCH, 0, 0, "GCH", + ".gch", ".szs", ".gch", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Gecko Cheat Handler + coded" + }, + + // FF_WCH = 77 + { + FF_WCH, 0, 0, "WCH", + ".wch", ".szs", ".wch", + FFT_VALID, + 0, {0}, // no magic + 0, + MinusString, + MinusString, + "Wiimms Cheat Handler + codes" + }, + + // FF_WPF = 78 + { + FF_WPF, 0, 0, "WPF", + ".wpf", ".szs", ".wpf", + FFT_VALID, + 4, {0x57,0x50,0x46,0x01}, // "WPF\001" + 0, + MinusString, + MinusString, + "Wiimms Patch File" + }, + + // FF_XPF = 79 + { + FF_XPF, 0, 0, "XPF", + ".xpf", ".szs", ".xpf", + FFT_VALID, + 4, {0x58,0x50,0x46,0x01}, // "XPF\001" + 0, + MinusString, + MinusString, + "Extended Patch File" + }, + + // FF_DISTRIB = 80 + { + FF_DISTRIB, 0, 0, "DISTRB", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT, + 8, {0x23,0x44,0x49,0x53,0x54,0x52,0x49,0x42}, // "#DISTRIB" + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_PNG = 81 + { + FF_PNG, 0, 0, "PNG", + ".png", ".png", ".png", + FFT_VALID | FFT_GRAPHIC | FFT_EXTERNAL | FFT_DECODE | FFT_ENCODE, + 8, {0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a}, // "‰PNG\r\n\032\n" + 0, + MinusString, + MinusString, + "A public image format" + }, + + // FF_TXT = 82 + { + FF_TXT, 0, 0, "TXT", + ".txt", ".szs", ".txt", + FFT_VALID | FFT_TEXT | FFT_EXTERNAL, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_SCRIPT = 83 + { + FF_SCRIPT, 0, 0, "SCRIPT", + ".script", ".szs", ".script", + FFT_VALID | FFT_TEXT | FFT_EXTERNAL, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_JSON = 84 + { + FF_JSON, 0, 0, "JSON", + ".json", ".szs", ".json", + FFT_VALID | FFT_TEXT | FFT_EXTERNAL, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_SH = 85 + { + FF_SH, 0, 0, "SH", + ".sh", ".szs", ".sh", + FFT_VALID | FFT_TEXT | FFT_EXTERNAL, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_BASH = 86 + { + FF_BASH, 0, 0, "BASH", + ".sh", ".szs", ".sh", + FFT_VALID | FFT_TEXT | FFT_EXTERNAL, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_PHP = 87 + { + FF_PHP, 0, 0, "PHP", + ".php", ".szs", ".php", + FFT_VALID | FFT_TEXT | FFT_EXTERNAL, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_MAKEDOC = 88 + { + FF_MAKEDOC, 0, 0, "MKDOC", + ".md", ".szs", ".md", + FFT_VALID | FFT_TEXT | FFT_EXTERNAL, + 0, {0}, // no magic + 0, + filetype_info_unknown, + filetype_info_unknown, + EmptyString + }, + + // FF_DIRECTORY = 89 + { + FF_DIRECTORY, 0, 0, "DIR", + ".d", ".d", ".d", + FFT_EXTERNAL, + 0, {0}, // no magic + 0, + "transparent", + "transparent", + "Directory of filesystem" + }, + + // FF_N + {0} +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////// FileTypeTab[] ////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t cmdtab_FileType[] = +{ + // INFO: cmd->opt := ff_attrib_t + + { FF_YAZ0, "YAZ", "YAZ0", 0x3 }, + { FF_YAZ1, "YAZ1", 0, 0x3 }, + { FF_XYZ, "XYZ", 0, 0x3 }, + { FF_BZ, "BZ", 0, 0x3 }, + { FF_BZ2, "BZ2", 0, 0x3 }, + { FF_U8, "U8", 0, 0x8705 }, + { FF_WU8, "WU8", 0, 0x8705 }, + { FF_RARC, "ARC", "RARC", 0x605 }, + { FF_BRRES, "BRES", "BRRES", 0x705 }, + { FF_BREFF, "BREFF", "REFF", 0x305 }, + { FF_BREFT, "BREFT", "REFT", 0x705 }, + { FF_RKC, "RKC", 0, 0x705 }, + { FF_PACK, "PACK", 0, 0x705 }, + { FF_CHR, "CHR", "CHR0", 0x461 }, + { FF_CLR, "CLR", "CLR0", 0x461 }, + { FF_MDL, "MDL", "MDL0", 0x4c61 }, + { FF_PAT, "PAT", "PAT0", 0x1c21 }, + { FF_SCN, "SCN", "SCN0", 0x461 }, + { FF_SHP, "SHP", "SHP0", 0x461 }, + { FF_SRT, "SRT", "SRT0", 0x461 }, + { FF_TEX, "TEX", "TEX0", 0x1c29 }, + { FF_TEX_CT, "TEX", "TEX+CT", 0x5c29 }, + { FF_TEX_CT, "TEX-CT", "TEX0", 0x5c29 }, + { FF_TEX_CT, "TEXCT", 0, 0x5c29 }, + { FF_CT_TEXT, "CT-TEXT", "CTTEXT", 0x3811 }, + { FF_CT0_CODE, "C0CODE", "CT0-CODE", 0x1 }, + { FF_CT0_CODE, "CT0CODE", 0, 0x1 }, + { FF_CT0_DATA, "C0DATA", "CT0-DATA", 0x1 }, + { FF_CT0_DATA, "CT0DATA", 0, 0x1 }, + { FF_CT1_CODE, "C1CODE", "CT1-CODE", 0x1 }, + { FF_CT1_CODE, "CT1CODE", 0, 0x1 }, + { FF_CT1_DATA, "C1DATA", "CT1-DATA", 0x4c01 }, + { FF_CT1_DATA, "CT1DATA", "CTCODE", 0x4c01 }, + { FF_CUP1, "CUP1", 0, 0x1 }, + { FF_CRS1, "CRS1", 0, 0x1 }, + { FF_MOD1, "MOD1", 0, 0x1 }, + { FF_MOD2, "MOD2", 0, 0x1 }, + { FF_OVR1, "OVR1", 0, 0x1 }, + { FF_LE_BIN, "LE-BIN", "LEBIN", 0x4c01 }, + { FF_LEX, "LEX", 0, 0x1c01 }, + { FF_LEX_TXT, "LEX-TXT", "LEXTXT", 0x3811 }, + { FF_LPAR, "LPAR", 0, 0x3811 }, + { FF_MDL_TXT, "MDL-TXT", "MDLTXT", 0x11 }, + { FF_PAT_TXT, "PAT-TXT", "PATTXT", 0x3811 }, + { FF_TPL, "TPL", 0, 0x1c09 }, + { FF_BTI, "BTI", "BTIENV", 0x1c09 }, + { FF_BTI, "BTIMAT", 0, 0x1c09 }, + { FF_BREFT_IMG, "BREFT-IMG", "BREFTIMG", 0x1c09 }, + { FF_BREFT_IMG, "BT-IMG", "BTIMG", 0x1c09 }, + { FF_BMG, "BMG", "MESGBMG1", 0x1c01 }, + { FF_BMG_TXT, "BMG-TXT", "BMGTXT", 0x1811 }, + { FF_KCL, "KCL", 0, 0x5c01 }, + { FF_KCL_TXT, "KCL-TXT", "KCLTXT", 0x3811 }, + { FF_WAV_OBJ, "WAV-OBJ", "WAVOBJ", 0x3811 }, + { FF_SKP_OBJ, "SKP-OBJ", "SKPOBJ", 0x3811 }, + { FF_KMP, "KMP", 0, 0x1c01 }, + { FF_KMP_TXT, "KMP-TXT", "KMPTXT", 0x3811 }, + { FF_ITEMSLT, "ITEMSLT", "SLT", 0x1 }, + { FF_ITEMSLT_TXT, "ITEMSLT-TXT", "ITEMSLTTXT", 0x11 }, + { FF_KMG, "KMG", 0, 0x1801 }, + { FF_KMG_TXT, "KMG-TXT", "KMGTXT", 0x3811 }, + { FF_KRM, "KRM", 0, 0x1 }, + { FF_KRM_TXT, "KRM-TXT", "KRMTXT", 0x11 }, + { FF_KRT, "KRT", 0, 0x1 }, + { FF_KRT_TXT, "KRT-TXT", "KRTTXT", 0x11 }, + { FF_OBJFLOW, "OBFLOW", "OBJFLOW", 0x1801 }, + { FF_OBJFLOW_TXT, "OBJFLOW-TXT", "OBJFLOWTXT", 0x3811 }, + { FF_OBJFLOW_TXT, "OF-TXT", "OFTXT", 0x3811 }, + { FF_GH_ITEM, "GH-ITEM", "GHITEM", 0x1801 }, + { FF_GH_ITEM_TXT, "GH-ITEM-TXT", "GHITEMTXT", 0x3811 }, + { FF_GH_ITEM_TXT, "GI-TXT", "GITXT", 0x3811 }, + { FF_GH_IOBJ, "GH-IOBJ", "GHIOBJ", 0x1801 }, + { FF_GH_IOBJ_TXT, "GH-IOBJ-TXT", "GHIOBJTXT", 0x3811 }, + { FF_GH_IOBJ_TXT, "GIOTXT", 0, 0x3811 }, + { FF_GH_KART, "GH-KART", "GHKART", 0x1801 }, + { FF_GH_KART_TXT, "GH-KART-TXT", "GHKARTTXT", 0x3811 }, + { FF_GH_KART_TXT, "GK-TXT", "GKTXT", 0x3811 }, + { FF_GH_KOBJ, "GH-KOBJ", "GHKOBJ", 0x1801 }, + { FF_GH_KOBJ_TXT, "GH-KOBJ-TXT", "GHKOBJTXT", 0x3811 }, + { FF_GH_KOBJ_TXT, "GKOTXT", 0, 0x3811 }, + { FF_DRIVER, "DRIVER", "DRV", 0x1 }, + { FF_VEHICLE, "VEH", "VEHICLE", 0x1 }, + { FF_BRASD, "BRASD", 0, 0x1 }, + { FF_RKG, "RKG", 0, 0x1 }, + { FF_RKCO, "RKCO", 0, 0x1 }, + { FF_STATICR, "REL", "STATICR", 0x4001 }, + { FF_DOL, "DOL", 0, 0x4201 }, + { FF_GCT, "GCT", 0, 0x1 }, + { FF_GCH, "GCH", 0, 0x1 }, + { FF_WCH, "WCH", 0, 0x1 }, + { FF_WPF, "WPF", 0, 0x1 }, + { FF_XPF, "XPF", 0, 0x1 }, + { FF_DISTRIB, "DISTRIB", 0, 0x11 }, + { FF_PNG, "PNG", 0, 0x1889 }, + { FF_SCRIPT, "SCRIPT", 0, 0x91 }, + { FF_JSON, "JSON", 0, 0x91 }, + { FF_SH, "SH", 0, 0x91 }, + { FF_BASH, "BASH", "SH", 0x91 }, + { FF_PHP, "PHP", 0, 0x91 }, + { FF_MAKEDOC, "MAKEDOC", "MD", 0x91 }, + { FF_DIRECTORY, "DIRECTORY", 0, 0x80 }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/file-type.h b/project/src/file-type.h new file mode 100644 index 0000000..6bd4fdf --- /dev/null +++ b/project/src/file-type.h @@ -0,0 +1,236 @@ + +// +/////////////////////////////////////////////////////////////////////////////// +////// This file is created by a script. Modifications will be lost! ////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_FILE_TYPE_H +#define SZS_FILE_TYPE_H 1 + +#include "types.h" +#include "dclib-basics.h" + + +// +/////////////////////////////////////////////////////////////////////////////// +//////////////////////////// enum ff_attrib_t ///////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[ff_attrib_t]] +typedef enum ff_attrib_t +{ + FFT_VALID = 0x0001, // a valid file format + + FFT_COMPRESS = 0x0002, // is a compression format + FFT_ARCHIVE = 0x0004, // is a supported archive + FFT_GRAPHIC = 0x0008, // graphic image format + FFT_TEXT = 0x0010, // (decoded) text file + FFT_M_FTYPE = 0x001e, // mask of file types + + FFT_BRSUB = 0x0020, // is a BRRES sub file + FFT_BRSUB2 = 0x0040, // have a scondary file structure like a BRSUB + FFT_EXTERNAL = 0x0080, // external file format + FFT_M_CLASS = 0x00e0, // mask of file classes + + FFT_CREATE = 0x0100, // creation supported + FFT_EXTRACT = 0x0200, // extracting supported + FFT_CUT = 0x0400, // 'cut-files' supported + FFT_DECODE = 0x0800, // decoding supported + FFT_ENCODE = 0x1000, // encoding supported + FFT_PARSER = 0x2000, // parser support on encoding + FFT_PATCH = 0x4000, // patching of binaries + FFT_LINK = 0x8000, // hardlinks supported + FFT_M_SUPPORT = 0xff00, // mask of supported operations + + FFT_NONE = 0x0000, // none set + FFT_M_ALL = 0xffff // all flags of above + +} ff_attrib_t; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// enum file_format_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[file_format_t]] +typedef enum file_format_t +{ + FF_UNKNOWN = 0, // definitley 0 + + //--- file modes + + FF_YAZ0, // 1 + FF_YAZ1, // 2 + FF_XYZ, // 3 + FF_BZ, // 4 + FF_BZ2, // 5 + + FF_U8, // 6 + FF_WU8, // 7 + FF_RARC, // 8 + FF_BRRES, // 9 + FF_BREFF, // 10 + FF_BREFT, // 11 + FF_RKC, // 12 + FF_PACK, // 13 + + FF_CHR, // 14 + FF_CLR, // 15 + FF_MDL, // 16 + FF_PAT, // 17 + FF_SCN, // 18 + FF_SHP, // 19 + FF_SRT, // 20 + FF_TEX, // 21 + + FF_TEX_CT, // 22 + FF_CT_TEXT, // 23 + + FF_CT0_CODE, // 24 + FF_CT0_DATA, // 25 + FF_CT1_CODE, // 26 + FF_CT1_DATA, // 27 + + FF_CUP1, // 28 + FF_CRS1, // 29 + FF_MOD1, // 30 + FF_MOD2, // 31 + FF_OVR1, // 32 + + FF_LE_BIN, // 33 + FF_LEX, // 34 + FF_LEX_TXT, // 35 + + FF_LPAR, // 36 + + FF_MDL_TXT, // 37 + + FF_PAT_TXT, // 38 + + FF_TPL, // 39 + FF_BTI, // 40 + FF_BREFT_IMG, // 41 + + FF_BMG, // 42 + FF_BMG_TXT, // 43 + + FF_KCL, // 44 + FF_KCL_TXT, // 45 + FF_WAV_OBJ, // 46 + FF_SKP_OBJ, // 47 + + FF_KMP, // 48 + FF_KMP_TXT, // 49 + + FF_ITEMSLT, // 50 + FF_ITEMSLT_TXT, // 51 + + FF_KMG, // 52 + FF_KMG_TXT, // 53 + + FF_KRM, // 54 + FF_KRM_TXT, // 55 + + FF_KRT, // 56 + FF_KRT_TXT, // 57 + + FF_OBJFLOW, // 58 + FF_OBJFLOW_TXT, // 59 + + FF_GH_ITEM, // 60 + FF_GH_ITEM_TXT, // 61 + + FF_GH_IOBJ, // 62 + FF_GH_IOBJ_TXT, // 63 + + FF_GH_KART, // 64 + FF_GH_KART_TXT, // 65 + + FF_GH_KOBJ, // 66 + FF_GH_KOBJ_TXT, // 67 + + FF_DRIVER, // 68 + FF_VEHICLE, // 69 + + FF_BRASD, // 70 + FF_RKG, // 71 + FF_RKCO, // 72 + + FF_STATICR, // 73 + FF_DOL, // 74 + + FF_GCT, // 75 + FF_GCH, // 76 + FF_WCH, // 77 + FF_WPF, // 78 + FF_XPF, // 79 + FF_DISTRIB, // 80 + + FF_PNG, // 81 + + FF_TXT, // 82 + FF_SCRIPT, // 83 + FF_JSON, // 84 + FF_SH, // 85 + FF_BASH, // 86 + FF_PHP, // 87 + FF_MAKEDOC, // 88 + + FF_DIRECTORY, // 89 + + //--- number of elements + + FF_N, + FF_AUTO = FF_N, + + //--- some more values + + FF_INVALID = -1, + FF__FIRST_BRSUB = FF_CHR, + FF__LAST_BRSUB = FF_TEX, + +} file_format_t; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////// struct file_type_t //////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// [[file_type_t]] +typedef struct file_type_t +{ + file_format_t fform; // file format + file_format_t fform_bin;// file format of binary partner (or 0=FF_UNKOWN) + file_format_t fform_txt;// file format of text partner (or 0=FF_UNKOWN) + + ccp name; // name + ccp ext; // standard file extension + ccp ext_compr; // file extension if compressed + ccp ext_magic; // magic based file extension + + ff_attrib_t attrib; // file atributes + + u8 magic_len; // length of magic, usually 0, 4 or 8 bytes + u8 magic[8+1]; // usual magic, NULL terminated + ccp subdir; // NULL or BRRES directory name + + ccp read_info; // info about reading support, never NULL + ccp write_info; // info about writing support, never NULL + ccp remark; // remark about file type, never NULL + +} file_type_t; + +extern const file_type_t FileTypeTab[FF_N+1]; +extern const struct KeywordTab_t cmdtab_FileType[]; +extern const char filetype_info_unknown[]; +extern const char filetype_info_not_supported[]; + + +// +/////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// E N D ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_FILE_TYPE_H diff --git a/project/src/getopt.c b/project/src/getopt.c new file mode 100644 index 0000000..f0b8ce0 --- /dev/null +++ b/project/src/getopt.c @@ -0,0 +1,511 @@ +/* + * getopt.c - Enhanced implementation of BSD getopt(1) + * Copyright (c) 1997-2014 Frodo Looijaard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Version 1.0-b4: Tue Sep 23 1997. First public release. + * Version 1.0: Wed Nov 19 1997. + * Bumped up the version number to 1.0 + * Fixed minor typo (CSH instead of TCSH) + * Version 1.0.1: Tue Jun 3 1998 + * Fixed sizeof instead of strlen bug + * Bumped up the version number to 1.0.1 + * Version 1.0.2: Thu Jun 11 1998 (not present) + * Fixed gcc-2.8.1 warnings + * Fixed --version/-V option (not present) + * Version 1.0.5: Tue Jun 22 1999 + * Make -u option work (not present) + * Version 1.0.6: Tue Jun 27 2000 + * No important changes + * Version 1.1.0: Tue Jun 30 2000 + * Added NLS support (partly written by Arkadiusz Miśkiewicz + * ) + * Version 1.1.4: Mon Nov 7 2005 + * Fixed a few type's in the manpage + * Version 1.1.5: Sun Aug 12 2012 + * Sync with util-linux-2.21, fixed build problems, many new translations + * Version 1.1.6: Mon Nov 24 2014 + * Sync with util-linux git 20141120, detect ambiguous long options, fix + * backslash problem in tcsh + */ + +/* Exit codes: + * 0) No errors, successful operation. + * 1) getopt(3) returned an error. + * 2) A problem with parameter parsing for getopt(1). + * 3) Internal error, out of memory + * 4) Returned for -T + */ +#define GETOPT_EXIT_CODE 1 +#define PARAMETER_EXIT_CODE 2 +#define XALLOC_EXIT_CODE 3 +#define CLOSE_EXIT_CODE XALLOC_EXIT_CODE +#define TEST_EXIT_CODE 4 + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_PARAM_H +# include /* BSD */ +#endif + +//#include "closestream.h" +//#include "nls.h" +//#include "xalloc.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// by Wiimm /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#define main main_getopt +#define _ + +void * dclib_xcalloc ( size_t nmemb, size_t size ); +void * dclib_xmalloc ( size_t size ); +void * dclib_xrealloc ( void * ptr, size_t size ); +char * dclib_xstrdup ( const char * src ); + +#define xcalloc dclib_xcalloc +#define xmalloc dclib_xmalloc +#define xrealloc dclib_xrealloc +#define xstrdup dclib_xstrdup + +#define errtryhelp exit +#define warnx(format,...) fprintf(stderr,"wszst getopt: " format "\n",__VA_ARGS__) + +#define USAGE_HEADER "\nwszst GETOPT\n\n" +#define USAGE_SEPARATOR "\n" +#define USAGE_OPTIONS "\n" +#define USAGE_HELP_OPTIONS(a) "--" +#define USAGE_MAN_TAIL(s) "\n" +#define UTIL_LINUX_VERSION "wszst" + +#define program_invocation_short_name "wszst getopt" + +#define bindtextdomain(a,b) +#define textdomain(a) +#define atexit(a) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +/* NON_OPT is the code that is returned getopt(3) when a non-option is + * found in 'char optstring[]="-abc...";', e.g., it begins by '-' */ +#define NON_OPT 1 +/* LONG_OPT is the code that is returned when a long option is found. */ +#define LONG_OPT 0 + +/* The shells recognized. */ +typedef enum { BASH, TCSH } shell_t; + +struct getopt_control { + shell_t shell; /* the shell we generate output for */ + char *optstr; /* getopt(3) optstring */ + char *name; + struct option *long_options; /* long options */ + int long_options_length; /* length of options array */ + int long_options_nr; /* number of used elements in array */ + unsigned int + compatible:1, /* compatibility mode for 'difficult' programs */ + quiet_errors:1, /* print errors */ + quiet_output:1, /* print output */ + quote:1; /* quote output */ +}; + +enum { REALLOC_INCREMENT = 8 }; + +/* Allow changing which getopt is in use with function pointer. */ +static int (*getopt_long_fp) (int argc, char *const *argv, const char *optstr, + const struct option * longopts, int *longindex); + +/* + * This function 'normalizes' a single argument: it puts single quotes + * around it and escapes other special characters. If quote is false, it + * just returns its argument. + * + * Bash only needs special treatment for single quotes; tcsh also recognizes + * exclamation marks within single quotes, and nukes whitespace. This + * function returns a pointer to a buffer that is overwritten by each call. + */ +static void print_normalized(const struct getopt_control *ctl, const char *arg) +{ + char *buf; + const char *argptr = arg; + char *bufptr; + + if (!ctl->quote) { + printf(" %s", arg); + return; + } + + /* + * Each character in arg may take up to four characters in the + * result: For a quote we need a closing quote, a backslash, a quote + * and an opening quote! We need also the global opening and closing + * quote, and one extra character for '\0'. + */ + buf = xmalloc(strlen(arg) * 4 + 3); + bufptr = buf; + + for (*bufptr++ = '\''; *argptr; argptr++) { + if (ctl->shell == TCSH) { + switch (*argptr) { + case '\\': + /* Backslash: replace it with: '\\' */ + *bufptr++ = '\\'; + *bufptr++ = '\\'; + continue; + case '!': + /* Exclamation mark: replace it with: \! */ + *bufptr++ = '\''; + *bufptr++ = '\\'; + *bufptr++ = '!'; + *bufptr++ = '\''; + continue; + case '\n': + /* Newline: replace it with: \n */ + *bufptr++ = '\\'; + *bufptr++ = 'n'; + continue; + } + if (isspace(*argptr)) { + /* Non-newline whitespace: replace it with \ */ + *bufptr++ = '\''; + *bufptr++ = '\\'; + *bufptr++ = *argptr; + *bufptr++ = '\''; + continue; + } + } + if (*argptr == '\'') { + /* Quote: replace it with: '\'' */ + *bufptr++ = '\''; + *bufptr++ = '\\'; + *bufptr++ = '\''; + *bufptr++ = '\''; + } else + /* Just copy */ + *bufptr++ = *argptr; + } + + *bufptr++ = '\''; + *bufptr++ = '\0'; + printf(" %s", buf); + free(buf); +} + +/* + * Generate the output. argv[0] is the program name (used for reporting errors). + * argv[1..] contains the options to be parsed. argc must be the number of + * elements in argv (ie. 1 if there are no options, only the program name), + * optstr must contain the short options, and longopts the long options. + * Other settings are found in global variables. + */ +static int generate_output(struct getopt_control *ctl, char *argv[], int argc) +{ + int exit_code = EXIT_SUCCESS; /* Assume everything will be OK */ + int opt; + int longindex; + const char *charptr; + + if (ctl->quiet_errors) + /* No error reporting from getopt(3) */ + opterr = 0; + /* Reset getopt(3) */ + optind = 0; + + while ((opt = + (getopt_long_fp + (argc, argv, ctl->optstr, + (const struct option *)ctl->long_options, &longindex))) + != EOF) { + if (opt == '?' || opt == ':') + exit_code = GETOPT_EXIT_CODE; + else if (!ctl->quiet_output) { + switch (opt) { + case LONG_OPT: + printf(" --%s", ctl->long_options[longindex].name); + if (ctl->long_options[longindex].has_arg) + print_normalized(ctl, optarg ? optarg : ""); + break; + case NON_OPT: + print_normalized(ctl, optarg ? optarg : ""); + break; + default: + printf(" -%c", opt); + charptr = strchr(ctl->optstr, opt); + if (charptr != NULL && *++charptr == ':') + print_normalized(ctl, optarg ? optarg : ""); + } + } + } + if (!ctl->quiet_output) { + printf(" --"); + while (optind < argc) + print_normalized(ctl, argv[optind++]); + printf("\n"); + } + for (longindex = 0; longindex < ctl->long_options_nr; longindex++) + free((char *)ctl->long_options[longindex].name); + free(ctl->long_options); + free(ctl->optstr); + free(ctl->name); + return exit_code; +} + +/* + * Report an error when parsing getopt's own arguments. If message is NULL, + * we already sent a message, we just exit with a helpful hint. + */ +static void __attribute__ ((__noreturn__)) parse_error(const char *message) +{ + if (message) + warnx("%s", message); + errtryhelp(PARAMETER_EXIT_CODE); +} + + +/* Register a long option. The contents of name is copied. */ +static void add_longopt(struct getopt_control *ctl, const char *name, int has_arg) +{ + static int flag; + int nr = ctl->long_options_nr; + + if (ctl->long_options_nr == ctl->long_options_length) { + ctl->long_options_length += REALLOC_INCREMENT; + ctl->long_options = xrealloc(ctl->long_options, + sizeof(struct option) * + ctl->long_options_length); + } + if (name) { + /* Not for init! */ + ctl->long_options[nr].has_arg = has_arg; + ctl->long_options[nr].flag = &flag; + ctl->long_options[nr].val = ctl->long_options_nr; + ctl->long_options[nr].name = xstrdup(name); + } else { + /* lets use add_longopt(ct, NULL, 0) to terminate the array */ + ctl->long_options[nr].name = NULL; + ctl->long_options[nr].has_arg = 0; + ctl->long_options[nr].flag = NULL; + ctl->long_options[nr].val = 0; + } +} + + +/* + * Register several long options. options is a string of long options, + * separated by commas or whitespace. This nukes options! + */ +static void add_long_options(struct getopt_control *ctl, char *options) +{ + int arg_opt; + char *tokptr = strtok(options, ", \t\n"); + + while (tokptr) { + size_t len = strlen(tokptr); + + arg_opt = no_argument; + if (len > 0) { + if (tokptr[len - 1] == ':') { + if (tokptr[len - 2] == ':') { + tokptr[len - 2] = '\0'; + arg_opt = optional_argument; + } else { + tokptr[len - 1] = '\0'; + arg_opt = required_argument; + } + if (!*tokptr) + parse_error(_ + ("empty long option after " + "-l or --long argument")); + } + add_longopt(ctl, tokptr, arg_opt); + ctl->long_options_nr++; + } + tokptr = strtok(NULL, ", \t\n"); + } + add_longopt(ctl, NULL, 0); /* ensure long_options[] is not full */ +} + +static shell_t shell_type(const char *new_shell) +{ + if (!strcmp(new_shell, "bash")) + return BASH; + if (!strcmp(new_shell, "sh")) + return BASH; + if (!strcmp(new_shell, "tcsh")) + return TCSH; + if (!strcmp(new_shell, "csh")) + return TCSH; + parse_error(_("unknown shell after -s or --shell argument")); +} + +static void __attribute__((__noreturn__)) usage(void) +{ + fputs(USAGE_HEADER, stdout); + printf(_( + " %1$s \n" + " %1$s [options] [--] \n" + " %1$s [options] -o|--options [options] [--] \n"), + program_invocation_short_name); + + fputs(USAGE_SEPARATOR, stdout); + fputs(_("Parse command options.\n"), stdout); + + fputs(USAGE_OPTIONS, stdout); + fputs(_(" -a, --alternative allow long options starting with single -\n"), stdout); + fputs(_(" -l, --longoptions the long options to be recognized\n"), stdout); + fputs(_(" -n, --name the name under which errors are reported\n"), stdout); + fputs(_(" -o, --options the short options to be recognized\n"), stdout); + fputs(_(" -q, --quiet disable error reporting by getopt(3)\n"), stdout); + fputs(_(" -Q, --quiet-output no normal output\n"), stdout); + fputs(_(" -s, --shell set quoting conventions to those of \n"), stdout); + fputs(_(" -T, --test test for getopt(1) version\n"), stdout); + fputs(_(" -u, --unquoted do not quote the output\n"), stdout); + fputs(USAGE_SEPARATOR, stdout); + printf(USAGE_HELP_OPTIONS(31)); + printf(USAGE_MAN_TAIL("getopt(1)")); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + struct getopt_control ctl = { + .shell = BASH, + .quote = 1 + }; + int opt; + + /* Stop scanning as soon as a non-option argument is found! */ + static const char *shortopts = "+ao:l:n:qQs:TuhV"; + static const struct option longopts[] = { + {"options", required_argument, NULL, 'o'}, + {"longoptions", required_argument, NULL, 'l'}, + {"quiet", no_argument, NULL, 'q'}, + {"quiet-output", no_argument, NULL, 'Q'}, + {"shell", required_argument, NULL, 's'}, + {"test", no_argument, NULL, 'T'}, + {"unquoted", no_argument, NULL, 'u'}, + {"help", no_argument, NULL, 'h'}, + {"alternative", no_argument, NULL, 'a'}, + {"name", required_argument, NULL, 'n'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + if (getenv("GETOPT_COMPATIBLE")) + ctl.compatible = 1; + + if (argc == 1) { + if (ctl.compatible) { + /* + * For some reason, the original getopt gave no + * error when there were no arguments. + */ + printf(" --\n"); + return EXIT_SUCCESS; + } else + parse_error(_("missing optstring argument")); + } + + add_longopt(&ctl, NULL, 0); /* init */ + getopt_long_fp = getopt_long; + + if (argv[1][0] != '-' || ctl.compatible) { + ctl.quote = 0; + ctl.optstr = xmalloc(strlen(argv[1]) + 1); + strcpy(ctl.optstr, argv[1] + strspn(argv[1], "-+")); + argv[1] = argv[0]; + return generate_output(&ctl, argv + 1, argc - 1); + } + + while ((opt = + getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF) + switch (opt) { + case 'a': + getopt_long_fp = getopt_long_only; + break; + case 'h': + usage(); + case 'o': + free(ctl.optstr); + ctl.optstr = xstrdup(optarg); + break; + case 'l': + add_long_options(&ctl, optarg); + break; + case 'n': + free(ctl.name); + ctl.name = xstrdup(optarg); + break; + case 'q': + ctl.quiet_errors = 1; + break; + case 'Q': + ctl.quiet_output = 1; + break; + case 's': + ctl.shell = shell_type(optarg); + break; + case 'T': + free(ctl.long_options); + return TEST_EXIT_CODE; + case 'u': + ctl.quote = 0; + break; + case 'V': + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case '?': + case ':': + parse_error(NULL); + default: + parse_error(_("internal error, contact the author.")); + } + + if (!ctl.optstr) { + if (optind >= argc) + parse_error(_("missing optstring argument")); + else { + ctl.optstr = xstrdup(argv[optind]); + optind++; + } + } + + if (ctl.name) { + argv[optind - 1] = ctl.name; +#if defined (HAVE_SETPROGNAME) && !defined (__linux__) + setprogname(ctl.name); +#endif + } else + argv[optind - 1] = argv[0]; + + return generate_output(&ctl, argv + optind - 1, argc - optind + 1); +} diff --git a/project/src/lib-breff.c b/project/src/lib-breff.c new file mode 100644 index 0000000..45b37ab --- /dev/null +++ b/project/src/lib-breff.c @@ -0,0 +1,451 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "lib-breff.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// breff+breft file iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesBREFF +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + //--- bref* header + + if ( !szs->data || szs->size < sizeof(brres_header_t) ) + return -1; + + const brres_header_t * bh = (brres_header_t*)szs->data; + ccp root_magic = 0; + u32 add_file_size = 0; + switch (szs->fform_file) + { + case FF_BREFF: + root_magic = BREFF_MAGIC; + break; + + case FF_BREFT: + root_magic = BREFT_MAGIC; + add_file_size = 0x20; + break; + + default: + return -1; + } + + if (memcmp(bh->magic,root_magic,sizeof(bh->magic))) + return -1; + + const endian_func_t * endian = GetEndianFunc(&bh->bom); + if (!endian) + return -1; + szs->endian = endian; + + const uint data_size = endian->rd32(&bh->size); + const uint root_off = endian->rd16(&bh->root_off); + TRACE("data_size=%u, root_off=%u, n_sections=%u\n", + data_size,root_off, endian->rd16(&bh->n_sections)); + if ( data_size > szs->size || root_off >= szs->size ) + return -1; + + // [extend] may be a list of roots (or itemlists) + + + //--- analyze root + + const breff_root_head_t * rh = (breff_root_head_t*)( szs->data + sizeof(*bh) ); + if (memcmp(rh->magic,root_magic,sizeof(rh->magic))) + return -1; + + const uint root_size = endian->rd32(&rh->data_size); + if ( root_size + sizeof(*bh) > szs->size ) + return -1; + + const breff_root_t * root = (breff_root_t*)rh->data; + szs->obj_name = root->name; + //szs->obj_id = endian->rd16(&root->unknown3); + //szs->obj_id_used = true; + TRACE("ROOT: val=%x,%x,%x, name[%u]=%s\n", + endian->rd32(&root->unknown1), + endian->rd32(&root->unknown2), + endian->rd16(&root->unknown3), + endian->rd16(&root->name_len0), szs->obj_name ); + + + //--- analyze item list + + const uint first_item_off = endian->rd32(&root->first_item_off); + breff_item_list_t * ilist = (breff_item_list_t*)( rh->data + first_item_off ); + if ( (u8*)ilist >= szs->data + szs->size ) + return -1; + const uint ilist_size = endian->rd32(&ilist->size); + const uint n_item = endian->rd16(&ilist->n_item); + TRACE("ITEM-LIST: n=%u, size=%x\n",n_item,ilist_size); + + + //--- call iterator function for file header + + StringCopyS(it->path,sizeof(it->path)-2,"header.bin"); + it->is_dir = false; + it->off = 0; + it->size = (u8*)rh - szs->data; + int stat = it->func_it(it,false); + if (stat) + return stat; + it->index++; + + + //--- call iterator function for root + + StringCopyS(it->path,sizeof(it->path)-2,"root.bin"); + it->off = (u8*)rh - szs->data; + it->size = (u8*)ilist - (u8*)rh; + stat = it->func_it(it,false); + if (stat) + return stat; + it->index++; + + + //--- call iterator function for file list + + StringCopyS(it->path,sizeof(it->path)-2,"file-list.bin"); + it->off = (u8*)ilist - szs->data; + it->size = ilist_size; + stat = it->func_it(it,false); + if (stat) + return stat; + it->index++; + + + //--- call iterator function for 'files' directory + + char * path_ptr = StringCopyS(it->path,sizeof(it->path)-2,"files"); + *path_ptr++ = '/'; + *path_ptr = 0; + it->is_dir = true; + it->off = 0; + it->size = 0; + stat = it->func_it(it,false); + if (stat) + return stat; + it->index++; + + + //--- iterate item list + + //************************************************************** + //*** This pointer is not aligned because of used names! *** + //*** It's not problematic if reading via endian functions *** + //************************************************************** + const breff_item_name_t * item = ilist->item; + const u32 base_off = (u8*)ilist - szs->data; + + uint ii; + for ( ii = 0; ii < n_item; ii++ ) + { + const uint namelen0 = endian->rd16(&item->name_len0); + char *name = (char*)(item+1); + const breff_item_data_t * idata = (breff_item_data_t*)( name + namelen0 ); + noPRINT("%3u: %6x %6x %s\n", + ii, + endian->rd32(&idata->offset), + endian->rd32(&idata->size), + name ); + + StringCopyE(path_ptr,it->path + sizeof(it->path), name ); + it->is_dir = false; + it->off = endian->rd32(&idata->offset) + base_off; + it->size = endian->rd32(&idata->size) + add_file_size; + + if ( szs->min_data_off > it->off ) + szs->min_data_off = it->off; + + const u32 max = it->off + it->size; + if ( szs->max_data_off < max ) + szs->max_data_off = max; + + const int stat = it->func_it(it,false); + if (stat) + break; + it->index++; + + item = (breff_item_name_t*)( (u8*)idata + sizeof(*idata) ); + } + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// create breff /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateBREFF +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 total_size, // total file size + SetupParam_t * setup_param // NULL or setup parameters +) +{ + DASSERT(szs); + PRINT("CreateBREFF() ndir=%d, totsize=%x, align=%x,%x\n", + szs->subfile.used, total_size, opt_align_breff, opt_align_breft ); + + enumError max_err = ERR_OK; + const endian_func_t * endian = &be_func; + szs->endian = endian; + + const bool is_breft = setup_param && setup_param->fform_arch == FF_BREFT; + szs->fform_arch = is_breft ? FF_BREFT : FF_BREFF; + szs->ff_attrib = GetAttribFF(szs->fform_arch); + szs->ff_version = setup_param && setup_param->version > 0 + ? setup_param->version : 9; + + uint align; + if (is_breft) + { + align = opt_align_breft + ? opt_align_breft + : szs->ff_version <= 9 + ? BREFT_9_DEFAULT_ALIGN + : BREFT_11_DEFAULT_ALIGN; + } + else + align = opt_align_breff; + + + //--- sort files using 'file-list.bin' + + SortSubFilesSZS(szs,SORT_BREFF); + + if ( logging > 1 ) + { + printf("\nsorted file list:\n"); + int i; + for ( i = 0; i < szs->subfile.used; i++ ) + { + szs_subfile_t * f = szs->subfile.list + i; + printf("%3d.: %u %6x %6x %s\n", i, f->is_dir, f->offset, f->size, f->path ); + } + } + + //--- find root name + + ccp root_name = 0; + bool root_name_alloced = false; + + if ( setup_param && setup_param->object_name && *setup_param->object_name ) + { + root_name = setup_param->object_name; + PRINT("ROOT-NAME: %s [setup-param]\n",root_name); + } + else + { + ccp end = source_dir + strlen(source_dir) - 1; + while ( end >= source_dir && *end == '/' ) + end--; + ccp beg = end++; + while ( beg >= source_dir && *beg != '/' ) + { + if ( *beg == '.' ) + { + const uint len = end - beg; + if ( len == 2 & !memcmp(beg,".d",2) + || len == 6 & !memcmp(beg,".breff",6) + || len == 6 & !memcmp(beg,".breft",6) ) + { + end = beg; + } + } + beg--; + } + beg++; + const uint len = end - beg; + char *name = MALLOC(len+1); + memcpy(name,beg,len); + name[len] = 0; + root_name = name; + root_name_alloced = true; + PRINT("ROOT-NAME: %s [path]\n",root_name); + } + + + //--- calculate data size + + uint root_name_size = strlen(root_name) + 1; + uint root_size = sizeof(breff_root_head_t) + sizeof(breff_root_t) + root_name_size; + if ( is_breft && root_size < 0x50 ) + root_size = 0x50; + root_size = ALIGN32(root_size,4); + + uint ilist_size = sizeof(breff_item_list_t); // size of item list + uint fdata_size = 0; // size of file data + + szs_subfile_t *fptr, *fend = szs->subfile.list + szs->subfile.used; + for ( fptr = szs->subfile.list; fptr < fend; fptr++ ) + { + //--- name + + ccp fname = strrchr(fptr->path,'/'); + fname = fname ? fname + 1 : fptr->path; + const uint namelen = strlen(fname) + 1; + ilist_size += sizeof(breff_item_data_t) + sizeof(breff_item_name_t) + namelen; + + //--- data + + fptr->offset = fdata_size; + fdata_size = ALIGN32( fdata_size + fptr->size, align ); + } + ilist_size = ALIGN32( ilist_size, align ); + szs->size = sizeof(brres_header_t) + root_size + ilist_size + fdata_size; + + PRINT("SIZE: root=%x, flist=%x, fdata=%x => %zx = %zu\n", + root_size, ilist_size, fdata_size, szs->size, szs->size ); + + + //--- setup data pointer + + szs->data = CALLOC(1,szs->size); + + brres_header_t *bh = (brres_header_t*)szs->data; + breff_root_head_t *rh = (breff_root_head_t*)(bh+1); + breff_root_t *root = (breff_root_t*)(&rh->data); + breff_item_list_t *ilist = (breff_item_list_t*)( (u8*)rh + root_size ); + u8 *data = (u8*)ilist + ilist_size; + DASSERT( data + fdata_size == szs->data + szs->size ); + + PRINT("%8zx file header\n", (u8*)bh - szs->data ); + PRINT("%8zx root header\n", (u8*)rh - szs->data ); + PRINT("%8zx root\n", (u8*)root - szs->data ); + PRINT("%8zx file list\n", (u8*)ilist - szs->data ); + PRINT("%8zx data\n", (u8*)data - szs->data ); + PRINT("%8zx END\n", szs->size ); + + + //--- setup file header + + ccp magic = is_breft ? BREFT_MAGIC : BREFF_MAGIC; + memcpy(bh->magic,magic,sizeof(bh->magic)); + endian->wr16(&bh->bom,0xfeff); + endian->wr16(&bh->version,szs->ff_version); + endian->wr32(&bh->size,szs->size); + endian->wr16(&bh->root_off,sizeof(brres_header_t)); + endian->wr16(&bh->n_sections,1); + + + //--- setup root + + memcpy(rh->magic,magic,sizeof(bh->magic)); + endian->wr32(&rh->data_size,szs->size-sizeof(*bh)-sizeof(*rh)); + endian->wr32(&root->first_item_off,(u8*)ilist-(u8*)root); + endian->wr16(&root->name_len0,(root_name_size)); + memcpy(root->name,root_name,root_name_size); + + + //--- setup item list + + endian->wr32(&ilist->size,ilist_size); + endian->wr16(&ilist->n_item,szs->subfile.used); + + + //--- load files + + u8 *idata = (u8*)ilist->item; + for ( fptr = szs->subfile.list; fptr < fend; fptr++ ) + { + //--- name + + ccp fname = strrchr(fptr->path,'/'); + fname = fname ? fname + 1 : fptr->path; + const uint namelen = strlen(fname) + 1; + endian->wr16(idata,namelen); + idata += 2; + memcpy(idata,fname,namelen); + idata += namelen; + + endian->wr32(idata,data-(u8*)ilist); + idata += 4; + endian->wr32( idata, fptr->size - ( is_breft ? 0x20 : 0 ) ); + idata += 4; + + //--- data + + PRINT("LOAD: %zx+%x %s\n",data-szs->data,fptr->size,fptr->path); + if (fptr->data) + memcpy(data,fptr->data,fptr->size); + else + { + enumError err = LoadFILE(source_dir,fptr->path,0, + data, fptr->size, 0, &szs->fatt,true ); + if ( max_err < err ) + max_err = err; + } + data += ALIGN32( fptr->size, align ); + } + + + //--- terminate + + if (root_name_alloced) + FreeString(root_name); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-breff.h b/project/src/lib-breff.h new file mode 100644 index 0000000..5799c18 --- /dev/null +++ b/project/src/lib-breff.h @@ -0,0 +1,168 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// all about SZS, YAZ0 and U8 files: +// http://wiki.tockdom.de/index.php?title=SZS +// http://wiki.tockdom.de/index.php?title=YAZ0 +// http://wiki.tockdom.de/index.php?title=U8 + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_BREFF_H +#define SZS_LIB_BREFF_H 1 + +#include "lib-brres.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BREFF + BREFT file header /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// BREFF + BREFT uses the same header like BRRES, but other magics + +#define BREFF_MAGIC "REFF" +#define BREFF_MAGIC_NUM 0x52454646 +#define BREFF_DEFAULT_ALIGN 0x04 + +#define BREFT_MAGIC "REFT" +#define BREFT_MAGIC_NUM 0x52454654 +#define BREFT_9_DEFAULT_ALIGN 0x20 +#define BREFT_11_DEFAULT_ALIGN 0x40 + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// breff root /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct breff_root_head_t +{ + char magic[4]; // magic + u32 data_size; // data size + u8 data[0]; // data, starting with a breff_root_t + +} +__attribute__ ((packed)) breff_root_head_t; + +//----------------------------------------------------------------------------- + +typedef struct breff_root_t +{ + u32 first_item_off; // offset of first breff_item_t + u32 unknown1; // always 0 in MKW + u32 unknown2; // always 0 in MKW + u16 name_len0; // length of name including terminating NULL + u16 unknown3; // always 0 in MKW + char name[0]; // NULL terminated name + //u8 data[]; // padding or real data +} +__attribute__ ((packed)) breff_root_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct breff_item_name_t +{ + u16 name_len0; // length of name including terminating NULL + //char name[0]; // NULL terminated name + // breff_item_data_t follows not aligned + // -> disabled because of MVC +} +__attribute__ ((packed)) breff_item_name_t; + +//----------------------------------------------------------------------------- + +typedef struct breff_item_data_t +{ + u32 offset; // data offset relative to item list + u32 size; // data size +} +__attribute__ ((packed)) breff_item_data_t; + +//----------------------------------------------------------------------------- + +typedef struct breff_item_list_t +{ + u32 size; // size of all items + u16 n_item; // number of itmes + u16 unknown1; + breff_item_name_t item[0]; // first item +} +__attribute__ ((packed)) breff_item_list_t; + +//----------------------------------------------------------------------------- + +typedef struct breft_image_t +{ + u32 unknown1; // always 0 + u16 width; + u16 height; + u32 img_size; + u8 iform; + u8 unknown2[3+4]; // nearly always 0 + u8 n_image; // >0: number of images (=N(mipmaps)+1) + u8 unknown3[3+8]; // nearly always 0 + u8 data[]; // image data +} +__attribute__ ((packed)) breft_image_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesBREFF +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +//----------------------------------------------------------------------------- + +enumError CreateBREFF +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 total_size, // total file size + SetupParam_t * setup_param // NULL or setup parameters +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_BREFF_H diff --git a/project/src/lib-brres.c b/project/src/lib-brres.c new file mode 100644 index 0000000..ea18724 --- /dev/null +++ b/project/src/lib-brres.c @@ -0,0 +1,2125 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "lib-brres.h" +#include "lib-mdl.h" +#include "lib-pat.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// brres helper /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u16 get_highest_bit ( u8 val ) +{ + // return bit number of highest set bit, or 0 if no bit is set. + + return val & 0xf0 + ? ( val & 0x80 ? 7 + : val & 0x40 ? 6 + : val & 0x20 ? 5 + : 4 ) + : ( val & 0x08 ? 3 + : val & 0x04 ? 2 + : val & 0x02 ? 1 + : 0 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static u16 calc_brres_id +( + ccp prev_name, + uint prev_len, + ccp cur_name, + uint cur_len +) +{ + if ( prev_len < cur_len ) + return cur_len - 1 << 3 | get_highest_bit(cur_name[cur_len-1]); + + while ( cur_len-- > 0 ) + { + const u8 ch = prev_name[cur_len] ^ cur_name[cur_len]; + if (ch) + return cur_len << 3 | get_highest_bit(ch); + } + + return ~(u16)0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void dump_brres_group +( + const brres_root_t * root, + const endian_func_t * endian +) +{ + const uint root_size = endian->rd32(&root->size); + const u8 * root_end = (u8*)root + root_size; + + int grp_idx = 0; + const brres_group_t * grp = BRRES_ROOT_GROUP(root); + while ( (u8*)grp < root_end ) + { + const uint grp_size = endian->rd32(&grp->size); + const uint n_entries = endian->rd32(&grp->n_entries); + const u8 * next_grp = (u8*)grp + grp_size; + noPRINT("GRP %2u: n=%02u, size=%04x\n", + grp_idx, n_entries, grp_size ); + if ( next_grp > root_end ) + break; + + printf( "\n" + " idx ID ? left right name data magic name\n" + "%.79s\n",Minus300); + + const brres_entry_t * entry = grp->entry; + const brres_entry_t * entry_end = entry + 1 + n_entries; + int entry_idx; + for ( entry_idx = 0; entry < entry_end; entry++, entry_idx++ ) + { + ccp name = ""; + uint name_len = 0; + const uint name_off = endian->rd32(&entry->name_off); + if (name_off) + { + name = (ccp)grp + name_off; + name_len = endian->rd32(name-4); + } + + const uint data_off = endian->rd32(&entry->data_off); + const u8 * data = (u8*)grp + data_off; + + printf("%4x: %4x %4x %4x %4x %8x %8x %-4s %.*s\n", + entry_idx, + endian->rd16(&entry->id), + endian->rd16(&entry->unknown), + endian->rd16(&entry->left_idx), + endian->rd16(&entry->right_idx), + endian->rd32(&entry->name_off), + endian->rd32(&entry->data_off), + data_off ? PrintID(data,4,0) : "-", + name_len, name ); + } + + grp = (brres_group_t*)next_grp; + grp_idx++; + } + putchar('\n'); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// get BRSUB info /////////////// +/////////////////////////////////////////////////////////////////////////////// +// http://wiki.tockdom.com/wiki/BRRES_Sub_Files_(File_Format)#Common_Format + +const brsub_info_t brsub_info[] = +{ + + //----------------------------------------------------------------------- + // fform1+2, vers nsect warn // comment + //----------------------------------------------------------------------- + + { FF_CHR, 0, 3,5, 1,2, BIMD_FATAL }, + { FF_CHR, 0, 5,5, 2,2, BIMD_OK }, // [[2do]] verify hexdump + + { FF_CLR, 0, 4,4, 2,2, BIMD_OK }, // [[2do]] verify hexdump + + { FF_MDL, 0, 8,11, 11,14, BIMD_FATAL }, + { FF_MDL, 0, 9,11, 11,14, BIMD_FATAL }, // found in SSBB + { FF_MDL, 0, 11,11, 14,14, BIMD_OK }, + + { FF_PAT, 0, 4,4, 6,6, BIMD_OK }, + + { FF_SCN, 0, 4,5, 6,7, BIMD_FATAL }, + { FF_SCN, 0, 5,5, 7,7, BIMD_OK }, // [[2do]] verify hexdump + + { FF_SHP, 0, 4,4, 3,3, BIMD_OK }, // [[2do]] verify hexdump + + { FF_SRT, 0, 4,5, 1,2, BIMD_FATAL }, + { FF_SRT, 0, 5,5, 2,2, BIMD_OK }, // [[2do]] verify hexdump + + { FF_TEX, + FF_TEX_CT, 1,3, 1,1, BIMD_HINT }, + { FF_TEX, + FF_TEX_CT, 2,3, 2,1, BIMD_FAIL }, + { FF_TEX, + FF_TEX_CT, 3,3, 1,1, BIMD_OK }, + + //--- list terminated with fform1 == 0 (FF_UNKNOWN) + {0,0,0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +const brsub_info_t * GetInfoBRSUB +( + file_format_t fform, // file format + u32 version // sub version +) +{ + if (fform) + { + const brsub_info_t *bi; + for ( bi = brsub_info; bi->fform1; bi++ ) + if ( ( bi->fform1 == fform || bi->fform2 == fform ) + && bi->version == version ) + { + return bi; + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const brsub_info_t * GetCorrectBRSUB +( + file_format_t fform // file format +) +{ + if (fform) + { + const brsub_info_t *bi; + for ( bi = brsub_info; bi->fform1; bi++ ) + if ( ( bi->fform1 == fform || bi->fform2 == fform ) && !bi->warn ) + return bi; + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// get num of sections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int GetKnownSectionNumBRSUB +( + file_format_t fform, // file format + u32 version // sub version +) +{ + noPRINT("GetKnownSectionNumBRSUB(%d,%d) ff=%s\n", + fform, version, GetNameFF(0,fform) ); + + const brsub_info_t *bi = GetInfoBRSUB(fform,version); + return bi ? bi->n_sect : -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetGenericSectionNumBRSUB +( + const u8 * data, // valid data pointer + uint data_size, // size of 'data' + const endian_func_t * endian // NULL or endian functions +) +{ + DASSERT(data); + if ( data_size < 0x20 ) + return -1; + + if (!endian) + endian = &be_func; + u32 max_offset = endian->rd32(data+4); + if ( max_offset > data_size ) + max_offset = data_size; + const u32 body_size = max_offset; + + int n_sect; + for ( n_sect = 0; ; n_sect++ ) + { + u32 offset = 0x10 + n_sect * 4; + if ( offset >= max_offset ) + break; + + u32 grp_offset = endian->rd32(data+offset); + noPRINT("n=%2u, off=%5x, goff=%5x, maxoff=%5x\n", + n_sect, offset, grp_offset, max_offset ); + if (!grp_offset) + continue; + if ( grp_offset <= offset || grp_offset > body_size || grp_offset&3 ) + break; + + if ( data[grp_offset] + && endian->rd32(data+grp_offset-4) == strlen((ccp)data+grp_offset) ) + break; + + if ( max_offset > grp_offset ) + max_offset = grp_offset; + } + + return n_sect; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetSectionNumBRSUB +( + const u8 * data, // valid data pointer + uint data_size, // size of 'data' + const endian_func_t * endian // NULL or endian functions +) +{ + DASSERT(data); + +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + if ( fform && data_size >= 0x0c ) + { + if (!endian) + endian = &be_func; + const u32 version = endian->rd32(data+8); + const int n = GetKnownSectionNumBRSUB(fform,version); + if ( n >= 0 ) + return n; + } + + return GetGenericSectionNumBRSUB(data,data_size,endian); +}; + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintHeaderNameBRSUB +( + char * buf, // result buffer + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + const brsub_header_t * bhead, // valid brsub header + uint n_grp, // number of groups + const endian_func_t * endian // NULL or endian +) +{ + DASSERT(bhead); + + if (!buf) + buf = GetCircBuf( buf_size = 40 ); + + if (endian) + endian = &be_func; + + snprintf( buf, buf_size, + ".BRSUB.header.%s-v%u.n%u.bin", + PrintID(bhead->magic,4,0), + endian->rd32(&bhead->version), + n_grp ); + + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// create brres /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool get_brres_id_bit +( + brres_info_t * info, + u16 id +) +{ + DASSERT(info); + const uint char_idx = id >> 3; + noPRINT("get_brres_id_bit(%x,%.*s) -> %d+%d/%d -> %d\n", + id, info->nlen, info->name, + char_idx, id&7, info->nlen, + char_idx < info->nlen + && info->name[char_idx] >> ( id & 7 ) & 1 ); + return char_idx < info->nlen + && info->name[char_idx] >> ( id & 7 ) & 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcEntryBRRES +( + brres_info_t * info, + uint entry_idx +) +{ + DASSERT(info); + + // setup 'entry' : item to insert + brres_info_t * entry = info + entry_idx; + entry->id = calc_brres_id(0,0,entry->name,entry->nlen); + entry->left_idx = entry->right_idx = entry_idx; + + // setup 'prev' : the previuos 'current' item + brres_info_t * prev = info; + + // setup 'current' : the current item while walking through the tree + uint current_idx = prev->left_idx; + brres_info_t * current = info + current_idx; + + // last direction + bool is_right = false; + + noPRINT("++ID: e=%zx,%x, p=%zx,%x, c=%zx,%x\n", + entry-info, entry->id, + prev-info, prev->id, + current-info, current->id ) ; + + while ( entry->id <= current->id && current->id < prev->id ) + { + if ( entry->id == current->id ) + { + // calculate a new entry id + entry->id = calc_brres_id( current->name, current->nlen, + entry->name, entry->nlen ); + if (get_brres_id_bit(current,entry->id)) + { + entry->left_idx = entry_idx; + entry->right_idx = current_idx; + } + else + { + entry->left_idx = current_idx; + entry->right_idx = entry_idx; + } + } + + prev = current; + + is_right = get_brres_id_bit(entry,current->id); + + current_idx = is_right ? current->right_idx : current->left_idx; + current = info + current_idx; + noPRINT("ID: e=%zx,%x, p=%zx,%x, c=%zx,%x\n", + entry-info, entry->id, + prev-info, prev->id, + current-info, current->id ) ; + } + + if ( current->nlen == entry->nlen && get_brres_id_bit(current,entry->id) ) + entry->right_idx = current_idx; + else + entry->left_idx = current_idx; + + if (is_right) + prev->right_idx = entry_idx; + else + prev->left_idx = entry_idx; +} + +//----------------------------------------------------------------------------- + +enumError CreateBRRES +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 total_size // total file size +) +{ + DASSERT(szs); + PRINT("CreateBRRES() ndir=%d, totsize=%x, align=%x\n", + szs->subfile.used, total_size, opt_align_brres ); + + enumError max_err = ERR_OK; + char path_buf[PATH_MAX]; + const endian_func_t * endian = &be_func; + szs->endian = endian; + + + //--- sort + + SortSubFilesSZS(szs,SORT_BRRES); + + + //--- load and analyze data + + total_size = ALIGN32(total_size,opt_align_brres); + u8 * file_data = CALLOC(1,total_size); + + uint n_directories = 1; + uint total_data_size = 0; + uint min_string_pool_size = 0; + + { + // find all strings + + u8 * data_ptr = file_data; + szs_subfile_t *f_ptr, *f_end = szs->subfile.list + szs->subfile.used; + for ( f_ptr = szs->subfile.list; f_ptr < f_end; f_ptr++ ) + { + noPRINT_IF(f_ptr->is_dir, "DIR-ID=%d, |%s|\n",f_ptr->dir_id,f_ptr->path); + + if (f_ptr->is_dir) + { + PRINT("DIR #%u FOUND: %s\n",n_directories,f_ptr->path); + IsolateFilename(path_buf,sizeof(path_buf),f_ptr->path,0); + InsertStringPool(&szs->string_pool,path_buf,false,0); + n_directories++; + } + else + { + f_ptr->offset = data_ptr - file_data; + u32 data_size = f_ptr->size; + if (f_ptr->data) + memcpy(data_ptr,f_ptr->data,data_size); + else + { + enumError err = LoadFILE(source_dir,f_ptr->path,0, + data_ptr,data_size,0,&szs->fatt,true); + if ( max_err < err ) + max_err = err; + } + +// [[analyse-magic]] + f_ptr->fform = GetByMagicFF(data_ptr,data_size,data_size); + IsolateFilename(path_buf,sizeof(path_buf),f_ptr->path,f_ptr->fform); + InsertStringPool(&szs->string_pool,path_buf,false,0); + + if (IsValidBRSUB(data_ptr,data_size,data_size, + 0,f_ptr->fform,true,f_ptr->path,endian) < VALID_ERROR ) + { + noPRINT("----- %s -> %s -----\n",f_ptr->path,path_buf); + szs_file_t sub; + AssignSZS(&sub,true,data_ptr,data_size,false,FF_UNKNOWN,f_ptr->path); + if ( CollectStringsBRSUB(&szs->string_pool,false,&sub,false) > 0 ) + { + // fix file size for later copy operation + const u32 new_data_size = endian->rd32(data_ptr+4); + if ( data_size > new_data_size ) + data_size = new_data_size; + + brsub_list_t * list = CALLOC(1,sizeof(*list)); + list->data = data_ptr; + list->size = data_size; + list->next = szs->brsub_list; + szs->brsub_list = list; + } + ResetSZS(&sub); + + const int string_pool_size = f_ptr->size - data_size; + if ( (int)min_string_pool_size < string_pool_size ) + min_string_pool_size = string_pool_size; + } + + total_data_size += ALIGN32(data_size,opt_align_brres); + data_ptr += ALIGN32(f_ptr->size,opt_align_brres); + DASSERT( data_ptr <= file_data + total_size ); + } + } + DASSERT( data_ptr == file_data + total_size ); + + #if defined(TEST) && 0 + ccp * ptr = szs->string_pool.sf.field; + ccp * end = ptr + szs->string_pool.sf.used; + while ( ptr < end ) + PRINT(">%s<\n",*ptr++); + #endif + } + + + //--- size calculations + + u32 grp_offset = sizeof(brres_header_t) + sizeof(brres_root_t); + u32 grp_end = grp_offset + + sizeof(brres_group_t) * n_directories + + sizeof(brres_entry_t) * ( n_directories + szs->subfile.used ); + + u32 data_offset = grp_end <= szs->min_data_off + ? ALIGN32(szs->min_data_off,opt_align_brres) + : ALIGN32(grp_end, + opt_align_brres > 0x20 ? opt_align_brres : 0x20 ); + u32 str_offset = ALIGN32(data_offset + total_data_size, opt_align_brres ); + CalcStringPool(&szs->string_pool,str_offset,endian); + u32 data_size = ALIGN32( str_offset + szs->string_pool.size, + opt_align_brres > 0x80 ? opt_align_brres : 0x80 ); + + PRINT("* GRP=%x..%x, DATA=%x, STR=%x, END=%x\n", + grp_offset, grp_end, data_offset, str_offset, data_size ); + + + szs->fform_arch = FF_BRRES; + szs->ff_attrib = GetAttribFF(szs->fform_arch); + szs->ff_version = -1; + szs->data_alloced = true; + szs->size = data_size; + szs->file_size = data_size; + + // alloc a little bit more temporary space if needed + if ( min_string_pool_size < szs->string_pool.size ) + min_string_pool_size = szs->string_pool.size; + szs->data = CALLOC(1,szs->size - szs->string_pool.size + min_string_pool_size ); + + + //--- setup BRRES header + + brres_header_t * bhead = (brres_header_t*)szs->data; + memcpy(bhead->magic,BRRES_MAGIC,sizeof(bhead->magic)); + endian->wr16(&bhead->bom,0xfeff); + endian->wr32(&bhead->size,data_size); + endian->wr16(&bhead->root_off,sizeof(brres_header_t)); + endian->wr16(&bhead->n_sections, 2 + szs->subfile.used - n_directories ); + + + //--- setup BRRES root + + brres_root_t * broot = (brres_root_t*)(szs->data+sizeof(brres_header_t)); + memcpy(broot->magic,BRRES_ROOT_MAGIC,sizeof(broot->magic)); + endian->wr32(&broot->size, grp_end - grp_offset + sizeof(brres_root_t) ); + + + //--- setup stack for directory offsets + + typedef struct entry_pt_t + { + u8 * dest; // destination + u32 delta; // delta, that must be substracted + + } entry_pt_t; + + entry_pt_t * ep_list = CALLOC(n_directories+1,sizeof(entry_pt_t)); + entry_pt_t * ep_read = ep_list; + entry_pt_t * ep_write = ep_list + 1; // skip first + + + //--- setup subfile int dir_id order + + const uint dir_order_size = ( n_directories + 1 ) * sizeof(u16); + u16 * dir_order = MALLOC(dir_order_size); + memset(dir_order,~0,dir_order_size); // special value for empty directories + + bool * dir_done = CALLOC(n_directories+1,sizeof(*dir_done)); + + u16 * dir_ptr = dir_order; + szs_subfile_t *f_ptr, *f_end = szs->subfile.list + szs->subfile.used; + for ( f_ptr = szs->subfile.list; f_ptr < f_end; f_ptr++ ) + { + const u16 dir_id = f_ptr->dir_id; + DASSERT( dir_id < n_directories ); + if (!dir_done[dir_id]) + { + dir_done[dir_id] = true; + *dir_ptr++ = dir_id; + } + } + PRINT("DIR-PTR = %zu/%u\n", dir_ptr - dir_order, n_directories ); + DASSERT( dir_ptr - dir_order <= n_directories ); + FREE(dir_done); + + + //--- setup info + + // that's enough! + const size_t info_size = ( szs->subfile.used + 1 ) * sizeof(brres_info_t); + brres_info_t * info_list = MALLOC(info_size); + + + //--- setup groups + + u8 * data_ptr = szs->data + data_offset; + brres_group_t * grp = BRRES_ROOT_GROUP(broot); + + int dir_index; + for ( dir_index = 0; dir_index < n_directories; dir_index++ ) + { + u16 dir_id = dir_order[dir_index]; + szs_subfile_t *f_ptr = szs->subfile.list; + while ( f_ptr < f_end && f_ptr->dir_id != dir_id ) + f_ptr++; + + int n_entries = 0; + szs_subfile_t *f_temp; + for ( f_temp = f_ptr; f_temp < f_end; f_temp++ ) + if ( f_temp->dir_id == dir_id ) + n_entries++; + + noPRINT("----- NEW GRP #%u: N=%u, %zx -----\n", + dir_id, n_entries, (u8*)grp - szs->data ); + + //--- set entry point + + noPRINT("EP-READ #%zu: %zx-%x := %zx\n", + ep_read - ep_list, + ep_read->dest ? ep_read->dest - szs->data : 0, + ep_read->delta, + (u8*)grp - szs->data - ep_read->delta ); + if (ep_read->dest) + endian->wr32( ep_read->dest, (u8*)grp - szs->data - ep_read->delta ); + ep_read++; + + + //--- setup info list + + memset(info_list,0,info_size); + info_list->id = 0xffff; + brres_info_t *info_ptr = info_list + 1; + + + //--- setup group + + endian->wr32(&grp->size, sizeof(brres_group_t) + + (n_entries+1) * sizeof(brres_entry_t) ); + endian->wr32(&grp->n_entries,n_entries); + + brres_entry_t * entry = grp->entry; + endian->wr16(&entry->id,0xffff); + entry++; + + //--- iterate files + + for ( ; f_ptr < f_end; f_ptr++ ) + { + if ( f_ptr->dir_id != dir_id ) + continue; + + noPRINT("*** %d %3u %s\n",f_ptr->is_dir,f_ptr->dir_id,f_ptr->path); + + //--- write name data + + IsolateFilename(path_buf,sizeof(path_buf),f_ptr->path,f_ptr->fform); + const u32 str_off = FindStringPool(&szs->string_pool,path_buf,0); + endian->wr32( &entry->name_off, (ccp)szs->data + str_off - (ccp)grp ); + + info_ptr->name = (ccp)szs->string_pool.data + str_off - str_offset; + info_ptr->nlen = strlen(info_ptr->name); + info_ptr++; + DASSERT( info_ptr - info_list <= szs->subfile.used ); + + //--- + + CalcEntryBRRES( info_list, entry - grp->entry ); + + //--- + + if (f_ptr->is_dir) + { + ep_write->dest = (u8*)&entry->data_off; + ep_write->delta = (u8*)grp - szs->data; + noPRINT("EP-WRITE #%zu: %zx-%x\n", + ep_write - ep_list, + ep_write->dest - szs->data, ep_write->delta ); + ep_write++; + DASSERT( ep_write - ep_list <= n_directories ); + } + else + { + endian->wr32(&entry->data_off,(ccp)data_ptr-(ccp)grp); + u32 data_size = f_ptr->size; + memcpy(data_ptr,file_data+f_ptr->offset,data_size); + + szs_file_t sub; +// [[fname+]] + InitializeSubSZS( &sub,szs, data_ptr - szs->data, data_size, + FF_UNKNOWN, f_ptr->path, false ); + AdjustStringsBRSUB(&sub,data_ptr,data_size); + ResetSZS(&sub); + + brsub_list_t * list; + for ( list = szs->brsub_list; list; list = list->next ) + if ( list->data == file_data + f_ptr->offset ) + { + noPRINT("ADJUST\n"); + DASSERT( list->size <= f_ptr->size ); + data_size = list->size; + memset( data_ptr + data_size, 0, f_ptr->size - data_size ); + break; + } + + data_ptr += ALIGN32(data_size,opt_align_brres); + } + + entry++; + } + + //--- transfer data + + info_ptr = info_list; + brres_entry_t * entry_ptr = grp->entry; + for ( ; entry_ptr < entry; entry_ptr++, info_ptr++ ) + { + endian->wr16( &entry_ptr->id, info_ptr->id ); + endian->wr16( &entry_ptr->left_idx, info_ptr->left_idx ); + endian->wr16( &entry_ptr->right_idx, info_ptr->right_idx ); + } + + grp = (brres_group_t*)entry; + } + + + //--- copy new string pool + + memcpy( szs->data + str_offset, szs->string_pool.data, szs->string_pool.size ); + + + //--- clean + + if (logging) + dump_brres_group(broot,endian); + + FREE(ep_list); + FREE(info_list); + FREE(dir_order); + FREE(file_data); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// brres file iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int iterate_brres_group +( + szs_iterator_t * it, // valid iterator + const brres_group_t * grp, // valid grp + uint path_len // current path length +) +{ + DASSERT(it); + DASSERT(it->szs); + DASSERT(it->func_it); + DASSERT(it->endian); + DASSERT(it->root_end); + DASSERT(grp); + + szs_file_t * szs = it->szs; + + const uint base_dir_index = it->index; + const uint n_entries = it->endian->rd32(&grp->n_entries); + const brres_entry_t * entry = grp->entry + 1; + const brres_entry_t * entry_end = entry + n_entries; + int stat = 0; + + if (it->cut_files) + { + it->index = 0; + it->fst_item = 0; + it->is_dir = 0; + + it->off = (u8*)grp - szs->data; + it->size = (u8*)entry_end - (u8*)grp; + StringCopyE(it->path+path_len,it->path+sizeof(it->path),".BRRES.group"); + it->func_it(it,false); + } + + for ( ; entry < entry_end && !stat; entry++ ) + { + it->name = 0; + ccp name = ""; + uint name_len = 0; + uint new_path_len = path_len; + const uint name_off = it->endian->rd32(&entry->name_off); + if (name_off) + { + it->name = name = (ccp)grp + name_off; + name_len = it->endian->rd32(name-4); + if ( path_len + name_len < sizeof(it->path)-2 ) + { + it->trail_path = it->path+path_len; + memcpy(it->trail_path,name,name_len); + new_path_len += name_len; + it->path[new_path_len] = 0; + } + } + + const u8 * data = (u8*)grp + it->endian->rd32(&entry->data_off); + it->index++; + if ( data < it->root_end ) + { + it->is_dir = true; + it->off = base_dir_index; + it->size = 0; + it->path[new_path_len++] = '/'; + it->path[new_path_len] = 0; + stat = it->func_it(it,false); + if (!stat) + stat = iterate_brres_group(it,(brres_group_t*)data,new_path_len); + } + else + { + it->is_dir = false; + it->off = (u8*)grp - szs->data + it->endian->rd32(&entry->data_off); + it->size = it->endian->rd32(szs->data+it->off+4); + + if ( szs->min_data_off > it->off ) + szs->min_data_off = it->off; + + const u32 end_off = it->off + it->size; + if ( szs->max_data_off < end_off ) + szs->max_data_off = end_off; + + if ( opt_ext && it->szs->fform_arch == FF_BRRES ) + { +// [[analyse-magic]] + it->fform = GetByMagicFF(szs->data+it->off,it->size,it->size); + ccp ext = opt_ext > 1 + ? GetMagicExtFF(0,it->fform) + : GetExtFF(0,it->fform); + StringCopyE( it->path+new_path_len, it->path+sizeof(it->path), ext ); + } + + noPRINT("DATA-OFF: %x .. %x\n",szs->min_data_off,szs->max_data_off); + stat = it->func_it(it,false); + it->fform = 0; + } + } + it->name = 0; + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesBRRES +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + //--- brres header + + if ( !szs->data || szs->size < sizeof(brres_header_t) ) + return -1; + + const brres_header_t * bh = (brres_header_t*)szs->data; + if ( !IsRepairMagic() && memcmp(bh->magic,BRRES_MAGIC,sizeof(bh->magic)) ) + return -1; + + const endian_func_t * endian = GetEndianFunc(&bh->bom); + if (!endian) + return -1; + szs->endian = endian; + + const uint data_size = endian->rd32(&bh->size); + const uint root_off = endian->rd16(&bh->root_off); + PRINT("data_size=%u, root_off=%u, n_sections=%u\n", + data_size,root_off, endian->rd16(&bh->n_sections)); + if ( data_size > szs->size || root_off >= szs->size ) + return -1; + + + //--- root + + const brres_root_t * root = (brres_root_t*)(szs->data+root_off); + if ( !IsRepairMagic() && memcmp(root->magic,BRRES_ROOT_MAGIC,sizeof(root->magic)) ) + return -1; + + const uint root_size = endian->rd32(&root->size); + const u8 * root_end = (u8*)root + root_size; + + + //----- cut files? + + if (it->cut_files) + { + it->index = 0; + it->fst_item = 0; + it->is_dir = 0; + + it->off = 0; + it->size = sizeof(brres_header_t); + StringCopyS(it->path,sizeof(it->path),".BRRES.header"); + it->func_it(it,false); + + it->off = (u8*)root - szs->data; + it->size = sizeof(brres_root_t); + StringCopyS(it->path,sizeof(it->path),".BRRES.root"); + it->func_it(it,false); + } + + + //--- iterate groups + + #if defined(TEST) || 1 + if (logging) + dump_brres_group(root,endian); + #endif + + szs->max_data_off = 0; + szs->min_data_off = ~(u32)0; + it->endian = endian; + it->root_end = root_end; + int stat = iterate_brres_group(it,BRRES_ROOT_GROUP(root),0); + + if ( !stat && szs->max_data_off ) + { + it->index++; + it->is_dir = false; + it->off = szs->max_data_off; + it->size = data_size - szs->max_data_off; + StringCopyS(it->path,sizeof(it->path),BRRES_STRING_POOL_FNAME); + stat = it->func_it(it,false); + } + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// brres sub file /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStringBRSUB +( + const void * data, // relevant data + const void * end_of_src, // end of 'data', 0 if unknown + u32 min_sp_off, // minimum string pool offset + // maybe end of sub file, 0 if unknown + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid, // result if invalid + const endian_func_t * endian // NULL (=use be) or endian functions +) +{ + DASSERT(data); + DASSERT(base); + + u32 data_size = end_of_src ? end_of_src - data : ~(u32)0; + if ( offset && !(offset&3) && offset < data_size && data ) + { + offset += (u8*)base - (u8*)data; + if ( offset >= min_sp_off && offset < data_size ) + { + ccp ptr = (ccp)data + offset; + if (!endian) + endian = &be_func; + if ( endian->rd32(ptr-4) == strlen(ptr) ) + return ptr; + } + } + return if_invalid; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStringIteratorBRSUB +( + struct + brsub_iterator_t * it, // iterator struct with all infos + const void * base, // pointer to base for calculation + const void * ptr, // pointer to u32 string offset + ccp if_null, // result if offset == NULL + ccp if_invalid // result if invalid +) +{ + u32 offset = it->endian->rd32(ptr); + return !offset + ? if_null + : GetStringBRSUB( + it->brsub, + (u8*)it->brsub + it->file_size, + it->static_strings ? 0 : it->data_size, + base, + offset, + if_invalid, + it->endian ); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStringBRRES +( + const szs_file_t * szs, // base BRRES file + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid // result if invalid +) +{ + // this functions find all strings, not only relocation candidates + + return szs + ? GetStringBRSUB( + szs->data, + szs->data + szs->size, + 0, //szs->max_data_off, + base, + offset, + if_invalid, + szs->endian ) + : if_invalid; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRSUB iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int brsub_string_func +( + brsub_cut_t *bcut, // pointer to data structure + int grp, // index of group, <0: grp_entry_t + int entry // index of entry, -1: no entry +) +{ + noPRINT("##A### XXX( %2d, %2d, %s )\n",grp,entry); + + DASSERT(bcut); + szs_iterator_t *it = bcut->it; + DASSERT(it); + brsub_iterator_t *brit = bcut->param; + DASSERT(brit); + const endian_func_t * endian = it->endian; + DASSERT(endian); + + if ( entry == ENTRY_IDX_GROUP ) + { + PRINT_IF( bcut->fform == FF_PAT, + "#PAT# g+e=%d,%d | %s #%u\n",grp,entry,__FUNCTION__,__LINE__); + if (brit->grp_func) + it->index += brit->grp_func(brit,bcut->gptr,grp); + } + else if ( grp >= 0 && entry == ENTRY_IDX_DATA && !brit->raw_func ) + { + PRINT_IF( bcut->fform == FF_PAT, + "#PAT# g+e=%d,%d v=%d | %s #%u\n", + grp, entry, bcut->version, __FUNCTION__, __LINE__ ); + + u8 *data = (u8*)bcut->data + it->off; + switch(BRSUB_MODE3(bcut->fform,bcut->version,grp)) + { + case BRSUB_MODE3(FF_PAT,4,0): + { + pat_analyse_t ana; + IsValidPAT(bcut->data,bcut->size,bcut->size,0,0,0,&ana); + PRINT("\e[32m#PAT# grp=%d val=%d,%d | %s #%u\e[0m\n", + grp, ana.valid, ana.data_complete, + __FUNCTION__, __LINE__ ); + if (!ana.data_complete) + break; + + HEXDUMP16(10,0,data,it->size); + uint i; + for ( i = 0; i < ana.n_sect0; i++ ) + { + pat_s0_belem_t *elem = ana.s0_base->elem + i; + it->index += brit->string_func(brit,data,&elem->offset_name); + } + + for ( i = 0; i < PAT_MAX_ELEM; i++ ) + { + pat_s0_sref_t *sref = ana.s0_sref[i]; + if (sref) + it->index += brit->string_func(brit,(u8*)sref,&sref->offset_name); + } + HEXDUMP16(10,0,data,it->size); + } + break; + + case BRSUB_MODE3(FF_PAT,4,1): + { + pat_analyse_t ana; + IsValidPAT(bcut->data,bcut->size,bcut->size,0,0,0,&ana); + PRINT("\e[32m#PAT# grp=%d val=%d,%d | %s #%u\e[0m\n", + grp, ana.valid, ana.data_complete, + __FUNCTION__, __LINE__ ); + if (!ana.data_complete) + break; + + HEXDUMP16(10,0,data,it->size); + uint i; + for ( i = 0; i < ana.n_sect1; i++ ) + it->index += brit->string_func(brit,data,(u32*)data+i); + HEXDUMP16(10,0,data,it->size); + } + break; + } + } + else if ( grp < 0 || entry < 0 ) + { + PRINT_IF( bcut->fform == FF_PAT, + "#PAT# g+e=%d,%d raw=%p | %s #%u\n", + grp,entry, brit->raw_func, __FUNCTION__, __LINE__ ); + if ( entry == ENTRY_IDX_DATA && brit->raw_func ) + it->index += brit->raw_func( brit, (u8*)bcut->data + it->off, it->size ); + } + else + { + PRINT_IF( bcut->fform == FF_PAT, + "#PAT# g+e=%d,%d | %s #%u\n",grp,entry,__FUNCTION__,__LINE__); + entry++; + u8 *edata = (u8*)bcut->gptr + endian->rd32(&bcut->eptr->data_off); + if (brit->entry_func) + it->index += brit->entry_func(brit,bcut->gptr,grp,bcut->eptr,edata); + + if (brit->string_func) + { + it->index += brit->string_func(brit,(u8*)bcut->gptr,&bcut->eptr->name_off); + + u32 data_off = endian->rd32(&bcut->eptr->data_off); + u8 *data = (u8*)bcut->gptr + data_off; + const u8 *data_end = bcut->data + bcut->file_size; + + if ( data_off && data < data_end ) + { + if ( bcut->fform == FF_MDL && bcut->version == 11 ) + { + switch(grp) + { + case 1: + it->index += brit->string_func(brit,data,(u32*)(data+0x08)); + break; + + case 2: + case 3: + case 4: + case 5: + it->index += brit->string_func(brit,data,(u32*)(data+0x0c)); + break; + + case 8: + { + it->index += brit->string_func(brit,data,(u32*)(data+0x08)); + + const uint n_layer = endian->rd32(data+0x2c); + u32 layer_off = endian->rd32(data+0x30); + uint i; + for ( i = 0; i < n_layer; i++, layer_off += 0x34 ) + { + u8 * d = data + layer_off; + it->index += brit->string_func(brit,d,(u32*)d); + } + } + break; + + case 10: + it->index += brit->string_func(brit,data,(u32*)(data+0x38)); + break; + } + } + else if ( grp == 0 ) + { + switch(BRSUB_MODE2(bcut->fform,bcut->version)) + { + case BRSUB_MODE2(FF_CHR,3): + case BRSUB_MODE2(FF_CHR,5): + case BRSUB_MODE2(FF_CLR,4): + case BRSUB_MODE2(FF_SRT,4): + case BRSUB_MODE2(FF_SRT,5): + it->index += brit->string_func(brit,data,(u32*)(data+0x00)); + break; + + case BRSUB_MODE2(FF_SHP,4): + it->index += brit->string_func(brit,data,(u32*)(data+0x04)); + break; + + case BRSUB_MODE2(FF_SCN,4): + case BRSUB_MODE2(FF_SCN,5): + it->index += brit->string_func(brit,data,(u32*)(data+0x20)); + break; + + case BRSUB_MODE2(FF_PAT,4): + DASSERT(0); + break; + + default: + break; + } // switch + } // else + } // if + } // if string_func + } // else entry >= 0 + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[IterateStringsBRSUB]] + +int IterateStringsBRSUB +( + brsub_iterator_t * it // valid iterator data +) +{ + DASSERT(it); + DASSERT(it->szs); + DASSERT( it->szs->file_size > 0 ); + DASSERT( it->szs->file_size < 0x1000000 ); + DASSERT( it->file_size > 0 ); + DASSERT( it->file_size < 0x1000000 ); + + const endian_func_t * endian = it->endian; + if (!endian) + it->endian = endian = it->szs && it->szs->endian ? it->szs->endian : &be_func; + + file_format_t fform; + if (it->force_fform) + fform = it->force_fform; + else + { +// [[analyse-magic]] + fform = GetByMagicFF(it->brsub,it->file_size,it->file_size); + if ( IsValidBRSUB( it->brsub, it->file_size, it->file_size, + it->adjust ? 0 : it->szs, + fform, false, 0, endian) >= VALID_ERROR ) + { + PRINT_IF(it->adjust,"INVALID, adjust=%d\n",it->adjust); + return -1; + } + } + + brsub_header_t *bh = it->brsub; + u8 * brsub_data = (u8*)bh; + it->data_size = endian->rd32(&bh->size); + const int n_grp = GetSectionNumBRSUB(brsub_data,it->data_size,endian); + u32 name_off = 0x10 + n_grp * 4; + uint count = 0; + + PRINT_IF( fform == FF_PAT, "#PAT# %s @ %s #%u | n_grp=%d\n", + __FUNCTION__,__FILE__,__LINE__,n_grp); + + + //--- static data + + it->fform = fform; + + if (it->offset_func) + count += it->offset_func(it,brsub_data,&bh->brres_offset); + + if ( it->string_func && n_grp >= 0 ) + count += it->string_func(it,brsub_data,(u32*)(brsub_data+name_off)); + + it->fform = FF_UNKNOWN; + + szs_iterator_t it2; + memset(&it2,0,sizeof(it2)); + it2.szs = it->szs; + it2.size = it->file_size; + it2.endian = it->endian; + it2.cut_files = true; + CutFilesBRSUB( &it2, brsub_string_func, it, 0, 0 ); + return count + it2.index; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRSUB iterator callback functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int clear_brsub_offset +( + struct brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +) +{ + DASSERT(it); + DASSERT(it->endian); + it->endian->wr32(ptr,0); + return 0; +} + +//----------------------------------------------------------------------------- + +static int adjust_brsub_offset +( + struct brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 offset +) +{ + DASSERT(it); + DASSERT(it->endian); + DASSERT(it->szs); + DASSERT(it->szs->parent); + DASSERT(it->szs->parent->data); + + noPRINT("##A## ADJUST OFFSET: %x -> %x\n", + it->endian->rd32(ptr), (u32)(it->szs->parent->data - base) ); + it->endian->wr32( ptr, it->szs->parent->data - base ); + return 0; +} + +//----------------------------------------------------------------------------- + +static int insert_brsub_string +( + struct brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +) +{ + DASSERT(it); + DASSERT(it->endian); + DASSERT(it->param); + + ccp string = GetStringIteratorBRSUB(it,base,ptr,0,0); + if (!string) + return 0; + PRINT_IF( it->fform == FF_PAT, "#PAT# INSERT %s @%s\n",string,__FUNCTION__); + PRINT_IF( it->fform == FF_MDL, "#MDL# INSERT %s @%s\n",string,__FUNCTION__); + + InsertStringPool(it->param,string,false,0); + return 1; +} + +//----------------------------------------------------------------------------- + +static int adjust_brsub_string_szs +( + struct brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +) +{ + noPRINT("##A## size=%x,%x, base=%zx, ptr=%zx\n", + it->data_size, it->file_size, + base - (u8*)it->brsub, (u8*)ptr - (u8*)it->brsub ); + + DASSERT(it); + DASSERT(it->endian); + DASSERT(it->szs); + DASSERT(it->szs->parent); + DASSERT(it->szs->parent->data); + DASSERT(it->param); + + ccp string = GetStringIteratorBRSUB(it,base,ptr,0,0); + if (!string) + return 0; + + const u32 off = FindStringPool(it->param,string,0) - (base - it->szs->parent->data); + noPRINT("##A## ADJUST STRING: %x -> %x : %s\n", it->endian->rd32(ptr), off, string ); + PRINT_IF( it->fform == FF_PAT, + "#PAT# ADJUST %x -> %x : %s @%s\n", + it->endian->rd32(ptr), off, string, __FUNCTION__ ); + it->endian->wr32(ptr,off); + return 1; +} + +//----------------------------------------------------------------------------- + +static int adjust_brsub_string +( + struct brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +) +{ + DASSERT(it); + DASSERT(it->endian); + DASSERT(it->szs); + DASSERT(it->szs->data); + DASSERT(it->param); + + ccp string = GetStringIteratorBRSUB(it,base,ptr,0,0); + if (!string) + return 0; + + const u32 off = FindStringPool(it->param,string,0) - (base - (u8*)it->brsub); + noPRINT("ADJUST STRING: %x -> %x : %s\n", it->endian->rd32(ptr), off, string ); + it->endian->wr32(ptr,off); + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRSUB iterator interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int CollectStringsBRSUB +( + string_pool_t * sp, // string pool + bool init_sp, // true: initialize 'sp' + szs_file_t * szs, // szs of data + bool adjust // true: create string pool and + // adjust offsets and strings +) +{ + DASSERT(sp); + DASSERT(szs); + + if (init_sp) + InitializeStringPool(sp); + + brsub_iterator_t it; + memset(&it,0,sizeof(it)); + it.szs = szs; + it.brsub = (void*)szs->data; + it.file_size = szs->file_size; + it.string_func = insert_brsub_string; + it.offset_func = adjust ? clear_brsub_offset : 0; + it.param = sp; + it.endian = szs->endian; + + const int stat = IterateStringsBRSUB(&it); + + if ( adjust && stat > 0 ) + { + CalcStringPool( sp, it.data_size, szs->endian ); + it.adjust = true; + it.string_func = adjust_brsub_string; + it.offset_func = 0; + IterateStringsBRSUB(&it); + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int AdjustStringsBRSUB +( + szs_file_t * szs, // base BRRES file + void * brsub_data, // brres sub data + size_t brsub_size // size of 'brsub_data' including string pool +) +{ + DASSERT(szs); + DASSERT(szs->parent); + DASSERT(brsub_data); + + brsub_iterator_t it; + memset(&it,0,sizeof(it)); + it.szs = szs; + it.brsub = brsub_data; + it.file_size = brsub_size; + it.string_func = adjust_brsub_string_szs; + it.offset_func = adjust_brsub_offset; + it.param = &szs->parent->string_pool; + it.adjust = true; + it.endian = szs->endian; + + return IterateStringsBRSUB(&it); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRSUB Dump Structure /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int dump_brsub_raw +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * data, // data pointer + u32 data_size // size of 'data' +) +{ + DASSERT(it); + FILE * f = it->param; + DASSERT(f); + + u32 data_offset = data - (u8*)it->brsub; + fprintf(f," - RAW DATA %x .. %x, size %x\n", + data_offset, data_offset + data_size, data_size ); + return 0; +} + +//----------------------------------------------------------------------------- + +static int dump_brsub_grp +( + struct + brsub_iterator_t * it, // iterator struct with all infos + brres_group_t * grp, // pointer to group + uint grp_index // index of group +) +{ + DASSERT(it); + FILE * f = it->param; + DASSERT(f); + + fprintf(f," - GRP #%-2u %4x\n", grp_index, (int)( (u8*)grp - (u8*)it->brsub )); + return 0; +} + +//----------------------------------------------------------------------------- + +static int dump_brsub_entry +( + struct + brsub_iterator_t * it, // iterator struct with all infos + brres_group_t * grp, // pointer to group + uint grp_index, // index of group + brres_entry_t * entry, // pointer to entry + u8 * data // pointer to entry related data +) +{ + DASSERT(it); + FILE * f = it->param; + DASSERT(f); + + ccp string = GetStringIteratorBRSUB(it,grp,&entry->name_off, + "", "**invalid**"); + + fprintf(f," - ENTRY #%-2u %4x [+%3x] : %8x %s-> %s\n", + (int)( entry - grp->entry ), + (int)( (u8*)entry - (u8*)it->brsub ), + (int)( (u8*)entry - (u8*)grp ), + it->endian->rd32(&entry->name_off), + string <= (ccp)it->brsub + it->data_size ? "[intern] " : "", + string ); + return 0; +} + +//----------------------------------------------------------------------------- + +static int dump_brsub_string +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +) +{ + DASSERT(it); + FILE * f = it->param; + DASSERT(f); + + ccp string = GetStringIteratorBRSUB(it,base,ptr,"","**invalid**"); + + fprintf(f," > STRING %5x [+%3x] : %8x %s-> %s\n", + (int)( (u8*)ptr - (u8*)it->brsub ), + (int)( (u8*)ptr - base ), + it->endian->rd32(ptr), + string <= (ccp)it->brsub + it->data_size ? "[intern] " : "", + string ); + return 0; +} + +//----------------------------------------------------------------------------- + +static int dump_brsub_offset +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +) +{ + DASSERT(it); + FILE * f = it->param; + DASSERT(f); + + fprintf(f," > OFFSET %5x [+%3x] : %8x\n", + (int)( (u8*)ptr - (u8*)it->brsub ), + (int)( (u8*)ptr - base ), + it->endian->rd32(ptr) ); + return 0; +} + +//----------------------------------------------------------------------------- + +int DumpStructureBRSUB +( + FILE * f, // output file + szs_file_t * base_szs, // valid base SZS + uint off, // offset of selected data within 'base' + uint size, // not NULL: size of selected data + file_format_t fform // not FF_UNKNOWN: use this type +) +{ + DASSERT(f); + DASSERT(base_szs); + PRINT("DumpStructureBRSUB(off=%x,size=%x,ff=%d)\n",off,size,fform); + + szs_file_t szs; +// [[fname-]] + InitializeSubSZS(&szs,base_szs,off,size,fform,0,false); + + brsub_iterator_t it; + memset(&it,0,sizeof(it)); + it.szs = &szs; + it.brsub = (void*)szs.data; + it.file_size = szs.file_size; + it.raw_func = dump_brsub_raw; + it.grp_func = dump_brsub_grp; + it.entry_func = dump_brsub_entry; + it.string_func = dump_brsub_string; + it.offset_func = dump_brsub_offset; + it.param = f; + it.static_strings = true; + + return IterateStringsBRSUB(&it); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int dump_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if ( term || it->is_dir ) + return 0; + + szs_file_t * szs = it->szs; + DASSERT(szs); + + u8 * data = szs->data+it->off; +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,it->size,it->size); + if (IsBRSUB(fform)) + fprintf(it->param,"\n * Sub file: %s.%u:%s\n", + GetNameFF(fform,fform), + it->endian->rd32(data+8), + it->path ); + else + fprintf(it->param,"\n * Data: %s\n",it->path); + + return DumpStructureBRSUB(it->param,szs,it->off,it->size,fform); +} + +/////////////////////////////////////////////////////////////////////////////// + +int DumpStructureBRRES +( + FILE * f, // output file + szs_file_t * szs // BRRES file +) +{ + DASSERT(f); + DASSERT(szs); + + if ( szs->fform_arch != FF_BRRES ) + return -1; + + return IterateFilesSZS(szs,dump_func,f,false,0,-1,SORT_NONE); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// NAME-REF support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeNameRef ( name_ref_t *nr ) +{ + DASSERT(nr); + memset(nr,0,sizeof(*nr)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetNameRef ( name_ref_t *nr ) +{ + DASSERT(nr); + + if (nr->vlist) + { + name_ref_var_t *vptr, *vend = nr->vlist + nr->vused; + for ( vptr = nr->vlist; vptr < vend; vptr++ ) + { + FreeString(vptr->var_name); + + if (vptr->elist) + { + name_ref_elem_t *eptr, *eend = vptr->elist + vptr->eused; + for ( eptr = vptr->elist; eptr < eend; eptr++ ) + FreeString(eptr->info); + + FREE(vptr->elist); + vptr->elist = 0; + } + vptr->eused = vptr->esize = 0; + + } + FREE(nr->vlist); + nr->vlist = 0; + } + nr->vused = nr->vsize = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void add_ref_string +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr, // pointer to u32 string offset + + ccp format, // format string for info + ... // arguments for 'format' +) +__attribute__ ((__format__(__printf__,4,5))); + +//----------------------------------------------------------------------------- + +static void add_ref_string +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr, // pointer to u32 string offset + + ccp format, // format string for info + ... // arguments for 'format' +) +{ + ccp info = 0; + if (format) + { + char buf[200]; + va_list arg; + va_start(arg,format); + int len = vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + if ( len > 0 ) + info = MEMDUP(buf,len); + } + + name_ref_t *nr = it->param; + DASSERT(nr); + + ccp var_name = GetStringIteratorBRSUB(it,base,ptr,0,0); + const u32 var_off = be32(ptr) + base - nr->data; + const u32 file_off = (u8*)ptr - nr->data; + const u32 base_off = (u8*)ptr - it->szs->data; + + PRINT0(">>> %06x %05x %06x %s [%s]\n", + base_off, file_off, var_off, var_name, info ); + + + //--- insert into vlist + + uint vi; + name_ref_var_t *vptr = nr->vlist; + for ( vi = 0; vi < nr->vused; vi++, vptr++ ) + if ( var_off <= vptr->var_off ) + break; + + if ( vi == nr->vused || var_off != vptr->var_off ) + { + // insert element + if ( nr->vused == nr->vsize ) + { + nr->vsize += 100; + nr->vlist = REALLOC( nr->vlist, nr->vsize * sizeof(*nr->vlist) ); + vptr = nr->vlist + vi; + } + + if ( vi < nr->vused ) + memmove(vptr+1,vptr,(nr->vused-vi)*sizeof(*vptr)); + + memset(vptr,0,sizeof(*vptr)); + vptr->var_name = var_name ? STRDUP(var_name) : EmptyString; + vptr->var_off = var_off; + vptr->min_file_off = file_off; + nr->vused++; + } + else if ( file_off < vptr->min_file_off ) + vptr->min_file_off = file_off; + + + //--- insert into elist + + uint ei; + name_ref_elem_t *eptr = vptr->elist; + for ( ei = 0; ei < vptr->eused; ei++, eptr++ ) + if ( file_off < eptr->file_off ) + break; + + if ( vptr->eused == vptr->esize ) + { + vptr->esize += 20; + vptr->elist = REALLOC( vptr->elist, vptr->esize * sizeof(*vptr->elist) ); + eptr = vptr->elist + ei; + } + + if ( ei < vptr->eused ) + memmove(eptr+1,eptr,(vptr->eused-ei)*sizeof(*eptr)); + + memset(eptr,0,sizeof(*eptr)); + eptr->file_off = file_off; + eptr->base_off = base_off; + eptr->info = info; + vptr->eused++; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int insert_name_ref +( + struct brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +) +{ + DASSERT(it); + DASSERT(it->szs); + name_ref_t *nr = it->param; + DASSERT(nr); + + if ( nr->brief > 2 ) + add_ref_string(it,base,ptr,"%s",nr->file_info); + else + add_ref_string(it,base,ptr,"%s" "offset %x", + nr->file_info, (uint)( (u8*)ptr - it->szs->data )); + return 1; +} + +//----------------------------------------------------------------------------- + +static void iterate_nr_brsub +( + name_ref_t *nr, // valid struct + szs_file_t *szs, // valid struct + cvp data, // pointer to data + uint data_size, // size of data + file_format_t fform // FF_UNKNOWN or file format of 'data' +) +{ + DASSERT(nr); + DASSERT(szs); + DASSERT(data||!data_size); + + if ( !data || !data_size ) + return; + + if ( fform == FF_UNKNOWN ) + fform = AnalyseMagicByOpt(0,data,data_size,data_size,FF_UNKNOWN,0); + + if (IsBRSUB(fform)) + { + if ( nr->brief > 2 ) + snprintf(nr->file_info,sizeof(nr->file_info),"%s", + GetNameFF(0,fform) ); + else + snprintf(nr->file_info,sizeof(nr->file_info),"%s @%x, ", + GetNameFF(0,fform), (uint)( (u8*)data - szs->data )); + + szs_file_t sub; + InitializeSubSZS( &sub, szs, (u8*)data - szs->data, 0, fform, 0, false ); + + brsub_iterator_t it; + memset(&it,0,sizeof(it)); + it.szs = ⊂ + it.brsub = (void*)data; + it.file_size = szs->data + szs->size - (u8*)data; + it.fform = fform; + it.param = nr; + it.endian = szs->endian; + it.string_func = insert_name_ref; + + IterateStringsBRSUB(&it); + ResetSZS(&sub); + *nr->file_info = 0; + } +} + +//----------------------------------------------------------------------------- + +static int iterate_nr_brres +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if (term) + return 0; + + const u8 * data = it->szs->data+it->off; + iterate_nr_brsub(it->param,it->szs,data,it->size,FF_UNKNOWN); + return 0; +} + +//----------------------------------------------------------------------------- + +enumError CreateNameRef + ( name_ref_t *nr, bool init_nr, szs_file_t *szs, int brief ) +{ + DASSERT(nr); + DASSERT(szs); + + if (init_nr) + InitializeNameRef(nr); + else + ResetNameRef(nr); + + nr->data = szs->data; + nr->data_size = szs->file_size; + nr->fform = szs->fform_arch; + nr->fname = STRDUP(szs->fname); + nr->brief = brief; + + if ( szs->fform_arch == FF_BRRES ) + IterateFilesSZS( szs, iterate_nr_brres,nr, true,0,-1, SORT_NONE ); + else + iterate_nr_brsub(nr,szs,szs->data,szs->file_size,FF_UNKNOWN); +#if 0 + { + brsub_iterator_t it; + memset(&it,0,sizeof(it)); + it.szs = szs; + it.brsub = (void*)szs->data; + it.file_size = szs->file_size; + it.string_func = insert_name_ref; + it.param = nr; + it.endian = szs->endian; + + IterateStringsBRSUB(&it); + } +#endif + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int sort_nr_by_name ( const void *va, const void *vb ) +{ + name_ref_var_t **a = (name_ref_var_t**)va; + name_ref_var_t **b = (name_ref_var_t**)vb; + return strcasecmp( (*a)->var_name, (*b)->var_name ); +} + +//----------------------------------------------------------------------------- + +int sort_nr_by_brres ( const void *va, const void *vb ) +{ + name_ref_var_t **a = (name_ref_var_t**)va; + name_ref_var_t **b = (name_ref_var_t**)vb; + return strcmp( (*a)->var_name, (*b)->var_name ); +} + +//----------------------------------------------------------------------------- + +int sort_nr_by_offset ( const void *va, const void *vb ) +{ + name_ref_var_t **a = (name_ref_var_t**)va; + name_ref_var_t **b = (name_ref_var_t**)vb; + return (int)(*a)->min_file_off - (int)(*b)->min_file_off; +} + +//----------------------------------------------------------------------------- + +static void list_var ( FILE *f, int indent, name_ref_var_t *vptr, int brief ) +{ + DASSERT(f); + DASSERT(vptr); + + if ( brief > 0 ) + fprintf(f,"\n%*s»%s«\n",indent,"", + vptr->var_name ? vptr->var_name : "" ); + else + fprintf(f,"\n%*s%6x »%s«\n",indent,"", + vptr->var_off, vptr->var_name ? vptr->var_name : "" ); + + name_ref_elem_t *eptr, *eend = vptr->elist + vptr->eused; + if ( brief > 1 ) + for ( eptr = vptr->elist; eptr < eend; eptr++ ) + fprintf(f,"%*s %s\n",indent,"", + eptr->info ? eptr->info : "" ); + else + for ( eptr = vptr->elist; eptr < eend; eptr++ ) + fprintf(f,"%*s %6x %s\n",indent,"", + eptr->file_off, eptr->info ? eptr->info : "" ); +} + +//----------------------------------------------------------------------------- + +void ListNameRef ( FILE *f, int indent, name_ref_t *nr, SortMode_t sort ) +{ + qsort_func sort_func; + ccp sort_info; + switch (sort) + { + case SORT_INAME: + sort_func = sort_nr_by_name; + sort_info = "sorted by name"; + break; + + case SORT_BRRES: + sort_func = sort_nr_by_brres; + sort_info = "sorted like BRRES"; + break; + + case SORT_OFFSET: + sort_func = sort_nr_by_offset; + sort_info = "sorted by offset"; + break; + + default: + sort_func = 0; + sort_info = "unsorted"; + break; + } + + indent = NormalizeIndent(indent); + printf("%*s" "Name reference, %s, file %s:%s\n", + indent,"", sort_info, GetNameFF(nr->fform,0), nr->fname ); + indent += 3; + + if ( !sort_func || nr->vused < 2 ) + { + //-- unsorted + + name_ref_var_t *vptr, *vend = nr->vlist + nr->vused; + for ( vptr = nr->vlist; vptr < vend; vptr++ ) + list_var(f,indent,vptr,nr->brief); + } + else + { + //--- sorted + + name_ref_var_t **ref_list = MALLOC(nr->vused*sizeof(*ref_list)); + DASSERT(ref_list); + uint i; + for ( i = 0; i < nr->vused; i++ ) + ref_list[i] = nr->vlist+i; + + qsort( ref_list, nr->vused, sizeof(*ref_list), sort_func ); + + for ( i = 0; i < nr->vused; i++ ) + list_var(f,indent,ref_list[i],nr->brief); + + FREE(ref_list); + } + + fputc('\n',f); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-brres.h b/project/src/lib-brres.h new file mode 100644 index 0000000..1e03edf --- /dev/null +++ b/project/src/lib-brres.h @@ -0,0 +1,505 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// all about SZS, YAZ0 and U8 files: +// http://wiki.tockdom.de/index.php?title=SZS +// http://wiki.tockdom.de/index.php?title=YAZ0 +// http://wiki.tockdom.de/index.php?title=U8 + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_BRRES_H +#define SZS_LIB_BRRES_H 1 + +#include "lib-szs.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRRES support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateBRRES +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 total_size // total file size +); + +//----------------------------------------------------------------------------- + +int IterateFilesBRRES +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +//----------------------------------------------------------------------------- + +int IterateFilesBRSUB +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +//----------------------------------------------------------------------------- + +int IterateFilesMDL +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +//----------------------------------------------------------------------------- + +int IterateFilesPAT +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +//----------------------------------------------------------------------------- + +int IterateFilesFuncBRSUB +( + brsub_cut_t *bcut, // pointer to data structure + int grp, // index of group, <0: grp_entry_t + int entry // index of entry, -1: no entry +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[brres_info_t]] + +typedef struct brres_info_t +{ + u16 id; // id + u16 left_idx; // left index + u16 right_idx; // right index + u32 name_off; // name offset + u32 data_off; // data offset + ccp name; // pointer to name + uint nlen; // length of name + +} brres_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +void CalcEntryBRRES +( + brres_info_t * info, + uint entry_idx +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRRES subfiles /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define MDL_MAGIC "MDL0" +#define TEX_MAGIC "TEX0" +#define SRT_MAGIC "SRT0" +#define CHR_MAGIC "CHR0" +#define PAT_MAGIC "PAT0" +#define CLR_MAGIC "CLR0" +#define SHP_MAGIC "SHP0" +#define SCN_MAGIC "SCN0" + +#define MDL_MAGIC_NUM 0x4d444c30 +#define TEX_MAGIC_NUM 0x54455830 +#define SRT_MAGIC_NUM 0x53525430 +#define CHR_MAGIC_NUM 0x43485230 +#define PAT_MAGIC_NUM 0x50415430 +#define CLR_MAGIC_NUM 0x434c5230 +#define SHP_MAGIC_NUM 0x53485030 +#define SCN_MAGIC_NUM 0x53434e30 + +#define MDL_TEXT_MAGIC "#MDL" +#define MDL_TEXT_MAGIC_NUM 0x234d444c + +#define PAT_TEXT_MAGIC "#PAT" +#define PAT_TEXT_MAGIC_NUM 0x23504154 + +//----------------------------------------------------------------------------- +// [[brsub_list_t]] + +typedef struct brsub_list_t +{ + u8 * data; // valid pointer to data + size_t size; // size of 'data' + struct brsub_list_t * next; // NULL or pointer to next element + +} brsub_list_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStringBRSUB +( + const void * data, // relevant data + const void * end_of_src, // end of 'data' + u32 min_sp_off, // minimum string pool offset + // maybe end of sub file, 0 if unknown + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid, // result if invalid + const endian_func_t * endian // NULL (=use be) or endian functions +); + +struct brsub_iterator_t; +ccp GetStringIteratorBRSUB +( + struct + brsub_iterator_t * it, // iterator struct with all infos + const void * base, // pointer to base for calculation + const void * ptr, // pointer to u32 string offset + ccp if_null, // result if offset == NULL + ccp if_invalid // result if invalid +); + +ccp GetStringBRRES +( + const szs_file_t * szs, // base BRRES file + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid // result if invalid +); + +/////////////////////////////////////////////////////////////////////////////// +// [[brsub_warn_t]] + +typedef enum brsub_warn_t +{ + BIMD_OK, // original record + BIMD_HINT, // hint about wrong version -> will work + BIMD_FAIL, // wrong n_sect, but will work (garbage display) + BIMD_FATAL, // wrong n_sect, will freeze +} +__attribute__ ((packed)) brsub_warn_t; + +//----------------------------------------------------------------------------- + +// [[brsub_info_t]] // BRSUB info + +typedef struct brsub_info_t +{ + file_format_t fform1; // main file format of this record + file_format_t fform2; // alternativ file format of this record + + u8 version; // sub version of this record + u8 good_version; // expected version for 'fform' + s8 n_sect; // number of sections + s8 good_sect; // expected sections for 'fform' + brsub_warn_t warn; // warning level +} +brsub_info_t; + +//----------------------------------------------------------------------------- + +// list terminated with fform1 == 0 (FF_UNKNOWN) +extern const brsub_info_t brsub_info[]; + +const brsub_info_t * GetInfoBRSUB +( + file_format_t fform, // file format + u32 version // sub version +); + +const brsub_info_t * GetCorrectBRSUB +( + file_format_t fform // file format +); + +/////////////////////////////////////////////////////////////////////////////// + +int GetKnownSectionNumBRSUB +( + file_format_t fform, // file format + u32 version // sub version +); + +int GetGenericSectionNumBRSUB +( + const u8 * data, // valid data pointer + uint data_size, // size of 'data' + const endian_func_t * endian // NULL or endian functions +); + +int GetSectionNumBRSUB +( + const u8 * data, // valid data pointer + uint data_size, // size of 'data' + const endian_func_t * endian // NULL or endian functions +); + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintHeaderNameBRSUB +( + char * buf, // result buffer + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + const brsub_header_t * bhead, // valid brsub header + uint n_grp, // number of groups + const endian_func_t * endian // NULL or endian +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRRES subfile iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct brsub_iterator_t; + +typedef int (*brsub_it_raw_func) +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * data, // data pointer + u32 data_size // size of 'data' +); + +typedef int (*brsub_it_grp_func) +( + struct + brsub_iterator_t * it, // iterator struct with all infos + brres_group_t * grp, // pointer to group + uint grp_index // index of group +); + +typedef int (*brsub_it_entry_func) +( + struct + brsub_iterator_t * it, // iterator struct with all infos + brres_group_t * grp, // pointer to group + uint grp_index, // index of group + brres_entry_t * entry, // pointer to entry + u8 * data // pointer to entry related data +); + +typedef int (*brsub_it_str_func) +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 string offset +); + +typedef int (*brsub_it_off_func) +( + struct + brsub_iterator_t * it, // iterator struct with all infos + u8 * base, // pointer to base for calculation + u32 * ptr // pointer to u32 offset +); + +//----------------------------------------------------------------------------- +// [[brsub_iterator_t]] + +typedef struct brsub_iterator_t +{ + //--- global parameters + + szs_file_t * szs; // valid szs pointer + brsub_header_t * brsub; // brsub data + uint data_size; // size of 'brsub' data + uint file_size; // size of 'brsub' file + file_format_t fform; // format of data, or FF_UNKNOWN + file_format_t force_fform; // force this and don't check brsub + bool adjust; // true: adjust offsets + bool static_strings; // false: find only relocable strings + // true: find static strings too + + brsub_it_raw_func raw_func; // NULL or called for each raw data block + brsub_it_grp_func grp_func; // NULL or called for each group + brsub_it_entry_func entry_func; // NULL or called for each entry + brsub_it_str_func string_func; // NULL or called for each string + brsub_it_off_func offset_func; // NULL or called for each offset value + + void * param; // user defined parameter + const endian_func_t * endian; // endian functions + +} brsub_iterator_t; + +//----------------------------------------------------------------------------- + +int IterateStringsBRSUB +( + brsub_iterator_t * it // valid iterator data +); + +//----------------------------------------------------------------------------- + +int CollectStringsBRSUB +( + string_pool_t * sp, // string pool + bool init_sp, // true: initialize 'sp' + szs_file_t * szs, // szs of data + bool adjust // true: create string pool and + // adjust offsets and strings +); + +//----------------------------------------------------------------------------- + +int AdjustStringsBRSUB +( + szs_file_t * szs, // base BRRES file + void * brsub_data, // brres sub data + size_t brsub_size // size of 'brsub_data' including string pool +); + +//----------------------------------------------------------------------------- + +int DumpStructureBRSUB +( + FILE * f, // output file + szs_file_t * base_szs, // valid base SZS + uint off, // offset of selected data within 'base' + uint size, // not NULL: size of selected data + file_format_t fform // not FF_UNKNOWN: use this type +); + +//----------------------------------------------------------------------------- + +int DumpStructureBRRES +( + FILE * f, // output file + szs_file_t * szs // BRRES file +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct brres_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[brres_t]] + +typedef struct brres_t +{ + SubDir_t sub; // sub filesystem + //string_pool; +} +brres_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TEX0 support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[tex_info_t]] + +typedef struct tex_info_t +{ + //--- part of BRSUB header + u32 name_off; // offset of object name + + //--- group -1 of BRSUB file + u32 unknown1; // always 0 im MKW + u16 width; // width of image in pixel + u16 height; // height of image in pixel + u32 iform; // image format + u32 n_image; // number of images (main+mipmaps) + u32 unknown2; // always 0 im MKW + float32 image_val; // 'n_image-1' as 32 bit float + u32 unknown4; // always 0 im MKW +} +__attribute__ ((packed)) tex_info_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// NAME-REF support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[name_ref_elem_t]] + +typedef struct name_ref_elem_t +{ + u32 file_off; // offset to file start + u32 base_off; // offset to section base + ccp info; // info text, alloced +} +name_ref_elem_t; + +//----------------------------------------------------------------------------- +// [[name_ref_var_t]] + +typedef struct name_ref_var_t +{ + ccp var_name; // NULL or variable name, alloced + u32 var_off; // file offset of var name + u32 min_file_off; // minimal file offset + + uint eused; // number of used elements + uint esize; // number of alloced elements + name_ref_elem_t *elist; // list of elements, orderd by 'file_off' +} +name_ref_var_t; + +//----------------------------------------------------------------------------- +// [[name_ref_t]] + +typedef struct name_ref_t +{ + u8 *data; // data + uint data_size; // size of data + file_format_t fform; // file format + ccp fname; // file name of source, alloced + + int brief; // >0: suppress string offsets + // >1: suppress reference offsets + // >2: suppress all offsets + + uint vused; // number of used variables + uint vsize; // number of alloced variables + name_ref_var_t *vlist; // list of variables, ordered by 'var_offset' + + char file_info[40]; // used for name_ref_elem_t::info +} +name_ref_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeNameRef ( name_ref_t *nr ); +void ResetNameRef ( name_ref_t *nr ); + +enumError CreateNameRef + ( name_ref_t *nr, bool init_nr, szs_file_t *szs, int brief ); +void ListNameRef ( FILE *f, int indent, name_ref_t *nr, SortMode_t sort ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_BRRES_H diff --git a/project/src/lib-bzip2.c b/project/src/lib-bzip2.c new file mode 100644 index 0000000..69ff271 --- /dev/null +++ b/project/src/lib-bzip2.c @@ -0,0 +1,932 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +// BZIP2 support: http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html + +#ifndef NO_BZIP2 + #include "libbz2/bzlib.h" + #include "lib-bzip2.h" +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZIP2 helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint IsBZIP2 +( + // returns + // 0: not BZIP2 data + // 1..9: seems to be BZIP2 data; compression level is returned + + cvp data, // NULL or data to investigate + uint size // size of 'data' +) +{ + const u8 *d = data; + if ( !d || size < 10 || memcmp(d,"BZh",3) || memcmp(d+4,"1AY&SY'",6) ) + return 0; + + const uint level = d[3] - '0'; + return level < 10 ? level : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetMessageBZIP2 +( + int err, // error code + ccp unkown_error // result for unkown error codes +) +{ + #ifndef NO_BZIP2 + switch (err) + { + case BZ_OK: return "OK"; + + case BZ_CONFIG_ERROR: return "CONFIG ERROR"; + case BZ_DATA_ERROR: return "DATA ERROR"; + case BZ_DATA_ERROR_MAGIC: return "DATA ERROR MAGIC"; + case BZ_IO_ERROR: return "IO ERROR"; + case BZ_MEM_ERROR: return "MEM ERROR"; + case BZ_PARAM_ERROR: return "PARAM ERROR"; + case BZ_SEQUENCE_ERROR: return "SEQUENCE ERROR"; + case BZ_STREAM_END: return "STREAM END"; + case BZ_UNEXPECTED_EOF: return "UNEXPECTED EOF"; + } + #endif + + return unkown_error; +}; + +/////////////////////////////////////////////////////////////////////////////// + +int CalcCompressionLevelBZIP2 +( + int compr_level // valid are 1..9 / 0: use default value +) +{ + return compr_level < 1 + ? BZIP2_DEFAULT_COMPR + : compr_level < 9 + ? compr_level + : 9; +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 CalcMemoryUsageBZIP2 +( + int compr_level, // valid are 1..9 / 0: use default value + bool is_writing // false: reading mode, true: writing mode +) +{ + compr_level = CalcCompressionLevelBZIP2(compr_level); + return is_writing + ? ( 4 + 8 * compr_level ) * 102400 + : ( 1 + 4 * compr_level ) * 102400; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZIP2 writing /////////////// +/////////////////////////////////////////////////////////////////////////////// +#if 0 // not needed yet +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeBZIP2_Open +( + BZIP2_t * bz, // data structure, will be initialized + File_t * file, // destination file + int compr_level // valid are 1..9 / 0: use default value +) +{ + DASSERT(bz); + DASSERT(file); + DASSERT(file->f); + + bz->file = file; + bz->compr_level = CalcCompressionLevelBZIP2(compr_level); + + int bzerror; + bz->handle = BZ2_bzWriteOpen(&bzerror,file->f,bz->compr_level,0,0); + if ( !bz || bzerror != BZ_OK ) + { + if (bz->handle) + { + BZ2_bzWriteClose(0,bz->handle,0,0,0); + bz->handle = 0; + } + + return ERROR0(ERR_BZIP2, + "Error while opening bzip2 stream: %s\n-> bzip2 error: %s\n", + bz->file->fname, GetMessageBZIP2(bzerror,"?") ); + return 0; + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeBZIP2_Write +( + BZIP2_t * bz, // created by EncodeBZIP2_Open() + const void * data, // data to write + size_t data_size // size of data to write +) +{ + DASSERT(bz); + DASSERT(bz->handle); + + if (data_size) + { + int bzerror; + BZ2_bzWrite(&bzerror,bz->handle,(u8*)data,data_size); + if ( bzerror != BZ_OK ) + { + BZ2_bzWriteClose(0,bz->handle,0,0,0); + bz->handle = 0; + + return ERROR0(ERR_BZIP2, + "Error while writing bzip2 stream: %s\n-> bzip2 error: %s\n", + bz->file->fname, GetMessageBZIP2(bzerror,"?") ); + } + //bz->file->bytes_written += data_size; + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeBZIP2_Close +( + BZIP2_t * bz, // NULL or created by EncodeBZIP2_Open() + u32 * bytes_written // not NULL: store written bytes +) +{ + u32 written = 0; + if ( bz && bz->handle ) + { + int bzerror; + BZ2_bzWriteClose(&bzerror,bz->handle,0,0,&written); + bz->handle = 0; + + if ( bzerror != BZ_OK ) + return ERROR0(ERR_BZIP2, + "Error while closing bzip2 stream: %s\n-> bzip2 error: %s\n", + bz->file->fname, GetMessageBZIP2(bzerror,"?") ); + } + + if (bytes_written) + *bytes_written = written; + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif // 0 // not needed yet +/////////////////////////////////////////////////////////////////////////////// + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZIP2 reading /////////////// +/////////////////////////////////////////////////////////////////////////////// +#if 0 // not needed yet +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2_Open +( + BZIP2_t * bz, // data structure, will be initialized + File_t * file // source file +) +{ + DASSERT(file); + DASSERT(file->f); + + bz->file = file; + + int bzerror; + bz->handle = BZ2_bzReadOpen(&bzerror,file->f,0,0,0,0); + if ( !bz->handle || bzerror != BZ_OK ) + { + if (bz->handle) + { + BZ2_bzReadClose(0,bz->handle); + bz->handle = 0; + } + + return ERROR0(ERR_BZIP2, + "Error while opening bzip2 stream: %s\n-> bzip2 error: %s\n", + bz->file->fname, GetMessageBZIP2(bzerror,"?") ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2_Read +( + BZIP2_t * bz, // created by DecodeBZIP2_Open() + void * buf, // destination buffer + size_t buf_size, // size of destination buffer + u32 * buf_written // not NULL: store bytes written to buf +) +{ + DASSERT(bz); + DASSERT(bz->handle); + DASSERT(buf); + + int bzerror; + const u32 written = BZ2_bzRead(&bzerror,bz->handle,buf,buf_size); + noPRINT("BZREAD, num=%x, buf_size=%zx, err=%d\n",written,buf_size,bzerror); + if ( bzerror != BZ_STREAM_END ) + { + BZ2_bzReadClose(0,bz->handle); + bz->handle = 0; + + return ERROR0(ERR_BZIP2, + "Error while reading bzip2 stream: %s\n-> bzip2 error: %s\n", + bz->file->fname, GetMessageBZIP2(bzerror,"?") ); + } + + //bz->file->bytes_read += written; + if (buf_written) + *buf_written = written; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2_Close +( + BZIP2_t * bz // NULL or created by DecodeBZIP2_Open() +) +{ + if ( bz && bz->handle ) + { + int bzerror; + BZ2_bzReadClose(&bzerror,bz->handle); + bz->handle = 0; + + if ( bzerror != BZ_OK ) + return ERROR0(ERR_BZIP2, + "Error while closing bzip2 stream: %s\n-> bzip2 error: %s\n", + bz->file->fname, GetMessageBZIP2(bzerror,"?") ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif // 0 // not needed yet +/////////////////////////////////////////////////////////////////////////////// + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZIP2 memory conversions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeBZIP2buf +( + void *dest, // valid destination buffer + uint dest_size, // size of 'dest' + uint *dest_written, // store num bytes written to 'dest', never NULL + + const void *src, // source buffer + uint src_size, // size of source buffer + + int compr_level // valid are 1..9 / 0: use default value +) +{ + #ifdef NO_BZIP2 + DASSERT(dest_written); + *dest_written = 0; + return ERROR0(ERR_BZIP2,"BZIP2 compression not supported\n"); + #else // !NO_BZIP2 + + // http://www.bzip.org/1.0.3/html/util-fns.html#bzbufftobuffcompress + + DASSERT(dest); + DASSERT( dest_size > sizeof(u32) ); + DASSERT(dest_written); + DASSERT(src); + + compr_level = CalcCompressionLevelBZIP2(compr_level); + PRINT("EncodeBZIP2buf() %u bytes, level = %u\n",src_size,compr_level); + + *(u32*)dest = htonl(src_size); + *dest_written = dest_size - sizeof(u32); + int bzerror = BZ2_bzBuffToBuffCompress ( dest+sizeof(u32), dest_written, + (char*)src, src_size, compr_level, 0, 0 ); + *dest_written += 4; + + if ( bzerror != BZ_OK ) + return ERROR0(ERR_BZIP2, + "Error while compressing data.\n-> bzip2 error: %s\n", + GetMessageBZIP2(bzerror,"?") ); + + return ERR_OK; + #endif // !NO_BZIP2 +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeBZIP2 +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest', never NULL + bool use_iobuf, // true: allow the usage of 'iobuf' + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer + uint src_size, // size of source buffer + + int compr_level // valid are 1..9 / 0: use default value +) +{ + DASSERT(dest_ptr); + DASSERT(dest_written); + DASSERT(src); + + char *dest_alloced = 0, *dest; + uint dest_size = src_size + src_size/100 + 600 + 20; + if ( dest_size <= sizeof(iobuf) && use_iobuf ) + { + dest = iobuf; + dest_size = sizeof(iobuf); + } + else + { + dest_alloced = MALLOC(dest_size+header_size); + memset(dest_alloced,0,header_size); + dest = dest_alloced + header_size; + } + + enumError err = EncodeBZIP2buf( dest, dest_size, dest_written, + src, src_size, compr_level ); + if (err) + { + FREE(dest_alloced); + *dest_ptr = 0; + *dest_written = 0; + } + else if (dest_alloced) + { + *dest_written += header_size; + *dest_ptr = REALLOC(dest_alloced,*dest_written); + } + else + { + DASSERT(dest==iobuf); + dest = MALLOC(*dest_written+header_size); + memset(dest,0,header_size); + memcpy(dest+header_size,iobuf,*dest_written); + *dest_ptr = (u8*)dest; + *dest_written += header_size; + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2stream +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest', never NULL + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer + uint src_size // size of source buffer +) +{ + DASSERT(dest_ptr); + DASSERT(dest_written); + DASSERT( src || !src_size ); + + *dest_ptr = 0; + *dest_written = 0; + + if ( !src || !src_size ) + return ERR_NOTHING_TO_DO; + + #ifdef NO_BZIP2 + return ERROR0(ERR_BZIP2,"BZIP2 compression not supported\n"); + #else // !NO_BZIP2 + + uint data_size = 2*MiB + header_size; + u8 *data = MALLOC(data_size); + memset(data,0,header_size); + uint data_used = header_size; + + bz_stream bzs; + memset(&bzs,0,sizeof(bzs)); + + int bz2err = BZ2_bzDecompressInit(&bzs,0,0); + if ( bz2err != BZ_OK ) + goto error; + + bzs.next_in = (char*)src; + bzs.avail_in = src_size; + + for(;;) + { + if ( data_size - data_used < MiB ) + { + data_size += 2*MiB; + data = REALLOC(data,data_size); + if (!data) + goto error; + } + + bzs.next_out = (char*)data + data_used; + bzs.avail_out = data_size - data_used; + bz2err = BZ2_bzDecompress(&bzs); + data_used = bzs.next_out - (char*)data; + if ( bz2err == BZ_STREAM_END ) + break; + if ( bz2err != BZ_OK ) + goto error; + } + BZ2_bzDecompressEnd(&bzs); + u8 *newdata = REALLOC(data,data_used); + *dest_ptr = newdata ? newdata : data; + *dest_written = data_used; + return ERR_OK; + + error: + BZ2_bzDecompressEnd(&bzs); + FREE(data); + return ERR_BZIP2; + + #endif // !NO_BZIP2 +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2part +( + // decompress until dest buffer full or end of source reached + // return ERR_WARNING for an incomplete decompression + + void *dest_buf, // result: store rad data hers + uint dest_size, // size of 'dest_buf' + uint *dest_written, // store num bytes written to 'dest_ptr', never NULL + + const void *src, // source buffer + uint src_size // size of source buffer +) +{ + DASSERT(dest_buf); + DASSERT(dest_size); + DASSERT(dest_written); + DASSERT( src || !src_size ); + + *dest_written = 0; + if ( !src || !src_size ) + return ERR_NOTHING_TO_DO; + + #ifdef NO_BZIP2 + return ERROR0(ERR_BZIP2,"BZIP2 compression not supported\n"); + #else // !NO_BZIP2 + + bz_stream bzs; + memset(&bzs,0,sizeof(bzs)); + + int bz2err = BZ2_bzDecompressInit(&bzs,0,0); + if ( bz2err != BZ_OK ) + goto error; + + bzs.next_in = (char*)src; + bzs.avail_in = src_size; + bzs.next_out = (char*)dest_buf; + bzs.avail_out = dest_size; + + bz2err = BZ2_bzDecompress(&bzs); + *dest_written = bzs.next_out - (char*)dest_buf; + if ( bz2err != BZ_STREAM_END && bz2err != BZ_OK ) + goto error; + + BZ2_bzDecompressEnd(&bzs); + return bz2err == BZ_OK ? ERR_OK : ERR_WARNING; + + error: + BZ2_bzDecompressEnd(&bzs); + return ERR_BZIP2; + + #endif // !NO_BZIP2 +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2buf +( + void *dest, // valid destination buffer + uint dest_size, // size of 'dest' + uint *dest_written, // store num bytes written to 'dest', never NULL + + const void *src, // source buffer + uint src_size // size of source buffer +) +{ + #ifdef NO_BZIP2 + DASSERT(dest_written); + *dest_written = 0; + return ERROR0(ERR_BZIP2,"BZIP2 compression not supported\n"); + #else // !NO_BZIP2 + + // http://www.bzip.org/1.0.3/html/util-fns.html#bzbufftobuffdecompress + + DASSERT(dest); + DASSERT( dest_size >= sizeof(u32) ); + DASSERT(dest_written); + DASSERT(src); + + uint dest_need = ntohl(*(u32*)src); + *dest_written = dest_need; + PRINT("DecodeBZIP2buf() %u -> %u,%u bytes\n",src_size,dest_size,dest_need); + + int bzerror = BZ2_bzBuffToBuffDecompress ( (char*)dest, dest_written, + (char*)src+4, src_size-4, 0, 0 ); + + if ( bzerror != BZ_OK ) + return ERROR0(ERR_BZIP2, + "Error while decompressing data.\n-> bzip2 error: %s\n", + GetMessageBZIP2(bzerror,"?") ); + + return ERR_OK; + #endif // !NO_BZIP2 +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2 +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest', never NULL + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer + uint src_size // size of source buffer +) +{ + DASSERT(dest_ptr); + DASSERT(dest_written); + DASSERT(src); + DASSERT( src_size >= sizeof(u32) ); + + uint dest_size = ntohl(*(u32*)src); + u8 *dest = MALLOC(dest_size+header_size); + memset(dest,0,header_size); + + enumError err = DecodeBZIP2buf(dest+header_size,dest_size,dest_written,src,src_size); + if (err) + { + FREE(dest); + *dest_ptr = 0; + *dest_written = 0; + } + else + { + *dest_ptr = dest; + *dest_written += header_size; + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2Manager +( + BZ2Manager_t *mgr // manager data +) +{ + DASSERT(mgr); + + if (!mgr->data) + { + mgr->size = 0; + if (mgr->src_data) + return DecodeBZIP2(&mgr->data,&mgr->size,0,mgr->src_data,mgr->src_size); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZ2S = BZ2-Source /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const char InternalBZ2S[] = ""; + +/////////////////////////////////////////////////////////////////////////////// + +uint ClearBZ2S +( + // Returns the number of cleard elements + // Call this function to free results of SearchBZ2S() and SearchListBZ2S() + // Both functions call ClearBZ2S() at the beginning. + + BZ2Source_t *src, // valid BZ2Source_t list + int n // >=0: clear 'n' elements max, + // but always stop at list end! +) +{ + DASSERT(src); + + BZ2Source_t *s = src; + for ( ; s->search && n-- != 0 ; s++ ) + { + if (s->fname) + { + if ( s->fname != InternalBZ2S ) + FreeString(s->fname); + s->fname = NULL; + } + + if (s->data) + { + if (IsDataAllocedBZ2S(s)) + FREE(s->data); + s->data = 0; + } + + s->size = 0; + s->status = BZ2S_T_NONE; + } + + return s - src; +} + +/////////////////////////////////////////////////////////////////////////////// + +u8 * NonConstDataBZ2S +( + // Converts src->data to malloced data (modifications possible) + // return src->data after conversion. + + BZ2Source_t *src // valid BZ2Source_t +) +{ + if ( !src || !src->data || !src->size ) + return 0; + + if ( !IsDataAllocedBZ2S(src) ) + src->data = MEMDUP(src->data,src->size); + return src->data; +} + +/////////////////////////////////////////////////////////////////////////////// + +BZ2Status_t SearchBZ2S +( + BZ2Source_t *src, // valid single BZ2Source_t + ccp *dir_list, // list with search directories + // NULL and empty strings are ignored. + int n_dir_list, // number of elements in 'dir_list' + // <0: NULL termianted list + SearchLog_t log_level // one of SEA_LOG_* +) +{ + DASSERT(src); + + ClearBZ2S(src,1); + + if ( dir_list && src->search && *src->search ) + { + for ( ; n_dir_list-- != 0 ; dir_list++ ) + { + if (!*dir_list) + { + if ( n_dir_list > 0 ) + continue; + break; + } + if (!**dir_list) + continue; + if ( log_level >= SEA_LOG_ALL ) + fprintf(stdlog,"> SEARCH %s @DIR %s\n",src->search,*dir_list); + + char path[PATH_MAX], *pend = path + sizeof(path) - 1; + char *pbase = StringCopyS(path,sizeof(path)-2,*dir_list); + if ( pbase == path ) + { + *pbase++ = '.'; + *pbase++ = '/'; + } + else if ( pbase[-1] != '/' ) + *pbase++ = '/'; + + ccp search = src->search; + DASSERT(search); + while( *search ) + { + while ( *search == ';' ) + search++; + char *pptr = pbase; + while ( *search && *search != ';' ) + { + if ( pptr < pend ) + *pptr++ = *search; + search++; + } + DASSERT( pptr < path + sizeof(path) ); + if ( pptr == pbase ) + continue; + *pptr = 0; + + noPRINT(" SEARCH: %s\n",path); + + u8 *data = 0; + uint size = 0; + enumError err = OpenReadFILE(path,0,true,&data,&size,0,0); + if ( err != ERR_OK ) + { + FREE(data); + StringCopyE(pptr,pend,".bz2"); + noPRINT(" SEARCH: %s\n",path); + err = OpenReadFILE(path,0,true,&data,&size,0,0); + if ( err != ERR_OK ) + { + if ( log_level >= SEA_LOG_ALL ) + fprintf(stdlog,">> NOT FOUND: %.*s\n",(int)(pptr-path),path); + FREE(data); + continue; + } + } + + src->status = BZ2S_T_FILE; + if (IsBZIP2(data,size)) + { + // seems to be bzip2 encoded + if ( log_level >= SEA_LOG_FOUND ) + fprintf(stdlog,"> BZIP2 READ: %s\n",path); + + PRINT(" \e[36;1m==> BZIP2: %s\e[0m\n",path); + + u8 *bz2data = 0; + uint bz2size; + if (DecodeBZIP2stream(&bz2data,&bz2size,0,data,size)) + FREE(bz2data); + else + { + FREE(data); + data = bz2data; + size = bz2size; + src->status |= BZ2S_F_BZIP2; + } + } + else if ( log_level >= SEA_LOG_FOUND ) + fprintf(stdlog,"> READ: %s\n",path); + + if ( src->fform != FF_UNKNOWN ) + { +// [[analyse-magic]] + file_format_t ff = GetByMagicFF(data,size,size); + if ( ff != src->fform ) + { + ERROR0(ERR_WARNING, + "Need file format %s, but file is %s -> ignore %s\n", + GetNameFF(src->fform,0), GetNameFF(ff,0), path ); + src->status = BZ2S_T_NONE; + continue; + } + } + + src->fname = STRDUP(path); + src->data = data; + src->size = size; + PRINT(" \e[33;1m==> %u bytes FOUND: %s\e[0m\n",size,path); + return src->status; + } + } + } + + + //--- fall back to internal source + + if ( src->src_data && src->src_size ) + { + src->fname = InternalBZ2S; + + u8 *data = (u8*)src->src_data; + uint size = src->src_size; + + src->status = BZ2S_T_INTERN; + if ( size > 4 && IsBZIP2(data+4,size-4) ) + { + PRINT(" \e[36;1m==> BZIP2: %s\e[0m\n",src->fname); + + u8 *bz2data = 0; + uint bz2size; + if (DecodeBZIP2(&bz2data,&bz2size,0,data,size)) + FREE(bz2data); + else + { + data = bz2data; + size = bz2size; + src->status |= BZ2S_F_BZIP2; + } + } + + if ( src->part_offset > 0 ) + { + const uint off = src->part_offset < size ? src->part_offset : size; + size -= off; + if ( src->part_size && src->part_size < size ) + size = src->part_size; + PRINT(" \e[36;1m==> PART %u..%u: %s\e[0m\n",off,off+size,src->fname); + if ( src->status == BZ2S_T_INTERN ) // without BZ2S_F_BZIP2 + data += off; + else + { + u8 *newdata = MEMDUP(data+off,size); + FREE(data); + data = newdata; + } + } + + src->data = data; + src->size = src->part_size && src->part_size < size ? src->part_size : size; + PRINT(" \e[33;1m==> %u/%u bytes FOUND: %s\e[0m\n",src->size,size,src->fname); + return src->status; + } + + + //--- no file found + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SearchListBZ2S +( + // returns the number of found (file or internal) sources + + BZ2Source_t *src, // valid BZ2Source_t list + int n, // >=0: search 'n' elements max + // but always stop at list end! + ccp *dir_list, // NULL or list with search directories + // NULL and empty strings are ignored. + int n_dir_list, // number of elements in 'dir_list' + // <0: NULL terminated list + SearchLog_t log_level // one of SEA_LOG_* +) +{ + DASSERT(src); + uint count = 0; + for ( ; src->search && n-- != 0 ; src++ ) + if (SearchBZ2S(src,dir_list,n_dir_list,log_level)) + count++; + return count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-bzip2.h b/project/src/lib-bzip2.h new file mode 100644 index 0000000..0b59c11 --- /dev/null +++ b/project/src/lib-bzip2.h @@ -0,0 +1,406 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef WIT_LIB_BZIP2_H +#define WIT_LIB_BZIP2_H 1 +#ifndef NO_BZIP2 + +#define _GNU_SOURCE 1 + +#include "lib-std.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _BZLIB_H + typedef void BZFILE; +#endif + +#define BZIP2_DEFAULT_COMPR 9 + +//----------------------------------------------------------------------------- + +typedef struct BZIP2_t +{ + File_t * file; // IO file + BZFILE * handle; // bzip2 handle + int compr_level; // active compression level + +} BZIP2_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint IsBZIP2 +( + // returns + // 0: not BZIP2 data + // 1..9: seems to be BZIP2 data; compression level is returned + + cvp data, // NULL or data to investigate + uint size // size of 'data' +); + +//----------------------------------------------------------------------------- + +ccp GetMessageBZIP2 +( + int err, // error code + ccp unkown_error // result for unkown error codes +); + +//----------------------------------------------------------------------------- + +int CalcCompressionLevelBZIP2 +( + int compr_level // valid are 1..9 / 0: use default value +); + +//----------------------------------------------------------------------------- + +u32 CalcMemoryUsageBZIP2 +( + int compr_level, // valid are 1..9 / 0: use default value + bool is_writing // false: reading mode, true: writing mode +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZIP2 writing /////////////// +/////////////////////////////////////////////////////////////////////////////// +#if 0 // not needed yet +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeBZIP2_Open +( + BZIP2_t * bz, // data structure, will be initialized + File_t * file, // destination file + int compr_level // valid are 1..9 / 0: use default value +); + +//----------------------------------------------------------------------------- + +enumError EncodeBZIP2_Write +( + BZIP2_t * bz, // created by EncodeBZIP2_Open() + const void * data, // data to write + size_t data_size // size of data to write +); + +//----------------------------------------------------------------------------- + +enumError EncodeBZIP2_Close +( + BZIP2_t * bz, // NULL or created by EncodeBZIP2_Open() + u32 * bytes_written // not NULL: store written bytes +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZIP2 reading /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeBZIP2_Open +( + BZIP2_t * bz, // data structure, will be initialized + File_t * file // source file +); + +//----------------------------------------------------------------------------- + +enumError DecodeBZIP2_Read +( + BZIP2_t * bz, // created by DecodeBZIP2_Open() + void * buf, // destination buffer + size_t buf_size, // size of destination buffer + u32 * buf_written // not NULL: store bytes written to buf +); + +//----------------------------------------------------------------------------- + +enumError DecodeBZIP2_Close +( + BZIP2_t * bz // NULL or created by DecodeBZIP2_Open() +); + +/////////////////////////////////////////////////////////////////////////////// +#endif // 0 // not needed yet +/////////////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZIP2 memory conversions /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[BZ2Manager_t]] + +typedef struct BZ2Manager_t +{ + //--- source + + cvp src_data; // BZ2 is automatically detected and + // decoded by DecodeBZIP2. Nver NULL + uint src_size; // size of 'src_data' + + //--- decoded + + u8 *data; // NULL or data, alloced if not part of 'src_data' + uint size; // size of 'data' +} +BZ2Manager_t; + +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeBZIP2buf +( + void *dest, // valid destination buffer + uint dest_size, // size of 'dest' + uint *dest_written, // store num bytes written to 'dest', never NULL + + const void *src, // source buffer + uint src_size, // size of source buffer + + int compr_level // valid are 1..9 / 0: use default value +); + +//----------------------------------------------------------------------------- + +enumError EncodeBZIP2 +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest_ptr', never NULL + bool use_iobuf, // true: allow thhe usage of 'iobuf' + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer + uint src_size, // size of source buffer + + int compr_level // valid are 1..9 / 0: use default value +); + +//----------------------------------------------------------------------------- + +enumError DecodeBZIP2stream +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest_ptr', never NULL + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer + uint src_size // size of source buffer +); + +//----------------------------------------------------------------------------- + +enumError DecodeBZIP2part +( + // decompress until dest buffer full or end of source reached + // return ERR_WARNING for an incomplete decompression + + void *dest_buf, // result: store rad data hers + uint dest_size, // size of 'dest_buf' + uint *dest_written, // store num bytes written to 'dest_ptr', never NULL + + const void *src, // source buffer + uint src_size // size of source buffer +); + +//----------------------------------------------------------------------------- + +enumError DecodeBZIP2buf +( + void *dest, // valid destination buffer + uint dest_size, // size of 'dest' + uint *dest_written, // store num bytes written to 'dest_ptr', never NULL + + const void *src, // source buffer + uint src_size // size of source buffer +); + +//----------------------------------------------------------------------------- + +enumError DecodeBZIP2 +( + u8 **dest_ptr, // result: store destination buffer addr + uint *dest_written, // store num bytes written to 'dest', never NULL + uint header_size, // insert 'header_size' bytes before dest data + + const void *src, // source buffer + uint src_size // size of source buffer +); + +//----------------------------------------------------------------------------- + +enumError DecodeBZIP2Manager +( + BZ2Manager_t *mgr // manager data +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZ2S = BZ2-Source /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[BZ2SMode_t]] + +typedef enum BZ2Status_t +{ + BZ2S_F_BZIP2 = 1, // flag: source was bzip2 encoded + BZ2S_M_TYPE = ~1, // types without flags + + BZ2S_T_NONE = 0, // no data found + BZ2S_T_INTERN = 2, // internal data used + BZ2S_T_FILE = 4, // external file used +} +BZ2Status_t; + +//----------------------------------------------------------------------------- +// [[BZ2Source_t]] + +typedef struct BZ2Source_t +{ + //--- search files + + ccp search; // Semicolon separated list of filenames + // or sub-pathes to search. + // NULL terminate source lists. + + //--- internal fall back data + + cvp src_data; // NULL or internal fall back data. + // BZ2 is automatically detected and + // decoded by DecodeBZIP2. + uint src_size; // size of 'src_data' + + file_format_t fform; // not FF_UNKNOWN: Allow only this type + + uint part_offset; // >0: use only a part of (decoced) 'src_data' + uint part_size; // >0: limit result to this size if + // 'src_data' is used + + //--- result of search + + BZ2Status_t status; // return value of SearchBZ2S() + ccp fname; // NULL or filename, alloced if not 'InternalBZ2S' + u8 *data; // NULL or data, alloced if not part of 'src_data' + uint size; // size of 'data' +} +BZ2Source_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef enum SearchLog_t +{ + SEA_LOG_OFF, + SEA_LOG_SUCCESS, + SEA_LOG_FOUND, + SEA_LOG_ALL, +} +SearchLog_t; + +extern const char InternalBZ2S[]; // = "" + +//----------------------------------------------------------------------------- + +uint ClearBZ2S +( + // Returns the number of cleard elements + // Call this function to free results of SearchBZ2S() and SearchListBZ2S() + // Both functions call ClearBZ2S() at the beginning. + + BZ2Source_t *src, // valid BZ2Source_t list + int n // >=0: clear 'n' elements max, + // but always stop at list end! +); + +//----------------------------------------------------------------------------- + +static inline bool IsDataAllocedBZ2S ( BZ2Source_t *src ) +{ + DASSERT(src); + return !src->src_data + || src->data < (u8*)src->src_data + || src->data > (u8*)src->src_data + src->src_size; +} + +//----------------------------------------------------------------------------- + +u8 * NonConstDataBZ2S +( + // Converts src->data to malloced data (modifications possible) + // return src->data after conversion. + + BZ2Source_t *src // valid BZ2Source_t +); + +//----------------------------------------------------------------------------- + +BZ2Status_t SearchBZ2S +( + BZ2Source_t *src, // valid single BZ2Source_t + ccp *dir_list, // list with search directories + // NULL and empty strings are ignored. + int n_dir_list, // number of elements in 'dir_list' + // <0: NULL termianted list + SearchLog_t log_level // one of SEA_LOG_* +); + +//----------------------------------------------------------------------------- + +uint SearchListBZ2S +( + // returns the number of found (file or internal) sources + + BZ2Source_t *src, // valid BZ2Source_t list + int n, // >=0: search 'n' elements max + // but always stop at list end! + ccp *dir_list, // NULL or list with search directories + // NULL and empty strings are ignored. + int n_dir_list, // number of elements in 'dir_list' + // <0: NULL terminated list + SearchLog_t log_level // one of SEA_LOG_* +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // !NO_BZIP2 +#endif // WIT_LIB_BZIP2_H 1 + diff --git a/project/src/lib-checksum.c b/project/src/lib-checksum.c new file mode 100755 index 0000000..990fa7f --- /dev/null +++ b/project/src/lib-checksum.c @@ -0,0 +1,982 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include + +#include "crypt.h" +#include "dclib-basics.h" +#include "dclib-numeric.h" +#include "lib-checksum.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct sha1_size_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CreateSSChecksum ( char *buf, uint bufsize, const sha1_size_t *ss ) +{ + DASSERT(buf); + DASSERT(bufsize>1); + DASSERT(ss); + + if (opt_db64) + { + PRINT("TAB64=%.65s\n",TableEncode64BySetup); + EncodeBase64(buf,bufsize,ss,sizeof(*ss), + TableEncode64BySetup, false,0,0 ); + } + else if (opt_base64) + { + PRINT("TAB64=%.65s\n",TableEncode64BySetup); + EncodeBase64(buf,bufsize,ss->hash,sizeof(ss->hash), + TableEncode64BySetup, false,0,0); + } + else if (opt_id) + { + sha1_id_t id; + SHA1_to_ID(id,ss->hash); + StringCopyS(buf,bufsize,(ccp)id); + } + else if ( bufsize > 2*sizeof(ss->hash) ) + { + int i; + for ( i = 0; i < sizeof(ss->hash); i++ ) + sprintf(buf+2*i,"%02x",ss->hash[i]); + } + else + *buf = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CreateSSChecksumBySZS ( char *buf, uint bufsize, const szs_file_t *szs ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + + if ( !szs || !szs->data ) + { + *buf = 0; + return; + } + + sha1_size_t info; + SHA1(szs->data,szs->size,info.hash); + info.size = htonl(szs->size); + CreateSSChecksum(buf,bufsize,&info); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// normed DB coding (independent of options) + +void CreateSSChecksumDB ( char *buf, uint bufsize, const sha1_size_t *ss ) +{ + DASSERT(buf); + DASSERT(bufsize>CHECKSUM_DB_SIZE); + DASSERT(ss); + + EncodeBase64(buf,bufsize,ss,sizeof(*ss),TableEncode64url,false,0,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CreateSSChecksumDBByData ( char *buf, uint bufsize, cvp data, uint size ) +{ + DASSERT(data||!size); + DASSERT(bufsize>CHECKSUM_DB_SIZE); + + if (!data) + *buf = 0; + else + { + sha1_size_t info; + SHA1(data,size,info.hash); + info.size = htonl(size); + CreateSSChecksumDB(buf,bufsize,&info); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CreateSSChecksumDBBySZS ( char *buf, uint bufsize, const szs_file_t *szs ) +{ + DASSERT(buf); + DASSERT(bufsize>CHECKSUM_DB_SIZE); + + if (szs) + CreateSSChecksumDBByData(buf,bufsize,szs->data,szs->size); + else + *buf = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError GetSSByFile ( sha1_size_t *ss, ccp path1, ccp path2 ) +{ + DASSERT(ss); + memset(ss,0,sizeof(*ss)); + + u8 *data = 0; + uint size = 0; + enumError err = OpenReadFILE(path1,path2,false,&data,&size,0,0); + if (err) + memset(ss,0,sizeof(*ss)); + else + { + SHA1(data,size,ss->hash); + ss->size = htonl(size); + } + + FREE(data); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct DistributionInfo_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeDistributionInfo + ( DistributionInfo_t * dinf, bool add_default_param ) +{ + DASSERT(dinf); + memset(dinf,0,sizeof(*dinf)); + InitializeParamField(&dinf->translate); + dinf->translate.free_data = true; + InitializeParamField(&dinf->param); + dinf->param.free_data = true; + + uint slot; + for ( slot = 0; slot < MAX_DISTRIBUTION_ARENA; slot++ ) + { + ParamField_t *pf = dinf->arena + slot; + InitializeParamField(pf); + pf->free_data = true; + } + + for ( slot = 0; slot < MAX_DISTRIBUTION_TRACK; slot++ ) + { + ParamField_t *pf = dinf->track + slot; + InitializeParamField(pf); + pf->free_data = true; + } + + if (add_default_param) + AddParamDistributionInfo(dinf,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetDistributionInfo ( DistributionInfo_t * dinf ) +{ + if (dinf) + { + ResetParamField(&dinf->translate); + ResetParamField(&dinf->param); + + uint slot; + for ( slot = 0; slot < MAX_DISTRIBUTION_ARENA; slot++ ) + ResetParamField(dinf->arena+slot); + for ( slot = 0; slot < MAX_DISTRIBUTION_TRACK; slot++ ) + ResetParamField(dinf->track+slot); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddParamDistributionInfo ( DistributionInfo_t * dinf, bool overwrite ) +{ + DASSERT(dinf); + + char buf[100]; + + //-- UUID + + uuid_buf_t uuid; + bool create_uuid = overwrite; + if (!create_uuid) + { + const ParamFieldItem_t *it = FindParamField(&dinf->param,"UUID"); + if ( !it || !it->data || ScanUUID(uuid,(ccp)it->data) == (ccp)it->data ) + create_uuid = true; + } + + if (create_uuid) + { + CreateTextUUID(buf,sizeof(buf)); + ReplaceParamField(&dinf->param,"UUID",false,0,STRDUP(buf)); + } + + + //-- time stamps + + time_t tim = time(0); + struct tm *tm = localtime(&tim); + strftime(buf,sizeof(buf),"%F %T %z",tm); + + if ( overwrite || !FindParamField(&dinf->param,"FIRST-CREATION") ) + ReplaceParamField(&dinf->param,"FIRST-CREATION",false,0,STRDUP(buf)); + ReplaceParamField(&dinf->param,"LAST-UPDATE",false,0,STRDUP(buf)); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// slot translation /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSlot ( uint *res_slot, ccp source, bool need_point ) +{ + DASSERT(res_slot); + DASSERT(source); + *res_slot = 0; + + char *end; + uint slot = strtoul(source,&end,10); + if ( !need_point + && slot >= MIN_DISTRIBUTION_SLOT + && slot < MAX_DISTRIBUTION_TRACK + && (uchar)*end <= ' ' + && ( end-source == 2 || end-source == 3 )) + { + *res_slot = slot; + return end; + } + + slot *= 10; + if ( slot < MIN_DISTRIBUTION_SLOT || slot >= MAX_DISTRIBUTION_TRACK || *end != '.' ) + return (char*)source; + + uint track = strtoul(end+1,&end,10); + slot += track; + if ( track < 1 || track > 9 || slot >= MAX_DISTRIBUTION_TRACK ) + return (char*)source; + + *res_slot = slot; + while ( *end == ' ' || *end == '\t' ) + end++; + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +void NormalizeSlotTranslation ( char *buf, uint bufsize, ccp name, + char ** p_first_para, char ** p_first_brack ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + + if (!name) + name = ""; + + char *dest = buf, *buf_end = buf + bufsize - 4; + char *first_para = 0; + char *first_brack = 0; + + uint have_space = 0; + while ( *name && dest < buf_end ) + { + int ch = (uchar)*name++; + switch(ch) + { + case ' ': + case '_': + case '-': + case '+': + have_space++; + break; + + case '\'': + break; // ignore + + case '[': + if (!first_brack) + first_brack = dest; + goto bracket; + + case '(': + if (!first_para) + first_para = dest; + // fall through + + case '{': + bracket: + ch = '('; + // fall through + + case '.': + if (have_space) + { + have_space = 0; + *dest++ = ' '; + } + *dest++ = ch; + break; + + case '}': + case ')': + case ']': + *dest++ = ')'; + break; + + default: + ch = tolower(ch); + if ( ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' ) + { + if (have_space) + { + have_space = 0; + *dest++ = ' '; + } + *dest++ = ch; + } + break; + } + } + + if ( dest > buf ) + { + if ( dest > buf+4 && !memcmp(dest-4,".szs",4) || !memcmp(dest-4,".wbz",4) ) + dest -= 4; + if ( dest > buf+2 && !memcmp(dest-2,"_d",2) ) + dest -= 2; + *dest = 0; + + if ( first_brack) + { + if ( first_brack >= dest ) + first_brack = 0; + else + dest = first_brack; + } + + if ( first_para >= dest ) + first_para = 0; + } + else + first_para = first_brack = 0; + + if (p_first_para) + *p_first_para = first_para; + if (p_first_brack) + *p_first_brack = first_brack; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DefineSlotTranslation + ( ParamField_t *translate, bool is_arena, uint slot, ccp name ) +{ + DASSERT(translate); + if (!name) + return 0; + + if (is_arena) + { + if ( slot >= MAX_DISTRIBUTION_ARENA ) + slot = 0; + else + slot += DISTRIBUTION_ARENA_DELTA; + } + else + { + if ( slot >= MAX_DISTRIBUTION_TRACK ) + slot = 0; + } + + char buf[1000]; + char *first_para = 0, *first_brack = 0; + NormalizeSlotTranslation(buf,sizeof(buf),name,&first_para,&first_brack); + + uint count = 0; + if (*buf) + { + ReplaceParamField(translate,buf,false,slot,0); + count++; + + if (first_brack) + { + *first_brack = 0; + ReplaceParamField(translate,buf,false,slot,0); + count++; + } + + if (first_para) + { + *first_para = 0; + ReplaceParamField(translate,buf,false,slot,0); + count++; + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef EARLY_NUM_SLOT +#define EARLY_NUM_SLOT 1 + +int FindSlotByTranslation ( const ParamField_t *translate, ccp fname, ccp sha1 ) +{ + DASSERT(translate); + ccp slash = strrchr(fname,'/'); + if (slash) + fname = slash+1; + + #if EARLY_NUM_SLOT + uint slot; + ScanSlot(&slot,fname,false); + if (slot) + return slot; + #endif + + char buf[1000]; + + char *first_para = 0, *first_brack = 0; + NormalizeSlotTranslation(buf,sizeof(buf),fname,&first_para,&first_brack); + const ParamFieldItem_t *it = FindParamField(translate,buf); + if ( !it && first_brack ) + { + *first_brack = 0; + it = FindParamField(translate,buf); + } + + if ( !it && first_para ) + { + *first_para = 0; + it = FindParamField(translate,buf); + } + + if ( !it && sha1 ) + it = FindParamField(translate,sha1); + + + #if EARLY_NUM_SLOT + return it ? it->num : 0; + #else + if (it) + return it->num; + + uint slot; + ScanSlot(&slot,fname,false); + return slot; + #endif +} + +#undef EARLY_NUM_SLOT + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ScanSlotTranslation + ( ParamField_t *translate, ParamField_t *param, ccp fname, bool ignore ) +{ + DASSERT(translate); + DASSERT(fname); + PRINT(" - scan translation%s: %s\n", param ? "+param" : "", fname); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,fname,true,ignore,true); + if (err) + return; + + + //--- support for special file formats + + switch((int)szs.fform_arch) + { + case FF_BMG: + if (verbose>1) + printf(" - scan translation [BMG]: %s\n",fname); + ResetSZS(&szs); + return; + } + + + //--- standart text file + + if ( verbose > 1 ) + printf("Scan translation%s[TEXT/%s]: %s\n", + param ? "+param" : "", + GetExtFF(szs.fform_file,szs.fform_arch), fname ); + else if ( verbose >= 0 ) + printf("Scan %s\n",fname); + + char *eol = (char*)szs.data; + char *end = eol + szs.size; + + for(;;) + { + while ( eol < end && (uchar)*eol <= ' ' ) + eol++; + if ( eol == end ) + break; + + char *src = eol; + while ( eol < end && *eol != 0 && *eol != '\r' && *eol != '\n' ) + eol++; + *eol = 0; + + if ( *src == '#' || *src == '!' ) + continue; + + + //--- manage parameters + + if ( *src == '@' ) + { + if (!param) + continue; + + char name_buf[100], *name = name_buf; + src++; + + for(;;) + { + const char ch = *src; + if ( !isalnum(ch) && ch != '_' && ch != '-' && ch != '.' ) + break; + src++; + if ( name < name_buf + sizeof(name_buf) - 1 ) + *name++ = toupper(ch); + } + + while ( src < eol && (uchar)*src <= ' ' ) + src++; + if ( src < eol && *src == '=' ) + { + src++; + while ( src < eol && (uchar)*src <= ' ' ) + src++; + } + + if ( name > name_buf ) + { + //PRINT(" %.*s = |%.*s|\n",(int)(name-name_buf),name_buf,(int)(eol-src),src); + ReplaceParamField( param, MEMDUP(name_buf,name-name_buf), + true, 0, MEMDUP(src,eol-src) ); + } + continue; + } + + //PRINTF("LINE: |%.*s|\n",(int)(eol-src),src); + + + //--- skip "()" | detect SHA1 checksum + + char *sha1 = 0; + if ( *src == '(' ) + { + while ( src < eol && *src != ')' ) + src++; + if ( src++ == eol ) + continue; + } + else + { + // detect sha1 checksum + + char *x = src; + while ( x < eol && isalnum((int)*x) ) + x++; + if ( x < eol && x - src == 40 ) + { + sha1 = src; + src = x; + } + } + + while ( src < eol && (uchar)*src <= ' ' ) + src++; + + const bool is_arena = *src == 'a' || *src == 'A'; + if (is_arena) + src++; + + if (!isdigit((int)*src)) + continue; + + uint slot; + src = ScanSlot(&slot,src,true); // ??? + if (!slot) + continue; + + char *tab = strrchr(src,'\t'); + if (tab) + src = tab; + while ( src < eol && (uchar)*src <= ' ' ) + src++; + + DefineSlotTranslation(translate,is_arena,slot,src); + if (sha1) + { + sha1[40] = 0; + DefineSlotTranslation(translate,is_arena,slot,sha1); + } + } + + ResetSZS(&szs); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SZS cache support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp szs_cache_dir = 0; +ParamField_t szs_cache = {0}; + +static bool szs_cache_loaded = false; +static bool szs_cache_dirty = false; +static u64 szs_cache_last_scan = 0; + +/////////////////////////////////////////////////////////////////////////////// + +void SetupSZSCache ( ccp dir_name, bool use_dirname ) +{ + PRINT("SetupSZSCache(%s,%d)\n",dir_name,use_dirname); + + char buf[PATH_MAX]; + if ( use_dirname && dir_name && !IsDirectory(dir_name,false) ) + { + StringCopyS(buf,sizeof(buf),dir_name); + if (!opt_cname) + opt_cname = STRDUP(basename(buf)); + dir_name = dirname(buf); + PRINT(" > new dir_name: %s\n",dir_name); + } + + if ( dir_name && szs_cache_dir && !strcmp(dir_name,szs_cache_dir) ) + return; + + FreeString(szs_cache_dir); + szs_cache_dir = IsDirectory(dir_name,false) ? STRDUP(dir_name) : 0; + ResetParamField(&szs_cache); + szs_cache.free_data = true; + szs_cache_loaded = false; + PRINT("SZS_CACHE_DIR = %s\n",szs_cache_dir); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScanSZSCache ( ccp dir_name, bool purge ) +{ + SetupSZSCache(dir_name,false); + if (!szs_cache_dir) + return; + szs_cache_loaded = szs_cache_dirty = true; + szs_cache_last_scan = time(0); + + DIR * dir = opendir(dir_name); + if (dir) + { + for(;;) + { + struct dirent * dent = readdir(dir); + if (!dent) + break; + + if ( dent->d_name && *dent->d_name != '.' ) + { + noPRINT("> %s\n",dent->d_name); + StoreSZSCache(dent->d_name,0,purge,0); + } + } + closedir(dir); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadSZSCache() +{ + if ( szs_cache_loaded || !szs_cache_dir ) + return ERR_OK; + szs_cache_loaded = true; + szs_cache_last_scan = 0; + + char buf[PATH_MAX]; + ccp path = PathCatPP(buf,sizeof(buf),szs_cache_dir,SZS_CACHE_FNAME); + PRINT("LoadSZSCache() %s\n",path); + + File_t F; + enumError err = OpenFile(&F,true,path,FM_SILENT,0,0); + if (err) + return err; + + while (fgets(buf,sizeof(buf)-1,F.f)) + { + char *ptr = buf; + while ( *(uchar*)ptr <= ' ' ) + ptr++; + if ( *ptr == '#' || *ptr == '!' ) + continue; + + if ( *ptr == '@' ) + { + char name_buf[100], *name = name_buf; + ptr++; + for(;;) + { + const char ch = *ptr; + if ( !isalnum(ch) && ch != '_' && ch != '-' && ch != '.' ) + break; + ptr++; + if ( name < name_buf + sizeof(name_buf) - 1 ) + *name++ = toupper(ch); + } + + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + if ( *ptr == '=' ) + { + ptr++; + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + } + + if ( name > name_buf ) + { + *name = 0; + if (!strcmp(name_buf,"LAST-CACHE-SCAN")) + szs_cache_last_scan = strtoul(ptr,0,10); + } + continue; + } + + uint len = strlen(ptr) - 1; + while ( len > 0 && (uchar)ptr[len] <= ' ' ) + len--; + ptr[len+1] = 0; + + if ( len <= CHECKSUM_DB_SIZE || ptr[CHECKSUM_DB_SIZE] != ' ' ) + continue; + + char *fname = ptr + CHECKSUM_DB_SIZE; + *fname++ = 0; + while ( *(uchar*)fname <= ' ' ) + fname++; + if (!*fname) + continue; + StoreSZSCache(fname,ptr,false,0); + } + + CloseFile(&F,0); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_szs_cache ( const void * va, const void * vb ) +{ + DASSERT(va); + DASSERT(vb); + const ParamFieldItem_t *a = *(ParamFieldItem_t**)va; + const ParamFieldItem_t *b = *(ParamFieldItem_t**)vb; + DASSERT(a); + DASSERT(b); + return strcasecmp((ccp)a->data,(ccp)b->data); +} + +//----------------------------------------------------------------------------- + +enumError SaveSZSCache() +{ + if ( !szs_cache_dir || !szs_cache_dirty ) + return ERR_NOTHING_TO_DO; + szs_cache_dirty = false; + + char path_buf[PATH_MAX]; + ccp path = PathCatPP(path_buf,sizeof(path_buf),szs_cache_dir,SZS_CACHE_FNAME); + PRINT("SaveSZSCache() N=%u, %s\n",szs_cache.used,path); + + File_t F; + enumError err = CreateFile(&F,true,path,FM_REMOVE); + if (err) + return err; + + char tbuf[50]; + if (szs_cache_last_scan) + { + time_t tim = szs_cache_last_scan; + struct tm *tm = localtime(&tim); + strftime(tbuf,sizeof(tbuf),"%F %T %z",tm); + } + else + StringCopyS(tbuf,sizeof(tbuf),"-"); + + fprintf(F.f,"#SZS-CACHE\n" + "# %u file%s cached\n" + "\n" + "@WSZST-VERSION = %s\n" + "@WSZST-REVISION = %u\n" + "@LAST-CACHE-SCAN = %llu = %s\n" + "\n" + ,szs_cache.used ,szs_cache.used==1 ? "" : "s" + ,VERSION + ,REVISION_NUM + ,szs_cache_last_scan,tbuf + ); + + if ( szs_cache.used ) + { + ParamFieldItem_t **list = CALLOC(szs_cache.used,sizeof(*list)); + + uint i; + for ( i = 0; i < szs_cache.used; i++ ) + list[i] = szs_cache.field+i; + + if ( szs_cache.used > 1 ) + qsort( list, szs_cache.used, sizeof(*list), cmp_szs_cache ); + + for ( i = 0; i < szs_cache.used; i++ ) + { + const ParamFieldItem_t *it = list[i]; + fprintf(F.f,"%s %s\n",it->key,(ccp)it->data); + } + + FREE(list); + } + + CloseFile(&F,0); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// only basename(fname) is used. +// if checksum==0: load file and calc checksum! + +ParamFieldItem_t * StoreSZSCache +( + // return NULL or pointer to cache item + + ccp fname, // NULL or filename, only basename(fname) is used + ccp checksum, // DB checksum; if NULL: load file and calc checksum + bool rename_file, // true: rename existing file + bool *r_exists // not NULL: store a status here + // => true: file result->data exists +) +{ + if (r_exists) + *r_exists = false; + if (!szs_cache_loaded) + LoadSZSCache(); + + if (fname) + { + ccp slash = strrchr(fname,'/'); + if (slash) + fname = slash+1; + slash = strrchr(fname,'\\'); + if (slash) + fname = slash+1; + } + if (!*fname) + fname = 0; + + PRINT("StoreSZSCache(%s,%s,%d,)\n",fname,checksum,rename_file); + + ccp path = 0; + char path_buf[PATH_MAX], oldpath_buf[PATH_MAX], checksum_buf[CHECKSUM_DB_SIZE+1]; + + if (!checksum) + { + if (!fname) + return 0; + + path = PathCatPP(path_buf,sizeof(path_buf),szs_cache_dir,fname); + noPRINT(">> %s\n",path); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,path,true,true,true); + if ( err > ERR_WARNING || err == ERR_NOT_EXISTS || !IsCompressedFF(szs.fform_file) ) + { + ResetSZS(&szs); + return 0; + } + + CreateSSChecksumDBBySZS(checksum_buf,sizeof(checksum_buf),&szs); + ResetSZS(&szs); + checksum = checksum_buf; + } + + bool found; + ParamFieldItem_t *it = InsertParamFieldEx(&szs_cache,checksum,false,0,&found); + if (found) + { + DASSERT(it); + ccp oldpath = PathCatPP(oldpath_buf,sizeof(oldpath_buf),szs_cache_dir,(ccp)it->data); + + struct stat st; + if ( stat(oldpath,&st) || !S_ISREG(st.st_mode) ) + return it; + + if (r_exists) + *r_exists = true; + + if ( !rename_file || !fname || !strcmp(fname,(ccp)it->data) ) + return it; + + if (!path) + path = PathCatPP(path_buf,sizeof(path_buf),szs_cache_dir,fname); + PRINT("RENAME: %s -> %s\n",oldpath,path); + if (rename(oldpath,path)) + return it; + + FreeString(it->data); + it->data = NULL; + } + + if (it) + { + PRINT("USE: %s\n",fname); + it->data = STRDUP(fname); + } + szs_cache_dirty = true; + return it; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-checksum.h b/project/src/lib-checksum.h new file mode 100755 index 0000000..088b2a1 --- /dev/null +++ b/project/src/lib-checksum.h @@ -0,0 +1,196 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_CHECKSUM_H +#define SZS_LIB_CHECKSUM_H 1 + +#define _GNU_SOURCE 1 + +#include "dclib-types.h" +#include "lib-szs.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct sha1_size_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sha1_size_t]] + +#define CHECKSUM_DB_SIZE 32 + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct sha1_size_t +{ + sha1_hash_t hash; // SHA1 checksum (binary) + u32 size; // size of date (big endian) +} +__attribute__ ((packed)) sha1_size_t; + +/////////////////////////////////////////////////////////////////////////////// + +// bufsize should be >40 +void CreateSSChecksum ( char *buf, uint bufsize, const sha1_size_t *ss ); +void CreateSSChecksumBySZS ( char *buf, uint bufsize, const szs_file_t *szs ); + +// normed DB coding (independent of options) +void CreateSSChecksumDB ( char *buf, uint bufsize, const sha1_size_t *ss ); +void CreateSSChecksumDBByData ( char *buf, uint bufsize, cvp data, uint size ); +void CreateSSChecksumDBBySZS ( char *buf, uint bufsize, const szs_file_t *szs ); + +enumError GetSSByFile ( sha1_size_t *ss, ccp path1, ccp path2 ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct DistributionInfo_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define DISTRIB_MAGIC8 "#DISTRIB" +#define DISTRIB_MAGIC8_NUM 0x2344495354524942ull + +#define DISTRIBUTION_FILE_VERSION 1 +#define MIN_DISTRIBUTION_SLOT 10 // included +#define MAX_DISTRIBUTION_TRACK 10000 // excluded +#define MAX_DISTRIBUTION_ARENA 1000 // excluded +#define DISTRIBUTION_ARENA_DELTA 100000 + +#define DEFAULT_DISTIBUTION_FNAME "distribution.txt" + +//----------------------------------------------------------------------------- + +static inline bool IsValidDistribArena ( uint slot ) +{ + return slot == DISTRIBUTION_ARENA_DELTA + || slot >= DISTRIBUTION_ARENA_DELTA+MIN_DISTRIBUTION_SLOT + && slot < DISTRIBUTION_ARENA_DELTA+MAX_DISTRIBUTION_ARENA; +} + +static inline bool IsValidDistribTrack ( uint slot ) +{ + return !slot + || slot >= MIN_DISTRIBUTION_SLOT && slot < MAX_DISTRIBUTION_TRACK; +} + +static inline bool IsValidDistribSlot ( uint slot ) +{ + return !slot + || slot >= MIN_DISTRIBUTION_SLOT + && slot < MAX_DISTRIBUTION_TRACK + || slot == DISTRIBUTION_ARENA_DELTA + || slot >= DISTRIBUTION_ARENA_DELTA+MIN_DISTRIBUTION_SLOT + && slot < DISTRIBUTION_ARENA_DELTA+MAX_DISTRIBUTION_ARENA; +} + +//----------------------------------------------------------------------------- +// [[DistributionInfo_t]] + +typedef struct DistributionInfo_t +{ + ParamField_t translate; + ParamField_t param; + ParamField_t arena[MAX_DISTRIBUTION_ARENA]; + ParamField_t track[MAX_DISTRIBUTION_TRACK]; +} +DistributionInfo_t; + +//----------------------------------------------------------------------------- + +void InitializeDistributionInfo + ( DistributionInfo_t * dinf, bool add_default_param ); +void ResetDistributionInfo ( DistributionInfo_t *dinf ); +void AddParamDistributionInfo ( DistributionInfo_t * dinf, bool overwrite ); + +enumError ScanDistribFile + ( DistributionInfo_t *dinf, ccp fname, int ignore, bool assume_arena ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// slot translation /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanSlot ( uint *res_slot, ccp source, bool need_point ); + +void NormalizeSlotTranslation ( char *buf, uint bufsize, ccp name, + char ** p_first_para, char ** p_first_brack ); + +uint DefineSlotTranslation + ( ParamField_t *translate, bool is_arena, uint slot, ccp name ); + +// 'sha1' can be NULL +int FindSlotByTranslation ( const ParamField_t *translate, ccp fname, ccp sha1 ); + +// 'param' not NULL: Scan "@PARAM = VALUE" lines and store result. +void ScanSlotTranslation + ( ParamField_t *translate, ParamField_t *param, ccp fname, bool ignore ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs cache support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define SZS_CACHE_FNAME "cache-content.txt" + +extern ccp szs_cache_dir; +extern ParamField_t szs_cache; + +/////////////////////////////////////////////////////////////////////////////// + +static inline bool IsSZSCacheEnabled() + { return szs_cache_dir != 0; } + +void SetupSZSCache ( ccp dir_name, bool use_dirname ); + +void ScanSZSCache ( ccp dir_name, bool purge ); +enumError LoadSZSCache(); +enumError SaveSZSCache(); + +ParamFieldItem_t * StoreSZSCache +( + // return NULL or pointer to cache item + + ccp fname, // NULL or filename, only basename(fname) is used + ccp checksum, // DB checksum; if NULL: load file and calc checksum + bool rename_file, // true: rename existing file + bool *r_exists // not NULL: store a status here + // => true: file result->data exists +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_CHECKSUM_H diff --git a/project/src/lib-common.c b/project/src/lib-common.c new file mode 100644 index 0000000..60d1184 --- /dev/null +++ b/project/src/lib-common.c @@ -0,0 +1,1434 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include + +#include "dclib-xdump.h" +#include "lib-szs.h" +#include "lib-common.h" +#include "lib-numeric.h" +#include "lib-bzip2.h" + +#include "common.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// original itemslot.bin /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 itemslot_bin_bz2[] = // ItemSlot.bin +{ + // decompressed size: 0x713 = 1811 bytes + 0x00,0x00,0x07,0x13, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xe5,0x95, 0x32,0x15,0x00,0x01, + 0xc8,0x7f,0x0c,0x7f, 0x7f,0xce,0xa1,0x08, 0xc6,0x10,0x84,0x21, 0x08,0x40,0x00,0x04, + 0x00,0x00,0x01,0x00, 0x40,0x40,0x02,0x98, 0xea,0x3b,0x40,0xe0, 0x25,0x54,0xfc,0xa7, + 0xa9,0xb4,0x53,0x6d, 0x4f,0x53,0x4a,0x7a, 0x80,0xd3,0x40,0xda, 0x82,0x54,0xf1,0x12, + 0x23,0x10,0x84,0xd3, 0x1a,0x11,0x90,0x09, 0x34,0xa8,0xd4,0xd4, 0x6c,0x90,0x01,0x88, + 0x01,0xa0,0x90,0x89, 0x11,0xa8,0xc8,0x0f, 0x53,0x13,0x13,0x40, 0xd8,0xe3,0xed,0x4d, + 0x64,0x4e,0x8d,0x79, 0x1f,0xd7,0x24,0xe7, 0x73,0xe7,0x9f,0xc2, 0x3c,0x70,0x6c,0xcc, + 0xdb,0x66,0xa9,0xbc, 0x4c,0xd1,0xe5,0x4d, 0xf8,0xa1,0x9c,0x78, 0x54,0x4a,0x0a,0xb2, + 0x83,0x37,0x23,0x5d, 0xa9,0xd3,0x29,0x48, 0x49,0x81,0x82,0x27, 0x62,0x1b,0x75,0x50, + 0x38,0x1a,0x94,0x09, 0xa3,0x24,0x26,0x86, 0xf9,0x6b,0x0d,0x77, 0x50,0xcb,0x28,0x92, + 0x98,0x1f,0x2a,0x2e, 0x5d,0x16,0x32,0xa4, 0x03,0x4d,0x20,0x02, 0xa4,0xc0,0x4d,0x80, + 0xdb,0x66,0x04,0x30, 0xc3,0x44,0x03,0x40, 0xc5,0x20,0x88,0x7e, 0x7e,0xde,0xb6,0x66, + 0x82,0x09,0xa0,0x37, 0x61,0x15,0x0d,0x4a, 0x8a,0x96,0x70,0x54, 0x9a,0x00,0x67,0x68, + 0x21,0x41,0xdc,0x4e, 0x10,0x6f,0x0d,0x8f, 0xce,0xcc,0xae,0x16, 0x91,0x1a,0x30,0x35, + 0xa0,0xf0,0xc2,0x91, 0x81,0xe4,0xd6,0xa3, 0x0c,0xd8,0x6b,0x55, 0x44,0xc1,0x11,0x86, + 0x5e,0xa7,0x0b,0x77, 0x06,0x96,0xc3,0x24, 0x31,0xd3,0x80,0xd3, 0x5b,0x63,0x8b,0x31, + 0xad,0x1e,0x7f,0x26, 0x60,0x4a,0xcb,0x25, 0x55,0x0a,0x18,0x91, 0x31,0x0f,0x19,0xb1, + 0xc3,0x14,0x88,0x52, 0x49,0xc2,0x99,0x65, 0x42,0xa9,0x3b,0xe2, 0xdb,0x04,0xc5,0x16, + 0x14,0xcc,0xa1,0x83, 0x30,0xd5,0x36,0x93, 0x2a,0x52,0x42,0xc3, 0x11,0xcd,0x16,0xe1, + 0x54,0xc8,0x23,0x0d, 0x25,0x7b,0x67,0x7f, 0x6b,0x1e,0x8c,0xd0, 0x47,0x7c,0x90,0x81, + 0xc6,0xe7,0x92,0x0f, 0x37,0xe2,0x9f,0x5b, 0xc3,0x5d,0x49,0x8c, 0x35,0x95,0xda,0x6a, + 0x4b,0xb7,0x99,0x0c, 0x46,0x0c,0x49,0xa1, 0x94,0x30,0x60,0x93, 0x17,0x29,0x43,0x38, + 0x29,0xa0,0x42,0x17, 0x6f,0x69,0x12,0xe2, 0xde,0xa1,0x1f,0x7f, 0x24,0xf5,0xab,0xf9, + 0x81,0xe1,0x84,0x23, 0xa7,0x30,0xe0,0x3a, 0x4d,0x91,0xae,0x3a, 0xc3,0xaa,0x16,0x84, + 0xc3,0x2c,0x73,0x47, 0x58,0xb2,0xaa,0x31, 0x5b,0x59,0xa2,0x1b, 0x6e,0xd2,0x5c,0xac, + 0x96,0x7d,0xd7,0xa7, 0xaf,0xf3,0x44,0xdf, 0x1b,0x1c,0x69,0xfb, 0xae,0x8f,0x7d,0xf3, + 0x6b,0x52,0x63,0x22, 0xe1,0x65,0x76,0x57, 0xc8,0xda,0x06,0xb1, 0x72,0x13,0x3c,0xde, + 0x0d,0xe8,0xb6,0x34, 0x6d,0xce,0x9e,0x65, 0x9c,0x5f,0x1e,0xa9, 0x04,0x11,0x0e,0xb6, + 0x52,0x06,0x14,0x46, 0x46,0x18,0x8b,0xaf, 0x45,0x55,0x51,0x5c, 0x78,0x4c,0x73,0xf9, + 0x1c,0x2e,0xdb,0x41, 0x55,0x12,0x64,0xea, 0x30,0xd9,0x26,0xb3, 0x2b,0x96,0xa4,0xe1, + 0xb8,0xa6,0x98,0x9b, 0x5d,0x2e,0x2a,0x5c, 0x71,0xae,0x93,0xfb, 0x4a,0x93,0x67,0xc8, + 0x7c,0x09,0xb1,0xa5, 0x63,0x47,0x35,0x6c, 0x3e,0x8a,0x6f,0x9c, 0x57,0x3a,0x23,0x3b, + 0x3a,0x99,0x74,0xa9, 0xb9,0x6c,0xe8,0xe8, 0xb4,0xb5,0x8e,0xb9, 0xd3,0xb3,0x0c,0x50, + 0x53,0x83,0xeb,0x7e, 0xc7,0x3e,0x3e,0x7d, 0xf9,0xff,0x17,0x72, 0x45,0x38,0x50,0x90, + 0xe5,0x95,0x32,0x15, + // 0x224 = 548 data bytes (30%) +}; + +static BZ2Manager_t itemslot_bin_mgr + = { itemslot_bin_bz2, sizeof(itemslot_bin_bz2), 0, 0 }; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// original minigame.kmg /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 minigame_kmg_bz2[] = // minigame.kmg +{ + // decompressed size: 0x718 = 1816 bytes + 0x00,0x00,0x07,0x18, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xf6,0xc1, 0x8d,0x99,0x00,0x03, + 0x1a,0xfe,0x8c,0xef, 0xfc,0xc7,0x65,0xe0, 0x7a,0x92,0xa0,0x88, 0x8a,0x10,0x00,0x00, + 0x40,0x04,0x00,0x00, 0x04,0x00,0x00,0xb0, 0x01,0x36,0x00,0x63, 0x46,0x8d,0x00,0xc9, + 0x90,0xd1,0x88,0xd3, 0x40,0xd3,0x02,0x1a, 0x53,0x40,0x34,0x00, 0x00,0x00,0x00,0x22, + 0x48,0x4a,0x7a,0x6a, 0x9e,0x88,0x36,0x9a, 0x4f,0xd2,0x99,0x31, 0x3f,0x50,0xd4,0xcd, + 0x3e,0x1e,0x09,0xb9, 0x66,0xa5,0x05,0xb8, 0xf2,0xce,0x19,0x49, 0x14,0xa0,0xd8,0x33, + 0x40,0x0c,0xc4,0x04, 0x68,0x44,0x71,0x03, 0x24,0x6d,0x86,0x23, 0x65,0x36,0x8b,0xf9, + 0x08,0xee,0xca,0x8d, 0xfa,0x5b,0x38,0x5c, 0x99,0x64,0xad,0x92, 0xaa,0xa4,0x18,0x15, + 0x07,0x1b,0xdb,0x92, 0xd2,0x5e,0x9a,0x83, 0x63,0x08,0x95,0x09, 0x09,0x00,0xb0,0x55, + 0x1a,0x0d,0xab,0x34, 0x3e,0x6e,0x27,0x8d, 0xbb,0xe7,0x2d,0xe3, 0x9e,0xe7,0x88,0xe2, + 0x13,0xcb,0x3d,0xaf, 0xa0,0x9a,0x89,0xc0, 0x6c,0x5a,0x49,0x24, 0xa4,0xe4,0x25,0xc4, + 0x89,0x58,0x43,0xb3, 0x2b,0x88,0xf6,0x01, 0x5d,0xbd,0xfc,0xfd, 0xbd,0xce,0x77,0xe1, + 0x9f,0x56,0xff,0x20, 0xfb,0x7d,0xfc,0x68, 0x4f,0x31,0x33,0x1a, 0xc7,0x0d,0x48,0xa3, + 0x81,0x20,0x5c,0x32, 0xaa,0x84,0x12,0x23, 0xa0,0x16,0xff,0x15, 0xb7,0x11,0xc5,0xe0, + 0x94,0x09,0xb2,0x7a, 0xa6,0xad,0xf2,0xb1, 0x7f,0x68,0x41,0xce, 0x16,0x3d,0xc2,0xab, + 0x98,0x7f,0x05,0x74, 0x13,0x6d,0x5c,0x82, 0x27,0x14,0x32,0xb1, 0xa8,0xee,0x31,0x46, + 0xae,0x39,0x37,0xe4, 0x38,0xaf,0x01,0x14, 0x7a,0x7e,0xfd,0x50, 0x03,0xae,0x08,0x02, + 0x7a,0x40,0x54,0x0f, 0x98,0x07,0x4e,0x9d, 0xbd,0x57,0x05,0x47, 0xe3,0xa5,0x7d,0x0b, + 0xb9,0x22,0x9c,0x28, 0x48,0x7b,0x60,0xc6, 0xcc,0x80, + // 0x12a = 298 data bytes (17%) +}; + +static BZ2Manager_t minigame_kmg_mgr + = { minigame_kmg_bz2, sizeof(minigame_kmg_bz2), 0, 0 }; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// original GeoHit data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 objflow_bin_bz2[] = // ObjFlow.bin +{ + // decompressed len: 0x951a = 38170 bytes + 0x00,0x00,0x95,0x1a, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xae,0x04, 0xda,0x11,0x00,0x09, + 0x4b,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe0, 0x14,0x79,0x4b,0x00, 0x1f,0x3d,0x07,0x5b, 0x3e,0x17,0xcf,0x67, + 0x8d,0xd7,0xdb,0x50, 0x01,0xda,0xe7,0x33, 0xbd,0xd1,0xad,0xec, 0x17,0x8e,0x00,0x06, + 0xf7,0x70,0x7a,0x03, 0xd0,0x3a,0xa0,0x1d, 0xb0,0xd0,0x3a,0x70, 0x07,0x0d,0x11,0x21, + 0x3c,0x89,0xe4,0x0a, 0x61,0x35,0x3c,0x69, 0x32,0x9a,0x6d,0x31, 0x4f,0x6a,0x27,0x89, + 0x3f,0x2a,0x7a,0x99, 0xa8,0xd0,0x34,0x6d, 0x4f,0x48,0xd0,0x6d, 0x46,0x09,0xa3,0xd2, + 0x6d,0x31,0xa2,0x34, 0x9e,0x51,0xe8,0x99, 0x3d,0x13,0xd4,0xd3, 0xf5,0x06,0x44,0xc4, + 0xd3,0x35,0x00,0xd0, 0x7a,0x80,0xd0,0x1a, 0x68,0xd1,0x90,0x34, 0x1a,0x1e,0xa3,0x62, + 0x40,0x00,0x34,0x06, 0x8c,0xd4,0xd8,0x26, 0xf2,0xa6,0x50,0x6a, 0x68,0x23,0x41,0x90, + 0x99,0x34,0x34,0x26, 0x51,0xb1,0x53,0xf4, 0x88,0x00,0xd0,0xd0, 0xc8,0x06,0x99,0x34, + 0x00,0x00,0x00,0x00, 0x03,0xd4,0x34,0x01, 0xa0,0x00,0x1b,0x50, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x01,0xa0, 0x00,0x00,0x00,0xd0, 0x00,0x7a,0x80,0x03, 0x4d,0x4c,0x81,0x12, + 0x64,0xf5,0x28,0x1b, 0x35,0x40,0x32,0x34, 0x0d,0x01,0x91,0xa1, 0xa0,0x1e,0xa0,0x68, + 0xf4,0x40,0x06,0x80, 0x32,0x34,0xd0,0x34, 0x00,0x64,0xd0,0x00, 0x00,0x69,0x90,0x00, + 0x0d,0x00,0x00,0xd0, 0x19,0x00,0x00,0x68, 0x01,0x89,0xea,0x68, 0xd3,0x20,0x01,0x91, + 0xa6,0x80,0x01,0x29, 0xe9,0x48,0x48,0x9a, 0x89,0xed,0x51,0xbd, 0x22,0x63,0x53,0x1a, + 0x9e,0xa7,0xea,0x4d, 0x30,0x26,0x23,0xd1, 0x3d,0x4f,0x44,0x64, 0xf5,0x1b,0x44,0xc0, + 0x86,0x10,0xc4,0x61, 0x30,0x83,0x26,0x4c, 0x08,0x69,0x9a,0x13, 0x23,0x26,0x46,0x4c, + 0x99,0x34,0xc8,0x34, 0xc8,0x0c,0x43,0x08, 0x60,0x4c,0x4d,0x18, 0x04,0x06,0x09,0x80, + 0x9a,0x32,0x62,0x64, 0x61,0x03,0x46,0x81, 0x49,0x14,0xd3,0x07, 0xa9,0x0c,0x0f,0x52, + 0x61,0x0c,0x04,0xc9, 0x84,0xc4,0xc0,0x20, 0x62,0x69,0x80,0x13, 0x04,0xc0,0x9a,0x60, + 0x00,0x00,0x46,0x4d, 0x03,0x46,0x04,0x60, 0x00,0x01,0x30,0xd0, 0x00,0x02,0x31,0x30, + 0xd0,0x04,0xc0,0x8c, 0x00,0x01,0x32,0x30, 0x04,0x1a,0x00,0x12, 0x24,0x40,0x46,0x80, + 0x9a,0x00,0x09,0xa3, 0x4a,0x7b,0x46,0xa3, 0x01,0x4c,0xc2,0x4c, 0x34,0xc8,0x89,0xfa, + 0x0a,0x7e,0xa9,0xea, 0x6f,0x49,0x84,0x9e, 0x9a,0x80,0x3d,0x35, 0x00,0x03,0x20,0x00, + 0x00,0x32,0x0c,0x26, 0x9a,0x1e,0xa0,0x1a, 0x0d,0x00,0x06,0x9a, 0x03,0x4d,0x00,0x1e, + 0xa7,0xa8,0x7a,0x83, 0x40,0x3d,0x40,0x00, 0xf5,0x1b,0x4c,0xa7, 0xea,0x97,0x77,0x59, + 0x3b,0xce,0x28,0x0b, 0x9c,0x6f,0xd7,0x85, 0x0d,0x20,0x8a,0xb8, 0x88,0xfa,0xd0,0x5d, + 0x64,0x44,0x17,0xba, 0x41,0xc5,0x0d,0xa5, 0x80,0x0a,0x24,0xab, 0xa0,0x41,0x24,0x21, + 0x13,0xaa,0x77,0xf2, 0x49,0x77,0x2e,0xe2, 0xce,0x7b,0xf2,0x1b, 0xfd,0x7f,0x1f,0x09, + 0x84,0xa5,0xf7,0x30, 0x7c,0x05,0x28,0x2d, 0x32,0xfc,0xfc,0x0b, 0x9d,0xf7,0xf8,0x6c, + 0xee,0x56,0xc5,0x48, 0xc7,0xf2,0x6d,0x8e, 0x01,0x40,0x00,0x88, 0xd5,0x8f,0xa9,0xb3, + 0xda,0x24,0x8c,0xb5, 0x52,0xfb,0x69,0x7f, 0xd5,0xb3,0x72,0x61, 0x04,0xa1,0xe9,0xc9, + 0xcf,0xd8,0x6e,0xbc, 0x37,0x53,0x7c,0xac, 0xb3,0xa0,0xa0,0xd6, 0x15,0xc3,0x6e,0x0c, + 0x8e,0x02,0xb3,0x1a, 0x3d,0x95,0x64,0x10, 0x30,0x73,0xd5,0x41, 0xa1,0xc8,0xf1,0x00, + 0xd7,0x4a,0x8c,0x0d, 0x81,0x3a,0x8b,0x8d, 0x6d,0x48,0x3c,0x25, 0x69,0xdb,0x4d,0xe4, + 0x49,0x1b,0x2b,0x53, 0x28,0xa4,0x4d,0x2b, 0x86,0xac,0x97,0xfa, 0xf1,0x69,0xfa,0x10, + 0xed,0xdd,0xcf,0x0b, 0x1c,0xe6,0xeb,0x8d, 0x1a,0x70,0x1d,0x0a, 0x87,0x16,0x2d,0xa4, + 0x57,0x71,0x44,0xea, 0xce,0x96,0xb3,0x70, 0xdd,0xb4,0x61,0xc6, 0xb9,0x52,0x2b,0x0a, + 0xac,0xae,0x8d,0xbc, 0xa5,0x35,0x02,0x81, 0x46,0xce,0x50,0xb6, 0xb1,0x0a,0xb6,0xd6, + 0xcd,0x37,0xb2,0x7c, 0x4c,0x53,0x73,0x0c, 0x53,0x9d,0x4d,0x02, 0x02,0x89,0xa4,0x38, + 0x67,0x6a,0xa4,0xe4, 0x5c,0x89,0xea,0x66, 0x14,0xea,0x60,0xcd, 0x07,0xe7,0xae,0xd0, + 0x84,0x11,0x11,0x6e, 0x87,0x39,0x31,0x64, 0xf2,0x13,0x15,0x1a, 0x4d,0x64,0x40,0x4e, + 0xac,0x8a,0x31,0x9a, 0x53,0x1c,0x39,0x4d, 0xbf,0xd9,0x88,0x3b, 0x28,0xa6,0xfc,0xca, + 0xa5,0x4a,0x59,0xe3, 0x6d,0xaa,0x56,0x13, 0x3a,0x91,0x0b,0xef, 0xbf,0x1b,0x90,0x9c, + 0x50,0xb4,0x84,0xda, 0x81,0xe7,0x9e,0x6b, 0x02,0xa4,0x0b,0x2a, 0x95,0x4c,0x5c,0xa4, + 0x09,0x65,0xb7,0x52, 0xac,0x95,0x25,0xa6, 0x40,0xfb,0xb3,0xd3, 0x6c,0xc7,0x96,0x59, + 0x66,0x44,0xb0,0x2e, 0x90,0x77,0x8a,0x53, 0x5d,0x4d,0xaa,0xcf, 0x92,0x52,0xa8,0xe5, + 0x3b,0x98,0x96,0x2c, 0x37,0x32,0x7b,0x9c, 0x3d,0x9b,0xaf,0x2d, 0xc9,0x23,0xc0,0xa2, + 0x3a,0x71,0x94,0xa7, 0xb6,0x4b,0x55,0x3e, 0x3d,0xc9,0xeb,0x57, 0xdf,0x12,0x94,0xa6, + 0xd6,0x56,0xdc,0x09, 0x6d,0xb4,0x2e,0xee, 0x86,0xab,0x98,0x0e, 0xbb,0x71,0xaf,0x36, + 0x8d,0x2b,0x1a,0xe2, 0x2e,0x86,0x96,0x39, 0xa8,0xb7,0x80,0x89, 0x5b,0x25,0xe7,0xef, + 0xa2,0xa5,0xe4,0xb9, 0x85,0x3b,0xed,0x34, 0x94,0xe3,0x18,0x54, 0x83,0xcf,0xb5,0xe6, + 0x1d,0x50,0x1a,0x37, 0x2c,0xba,0x97,0x8a, 0x89,0x31,0x8a,0x0d, 0x52,0xee,0x34,0xa7, + 0x50,0x81,0x91,0x21, 0xc8,0x9e,0x45,0xc5, 0x22,0x68,0x15,0x5d, 0x6a,0xba,0xa5,0x2d, + 0x4d,0xba,0x22,0xa1, 0x2d,0x2b,0xa9,0x90, 0x3d,0x69,0x82,0xc3, 0x12,0x24,0x38,0x86, + 0x61,0x3a,0x2e,0x92, 0x65,0x7a,0x80,0xa7, 0x92,0x49,0x04,0x70, 0x45,0x6e,0x34,0x84, + 0xa5,0x56,0x2d,0x3e, 0xa1,0x4c,0x95,0x12, 0xd5,0x3c,0xcf,0xc2, 0xb9,0x43,0xa1,0x82, + 0x88,0x65,0x9e,0xe9, 0x82,0x27,0x8d,0x89, 0x07,0x80,0xb0,0x20, 0x76,0xcd,0x54,0x55, + 0x02,0x2a,0xb1,0xca, 0x17,0x04,0xb2,0xd5, 0x95,0x4c,0x27,0x1c, 0x70,0xe7,0x3d,0xc8, + 0x69,0xbf,0x04,0xeb, 0x5d,0x29,0xd4,0xd2, 0x02,0xd3,0x13,0x7d, 0xea,0x10,0xf4,0x55, + 0xd5,0x0d,0x99,0xec, 0xaa,0x7b,0xea,0xb5, 0x7e,0x89,0x96,0xa9, 0x80,0x5b,0xf2,0xcd, + 0x60,0xa2,0xab,0x7d, 0xc3,0xa5,0x66,0xaf, 0x6d,0xf4,0xe3,0x2f, 0x56,0xbc,0xfd,0x4a, + 0xc4,0x55,0x9d,0xa5, 0x77,0x0c,0x63,0x48, 0x4d,0x64,0xe0,0x53, 0xaa,0x71,0x1b,0xb7, + 0x51,0x39,0x3d,0x6d, 0x72,0x5f,0xc2,0xb2, 0xa7,0x5c,0x82,0x04, 0xb8,0x49,0x89,0xf5, + 0x9a,0x40,0x81,0x2e, 0x76,0xed,0xef,0x35, 0x8d,0x93,0x17,0x25, 0x89,0x23,0xb6,0x68, + 0x0e,0x3b,0x48,0x38, 0xec,0x48,0x04,0x2c, 0xb6,0x72,0x7b,0x21, 0xf2,0xd9,0x2f,0x79, + 0x15,0xfa,0x46,0x53, 0x2a,0xa9,0x52,0xe8, 0x77,0xa5,0xe1,0xf6, 0x47,0x39,0xd0,0xe8, + 0xc3,0x46,0x1e,0x89, 0xa0,0x88,0xdf,0xcb, 0xa4,0xb0,0xa9,0x8d, 0x28,0x08,0x95,0xb7, + 0xa6,0x00,0xa5,0x0b, 0x55,0xa3,0x68,0x90, 0xc0,0x52,0xbe,0x62, 0x7d,0xa3,0x2e,0xc3, + 0x90,0xe8,0xe3,0x90, 0x3d,0xac,0xa4,0xb1, 0x6f,0x1b,0x96,0x8e, 0xdb,0x5d,0x80,0xf3, + 0x8c,0xec,0x39,0x51, 0x7a,0xca,0x6a,0x8c, 0x78,0x8b,0x20,0x2a, 0x60,0xa8,0xae,0x43, + 0xc9,0x13,0x99,0x30, 0x90,0xd2,0x93,0x13, 0x69,0x36,0x0d,0x8d, 0xce,0x6b,0x0a,0x65, + 0x34,0x74,0x1a,0x11, 0xb7,0x75,0xca,0x39, 0x34,0xdc,0x4e,0x08, 0xc9,0x2b,0x26,0x2d, + 0xc3,0x06,0x12,0xf2, 0x49,0x74,0x57,0x73, 0x1a,0xcc,0x10,0x07, 0x7e,0xd2,0x3d,0x45, + 0x59,0xc2,0x5a,0x6c, 0xf0,0x1c,0x9b,0x68, 0x6e,0x83,0xc1,0xf0, 0xa1,0x1e,0x1b,0x49, + 0x7a,0x96,0x85,0xf0, 0x4d,0x21,0x60,0x62, 0x17,0x4d,0x82,0x47, 0x88,0xc1,0x2f,0x54, + 0xd0,0x0b,0xc5,0x68, 0x4b,0xe0,0xda,0x49, 0x7c,0x23,0x04,0x86, 0x7e,0x39,0x00,0x0d, + 0x01,0x90,0xa7,0xd0, 0xd4,0x54,0xd5,0x44, 0xab,0x81,0x58,0xae, 0x8a,0x6c,0x68,0xeb, + 0x54,0xd2,0x76,0x48, 0x02,0xba,0x12,0x2b, 0xfc,0xf4,0x01,0x61, 0x5c,0x82,0xe7,0x8c, + 0x0d,0x29,0x9e,0xb1, 0x88,0x39,0x4d,0x23, 0x85,0xd4,0x81,0x18, 0xd3,0x47,0x51,0x89, + 0x1d,0x46,0x24,0x1d, 0xa3,0x42,0x3c,0x56, 0x20,0x3d,0x6b,0x54, 0x57,0xc8,0x6a,0xaa, + 0xb8,0x76,0x80,0xe2, 0x1a,0x0d,0x1b,0x5c, 0x4a,0x56,0xcc,0xf1, 0x49,0x5c,0x5f,0xc9, + 0xd9,0x2c,0x7b,0xed, 0xca,0x57,0xbc,0x43, 0x4a,0xf8,0x68,0xee, 0x12,0x35,0x69,0x1d, + 0xcc,0x44,0x08,0xa7, 0x24,0x2c,0x11,0x62, 0x65,0x59,0x56,0x82, 0x22,0x1a,0xcd,0x6b, + 0xa0,0x35,0xda,0xfb, 0x0a,0x75,0x2c,0xf4, 0x1b,0x0a,0xfc,0xd3, 0xc1,0x5e,0xe2,0x26, + 0xc7,0xba,0xd9,0x5a, 0x6c,0xf6,0x9a,0x4d, 0x33,0xfa,0x90,0x20, 0x38,0x49,0xae,0x06, + 0x69,0x72,0xb1,0x30, 0x41,0xc2,0x95,0xd6, 0x3a,0x5d,0x17,0x69, 0xdd,0xea,0x34,0xda, + 0x77,0x20,0x49,0xf6, 0xd5,0xbb,0x55,0xec, 0x75,0x35,0xb8,0xdd, 0x57,0x6e,0xb0,0x4d, + 0xc4,0xf2,0x93,0x80, 0x05,0x5e,0x41,0xfe, 0x69,0x29,0x33,0x30, 0x78,0x63,0xa5,0x25, + 0x60,0x85,0x0c,0xb4, 0x6c,0xbc,0xc4,0xcc, 0xd4,0xdc,0x27,0xaa, 0x72,0x74,0x60,0x1a, + 0x08,0x00,0x49,0x4f, 0x4f,0x90,0x9c,0xa0, 0xa1,0xa2,0xf1,0x24, 0x80,0x6a,0x31,0x40, + 0x40,0x24,0x29,0x26, 0xa9,0x69,0xa9,0xea, 0x2a,0x5f,0x45,0xaa, 0x16,0x5e,0x66,0xae, + 0xb2,0xb6,0xba,0xbe, 0xc3,0xd7,0xec,0xb1, 0xb2,0xb3,0xa2,0xf6, 0xda,0x5a,0xdb,0x5b, + 0x91,0x30,0x0d,0xc7, 0x6c,0x03,0x5a,0xdc, 0xdd,0x5d,0xd9,0x5e, 0x5e,0xdf,0x5d,0xdf, + 0xfb,0xb0,0x30,0x70, 0xb0,0xf1,0x08,0xe2, 0xfb,0xf8,0x46,0x01, 0x80,0x71,0x87,0x04, + 0x61,0x00,0x20,0x04, 0x0d,0xb0,0xe2,0x23, 0xc0,0x38,0x29,0x58, 0xf9,0x19,0x2a,0x59, + 0x42,0x65,0xe6,0x66, 0xe1,0x71,0xb3,0xb9, 0x40,0x14,0x7e,0x7e, 0x82,0xd6,0x89,0x58, + 0x34,0xb3,0xf1,0xf4, 0xf5,0x09,0x42,0x1d, 0x51,0x72,0xf5,0xbe, 0x3a,0xfa,0x05,0x7b, + 0x02,0x7c,0xbe,0x7b, 0x25,0x9b,0x5b,0x1b, 0x64,0xba,0xe2,0x6e, 0x71,0xc3,0xbb,0xb3, + 0xbd,0xbf,0xc0,0x5f, 0xc2,0x9c,0x01,0xdd, 0x1b,0xcb,0xdb,0xeb, 0xbb,0xfc,0x0e,0x46, + 0x0e,0x0d,0x93,0xe0, 0xce,0x79,0x1c,0x8b, 0x4e,0x4e,0x16,0x1f, 0xd3,0x74,0x56,0xf7, + 0x1f,0x3d,0xc0,0xe0, 0xa4,0xee,0x4e,0xe9, 0x34,0xb7,0x2a,0x5d, 0x04,0x03,0xb8,0xc2, + 0x40,0x70,0xaa,0xb3, 0xac,0x8c,0xec,0xaf, 0xc5,0x4b,0x46,0x55, 0xc4,0xae,0xda,0x8a, + 0x01,0x94,0xb2,0xd6, 0xb3,0x9e,0xa7,0x19, 0x56,0x98,0x75,0x67, 0x19,0xc9,0x29,0x9c, + 0xad,0x2c,0x66,0x56, 0x08,0x25,0x4c,0x6a, 0xa5,0xdf,0x6b,0x42, 0x98,0x89,0x7c,0xd5, + 0x49,0x34,0x20,0x5a, 0x50,0x26,0x87,0x41, 0x81,0x7d,0x48,0x07, 0xb0,0x51,0x12,0x08, + 0x43,0x10,0xe2,0x05, 0xaa,0xcf,0xe2,0x62, 0x1b,0x14,0x44,0x05, 0x9e,0x5b,0x9d,0x1c, + 0x4c,0x34,0xac,0x79, 0xf8,0x31,0xc7,0xf2, 0x5d,0x88,0xb2,0xbe, 0x9f,0xab,0x81,0x4c, + 0xf7,0xde,0xfa,0x55, 0x9a,0xe0,0x18,0x36, 0x73,0x29,0x20,0xce, 0x6d,0x01,0xfc,0x8b, + 0x2c,0xb2,0xcb,0x2c, 0xb2,0xca,0xcc,0xb2, 0xcb,0x32,0x78,0x4d, 0x4c,0xb4,0xa9,0x4e, + 0x99,0x34,0xd2,0x03, 0x4d,0x2d,0x8c,0x69, 0xa6,0xb9,0x86,0x9a, 0x9f,0x6a,0x1b,0x4d, + 0x3a,0xe6,0xf1,0xf6, 0xdd,0x0f,0x2d,0x58, 0x21,0x09,0xc3,0x29, 0xc3,0x04,0x82,0x30, + 0x10,0xd6,0x42,0x8a, 0x7a,0x56,0x4f,0x9c, 0x4d,0x12,0x68,0x52, 0xec,0xe8,0x4e,0x1d, + 0xf9,0x56,0xf5,0xae, 0xc0,0xfa,0xeb,0xf3, 0xb6,0x99,0xf9,0xb0, 0xba,0x24,0xb8,0x5a, + 0x17,0x34,0x12,0x29, 0x8d,0x02,0x25,0x2a, 0x3a,0x4e,0x9c,0xe5, 0x55,0xc1,0x70,0x28, + 0xaa,0xb1,0x09,0x88, 0x95,0x06,0xd7,0xfb, 0x71,0x25,0x82,0x85, 0xad,0xaf,0x36,0x7a, + 0x83,0xbe,0xbf,0x48, 0xc3,0x1e,0xc5,0x5a, 0x46,0xac,0x91,0x55, 0xa4,0x6a,0x8d,0x38, + 0x97,0x22,0xa2,0xe6, 0xe6,0xc2,0x85,0xac, 0x8e,0x8b,0x90,0x55, 0x83,0x53,0x4f,0x46, + 0x91,0x52,0xc0,0xf3, 0x5a,0x12,0xcf,0x60, 0x5a,0xd0,0x96,0x8a, 0x62,0x33,0xef,0x41, + 0xd0,0x7a,0x84,0x34, 0x5c,0x04,0x21,0x45, 0x92,0x29,0x05,0xef, 0x10,0x21,0x6d,0xda, + 0x62,0x31,0x0a,0x08, 0xb1,0x28,0x81,0x2d, 0x16,0x4b,0x34,0x2f, 0x0c,0xd4,0xd7,0x6d, + 0x8e,0x05,0x03,0x6c, 0xa9,0xaa,0x08,0x25, 0x32,0x76,0xdb,0x30, 0xa0,0xd5,0xa2,0xf3, + 0xcb,0xd1,0x99,0xdb, 0x48,0xc7,0x5d,0xe4, 0x36,0x50,0xe5,0xf0, 0x2a,0xba,0x03,0x80, + 0x40,0x46,0x30,0x04, 0x43,0x14,0x8c,0x63, 0x4e,0x40,0xd8,0xb8, 0xa1,0x21,0x11,0xd2, + 0x86,0xe1,0xcc,0x8c, 0x1a,0xb5,0xeb,0x54, 0x55,0x5a,0x84,0x5e, 0x18,0x32,0xab,0x0a, + 0x95,0x96,0xdd,0x80, 0x17,0x82,0x99,0x0d, 0x2e,0xe0,0xe6,0x72, 0xd8,0x6b,0x84,0x91, + 0xb2,0xe7,0x4f,0x3c, 0x40,0x50,0x42,0x0f, 0xb8,0x73,0x04,0x77, 0x97,0x54,0x1e,0x55, + 0x84,0x56,0xc1,0xa6, 0xd1,0x48,0x0a,0x96, 0x70,0x06,0x24,0x12, 0xf1,0x28,0xf3,0x65, + 0x09,0xcd,0x6d,0xf7, 0x10,0xa5,0x5a,0xc5, 0xb0,0xe2,0xf2,0xf6, 0xb7,0xcb,0xe2,0x50, + 0x12,0x44,0xda,0x95, 0x0a,0xea,0x5c,0x51, 0x00,0x00,0x5f,0x56, 0xac,0xf3,0x07,0xef, + 0x59,0x37,0x41,0xef, 0x4d,0x1f,0xde,0xdf, 0x57,0x54,0x54,0x30, 0x69,0x36,0x17,0x41, + 0x50,0x1d,0x51,0xee, 0x4a,0x10,0xb7,0xec, 0xdb,0xa3,0xec,0xf8, 0x4a,0xcf,0x4f,0x4d, + 0x6c,0x96,0x1f,0x9f, 0xda,0x30,0x18,0x7d, 0x07,0xc9,0x91,0xcc, 0x34,0xaf,0x2b,0x72, + 0xb8,0x2f,0x8d,0x8b, 0x75,0xe5,0x38,0xa4, 0x19,0xcd,0x62,0xab, 0xf4,0xe5,0x5c,0xe7, + 0x80,0xee,0x5a,0x67, 0xb0,0x54,0x29,0xab, 0xae,0x21,0x26,0x31, 0x44,0xa1,0x57,0x0d, + 0x11,0x5e,0x15,0x29, 0xb4,0x13,0x68,0x9f, 0xa1,0x7b,0x16,0x76, 0x9e,0x49,0x61,0x6a, + 0xf1,0xc8,0x85,0x4e, 0xff,0x4c,0xc9,0x92, 0x4e,0x78,0x0c,0x4d, 0x2c,0x4d,0x18,0xb4, + 0x48,0xbb,0x17,0x44, 0x25,0x53,0x02,0xa6, 0x92,0xa9,0xf9,0xf7, 0xcf,0x5f,0xf9,0x5b, + 0xee,0x09,0x7d,0xe7, 0x66,0xc4,0x15,0xaa, 0x02,0xd5,0x93,0xaf, 0xd3,0xc2,0x8d,0x37, + 0x92,0x9c,0x90,0x42, 0x43,0x60,0x5a,0x86, 0x2c,0x94,0x41,0x43, 0x0c,0xac,0x11,0x26, + 0x01,0x36,0x19,0x2e, 0x66,0x82,0xd8,0x29, 0xa2,0xb6,0x0a,0x1a, 0x16,0xcb,0x09,0x34, + 0x04,0x98,0x85,0x26, 0x2b,0x69,0x0c,0x06, 0x16,0xb2,0xac,0xb9, 0x2f,0x68,0x55,0x42, + 0xcd,0x4d,0x33,0x20, 0xc3,0x6d,0x9e,0x29, 0x16,0x60,0x03,0x0b, 0xc1,0x18,0x21,0x12, + 0x68,0x58,0x58,0x29, 0xb0,0x09,0x30,0x58, 0xda,0xfd,0x7c,0x58, 0xe5,0x4b,0x48,0xa5, + 0xa5,0x4b,0x41,0x26, 0x89,0x34,0xa4,0xf5, 0x28,0xdc,0x7a,0xc3, 0x2f,0x8c,0x5e,0xa6, + 0x9e,0x65,0xe9,0xac, 0x99,0xda,0x38,0xd6, 0x6d,0x1a,0x4a,0xb3, 0x9e,0xb4,0xf2,0xe5, + 0xd4,0x78,0x15,0x6c, 0x9b,0x93,0x4b,0x41, 0x92,0x94,0x16,0x5e, 0xae,0x24,0xa8,0xa2, + 0x22,0x2d,0x9d,0x6a, 0x62,0x98,0xe8,0x6d, 0xf1,0x19,0x0d,0xba, 0x2b,0x55,0xd6,0x6c, + 0x64,0xa6,0xcd,0x78, 0xe1,0xa3,0x48,0x42, 0x6c,0x4a,0xf4,0xa4, 0x29,0x03,0x18,0x4a, + 0x4c,0x6d,0xb3,0x3f, 0x57,0x1e,0x29,0xd0, 0xc5,0x51,0xc8,0x95, 0xd5,0xe6,0x32,0x1f, + 0x68,0x17,0x60,0xaa, 0x25,0x20,0xa0,0x56, 0xe5,0xef,0x77,0x6f, 0x59,0xbb,0x6e,0xad, + 0xda,0x55,0xb7,0x5b, 0xee,0x20,0xd0,0x79, 0xc2,0x9d,0x34,0x32, 0xb2,0xaa,0xa8,0x14, + 0xa5,0x24,0x90,0x09, 0x35,0x5b,0xd9,0xb7, 0x7a,0x3c,0x5d,0xa0, 0xbc,0xa2,0x57,0x64, + 0x00,0x32,0x5d,0xdf, 0x78,0x7f,0xc2,0x12, 0x3a,0x07,0xbf,0x59, 0x36,0x01,0x88,0xa2, + 0x8b,0x86,0x37,0x9f, 0x86,0xc7,0x86,0xa7, 0xec,0xb4,0x3f,0xc6, 0xff,0x33,0x0c,0x3c, + 0xfe,0xc7,0xc8,0xca, 0x22,0x19,0xcf,0xda, 0x92,0x32,0x5f,0xd9, 0x38,0x18,0xd8,0xe2, + 0x12,0x49,0x14,0x6b, 0x11,0xc7,0x22,0x95, 0x24,0x46,0x8a,0x29, 0x0a,0x44,0xd4,0x30, + 0x12,0x91,0xa5,0x68, 0x48,0x24,0xa6,0x80, 0xc8,0x59,0x13,0xab, 0x22,0x14,0xd9,0x0b, + 0x1b,0x9c,0x3c,0x21, 0x6f,0xc1,0x04,0x30, 0x87,0x97,0x01,0x40, 0x96,0x00,0x66,0x90, + 0x33,0x21,0x31,0xe2, 0x4b,0x29,0xa1,0x95, 0xda,0xe7,0x22,0x52, 0x22,0xa0,0x54,0xb4, + 0x8a,0xa3,0xa2,0x56, 0xd5,0x35,0x42,0x50, 0xe1,0xef,0x54,0x01, 0x4b,0x49,0x12,0x08, + 0xa0,0x90,0x28,0x62, 0x1b,0x40,0x36,0x80, 0xa7,0x52,0x75,0x63, 0x94,0xef,0xd1,0x43, + 0x73,0x28,0x94,0xa7, 0x4d,0x4d,0x55,0x2d, 0x14,0xa5,0x50,0xa5, 0x0b,0x54,0xa5,0x0a, + 0x0b,0x54,0xeb,0x5a, 0xb9,0x2c,0x29,0x9a, 0xda,0xfd,0xb7,0x89, 0x9b,0xca,0xd1,0x7f, + 0x5c,0xa6,0xec,0xc1, 0x98,0xaa,0x51,0x6a, 0x95,0xdb,0xbe,0x05, 0x05,0x9a,0xb3,0x92, + 0x65,0x78,0xf9,0x76, 0x2c,0xda,0xd8,0x56, 0x4e,0x3a,0xa9,0x24, 0x92,0x40,0x80,0x12, + 0x8d,0xcf,0x14,0xd3, 0xfc,0xe8,0x9b,0x33, 0x33,0x51,0x40,0x02, 0x00,0x6e,0xe1,0x08, + 0xf8,0x75,0x1f,0xe8, 0x28,0xa8,0x93,0x70, 0x07,0x14,0x5f,0x42, 0xbe,0xa0,0xbe,0x3e, + 0x35,0x0a,0xa2,0x43, 0x8b,0x09,0xe8,0x87, 0x4b,0x91,0xcf,0xeb, 0xa1,0x08,0x60,0xa4, + 0x41,0x54,0x18,0x02, 0x55,0x08,0x32,0x30, 0x01,0x99,0x02,0x1c, 0x3d,0x50,0xb8,0x3d, + 0xb6,0xd7,0xd7,0x3e, 0xcd,0xf9,0x9f,0x37, 0x61,0x63,0x61,0xba, 0x19,0xb6,0x0e,0xb5, + 0x7f,0x63,0xab,0xcb, 0x17,0x1f,0x1a,0x3b, 0x61,0x6f,0x6b,0xb6, 0x43,0x63,0x94,0xc6, + 0xe8,0x7a,0x89,0x89, 0x88,0x2e,0x53,0x80, 0xa3,0x10,0xf9,0xd4, 0xa6,0x67,0x2b,0x42, + 0xec,0x74,0x18,0xb4, 0x10,0x42,0x10,0x56, 0x4f,0x6d,0xb5,0x7e, 0x2b,0x9b,0xc9,0x50, + 0xc4,0x1c,0x47,0x4d, 0x8a,0xbd,0x90,0x08, 0xbc,0x8e,0x96,0x0b, 0x07,0xa3,0x6e,0xf6, + 0xd5,0xbe,0xde,0x05, 0xab,0x6b,0x87,0xeb, 0x96,0x08,0x21,0x3c, 0x32,0xe3,0x74,0xe2, + 0x7c,0xf7,0x47,0xb9, 0x3d,0xcc,0xa4,0x60, 0xe7,0xc0,0xef,0x47, 0x09,0xf3,0xdb,0x96, + 0xc1,0x98,0xc7,0x66, 0xc9,0xf1,0x8e,0xec, 0xf3,0x67,0xe2,0x1d, 0x51,0xef,0x8f,0x70, + 0xbd,0x5e,0x5b,0x94, 0xd7,0xbd,0x71,0x33, 0xbc,0x91,0xd1,0x9e, 0xf0,0xf0,0xcf,0x56, + 0x73,0x93,0x05,0x49, 0x4c,0x67,0x36,0x39, 0x91,0xbc,0x6e,0x58, 0x6d,0xa3,0x79,0x80, + 0x85,0xf8,0xdd,0x50, 0xf9,0x86,0x70,0x32, 0x94,0x80,0x8b,0x44, 0xcb,0x4c,0x7c,0xc8, + 0x71,0x98,0xb4,0xbf, 0x0d,0xa4,0x6a,0xc2, 0xd5,0xb6,0xcb,0x6d, 0xab,0x13,0xcb,0x86, + 0x3c,0xd8,0x1b,0xd3, 0xc8,0xd5,0xc7,0x0c, 0xe0,0xf3,0x98,0x0c, 0x51,0xb0,0x49,0xe8, + 0x94,0x01,0x0f,0x82, 0x10,0xfc,0x18,0x11, 0x02,0x77,0x73,0x3d, 0x3c,0xce,0xef,0x39, + 0x1b,0x37,0x84,0xfe, 0x33,0xf0,0xde,0xf8, 0x39,0xe1,0xdb,0xe1, 0x33,0xc6,0x79,0xdb, + 0x1f,0xe9,0x6c,0xc5, 0xe3,0xf2,0xa0,0x47, 0xae,0xfb,0x9e,0xed, 0x69,0x10,0x3d,0x4d, + 0xd6,0xd1,0xdf,0xa0, 0x93,0x2c,0x10,0x70, 0x21,0x20,0x66,0x1f, 0x48,0x72,0x19,0xba, + 0xcb,0x1f,0xc5,0xed, 0xbf,0x62,0x7d,0x1d, 0x73,0xdb,0x8a,0xf2, 0xc2,0x0c,0xcf,0x8a, + 0xe6,0xd8,0x23,0xb3, 0xf7,0x13,0x0b,0x20, 0x57,0x8b,0x89,0x23, 0x95,0x69,0xa2,0xa9, + 0xbe,0x38,0xe7,0xce, 0x23,0xdf,0x70,0xfd, 0x88,0xfb,0xf4,0xdf, 0x9c,0x1d,0xeb,0x6d, + 0xf1,0xe0,0x36,0xbd, 0x3e,0x3a,0x9c,0x84, 0xdf,0x9e,0xfc,0xea, 0x0f,0x9a,0x3d,0x91, + 0xf4,0x63,0x83,0x90, 0x82,0x44,0x56,0x6d, 0xcb,0x6a,0x49,0xdc, 0x27,0x31,0x28,0x9e, + 0xf2,0xc0,0xc5,0xa5, 0xb5,0x2d,0xc4,0xeb, 0x9d,0x82,0xdb,0x3e, 0xf8,0xd7,0x3c,0xfc, + 0x93,0x8c,0xcb,0x80, 0x5b,0x22,0xd4,0x64, 0x8c,0x18,0x6c,0x43, 0x30,0x0c,0x4f,0x84, + 0xd1,0x82,0x01,0x24, 0x32,0x62,0x90,0x79, 0x73,0xdb,0x3a,0x18, 0xce,0xa4,0x6b,0xf1, + 0x5a,0xd5,0x88,0x0e, 0x26,0x3e,0x8b,0x88, 0xfb,0xae,0x78,0xeb, 0x3c,0x84,0x4d,0xbc, + 0xe0,0x22,0x89,0xb8, 0x37,0x83,0x2e,0x52, 0x06,0x44,0x66,0xac, 0xed,0x36,0x74,0x90, + 0x89,0x5b,0x65,0x17, 0x70,0x57,0xf9,0x3a, 0x7c,0xaa,0xe7,0x2c, 0x6c,0x04,0x02,0xb2, + 0x02,0x48,0x0c,0xd8, 0xdb,0x47,0xb6,0x02, 0xde,0x06,0x0a,0x8a, 0x11,0x10,0xc1,0x95, + 0x54,0x48,0x3a,0x01, 0xf4,0xc7,0xbc,0xcc, 0xa3,0xdd,0xb3,0xe1, 0x34,0x02,0xa3,0xfe, + 0x6d,0xf3,0x55,0x6b, 0x13,0xc0,0x36,0x18, 0x97,0x17,0x2b,0xa8, 0x14,0x4c,0xf7,0xc6, + 0x76,0xe5,0x72,0xf9, 0x13,0xae,0xf6,0xb7, 0x3f,0xe2,0xc5,0xd0, 0x18,0xff,0xe5,0xdf, + 0x49,0x89,0x83,0x10, 0xa0,0x6e,0xe3,0x72, 0x37,0x46,0x08,0x52, 0xc8,0x03,0x98,0x02, + 0x96,0x60,0x9c,0x78, 0x08,0x4d,0x62,0xe3, 0x1c,0x4b,0x5d,0x24, 0x92,0x49,0x02,0x21, + 0x26,0xb0,0x5a,0xda, 0x9d,0x83,0x5d,0x34, 0x8e,0x1c,0xb0,0x58, 0xcf,0xdf,0xab,0xb4, + 0x44,0x8c,0x15,0xc2, 0x5a,0xd0,0x91,0x32, 0xfd,0x5a,0x37,0x99, 0xe0,0xdf,0x0a,0xc5, + 0x30,0x8e,0x23,0xd0, 0x0b,0xd1,0x85,0xc6, 0x48,0x64,0x98,0xb3, 0x56,0xba,0xd6,0x95, + 0xb6,0x9e,0xd4,0x02, 0x60,0xb7,0xe4,0x67, 0x9c,0x73,0xe3,0x5d, 0x0e,0x5c,0xc3,0x47, + 0x98,0xe2,0x23,0xa3, 0x55,0x54,0x65,0xa0, 0xb6,0xc1,0xc2,0xe4, 0x8b,0x09,0x84,0x57, + 0xca,0xbc,0x61,0x2c, 0xe2,0xc2,0xa5,0xf7, 0xa4,0x13,0x31,0xde, 0xa8,0xbc,0x5f,0x0b, + 0x49,0xd8,0x5c,0x45, 0x04,0xa5,0x29,0x19, 0xe1,0x49,0x98,0xcf, 0x28,0xcb,0x26,0x5b, + 0x51,0x68,0xa8,0x14, 0x06,0x46,0x6a,0xea, 0x6a,0xe9,0xeb,0x9c, 0xab,0x35,0xa5,0x8c, + 0x76,0x66,0x89,0xd1, 0x08,0xa6,0x92,0x0a, 0x5d,0x34,0x94,0xdb, 0x3a,0xf4,0x40,0xc2, + 0x52,0x58,0x30,0xa0, 0x22,0xb1,0xab,0x0d, 0xa2,0xa2,0xd2,0x48, 0x61,0x60,0xd3,0x12, + 0x23,0xe4,0x89,0x16, 0xab,0xaf,0x53,0x48, 0x65,0x15,0x62,0xb4, 0xa8,0xcf,0x0d,0x55, + 0xf2,0x63,0x63,0x65, 0xc2,0x66,0x52,0x02, 0xb0,0x91,0x33,0x50, 0xc0,0x6c,0x17,0x20, + 0xbb,0xe4,0x7d,0x9c, 0x69,0x96,0xe3,0xd9, 0x87,0xaf,0x12,0xc2, 0xa7,0x39,0xcc,0x9a, + 0x82,0x6a,0x5a,0x50, 0x32,0x64,0xc9,0x9d, 0x34,0xd3,0x9d,0x15, 0x0d,0x8c,0xce,0x4b, + 0x38,0x4c,0x34,0x4c, 0xc7,0xb8,0x06,0x95, 0xe0,0x25,0x7a,0x58, 0x9b,0x64,0x50,0x03, + 0x0a,0x82,0x41,0x36, 0x68,0x06,0x00,0xbe, 0x17,0xc2,0xf9,0xa4, 0x6d,0x13,0x0a,0xcf, + 0x6c,0x5a,0x52,0xbb, 0xb2,0x28,0x2f,0x8a, 0xa2,0xa1,0x10,0x64, 0x2e,0x30,0x83,0x11, + 0x59,0xe4,0x18,0xea, 0x0d,0x9b,0x4b,0xc9, 0x23,0x63,0xd6,0xf4, 0x6a,0x0d,0xac,0x97, + 0x37,0x80,0xa9,0x70, 0x98,0x3d,0xbb,0xc2, 0xf2,0xba,0xee,0x08, 0xa9,0x37,0xc6,0xc0, + 0x0c,0x55,0xaf,0x61, 0x4f,0x91,0xc4,0x3e, 0xa3,0x26,0x37,0xff, 0x1d,0xc5,0xfb,0x46, + 0x2f,0x69,0xd4,0xc2, 0x7c,0xb0,0x81,0x80, 0x66,0xd8,0x31,0x8b, 0x06,0x46,0xd7,0x20, + 0x67,0xf0,0xf4,0xef, 0x88,0xd0,0x0e,0x45, 0x11,0x03,0x6c,0x54, 0x9e,0xe3,0xaf,0xf2, + 0x74,0x37,0x83,0xc0, 0xf6,0x45,0xee,0x64, 0xc8,0x72,0xf6,0xea, 0x37,0x3d,0x1d,0x18, + 0x47,0x12,0x8d,0xbd, 0x8e,0x38,0xb2,0x22, 0xae,0x40,0x52,0x70, 0xd2,0x61,0xeb,0xdf, + 0x81,0x96,0x0f,0x6f, 0x97,0xda,0xcf,0xc2, 0xe0,0x7e,0xa7,0x71, 0xdc,0xf8,0xda,0x51, + 0x8b,0x7d,0xbe,0x1f, 0x08,0x20,0x0c,0xa4, 0x1b,0x82,0xc7,0x08, 0xda,0x3c,0xef,0x81, + 0xeb,0xff,0xf3,0xf2, 0x29,0x0a,0x6b,0x1b, 0x13,0x06,0x40,0xc2, 0x02,0xd1,0x59,0x6f, + 0x7b,0xd7,0x6b,0x13, 0xf3,0xba,0x1e,0xba, 0x8a,0x83,0x9f,0x87, 0x17,0x97,0x95,0x70, + 0x10,0xac,0x31,0xe6, 0xc5,0x82,0x0c,0x6c, 0xe3,0xca,0x21,0xd2, 0x54,0x92,0xdf,0x92, + 0x46,0xe5,0xa4,0x24, 0x19,0x83,0x70,0x9f, 0xf4,0xcd,0xd8,0x70, 0x30,0x06,0x01,0x10, + 0x11,0x31,0xcf,0x8d, 0xf5,0x85,0x8c,0xa0, 0xcd,0xa5,0x23,0xca, 0xc1,0xc3,0x2c,0x8e, + 0xc3,0x93,0xaf,0x97, 0xed,0x38,0x39,0x00, 0x88,0x0f,0x38,0x93, 0x60,0xc1,0x0b,0x20, + 0x5f,0xf0,0x56,0xd8, 0x38,0xaa,0xd9,0xbd, 0xbd,0xd6,0x6b,0x05, 0x3a,0x4c,0xdf,0xae, + 0x49,0xe2,0x6f,0xa2, 0x4f,0x8c,0xd6,0x24, 0xd1,0x1a,0x16,0x26, 0x89,0x51,0x9d,0xc3, + 0x6f,0xc8,0xba,0xc5, 0x59,0xc6,0x48,0x60, 0x24,0x04,0x42,0xf0, 0xb8,0xbc,0x85,0x58, + 0xdb,0x10,0x53,0x1c, 0x40,0xb1,0x8e,0x60, 0xba,0x08,0x2c,0x0a, 0x8b,0xca,0x1a,0x1b, + 0x19,0x49,0x55,0x94, 0x01,0x74,0x91,0x72, 0xf8,0x02,0xf0,0x75, 0x26,0x4c,0xb4,0x82, + 0xd1,0x54,0x2d,0xf9, 0xd9,0xc9,0xd0,0x40, 0x24,0x64,0x29,0x0d, 0x65,0x21,0x65,0xb0, + 0x2b,0x37,0xaf,0x9d, 0xf0,0x16,0xa1,0x67, 0x1c,0x80,0xf1,0xc5, 0xbd,0x23,0x38,0x03, + 0x30,0x5b,0x83,0x09, 0x88,0x02,0xf8,0xb4, 0x40,0x91,0x9e,0x76, 0xe5,0xc1,0xe0,0xeb, + 0x53,0x73,0xab,0x16, 0x58,0x64,0x75,0x08, 0x8f,0x18,0x3a,0x90, 0x1a,0x28,0xfc,0x6e, + 0x27,0x6f,0xff,0xd3, 0x16,0xb9,0x78,0x0a, 0xc3,0x60,0x59,0xdd, 0x5c,0xfd,0x97,0xdf, + 0xb8,0x71,0xb5,0xf7, 0x1b,0x1c,0xc3,0xde, 0x52,0xaf,0x9b,0xc6, 0x79,0x69,0x69,0xb4, + 0x72,0x91,0xe8,0x7a, 0x5d,0x6e,0xcf,0x3b, 0x4a,0xa2,0xe7,0xc0, 0x6c,0x5b,0x83,0x1e, + 0x36,0x9b,0xf4,0x04, 0xa4,0x02,0x08,0x7c, 0x91,0x38,0x26,0xa7, 0xf2,0x98,0x78,0xc0, + 0x20,0x85,0x95,0x54, 0x88,0x67,0x60,0x49, 0x63,0x97,0x76,0x51, 0x81,0xe0,0x1c,0x35, + 0xe1,0x50,0x6f,0xff, 0x84,0xa9,0xe6,0x3f, 0xed,0x7e,0xed,0xfb, 0x47,0xb9,0xd6,0x83, + 0xc3,0xb7,0x10,0xb0, 0x99,0x83,0x4d,0x70, 0x46,0x6b,0xc1,0xd8, 0x1b,0x67,0xb5,0x3b, + 0x11,0x72,0x4d,0x7a, 0x44,0x69,0xd4,0x36, 0x31,0x81,0x69,0xec, 0x7b,0x2b,0xfa,0xa5, + 0x27,0x1b,0xcf,0xe6, 0xf9,0x3d,0xbd,0xd7, 0x17,0x1e,0x18,0x56, 0x12,0x0c,0x47,0xc5, + 0x08,0xf7,0xb8,0x6a, 0x46,0x03,0x38,0x20, 0x32,0x1c,0x1b,0x37, 0x22,0x5c,0xce,0xd6, + 0x72,0x28,0x9d,0x14, 0x50,0x69,0x52,0x4c, 0x38,0x7c,0x66,0xe4, 0x24,0x64,0x68,0x11, + 0x05,0x61,0x9b,0xbd, 0xd7,0x03,0x54,0x2b, 0x38,0xd4,0x87,0x18, 0xf5,0x67,0x1b,0x8a, + 0x18,0x8c,0x62,0xd2, 0x2a,0x26,0x40,0x62, 0xa8,0x2c,0x3b,0x53, 0xa4,0xcb,0x08,0x49, + 0x09,0x82,0xd5,0x26, 0x35,0xd2,0x93,0x70, 0xb2,0x86,0xe2,0xa0, 0x34,0x71,0x8a,0xc2, + 0xe5,0x2d,0xc1,0xb3, 0x0a,0xe7,0x92,0x34, 0x92,0x2d,0x09,0x86, 0x90,0xc2,0x05,0x7c, + 0x91,0x7f,0x29,0x8c, 0x0e,0x78,0x74,0x9c, 0xf4,0x71,0x06,0x23, 0x44,0xd8,0x0e,0x0f, + 0x62,0x77,0x7e,0x9a, 0x39,0x74,0x6c,0x19, 0x4d,0xf6,0x30,0x46, 0xbb,0x12,0xfb,0x5a, + 0x40,0xd6,0xdf,0xa6, 0x37,0x4c,0x08,0x20, 0x61,0xbe,0x36,0x00, 0xa0,0x0c,0xae,0x5b, + 0x13,0x98,0xeb,0xec, 0xd0,0x08,0x8c,0x03, 0xe1,0x95,0x8d,0x50, 0x0a,0x93,0x8b,0xb8, + 0xfc,0xf4,0xbe,0x8a, 0x9e,0xc7,0xd2,0xc0, 0x66,0x04,0xce,0x27, 0x31,0x8d,0xf5,0x43, + 0xd9,0x37,0xce,0xec, 0x38,0x05,0xa5,0xec, 0xad,0xb3,0x53,0x74, 0x52,0x3a,0xde,0x8f, + 0x6c,0x37,0x86,0x1c, 0x55,0x8d,0x95,0xc4, 0x0c,0x69,0xc4,0x43, 0x07,0xb7,0xdd,0xc8, + 0x16,0xb8,0xc2,0x66, 0xa9,0x84,0xde,0x10, 0x32,0xb8,0x41,0x42, 0xf1,0xc8,0x11,0x88, + 0x2a,0x4b,0x60,0xd3, 0xa4,0x24,0xb8,0x37, 0xa0,0x88,0x81,0xb4, 0x64,0xd0,0xde,0x0b, + 0xe6,0x03,0xbc,0xce, 0xb4,0xa0,0x55,0x15, 0x9b,0x75,0xe8,0x6e, 0xbd,0xdb,0xff,0x2b, + 0x66,0x3c,0x8b,0x13, 0x1d,0xf8,0x4e,0x08, 0x40,0x35,0x01,0x22, 0x42,0x31,0xa3,0x1b, + 0x0e,0x69,0x88,0x59, 0xe3,0x31,0x95,0x01, 0xa5,0x8f,0x1d,0xb0, 0xe3,0x4a,0x8b,0x82, + 0xb2,0x82,0xb5,0x57, 0xd0,0x66,0xf1,0x1f, 0xd3,0xec,0xe6,0xf8, 0xde,0xc0,0xbe,0x5a, + 0x2b,0x81,0x5f,0x41, 0xa6,0xc5,0x05,0xc1, 0xe9,0xec,0x80,0xd4, 0xdc,0xf4,0x47,0x3b, + 0x7e,0xdb,0x36,0xb5, 0x84,0x5a,0x22,0xa2, 0xb2,0x39,0x24,0x1c, 0xa6,0x63,0x3c,0x8f, + 0x49,0x58,0x15,0x1b, 0xf1,0x0c,0x03,0x3b, 0x25,0x5c,0x99,0x54, 0x96,0xb4,0xab,0x49, + 0x68,0x46,0xd1,0x56, 0xa2,0x23,0x62,0xd0, 0xb7,0x1f,0xe9,0xa8, 0xb8,0x33,0xe5,0xc4, + 0x15,0x53,0x04,0x41, 0xda,0x14,0x03,0x26, 0x50,0x15,0xa2,0x86, 0x31,0x4a,0x96,0xe0, + 0x16,0x40,0xc0,0x40, 0x94,0xe9,0x57,0xa9, 0x61,0x4a,0x80,0xa5, 0x62,0x11,0x08,0x62, + 0x2f,0x16,0x2c,0x83, 0x78,0x03,0x41,0xe9, 0x60,0x94,0x84,0x55, 0x42,0x54,0x0c,0x4d, + 0x0b,0x68,0x56,0x1e, 0xcb,0x47,0xd9,0xbc, 0xcf,0x42,0xf1,0xdb, 0x86,0x12,0x06,0xcb, + 0x84,0x30,0x6c,0xf5, 0x00,0x5c,0x05,0xc1, 0x9c,0x60,0xd2,0x94, 0x41,0xe8,0xbb,0x57, + 0x41,0x29,0xd1,0x3a, 0x1a,0x9a,0xcf,0x91, 0xf0,0xe5,0x24,0x15, 0x14,0x8c,0xa8,0x64, + 0xc2,0x65,0x40,0xc7, 0x40,0xc9,0x0e,0xf7, 0x6d,0xdf,0xf8,0x47, 0xb1,0xb4,0xd1,0x12, + 0xb0,0xa4,0x5c,0x33, 0xa4,0x30,0xde,0x18, 0x5a,0x14,0x07,0xba, 0x3c,0x4a,0x8f,0xda, + 0xeb,0xb9,0xa1,0xbb, 0xe9,0x5b,0x61,0xc3, 0xdf,0xb7,0x99,0x23, 0x04,0xba,0x0d,0x3a, + 0x0e,0xe7,0xf4,0x05, 0xe9,0xbd,0x53,0x6c, 0x09,0x06,0xea,0xc8, 0x2c,0xb7,0xa5,0xcd, + 0x88,0x94,0xb3,0x10, 0x4d,0xb6,0x99,0x75, 0x06,0x23,0x58,0xd7, 0x32,0x06,0x10,0x81, + 0x13,0xdc,0xe2,0x05, 0xe1,0x4c,0xd9,0x0e, 0x28,0xcb,0x80,0xbe, 0x83,0x00,0x99,0x41, + 0xd3,0x2e,0x47,0x38, 0x26,0x5c,0x96,0xa1, 0xa4,0xc3,0x64,0x26, 0xb4,0xc1,0x73,0x61, + 0x24,0x56,0x66,0xca, 0x39,0x18,0xcf,0x72, 0x2d,0x04,0x05,0xf0, 0xe4,0x00,0xb8,0x98, + 0xfd,0xcb,0xc0,0xc7, 0x13,0x12,0xdd,0x6a, 0x61,0x30,0x8a,0x04, 0x8c,0xc0,0x72,0x0e, + 0x09,0xe7,0xf3,0x7c, 0x9e,0x9c,0xfa,0x0f, 0x9b,0x9c,0x2b,0xe5, 0xa7,0x95,0xcf,0xb7, + 0xcb,0x99,0xee,0xe1, 0xdb,0x9d,0xa9,0xc2, 0x80,0xf8,0x03,0x1c, 0x9d,0x0e,0x35,0xe9, + 0x09,0xab,0xac,0x4a, 0xc1,0xe5,0x2e,0x1c, 0x7c,0x70,0x8d,0x0b, 0x8c,0x90,0x7e,0x43, + 0x0a,0x3e,0x50,0x62, 0xe4,0xea,0xf1,0xee, 0xa5,0xf2,0xd8,0x8f, 0x75,0x95,0xa1,0x33, + 0x68,0x99,0x92,0x54, 0x8b,0x2b,0x17,0x17, 0x2e,0x16,0xe4,0xaa, 0x8d,0x03,0x13,0x3b, + 0x84,0x2c,0x6b,0x06, 0x80,0xe5,0x54,0x13, 0x34,0x54,0x06,0x5a, 0xa5,0x0d,0x8c,0x24, + 0x0d,0x2e,0xb8,0xa5, 0x20,0x6c,0x63,0x69, 0x88,0xac,0x26,0x29, 0xd2,0x15,0x9c,0xc0, + 0xb0,0xb0,0x39,0x47, 0x57,0x5f,0x7c,0x15, 0xda,0x04,0x16,0xc1, 0xd8,0x97,0x4a,0x0e, + 0x59,0x79,0x17,0x8b, 0x8a,0xce,0x60,0x0b, 0x54,0xd0,0xbc,0x74, 0x98,0x17,0xc5,0x94, + 0x66,0x50,0x91,0x68, 0x48,0xd0,0xb7,0x97, 0xb6,0x43,0x89,0x66, 0x16,0x89,0x30,0xe7, + 0x52,0x1c,0xe2,0x59, 0xf7,0x9b,0x63,0x09, 0x9d,0x6d,0x43,0x85, 0xd6,0xeb,0x58,0x52, + 0xab,0x17,0xba,0xe0, 0x12,0x32,0x0f,0x64, 0xfc,0x7e,0x06,0x30, 0xd9,0x0a,0x06,0xe1, + 0xd8,0x22,0xea,0xc4, 0xb0,0x40,0xe1,0x3f, 0x08,0x1f,0x12,0xd2, 0x21,0x50,0xa0,0x09, + 0xa3,0xd4,0x52,0x5d, 0xea,0x60,0xf0,0xea, 0xe2,0x41,0x83,0x1a, 0x5c,0x7a,0x19,0x32, + 0x9d,0x00,0xcc,0x83, 0xa6,0x50,0x17,0xb9, 0x70,0x53,0xa8,0x52, 0x69,0x13,0x33,0x08, + 0x99,0xba,0x1a,0xc6, 0x9d,0x4d,0x78,0xdc, 0x5e,0x0c,0x97,0x16, 0xc3,0xdf,0xba,0x74, + 0xf1,0x48,0xb2,0x71, 0x29,0x4f,0x54,0xe2, 0xd0,0xaf,0xd0,0x3d, 0xa0,0x2b,0x2a,0x5c, + 0x03,0xfe,0xb3,0x52, 0xb0,0x01,0xcf,0x1a, 0x69,0x23,0x48,0xcb, 0x89,0xba,0x04,0x4c, + 0xa0,0xe4,0xe6,0x05, 0x05,0x19,0xe7,0x54, 0xb8,0x98,0x73,0x58, 0x88,0x65,0x34,0x8d, + 0x94,0xde,0x5b,0xa3, 0x4c,0xd3,0x02,0xf8, 0x65,0xc8,0xc6,0x37, 0x57,0x67,0x3a,0xce, + 0xbb,0x5f,0xf1,0x38, 0x13,0x28,0x2a,0x3b, 0xa2,0xe2,0x12,0x45, 0xf3,0x5f,0x8f,0xdd, + 0x3c,0x9c,0xf8,0x94, 0xa4,0x4b,0x63,0xc1, 0xfc,0x58,0xfc,0x1f, 0x8c,0xb0,0xaa,0xa3, + 0x4e,0x2c,0x36,0xc8, 0x35,0x04,0x64,0xc2, 0xcd,0x84,0x91,0xd8, 0x15,0x1e,0x31,0xda, + 0x0a,0xa0,0xbd,0xdf, 0x16,0x85,0xda,0xe5, 0x24,0xc3,0xbe,0x41, 0xd2,0xf9,0x80,0x2b, + 0xdc,0xbe,0xa2,0x21, 0x10,0x0d,0x56,0x85, 0xd5,0x18,0x0a,0x45, 0x81,0x84,0x83,0x18, + 0xca,0x00,0xa1,0x00, 0x88,0x52,0x02,0xa6, 0xd3,0x68,0x00,0x80, 0xcc,0x74,0x25,0x00, + 0x0b,0x03,0x23,0x9f, 0x68,0xd7,0xfa,0x02, 0xc8,0x96,0x78,0x28, 0x67,0x9f,0x93,0xf4, + 0x36,0x5d,0x66,0xa0, 0x2a,0x46,0x7b,0x83, 0xf6,0xff,0x47,0xf7, 0x39,0x06,0x90,0x1d, + 0xe6,0xcf,0x99,0x17, 0xf0,0x41,0x89,0x17, 0x86,0x1a,0x5a,0x73, 0x26,0x29,0xee,0x77, + 0x55,0x70,0xf2,0x08, 0xa5,0x25,0xa3,0x04, 0x65,0xc2,0x11,0xc5, 0x56,0xbb,0xb6,0x77, + 0xd2,0xd9,0x9c,0x2a, 0x27,0x2a,0x28,0x8a, 0x2c,0xb3,0x7e,0x12, 0x42,0xa8,0x61,0x41, + 0xc7,0x11,0x92,0x5e, 0x56,0x2c,0x59,0x01, 0x7d,0xad,0x99,0xa9, 0x1a,0x55,0x7d,0x9d, + 0x48,0xa8,0x2a,0xe4, 0xc8,0xaa,0xfa,0x30, 0xa0,0x8d,0x11,0xa2, 0x45,0x5c,0xe4,0xcb, + 0x6a,0xac,0xaa,0xb5, 0x4b,0x8c,0xc5,0x57, 0x39,0x41,0x75,0xa5, 0xa2,0xd0,0x2b,0xbd, + 0xc4,0x8c,0x8a,0xaa, 0xb7,0x48,0xae,0x78, 0x2a,0x9a,0x40,0x91, 0x55,0x55,0x55,0x55, + 0x55,0xc7,0xd9,0x6a, 0x8a,0xb5,0x90,0xcb, 0x04,0xc1,0xaa,0x3d, 0x48,0x14,0x21,0xbf, + 0xad,0x0a,0xaa,0xe7, 0x64,0x55,0x76,0x6a, 0x2a,0x20,0x46,0x45, 0x54,0xa2,0x97,0x1e, + 0x49,0x23,0x97,0x08, 0xa9,0x8f,0x25,0xa2, 0x54,0x68,0xa2,0x37, 0x36,0xb3,0x42,0x8c, + 0xd0,0xcb,0x9b,0x86, 0x49,0x25,0x49,0xaa, 0x96,0xb6,0x12,0x49, 0x30,0x92,0x4d,0x46, + 0x92,0x9a,0x44,0xa0, 0xba,0xeb,0x5d,0x26, 0x12,0x49,0xc8,0x05, 0xa5,0xa4,0x45,0x1b, + 0xe4,0xbe,0x49,0x09, 0x68,0xcb,0xa8,0xb3, 0x55,0x4c,0x94,0x10, 0x8c,0x90,0x24,0x92, + 0x46,0xe9,0x24,0x96, 0x94,0x32,0x37,0x46, 0x46,0x54,0x4a,0xa1, 0x55,0x5f,0x1e,0x45, + 0xaa,0x44,0x55,0x74, 0x92,0x48,0x0b,0xa4, 0xf5,0x90,0x01,0x64, 0x7e,0xf7,0x6c,0x2a, + 0xf7,0xff,0x17,0x72, 0x45,0x38,0x50,0x90, 0xae,0x04,0xda,0x11, + // 0x152c = 5420 data bytes (14% including 4 header bytes) +}; + +static BZ2Manager_t objflow_bin_mgr + = { objflow_bin_bz2, sizeof(objflow_bin_bz2), 0, 0 }; + +/////////////////////////////////////////////////////////////////////////////// + +static const u8 geohittableitem_bin_bz2[] = // GeoHitTableItem.bin +{ + // decompressed len: 0x2d6c = 11628 bytes + 0x00,0x00,0x2d,0x6c, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xf2,0xde, 0xe6,0xc5,0x00,0x06, + 0x03,0x7f,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xd0, 0x04,0x5e,0xee,0x75, 0xc2,0x69,0x6a,0x39, 0x1e,0x44,0x8e,0x49, + 0xea,0x1a,0x03,0x20, 0xd0,0x68,0x34,0x34, 0x06,0x40,0x00,0x03, 0x10,0x34,0x00,0x69, + 0xf9,0x28,0x0c,0x80, 0x06,0x80,0x00,0xc8, 0x61,0x34,0xc2,0x03, 0xd4,0xd0,0x00,0xc8, + 0x1a,0x03,0x4d,0x00, 0x00,0x01,0xa0,0x00, 0xc8,0x01,0xa6,0x43, 0x6c,0x4d,0x4f,0x53, + 0xfd,0x52,0x9f,0x9a, 0xa8,0x33,0xfc,0xaa, 0x62,0x05,0x6a,0x94, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x3d,0x40, 0xc8,0x06,0x80,0x0d, 0x07,0xa9,0xa0,0x00, 0x32,0x18,0x4c,0x65, + 0x3f,0x53,0xf5,0x4f, 0x54,0x68,0xd0,0x3f, 0xc5,0x29,0xa0,0x00, 0xcd,0x47,0xa8,0x3d, + 0x40,0x07,0xea,0x81, 0xa0,0x03,0xfd,0x2a, 0x0d,0x3d,0x4f,0x28, 0x06,0x9a,0x00,0x30, + 0x80,0x0f,0xfd,0x52, 0x9a,0x68,0x30,0x80, 0x0a,0x8d,0xea,0xaa, 0x68,0x30,0x8c,0x46, + 0x13,0x08,0x62,0x32, 0x68,0x60,0x26,0x4c, 0x10,0x0c,0x4c,0x9a, 0x64,0x68,0xc2,0x69, + 0x80,0x08,0xd1,0x82, 0x60,0x20,0x68,0x64, 0x30,0x9a,0x31,0x34, 0xd3,0x4d,0x1a,0x32, + 0x1a,0x62,0x62,0x62, 0x34,0x64,0xc4,0xc2, 0x1a,0x19,0x19,0x32, 0x64,0xc2,0x64,0xd3, + 0x26,0x08,0xc9,0x84, 0xc9,0x90,0x68,0xd0, 0x24,0xff,0xde,0xa5, 0x52,0x64,0x0c,0x20, + 0xd0,0xd0,0x01,0xa0, 0xc2,0x0d,0x1a,0x34, 0x1a,0x34,0x34,0x0c, 0x80,0xd1,0x84,0x64, + 0x03,0x10,0x06,0x10, 0xc4,0x68,0x34,0x0d, 0x00,0x06,0x80,0x69, 0xa0,0x00,0xd3,0x10, + 0xd0,0x03,0x40,0x00, 0xd1,0xa0,0x0c,0x8d, 0x34,0x68,0x19,0x00, 0x34,0x61,0x00,0x01, + 0x9a,0x92,0xa7,0xaa, 0x46,0x80,0x36,0x90, 0x32,0x03,0x43,0x13, 0x41,0xa6,0x9a,0x34, + 0xd1,0xea,0x00,0x34, 0x0d,0x34,0xc2,0x03, 0x20,0xd1,0x82,0x03, 0x26,0x40,0x3d,0x26, + 0x80,0x00,0x00,0x03, 0x43,0xd1,0xa2,0x31, 0x0d,0x0d,0x00,0x34, 0x06,0x86,0x4c,0x23, + 0x11,0xa6,0x80,0x33, 0x48,0xd1,0xa0,0x7a, 0x7a,0xa0,0x34,0xcf, 0xf5,0x4d,0x51,0xfa, + 0xa0,0xdb,0x49,0x42, 0x4d,0x09,0x83,0x63, 0x4c,0x04,0x02,0x68, 0x40,0x21,0xb1,0x03, + 0x69,0x9a,0x69,0x13, 0x94,0x14,0x94,0xd5, 0x15,0x75,0x4b,0x0b, 0x2b,0x4b,0x7a,0xbd, + 0x66,0xbb,0x5f,0xb0, 0xd8,0xec,0x97,0xd8, 0x18,0x76,0x8c,0x4c, 0x7b,0x5d,0xb6,0xdf, + 0x70,0xca,0xcc,0xce, 0xd0,0xd2,0xd6,0xd8, 0xda,0xdc,0xde,0xe0, 0xe2,0xe4,0xe6,0xe9, + 0xbb,0xde,0x3a,0xef, 0x60,0x60,0xa0,0xe1, 0x21,0x61,0xa1,0xd9, 0xb4,0xcd,0x29,0x10, + 0x58,0x95,0x73,0x79, 0xc8,0xac,0xee,0x7a, 0x2f,0x3f,0xa0,0xd0, 0xe8,0xb4,0x66,0x3a, + 0x1a,0xe9,0x23,0x34, 0xda,0x7d,0x46,0xa6, 0x66,0x6a,0x6e,0x73, 0xf1,0xf9,0xfd,0x7e, + 0xff,0x9f,0xdf,0xf6, 0x3f,0x27,0x94,0xca, 0xbf,0xc0,0x65,0xb2, 0xf0,0x30,0x50,0x70, + 0x99,0x98,0x58,0x76, 0x6d,0x14,0x88,0x2c, 0x4a,0xb1,0xb1,0xcc, 0x99,0x32,0xc3,0x3b, + 0xeb,0x75,0xfb,0x1d, 0x9e,0xd7,0x6f,0xb9, 0x23,0xdd,0x92,0xef, 0x77,0xfc,0x1e,0x1f, + 0x17,0x8f,0xc9,0xe5, 0xf3,0x79,0xe4,0xe5, 0x25,0x65,0xbf,0x53, 0x6c,0xd0,0x45,0x26, + 0x89,0xce,0x29,0x3a, 0x0d,0x7f,0x6a,0xac, 0x6f,0xe7,0xf7,0xfd, 0xff,0x9e,0x9f,0x12, + 0xa0,0x6c,0xdd,0xc5, 0x0e,0x27,0x9c,0x71, 0xc7,0x1c,0x71,0xc7, 0x73,0xcf,0x3c,0xf3, + 0xcf,0x3f,0xa0,0xc5, 0x8b,0x18,0xf7,0xe7, 0xe8,0xc4,0x21,0x08, 0x44,0x8b,0x54,0x21, + 0x07,0x39,0xdf,0x5f, 0x70,0x17,0xdb,0xe3, 0xe3,0xe3,0xdb,0xdb, 0x16,0x2c,0x63,0xc0, + 0x80,0x10,0x1c,0x62, 0x01,0x00,0x09,0x3a, 0x00,0xb5,0xb0,0x00, 0x60,0x81,0x31,0x8c, + 0x48,0x12,0x06,0x21, 0x34,0x21,0xc3,0x49, 0xb4,0xd4,0x0d,0xa4, 0xd3,0x70,0xdb,0x49, + 0x0d,0x36,0x36,0xe0, 0x69,0x82,0x1a,0x40, 0x81,0x34,0xdd,0xbb, 0x2f,0x98,0xf7,0xf3, + 0x39,0xac,0xdf,0xc1, 0xf0,0xe7,0x73,0xd9, 0xfc,0x36,0x1b,0x47, 0xa4,0xd2,0xd5,0xd5, + 0xd6,0x57,0xd7,0xd7, 0xd5,0x57,0x57,0x57, 0x72,0xfc,0x4d,0x87, 0xeb,0xf8,0x63,0x75, + 0xc4,0x93,0x4b,0x58, 0x1b,0x4b,0x69,0x6d, 0x21,0x26,0xd8,0xdb, 0x4a,0x12,0x6c,0x42, + 0x1b,0x6c,0x18,0x26, 0xd7,0xcb,0x74,0x86, 0xd2,0xfb,0xef,0x7f, 0x4f,0xd5,0xf5,0xfd, + 0x9a,0x3b,0x0d,0x27, 0xdb,0xf7,0x69,0x6c, 0x74,0xfe,0x66,0x27, 0x13,0x5b,0x5b,0x5b, + 0x5a,0xaa,0xaa,0x94, 0xa5,0x29,0x4a,0x52, 0x94,0xaa,0x28,0xa5, 0x74,0x00,0x84,0xf5, + 0xfb,0x7d,0xde,0xff, 0x87,0xc4,0x5e,0x7c, 0xa5,0xe2,0xda,0x43, 0x18,0xc6,0x31,0x8c, + 0xf5,0x4c,0xa1,0x18, 0xd7,0x30,0x20,0x10, 0x0c,0xc0,0x84,0x02, 0x10,0xc6,0x34,0xec, + 0xd8,0x33,0x1a,0x48, 0xef,0xe7,0x3d,0x48, 0xe7,0x3d,0x56,0xf6, 0x66,0xd4,0x90,0x21, + 0x08,0x00,0x46,0xcc, 0x00,0x84,0x20,0x36, 0x93,0x94,0x95,0x96, 0xf4,0x4d,0xb3,0x41, + 0x14,0x9a,0x27,0x38, 0xa5,0xd0,0x1a,0x84, 0x40,0x44,0x80,0x06, 0xb4,0x22,0xc2,0x30, + 0x00,0x2d,0x60,0x1b, 0x00,0x00,0x08,0x46, 0x4c,0x80,0x03,0x6a, 0x00,0x00,0x01,0xbb, + 0x07,0x8f,0x1a,0x88, 0xb7,0x8d,0x6e,0xdd, 0xd3,0xa7,0x4d,0xdd, 0x6c,0xc4,0x45,0x2e, + 0x1c,0x38,0x4a,0x52, 0x94,0xd4,0x13,0xae, 0x4a,0x53,0xba,0x4a, 0x52,0x22,0x22,0x22, + 0x22,0x22,0x22,0x22, 0x22,0x22,0x31,0xc2, 0x22,0x32,0xeb,0xae, 0xbc,0x56,0xd9,0x75, + 0xd7,0x5d,0x79,0x0e, 0x83,0xb6,0x6e,0xe4, 0x37,0xee,0xdc,0xac, 0xb2,0xcb,0x2c,0xb7, + 0x55,0xcb,0x9f,0x9c, 0xc7,0xd3,0xeb,0xf6, 0xfb,0xcc,0xcd,0x4d, 0xce,0x17,0xc1,0xb3, + 0x77,0x14,0x26,0x31, 0x8c,0x63,0x18,0xc6, 0x31,0x8c,0xb2,0xc9, 0x20,0x82,0xd2,0xe8, + 0xa2,0xb2,0x57,0xf4, 0xbe,0x49,0x24,0xaa, 0xa9,0xcc,0x4c,0x26, 0x9a,0x69,0xa6,0xaa, + 0xaa,0x28,0xa2,0x9f, 0x9e,0x9b,0x4d,0x4a, 0x95,0x2b,0x1b,0x1a, 0x5a,0x7a,0x55,0x29, + 0xd3,0xa7,0x4e,0x9d, 0x3e,0xae,0xca,0xca, 0xca,0xc1,0x26,0x93, 0xd5,0x36,0xd2,0x43, + 0x6d,0x27,0xaa,0x38, 0x61,0xa4,0xbd,0x72, 0xf6,0x0e,0x4b,0xd7, 0xae,0x5c,0xe4,0xc0, + 0xb1,0x02,0x22,0x22, 0x22,0x22,0x22,0x22, 0x22,0x22,0x2d,0x88, 0x88,0x88,0x88,0x88, + 0x88,0x88,0x88,0x88, 0xbf,0xe9,0xed,0xb3, 0xab,0x9c,0xa7,0x29, 0xca,0x72,0x99,0x32, + 0x74,0x4e,0x53,0x94, 0xe5,0x39,0x4e,0x53, 0x26,0x4c,0x74,0x09, 0x0c,0x60,0x98,0x26, + 0x02,0x13,0x48,0x12, 0x6f,0x74,0x12,0x84, 0x9b,0x13,0x55,0x8d, 0xb6,0x95,0x65,0x65, + 0xd6,0x13,0x4a,0xaa, 0xa9,0x34,0x93,0x74, 0x48,0x90,0x36,0x09, 0x83,0x06,0x81,0xa0, + 0x04,0xda,0x76,0xeb, 0x35,0x14,0x44,0xa5, 0x29,0x4a,0x52,0x94, 0x80,0x34,0xc2,0x18, + 0x43,0x00,0x28,0x34, 0x84,0x30,0x84,0x00, 0xa0,0x00,0x00,0x00, 0xd2,0x82,0x82,0x80, + 0x00,0x00,0x00,0x00, 0x04,0x00,0x00,0x00, 0x05,0x00,0x14,0x00, 0x00,0x00,0x00,0x50, + 0x01,0x41,0x40,0x05, 0x00,0xa5,0x29,0x4a, 0x69,0x84,0x21,0x0c, 0x6a,0x08,0x13,0x49, + 0x80,0x00,0x00,0x00, 0x00,0x00,0x00,0x84, 0x02,0x04,0x08,0x10, 0x20,0x40,0x81,0x02, + 0x04,0x08,0x10,0x20, 0x40,0x81,0x80,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x04,0x42,0x00,0x08, 0x00,0x00,0x00,0x00, 0x08,0x88,0x88,0x8a, + 0x00,0x30,0x80,0x00, 0x28,0x00,0x00,0x20, 0xe2,0xa2,0x80,0x0c, 0x00,0xa0,0x00,0x0a, + 0x0a,0x30,0x2a,0xa8, 0x88,0x88,0x88,0x88, 0x88,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x04,0xa2,0x00,0x10, 0x00,0x11,0x10,0x00, 0x00,0x00,0x00,0x11, 0x10,0x00,0x00,0x00, + 0x00,0x00,0x00,0x03, 0x70,0x26,0xa8,0x00, 0x00,0x00,0x00,0x00, 0x00,0xaa,0x88,0x88, + 0x88,0x88,0xa2,0x22, 0x22,0x22,0x22,0x51, 0x0a,0x00,0x00,0xa0, 0xa0,0xa0,0xa0,0x00, + 0x00,0x28,0x00,0xa0, 0x00,0x00,0x00,0x00, 0x00,0x28,0x00,0xa0, 0x00,0x00,0x00,0x00, + 0x0a,0x00,0x00,0xa0, 0x44,0x44,0x44,0x42, 0xa0,0x00,0x00,0x00, 0x00,0x02,0x85,0x02, + 0x76,0x6a,0x90,0x93, 0xa9,0x52,0xa5,0xc7, 0x68,0xa9,0xff,0x8b, 0xb9,0x22,0x9c,0x28, + 0x48,0x79,0x6f,0x73, 0x62,0x80, + // 0x536 = 1334 data bytes (12% including 4 header bytes) +}; + +static BZ2Manager_t geohittableitem_bin_mgr + = { geohittableitem_bin_bz2, sizeof(geohittableitem_bin_bz2), 0, 0 }; + +//----------------------------------------------------------------------------------- + +static const u8 geohittableitemobj_bin_bz2[] = // GeoHitTableItemObj.bin +{ + // decompressed len: 0x2d6c = 11628 bytes + 0x00,0x00,0x2d,0x6c, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x27,0x12, 0x9a,0x09,0x00,0x04, + 0x12,0x7f,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xd0, 0x04,0x1e,0xc6,0xee, 0x5b,0xa1,0xc3,0xa1, 0xe8,0x88,0xc4,0xa6, + 0x80,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x19, + 0x00,0x00,0x01,0xa0, 0x0d,0x00,0x01,0xea, 0x68,0x34,0x68,0xf5, 0x34,0x1e,0xa0,0x00, + 0x0d,0x00,0x7a,0x9b, 0x4f,0x49,0xa6,0x9f, 0xe5,0x53,0x69,0xa2, 0x7f,0xaa,0x80,0xfd, + 0xa9,0x35,0x4d,0x09, 0x32,0x69,0x93,0x4d, 0x0c,0x46,0x86,0x8d, 0x01,0x84,0x03,0x13, + 0x26,0x4d,0x31,0x34, 0xd3,0x4d,0x1a,0x69, 0xa6,0x43,0x4d,0x0d, 0xa4,0xc1,0x06,0xfd, + 0x51,0x18,0x46,0x4c, 0x10,0xf4,0x40,0xd0, 0xc2,0x66,0x00,0x8c, 0x27,0xea,0x02,0x4d, + 0x32,0x0d,0x18,0x99, 0xfa,0xa8,0x64,0x32, 0x0d,0x1a,0x68,0xc4, 0x01,0xa1,0xa1,0x90, + 0xfd,0xea,0xa8,0xc0, 0x4c,0x41,0xa3,0x40, 0xa9,0x4f,0x7e,0xaa, 0xa0,0x34,0x0d,0x32, + 0x34,0x03,0x46,0x4c, 0x46,0x26,0x9a,0x32, 0x68,0x03,0x46,0x8d, 0x03,0x13,0x46,0x9b, + 0xf2,0xa2,0x3f,0xf5, 0x51,0x0d,0x03,0x08, 0xd0,0xc8,0x00,0x0d, 0x34,0x34,0xc8,0xd0, + 0x34,0x07,0xfa,0xa2, 0x31,0x18,0x8d,0x34, 0xd0,0x68,0x34,0x68, 0xc8,0x1a,0x69,0x84, + 0x31,0x34,0x00,0xd0, 0x01,0xa1,0x84,0x68, 0x1a,0x62,0x68,0x24, 0xff,0xf6,0xa9,0x2a, + 0x68,0x03,0x40,0x00, 0x01,0xa0,0x03,0x4d, 0x34,0xd3,0x20,0x03, 0x4c,0x86,0x80,0xd0, + 0x00,0x34,0x62,0x1a, 0x34,0x1a,0x68,0x00, 0xd0,0x00,0x06,0x83, 0x41,0xa3,0x40,0x06, + 0x4d,0x00,0x68,0xc8, 0x1a,0x00,0x01,0xa6, 0x4d,0x00,0x34,0x00, 0x1a,0x03,0x46,0x80, + 0x00,0x19,0xa4,0xaa, 0x68,0xa0,0x00,0x00, 0x00,0x00,0x00,0x07, 0xa7,0xa5,0x01,0xea, + 0x00,0x1a,0x3d,0x40, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x6d,0x04, 0x00,0x3f,0xd5,0x41, 0xfa,0xa5,0xa4,0xdd, + 0xd8,0xad,0x21,0x24, 0xd2,0x04,0xb9,0xde, 0x7f,0xa2,0xf4,0xfd, 0x7f,0x63,0xdc,0xf7, + 0x7e,0x2f,0x8f,0xe4, 0xf9,0xfe,0x8f,0xbb, 0xef,0xc3,0x61,0xff, 0x0f,0x03,0xae,0xc8, + 0x93,0x6b,0xea,0xd3, 0x2f,0xf4,0xdd,0x39, 0xdb,0x56,0xef,0xac, 0x78,0xd6,0xbc,0xbd, + 0x6b,0xb5,0xfb,0x07, 0xbd,0x8b,0xe6,0xcb, 0x67,0xb4,0x7d,0x2d, 0xb5,0x7f,0x80,0x81, + 0x82,0x83,0xdb,0x6d, 0xf7,0x1b,0x98,0x41, 0x42,0xc3,0x6e,0xb7, 0x70,0xf1,0x11,0x31, + 0x5b,0xcd,0xec,0x5c, 0x66,0xfa,0x36,0x3a, 0x3e,0x42,0x47,0x7f, 0xc0,0xe0,0x97,0x92, + 0x93,0xe1,0x70,0xf8, 0x9c,0x5e,0x37,0x1f, 0x91,0xc9,0x95,0x96, 0x97,0x98,0x99,0x9a, + 0x30,0x64,0xd1,0xb3, 0x87,0x4f,0x1f,0x41, 0x37,0x38,0x85,0x12, 0x34,0x93,0xa3,0x4a, + 0x99,0x3a,0x85,0x2a, 0xa8,0x3a,0x14,0x5d, 0x1e,0x95,0x1d,0x25, 0x35,0x39,0x3a,0xb7, + 0xff,0x7f,0xc7,0xe7, 0xf5,0x81,0xfb,0xfe, 0x60,0xff,0x7f,0xc6, 0x4d,0x18,0x36,0x5c, + 0xd1,0xc3,0xa7,0x8f, 0xa0,0x9b,0x9c,0x42, 0x89,0x1a,0x49,0xd1, 0xa5,0x4c,0x9d,0x42, + 0x95,0x53,0xdc,0xb1, 0x08,0x42,0x10,0x84, 0x21,0x7e,0x7f,0x5f, 0xb6,0xdf,0xc8,0xa6, + 0xf1,0x6a,0x38,0x8c, 0x73,0x1a,0xea,0x3b, 0xb0,0x03,0x1f,0x0d, 0x0c,0xed,0x57,0x90, + 0xf0,0xeb,0x3d,0x66, 0xf9,0xa3,0x46,0x92, 0x05,0x11,0x11,0x23, 0x25,0x27,0x29,0x2a, + 0xd5,0xab,0x59,0x6f, 0x22,0x0d,0x79,0x9a, 0xcf,0xe5,0x6f,0xf4, 0x88,0xc4,0x44,0x44, + 0x44,0x44,0x44,0x44, 0x44,0x44,0x44,0x44, 0x49,0x4a,0x52,0x94, 0xa5,0x29,0xf5,0x44, + 0x25,0x29,0x4a,0x7d, 0x71,0x07,0x3e,0xb7, 0x81,0x81,0xac,0x61, 0x75,0x3b,0xfb,0xfb, + 0xfb,0xeb,0xeb,0xeb, 0x76,0xed,0xee,0x9e, 0xb3,0x49,0x35,0xa2, 0xe8,0xc9,0x22,0x44, + 0x36,0x9a,0x6d,0xac, 0xf8,0xbe,0x13,0x40, 0xdd,0xbc,0x1d,0x1e, 0x92,0x49,0x2a,0xbb, + 0x4d,0x44,0xc4,0x93, 0x12,0xd2,0x3e,0x9c, 0xaf,0x0f,0x88,0xc4, 0xe2,0xba,0x9d,0x8f, + 0x61,0xc0,0xc0,0xc2, 0xea,0xb8,0x38,0x6c, 0x1c,0x4e,0x23,0x15, 0xb2,0xe4,0xf2,0x14, + 0xf7,0x1b,0x69,0x8f, 0x6e,0x51,0x51,0x7f, 0x54,0x19,0x45,0x02, 0xc1,0x94,0x32,0x86, + 0x39,0xd5,0x39,0xce, 0x73,0xcd,0xa1,0x95, 0x08,0x42,0x10,0x84, 0x78,0xd1,0xab,0x5b, + 0x50,0xc6,0x32,0x69, 0x27,0x04,0xc9,0x24, 0x93,0x4d,0x38,0x28, 0xa6,0xf1,0x6a,0x38, + 0x8c,0x73,0x1a,0xea, 0x38,0x06,0x3d,0xdf, 0x21,0xe2,0xc2,0x22, 0x22,0x22,0x22,0x22, + 0x22,0x23,0x5c,0x11, 0x11,0x11,0x02,0xc0, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x0f, + 0x65,0x14,0x51,0x08, 0x42,0x10,0x84,0x21, 0x0a,0x28,0xa1,0x8c, 0x63,0x18,0xc6,0x31, + 0xb0,0x8e,0x48,0x63, 0x18,0xe1,0x36,0x30, 0x80,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x0f,0x60,0xe0,0xf8, 0x1a,0x91,0x5b,0x82, 0xb6,0xff,0x86,0xb7, + 0x11,0x5c,0xfa,0xaa, 0xaa,0xab,0x3e,0x9a, 0xaa,0xaa,0xcd,0x55, 0xa1,0x61,0x5f,0x48, + 0x14,0x8c,0x94,0x9c, 0xa4,0xac,0xb2,0x0f, 0xc4,0xfd,0x8f,0xec, 0xb9,0x11,0x11,0x11, + 0x11,0x11,0x12,0x8a, 0x28,0x5f,0x17,0x0e, 0x08,0x9c,0xfd,0x6e, 0x84,0x51,0x31,0x24, + 0xe8,0x88,0x9b,0x36, 0x22,0x76,0xed,0xdb, 0x75,0x08,0x89,0xc9, 0x3d,0x7a,0xf4,0x9f, + 0x3e,0x7c,0xf3,0xfc, 0xc3,0x0c,0x30,0xc3, 0x1a,0xe7,0xef,0xdf, 0xa1,0xc5,0xd5,0x92, + 0x46,0x94,0x01,0xc0, 0x24,0x9a,0x57,0xd4, 0x21,0x08,0x42,0x10, 0x84,0x21,0x08,0x42, + 0x10,0x84,0x21,0xf9, 0x99,0x99,0x99,0x99, 0x99,0x99,0x99,0x99, 0x9c,0xba,0xf5,0x25, + 0xe8,0x0b,0xb2,0x5d, 0x92,0xec,0xac,0x2c, 0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c, + 0x2c,0x2c,0x12,0x58, 0xf8,0xd2,0x6b,0x08, 0x37,0x06,0x93,0x14, 0x52,0x40,0x11,0x13, + 0x88,0x61,0x22,0x89, 0xa8,0x66,0x37,0xa5, 0xb8,0xdd,0x26,0x9b, 0x45,0xb5,0x6a,0xd5, + 0x2b,0x42,0x49,0x25, 0x55,0x28,0x6d,0xbb, 0xa7,0x7a,0xc6,0x80, 0x40,0xd0,0x84,0x0c, + 0x10,0x46,0xd6,0x10, 0xbd,0xba,0x76,0x90, 0x81,0xb4,0xef,0x2f, 0x25,0x30,0x4a,0xd4, + 0x77,0x82,0x71,0x26, 0xe8,0x56,0xad,0x46, 0xa8,0x4d,0xd5,0x4a, 0x13,0x4a,0x18,0x05, + 0x40,0x00,0x2c,0x00, 0xa8,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x18,0x00,0x00,0x00, + 0x00,0x00,0x15,0x55, 0x55,0x55,0x55,0x55, 0x55,0x55,0x52,0xaa, 0xa5,0x54,0x92,0x41, + 0xa4,0x9a,0x70,0x83, 0x4d,0xb0,0x00,0x00, 0x00,0x00,0x00,0x00, 0x10,0x80,0x40,0x81, + 0x02,0x04,0x08,0x10, 0x20,0x40,0x81,0x02, 0x04,0x08,0x10,0x30, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x92,0x49, 0x24,0x92,0x49,0x24, + 0x90,0x00,0x00,0x49, 0x24,0x98,0x00,0x00, 0x00,0x00,0x00,0x00, 0x22,0xb8,0xc1,0x80, + 0x49,0x77,0x25,0x49, 0x24,0x92,0x48,0x49, 0x75,0x2a,0xa0,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x01,0x52,0x00,0x20, 0x00,0x24,0x80,0x00, 0x00,0x00,0x01,0x24, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0xb8, 0x26,0xad,0x00,0x00, 0x00,0x92,0x49,0x24, + 0x92,0x49,0xa0,0x00, 0x10,0x29,0x70,0x00, 0xc1,0x03,0x01,0x00, 0xc0,0x60,0xc6,0x0c, + 0x04,0x03,0x60,0x81, 0x03,0x4c,0x1a,0x00, 0x10,0x00,0x80,0x04, 0x03,0x00,0x00,0x1a, + 0x1a,0x60,0x00,0x08, 0x10,0x20,0x1a,0x00, 0x40,0x98,0x0c,0x00, 0x02,0x58,0xd8,0x92, + 0x49,0x20,0x00,0x00, 0x02,0xed,0x41,0x3c, 0x5e,0xe4,0x49,0xdc, 0xb9,0x73,0x3f,0xcb, + 0xee,0x7f,0xe2,0xee, 0x48,0xa7,0x0a,0x12, 0x04,0xe2,0x53,0x41, 0x20, + // 0x4dd = 1245 data bytes (11% including 4 header bytes) +}; + +static BZ2Manager_t geohittableitemobj_bin_mgr + = { geohittableitemobj_bin_bz2, sizeof(geohittableitemobj_bin_bz2), 0, 0 }; + +//----------------------------------------------------------------------------------- + +static const u8 geohittablekart_bin_bz2[] = // GeoHitTableKart.bin +{ + // decompressed len: 0x1244 = 4676 bytes + 0x00,0x00,0x12,0x44, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xeb,0x5c, 0x75,0x98,0x00,0x03, + 0x49,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xd0, 0x04,0x3b,0x9c,0xf2, 0x3d,0x23,0x00,0x00, 0x1d,0x4f,0x4d,0x4d, + 0x34,0x1a,0x64,0x03, 0x26,0x80,0xd0,0x34, 0xc8,0x0c,0x86,0x11, 0xea,0x36,0xa1,0x88, + 0xc4,0x61,0x1a,0x68, 0x19,0x01,0xa0,0x01, 0xa6,0x10,0x34,0xd1, 0xa6,0x8d,0x03,0x26, + 0x43,0x46,0x04,0x68, 0x03,0x46,0x40,0x00, 0x0d,0x34,0xc4,0x1a, 0x03,0x46,0x10,0x03, + 0xd1,0xa0,0x41,0xff, 0xaa,0x93,0xfd,0x4f, 0x54,0x86,0x9f,0xf9, 0x53,0xd2,0xa6,0x82, + 0x94,0xa0,0x01,0xea, 0x1a,0x00,0x00,0x00, 0x0d,0x00,0x00,0x00, 0x00,0xd3,0xf5,0x47, + 0xe4,0x91,0xa1,0xe9, 0x01,0xea,0x68,0xd0, 0xf4,0x9a,0x34,0x01, 0xa0,0x3f,0x54,0xd1, + 0xa1,0x9e,0x94,0x7a, 0x23,0xd2,0x33,0x48, 0x01,0xa3,0x10,0x07, 0xea,0x87,0xa9,0xfe, + 0xaa,0x00,0x7a,0x8c, 0x99,0x34,0x34,0xd0, 0xc4,0xd9,0xa4,0xf5, 0x1b,0x53,0x7e,0xa4, + 0xd4,0x04,0x00,0x06, 0x51,0xea,0x6d,0x46, 0x93,0x0d,0x02,0x63, 0x4d,0x00,0x9a,0x60, + 0x4d,0x30,0x00,0x00, 0x00,0x8c,0x00,0x02, 0x18,0x00,0x00,0x00, 0x00,0x00,0x02,0x61, + 0x18,0x04,0xc0,0x99, 0x18,0x11,0x82,0x61, 0x31,0x1a,0x62,0x30, 0x02,0x64,0x60,0x8c, + 0x00,0x00,0x00,0x01, 0x26,0x98,0x98,0x09, 0x80,0x00,0x98,0x04, 0xc1,0x30,0x26,0x00, + 0x00,0x00,0x26,0x00, 0x4c,0x19,0x06,0x4d, 0x34,0x00,0x00,0x00, 0x00,0x00,0x00,0x13, + 0x00,0x00,0x00,0x00, 0x4c,0x11,0x80,0x09, 0x80,0x00,0x00,0x00, 0x00,0x03,0x4f,0x29, + 0x54,0xd2,0x84,0x30, 0x9a,0x64,0xc8,0xc0, 0x04,0xc0,0x46,0x26, 0xd4,0xfd,0x40,0xd3, + 0x44,0x7a,0x00,0x34, 0x4c,0x00,0x46,0x01, 0x1a,0x0c,0x00,0x00, 0x06,0x80,0x00,0x46, + 0x00,0x4d,0x0d,0x18, 0x1a,0x02,0x0c,0x4c, 0x8c,0x41,0x93,0x00, 0x04,0xc2,0x18,0x00, + 0x00,0x03,0xd4,0x34, 0x2d,0x14,0x24,0x48, 0x22,0x04,0x10,0x91, 0x00,0x30,0x24,0x50, + 0x41,0x02,0x08,0xa0, 0x85,0x80,0x04,0x98, 0x00,0x85,0x89,0x03, 0x89,0x0e,0x58,0x18, + 0x00,0xe1,0xc0,0x04, 0xa0,0x60,0x40,0x05, 0x41,0x15,0xf8,0x00, 0x0c,0x18,0x03,0x73, + 0x43,0x63,0x73,0x83, 0x93,0xb3,0xd3,0xe3, 0xf4,0x14,0x24,0x54, 0xb4,0xc4,0xd4,0xf5, + 0x05,0x15,0x25,0x3f, 0xca,0xaa,0xb2,0xba, 0xc2,0xca,0xd2,0xe2, 0xf3,0x03,0x0b,0x10, + 0x00,0x2c,0x6c,0x8c, 0xac,0xc0,0x81,0xb3, 0x84,0x68,0x0a,0xd2, 0xdc,0xde,0xe0,0xe2, + 0xe4,0xe6,0xe8,0xea, 0xec,0xee,0xf0,0xf2, 0xf4,0xf6,0xf8,0xfa, 0xfc,0xff,0x01,0x03, + 0x05,0x07,0x09,0x0b, 0x0d,0x11,0x13,0x15, 0x17,0xd9,0xdb,0xdd, 0xdf,0xe1,0xe3,0xe5, + 0xe7,0xe9,0xeb,0xec, 0x10,0x2b,0x4b,0x53, 0x58,0x16,0xc0,0x0d, 0xa0,0xc0,0xb7,0x04, + 0x04,0xdf,0x04,0xbe, 0x06,0x01,0x7e,0xaf, 0x50,0xc6,0x20,0xa8, 0x9c,0xc1,0x5e,0x8f, + 0x4c,0x94,0x9f,0xaa, 0x52,0x57,0xd7,0x2d, 0xec,0xf6,0xfb,0xa5, 0xfd,0xf3,0x1f,0x0f, + 0x8f,0xca,0x66,0x6a, 0x6f,0xe7,0x38,0xd9, 0xba,0x0e,0x27,0x51, 0x9e,0x9f,0x72,0x95, + 0x03,0xa0,0x4d,0xda, 0x94,0x34,0x54,0x74, 0x94,0xb4,0xdf,0x4f, 0xaf,0xdb,0xef,0xf8, + 0xfc,0xfe,0xbf,0x7f, 0xcf,0xee,0x66,0x44, 0xb2,0xcb,0x2c,0xb2, 0xcb,0x8e,0x30,0xc3, + 0x0c,0x30,0xce,0x92, 0xc5,0x8b,0x23,0xde, 0xde,0xf7,0xa6,0x9a, 0x69,0xa6,0xca,0x4a, + 0xcb,0x71,0x3c,0x4e, 0x47,0x23,0x91,0xc8, 0x64,0x35,0x9d,0x5b, 0x55,0xc7,0xe3,0xf1, + 0xd8,0xed,0x3f,0x4f, 0xc4,0xc4,0xc4,0xdb, 0xb2,0x52,0x49,0x52, 0x4c,0x96,0x7e,0x13, + 0xed,0x68,0x1f,0x61, 0x93,0x01,0x4b,0x55, 0x52,0xa4,0x98,0xc2, 0x56,0x93,0xd5,0x32, + 0x60,0x5a,0x95,0x58, 0x45,0x55,0x4e,0xdf, 0x11,0xa9,0x2a,0x54, 0xc3,0x2a,0xa4,0xac, + 0x4c,0x4e,0xf7,0x21, 0x90,0xd6,0x7f,0xee, 0x50,0x31,0xab,0x1a, 0xb3,0x8e,0x39,0xf3, + 0x18,0xf1,0x25,0x26, 0xf3,0x5b,0x79,0x57, 0x89,0x64,0xc9,0x93, 0xc0,0xc6,0x2d,0x6a, + 0xa1,0x0c,0x62,0xc8, 0x2a,0x10,0xc4,0x29, 0x79,0xb6,0xc9,0xad, 0x5b,0x9b,0x7a,0x98, + 0x9a,0x45,0xca,0x40, 0x9a,0xed,0x26,0x6a, 0xc5,0x38,0x8d,0xe1, 0x61,0x6f,0x8d,0xd0, + 0x5e,0xb9,0x73,0xeb, 0xeb,0xb5,0x1f,0x97, 0x23,0xa3,0xd5,0x25, 0x8e,0xd6,0x63,0xd3, + 0x7e,0x15,0x81,0x53, 0xbb,0xed,0x39,0x2c, 0x99,0xaf,0xee,0x2c, 0xe9,0xd9,0x9d,0x06, + 0xcd,0x48,0x06,0xef, 0xfd,0x54,0x1c,0x4e, 0xa3,0x3d,0x3e,0xe5, 0x2a,0x07,0x41,0x15, + 0x14,0xed,0x45,0xb1, 0x82,0x18,0x84,0x22, 0x95,0x0c,0x42,0x14, 0x2c,0x24,0x25,0x3e, + 0x16,0x31,0xb4,0x39, 0xdc,0x16,0xf0,0x30, 0xcc,0x18,0x30,0x41, 0x05,0x94,0xf5,0x15, + 0x35,0x55,0x7f,0x4f, 0xaf,0xd9,0xc8,0x2b, 0x01,0xb0,0x1a,0xc0, 0x03,0x06,0x18,0x40, + 0x6d,0x00,0x0d,0x88, 0x02,0x95,0x21,0x13, 0x12,0xec,0xa9,0x50, 0x00,0x62,0xc0,0x00, + 0x00,0x1d,0x41,0x34, 0xde,0x80,0x32,0xf0, 0xc5,0x14,0xd7,0x3c, 0xd4,0xe6,0xac,0x00, + 0x00,0x85,0xe7,0x58, 0xc5,0xaa,0x8b,0x21, 0x08,0x42,0x59,0x19, 0xb3,0x66,0x42,0x56, + 0x39,0x64,0x21,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x02,0x44,0x00,0x01, + 0x46,0x9d,0x1e,0x7c, 0x3f,0x62,0x36,0x27, 0x84,0xd2,0x3d,0x8b, 0x4f,0x04,0x17,0x0f, + 0x88,0x7c,0x44,0x69, 0xe7,0xf2,0x38,0xc7, 0x9f,0xde,0x90,0x90, 0xa1,0xa2,0xa3,0xa4, + 0xa5,0xa6,0xfd,0xd7, 0x7f,0x04,0xa8,0x50, 0x97,0xf6,0xbc,0xe7, 0x39,0xce,0x73,0x9c, + 0xe7,0x39,0xd6,0xad, 0xc9,0xfc,0xbd,0x53, 0xeb,0x67,0x7c,0x5d, 0x9e,0xd2,0xd5,0x8e, + 0xab,0x3e,0x0a,0xd5, 0x98,0xd5,0x14,0x74, 0xb3,0x93,0x86,0x37, 0x9c,0xc6,0xaa,0xf4, + 0x53,0x53,0x5b,0xd5, 0xf9,0x57,0xa8,0x0c, 0x63,0x58,0x56,0x7b, 0x1a,0x3e,0xfe,0xda, + 0x3f,0xf2,0x49,0x24, 0x92,0x49,0x45,0x21, 0x08,0xc6,0xda,0x93, 0x12,0x7a,0x94,0xcb, + 0xd5,0xaf,0x62,0x22, 0xc8,0x22,0x22,0x22, 0x22,0x22,0x22,0x22, 0x82,0x2e,0x81,0x11, + 0x11,0x11,0x11,0x11, 0x11,0x11,0x57,0xae, 0x69,0x5c,0x84,0x28, 0x42,0x84,0x32,0x21, + 0x42,0x13,0xa1,0x3a, 0x17,0xd0,0xa3,0x16, 0xf1,0x6f,0x16,0xf1, 0x6f,0x14,0xc5,0xc5, + 0x71,0x84,0xc3,0x83, 0x3e,0x9d,0xcb,0xa0, 0x94,0x12,0x80,0x82, 0x49,0x21,0x74,0xd4, + 0x94,0x92,0x15,0x34, 0xe2,0x51,0x30,0x98, 0x68,0x8c,0xbc,0x0d, 0x4c,0x12,0xa1,0x69, + 0x7a,0x73,0x2a,0x56, 0x19,0x58,0x58,0x0a, 0xac,0x04,0xa4,0xa0, 0xa8,0x95,0x00,0x25, + 0x54,0xad,0x33,0x0b, 0x0b,0x0d,0xaa,0xa8, 0xdb,0x55,0x2a,0x54, 0xbb,0xbb,0xb2,0xaa, + 0x4a,0xbb,0x6e,0xee, 0xec,0x92,0xed,0x92, 0xa4,0x96,0x49,0x57, 0x6d,0x95,0x57,0x6d, + 0x95,0x23,0x6d,0xb7, 0x76,0x54,0xbb,0x6c, 0xa9,0x76,0xdd,0xdd, 0xd9,0x2e,0xd9,0x2c, + 0xbb,0x65,0x8d,0xb6, 0xdd,0xc9,0x65,0xdb, 0x56,0x5d,0xb6,0x36, 0xd8,0xdd,0xdc,0xbb, + 0x6c,0xab,0x22,0x44, 0xa9,0x52,0x92,0xaa, 0x44,0x48,0x01,0x20, 0x05,0x40,0x00,0x00, + 0x00,0x00,0x21,0x00, 0x81,0x02,0x04,0x08, 0x10,0x20,0x40,0x81, 0x02,0x04,0x08,0x10, + 0x20,0x50,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x28,0xaa,0x8a,0x28, 0xaa,0xaa,0xaa,0x00, 0x16,0xaa,0x90,0x20, 0x41,0x55,0x55,0x51, + 0xc6,0xdd,0xda,0xae, 0x35,0x4b,0x55,0x55, 0x20,0xb8,0x57,0x76, 0x80,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x16,0xa0, 0x10,0x00,0x05,0x40, 0x00,0x00,0x00,0x01, + 0x50,0x00,0x00,0x00, 0x00,0x00,0x00,0x2a, 0x92,0x54,0xc0,0x00, 0x00,0x00,0x00,0x00, + 0x01,0x84,0xaa,0xaa, 0x43,0x02,0xab,0x6b, 0x6a,0x41,0x5b,0x5b, 0x5b,0x5b,0x55,0x55, + 0x56,0xd5,0x5b,0x55, 0x55,0x55,0x5b,0x55, 0x6d,0x6d,0x55,0xb5, 0x55,0x55,0x48,0x2a, + 0x42,0xd5,0x55,0x48, 0x5a,0x90,0x59,0x80, 0x00,0x00,0x00,0x00, 0x03,0x04,0x49,0x58, + 0x79,0x36,0x4a,0xc9, 0xe4,0xf2,0x7a,0x3e, 0x73,0x94,0x7c,0xe2, 0xee,0x48,0xa7,0x0a, + 0x12,0x1d,0x6b,0x8e, 0xb3,0x00, + // 0x526 = 1318 data bytes (28% including 4 header bytes) +}; + +static BZ2Manager_t geohittablekart_bin_mgr + = { geohittablekart_bin_bz2, sizeof(geohittablekart_bin_bz2), 0, 0 }; + +//----------------------------------------------------------------------------------- + +static const u8 geohittablekartobj_bin_bz2[] = // GeoHitTableKartObj.bin +{ + // decompressed len: 0x1244 = 4676 bytes + 0x00,0x00,0x12,0x44, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x97,0x97, 0xae,0xb9,0x00,0x03, + 0x45,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xd0, 0x03,0xfd,0x8e,0x78, 0xde,0x06,0x72,0x00, 0x10,0x9f,0xfa,0xa7, + 0xaa,0x80,0x00,0x00, 0x00,0x00,0x00,0x00, 0x68,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x34,0x06,0x80,0x00, 0x00,0x00,0x00,0x68, 0xd0,0x0f,0x7a,0xa8, 0x01,0xfe,0xaa,0x00, + 0x00,0x06,0x80,0x3d, 0x46,0x86,0x9a,0x1b, 0x7e,0x54,0xa7,0xbc, 0xaa,0x06,0xfc,0xa7, + 0xa5,0x4d,0x08,0x20, 0x34,0x34,0xc2,0x64, 0x19,0x32,0x68,0xc4, 0xc2,0x68,0x34,0x61, + 0x1a,0x68,0x0d,0x30, 0x40,0xd1,0x88,0xc4, 0x01,0xa7,0xfa,0xa8, 0x61,0x3d,0xea,0xa0, + 0x7a,0x34,0xc0,0x43, 0x27,0xa9,0xa6,0x40, 0x43,0x4c,0x4d,0x34, 0x01,0x88,0x64,0x68, + 0x1a,0x00,0x06,0x4d, 0x19,0x1a,0x68,0x0c, 0x46,0x98,0x60,0x21, 0xa9,0xff,0xaa,0x53, + 0x4d,0x34,0xf2,0x69, 0xa9,0xa3,0x0a,0x8d, 0xfa,0xaa,0xa0,0x00, 0x00,0x00,0x1a,0x00, + 0x00,0x69,0xa6,0x80, 0xf2,0x83,0x4d,0x06, 0x9f,0xa6,0xa8,0xd0, 0x3f,0xf5,0x54,0x00, + 0x32,0x18,0x8d,0x00, 0x06,0x80,0x03,0x40, 0x07,0xea,0x20,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x32,0x00, 0x34,0x00,0xd0,0x00, 0x00,0x24,0xc0,0x01, 0x30,0x00,0x13,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x08, 0x9f,0xea,0x54,0x95, + 0x1a,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x01,0xea,0x0f,0x50, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x1a, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x64,0x00,0x1a,0x1a, 0x15,0x16,0x16,0x97, 0x17,0x98,0x18,0x99, 0x19,0x9a,0x1a,0x9b, + 0x1b,0x9c,0x1d,0x1d, 0x9e,0x1e,0x9f,0x1f, 0xa0,0x21,0x21,0xa2, 0x22,0xa3,0x23,0xa4, + 0x24,0xa5,0x25,0xa6, 0x26,0xa7,0x27,0xa8, 0x28,0xa9,0x29,0xaa, 0x2a,0xab,0x2b,0xac, + 0x2c,0xad,0x2d,0xae, 0x2e,0xaf,0x0b,0x77, 0xbc,0xde,0xc4,0x44, 0xc5,0x45,0xef,0x89, + 0x0f,0x7f,0xc3,0x27, 0x19,0x1a,0x50,0xa9, 0x62,0xe6,0x0c,0x9a, 0x37,0x1f,0x21,0x22, + 0x70,0xec,0x91,0x12, 0x67,0xa5,0x0f,0xa0, 0x95,0x96,0x97,0x98, 0x99,0x9a,0x9c,0xe5, + 0xf3,0x39,0xbc,0xee, 0x7f,0x42,0x8a,0x8f, 0xa4,0xa9,0x5d,0xea, 0xcb,0xe5,0xbf,0xdb, + 0xff,0xf7,0xff,0x00, 0x9c,0x64,0x69,0x48, 0xe2,0xa5,0x8b,0x98, 0x32,0x68,0xdc,0x7c, + 0x84,0x89,0xc3,0xb2, 0x44,0x49,0x9e,0x94, 0x3e,0x82,0x56,0x5a, 0x5c,0x42,0x10,0x84, + 0x21,0x08,0x55,0x75, 0x9e,0xcf,0x6d,0x6f, 0xba,0xbb,0xdf,0xf0, 0xf8,0xfc,0xbe,0x7f, + 0x4a,0xfb,0x0b,0x1f, 0xaf,0xda,0xcb,0xef, 0x67,0x69,0x6b,0x6c, 0x85,0x12,0x34,0x89, + 0x53,0x27,0xb7,0xb8, 0xb9,0x51,0xf8,0x05, 0x3f,0x9b,0xaf,0xd5, 0xdf,0xee,0xf3,0xf8, + 0xa9,0x5d,0xea,0xcb, 0xe5,0xbf,0xaf,0xdf, 0xf3,0xfa,0x88,0xa4, 0x44,0x44,0x44,0x44, + 0x44,0x44,0x44,0x44, 0x44,0x44,0x47,0x1c, 0x71,0xc7,0x1c,0x71, 0xce,0xcf,0x6a,0x2e, + 0x2e,0x2a,0x2b,0xb8, 0xf1,0xe5,0x86,0xbe, 0xfe,0xfc,0xfc,0xed, 0xdb,0xa7,0x4f,0xaf, + 0xb5,0x0b,0xb5,0x6f, 0xc7,0xca,0x52,0x98, 0x61,0x4a,0x61,0xe3, 0xb7,0x55,0x78,0xe8, + 0x18,0x1b,0x1b,0x6d, 0xb6,0xdb,0x6d,0x34, 0xd3,0x4d,0x40,0x40, 0x76,0xfc,0x1e,0x28, + 0xef,0x37,0x9f,0xd3, 0xea,0x8f,0xf6,0x7b, 0x75,0x9b,0x56,0x4d, 0x56,0xb5,0x5b,0x65, + 0x55,0xaa,0xd5,0x6a, 0xad,0x6d,0xaf,0x57, 0xd1,0xa0,0xfd,0x58, 0x2c,0x1e,0x1b,0xb0, + 0xd8,0xb5,0x56,0xc4, 0xb5,0xb0,0x0b,0xad, 0x55,0x56,0xab,0x60, 0x70,0x18,0x1c,0x0d, + 0xf5,0xf5,0xf5,0xf5, 0xf5,0xf5,0xf5,0xf7, 0xd9,0xa7,0x7d,0xd8, 0x3c,0x82,0xd5,0x8b, + 0xd9,0x4b,0x52,0x96, 0xa5,0x28,0x50,0xad, 0xbe,0xa4,0xb5,0x95, 0x56,0xb5,0x6a,0xd9, + 0x0d,0xeb,0xad,0x6b, 0xcb,0x5a,0xc0,0x55, 0xa8,0xab,0x52,0xad, 0x6a,0xb6,0x0b,0x07, + 0x2e,0xbc,0xa5,0x5f, 0xdf,0xdf,0xdf,0xdf, 0x8c,0x63,0x18,0xc6, 0x31,0x8c,0x63,0xcf, + 0x92,0x20,0x82,0x2d, 0x2d,0x6d,0x90,0xa2, 0x46,0x91,0x2a,0x64, 0xf6,0xf7,0x17,0x2a, + 0x28,0x41,0x48,0x6b, 0xc0,0x00,0x00,0x00, 0x00,0x00,0x02,0x00, 0x08,0x88,0x88,0xac, + 0x24,0x44,0x44,0x44, 0x44,0x44,0x44,0x44, 0x56,0xe1,0x11,0x42, 0x10,0x84,0x21,0x08, + 0x40,0x88,0xde,0x71, 0x87,0x39,0xce,0x73, 0x9c,0xf3,0x48,0x59, 0x36,0x50,0xe7,0x36, + 0x58,0xe7,0xc8,0x42, 0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88, 0x88,0x88,0x88,0x88, + 0xfd,0xe1,0xb5,0x30, 0xd9,0x52,0xe6,0x6a, 0x91,0xab,0xd6,0xc3, 0xc3,0x96,0x34,0x88, + 0x8b,0x7f,0xc5,0x22, 0x27,0x7c,0x37,0x6e, 0xdd,0xbf,0x4a,0xbf, 0x1f,0x9f,0xd7,0xef, + 0xf9,0x1a,0xb6,0x79, 0x5a,0xe7,0xf6,0x39, 0x4a,0x52,0x94,0xa5, 0x29,0x4a,0x55,0xbd, + 0x41,0xdd,0x4a,0x4d, 0x12,0x11,0x29,0xec, 0xc4,0x47,0xfe,0x61, 0xf1,0x4b,0xa8,0x80, + 0xb1,0x87,0xc9,0x2e, 0xa2,0x82,0x29,0x29, 0x34,0x58,0x45,0xbc, 0x8c,0x33,0xc0,0x78, + 0x94,0x9b,0xec,0x94, 0x99,0xe8,0x3d,0x07, 0xa0,0xf4,0x1f,0x07, 0xd0,0x1f,0x25,0x26, + 0x8c,0x07,0xe0,0xfd, 0x2e,0x92,0xfb,0xfc, 0xe3,0x8e,0x38,0xe3, 0x99,0xe6,0xb5,0xac, + 0x8d,0xb2,0x84,0x14, 0x08,0x23,0x50,0xad, 0x92,0xea,0x92,0xd6, 0xb5,0xad,0x6b,0x5a, + 0xd6,0xb5,0xad,0x6b, 0x5b,0x58,0xc6,0x31, 0x8c,0x63,0x18,0xc6, 0x31,0x8c,0x63,0x18, + 0xc8,0xe8,0x14,0x18, 0x0d,0x69,0x9a,0x66, 0x83,0x41,0xa0,0xd0, 0x68,0x34,0x1a,0x0d, + 0x06,0x83,0x58,0x46, 0x11,0x84,0xcf,0xe5, 0xad,0x8b,0x2d,0x55, 0x6a,0xcf,0xae,0xa9, + 0x6a,0xb9,0x6a,0xab, 0x2a,0x8a,0x8a,0xa9, 0x56,0x92,0x59,0x16, 0x95,0x6a,0xd0,0x13, + 0x42,0x68,0x57,0x55, 0xaa,0xd6,0xab,0xd7, 0x5e,0xb5,0x5a,0xaf, 0x2a,0xd7,0x5d,0x2a, + 0xd6,0xb5,0x55,0xae, 0xba,0xeb,0xaa,0xd6, 0xb5,0x55,0x5c,0xcb, 0x0a,0x53,0x0a,0x50, + 0x2d,0x86,0x06,0x41, 0x60,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x2c,0x05,0x82,0xd6, 0xb5,0xad,0x6b,0x5b, 0x2c,0xb2,0xcb,0x20, + 0x00,0x00,0x29,0x42, 0x8a,0x28,0xa2,0x8a, 0x28,0xa2,0x8a,0x28, 0xa2,0x8a,0x28,0xb0, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x01,0x24, 0x92,0x49,0x24,0x92, 0x49,0x24,0x92,0x49, + 0x24,0x02,0xe9,0x14, 0x92,0x49,0x24,0x92, 0x49,0x24,0x92,0x44, 0xd0,0xee,0xba,0xe8, + 0x92,0x29,0x24,0x92, 0x29,0x22,0x4b,0xa5, 0xd7,0x40,0x00,0x00, 0x00,0x00,0x05,0xd2, + 0x05,0x00,0x92,0x00, 0x00,0x12,0x40,0x00, 0x00,0x01,0x6b,0x45, 0x5a,0xaf,0x00,0x00, + 0x00,0x28,0xa5,0xe9, 0x24,0x8a,0x29,0x24, 0x51,0x45,0x24,0x8a, 0x28,0xa4,0x91,0x45, + 0x24,0x92,0x45,0x14, 0x51,0x45,0x24,0x92, 0x45,0x24,0x92,0x45, 0x14,0x51,0x45,0x14, + 0x52,0x49,0x24,0x51, 0x45,0x24,0x8b,0x5e, 0x00,0x00,0x0b,0xd5, 0x15,0x6c,0x33,0x0b, + 0x2a,0xad,0x85,0xc2, 0xe1,0x73,0x6c,0x93, 0x01,0xff,0x8b,0xb9, 0x22,0x9c,0x28,0x48, + 0x4b,0xcb,0xd7,0x5c, 0x80, + // 0x495 = 1173 data bytes (25% including 4 header bytes) +}; + +static BZ2Manager_t geohittablekartobj_bin_mgr + = { geohittablekartobj_bin_bz2, sizeof(geohittablekartobj_bin_bz2), 0, 0 }; + +/////////////////////////////////////////////////////////////////////////////// + +BZ2Manager_t * GetCommonBZ2Manager ( file_format_t fform ) +{ + BZ2Manager_t *bm; + switch(fform) + { + case FF_ITEMSLT: + case FF_ITEMSLT_TXT: + bm = &itemslot_bin_mgr; + break; + + case FF_KMG: + case FF_KMG_TXT: + bm = &minigame_kmg_mgr; + break; + + case FF_OBJFLOW: + case FF_OBJFLOW_TXT: + bm = &objflow_bin_mgr; + break; + + case FF_GH_ITEM: + case FF_GH_ITEM_TXT: + bm = &geohittableitem_bin_mgr; + break; + + case FF_GH_IOBJ: + case FF_GH_IOBJ_TXT: + bm = &geohittableitemobj_bin_mgr; + break; + + case FF_GH_KART: + case FF_GH_KART_TXT: + bm = &geohittablekart_bin_mgr; + break; + + case FF_GH_KOBJ: + case FF_GH_KOBJ_TXT: + bm = &geohittablekartobj_bin_mgr; + break; + + default: + return 0; + } + + DecodeBZIP2Manager(bm); + return bm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global common /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsCOMMON() +{ + static VarMap_t vm = {0}; + if (!vm.used) + { + DefineObjectNameVars(&vm); + DefineParserVars(&vm); + + + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + { "NEWOBJ$OFF", NEWOBJ_OFF }, + { "NEWOBJ$REPLACE", NEWOBJ_REPLACE }, + { "NEWOBJ$SHRINK", NEWOBJ_SHRINK }, + { "NEWOBJ$GROW", NEWOBJ_GROW }, + + { "NEW_OBJECT", NEWOBJ__DEFAULT }, + { "OBJECT_SLOTS", OBJMGR_USUAL_SLOTS }, + { "MAX_OBJECT_ID", OBJMGR_USUAL_MAX_OBJID }, + + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(&vm,ip->name,ip->val); + } + + return &vm; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool WriteObjMgrToSetupSection +( + FILE *f, // output file + const object_mgr_t *om, // valid object manager + bool always, // false: write only if not standard + uint header, // 0: off + // 1: start with: [SECTION]\n @REVISION= + // 2: add 'section_sep' before step 1 + bool explain // true: insert extented explainations +) +{ + DASSERT(f); + DASSERT(om); + + if ( !always && IsStandardObjType(&om->otype) ) + return false; + + if (header) + { + if (header>1) + fputs(section_sep,f); + fprintf(f,"\r\n[SETUP]\r\nREVISION = %u\r\n",REVISION_NUM ); + } + + if (explain) + fputs(text_explain_objmgr_cr,f); + + fprintf(f, + "\r\n" + "NEW-OBJECT = NEWOBJ$%s\r\n" + "OBJECT-SLOTS = 0x%03x\r\n" + "MAX-OBJECT-ID = 0x%03x\r\n" + ,GetNewObjectName(om->otype.new_object) + ,om->otype.object_slots + ,om->otype.max_object_id + ); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SetupObjMgrBySI +( + object_mgr_t * om, // valid data + ScanInfo_t * si, // valid data + ccp name // name of command +) +{ + DASSERT(om); + DASSERT(si); + DASSERT(name); + + if ( *name == '@' ) + name++; + + if (!strcmp(name,"NEW-OBJECT")) + { + u32 val; + ScanU32SI(si,&val,1,0); + if ( val < NEWOBJ__N ) + { + om->otype.new_object = val; + DefineIntVar(&si->gvar,"NEW_OBJECT",val); + PRINT("NEW_OBJECT = %d",val); + } + return true; + } + + if (!strcmp(name,"OBJECT-SLOTS")) + { + u32 val; + ScanU32SI(si,&val,1,0); + if ( val > OBJMGR_MAX_SLOTS ) + val = OBJMGR_MAX_SLOTS; + om->otype.object_slots = val; + DefineIntVar(&si->gvar,"OBJECT_SLOTS",val); + PRINT("OBJECT_SLOTS = %d = 0x%x",val,val); + return true; + } + + if (!strcmp(name,"MAX-OBJECT-ID")) + { + u32 val; + ScanU32SI(si,&val,1,0); + if ( val > OBJMGR_MAX_OBJID ) + val = OBJMGR_MAX_OBJID; + om->otype.max_object_id = val; + DefineIntVar(&si->gvar,"MAX_OBJECT_ID",val); + PRINT("MAX_OBJECT_ID = %d = 0x%x",val,val); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// object_mgr_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeObjMgr ( object_mgr_t *om, file_format_t fform ) +{ + DASSERT(om); + memset(om,0,sizeof(*om)); + InitializeObjType(&om->otype,fform); + #if USE_OBJECT_MANAGER + if (om->otype.row_size) + SetupObjMgr(om,om->otype.row_size); + #endif +} + +//----------------------------------------------------------------------------- + +void ResetObjMgr ( object_mgr_t *om ) +{ + if (om) + { + FREE(om->list); + InitializeObjMgr(om,om->otype.fform); + } +} + +//----------------------------------------------------------------------------- + +ccp LogStateObjMgr ( const object_mgr_t *om ) +{ + if (!om) + return "-"; + + if (!om->list) + return LogObjType(&om->otype); + + const int PRIO_SIZE = OBJMGR_MAX_PRIO+1; + + uint usage_count = 0; + u16 count[PRIO_SIZE], max[PRIO_SIZE]; + memset(count,0,sizeof(count)); + memset(max,0,sizeof(max)); + + int i; + for ( i = 0; i < OBJMGR_MAX_SLOTS; i++ ) + { + const u8 prio = om->prio[i]; + if ( prio <= OBJMGR_MAX_PRIO ) + { + count[prio]++; + max[prio] = i; + if (prio) + usage_count++; + } + } + + char buf[200], *dest = buf; + dest = snprintfE(dest,buf+sizeof(buf), + "nr=%u/%u ds=%u %s", + usage_count, OBJMGR_MAX_SLOTS, om->row_data_size, + LogObjType(&om->otype) ); + + for ( i = 0; i < PRIO_SIZE; i++ ) + if (count[i]) + dest = snprintfE(dest,buf+sizeof(buf), + " P%u=%u,%x",i,count[i],max[i]); + + MARK_USED(count,max); + return CopyCircBuf0(buf,dest-buf); +} + +/////////////////////////////////////////////////////////////////////////////// +#if USE_OBJECT_MANAGER +/////////////////////////////////////////////////////////////////////////////// + +bool SetupObjMgr ( object_mgr_t *om, uint row_size ) +{ + DASSERT(om); + DASSERT( row_size <= OBJMGR_MAX_ROW_SIZE ); + + if ( row_size >= 2 && row_size <= OBJMGR_MAX_ROW_SIZE ) + { + FREE(om->list); + om->otype.row_size = row_size; + om->row_data_size = row_size - 2; + om->list = CALLOC(row_size,OBJMGR_MAX_SLOTS); + + uint obj_id; + u8 *dest = om->list; + for ( obj_id = 0; obj_id < OBJMGR_MAX_SLOTS; obj_id++ ) + { + write_be16(dest,obj_id); + dest += row_size; + } + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool SetupObjMgrByData ( object_mgr_t *om, cvp data, uint data_size, u8 prio ) +{ + bool stat = ScanObjType(&om->otype,data,data_size,data_size); + if (stat) + { + SetupObjMgr(om,om->otype.row_size); + + uint i; + const u8 *ptr = (u8*)data + om->otype.slot_offset; + for ( i = 0; i < om->otype.object_slots; i++, ptr += om->otype.row_size ) + InsertObjMgr(om,be16(ptr),ptr+2,prio); + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +u8 * InsertObjMgr ( object_mgr_t *om, uint obj_id, cvp data, u8 prio ) +{ + DASSERT(om); + if (!prio) + return RemoveObjMgr(om,obj_id); + + if ( om && om->list && obj_id < OBJMGR_MAX_SLOTS ) + { + u8 *dest = om->list + obj_id * om->otype.row_size; + if ( om->prio[obj_id] <= prio ) + { + om->prio[obj_id] = prio; + memcpy( dest+2, data, om->row_data_size ); + if ( om->max_prio < prio ) + om->max_prio = prio; + } + return dest; + } + return 0; +} + +//----------------------------------------------------------------------------- + +u8 * ClearObjMgr ( object_mgr_t *om, uint obj_id ) +{ + DASSERT(om); + if ( om && om->list && obj_id < OBJMGR_MAX_SLOTS ) + { + om->prio[obj_id] = 1; + u8 *dest = om->list + obj_id * om->otype.row_size; + memset( dest+2, 0, om->row_data_size ); + return dest; + } + return 0; +} + +//----------------------------------------------------------------------------- + +u8 * RemoveObjMgr ( object_mgr_t *om, uint obj_id ) +{ + DASSERT(om); + if ( om && om->list && obj_id < OBJMGR_MAX_SLOTS ) + { + om->prio[obj_id] = 0; + u8 *dest = om->list + obj_id * om->otype.row_size; + memset( dest+2, 0, om->row_data_size ); + return dest; + } + return 0; +} + +//----------------------------------------------------------------------------- + +u8 * GetObjMgr ( object_mgr_t *om, uint obj_id ) +{ + DASSERT(om); + if ( om && om->list && obj_id < OBJMGR_MAX_SLOTS ) + return om->list + obj_id * om->otype.row_size; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +u8 * CreateBinObjMgr +( + // return alloced data + uint *res_size, // not NULL: store size of alloced data + + const object_mgr_t + *om, // valid object manager + uint min_prio, // minimal prio + uint max_elem, // >0: limit number of elements + uint max_obj_id // >0: ignore objects with id ># +) +{ + DASSERT(om); + PRINT("CreateBinObjMgr( minp=%u, maxe=%u, maxo=%u )" + " list=%d, USE_OBJECT_MANAGER=%d\n", + min_prio, max_elem, max_obj_id, !om ? -1 : om->list!=0, + USE_OBJECT_MANAGER ); + PRINT(" -> %s\n",LogStateObjMgr(om)); + if ( !om || !om->list || !om->otype.row_size ) + return 0; + + if ( !max_elem || max_elem >= OBJMGR_MAX_SLOTS ) + max_elem = OBJMGR_MAX_SLOTS - 1; + const uint limit_obj_id = max_obj_id && max_obj_id < OBJMGR_MAX_SLOTS + ? max_obj_id : OBJMGR_MAX_SLOTS - 1; + + + //--- statistical analysis + + const int PRIO_SIZE = OBJMGR_MAX_PRIO+1; + + uint total_count = 0; + u16 count[PRIO_SIZE], max[PRIO_SIZE]; + memset(count,0,sizeof(count)); + memset(max,0,sizeof(max)); + + int i; + for ( i = 0; i < limit_obj_id; i++ ) + { + const u8 prio = om->prio[i]; + if ( prio >= min_prio && prio <= OBJMGR_MAX_PRIO ) + { + count[prio]++; + max[prio] = i; + total_count++; + } + } + + + //--- find elements to insert + + uint total_use = 0; + u16 use[OBJMGR_MAX_SLOTS]; + memset(use,0,sizeof(use)); + + int prio, max_obj_used = 0; + for ( prio = OBJMGR_MAX_PRIO; + prio >= min_prio && total_use < max_elem; prio-- ) + { + if (count[prio]) + { + uint i; + for ( i = 0; i < limit_obj_id; i++ ) + if ( prio == om->prio[i] ) + { + use[i]++; + if ( max_obj_used < i ) + max_obj_used = i; + if ( ++total_use >= max_elem ) + break; + } + } + } + + if (!max_obj_id) + max_obj_id = max_obj_used; + + PRINT(" -> FOUND: %u/%u/%u, max_obj=%x/%x/%x\n", + total_use, total_count, max_elem, + max_obj_used, max_obj_id, limit_obj_id ); + + + //--- calculate data size + + const uint data_size + = om->otype.slot_offset + + total_use * om->otype.row_size + + (max_obj_id+1) * sizeof(u16); + if (res_size) + *res_size = data_size; + u8 *data = MALLOC(data_size); + + + //--- fill data + + write_be16( data, total_use ); + write_be16( data+2, om->row_data_size/2 ); + + u8 *bin = data + om->otype.slot_offset; + u16 *ref = (u16*)( bin + total_use * om->otype.row_size ); + DASSERT( (u8*)ref + (max_obj_id+1) * sizeof(u16) == data + data_size ); + memset( ref, ~0, (max_obj_id+1) * sizeof(u16) ); + + uint idx = 0; + + for ( i = 0; i <= max_obj_id; i++ ) + { + if (!use[i]) + continue; + + memcpy(bin,om->list+i*om->otype.row_size,om->otype.row_size); + bin += om->otype.row_size; + ref[i] = htons(idx++); + } + DASSERT( idx == total_use ); + + return data; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VerifyObjMgr +( + const object_mgr_t + *om, // valid object manager + cvp data, // data to compare + uint data_size, // size of 'data' + ccp identify // string to identify the caller +) +{ + uint om_size; + u8 *om_data = CreateBinObjMgr(&om_size,om,1,0,0); + + bool status = false; + if (!om_data) + fprintf(stderr,"VERIFY OBJ-MGR[%s]: no data\n",identify); + else if ( om_size != data_size ) + fprintf(stderr, + "VERIFY OBJ-MGR[%s]: size differ: %u : %u\n", + identify, data_size, om_size ); + else if ( memcmp(om_data,data,data_size) ) + fprintf(stderr,"VERIFY OBJ-MGR[%s]: data differ\n",identify); + else + status = true; + + if (!status) + fprintf(stderr,"LOG: %s\n",LogStateObjMgr(om)); + + if ( !status && om_data ) + { + XDump_t xd; + InitializeXDump(&xd); + xd.f = stderr; + xd.indent = 6; + xd.format = XDUMPF_INT_2; + xd.endian = DC_BIG_ENDIAN; + xd.assumed_size = data_size < om_size ? data_size : om_size; + xd.min_width = om->otype.row_size; + while ( xd.min_width > 40 && !(xd.min_width&1) ) + xd.min_width /= 2; + + #if 0 + const uint off = om->otype.slot_offset; + if ( data_size <= off || om_size <= off || memcmp(data,om_data,off) ) + XDiff( &xd, data,data_size,true, om_data,om_size,true, 5,false ); + else + XDiff( &xd, data+off,data_size-off,true, + om_data+off,om_size-off,true, 5,false ); + #else + u64 break_list[] = { om->otype.slot_offset, om->otype.ref_offset, 0 }; + xd.addr_break = break_list; + XDiff( &xd, data,data_size,true, om_data,om_size,true, 5,false ); + #endif + ResetXDump(&xd); + } + FREE(om_data); + return status; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool VerifyBinObjMgr ( cvp data, uint data_size, ccp identify ) +{ + object_mgr_t om; + InitializeObjMgr(&om,FF_UNKNOWN); + SetupObjMgrByData(&om,data,data_size,1); + const bool stat = VerifyObjMgr(&om,data,data_size,identify); + ResetObjMgr(&om); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif // USE_OBJECT_MANAGER +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-common.h b/project/src/lib-common.h new file mode 100644 index 0000000..2f72c56 --- /dev/null +++ b/project/src/lib-common.h @@ -0,0 +1,849 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_COMMON_H +#define SZS_LIB_COMMON_H 1 + +#include "lib-std.h" + +struct BZ2Manager_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// USE_OBJECT_MANAGER /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------ +// USE_OBJECT_MANAGER: +// 0: only struct and initialization available +// 1: scan+compare after reading and before writing +// 2: additional use + verify +// 3: replace old code and support new features +//------------------------------------------------------ + +#ifdef TEST + #define USE_OBJECT_MANAGER 2 +#elif HAVE_WIIMM_EXT + #define USE_OBJECT_MANAGER 1 +#else + #define USE_OBJECT_MANAGER 0 +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// magics /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define ITEMSLT_TEXT_MAGIC "#ITEMSLT" +#define ITEMSLT_TEXT_MAGIC_NUM 0x234954454d534c54ull +#define ITEMSLT_SIZE6 1457 +#define ITEMSLT_SIZE12 1811 + +#define KMG_MAGIC "RKMG" +#define KMG_MAGIC_NUM 0x524b4d47 +#define KMG_TEXT_MAGIC "#KMG" +#define KMG_TEXT_MAGIC_NUM 0x234b4d47 + +#define KRT_MAGIC "RKGT" +#define KRT_MAGIC_NUM 0x524b4754 +#define KRT_TEXT_MAGIC "#KRT" +#define KRT_TEXT_MAGIC_NUM 0x234b5254 + +#define KRM_MAGIC "RKRM" +#define KRM_MAGIC_NUM 0x524b524d +#define KRM_TEXT_MAGIC "#KRM" +#define KRM_TEXT_MAGIC_NUM 0x234b524d + +#define OBJFLOW_TEXT_MAGIC8 "#OBJFLOW" +#define OBJFLOW_TEXT_MAGIC8_NUM 0x234f424a464c4f57ull +#define OBJFLOW_SIZE 38170 // [[obsolete]] + +#define GH_ITEM_TEXT_MAGIC8 "#GH-ITEM" +#define GH_ITEM_TEXT_MAGIC8_NUM 0x2347482d4954454dull + +#define GH_IOBJ_TEXT_MAGIC8 "#GH-IOBJ" +#define GH_IOBJ_TEXT_MAGIC8_NUM 0x2347482d494f424aull + +#define GH_KART_TEXT_MAGIC8 "#GH-KART" +#define GH_KART_TEXT_MAGIC8_NUM 0x2347482d4b415254ull + +#define GH_KOBJ_TEXT_MAGIC8 "#GH-KOBJ" +#define GH_KOBJ_TEXT_MAGIC8_NUM 0x2347482d4b4f424aull + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global common /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define OBJMGR_USUAL_SLOTS 0x13c // usual number of object slots +#define OBJMGR_MAX_SLOTS 0x400 // max number of object slots + +#define OBJMGR_USUAL_MAX_OBJID 0x2f3 // usual max allowed object id +#define OBJMGR_MAX_OBJID 0x3ff // max allowed object id + +#define OBJMGR_MAX_ROW_SIZE 0x80 +#define OBJMGR_MAX_PRIO 0xff + +//----------------------------------------------------------------------------- +// [[new_object_t]] + +typedef enum new_object_t +{ + NEWOBJ_OFF, + NEWOBJ_REPLACE, + NEWOBJ_SHRINK, + NEWOBJ_GROW, + + NEWOBJ__N, + NEWOBJ__DEFAULT = NEWOBJ_OFF +} +new_object_t; + +//----------------------------------------------------------------------------- +// [[object_type_t]] + +typedef struct object_type_t +{ + file_format_t + fform; // FF_UNKNOWN or known file format + bool is_objflow; // FALSE: unknown, TRUE: is ObjFlow + bool is_geohit; // FALSE: unknown, TRUE: is GeoHit* + bool is_kart; // GeoHit only: FALSE: is Item, TRUE: is Kart + bool is_obj; // GeoHit only: TRUE: is *Obj variant + + uint row_size; // data size of object icluding object id + uint slot_offset; // offset of slot list + uint ref_offset; // offset of reference list + + new_object_t new_object; // how to handle new objects + uint object_slots; // init by OBJMGR_USUAL_SLOTS + uint max_object_id; // init by OBJMGR_USUAL_MAX_OBJID +} +object_type_t; + +//----------------------------------------------------------------------------- + +struct BZ2Manager_t * GetCommonBZ2Manager ( file_format_t fform ); +const VarMap_t * SetupVarsCOMMON(); + +ccp GetNewObjectName ( new_object_t no ); + +//----------------------------------------------------------------------------- + +void InitializeObjType ( object_type_t *ot, file_format_t fform ); +void SetObjTypeByFF ( object_type_t *ot, file_format_t fform ); +bool ScanObjType ( object_type_t *ot, cvp data, uint data_size, uint file_size ); +ccp LogObjType ( const object_type_t *ot ); +file_format_t GetObjTypeFF ( const object_type_t *ot, bool want_text ); + +static inline bool IsStandardObjType ( const object_type_t *ot ) +{ + DASSERT(ot); + return ot->new_object == NEWOBJ_OFF + && ot->object_slots == OBJMGR_USUAL_SLOTS + && ot->max_object_id == OBJMGR_USUAL_MAX_OBJID; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// object_mgr_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + OM_PRIO_UNUSED, + OM_PRIO_DELETED, + OM_PRIO_ORIG, + OM_PRIO_DEFAULT, + OM_PRIO_INSERTED, +}; + +//----------------------------------------------------------------------------- +// [[object_mgr_t]] + +typedef struct object_mgr_t +{ + object_type_t otype; // object type + + //--- object table + + uint row_data_size; // data size of each object row := row_size-2 + u8 *list; // data, size=OBJMGR_MAX_SLOTS * row_size + u8 prio[OBJMGR_MAX_SLOTS]; + // >0: object in use + u8 max_prio; // max prio ever used +} +object_mgr_t; + +//----------------------------------------------------------------------------- + +void InitializeObjMgr ( object_mgr_t *om, file_format_t fform ); +void ResetObjMgr ( object_mgr_t *om ); +ccp LogStateObjMgr ( const object_mgr_t *om ); + +//----------------------------------------------------------------------------- +#if USE_OBJECT_MANAGER +//----------------------------------------------------------------------------- + +bool SetupObjMgr ( object_mgr_t *om, uint row_size ); +bool SetupObjMgrByData ( object_mgr_t *om, cvp data, uint data_size, u8 prio ); + +u8 * InsertObjMgr ( object_mgr_t *om, uint obj_id, cvp data, u8 prio ); +u8 * ClearObjMgr ( object_mgr_t *om, uint obj_id ); +u8 * RemoveObjMgr ( object_mgr_t *om, uint obj_id ); +u8 * GetObjMgr ( object_mgr_t *om, uint obj_id ); + +u8 * CreateBinObjMgr +( + // return alloced data + uint *res_size, // not NULL: store size of alloced data + + const object_mgr_t + *om, // valid object manager + uint min_prio, // minimal prio + uint max_elem, // >0: limit number of elements + uint max_obj_id // >0: ignore objects with id ># +); + +bool VerifyObjMgr +( + const object_mgr_t + *om, // valid object manager + cvp data, // data to compare + uint data_size, // size of 'data' + ccp identify // string to identify the caller +); + +bool VerifyBinObjMgr ( cvp data, uint data_size, ccp identify ); + +/////////////////////////////////////////////////////////////////////////////// + +bool WriteObjMgrToSetupSection +( + FILE *f, // output file + const object_mgr_t *om, // valid object manager + bool always, // false: write only if not standard + uint header, // 0: off + // 1: start with: [SECTION]\n @REVISION= + // 2: add 'section_sep' before step 1 + bool explain // true: insert extented explainations +); + +//----------------------------------------------------------------------------- + +bool SetupObjMgrBySI +( + object_mgr_t * om, // valid data + ScanInfo_t * si, // valid data + ccp name // name of command +); + +//----------------------------------------------------------------------------- +#endif // USE_OBJECT_MANAGER +//----------------------------------------------------------------------------- +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// itemslot_bin_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[itemslot_bin_t]] + +typedef struct itemslot_bin_t +{ + /*000*/ u8 n_table; // number of tables (6 or 12) + + //--- probabilities for for racing + + /*001*/ u8 gp_player_rows; // players in Grand Prix + u8 gp_player_cols; + u8 gp_player [12][19]; + + /*0e7*/ u8 gp_enemy_rows; // enemies in Grand Prix + u8 gp_enemy_cols; + u8 gp_enemy [12][19]; + + /*1cd*/ u8 vs_player_rows; // players in VS + u8 vs_player_cols; + u8 vs_player [12][19]; + + /*2b3*/ u8 vs_enemy_rows; // enemies in VS + u8 vs_enemy_cols; + u8 vs_enemy [12][19]; + + /*399*/ u8 vs_online_rows; // players in online VS + u8 vs_online_cols; + u8 vs_online [12][19]; + + /*47f*/ u8 special_rows; // special item boxes + u8 special_cols; + u8 special [16][19]; + + //--- 0x5b1 = 1457 bytes until here + //--- probabilities for for battles + + /*5b1*/ u8 balloon_player_rows; // players in Balloon Battle + u8 balloon_player_cols; + u8 balloon_player [3][19]; + + /*5ec*/ u8 balloon_enemy_rows; // enemies in Balloon Battle + u8 balloon_enemyv; + u8 balloon_enemy [3][19]; + + /*627*/ u8 coin_player_rows; // players in Coin Runners + u8 coin_player_cols; + u8 coin_player [3][19]; + + /*662*/ u8 coin_enemy_rows; // enemies in Coin Runners + u8 coin_enemy_cols; + u8 coin_enemy [3][19]; + + /*69d*/ u8 balloon_online_rows; // players in online Balloon Battle + u8 balloon_online_cols; + u8 balloon_online [3][19]; + + /*6d8*/ u8 coin_online_rows; // players in online Coin Runners + u8 coin_online_cols; + u8 coin_online [3][19]; + + //--- 0x713 = 1811 bytes total +} +__attribute__ ((packed)) itemslot_bin_t; + +//----------------------------------------------------------------------------- +// [[itemslot_t]] + +typedef struct itemslot_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform_source; // file format of input file + + bool add_battle; // true: add battle tables (default) + bool use_slt; // true: use '.slt' instead of '.bin' + + + //--- parser helpers + + int revision; // tool revision + bool is_pass2; // true: ITEMSLOT compiler runs pass2 + + + //--- data + + itemslot_bin_t data; // data +} +itemslot_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeITEMSLOT ( itemslot_t * itemslot, file_format_t fform ); +void SetupBZ2MgrITEMSLOT ( itemslot_t * itemslot, bool reload_bin ); +void ResetITEMSLOT ( itemslot_t * itemslot ); + +bool GetOriginalITEMSLOT ( itemslot_bin_t *dest ); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanTextITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // file format +); + +//----------------------------------------------------------------------------- + +enumError ScanITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + const void * data, // data to scan + uint data_size, // size of 'data' + ccp fname // not NULL: Analyse file extention for '.slt' +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +//----------------------------------------------------------------------------- + +enumError SaveRawITEMSLOT +( + itemslot_t * itemslot, // pointer to valid ITEMSLOT + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextITEMSLOT +( + const itemslot_t * itemslot, // pointer to valid ITEMSLOT + ccp fname, // filename of destination + bool set_time, // true: set time stamps + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >1: write only modified records + // >9: minimize output +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// minigame_kmg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[minigame_kmg_t]] + +typedef struct minigame_kmg_head_t +{ + //--- header + + /*000*/ u8 magic[4]; // always "RKMG" + /*004*/ be32_t size; // file size + /*008*/ be32_t unknown_008; // unknown + /*00c*/ be32_t coin_offset; // Offset to Coin Runners Data, relative to 'data' + /*010*/ u8 data[]; // file date +} +__attribute__ ((packed)) minigame_kmg_head_t; + +//-----------------------------------------------------------------------------# +// [[minigame_kmg_t]] + +typedef struct minigame_kmg_t +{ + /*000*/ minigame_kmg_head_t + head; // file header + + //--- balloon tables + /*010*/ be16_t dur_balloon[10][11]; // duration in seconds (always 0xb4=180) + /*0ec*/ be16_t off_0ec[10][11]; // ? + + //--- 10 balloon parameters + /*1c8*/ be16_t off_1c8; // Number of points when hit a player + /*1ca*/ be16_t off_1ca; // Number of extra points when defeating + // a player (originally unused). + /*1cc*/ be16_t off_1cc; // Number of points when losing all balloons. + /*1ce*/ be16_t off_1ce; // Number of points when hit by a player + // (originally unused) + /*1d0*/ be16_t off_1d0; // ? + /*1d2*/ be16_t off_1d2; // ? + /*1d4*/ be16_t off_1d4; // ? + /*1d6*/ be16_t off_1d6; // ? + /*1d8*/ be16_t off_1d8; // ? + /*1da*/ be16_t off_1da; // ? + + //--- coin runners tables + /*1dc*/ be16_t dur_coin[10][11]; // duration in seconds (always 0xb4=180) + /*2b8*/ be16_t off_2b8[10][11]; // ? + /*394*/ be16_t start_coins[10][11]; // number of coins at start + /*470*/ be16_t max_coins[10][11]; // max number of coins + /*54c*/ be16_t off_5c4[10][11]; // ? + /*628*/ be16_t off_628[10][11]; // ? + + //--- 10 coin runners parameters + /*704*/ be16_t off_704; // ? + /*706*/ be16_t off_706; // ? + /*708*/ be16_t off_708; // ? + /*70a*/ be16_t off_70a; // ? + /*70c*/ be16_t off_70c; // ? + /*70e*/ be16_t off_70e; // ? + /*710*/ be16_t off_710; // ? + /*712*/ be16_t off_712; // Min num of coins lost when falling down + /*714*/ be16_t off_714; // Min num of coins lost when hit by a player + /*716*/ be16_t off_716; // Percentage of coins to lose (0-100) + /*718*/ +} +__attribute__ ((packed)) minigame_kmg_t; + +//----------------------------------------------------------------------------- +// [[minigame_t]] + +typedef struct minigame_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform_source; // file format of input file + + + //--- parser helpers + + int revision; // tool revision + bool is_pass2; // true: MINIGAME compiler runs pass2 + + + //--- data + + minigame_kmg_t data; // raw data, valid if magic is ok +} +minigame_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMINIGAME ( minigame_t * minigame, file_format_t fform ); +void SetupBZ2MgrMINIGAME ( minigame_t * minigame, bool reload_bin ); +void ResetMINIGAME ( minigame_t * minigame ); + +bool GetOriginalMINIGAME ( minigame_kmg_t *dest ); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanTextMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // file format +); + +//----------------------------------------------------------------------------- + +enumError ScanMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +//----------------------------------------------------------------------------- + +enumError SaveRawMINIGAME +( + minigame_t * minigame, // pointer to valid MINIGAME + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextMINIGAME +( + const minigame_t * minigame, // pointer to valid MINIGAME + ccp fname, // filename of destination + bool set_time, // true: set time stamps + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >9: minimize output +); + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKmgLimit ( ccp arg ); +int ScanOptKmgCopy ( ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ObjFlow.bin /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[objflow_bin_t]] + +typedef struct objflow_bin_t +{ + u16 id; + char name[0x20]; + char resources[0x40]; + s16 param[9]; +} +__attribute__ ((packed)) objflow_bin_t; + +//----------------------------------------------------------------------------- +// [[objflow_t]] + +typedef struct objflow_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // FF_OBJFLOW | FF_OBJFLOW_TXT + + //--- scan helpers + + int revision; // tool revision + bool is_pass2; // true: OBJFLOW compiler runs pass2 + + //--- data + + // if USE_OBJECT_MANAGER < 2 => use obj_mgr.otype only + object_mgr_t obj_mgr; // object manager + + #if USE_OBJECT_MANAGER < 3 + u8 bin[OBJFLOW_SIZE]; // raw binary data + #endif +} +objflow_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeOBJFLOW ( objflow_t * objflow ); +void ResetOBJFLOW ( objflow_t * objflow ); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanTextOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +//----------------------------------------------------------------------------- + +enumError SaveRawOBJFLOW +( + objflow_t * objflow, // pointer to valid OBJFLOW + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextOBJFLOW +( + const objflow_t * objflow, // pointer to valid OBJFLOW + ccp fname, // filename of destination + bool set_time, // true: set time stamps + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >9: minimize output +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GeoHit*.bin /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[geohit_bin_t]] + +typedef struct geohit_bin_t +{ + u16 id; + u16 param[]; +} +__attribute__ ((packed)) geohit_bin_t; + +//----------------------------------------------------------------------------- +// [[geohit_t]] + +typedef struct geohit_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform_source; // file format of input file + + #if USE_OBJECT_MANAGER < 3 + uint n_rec; // number of records + #endif + + + //--- parser helpers + + int revision; // tool revision + bool is_pass2; // true: GEOHIT compiler runs pass2 + + + //--- data + + // if USE_OBJECT_MANAGER < 2 => use obj_mgr.otype only + object_mgr_t obj_mgr; // object manager + + #if USE_OBJECT_MANAGER < 3 + u8 *bin; // pointer to binary data, alloced + uint bin_size; // size of 'bin' + #endif + +} +geohit_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeGEOHIT ( geohit_t * geohit, file_format_t fform ); +void SetupBZ2MgrGEOHIT ( geohit_t * geohit, file_format_t fform, bool reload_bin ); +void ResetGEOHIT ( geohit_t * geohit ); + +geohit_bin_t * GetBinGEOHIT ( cvp data, uint index ); + +void PrintParamGEOHIT ( FILE *f, uint fw, cvp param0, cvp cmp0, int n ); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanTextGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // file format +); + +//----------------------------------------------------------------------------- + +enumError ScanGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +//----------------------------------------------------------------------------- + +enumError SaveRawGEOHIT +( + geohit_t * geohit, // pointer to valid GEOHIT + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextGEOHIT +( + const geohit_t * geohit, // pointer to valid GEOHIT + ccp fname, // filename of destination + bool set_time, // true: set time stamps + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >9: minimize output +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_COMMON_H diff --git a/project/src/lib-ctcode.c b/project/src/lib-ctcode.c new file mode 100644 index 0000000..11b60e4 --- /dev/null +++ b/project/src/lib-ctcode.c @@ -0,0 +1,4248 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-ctcode.h" +#include "lib-lecode.h" +#include "lib-szs.h" +#include "lib-xbmg.h" +#include "lib-bzip2.h" +#include "lib-image.h" +#include "dclib-utf8.h" + +#include "db-ctcode.h" +#include "lib-ctdata.c" +#include "ctcode.inc" +#include + +#define TNAME_BUF_SIZE 1000 +#define HIDDEN_CUP 0x7f + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int MusicID2TrackId ( uint music_id, int no_track, int not_found ) +{ + if ( music_id >= MKW_MUSIC_MIN_ID && music_id <= MKW_MUSIC_MAX_ID ) + { + music_id = ( music_id - MKW_MUSIC_MIN_ID ) / 2; + DASSERT( music_id < MKW_N_MUSIC ); + const int tid = music_info[music_id].track; + return tid < 0 ? no_track : tid; + } + return not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetName16 ( u16 *dest, ccp src ) +{ + DASSERT(dest); + DASSERT(src); + + memset(dest,0,CT_NAME_SIZE*sizeof(*dest)); + ScanString16BMG(dest,CT_NAME_SIZE-1,src,-1,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetTrackBMG +( + ctcode_t *ctcode, // valid CTCODE + bmg_t *bmg, // destination BMG + uint tidx, // index of the track + ccp text // any text +) +{ + DASSERT(ctcode); + DASSERT(bmg); + + u16 *dest = ctcode->crs->data[tidx].tname; + memset(dest,0,CT_NAME_SIZE*sizeof(*dest)); + + if ( text && *text ) + { + bmg_item_t *bi = InsertItemBMG(bmg,ctcode->ctb.track_name1.beg+tidx,0,0,0); + DASSERT(bi); + AssignItemScanTextBMG(bi,text,-1); + + const uint max_len = bi->len < CT_NAME_SIZE-1 ? bi->len : CT_NAME_SIZE-1; + memcpy(dest,bi->text,max_len*sizeof(*dest)); + } + else + { + bmg_item_t *bi = FindItemBMG(bmg,ctcode->ctb.track_name1.beg+tidx); + if (bi) + FreeItemBMG(bi); + } +} + +//----------------------------------------------------------------------------- + +static inline void SetTrackFile +( + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + ccp text // any text +) +{ + SetTrackBMG(ctcode,&ctcode->track_file,tidx,text); +} + +//----------------------------------------------------------------------------- + +static inline void SetTrackString +( + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + ccp text // any text +) +{ + SetTrackBMG(ctcode,&ctcode->track_string,tidx,text); +} + +//----------------------------------------------------------------------------- + +static inline void SetTrackIdentifier +( + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + ccp text // any text +) +{ + if ( !text || !*text || !strcmp(text,"-") ) + text = 0; + SetTrackBMG(ctcode,&ctcode->track_ident,tidx,text); +} + +/////////////////////////////////////////////////////////////////////////////// + +// [[file-name]] +static void SetTrackInfo16 +( + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + const u16 *text, // any text + int len // length of 'text', or -1 +) +{ + DASSERT(ctcode); + DASSERT(text); + + if ( len < 0 ) + len = GetLength16BMG(text,1000); + + u16 *dest = ctcode->crs->data[tidx].tname; + memset(dest,0,CT_NAME_SIZE*sizeof(*dest)); + + if ( text && len ) + { + bmg_item_t *bi = InsertItemBMG( &ctcode->track_string, + ctcode->ctb.track_name1.beg+tidx,0,0,0); + DASSERT(bi); + AssignItemText16BMG(bi,text,len); + + const uint max_len = bi->len < CT_NAME_SIZE-1 ? bi->len : CT_NAME_SIZE-1; + memcpy(dest,bi->text,max_len*sizeof(*dest)); + } + else + { + bmg_item_t *bi = FindItemBMG(&ctcode->track_string, + ctcode->ctb.track_name1.beg+tidx); + if (bi) + FreeItemBMG(bi); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetRawTrackInfo +( + char *buf, // dest buffer + uint size, // size of 'buf' + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + const bmg_item_t *bi // NULL or bmg text +) +{ + DASSERT(buf); + DASSERT(size); + DASSERT(ctcode); + +// [[file-name]] + if ( !bi || !bi->len ) + bi = FindItemBMG(&ctcode->track_string,tidx+ctcode->ctb.track_name1.beg); + + const u16 *src; + uint len; + if ( bi && bi->len ) + { + src = bi->text; + len = bi->len; + } + else + { + src = ctcode->crs->data[tidx].tname; + len = GetLength16BMG(src,CT_NAME_SIZE); + } + + const u16 *end = src + len; + char *dest = buf; + char *dest_end = buf + size - 4; + while ( dest < dest_end && src < end ) + dest = PrintUTF8Char(dest,ntohs(*src++)); + *dest = 0; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetTrackFile +( + char *buf, // dest buffer + uint size, // size of 'buf' + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + const bmg_item_t *bi // NULL or bmg text +) +{ + DASSERT(buf); + DASSERT(size); + DASSERT(ctcode); + + if ( !bi || !bi->len ) + bi = FindItemBMG(&ctcode->track_file,tidx+ctcode->ctb.track_name1.beg); + + if ( bi && bi->len ) + PrintString16BMG(buf,size,bi->text,bi->len,BMG_UTF8_MAX,0,opt_bmg_colors); + else + { + const u16 *src = ctcode->crs->data[tidx].tname; + const uint len = GetLength16BMG(src,CT_NAME_SIZE); + PrintString16BMG(buf,size,src,len,BMG_UTF8_MAX,0,opt_bmg_colors); + } + return buf; +} + +//----------------------------------------------------------------------------- + +static ccp GetTrackString +( + char *buf, // dest buffer + uint size, // size of 'buf' + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + const bmg_item_t *bi // NULL or bmg text +) +{ + DASSERT(buf); + DASSERT(size); + DASSERT(ctcode); + + if ( !bi || !bi->len ) + bi = FindItemBMG(&ctcode->track_string,tidx+ctcode->ctb.track_name1.beg); + + if ( bi && bi->len ) + PrintString16BMG(buf,size,bi->text,bi->len,BMG_UTF8_MAX,0,opt_bmg_colors); + else + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +static ccp GetTrackIdentifier +( + char *buf, // dest buffer + uint size, // size of 'buf' + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + const bmg_item_t *bi // NULL or bmg text +) +{ + DASSERT(buf); + DASSERT(size); + DASSERT(ctcode); + + if ( !bi || !bi->len ) + bi = FindItemBMG(&ctcode->track_ident,tidx+ctcode->ctb.track_name1.beg); + + if ( bi && bi->len ) + PrintString16BMG(buf,size,bi->text,bi->len,BMG_UTF8_MAX,0,opt_bmg_colors); + else + *buf = 0; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +const u16 * GetTrackName16 +( + uint *tlen, // not nULL: store length of result here + ctcode_t *ctcode, // valid CTCODE + uint tidx, // index of the track + const bmg_item_t *bi // NULL or bmg text +) +{ + DASSERT(ctcode); + +// [[file-name]] + if ( !bi || !bi->len ) + bi = FindItemBMG(&ctcode->track_string,tidx+ctcode->ctb.track_name1.beg); + + if ( bi && bi->len ) + { + if (tlen) + *tlen = bi->len; + return bi->text; + } + + const ctcode_crs1_data_t *td = ctcode->crs->data + tidx; + if (tlen) + *tlen = GetLength16BMG(td->tname,sizeof(td->tname)/sizeof(*td->tname)); + return td->tname; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintMusicID ( uint mid, bool force_hex ) +{ + if ( !force_hex && mid >= MKW_MUSIC_MIN_ID && mid <= MKW_MUSIC_MAX_ID ) + { + const uint mid0 = mid - MKW_MUSIC_MIN_ID; + if (!(mid0&1)) + return music_info[mid0/2].name; + } + + if (!mid) + return "0"; + + const int bufsize = 8; + char *buf = GetCircBuf(bufsize); + snprintf(buf,bufsize,"0x%02x",mid); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintPropertyID ( uint tid, bool force_hex ) +{ + if ( tid < MKW_N_TRACKS && !force_hex ) + return track_info[tid].abbrev; + + const int bufsize = 8; + char *buf = GetCircBuf(bufsize); + snprintf(buf,bufsize,"0x%02x",tid); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ctcode_cup1_data_t * GetCupData ( const ctcode_t * ctcode, uint cup_idx ) +{ + DASSERT(ctcode); + if ( cup_idx < ctcode->n_racing_cups ) + return ctcode->cup_racing + cup_idx; + + cup_idx -= ctcode->n_racing_cups; + return cup_idx < ctcode->n_battle_cups ? ctcode->cup_battle + cup_idx : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool CheckLEFlagsUsage ( ctcode_t * ctcode ) +{ + PRINT("USE_LE_FLAGS=%d\n",ctcode->use_le_flags); + if (!ctcode->use_le_flags) + { + uint tid; + for ( tid = 0; tid < CODE_MAX_TRACKS; tid++ ) + if (ctcode->le_flags[tid]) + { + ctcode->use_le_flags = true; + break; + } + PRINT("USE_LE_FLAGS=%d\n",ctcode->use_le_flags); + } + + return ctcode->use_le_flags; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CTCODE base interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeCTCODE ( ctcode_t * ctcode, ct_mode_t ct_mode ) +{ + DASSERT(ctcode); + memset(ctcode,0,sizeof(*ctcode)); + SetupCtBMG( &ctcode->ctb, ct_mode, opt_ct_mode ); + PRINT("CTCODE: %s\n",GetCtBMGIdentification(&ctcode->ctb,true)); + + ctcode->use_lecode = lecode_enabled; + ctcode->fname = EmptyString; + InitializeBMG(&ctcode->track_file); + InitializeBMG(&ctcode->track_string); + InitializeBMG(&ctcode->track_ident); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetCTCODE ( ctcode_t * ctcode ) +{ + DASSERT(ctcode); + ResetBMG(&ctcode->track_file); + ResetBMG(&ctcode->track_string); + ResetBMG(&ctcode->track_ident); + ResetDataCTCODE(ctcode,false); + FreeString(ctcode->fname); + FreeString(ctcode->next_cup_name); + InitializeCTCODE(ctcode,ctcode->ctb.ct_mode); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupUsedSlotList ( u8 *slot_list, uint n_slots, uint le_phase ) +{ + DASSERT(slot_list); + DASSERT(n_slots>=0x100); + + memset(slot_list, 0, n_slots ); + if (le_phase) + { + memset( slot_list + MKW_ARENA_BEG, + 2, + LE_FIRST_LOWER_CT_SLOT - MKW_ARENA_BEG ); + if ( le_phase == 1 ) + { + const uint max = n_slots < LE_FIRST_UPPER_CT_SLOT + ? n_slots : LE_FIRST_UPPER_CT_SLOT; + if ( max > 0xff ) + memset(slot_list+0xff,2,max-0xff); // 0xff == random + } + } + else + { + memset( slot_list + MKW_ARENA_BEG, 2, MKW_N_ARENAS ); + memset( slot_list + 0xff, 2, n_slots - 0xff ); + + slot_list[0x36] = 2; // ring_mission = Galaxy Colosseum (music_id=0xc9) + slot_list[0x37] = 2; // winningrun_demo + slot_list[0x38] = 2; // loser_demo + slot_list[0x39] = 2; // draw_demo + slot_list[0x3a] = 2; // ending_demo + + slot_list[0x42] = 2; // invalid -> can be used? (99%) + slot_list[0x43] = 2; // no track selected + } +} + +//----------------------------------------------------------------------------- + +// 0:free, 1:used, 2:reserved +u8 ctcode_used_slot[USED_SLOT_MAX] = {0x7f}; + +static void SetupUsedSlots() +{ + if ( ctcode_used_slot[0] == 0x7f ) + SetupUsedSlotList(ctcode_used_slot,sizeof(ctcode_used_slot),0); +} + +//----------------------------------------------------------------------------- + +void SetupUsedSlotsCTCODE ( ctcode_t * ctcode ) +{ + SetupUsedSlots(); + if ( ctcode->ctb.le_phase ) + { + SetupUsedSlotList( ctcode->used_slot, + sizeof(ctcode->used_slot), ctcode->ctb.le_phase ); + ctcode->n_tracks = LE_FIRST_LOWER_CT_SLOT; + } + else + { + SetupUsedSlotList(ctcode->used_slot,sizeof(ctcode->used_slot),0); + memcpy(ctcode->used_slot,ctcode_used_slot,sizeof(ctcode_used_slot)); + ctcode->n_tracks = 0x3b; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetDataCTCODE ( ctcode_t * ctcode, bool create_empty ) +{ + DASSERT(ctcode); + + //--- cups (CUP1) + + if (ctcode->cup_alloced) + { + ctcode->cup_alloced = false; + FREE(ctcode->cup); + } + ctcode->cup = 0; + ctcode->cup_size = 0; + + ctcode->cup_racing = 0; + ctcode->n_racing_cups = 0; + ctcode->max_racing_cups = 0; + + ctcode->cup_battle = 0; + ctcode->n_battle_cups = 0; + ctcode->max_battle_cups = 0; + + ctcode->n_unused_cups = 0; + + + //--- tracks (CRS1) + + if (ctcode->crs_alloced) + { + ctcode->crs_alloced = false; + FREE(ctcode->crs); + } + ctcode->crs = 0; + ctcode->crs_size = 0; + + ctcode->n_tracks = 0; + ctcode->max_tracks = 0; + + + //--- misc + + SetupUsedSlotsCTCODE(ctcode); + CalcCupRefCTCODE(ctcode,true); + #if USE_NEW_CONTAINER_CTC + ResetContainer(&ctcode->container); + #else + FreeContainer(ctcode->old_container); + ctcode->old_container = 0; + #endif + + + + //--- create empty image? + + if (!create_empty) + return; + + char namebuf[100]; // no more chars needed + + + //--- create empty cups + + const uint max_battle_cups = 2; + const uint max_racing_cups = ( ctcode->ctb.le_phase + ? LE_CODE_MAX_CUPS : CT_CODE_DEFAULT_CUPS ) + - max_battle_cups ; + + ctcode->cup_size = sizeof(ctcode_cup1_head_t) + + (max_racing_cups+max_battle_cups) * sizeof(ctcode_cup1_data_t); + ctcode->cup = CALLOC(ctcode->cup_size,1); + ctcode->cup_racing = ctcode->cup->data; + ctcode->cup_battle = ctcode->cup_racing + max_racing_cups; + memcpy(ctcode->cup,cup1_header,sizeof(cup1_header)); + + //ctcode->n_racing_cups = 0; + ctcode->max_racing_cups = max_racing_cups; + ctcode->n_unused_cups = max_racing_cups; + ctcode->n_battle_cups = max_battle_cups; + ctcode->max_battle_cups = max_battle_cups; + + uint cidx, tidx = MKW_ARENA_BEG; + for ( cidx = 0; cidx < max_battle_cups; cidx++ ) + { + ctcode_cup1_data_t *cd = ctcode->cup_battle + cidx; + snprintf(namebuf,sizeof(namebuf),"Battle %u",cidx+1); + SetName16(cd->name,namebuf); + + if ( !ctcode->ctb.le_phase ) + { + uint t; + for ( t = 0; t < 5; t++ ) + cd->track_id[t] = htonl(tidx++); + } + } + + + //--- create empty tracks: data + + const uint max_tracks = ctcode->ctb.le_phase ? LE_CODE_MAX_TRACKS : CT_CODE_MAX_TRACKS; + ctcode->crs_size = sizeof(ctcode_crs1_head_t) + + max_tracks * sizeof(ctcode_crs1_data_t); + ctcode->crs = CALLOC(ctcode->crs_size,1); + + struct tab_t + { + u32 tidx; + u32 music_id; + u32 property_id; + ccp name; + }; + + static const struct tab_t tab[] = + { + { 0x36, 0xc9, 0x36, "ring_mission" }, + { 0x37, 0, 0, "winningrun_demo" }, + { 0x38, 0, 0, "loser_demo" }, + { 0x39, 0, 0, "draw_demo" }, + { 0x3a, 0x75, 0x08, "ending_demo" }, + // { 0x42, 0, 0, "invalid" }, + { 0x43, 0, 0, "- " TOOLSET_LONG " v" VERSION " r" REVISION }, + {0,0,0,0} + }; + + const struct tab_t *ptr; + for ( ptr = tab; ptr->name; ptr++ ) + { + ctcode_crs1_data_t *td = ctcode->crs->data + ptr->tidx; + td->music_id = htonl(ptr->music_id); + td->property_id = htonl(ptr->property_id); + + ccp src = ptr->name; + if ( *src == '-' ) + src++; + else + strcpy(td->filename,ptr->name); + + char buf[200], *dest = buf; + bool upper = true; + while (*src) + { + int ch = (uchar)*src++; + if ( ch == '_' ) + { + upper = true; + ch = ' '; + } + else if (upper) + { + upper = false; + ch = toupper((int)ch); + } + *dest++ = ch; + } + *dest = 0; + SetTrackString(ctcode,ptr->tidx,buf); + } + + + //--- create empty tracks: header + + memcpy(ctcode->crs,crs1_header,sizeof(crs1_header)); + + uint i; + const u32 val = htonl(crs1_property); + for ( i = 0; i < 8; i++ ) + ctcode->crs->property[i] = val; + + //ctcode->n_tracks = 0; + ctcode->max_tracks = max_tracks; + ctcode->used_arenas = 5 * max_battle_cups; + + ctcode_crs1_data_t *td = ctcode->crs->data; + for ( tidx = 0; tidx < max_tracks; tidx++, td++ ) + { + if ( tidx >= MKW_N_TRACKS && tidx < MKW_N_TRACKS + MKW_N_ARENAS ) + { + const TrackInfo_t *info = arena_info + (tidx-MKW_N_TRACKS); + SetName16(td->tname,info->name_en); + StringCopyS(td->filename,sizeof(td->filename),info->track_fname); + + if ( ctcode->ctb.le_phase ) + { + td->music_id = htonl(arena_info[tidx-MKW_N_TRACKS].music_id); + td->property_id = htonl(tidx); + } + } + else if (!ctcode->used_slot[tidx]) + { + snprintf(namebuf,sizeof(namebuf),"Slot %u = 0x%03x",tidx,tidx); + SetName16(td->tname,namebuf); + snprintf(namebuf,sizeof(namebuf),"slot_%02x",tidx); + StringCopyS(td->filename,sizeof(td->filename),namebuf); + + td->music_id = htonl(0x75); + td->property_id = htonl(0); + td->cup_id = htonl(0xff); + } + } + + + //--- LE-CODE: battles again + + if ( ctcode->ctb.le_phase ) + { + ctcode_cup1_data_t *cd = ctcode->cup_battle; + uint aid; + for ( aid = 0; aid < MKW_N_ARENAS; aid++ ) + { + uint tid = arena_info[aid].def_index; + cd[tid/5].track_id[tid%5] = htonl(aid+MKW_N_TRACKS); + } + //HexDump16(stdout,0,0,cd[0].track_id,sizeof(cd[0].track_id)); + //HexDump16(stdout,0,0,cd[1].track_id,sizeof(cd[1].track_id)); + } + + + //--- terminate + + ctcode->cupref_dirty = false; + + PRINT("STAT/CUP: racing: %u/%u, battle: %u/%u, unused: %u\n", + ctcode->n_racing_cups, ctcode->max_racing_cups, + ctcode->n_battle_cups, ctcode->max_battle_cups, + ctcode->n_unused_cups ); + PRINT("STAT/TRACK: %u/%u\n", ctcode->n_tracks, ctcode->max_tracks ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcCupRefCTCODE ( ctcode_t * ctcode, bool force ) +{ + DASSERT(ctcode); + if ( !force && !ctcode->cupref_dirty ) + return; + + + //--- clear data + + ctcode->used_tracks = 0; + ctcode->used_arenas = 0; + memset(ctcode->cupref,0,sizeof(ctcode->cupref)); + + + //--- source available? + + if ( !ctcode->cup || !ctcode->crs ) + { + ctcode->cupref_dirty = true; + return; + } + ctcode->cupref_dirty = false; + + + //--- cup loop + + uint cidx; + for ( cidx = 0; cidx < ctcode->n_racing_cups + ctcode->n_battle_cups; cidx++ ) + { + const bool is_battle = cidx >= ctcode->n_racing_cups; + const ctcode_cup1_data_t *cd = GetCupData(ctcode,cidx); + DASSERT(cd); + + uint ti; + for ( ti = 0; ti < 4 + is_battle; ti++ ) + { + const uint tidx = be32(cd->track_id+ti); + if ( tidx < ctcode->max_tracks ) + { + ctcode_cupref_t *cr = ctcode->cupref + tidx; + if (!cr->n) + { + if (is_battle) + ctcode->used_arenas++; + else + ctcode->used_tracks++; + } + + if (is_battle) + cr->n_battle++; + else + cr->n_racing++; + + if ( cr->n < CT_CODE_MAX_CUPREF ) + { + cr->cup[cr->n] = is_battle + ? cidx - ctcode->n_racing_cups + + ctcode->max_racing_cups + : cidx; + cr->idx[cr->n] = ti; + cr->n++; + } + } + } + } + + PRINT("STAT/REF: nT=%u, nA=%u, total=%u/%u\n", + ctcode->used_tracks, ctcode->used_arenas, + ctcode->n_tracks, ctcode->max_tracks ); + + //HexDump16(stderr,0,0x16,ctcode->cupref+0,sizeof(*ctcode->cupref)); + //HexDump16(stderr,0,0x16,ctcode->cupref+1,sizeof(*ctcode->cupref)); + + + //--- hidden tracks: create reference + + ParamField_t ref; + InitializeParamField(&ref); + char namebuf[TNAME_BUF_SIZE]; + + uint tidx; + for ( tidx = 0; tidx < ctcode->n_tracks; tidx++ ) + { + if (ctcode->hidden[tidx]) + continue; + + GetTrackIdentifier(namebuf,sizeof(namebuf),ctcode,tidx,0); + if (*namebuf && !FindParamField(&ref,namebuf) ) + InsertParamField(&ref,namebuf,false,tidx,0); + } + + + //--- hidden tracks: use reference + + for ( tidx = 0; tidx < ctcode->n_tracks; tidx++ ) + { + if (!ctcode->hidden[tidx]) + continue; + + GetTrackIdentifier(namebuf,sizeof(namebuf),ctcode,tidx,0); + if (*namebuf) + { + const ParamFieldItem_t *it = FindParamField(&ref,namebuf); + if (it) + memcpy( ctcode->cupref+tidx, + ctcode->cupref+it->num, + sizeof(*ctcode->cupref) ); + } + } + + ResetParamField(&ref); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrepareExportCTCODE ( ctcode_t * ctcode ) +{ + DASSERT(ctcode); + DASSERT(ctcode->cup); + DASSERT(ctcode->cup_racing); + DASSERT(ctcode->cup_battle); + DASSERT(ctcode->crs); + + if ( !ctcode->cup || !ctcode->cup_racing || !ctcode->cup_battle || !ctcode->crs ) + return; + + //--- setup ctcode header + + ctcode_header_t *ch = &ctcode->ct_head; + memcpy(ch,ctcode_header,sizeof(*ch)); + u32 off = sizeof(*ch); + + + //--- setup CUP1 + + DASSERT( ctcode->max_racing_cups >= ctcode->n_racing_cups ); + DASSERT( ctcode->cup_racing == ctcode->cup->data ); + ctcode->max_racing_cups = ctcode->n_racing_cups; + + ctcode_cup1_data_t *battle = ctcode->cup_racing + ctcode->n_racing_cups; + DASSERT( ctcode->cup_battle >= battle ); + if ( ctcode->cup_battle > battle ) + { + memmove( battle,ctcode->cup_battle, ctcode->n_battle_cups * sizeof(*battle) ); + ctcode->cup_battle = battle; + ctcode->cupref_dirty = true; + } + + ctcode->cup->n_racing_cups = htonl(ctcode->n_racing_cups); + ctcode->cup->n_battle_cups = htonl(ctcode->n_battle_cups); + const u8 *end = (u8*)(battle + ctcode->n_battle_cups); + DASSERT( end <= (u8*)ctcode->cup + ctcode->cup_size ); + ctcode->cup_size = end - (u8*)ctcode->cup; + ctcode->cup->size = htonl(ctcode->cup_size); + ch->sect_info[0].off = htonl(off); + off += ctcode->cup_size; + + + //--- setup CRS1 + + DASSERT( ctcode->max_tracks >= ctcode->n_tracks ); + ctcode->max_tracks = ctcode->n_tracks; + end = (u8*)( ctcode->crs->data + ctcode->n_tracks ); + DASSERT( end <= (u8*)ctcode->crs + ctcode->crs_size ); + ctcode->crs_size = end - (u8*)ctcode->crs; + ctcode->crs->size = htonl(ctcode->crs_size); + ctcode->crs->n_tracks = htonl(ctcode->n_tracks); + ch->sect_info[1].off = htonl(off); + //off += ctcode->crs_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PatchNamesCTCODE ( ctcode_t * ctcode ) +{ + uint cidx; + for ( cidx = 0; cidx < ctcode->n_racing_cups + ctcode->n_battle_cups; cidx++ ) + { + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,ctcode->ctb.rcup_name.beg+cidx); + if ( bi && bi->len ) + { + ctcode_cup1_data_t *cd = GetCupData(ctcode,cidx); + memset(cd->name,0,sizeof(cd->name)); + const uint len = bi->len < CT_NAME_SIZE-1 ? bi->len : CT_NAME_SIZE-1; + memcpy(cd->name,bi->text,len*sizeof(*cd->name)); + } + } + + uint tidx; + for ( tidx = 0; tidx < ctcode->n_tracks; tidx++ ) + { + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,ctcode->ctb.track_name1.beg+tidx); + if ( bi && bi->len ) + SetTrackInfo16(ctcode,tidx,bi->text,bi->len); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrepareSaveCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp opt_image_dir = 0; +bool opt_dynamic_ctcode = false; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum { MAX_KEY = 200 }; +typedef struct track_info_t +{ + char key[MAX_KEY]; // sort key + uint order; // index reflecting inital order + uint tidx; // track index +} +track_info_t; + +static int cmp_track_info ( const track_info_t * a, const track_info_t * b ) +{ + const int stat = strcoll(a->key,b->key); + return stat ? stat : a->order < b->order ? -1 : 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ReorderTracksCTCODE ( ctcode_t * ctcode ) +{ + DASSERT(ctcode); + PRINT("ReorderTracksCTCODE()\n"); + setlocale(LC_COLLATE,""); + + if ( !opt_order_by || !*opt_order_by ) + return ERR_NOTHING_TO_DO; + + bmg_t bmg; + enumError err = LoadXBMG(&bmg,true,opt_order_by,true,false); + if (!err) + { + track_info_t info[CODE_MAX_TRACKS]; + memset(info,0,sizeof(info)); + + track_info_t *ptr = info; + uint cidx; + for ( cidx = opt_order_all ? 0 : 8; + cidx < ctcode->n_racing_cups; cidx++ ) + { + const ctcode_cup1_data_t *cd = ctcode->cup->data + cidx; + + uint ti; + for ( ti = 0; ti < 4; ti++, ptr++ ) + { + DASSERT( ptr < info + ctcode->max_tracks ); + const uint tidx = be32(cd->track_id+ti); + const bmg_item_t *bi + = FindItemBMG(&bmg,tidx+ctcode->ctb.track_name1.beg); + if ( bi && bi->len ) + { + *ptr->key = 'A'; + PrintString16BMG(ptr->key+1,sizeof(ptr->key)-1, + bi->text,bi->len,BMG_UTF8_MAX,0,opt_bmg_colors); + } + else + *ptr->key = 'B'; + ptr->tidx = tidx; + ptr->order = ptr - info; + + noPRINT("KEY: %02x %s\n",*ptr->key,ptr->key+1); + } + } + + const uint n = ptr - info; + if ( n > 1 ) + qsort( info, n, sizeof(*info), (qsort_func)cmp_track_info ); + + track_info_t *src = info; + for ( cidx = opt_order_all ? 0 : 8; + cidx < ctcode->n_racing_cups; cidx++ ) + { + ctcode_cup1_data_t *cd = ctcode->cup->data + cidx; + + uint ti; + for ( ti = 0; ti < 4; ti++, src++ ) + { + noPRINT("KEY: %3u,%02x: %02x %s\n", + src->order, src->tidx, *src->key, src->key+1 ); + write_be32(cd->track_id+ti,src->tidx); + } + } + } + + ResetBMG(&bmg); + CalcCupRefCTCODE(ctcode,true); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrepareSaveCTCODE ( ctcode_t * ctcode ) +{ + DASSERT(ctcode); + PRINT("PrepareSaveCTCODE()\n"); + + if (ctcode->save_prepared) + return; + ctcode->save_prepared++; + + ReorderTracksCTCODE(ctcode); + if (opt_write_tracks) + SaveTrackTableCTCODE(ctcode,opt_write_tracks,false); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DumpCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void hexdump + ( FILE *f, uint indent, const void *vdata, uint size, ccp title ) +{ + DASSERT(f); + DASSERT(title); + + if ( !vdata || !size ) + return; + + fprintf(f,"\n%*s%s\n",indent+2,"",title); + const u8* data = vdata; + + uint addr; + for ( addr = 0; addr < size; ) + { + fprintf(f,"%*x:",indent+6,addr); + uint i; + for ( i = 0; i < 4 && addr < size; i++, addr+=4 ) + fprintf(f," %8x",be32(data+addr)); + fputc('\n',f); + } +}; + +//----------------------------------------------------------------------------- + +void DumpCTCODE ( FILE *f, uint indent, ctcode_t * ctcode ) +{ + DASSERT(f); + DASSERT(ctcode); + indent = NormalizeIndent(indent); + const uint numindent = indent + 5; + char namebuf[TNAME_BUF_SIZE]; + + + //--- statistics + + fprintf(f,"\n%*sStatistics:\n\n",indent,""); + fprintf(f,"%*u racing cups defined.\n",numindent,ctcode->n_racing_cups); + fprintf(f,"%*u battle cups defined.\n",numindent,ctcode->n_battle_cups); + fprintf(f,"%*u tracks defined.\n",numindent,ctcode->n_tracks); + fprintf(f,"%*u track slots available.\n",numindent,ctcode->max_tracks); + + + //--- track reference + + u8 count[CODE_MAX_TRACKS] = {0}; + u8 usage[CODE_MAX_TRACKS] = {0}; + uint invalid_track_ref = 0; + const uint max = ctcode->max_tracks; + + uint cidx; + for ( cidx = 0; cidx < ctcode->n_racing_cups; cidx++ ) + { + const ctcode_cup1_data_t *cd = ctcode->cup_racing + cidx; + DASSERT(cd); + uint ti; + for ( ti = 0; ti < 4; ti++ ) + { + uint tidx = be32(cd->track_id+ti); + if ( tidx < max ) + { + count[tidx]++; + usage[tidx] |= 1; + } + else + invalid_track_ref++; + } + } + + for ( cidx = 0; cidx < ctcode->n_battle_cups; cidx++ ) + { + const ctcode_cup1_data_t *cd = ctcode->cup_battle + cidx; + DASSERT(cd); + uint ti; + for ( ti = 0; ti < 5; ti++ ) + { + uint tidx = be32(cd->track_id+ti); + if ( tidx < max ) + { + count[tidx]++; + usage[tidx] |= 2; + } + else + invalid_track_ref++; + } + } + + if (invalid_track_ref) + fprintf(f,"%*u invalid `cup to track´ references.\n", + numindent, invalid_track_ref ); + + fprintf(f,"\n%*sTrack usage:\n\n",indent,""); + uint tidx = 0; + while ( tidx < max ) + { + uint end = tidx + 0x40; + if ( end > max ) + end = max; + fprintf(f,"%*s%02x-%02x:",indent+2,"",tidx,end-1); + uint i; + for ( i = 0; i < 0x40 && tidx < max; i++, tidx++ ) + { + if (!(i%8)) + fputc(' ',f); + if (!usage[tidx]) + { + #if 1 + fputc('-',f); + #else + ctcode_crs1_data_t *td = ctcode->crs->data + tidx; + fputc( td->filename[0] && td->filename[1] ? '+' : '-', f ); + #endif + } + else if ( usage[tidx] == 1 ) + { + const uint n = count[tidx]; + if ( n < 10 ) + fputc('0'+n,f); + else + fputc('*',f); + } + else if ( usage[tidx] == 2 ) + { + const uint n = count[tidx] < 26 ? count[tidx] : 26; + fputc('a'-1+n,f); + } + else + fputc('?',f); + } + fputc('\n',f); + } + + + //--- memory dump + + PrepareExportCTCODE(ctcode); + + if ( ctcode->fform == FF_CT_TEXT ) + fprintf(f,"\n%*sSections in virtual binary code:\n\n",indent,""); + else + fprintf(f,"\n%*sSections:\n\n",indent,""); + + const ctcode_header_t *ch = &ctcode->ct_head; + const u32 n_sect = be32(&ch->n_sect); + + uint sect; + for ( sect = 0; sect < n_sect; sect++ ) + { + u32 off = be32(&ch->sect_info[sect].off); + if (!off) + continue; + fprintf(f,"%*u: %6x : %s\n", + numindent, sect, + off, PrintID(ch->sect_info[sect].name,4,0) ); + } + + + //--- hexdump of headers + + if (long_count) + { + if ( ctcode->fform == FF_CT_TEXT ) + fprintf(f,"\n%*sHexdump of headers in virtual binary code:\n",indent,""); + else + fprintf(f,"\n%*sHexdump of headers:\n",indent,""); + + hexdump(f,indent,&ctcode->ct_head,sizeof(ctcode->ct_head),"CT-CODE Header"); + hexdump(f,indent,ctcode->cup,sizeof(*ctcode->cup),"Section CUP1 Header"); + hexdump(f,indent,ctcode->crs,sizeof(*ctcode->crs),"Section CRS1 Header"); + } + + + //--- dump a track listing + + if ( long_count > 2 ) + { + fprintf(f,"\n%*sTechnical Track list:\n\n",indent,""); + CalcCupRefCTCODE(ctcode,false); + + const ctcode_cupref_t *cr = ctcode->cupref; + + uint tidx; + for ( tidx = 0; tidx < ctcode->max_tracks; tidx++, cr++ ) + { + if (cr->n_racing) + { + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,tidx+ctcode->ctb.track_name1.beg); + GetTrackString(namebuf,sizeof(namebuf),ctcode,tidx,bi); + const ctcode_crs1_data_t *td = ctcode->crs->data + tidx; + + fprintf(f,"%*s%s [M=%s,P=%s,S=0x%02x,F=%s]\n", + indent+2,"", namebuf, + PrintMusicID(ntohl(td->music_id),false), + PrintPropertyID(ntohl(td->property_id),false), + tidx, + td->filename ); + } + } + } + + + //--- terminate + + fputc('\n',f); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup ctcode parser /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsCTCODE() +{ + static VarMap_t vm = {0}; + if (!vm.used) + DefineMkwVars(&vm); + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan CTCODE helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanSetup ( ctcode_t *ctcode, ScanInfo_t * si ) +{ + // nothing to do (ignore all yet) + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static char * ReplaceName ( char * str, ccp search, ccp replace ) +{ + ccp pos = strstr(str,search); + if ( !pos || !*search ) + return str; + + char fb_buf[1000]; + FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf)); + + + const int search_len = strlen(search); + const int repl_len = strlen(replace); + char *src = str; + do + { + AppendFastBuf(fb,src,pos-src); + AppendFastBuf(fb,replace,repl_len); + src = (char*)pos + search_len; + pos = strstr(src,search); + + } while (pos); + + AppendFastBuf(fb,src,strlen(src)); + FreeString(str); + return MoveFromFastBufString(fb); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int InsertTrackIntoCup ( ctcode_t *ctcode, uint tidx, bool hidden ); + +static void AddWiimmCup ( ctcode_t *ctcode ) +{ + PRINT("+++++ ADD WIIMM CUP +++++\n"); + ctcode->add_wiimm_cup = 0; + + uint slot; + for ( slot = LE_SLOT_RND_ALL; slot <= LE_SLOT_RND_NEW; slot++ ) + { + ctcode->used_slot[slot] = 1; + if ( ctcode->n_tracks < slot+1 ) + ctcode->n_tracks = slot+1; + + ctcode_crs1_data_t *td = ctcode->crs->data + slot; + td->cup_id = htonl( InsertTrackIntoCup(ctcode,slot,false) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static int InsertTrackIntoCup ( ctcode_t *ctcode, uint tidx, bool hidden ) +{ + DASSERT(ctcode); + DASSERT( tidx < ctcode->max_tracks ); + +// [[2do]] [[hidden]] + + if (hidden) + { + ctcode->tracks_defined++; + return HIDDEN_CUP; + } + + uint cidx = ctcode->n_racing_cups; + if (ctcode->track_of_cup) + { + DASSERT( cidx > 0 ); + DASSERT( ctcode->track_of_cup < 4 ); + cidx--; + } + else if ( ctcode->add_wiimm_cup + && ctcode->ctb.le_phase + && cidx == 8 ) + { + AddWiimmCup(ctcode); + // redo this function again + return InsertTrackIntoCup(ctcode,tidx,hidden); + } + else + { + if ( cidx == ctcode->max_racing_cups ) + { + ERROR0(ERR_WARNING,"No more track cups available!\n"); + return -1; + } + ctcode->n_racing_cups++; + + char buf[20]; + ctcode_cup1_data_t *cd = GetCupData(ctcode,cidx); + ccp cname = ctcode->next_cup_name; + if ( cname && *cname ) + SetName16(cd->name,cname); + else + { + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,ctcode->ctb.rcup_name.beg+cidx); + if ( bi && bi->len ) + { + memset(cd->name,0,sizeof(cd->name)); + const uint len = bi->len < CT_NAME_SIZE-1 ? bi->len : CT_NAME_SIZE-1; + memcpy(cd->name,bi->text,len*sizeof(*cd->name)); + } + else + { + snprintf(buf,sizeof(buf),"Cup %02x",cidx); + SetName16(cd->name,buf); + } + } + FreeString(ctcode->next_cup_name); + ctcode->next_cup_name = 0; + } + + noPRINT("INSERT TRACK %u INTO CUP %u.%u/%u\n", + tidx, cidx, ctcode->track_of_cup, ctcode->max_racing_cups ); + + ctcode->cup_racing[cidx].track_id[ctcode->track_of_cup++] = htonl(tidx); + if ( ctcode->track_of_cup == 4 ) + ctcode->track_of_cup = 0; + + ctcode->tracks_defined++; + + return cidx; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ctcode_crs1_data_t * GetNextTrackSlot ( ctcode_t *ctcode, bool hidden ) +{ + DASSERT(ctcode); + + //--- next_slot available + + uint slot = 0; + while (ctcode->n_next_slot) + { + slot = ctcode->next_slot[--ctcode->n_next_slot]; + if ( !ctcode->used_slot[slot] || ctcode->force_slot[ctcode->n_next_slot] ) + { + found: + ctcode->used_slot[slot] = 1; + if ( ctcode->n_tracks < slot+1 ) + ctcode->n_tracks = slot+1; + + ctcode_crs1_data_t *td = ctcode->crs->data + slot; + td->cup_id = htonl( InsertTrackIntoCup(ctcode,slot,hidden) ); + return td; + } + } + + //--- search first free slot + + for ( slot = 0; slot < ctcode->max_tracks; slot++ ) + if (!ctcode->used_slot[slot]) + goto found; + + ERROR0(ERR_WARNING,"No more track slots available!\n"); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ctcode_crs1_data_t * GetNextArenaSlot + ( ctcode_t *ctcode, uint prop, ScanInfo_t * si ) +{ + if (!ctcode->ctb.le_phase) + { + static int err_done = 0; + if (!err_done++) + ERROR0(ERR_WARNING,"Battle arenas only supported for LE-CODE!\n"); + return 0; + } + + const uint arena = prop - MKW_N_TRACKS; + if ( arena >= MKW_N_ARENAS ) + { + ERROR0(ERR_WARNING, + "Invalid arena slot 0x%02x, use A11..A25 (0x%x..0x%x) instead: %s @%u\n", + prop, MKW_N_TRACKS, MKW_N_TRACKS + MKW_N_ARENAS - 1, + si->cur_file->name, si->cur_file->line ); + return 0; + } + + if (ctcode->arena_usage[arena]) + { + ERROR0(ERR_WARNING, + "Arena slot 0x%02x already used: %s @%u\n", + prop, si->cur_file->name, si->cur_file->line ); + return 0; + } + ctcode->arena_usage[arena]++; + ctcode->assigned_arenas++; + + return ctcode->crs->data + prop; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanRTL_Option ( ctcode_t *ctcode, ScanInfo_t * si ) +{ + DASSERT(ctcode); + DASSERT(si); + + ScanFile_t *sf = si->cur_file; + ccp start = sf->ptr; + + while ( NextCharSI(si,false) == '%' ) + sf->ptr++; + + char name[50]; + const uint namelen = ScanNameSI(si,name,sizeof(name),true,true,0); + if (!namelen) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing name for option: [%s @%u]: %.*s\n", + sf->name, sf->line, (int)(eol - start), start ); + } + return ERR_WARNING; + } + + if ( NextCharSI(si,false) == '=' ) + sf->ptr++; + + if (!strcmp(name,"LE-PHASE")) // [[obsolete]] since 2019-12 + { + long num; + enumError err = ScanUValueSI(si,&num,false); + if (err) + return err; + ctcode->use_lecode = num > 0; + PRINT("%%LE-PHASE: lecode=%d\n",ctcode->use_lecode); + } + else if (!strcmp(name,"LE-FLAGS")) + { + long num; + enumError err = ScanUValueSI(si,&num,false); + if (err) + return err; + ctcode->use_le_flags = num > 0; + PRINT("%%LE-FLAGS = %d\n",ctcode->use_le_flags); + } + else if (!strcmp(name,"REPLACE-AT")) + { + long num; + enumError err = ScanUValueSI(si,&num,false); + if (err) + return err; + ctcode->replace_at = num > 0; + PRINT("%%REPLACE-AT = %d\n",ctcode->use_le_flags); + } + else if (!strcmp(name,"WIIMM-CUP")) + { + long num; + enumError err = ScanUValueSI(si,&num,false); + if (err) + return err; + ctcode->add_wiimm_cup = num > 0; + PRINT("%%WIIMM-CUP = %d\n",ctcode->use_le_flags); + } + else + { + if ( si->no_warn <= 0 ) + ERROR0(ERR_WARNING, + "Unknown option [%s @%u]: %s\n", + sf->name, sf->line, name ); + return ERR_WARNING; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanRTL_Nintendo ( ctcode_t *ctcode, ScanInfo_t * si ) +{ + DASSERT(ctcode); + DASSERT(si); + + if (ctcode->tracks_defined) + { + ccp eol = FindNextLineFeedSI(si,true); + ScanFile_t *sf = si->cur_file; + ERROR0(ERR_WARNING, + "The 'N' command can only be used before" + " the first track definition [%s @%u]: %.*s\n", + sf->name, ctcode->tracks_defined /*sf->line*/, (int)(eol - sf->ptr), sf->ptr ); + GotoEolSI(si); + return ERR_WARNING; + } + + DEFINE_VAR(val); + enumError err = ScanExprSI(si,&val); + if (err) + return err; + uint mode = GetIntV(&val); + const uint flag_hex = mode & RTL_N_F_HEX; + const uint flag_wii = mode & RTL_N_F_WII; + + struct coldef_t { uint flag; const char name[8]; }; + static const struct coldef_t coltab[] = + { + { RTL_N_F_RED1, "red1" }, + { RTL_N_F_RED2, "red2" }, + { RTL_N_F_RED3, "red3" }, + { RTL_N_F_RED4, "red4" }, + { RTL_N_F_YELLOW, "yellow" }, + { RTL_N_F_GREEN, "green" }, + { RTL_N_F_BLUE1, "blue1" }, + { RTL_N_F_BLUE2, "blue2" }, + { RTL_N_F_WHITE, "white" }, + { RTL_N_F_CLEAR, "clear" }, + + { 0, "" } + }; + + ccp col = 0; + static const struct coldef_t *colptr; + for ( colptr = coltab; colptr->flag; colptr++ ) + { + if ( mode & colptr->flag ) + { + col = colptr->name; + break; + } + } + + mode &= RTL_N_M_MODE; + if ( mode == RTL_N_HIDE || mode == RTL_N_SHOW || mode == RTL_N_SWAP ) + { + const TrackInfo_t *info = track_info; + ctcode_crs1_data_t *td = ctcode->crs->data; + const u32 *tpos = mode == RTL_N_SWAP ? track_pos_swap : track_pos; + + uint ti; + for ( ti = 0; ti < MKW_N_TRACKS; ti++, info++, td++ ) + { + //--- here we insert the correct cup index ... + + const uint tidx = tpos[ti]; + InsertTrackIntoCup(ctcode,tidx,false); + + //--- and here we fill the track data independent of the cup index + + td->music_id = htonl(info->music_id); + td->property_id = htonl(ti); + td->cup_id = htonl(info->def_slot/10-1); + + char buf1[50], buf2[80]; + + ccp name = info->name_en; + bool have_prefix = info->def_slot >= 50; + if ( !have_prefix && flag_wii ) + { + snprintf(buf1,sizeof(buf1),"Wii %s",name); + name = buf1; + have_prefix = true; + } + + if ( have_prefix && col ) + { + ccp space = strchr(name,' '); + if (space) + { + snprintf(buf2,sizeof(buf2),"\\c{%s}%.*s \\c{OFF}%s", + col, (int)(space-name), name, space+1 ); + name = buf2; + } + } + SetName16(td->tname,name); + SetTrackFile(ctcode,ti,info->track_fname); + + if (flag_hex) + snprintf(td->filename,sizeof(td->filename),"%02x",ti); + else + StringCopyS(td->filename,sizeof(td->filename),info->track_fname); + } + memset(ctcode->used_slot,1,MKW_N_TRACKS); + ctcode->tracks_defined = MKW_N_TRACKS; + if ( mode == RTL_N_HIDE ) + ctcode->n_racing_cups = 0; + ctcode->cmd_n_used = true; + } + + + if ( ctcode->ctb.le_phase ) + { + const TrackInfo_t *info = arena_info; + uint ti; + for ( ti = MKW_N_TRACKS; ti < MKW_N_TRACKS+MKW_N_ARENAS; ti++, info++ ) + SetTrackFile(ctcode,ti,info->track_fname); + + // special files + SetTrackFile(ctcode,0x36,"ring_mission"); + SetTrackFile(ctcode,0x37,"winningrun_demo"); + SetTrackFile(ctcode,0x38,"loser_demo"); + SetTrackFile(ctcode,0x39,"draw_demo"); + SetTrackFile(ctcode,0x3a,"ending_demo"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanRTL_Slot + ( ctcode_t *ctcode, ScanInfo_t * si, bool mark_reserved ) +{ + DASSERT(ctcode); + DASSERT(si); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + sf->disable_comma++; + + enumError err = ERR_OK; + + uint idx = ctcode->max_tracks; + for(;;) + { + char ch = NextCharSI(si,false); + if (!ch) + break; + + if ( ch == ',' ) + { + sf->ptr++; + continue; + } + + long n1, n2; + err = ScanUValueSI(si,&n1,0); + if (err) + break; + + if ( NextCharSI(si,false) == ':' ) + { + sf->ptr++; + err = ScanUValueSI(si,&n2,0); + if (err) + break; + } + else + n2 = n1; + + const bool force = NextCharSI(si,false) == '!'; + if (force) + sf->ptr++; + + if ( n1 < 0 ) + n1 = 0; + if ( ++n2 > ctcode->max_tracks ) + n2 = ctcode->max_tracks; + noPRINT("SLOT: %02lx:%02lx = %lu:%lu%s\n",n1,n2,n1,n2,force ? " /FORCE" : ""); + + if ( n1 >= n2 ) + continue; + + if (mark_reserved) + { + if (force) + memset(ctcode->used_slot+n1,2,n2-n1); + else + for ( ; n1 < n2; n1++ ) + if (!ctcode->used_slot[n1]) + ctcode->used_slot[n1] = 2; + } + else + for ( ; idx > 0 && n1 < n2; n1++ ) + { + idx--; + ctcode->next_slot[idx] = n1; + ctcode->force_slot[idx] = force; + } + } + + if (!mark_reserved) + { + ctcode->n_next_slot = ctcode->max_tracks - idx; + if (ctcode->n_next_slot) + { + memmove(ctcode->next_slot,ctcode->next_slot+idx,ctcode->n_next_slot); + memmove(ctcode->force_slot,ctcode->force_slot+idx,ctcode->n_next_slot); + } + } + + sf->disable_comma--; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanRTL_Track + ( ctcode_t *ctcode, ScanInfo_t * si, bool is_arena, bool hidden ) +{ + DASSERT(ctcode); + DASSERT(si); + //ScanFile_t *sf = si->cur_file; + //DASSERT(sf); + + char buf[100]; + + + //--- scan music id + + ulong music_id; + enumError err = ScanUValueSI(si,(long*)&music_id,0); + if (err) + return err; + + if ( NextCharSI(si,false) == '!' ) + si->cur_file->ptr++; + else + { + if ( music_id < MKW_N_TRACKS ) + music_id = track_info[music_id].music_id; + else if ( music_id < MKW_N_TRACKS + MKW_N_ARENAS ) + music_id = arena_info[music_id-MKW_N_TRACKS].music_id; + + if ( MusicID2TrackId(music_id,music_id,-1) == -1 ) + music_id = track_info[0].music_id; + } + SkipCharSI(si,';'); + + + //--- scan property id + + ulong property_id; + err = ScanUValueSI(si,(long*)&property_id,0); + if (err) + return err; + + if ( NextCharSI(si,false) == '!' ) + si->cur_file->ptr++; + else if (!is_arena) + property_id &= 0x1f; + SkipCharSI(si,';'); + + + //--- scan le-flags + + le_flags_t le_flags = 0; + if (ctcode->use_le_flags) + { + long flags; + err = ScanUValueSI(si,&flags,0); + if (err) + return err; + le_flags = flags; + SkipCharSI(si,';'); + } + + + //--- scan track_file, track_string and track_ident => create fname + + char *track_file = 0, *track_string = 0, *track_ident = 0; + char fname[100]; + + err = ConcatStringsSI(si,&track_file,0); + if (err) + { + abort: + FreeString(track_file); + FreeString(track_string); + FreeString(track_ident); + return err; + } + + + //--- setup 'fname' and remove unwanted characters + + *fname = 0; + if (track_file) + { + + char *src = track_file, *dest = fname; + while ( *src && dest < fname+sizeof(fname) ) + { + int ch = *src++; + if ( isalnum(ch) || strchr("+-._",ch) ) + *dest++ = ch; + } + *dest = 0; + } + + if ( NextCharSI(si,false) == ';' ) + { + SkipCharSI(si,';'); + err = ConcatStringsSI(si,&track_string,0); + if (err) + goto abort; + + if ( NextCharSI(si,false) == ';' ) + { + SkipCharSI(si,';'); + err = ConcatStringsSI(si,&track_ident,0); + if (err) + goto abort; + } + } + + CheckEolSI(si); + + + //--- insert new track + + ctcode_crs1_data_t *td = is_arena + ? GetNextArenaSlot(ctcode,property_id,si) + : GetNextTrackSlot(ctcode,hidden); + if (!td) + return ERR_WARNING; + + const int tidx = td - ctcode->crs->data; + if (ctcode->replace_at) + { + PRINT(">>> REPLACE: %s\n",track_string); + snprintf(buf,sizeof(buf),"%03X",tidx); + track_string = ReplaceName(track_string,"@SLOT@",buf); + track_string = ReplaceName(track_string,"@@","@"); + PRINT(">>> REPLACED: %s\n",track_string); + } + + SetTrackFile(ctcode,tidx,track_file); + SetTrackString(ctcode,tidx,track_string); + SetTrackIdentifier(ctcode,tidx,track_ident); + + td->music_id = htonl(music_id); + td->property_id = htonl(property_id); + + ctcode->property[tidx] = property_id; + ctcode->music[tidx] = music_id; + ctcode->le_flags[tidx] = le_flags; + ctcode->hidden[tidx] = hidden != 0; + + if (!*fname) + { + if (ctcode->use_lecode) + snprintf(fname,sizeof(fname),"%03x",tidx); + else + snprintf(fname,sizeof(fname),"slot_%02x",tidx); + } + StringCopyS(td->filename,sizeof(td->filename),fname); + + FreeString(track_file); + FreeString(track_string); + FreeString(track_ident); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanRacingTrackList ( ctcode_t *ctcode, ScanInfo_t * si ) +{ + DASSERT(ctcode); + DASSERT(si); + + ctcode->use_le_flags = 0; + + enumError max_err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + enumError err = ERR_OK; + switch (ch) + { + case '%': + err = ScanRTL_Option(ctcode,si); + break; + + case 'n': + case 'N': + sf->ptr++; + err = ScanRTL_Nintendo(ctcode,si); + break; + + case 'c': + case 'C': + ctcode->track_of_cup = 0; + sf->ptr++; + err = ConcatStringsSI(si,&ctcode->next_cup_name,0); + break; + + case 's': + case 'S': + sf->ptr++; + err = ScanRTL_Slot(ctcode,si,false); + break; + + case 'x': + case 'X': + sf->ptr++; + err = ScanRTL_Slot(ctcode,si,true); + break; + + case 't': + case 'T': + sf->ptr++; + err = ScanRTL_Track(ctcode,si,false,false); + break; + + case 'h': + case 'H': + sf->ptr++; + err = ScanRTL_Track(ctcode,si,false,true); + break; + + case 'a': + case 'A': + sf->ptr++; + err = ScanRTL_Track(ctcode,si,true,false); + break; + + default: + { + ccp eol = FindNextLineFeedSI(si,true); + err = ERROR0(ERR_WARNING, + "Line ignored [%s @%u]: %.*s\n", + sf->name, sf->line, (int)(eol - sf->ptr), sf->ptr ); + } + break; + } + CheckEolSI(si); + if ( max_err < err ) + max_err = err; + } + + CheckLEFlagsUsage(ctcode); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanSetup ( ctcode_t *ctcode, ScanInfo_t * si ); +static enumError ScanRacingTrackList ( ctcode_t *ctcode, ScanInfo_t * si ); + +//----------------------------------------------------------------------------- + +enumError ScanTextCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(ctcode); + + const uint CTCODE_VERSION = 1; + + if (ct_mode) + InitializeCTCODE(ctcode,ct_mode); + ResetDataCTCODE(ctcode,true); + + + //--- setup data + + ctcode->n_next_slot = 0; + ctcode->track_of_cup = 0; + //ctcode->n_tracks = MKW_ARENA_END; + FreeString(ctcode->next_cup_name); + ctcode->next_cup_name = 0; + //HEXDUMP16(6,0,ctcode->used_slot,sizeof(ctcode->used_slot)); + + + //--- setup parser + + enum { SECT_SETUP, SECT_RACING_TRACK_LIST, N_SECT }; + const KeywordTab_t sect_names[] = + { + { SECT_SETUP, "SETUP", 0, 0 }, + { SECT_RACING_TRACK_LIST, "RACING-TRACK-LIST", 0, 0 }, + { 0,0,0,0 } + }; + + ScanInfo_t si; + InitializeSI(&si,data,data_size,ctcode->fname,CTCODE_VERSION); + si.predef = SetupVarsCTCODE(); + + + //--- main loop + + enumError max_err = ERR_OK; + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,CTCODE_VERSION); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,sect_names); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + PRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + if ( cmd->id < 0 || cmd->id > N_SECT ) + continue; + + enumError err = ERR_OK; + switch (cmd->id) + { + case SECT_SETUP: + PRINT("SCAN [SETUP]\n"); + err = ScanSetup(ctcode,&si); + break; + + case SECT_RACING_TRACK_LIST: + PRINT("SCAN [RACING-TRACK-LIST]\n"); + err = ScanRacingTrackList(ctcode,&si); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextCTCODE) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + +// [[file-name]] + UsePatchingListBMG(&ctcode->track_string); + CalcCupRefCTCODE(ctcode,true); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int scan_raw_ctcode_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + ctcode_t *ctcode = it->param; + DASSERT(ctcode); + + noPRINT("--> %3d,%3d term=%u size=%4u : %s\n", + it->group, it->entry, term, it->size, it->path ); + if ( term || it->size <= 0 ) + return 0; + + const u8 *data = it->szs->data + it->off; + if (!memcmp(data,"CUP1",4)) + { + const ctcode_cup1_head_t *hd = (ctcode_cup1_head_t*)data; + uint size = be32(&hd->size); + if ( size > it->size ) + size = it->size; + + const uint nc1 = be32(&hd->n_racing_cups); + const uint nc2 = be32(&hd->n_battle_cups); + + if ( it->off + size <= it->szs->size ) + { + #if USE_NEW_CONTAINER_CTC + PRINT("CTCODE/CUP1 found, SIZEx=%x/%x/%x, Nx=%x+%x, Nd=%d+%d, IN-C=%d\n", + size, be32(&hd->size), it->size, + nc1, nc2, nc1, nc2, InContainerP(&ctcode->container,data) ); + #else + PRINT("CTCODE/CUP1 found, SIZEx=%x/%x/%x, Nx=%x+%x, Nd=%d+%d, IN-C=%d\n", + size, be32(&hd->size), it->size, + nc1, nc2, nc1, nc2, InDataContainer(ctcode->old_container,data) ); + #endif + + if (ctcode->cup_alloced) + FREE(ctcode->cup); + + #if USE_NEW_CONTAINER_CTC + if (InContainerP(&ctcode->container,data)) + #else + if (InDataContainer(ctcode->old_container,data)) + #endif + { + ctcode->cup_alloced = false; + ctcode->cup = (ctcode_cup1_head_t*)data; + } + else + { + ctcode->cup_alloced = true; + ctcode->cup = MEMDUP(data,size); + } + ctcode->cup_size = size; + + ctcode->cup_racing = ctcode->cup->data; + ctcode->cup_battle = ctcode->cup_racing + nc1; + + ctcode->n_racing_cups = nc1; + ctcode->max_racing_cups = nc1; + ctcode->n_battle_cups = nc2; + + const uint max_cups = ( data+size - (u8*)ctcode->cup_racing ) + / sizeof(ctcode_cup1_data_t); + + ctcode->max_battle_cups = max_cups - nc1; + if ( ctcode->max_battle_cups > 2 ) + ctcode->max_battle_cups = 2; + if ( ctcode->max_battle_cups < nc2 ) + ctcode->max_battle_cups = nc2; + + if ( max_cups > nc1 + ctcode->max_battle_cups ) + ctcode->n_unused_cups = max_cups - nc1 - ctcode->max_battle_cups; + + PRINT("STAT/CUP: racing: %u/%u, battle: %u/%u, unused: %u\n", + ctcode->n_racing_cups, ctcode->max_racing_cups, + ctcode->n_battle_cups, ctcode->max_battle_cups, + ctcode->n_unused_cups ); + } + } + else if (!memcmp(data,"CRS1",4)) + { + const ctcode_crs1_head_t *hd = (ctcode_crs1_head_t*)data; + uint size = be32(&hd->size); + if ( size > it->size ) + size = it->size; + + const uint nt = be32(&hd->n_tracks); + + if ( it->off + size <= it->szs->size ) + { + #if USE_NEW_CONTAINER_CTC + PRINT("CTCODE/CRS1 found, SIZEx=%x/%x/%x, Nxd=%x,%d, IN-C=%d\n", + size, be32(&hd->size), it->size, + nt, nt, InContainerP(&ctcode->container,data) ); + #else + PRINT("CTCODE/CRS1 found, SIZEx=%x/%x/%x, Nxd=%x,%d, IN-C=%d\n", + size, be32(&hd->size), it->size, + nt, nt, InDataContainer(ctcode->old_container,data) ); + #endif + + if (ctcode->crs_alloced) + FREE(ctcode->crs); + + #if USE_NEW_CONTAINER_CTC + if (InContainerP(&ctcode->container,data)) + #else + if (InDataContainer(ctcode->old_container,data)) + #endif + { + ctcode->crs_alloced = false; + ctcode->crs = (ctcode_crs1_head_t*)data; + } + else + { + ctcode->crs_alloced = true; + ctcode->crs = MEMDUP(data,size); + } + ctcode->crs_size = size; + + ctcode->n_tracks = nt; + ctcode->max_tracks = ( data + size - (u8*)ctcode->crs->data ) + / sizeof(ctcode_crs1_data_t); + + PRINT("STAT/TRACK: %u/%u\n", ctcode->n_tracks, ctcode->max_tracks ); + } + } + MARK_USED(ctcode); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesCTCODE +( + szs_iterator_t *it, // iterator struct with all infos + bool multi_img // false: mipmaps, true: multi images +) +{ + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + const u8 * data = szs->data; + DASSERT(data); + + if ( !data || szs->size < sizeof(ctcode_header_t) ) + return -1; + + const ctcode_header_t *ch = (ctcode_header_t*)data; + const u32 n_sect = be32(&ch->n_sect); + const u32 size = be32(&ch->size); + const u32 min_data_off = sizeof(ctcode_header_t) + + n_sect * sizeof(ctcode_sect_info_t) + - sizeof(ch->sect_info); + if ( n_sect < 2 || szs->size < min_data_off || szs->size < size ) + return -1; + + it->no_recurse++; + + it->index = 0; + it->off = 0; + it->size = min_data_off; + it->is_dir = 0; + StringCopyS(it->path,sizeof(it->path),".CTCODE.header"); + int stat = it->func_it(it,false); + + uint sect; + for ( sect = 0; sect < n_sect && !stat; sect++ ) + { + u32 off = be32(&ch->sect_info[sect].off); + if ( off < min_data_off || off >= szs->size ) + continue; + + u32 max_off = szs->size; + if ( sect+1 < n_sect ) + { + const u32 next_off = be32(&ch->sect_info[sect+1].off); + noPRINT("NEXT-OFF: %x\n",next_off); + if ( next_off > off && max_off > next_off ) + max_off = next_off; + } + + it->index++; + it->off = off; + it->size = max_off - off; + ccp name = ch->sect_info[sect].name; + if ( off + 8 < szs->size ) + { + u32 *ptr = (u32*)(data + off); + const u32 new_size = be32(ptr+1); + if ( it->size > new_size ) + it->size = new_size; + name = (ccp)ptr; + } + snprintf(it->path,sizeof(it->path), + "ctcode-%i-%s.bin", sect, PrintID(name,4,0)); + noPRINT("MAX-OFF: %x..%x [%x] %x/%zx / sect %u/%u : %s\n", + off, off+it->size, it->size, + max_off, szs->size, sect, n_sect, it->path ); + stat = it->func_it(it,false); + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void ScanSetupCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerCTC_t * cdata // NULL or container-data +) +{ + DASSERT(ctcode); + + #if USE_NEW_CONTAINER_CTC // --------------------------------------- + + if (ct_mode) + InitializeCTCODE(ctcode,ct_mode); + else + ResetDataCTCODE(ctcode,false); + + CatchContainerData(&ctcode->container,0,cdata); + AssignContainer(&ctcode->container,0,data,data_size,CPM_COPY); + #ifdef TEST + DumpInfoContainer(stdlog,collog,2,"CTC: ",&ctcode->container,0); + #endif + + #else // !USE_NEW_CONTAINER_CTC // --------------------------------- + + if (ct_mode) + InitializeCTCODE(ctcode,ct_mode); + else + ResetDataCTCODE(ctcode,false); + ctcode->old_container = cdata; + + #endif // !USE_NEW_CONTAINER_CTC // -------------------------------- +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanRawCTCODE_helper +( + ctcode_t * ctcode, // initialized CTCODE data structure + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(ctcode); + DASSERT( IsValidCTCODE(data,data_size,data_size,0) < VALID_ERROR ); + + memcpy(&ctcode->ct_head,data,sizeof(ctcode->ct_head)); + + //--- scan CTCODE header & sections + + cut_iter_func[FF_CT1_DATA] = IterateFilesCTCODE; // enable CTCODE support +// [[fname-]] + IterateFilesData( data, data_size, scan_raw_ctcode_func, ctcode, FF_CT1_DATA, 0 ); + CalcCupRefCTCODE(ctcode,true); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerCTC_t * cdata // NULL or container-data +) +{ + DASSERT(ctcode); + ScanSetupCTCODE(ctcode,ct_mode,data,data_size,cdata); + + if ( IsValidCTCODE(data,data_size,data_size,ctcode->fname) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid CT-CODE file: %s\n", ctcode->fname ? ctcode->fname : "?"); + } + + return ScanRawCTCODE_helper(ctcode,data,data_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTexCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerCTC_t * cdata // NULL or container-data +) +{ + DASSERT(ctcode); + ScanSetupCTCODE(ctcode,ct_mode,data,data_size,cdata); + + if ( IsValidTEXCT(data,data_size,data_size,0,ctcode->fname) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid TEX+CT file: %s\n", ctcode->fname ? ctcode->fname : "?"); + } + + const brsub_header_t *bh = (brsub_header_t*)data; + const u32 img_off = be32(bh->grp_offset); + return ScanRawCTCODE_helper( ctcode, + data + CT_CODE_TEX_OFFSET, + img_off - CT_CODE_TEX_OFFSET ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int iter_brres_ctcode +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if (term) + return 0; + + szs_file_t * szs = it->szs; + DASSERT(szs); + const u8 * data = szs->data; + DASSERT(data); + data += it->off; + + ctcode_t *ctcode = it->param; + DASSERT(ctcode); + + noPRINT("%6x %6x : %02x %02x %02x %02x : %s\n", + it->off, it->size, + data[0], data[1], data[2], data[3], it->name ); + + if ( IsValidTEXCT(data,it->size,it->size,0,ctcode->fname) < VALID_ERROR ) + { + const brsub_header_t *bh = (brsub_header_t*)data; + const u32 img_off = be32(bh->grp_offset); + ScanRawCTCODE_helper( ctcode, + data + CT_CODE_TEX_OFFSET, + img_off - CT_CODE_TEX_OFFSET ); + return 1; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanBrresCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerCTC_t * cdata // NULL or container-data +) +{ + DASSERT(ctcode); + ScanSetupCTCODE(ctcode,ct_mode,data,data_size,cdata); + + szs_file_t szs; + AssignSZS(&szs,true,(void*)data,data_size,false,FF_BRRES,0); + IterateFilesSZS(&szs,iter_brres_ctcode,ctcode,false,0,-1,SORT_NONE); + ResetSZS(&szs); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanLEBinCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanLEBinCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(ctcode); + + if (ct_mode) + { + ct_mode = NormalizeCtModeBMG(ct_mode,opt_ct_mode); + InitializeCTCODE( ctcode, ct_mode < CTM_CTCODE ? CTM_CTCODE : ct_mode ); + } + ResetDataCTCODE(ctcode,true); + + le_analyse_t ana; + AnalyseLEBinary(&ana,data,data_size); + + + //--- copy racing cups + + ctcode->n_racing_cups = 0; + ctcode_cup1_data_t *cup = ctcode->cup_racing; + const le_cup_track_t *cup_dest = ana.cup_track; + if ( cup && cup_dest ) + { + uint cup_idx, max = ctcode->max_racing_cups; + if ( max > ana.n_cup_track ) + max = ana.n_cup_track ; + + for ( cup_idx = 0; cup_idx < max; cup_idx++, cup++ ) + { + if ( cup_dest[0] || cup_dest[1] || cup_dest[2] || cup_dest[3] ) + { + cup->track_id[0] = *cup_dest++; + cup->track_id[1] = *cup_dest++; + cup->track_id[2] = *cup_dest++; + cup->track_id[3] = *cup_dest++; + ctcode->n_racing_cups++; + } + else + cup_dest += 4; + } + } + + + //--- copy battle cups + + ctcode->n_battle_cups = 0; + cup = ctcode->cup_battle; + cup_dest = ana.cup_arena; + if ( cup && cup_dest ) + { + uint cup_idx, max = ctcode->max_battle_cups; + if ( max > ana.n_cup_arena ) + max = ana.n_cup_arena ; + + for ( cup_idx = 0; cup_idx < max; cup_idx++, cup++ ) + { + if ( cup_dest[0] || cup_dest[1] || cup_dest[2] || cup_dest[3] || cup_dest[4] ) + { + cup->track_id[0] = *cup_dest++; + cup->track_id[1] = *cup_dest++; + cup->track_id[2] = *cup_dest++; + cup->track_id[3] = *cup_dest++; + cup->track_id[4] = *cup_dest++; + ctcode->n_battle_cups++; + } + else + cup_dest += 4; + } + } + + + //--- copy track properties + + ctcode->n_tracks = 0; + ctcode_crs1_data_t *td = ctcode->crs->data; + const le_property_t *prop = ana.property; + const le_music_t *music = ana.music; + const le_flags_t *flags = ana.flags; + le_flags_t *ct_flags = ctcode->le_flags; + + if ( td && prop && music && flags ) + { + uint tidx, max = ctcode->max_tracks; + if ( max > ana.n_slot ) + max = ana.n_slot ; + + int max_tidx = -1; + for ( tidx = 0; tidx < max; + tidx++, td++, prop++, music++, flags++, ct_flags++ ) + { + if ( tidx >= 0x2a && tidx < LE_FIRST_LOWER_CT_SLOT ) + { + td->music_id = htonl(0x75); + td->property_id = htonl(0); + *ct_flags = 0; + } + else + { + if ( *music || *prop ) + max_tidx = tidx; + td->music_id = htonl(*music); + td->property_id = htonl(*prop); + *ct_flags = *flags; + snprintf(td->filename,sizeof(td->filename),"%03x",tidx); + } + } + ctcode->n_tracks = max_tidx+1; + } + + + //--- terminate + + ResetLEAnalyse(&ana); + CalcCupRefCTCODE(ctcode,true); + CheckLEFlagsUsage(ctcode); + + PRINT("STAT/CUP: racing: %u/%u, battle: %u/%u, unused: %u\n", + ctcode->n_racing_cups, ctcode->max_racing_cups, + ctcode->n_battle_cups, ctcode->max_battle_cups, + ctcode->n_unused_cups ); + PRINT("STAT/TRACK: %u/%u\n", ctcode->n_tracks, ctcode->max_tracks ); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CTCODE scan + load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerCTC_t * cdata, // NULL or container-data + CheckMode_t mode // not NULL: call CheckCTCODE(mode) +) +{ + DASSERT(ctcode); + PRINT("ScanCTCODE(mode=%x) size=%u\n",mode,data_size); + + enumError err; +// [[analyse-magic]] + switch (GetByMagicFF(data,data_size,data_size)) + { + case FF_BRRES: + err = ScanBrresCTCODE(ctcode,ct_mode,data,data_size,cdata); + if ( !ctcode->cup && !ctcode->crs ) + { + ERROR0(ERR_INVALID_DATA, + "No CTCODE in BRRES found: %s\n", ctcode->fname ? ctcode->fname : "?"); + ResetCTCODE(ctcode); + return ERR_INVALID_DATA; + } + ctcode->fform = FF_BRRES; + break; + + case FF_TEX_CT: + err = ScanTexCTCODE(ctcode,ct_mode,data,data_size,cdata); + ctcode->fform = FF_TEX_CT; + break; + + case FF_CT1_DATA: + err = ScanRawCTCODE(ctcode,ct_mode,data,data_size,cdata); + ctcode->fform = FF_CT1_DATA; + break; + + case FF_CT_TEXT: + #if USE_NEW_CONTAINER_CTC + FreeContainerData(cdata); + #else + FreeContainer(cdata); + #endif + err = ScanTextCTCODE(ctcode,ct_mode,data,data_size); + ctcode->fform = FF_CT_TEXT; + break; + + case FF_LE_BIN: + #if USE_NEW_CONTAINER_CTC + FreeContainerData(cdata); + #else + FreeContainer(cdata); + #endif + err = ScanLEBinCTCODE(ctcode,ct_mode,data,data_size); + ctcode->fform = FF_LE_BIN; + break; + + default: + #if USE_NEW_CONTAINER_CTC + FreeContainerData(cdata); + #else + FreeContainer(cdata); + #endif + if (ct_mode) + InitializeCTCODE(ctcode,ct_mode); + return ERROR0(ERR_INVALID_DATA, + "No CTCODE file: %s\n", ctcode->fname ? ctcode->fname : "?"); + } + + if ( !err && opt_patch_names ) + PatchNamesCTCODE(ctcode); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckCTCODE(mode) +) +{ + DASSERT(ctcode); + DASSERT(raw); + if (ct_mode) + InitializeCTCODE(ctcode,ct_mode); + else + ResetCTCODE(ctcode); + if (!raw->data) + return ERR_OK; + + ctcode->fname = raw->fname; + raw->fname = 0; + #if USE_NEW_CONTAINER_CTC + return ScanCTCODE( ctcode, CTM_NO_INIT, raw->data, raw->data_size, + LinkContainerRawData(raw), mode ); + #else + return ScanCTCODE( ctcode, CTM_NO_INIT, raw->data, raw->data_size, + ContainerRawData(raw), mode ); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadCTCODE +( + ctcode_t *ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckCTCODE(mode) +) +{ + DASSERT(ctcode); + DASSERT(fname); + if (ct_mode) + InitializeCTCODE(ctcode,ct_mode); + else + ResetCTCODE(ctcode); + + if (!strcmp(fname,"0")) + { + ctcode->fform = FF_CT_TEXT; + ctcode->fname = STRDUP(fname); + return ERR_OK; + } + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,false); + if (!err) + { + ctcode->fname = raw.fname; + raw.fname = 0; + #if USE_NEW_CONTAINER_CTC + err = ScanCTCODE( ctcode, CTM_NO_INIT, raw.data, raw.data_size, + LinkContainerRawData(&raw),mode); + #else + err = ScanCTCODE( ctcode, CTM_NO_INIT, raw.data, raw.data_size, + ContainerRawData(&raw), mode ); + #endif + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_track +( + FILE *f, // output file + ctcode_t *ctcode, // pointer to valid CTCODE + char letter, // record letter + uint tidx, // valid track index + bool print_hex // true: force hex output +) +{ + DASSERT( f ); + DASSERT( ctcode ); + DASSERT( letter == 'T' || letter == 'H' || letter == 'A' ); + DASSERT( tidx < ctcode->n_tracks ); + + char namebuf[TNAME_BUF_SIZE]; + + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,tidx+ctcode->ctb.track_name1.beg); + GetTrackString(namebuf,sizeof(namebuf),ctcode,tidx,bi); + const ctcode_crs1_data_t *td = ctcode->crs->data + tidx; + + fprintf(f,"%c %s\t; %s\t;", + letter, + PrintMusicID(ntohl(td->music_id),print_hex), + PrintPropertyID(ntohl(td->property_id),print_hex) ); + + if (ctcode->use_le_flags) + fprintf(f," 0x%02x\t;",ctcode->le_flags[tidx]); + + GetTrackFile(namebuf,sizeof(namebuf),ctcode,tidx,0); + fprintf(f," \"%s\"",namebuf); + + ccp placeholder; + GetTrackString(namebuf,sizeof(namebuf),ctcode,tidx,bi); + if (*namebuf) + { + fprintf(f," ; \"%s\"",namebuf); + placeholder = ""; + } + else + placeholder = " ; \"\""; + + GetTrackIdentifier(namebuf,sizeof(namebuf),ctcode,tidx,0); + if (*namebuf) + fprintf(f,"%s ; \"%s\"\r\n",placeholder,namebuf); + else + fputs("\r\n",f); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time, // true: set time stamps + const output_mode_t * outmode // output mode, if NULL: use defaults +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(ctcode); + PrepareSaveCTCODE(ctcode); + + DASSERT(fname); + PRINT("SaveTextCTCODE(%s,%d)\n",fname,set_time); + + output_mode_t omode; + if (outmode) + memcpy(&omode,outmode,sizeof(omode)); + else + InitializeOutputMode(&omode); + CalcCupRefCTCODE(ctcode,false); + + char namebuf[TNAME_BUF_SIZE], keybuf[20]; + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,ctcode->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&ctcode->fatt,0); + + + //--- print file header + syntax info + + if (omode.syntax) + { + fprintf( F.f, text_ctcode_head_cr ); + fputs(section_sep,F.f); + } + else + fprintf(F.f,"%s\r\n\r\n",CT_TEXT_MAGIC8); + + MARK_USED(keybuf,namebuf); + fprintf( F.f, text_ctcode_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, + ctcode->ctb.ct_mode, + ctcode->n_racing_cups, + ctcode->max_racing_cups, + ctcode->n_battle_cups, + ctcode->max_battle_cups, + ctcode->used_tracks, + ctcode->used_arenas, + ctcode->n_tracks, + ctcode->max_tracks, + ctcode->n_racing_cups, + ctcode->max_racing_cups, + ctcode->n_battle_cups, + ctcode->max_battle_cups, + ctcode->n_unused_cups ); + + + //--- print track list + + if ( omode.mode & 1 && ctcode->used_tracks ) + { + fprintf(F.f,"%s[RACING-TRACK-LIST]\r\n%s", + section_sep, + omode.syntax + ? text_ctcode_list_syntax_full_cr + : text_ctcode_list_syntax_brief_cr ); + + if (ctcode->use_le_flags) + fputs("\n%LE-FLAGS = 1 # 0:disabled, 1:enabled\n",F.f); + + if (ctcode->replace_at) + fputs("\n%REPLACE-AT = 1" + " # replace @*@ codes in track names (1:enabled)\n",F.f); + + + //-- print battle cups + + if (ctcode->assigned_arenas) + { + fprintf(F.f,"\n#--- battle arenas\n\nC \"Wii Arenas\"\n"); + + uint arena; + for ( arena = 0; arena < MKW_N_ARENAS; arena++ ) + { + if ( arena == 5 ) + fprintf(F.f,"\nC \"Retro Arenas\"\n"); + + const uint tidx = arena_pos_default[arena] + MKW_N_TRACKS; + print_track(F.f,ctcode,'A',tidx,omode.hex); + } + fprintf(F.f,"\n#--- racing tracks\n"); + } + + + //-- racing cups + + u8 done[CODE_MAX_TRACKS]; + memset(done,0,sizeof(done)); + memset(done+0x20,1,10); + memset(done+0x36,1, 5); + memset(done+0x42,1, 2); + + uint cidx; + for ( cidx = 0; cidx < ctcode->n_racing_cups; cidx++ ) + { + const ctcode_cup1_data_t *cd = ctcode->cup->data + cidx; + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,ctcode->ctb.rcup_name.beg+cidx); + if ( bi && bi->len ) + PrintString16BMG(namebuf,sizeof(namebuf),bi->text,bi->len, + BMG_UTF8_MAX,2,opt_bmg_colors); + else + PrintString16BMG(namebuf,sizeof(namebuf),cd->name,-1, + BMG_UTF8_MAX,2,opt_bmg_colors); + fprintf(F.f,"\r\nC %s # 0x%02x = %2u\r\n",namebuf,cidx,cidx); + + fprintf(F.f,"S 0x%02x!, 0x%02x!, 0x%02x!, 0x%02x!\r\n", + be32(cd->track_id+0), + be32(cd->track_id+1), + be32(cd->track_id+2), + be32(cd->track_id+3) ); + + uint ti; + for ( ti = 0; ti < 4; ti++ ) + { + const uint tidx = be32(cd->track_id+ti); + if ( tidx < ctcode->n_tracks ) + { + print_track(F.f,ctcode,'T',tidx,omode.hex); + done[tidx] = 2; + } + else + fprintf(F.f,"T= -\r\n"); + } + } + + uint tidx, h_count = 0; + DASSERT( ctcode->n_tracks <= ctcode->max_tracks ); + for ( tidx = 0; tidx < ctcode->n_tracks; tidx++ ) + { + if ( done[tidx] ) + continue; + + const ctcode_crs1_data_t *td = ctcode->crs->data + tidx; + if ( ntohl(td->music_id) == 0x75 && !ntohl(td->property_id) ) + continue; + + if (!h_count++) + fprintf(F.f,"\r\n#--- hidden tracks (not linked to a cup)\r\n\r\n"); + + fprintf(F.f,"S 0x%02x!\r\n",tidx); + print_track(F.f,ctcode,'H',tidx,omode.hex); + } + } + + + //--- print track reference + + if ( omode.mode & 2 && ctcode->n_tracks ) + { + fputs(section_sep,F.f); + + const uint keyfw = 8; + uint tidx; + for ( tidx = 0; tidx < ctcode->n_tracks; tidx++ ) + { + const ctcode_crs1_data_t *td = ctcode->crs->data+tidx; + fprintf(F.f,"[TRACK-REF]\r\n%-*s = 0x%02x # %u\r\n",keyfw,"index",tidx,tidx); + const bool is_battle = IsMkwArena(tidx); + if (is_battle) + fprintf(F.f,"%-*s = %2u\r\n",keyfw,"battle",tidx-MKW_ARENA_BEG); + + GetTrackString(namebuf,sizeof(namebuf),ctcode,tidx,0); + fprintf(F.f,"%-*s = %s\r\n",keyfw,"name",namebuf); + + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,tidx+ctcode->ctb.track_name1.beg); + if ( bi && bi->len ) + { + PrintString16BMG(namebuf,sizeof(namebuf),bi->text,bi->len, + BMG_UTF8_MAX,0,opt_bmg_colors); + fprintf(F.f,"%-*s = %s\r\n",keyfw,"bmg",namebuf); + } + + fprintf(F.f,"%-*s = %s\r\n",keyfw,"file",td->filename); + + GetTrackFile(namebuf,sizeof(namebuf),ctcode,tidx,0); + if (*namebuf) + fprintf(F.f,"%-*s = %s\r\n",keyfw,"filename",namebuf); + + const uint music_id = be32(&td->music_id); + fprintf(F.f,"%-*s = 0x%02x",keyfw,"music",music_id); + const uint mtrack = MusicID2TrackId(music_id,0xff,0xff); + if ( omode.cross_ref && mtrack < MKW_N_TRACKS + MKW_N_ARENAS ) + { + const TrackInfo_t *info = GetTrackInfo(mtrack); + fprintf(F.f," # %s, %s, %s\r\n", + info->sound_n_fname, info->sound_f_fname, info->name_en ); + } + else + fputs("\r\n",F.f); + + const uint prop_id = be32(&td->property_id); + fprintf(F.f,"%-*s = 0x%02x",keyfw,"property",prop_id); + if ( omode.cross_ref && prop_id < ctcode->n_tracks ) + { + GetTrackString(namebuf,sizeof(namebuf),ctcode,prop_id,0); + const ctcode_crs1_data_t *pd = ctcode->crs->data+prop_id; + fprintf(F.f," # %s, %s\r\n", pd->filename, namebuf ); + } + else + fputs("\r\n",F.f); + + const uint cup_id = be32(&td->cup_id); + fprintf(F.f,"%-*s = 0x%02x",keyfw,"cup",cup_id); + if ( omode.cross_ref && !is_battle && tidx < ctcode->max_tracks ) + { + ctcode_cupref_t *cr = ctcode->cupref + tidx; + if (!cr->n) + fprintf(F.f," # not used in any cup!\r\n"); + else + { + if ( cup_id == cr->cup[0] ) + { + const ctcode_cup1_data_t *cd = GetCupData(ctcode,cup_id); + if (cd) + { + const ctcode_cup1_data_t *cd = ctcode->cup->data+cup_id; + PrintString16BMG(namebuf,sizeof(namebuf),cd->name,-1, + BMG_UTF8_MAX,0,opt_bmg_colors); + fprintf(F.f," # used in cup '%s' @%d\r\n", + namebuf, cr->idx[0] ); + } + } + else + { + const uint cup_id2 = cr->cup[0]; + const ctcode_cup1_data_t *cd = GetCupData(ctcode,cup_id2); + if (cd) + { + PrintString16BMG(namebuf,sizeof(namebuf),cd->name,-1, + BMG_UTF8_MAX,0,opt_bmg_colors); + fprintf(F.f," # not used in cup 0x%02x," + " but in cup 0x%02x '%s' @%d\r\n", + cup_id, cup_id2, namebuf, cr->idx[0] ); + } + } + + uint i; + for ( i = 1; i < cr->n; i++ ) + { + const ctcode_cup1_data_t *cd = GetCupData(ctcode,cr->cup[i]); + if (cd) + { + PrintString16BMG(namebuf,sizeof(namebuf),cd->name,-1, + BMG_UTF8_MAX,0,opt_bmg_colors); + fprintf(F.f,"%-*s = 0x%02x # Also used in cup '%s' @%d\r\n", + keyfw, "#cup", cr->cup[i], namebuf, cr->idx[i] ); + } + } + } + } + else + fputs("\r\n",F.f); + + fputs("\r\n",F.f); + } + } + + + //--- print cup reference + + if ( omode.mode & 2 && ctcode->n_racing_cups + ctcode->n_battle_cups ) + { + fputs(section_sep,F.f); + + const uint keyfw = 7; + uint cidx; + for ( cidx = 0; cidx < ctcode->n_racing_cups + ctcode->n_battle_cups; cidx++ ) + { + const ctcode_cup1_data_t *cd = GetCupData(ctcode,cidx); + DASSERT(cd); + + fprintf(F.f,"[CUP-REF]\r\n%-*s = 0x%02x # %u\r\n",keyfw,"index",cidx,cidx); + const bool is_battle = cidx >= ctcode->n_racing_cups; + if (is_battle) + fprintf(F.f,"%-*s = %u\r\n",keyfw,"battle",cidx-ctcode->n_racing_cups); + PrintString16BMG(namebuf,sizeof(namebuf),cd->name,-1, + BMG_UTF8_MAX,0,opt_bmg_colors); + fprintf(F.f,"%-*s = %s\r\n",keyfw,"name",namebuf); + + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,cidx+ctcode->ctb.rcup_name.beg); + if ( bi && bi->len ) + { + PrintString16BMG(namebuf,sizeof(namebuf),bi->text,bi->len, + BMG_UTF8_MAX,0,opt_bmg_colors); + fprintf(F.f,"%-*s = %s\r\n",keyfw,"bmg",namebuf); + } + + uint ti; + for ( ti = 0; ti < 4+is_battle; ti++ ) + { + snprintf(keybuf,sizeof(keybuf),"track-%u",ti); + const uint tidx = be32(cd->track_id+ti); + fprintf(F.f,"%-*s = 0x%02x",keyfw,keybuf,tidx); + + if ( omode.cross_ref && tidx < ctcode->n_tracks ) + { + const bmg_item_t *bi + = FindItemBMG(&opt_load_bmg,tidx+ctcode->ctb.track_name1.beg); + GetTrackString(namebuf,sizeof(namebuf),ctcode,tidx,bi); + const ctcode_crs1_data_t *td = ctcode->crs->data + tidx; + + fprintf(F.f," # M=%02x, P=%02x, %s, %s\r\n", + be32(&td->music_id), be32(&td->property_id), + td->filename, namebuf ); + } + else + fputs("\r\n",F.f); + } + fputs("\r\n",F.f); + } + } + + + //--- terminate + + fputs(section_end,F.f); + ResetFile(&F,set_time); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveMessageCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveMessageCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(ctcode); + DASSERT(fname); + + PRINT("SaveMessageCTCODE(%s,%d)\n",fname,set_time); + PrepareSaveCTCODE(ctcode); + + const bool brief = brief_count > 0 || export_count > 0; + if ( brief || long_count > 1 ) + CalcCupRefCTCODE(ctcode,false); + + bmg_t bmg; + InitializeBMG(&bmg); + + + //--- identification + + PatchIdentificationBMG(&bmg,&ctcode->ctb); + + + //--- define cup names + + if ( long_count > 0 ) + { + uint cidx, max = ctcode->n_racing_cups; + if (!brief) + max += ctcode->n_battle_cups; + for ( cidx = 0; cidx < max; cidx++ ) + { + const ctcode_cup1_data_t *cd = GetCupData(ctcode,cidx); + DASSERT(cd); + bmg_item_t *bi = InsertItemBMG(&bmg,ctcode->ctb.rcup_name.beg+cidx,0,0,0); + DASSERT(bi); + uint len = GetLength16BMG(cd->name,sizeof(cd->name)/sizeof(*cd->name)); + AssignItemText16BMG(bi,cd->name,len); + } + } + + + //--- define track names + + uint tidx, max = ctcode->n_tracks; + if ( brief && max > ctcode->max_tracks ) + max = ctcode->max_tracks; + + uint begin = brief_count > 2 || brief_count > 1 && ctcode->cmd_n_used + ? 32 : 0; + + const u16 *prev_cup = 0; + uint prev_cup_len = 0; + + for ( tidx = begin; tidx < max; tidx++ ) + { + if ( brief && !ctcode->cupref[tidx].n_racing ) + continue; + + const uint mid = tidx + ctcode->ctb.track_name1.beg; + const bmg_item_t *bi0 = FindItemBMG(&opt_load_bmg,mid); + uint tlen; + const u16 *text = GetTrackName16(&tlen,ctcode,tidx,bi0); + bmg_item_t *bi = InsertItemBMG(&bmg,mid,0,0,0); + DASSERT(bi); + AssignItemText16BMG(bi,text,tlen); + + if ( long_count > 1 && tidx < ctcode->max_tracks ) + { + char buf[1000], *dest = buf; + ctcode_cupref_t *cr = ctcode->cupref + tidx; + + if (cr->n) + { + uint i; + for ( i = 0; i < cr->n; i++ ) + { + dest += snprintf(dest,buf+sizeof(buf)-dest, + ",%u.%u", cr->cup[i]+1, cr->idx[i]+1 ); + } + bmg_item_t *bi + = InsertItemBMG(&bmg,ctcode->ctb.cup_ref.beg+tidx,0,0,0); + DASSERT(bi); + AssignItemTextBMG(bi,buf+1,dest-buf-1); + prev_cup = bi->text; + prev_cup_len = bi->len; + } + else if ( prev_cup && tidx >= LE_FIRST_LOWER_CT_SLOT ) + { + bmg_item_t *bi + = InsertItemBMG(&bmg,ctcode->ctb.cup_ref.beg+tidx,0,0,0); + DASSERT(bi); + AssignItemText16BMG(bi,prev_cup,prev_cup_len); + } + } + } + + + //--- save messages + + long_count = 1; // no special groups + brief_count = 1; // no header, but magic + opt_bmg_single_line = 2; // only pure messages in one line + + UsePatchingListBMG(&bmg); + enumError err = SaveTextXBMG(&bmg,fname,set_time); + ResetBMG(&bmg); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTrackListCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTrackListCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time, // true: set time stamps + uint select, // track selection + // 0: only custom tracks + // 1: standard + custom tracks + // 2: standard + custom + battle tracks + bool print_slots, // true: print music and property slots + bool print_head, // true: print table header + bool raw_mode // true: print raw text without escapes + +) +{ + DASSERT(ctcode); + DASSERT(fname); + + PrepareSaveCTCODE(ctcode); + PRINT("SaveTrackListCTCODE(%s,%d)\n",fname,set_time); + + char namebuf[TNAME_BUF_SIZE]; + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,ctcode->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&ctcode->fatt,0); + + + //--- print header + + if (print_head) + { + if (print_slots) + fprintf(F.f, "\n Slot\tProp\tMusic\tTrack name\n%.79s\n",Minus300); + else + fprintf(F.f, "\n Slot\tTrack name\n%.63s\n",Minus300); + } + + + //--- print track list + + uint cidx, max = ctcode->n_racing_cups; + if ( select > 1 ) + max += ctcode->n_battle_cups; + for ( cidx = select ? 0 : 8; cidx < max; cidx++ ) + { + const bool is_battle = cidx >= ctcode->n_racing_cups; + const ctcode_cup1_data_t *cd = GetCupData(ctcode,cidx); + DASSERT(cd); + fputc('\n',F.f); + + uint ti; + for ( ti = 0; ti < 4+is_battle; ti++ ) + { + const uint tidx = be32(cd->track_id+ti); + if ( tidx < ctcode->n_tracks ) + { + const ctcode_crs1_data_t *td = ctcode->crs->data + tidx; + uint music_id = ntohl(td->music_id); + uint property_id = ntohl(td->property_id); + + if (is_battle) + { + fprintf(F.f," A%u.%u", cidx - ctcode->n_racing_cups + 1, ti+1 ); + + if (!music_id) + music_id = arena_info[tidx-MKW_N_TRACKS].music_id; + if (!property_id) + property_id = tidx; + } + else + fprintf(F.f,"%3u.%u", cidx+1, ti+1 ); + + if (print_slots) + { + fprintf(F.f,"\t%s\t%s", + PrintPropertyID(property_id,false), + PrintMusicID(music_id,false) ); + } + + const uint mid = tidx + ctcode->ctb.track_name1.beg; + const bmg_item_t *bi = FindItemBMG(&opt_load_bmg,mid); + if (raw_mode) + GetRawTrackInfo(namebuf,sizeof(namebuf),ctcode,tidx,bi); + else + GetTrackString(namebuf,sizeof(namebuf),ctcode,tidx,bi); + fprintf(F.f,"\t%s\n",namebuf); + } + } + } + fputc('\n',F.f); + + + //--- terminate + + ResetFile(&F,set_time); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTrackTableCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTrackTableCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(ctcode); + DASSERT(fname); + + PrepareSaveCTCODE(ctcode); + PRINT("SaveTrackTableCTCODE(%s,%d)\n",fname,set_time); + + char namebuf[TNAME_BUF_SIZE]; + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,ctcode->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&ctcode->fatt,0); + + + //--- print track list + + uint cidx; + for ( cidx = 0; cidx < ctcode->n_racing_cups; cidx++ ) + { + const ctcode_cup1_data_t *cd = ctcode->cup->data + cidx; + + uint ti; + for ( ti = 0; ti < 4; ti++ ) + { + const uint tidx = be32(cd->track_id+ti); + if ( tidx < ctcode->n_tracks ) + { + const uint mid = tidx + ctcode->ctb.track_name1.beg; + const bmg_item_t *bi = FindItemBMG(&opt_load_bmg,mid); + GetTrackString(namebuf,sizeof(namebuf),ctcode,tidx,bi); + fprintf(F.f,"%02u%u|%04x|%s\n",cidx,ti,mid,namebuf); + } + } + } + + + //--- terminate + + ResetFile(&F,set_time); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint DeactivateMOD2 +( + BZ2Source_t *bz2s, // data + ctlang_save_t lang_mode, // language mode + const u32 *search_pal, // PAL list + const u32 *search_usa, // USA list + const u32 *search_jap, // JAP list + const u32 *search_kor // KOR list +) +{ + DASSERT(bz2s); + + uint patch_count = 0; + u8 *data = NonConstDataBZ2S(bz2s); + if (data) + { + const u32 *search_list; + switch (lang_mode) + { + case CTL_US: search_list = search_usa; break; + case CTL_JP: search_list = search_jap; break; + case CTL_KO: search_list = search_kor; break; + default: search_list = search_pal; break; + } + + if (search_list) + { + u8 *end = data + bz2s->size - 0x10; + while ( *search_list ) + { + const u32 search = *search_list++; + u8 *ptr = data + 0x20; + for ( ; ptr < end; ptr += 0x10 ) + if ( be32(ptr) == search && be32(ptr+8) == 4 && !be32(ptr+12) ) + { + ptr[11] = 0; + patch_count++; + break; + } + } + } + } + return patch_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time, // true: set time stamps + ctcode_save_t save_mode, // save mode + ctlang_save_t lang_mode, // language mode + const output_mode_t * outmode // output mode for text output + // if NULL: use defaults +) +{ + DASSERT(ctcode); + PrepareSaveCTCODE(ctcode); + + char pathbuf[PATH_MAX]; + + + //--- define sections + + enum { BIDX_BAD0CODE, BIDX_BAD0DATA, BIDX_BAD1CODE, + BIDX_MOD1, BIDX_MOD2, BIDX_OVR1, BIDX__N }; + + #undef REF + #define REF(p,n,a) p "/" n ";" n, a, sizeof(a) + static BZ2Source_t srctab[] = + { + { REF("rmcp","boot_code.bin", ctcode_boot_code_pal_bz2), FF_CT0_CODE }, + { REF("rmcp","boot_data.bin", ctcode_boot_data_pal_bz2), FF_CT0_DATA }, + { REF("rmcp","bad1code.bin", ctcode_bad1code_pal_bz2), FF_CT1_CODE }, + { REF("rmcp","mod1.bin", ctcode_mod1_pal_bz2), FF_MOD1 }, + { REF("rmcp","mod2.bin", ctcode_mod2_pal_bz2), FF_MOD2 }, + { REF("rmcp","ovr1.bin", ctcode_ovr1_pal_bz2), FF_OVR1 }, + + { REF("rmce","boot_code.bin", ctcode_boot_code_usa_bz2), FF_CT0_CODE }, + { REF("rmce","boot_data.bin", ctcode_boot_data_usa_bz2), FF_CT0_DATA }, + { REF("rmce","bad1code.bin", ctcode_bad1code_usa_bz2), FF_CT1_CODE }, + { REF("rmce","mod1.bin", ctcode_mod1_usa_bz2), FF_MOD1 }, + { REF("rmce","mod2.bin", ctcode_mod2_usa_bz2), FF_MOD2 }, + { REF("rmce","ovr1.bin", ctcode_ovr1_usa_bz2), FF_OVR1 }, + + { REF("rmcj","boot_code.bin", ctcode_boot_code_jap_bz2), FF_CT0_CODE }, + { REF("rmcj","boot_data.bin", ctcode_boot_data_jap_bz2), FF_CT0_DATA }, + { REF("rmcj","bad1code.bin", ctcode_bad1code_jap_bz2), FF_CT1_CODE }, + { REF("rmcj","mod1.bin", ctcode_mod1_jap_bz2), FF_MOD1 }, + { REF("rmcj","mod2.bin", ctcode_mod2_jap_bz2), FF_MOD2 }, + { REF("rmcj","ovr1.bin", ctcode_ovr1_jap_bz2), FF_OVR1 }, + + // [[2do]] [[ct-korea]] + { REF("rmck","boot_code.bin", 0), FF_CT0_CODE }, + { REF("rmck","boot_data.bin", 0), FF_CT0_DATA }, + { REF("rmck","bad1code.bin", 0), FF_CT1_CODE }, + { REF("rmck","mod1.bin", 0), FF_MOD1 }, + { REF("rmck","mod2.bin", 0), FF_MOD2 }, + { REF("rmck","ovr1.bin", 0), FF_OVR1 }, + + {0} + }; + #undef REF + + ClearBZ2S(srctab,-1); + + + //--- setup jobs + + bool copy_brres = false; // copy BRRES header and string pool + bool copy_tex = false; // copy TEX0 header and IMAGE + bool copy_ct0code = false; // copy CT0CODE = boot_code + bool copy_ct0data = false; // copy CT0DATA = boot_data + bool copy_ct1code = false; // copy CT1CODE + bool copy_ct1data = false; // copy CT1DATA (header + CUP1 + CRS1 + MOD1 + MOD2 + OVR ) + bool copy_cup1 = false; // copy CUP1 section + bool copy_crs1 = false; // copy CRS1 section + bool copy_mod1 = false; // copy MOD1 section + bool copy_mod2 = false; // copy MOD2 section + bool copy_ovr1 = false; // copy OVR1 section + + uint load_data = 0; // load data from external files, bit field: 1<= srctab && bz2s < srctab + sizeof(srctab)/sizeof(*srctab) ); + + if (load_data) + { + if ( SearchListBZ2S( bz2s, BIDX__N, ct_dir_list.field, + ct_dir_list.used, opt_ct_log ) != BIDX__N ) + { + uint i, count = 0; + char *dest = pathbuf; + for ( i = 0; i < BIDX__N; i++ ) + { + if ( load_data & 1 << i ) + { + BZ2Source_t *bs = bz2s + i; + if ( !bs->data || !bs->size ) + { + count++; + ccp sem = strchr(bs->search,';'); + dest = snprintfE(dest,pathbuf+sizeof(pathbuf),", %.*s", + (int)( sem ? sem - bs->search : strlen(bs->search) ), + bs->search ); + } + } + } + if (count) + return ERROR0(ERR_INVALID_DATA, + "Missing CT-CODE file%s: %s\n", + count == 1 ? "" : "s", pathbuf+2 ); + } + + + //--- deactivate region patch + + { + static const u32 pal[] = { 0x8065920c, 0x80653644, 0x8065a034, 0 }; + static const u32 usa[] = { 0x80654d84, 0x8064f1bc, 0x80655bac, 0 }; + static const u32 jap[] = { 0x80658878, 0x80652cb0, 0x806596a0, 0 }; + static const u32 kor[] = { 0 }; // [[2do]] [[ct-korea]] + DeactivateMOD2( bz2s+BIDX_MOD2, lang_mode, pal, usa, jap, kor ); + } + + + //--- re-activate old spiny + + if ( old_spiny ) + { + static const u32 pal[] = { 0x808a5b30, 0 }; + static const u32 usa[] = { 0x808a1058, 0 }; + static const u32 jap[] = { 0x808a4c90, 0 }; + static const u32 kor[] = { 0 }; // [[2do]] [[ct-korea]] + DeactivateMOD2( bz2s+BIDX_MOD2, lang_mode, pal, usa, jap, kor ); + } + } + + + //--- setup + + size_t cur_off = 0, off = 0, tex_off = 0; + PrepareExportCTCODE(ctcode); + + + //--- open mem file + + MemFile_t mf; + InitializeMemFile(&mf,CT_CODE_BRRES_SIZE,0); + SetFileAttrib(&mf.fatt,&ctcode->fatt,0); + enumError err = ERR_OK; + + + //--- setup dynamic CTGP_CODE size + + // all offsets are relative to CT_CODE_TEX_OFFSET + + u32 off_head = 0; + u32 off_cup = off_head + sizeof(ctcode->ct_head); + u32 off_crs = off_cup + ctcode->cup_size; + u32 off_mod1 = CT_CODE_EXTEND_OFFSET + CT_CODE_MOD1_OFFSET; + u32 off_mod2 = CT_CODE_EXTEND_OFFSET + CT_CODE_MOD2_OFFSET; + u32 off_ovr1 = CT_CODE_EXTEND_OFFSET + CT_CODE_OVR1_OFFSET; + u32 off_image = CT_CODE_TEX_IMAGE_OFF - CT_CODE_TEX_OFFSET; + u32 off_term = CT_CODE_TEX_SIZE - CT_CODE_TEX_OFFSET; + + if (use_dynamic) + { + fprintf(stderr,"off: %x %x %x %x %x %x | %x %x\n", + off_head, off_cup, off_crs, off_mod1, off_mod2, off_ovr1, + off_image, off_term ); + } + + + //--- write strap header + + if ( !err && copy_brres ) + { + u8 *data; + uint size; + DecodeBZIP2(&data,&size,0,ctcode_brres_base_bz2,sizeof(ctcode_brres_base_bz2)); + + if (opt_image_dir) + { + struct ftab_t + { + u32 offset; + u32 width; + ccp fname; + }; + + const uint height_of_all = 456; + static const struct ftab_t ftab[] = + { + { 0x000140, 832, "strapA_16_9_832x456" }, + { 0x0b9580, 608, "strapA_608x456" }, + { 0x140bc0, 832, "strapB_16_9_832x456" }, + { 0x1fa000, 608, "strapB_608x456" }, + { 0x281640, 832, "strapC_832x456" }, + { 0x33aa80, 608, "strapC_608x456" }, + { 0x3c20c0, 832, "strapD_832x456" }, // relevant image for 16:9 and 4:3 + { 0x47b500, 608, "strapD_608x456" }, + {0,0,0} + }; + + static const ccp exttab[] = + { + ".png", ".PNG", + ".tex", ".TEX", + ".tex0",".TEX0", + "", + 0 + }; + + Image_t img; + InitializeIMG(&img); + + const struct ftab_t *fi; + for ( fi = ftab; fi->width; fi++ ) + { + const ccp *ext; + for ( ext = exttab; *ext; ext++ ) + { + ccp path = PathCatPPE(pathbuf,sizeof(pathbuf),opt_image_dir,fi->fname,*ext); + + if (LoadIMG(&img,false,path,0,false,false,true)) + continue; + + if ( img.width != fi->width || img.height != height_of_all ) + { + if ( verbose >= 0 ) + fprintf(stdlog," - load image [%s,%ux%u -> %ux%u]: %s\n", + PrintFormatIMG(&img), img.width, img.height, + fi->width, height_of_all, path ); + SmartResizeIMG(&img,false,0,fi->width,height_of_all); + } + else if ( verbose >= 0 ) + fprintf(stdlog," - load image [%s,%ux%u]: %s\n", + PrintFormatIMG(&img), img.width, img.height, path ); + + ConvertIMG(&img,false,0,IMG_RGB565,0); + TRACE(" -> [%s,%ux%u]: *** %u *** %s\n", + PrintFormatIMG(&img), img.width, img.height, + img.data_size, path ); + + const uint size = fi->width * height_of_all * 2; + if ( img.iform == IMG_RGB565 && img.data_size == size ) + { + memcpy(data+fi->offset,img.data,size); + break; + } + } + } + ResetIMG(&img); + } + + err = WriteMemFileAt( &mf, 0, data, size ); + FREE(data); + off = tex_off = size; + } + + + //--- write tex header + + if ( !err && copy_tex ) + { + err = WriteMemFileAt( &mf, off, ctcode_tex_header, sizeof(ctcode_tex_header) ); + if ( !err && bz2s[BIDX_BAD1CODE].status ) + err = WriteMemFileAt( &mf, off+0x40, bz2s[BIDX_BAD1CODE].data, + bz2s[BIDX_BAD1CODE].size ); + + if (copy_brres) + { + u8 *ptr = GetDataMemFile(&mf,off+12,4); + if (ptr) + write_be32(ptr,0xffafd500); + } + off += CT_CODE_TEX_OFFSET; + } + + + //--- write CTCODE data + + if ( !err && copy_ct1data ) + { +//----------------- +if (use_dynamic) { +//----------------- + err = WriteMemFileAt( &mf, off + off_term, "", 0 ); + + if (!err) + err = WriteMemFileAt( &mf, off + off_head, + &ctcode->ct_head, sizeof(ctcode->ct_head) ); + + if ( !err && ctcode->cup ) + err = WriteMemFileAt( &mf, off + off_cup, ctcode->cup, ctcode->cup_size ); +PRINT("space behind cup: %x\n",off_crs-off_cup-ctcode->cup_size); + + if ( !err && ctcode->crs ) + err = WriteMemFileAt( &mf, off + off_crs, ctcode->crs, ctcode->crs_size ); +#if 1 +{ + uint space = off_mod1 - off_crs - ctcode->crs_size; + fprintf(stderr,"space behind crs: %x => (%zu) + %zu tracks\n", + space, + (ctcode->crs_size-sizeof(*ctcode->crs))/sizeof(ctcode_crs1_data_t), + space/sizeof(ctcode_crs1_data_t) ); +} +#endif + + if ( !err && bz2s[BIDX_MOD1].data ) + err = WriteMemFileAt( &mf, off + off_mod1, + bz2s[BIDX_MOD1].data, bz2s[BIDX_MOD1].size ); + + if ( !err && bz2s[BIDX_MOD2].data ) + err = WriteMemFileAt( &mf, off + off_mod2, + bz2s[BIDX_MOD2].data, bz2s[BIDX_MOD2].size ); + + if ( !err && bz2s[BIDX_OVR1].data ) + err = WriteMemFileAt( &mf, off + off_ovr1, + bz2s[BIDX_OVR1].data, bz2s[BIDX_OVR1].size ); +//-------------- +} else { +//-------------- + if (!err) + err = WriteMemFileAt( &mf, off + CT_CODE_EXTEND_OFFSET + CT_CODE_EXTEND_SIZE, + "", 0 ); + + err = WriteMemFileAt( &mf, off, + &ctcode->ct_head, sizeof(ctcode->ct_head) ); + + if ( !err && ctcode->cup ) + err = WriteMemFile( &mf, ctcode->cup, ctcode->cup_size ); + + if ( !err && ctcode->crs ) + err = WriteMemFile( &mf, ctcode->crs, ctcode->crs_size ); + + if ( !err && bz2s[BIDX_MOD1].data ) + err = WriteMemFileAt( &mf, off + CT_CODE_EXTEND_OFFSET + CT_CODE_MOD1_OFFSET, + bz2s[BIDX_MOD1].data, bz2s[BIDX_MOD1].size ); + + if ( !err && bz2s[BIDX_MOD2].data ) + err = WriteMemFileAt( &mf, off + CT_CODE_EXTEND_OFFSET + CT_CODE_MOD2_OFFSET, + bz2s[BIDX_MOD2].data, bz2s[BIDX_MOD2].size ); + + if ( !err && bz2s[BIDX_OVR1].data ) + err = WriteMemFileAt( &mf, off + CT_CODE_EXTEND_OFFSET + CT_CODE_OVR1_OFFSET, + bz2s[BIDX_OVR1].data, bz2s[BIDX_OVR1].size ); +//-------------- +} +//-------------- + } + + + //--- write isolated sections + + if ( !err && copy_cup1 && ctcode->cup ) + err = WriteMemFileAt( &mf, cur_off, ctcode->cup, ctcode->cup_size ); + + if ( !err && copy_crs1 && ctcode->crs ) + err = WriteMemFileAt( &mf, cur_off, ctcode->crs, ctcode->crs_size ); + + if ( !err && copy_mod1 && bz2s[BIDX_MOD1].status ) + err = WriteMemFileAt( &mf, cur_off, bz2s[BIDX_MOD1].data, bz2s[BIDX_MOD1].size ); + + if ( !err && copy_mod2 && bz2s[BIDX_MOD2].status ) + err = WriteMemFileAt( &mf, cur_off, bz2s[BIDX_MOD2].data, bz2s[BIDX_MOD2].size ); + + if ( !err && copy_ovr1 && bz2s[BIDX_OVR1].status ) + err = WriteMemFileAt( &mf, cur_off, bz2s[BIDX_OVR1].data, bz2s[BIDX_OVR1].size ); + + if ( !err && copy_ct1code && bz2s[BIDX_BAD1CODE].status ) + err = WriteMemFileAt( &mf, cur_off, bz2s[BIDX_BAD1CODE].data, bz2s[BIDX_BAD1CODE].size ); + + if ( !err && copy_ct0code && bz2s[BIDX_BAD0CODE].status ) + err = WriteMemFileAt( &mf, cur_off, bz2s[BIDX_BAD0CODE].data, bz2s[BIDX_BAD0CODE].size ); + + if ( !err && copy_ct0data && bz2s[BIDX_BAD0DATA].status ) + err = WriteMemFileAt( &mf, cur_off, bz2s[BIDX_BAD0DATA].data, bz2s[BIDX_BAD0DATA].size ); + + + //--- write image + + if ( !err && copy_tex ) + { + u8 *data; + uint size; + DecodeBZIP2(&data,&size,0,ctcode_image_bz2, sizeof(ctcode_image_bz2)); + err = WriteMemFileAt( &mf, tex_off+CT_CODE_TEX_IMAGE_OFF, data, size ); + FREE(data); + } + + + //--- write strap string-pool + + if ( !err && copy_brres ) + { + if (use_dynamic) + fprintf(stderr,"str-pool: %x %zx\n",CT_CODE_STRINGS_OFF,off+off_term); + u8 *data; + uint size; + DecodeBZIP2(&data,&size,0,ctcode_brres_strings_bz2, sizeof(ctcode_brres_strings_bz2)); + err = WriteMemFileAt( &mf, CT_CODE_STRINGS_OFF, data, size ); + FREE(data); + } + + + //--- terminate + + FileMode_t fmode = GetFileModeByOpt(testmode,0,0); + if ( save_mode == CTS_SZS ) + { + szs_file_t szs; + AssignSZS(&szs,true,mf.data,mf.fend,true,FF_BRRES,0); + mf.data = 0; + err = SaveSZS(&szs,fname,opt_overwrite,true); + ResetSZS(&szs); + } + else + err = SaveMemFile(&mf,fname,0,fmode,false); + + ResetMemFile(&mf); + ClearBZ2S(srctab,-1); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int opt_ct_log = 0; +bool old_spiny = false; +u32 crs1_property = 0x800; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCtLog ( ccp arg ) +{ + static const KeywordTab_t keytab[] = + { + { SEA_LOG_OFF, "OFF", "NONE", 0 }, + { SEA_LOG_SUCCESS, "SUCCESS", 0, 0 }, + { SEA_LOG_FOUND, "FOUND", 0, 0 }, + { SEA_LOG_ALL, "ALL", 0, 0 }, + {0,0,0,0} + }; + + if ( !arg || !*arg ) + opt_ct_log++; + else if ( *arg >= '0' && *arg <= '9' ) + { + opt_ct_log = str2l(arg,0,10); + if ( opt_ct_log < 0 ) + opt_ct_log = 0 ; + } + else + { + int abbrev; + const KeywordTab_t *key = ScanKeyword(&abbrev,arg,keytab); + if (!key) + { + PrintKeywordError(keytab,arg,abbrev,0, + "keyword for option --ct-log"); + return 1; + } + opt_ct_log = key->id; + } + + if ( opt_ct_log > SEA_LOG_ALL ) + opt_ct_log = SEA_LOG_ALL; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCRS1 ( ccp arg ) +{ + return ERR_OK != ScanSizeOptU32( + &crs1_property, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "crs1", // ccp opt_name + 0, // u64 min + 0xffffffff, // u64 max + 1, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptAllowSlots ( ccp arg ) +{ + enum { K_TRACKS, K_ARENAS, K_SPECIAL, K_CTCODE }; + + static const KeywordTab_t keytab[] = + { + { K_TRACKS, "TRACKS", 0, 0 }, + { K_ARENAS, "ARENAS", 0, 0 }, + { K_SPECIAL, "SPECIAL", 0, 0 }, + { K_CTCODE, "CTCODE", "CT-CODE", 0 }, + {0,0,0,0} + }; + + SetupUsedSlots(); + if ( !arg || !*arg ) + return 0; + + for(;;) + { + while ( *arg == ' ' || *arg == '\t' || *arg == ',' ) + arg++; + + const u8 value = *arg == '-' ? 2 : 0; + if (value) + arg++; + + while ( *arg == ' ' || *arg == '\t' ) + arg++; + if (!*arg) + break; + + u32 stat = 0, n1 = 0, n2 = 0; + if ( *arg >= '0' && *arg <= '9' ) + { + arg = ScanRangeU32(arg,&stat,&n1,&n2,0,BMG_N_CT_TRACK-1); + } + else + { + char namebuf[50]; + char *dest = namebuf; + while ( isalnum((int)*arg) ) + { + if ( dest < namebuf + sizeof(namebuf)-1 ) + *dest++ = *arg; + arg++; + } + *dest = 0; + + int abbrev; + const KeywordTab_t *key = ScanKeyword(&abbrev,namebuf,keytab); + if (!key) + { + PrintKeywordError(keytab,namebuf,abbrev,0, + "keyword for option --allow-slots"); + return 1; + } + DASSERT(key); + + switch(key->id) + { + case K_TRACKS: + stat = 2; + n1 = 0; + n2 = BMG_N_TRACK-1; + break; + + case K_ARENAS: + stat = 2; + n1 = BMG_N_TRACK; + n2 = BMG_N_TRACK + BMG_N_ARENA - 1; + break; + + case K_CTCODE: + memset(ctcode_used_slot,value,BMG_N_TRACK+BMG_N_ARENA); + // fall through + + case K_SPECIAL: + stat = 2; + n1 = 0x36; + n2 = 0x3a; + break; + } + } + + noPRINT("RANGE[%d]: 0x%02x .. 0x%02x\n",stat,n1,n2); + if ( stat && n1 <= n2 ) + { + DASSERT( n2 < BMG_N_CT_TRACK ); + memset(ctcode_used_slot + n1, value, n2+1-n1 ); + } + } + + if ( logging > 1 ) // must be set before --allow-slots + HexDump16(stdlog,0,0,ctcode_used_slot,sizeof(ctcode_used_slot)); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-ctcode.h b/project/src/lib-ctcode.h new file mode 100644 index 0000000..facd900 --- /dev/null +++ b/project/src/lib-ctcode.h @@ -0,0 +1,543 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_CTCODE_H +#define SZS_LIB_CTCODE_H 1 + +#include "lib-std.h" +#include "lib-xbmg.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CT0_CODE_MAGIC_NUM 0xbad0c0de +#define CT0_DATA_MAGIC_NUM 0xbad0da7a +#define CT1_CODE_MAGIC_NUM 0xbad1c0de +#define CT1_DATA_MAGIC_NUM 0xbad1da7a + +#define CT_CUP1_MAGIC_NUM 0x43555031 +#define CT_CRS1_MAGIC_NUM 0x43525331 +#define CT_MOD1_MAGIC_NUM 0x4d4f4431 +#define CT_MOD2_MAGIC_NUM 0x4d4f4432 +#define CT_OVR1_MAGIC_NUM 0x4f565231 + +#define CT_TEXT_MAGIC8 "#CT-CODE" +#define CT_TEXT_MAGIC8_NUM 0x2343542d434f4445ull + +#define CT_CODE_TEX_OFFSET 0x760 // CTCODE offset in TEX0 file +#define CT_CODE_TEX_IMAGE_OFF 0x658c0 // IMAGE offset in TEX0 file +#define CT_CODE_TEX_SIZE 0x87640 // total TEX0 size +#define CT_CODE_STRINGS_OFF 0x58a140 // total string pool offset +#define CT_CODE_BRRES_SIZE 0x58a280 // BRRES file size + +#define CT_CODE_EXTEND_OFFSET 0x142d0 // EXTEND offset in CTCODE +#define CT_CODE_MOD1_OFFSET 0x00000 // MOD1 offset in EXTEND +#define CT_CODE_MOD2_OFFSET 0x00f00 // MOD2 offset in EXTEND +#define CT_CODE_OVR1_OFFSET 0x02980 // OVR1 offset in EXTEND +#define CT_CODE_EXTEND_SIZE 0x07ce4 // total EXTEND size + +#define CT_NAME_SIZE 64 // defined by CTCODE + +#define CT_CODE_DEFAULT_CUPS 64 // default supported cups, must be <= 0x100 +#define CT_CODE_MAX_CUPS 64 // max supported cups, must be <= 0x100 +#define CT_CODE_DEFAULT_TRACKS 0x100 // default supported tracks +#define CT_CODE_MAX_TRACKS 0x100 // max supported tracks +#define CT_CODE_MAX_CUPREF 0x10 // track usage cross-ref, must be <= 0x100 + +#define LE_CODE_MAX_TRACKS BMG_N_LE_TRACK // max supported tracks +#define LE_CODE_MAX_CUPS BMG_N_LE_RCUP // max supported cups, must be <= 0x100 +#define LE_FIRST_LOWER_CT_SLOT 0x44 // LE: first used custom slot +#define LE_FIRST_UPPER_CT_SLOT 0x120 // LE: first used upper custom slot +#define LE_LAST_UPPER_CT_SLOT 0x13d // LE: first used upper custom slot +#define LE_FIRST_RANDOM_SLOT 0x3e // LE: first used upper custom slot +#define LE_LAST_RANDOM_SLOT 0x41 // LE: first used upper custom slot + +#define CODE_MAX_TRACKS LE_CODE_MAX_TRACKS // max(CT/LE CODE_MAX_TRACKS) +#define CODE_MAX_CUPS LE_CODE_MAX_CUPS // max(CT/LE CODE_MAX_CUPS) +#define USED_SLOT_MAX 0x100 // max predefined used slot + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LE-CODE support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[le_cup_track_t]] [[le_property_t]] [[le_music_t]] [[le_flags_t]] + +#define LE_CODE_MIN_PHASE CTM_LECODE_MIN - CTM_LECODE_BASE +#define LE_CODE_STD_PHASE CTM_LECODE_DEF - CTM_LECODE_BASE +#define LE_CODE_MAX_PHASE CTM_LECODE_MAX - CTM_LECODE_BASE + +typedef u32 le_cup_track_t; // for tracks and arenas + +typedef u8 le_property_t; // type of properties +typedef u8 le_music_t; // type of music slots +typedef u8 le_flags_t; // type of ttack flags + +static inline int TrackByAliasLE ( le_property_t prop, le_music_t music ) + { return prop << 8 | music; } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ctcode_*_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ctcode_sect_info_t]] + +typedef struct ctcode_sect_info_t +{ + /* 00 */ be32_t off; // file offset of section + /* 04 */ char name[4]; // section name + /* 08 */ +} +__attribute__ ((packed)) ctcode_sect_info_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[ctcode_header_t]] + +typedef struct ctcode_header_t +{ + /* 00 */ char magic[4]; // = CT1_DATA_MAGIC_NUM + /* 04 */ be32_t size; // total size + /* 08 */ be32_t unknown_08; + /* 0c */ be32_t n_sect; // number of section + /* 10 */ be32_t padding[4]; + /* 20 */ ctcode_sect_info_t + sect_info[6]; // section offsets and names + /* 50 */ +} +__attribute__ ((packed)) ctcode_header_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[ctcode_cup1_data_t]] + +typedef struct ctcode_cup1_data_t +{ + /* 00 */ be16_t name[CT_NAME_SIZE]; // name of cup (not relevant) + /* 80 */ be32_t track_id[5]; // index of related tracks + /* 94 */ be32_t unknown_84[0x1b]; // ?? padding + /* 100 */ +} +__attribute__ ((packed)) ctcode_cup1_data_t; + +//----------------------------------------------------------------------------- +// [[ctcode_cup1_head_t]] + +typedef struct ctcode_cup1_head_t +{ + /* 00 */ char name[4]; // section name: "CUP1" + /* 04 */ be32_t size; // size of section + /* 08 */ be32_t unknown_08; + /* 0c */ be32_t n_racing_cups; // number of racing cups + /* 10 */ be32_t n_battle_cups; // number of battle cups + /* 14 */ be32_t unknown_34[11]; // 5*0x800 + 6*0x000 + /* 40 */ ctcode_cup1_data_t data[]; +} +__attribute__ ((packed)) ctcode_cup1_head_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[ctcode_crs1_data_t]] + +typedef struct ctcode_crs1_data_t +{ + /* 00 */ be16_t tname[CT_NAME_SIZE]; // name of track (not relevant) + /* 80 */ char filename[64]; // file name of track + /* c0 */ be32_t music_id; // index of music + /* c4 */ be32_t property_id; // track index for properties + /* c8 */ be32_t cup_id; // cup index (not relevant) + /* cc */ be32_t unknown_cc[0x0d]; // ?? padding + /* 100 */ +} +__attribute__ ((packed)) ctcode_crs1_data_t; + +//----------------------------------------------------------------------------- +// [[ctcode_crs1_head_t]] + +typedef struct ctcode_crs1_head_t +{ + /* 00 */ char name[4]; // section name: "CRS1" + /* 04 */ be32_t size; // size of section + /* 08 */ be32_t unknown_08; // value 0x00 + /* 0c */ be32_t n_tracks; // number of tracks + /* 10 */ be32_t unknown_10; // value 0x2a (first track behind arenas?) + /* 14 */ be32_t property[8]; // properties for first 8 cups? + // 0x400: no sound + // 0x800: sound + /* 34 */ be32_t unknown_14[3]; // always 0 (padding?) + /* 40 */ ctcode_crs1_data_t data[]; +} +__attribute__ ((packed)) ctcode_crs1_head_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct ctcode_cupref_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ctcode_cupref_t]] + +typedef struct ctcode_cupref_t +{ + uint n_racing; // used by n_racing cups + uint n_battle; // used by n_battle cups + + uint n; // number of references + u8 cup[CT_CODE_MAX_CUPREF]; // cup index + u8 idx[CT_CODE_MAX_CUPREF]; // track index within cup +} +ctcode_cupref_t; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct ctcode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ctcode_t]] + +typedef struct ctcode_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // file format of source + ct_bmg_t ctb; // CT mode & message identifiers (MID) + + + //--- cup data + + ctcode_cup1_head_t *cup; // NULL or data pointer to CUP1 data + bool cup_alloced; // true: 'cup' is alloced + uint cup_size; // size of 'cup' + + ctcode_cup1_data_t *cup_racing; // pointer to first racing cup + uint n_racing_cups; // number of defined racing cups (CUP1 counter) + uint max_racing_cups;// max possible number of racing cups + + ctcode_cup1_data_t *cup_battle; // pointer to first battle cup + uint n_battle_cups; // number of defined battle cups (CUP1 counter) + uint max_battle_cups;// max possible number of battle cups + + uint n_unused_cups; // number of unused cups + + + //--- track data + + ctcode_crs1_head_t *crs; // NULL or data pointer to CRS1 data + bool crs_alloced; // true: 'crs' is alloced + uint crs_size; // size of 'crs' + + uint n_tracks; // number of used track slots (CRS1 counter) + uint max_tracks; // total number of track slots (CRS1 data records) + + bmg_t track_file; // file names of any length + bmg_t track_string; // track strings of any length + bmg_t track_ident; // identifiers of any length (e.g. checksums) + + // property, music and le_flags + le_property_t property[CODE_MAX_TRACKS]; // not in use yet!! + le_music_t music[CODE_MAX_TRACKS]; // not in use yet!! + le_flags_t le_flags[CODE_MAX_TRACKS]; + u8 hidden[CODE_MAX_TRACKS]; + + + //--- cup/track reference + + bool cupref_dirty; // true: 'cupref' must be calculated + ctcode_cupref_t cupref[CODE_MAX_TRACKS]; + // cross reference track to cup + uint used_tracks; // number of active racing tracks (used by cup) + uint used_arenas; // number of active battle tracks + uint assigned_arenas;// number of assigned battle tracks + u8 arena_usage[BMG_N_CT_ARENA]; + // 1: arena assigned + + + //--- LE-CODE support + + bool add_wiimm_cup; // true: assing 4*random to cup 8 + bool replace_at; // true: Replace "@..@" on track names + bool use_lecode; // true: enable le-code support + bool use_le_flags; // true: le_flags enabled + + //--- misc + + ctcode_header_t ct_head; // copy of CTCODE header; + #if USE_NEW_CONTAINER_CTC + Container_t container; // data container + #else + DataContainer_t *old_container; // old data container support, init with NULL + #endif + uint save_prepared; // 1:PrepareSaveCTCODE() already called + + + //--- text scan support + + u8 used_slot[CODE_MAX_TRACKS]; // 0:free, 1:used, 2:reserved + u8 next_slot[CODE_MAX_TRACKS]; // predefined slots + u8 force_slot[CODE_MAX_TRACKS]; // 0:std, 1:force slot usage + uint n_next_slot; // >0: index-1 into 'next_slot' and 'force_slot' + uint tracks_defined; // number of really defined tracks + + char *next_cup_name; // NULL or name of next cup + uint track_of_cup; // number of tracks in current cup + + bool cmd_n_used; // true: sub command 'N' inserted 32 tracks +} +ctcode_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[ctcode_save_t]] + +typedef enum ctcode_save_t // save modes +{ + CTS_CT0CODE, // save as FF_CT0_CODE file + CTS_CT0DATA, // save as FF_CT0_DATA file + CTS_CT1CODE, // save as FF_CT1_CODE file + CTS_CT1DATA, // save as FF_CT1_DATA file + + CTS_CUP1, // save only 'CUP1' section as binary file + CTS_CRS1, // save only 'CRS1' section as binary file + CTS_MOD1, // save only 'MOD1' section as binary file + CTS_MOD2, // save only 'MOD2' section as binary file + CTS_OVR1, // save only 'OVR1' section as binary file + + CTS_TEX0, // save as FF_TEX_CT file + CTS_BRRES, // save as FF_BRRES file + CTS_SZS, // save as compressed FF_BRRES file + CTS_BMG, // save as FF_BMG_TXT file + CTS_TRACKS, // save as track listing + + CTS_LIST, // save as text file, mode --list + CTS_REF, // save as text file, mode --ref + CTS_FULL, // save as text file, mode --full +} +ctcode_save_t; + +typedef enum ctlang_save_t // language modes +{ + CTL_ANY, + CTL_EU, + CTL_US, + CTL_JP, + CTL_KO, +} +ctlang_save_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeCTCODE ( ctcode_t * ctcode, ct_mode_t ct_mode ); +void ResetCTCODE ( ctcode_t * ctcode ); +void ResetDataCTCODE ( ctcode_t * ctcode, bool create_empty ); +const VarMap_t * SetupVarsCTCODE(); + +void DumpCTCODE ( FILE *f, uint indent, ctcode_t * ctcode ); + +void CalcCupRefCTCODE ( ctcode_t * ctcode, bool force ); +void PrepareExportCTCODE ( ctcode_t * ctcode ); +void PatchNamesCTCODE ( ctcode_t * ctcode ); +void PrepareSaveCTCODE ( ctcode_t * ctcode ); + +int MusicID2TrackId ( uint music_id, int no_track, int not_found ); +ccp PrintMusicID ( uint mid, bool force_hex ); +ccp PrintPropertyID ( uint tid, bool force_hex ); + +/////////////////////////////////////////////////////////////////////////////// + +struct szs_iterator_t; +int IterateFilesCTCODE +( + struct szs_iterator_t *it, // iterator struct with all infos + bool multi_img // false: mipmaps, true: multi images +); + +//----------------------------------------------------------------------------- + +static inline bool IsCtcodeFF ( file_format_t ff ) + { return ff == FF_TEX_CT || ff == FF_CT_TEXT || ff == FF_LE_BIN; } + +//----------------------------------------------------------------------------- + +enumError ScanRawCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerCTC_t * cdata // NULL or container-data +); + +//----------------------------------------------------------------------------- + +enumError ScanTextCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerCTC_t * cdata, // NULL or container-data + CheckMode_t mode // not NULL: call CheckCTCODE(mode) +); + +//----------------------------------------------------------------------------- + +struct raw_data_t; + +enumError ScanRawDataCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckCTCODE(mode) +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadCTCODE +( + ctcode_t * ctcode, // CTCODE data structure + ct_mode_t ct_mode, // not CTM_NO_INIT: Initialize + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckCTCODE(mode) +); + +//----------------------------------------------------------------------------- + +enumError SaveTextCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time, // true: set time stamps + const output_mode_t * outmode // output mode, if NULL: use defaults +); + +//----------------------------------------------------------------------------- + +enumError SaveMessageCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTrackListCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time, // true: set time stamps + uint select, // track selection + // 0: only custom tracks + // 1: standard + custom tracks + // 2: standard + custom + battle tracks + bool print_slots, // true: print music and property slots + bool print_head, // true: print table header + bool raw_mode // true: print raw text without escapes + +); + +static inline enumError SaveTrackListByOptCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + return SaveTrackListCTCODE( ctcode, fname, raw_mode, + long_count, !brief_count, + print_header, raw_mode ); +} + +//----------------------------------------------------------------------------- + +enumError SaveTrackTableCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveCTCODE +( + ctcode_t * ctcode, // pointer to valid CTCODE + ccp fname, // filename of destination + bool set_time, // true: set time stamps + ctcode_save_t save_mode, // save mode + ctlang_save_t lang_mode, // language mode + const output_mode_t * outmode // output mode for text output + // if NULL: use defaults +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern int opt_ct_log; // values: see CTLOG_* above + +//----------------------------------------------------------------------------- + +extern bool old_spiny; +extern u32 crs1_property; +extern u8 ctcode_used_slot[USED_SLOT_MAX]; // 0:free, 1:used, 2:reserved + +int ScanOptCtLog ( ccp arg ); +int ScanOptCRS1 ( ccp arg ); +int ScanOptAllowSlots ( ccp arg ); + +extern ccp opt_image_dir; +extern bool opt_dynamic_ctcode; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_CTCODE_H + diff --git a/project/src/lib-ctdata.c b/project/src/lib-ctdata.c new file mode 100644 index 0000000..69afa41 --- /dev/null +++ b/project/src/lib-ctdata.c @@ -0,0 +1,960 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "dclib-types.h" + +// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 ctcode_tex_header[] = +{ + 0x54,0x45,0x58,0x30, 0x00,0x08,0x76,0x40, 0x00,0x00,0x00,0x03, 0x00,0x00,0x00,0x00, + 0x00,0x06,0x58,0xc0, 0x00,0x08,0x76,0x44, 0x00,0x00,0x00,0x00, 0x02,0x60,0x01,0xc8, + 0x00,0x00,0x00,0x0e, 0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, +}; + +// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 ctcode_header[] = +{ + 0xba,0xd1,0xda,0x7a, 0x00,0x01,0xbf,0xb4, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x05, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x50, 0x43,0x55,0x50,0x31, 0x00,0x00,0x38,0x90, 0x43,0x52,0x53,0x31, + 0x00,0x01,0x42,0xd0, 0x4d,0x4f,0x44,0x31, 0x00,0x01,0x51,0xd0, 0x4d,0x4f,0x44,0x32, + 0x00,0x01,0x6c,0x50, 0x4f,0x56,0x52,0x31, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + // 0x50 = 80 Bytes +}; + +// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 cup1_header[] = +{ + 0x43,0x55,0x50,0x31, 0x00,0x00,0x38,0x40, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x36, + 0x00,0x00,0x00,0x02, 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, + 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + // 0x40 = 64 Bytes +}; + +// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 crs1_header[] = +{ + 0x43,0x52,0x53,0x31, 0x00,0x01,0x0b,0x40, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xfa, + 0x00,0x00,0x00,0x2a, 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, + 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, 0x00,0x00,0x08,0x00, + 0x00,0x00,0x08,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + // 0x40 = 64 Bytes +}; + +// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 ctcode_image_bz2[] = +{ + 0x00,0x02,0x1d,0x90, 0x42,0x5a,0x68,0x32, 0x31,0x41,0x59,0x26, 0x53,0x59,0x14,0xfc, + 0x47,0x10,0x00,0xb4, 0x04,0x7f,0xff,0xfa, 0x70,0x67,0x5f,0xf7, 0x70,0x60,0x2f,0xff, + 0xf4,0xf7,0x7f,0xf2, 0x70,0x18,0x0f,0xfb, 0x7c,0x67,0x7f,0xf7, 0x76,0x02,0x1f,0x83, + 0x01,0x01,0x1f,0x81, 0x80,0x80,0x8f,0xe0, 0x21,0x3e,0x3d,0x45, 0x29,0xf2,0x94,0x89, + 0xde,0xc7,0xd5,0x5b, 0x7c,0xee,0xbc,0xfb, 0xc9,0x4b,0xed,0x55, 0x51,0x47,0xd0,0x2b, + 0xef,0x9a,0xdc,0x31, 0x54,0x02,0x05,0x14, 0xd2,0x9e,0x7b,0xef, 0xb3,0xd8,0xb4,0xd7, + 0xdd,0x63,0xe7,0xdd, 0xcd,0xc7,0x89,0xdf, 0x4e,0xe5,0x9d,0xc3, 0x9d,0x2f,0xa6,0xd8, + 0x3d,0x7d,0xf7,0xde, 0x50,0xee,0xb3,0xc9, 0xef,0xae,0x6f,0x2f, 0x1d,0x7b,0x7b,0xae, + 0xdf,0x6a,0xd9,0xbe, 0x8f,0xa6,0xd7,0xcf, 0x1f,0x3e,0xf5,0xb6, 0xf0,0xd3,0x44,0x4d, + 0x00,0x13,0x26,0x40, 0x01,0x32,0x22,0x32, 0x4f,0x32,0x9e,0x84, 0xf4,0x09,0x01,0xe8, + 0xd4,0x69,0xb5,0x0d, 0xa4,0x1a,0x00,0x1a, 0x00,0x00,0x0f,0x50, 0x0d,0x06,0x82,0x53, + 0x41,0x04,0x26,0x82, 0x04,0xd2,0x61,0x14, 0x7e,0x4c,0xa9,0xfa, 0x69,0x27,0xa8,0x7a, + 0x27,0xa9,0xa3,0xd4, 0x0d,0x34,0x06,0x83, 0x6a,0x03,0xd4,0x00, 0x0d,0x1e,0x90,0x34, + 0x00,0xc9,0xea,0x0d, 0x34,0x00,0x94,0xc5, 0x11,0x08,0xd4,0xf4, 0x4c,0x53,0x41,0xa6, + 0x8f,0x53,0xd2,0x1e, 0x90,0xd0,0xd3,0x4d, 0x3d,0x4d,0x00,0x34, 0x36,0x93,0xd4,0x06, + 0x83,0x40,0x00,0x00, 0x1a,0x00,0x00,0x00, 0x02,0x14,0x92,0x21, 0xa5,0x4f,0xcd,0x54, + // 0x100: + 0xf5,0x37,0xa5,0x3d, 0x47,0x94,0x7a,0x80, 0x1f,0xa4,0x80,0xd0, 0x64,0x00,0x1a,0x00, + 0x00,0xf5,0x01,0xa0, 0x00,0xd0,0x68,0x0c, 0x83,0x40,0x00,0xd0, 0x21,0x52,0x88,0x49, + 0xbd,0x22,0x7a,0x40, 0xd0,0xd3,0xf5,0x4d, 0x3d,0x4c,0x43,0x4d, 0x0d,0x0d,0xa8,0xd8, + 0x91,0x93,0x40,0x68, 0x64,0x00,0x34,0x0d, 0x03,0x40,0x00,0x01, 0xa0,0x06,0x81,0xa0, + 0x93,0x51,0x21,0x34, 0x13,0x23,0x42,0x68, 0x1a,0x49,0xe1,0x51, 0xea,0x7e,0xa9,0xed, + 0x29,0xea,0x1f,0xaa, 0x64,0x79,0x4c,0x80, 0xd1,0xa1,0xfa,0xa0, 0x34,0x0c,0x86,0x80, + 0x68,0x31,0x00,0x00, 0x00,0x01,0xec,0x02, 0x88,0xa0,0xb0,0x28, 0x8a,0x0b,0xf2,0x76, + 0x98,0xd7,0x21,0x00, 0x21,0x10,0x91,0x00, 0x61,0x10,0x81,0x45, 0x41,0x1f,0x14,0x51, + 0x09,0x10,0x15,0x51, 0xc6,0xa6,0x7c,0x39, 0x15,0x40,0x0a,0x15, 0x40,0xa4,0x14,0x02, + 0x84,0xa4,0x1f,0x47, 0x87,0xad,0xdf,0xdd, 0xb9,0x28,0xa4,0x40, 0xa4,0x29,0x5e,0x73, + 0xc8,0xcb,0x58,0xf4, 0x00,0xb2,0x28,0x34, 0x22,0xa7,0x04,0x05, 0x84,0x42,0x84,0x43, + 0x0a,0x8c,0x8a,0xaa, 0x26,0x69,0x01,0x50, 0x39,0xc8,0x14,0x41, 0x38,0x40,0x8a,0x80, + 0x9b,0x84,0x60,0x0a, 0x06,0x24,0x5a,0x11, 0xa4,0xa0,0x66,0x82, 0x28,0x2a,0x99,0xaa, + 0x98,0x21,0x11,0x88, 0x46,0xa9,0x51,0x10, 0xe0,0x42,0x1d,0x10, 0x2a,0x0e,0xe8,0x51, + 0xe2,0x42,0xa8,0x69, 0x40,0x89,0x40,0x28, 0xf1,0x65,0x5d,0xbc, 0x0a,0x1d,0xe4,0xaa, + 0x9a,0x32,0xa0,0x6d, 0x01,0x4a,0xd0,0x52, 0x6e,0x94,0x47,0x90, 0x84,0x13,0x20,0x4e, + // 0x200: + 0x30,0x02,0x64,0x01, 0xe0,0xc1,0xdf,0x87, 0x96,0x10,0x76,0x8e, 0x32,0xea,0x04,0x28, + 0x57,0xde,0x7b,0xec, 0x53,0x34,0x29,0xa1, 0x20,0xb9,0x23,0x46, 0x04,0x39,0x12,0x71, + 0xe0,0xda,0xca,0x1a, 0x71,0xd1,0xcb,0x9a, 0x55,0xe1,0x31,0x29, 0xd2,0xc2,0x64,0x9b, + 0xb9,0xb1,0x17,0x7c, 0x8b,0xab,0x7c,0x85, 0xbf,0x17,0x44,0x6e, 0xdf,0x82,0xb9,0x20, + 0xd2,0x1a,0x9e,0x58, 0x07,0x53,0xaa,0xb7, 0xb6,0xa0,0x5a,0xe6, 0x8d,0x95,0x84,0xb6, + 0x92,0x06,0xe6,0x1c, 0xa2,0x75,0x3b,0xb6, 0x35,0x26,0x62,0x01, 0xab,0x24,0x1a,0xa0, + 0xe4,0x82,0x22,0x9d, 0x2c,0x26,0x3b,0x0a, 0xcb,0xa6,0xa1,0xb1, 0x90,0xe3,0x93,0xc2, + 0xae,0x92,0x69,0x13, 0x82,0x14,0xec,0x7b, 0x30,0xa6,0x4b,0xb1, 0x03,0x92,0xea,0x77, + 0xc2,0xf6,0xb5,0xad, 0x0a,0x21,0x8e,0x28, 0x1c,0x37,0x97,0x2a, 0x30,0xe7,0x66,0xa8, + 0x4c,0x87,0x09,0x59, 0x92,0x6b,0xb6,0x83, 0x06,0x33,0xb8,0x5f, 0xbb,0x16,0x43,0x9a, + 0x69,0x19,0xc8,0x4e, 0x8c,0xb0,0x58,0x6f, 0x64,0xc3,0x0b,0x20, 0xea,0x91,0x06,0x43, + 0x6b,0xb7,0x41,0xa0, 0x5c,0x21,0xd3,0x23, 0xb6,0xc4,0x07,0x2e, 0x1a,0x03,0x92,0x50, + 0xde,0xfd,0x90,0xd6, 0x8d,0x89,0x32,0x50, 0xf2,0x24,0xda,0x93, 0x57,0x44,0xcb,0x15, + 0x37,0x26,0x04,0x82, 0xbf,0x25,0x42,0x97, 0x2d,0x97,0x09,0x55, 0x40,0xa0,0x66,0xc2, + 0xd8,0x0a,0x5a,0x00, 0xb0,0x9a,0x25,0x72, 0xbb,0x92,0xd8,0x06, 0x69,0x69,0x3e,0x7b, + 0x68,0x43,0x7c,0xae, 0xd1,0xa8,0x50,0xc9, 0x0a,0x0d,0xd0,0x86, 0x89,0xa7,0x69,0x03, + // 0x300: + 0x24,0x4e,0xa8,0xe5, 0xd6,0x6d,0x27,0x02, 0x35,0x6d,0x0a,0x39, 0x0f,0x24,0x3c,0x92, + 0x1a,0x95,0xad,0x49, 0xab,0x7e,0xd9,0xa9, 0x3b,0x33,0xb7,0xd1, 0x93,0x12,0x24,0x4d, + 0xa1,0xdd,0x34,0x6f, 0x84,0xf3,0x9b,0xf1, 0xe1,0x29,0xf2,0xad, 0x1a,0x73,0x18,0x73, + 0x48,0x1d,0x82,0x1a, 0xb0,0xa6,0x1d,0x2c, 0xd1,0x90,0x0d,0xd4, 0x09,0x52,0x1e,0x50, + 0x08,0x43,0xbc,0x02, 0x4e,0xd8,0x65,0x3e, 0xc7,0x66,0x5e,0x9b, 0xf1,0x19,0x72,0xd0, + 0xb4,0x2c,0x3f,0x17, 0xcf,0x47,0x8a,0xb7, 0xa6,0x69,0x8c,0x47, 0xf0,0xda,0xf0,0xd2, + 0x22,0x68,0x04,0x05, 0x44,0xc0,0x2a,0xe3, 0xbd,0x58,0xca,0x63, 0x84,0xac,0x15,0x26, + 0xa6,0x08,0x89,0xf8, 0xc3,0xeb,0x9c,0x2f, 0x1e,0x43,0xf4,0xde, 0xb0,0xdb,0xf1,0xb0, + 0x6e,0x17,0xa0,0x23, 0x3d,0x54,0x65,0xaa, 0xb8,0x27,0x7d,0xf5, 0x23,0x9e,0xef,0xf1, + 0x19,0xf1,0xe7,0x9f, 0x39,0x8c,0xe1,0x20, 0x07,0x7d,0x1f,0x4e, 0xfc,0xb6,0x6d,0x7b, + 0x50,0xdf,0xf1,0x7b, 0xfa,0x38,0xe4,0xdc, 0x72,0xcc,0xb8,0x73, 0xc5,0x13,0xe5,0x62, + 0x57,0xa5,0x37,0x77, 0x57,0x23,0x49,0xc1, 0x3d,0xfd,0x9e,0x7f, 0x27,0xa6,0x6c,0xcf, + 0xea,0x6b,0x9e,0x58, 0x3d,0x59,0x9d,0x0a, 0xf3,0x61,0x8e,0x10, 0x0a,0x64,0x02,0xab, + 0x63,0x70,0x05,0x02, 0x0a,0x49,0x16,0x41, 0x00,0x95,0x67,0x8a, 0xcb,0x60,0x5d,0x79, + 0x5c,0xae,0xb5,0xe0, 0xe6,0xa4,0x38,0x8e, 0x16,0x46,0x34,0x9d, 0xfa,0x55,0x60,0xa1, + 0x16,0xb2,0x8e,0x2d, 0x4b,0x42,0xd4,0x3d, 0x88,0xe8,0xb4,0xc8, 0x1d,0x8f,0xdb,0xe5, + // 0x400: + 0x82,0xd8,0xb3,0x56, 0x6a,0xc9,0x87,0x0c, 0x94,0x67,0x55,0x74, 0x7b,0xaa,0xdc,0xd3, + 0x10,0x51,0xb0,0xd2, 0x6d,0x28,0xcc,0xf0, 0x75,0x99,0x21,0x0d, 0x3a,0x66,0x15,0x0a, + 0x74,0xae,0x34,0x36, 0xd2,0x28,0xc9,0x2f, 0x22,0xb1,0xc3,0xc9, 0x66,0x79,0x98,0x65, + 0xb5,0x04,0x78,0x56, 0xf6,0x5b,0x14,0xc8, 0x65,0x5d,0x1a,0x67, 0x49,0x0e,0x9e,0x54, + 0x0f,0x2e,0xcc,0xa9, 0x45,0xd9,0x6a,0x52, 0x25,0x58,0x04,0x19, 0x91,0x5a,0x34,0xae, + 0x31,0xda,0x44,0x45, 0x62,0x17,0xa9,0xfc, 0x25,0x9d,0xb4,0xb5, 0xe2,0x4b,0x8e,0xd8, + 0x63,0x2f,0x24,0x08, 0x81,0x22,0x9d,0xc9, 0x43,0x97,0x6d,0x96, 0xa8,0x22,0x52,0x4d, + 0x2e,0xb0,0x87,0x82, 0x64,0x61,0xba,0x42, 0xa7,0x2a,0xd1,0xb1, 0x9a,0x3f,0x1e,0xb8, + 0x2f,0x97,0x1c,0x6b, 0x20,0x08,0x07,0x37, 0x7a,0x3d,0xf9,0x54, 0xc5,0x7a,0x48,0xbc, + 0xdf,0x6d,0xaa,0x0a, 0xf9,0xf9,0xc4,0x51, 0x4c,0x5c,0xeb,0x11, 0x17,0xf2,0xa2,0xe6, + 0xcc,0x0a,0x22,0x82, 0xf4,0x33,0xc3,0xd0, 0xf2,0x83,0x9f,0xcd, 0x6c,0xb2,0x44,0xdd, + 0xaf,0x6d,0xdf,0xe7, 0xfc,0x71,0xbf,0xea, 0x0b,0xd7,0xcb,0x52, 0xee,0xb1,0x41,0x98, + 0x10,0x85,0x63,0x0f, 0x53,0x25,0x8f,0xf2, 0x95,0xda,0x9b,0x4c, 0xbd,0xa9,0xf0,0xbe, + 0x94,0x7d,0x7f,0x8d, 0x7d,0x9f,0x8a,0xe7, 0xca,0xb1,0xae,0x21, 0xbf,0xbc,0xd0,0xe9, + 0x45,0x73,0xe8,0xfd, 0x6c,0xc0,0xcd,0xf0, 0xa0,0xa2,0x83,0xeb, 0xfe,0x79,0xf5,0xa7, + 0x86,0x3a,0x0d,0xfc, 0xd2,0xd1,0x9d,0xb4, 0xb0,0x2c,0x3d,0x7e, 0x77,0x49,0x2b,0xd5, + // 0x500: + 0xf7,0xbf,0x8e,0x7e, 0x43,0xf6,0x41,0xe8, 0xcb,0xd4,0x7a,0x5a, 0x32,0x5e,0xa1,0xf8, + 0x2a,0x0f,0x83,0xd0, 0x8d,0xfe,0x42,0xc2, 0xc8,0x3f,0x60,0x79, 0x7f,0xdf,0x2f,0x69, + 0x65,0x64,0x04,0x5f, 0x20,0x89,0x0d,0xcb, 0x8f,0xf5,0xc7,0x8b, 0x5e,0xe1,0xa2,0xb8, + 0xb3,0x07,0xe5,0x10, 0x54,0xe4,0x7d,0x7c, 0x28,0x32,0x9a,0x1d, 0x48,0x2f,0xc5,0xf0, + 0xe5,0xd2,0xe8,0x84, 0x43,0x3b,0x28,0x69, 0x16,0x32,0xed,0xaa, 0x21,0xdd,0x11,0x0f, + 0x27,0x00,0x17,0xb9, 0xd1,0x8a,0x88,0xfd, 0x09,0x14,0xdd,0x20, 0x01,0x08,0x84,0x0a, + 0x8a,0x25,0x02,0x06, 0xc4,0x80,0xaf,0x12, 0x76,0x24,0x00,0x0c, 0xb0,0x0a,0x65,0x93, + 0x15,0x3b,0x32,0xa1, 0xc5,0x80,0x4c,0x90, 0x29,0x40,0x22,0x14, 0x88,0x52,0xa8,0x03, + 0x4a,0xfc,0x08,0x45, 0x4c,0x80,0x0d,0xa5, 0x57,0x74,0x20,0x1a, 0x80,0x40,0x0a,0x04, + 0x14,0x0d,0x48,0x20, 0x29,0x48,0x26,0x4a, 0x00,0x1c,0x89,0x41, 0x15,0x71,0x08,0x29, + 0xcb,0x85,0x51,0x71, 0x0a,0x26,0xc4,0x83, 0xe0,0xc2,0x0e,0xa4, 0x10,0xf1,0x63,0x25, + 0x5a,0x5a,0x39,0x21, 0xc9,0x43,0xbf,0x1e, 0x34,0xae,0xa0,0x44, 0xe3,0x20,0xb8,0x48, + 0x85,0xf0,0xb0,0x0c, 0x8a,0x14,0xa1,0x57, 0x7c,0x8f,0x93,0x1a, 0x80,0xa6,0x94,0x4c, + 0xd6,0xa5,0xa7,0x20, 0x65,0x80,0xdc,0x49, 0x82,0x4d,0x28,0x31, 0x0d,0x0b,0xed,0x3c, + 0x9c,0x20,0x43,0x0c, 0xea,0x4a,0x5f,0xa1, 0x40,0x63,0xa2,0xa0, 0x61,0x02,0x16,0x85, + 0x27,0xd4,0x43,0xe7, 0xab,0xa9,0x4d,0xa3, 0xc6,0xb5,0x2e,0xa4, 0x72,0x47,0xa2,0x50, + // 0x600: + 0xec,0x41,0xd3,0x3a, 0x85,0xa3,0xa7,0x7e, 0x1d,0x28,0x74,0x33, 0x62,0x06,0x6d,0xa1, + 0x8c,0x56,0x4c,0x50, 0x29,0x22,0x8a,0x51, 0xd3,0x41,0x69,0xf4, 0x6c,0x84,0xaa,0x22, + 0xa1,0x76,0xb7,0xc0, 0x6a,0x40,0x75,0x01, 0x92,0x14,0xf5,0x49, 0xbf,0x2a,0x99,0x33, + 0x24,0x8a,0x41,0x48, 0x1e,0x04,0x26,0xe7, 0xb5,0x90,0x32,0x60, 0x2c,0xe0,0x86,0xd4, + 0x3a,0x65,0x57,0x96, 0x0a,0x06,0x84,0x0a, 0xa1,0x29,0x57,0x74, 0x70,0x8d,0x42,0x1e, + 0x03,0x49,0xc1,0xa4, 0x8b,0x00,0x14,0x27, 0x17,0x54,0x03,0xa9, 0x09,0x3d,0xc9,0x21, + 0xda,0x84,0x3a,0x35, 0xc1,0x5e,0x78,0xe6, 0x82,0x25,0x38,0xc2, 0x70,0xb6,0x80,0x7a, + 0x24,0x37,0x4d,0x0d, 0x6b,0x30,0x3a,0x99, 0x29,0xa6,0x14,0x87, 0x82,0xbb,0xae,0x53, + 0x9d,0x51,0x2d,0xfd, 0x24,0xd5,0x98,0x4c, 0x23,0x54,0xf5,0xfd, 0x5f,0x76,0x30,0xa9, + 0xb9,0xac,0xef,0x4c, 0x13,0x08,0x8c,0xe0, 0xca,0x4f,0x03,0x49, 0xd2,0x9c,0xd9,0x6c, + 0x52,0x1a,0x27,0x75, 0x94,0x68,0xce,0xe6, 0x16,0xc9,0xdf,0x54, 0x29,0xf4,0x7b,0x68, + 0xd8,0x98,0x61,0x86, 0x15,0x31,0x53,0x3f, 0x1e,0x0b,0x82,0x1b, 0x60,0x98,0x31,0x48, + 0x44,0x63,0x16,0x61, 0x48,0x64,0xf9,0x77, 0x18,0x3c,0xae,0x6d, 0xfa,0xcb,0x27,0xbd, + 0x29,0x8c,0x45,0x3b, 0xbc,0x74,0x17,0xba, 0xb3,0x6e,0x7d,0x6f, 0x26,0x2f,0x1f,0xa6, + 0x2e,0x45,0x59,0xd4, 0x32,0x96,0x1a,0x88, 0x6b,0xa7,0x1e,0x38, 0x3c,0x29,0xa3,0xf4, + 0xa8,0xe3,0x71,0x68, 0x11,0x3e,0x95,0x01, 0x41,0xcf,0xf0,0xfe, 0x9d,0xe1,0x7f,0xc6, + // 0x700: + 0xa6,0xfd,0xb8,0xfe, 0x3c,0x15,0x11,0xf7, 0x6e,0xbb,0x88,0x33, 0x83,0x48,0x7e,0xa3, + 0xe0,0xe5,0x8b,0xf5, 0x34,0x82,0x67,0x41, 0xf6,0x5b,0x2f,0x00, 0x61,0x0b,0x09,0x79, + 0x20,0xe1,0x23,0xaf, 0x4f,0xc7,0x97,0xaa, 0x29,0x95,0x0f,0xa7, 0x7e,0x68,0xf0,0x2a, + 0x12,0x4c,0x24,0x92, 0xba,0xd8,0x19,0xbd, 0xc3,0x53,0xa2,0x89, 0x69,0x93,0x0a,0xf1, + 0xd6,0x4f,0xd5,0x78, 0x3b,0x5c,0xcd,0x10, 0xf1,0xa5,0x91,0x26, 0x95,0x4d,0x4f,0x0d, + 0x96,0xf8,0x71,0x61, 0x79,0x34,0x44,0x40, 0x58,0xe9,0x40,0x64, 0xec,0xcf,0xf6,0x2e, + 0x1c,0x19,0x2f,0x47, 0xc3,0x83,0x13,0x09, 0x14,0x8a,0xa9,0xaa, 0x1b,0x9d,0xbe,0x6c, + 0x8a,0x54,0x02,0xc4, 0x9b,0x59,0x0a,0x60, 0x6a,0x91,0x42,0x28, 0x01,0x4c,0x23,0xea, + 0xd0,0xbc,0xd1,0x4c, 0x24,0x14,0x87,0x93, 0xbb,0x8d,0xf4,0xd6, 0x9d,0xf8,0x21,0x5a, + 0x54,0x80,0xa1,0xb8, 0x4c,0x90,0xb6,0x18, 0x33,0xa8,0x52,0x0a, 0x4b,0x7f,0x77,0x7d, + 0x4d,0xe9,0xe9,0x66, 0x9a,0x54,0x26,0x8c, 0x59,0x93,0xd1,0xd1, 0xfb,0x2d,0x99,0xe7, + 0x50,0x3c,0x28,0x1a, 0xa1,0x6a,0xcf,0x67, 0xa6,0xa4,0xde,0xca, 0x61,0xfd,0x0e,0x07, + 0x85,0x56,0x66,0x0d, 0xfd,0x6e,0x7f,0x5f, 0xcc,0x70,0xe4,0x21, 0xb4,0x5d,0x89,0x98, + 0xec,0xea,0x76,0xde, 0x9d,0x8f,0x65,0x34, 0xa7,0x9b,0xe0,0x67, 0x16,0x2d,0xf8,0x28, + 0x2d,0x37,0x08,0xbd, 0x76,0x51,0x35,0xdb, 0x52,0x43,0xcc,0xc3, 0xa7,0xd5,0x40,0x65, + 0x41,0x43,0x2f,0xd4, 0x40,0xc8,0x20,0x80, 0xb0,0x43,0x8d,0xfb, 0xaf,0x57,0x43,0x79, + // 0x800: + 0xdd,0x5f,0x6e,0x04, 0xe9,0x85,0x78,0xc5, 0x25,0x00,0x90,0x88, 0xee,0x54,0x09,0x11, + 0xdd,0xea,0x2a,0x76, 0x08,0x14,0x94,0x19, 0xcf,0xe2,0xa8,0x2e, 0x13,0x2d,0x0a,0x0a, + 0x81,0x9e,0xa5,0x14, 0xdc,0xb5,0x98,0x8e, 0xb2,0x13,0xef,0x66, 0x51,0xd9,0x8e,0x0c, + 0x36,0x52,0xa4,0x84, 0x63,0xb7,0x6e,0x37, 0x38,0xdb,0x52,0xb1, 0x3a,0x88,0xd5,0x25, + 0x59,0x9c,0x83,0xb9, 0x9d,0x9a,0xca,0x02, 0x9d,0x53,0x9a,0x4f, 0x79,0x2f,0xc9,0x58, + 0x64,0xee,0x61,0x0a, 0x4c,0xfb,0x68,0xbd, 0xbe,0xbc,0xb1,0xe8, 0x49,0x4e,0x9a,0x5f, + 0x96,0xa7,0x8a,0xce, 0xa3,0x90,0xf0,0x38, 0xd9,0x50,0x49,0x7a, 0x84,0xb8,0x80,0x48, + 0x4f,0x57,0xa9,0x3d, 0x9e,0xe8,0xf2,0xf8, 0x7d,0x8f,0x27,0x99, 0xfe,0x00,0x55,0x52, + 0xae,0x56,0xf7,0x7b, 0x3b,0xce,0x56,0x9e, 0x79,0x6a,0xe5,0x23, 0x15,0x7f,0x19,0xbc, + 0x24,0xf2,0x3c,0x91, 0xf8,0x97,0xd6,0x8f, 0x96,0x98,0xd1,0x28, 0x60,0x8b,0x90,0xf1, + 0xa4,0x3e,0x89,0xe4, 0x1d,0x35,0x4b,0xd5, 0x0e,0xe2,0x8a,0x43, 0xdb,0x5f,0xce,0xbd, + 0xa6,0x9d,0x5a,0xfb, 0xa7,0x82,0xd5,0x04, 0xd9,0xe2,0x66,0x5c, 0xc8,0xac,0x86,0xe8, + 0x56,0x20,0x92,0xe9, 0x0e,0x0e,0x2b,0x82, 0x86,0xbb,0x9b,0x8a, 0x09,0x7c,0xd5,0x82, + 0x94,0x4b,0xb6,0x13, 0xaa,0xa7,0x71,0x52, 0x12,0x09,0x07,0x1f, 0xf4,0x69,0xf8,0xe7, + 0x81,0x25,0xf4,0x67, 0x7c,0x4e,0x98,0x2a, 0x8b,0x88,0x4c,0x26, 0xb4,0x3d,0x3d,0x1d, + 0x77,0x58,0xe1,0x38, 0xea,0x68,0x34,0x18, 0xe1,0x72,0xdb,0x7d, 0x38,0x9b,0x2b,0xcd, + // 0x900: + 0x02,0x32,0x8a,0x1e, 0x44,0x61,0x5a,0x33, 0x24,0x11,0xd6,0xa3, 0xa8,0x7d,0x15,0xc1, + 0x55,0xf5,0x79,0xc6, 0x49,0x96,0xab,0x47, 0xb6,0x3f,0x6f,0xb9, 0x1f,0xee,0x4b,0x7d, + 0xdd,0x32,0x66,0xba, 0xaf,0x77,0x2c,0x54, 0x09,0xfd,0xc0,0xfa, 0xbc,0xb8,0x7c,0x2b, + 0x9d,0xde,0x7c,0x52, 0x86,0xba,0x55,0xcd, 0xa2,0xef,0x3c,0x96, 0x7d,0x1c,0x7a,0x3f, + 0xb6,0x39,0x85,0x23, 0x3a,0xef,0xaf,0x56, 0xad,0xcf,0x19,0xe9, 0x9b,0x1b,0x26,0xb0, + 0x45,0x23,0x66,0xe0, 0x30,0xc2,0xdd,0xb0, 0x48,0xe0,0xa9,0xa4, 0x53,0x1a,0xb0,0x12, + 0xb3,0xd7,0x73,0x4b, 0x0d,0xc2,0x10,0x5a, 0x47,0xf3,0xd8,0x1e, 0x8b,0xf0,0xdb,0x72, + 0x5b,0x84,0x67,0xd9, 0xf0,0x6c,0x57,0x0e, 0xc5,0xe5,0xdf,0x97, 0x6e,0xdc,0x70,0x3d, + 0xcb,0x6c,0x5e,0x87, 0x2c,0xb1,0xec,0xaa, 0x09,0x7c,0x2f,0xcb, 0x82,0xf0,0xdd,0x64, + 0x59,0x3e,0x96,0xeb, 0xdc,0x81,0x27,0x13, 0x85,0x4c,0xc4,0x25, 0x81,0xc0,0x66,0xba, + 0xf7,0x87,0x35,0xbd, 0x96,0x9b,0x59,0xcf, 0x22,0x7e,0xcd,0xbc, 0x56,0x48,0xdb,0x4b, + 0x70,0xc1,0xd7,0xd2, 0x0f,0xb5,0x5a,0xac, 0xfb,0x65,0xb9,0x5e, 0xb9,0xe8,0xec,0x8d, + 0xa5,0xe3,0x6b,0x2d, 0x66,0x60,0xd9,0x64, 0x66,0x70,0x93,0x26, 0x9e,0x3c,0x34,0xdb, + 0x44,0x05,0xdb,0xb3, 0x0a,0x27,0xa2,0xb9, 0x58,0x28,0x72,0xdb, 0x6b,0x58,0xaa,0xe7, + 0xcc,0xb6,0xc8,0x1e, 0xcf,0x6c,0x6c,0x20, 0xab,0x24,0x85,0x43, 0x4a,0xb0,0x20,0x40, + 0xee,0x9e,0xc5,0xdf, 0x8b,0x4f,0xf2,0xd3, 0x6f,0x19,0xac,0x6d, 0xdc,0x57,0x7c,0x4b, + // 0xa00: + 0x7c,0xd9,0x71,0xb8, 0x36,0xfc,0x2d,0xcb, 0x92,0xad,0x7b,0xa1, 0xb6,0xd8,0xe8,0x5e, + 0x5c,0x39,0x38,0xd3, 0xbb,0x9a,0x48,0xa5, 0xc9,0x3d,0xd9,0x31, 0xe3,0xdd,0x10,0xcd, + 0x9f,0x32,0xee,0xb2, 0xae,0x56,0x96,0x53, 0xcd,0xc0,0x64,0xe5, 0xe3,0x0d,0x58,0x68, + 0x80,0xc5,0x7c,0x89, 0xb0,0x6c,0x19,0xd7, 0x86,0x92,0x43,0x3c, 0x68,0x74,0xe7,0x6b, + 0xf8,0xc9,0x20,0x3a, 0x72,0xb3,0xe0,0x23, 0x82,0x8c,0xcc,0xf6, 0xd2,0x95,0xd3,0xca, + 0x5d,0x47,0xbd,0x2b, 0x36,0xd9,0xb5,0xcb, 0xaf,0x87,0x5a,0x17, 0x6f,0x47,0x44,0x86, + 0x28,0xe6,0xe1,0xba, 0xb4,0xe5,0x57,0xd9, 0xef,0x71,0x14,0x53, 0xc2,0xbb,0x75,0xe5, + 0x17,0xd9,0x27,0x5e, 0xed,0xe2,0x9d,0x09, 0xa3,0x0d,0x91,0x54, 0x36,0x64,0xbb,0x12, + 0xb8,0x71,0xc7,0x7e, 0xec,0xd1,0x4d,0x93, 0x4c,0x5d,0x7c,0x64, 0x53,0x93,0x30,0x58, + 0xec,0x91,0xc9,0xbe, 0x20,0xdb,0xe8,0xc9, 0x87,0x08,0xe2,0x99, 0x55,0xc0,0x64,0xda, + 0xe0,0xec,0xdb,0x31, 0x56,0x69,0xbe,0x5d, 0xbb,0xa5,0x9e,0x60, 0xaf,0x18,0x93,0x90, + 0x31,0x49,0xb9,0x5e, 0x8d,0x09,0xab,0xc2, 0x30,0x19,0x03,0x14, 0x51,0x8b,0x28,0x57, + 0x5f,0xc3,0x8e,0xe7, 0xc4,0x04,0x0d,0x32, 0xbb,0x73,0xa4,0xba, 0xa1,0x65,0x22,0x94, + 0xad,0x31,0x3c,0x4a, 0x1e,0xcb,0xac,0xb7, 0x48,0x73,0x47,0xa3, 0x19,0x7b,0xc6,0x16, + 0xba,0xdc,0xba,0x91, 0x25,0x7d,0x5a,0x16, 0x85,0x26,0xd3,0x39, 0x6c,0x0e,0xfe,0x88, + 0x30,0xa3,0xd0,0xba, 0xb8,0x6e,0xb5,0xbe, 0x9e,0x5f,0x1d,0xee, 0x73,0xcd,0xf5,0xe3, + // 0xb00: + 0xbb,0x39,0xc5,0xdf, 0x6f,0x1d,0x88,0x2e, 0x5a,0xd1,0xc0,0x52, 0xa6,0xa2,0x03,0xa1, + 0xaf,0x48,0xdf,0x57, 0xda,0xb1,0xdd,0xe6, 0xeb,0x9b,0xed,0x8e, 0xa2,0xa6,0x45,0xab, + 0xd3,0x6d,0xa7,0x08, 0x21,0x46,0xd4,0x9a, 0xc4,0x2c,0x89,0x9e, 0x27,0xb8,0xb1,0xc8, + 0xe5,0x5a,0xac,0x17, 0x53,0x6c,0xed,0x6c, 0xa3,0x4e,0xa3,0x7b, 0xfa,0x77,0x46,0x3d, + 0x3e,0x4e,0xb6,0xcd, 0xf4,0xdb,0x93,0x4c, 0xf3,0x49,0x9c,0x1f, 0x6a,0xe5,0xc6,0xf6, + 0x63,0x13,0xd1,0x10, 0x4d,0x59,0x8c,0x4a, 0xab,0xe6,0xb0,0xc3, 0xaf,0x51,0x93,0x96, + 0x99,0x59,0x73,0x46, 0x46,0x9a,0x78,0x7d, 0x99,0x3b,0x28,0x1e, 0xc4,0xde,0x21,0x37, + 0xb5,0xc9,0xcc,0xf6, 0xbd,0x32,0x38,0x99, 0x1d,0x1c,0x8e,0x3b, 0x13,0xad,0xdc,0x95, + 0x2c,0x40,0x27,0x45, 0xa9,0x76,0xf9,0x6d, 0x93,0x09,0xf3,0xd3, 0x15,0x0e,0x88,0x2e, + 0x30,0xa2,0x80,0xd4, 0xb0,0x56,0xb0,0x01, 0x1e,0x46,0x62,0xfd, 0x9c,0x7b,0x83,0xb2, + 0x4d,0xa2,0x34,0x9c, 0x46,0x61,0xd6,0x96, 0x78,0xe6,0xec,0xfd, 0x09,0x4a,0x20,0x53, + 0x6c,0x5a,0x6b,0x5c, 0x2d,0x24,0xb9,0x8b, 0x5e,0xed,0x6e,0x13, 0xa5,0xc5,0x82,0x1b, + 0x41,0xc7,0xaa,0x7d, 0x39,0x15,0x0d,0xaa, 0x30,0x98,0xcc,0xde, 0x32,0xbd,0xab,0x3e, + 0xd5,0x63,0x17,0x77, 0xdb,0x35,0x7a,0x5a, 0xa7,0xbb,0xb4,0xe5, 0xb0,0x43,0x52,0x9b, + 0xcd,0x2a,0xe7,0xa7, 0x8a,0x4c,0x35,0xc5, 0x68,0xcd,0x81,0x21, 0x0f,0x71,0xd4,0x62, + 0x21,0xe6,0x62,0x99, 0xca,0xcf,0xa9,0x80, 0x9a,0x8b,0xec,0x5e, 0x0e,0x9a,0x06,0x08, + // 0xc00: + 0x8c,0x42,0xa5,0x8b, 0x25,0x77,0x3d,0xa4, 0xdb,0xc9,0xb9,0x01, 0x35,0xec,0x6f,0x9a, + 0x69,0xd7,0x42,0x0b, 0xcd,0x11,0x18,0x27, 0xbe,0x1c,0xea,0xae, 0xbe,0xa0,0xdf,0x8c, + 0xb1,0xbf,0x47,0xdb, 0x77,0x07,0xb8,0xed, 0xa1,0xa6,0x6a,0xd6, 0xb3,0xa2,0xdb,0x8f, + 0xce,0x25,0x63,0x61, 0xee,0x22,0xc8,0x99, 0x78,0xc3,0xda,0x4a, 0x80,0x97,0x6a,0xa8, + 0x9d,0xc1,0xd7,0x2a, 0x0e,0x32,0xe2,0xfb, 0x4d,0x86,0xed,0x15, 0xbd,0x2a,0x3e,0x33, + 0xe6,0xf2,0x1d,0x64, 0x47,0x6b,0xc8,0xf8, 0x72,0x99,0x95,0xfe, 0x7f,0xc1,0x06,0xc4, + 0xbb,0x66,0xc0,0xce, 0x81,0x9d,0x86,0x52, 0xf2,0xf1,0x24,0xa2, 0x19,0xdd,0x45,0x4e, + 0x86,0xff,0x4b,0xf1, 0x76,0xc9,0x60,0x46, 0x51,0xc7,0xf2,0xf3, 0x10,0x29,0x29,0x1c, + 0x22,0x5e,0x0c,0x36, 0x1d,0xfe,0x1d,0xf7, 0x4b,0x39,0x13,0x43, 0xe6,0x6d,0x5f,0xb1, + 0x37,0xac,0xb6,0x9c, 0xe2,0xf7,0x0c,0x5e, 0xef,0xa1,0x5a,0x7a, 0xb5,0x80,0x90,0x83, + 0x79,0x1b,0x1b,0xcf, 0x57,0x98,0xcc,0x11, 0xbb,0xcb,0x90,0x35, 0xc7,0x92,0x07,0x34, + 0x45,0x8f,0x93,0xae, 0xbe,0x9f,0x40,0x06, 0x6b,0xfb,0x0e,0x33, 0x0c,0x13,0x80,0xec, + 0xbf,0xee,0x7c,0x71, 0xcb,0xf9,0xb2,0xde, 0x3c,0xd6,0x7b,0xcf, 0x92,0x5e,0x7f,0x58, + 0x8f,0x0e,0xbc,0x70, 0x36,0x31,0x80,0x42, 0xfd,0x0f,0x26,0x42, 0x44,0x5b,0xbb,0xb2, + 0x19,0x92,0x63,0xf6, 0x42,0x6d,0x1e,0x45, 0x14,0xe3,0xd3,0x6a, 0x73,0x97,0xd0,0x7d, + 0x8c,0xd8,0xf2,0x9f, 0x43,0x25,0xe3,0xf2, 0x45,0x98,0xd9,0x92, 0x78,0xe0,0xfe,0x11, + // 0xd00: + 0xff,0x52,0x25,0x28, 0x4f,0xe2,0xc0,0xc9, 0x1a,0x3c,0xc8,0x82, 0x95,0x2a,0x82,0x81, + 0x49,0x9f,0xa4,0x01, 0x27,0x83,0x95,0x72, 0x8c,0x00,0x91,0xf8, 0x00,0x24,0xfe,0xbb, + 0xfd,0x6f,0xdc,0xf2, 0xae,0xc3,0x43,0xd1, 0x9e,0xbc,0x73,0x32, 0x31,0xf0,0x9e,0x9b, + 0xaa,0xcf,0x1c,0xc8, 0xda,0xef,0xa3,0x4a, 0xd1,0xe0,0xe0,0x33, 0xee,0x39,0xfb,0xa7, + 0x77,0xaa,0xf0,0x32, 0x3d,0xbf,0xbd,0x81, 0xcd,0x6a,0xd8,0x34, 0x71,0x4f,0x67,0x3e, + 0xe3,0xfd,0x15,0xc0, 0x55,0xc7,0xec,0x28, 0x16,0x1c,0xad,0xf6, 0x7c,0x35,0xfe,0x8f, + 0xe9,0xe8,0xfd,0x47, 0x1a,0x20,0x4e,0xe9, 0x09,0x61,0xc0,0x0f, 0x7d,0x16,0x93,0x60, + 0xac,0x1f,0x7e,0x28, 0xb2,0x80,0x98,0xf8, 0xec,0xd3,0xe6,0xd4, 0x20,0x2e,0xba,0x77, + 0x96,0xeb,0x93,0x7e, 0x66,0x49,0xa0,0x80, 0xa0,0x25,0x66,0xe5, 0x60,0x05,0xb5,0xef, + 0x7c,0x5a,0x62,0x69, 0xbe,0xa9,0x91,0x9c, 0x34,0xe1,0xb7,0xab, 0x29,0x9b,0x15,0xdd, + 0x54,0x67,0x65,0x36, 0x91,0x9d,0xaa,0xea, 0xd6,0x70,0xd1,0x43, 0x23,0x02,0xdd,0x34, + 0x09,0x91,0xaa,0x78, 0x13,0xa6,0xbe,0x7e, 0x43,0x3f,0x54,0xb0, 0x4d,0xd4,0x54,0x75, + 0x42,0x36,0x8c,0xdb, 0x2a,0xb4,0xfb,0x19, 0xd1,0x04,0xe6,0x38, 0xb4,0x53,0xa5,0xd8, + 0x07,0xb3,0xa1,0xd0, 0x46,0xc2,0x32,0x18, 0xc2,0x5e,0x50,0x41, 0x91,0x14,0x1b,0xeb, + 0x2a,0xc1,0xb2,0x96, 0xa8,0x34,0x19,0xe5, 0x29,0xa4,0x47,0x75, 0x93,0xaa,0x0b,0xf2, + 0x01,0x89,0x90,0x54, 0xaa,0x41,0x41,0xc4, 0x85,0x43,0x49,0xe2, 0x43,0x6e,0xbd,0xa6, + // 0xe00: + 0x32,0xdb,0x55,0x76, 0xe0,0x15,0x1d,0x63, 0xbf,0x7f,0xa3,0x78, 0x1c,0x95,0x00,0x48, + 0x31,0xb0,0x42,0x93, 0xb8,0x88,0xed,0x47, 0x1e,0x0e,0xc1,0x4d, 0x51,0x78,0x95,0xe1, + 0x76,0xee,0xc3,0x63, 0xe5,0xbb,0xa3,0x38, 0x76,0xe9,0xeb,0x99, 0xbb,0x30,0x65,0x37, + 0x77,0x83,0xce,0x3d, 0xb8,0x93,0xbd,0x3e, 0xed,0xdc,0xdb,0xf1, 0x0f,0x0e,0xaa,0xdd, + 0x3d,0x92,0x82,0x14, 0xeb,0xa5,0x19,0x42, 0x94,0xc5,0xc8,0x0a, 0x60,0x71,0x98,0x7d, + 0xf1,0xd2,0x42,0xc2, 0x9d,0xa2,0x05,0x77, 0x0a,0xc9,0x2e,0x8a, 0x99,0x47,0x99,0x31, + 0xed,0x6b,0xac,0x02, 0x98,0x8a,0xb8,0x38, 0x71,0x3c,0xda,0x96, 0xa6,0xfb,0x37,0xbc, + 0x1c,0xb8,0x45,0xa5, 0x21,0x21,0x9c,0x5e, 0xde,0x22,0x32,0xa1, 0x14,0x53,0x92,0x19, + 0x93,0xac,0x5e,0xc1, 0x04,0xb8,0x38,0x0e, 0x4c,0xca,0x03,0xdd, 0x70,0xc4,0x70,0x75, + 0x1d,0xf8,0x40,0xd7, 0x15,0x32,0x36,0x12, 0x1a,0x15,0x10,0x2c, 0x38,0xc7,0x53,0xb3, + 0x0f,0xd2,0xf0,0xdc, 0xe8,0xb3,0xc0,0xb8, 0x7b,0x31,0x68,0x7e, 0xa4,0x4e,0xe6,0x9c, + 0x04,0xfb,0x0c,0x0d, 0xbf,0x86,0x0e,0xc2, 0x95,0xb0,0xef,0xe5, 0x53,0x55,0x99,0x33, + 0xa0,0x66,0xc3,0x71, 0x60,0x85,0x99,0xbb, 0xb6,0xae,0x8a,0xd9, 0xd6,0xbc,0xac,0x56, + 0x14,0x61,0x10,0x21, 0xf7,0xcd,0x97,0x58, 0x61,0x21,0x96,0xbc, 0x64,0x84,0xcb,0x2a, + 0xc8,0xa1,0x6e,0xc9, 0x76,0x95,0x10,0x55, 0x81,0x1a,0x91,0x31, 0x9b,0xe7,0xc7,0x9b, + 0x19,0xc7,0x85,0xf0, 0xc6,0xa8,0xa0,0xb4, 0x55,0x75,0x4e,0xf4, 0xc6,0x84,0x26,0x1e, + // 0xf00: + 0x5b,0x2e,0x65,0x6b, 0x4d,0x14,0xcc,0x12, 0xda,0xbb,0x8e,0xae, 0xa4,0xc5,0xb4,0x24, + 0x7a,0x7b,0x97,0x72, 0x61,0xd0,0xd2,0x81, 0xd8,0x75,0x79,0x3f, 0xa1,0x47,0xd1,0x96, + 0x72,0x1a,0x93,0x58, 0x99,0x1e,0x0b,0xc5, 0x54,0xb7,0xb0,0x80, 0x80,0x97,0x0d,0x6a, + 0x60,0x94,0x2f,0x29, 0x24,0x31,0x3b,0x0f, 0x58,0x4e,0x09,0x3a, 0x94,0x53,0x3b,0x92, + 0xee,0xc3,0xfb,0x83, 0x30,0x86,0x02,0x70, 0x21,0x68,0x6a,0x60, 0x1a,0x7c,0x48,0x30, + 0x26,0x9a,0x49,0x21, 0x4a,0x60,0x92,0x24, 0x06,0x24,0x88,0x60, 0x84,0x98,0x92,0x4a, + 0x01,0xe3,0x32,0x98, 0x83,0x70,0x28,0x87, 0x03,0x19,0x76,0xf8, 0x4e,0x04,0x87,0x57, + 0x0e,0x24,0x37,0x51, 0xc7,0x9b,0xa3,0x04, 0x06,0x3a,0x8c,0xab, 0x59,0x62,0xa5,0x04, + 0xe1,0x38,0x8c,0x10, 0xdb,0xdb,0x5c,0x0c, 0x1a,0xca,0x9e,0x1d, 0xa3,0x88,0x03,0xa9, + 0x37,0x5c,0xbb,0x1f, 0x46,0x53,0x38,0xda, 0xef,0x62,0x89,0x66, 0xd9,0x2f,0x77,0xb4, + 0x8c,0xa6,0xf5,0xa0, 0xf8,0x36,0xfe,0xa3, 0x32,0x8e,0x0e,0xcc, 0xfe,0xbe,0x27,0xd0, + 0x3d,0x4f,0xeb,0xb3, 0x67,0x87,0x18,0xad, 0x5c,0xf9,0xed,0x5b, 0xc3,0x35,0x23,0xa2, + 0x94,0x1e,0x42,0x9d, 0x83,0xce,0x83,0x9a, 0x34,0xa4,0xd1,0x8c, 0x13,0xa8,0x46,0x9c, + 0x8e,0x78,0x1a,0x77, 0xb3,0x82,0xa9,0x90, 0xdb,0xc8,0x60,0x88, 0x7b,0x79,0xd5,0x07, + 0x97,0xb6,0x25,0x6e, 0xe1,0x80,0x6a,0x16, 0xa6,0xa5,0x42,0x25, 0x90,0x90,0x98,0x51, + 0x85,0x82,0xa0,0x4a, 0x94,0xe5,0x83,0x41, 0xdc,0xc4,0xd8,0xe1, 0x98,0xd1,0x3e,0xc2, + // 0x1000: + 0xc2,0x69,0x08,0x87, 0x75,0x8a,0x4a,0x1b, 0xe5,0x29,0x0f,0x35, 0xa3,0x12,0x01,0x04, + 0x88,0xc4,0x25,0x30, 0x96,0xc0,0xb1,0xbb, 0xf2,0xd8,0x5a,0x01, 0xae,0x3c,0x26,0x02, + 0xbc,0xb5,0xde,0xe6, 0x03,0xd9,0x52,0x67, 0x6d,0xe6,0xe0,0x39, 0x36,0x6c,0xae,0xb4, + 0x90,0xd1,0x0f,0xb3, 0x4e,0xdb,0xa0,0xa9, 0x5c,0x8e,0x38,0xa6, 0x50,0xfc,0xb6,0x63, + 0x21,0xca,0xe0,0x64, 0xed,0x7c,0xc9,0xe7, 0xf9,0x7b,0x28,0xf3, 0xe0,0xe8,0x4d,0xac, + 0x14,0xba,0xd2,0xc8, 0x75,0x44,0x40,0x54, 0x32,0x1c,0x91,0xb4, 0x3a,0x64,0xa0,0xf3, + 0xd0,0xe4,0x06,0xa1, 0xc2,0x38,0xdc,0xba, 0xc5,0x59,0x12,0x1e, 0xd4,0x2b,0xa9,0x1d, + 0x2a,0x01,0xe1,0x70, 0x3c,0xdf,0x7b,0xbb, 0xe9,0xfa,0x7d,0x15, 0xec,0xb3,0x97,0xaa, + 0x70,0xbe,0xef,0x51, 0xfd,0x16,0x94,0x94, 0x4d,0x75,0xf9,0x2a, 0x09,0x87,0xd7,0x11, + 0xc3,0xde,0x7f,0x9b, 0x8c,0x9e,0x85,0xab, 0x7c,0xa7,0xd0,0xa0, 0x85,0xe5,0x9e,0x5b, + 0x3d,0x0f,0x8e,0x2e, 0xd0,0x95,0xd5,0x94, 0xf4,0xca,0x74,0x80, 0x47,0xec,0x23,0x19, + 0x8c,0x53,0xf1,0x1b, 0x91,0xda,0x1a,0x3a, 0xc5,0x2c,0x3c,0x9a, 0x91,0x02,0x2b,0x51, + 0x0c,0x21,0x0c,0xda, 0xc6,0x79,0xb9,0xd7, 0x40,0x00,0x67,0x0e, 0x30,0xde,0x0d,0x6d, + 0x0e,0xf3,0xe7,0x91, 0x8d,0x23,0xdc,0x5d, 0x6b,0x87,0x0c,0x42, 0x5c,0x53,0x5f,0x6e, + 0xfa,0x09,0xf0,0xb2, 0x7e,0x48,0x70,0x7d, 0xdf,0x3e,0x27,0xd5, 0x59,0x68,0xfe,0x5b, + 0x13,0xd9,0x55,0x1c, 0x20,0x71,0x42,0xd9, 0xed,0xf1,0x5c,0x34, 0x71,0x35,0x33,0x3a, + // 0x1100: + 0x2e,0x79,0x38,0x5f, 0x0c,0x69,0x1c,0xf4, 0xbb,0x3a,0xf9,0xd7, 0x5c,0x3b,0x59,0x24, + 0xad,0x2f,0x3f,0x69, 0x3d,0x28,0x40,0x90, 0xb2,0x1c,0x2c,0x42, 0x48,0x68,0x22,0x96, + 0x83,0x52,0x75,0xf7, 0xb6,0xe7,0xd1,0x9b, 0x45,0x86,0x26,0x45, 0x3d,0x79,0x81,0x6a, + 0xc4,0x22,0x70,0xd6, 0x2e,0x95,0x95,0x41, 0x4c,0x16,0x7c,0x14, 0x10,0xab,0x19,0x0c, + 0x26,0xdb,0xaf,0x58, 0x84,0xd6,0xf0,0xed, 0x70,0x1e,0xfd,0x4f, 0x82,0x3d,0x71,0xee, + 0xf0,0xb5,0x65,0x31, 0x4b,0x9e,0xd0,0x40, 0xdd,0x13,0x52,0xb7, 0x1e,0x50,0xc4,0xa1, + 0x71,0xbe,0x38,0xcb, 0xee,0x8e,0xad,0x1c, 0x30,0xc1,0xe1,0xec, 0x70,0xed,0x30,0xd5, + 0x0c,0x7b,0x1a,0x46, 0xb3,0xd6,0x9f,0x4c, 0x43,0x52,0x04,0x2e, 0x9b,0xf7,0xc1,0x7a, + 0xf7,0x5b,0xdc,0x96, 0x27,0x31,0x50,0xae, 0x11,0xf6,0xf0,0x64, 0x96,0x0a,0xa3,0xb6, + 0xaa,0x9d,0x1c,0x58, 0x47,0x6e,0x26,0x6a, 0x15,0x2b,0x05,0x06, 0x82,0x49,0x42,0x10, + 0x10,0x04,0x84,0x30, 0x4d,0x30,0x9c,0xd6, 0x10,0x34,0xc4,0x94, 0x44,0x50,0x25,0x45, + 0x44,0xd2,0x45,0x13, 0x52,0x11,0x44,0x81, 0xd3,0x06,0x24,0x11, 0x34,0x4c,0xcc,0xde, + 0x17,0x6f,0x4b,0x6f, 0xe5,0xd6,0xea,0x90, 0xa6,0x93,0x49,0x26, 0x12,0x07,0x54,0x76, + 0xe7,0xb1,0x78,0x05, 0xed,0x48,0xcb,0x87, 0x20,0x62,0x4a,0x48, 0xc0,0x51,0x2c,0xd2, + 0x73,0xc6,0x70,0xaa, 0x00,0xde,0x98,0xac, 0x5e,0xe3,0x2a,0x05, 0x14,0x60,0xa4,0x14, + 0x54,0x48,0x89,0x10, 0x41,0x62,0x4d,0x19, 0x0e,0x0c,0x03,0x45, 0x6c,0x49,0xda,0x32, + // 0x1200: + 0x63,0x9e,0xcb,0xef, 0x42,0x93,0x0c,0x39, 0x26,0xd1,0x60,0xe6, 0x3d,0x21,0x8f,0x7f, + 0x1d,0xd0,0x69,0xd5, 0x8a,0x3a,0xaa,0x51, 0x85,0xa2,0x67,0x41, 0xc9,0x31,0x06,0xee, + 0xd0,0x1b,0x28,0xcb, 0xc9,0x76,0x23,0x9e, 0xcc,0xde,0x5b,0xf2, 0x26,0xd6,0x16,0xcf, + 0x2a,0x6d,0x4d,0x53, 0x44,0xb4,0xe2,0xf1, 0x18,0x5c,0xd2,0x80, 0x78,0x15,0x21,0x77, + 0x53,0xb1,0x38,0x46, 0x1c,0x19,0x41,0xe2, 0xac,0xb1,0x53,0xd3, 0xe3,0xa8,0x58,0xe8, + 0xc0,0xd5,0x37,0x62, 0x6b,0xaa,0x32,0xce, 0xdd,0x76,0xa8,0x21, 0x75,0x58,0x73,0x6b, + 0x8e,0x18,0xec,0xe7, 0x0e,0x8b,0x28,0x43, 0x9d,0xb0,0x0a,0xa1, 0x5a,0x3f,0x2b,0x19, + 0xed,0x84,0x11,0xac, 0xed,0x16,0xcd,0xc9, 0xc4,0x74,0xf2,0xba, 0x49,0x22,0x9b,0x6a, + 0x84,0x34,0x3b,0x26, 0x57,0x50,0xd0,0xc1, 0x9c,0x17,0x46,0x2c, 0xc7,0x74,0x0c,0xed, + 0x03,0xf8,0xb7,0xb3, 0xcc,0xb3,0x3f,0x8d, 0x45,0x4b,0x0f,0xf6, 0x9f,0x66,0xab,0xc3, + 0xdd,0xec,0x44,0xc6, 0xc5,0xb4,0x73,0x44, 0x21,0x6c,0xe6,0x2c, 0xe1,0xd3,0xcf,0xdc, + 0x55,0xec,0xb7,0xbb, 0x3c,0x02,0x97,0x7b, 0x20,0x90,0x41,0x8c, 0xb6,0x9c,0x32,0xa7, + 0xae,0xa6,0x0c,0x87, 0x1b,0xaa,0x8c,0x16, 0x5d,0x8c,0xcc,0x5b, 0x6d,0xae,0x02,0x34, + 0x29,0x8e,0xfc,0x63, 0xd2,0x57,0x52,0x38, 0xb4,0x60,0x5d,0x5e, 0x82,0xc2,0xa8,0xd6, + 0x51,0x84,0xb6,0xcc, 0x7a,0x11,0x24,0x09, 0xdb,0xed,0xfd,0xbd, 0xf1,0x95,0x49,0x2d, + 0xb6,0xf7,0xbc,0xb2, 0x15,0x7d,0x3a,0xa0, 0xe8,0xf4,0xcc,0xc7, 0x09,0xa5,0xb6,0xc3, + // 0x1300: + 0x89,0x67,0xbe,0x6e, 0x0b,0x9f,0xbd,0x05, 0xbd,0xfc,0xb0,0xdf, 0xe0,0x9a,0x50,0x37, + 0x6d,0xc8,0x66,0x6e, 0xc2,0x7b,0xdf,0x1c, 0x19,0xa9,0xd0,0x53, 0xa3,0x98,0x38,0xf2, + 0x3c,0xa6,0x42,0x62, 0xaf,0x00,0xf1,0x54, 0x9d,0xc4,0x76,0xb7, 0x9c,0x78,0x41,0x83, + 0x99,0x17,0x92,0xe9, 0xb6,0x47,0xce,0xe6, 0xe6,0xad,0x9d,0x07, 0x69,0x12,0x3d,0xa7, + 0x2d,0xd2,0x62,0xec, 0x99,0x0f,0x3b,0xb6, 0x0d,0x55,0x07,0xaf, 0xcb,0xd9,0x38,0xf9, + 0xcf,0x63,0x83,0xf8, 0x00,0x5b,0x69,0x43, 0xa4,0xed,0xcc,0x4e, 0xee,0xe2,0x1e,0x7e, + 0xfd,0x4b,0x5d,0x19, 0x5c,0x49,0x86,0x8c, 0x33,0x7c,0x16,0xa6, 0xca,0xd4,0x73,0x91, + 0x9f,0x5f,0x8a,0x67, 0xa3,0x9f,0x74,0x6a, 0xe8,0x8c,0xb0,0x20, 0x51,0xb4,0x51,0x0c, + 0x2d,0x4a,0xec,0x92, 0x24,0x47,0x10,0x15, 0xac,0x71,0x71,0x9d, 0x40,0x9f,0xac,0xf5, + 0x26,0x23,0x14,0x61, 0xde,0x5f,0xef,0xbd, 0xc8,0x0b,0x68,0x8a, 0x62,0xdb,0xb5,0x0b, + 0x4f,0x1b,0xb9,0x96, 0xd4,0xb7,0x51,0xc9, 0x00,0xca,0xfd,0x1c, 0xa9,0xc2,0x6f,0xc1, + 0x43,0x92,0x87,0x8e, 0x9f,0xef,0x28,0xc3, 0x88,0x0a,0x62,0x56, 0x9c,0xb0,0x85,0x63, + 0x31,0x0a,0xc2,0xa4, 0x4e,0x1c,0x5e,0xcc, 0x9e,0x1f,0xad,0x3a, 0x9d,0x1d,0x51,0x3d, + 0x05,0x63,0xd5,0x52, 0x18,0xe8,0x5a,0x3e, 0x73,0x57,0x39,0x97, 0x4b,0x5e,0xce,0xdb, + 0xdf,0x0c,0xe8,0x76, 0xb9,0x6b,0x32,0xcf, 0x3e,0x0a,0xe7,0xe7, 0xfe,0x30,0xd4,0xfd, + 0x42,0x81,0x48,0x2c, 0xfd,0x72,0x3c,0x48, 0x7a,0x81,0xeb,0xd2, 0xc5,0xa0,0x31,0x53, + // 0x1400: + 0x13,0x07,0x09,0xae, 0x9e,0x79,0xd6,0x67, 0xb6,0x32,0xb2,0x81, 0x77,0x51,0x51,0xd0, + 0x37,0x57,0xe0,0xa4, 0x5b,0x24,0xea,0x43, 0xba,0x9f,0xeb,0x45, 0x92,0x7a,0x26,0x0a, + 0x79,0x90,0xb3,0x0b, 0x2a,0xf5,0x9f,0xa2, 0xb5,0xf6,0x2f,0xa3, 0x5f,0x5e,0xbf,0x36, + 0x3c,0xea,0x73,0x7a, 0xec,0xe6,0x3d,0xb9, 0x96,0x22,0x7c,0x6e, 0x71,0x33,0xce,0xa0, + 0xdd,0x07,0xde,0x5e, 0x39,0x9a,0x00,0x9b, 0x34,0x07,0x8a,0xa7, 0x0e,0xac,0x4f,0x87, + 0xb5,0x67,0xa8,0xce, 0xc9,0x8b,0x9d,0x54, 0xd6,0xa3,0x53,0x3d, 0x6a,0x67,0x08,0x18, + 0xb5,0x93,0x00,0xb7, 0x12,0xf5,0x0d,0xeb, 0x77,0x4c,0xf8,0x0f, 0x16,0xde,0x58,0xc4, + 0x60,0x99,0x26,0x2a, 0xd3,0xb5,0x2b,0x69, 0x47,0x91,0x38,0x50, 0x22,0x2c,0x6a,0x58, + 0x28,0x65,0x52,0xa2, 0x12,0x81,0x23,0xfa, 0xc2,0x28,0x3e,0x31, 0xdd,0xeb,0x3d,0x04, + 0xa4,0x6b,0xe3,0xac, 0xfa,0xf5,0x94,0xce, 0x19,0x27,0xc2,0x79, 0x05,0x07,0x86,0x5f, + 0x4e,0x67,0x17,0x1e, 0x1e,0x60,0x88,0x00, 0x01,0x44,0x6f,0x45, 0x17,0x02,0x1f,0x74, + 0xed,0xdc,0x35,0x6b, 0x5d,0xc7,0xbe,0x53, 0x7f,0xa5,0x3a,0x3f, 0xa1,0xe1,0x7b,0x28, + 0x89,0xe1,0x64,0xd0, 0xae,0x2e,0x87,0x51, 0x64,0xcb,0x5d,0x37, 0x6c,0xf0,0xc2,0x24, + 0x9b,0x99,0x98,0x9d, 0x70,0x3e,0x3a,0xab, 0xa0,0xae,0xc4,0x82, 0x50,0x05,0x02,0x50, + 0x0c,0x49,0x40,0xd0, 0x34,0x94,0x85,0x05, 0x01,0x35,0x01,0x40, 0x14,0xa5,0x0d,0x0d, + 0x23,0x9b,0x36,0x99, 0xd7,0xe7,0xde,0x65, 0x32,0x61,0x0c,0x6a, 0xe5,0x6b,0x1c,0xa6, + // 0x1500: + 0xde,0x65,0x0c,0x81, 0xf8,0x3d,0x52,0xe7, 0xf0,0x65,0xeb,0x77, 0x36,0x89,0x13,0xa7, + 0x9e,0x39,0x5e,0x3d, 0x7c,0x59,0xe7,0xdf, 0xbf,0x65,0x48,0x28, 0x47,0x1a,0xde,0xbe, + 0x37,0x85,0x42,0x09, 0x4b,0x87,0x60,0xa2, 0xca,0xa1,0x53,0x8e, 0x65,0x0f,0x70,0x93, + 0xa4,0x01,0x19,0x3c, 0xc7,0xb6,0x54,0xa0, 0xaf,0xbd,0x24,0x2e, 0xbb,0x61,0xb8,0xfa, + 0xf9,0xf5,0x46,0xf5, 0x83,0xc5,0x88,0x8d, 0x8b,0xa1,0x30,0x89, 0xe6,0x39,0x35,0x11, + 0x0c,0xc9,0x0e,0xfe, 0xec,0x33,0x55,0xe5, 0x9a,0xa5,0xec,0x64, 0xee,0x22,0x45,0x8a, + 0x7e,0x16,0xcb,0xb1, 0x6f,0x17,0x1b,0xfe, 0x15,0x9f,0x9f,0x06, 0xd3,0xee,0x09,0x2e, + 0x9a,0xdb,0xfb,0xf1, 0x0a,0xb8,0x6a,0xd4, 0xbd,0xe7,0xbd,0x88, 0x05,0x12,0xb5,0x22, + 0x40,0xc1,0x27,0x00, 0x88,0xd7,0x38,0x6e, 0xce,0x12,0x65,0x4e, 0x8c,0x20,0x8e,0xde, + 0x17,0x18,0xc9,0x6c, 0xc9,0x51,0x42,0x9b, 0xa6,0x10,0x29,0x35, 0x32,0xcd,0xaa,0xc5, + 0x91,0xb8,0xb2,0x0e, 0xe1,0x2c,0x16,0xfb, 0xc5,0xf5,0x3c,0x5d, 0x10,0x5d,0x47,0x56, + 0xd4,0xd3,0x76,0xe8, 0x0d,0xb0,0x38,0xbb, 0x36,0x15,0x0e,0x35, 0xf1,0x72,0xc4,0xb1, + 0xa6,0x3b,0xea,0x50, 0x9f,0x83,0xef,0xeb, 0xc0,0xc3,0xa7,0x9d, 0x64,0xad,0xa4,0x68, + 0xec,0xb2,0x99,0x38, 0x7b,0x4f,0x21,0x8e, 0x5e,0x09,0xbd,0x34, 0x6b,0xdf,0x50,0x20, + 0xd5,0x42,0x99,0xb0, 0x30,0x6a,0x94,0x50, 0x59,0x2b,0xd1,0x52, 0xd6,0x0d,0x9e,0x9a, + 0x3a,0x93,0x09,0x1e, 0xbd,0x3b,0x35,0x0d, 0xb5,0x95,0xe3,0x4a, 0xad,0x4d,0x82,0xa9, + // 0x1600: + 0x21,0x90,0x2a,0x4e, 0xe2,0x05,0xea,0x6a, 0x6f,0x1e,0xb8,0x1e, 0x08,0xb0,0x78,0x9f, + 0x47,0x14,0xfa,0x69, 0xf6,0xe4,0x09,0xdf, 0xf4,0xaf,0x33,0x74, 0x84,0xbd,0x1c,0x0d, + 0xc0,0xca,0x10,0x99, 0x20,0x71,0x3b,0x85, 0x54,0x31,0x04,0xcf, 0x09,0x26,0x38,0xb7, + 0x28,0x1b,0x0b,0xc8, 0xc6,0x0b,0x72,0xd6, 0xdc,0xc8,0xcd,0xbb, 0x64,0x8e,0xab,0x50, + 0x5a,0xe8,0xec,0xf3, 0x17,0xd2,0xa5,0xb5, 0xb9,0xc9,0x99,0x4e, 0xc3,0xcd,0xd2,0xf1, + 0x01,0x15,0x05,0x17, 0x36,0x93,0x80,0x54, 0x3a,0xd9,0x54,0x05, 0x33,0xc8,0xaa,0x6e, + 0x63,0x64,0x13,0x55, 0x0e,0xa8,0x3a,0xe3, 0x7f,0xb4,0xc9,0xb9, 0xd3,0xb6,0x4d,0x91, + 0xd9,0x05,0x0d,0x73, 0xd4,0xdb,0x18,0x5d, 0x99,0x12,0x2a,0x18, 0xd0,0xea,0x86,0xa9, + 0xaa,0x06,0xc7,0x45, 0x35,0x55,0xb1,0xbb, 0xb7,0xc2,0x95,0xbe, 0x5d,0x64,0x38,0xf0, + 0xeb,0xf8,0x3a,0xa0, 0x33,0xc0,0xec,0xb7, 0x6b,0xbb,0x9d,0x18, 0x23,0xe2,0x00,0xf6, + 0x35,0x74,0xbb,0x3d, 0x40,0xa8,0x51,0xda, 0x6e,0xc7,0x2e,0x92, 0x40,0xef,0x1d,0xd4, + 0x5c,0x96,0xf4,0xb0, 0xba,0x57,0x89,0x04, 0x6f,0x24,0xa1,0x04, 0xc4,0xa0,0x61,0xea, + 0xf5,0x30,0x0f,0x15, 0x09,0x7a,0x7b,0x57, 0xbc,0x5a,0x46,0x49, 0xdc,0x15,0x42,0x8f, + 0x21,0xf1,0x55,0x97, 0x99,0x5c,0x61,0x23, 0x72,0xac,0xdd,0x4b, 0x23,0x85,0xe0,0x50, + 0x1c,0x24,0x20,0x91, 0xd8,0x74,0x8e,0x4a, 0x4b,0xe0,0x3a,0xfd, 0xc0,0xb5,0x2b,0x75, + 0x79,0x88,0xa6,0xbe, 0xd8,0x68,0x82,0x33, 0x27,0x23,0x58,0xf2, 0xd0,0xd4,0x89,0xb5, + // 0x1700: + 0x49,0x17,0x53,0x32, 0xdc,0xd4,0xcc,0xbf, 0x4d,0xe4,0x84,0x70, 0xf1,0xec,0xc9,0x35, + 0xba,0x13,0x37,0x6d, 0x03,0xa8,0x12,0xaa, 0x90,0x40,0x29,0xae, 0x36,0x55,0x85,0x72, + 0x44,0x91,0x14,0x25, 0x3a,0xbb,0xd8,0x04, 0xbc,0x7c,0xce,0x77, 0x33,0xd3,0xb1,0xaf, + 0x51,0xad,0x43,0x3c, 0xb2,0x2c,0x48,0x91, 0x61,0xa1,0xa5,0x4f, 0x35,0x27,0x51,0x4c, + 0x8b,0x55,0xb4,0x17, 0xc3,0xef,0x7b,0xa6, 0xe3,0xa5,0x77,0xfa, 0x7a,0x43,0x79,0xbb, + 0xc7,0x3b,0xdd,0xc5, 0x9f,0xae,0x75,0x4c, 0x34,0xbb,0x38,0x9b, 0x5e,0xe7,0x1d,0x5f, + 0x43,0x58,0x57,0x5b, 0x6c,0xd2,0x67,0x5e, 0x7b,0xad,0xc1,0x2c, 0x18,0x92,0x9b,0x85, + 0xb5,0xdf,0x69,0xbd, 0x2d,0xb4,0xca,0x0e, 0xee,0x1d,0x48,0xa7, 0x22,0xa9,0x03,0x19, + 0xf0,0x11,0x0c,0xf7, 0x35,0xa4,0x38,0xf3, 0x63,0x11,0xb2,0x22, 0x51,0xd9,0xf4,0xb6, + 0x3a,0x80,0x45,0x56, 0x28,0x0c,0x47,0xaa, 0x03,0xcb,0xee,0xaa, 0x0a,0x88,0x24,0x92, + 0x62,0x89,0xa2,0x88, 0x26,0xa6,0x8a,0x60, 0x89,0x18,0x22,0x8b, 0x15,0x8c,0x62,0xac, + 0x71,0xb9,0xc8,0x8f, 0xa3,0xb2,0xb1,0x8f, 0x6b,0x4f,0x3e,0x2f, 0xe7,0x70,0xeb,0x11, + 0x63,0xbc,0x45,0x89, 0x42,0x2f,0x51,0xed, 0x9c,0x96,0xbf,0x79, 0x40,0xe0,0x40,0x3e, + 0x35,0x0a,0x69,0x5e, 0x42,0x90,0x20,0x9e, 0x77,0x1d,0xc4,0x19, 0x0f,0x5f,0x73,0x6f, + 0x77,0xb1,0x87,0xa8, 0xb1,0xa8,0xa8,0xa0, 0xf1,0x70,0xc8,0xbb, 0x59,0x8f,0x77,0x3a, + 0x52,0xe0,0x9b,0xab, 0x03,0x57,0x41,0xef, 0x0e,0xf4,0xbe,0x85, 0xd9,0x73,0x91,0xc7, + // 0x1800: + 0xfe,0x05,0x20,0xa7, 0x08,0x01,0xe2,0x3f, 0xf0,0xeb,0xa5,0xba, 0xef,0x33,0x21,0x98, + 0x38,0x70,0xea,0x8f, 0x0f,0xb1,0x9c,0x48, 0x88,0xe3,0x19,0x37, 0xb6,0xe3,0x9d,0x43, + 0x8c,0xda,0xac,0xcb, 0xf0,0xbf,0x6d,0xab, 0x53,0xaf,0xb3,0x65, 0xcd,0xe8,0x6a,0x58, + 0x54,0xee,0x4a,0x43, 0x72,0x1e,0x64,0xb4, 0x08,0x39,0x17,0xcc, 0x58,0x9c,0xde,0x9b, + 0x49,0x2b,0xd4,0xa1, 0x2c,0x2b,0xda,0x51, 0x9b,0x45,0x20,0x2c, 0x45,0xc3,0xe4,0x1d, + 0x5d,0x97,0x32,0x6a, 0x51,0xdf,0x3d,0xe1, 0xf0,0x60,0xa2,0x94, 0x8a,0x20,0x25,0xff, + 0xd8,0x61,0x99,0x46, 0x49,0x41,0x45,0x11, 0x53,0x54,0xd4,0x15, 0x98,0xb8,0x45,0x05, + 0x25,0x31,0x1b,0xd3, 0x6e,0x36,0x76,0x6b, 0x7d,0x87,0xda,0xa5, 0xa8,0x8b,0xdd,0xeb, + 0xc7,0x71,0x83,0xc4, 0x86,0x7c,0x2b,0xef, 0x5d,0x35,0xc8,0xb2, 0x3e,0xda,0x29,0xdb, + 0x53,0x55,0xc2,0xea, 0x22,0x84,0x95,0x26, 0x9b,0x37,0x26,0x74, 0x6c,0x5e,0xb8,0x12, + 0xb8,0xd2,0x9b,0x54, 0xd6,0x28,0xb2,0x82, 0x60,0x94,0x9e,0xba, 0x95,0x18,0x6b,0x59, + 0xef,0xbb,0xc0,0xfb, 0x07,0x1f,0x0d,0x61, 0xd8,0x81,0xaa,0x1f, 0x3a,0x7b,0x7b,0x3c, + 0xe6,0x38,0xbb,0x23, 0xe6,0xed,0x2a,0x74, 0xa1,0xc9,0x9b,0x9c, 0xfd,0xad,0x03,0x86, + 0xb1,0xd2,0x54,0x2f, 0xd3,0xbf,0x8e,0x33, 0x72,0x7e,0x4e,0xdb, 0x37,0xe0,0xa7,0x89, + 0xd7,0xa6,0xa6,0x3f, 0xf3,0xe4,0xf3,0xfc, 0x38,0x9c,0x35,0xa0, 0xf2,0xcb,0x35,0x2e, + 0x4d,0xc5,0xd4,0xa0, 0x7b,0x1a,0x4c,0x63, 0x85,0x98,0x13,0xbf, 0xbe,0xa5,0x8d,0xc4, + // 0x1900: + 0x56,0x2d,0x12,0x9b, 0x0a,0x8e,0xd7,0x3c, 0xf1,0xcd,0x36,0x16, 0x55,0x1c,0x1d,0xd7, + 0xc2,0x9d,0x01,0x22, 0x14,0xa1,0xa4,0xeb, 0xbf,0x93,0xe8,0x6f, 0xc8,0x8b,0x17,0x34, + 0xee,0x4c,0xf0,0xd0, 0x29,0x52,0xaa,0xea, 0x91,0x62,0x53,0x4c, 0xde,0x6e,0xa0,0xb0, + 0x73,0x36,0xee,0x2c, 0xc0,0x2c,0x39,0x35, 0x6e,0x69,0xd4,0x86, 0x15,0x86,0xd9,0xd0, + 0xb2,0x5b,0xa2,0xb9, 0x2c,0x91,0x33,0x36, 0x87,0x1f,0x12,0xae, 0xe0,0x67,0x7a,0xcb, + 0x6c,0x56,0x45,0xf1, 0x72,0x8b,0xb4,0xc1, 0xda,0x07,0x9d,0xa2, 0x58,0xda,0x70,0xad, + 0x37,0xb4,0x0b,0xc5, 0x3e,0xa3,0x2e,0x15, 0x7b,0xc4,0x09,0x8f, 0x20,0xbe,0x0f,0x81, + 0xf4,0x3e,0x2f,0x99, 0x03,0x2d,0x9e,0x45, 0xc5,0x44,0x80,0xd7, 0x0a,0x4b,0xab,0x7f, + 0xba,0xf7,0x96,0x12, 0x05,0xc9,0xab,0x52, 0xc8,0x84,0xa9,0xb1, 0xbd,0x85,0xd7,0x0d, + 0xe0,0x1a,0xe4,0x51, 0x4a,0x4d,0x34,0x31, 0x4d,0x0f,0x42,0xc4, 0x93,0x54,0x9d,0xc8, + 0x83,0xc2,0x82,0x60, 0x28,0xaa,0x68,0x6a, 0x49,0x0a,0x49,0x88, 0x92,0x86,0x20,0x28, + 0xa4,0x92,0xa5,0x22, 0x51,0x88,0x28,0x29, 0x28,0x29,0x4a,0x18, 0xa2,0x06,0x22,0x9a, + 0xaa,0x18,0x20,0x08, 0x96,0x20,0x09,0xa2, 0x52,0x9a,0x58,0x96, 0x94,0x62,0x40,0x26, + 0x1a,0x40,0x4d,0xbd, 0x38,0x50,0x96,0x2d, 0x61,0xab,0x28,0x48, 0xce,0x0a,0xeb,0x23, + 0xa4,0x8e,0xe4,0xba, 0x83,0x24,0x47,0x49, 0xbe,0x55,0x3d,0xe5, 0xf6,0x62,0x45,0x00, + 0x82,0x09,0x04,0x92, 0x0c,0xef,0xf0,0x9a, 0x2a,0x8a,0x51,0x57, 0x16,0x63,0x4f,0x8e, + // 0x1a00: + 0x61,0xd8,0x72,0x67, 0x8d,0xcd,0x71,0x5e, 0xa5,0x51,0xbc,0xf3, 0xf4,0x66,0x5e,0x5d, + 0x17,0xf1,0xe3,0x37, 0x25,0x47,0xa5,0x94, 0xc1,0xe9,0xbb,0x2f, 0x95,0x02,0xd7,0x1d, + 0xf5,0x60,0xaa,0xbb, 0x39,0xb3,0x6c,0xe1, 0xe9,0xcc,0xab,0x15, 0x40,0xb7,0x18,0x75, + 0xa2,0xaa,0x88,0x34, 0xf4,0x30,0xbd,0x27, 0x99,0xd6,0xbc,0xc3, 0x35,0x87,0xcc,0x61, + 0x36,0xdc,0xf5,0xc6, 0x59,0xb6,0xa9,0x78, 0x3d,0x8c,0x2f,0xe0, 0x7d,0x9b,0x3c,0x59, + 0x66,0x9b,0x84,0xec, 0x56,0x07,0x07,0xb7, 0x7d,0x0a,0x4a,0xa0, 0x38,0xa0,0x66,0x99, + 0xbc,0xa6,0x47,0xbe, 0x37,0x23,0x1e,0x82, 0x9b,0xcb,0xc9,0x72, 0xac,0x8b,0xdc,0xf9, + 0x2a,0x2a,0x39,0x0a, 0xfb,0x5c,0xed,0x94, 0xe5,0x6f,0x8e,0x8c, 0xd4,0xad,0x1e,0x92, + 0xb1,0x66,0x10,0x55, 0x5e,0x08,0xb1,0xdf, 0x5c,0x89,0x6b,0x36, 0x90,0x77,0x7c,0x31, + 0x9b,0x5c,0x27,0x92, 0x1d,0x6d,0x2a,0x0a, 0x2a,0x2b,0xd9,0xef, 0x46,0xec,0xea,0xf9, + 0x55,0xc7,0xc2,0xd1, 0x3f,0x01,0x85,0x39, 0x8d,0x35,0xab,0x61, 0xff,0xc5,0xdc,0x91, + 0x4e,0x14,0x24,0x05, 0x3f,0x11,0xc4,0x00, + // 0x1ab8 = 6840 Bytes +}; + +// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 ctcode_brres_base_bz2[] = +{ + 0x00,0x50,0x2b,0x00, 0x42,0x5a,0x68,0x32, 0x31,0x41,0x59,0x26, 0x53,0x59,0x75,0x3e, + 0x0c,0x2a,0x00,0xc3, 0x65,0xff,0xff,0xff, 0xff,0x57,0xf7,0xf7, 0x71,0x6f,0xfd,0xff, + 0x9a,0x56,0xf7,0x5e, 0x5f,0xbd,0xed,0x7f, 0x4e,0x47,0xd5,0xf7, 0x53,0x7e,0xf7,0xc9, + 0x45,0x3f,0xf5,0xc0, 0x9e,0x94,0x89,0xe0, 0x1c,0x4f,0x7c,0x48, 0xd3,0x41,0xa6,0x81, + 0x52,0x05,0x2a,0x88, 0xaa,0xa5,0x5b,0x58, 0xb6,0xb5,0x5b,0x69, 0x98,0xcd,0xa2,0x83, + 0x56,0x61,0x4d,0x5b, 0x00,0xb5,0x90,0xd8, 0xd0,0x51,0x55,0x54, 0x0b,0x5a,0x92,0x00, + 0x02,0x85,0x09,0x00, 0x34,0x18,0x45,0x2a, 0xac,0x24,0x88,0x26, 0x89,0x8a,0x46,0xa7, + 0xa9,0xe5,0x1e,0xa1, 0xa3,0xd3,0x0a,0x36, 0xa6,0xd2,0x7a,0x80, 0x03,0x4f,0x50,0x01, + 0xea,0x00,0x00,0x68, 0x32,0x03,0x40,0x34, 0x19,0x3d,0x4d,0x00, 0xf5,0x00,0xda,0x83, + 0xd4,0x34,0x69,0xa0, 0x07,0x94,0x06,0x44, 0x01,0xa6,0x86,0x9a, 0x32,0x03,0x46,0x81, + 0x93,0x26,0x8d,0x32, 0x01,0x82,0x32,0x68, 0x34,0xc9,0x82,0x34, 0x31,0x18,0x26,0x9a, + 0x01,0x88,0xd0,0x1a, 0x34,0xc4,0x34,0xd3, 0x26,0x13,0x4d,0x03, 0x46,0x09,0xa3,0x08, + 0xc4,0xc9,0x88,0x03, 0x4d,0x0d,0x34,0x64, 0x06,0x8d,0x03,0x26, 0x4d,0x1a,0x64,0x03, + 0x04,0x64,0xd0,0x69, 0x93,0x04,0x68,0x62, 0x30,0x4d,0x34,0x03, 0x11,0xa0,0x34,0x69, + 0x88,0x69,0xa6,0x4c, 0x26,0x9a,0x06,0x8c, 0x13,0x46,0x11,0x89, 0x93,0x10,0x06,0x9a, + 0x1a,0x68,0xc8,0x0d, 0x1a,0x06,0x4c,0x9a, 0x34,0xc8,0x06,0x08, 0xc9,0xa0,0xd3,0x26, + // 0x100: + 0x08,0xd0,0xc4,0x60, 0x9a,0x68,0x06,0x23, 0x40,0x68,0xd3,0x10, 0xd3,0x4c,0x98,0x4d, + 0x34,0x0d,0x18,0x26, 0x8c,0x23,0x13,0x26, 0x08,0x91,0x04,0x92, 0x64,0x99,0x26,0x29, + 0xf8,0x1a,0x51,0xa1, 0xa8,0xf2,0x8c,0x8d, 0x33,0x44,0x64,0xc2, 0x64,0x64,0xd0,0x03, + 0x13,0x43,0x6a,0x7a, 0x8d,0xa8,0x68,0xc9, 0xea,0x0d,0x0d,0xa2, 0x18,0x04,0x64,0x03, + 0x47,0xa4,0xf5,0x19, 0x06,0x8d,0x3d,0x46, 0x98,0x98,0x0a,0x89, 0x10,0x84,0x40,0x9a, + 0x09,0x3d,0x3d,0x29, 0x3c,0x4d,0x26,0x9a, 0x7a,0x9e,0x90,0x7a, 0x80,0x7a,0x80,0x7a, + 0x80,0x69,0xa7,0xa2, 0x00,0x68,0x00,0x34, 0x00,0x01,0xa6,0x80, 0x1a,0x34,0x00,0x00, + 0x19,0x3d,0x46,0x80, 0xf2,0x9e,0xa6,0xcf, 0x5f,0x75,0xcf,0x6e, 0x71,0x78,0x5d,0xc7, + 0x69,0x3f,0xdf,0x49, 0x81,0x66,0x30,0x06, 0xab,0x33,0x03,0xab, 0x26,0x04,0x88,0xa7, + 0x4d,0x36,0x00,0xd6, 0xe2,0xdd,0x84,0x02, 0x24,0x66,0x02,0x3a, 0x88,0x6b,0x8c,0x81, + 0x67,0x11,0xa6,0x26, 0x98,0xdd,0x34,0xbc, 0x59,0x1b,0xd8,0xc9, 0x07,0x12,0xc3,0x81, + 0x98,0x28,0xa2,0x71, 0x35,0x33,0x26,0xd3, 0x90,0xb3,0x43,0x41, 0xad,0xcd,0x8d,0x59, + 0xc4,0xc1,0xab,0x37, 0xac,0xd0,0xe6,0x51, 0x6d,0xe7,0x6d,0x48, 0x67,0x2e,0x27,0x1b, + 0x19,0xad,0x97,0x86, 0x24,0x84,0xbc,0x4c, 0x9c,0x0d,0x72,0x6c, 0x60,0x17,0x0e,0x6f, + 0x1e,0x47,0xb3,0x7c, 0xbf,0x6d,0x38,0x4d, 0x30,0xf9,0x3e,0x77, 0x35,0xf3,0x40,0x90, + 0x69,0xa4,0x33,0x3a, 0x98,0x00,0xea,0x19, 0xc2,0x67,0x14,0xf3, 0xc7,0xc1,0x0c,0xe0, + // 0x200: + 0x0b,0x35,0x92,0xa6, 0xf6,0xd6,0x5c,0xcc, 0xc6,0x09,0x61,0x76, 0xcc,0xb6,0xdc,0x1a, + 0x1a,0x6b,0x7b,0x97, 0x90,0xe6,0xdd,0x23, 0x35,0x6e,0xe7,0x3a, 0xd6,0xc6,0xa8,0xe6, + 0x5e,0x02,0xdd,0xf0, 0x19,0xae,0x15,0x39, 0xb2,0x44,0x30,0x50, 0x94,0xa2,0x94,0x92, + 0x47,0x14,0x93,0x81, 0xcb,0x59,0x34,0x12, 0xf1,0x30,0x8f,0x1d, 0x6e,0x59,0xa9,0xb0, + 0x94,0xd4,0x2c,0xac, 0x19,0x06,0x7e,0x0d, 0x70,0x30,0x71,0x37, 0x14,0x48,0x56,0x00, + 0xc9,0x24,0x16,0x32, 0x11,0x7a,0x60,0x3d, 0x3e,0x87,0x17,0x19, 0x34,0x79,0x0f,0x21, + 0x78,0xea,0x71,0x39, 0x3c,0x1f,0x1b,0xd3, 0x27,0x70,0x7a,0xaa, 0x73,0x2c,0x76,0x2b, + 0xab,0x21,0x92,0x92, 0x32,0xa1,0x36,0x3c, 0x87,0x7d,0x38,0xa4, 0xef,0x33,0x41,0x1e, + 0x04,0x71,0x90,0x0e, 0x37,0x97,0x4e,0x40, 0xe1,0xcd,0x01,0x98, 0x01,0x9d,0x47,0x52, + 0xdc,0xc3,0x3a,0xd6, 0x9a,0xbb,0xdf,0x3a, 0x93,0x2e,0xec,0x9a, 0x96,0x48,0x6a,0xc3, + 0x70,0xf1,0xe9,0xa3, 0xbb,0x61,0xe9,0x39, 0xe4,0x68,0x6b,0xc6, 0x0f,0x02,0x92,0x47, + 0x34,0xb3,0xa1,0x9c, 0x8b,0x3a,0x53,0xbc, 0xf4,0x8c,0x86,0xee, 0xa8,0xe4,0xbe,0x52, + 0xcb,0x44,0x73,0xa4, 0x78,0x8e,0x05,0x93, 0x81,0x7c,0xef,0x30, 0xe2,0x13,0x18,0x78, + 0x14,0x78,0x1a,0xea, 0x6b,0x79,0xc3,0x4d, 0xb2,0x26,0x6b,0xcf, 0x33,0x4c,0x9d,0xf2, + 0x4c,0xd7,0x19,0x15, 0x6e,0xa9,0xe7,0xce, 0xe3,0x93,0x9f,0x2e, 0xa6,0x6e,0x5e,0x73, + 0x42,0x53,0xc8,0xb2, 0x51,0x08,0xba,0xad, 0x3a,0xb1,0xe2,0x82, 0x2a,0x10,0x0a,0xe4, + // 0x300: + 0x44,0x7a,0x55,0x65, 0x05,0x35,0x19,0xc6, 0xca,0xa6,0x95,0x02, 0xad,0xef,0x66,0xca, + 0x72,0xd6,0xaf,0x1b, 0x1b,0xd9,0xbb,0x2d, 0x66,0xb0,0xb0,0x96, 0x35,0x7c,0x52,0xd1, + 0xa0,0xa9,0xc7,0xa6, 0xe1,0xd5,0x6e,0x29, 0xec,0xed,0x92,0x73, 0x64,0xdc,0x38,0xc3, + 0x8b,0x1a,0x40,0x64, 0x73,0x40,0xa8,0xa3, 0xb0,0xd8,0x52,0xb0, 0x24,0x08,0x0a,0xea, + 0x2f,0x11,0x5d,0x53, 0x79,0x8d,0xeb,0x5a, 0xcb,0xb3,0x5e,0x78, 0x6a,0xb5,0xc6,0xb4, + 0x84,0xd1,0x4a,0x50, 0x92,0x38,0x30,0xc5, 0x15,0xcc,0xe7,0x58, 0x65,0xd2,0xcc,0x5d, + 0x59,0xc6,0xf2,0xea, 0x92,0x29,0x4a,0x52, 0x42,0x47,0x91,0x49, 0x21,0x3a,0x28,0x48, + 0x24,0x4d,0x14,0xf5, 0x14,0x48,0x91,0x18, 0x61,0xdd,0x21,0x13, 0xda,0xf9,0x07,0xe7, + 0x75,0x21,0xee,0xfc, 0x1d,0x6a,0x12,0x42, 0x78,0x2d,0xb6,0x7f, 0xc0,0x71,0xf4,0xf7, + 0x02,0x2d,0x13,0xef, 0xac,0x13,0xe3,0xfe, 0xfe,0x21,0x12,0x75, 0x4f,0x47,0xab,0x0c, + 0x29,0x24,0xf8,0xac, 0x44,0xff,0xb7,0xdd, 0xe1,0x34,0x2c,0x42, 0x39,0xf8,0x3a,0xd5, + 0xdd,0xd8,0x29,0xd7, 0x80,0x8a,0x74,0x40, 0x14,0x90,0x47,0xf7, 0x22,0xa8,0x2a,0xa8, + 0x76,0x23,0xe4,0xb1, 0x30,0x58,0x84,0x99, 0xf1,0xe0,0xf1,0x7d, 0xd9,0x61,0xc6,0xa0, + 0x6a,0x58,0x89,0x00, 0xec,0xce,0xfa,0x75, 0x21,0x44,0x15,0x47, 0x9c,0xa7,0xb5,0x68, + 0x52,0x04,0x15,0x53, 0xb6,0xd3,0x5d,0xf5, 0x05,0x54,0x55,0x0e, 0xa3,0x3b,0x8c,0x40, + 0x60,0x77,0x69,0x07, 0x31,0x08,0x11,0x45, 0xed,0x72,0xa0,0x37, 0x4d,0xe5,0x88,0xfc, + // 0x400: + 0x4d,0x21,0x3c,0x06, 0x83,0x06,0x4b,0x08, 0xe2,0x9a,0x96,0x58, 0xa8,0x5a,0x91,0x2a, + 0x28,0xb2,0xc5,0xcc, 0xc9,0x62,0xd9,0x65, 0x82,0xdb,0x29,0x14, 0x59,0x17,0xcb,0x24, + 0x30,0x54,0x96,0xd1, 0x49,0xa9,0x72,0x8a, 0xa4,0x65,0x6e,0x6b, 0x2c,0x8b,0x24,0xe5, + 0x8f,0x2d,0x48,0xcd, 0x4c,0x8c,0x2c,0x3c, 0xfa,0xd4,0x34,0x2c, 0xa4,0x96,0x50,0xa1, + 0x62,0x16,0x50,0xb0, 0xb1,0x61,0x64,0xb1, 0x64,0x9a,0xdc,0xd0, 0xd0,0xe6,0x64,0xca, + 0x52,0x52,0x58,0x2c, 0xa3,0x9a,0x26,0x05, 0xdb,0x4d,0x2a,0x80, 0x2a,0x86,0x59,0x4e, + 0x9a,0x40,0x0d,0xb0, 0x5d,0xc4,0xa9,0xa9, 0xb8,0xc3,0xa1,0x79, 0xbd,0x5e,0x85,0x44, + 0xea,0x58,0xa2,0xc3, 0xab,0x12,0x8b,0xd5, 0xe2,0x54,0x84,0xe6, 0x84,0x9b,0xeb,0x21, + 0xbd,0xb8,0x1b,0xda, 0x56,0xc6,0x4e,0x7a, 0xe7,0x54,0x71,0xb3, 0x7a,0x02,0x68,0x0c, + 0xd6,0xa4,0xc8,0x66, 0xb7,0x42,0x56,0x52, 0xb7,0x1d,0x81,0xac, 0x16,0xe2,0x9b,0x65, + 0x91,0x24,0xcb,0x27, 0x34,0xc9,0x60,0x23, 0x81,0xc8,0xd6,0xc3, 0x36,0x1d,0x4d,0xe8, + 0x5b,0x50,0x18,0xe9, 0x86,0x2c,0xd5,0xb5, 0x04,0x44,0x1f,0x13, 0xa6,0x95,0x05,0xfb, + 0xc8,0x81,0x7d,0xec, 0x1f,0x73,0x51,0xf4, 0x35,0xeb,0xd4,0x5b, 0x27,0xd4,0x2a,0x0f, + 0xa5,0x70,0xf5,0xe2, 0x44,0x8f,0x45,0x3e, 0x1a,0x13,0x55,0x23, 0xf6,0xf6,0x20,0x99, + 0x64,0x91,0xfc,0x4c, 0xf5,0x8f,0x5a,0x6e, 0x57,0x41,0x98,0xb4, 0xf6,0x7b,0x5e,0xd2, + 0xcd,0x6d,0xc4,0xb1, 0xc5,0xe6,0xc6,0xf9, 0xc9,0x81,0x64,0x16, 0x96,0xb5,0x2c,0x87, + // 0x500: + 0xb3,0x17,0xb1,0x2a, 0x22,0xf2,0x80,0x9a, 0x9f,0x1a,0x23,0x71, 0x10,0x91,0x02,0x41, + 0x10,0xec,0xb1,0x39, 0xa2,0xa1,0x71,0x5b, 0x8a,0x0f,0x9d,0x04, 0x3b,0x92,0x3d,0xca, + 0x05,0xcc,0xd4,0xf0, 0xe1,0x88,0x97,0x8a, 0x7c,0x28,0x09,0x5d, 0xaf,0xbe,0xee,0x76, + 0xeb,0xd4,0x1f,0x7e, 0x77,0x78,0x10,0x4e, 0xbc,0x11,0x0e,0xfe, 0x22,0x77,0x60,0x81, + 0xdf,0xc6,0x3d,0x52, 0xc8,0x30,0xf9,0x66, 0x24,0x1a,0x0a,0x44, 0x3d,0xe9,0x75,0x46, + 0x59,0x40,0x80,0x2d, 0xc0,0x50,0x05,0x50, 0xd3,0x04,0x94,0xc6, 0xf2,0xde,0x1c,0x66, + 0x62,0xc1,0x18,0x16, 0x12,0x4f,0x82,0xc8, 0x81,0x92,0xb5,0xc3, 0x5a,0x49,0xaa,0x89, + 0x65,0x8b,0x41,0xc1, 0x4d,0x6e,0x03,0x90, 0x20,0xea,0x22,0x97, 0x15,0x1c,0x41,0x55, + 0x24,0x05,0x09,0xc9, 0x50,0xe9,0xd0,0x64, 0x3a,0x66,0x10,0xcd, 0xa5,0xac,0x09,0x39, + 0xea,0x47,0x1a,0x55, 0xcc,0x36,0x45,0x3a, 0xb1,0x03,0xc2,0x0e, 0xb8,0x57,0xe6,0x45, + 0x04,0x47,0xe6,0xc5, 0x10,0x70,0x7a,0x1f, 0xda,0xed,0xbb,0x09, 0xd6,0xcb,0xd8,0x02, + 0x1b,0xc0,0xc2,0x3d, 0xc2,0xb1,0x3e,0x98, 0x79,0xe1,0x90,0x2f, 0x44,0x9e,0xe2,0xdf, + 0x43,0xc7,0x20,0x79, 0xb7,0x67,0xda,0x76, 0x7e,0x6f,0xbf,0x6c, 0x75,0x68,0xce,0xb8, + 0x5f,0x44,0xbc,0x87, 0xb2,0x6e,0xfe,0x9f, 0x63,0x4e,0x5d,0x00, 0xb1,0xe6,0xc2,0x21, + 0xa8,0xf2,0x8a,0xf9, 0x30,0x01,0x4e,0x7e, 0x6f,0xd6,0xbe,0x4c, 0xe5,0x5c,0xd5,0x1d, + 0x3d,0x7d,0xeb,0x30, 0xa5,0x16,0x68,0xdf, 0x1a,0x9f,0xa2,0xff, 0x17,0xdf,0xfc,0xff, + // 0x600: + 0x51,0xdb,0xb7,0x78, 0x3d,0x55,0xdf,0x8c, 0x0c,0x0f,0x3d,0x69, 0x20,0xd3,0x61,0x02, + 0x0c,0xf6,0x11,0x7a, 0xa4,0xee,0xa8,0x32, 0xb0,0xd7,0x7c,0xbb, 0xb0,0x31,0x70,0x8b, + 0xc1,0xac,0x56,0x60, 0x89,0x6a,0xe3,0xca, 0x7a,0x2d,0xc4,0x8f, 0x1a,0x7f,0x7e,0xc0, + 0xb0,0x80,0x19,0x68, 0x0c,0x85,0xc2,0x3d, 0x97,0xf3,0x7e,0x98, 0x7e,0x08,0x63,0x0e, + 0xc7,0xab,0x76,0xc8, 0x04,0x66,0xe6,0xae, 0x56,0x09,0x27,0x50, 0x01,0xd7,0xa3,0x06, + 0x68,0xc3,0x1b,0x36, 0x40,0x68,0xe7,0x2a, 0x89,0x0b,0x6a,0x75, 0xf8,0x78,0x3b,0x77, + 0xfa,0x23,0xbe,0xfc, 0x9a,0x0e,0xd2,0x3b, 0x06,0x7d,0xfd,0x9d, 0x1c,0x8b,0x37,0xc6, + 0xf5,0xa7,0xde,0xd7, 0xa1,0x4d,0xdd,0xad, 0x9e,0x2e,0xab,0x31, 0x99,0x9a,0xfb,0xdb, + 0xab,0xaf,0x18,0x73, 0x67,0x3d,0xc7,0xa3, 0x65,0xb9,0x93,0x47, 0x3a,0xab,0x51,0xf6, + 0xdd,0x06,0x4d,0xdf, 0xd2,0x5f,0xad,0xef, 0x60,0xea,0x71,0xe2, 0x7d,0x5a,0x68,0xec, + 0x6e,0x79,0x8c,0xac, 0x73,0x00,0x51,0x55, 0x15,0xab,0xf5,0xfe, 0xb2,0xad,0x56,0xd3, + 0x55,0x25,0x0c,0x26, 0x4e,0xcd,0xe4,0x40, 0xb7,0x19,0x80,0x26, 0x4d,0x50,0x0c,0xbd, + 0x0d,0x74,0x5d,0xe0, 0x34,0xf2,0xc6,0x26, 0x54,0xb7,0x26,0x8c, 0x21,0x9d,0x3e,0x03, + 0x7d,0x47,0x28,0x60, 0x0e,0x03,0x91,0xb1, 0x83,0x8e,0xd2,0x13, 0x70,0x97,0x56,0x5b, + 0x85,0xe1,0xda,0x6b, 0xa9,0xc8,0xe5,0x86, 0x8d,0x56,0xc4,0xad, 0xd4,0xd1,0x66,0x5e, + 0x31,0xa8,0x19,0x03, 0x00,0x3f,0x0b,0xc5, 0x03,0xb7,0xe3,0x77, 0x40,0xee,0x88,0x6d, + // 0x700: + 0x18,0x43,0x26,0x8b, 0x24,0x0e,0xcb,0xf6, 0xa6,0xf3,0x34,0x08, 0x9c,0xbb,0x47,0x7f, + 0x4d,0xce,0x4a,0xc1, 0x72,0x8b,0x6d,0x2a, 0x8d,0x11,0x64,0x26, 0xd9,0xe3,0x31,0xae, + 0x58,0x5a,0x61,0x32, 0xd7,0xe9,0xef,0x56, 0x80,0xfd,0x68,0xfa, 0xf8,0xad,0xf9,0x35, + 0xd8,0xb4,0xa0,0x22, 0xd3,0x00,0x61,0x67, 0x01,0xb1,0x00,0x75, 0x1e,0x0b,0x01,0xed, + 0xa6,0x02,0x72,0x1b, 0x60,0x01,0xd8,0xab, 0xfa,0x29,0xec,0x38, 0xfd,0x4b,0x5c,0x40, + 0x84,0x53,0x4d,0x3a, 0x6c,0xf1,0xe0,0x07, 0x98,0x04,0x10,0x32, 0xe4,0x08,0x9c,0xff, + 0xd1,0xe4,0x79,0xbd, 0x9d,0x0e,0xa8,0xf8, 0xc7,0x5e,0xcd,0x77, 0xd7,0x01,0xef,0x8d, + 0xf8,0xfc,0x23,0x3e, 0x98,0xea,0x72,0x27, 0xab,0x66,0x4e,0xeb, 0x87,0x0d,0x08,0x45, + 0x64,0xb6,0xf1,0x84, 0x23,0x16,0xba,0x80, 0xe1,0x87,0xe6,0x14, 0x9e,0xbb,0x04,0xc2, + 0x68,0x1d,0x35,0x80, 0x16,0xd3,0x6b,0x3e, 0x10,0x15,0x80,0x32, 0xd1,0x8e,0x6e,0xe3, + 0xbf,0x03,0x39,0x00, 0xe9,0xfe,0x2b,0x40, 0xb8,0xab,0xe1,0xc5, 0xec,0x7e,0x76,0xdb, + 0x7e,0x00,0xfe,0x2f, 0x09,0x6a,0x84,0x21, 0xfe,0x4a,0x1a,0xce, 0x3a,0x9c,0xd9,0x4d, + 0xd3,0x38,0x13,0x55, 0x46,0x2e,0x34,0x83, 0x98,0xdc,0x33,0x33, 0x33,0x57,0xe3,0xe2, + 0x48,0x99,0x44,0x49, 0x41,0x21,0xd8,0xb2, 0x2e,0xf7,0xde,0x5a, 0xdd,0x3e,0x53,0xa3, + 0x38,0x30,0x82,0x8a, 0x76,0x04,0xb3,0xbf, 0x14,0xe2,0x78,0x1e, 0x47,0x3c,0x4d,0x4d, + 0x1b,0x64,0x19,0x93, 0x2b,0x2a,0xe4,0xcb, 0x43,0x5a,0x99,0xc6, 0xe1,0xd0,0xa1,0x86, + // 0x800: + 0x52,0xef,0x53,0xa0, 0xcc,0xd4,0xe3,0x89, 0xae,0x1e,0xbe,0x6b, 0x7c,0x21,0x90,0x26, + 0xf4,0x89,0xa0,0xce, 0x8e,0x0f,0x9f,0xe2, 0xfa,0x7e,0xd9,0xe1, 0x21,0x99,0x80,0xed, + 0xdd,0xc6,0x01,0xde, 0xfc,0x2a,0x0f,0x97, 0xf0,0x73,0x70,0x52, 0x20,0x46,0x78,0x94, + 0x9e,0x50,0x11,0xb4, 0x0f,0x53,0x1c,0x4b, 0x8f,0x4b,0x47,0x35, 0x9e,0xfb,0xfa,0xbb, + 0xd6,0xe7,0xa2,0x34, 0x81,0x29,0xd5,0x7d, 0xdc,0x1e,0x5d,0xbc, 0xe8,0x0b,0x7b,0x94, + 0x04,0x25,0x39,0x5a, 0xb6,0x0f,0xa4,0x03, 0x6a,0xc1,0x14,0xc1, 0x35,0x05,0x89,0x2c, + 0xc9,0xb8,0x06,0x47, 0xbe,0x17,0x2a,0xe4, 0x6b,0x36,0x98,0x0c, 0x84,0x55,0x6c,0x9c, + 0x03,0xf4,0x0f,0x24, 0x1e,0x46,0x83,0x9f, 0xec,0x13,0xb2,0x46, 0x46,0x48,0x19,0x49, + 0x99,0x66,0x2c,0xb4, 0xc0,0x66,0x29,0x04, 0x80,0x90,0x64,0x16, 0x68,0x68,0x7e,0x46, + 0x6f,0x99,0xbc,0xe0, 0x2f,0xdc,0x63,0x41, 0xc8,0xe4,0xe2,0x75, 0x8c,0x86,0xf3,0xb6, + 0x1a,0xb9,0xa7,0x36, 0x92,0xf5,0x2e,0xa8, 0xad,0x49,0x21,0x28, 0x31,0xad,0x00,0x69, + 0x8d,0x37,0x70,0xd8, 0xe8,0x6c,0x6e,0x8d, 0x1f,0x28,0xeb,0x56, 0x5a,0xed,0xd5,0xc4, + 0x35,0x38,0x2e,0xd7, 0x76,0x5d,0x9a,0xd6, 0xbc,0x68,0xd4,0x3b, 0x2f,0x83,0x9e,0xf3, + 0x80,0xbe,0x34,0xc3, 0x91,0xc9,0xba,0x9d, 0xcb,0x38,0xee,0x66, 0xfa,0x99,0x59,0xa9, + 0xc0,0xe2,0x37,0x85, 0x1c,0x4d,0x74,0x9a, 0xe8,0x6c,0x32,0x05, 0x01,0x76,0xe6,0xda, + 0x00,0xc4,0xb8,0x3b, 0xce,0x94,0xc6,0x1d, 0xb1,0xd0,0x06,0xdc, 0x30,0x1d,0x4b,0xb1, + // 0x900: + 0xaa,0x38,0xe6,0x72, 0x75,0xb9,0x14,0x5a, 0x35,0x91,0xc7,0x50, 0xb1,0x9a,0x6e,0x6c, + 0x73,0xbe,0x46,0x66, 0x75,0xb2,0x8d,0x86, 0x59,0xbb,0xcc,0xc1, 0xdb,0xb4,0xec,0x7b, + 0x75,0xe2,0x3f,0x59, 0x28,0xf9,0x9e,0x8f, 0x70,0xfe,0xef,0x33, 0x88,0x3a,0x3e,0x03, + 0x0c,0x32,0x7d,0x03, 0xbf,0x03,0x3b,0x0a, 0x39,0x0c,0xa3,0x9d, 0x33,0x57,0x5b,0x3e, + 0x6f,0xbb,0x27,0xd8, 0xe1,0xc0,0xa8,0xef, 0xb8,0xfb,0xab,0x0d, 0x67,0xcd,0x1a,0xef, + 0xb7,0xce,0xfc,0xbe, 0xb5,0x17,0xfa,0xd3, 0xe9,0x73,0xf5,0x27, 0x09,0x39,0x14,0x72, + 0xb2,0xd6,0xe8,0xec, 0x79,0x1e,0x42,0x00, 0x65,0x84,0x80,0xa6, 0xec,0x07,0x41,0x45, + 0x50,0x13,0xcc,0x8a, 0xe3,0x18,0x58,0x7a, 0x8e,0x70,0x3d,0x8a, 0x80,0xb5,0x58,0xed, + 0x3f,0x0a,0x67,0xc2, 0x79,0xa6,0x5a,0x76, 0x7a,0xa5,0x64,0xcb, 0xd4,0xd6,0x19,0x3e, + 0x2f,0xbc,0x16,0x7e, 0x29,0xf8,0xc6,0x72, 0x1f,0x3e,0x13,0x9c, 0x98,0x30,0x8a,0xcb, + 0xdd,0x38,0x96,0x30, 0x03,0x9e,0x7a,0x85, 0x19,0x23,0x46,0x2a, 0x65,0xd1,0x15,0xe0, + 0xe1,0xe9,0x9d,0x9d, 0xad,0x86,0xd8,0xc0, 0x36,0x9e,0x75,0x87, 0xd9,0xf5,0x5f,0x95, + 0x3c,0xd0,0x3c,0xa5, 0x70,0xe2,0x57,0xb8, 0x70,0xf9,0x76,0x13, 0x59,0x75,0x2b,0x3e, + 0x3e,0xb7,0x7b,0xb0, 0x37,0x6c,0x63,0x58, 0x37,0x48,0x14,0x4a, 0x0f,0xff,0xf1,0x74, + 0x07,0x28,0xe6,0x70, 0x3b,0x6b,0xb6,0xfb, 0x71,0x37,0xf5,0x68, 0x71,0x3e,0x7f,0xe3, + 0xfc,0x9d,0x0e,0xbb, 0x9c,0xab,0x4a,0x68, 0x6b,0xd0,0x1b,0x9e, 0x5e,0x5a,0xf4,0x9b, + // 0xa00: + 0x34,0x69,0xb1,0xa3, 0xc8,0x69,0x86,0x72, 0x9d,0x2f,0x13,0x61, 0x4e,0x59,0x34,0x72, + 0x6e,0x6b,0xb3,0x89, 0xc9,0xc9,0x4d,0xec, 0xe1,0x5d,0x6a,0x71, 0x38,0x9a,0x99,0x99, + 0x86,0xa1,0xd0,0xe6, 0x74,0x30,0x6a,0x76, 0xde,0xe6,0xb2,0xf5, 0x2c,0x87,0x33,0x54, + 0x67,0x68,0xed,0x93, 0x41,0x80,0xaa,0x73, 0xa6,0xb2,0x04,0xcb, 0xa7,0x34,0x86,0x83, + 0x2c,0xcb,0x9a,0x94, 0xf0,0x17,0x80,0x2e, 0x00,0xb0,0x5f,0xb9, 0xee,0x3d,0xe7,0xc7, + 0xf4,0x7d,0xf9,0xaf, 0xb4,0x30,0x9f,0x17, 0x26,0x4a,0xf9,0xc1, 0x63,0xb4,0xd0,0xe6, + 0x58,0xf9,0xdd,0xb2, 0x3f,0x8c,0x72,0x4e, 0xc1,0xa8,0x98,0x3f, 0x07,0x1d,0x6b,0x32, + 0x56,0x51,0x51,0x66, 0x54,0x9d,0x10,0x7a, 0xcb,0x16,0x7b,0xab, 0x56,0xca,0xea,0x02, + 0x23,0x41,0x71,0xdd, 0x70,0x3e,0x05,0xd4, 0x5f,0x5d,0x00,0xd2, 0x5c,0xa2,0xc8,0xbd, + 0x4e,0x5d,0xce,0xae, 0x16,0x58,0x52,0xbb, 0xb5,0xbd,0x23,0x77, 0x9a,0x7a,0xb7,0x72, + 0xcc,0x3b,0x4e,0x26, 0xe5,0xfc,0x50,0xc7, 0x08,0x10,0x0d,0xb5, 0xfa,0xb0,0x14,0x6c, + 0x0c,0x4f,0x0b,0x4f, 0x3e,0xfa,0xaa,0x06, 0x58,0xe0,0x6c,0x40, 0xa4,0x50,0x8a,0xd9, + 0x49,0x94,0xbd,0x5a, 0x12,0x01,0x62,0x46, 0x31,0x3f,0xaf,0xc3, 0xef,0x49,0xd6,0xe9, + 0x7c,0x37,0xd1,0x00, 0x66,0x01,0x1b,0x0d, 0xb2,0x30,0xe2,0xfe, 0x06,0x4e,0x03,0x5c, + 0x73,0xcc,0xd6,0x0d, 0x2f,0x1f,0x3b,0xed, 0x8a,0x4e,0xcd,0xa4, 0xe9,0xca,0x3b,0x75, + 0xc0,0x69,0xd4,0xeb, 0x2b,0x7a,0x2e,0x8e, 0x6b,0x3e,0xf3,0x83, 0x61,0x75,0x65,0xee, + // 0xb00: + 0x77,0x99,0xcd,0xeb, 0x0d,0x35,0xcb,0x2b, 0x9e,0xc3,0x7d,0x3a, 0x1c,0x75,0x28,0x64, + 0xeb,0xa7,0x32,0xf7, 0xe4,0xd6,0xa7,0x02, 0xcd,0xcd,0x06,0xe6, 0xf6,0xa3,0x58,0x59, + 0x66,0xa5,0x91,0xbc, 0xe4,0x39,0x0d,0xa1, 0xa8,0x6b,0x7a,0x1c, 0x51,0x7b,0x71,0x1b, + 0x98,0xd4,0xd9,0xc5, 0x3b,0xa3,0xca,0xd9, 0x3c,0xfe,0x86,0x7b, 0x06,0x98,0x3d,0xc1, + 0xf9,0xfd,0x37,0x45, 0x94,0x3d,0xc7,0xbc, 0x64,0xf9,0x05,0x91, 0xd4,0x7c,0xdf,0xbe, + 0xd1,0xd4,0xd8,0xc7, 0x5f,0x33,0xb4,0xdd, 0x2c,0xfe,0x1f,0x07, 0xca,0xbc,0xd9,0x1d, + 0x41,0x47,0x5e,0x78, 0x3a,0x0f,0x9f,0x3f, 0x83,0x30,0x33,0xeb, 0x6c,0x6a,0x27,0xfa, + 0xe8,0xed,0x43,0xd5, 0xd9,0xf2,0x73,0xe3, 0xce,0x72,0x1a,0xe6, 0x58,0x42,0x53,0x49, + 0x13,0xe5,0xc0,0xeb, 0xc5,0xb0,0x39,0x4e, 0x77,0x97,0x9f,0x90, 0x3c,0xd9,0x9f,0xfa, + 0xc7,0xfe,0x59,0xc4, 0x07,0x33,0x9c,0x02, 0xd5,0x5d,0xd1,0x88, 0x48,0x34,0x99,0x04, + 0x00,0x64,0x5f,0xee, 0x8c,0xbf,0x17,0x03, 0xe9,0x08,0x34,0x3f, 0xe3,0xfa,0x8c,0x63, + 0xb3,0x83,0xba,0x13, 0x1a,0xc0,0x50,0x5c, 0x74,0xff,0x6d,0x38, 0x20,0x1d,0x8d,0x01, + 0xb0,0x2f,0x83,0x8a, 0x23,0x03,0x5f,0xeb, 0xf0,0x7a,0xd5,0xe0, 0xea,0x51,0xce,0xac, + 0xdc,0xb9,0xb9,0x4f, 0x1a,0xef,0x31,0xfd, 0x5f,0x3e,0xdd,0x06, 0xfb,0x09,0xa2,0xe2, + 0x90,0xe5,0xd4,0x64, 0x2c,0x53,0x61,0xda, 0x61,0x96,0x2c,0xb9, 0xa3,0x17,0x82,0xb8, + 0x5b,0x64,0x5e,0xa6, 0xea,0xc0,0xce,0xdb, 0x4b,0xaa,0x43,0x16, 0x05,0x60,0x08,0x6d, + // 0xc00: + 0xc3,0x75,0x9d,0x39, 0x74,0xc6,0x10,0xcc, 0x72,0x06,0x5c,0x55, 0xa6,0xea,0xb7,0x49, + 0xb9,0x52,0xc4,0xac, 0xd1,0xa9,0xa7,0x3b, 0x98,0x70,0xbd,0x1e, 0x4c,0x03,0xc0,0x83, + 0xd7,0x22,0x07,0xd5, 0x9d,0xec,0x09,0x42, 0x98,0x7c,0x1a,0xc0, 0x04,0x7d,0x13,0x10, + 0xe9,0x87,0xdc,0x4e, 0xff,0xcc,0x03,0x9f, 0x58,0x8f,0x2d,0xbd, 0x96,0xf0,0x48,0xef, + 0xa6,0x8d,0x38,0x72, 0xf3,0xf7,0x72,0x1d, 0x11,0x33,0x14,0x59, 0xec,0x52,0xe7,0x52, + 0xa7,0x06,0xab,0x6d, 0x09,0xc9,0x41,0x9d, 0x41,0x0b,0x59,0x87, 0x56,0xb5,0x8e,0xe3, + 0x28,0x3b,0xb4,0xc2, 0xb8,0x7a,0xf7,0x3d, 0x27,0x79,0xe7,0x72, 0x76,0x91,0xfa,0x10, + 0xe2,0x6f,0xcf,0x21, 0x36,0x2c,0x9e,0x59, 0x30,0xf1,0x5a,0xf2, 0x9b,0x8d,0x78,0x94, + 0x3f,0xa2,0x16,0x0d, 0x4e,0x26,0x67,0xb2, 0xc8,0xc9,0x0f,0x37, 0x9e,0x78,0x9e,0x5f, + 0x53,0xf0,0xeb,0xfe, 0xaf,0xad,0x79,0x73, 0x4f,0x65,0xec,0xd6, 0x87,0xa7,0xd5,0x9c, + 0x21,0xe2,0x51,0xab, 0xeb,0xf3,0xde,0x7a, 0xb6,0x1c,0x4d,0xeb, 0xd3,0x22,0xf1,0x20, + 0x3c,0xbe,0xbf,0x58, 0x40,0xe9,0x62,0x07, 0x75,0x9d,0x30,0x13, 0xbd,0x17,0xbd,0x01, + 0x1f,0x5b,0x30,0x04, 0x43,0x8e,0x06,0xed, 0x9a,0x82,0x26,0xac, 0x83,0x5a,0x99,0xb2, + 0xa8,0x5f,0x98,0x2c, 0x99,0x34,0x32,0x5b, 0x0d,0x0a,0x8c,0xb1, 0x08,0x9c,0x8a,0x41, + 0xc4,0xb2,0x1a,0x14, 0x23,0x2a,0x06,0x59, 0x19,0x50,0x8d,0x58, 0x1c,0xd8,0x91,0x97, + 0x5c,0x64,0x91,0x1c, 0x8a,0x41,0x37,0x63, 0x46,0x60,0xdd,0x96, 0xa7,0x4b,0x24,0xb6, + // 0xd00: + 0x23,0xa9,0x40,0x51, 0x44,0x70,0x2c,0x81, 0xc0,0xb2,0x47,0x13, 0x7d,0x4e,0x28,0xc0, + 0x31,0x8c,0x01,0x48, 0x88,0xe6,0x2a,0x8b, 0x00,0xcd,0x0a,0xac, 0x2c,0x28,0x56,0xda, + 0x4e,0x2c,0x4b,0x2d, 0xb1,0x65,0x23,0x82, 0x91,0x31,0x61,0x6c, 0x25,0xa9,0x12,0x4e, + 0x02,0xc8,0x38,0x28, 0x8e,0xae,0x8b,0x24, 0x4c,0x29,0x24,0xc5, 0x88,0xe4,0x51,0x24, + 0xb6,0xd4,0x91,0x2d, 0x10,0xb4,0x92,0x72, 0x59,0xc0,0xa0,0xd9, 0x6d,0x08,0x97,0x14, + 0xc9,0x23,0x05,0x44, 0xd0,0x68,0x36,0x01, 0x6a,0x0d,0xb1,0x45, 0x96,0x44,0xb6,0x24, + 0x4c,0x54,0x9c,0xa9, 0x75,0x9a,0x0e,0xb2, 0x24,0x0d,0x9a,0x68, 0x55,0x2c,0x02,0x28, + 0x29,0xa8,0xa2,0xa1, 0x18,0x29,0x20,0x2a, 0x03,0x92,0x88,0x92, 0x72,0x2c,0x07,0x05, + 0x89,0x26,0x54,0x92, 0x75,0x44,0x37,0x60, 0x5a,0x02,0x0a,0x2b, 0x8d,0xd2,0x2e,0xe0, + 0x02,0x2e,0x73,0x4a, 0x53,0x05,0xcc,0x04, 0x48,0x6a,0xc8,0x87, 0x54,0x89,0xb1,0x52, + 0x13,0x43,0x43,0x36, 0x2c,0x80,0x76,0x04, 0x50,0xa8,0xa0,0xe6, 0x22,0xe4,0x82,0x2c, + 0x02,0x2a,0x03,0x6c, 0x40,0x36,0x39,0x1d, 0x6a,0x07,0x05,0x84, 0x96,0xc2,0xdd,0x28, + 0x2d,0x43,0x62,0xa3, 0x9a,0x48,0x73,0x50, 0x9b,0xa4,0x8d,0x50, 0x93,0x62,0xc4,0x90, + 0x8c,0x15,0x00,0x96, 0xa2,0x49,0x65,0x24, 0x35,0x62,0x43,0x2c, 0x44,0x9d,0x0a,0xd5, + 0x44,0x83,0xd8,0x3c, 0x89,0x65,0x7b,0x2c, 0xf3,0x4b,0x3d,0x1e, 0xd9,0x9c,0x5f,0x54, + 0x30,0x33,0x26,0x64, 0x7b,0x2c,0xd5,0x9f, 0x6b,0x77,0x4b,0xac, 0x47,0x69,0x72,0x1e, + // 0xe00: + 0xfb,0xdb,0xa1,0xa9, 0x5d,0x8a,0x7d,0x7f, 0x3e,0xff,0x99,0xf4, 0x71,0x3b,0xdf,0x3b, + 0x07,0xc3,0xb7,0x80, 0xd4,0xf4,0x0e,0x70, 0x30,0x7a,0x71,0xe8, 0xb2,0x38,0x1b,0xae, + 0xab,0xd5,0xdd,0xb9, 0x82,0xf2,0x79,0x9c, 0xb9,0xb8,0xe4,0x0e, 0x4c,0x00,0xae,0x59, + 0x0c,0x6d,0x8e,0x40, 0x36,0x04,0x00,0xa4, 0x9e,0xa8,0xb8,0x12, 0xbb,0x89,0xde,0x45, + 0x2b,0x18,0x02,0x49, 0x92,0x2d,0xd9,0x00, 0x5c,0xad,0xbe,0xe5, 0x3b,0x80,0xe8,0x6e, + 0x65,0xc0,0x05,0x72, 0x6a,0x83,0x74,0x6f, 0x9b,0x21,0x6f,0x34, 0x1e,0x23,0x39,0x01, + 0xc0,0x1a,0x8f,0x89, 0x93,0xa8,0x40,0x92, 0x43,0xe9,0xfa,0xa7, 0xd9,0xf9,0xfd,0x7b, + 0xd3,0xaf,0x2f,0xb8, 0xbd,0xe6,0x99,0xe7, 0x84,0x8d,0x25,0xf4, 0x53,0x22,0xe1,0xa6, + 0x80,0xc3,0x03,0xbe, 0x8e,0x06,0x47,0x1c, 0x07,0x1d,0x67,0x1d, 0xf6,0xf0,0xde,0xe6, + 0xda,0xd9,0x0c,0x32, 0x4d,0x9b,0x98,0x7d, 0xd5,0x8d,0xf9,0xce, 0x75,0x3b,0x0b,0x05, + 0xf2,0x9d,0x6b,0xd8, 0x1a,0xbd,0x4b,0xb9, 0xc8,0xf3,0xf1,0xd3, 0x9b,0x65,0xe3,0xb6, + 0x25,0xea,0x46,0x74, 0x1a,0x1c,0x8e,0x77, 0x06,0xc6,0x86,0x22, 0x8b,0xba,0x6f,0x38, + 0x5c,0xd3,0xa7,0x5a, 0x02,0x01,0xc1,0xa0, 0x84,0x44,0x4f,0xf8, 0xfa,0xbe,0xab,0xf9, + 0x3a,0x27,0xff,0x7b, 0x90,0x0f,0x8b,0xf5, 0xbe,0x45,0x80,0x7f, 0xef,0x71,0xe8,0x7d, + 0x5c,0x51,0x5e,0xa9, 0x00,0x03,0xd1,0x80, 0x76,0x40,0x69,0x18, 0xc9,0x12,0x8a,0xf8, + 0xa5,0x91,0x1f,0x00, 0x71,0x32,0xc4,0x2c, 0xb2,0x24,0xa4,0x94, 0xa0,0xa9,0x3e,0x1a, + // 0xf00: + 0x11,0xf0,0x0b,0xa0, 0xf8,0x66,0x48,0x4d, 0x4b,0xf1,0x4d,0x60, 0x99,0xc0,0xcf,0xe6, + 0xff,0x2b,0xff,0xdd, 0x76,0x1c,0x42,0x85, 0x84,0x4b,0x29,0x2a, 0xc2,0xca,0x22,0xd8, + 0x9e,0xe9,0x6c,0xa6, 0x58,0x59,0x62,0xcb, 0x65,0x52,0x55,0xb0, 0xa4,0xa2,0x8b,0xe5, + 0x66,0xee,0x59,0xea, 0x0c,0xf1,0x34,0x78, 0xa7,0x95,0x4f,0x39, 0x99,0x23,0x61,0x65, + 0xf9,0x73,0x24,0x9c, 0xf3,0x30,0xdd,0x88, 0x5b,0x92,0xc8,0x2a, 0x91,0x24,0x35,0x2a, + 0x46,0xe5,0x92,0x64, 0xa1,0xbb,0x32,0x54, 0x39,0xa7,0xa4,0x68, 0x31,0x23,0xc0,0xa0, + 0xed,0x64,0xe9,0x49, 0x1a,0x16,0x6e,0x55, 0x16,0x49,0x42,0xc8, 0x8a,0x16,0x3c,0x4a, + 0x81,0x97,0xe5,0x7d, 0xbf,0x6d,0xcd,0xc2, 0x28,0xae,0x54,0x98, 0x51,0x65,0x16,0x59, + 0x0a,0x39,0xc4,0xed, 0x2f,0x97,0x18,0x1c, 0x51,0x6c,0x8b,0x28, 0x96,0x92,0xd9,0x3c, + 0x8a,0x99,0x64,0x45, 0x54,0x5d,0xe4,0x83, 0x72,0xa4,0x71,0x4d, 0x8b,0x08,0xb2,0x2c, + 0x27,0x80,0xc1,0x89, 0x16,0x96,0xad,0x92, 0x5a,0x2a,0x2c,0x49, 0x3d,0x16,0x66,0xe5, + 0xca,0x1e,0x6c,0x19, 0x2c,0xd5,0x48,0x78, 0xed,0x32,0x06,0xd5, 0x09,0x6c,0x82,0xaa, + 0x6a,0x54,0x98,0x3c, 0x62,0x1b,0xb1,0xa0, 0xd6,0x13,0x32,0x62, 0x37,0x93,0x24,0xd5, + 0x87,0x32,0xf8,0xb1, 0xa8,0x5e,0xe5,0x32, 0x59,0x0e,0xac,0xdd, 0x65,0x1a,0xb1,0x45, + 0x84,0xb6,0x5a,0x5a, 0xc6,0xe8,0x1e,0x4c, 0x00,0xd6,0x5a,0x6a, 0xa0,0x61,0xe5,0x8a, + 0x03,0x00,0x6a,0xd1, 0x7e,0x27,0x6b,0xcf, 0x03,0xa4,0x7e,0x38, 0xfb,0x5f,0xe9,0x8e, + // 0x1000: + 0xd1,0x27,0xbe,0x2c, 0x83,0xff,0x43,0xf0, 0x06,0x44,0x3d,0xb0, 0x79,0xb0,0x92,0xc0, + 0x9e,0xf5,0x43,0xda, 0x16,0x41,0xa1,0x60, 0xf5,0xd9,0x35,0x2b, 0x25,0x09,0xa0,0xf6, + 0x62,0x24,0xbf,0xee, 0x1f,0xc7,0xd4,0x44, 0xdd,0x96,0xc8,0x2c, 0x25,0x96,0x96,0x8a, + 0x2c,0x25,0x0f,0x80, 0x62,0x65,0x46,0x58, 0xcb,0x2c,0xa1,0xd6, 0x4c,0x93,0xe0,0xc8, + 0xd3,0xc3,0x4a,0x51, 0x7c,0x29,0xe0,0xe5, 0xa6,0xf0,0x86,0x04, 0xa3,0x40,0x69,0xc1, + 0x9b,0xa6,0x3a,0x03, 0x99,0xc0,0x6f,0xc4, 0xb0,0xdc,0xba,0x94, 0xc9,0x0b,0x20,0x43, + 0x9e,0xb2,0x09,0xa9, 0xc6,0x41,0x95,0x27, 0x61,0xbc,0x23,0xa2, 0xc6,0x58,0xe6,0xe6, + 0x61,0xc0,0xa8,0xeb, 0x59,0x23,0x70,0xad, 0x28,0xe2,0x9d,0x56, 0xe9,0x16,0xc4,0x65, + 0x62,0x93,0x54,0x60, 0xb0,0x39,0xb9,0x78, 0x1c,0x8d,0x68,0x36, 0x2c,0xeb,0x35,0x1a, + 0xdd,0x90,0xca,0x1c, 0x65,0x0c,0x16,0x09, 0x26,0x1a,0x30,0xdd, 0x09,0xa6,0x98,0xc4, + 0xc4,0xba,0x69,0x38, 0x21,0x87,0x14,0xc7, 0xab,0x48,0xbb,0x08, 0x86,0x73,0x40,0x6d, + 0x89,0x50,0x0d,0x6e, 0x21,0x48,0x92,0x28, 0x46,0xc8,0xab,0x14, 0x60,0xc8,0x4c,0xa2, + 0xee,0xef,0x5b,0x91, 0x61,0xae,0xf3,0x5a, 0x42,0x8d,0x0c,0x71, 0x29,0xaa,0x8d,0x46, + 0x19,0x26,0xb9,0x99, 0x3a,0x94,0xc0,0xa7, 0x1d,0x34,0x69,0x1c, 0x0b,0x23,0xb8,0xe3, + 0xce,0xcd,0x23,0xb5, 0x60,0xe0,0x4c,0xa2, 0xc9,0x92,0x73,0x82, 0xcc,0xb8,0x99,0x64, + 0x9d,0xa5,0xc9,0x17, 0x62,0xc6,0xe5,0x26, 0xaa,0xd3,0xb8,0x59, 0x97,0xbf,0x8c,0xed, + // 0x1100: + 0x61,0xb9,0x83,0xb8, 0xcd,0x0b,0x3b,0xcc, 0xc9,0x93,0x13,0x2b, 0xc6,0xf8,0xd7,0x55, + 0x16,0x80,0xe1,0x42, 0xd0,0xb2,0x14,0xed, 0xdf,0x83,0x49,0x2d, 0xd8,0x96,0x51,0xd8, + 0x67,0x33,0x79,0x26, 0xd5,0x6b,0xc0,0xd8, 0xc2,0x32,0xff,0x9a, 0x7d,0x9f,0xb1,0xf5, + 0x7c,0x0e,0x07,0xef, 0xff,0x21,0x80,0xf0, 0x48,0xb6,0x58,0x28, 0x59,0xea,0xb1,0x86, + 0x4c,0x8c,0xb2,0x1e, 0x52,0xa4,0xf4,0x3d, 0x0d,0x6a,0x1b,0x21, 0xfb,0xd1,0xff,0xc1, + 0xd0,0xc1,0xf7,0x6d, 0xc9,0x23,0xde,0x90, 0xbe,0xd9,0x63,0xeb, 0xfe,0x27,0xd8,0xd6, + 0xff,0x25,0x3e,0xb8, 0xe8,0x6e,0x0f,0x88, 0x94,0xa5,0x91,0x67, 0xb7,0xd3,0x03,0xf3, + 0x93,0x40,0xe0,0x01, 0x58,0x0c,0x03,0x01, 0xcd,0x99,0x7d,0x5f, 0x59,0xf1,0x63,0xcd, + 0x50,0x01,0xe1,0x55, 0x00,0x90,0x11,0xdf, 0x72,0x17,0xee,0x99, 0x22,0x7c,0xb9,0x50, + 0x9e,0xe0,0xa9,0x09, 0xeb,0x3e,0x6d,0x0c, 0x7c,0x98,0x9e,0x91, 0x5c,0x8f,0x6c,0xc6, + 0x82,0xf1,0x2e,0x64, 0x30,0xe8,0x33,0x5b, 0x39,0x9d,0x73,0x39, 0xa8,0xce,0xaf,0x43, + 0x56,0x8d,0xd9,0xd5, 0x91,0x65,0xc0,0xeb, 0x5a,0x79,0xe6,0x48, 0xd8,0xd8,0xce,0xb5, + 0xcf,0x06,0xf0,0x39, 0xd6,0x86,0x86,0x3d, 0xda,0xef,0xbe,0xff, 0x00,0xde,0x4e,0xd9, + 0x9c,0x60,0xbc,0x75, 0x2d,0x3c,0x67,0x39, 0x38,0x71,0xcc,0xe0, 0x3f,0xe3,0x39,0x1c, + 0x58,0x52,0x6f,0x78, 0x73,0x2c,0x85,0xde, 0x39,0xa1,0xd0,0xe5, 0xbe,0x07,0xde,0xf3, + 0x81,0xbe,0x00,0x38, 0xae,0x1b,0x6d,0x43, 0x9e,0xeb,0x35,0x8b, 0x1d,0xd3,0xca,0x38, + // 0x1200: + 0xbc,0xc7,0x2d,0x5d, 0x03,0x50,0x0e,0x49, 0xd4,0xad,0x5b,0xad, 0x6b,0x24,0x6a,0xb3, + 0xba,0x49,0x04,0xb0, 0x2e,0xb9,0xe2,0xe4, 0x09,0xcc,0xc5,0xe5, 0x35,0x03,0x74,0xeb, + 0x38,0x1c,0x62,0xae, 0x24,0xcc,0xdd,0x90, 0x16,0x62,0x06,0xa5, 0xa2,0xb1,0x80,0x90, + 0x92,0x44,0x43,0x86, 0x63,0x34,0x5d,0x52, 0xb8,0xcf,0x00,0x14, 0xca,0x0f,0x4f,0x8a, + 0xf6,0xdf,0xed,0x62, 0x4f,0xa5,0x3f,0x8b, 0xf6,0x74,0x8f,0xc6, 0x29,0x4b,0x23,0xb4, + 0x49,0x4a,0x14,0x3b, 0xca,0x91,0xe6,0x2c, 0x79,0x0b,0x13,0xd4, 0x35,0x94,0x54,0x51, + 0x49,0x19,0x53,0xcc, 0x6f,0x11,0x35,0x53, 0xd5,0x47,0xae,0xcb, 0x40,0xf5,0x14,0x48, + 0x70,0x14,0x43,0x25, 0x91,0x5e,0x20,0x28, 0x98,0x92,0x08,0x1d, 0x68,0x83,0x88,0x82, + 0xb5,0x04,0x16,0xe2, 0x86,0xd6,0x0f,0x16, 0x3b,0x59,0x3c,0x05, 0x23,0x12,0xdf,0x85, + 0x9b,0xb2,0x23,0xed, 0xc5,0x85,0x1b,0xc9, 0xe5,0x29,0x65,0x2a, 0xe7,0x8c,0x8e,0x2c, + 0x2c,0xdf,0x8f,0xa3, 0xef,0xfa,0x39,0xca, 0x8e,0xb3,0x0d,0x4b, 0x15,0x1d,0x80,0x40, + 0xd3,0x1c,0xf9,0x74, 0x83,0xcf,0x17,0x4c, 0x47,0xa0,0x02,0x22, 0x27,0x00,0x46,0x13, + 0x9b,0x22,0x1a,0x96, 0x41,0x96,0x23,0x54, 0x65,0x89,0x24,0xb6, 0x3d,0x25,0x36,0x18, + 0x68,0x33,0x21,0xca, 0x9e,0xa1,0x2a,0x32, 0x0f,0xa3,0x08,0x23, 0xfa,0xa0,0x41,0x39, + 0x33,0x6e,0xf7,0xcf, 0x1d,0x78,0xe0,0x78, 0xbe,0x5c,0xe4,0xe7, 0x6c,0x8e,0x26,0xf0, + 0xa3,0xb2,0xdd,0x69, 0x03,0x25,0x89,0x24, 0xd3,0x45,0x31,0xcf, 0x93,0x7e,0x2c,0x27, + // 0x1300: + 0x8e,0xfc,0xfd,0x80, 0xeb,0xed,0x6f,0x50, 0xd8,0x7b,0x98,0x01, 0xca,0x02,0x3d,0xde, + 0x54,0xa2,0x9b,0x24, 0x08,0x30,0xeb,0x1d, 0x00,0x71,0x7e,0x4d, 0x4c,0x83,0x47,0x43, + 0x5a,0x1f,0xf8,0xbb, 0x92,0x29,0xc2,0x84, 0x83,0xa9,0xf0,0x61, 0x50, + // 0x132d = 4909 Bytes +}; + +// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 ctcode_brres_strings_bz2[] = +{ + 0x00,0x00,0x01,0x40, 0x42,0x5a,0x68,0x31, 0x31,0x41,0x59,0x26, 0x53,0x59,0x0f,0x63, + 0x56,0x7c,0x00,0x00, 0x09,0xff,0x80,0x70, 0x21,0x08,0x00,0x00, 0x60,0x7f,0x60,0x3e, + 0x81,0xd4,0x80,0xa2, 0x00,0x5e,0x40,0x20, 0x00,0x75,0x09,0x54, 0xf4,0x40,0x00,0x0d, + 0x00,0xd3,0x20,0x02, 0xa8,0xa8,0x3d,0x40, 0x00,0x68,0x06,0x8d, 0x34,0xda,0x9b,0x51, + 0x9e,0x6c,0xc6,0xc5, 0x31,0x1f,0x66,0x02, 0x92,0xa2,0x2f,0xf5, 0x43,0xe3,0x81,0xcc, + 0xa8,0xba,0xc1,0xd0, 0x23,0x06,0x08,0x0c, 0x04,0x3c,0x04,0xc1, 0x43,0x90,0x6f,0x5a, + 0xf2,0xea,0x55,0xb3, 0xdc,0xf2,0x19,0x96, 0xf1,0x6b,0xc8,0xe9, 0xb0,0x06,0xcb,0xc5, + 0xb8,0x0a,0xd7,0x0d, 0xf3,0x30,0xb8,0x5b, 0x4a,0xbe,0x26,0x53, 0x9b,0xf8,0xbb,0x92, + 0x29,0xc2,0x84,0x80, 0x7b,0x1a,0xb3,0xe0, + // 0x88 = 136 Bytes +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ctcode_data_tab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef DEF_DATA +#undef DEF_BZIP +//#undef DEF_PATCH + +#define DEF_DATA(a) { #a, 0, a, sizeof(a) }, +#define DEF_BZIP(a) { #a, 1, a##_bz2, sizeof(a##_bz2) }, +//#define DEF_PATCH(m,a,b) { #a, m, b, sizeof(b), a }, + +const data_tab_t ctcode_data_tab[] = +{ + DEF_DATA(ctcode_header) + DEF_DATA(cup1_header) + DEF_DATA(crs1_header) + + DEF_BZIP(ctcode_boot_code_pal) + DEF_BZIP(ctcode_boot_code_usa) + DEF_BZIP(ctcode_boot_code_jap) + + DEF_BZIP(ctcode_boot_data_pal) + DEF_BZIP(ctcode_boot_data_usa) + DEF_BZIP(ctcode_boot_data_jap) + + DEF_BZIP(ctcode_bad1code_pal) + DEF_BZIP(ctcode_bad1code_usa) + DEF_BZIP(ctcode_bad1code_jap) + + DEF_BZIP(ctcode_mod1_pal) + DEF_BZIP(ctcode_mod1_usa) + DEF_BZIP(ctcode_mod1_jap) + + DEF_BZIP(ctcode_mod2_pal) + DEF_BZIP(ctcode_mod2_usa) + DEF_BZIP(ctcode_mod2_jap) + + DEF_BZIP(ctcode_ovr1_pal) + DEF_BZIP(ctcode_ovr1_usa) + DEF_BZIP(ctcode_ovr1_jap) + + DEF_BZIP(ctcode_image) + + DEF_BZIP(ctcode_brres_base) + DEF_BZIP(ctcode_brres_strings) + + {0,0,0} +}; + +#undef DEF_DATA +#undef DEF_BZIP +//#undef DEF_PATCH + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-file.c b/project/src/lib-file.c new file mode 100644 index 0000000..5a1cc44 --- /dev/null +++ b/project/src/lib-file.c @@ -0,0 +1,3435 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "lib-std.h" +#include "lib-szs.h" +#include "lib-rarc.h" +#include "lib-pack.h" +#include "lib-brres.h" +#include "lib-breff.h" +#include "lib-xbmg.h" +#include "lib-kcl.h" +#include "lib-kmp.h" +#include "lib-rkc.h" +#include "lib-rkg.h" +#include "lib-image.h" +#include "lib-staticr.h" +#include "lib-ctcode.h" +#include "lib-lecode.h" +#include "lib-common.h" +#include "lib-bzip2.h" +#include "lib-checksum.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cygwin support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __CYGWIN__ + + void NormalizeFilenameParam ( ParamList_t *param ) + { + if ( param->arg && *param->arg ) + { + char *res = AllocNormalizedFilenameCygwin(param->arg); + FREE(param->arg); + param->arg = res; + } + } + +#endif // __CYGWIN__ + +/////////////////////////////////////////////////////////////////////////////// + +void SetSource ( ccp source ) +{ + #ifdef __CYGWIN__ + opt_source = IsWindowsDriveSpec(source) + ? AllocNormalizedFilenameCygwin(source) + : source; + #else + opt_source = source; + #endif + + //if ( source && *source ) + AppendStringField(&source_list,source,false); + noPRINT("SetSource(%s) N=%u/%u\n",source,source_list.used,source_list.size); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetDest ( ccp dest, bool mkdir ) +{ + #ifdef __CYGWIN__ + opt_dest = IsWindowsDriveSpec(dest) + ? AllocNormalizedFilenameCygwin(dest) + : dest; + #else + opt_dest = dest; + #endif + opt_mkdir = mkdir; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CheckOptDest ( ccp default_dest, bool default_mkdir ) +{ + if (!opt_dest) + { + opt_dest = default_dest; + if (!opt_mkdir) + opt_mkdir = default_mkdir; + } + + if ( opt_dest && opt_dest[0] == '-' && !opt_dest[1] ) + stdlog = stderr; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct File_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool is_seekable +( + File_t * F // file structure +) +{ + mode_t mode = F->st.st_mode; + F->is_seekable = ( S_ISREG(mode) || S_ISBLK(mode) || S_ISCHR(mode) ) + && F->st.st_size + && lseek(fileno(F->f),0,SEEK_SET) != (off_t)-1; + return F->is_seekable; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenFILE +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + bool ignore_limit // ignore opt_max_file_size +) +{ + FileMode_t fmode = FM_STDIO; + if (ignore_no_file) + fmode |= FM_IGNORE; + return OpenFile( f,initialize, fname,fmode, + ignore_limit ? 0 : opt_max_file_size, + "You can change this security limit with --max-file-size=MiB.\n" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateFILE +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool testmode, // true: don't open + bool create_path, // create path if necessary + bool overwrite, // overwrite existing files without warning + bool remove_dest, // remove existing file before creating + bool update // don't create new files +) +{ + FileMode_t fmode = FM_STDIO; + if (testmode) + fmode |= FM_TEST; + if (create_path) + fmode |= FM_MKDIR; + if (overwrite) + fmode |= FM_OVERWRITE; + if (opt_number) + fmode |= FM_NUMBER; + if (remove_dest) + fmode |= FM_REMOVE; + if (update) + fmode |= FM_UPDATE; + return CreateFile(f,initialize,fname,fmode); +} + +/////////////////////////////////////////////////////////////////////////////// + +FileMode_t GetFileModeByOpt +( + int testmode, // test mode + ccp fname1, // NULL or dest filename + ccp fname2 // NULL or source filename +) +{ + FileMode_t fmode = FM_STDIO; + + if ( testmode ) fmode |= FM_TEST; + if ( opt_mkdir ) fmode |= FM_MKDIR; + if ( opt_number ) fmode |= FM_NUMBER; + if ( opt_remove_dest ) fmode |= FM_REMOVE; + if ( opt_update ) fmode |= FM_UPDATE; + + if ( opt_overwrite || fname1 && *fname1 && fname2 && !strcmp(fname1,fname2) ) + fmode |= FM_OVERWRITE; + + return fmode; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateFileOpt +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool testmode, // true: don't open + ccp srcfile // NULL or src file name + // if fname == srcfile: overwrite enabled +) +{ + DASSERT(f); + DASSERT(fname); + + FileMode_t fmode = GetFileModeByOpt(testmode,fname,srcfile) | FM_TOUCH; + return CreateFile(f,initialize,fname,fmode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError AppendFILE +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool testmode, // true: don't open + bool create_path // create path if necessary +) +{ + FileMode_t fmode = FM_STDIO | FM_APPEND; + if (testmode) + fmode |= FM_TEST; + if (create_path) + fmode |= FM_MKDIR; + return CreateFile(f,initialize,fname,fmode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +FILE * CreateAnalyzeFile() +{ + static bool done = false; + if (done) + return analyze_file; + done = true; + + if ( !analyze_fname || !*analyze_fname ) + return 0; + + if (!strcmp(analyze_fname,"-")) + analyze_file = stdout; + else + analyze_file = fopen(analyze_fname,"w"); + if (!analyze_file) + ERROR1(ERR_CANT_CREATE,"Can't create analyse log file: %s\n",analyze_fname); + + return analyze_file; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseAnalyzeFile() +{ + if ( analyze_file && analyze_file != stdout ) + fclose(analyze_file); + analyze_file = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * SplitSubPath +( + char * buf, // destination buffer + size_t buf_size, // size of 'buf' + ccp path // source path, if NULL: use 'buf' +) +{ + char * end = path && path != buf + ? StringCopyS(buf,buf_size,path) + : buf + strlen(buf); + + if ( end >= buf + buf_size - 2 ) + { + end = buf + buf_size - 2; + *end = 0; + } + + struct stat st; + if (!stat(buf,&st)) + return end; + + char * ptr = end; + while ( ptr > buf ) + { + ptr--; + while ( ptr > buf && *ptr != '/' && *ptr != '\\' ) + ptr--; + if ( ptr == buf ) + break; + + *ptr = 0; + noPRINT("TEST: |%s|\n",buf); + if (!stat(buf,&st)) + { + noPRINT("FOUND: |%s|\n",buf); + if (S_ISREG(st.st_mode)) + { + memmove(ptr+1,ptr,end-ptr+1); + *++ptr = '/'; + return ptr; + } + *ptr = '/'; + return 0; + } + *ptr = '/'; + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// more file support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptChdir ( ccp arg ) +{ + PRINT("CHDIR: %s\n",arg); + if ( !arg || !*arg || !chdir(arg) ) + return 0; + ERROR1(ERR_CANT_OPEN,"Can't change dir: %s\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenReadFILE +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + bool silent, // suppress all error messages + u8 ** res_data, // store alloced data here + uint * res_size, // not NULL: store data size + ccp * res_fname, // not NULL: store alloced filename + FileAttrib_t * res_fatt // not NULL: store file attributes +) +{ + DASSERT( path1 || path2 ); + DASSERT(res_data); + + *res_data = 0; + if (res_size) + *res_size = 0; + if (res_fname) + *res_fname = 0; + if (res_fatt) + memset(res_fatt,0,sizeof(*res_fatt)); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + PRINT("OpenReadFILE(%s,%d)\n",path,silent); + + File_t F; + enumError err = OpenFILE(&F,true,path,silent,false); + if (err) + return err; + + uint data_size = F.st.st_size; + if (!F.is_seekable) + data_size = opt_max_file_size; + u8 *data = MALLOC(data_size+1); + + size_t read_size = fread(data,1,data_size,F.f); + if ( read_size && !F.is_seekable ) + { + PRINT("non seekable read: %zu\n",read_size); + data_size = read_size; + data = REALLOC(data,data_size+1); + } + data[data_size] = 0; // termination for text files + + if ( read_size != data_size ) + { + if (!silent) + FILEERROR1(&F,ERR_READ_FAILED,"Read failed: %s\n",path); + ResetFile(&F,false); + FREE(data); + return ERR_READ_FAILED; + } + + + *res_data = data; + if (res_size) + *res_size = data_size; + if (res_fname) + { + *res_fname = F.fname; + F.fname = EmptyString; + } + if (res_fatt) + SetFileAttrib(res_fatt,&F.fatt,0); + + ResetFile(&F,false); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadFILE +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // skip num of bytes before reading + void * data, // destination buffer, size = 'size' + size_t size, // size to read + int silent, // 0: print all error messages + // 1: suppress file size warning + // 2: suppress all error messages + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store *max* values to 'fatt' +) +{ + ASSERT(data); + if (!size) + return ERR_OK; + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + TRACE("LoadFILE(%s,%zu,%zu,%d)\n",path,skip,size,silent); + + if (fatt) + { + struct stat st; + if (!stat(path,&st)) + UseFileAttrib(fatt,0,&st,fatt_max); + } + + FILE * f = fopen(path,"rb"); + if (!f) + { + if (silent<2) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path); + return ERR_CANT_OPEN; + } + + if ( skip > 0 ) + fseek(f,skip,SEEK_SET); + + size_t read_stat = fread(data,1,size,f); + fclose(f); + + if ( read_stat == size ) + return ERR_OK; + + enumError err = ERR_READ_FAILED; + if ( silent == 1 && size > 0 ) + err = ERR_WARNING; + else if ( silent < 2 ) + ERROR1(ERR_READ_FAILED,"Can't read file: %s\n",path); + + noPRINT("D=%p, s=%zu/%zu: %s\n",data,read_stat,size,path); + if ( read_stat >= 0 && read_stat < size ) + memset((char*)data+read_stat,0,size-read_stat); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveFILE +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + bool overwrite, // true: force overwriting + const void * data, // data to write + uint data_size, // size of 'data' + FileAttrib_t * fatt // not NULL: set timestamps using this attribs +) +{ + DASSERT(data); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + TRACE("SaveFILE(%d) %s\n",overwrite,path); + + File_t F; + enumError err = CreateFileOpt(&F,true,path,testmode, overwrite ? path : 0 ); + if ( err || !F.f ) + { + ResetFile(&F,0); + return err; + } + + const uint written = fwrite(data,1,data_size,F.f); + if ( written != data_size ) + { + ERROR1(ERR_WRITE_FAILED,"Write to file failed: %s\n",path); + ResetFile(&F,0); + return ERR_WRITE_FAILED; + } + + if (fatt) + memcpy(&F.fatt,fatt,sizeof(F.fatt)); + return ResetFile(&F,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint IsolateFilename +( + // return length of found and copied string + + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + ccp path, // path to analyze + file_format_t fform // not NULL: remove related file extension +) +{ + DASSERT(buf); + DASSERT(path); + + uint len; + path = FindFilename(path,&len); + if ( len >= bufsize ) + len = bufsize-1; + memcpy(buf,path,len); + buf[len] = 0; + if (fform) + { + ccp ext = GetExtFF(0,fform); + uint elen = strlen(ext); + char *bufext = buf+len-elen; + if ( elen < len && !strcasecmp(ext,bufext) ) + { + *bufext = 0; + len = bufext - buf; + } + else + { + ext = GetMagicExtFF(0,fform); + elen = strlen(ext); + bufext = buf+len-elen; + if ( elen < len && !strcasecmp(ext,bufext) ) + { + *bufext = 0; + len = bufext - buf; + } + } + } + return len; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum { N_NAME_BUF = 16, NAME_BUF_SIZE = 24 }; + +//----------------------------------------------------------------------------- + +static char *GetNameBuf() +{ + static char buffers[N_NAME_BUF][NAME_BUF_SIZE]; + static int buf_idx = 0; + + if ( --buf_idx < 0 ) + buf_idx = N_NAME_BUF - 1; + return buffers[buf_idx]; +} + +//----------------------------------------------------------------------------- + +ccp GetNameFF ( file_format_t ff1, file_format_t ff2 ) +{ + if ( ff1 == FF_DIRECTORY || IsCompressedFF(ff1) ) + { + ccp prefix = FileTypeTab[ff1].name; + if ( !ff2 || (uint)ff2 >= FF_N || ff2 == ff1 ) + { + noPRINT("GetNameFF(%u,%u) -> '%s' [ff1]\n",ff1,ff2,prefix); + return prefix; + } + + char *buf = GetNameBuf(); + snprintf(buf,NAME_BUF_SIZE,"%s.%s",prefix,FileTypeTab[ff2].name); + noPRINT("GetNameFF(%u,%u) -> '%s' [1+2]\n",ff1,ff2,prefix); + return buf; + } + + noPRINT("GetNameFF(%u,%u) -> '%s' [tab]\n", ff1, ff2, + FileTypeTab[ ff2 && (uint)ff2 < FF_N ? ff2 : (uint)ff1 < FF_N ? ff1 : 0].name ); + return FileTypeTab[ ff2 && (uint)ff2 < FF_N ? ff2 : (uint)ff1 < FF_N ? ff1 : 0].name; +} + +//----------------------------------------------------------------------------- + +ccp GetNameFFv ( file_format_t ff1, file_format_t ff2, int version ) +{ + ccp name = GetNameFF(ff1,ff2); + if ( version > 0 ) + { + char *buf = GetNameBuf(); + snprintf(buf,NAME_BUF_SIZE,"%s.v%u",name,version); + name = buf; + } + return name; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetExtFF ( file_format_t ff1, file_format_t ff2 ) +{ + if ( IsCompressedFF(ff1) && ff1 != FF_BZ2 ) + { + return ff1 == FF_BZ + ? ( ff2 == FF_WU8 ? ".wbz" : ".bz" ) + : (uint)ff2 < FF_N ? FileTypeTab[ff2].ext_compr : ".szs"; + } + + if ( ff1 <= FF_UNKNOWN || ff1 >= FF_N ) + ff1 = ff2; + + return (uint)ff1 < FF_N + ? FileTypeTab[ff1].ext + : ".bin"; +} + +//----------------------------------------------------------------------------- + +ccp GetBinExtFF ( file_format_t ff ) +{ + if ( ff > FF_UNKNOWN && ff < FF_N && FileTypeTab[ff].fform_bin ) + return GetExtFF(0,FileTypeTab[ff].fform_bin); + + return ".bin"; +} + +//----------------------------------------------------------------------------- + +ccp GetTextExtFF ( file_format_t ff ) +{ + if ( ff > FF_UNKNOWN && ff < FF_N && FileTypeTab[ff].fform_txt ) + return GetExtFF(0,FileTypeTab[ff].fform_txt); + + return ".txt"; +} + +//----------------------------------------------------------------------------- + +ccp GetMagicStringFF ( file_format_t ff ) +{ + return ff > FF_UNKNOWN && ff < FF_N && FileTypeTab[ff].magic_len + ? (ccp)(FileTypeTab[ff].magic) + : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +file_format_t GetBinFF ( file_format_t ff ) +{ + return ff > FF_UNKNOWN && ff < FF_N && FileTypeTab[ff].fform_bin + ? FileTypeTab[ff].fform_bin + : FF_UNKNOWN; +} + +//----------------------------------------------------------------------------- + +file_format_t GetTextFF ( file_format_t ff ) +{ + return ff > FF_UNKNOWN && ff < FF_N && FileTypeTab[ff].fform_txt + ? FileTypeTab[ff].fform_txt + : FF_UNKNOWN; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetMagicExtFF ( file_format_t ff1, file_format_t ff2 ) +{ + if (IsCompressedFF(ff1)) + return (uint)ff2 < FF_N ? FileTypeTab[ff2].ext_compr : ".szs"; + + if ( ff1 <= FF_UNKNOWN || ff1 >= FF_N ) + ff1 = ff2; + + return (uint)ff1 < FF_N + ? FileTypeTab[ff1].ext_magic + : ".bin"; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[GetByMagicFF]] + +file_format_t GetByMagicFF +( + const void *data, // pointer to data + uint data_size, // size of data + uint file_size // NULL or total size of file +) +{ + if ( !data || !data_size ) + return FF_UNKNOWN; + + if ( file_size && data_size > file_size ) + data_size = file_size; + if (!file_size) + file_size = data_size; + + const u8 *data8 = (u8*)data; + if ( data_size >= 8 ) + { + if ( IsBZIP2(data,data_size) ) + return FF_BZ2; + + const u64 magic64 = be64(data); + switch(magic64) + { + case BMG_MAGIC8_NUM: return FF_BMG; + case PNG_MAGIC8_NUM: return FF_PNG; + case GCT_MAGIC8_NUM: return FF_GCT; + case LE_LPAR_MAGIC_NUM: return FF_LPAR; + + //--- see below for BOM support + case CT_TEXT_MAGIC8_NUM: return FF_CT_TEXT; + case DISTRIB_MAGIC8_NUM: return FF_DISTRIB; + case ITEMSLT_TEXT_MAGIC_NUM: return FF_ITEMSLT_TXT; + case OBJFLOW_TEXT_MAGIC8_NUM: return FF_OBJFLOW_TXT; + case GH_ITEM_TEXT_MAGIC8_NUM: return FF_GH_ITEM_TXT; + case GH_IOBJ_TEXT_MAGIC8_NUM: return FF_GH_IOBJ_TXT; + case GH_KART_TEXT_MAGIC8_NUM: return FF_GH_KART_TXT; + case GH_KOBJ_TEXT_MAGIC8_NUM: return FF_GH_KOBJ_TXT; + } + } + + if ( data_size >= 4 ) + { + const u32 magic32 = be32(data); + switch(magic32) + { + //--- see below for BOM support + case BMG_TEXT_MAGIC_NUM: return FF_BMG_TXT; + case KCL_TEXT_MAGIC_NUM: return FF_KCL_TXT; + case KMP_TEXT_MAGIC_NUM: return FF_KMP_TXT; + case MDL_TEXT_MAGIC_NUM: return FF_MDL_TXT; + case PAT_TEXT_MAGIC_NUM: return FF_PAT_TXT; + case KMG_TEXT_MAGIC_NUM: return FF_KMG_TXT; + case KRT_TEXT_MAGIC_NUM: return FF_KRT_TXT; + case KRM_TEXT_MAGIC_NUM: return FF_KRM_TXT; + + case YAZ0_MAGIC_NUM: return FF_YAZ0; + case YAZ1_MAGIC_NUM: return FF_YAZ1; + case XYZ0_MAGIC_NUM: return FF_XYZ; + case BZ_MAGIC_NUM: return FF_BZ; + case GCH_MAGIC_NUM: return FF_GCH; + case WCH_MAGIC_NUM: return FF_WCH; + case WPF_MAGIC_NUM: return FF_WPF; + case XPF_MAGIC_NUM: return FF_XPF; + case RKG_MAGIC_NUM: return FF_RKG; + + case KMG_MAGIC_NUM: return FF_KMG; + case KRT_MAGIC_NUM: return FF_KRT; + case KRM_MAGIC_NUM: return FF_KRM; +// [[obj-type+]] 3x +// case OBJFLOW_MAGIC_NUM: return FF_OBJFLOW; +// case GH_ITEM_MAGIC_NUM: return FF_GH_ITEM; +// case GH_KART_MAGIC_NUM: return FF_GH_KART; + + case U8_MAGIC_NUM: return FF_U8; + case WU8_MAGIC_NUM: return FF_WU8; + case RARC_MAGIC_NUM: return FF_RARC; + case RKCT_MAGIC_NUM: return FF_RKC; + case RKCO_MAGIC_NUM: return FF_RKCO; + case PACK_MAGIC_NUM: return FF_PACK; + + case BRRES_MAGIC_NUM: return FF_BRRES; + + case TPL_MAGIC_NUM: return FF_TPL; + case KMP_MAGIC_NUM: return FF_KMP; + case BRASD_MAGIC_NUM: return FF_BRASD; + + case LE_BINARY_MAGIC_NUM: return FF_LE_BIN; + case LEX_BIN_MAGIC_NUM: return FF_LEX; + case LEX_TEXT_MAGIC_NUM: return FF_LEX_TXT; + + case CT0_CODE_MAGIC_NUM: return FF_CT0_CODE; + case CT0_DATA_MAGIC_NUM: return FF_CT0_DATA; + case CT1_CODE_MAGIC_NUM: return FF_CT1_CODE; + case CT1_DATA_MAGIC_NUM: return FF_CT1_DATA; + + case CT_CUP1_MAGIC_NUM: return FF_CUP1; + case CT_CRS1_MAGIC_NUM: return FF_CRS1; + case CT_MOD1_MAGIC_NUM: return FF_MOD1; + case CT_MOD2_MAGIC_NUM: return FF_MOD2; + case CT_OVR1_MAGIC_NUM: return FF_OVR1; + + case BREFF_MAGIC_NUM: + if ( file_size >= 0x20 ) + return data_size < 0x14 || !memcmp(data+0x10,BREFF_MAGIC,4) + ? FF_BREFF : FF_UNKNOWN; + break; + + case BREFT_MAGIC_NUM: + if ( file_size >= 0x20 ) + return data_size < 0x14 || !memcmp(data+0x10,BREFT_MAGIC,4) + ? FF_BREFT : FF_UNKNOWN; + break; + } + + if ( data_size >= 0x20 ) + { + // size => avoid BRSUB.header detection + switch(magic32) + { + case CHR_MAGIC_NUM: return FF_CHR; + case CLR_MAGIC_NUM: return FF_CLR; + case MDL_MAGIC_NUM: return FF_MDL; + case PAT_MAGIC_NUM: return FF_PAT; + case SCN_MAGIC_NUM: return FF_SCN; + case SHP_MAGIC_NUM: return FF_SHP; + case SRT_MAGIC_NUM: return FF_SRT; + case TEX_MAGIC_NUM: + return IsValidTEXCT(data,data_size,0,0,0) < VALID_ERROR + ? FF_TEX_CT : FF_TEX; + } + } + } + + const uint bom_len = GetTextBOMLen(data,data_size); + if ( bom_len > 0 ) + { + if ( data_size >= 8 + bom_len ) + { + const u64 magic64 = be64(data+bom_len); + switch(magic64) + { + case CT_TEXT_MAGIC8_NUM: return FF_CT_TEXT; + case DISTRIB_MAGIC8_NUM: return FF_DISTRIB; + case OBJFLOW_TEXT_MAGIC8_NUM: return FF_OBJFLOW_TXT; +// [[obj-type+]] + case GH_ITEM_TEXT_MAGIC8_NUM: return FF_GH_ITEM_TXT; + case GH_IOBJ_TEXT_MAGIC8_NUM: return FF_GH_IOBJ_TXT; + case GH_KART_TEXT_MAGIC8_NUM: return FF_GH_KART_TXT; + case GH_KOBJ_TEXT_MAGIC8_NUM: return FF_GH_KOBJ_TXT; + } + } + + if ( data_size >= 4 + bom_len ) + { + const u32 magic32 = be32(data+bom_len); + switch(magic32) + { + case BMG_TEXT_MAGIC_NUM: return FF_BMG_TXT; + case KCL_TEXT_MAGIC_NUM: return FF_KCL_TXT; + case KMP_TEXT_MAGIC_NUM: return FF_KMP_TXT; + case MDL_TEXT_MAGIC_NUM: return FF_MDL_TXT; + case PAT_TEXT_MAGIC_NUM: return FF_PAT_TXT; + case KMG_TEXT_MAGIC_NUM: return FF_KMG_TXT; + case KRT_TEXT_MAGIC_NUM: return FF_KRT_TXT; + case KRM_TEXT_MAGIC_NUM: return FF_KRM_TXT; + } + } + } + + + //--- test for FF_OBJFLOW and FF_GH_* + + object_type_t ot; + if (ScanObjType(&ot,data,data_size,file_size)) + { + PRINT("GetByMagicFF(0x%x,0x%x) = %s\n",data_size,file_size,LogObjType(&ot)); + return ot.fform; + } + + //--- test for FF_BREFT_IMG + + if ( data_size >= sizeof(breft_image_t) && !be32(data) ) + { + const breft_image_t * bi = (breft_image_t*)data; + const ImageGeometry_t * geo = GetImageGeometry(bi->iform); + if ( geo && !geo->is_x ) + { + const uint width = be16(&bi->width); + const uint xwidth = ( width + geo->block_width - 1 ) + / geo->block_width * geo->block_width; + const uint height = be16(&bi->height); + const uint xheight = ( height + geo->block_height - 1 ) + / geo->block_height * geo->block_height; + const uint size = be32(&bi->img_size); + noPRINT("RAW IMG FOUND: %02x, %u*%u -> %u*%u, %u/%u\n", + bi->iform, width, height, xwidth, xheight, + xwidth * xheight * geo->bits_per_pixel / 8, size ); + if ( xwidth > 0 && xheight > 0 + && xwidth * xheight * geo->bits_per_pixel <= 8 * size ) + { + return FF_BREFT_IMG; + } + } + } + + if ( data_size > 3 && data8[1] == 12 && data8[2] == 19 ) + { + // mabye ItemSlot + if ( data8[0] == 6 && file_size == ITEMSLT_SIZE6 + || data8[0] == 12 + && ( file_size == ITEMSLT_SIZE12 || file_size == ITEMSLT_SIZE6 )) + { + return FF_ITEMSLT; + } + } + + if ( data_size > 4 && file_size == 4+27*396 && be32(data8) == 27 ) + return FF_DRIVER; + + if ( data_size > 4 && file_size == 4+36*396 && be32(data8) == 36 ) + return FF_VEHICLE; + + if (IsDolHeader(data,data_size,0)) + return FF_DOL; + + if ( IsValidKCL(0,data,data_size,file_size,0) < VALID_ERROR ) + return FF_KCL; + + if ( IsValidCTCODE(data,data_size,0,0) < VALID_ERROR ) + return FF_CT1_DATA; + + // test FF_WAV_OBJ & FF_SKP_OBJ + { + ccp ptr = data, end = ptr + data_size; + for(;;) + { + while ( ptr < end && ( *ptr == ' ' || *ptr == '\t' + || *ptr == '\r' || *ptr == '\n' )) + ptr++; + + if ( *ptr == 'g' ) + { + // ignore group commands + while ( ptr < end && *ptr != '\n' ) + ptr++; + continue; + } + + if ( *ptr != '#' ) + break; + + bool have_obj = false; + bool have_wavefront = false; + bool have_model = false; + while ( ptr < end && *ptr != '\n' ) + { + have_obj = have_obj || !strncasecmp(ptr,"obj",3); + have_wavefront = have_wavefront || !strncasecmp(ptr,"wavefront",9); + have_model = have_model || !strncasecmp(ptr,"model",5); + + if ( have_obj && have_wavefront ) + return FF_WAV_OBJ; + if ( have_obj && have_model ) + return FF_SKP_OBJ; + ptr++; + } + } + + if ( ( !memcmp(ptr,"mtllib",6) || !memcmp(ptr,"usemtl",6) ) + && ( ptr[6] == ' ' || ptr[6] == '\t' )) + { + return FF_WAV_OBJ; + } + + if ( *ptr == 'v' && ptr[1] == ' ' ) + { + ptr += 2; + int i; + bool ok = true; + for ( i = 0; i < 3 && ok; i++ ) + { + char *endscan; + strtod_comma(ptr,&endscan); + ok = endscan > ptr; + ptr = endscan; + } + while ( ptr < end && ( *ptr == ' ' || *ptr == '\r' )) + ptr++; + if ( ptr < end && *ptr == '\n' ) + return FF_WAV_OBJ; + } + } + + if ( IsValidSTATICR(data,data_size,0,0) < VALID_ERROR ) + return FF_STATICR; + + if ( IsValidBTI(data,data_size,0,0) < VALID_ERROR ) + return FF_BTI; + + + //--- test for scripts + + ccp beg = data + bom_len; + ccp end = data + data_size; + if ( beg < end ) + { + bool is_text = false; + ccp ptr = beg; + while ( ptr < end ) + { + const u8 ch = *ptr++; + if ( ch == '\r' || ch == '\n' ) + { + is_text = true; + ptr--; + break; + } + + if ( ch < ' ' && ch != '\f' && ch != '\v' && ch != '\t' ) + break; + } + + if (is_text) + { + while ( ptr > beg && ptr[-1] <= ' ' ) + ptr--; + ccp name = ptr; + while ( name > beg && isalnum((int)(name[-1])) ) + name--; + noPRINT("|%.*s|%.*s|\n",(int)(name-beg),beg,(int)(ptr-name),name); + if (!memcmp(beg,"#!",2)) + { + switch( ptr - name ) + { + case 2: + if (!strncasecmp(name,"sh",2)) return FF_SH; + break; + + case 3: + if (!strncasecmp(name,"php",3)) return FF_PHP; + break; + + case 4: + if (!strncasecmp(name,"bash",4)) return FF_BASH; + break; + + case 7: + if (!strncasecmp(name,"makedoc",7)) return FF_MAKEDOC; + break; + } + return FF_SCRIPT; + } + if ( !memcmp(beg,"name; p++ ) + if ( ( !need_magic || p->magic_len ) + && p->ext && !strcasecmp(ext,p->ext+1) ) + return p; + + // second: try member 'ext_compr' + for ( p = FileTypeTab; p->name; p++ ) + if ( ( !need_magic || p->magic_len ) + && p->ext_compr && !strcasecmp(ext,p->ext_compr+1) ) + return p; + + // third: try member 'ext_magic' + for ( p = FileTypeTab; p->name; p++ ) + if ( ( !need_magic || p->magic_len ) + && p->ext_magic && !strcasecmp(ext,p->ext_magic+1) ) + return p; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[GetFileTypeByExt]] + +const file_type_t * GetFileTypeBySubdir +( + ccp path, // NULL or file extension with optional preceeding point + bool need_magic // filter only file types with magic +) +{ + if (!path) + return 0; + + ccp p1 = 0, p2 = 0; + for(;;) + { + while ( *path == '/' ) + path++; + + ccp end = strchr(path,'/'); + if (!end) + break; + p1 = path; + p2 = end; + path = p2; + } + + if (p1) + { + DASSERT(p2); + + char name[20]; + const uint len = p2 - p1; + if ( len < sizeof(name) ) + { + memcpy(name,p1,len); + name[len] = 0; + noPRINT("### %s : %s\n",name,p1); + + const file_type_t *p; + for ( p = FileTypeTab; p->name; p++ ) + if ( ( !need_magic || p->magic_len ) + && p->subdir && !strcasecmp(name,p->subdir) ) + return p; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[GetByNameFF]] + +file_format_t GetByNameFF ( ccp name ) +{ + const KeywordTab_t * cmd = ScanKeyword(0,name,cmdtab_FileType); + return cmd ? cmd->id : FF_UNKNOWN; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +file_format_t IsImageFF +( + // returns the normalized FF for a valid image or NULL (=FF_UNKNOWN) + + file_format_t fform, // file format to check + bool allow_png // true: allow PNG +) +{ + switch (fform) + { + case FF_TPL: + case FF_BTI: + case FF_TEX: + case FF_TEX_CT: + case FF_BREFT_IMG: + return fform; + + case FF_BREFT: + return FF_BREFT_IMG; + + case FF_PNG: + return allow_png ? FF_PNG : FF_UNKNOWN; + + default: + return FF_UNKNOWN; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +file_format_t GetImageFF +( + file_format_t fform1, // if a valid image ff, use this one + file_format_t fform2, // if a valid image ff, use this one + ccp path, // not NULL and exists: use file format + file_format_t default_fform, // use this as default, if valid ff + bool allow_png, // true: allow PNG + file_format_t not_found // return this, if all other fails +) +{ + PRINT("GetImageFF(%s,%s,%s,%d,%s) %s\n", + GetNameFF(0,fform1), GetNameFF(0,fform2), GetNameFF(0,default_fform), + allow_png, GetNameFF(0,not_found), path ? path : "-" ); + + //--- analyze 'fform*' + + fform1 = IsImageFF(fform1,allow_png); + if ( fform1 != FF_UNKNOWN ) + return fform1; + + fform2 = IsImageFF(fform2,allow_png); + if ( fform2 != FF_UNKNOWN ) + return fform2; + + + //--- analyse path + + if ( path && *path ) + { + //--- analyse file content + + char buf[CHECK_FILE_SIZE]; + memset(buf,0,sizeof(buf)); + enumError err = LoadFILE(path,0,0,buf,sizeof(buf),2,0,false); + if (!err) + { +// [[analyse-magic]] + const file_format_t fform + = IsImageFF( GetByMagicFF(buf,sizeof(buf),0), allow_png ); + if ( fform != FF_UNKNOWN ) + return fform; + } + + //--- analyse file extension + + ccp point = strrchr(path,'.'); + if (point) + { + const file_format_t fform = IsImageFF(GetByNameFF(point+1),true); + if ( fform != FF_UNKNOWN ) + return fform; + } + + //--- analyse last directory + + ccp p1 = 0, p2 = path, ptr = path; + while (*ptr) + { + if ( *ptr++ == '/' || *ptr++ == '\\' ) + { + p1 = p2; + p2 = ptr; // position behind '/' + } + } + if (p1) + { + if (!strncasecmp(p1,"files/",6)) + return FF_BREFT_IMG; + if (!strncasecmp(p1,"Textures(NW4R)/",15)) + return FF_TEX; + } + } + + + //--- use default + + default_fform = IsImageFF(default_fform,allow_png); + return default_fform != FF_UNKNOWN ? default_fform : not_found; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// object name/type /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetNewObjectName ( new_object_t no ) +{ + switch (no) + { + case NEWOBJ_OFF: return "OFF"; + case NEWOBJ_REPLACE: return "REPLACE"; + case NEWOBJ_SHRINK: return "SHRING"; + case NEWOBJ_GROW: return "GROW"; + default: return "?"; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeObjType ( object_type_t *ot, file_format_t fform ) +{ + DASSERT(ot); + memset(ot,0,sizeof(*ot)); + + ot->new_object = NEWOBJ_OFF; + ot->object_slots = OBJMGR_USUAL_SLOTS; + ot->max_object_id = OBJMGR_USUAL_MAX_OBJID; + + SetObjTypeByFF(ot,fform); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetObjTypeByFF ( object_type_t *ot, file_format_t fform ) +{ + DASSERT(ot); + + switch(fform) + { + case FF_OBJFLOW: + case FF_OBJFLOW_TXT: + ot->is_objflow = true; + ot->is_geohit = false; + ot->is_kart = false; + ot->is_obj = false; + ot->row_size = 0x74; + ot->slot_offset = 2; + break; + + case FF_GH_ITEM: + case FF_GH_ITEM_TXT: + ot->is_objflow = false; + ot->is_geohit = true; + ot->is_kart = false; + ot->is_obj = false; + ot->row_size = 0x20; + ot->slot_offset = 4; + break; + + case FF_GH_IOBJ: + case FF_GH_IOBJ_TXT: + ot->is_objflow = false; + ot->is_geohit = true; + ot->is_kart = false; + ot->is_obj = true; + ot->row_size = 0x20; + ot->slot_offset = 4; + break; + + case FF_GH_KART: + case FF_GH_KART_TXT: + ot->is_objflow = false; + ot->is_geohit = true; + ot->is_kart = true; + ot->is_obj = false; + ot->row_size = 0x0a; + ot->slot_offset = 4; + break; + + case FF_GH_KOBJ: + case FF_GH_KOBJ_TXT: + ot->is_objflow = false; + ot->is_geohit = true; + ot->is_kart = true; + ot->is_obj = true; + ot->row_size = 0x0a; + ot->slot_offset = 4; + break; + + default: + ot->is_objflow = false; + ot->is_geohit = false; + ot->is_kart = false; + ot->is_obj = false; + ot->row_size = 0; + ot->slot_offset = 0; + break; + } + + ot->ref_offset = ot->slot_offset + ot->object_slots * ot->row_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ScanObjType + ( object_type_t *ot, cvp data, uint data_size, uint file_size ) +{ + //--- check for valid data + + DASSERT(ot); + memset(ot,0,sizeof(*ot)); + if ( !data || data_size < 4 || file_size&1 ) + return false; + + const uint n_slot = be16(data); + if ( !n_slot || n_slot > OBJMGR_MAX_SLOTS ) + return false; + + + //--- determine possible file types + + file_format_t ff_std, ff_obj; + + const uint n_param = be16((u8*)data+2); + switch (n_param) + { + case 4: + ff_std = FF_GH_KART; + ff_obj = FF_GH_KOBJ; + ot->is_geohit = true; + ot->is_kart = true; + ot->row_size = 0x0a; + ot->slot_offset = 4; + break; + + case 15: + ff_std = FF_GH_ITEM; + ff_obj = FF_GH_IOBJ; + ot->is_geohit = true; + ot->row_size = 0x20; + ot->slot_offset = 4; + break; + + default: + ff_std = FF_OBJFLOW; + ff_obj = FF_UNKNOWN; + ot->is_objflow = true; + ot->row_size = 0x74; + ot->slot_offset = 2; + break; + } + + int max_ref_obj_id = OBJMGR_MAX_OBJID; + ot->ref_offset = ot->slot_offset + n_slot * ot->row_size; + if (file_size) + { + if ( file_size < ot->ref_offset + 2 ) + return false; + + max_ref_obj_id = ( file_size - ot->ref_offset ) / 2 - 1; + if ( max_ref_obj_id > OBJMGR_MAX_OBJID ) + max_ref_obj_id = OBJMGR_MAX_OBJID; + } + + + //--- determine max_object_id + + const u8 *bin, *bin0 = (u8*)data + ot->slot_offset; + uint slots_avail = ( (u8*)data + data_size - bin0 ) / ot->row_size; + if ( slots_avail > n_slot ) + slots_avail = n_slot; + + uint slot, max_obj_id = 0; + for ( slot = 0, bin = bin0; slot < slots_avail; slot++, bin += ot->row_size ) + { + uint obj_id = be16(bin); + if ( !obj_id || obj_id > max_ref_obj_id ) + return false; + + if ( max_obj_id < obj_id ) + max_obj_id = obj_id; + } + + ot->object_slots = n_slot; + ot->max_object_id = max_ref_obj_id; + + + //--- determine is_object + + if (ot->is_geohit) + { + if (ot->is_kart) + { + uint n7x = 0, n36 = 0; + for ( slot = 0, bin = bin0; slot < slots_avail; slot++, bin += ot->row_size ) + { + const geohit_bin_t *gb = (geohit_bin_t*)bin; + uint ip; + for ( ip = 0; ip < n_param; ip++ ) + { + const u16 p = ntohs(gb->param[ip]); + if ( p >= 7 ) + n7x++; + else if ( p >= 3 && p <= 6 ) + n36++; + } + } + ot->is_obj = n7x < n36; + PRINT("KART: N7x=%u, N36=%u -> obj=%u\n",n7x,n36,ot->is_obj); + } + else + { + uint n12 = 0, n36 = 0; + for ( slot = 0, bin = bin0; slot < slots_avail; slot++, bin += ot->row_size ) + { + const geohit_bin_t *gb = (geohit_bin_t*)bin; + uint ip; + for ( ip = 0; ip < n_param; ip++ ) + { + const u16 p = ntohs(gb->param[ip]); + if ( p >= 1 && p <= 2 ) + n12++; + else if ( p >= 3 && p <= 6 ) + n36++; + } + } + ot->is_obj = n12 < n36; + PRINT("ITEM: N12=%u, N36=%u -> obj=%u\n",n12,n36,ot->is_obj); + } + } + + + //--- terminate + + ot->fform = ot->is_obj ? ff_obj : ff_std; + + PRINT("ScanObjType(0x%x,0x%x) = %s\n", + data_size, file_size, LogObjType(ot) ); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp LogObjType ( const object_type_t *ot ) +{ + DASSERT(ot); + if (!ot) + return "?"; + + char buf[100]; + const uint len + = snprintf(buf,sizeof(buf), + "%s[%s%s%s%s] rs=%u off:%x:%x %s,ns:%x,mo:%x", + GetNameFF(0,ot->fform), + ot->is_objflow ? "O" : "", + ot->is_geohit ? "G" : "", + ot->is_kart ? "K" : ot->is_objflow ? "" : "I", + ot->is_obj ? "o" : "", + ot->row_size, + ot->slot_offset, + ot->ref_offset, + GetNewObjectName(ot->new_object), + ot->object_slots, + ot->max_object_id ); + + return CopyCircBuf(buf,len+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +file_format_t GetObjTypeFF ( const object_type_t *ot, bool want_text ) +{ + DASSERT(ot); + if (ot->is_objflow) + return want_text ? FF_OBJFLOW_TXT : FF_OBJFLOW; + + if (ot->is_geohit) + { + if (ot->is_kart) + { + return ot->is_obj + ? ( want_text ? FF_GH_KOBJ_TXT : FF_GH_KOBJ ) + : ( want_text ? FF_GH_KART_TXT : FF_GH_KART ); + } + else + { + return ot->is_obj + ? ( want_text ? FF_GH_IOBJ_TXT : FF_GH_IOBJ ) + : ( want_text ? FF_GH_ITEM_TXT : FF_GH_ITEM ); + } + } + + return FF_UNKNOWN; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// file class /////////////// +/////////////////////////////////////////////////////////////////////////////// + +patch_file_t PATCH_FILE_MODE = PFILE_M_DEFAULT | PFILE_F_DEFAULT; +int disable_patch_on_load = 0; // if >0: disable patching after loading + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_patch_tab[] = +{ + { 0, "CLEAR", "RESET", PFILE_M_ALL | PFILE_F_HIDE }, + { PFILE_M_DEFAULT, "DEFAULT", 0, PFILE_M_ALL | PFILE_F_HIDE }, + { PFILE_M_ALL, "ALL", 0, PFILE_M_ALL | PFILE_F_HIDE }, + { PFILE_F_LOG, "LOG", 0, 0 }, + { PFILE_F_LOG_ALL, "LOG-ALL", "LOGALL", 0 }, + + { PFILE_COURSE_KMP, "KMP", 0, 0 }, + + { PFILE_M_KCL, "ALL-KCL", "ALLKCL", PFILE_M_KCL }, + { PFILE_COURSE_KCL, "KCL", 0, 0 }, + { PFILE_OTHER_KCL, "OTHER-KCL", "OTHERKCL", 0 }, + + { PFILE_M_BRRES, "ALL-BRRES", "ALLBRRES", PFILE_M_BRRES }, + { PFILE_MODEL, "MODEL", 0, 0 }, + { PFILE_MINIMAP, "MINIMAP", "MAP", 0 }, + { PFILE_VRCORN, "VRCORN", 0, 0 }, + { PFILE_OTHER_BRRES, "OTHER-BRRES", "OTHERBRRES", 0 }, + + { PFILE_M_TRACK, "TRACK", 0, PFILE_M_TRACK | PFILE_F_HIDE }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPatchFile ( ccp arg ) +{ + if (!arg) + return 0; + + static bool done = false; + if (!done) + { + done = true; + if ( *arg != '+' && *arg != '-' ) + PATCH_FILE_MODE = 0; + } + + s64 stat = ScanKeywordList(arg,opt_patch_tab,0,true,0,PATCH_FILE_MODE, + "Option --patch-file",ERR_SYNTAX); + if ( stat != -1 ) + { + PATCH_FILE_MODE = stat & PFILE_M_ALL; + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetPatchFileModeReadonly() +{ + // switch to readonly default flags (silent), if not set by user + + if ( PATCH_FILE_MODE & PFILE_F_DEFAULT ) + PATCH_FILE_MODE = PFILE_M_SILENT; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintFileClassInfo ( char *buf, uint bufsize, patch_file_t mode ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + char *dest = buf; + char *end = buf + bufsize - 1; + + mode = mode & PFILE_M_ALL | PFILE_F_HIDE; + patch_file_t mode1 = mode; + + const KeywordTab_t *ct; + for ( ct = opt_patch_tab; ct->name1 && dest < end; ct++ ) + { + if ( ct->opt & PFILE_F_HIDE ) + continue; + + if ( ct->opt ? (mode & ct->opt) == ct->id : mode & ct->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,ct->name1); + mode &= ~(ct->id|ct->opt); + } + } + + if ( mode1 == (PFILE_M_DEFAULT|PFILE_F_HIDE) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + return dest-buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFileClassInfo() +{ + static char buf[100] = {0}; + if (!*buf) + PrintFileClassInfo(buf,sizeof(buf),PATCH_FILE_MODE); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +patch_file_t GetFileClass +( + file_format_t fform, // not FF_UNKNOWN: check file type + ccp fname // NULL or file name to check +) +{ + if ( !fname || !*fname ) + return PFILE_NO_FILENAME; + + ccp slash = strrchr(fname,'/'); + if (slash) + fname = slash+1; + + if ( fform == FF_UNKNOWN && !strcasecmp(fname,"course.txt") ) + return PFILE_COURSE; + + if ( fform == FF_UNKNOWN || fform == FF_KMP || fform == FF_KMP_TXT ) + { + if ( !strcasecmp(fname,"course.kmp") + || !strcasecmp(fname,"course.txt") + || !strcasecmp(fname,"course.kmp.txt") ) + { + return PFILE_COURSE_KMP; + } + } + + if ( fform == FF_UNKNOWN || fform == FF_KCL || fform == FF_KCL_TXT ) + { + if ( !strcasecmp(fname,"course.kcl") + || !strcasecmp(fname,"course.txt") + || !strcasecmp(fname,"course.kcl.txt") ) + { + return PFILE_COURSE_KCL; + } + + if ( fform == FF_KCL || fform == FF_KCL_TXT ) + return PFILE_OTHER_KCL; + } + + if ( fform == FF_UNKNOWN || fform == FF_BRRES ) + { + if ( !strcasecmp(fname,"course_model.brres") + || !strcasecmp(fname,"course_d_model.brres") + || !strcasecmp(fname,"course_model.d") + || !strcasecmp(fname,"course_d_model.d") ) + { + return PFILE_MODEL; + } + + if ( !strcasecmp(fname,"map_model.brres") + || !strcasecmp(fname,"map_model.d") ) + { + return PFILE_MINIMAP; + } + + if ( !strcasecmp(fname,"vrcorn_model.brres") + || !strcasecmp(fname,"vrcorn_model.d") ) + { + return PFILE_VRCORN; + } + + if ( fform == FF_BRRES ) + return PFILE_OTHER_BRRES; + } + + return PFILE_UNKNOWN; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchFileClass +( + file_format_t fform, // not FF_UNKNOWN: check file type + ccp fname // NULL or file name to check +) +{ + const patch_file_t fc = GetFileClass(fform,fname); + return 0 != ( fc & PATCH_FILE_MODE ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int patch_action_log_disabled = 0; + +bool PATCH_ACTION_LOG ( ccp action, ccp object, ccp format, ... ) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[PATCH]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + + if ( patch_action_log_disabled <= 0 + && ( verbose > 2 || PATCH_FILE_MODE & (PFILE_F_LOG|PFILE_F_LOG_ALL) )) + { + fflush(stdout); + fprintf(stdlog," %s>[PATCH] %-6s %-7s%s ", + colset->heading, action, object, colset->info ); + if (format) + { + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + } + else + fprintf(stdlog,"%s\n",colset->reset); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// auto add support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool SetupAutoAdd() +{ + static bool done = false; + if (!done) + { + done = true; + + ccp *spath = search_path; + ccp *apath = auto_add_path; + while (*spath) + { + char path_buf[PATH_MAX]; + ccp path = PathCatPP(path_buf,sizeof(path_buf),*spath++,"auto-add"); + if (IsDirectory(path,0)) + { + PRINT("AUTO-PATH[%u] = %s\n",(int)(apath-auto_add_path),path); + *apath++ = STRDUP(path); + DASSERT( apath - auto_add_path + < sizeof(auto_add_path)/sizeof(*auto_add_path) ); + } + } + *apath = 0; + } + + return auto_add_path[0] != 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DefineAutoAddPath ( ccp path ) +{ + SetupAutoAdd(); + if (path) + { + uint i; + for ( i = 0; i < sizeof(auto_add_path)/sizeof(*auto_add_path); i++ ) + { + FreeString(auto_add_path[i]); + auto_add_path[i] = 0; + } + auto_add_path[0] = STRDUP(path); + } + + return auto_add_path[0] != 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +s64 FindAutoAdd ( ccp fname, ccp ext, char *buf, uint buf_size ) +{ + DASSERT(fname); + DASSERT(buf); + DASSERT( buf_size > 100 ); + + if (!SetupAutoAdd()) + return -1; + + ccp *apath = auto_add_path; + while (*apath) + { + PathCatBufPPE(buf,buf_size,*apath++,fname,ext); + struct stat st; + if ( !stat(buf,&st) && S_ISREG(st.st_mode) ) + { + noPRINT("FindAutoAdd(%s) -> %llu %s\n",fname,(u64)st.st_size,buf); + return st.st_size; + } + } + + noPRINT("FindAutoAdd(%s) FAILED\n",fname); + *buf = 0; + return -1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// file format atribs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const file_type_t * GetFileTypeFF ( file_format_t ff ) +{ + return (uint)ff < FF_N ? FileTypeTab + ff : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ff_attrib_t GetAttribFF ( file_format_t ff ) +{ + return (uint)ff < FF_N + ? FileTypeTab[ff].attrib + : (ff_attrib_t)0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ff_attrib_t GetAttribByMagicFF +( + const void * data, // valid pointer to data + uint data_size, // size of 'data' + file_format_t *ff // not NULL: store file format here +) +{ +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,data_size,0); + if (ff) + *ff = fform; + return GetAttribFF(fform); +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetVersionFF +( + // returns -1 for unknown, or the version number + + file_format_t fform, // valid firl format + const void *data, // data pointer + uint data_size, // size of available data + char *res_suffix // not NULL: return 1 char as suffix, or 0 +) +{ + if (res_suffix) + *res_suffix = 0; + + switch(fform) + { + #if 0 + case FF_YAZ0: + case FF_XYZ: + return 0; + + case FF_YAZ1: + return 1; + + case FF_BZ: + return ((u8*)data)[3] - 'a' + 1; + #endif + + case FF_BREFF: + case FF_BREFT: + if ( data_size >= 0x08 ) + return be16((u8*)data+0x06); + break; + + case FF_CHR: + case FF_CLR: + case FF_MDL: + case FF_PAT: + case FF_SCN: + case FF_SHP: + case FF_SRT: + case FF_TEX: + case FF_TEX_CT: // ??? [[CTCODE]] + if ( data_size >= 0x0c ) + return be32((u8*)data+0x08); + break; + + #if 0 + case FF_KMP: + if ( data_size >= 0x10 ) + return be32((u8*)data+0x0c); + break; + #endif + + case FF_BMG: // use encoding + if ( data_size >= sizeof(bmg_header_t) ) + { + const bmg_header_t *bh = (bmg_header_t*)data; + return ntohl(bh->n_sections)*10 + bh->encoding; + } + break; + + case FF_LE_BIN: + { + const int vers = be32((u8*)data+4); + if (res_suffix) + { + if ( vers == 4 && data_size >= sizeof(le_binary_head_v4_t) ) + { + const le_binary_head_v4_t *leh = (le_binary_head_v4_t*)data; + *res_suffix = leh->debug == 'R' + ? toupper(leh->region) + : tolower(leh->region); + } + } + return vers; + } + break; + + default: + //printf("x[%u]\n",fform); + { + const ff_attrib_t att = GetAttribFF(fform); + if ( att & FFT_BRSUB && data_size >= 0x0c ) + return be32((u8*)data+0x08); + } + break; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetVersionByMagic +( + // returns -1 for unknown, or the version number + + const void *data, // data pointer + uint data_size, // size of available data + char *res_suffix // not NULL: return 1 char as suffix, or 0 +) +{ +// [[analyse-magic]] + return GetVersionFF( GetByMagicFF(data,data_size,0), + data, data_size, res_suffix ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool IsBRSUB ( file_format_t ff ) +{ + return (uint)ff < FF_N && FileTypeTab[ff].attrib & FFT_BRSUB; +} + +//----------------------------------------------------------------------------- + +bool IsByMagicBRSUB ( const void * data, uint data_size ) +{ + return GetAttribByMagicFF(data,data_size,0) & FFT_BRSUB; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsArchiveFF ( file_format_t ff ) +{ + return (uint)ff < FF_N && FileTypeTab[ff].attrib & FFT_ARCHIVE; +} + +//----------------------------------------------------------------------------- + +bool IsTextFF ( file_format_t ff ) +{ + return (uint)ff < FF_N && FileTypeTab[ff].attrib & FFT_TEXT; +} + +//----------------------------------------------------------------------------- + +bool IsGeoHitKartFF ( file_format_t ff ) +{ + return ff == FF_GH_KART + || ff == FF_GH_KART_TXT + || ff == FF_GH_KOBJ + || ff == FF_GH_KOBJ_TXT; +} + +//----------------------------------------------------------------------------- + +bool IsGeoHitObjFF ( file_format_t ff ) +{ + return ff == FF_GH_IOBJ + || ff == FF_GH_IOBJ_TXT + || ff == FF_GH_KOBJ + || ff == FF_GH_KOBJ_TXT; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsYazFF ( file_format_t ff ) +{ + return ff == FF_YAZ0 || ff == FF_YAZ1 || ff == FF_XYZ; +} + +//----------------------------------------------------------------------------- + +int GetYazVersionFF ( file_format_t ff ) +{ + switch (ff) + { + case FF_YAZ0: + case FF_XYZ: + return 0; + + case FF_YAZ1: + return 1; + + default: + return -1; + } +} + +//----------------------------------------------------------------------------- + +file_format_t GetYazFF ( file_format_t ff ) +{ + return fform_compr_force && ( ff == FF_YAZ0 || ff == FF_YAZ1 || ff == FF_XYZ ) + ? fform_compr_force + : IsYazFF(ff) + ? ff + : FF_YAZ0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsCompressedFF ( file_format_t ff ) +{ + return (uint)ff < FF_N && FileTypeTab[ff].attrib & FFT_COMPRESS; +} + +//----------------------------------------------------------------------------- + +file_format_t GetCompressedFF ( file_format_t ff ) +{ + return fform_compr_force + && (uint)ff < FF_N + && FileTypeTab[ff].attrib & FFT_COMPRESS + ? fform_compr_force + : ff; +} + +//----------------------------------------------------------------------------- + +void SetCompressionFF +( + file_format_t ff_arch, // if >0: set 'opt_fform' + file_format_t ff_compr // if >0: set 'fform_compr' + // and 'fform_compr_force' +) +{ + if ( ff_arch > 0 ) + opt_fform = ff_arch; + + if ( ff_compr > 0 ) + fform_compr = fform_compr_force = ff_compr; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan setup files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +size_t ResetSetup +( + SetupDef_t * list // object list terminated with an element 'name=NULL' +) +{ + DASSERT(list); + size_t count; + for ( count = 0; list->name; list++, count++ ) + { + FreeString(list->param); + list->param = 0; + list->value = 0; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static char * trim_line ( char * ptr, char ** begin ) +{ + DASSERT(ptr); + + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + + char *start = ptr; + if (begin) + *begin = ptr; + + while (*ptr) + ptr++; + + ptr--; + while ( *ptr > 0 && *ptr <= ' ' && ptr > start ) + ptr--; + *++ptr = 0; + return ptr; +} + +//----------------------------------------------------------------------------- + +enumError ScanSetupFile +( + SetupDef_t * sdef, // object list terminated with an element 'name=NULL' + ListDef_t * ldef, // NULL or object list terminated + // with an element 'name=NULL' + ccp path1, // filename of text file, part 1 + ccp path2, // filename of text file, part 2 + SubDir_t * sdir, // not NULL: use it and ignore 'path1' + bool silent // true: suppress error message if file not found +) +{ + DASSERT(sdef); + DASSERT(path1||path2); + + ResetSetup(sdef); + + char pathbuf[PATH_MAX]; + ccp path; + FILE * f; + + if (sdir) + { + path = path2; + f = OpenSubFile(sdir,MemByString(path2)); + } + else + { + path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + f = fopen(path,"rb"); + } + + if (!f) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path); + return ERR_CANT_OPEN; + } + + bool section_found = false; + uint linenum = 0; + while (fgets(iobuf,sizeof(iobuf)-1,f)) + { + char * ptr = iobuf; + + //----- check BOM + + if (!linenum++) + ptr += GetTextBOMLen(ptr,3); + + //----- skip spaces + + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + + if ( *ptr == '!' || *ptr == '#' ) + continue; + + if ( *ptr == '[' ) + { + ptr = trim_line(ptr,0); + if ( ptr[-1] == ']' ) + { + section_found = true; + break; + } + continue; + } + + //----- find end of name + + char * name = ptr; + while ( isalnum((int)*ptr) || *ptr == '-' ) + ptr++; + if (!*ptr) + continue; + + char * name_end = ptr; + + //----- skip spaces and check for '=' + + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + + if ( *ptr != '=' ) + continue; + + *name_end = 0; + + //----- check if name is a known parameter + + SetupDef_t * item; + for ( item = sdef; item->name; item++ ) + if (!strcmp(item->name,name)) + break; + if (!item->name) + continue; + + //----- trim parameter + + ptr++; // skip '=' + char * param; + ptr = trim_line(ptr,¶m); + + item->param = STRDUP(param); + if (item->factor) + { + ScanSizeU64(&item->value,param,1,1,0); + if ( item->factor > 1 ) + item->value = item->value / item->factor * item->factor; + } + } + + while ( section_found && ldef ) + { + char * beg; + char * ptr = trim_line(iobuf,&beg) - 1; + if ( *beg != '[' || *ptr != ']' ) + break; + + *ptr = 0; + beg++; + TRACE("SECTION |%s|\n",beg); + StringField_t *sf = 0; + bool append = false; + ListDef_t * ld; + for ( ld = ldef; ld->name; ld++ ) + if (!strcmp(beg,ld->name)) + { + sf = ld->sf; + append = ld->append; + break; + } + + while (fgets(iobuf,sizeof(iobuf)-1,f)) + { + char * ptr = trim_line(iobuf,&beg); + if ( *beg == '[' ) + { + if ( ptr[-1] == ']' ) + break; + continue; + } + + if ( *beg == '.' && beg[1] == '/' ) + beg += 2; + if ( !sf || !*beg || *beg == '#' ) + continue; + TRACE("INSERT |%s|\n",beg); + if (append) + AppendStringField(sf,beg,false); + else + InsertStringField(sf,beg,false); + } + } + + fclose(f); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SetupParam_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSetupParam ( SetupParam_t * sp ) +{ + DASSERT(sp); + memset(sp,0,sizeof(*sp)); + + InitializeStringField(&sp->include_pattern); + InitializeStringField(&sp->include_name); + InitializeStringField(&sp->exclude_pattern); + InitializeStringField(&sp->exclude_name); + InitializeFormatField(&sp->encode_list); + InitializeStringField(&sp->create_list); + InitializeFormatField(&sp->order_list); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSetupParam ( SetupParam_t * sp ) +{ + if (sp) + { + ResetStringField(&sp->include_pattern); + ResetStringField(&sp->include_name); + ResetStringField(&sp->exclude_pattern); + ResetStringField(&sp->exclude_name); + ResetFormatField(&sp->encode_list); + ResetStringField(&sp->create_list); + ResetFormatField(&sp->order_list); + FreeString(sp->object_name); + + InitializeSetupParam(sp); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupParamOptions +( + SetupParam_t * sp // setup param to scan +) +{ + //--- use options? + + if ( opt_fform != FF_UNKNOWN ) + sp->fform_arch = opt_fform; + if ( opt_compr_mode ) + { + sp->compr_mode = opt_compr_mode; + if ( opt_compr_mode < 0 ) + sp->fform_file = FF_UNKNOWN; + } + + if ( sp->fform_arch == FF_UNKNOWN ) + sp->fform_arch = FF_U8; + if ( sp->fform_file == FF_UNKNOWN ) + sp->fform_file = opt_compr_mode < 0 ? sp->fform_arch : fform_compr; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanSetupParam +( + SetupParam_t * sp, // setup param to scan + bool use_options, // true: use options to override values + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + SubDir_t * sdir, // not NULL: use it and ignore 'path1' + bool silent // true: suppress printing of error messages +) +{ + DASSERT(sp); + memset(sp,0,sizeof(*sp)); + + SetupDef_t setup_def[] = + { + { "object-name", 0 }, + { "object-id", 1 }, + { "archive-format", 0 }, + { "file-format", 0 }, + { "version", 1 }, + { "have-pt-dir", 1 }, + { "min-data-offset", 1 }, + { "max-data-offset", 1 }, + { "data-align", 1 }, + {0,0} + }; + + if (!opt_ignore_setup) + { + StringField_t encode_helper, order_helper; + InitializeStringField(&encode_helper); + InitializeStringField(&order_helper); + + ListDef_t list_def[] = + { + { "include-pattern", 0, &sp->include_pattern }, + { "include", 0, &sp->include_name }, + { "exclude-pattern", 0, &sp->exclude_pattern }, + { "exclude", 0, &sp->exclude_name }, + { "encode", 0, &encode_helper }, + { "create", 0, &sp->create_list }, + { "order", 1, &order_helper }, + {0,0} + }; + + enumError err = ScanSetupFile(setup_def,list_def,path1,path2,sdir,silent); + if (err) + { + if (use_options) + SetupParamOptions(sp); + return err; + } + + ccp *ptr = encode_helper.field, *end; + for ( end = ptr + encode_helper.used; ptr < end; ptr++ ) + { + InsertFormatField(&sp->encode_list,*ptr,true,true,0); + *ptr = 0; + } + ResetStringField(&encode_helper); + + ptr = order_helper.field; + for ( end = ptr + order_helper.used; ptr < end; ptr++ ) + { + AppendFormatField(&sp->order_list,*ptr,true,true); + *ptr = 0; + } + ResetStringField(&order_helper); + } + + + //--- object-name + + SetupDef_t * sd = setup_def; + noPRINT("object-name=%s\n",sd->param); + if (sd->param) + { + sp->object_name = sd->param; + sd->param = 0; + } + + //--- object-id + + sd++; + noPRINT("object-id=%s\n",sd->param); + if (sd->param) + { + sp->object_id_used = true; + sp->object_id = sd->value; + } + + //--- archive-format + + sd++; + noPRINT("ff-arch=%s\n",sd->param); + if (sd->param) + sp->fform_arch = GetByNameFF(sd->param); + + + //--- file-format + + sd++; + noPRINT("ff-file=%s\n",sd->param); + if ( sd->param && *sd->param ) + { + sp->fform_file = GetByNameFF(sd->param); + if (IsCompressedFF(sp->fform_file)) + { + sp->compr_mode = 1; + sp->fform_file = GetCompressedFF(sp->fform_file); + } + else + { + sp->fform_file = sp->fform_arch; + sp->compr_mode = -1; + } + } + + //--- version + + sd++; + noPRINT("version=%s\n",sd->param); + if (sd->param) + sp->version = sd->value; + + //--- have-pt-dir + + sd++; + noPRINT("pt-dir=%s\n",sd->param); + if (sd->param) + sp->have_pt_dir = sd->value > 0 ? 1 : -1; + + //--- min-data-size + + sd++; + noPRINT("pt-dir=%s\n",sd->param); + if (sd->param) + sp->min_data_off = sd->value; + + //--- max-data-size + + sd++; + noPRINT("pt-dir=%s\n",sd->param); + if (sd->param) + sp->max_data_off = sd->value; + + //--- data-align + + sd++; + noPRINT("pt-dir=%s\n",sd->param); + if (sd->param) + sp->data_align = sd->value; + + //--- terminate + + if (use_options) + SetupParamOptions(sp); + ResetSetup(setup_def); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sub file system /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetSubDirList ( SubDirList_t *sdl ) +{ + if (sdl) + { + uint i; + for ( i = 0; i < sdl->used; i++ ) + ResetSubDir(sdl->list[i]); + FREE(sdl->list); + InitializeSubDirList(sdl); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSubDir ( SubDir_t *sd ) +{ + if (sd) + { + if (sd->dname_alloced) + FreeString(sd->dname); + ResetSubFileList(&sd->file); + ResetSubDirList(&sd->dir); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSubFileList ( SubFileList_t *sfl ) +{ + if (sfl) + { + uint i; + for ( i = 0; i < sfl->used; i++ ) + ResetSubFile(sfl->list[i]); + FREE(sfl->list); + InitializeSubFileList(sfl); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSubFile ( SubFile_t *sf ) +{ + if (sf) + { + if (sf->fname_alloced) + FreeString(sf->fname); + if (sf->data_alloced) + FREE(sf->data); + InitializeSubFile(sf); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void DumpSubDir ( FILE *f, int indent, const SubDir_t *sd ) +{ + DASSERT(f); + DASSERT(sd); + + indent = NormalizeIndent(indent); + fprintf(f,"%*sDIR %s/\n",indent,"",sd->dname); + + indent += 2; + DumpSubFileList(f,indent,&sd->file); + DumpSubDirList(f,indent,&sd->dir); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpSubDirList ( FILE *f, int indent, const SubDirList_t *sdl ) +{ + DASSERT(f); + DASSERT(sdl); + + uint i; + for ( i = 0; i < sdl->used; i++ ) + DumpSubDir(f,indent,sdl->list[i]); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpSubFileList ( FILE *f, int indent, const SubFileList_t *sfl ) +{ + DASSERT(f); + DASSERT(sfl); + indent = NormalizeIndent(indent); + + uint i; + for ( i = 0; i < sfl->used; i++ ) + { + const SubFile_t *sf = sfl->list[i]; + fprintf(f,"%*s> %s [%u,t=%d]\n",indent,"",sf->fname,sf->size,sf->type); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint FindSubDirHelper + ( const SubDirList_t *sdl, mem_t path, bool * p_found ) +{ + int beg = 0; + if ( sdl && path.len ) + { + int end = sdl->used - 1; + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + int stat = StrCmpMem(path,sdl->list[idx]->dname); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + { + noPRINT("InsertSubDirHelper(%.*s) FOUND=%d/%d/%d\n", + path.len, path.ptr, idx, sdl->used, sdl->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + noPRINT("InsertSubDirHelper(%.*s) failed=%d/%d/%d\n", + path.len, path.ptr, beg, sdl->used, sdl->size ); + + if (p_found) + *p_found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +static SubDir_t * InsertSubDirHelper ( SubDirList_t * sdl, int idx ) +{ + DASSERT(sdl); + DASSERT( sdl->used <= sdl->size ); + noPRINT("+SF: %u/%u/%u\n",idx,sdl->used,sdl->size); + if ( sdl->used == sdl->size ) + { + sdl->size = 3*sdl->size/2 + 0x20; + sdl->list = REALLOC(sdl->list,sizeof(*sdl->list)*sdl->size); + } + DASSERT( idx <= sdl->used ); + SubDir_t ** dest = sdl->list + idx; + memmove(dest+1,dest,(sdl->used-idx)*sizeof(*dest)); + sdl->used++; + + *dest = MALLOC(sizeof(**dest)); + InitializeSubDir(*dest); + return *dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +SubDir_t * InsertSubDir ( SubDir_t *sd, mem_t path, bool *new_dir ) +{ + DASSERT(sd); + ccp ptr = path.ptr; + ccp end = ptr + path.len; + + while ( ptr < end ) + { + while ( ptr < end && *ptr == '/' ) + ptr++; + ccp start = ptr; + while ( ptr < end && *ptr && *ptr != '/' ) + ptr++; + noPRINT("\t\tDIR: |%.*s|\n",(int)(ptr-start),start); + + const uint len = ptr - start; + if (!len) + continue; + + bool found; + const uint idx = FindSubDirHelper(&sd->dir,MemByStringS(start,len),&found); + if (found) + { + noPRINT("\t-> DIR FOUND\n"); + sd = sd->dir.list[idx]; + } + else + { + noPRINT("\t\tDIR NOT FOUND\n"); + sd = InsertSubDirHelper(&sd->dir,idx); + sd->dname = MEMDUP(start,len); + sd->dname_alloced = true; + } + } + + return sd; +} + +/////////////////////////////////////////////////////////////////////////////// + +SubDir_t * FindSubDir ( const SubDir_t *sd, mem_t path ) +{ + if ( !sd || !path.len ) + return 0; + + ccp ptr = path.ptr; + ccp end = ptr + path.len; + + while ( ptr < end ) + { + while ( ptr < end && *ptr == '/' ) + ptr++; + ccp start = ptr; + while ( ptr < end && *ptr && *ptr != '/' ) + ptr++; + + const uint len = ptr - start; + if (!len) + continue; + + bool found; + const uint idx = FindSubDirHelper(&sd->dir,MemByStringS(start,len),&found); + noPRINT("\t\tDIR: |%.*s| %s\n",len,start,found?"FOUND":""); + if (!found) + return 0; + + sd = sd->dir.list[idx]; + } + return (SubDir_t*)sd; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveSubDir ( SubDir_t *sd, mem_t path ) +{ + // [[2do]] ??? + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint FindSubFileHelper + ( const SubFileList_t *sfl, mem_t path, bool * p_found ) +{ + int beg = 0; + if ( sfl && path.len ) + { + int end = sfl->used - 1; + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + int stat = StrCmpMem(path,sfl->list[idx]->fname); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + { + noPRINT("InsertSubFileHelper(%.*s) FOUND=%d/%d/%d\n", + path.len, path.ptr, idx, sfl->used, sfl->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + noPRINT("InsertSubFileHelper(%.*s) failed=%d/%d/%d\n", + path.len, path.ptr, beg, sfl->used, sfl->size ); + + if (p_found) + *p_found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +static SubFile_t * InsertSubFileHelper ( SubFileList_t * sfl, int idx ) +{ + DASSERT(sfl); + DASSERT( sfl->used <= sfl->size ); + noPRINT("+SF: %u/%u/%u\n",idx,sfl->used,sfl->size); + if ( sfl->used == sfl->size ) + { + sfl->size = 3*sfl->size/2 + 0x20; + sfl->list = REALLOC(sfl->list,sizeof(*sfl->list)*sfl->size); + } + DASSERT( idx <= sfl->used ); + SubFile_t ** dest = sfl->list + idx; + memmove(dest+1,dest,(sfl->used-idx)*sizeof(*dest)); + sfl->used++; + + *dest = MALLOC(sizeof(**dest)); + InitializeSubFile(*dest); + return *dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +SubFile_t * InsertSubFile ( SubDir_t *sd, mem_t path, bool *new_file ) +{ + DASSERT(sd); + ccp beg = path.ptr; + ccp end = beg + path.len; + ccp ptr = end; + + while ( ptr > beg && *ptr != '/' ) + ptr--; + if ( ptr > beg ) + { + sd = InsertSubDir(sd,MemByStringE(beg,ptr),0); + if ( ptr < end ) + ptr++; + } + DASSERT(sd); + + noPRINT("\t-> FILE: |%.*s|\n",(int)(end-ptr),ptr); + + const uint len = end-ptr; + bool found; + const uint idx = FindSubFileHelper(&sd->file,MemByStringS(ptr,len),&found); + if (found) + { + noPRINT("\t-> FILE FOUND\n"); + if (new_file) + *new_file = false; + return sd->file.list[idx]; + } + + noPRINT("\t\tFILE NOT FOUND\n"); + if (new_file) + *new_file = true; + + SubFile_t *sf = InsertSubFileHelper(&sd->file,idx); + sf->fname = MEMDUP(ptr,len); + sf->fname_alloced = true; + return sf; +} + +/////////////////////////////////////////////////////////////////////////////// + +SubFile_t * FindSubFile ( const SubDir_t *sd, mem_t path ) +{ + if ( !sd || !path.len ) + return 0; + + ccp beg = path.ptr; + ccp end = beg + path.len; + ccp ptr = end; + + while ( ptr > beg && *ptr != '/' ) + ptr--; + if ( ptr > beg ) + { + sd = FindSubDir(sd,MemByStringE(beg,ptr)); + if (!sd) + return 0; + if ( ptr < end ) + ptr++; + } + DASSERT(sd); + + const uint len = end - ptr; + bool found; + const uint idx = FindSubFileHelper(&sd->file,MemByStringS(ptr,len),&found); + noPRINT("\t\tFILE: |%.*s| %s\n",len,ptr,found?"FOUND":""); + return found ? sd->file.list[idx] : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveSubFile ( SubDir_t *sd, mem_t path ) +{ + // [[2do]] ??? + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +FILE * OpenSubFile ( const SubDir_t *sd, mem_t path ) +{ + #ifdef __APPLE__ + ERROR0(ERR_FATAL,"OpenSubFile() not supported by MAC version."); + return 0; + #else + SubFile_t *sf = FindSubFile(sd,path); + return sf && sf->data ? fmemopen(sf->data,sf->size,"rb") : 0; + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int IterateSubDirHelper ( SubDir_t *sd, SubFileIterator_t *sfi ) +{ + DASSERT(sd); + DASSERT(sfi); + + char *path_end = sfi->path + sizeof(sfi->path) - 1; + char *path_old = sfi->path_ptr; + char *path_ptr = StringCopyE(path_old,path_end,sd->dname); + + int stat = 0; + if (sfi->func) + { + sfi->path_ptr = path_ptr; + const int xstat = sfi->func(sfi,1,sd,0); + if ( xstat < 0 ) + return xstat; + if ( stat < xstat ) + stat = xstat; + } + + sfi->depth++; + char *path_file = path_ptr; + if ( sd->dname && *sd->dname && path_file < path_end ) + *path_file++ = '/'; + + uint i; + for ( i = 0; i < sd->file.used; i++ ) + { + SubFile_t *sf = sd->file.list[i]; + sfi->path_ptr = StringCopyE(path_file,path_end,sf->fname); + if ( sfi->func) + { + const int xstat = sfi->func(sfi,0,sd,sf); + if ( xstat < 0 ) + return xstat; + if ( stat < xstat ) + stat = xstat; + } + } + + sfi->path_ptr = path_file; + for ( i = 0; i < sd->dir.used; i++ ) + { + const int xstat = IterateSubDirHelper(sd->dir.list[i],sfi); + if ( xstat < 0 ) + return xstat; + if ( stat < xstat ) + stat = xstat; + } + + sfi->depth--; + *path_ptr = 0; + + if ( sfi->func) + { + sfi->path_ptr = path_ptr; + const int xstat = sfi->func(sfi,2,sd,0); + if ( xstat < 0 ) + return xstat; + if ( stat < xstat ) + stat = xstat; + } + + sfi->path_ptr = path_old; + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int IterateSubDir ( SubDir_t *sd, SubFileIterator_t *sfi ) +{ + DASSERT(sd); + DASSERT(sfi); + + sfi->depth = 0; + sfi->path_ptr = sfi->path; + return IterateSubDirHelper(sd,sfi); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int SaveSubFile +( + SubFileIterator_t *sfi, // iterator data + uint mode, // 0:file, 1:open dir, 2:close dir + SubDir_t *dir, // current directory + SubFile_t *file // NULL or current file +) +{ + if ( !mode && file && file->data ) + { + DASSERT(file); + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),(ccp)sfi->user_ptr,sfi->path); + printf(" >SAVE %s\n",path); + + return SaveFile( (ccp)sfi->user_ptr, sfi->path, + FM_REMOVE|FM_OVERWRITE|FM_MKDIR, + file->data, file->size, 0 ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveSubFiles ( SubDir_t *sd, ccp basepath ) +{ + SubFileIterator_t it; + memset(&it,0,sizeof(it)); + it.func = SaveSubFile; + it.user_ptr = (void*)basepath; + return IterateSubDir(sd,&it); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrintScript /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintScriptHeader ( PrintScript_t *ps ) +{ + DASSERT(ps); + + ps->index = 0; + ps->add_index = false; + ps->sep[0] = ps->sep[1] = 0; + + if ( ps->fo && ps->fo->f ) + { + switch (script_fform) + { + case FF_JSON: + if ( script_array > 0 ) + fputs("[",ps->fo->f); + break; + + case FF_SH: + ps->add_index = script_array > 0; + break; + + case FF_PHP: + if ( script_array > 0 ) + fprintf(ps->fo->f,"$%s = array();\n\n",script_varname); + break; + + case FF_MAKEDOC: + if ( script_array > 0 ) + fprintf(ps->fo->f,"%s = @LIST\n\n",script_varname); + break; + + default: + //fputc('\n',ps->fo->f); + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintScriptFooter ( PrintScript_t *ps ) +{ + DASSERT(ps); + + if ( ps && ps->fo && ps->fo->f ) + { + switch (script_fform) + { + case FF_JSON: + if ( script_array > 0 ) + fputs("]\n",ps->fo->f); + break; + + case FF_SH: + if (script_array) + fprintf(ps->fo->f,"%s_N=%u\n\n",script_varname,ps->index); + break; + + case FF_PHP: + fputs("?>\n",ps->fo->f); + break; + + default: + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int PutScriptVars +( + PrintScript_t *ps, // valid control struct + uint mode, // bit field: 1=open var, 2:close var + ccp text // text with line of format NAME=VALUE +) +{ + DASSERT(ps); + + FILE * f = ps->fo ? ps->fo->f : 0; + if (!f) + return 0; + + + //--- begin of output + + if ( mode & 1 ) + { + if ( ps->add_index ) + snprintf( ps->prefix, sizeof(ps->prefix), + "%s_%u", script_varname, ps->index ); + else + snprintf( ps->prefix, sizeof(ps->prefix), + "%s", script_varname ); + + switch (script_fform) + { + case FF_JSON: + fprintf(f,"%s{",ps->sep); + ps->sep[0] = ','; + break; + + case FF_PHP: + fputs("$d = new stdClass;\n",f); + break; + + case FF_MAKEDOC: + fprintf(f,"d = @MAP\n"); + break; + + default: + break; + } + } + + + //--- print var lines + + if (!text) + text = ""; + + uint count; + for( count = 0; ; count++ ) + { + while ( *text && (uchar)*text <= ' ' ) + text++; + + ccp ptr = text; + while ( *ptr && *ptr != '=' ) + ptr++; + if ( *ptr != '=' || ptr == text ) + break; + + ccp param = ptr++; + while ( (uchar)*ptr >= ' ' ) + ptr++; + + switch (script_fform) + { + case FF_JSON: + fprintf(f, "%s\"%.*s\":%.*s", + count ? "," : "", + (int)( param - text ), text, + (int)( ptr - param - 1 ), param+1 ); + break; + + case FF_BASH: + if (script_array) + { + fprintf(f, "%s_%.*s%s=(%.*s)\n", ps->prefix, + (int)( param - text ), text, + ps->index ? "+" : "", + (int)( ptr - param - 1 ), param+1 ); + break; + } + // fall through + + case FF_SH: + fprintf(f, "%s_%.*s=%.*s\n", ps->prefix, + (int)( param - text ), text, + (int)( ptr - param - 1 ), param+1 ); + break; + + case FF_PHP: + fprintf(f, "$d->%.*s = %.*s;\n", + (int)( param - text ), text, + (int)( ptr - param - 1 ), param+1 ); + break; + + case FF_MAKEDOC: + fprintf(f, "d[\"%.*s\"] = %.*s\n", + (int)( param - text ), text, + (int)( ptr - param - 1 ), param+1 ); + break; + + default: + fprintf(f, "%20.*s = %.*s\n", + (int)( param - text ), text, + (int)( ptr - param - 1 ), param+1 ); + break; + } + + text = ptr; + } + + + //--- end of output + + if ( mode & 2 ) + { + switch (script_fform) + { + case FF_JSON: + fputs("}\n",f); + break; + + case FF_PHP: + fprintf(f, "$%s%s = $d;\n\n", + ps->prefix, script_array ? "[]" : "" ); + break; + + case FF_MAKEDOC: + fprintf(f, "%s %s= move(d);\n\n", + ps->prefix, script_array ? "#" : "" ); + break; + + default: + fputc('\n',f); + break; + } + } + + ps->index++; + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintScriptVars +( + PrintScript_t *ps, // valid control struct + uint mode, // bit field: 1=open var, 2:close var + ccp format, // format of message + ... // arguments + +) +{ + DASSERT(ps); + + char buf[5000]; + if (format) + { + va_list arg; + va_start(arg,format); + vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + } + else + *buf = 0; + + return PutScriptVars(ps,mode,buf); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Base64 coding /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char *TableDecode64BySetup = TableDecode64; +const char *TableEncode64BySetup = TableEncode64; + +//----------------------------------------------------------------------------- + +int ScanOptCoding64 ( ccp arg ) +{ + if ( !arg || !*arg ) + return 0; + + static const KeywordTab_t ktab[] = + { + { ENCODE_OFF, "DEFAULT", 0, 0 }, + { ENCODE_BASE64, "STANDARD", "STD", 0 }, + { ENCODE_BASE64URL, "URL", 0, 0 }, + { ENCODE_BASE64STAR, "STAR", 0, 0 }, + { ENCODE_BASE64XML, "XML", 0, 0 }, + { 0,0,0,0 } + }; + + const KeywordTab_t *key = ScanKeyword(0,arg,ktab); + if (!key) + { + ERROR0(ERR_SYNTAX,"Invalid --coding mode: '%s'\n",arg); + return 1; + } + + opt_coding64 = key->id; + PRINT("opt_coding64=%u\n",opt_coding64); + return 0; +} + +//----------------------------------------------------------------------------- + +EncodeMode_t SetupCoding64 ( EncodeMode_t mode, EncodeMode_t fallback ) +{ + PRINT("SetupCoding64(%d,%d) std=%d, url=%d, star=%d, xml=%d\n", + mode, fallback, + ENCODE_BASE64, ENCODE_BASE64URL, ENCODE_BASE64STAR, ENCODE_BASE64XML ); + + switch(mode) + { + case ENCODE_BASE64: + PRINT("USE ENCODE_BASE64\n"); + TableDecode64BySetup = TableDecode64; + TableEncode64BySetup = TableEncode64; + break; + + case ENCODE_BASE64URL: + PRINT("USE ENCODE_BASE64URL\n"); + TableDecode64BySetup = TableDecode64url; + TableEncode64BySetup = TableEncode64url; + break; + + case ENCODE_BASE64STAR: + PRINT("USE ENCODE_BASE64STAR\n"); + TableDecode64BySetup = TableDecode64url; + TableEncode64BySetup = TableEncode64star; + break; + + case ENCODE_BASE64XML: + PRINT("USE ENCODE_BASE64XML\n"); + TableDecode64BySetup = TableDecode64xml; + TableEncode64BySetup = TableEncode64xml; + break; + + default: + PRINT("BASE64 -> FALLBACK\n"); + return SetupCoding64( fallback == ENCODE_OFF + ? ENCODE_BASE64 : fallback, ENCODE_OFF ); + } + return mode; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-geohit.c b/project/src/lib-geohit.c new file mode 100644 index 0000000..55d13c9 --- /dev/null +++ b/project/src/lib-geohit.c @@ -0,0 +1,766 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include + +#include "dclib-xdump.h" +#include "lib-szs.h" +#include "lib-common.h" +#include "lib-numeric.h" +#include "lib-bzip2.h" + +#include "geohit.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GEOHIT setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void verify_geohit_obj ( const geohit_t * geohit, ccp identify ) +{ + if ( geohit && geohit->bin ) + { + PRINT("verify_geohit_obj(%s) USE_OBJECT_MANAGER=%d\n", + identify, USE_OBJECT_MANAGER ); + + #if USE_OBJECT_MANAGER > 1 + VerifyObjMgr(&geohit->obj_mgr,geohit->bin,geohit->bin_size,identify); + #elif USE_OBJECT_MANAGER + VerifyBinObjMgr(geohit->bin,geohit->bin_size,identify); + #endif + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeGEOHIT ( geohit_t * geohit, file_format_t fform ) +{ + DASSERT(geohit); + memset(geohit,0,sizeof(*geohit)); + geohit->fname = EmptyString; + geohit->fform_source = fform; + + InitializeObjMgr(&geohit->obj_mgr,fform); + SetupBZ2MgrGEOHIT(geohit,fform,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetGEOHIT ( geohit_t * geohit ) +{ + DASSERT(geohit); + const typeof(geohit->fform_source) fform = geohit->fform_source; + + ResetObjMgr(&geohit->obj_mgr); + FreeString(geohit->fname); + FREE(geohit->bin); + + InitializeGEOHIT(geohit,fform); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupBZ2MgrGEOHIT + ( geohit_t * geohit, file_format_t fform, bool reload_bin ) +{ + DASSERT(geohit); + if (geohit->bin) + ScanObjType( &geohit->obj_mgr.otype, + geohit->bin, geohit->bin_size, geohit->bin_size ); + + #if USE_OBJECT_MANAGER + uint prio = OM_PRIO_DEFAULT; + #endif + if ( reload_bin || !geohit->bin ) + { + BZ2Manager_t *bz = GetCommonBZ2Manager(fform); + if (!bz) + { + const file_format_t ff = GetObjTypeFF(&geohit->obj_mgr.otype,false); + bz = GetCommonBZ2Manager(ff); + } + + if (bz) + { + FREE(geohit->bin); + geohit->bin_size = bz->size; + geohit->bin = MEMDUP(bz->data,geohit->bin_size); + #if USE_OBJECT_MANAGER + prio = OM_PRIO_ORIG; + #endif + } + } + + if (geohit->bin) + { + #if USE_OBJECT_MANAGER + SetupObjMgrByData( &geohit->obj_mgr, geohit->bin, geohit->bin_size, prio ); + #else + ScanObjType( &geohit->obj_mgr.otype, + geohit->bin, geohit->bin_size, geohit->bin_size ); + #endif + + #if USE_OBJECT_MANAGER < 3 + geohit->n_rec = be16(geohit->bin); + const uint max = ( geohit->bin_size - 2 ) / geohit->obj_mgr.otype.row_size; + if ( geohit->n_rec > max ) + geohit->n_rec = max; + #endif + } + + PRINT0("GEOHIT: ff=%s,%s, nr=%03d, np=%02zd, rs=%02u, kart=%d, obj=%d\n", + GetNameFF(0,geohit->fform_source), + GetNameFF(0,geohit->obj_mgr.otype.fform), + #if USE_OBJECT_MANAGER < 2 + geohit->n_rec, + #else + -1, + #endif + geohit->obj_mgr.otype.row_size/sizeof(s16) - 1, + geohit->obj_mgr.otype.row_size, + geohit->obj_mgr.otype.is_kart, geohit->obj_mgr.otype.is_obj ); + + verify_geohit_obj(geohit,"GeoHit/setup"); +} + +/////////////////////////////////////////////////////////////////////////////// + +geohit_bin_t * GetBinGEOHIT ( cvp data, uint index ) +{ + DASSERT(data); + const uint nr = be16(data); + const uint rsize = (be16(data+2)+1) * sizeof(u16); + return index < nr + ? (geohit_bin_t*)( (u8*)data + 4 + index * rsize ) + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawGEOHIT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(geohit); + if (init_geohit) + InitializeGEOHIT(geohit,FF_GH_ITEM); + + FREE(geohit->bin); + geohit->bin_size = data_size; + geohit->bin = MEMDUP(data,data_size); + SetupBZ2MgrGEOHIT(geohit,FF_UNKNOWN,false); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextGEOHIT() helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + GEOHIT_IGNORE, + GEOHIT_SETUP, + GEOHIT_OBJECTS, +}; + +//----------------------------------------------------------------------------- + +static const KeywordTab_t geohit_section_name[] = +{ + { GEOHIT_IGNORE, "END", 0, 0 }, + { GEOHIT_SETUP, "SETUP", 0, 0 }, + { GEOHIT_OBJECTS, "OBJECTS", 0, 0 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextSETUP +( + geohit_t * geohit, // GEOHIT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(geohit); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + bool setup_geohit = false; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + ScanU32SI(si,(u32*)&geohit->revision,1,0); + si->init_revision = si->cur_file->revision = geohit->revision; + DefineIntVar(&si->gvar,"REVISION$SETUP",geohit->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",geohit->revision); + } + else if (!strcmp(name,"IS-OBJ")) // [[obsolete]] in mid 2020 + { + u32 num; + if (!ScanU32SI(si,&num,1,0)) + { + setup_geohit = true; + geohit->obj_mgr.otype.is_obj = num > 0; + } + } +// [[obj-type]] + #if USE_OBJECT_MANAGER > 1 + else if (!SetupObjMgrBySI(&geohit->obj_mgr,si,name)) + GotoEolSI(si); + #else + else + GotoEolSI(si); + #endif + CheckEolSI(si); + } + + CheckLevelSI(si); + if (setup_geohit) + SetupBZ2MgrGEOHIT(geohit,FF_UNKNOWN,false); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static geohit_bin_t * FindObjectGEOHIT ( const geohit_t *geohit, u16 oid ) +{ + oid = htons(oid); + geohit_bin_t *bin = GetBinGEOHIT(geohit->bin,0); + + uint i; + for ( i = 0; i < geohit->n_rec; i++ ) + { + if ( bin->id == oid ) + return bin; + + bin = (geohit_bin_t*)( (u8*)bin + geohit->obj_mgr.otype.row_size ); + } + return 0; +} + +//----------------------------------------------------------------------------- + +static enumError ScanTextOBJECTS +( + geohit_t * geohit, // GEOHIT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(geohit); + DASSERT(geohit->bin); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + geohit_bin_t *obj = 0; + si->point_is_null++; + const int n_param = geohit->obj_mgr.otype.row_size/sizeof(s16) - 1; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,0); + continue; + } + + long oid; + ScanUValueSI(si,&oid,0); + obj = FindObjectGEOHIT(geohit,oid); + PRINT0("OID: %lx -> %p\n",oid,obj); + if (!obj) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ScanFile_t *sf = si->cur_file; + ERROR0(ERR_WARNING, + "Object 0x%03lx not found: [%s @%u]: %.*s\n", + oid, sf->name, sf->line, (int)(eol-sf->ptr), sf->ptr ); + } + GotoEolSI(si); + continue; + } + + int i; + for ( i = 0; i < n_param; i++ ) + { + const char ch = NextCharSI(si,false); + if (!ch) + break; + if ( ch == '=' ) + { + si->cur_file->ptr++; + SkipCharSI(si,','); + } + else + { + long num; + ScanUValueSI(si,&num,0); + obj->param[i] = htons(num); + } + } + CheckEolSI(si); + } + + si->point_is_null--; + CheckLevelSI(si); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextGEOHIT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // file format +) +{ + DASSERT(geohit); + PRINT("ScanTextGEOHIT(init=%d)\n",init_geohit); + + if ( fform == FF_UNKNOWN ) + fform = GetByMagicFF(data,data_size,data_size); + if (init_geohit) + InitializeGEOHIT(geohit,fform); + else + { + geohit->fform_source = fform; + SetupBZ2MgrGEOHIT(geohit,fform,true); + } + + + ScanInfo_t si; + InitializeSI(&si,data,data_size,geohit->fname,geohit->revision); + si.predef = SetupVarsCOMMON(); + + enumError max_err = ERR_OK; + geohit->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN GEOHIT SECTIONS, PASS%u ...\n",geohit->is_pass2+1); + + max_err = ERR_OK; + si.no_warn = !geohit->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", geohit->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,geohit->revision); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + noPRINT("--> pass=%u: #%04u: %s\n",geohit->is_pass2+1,si.cur_file->line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,geohit_section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case GEOHIT_SETUP: + err = ScanTextSETUP(geohit,&si); + break; + + case GEOHIT_OBJECTS: + err = ScanTextOBJECTS(geohit,&si); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (geohit->is_pass2) + break; + + geohit->is_pass2 = true; + + RestartSI(&si); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextGEOHIT) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GEOHIT: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(geohit); + + enumError err; +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch (fform) + { + case FF_GH_ITEM: + case FF_GH_IOBJ: + case FF_GH_KART: + case FF_GH_KOBJ: + err = ScanRawGEOHIT(geohit,init_geohit,data,data_size); + break; + + case FF_GH_ITEM_TXT: + case FF_GH_IOBJ_TXT: + case FF_GH_KART_TXT: + case FF_GH_KOBJ_TXT: + err = ScanTextGEOHIT(geohit,init_geohit,data,data_size,fform); + break; + + default: + if (init_geohit) + InitializeGEOHIT(geohit,0); + return ERROR0(ERR_INVALID_DATA, + "No GEOHIT file: %s\n", geohit->fname ? geohit->fname : "?"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadGEOHIT +( + geohit_t * geohit, // GEOHIT data structure + bool init_geohit, // true: initialize 'geohit' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(geohit); + DASSERT(fname); + if (init_geohit) + InitializeGEOHIT(geohit,0); + else + ResetGEOHIT(geohit); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + geohit->fname = raw.fname; + raw.fname = 0; + err = ScanGEOHIT(geohit,false,raw.data,raw.data_size); + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawGEOHIT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawGEOHIT +( + geohit_t * geohit, // pointer to valid GEOHIT + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(geohit); + DASSERT(geohit->bin); + DASSERT(fname); + + PRINT("SaveRawGEOHIT(%s,%d)\n",fname,set_time); + verify_geohit_obj(geohit,"GeoHit/save-raw"); + + + //--- write to file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&geohit->fatt,0); + + if ( fwrite(geohit->bin,1,geohit->bin_size,F.f) != geohit->bin_size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextGEOHIT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintParamGEOHIT ( FILE *f, uint fw, cvp param0, cvp cmp0, int n ) +{ + const u16 *param = param0; + const u16 *cmp = cmp0; + + int extra = 4; + while ( n-- > 0 ) + { + if (!extra--) + { + extra = 3; + fputc(' ',f); + } + + const u16 p = ntohs(*param++); + if ( cmp && ntohs(*cmp++) == p ) + fprintf(f," %*c",fw,'='); + else if (p) + fprintf(f," %*d",fw,(s16)p); + else + fprintf(f," %*c",fw,'.'); + } +} + +//----------------------------------------------------------------------------- + +enumError SaveTextGEOHIT +( + const geohit_t * geohit, // pointer to valid GEOHIT + ccp fname, // filename of destination + bool set_time, // true: set time stamps +// [[minimize]] + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >9: minimize output +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(geohit); + DASSERT(geohit->bin); + DASSERT(fname); + PRINT("SaveTextGEOHIT(%s,%d)\n",fname,set_time); + verify_geohit_obj(geohit,"GeoHit/save-text"); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,geohit->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&geohit->fatt,0); + + + //--- print header + + const bool explain = print_header && !brief_count && !export_count; + const bool tiny = brief_count > 1 || export_count; + const file_format_t ff_text = GetObjTypeFF(&geohit->obj_mgr.otype,true); + ccp magic = GetMagicStringFF(ff_text); + BZ2Manager_t *bz_orig = GetCommonBZ2Manager(ff_text); + + if (geohit->obj_mgr.otype.is_kart) + { + if (explain) + fprintf(F.f,text_geohit_kart_info_cr,magic); + else + fprintf(F.f,text_geohit_kart_head_cr,magic); + + if (!tiny) + fprintf(F.f,text_geohit_kart_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + + #if USE_OBJECT_MANAGER > 1 + WriteObjMgrToSetupSection( F.f, &geohit->obj_mgr, + long_count>0, tiny, explain ); + #endif + } + else + { + if (explain) + fprintf(F.f,text_geohit_item_info_cr,magic); + else + fprintf(F.f,text_geohit_item_head_cr,magic); + + if (!tiny) + fprintf(F.f,text_geohit_item_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + + #if USE_OBJECT_MANAGER > 1 + WriteObjMgrToSetupSection( F.f, &geohit->obj_mgr, + long_count>0, tiny, explain ); + #endif + } + + + //--- iterate records + + const int row_size = geohit->obj_mgr.otype.row_size; + const int n_param = row_size/sizeof(s16) - 1; + const int fw = n_param < 8 ? 5 : 3; + + const geohit_bin_t *bin = GetBinGEOHIT(geohit->bin,0); + const geohit_bin_t *ref = bz_orig ? GetBinGEOHIT(bz_orig->data,0) : 0; + if (!ref) + minimize = 0; + + bool sect_head_done = false; + int i, last_grp = -1; + for ( i = 0; i < geohit->n_rec; i++ ) + { + const int differ = ref && memcmp( bin, ref, row_size ); + + if ( differ || !minimize ) + { + if (!sect_head_done) + { + sect_head_done = true; + fputs( brief_count < 2 ? section_sep : "\r\n", F.f ); + if (geohit->obj_mgr.otype.is_kart) + fprintf(F.f,text_geohit_kart_objects_cr,REVISION_NUM); + else + fprintf(F.f,text_geohit_item_objects_cr,REVISION_NUM); + } + + const uint obj_id = ntohs(bin->id); + const uint grp = obj_id >> 4; + if ( last_grp != grp ) + { + if ( last_grp != -1 ) + fputs("\r\n",F.f); + last_grp = grp; + } + + fprintf(F.f,"\r\n 0x%03x ",obj_id); + PrintParamGEOHIT(F.f,fw,bin->param,0,n_param); + if ( obj_id < N_KMP_GOBJ ) + fprintf(F.f," # %s",ObjectInfo[obj_id].name); + + if (differ) + { + fputs("\r\n #ORIG:",F.f); + PrintParamGEOHIT(F.f,fw,ref->param,bin->param,n_param); +//DEL HexDump16(stderr,0,0,bin,row_size); +//DEL HexDump16(stderr,0,0,ref,row_size); + } + } + + bin = (geohit_bin_t*)( (u8*)bin + row_size ); + if (ref) + ref = (geohit_bin_t*)( (u8*)ref + row_size ); + } + + + //--- terminate + + fputs("\r\n",F.f); + fputs( brief_count < 2 ? section_end :"\r\n", F.f ); + ResetFile(&F,set_time); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-image.h b/project/src/lib-image.h new file mode 100644 index 0000000..366d61f --- /dev/null +++ b/project/src/lib-image.h @@ -0,0 +1,1077 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_IMAGE_H +#define SZS_LIB_IMAGE_H 1 + +#define _GNU_SOURCE 1 + +#include "lib-std.h" +#include "lib-numeric.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// relevant infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// http://wiki.tockdom.de/index.php?title=Image_Formats + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Color_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[Color_t]] + +typedef union Color_t +{ + struct + { + u8 r; // red + u8 g; // green + u8 b; // blue + u8 a; // alpha + }; + + u8 rgba[4]; // array + u32 val; // combined value +} +__attribute__ ((packed)) Color_t; + +#define SETCOLOR(C,R,G,B,A) (C).r=(R), (C).g=(G), (C).b=(B), (C).a=(A) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MipmapOptions_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MipmapOptions_t]] + +struct Image_t; + +typedef struct MipmapOptions_t +{ + bool valid; // TRUE: setup of this record done + bool force; // TRUE: force 'n_mipmap' + uint n_mipmap; // number of mipmaps + uint n_image; // := n_mipmap+1 + uint min_size; // minimal mipmap width and height +} +MipmapOptions_t; + +// both functions return 'dest', 'src' may be NULL +void SetupMipmapOptions ( MipmapOptions_t *mmo ); +void SetupMipmapOptions1 ( MipmapOptions_t *mmo ); +void CopyMipmapOptions ( MipmapOptions_t *dest, const MipmapOptions_t *src ); +void MipmapOptionsByImage ( MipmapOptions_t *mmo, const struct Image_t *img ); + +ccp InfoMipmapOptions ( const MipmapOptions_t *mmo ); +ccp TextMipmapOptions ( const MipmapOptions_t *mmo ); +void PrintMipmapOptions ( FILE *f, int indent, const MipmapOptions_t *mmo ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ImageGeometry_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ImageGeometry_t]] + +typedef struct ImageGeometry_t +{ + image_format_t iform; // image format + + bool is_x; // true: is a IMG_X_* format + bool is_gray; // true: is a gray scale format + bool has_alpha; // true: supports alpha/transparent infos + bool has_blocks; // true: is structured in blocks + + u16 max_palette; // >0: max number of palette entries + bool read_support; // true: tools can read this format + bool write_support; // true: tools can write this format + + uint bits_per_pixel; // number of bits per pixel + uint block_width; // width of a block in pixel + uint block_height; // height of a block in pixel + uint block_size; // size of a block in bytes + + ccp name; // name of image +} +ImageGeometry_t; + +//----------------------------------------------------------------------------- + +const ImageGeometry_t * GetImageGeometry ( image_format_t iform ); +int PaletteToImageFormat ( int pform, int return_if_invalid ); + +uint CalcImageSize +( + uint width, // width of image in pixel + uint height, // height of image in pixel + + uint bits_per_pixel, // number of bits per pixel + uint block_width, // width of a single block + uint block_height, // height of a single block + + uint * x_width, // not NULL: store extended width here + uint * x_height, // not NULL: store extended height here + uint * h_blocks, // not NULL: store number of horizontal blocks + uint * v_blocks // not NULL: store number of vertical blocks +); + +const ImageGeometry_t * CalcImageGeometry +( + image_format_t iform, // image format + uint width, // width of image in pixel + uint height, // height of image in pixel + + uint * x_width, // not NULL: store extended width here + uint * x_height, // not NULL: store extended height here + uint * h_blocks, // not NULL: store number of horizontal blocks + uint * v_blocks, // not NULL: store number of vertical blocks + uint * img_size // not NULL: return image size +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Image_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[Image_t]] + +typedef struct Image_t +{ + //--- image data + + image_format_t iform; // current data format + u8 * data; // pointer to data or NULL if iform==IMG_INVALID + uint data_size; // size of data in bytes + bool data_alloced; // true: 'data' is alloced -> free() it + + uint width; // real width of image in pixel + uint height; // real height of image in pixel + bool is_grayed; // is grayed + int alpha_status; // -1: alpha not used (all=0xff) + // 0: unknown + // 1: alpha channel used (at least one != 0xff) + + // X_GRAY+X_RGB: The image is always extended to multiple of 8*8 + // xwidth+xheight show this value and are generally = width,height + uint xwidth; // extended width, only valid for IMG_X_ formats + uint xheight; // extended height, only valid for IMG_X_ formats + + uint seq_num; // a unique number, changed for every conversion + // copies will get the same number + uint conv_count; // initialized with 0 + // it's incremented for every conversion + + //--- mipmap support + + struct Image_t * mipmap; // NULL or mipmap of next level + + + //--- palette data + + palette_format_t pform; // palette format + u8 * pal; // NULL or pointer to palette + uint pal_size; // size of 'pal' in bytes + bool pal_alloced; // true: 'pal' is alloced -> free() it + uint n_pal; // number of used palette entries + + + //--- control data + + u8 * container; // free it on reset + const endian_func_t * endian; // endian functions to access internal formats + + + //--- additional image infos + + ccp path; // NULL or alloced filename of image + bool path_alloced; // true: 'path' is alloced -> free() it + file_format_t info_fform; // info about original file format + image_format_t info_iform; // info about original image format + palette_format_t info_pform; // info about original palette format + uint info_size; // info about projected image size + uint info_n_image; // number of images in original + FileAttrib_t fatt; // file attributes + + + //--- transforming results + + bool tform_valid; // false: the 'tform_' are invalid + bool tform_exec; // true: job for ExecTransformIMG() + bool tform_gray; // result is grayed + bool tform_noalpha; // alpha channel must be cleard or removed + file_format_t tform_fform0; // source value for transformation calculations + image_format_t tform_iform0; // " + palette_format_t tform_pform0; // " + file_format_t tform_fform; // planned transformation + image_format_t tform_iform; // " + palette_format_t tform_pform; // " + + + //--- patching infos + + bool patch_done; // true: image already patched + PatchImage_t patch_mode; // only used by the patching list + +} Image_t; + +extern uint image_seq_num; + +#define MM_COUNT(a) PRINT("MM-COUNT: %u [%s() @%u]\n", \ + CountMipmapsIMG(a),__FUNCTION__,__LINE__) +//#define MM_COUNT(a) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeIMG ( Image_t * img ); +void ResetIMG ( Image_t * img ); +void RemoveContainerIMG ( Image_t * img ); + +/////////////////////////////////////////////////////////////////////////////// + +void FreeIMG +( + Image_t * img, // destination image, dynamic data freed + const Image_t * src // not NULL: copy parameters, but not data +); + +/////////////////////////////////////////////////////////////////////////////// + +void FreeMipmapsIMG +( + Image_t * img // not NULL: remove mipmaps +); + +/////////////////////////////////////////////////////////////////////////////// + +uint CountMipmapsIMG +( + const Image_t * img // count mipmaps +); + +/////////////////////////////////////////////////////////////////////////////// + +void CopyIMG +( + Image_t * dest, // destination image + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // valid source image + bool copy_mipmap // true: copy mipmaps too +); + +/////////////////////////////////////////////////////////////////////////////// + +void MoveIMG +( + Image_t * dest, // destination image + bool init_dest, // true: initialize 'dest' first + Image_t * src // NULL or source image, isresetted +); + +/////////////////////////////////////////////////////////////////////////////// + +void MoveDataIMG +( + Image_t * dest, // valid destination image + Image_t * src // valid source image, is resetted +); + +/////////////////////////////////////////////////////////////////////////////// + +void AssignDataGRAY +( + Image_t * dest, // valid destination + const Image_t * src, // NULL or image template + u8 * data // with AllocDataIMG() alloced data +); + +/////////////////////////////////////////////////////////////////////////////// + +void AssignDataRGB +( + Image_t * dest, // valid destination + const Image_t * src, // NULL or image template + u8 * data // with AllocDataIMG() alloced data +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ScanDataIMG +( + Image_t * img, // destination image + bool init_img, // true: initialize 'img' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError AssignIMG +( + Image_t * img, // pointer to valid img + int init_img, // <0:none, =0:reset, >0:init + const u8 * data, // source data + uint data_size, // soze of 'data' + uint img_index, // index of sub image, 0:main, >0:mipmaps + bool mipmaps, // true: assign mipmaps + const endian_func_t * endian, // endian functions to read data + ccp fname // object name, assigned +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadIMG +( + Image_t * img, // pointer to valid img + bool init_img, // true: initialize 'img' + ccp fname, // filename of source + uint img_index, // index of sub image, 0:main, >0:mipmaps + bool mipmaps, // true: load and assign mipmaps + bool allow_subfile, // allow to extract szs sub files + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveIMG +( + Image_t * img, // pointer to valid img + file_format_t fform, // file format + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite // true: force overwriting +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTPL +( + Image_t * src_img, // pointer to valid source img + ccp fname, // filename of source + bool overwrite // true: force overwriting +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveBTI +( + Image_t * src_img, // pointer to valid source img + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite // true: force overwriting +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTEX +( + Image_t * src_img, // pointer to valid source img + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite, // true: force overwriting + bool ctcode_support // true: include a CT-CODE file +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveBREFTIMG +( + Image_t * src_img, // pointer to valid img + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite // true: force overwriting +); + +/////////////////////////////////////////////////////////////////////////////// + +bool NormalizeFrameIMG +( + Image_t * img // pointer to valid img +); + +/////////////////////////////////////////////////////////////////////////////// + +int CheckAlphaIMG +( + const Image_t * img, // pointer to valid img + bool force // true: don't believe 'img->alpha_status' +); + +/////////////////////////////////////////////////////////////////////////////// + +bool IsGrayIMG +( + const Image_t * img // pointer to valid img +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +image_format_t NormalizeIF +( + image_format_t * xform, // not NULL: store the best IMG_X_* here + image_format_t iform, // wanted image format + image_format_t x_default, // default format, use if iform == AUTO + // and return a X-format + palette_format_t pform // related palette format +); + +/////////////////////////////////////////////////////////////////////////////// + +palette_format_t NormalizePF +( + image_format_t iform, // related image format + palette_format_t pform, // wanted palette format + palette_format_t default_pform // palette format for auto +); + +/////////////////////////////////////////////////////////////////////////////// + +uint GetPaletteCountIF +( + image_format_t iform // image format +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertIMG +( + Image_t * dest, // destination of conversion + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source of conversion. + // if src==NULL or src==dest + // -> inplace conversion of 'dest' + image_format_t iform, // new image format + palette_format_t pform // new palette format +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertToRGB +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertToGRAY +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertToPALETTE +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + uint max_pal, // maximum palette values, 0 = auto + image_format_t iform // if a valid PAL format: + // convert to this and + // reduce 'max_pal' if neccessary +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadPNG +( + Image_t * img, // destination image + bool init_img, // true: initialize 'img' first + bool mipmaps, // true: load and export mipmaps + + ccp path1, // NULL or part #1 of path + ccp path2 // NULL or part #2 of path +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ReadPNG +( + Image_t * img, // destination image + bool mipmaps, // true: try to load mipmaps + File_t * f, // valid opened file + u8 * cache, // pre read data + uint cache_size // size of 'cache' +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError SavePNG +( + Image_t * img, // pointer to valid img + bool mipmaps, // save: save mipmaps (auto file name) + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + int store_alpha, // <0:no alpha, =0:auto alpha, >0:store alpha + bool overwrite, // true: force overwriting + StringField_t *file_list // not NULL: store filenames of created png files +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ExportPNG +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileAttrib_t * fatt, // NULL or file attributes + const void * img_data, // image data + uint img_size, // image size, needed for validation + uint img_index, // index of sub image, 0:main, >0:mipmaps + bool mipmaps, // true: load and export mipmaps + uint * n_image, // not null: store detected image count + const endian_func_t * endian, // endian functions to read data + FormatFieldItem_t * ffi, // not null: store detected image+pal format + bool create_png, // false: do some calculations but don't create png + StringField_t *file_list // not NULL: store filenames of created png files +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan file and image format /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTransform ( ccp arg ); +void DumpTransformList ( FILE * f, int indent, bool force ); + +ccp PrintFormat3 // print only output (internal) formats +( + file_format_t fform, // file format + image_format_t iform, // image format + palette_format_t pform // palette format +); + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintImageFormat // print any format, if needed as hex +( + image_format_t iform, // image format + palette_format_t pform // palette format +); + +static inline ccp PrintFormatIMG ( const Image_t * img ) +{ + DASSERT(img); + return PrintImageFormat(img->iform,img->pform); +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image format transformations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2InternIMG ( Image_t * img ); +bool Transform2XIMG ( Image_t * img ); +bool Transform2XRGB ( Image_t * img ); +bool Transform2PaletteIMG ( Image_t * img ); +bool Transform2NoPaletteIMG ( Image_t * img ); +bool Transform2GrayIMG ( Image_t * img ); +bool Transform2ColorIMG ( Image_t * img ); +bool Transform2AlphaIMG ( Image_t * img ); +bool Transform2NoAlphaIMG ( Image_t * img ); + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform3IMG +( + Image_t * img, // valid image + file_format_t fform, // file format + image_format_t iform, // image format + palette_format_t pform, // palette format + bool def_base // define *form values as base +); + +/////////////////////////////////////////////////////////////////////////////// + +bool TransformIMG +( + // The image is NOT converted, only the planned transformations + // are calculated. Use ExecTransformIMG() to execute transformation + // -> returns TRUE if a rule match + + Image_t * img, // destination of transforming + int indent // <0: no logging + // >=0: log transform with indent +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ExecTransformIMG +( + Image_t * img // image to transform +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image creation + drawing /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateIMG +( + Image_t * img, // image object + bool init_img, // true: initialize 'img' first + uint width, // width a new image + uint height, // height of new image + Color_t col // fill color +); + +//----------------------------------------------------------------------------- + +enumError CopyRectIMG +( + Image_t * dest, // dest image + int dest_x, // left coordinate of dest + int dest_y, // top coordinate of dest + + const Image_t * src, // source image + int src_x, // left coordinate of src + int src_y, // top coordinate of src + + int width, // width of rect to copy + int height, // height of rect to copy + + PatchImage_t patch_mode // one of COPY, MIX, FOREGROUND, BACKGROUND +); + +//----------------------------------------------------------------------------- + +enumError DrawPointIMG +( + Image_t * img, // image + int x, // x position (robust) + int y, // y position (robust) + Color_t col, // draw color + bool combine // true: combine 'col' with current one +); + +//----------------------------------------------------------------------------- + +enumError DrawHLineIMG +( + Image_t * img, // image + int x1, // start point (robust) + int x2, // end point (not drawn, robust) + int y, // common y coordinate (robust) + Color_t col // draw color +); + +//----------------------------------------------------------------------------- + +enumError DrawVLineIMG +( + Image_t * img, // image + int x, // common x coordinate (robust) + int y1, // start point (robust) + int y2, // end point (not drawn, robust) + Color_t col // draw color +); + +//----------------------------------------------------------------------------- + +enumError DrawFrameIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col // draw color +); + +//----------------------------------------------------------------------------- + +enumError FillRectIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col, // draw color + bool combine // true: combine 'col' with current one +); + +//----------------------------------------------------------------------------- + +enumError FillHGradientIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col1, // draw color left + Color_t col2 // draw color right +); + +//----------------------------------------------------------------------------- + +enumError FillVGradientIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col1, // draw color top + Color_t col2 // draw color bottom +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image resizing /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern bool fast_resize_enabled; + +enumError ResizeIMG +( + Image_t * dest, // dest image (may same as src) + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source image (if NULL: use dest) + uint width, // new width of image + uint height // new height of image + // if one of 'width' or 'height' is NULL + // -> keep aspect ratio +); + +//----------------------------------------------------------------------------- + +enumError FastResizeIMG +( + Image_t * dest, // dest image (may same as src) + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source image (if NULL: use dest) + uint width, // new width of image + uint height // new height of image + // if one of 'width' or 'height' is NULL + // -> keep aspect ratio +); + +//----------------------------------------------------------------------------- + +enumError SmartResizeIMG +( + Image_t * dest, // dest image (may same as src) + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source image (if NULL: use dest) + uint width, // new width of image + uint height // new height of image + // if one of 'width' or 'height' is NULL + // -> keep aspect ratio +); + +//----------------------------------------------------------------------------- + +enumError HalfIMG +( + Image_t * img // source and dest +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CMPR interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CMPR_MAX_COL 16 +#define CMPR_DATA_SIZE (4*CMPR_MAX_COL) + +//----------------------------------------------------------------------------- +// [[cmpr_info_t]] + +typedef struct cmpr_info_t +{ + ccp name; // name of method + uint opaque_count; // number of opaque (=non transparent) pixels + u8 p[4][4]; // 4 calculated values + // p[0] + p[1]: values + // if opaque_count < 16: p[2] == p[3] + u8 default_vector[8]; // init by 'opt_cmpr_def' + + //--- statistics, only valid if 'fill_info' set + + u8 index[CMPR_MAX_COL]; // color index of each value + uint dist[CMPR_MAX_COL]; // distance of each value + uint total_dist; // sum of all distances + + u8 cmpr[8]; // compressed data + u64 usec; // time of calculation + +} cmpr_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeCmprInfo ( cmpr_info_t *info ) +{ + DASSERT(info); + memset(info,0,sizeof(*info)); + memcpy(info->default_vector,opt_cmpr_def,sizeof(info->default_vector)); +}; + +//----------------------------------------------------------------------------- + +void CMPR_wiimm +( + const u8 *data, // source data + cmpr_info_t *info // info data structure +); + +//----------------------------------------------------------------------------- + +void CMPR_close_info +( + const u8 *data, // source data + cmpr_info_t *info, // info data structure + u8 *dest, // store destination data here (never 0) + bool fill_info // true: fill info with statistics +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ScanPatchImage ( ccp arg, PatchImage_t * mode ); +int ScanOptPatchImage ( ccp arg ); + +/////////////////////////////////////////////////////////////////////////////// + +bool SetupPatchListIMG(); // returns true if patching enabled +void ResetPatchListIMG(); + +enumError PatchListIMG +( + Image_t * img // image to patch +); + +enumError PatchIMG +( + Image_t * dest_img, // destination image, can be one of the sources + Image_t * src_img1, // base source + Image_t * src_img2, // patch source + PatchImage_t patch_mode // patch mode +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image lists /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintImageHead +( + int line_indent, // indention of complete line + uint long_count // set the verbosity +); + +//----------------------------------------------------------------------------- + +int PrintImage +( + const void * data, // valid data pointer + // if NULL: force data line + uint data_size, // size of 'data' + ccp fname, // file name for listing + int line_indent, // indention of complete line + int fname_indent, // indention of 'fname' + uint long_count, // set the verbosity + bool ignore // true: ignore non images +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// median cut /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint MedianCut +( + const u32 * data, // input: 4-tuple aka RGBA + // data is read at the very beginning + // and may overlay with 'index' and/or 'pal' + u16 * index, // not NULL: store index into palette, + // can be the space as 'data' + + // The follwing 3 parameters are used for 'data' and 'index'. + // They allow to skip unused pixel at the end or each row. + uint width, // used pixels per row + uint xwidth, // >width: pixels per row + uint height, // number or rows + + // pallette data + u32 * pal, // not NULL: store calculated palette + uint pal_elem // number of wanted palette entries + + // return value = number of calculated palette values +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TPL support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TPL_MAGIC_NUM 0x0020AF30 + +typedef struct tpl_header_t +{ + u8 magic[4]; // always TPL_MAGIC_NUM + u32 n_image; // number of images + u32 imgtab_off; // image table offset relative to file start +} +__attribute__ ((packed)) tpl_header_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct tpl_imgtab_t +{ + u32 image_off; // image offset relative to file start + u32 palette_off; // palette offset relative to file start +} +__attribute__ ((packed)) tpl_imgtab_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct tpl_pal_header_t +{ + u16 n_entry; // number of palette entries + u16 unknown1; + u32 pform; // image format of palette + u32 data_off; // data offset relative to tpl_header_t +} +__attribute__ ((packed)) tpl_pal_header_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct tpl_img_header_t +{ + u16 height; // height of image in pixel + u16 width; // width of image in pixel + u32 iform; // image format + u32 data_off; // data offset relative to tpl_header_t + + // the following is unknown + // names taken from http://hitmen.c02.at/files/yagcd/yagcd/chap15.html#sec15.35 + + u32 wrap_s; // value 0 or 1 + u32 wrap_t; // value 0 or 1 + u32 min_filter; // always 1 + u32 mag_filter; // always 1 + float32 load_bias; // always 0 + u8 edge_load; // always 0 + u8 min_load; // always 0 + u8 max_load; // always 0 + u8 unpacked; // always 0 +} +__attribute__ ((packed)) tpl_img_header_t; + +/////////////////////////////////////////////////////////////////////////////// + +uint GetNImagesTPL +( + const u8 * data, // TPL data + uint data_size, // size of tpl data + const endian_func_t * endian // endian functions +); + +/////////////////////////////////////////////////////////////////////////////// + +bool SetupPointerTPL +( + const u8 * data, // TPL data + uint data_size, // size of tpl data + uint img_index, // index of image to extract + const tpl_header_t ** tpl_head, // not NULL: store pointer here + const tpl_imgtab_t ** tpl_tab, // not NULL: store pointer here + const tpl_pal_header_t ** tpl_pal, // not NULL: store pointer here + const tpl_img_header_t ** tpl_img, // not NULL: store pointer here + const u8 ** pal_data, // not NULL: store pointer to pal data + const u8 ** img_data, // not NULL: store pointer to img data + const endian_func_t * endian // endian functions +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BTI support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct bti_header_t +{ + // --> unknown field names taken from http://www.amnoid.de/gc/bti.txt + + /* 00 */ u8 iform; // image format + /* 01 */ u8 unknown_01; // 02 for 'posteffect.bti', 00 else + /* 02 */ u16 width; // width of image in pixel + /* 04 */ u16 height; // height of image in pixel + + /* 06 */ u8 wrap_s; // 01 for 'posteffect.bti', 00 else + /* 07 */ u8 wrap_t; // 01 for 'posteffect.bti', 00 else + + /* 08 */ u16 pform; // image format of palette + /* 0a */ u16 n_pal; // number of palette entries + /* 0c */ u32 pal_off; // palette offset relative file header + + /* 10 */ u32 unknown_10; // always 0 in MKWii + /* 14 */ u8 unknown_14; // always 1 in MKWii + /* 15 */ u8 unknown_15; // always 1 in MKWii + /* 16 */ u16 unknown_16; // always 0 in MKWii + /* 18 */ u8 n_image; // number of images (main+mipmaps) + /* 19 */ u8 unknown_19; // always 0 in MKWii + /* 1a */ u16 unknown_1a; // always 0 in MKWii + + /* 1c */ u32 data_off; // offset of image data relative file header + /* 20 */ +} +__attribute__ ((packed)) bti_header_t; + +/////////////////////////////////////////////////////////////////////////////// + +uint GetNImagesBTI +( + const u8 * data, // bti data + uint data_size // size of bti data +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_IMAGE_H + diff --git a/project/src/lib-image1.c b/project/src/lib-image1.c new file mode 100644 index 0000000..bf7673f --- /dev/null +++ b/project/src/lib-image1.c @@ -0,0 +1,4089 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-std.h" +#include "lib-image.h" +#include "lib-breff.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IMG management /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint image_seq_num = 0; +static int convert_depth = 0; + +//----------------------------------------------------------------------------- + +void InitializeIMG ( Image_t * img ) +{ + DASSERT(img); + noPRINT("InitializeIMG() img=%p\n",img); + + memset(img,0,sizeof(*img)); + + img->iform = IMG_INVALID; + img->pform = PAL_INVALID; + + img->info_fform = FF_INVALID; + img->info_iform = IMG_INVALID; + img->info_pform = PAL_INVALID; + + img->tform_fform0 = img->tform_fform = FF_INVALID; + img->tform_iform0 = img->tform_iform = IMG_INVALID; + img->tform_pform0 = img->tform_pform = PAL_INVALID; + + img->endian = &be_func; + img->path = EmptyString; + + //img->seq_num = ++image_seq_num; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetIMG ( Image_t * img ) +{ + DASSERT(img); + noPRINT("ResetIMG() img=%p, container=%p\n",img,img->container); + + FreeIMG(img,0); + image_seq_num--; + InitializeIMG(img); +} + +/////////////////////////////////////////////////////////////////////////////// + +void RemoveContainerIMG ( Image_t * img ) +{ + DASSERT(img); + if (img->container) + { + u8 * container = img->container; + while (img) + { + if ( img->data && !img->data_alloced ) + { + u8 *data = MALLOC(img->data_size); + memcpy(data,img->data,img->data_size); + img->data = data; + img->data_alloced = true; + } + + if ( img->pal && !img->pal_alloced ) + { + u8 *pal = MALLOC(img->pal_size); + memcpy(pal,img->pal,img->pal_size); + img->pal = pal; + img->pal_alloced = true; + } + + img->container = 0; + img = img->mipmap; + } + FREE(container); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeIMG +( + Image_t * img, // destination image, dynamic data freed + const Image_t * src // not NULL: copy parameters, but not data +) +{ + DASSERT(img); + + //--- free data + + if (img->mipmap) + FreeMipmapsIMG(img); + + FREE(img->container); + + if (img->data_alloced) + FREE(img->data); + + if (img->pal_alloced) + FREE(img->pal); + + + //--- copy param & path handling + + if ( !src ) + { + if (img->path_alloced) + FreeString(img->path); + img->path_alloced = 0; + img->path = EmptyString; + } + else if ( src != img ) + { + if (img->path_alloced) + FreeString(img->path); + + memcpy(img,src,sizeof(*img)); + + if (img->path_alloced) + img->path = STRDUP(src->path); + } + + + //--- reset dynamic data + + img->container = 0; + + img->data = 0; + img->data_size = 0; + img->data_alloced = false; + + img->pal = 0; + img->pal_size = 0; + img->pal_alloced = false; + img->n_pal = 0; + + img->seq_num = ++image_seq_num; + img->conv_count++; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeMipmapsIMG +( + Image_t * img // not NULL: remove mipmaps +) +{ + if ( img && img->mipmap ) + { + ResetIMG(img->mipmap); + FREE(img->mipmap); + img->mipmap = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountMipmapsIMG +( + const Image_t * img // count mipmaps +) +{ + DASSERT(img); + + uint count = 0; + for(;;) + { + img = img->mipmap; + if (!img) + return count; + count++; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyIMG +( + Image_t * dest, // destination image + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // valid source image + bool copy_mipmap // true: copy mipmaps too +) +{ + DASSERT(dest); + noPRINT("%*sCopyIMG() %p[%d] < %p mm=%d\n", + convert_depth*2, "", + dest, init_dest, src, src ? CountMipmapsIMG(src) : -1 ); + + if (init_dest) + InitializeIMG(dest); + + if ( src != dest ) + { + if (!init_dest) + ResetIMG(dest); + + if (src) + { + DASSERT(!dest->mipmap); + memcpy(dest,src,sizeof(*dest)); + + if ( copy_mipmap && dest->mipmap ) + { + dest->mipmap = MALLOC(sizeof(*dest->mipmap)); + CopyIMG(dest->mipmap,true,src->mipmap,true); + } + else + dest->mipmap = 0; + + if ( dest->data_alloced || dest->container ) + { + dest->data = MALLOC(dest->data_size); + memcpy(dest->data,src->data,dest->data_size); + dest->data_alloced = true; + } + + if ( dest->pal_alloced || dest->container ) + { + dest->pal = MALLOC(dest->pal_size); + memcpy(dest->pal,src->pal,dest->pal_size); + dest->pal_alloced = true; + } + + dest->container = 0; + + if (dest->path_alloced) + dest->path = STRDUP(src->path); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveIMG +( + Image_t * dest, // destination image + bool init_dest, // true: initialize 'dest' first + Image_t * src // NULL or source image, isresetted +) +{ + DASSERT(dest); + + if (init_dest) + InitializeIMG(dest); + + if ( src != dest ) + { + if (!init_dest) + ResetIMG(dest); + if (src) + { + memcpy(dest,src,sizeof(*dest)); + InitializeIMG(src); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveDataIMG +( + Image_t * dest, // valid destination image + Image_t * src // valid source image, is resetted +) +{ + DASSERT(dest); + DASSERT(src); + + if ( src != dest ) + { + if (dest->container) + FREE(dest->container); + dest->container = src->container; + src->container = 0; + + dest->iform = src->iform; + if (dest->data_alloced) + FREE(dest->data); + dest->data = src->data; + dest->data_size = src->data_size; + dest->data_alloced = src->data_alloced; + src->data_alloced = false; + + dest->pform = src->pform; + if (dest->pal_alloced) + FREE(dest->pal); + dest->pal = src->pal; + dest->pal_size = src->pal_size; + dest->n_pal = src->n_pal; + dest->pal_alloced = src->pal_alloced; + src->pal_alloced = false; + + dest->mipmap = src->mipmap; + src->mipmap = 0; + + dest->width = src->width; + dest->height = src->height; + dest->xwidth = src->xwidth; + dest->xheight = src->xheight; + + ResetIMG(src); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScanDataIMG +( + Image_t * img, // destination image + bool init_img, // true: initialize 'img' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(img); + + if (init_img) + InitializeIMG(img); + else + ResetIMG(img); + + if ( !data || !data_size ) + return; + +// [[analyse-magic]] + img->info_fform = GetByMagicFF(data,data_size,0); // [[magic]] + switch (img->info_fform) + { + case FF_TPL: + { + const tpl_header_t * tpl; + const tpl_pal_header_t * tp; + const tpl_img_header_t * ti; + if (SetupPointerTPL( data, data_size, 0, + &tpl, 0, &tp, &ti, 0, 0, &be_func )) + { + img->iform = be32(&ti->iform); + img->info_iform = img->iform; + img->width = be16(&ti->width); + img->height = be16(&ti->height); + img->info_n_image = be32(&tpl->n_image); + if (tp) + img->pform = img->info_pform = be32(&tp->pform); + } + } + break; + + case FF_BTI: + { + const bti_header_t *bti = (bti_header_t*)data; + img->iform = bti->iform; + img->info_iform = img->iform; + img->width = be16(&bti->width); + img->height = be16(&bti->height); + img->info_n_image = bti->n_image; + img->pform = be16(&bti->pform); + img->info_pform = img->pform; + } + break; + + case FF_TEX: + case FF_TEX_CT: // ??? [[CTCODE]] add ctcode info + { + const brsub_header_t *bh = (brsub_header_t*)data; + const uint n_grp = GetSectionNumBRSUB(data,data_size,&be_func); + const tex_info_t *ti = (tex_info_t*)( bh->grp_offset + n_grp ); + + img->iform = be32(&ti->iform); + img->info_iform = img->iform; + img->width = be16(&ti->width); + img->height = be16(&ti->height); + img->info_n_image = be32(&ti->n_image); + } + break; + + case FF_BREFT_IMG: + { + const breft_image_t * bi = (breft_image_t*)data; + img->iform = bi->iform; + img->info_iform = img->iform; + img->width = be16(&bi->width); + img->height = be16(&bi->height); + img->info_size = be32(&bi->img_size); + img->info_n_image = bi->n_image ? bi->n_image : 1; + } + break; + + default: + return; + } + + const ImageGeometry_t *geo = GetImageGeometry(img->iform); + if (geo) + { + img->xwidth = ALIGN32(img->width, geo->block_width); + img->xheight = ALIGN32(img->height,geo->block_height); + } + else + { + img->xwidth = EXPAND8(img->width); + img->xheight = EXPAND8(img->height); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NormalizeFrameIMG +( + Image_t * img // pointer to valid img +) +{ + bool status = false; + + if ( img->iform >= IMG_X__MIN && img->iform <= IMG_X__MAX ) + { + const uint cell_size = img->iform == IMG_X_RGB ? 4 : 2; + // IMG_X_GRAY & IMG_X_PAL* : size is 2 + + if ( img->width > 0 && img->xwidth > img->width ) + { + const uint fill_size = ( img->xwidth - img->width ) * cell_size; + u8 *row = img->data + img->width * cell_size; + uint ih = img->height; + while ( ih-- > 0 ) + { + u8 *src = row - cell_size; + u8 *dest = row; + u8 *end = row + fill_size; + while ( dest < end ) + *dest++ = *src++; + row += img->xwidth * cell_size; + } + status = true; + } + + if ( img->height > 0 && img->xheight > img->height ) + { + const uint line_size = img->xwidth * cell_size; + u8 *dest = img->data + img->height * line_size; + u8 *end = img->data + img->xheight * line_size; + u8 *src = dest - line_size; + while ( dest < end ) + *dest++ = *src++; + status = true; + } + } + + return status; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckAlphaIMG +( + const Image_t * img, // pointer to valid img + bool force // true: don't believe 'img->alpha_status' +) +{ + DASSERT(img); + if ( !force && img->alpha_status ) + return img->alpha_status; + + uint size = 4; + switch (img->iform) + { + case IMG_X_GRAY: + size = 2; + case IMG_X_RGB: + { + const u8 * data = img->data + size - 1; + uint ih = img->height; + while ( ih-- > 0 ) + { + uint iw = img->width; + while ( iw-- > 0 ) + { + if ( *data != 0xff ) + return ((Image_t*)img)->alpha_status = 1; + data += size; + } + data += ( img->xwidth - img->width ) * size; + } + return ((Image_t*)img)->alpha_status = -1; + } + + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + if (img->n_pal) + { + DASSERT(img->pal); + uint n = img->n_pal; + const u8 * data = img->pal + 3; + while ( n-- > 0 ) + { + if ( *data != 0xff ) + return ((Image_t*)img)->alpha_status = 1; + data += 4; + } + } + return ((Image_t*)img)->alpha_status = -1; + + default: + { + const ImageGeometry_t *geo = GetImageGeometry(img->iform); + return ((Image_t*)img)->alpha_status + = geo && !geo->has_alpha ? -1 : 0; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsGrayIMG +( + const Image_t * img // pointer to valid img +) +{ + DASSERT(img); + image_format_t xform; + NormalizeIF(&xform,img->iform,IMG_X_RGB,img->iform); + return xform == IMG_X_GRAY; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// conversion helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 * AllocDataIMG +( + const Image_t * src, // image template (read width and height) + uint bytes_per_pix, // number of bytes per pixel + uint * data_size // not NULL: store data size +) +{ + DASSERT(src); + + const uint size = EXPAND8(src->width) * EXPAND8(src->height) * bytes_per_pix; + if (data_size) + *data_size = size; + return CALLOC(1,size); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AssignDataGRAY +( + Image_t * dest, // valid destination + const Image_t * src, // NULL or image template + u8 * data // with AllocDataIMG() alloced data +) +{ + DASSERT(dest); + DASSERT(data); + + FreeIMG(dest,src); + + dest->data = data; + dest->data_alloced = true; + dest->xwidth = EXPAND8(dest->width); + dest->xheight = EXPAND8(dest->height); + dest->data_size = dest->xwidth * dest->xheight * 2; + dest->iform = IMG_X_GRAY; + dest->is_grayed = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignDataRGB +( + Image_t * dest, // valid destination + const Image_t * src, // NULL or image template + u8 * data // with AllocDataIMG() alloced data +) +{ + DASSERT(dest); + DASSERT(data); + + FreeIMG(dest,src); + + dest->data = data; + dest->data_alloced = true; + dest->xwidth = EXPAND8(dest->width); + dest->xheight = EXPAND8(dest->height); + dest->data_size = dest->xwidth * dest->xheight * 4; + dest->iform = IMG_X_RGB; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AssignDataPAL +( + Image_t * dest, // valid destination + const Image_t * src, // NULL or image template + u16 * itab, // with AllocDataIMG() alloced data + u8 * pal_data, // palette data + uint size_pal, // NULL or size of 'pal_data' + uint n_pal, // number of used palette entries + image_format_t iform, // palette image format + int alpha_status // alpha status of image +) +{ + DASSERT(dest); + DASSERT(itab); + DASSERT(pal_data); + DASSERT( iform >= IMG_X_PAL__MIN && iform <= IMG_X_PAL__MAX ); + + FreeIMG(dest,src); + + dest->data = (u8*)itab; + dest->data_alloced = true; + dest->xwidth = EXPAND8(dest->width); + dest->xheight = EXPAND8(dest->height); + dest->data_size = dest->xwidth * dest->xheight * 2; + dest->iform = iform; + dest->alpha_status = alpha_status; + + dest->pform = PAL_X_RGB; + dest->pal = pal_data; + dest->pal_size = size_pal ? size_pal : n_pal * 4; + dest->pal_alloced = true; + dest->n_pal = n_pal; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AssignData +( + Image_t * dest, // valid destination + const Image_t * src, // NULL or image template + u8 * data, // alloced data + uint data_size, // size of 'data' + image_format_t iform // image format +) +{ + DASSERT(dest); + DASSERT(data); + + FreeIMG(dest,src); + + dest->data = data; + dest->data_alloced = true; + dest->data_size = data_size; + dest->xwidth = EXPAND8(dest->width); + dest->xheight = EXPAND8(dest->height); + dest->iform = iform; + dest->pform = PAL_INVALID; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CalcImageBlock +( + const Image_t * img, // image template (read width and height) + uint bits_per_pixel, // number of bits per pixel + uint block_width, // width of a single block + uint block_height, // height of a single block + + uint * h_blocks, // not NULL: store number of horizontal blocks + uint * v_blocks, // not NULL: store number of vertical blocks + uint * img_size, // not NULL: return image size + + bool calc_only // true: do no validation +) +{ + DASSERT(img); + + uint xwidth, xheight; + const uint size = CalcImageSize( img->width, img->height, + bits_per_pixel, block_width, block_height, + &xwidth, &xheight, h_blocks, v_blocks ); + if (img_size) + *img_size = size; + + if ( calc_only || xwidth > 0 && xheight > 0 && size <= img->data_size ) + return ERR_OK; + + PRINT("h=%u,%u,%u v=%u,%u,%u, size=%u/%u <= %x/%x\n", + img->width, block_width, xwidth, + img->height, block_height, xheight, + size, img->data_size, size, img->data_size ); + + return ERROR0(ERR_INVALID_IFORM, + "Impossible geometry of image [0x%02x=%s]: %s\n", + img->iform, GetImageFormatName(img->iform,"?"), img->path ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// palette helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError TransformPalette +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform, // new image format, only valid IMG_X_PAL* + u16 * itab // index table (2*xwidth*xheight), alloced +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT(itab); + DASSERT( iform >= IMG_X_PAL__MIN && iform <= IMG_X_PAL__MAX ); + + + //--- create palette + + const uint n_idx = GetPaletteCountIF(iform); + DASSERT( n_idx ); + DASSERT( n_idx * 4 <= sizeof(iobuf) ); + u8 *pal_data = CALLOC(n_idx,4); + u8 *pdest = pal_data; + + u16 (*rd16) ( const void * data_ptr ) = src_img->endian->rd16; + //u32 (*rd32) ( const void * data_ptr ) = src_img->endian->rd32; + + const u8 * src = src_img->pal; + const uint n_pal = src ? src_img->n_pal : 0; + uint pi; + + int alpha_status = 0; // don't know + + switch (src_img->pform) + { + #if 0 + case PAL_I4: + alpha_status = -1; + for ( pi = n_pal/2; pi > 0; pi-- ) + { + const u8 val = *src++; + const u8 gray1 = cc48[ val >> 4 ]; + *pdest++ = gray1; // red + *pdest++ = gray1; // green + *pdest++ = gray1; // blue + *pdest++ = 0xff; // alpha + const u8 gray2 = cc48[ val & 0x0f ]; + *pdest++ = gray2; // red + *pdest++ = gray2; // green + *pdest++ = gray2; // blue + *pdest++ = 0xff; // alpha + } + break; + #endif + + #if 0 + case PAL_I8: + alpha_status = -1; + for ( pi = n_pal; pi > 0; pi-- ) + { + const u8 gray = *src++; + *pdest++ = gray; // red + *pdest++ = gray; // green + *pdest++ = gray; // blue + *pdest++ = 0xff; // alpha + } + break; + #endif + + #if 0 + case PAL_IA4: + for ( pi = n_pal; pi > 0; pi-- ) + { + const u8 val = *src++; + const u8 gray = cc48[ val & 0x0f ]; + *pdest++ = gray; // red + *pdest++ = gray; // green + *pdest++ = gray; // blue + *pdest++ = cc48[ val >> 4 ]; // alpha + } + break; + #endif + + case PAL_IA8: + for ( pi = n_pal; pi > 0; pi-- ) + { + const u16 val = rd16(src); + src += 2; + *pdest++ = val; // red + *pdest++ = val; // green + *pdest++ = val; // blue + *pdest++ = val >> 8; // alpha + } + break; + + case PAL_RGB565: + alpha_status = -1; + for ( pi = n_pal; pi > 0; pi-- ) + { + const u16 val = rd16(src); + src += 2; + *pdest++ = cc58[ val >> 11 ]; // red + *pdest++ = cc68[ val >> 5 & 0x3f ]; // green + *pdest++ = cc58[ val & 0x1f ]; // blue + *pdest++ = 0xff; // alpha + } + break; + + case PAL_RGB5A3: + for ( pi = n_pal; pi > 0; pi-- ) + { + const u16 val = rd16(src); + src += 2; + if ( val & 0x8000 ) + { + *pdest++ = cc58[ val >> 10 & 0x1f ]; // red + *pdest++ = cc58[ val >> 5 & 0x1f ]; // green + *pdest++ = cc58[ val & 0x1f ]; // blue + *pdest++ = 0xff; // alpha + } + else + { + *pdest++ = cc48[ val >> 8 & 0x0f ]; // red + *pdest++ = cc48[ val >> 4 & 0x0f ]; // green + *pdest++ = cc48[ val & 0x0f ]; // blue + *pdest++ = cc38[ val >> 12 & 0x07 ]; // alpha + } + } + break; + +#if 0 // [[not-needed]] not needed yet 2011-06 + case PAL_RGBA32: + //alpha_status = 0; + for ( pi = n_pal; pi > 0; pi-- ) + { + #if 1 // unkown, which variant will be true + const u32 val = rd32(src); + src += 4; + *pdest++ = val >> 16; // red + *pdest++ = val >> 8; // green + *pdest++ = val; // blue + *pdest++ = val >> 24; // alpha + #else + const u16 ar = rd16(src); + const u16 gb = rd16(src+n_pal*2); // delta unclear ??? + src += 2; + *pdest++ = ar; // red + *pdest++ = gb >> 8; // green + *pdest++ = gb; // blue + *pdest++ = ar >> 8; // alpha + #endif + } + break; + #endif + + default: + FREE(pal_data); + FREE(itab); + return ERROR0(ERR_INVALID_IFORM, + "Palette format 0x%02x [%s] not supported: %s\n", + src_img->pform, GetImageFormatName(src_img->pform,"?"), src_img->path ); + } + + + //--- create image using the palette + + AssignDataPAL( dest_img, src_img, itab, + pal_data, 0, n_pal, iform, alpha_status ); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// convert gray : rgb : palette /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertToRGB +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + + //--- mipmap support + + if (src_img->mipmap) // --> do the conversion for each image standalone! + { + if ( src_img != dest_img ) + CopyIMG(dest_img,false,src_img,true); // this makes it easier to handle + RemoveContainerIMG(dest_img); + DASSERT(!dest_img->container); + + while (dest_img) + { + Image_t *next = dest_img->mipmap; + dest_img->mipmap = 0; + enumError err = ConvertToRGB(dest_img,dest_img,pform); + dest_img->mipmap = next; + if (err) + return err; + dest_img = next; + } + return ERR_OK; + } + + + //--- convert image + + if ( src_img->iform == IMG_X_RGB ) + { + PRINT("%*sCONVERT: RGB {%u,%u} -> RGB {%u,%u}\n", + convert_depth*2,"", + src_img->conv_count, src_img->seq_num, + dest_img->conv_count, dest_img->seq_num ); + CopyIMG(dest_img,false,src_img,true); + } + else if ( src_img->iform == IMG_X_GRAY ) + { + PRINT("%*sCONVERT: GRAY {%u,%u} -> RGB {%u,%u}\n", + convert_depth*2,"", + src_img->conv_count, src_img->seq_num, + dest_img->conv_count, dest_img->seq_num ); + + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + DASSERT( EXPAND8(src_img->height) == src_img->xheight ); + + const u8 *src = src_img->data; + const u8 *src_end = src + 2 * src_img->xwidth * src_img->xheight; + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest = data; + + while ( src < src_end ) + { + const u8 val = *src++; + *dest++ = val; + *dest++ = val; + *dest++ = val; + *dest++ = *src++; + } + + DASSERT( dest == data + data_size ); + AssignDataRGB(dest_img,src_img,data); + } + else if ( src_img->iform >= IMG_X_PAL__MIN && src_img->iform <= IMG_X_PAL__MAX ) + { + PRINT("%*sCONVERT: PAL {%u,%u} -> RGB {%u,%u}\n", + convert_depth*2,"", + src_img->conv_count, src_img->seq_num, + dest_img->conv_count, dest_img->seq_num ); + + uint data_size; + u8 *data = AllocDataIMG(src_img,4,&data_size); + + const uint n_pal = src_img->n_pal; + if (!n_pal) + memset(data,0xff,data_size); + else + { + DASSERT(src_img->pal); + const u32 * pal = (u32*)src_img->pal; + + const u16 *src = (u16*)src_img->data; + const u16 *src_end = src + src_img->xwidth * src_img->xheight; + + u8 *dest = data; + + while ( src < src_end ) + { + const u16 val = *src++; + memcpy( dest, pal + ( val < n_pal ? val : 0 ), 4 ); + dest += 4; + } + + DASSERT( dest == data + data_size ); + } + + AssignDataRGB(dest_img,src_img,data); + } + else + ASSERT(0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertToGRAY +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + + //--- mipmap support + + if (src_img->mipmap) // --> do the conversion for each image standalone! + { + if ( src_img != dest_img ) + CopyIMG(dest_img,false,src_img,true); // this makes it easier to handle + RemoveContainerIMG(dest_img); + DASSERT(!dest_img->container); + + while (dest_img) + { + Image_t *next = dest_img->mipmap; + dest_img->mipmap = 0; + enumError err = ConvertToGRAY(dest_img,dest_img,pform); + dest_img->mipmap = next; + if (err) + return err; + dest_img = next; + } + return ERR_OK; + } + + + //--- convert image + + if ( src_img->iform == IMG_X_GRAY ) + { + PRINT("%*sCONVERT: GRAY {%u,%u} -> GRAY {%u,%u}\n", + convert_depth*2,"", + src_img->conv_count, src_img->seq_num, + dest_img->conv_count, dest_img->seq_num ); + + CopyIMG(dest_img,false,src_img,true); + return ERR_OK; + } + + if ( src_img->iform >= IMG_X_PAL__MIN && src_img->iform <= IMG_X_PAL__MAX ) + { + convert_depth++; + enumError err = ConvertToRGB(dest_img,src_img,pform); + convert_depth--; + if (err) + return err; + src_img = dest_img; + } + + if ( src_img->iform == IMG_X_RGB ) + { + PRINT("%*sCONVERT: RGB {%u,%u} -> GRAY {%u,%u}\n", + convert_depth*2,"", + src_img->conv_count, src_img->seq_num, + dest_img->conv_count, dest_img->seq_num ); + + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + DASSERT( EXPAND8(src_img->height) == src_img->xheight ); + + const u8 *src = src_img->data; + const u8 *src_end = src + 4 * src_img->xwidth * src_img->xheight; + + uint data_size; + u8 * data = AllocDataIMG(src_img,2,&data_size); + u8 * dest = data; + + while ( src < src_end ) + { + *dest++ = ( src[0] + src[1] + src[2] + 1 ) / 3; + src += 3; + *dest++ = *src++; + } + + DASSERT( dest == data + data_size ); + AssignDataGRAY(dest_img,src_img,data); + } + else + ASSERT(0); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertToPALETTE +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + uint max_pal, // maximum palette values, 0 = auto + image_format_t iform // if a valid PAL format: + // convert to this and + // reduce 'max_pal' if neccessary +) +{ + DASSERT(dest_img); + DASSERT(src_img); + + + //--- mipmap support + + if (src_img->mipmap) // --> do the conversion for each image standalone! + { + if ( src_img != dest_img ) + CopyIMG(dest_img,false,src_img,true); // this makes it easier to handle + RemoveContainerIMG(dest_img); + DASSERT(!dest_img->container); + + while (dest_img) + { + Image_t *next = dest_img->mipmap; + dest_img->mipmap = 0; + enumError err = ConvertToPALETTE(dest_img,dest_img,max_pal,iform); + dest_img->mipmap = next; + if (err) + return err; + dest_img = next; + } + return ERR_OK; + } + + + //--- convert image + + bool auto_mode = false; + uint pal_limit = 0; + switch(iform) + { + case IMG_X_PAL4: + pal_limit = 0x10; + break; + + case IMG_X_PAL8: + pal_limit = 0x100; + break; + + case IMG_X_PAL14: + pal_limit = 0x4000; + break; + + default: + iform = IMG_X_PAL; + pal_limit = 0x4000; + auto_mode = true; + break; + } + + if ( !max_pal || max_pal > pal_limit ) + max_pal = pal_limit; + + if ( src_img->iform >= IMG_X_PAL__MIN + && src_img->iform <= IMG_X_PAL__MAX + && src_img->n_pal <= max_pal ) + { + CopyIMG(dest_img,false,src_img,true); + if (auto_mode) + { + if ( dest_img->n_pal <= 0x10 ) + iform = IMG_X_PAL4; + else if ( dest_img->n_pal <= 0x100 ) + iform = IMG_X_PAL8; + else + iform = IMG_X_PAL14; + } + dest_img->iform = iform; + return ERR_OK; + } + + if ( src_img->iform != IMG_X_RGB ) + { + convert_depth++; + enumError err = ConvertToRGB(dest_img,src_img,PAL_AUTO); + convert_depth--; + if (err) + return err; + src_img = dest_img; + } + + PRINT("%*sCONVERT: RGB {%u,%u} -> PAL {%u,%u}\n", + convert_depth*2,"", + src_img->conv_count, src_img->seq_num, + dest_img->conv_count, dest_img->seq_num ); + if ( src_img->iform != IMG_X_RGB ) + ERROR0(ERR_INTERNAL,0); + + u16 *index = (u16*)AllocDataIMG(src_img,2,0); + u8 *pal_data = CALLOC(max_pal,4); + uint n_pal = MedianCut( (u32*)src_img->data, index, + src_img->width, src_img->xwidth, src_img->height, + (u32*)pal_data, max_pal ); + + if (auto_mode) + { + if ( n_pal <= 0x10 ) + iform = IMG_X_PAL4; + else if ( n_pal <= 0x100 ) + iform = IMG_X_PAL8; + else + iform = IMG_X_PAL14; + } + + AssignDataPAL( dest_img, src_img, + index, pal_data, max_pal*4, n_pal, iform, 0 ); + return ERR_OK; +} + +//----------------------------------------------------------------------------- + +static enumError conv_to_palette_auto +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + return ConvertToPALETTE(dest_img,src_img,0,IMG_X_PAL); +} + +//----------------------------------------------------------------------------- + +static enumError conv_to_palette_4 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + return ConvertToPALETTE(dest_img,src_img,0,IMG_X_PAL8); +} + +//----------------------------------------------------------------------------- + +static enumError conv_to_palette_8 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + return ConvertToPALETTE(dest_img,src_img,0,IMG_X_PAL8); +} + +//----------------------------------------------------------------------------- + +static enumError conv_to_palette_14 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + return ConvertToPALETTE(dest_img,src_img,0,IMG_X_PAL14); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// conv_from_*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_I4 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_I4 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 4; + const uint block_width = 8; + const uint block_height = 8; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + if ( iform == IMG_X_RGB ) + { + // special RGB conversion + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width/2; + while ( ih-- > 0 ) + { + const u8 val = *src++; + const u8 rgb1 = cc48[ val >> 4 ]; + *dest3++ = rgb1; // red + *dest3++ = rgb1; // green + *dest3++ = rgb1; // blue + *dest3++ = 0xff; // alpha + + const u8 rgb2 = cc48[ val & 0x0f ]; + *dest3++ = rgb2; // red + *dest3++ = rgb2; // green + *dest3++ = rgb2; // blue + *dest3++ = 0xff; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + } + else + { + // use GRAY conversion elsewise + + uint data_size; + u8 * data = AllocDataIMG(src_img,2,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 2; + const uint line_size = EXPAND8(src_img->width) * 2; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width/2; + while ( ih-- > 0 ) + { + const u8 val = *src++; + *dest3++ = cc48[ val >> 4 ]; // gray + *dest3++ = 0xff; // alpha + *dest3++ = cc48[ val & 0x0f ]; // gray + *dest3++ = 0xff; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataGRAY(dest_img,src_img,data); + } + + dest_img->alpha_status = -1; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_I8 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_I8 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 8; + const uint block_width = 8; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + if ( iform == IMG_X_RGB ) + { + // special RGB conversion + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u8 val = *src++; + *dest3++ = val; // red + *dest3++ = val; // green + *dest3++ = val; // blue + *dest3++ = 0xff; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + } + else + { + // use GRAY conversion elsewise + + uint data_size; + u8 * data = AllocDataIMG(src_img,2,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 2; + const uint line_size = EXPAND8(src_img->width) * 2; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + *dest3++ = *src++; // gray + *dest3++ = 0xff; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataGRAY(dest_img,src_img,data); + } + + dest_img->alpha_status = -1; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_IA4 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_IA4 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 8; + const uint block_width = 8; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + if ( iform == IMG_X_RGB ) + { + // special RGB conversion + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u8 val = *src++; + const u8 rgb = cc48[ val & 0x0f ]; + *dest3++ = rgb; // red + *dest3++ = rgb; // green + *dest3++ = rgb; // blue + *dest3++ = cc48[ val >> 4 ]; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + } + else + { + // use GRAY conversion elsewise + + uint data_size; + u8 * data = AllocDataIMG(src_img,2,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 2; + const uint line_size = EXPAND8(src_img->width) * 2; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u8 val = *src++; + *dest3++ = cc48[ val & 0x0f ]; // gray + *dest3++ = cc48[ val >> 4 ]; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataGRAY(dest_img,src_img,data); + } + + dest_img->alpha_status = 0; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_IA8 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_IA8 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + u16 (*rd16) ( const void * data_ptr ) = src_img->endian->rd16; + noPRINT("from_IA8: wh=%u*%u, blk=%u,%u -> nb=%u,%u\n", + src_img->width, src_img->height, + block_width, block_height, + h_blocks, v_blocks ); + + if ( iform == IMG_X_RGB ) + { + // special RGB conversion + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u16 val = rd16(src); + src += 2; + *dest3++ = val; // red + *dest3++ = val; // green + *dest3++ = val; // blue + *dest3++ = val >> 8; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + } + else + { + // use GRAY conversion elsewise + + uint data_size; + u8 * data = AllocDataIMG(src_img,2,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 2; + const uint line_size = EXPAND8(src_img->width) * 2; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u16 val = rd16(src); + src += 2; + *dest3++ = val; // gray + *dest3++ = val >> 8; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataGRAY(dest_img,src_img,data); + } + + dest_img->alpha_status = 0; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_RGB565 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_RGB565 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + u16 (*rd16) ( const void * data_ptr ) = src_img->endian->rd16; + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u16 val = rd16(src); + src += 2; + *dest3++ = cc58[ val >> 11 ]; // red + *dest3++ = cc68[ val >> 5 & 0x3f ]; // green + *dest3++ = cc58[ val & 0x1f ]; // blue + *dest3++ = 0xff; // alpha + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + dest_img->alpha_status = -1; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_RGB5A3 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_RGB5A3 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + u16 (*rd16) ( const void * data_ptr ) = src_img->endian->rd16; + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u16 val = rd16(src); + src += 2; + if ( val & 0x8000 ) + { + *dest3++ = cc58[ val >> 10 & 0x1f ]; // red + *dest3++ = cc58[ val >> 5 & 0x1f ]; // green + *dest3++ = cc58[ val & 0x1f ]; // blue + *dest3++ = 0xff; // alpha + } + else + { + *dest3++ = cc48[ val >> 8 & 0x0f ]; // red + *dest3++ = cc48[ val >> 4 & 0x0f ]; // green + *dest3++ = cc48[ val & 0x0f ]; // blue + *dest3++ = cc38[ val >> 12 & 0x07 ]; // alpha + } + } + dest3 += line_size - block_size; + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + dest_img->alpha_status = 0; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_RGBA32 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_RGBA32 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + + const uint bits_per_pixel = 32; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + u16 (*rd16) ( const void * data_ptr ) = src_img->endian->rd16; + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u8 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + const u16 ar = rd16(src); + const u16 gb = rd16(src+0x20); + src += 2; + *dest3++ = ar; // red + *dest3++ = gb >> 8; // green + *dest3++ = gb; // blue + *dest3++ = ar >> 8; // alpha + } + dest3 += line_size - block_size; + } + src += block_height * block_width * 2; + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + dest_img->alpha_status = 0; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_C4 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_C4 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 4; + const uint block_width = 8; + const uint block_height = 8; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + // here we transform the data to a linear 16-bit palette-index table + + const uint xwidth = EXPAND8(src_img->width); + const uint xheight = EXPAND8(src_img->height); + u16 * itab = CALLOC( xwidth * xheight, 2 ); + u16 * dest1 = itab; + const u8 *src = src_img->data; + + while ( v_blocks-- > 0 ) + { + u16 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u16 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width/2; + while ( ih-- > 0 ) + { + const u8 val = *src++; + *dest3++ = val >> 4; + *dest3++ = val & 0x0f; + } + dest3 += xwidth - block_width; + } + dest2 += block_width; + } + dest1 += xwidth * block_height; + } + noPRINT("src=0x%zx/0x%x = %zu/%u [%u*%u]\n", + src - src_img->data, img_size, + src - src_img->data, img_size, + src_img->width, src_img->height ); + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= itab + xwidth * xheight * 2 ); + + //--- now do the palette transformation + + return TransformPalette(dest_img,src_img,IMG_X_PAL4,itab); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_C8 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_C8 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 8; + const uint block_width = 8; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + // here we transform the data to a linear 16-bit palette-index table + + const uint xwidth = EXPAND8(src_img->width); + const uint xheight = EXPAND8(src_img->height); + u16 * itab = CALLOC( xwidth * xheight, 2 ); + u16 * dest1 = itab; + const u8 *src = src_img->data; + + while ( v_blocks-- > 0 ) + { + u16 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u16 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + *dest3++ = *src++; + dest3 += xwidth - block_width; + } + dest2 += block_width; + } + dest1 += xwidth * block_height; + } + noPRINT("src=0x%zx/0x%x = %zu/%u [%u*%u]\n", + src - src_img->data, img_size, + src - src_img->data, img_size, + src_img->width, src_img->height ); + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= itab + xwidth * xheight * 2 ); + + //--- now do the palette transformation + + return TransformPalette(dest_img,src_img,IMG_X_PAL8,itab); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_C14X2 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_C14X2 ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + // here we transform the data to a linear 16-bit palette-index table + + u16 (*rd16) ( const void * data_ptr ) = src_img->endian->rd16; + + const uint xwidth = EXPAND8(src_img->width); + const uint xheight = EXPAND8(src_img->height); + u16 * itab = CALLOC( xwidth * xheight, 2 ); + u16 * dest1 = itab; + const u8 *src = src_img->data; + + while ( v_blocks-- > 0 ) + { + u16 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + u16 * dest3 = dest2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + *dest3++ = rd16(src) & 0x3fff; + src += 2; + } + dest3 += xwidth - block_width; + } + dest2 += block_width; + } + dest1 += xwidth * block_height; + } + noPRINT("src=0x%zx/0x%x = %zu/%u [%u*%u]\n", + src - src_img->data, img_size, + src - src_img->data, img_size, + src_img->width, src_img->height ); + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= itab + xwidth * xheight * 2 ); + + //--- now do the palette transformation + + return TransformPalette(dest_img,src_img,IMG_X_PAL14,itab); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_from_CMPR +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform // new image format, only valid IMG_X_* +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform == IMG_CMPR ); + DASSERT( iform >= IMG_X__MIN && iform <= IMG_X__MAX ); + + const uint bits_per_pixel = 4; + const uint block_width = 8; + const uint block_height = 8; + + uint h_blocks, v_blocks, img_size; + enumError err = CalcImageBlock(src_img, + bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, false ); + if (err) + return err; + + u16 (*rd16) ( const void * data_ptr ) = src_img->endian->rd16; + + uint data_size; + u8 * data = AllocDataIMG(src_img,4,&data_size); + u8 * dest1 = data; + const u8 *src = src_img->data; + + const uint block_size = block_width * 4; + const uint line_size = EXPAND8(src_img->width) * 4; + const uint delta[] = { 0, 16, 4*line_size, 4*line_size+16 }; + + while ( v_blocks-- > 0 ) + { + u8 * dest2 = dest1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + uint subb; + for ( subb = 0; subb < 4; subb++ ) + { + u8 palette[4][4], *pal = palette[0]; + + const u16 val1 = rd16(src); + src += 2; + *pal++ = cc58[ val1 >> 11 & 0x1f ]; + *pal++ = cc68[ val1 >> 5 & 0x3f ]; + *pal++ = cc58[ val1 & 0x1f ]; + *pal++ = 0xff; + + const u16 val2 = rd16(src); + src += 2; + *pal++ = cc58[ val2 >> 11 & 0x1f ]; + *pal++ = cc68[ val2 >> 5 & 0x3f ]; + *pal++ = cc58[ val2 & 0x1f ]; + *pal++ = 0xff; + + if ( val1 > val2 ) + { + *pal++ = ( 2 * palette[0][0] + palette[1][0] ) / 3; + *pal++ = ( 2 * palette[0][1] + palette[1][1] ) / 3; + *pal++ = ( 2 * palette[0][2] + palette[1][2] ) / 3; + *pal++ = 0xff; + + *pal++ = ( 2 * palette[1][0] + palette[0][0] ) / 3; + *pal++ = ( 2 * palette[1][1] + palette[0][1] ) / 3; + *pal++ = ( 2 * palette[1][2] + palette[0][2] ) / 3; + *pal++ = 0xff; + } + else + { + *pal++ = ( palette[0][0] + palette[1][0] ) / 2; + *pal++ = ( palette[0][1] + palette[1][1] ) / 2; + *pal++ = ( palette[0][2] + palette[1][2] ) / 2; + *pal++ = 0xff; + + *pal++ = 0; + *pal++ = 0; + *pal++ = 0; + *pal++ = 0; + } + + u8 *dest3 = dest2 + delta[subb]; + uint i; + for ( i = 0; i < 4; i++ ) + { + u8 val = *src++; + memcpy(dest3+12, palette[ val & 3 ], 4); val >>= 2; + memcpy(dest3+ 8, palette[ val & 3 ], 4); val >>= 2; + memcpy(dest3+ 4, palette[ val & 3 ], 4); val >>= 2; + memcpy(dest3 , palette[ val & 3 ], 4); + dest3 += line_size; + } + } + dest2 += block_size; + } + dest1 += line_size * block_height; + } + + DASSERT( src == src_img->data + img_size ); + DASSERT( dest1 <= data + data_size ); + AssignDataRGB(dest_img,src_img,data); + dest_img->alpha_status = 0; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// conv_to_*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_I4 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_GRAY + + if ( src_img->iform != IMG_X_GRAY ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_GRAY,PAL_INVALID); + if (err) + return err; + src_img = dest_img; + } + DASSERT( src_img->iform == IMG_X_GRAY ); + + + //--- and now convert IMG_X_GRAY -> I4 + + const uint bits_per_pixel = 4; + const uint block_width = 8; + const uint block_height = 8; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock( src_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + + // use GRAY conversion elsewise + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = src_img->data; + + const uint block_size = block_width * 2; + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + const uint line_size = src_img->xwidth * 2; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u8 *src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width/2; + while ( ih-- > 0 ) + { + *dest++ = cc84[ src3[0] ] << 4 | cc84[ src3[2] ]; + src3 += 4; + } + src3 += line_size - block_size; + } + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= src_img->data + src_img->data_size ); + + AssignData(dest_img,src_img,data,img_size,IMG_I4); + dest_img->is_grayed = true; + dest_img->alpha_status = -1; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_I8 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_GRAY + + if ( src_img->iform != IMG_X_GRAY ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_GRAY,PAL_INVALID); + if (err) + return err; + src_img = dest_img; + } + DASSERT( src_img->iform == IMG_X_GRAY ); + + + //--- and now convert IMG_X_GRAY -> I8 + + const uint bits_per_pixel = 8; + const uint block_width = 8; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock( src_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = src_img->data; + + const uint block_size = block_width * 2; + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + const uint line_size = src_img->xwidth * 2; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u8 *src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + *dest++ = *src3; // gray + src3 += 2; // skip alpha + } + src3 += line_size - block_size; + } + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= src_img->data + src_img->data_size ); + + AssignData(dest_img,src_img,data,img_size,IMG_I8); + dest_img->is_grayed = true; + dest_img->alpha_status = -1; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_IA4 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_GRAY + + if ( src_img->iform != IMG_X_GRAY ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_GRAY,PAL_INVALID); + if (err) + return err; + src_img = dest_img; + } + DASSERT( src_img->iform == IMG_X_GRAY ); + + + //--- and now convert IMG_X_GRAY -> IA4 + + const uint bits_per_pixel = 8; + const uint block_width = 8; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock(src_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = src_img->data; + + const uint block_size = block_width * 2; + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + const uint line_size = src_img->xwidth * 2; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u8 *src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + *dest++ = cc84[ src3[1] ] << 4 | cc84[ src3[0] ]; + src3 += 2; + } + src3 += line_size - block_size; + } + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= src_img->data + src_img->data_size ); + + AssignData(dest_img,src_img,data,img_size,IMG_IA4); + dest_img->is_grayed = true; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_IA8 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_GRAY + + if ( src_img->iform != IMG_X_GRAY ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_GRAY,PAL_INVALID); + if (err) + return err; + src_img = dest_img; + } + DASSERT( src_img->iform == IMG_X_GRAY ); + + + //--- and now convert IMG_X_GRAY -> IA8 + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock( src_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + void (*wr16) ( void*, u16 ) = src_img->endian->wr16; + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = src_img->data; + + const uint block_size = block_width * 2; + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + const uint line_size = src_img->xwidth * 2; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u8 *src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + wr16( dest, src3[0] | (u16)src3[1] << 8 ); + dest += 2; + src3 += 2; + } + src3 += line_size - block_size; + } + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= src_img->data + src_img->data_size ); + + AssignData(dest_img,src_img,data,img_size,IMG_IA8); + dest_img->is_grayed = true; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_RGB565 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_RGB + + if ( src_img->iform != IMG_X_RGB ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_RGB,PAL_INVALID); + if (err) + return err; + src_img = dest_img; + } + DASSERT( src_img->iform == IMG_X_RGB ); + + + //--- and now convert IMG_X_RGB -> RGB565 + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock( src_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + void (*wr16) ( void*, u16 ) = src_img->endian->wr16; + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = src_img->data; + + const uint block_size = block_width * 4; + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + const uint line_size = src_img->xwidth * 4; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u8 *src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + wr16( dest, cc85[ src3[0] ] << 11 + | cc86[ src3[1] ] << 5 + | cc85[ src3[2] ] ); + dest += 2; + src3 += 4; + } + src3 += line_size - block_size; + } + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= src_img->data + src_img->data_size ); + + AssignData(dest_img,src_img,data,img_size,IMG_RGB565); + dest_img->alpha_status = -1; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_RGB5A3 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_RGB + + if ( src_img->iform != IMG_X_RGB ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_RGB,PAL_INVALID); + if (err) + return err; + src_img = dest_img; + } + DASSERT( src_img->iform == IMG_X_RGB ); + + + //--- and now convert IMG_X_RGB -> RGB5A3 + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock( src_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + void (*wr16) ( void*, u16 ) = src_img->endian->wr16; + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = src_img->data; + + const uint block_size = block_width * 4; + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + const uint line_size = src_img->xwidth * 4; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u8 *src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + if ( src3[3] == 0xff ) + wr16( dest, cc85[ src3[0] ] << 10 + | cc85[ src3[1] ] << 5 + | cc85[ src3[2] ] + | 0x8000 ); + else + wr16( dest, cc84[ src3[0] ] << 8 + | cc84[ src3[1] ] << 4 + | cc84[ src3[2] ] + | cc83[ src3[3] ] << 12 ); + dest += 2; + src3 += 4; + } + src3 += line_size - block_size; + } + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= src_img->data + src_img->data_size ); + + AssignData(dest_img,src_img,data,img_size,IMG_RGB5A3); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_RGBA32 +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_RGB + + if ( src_img->iform != IMG_X_RGB ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_RGB,PAL_INVALID); + if (err) + return err; + src_img = dest_img; + } + DASSERT( src_img->iform == IMG_X_RGB ); + + + //--- and now convert IMG_X_RGB -> RGBA32 + + const uint bits_per_pixel = 32; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock( src_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + void (*wr16) ( void*, u16 ) = src_img->endian->wr16; + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = src_img->data; + + const uint block_size = block_width * 4; + DASSERT( EXPAND8(src_img->width) == src_img->xwidth ); + const uint line_size = src_img->xwidth * 4; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u8 *src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + wr16( dest, src3[0] | (u16)src3[3] << 8 ); + wr16( dest+0x20, src3[2] | (u16)src3[1] << 8 ); + dest += 2; + src3 += 4; + } + src3 += line_size - block_size; + } + dest += block_height * block_width * 2; + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= src_img->data + src_img->data_size ); + + AssignData(dest_img,src_img,data,img_size,IMG_RGBA32); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// conv_to_C*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError create_C_palette +( + Image_t * img, // valid destination + const Image_t * src_img, // valid source + image_format_t iform, // wanted image format (one of IMG_X_PAL*) + palette_format_t pform // wanted palette format +) +{ + DASSERT(img); + + //--- convert to IMG_X_RGB + + CopyIMG(img,false,src_img,true); + Transform2XRGB(img); + enumError err = ExecTransformIMG(img); + if (err) + return err; + DASSERT( img->iform == IMG_X_RGB ); + + + //--- find palette format + + switch (pform) + { + case PAL_IA8: + case PAL_RGB565: + case PAL_RGB5A3: + // ok for the moment + break; + + default: + // calculate the best palette type + pform = img->is_grayed + ? PAL_IA8 + : CheckAlphaIMG(img,false) < 0 + ? PAL_RGB565 + : PAL_RGB5A3; + break; + } + PRINT("%*s create_C_palette() %s.%s\n", + 2*convert_depth, "", + GetImageFormatName(iform,"?"), + GetPaletteFormatName(pform,"?") ); + + + //--- reduce color depth or normalize image + + u8 *ptr = img->data; + u8 *end = ptr + img->data_size; + + switch (pform) + { + case PAL_IA8: + while ( ptr < end ) + { + const u8 gray = ( ptr[0] + ptr[1] + ptr[2] + 1 ) / 3; + *ptr++ = gray; + *ptr++ = gray; + *ptr++ = gray; + ptr++; // dont't touch alhpa + } + break; + + case PAL_RGB565: + while ( ptr < end ) + { + *ptr = cc85s1[*ptr]; ptr++; + *ptr = cc86 [*ptr]; ptr++; + *ptr = cc85s1[*ptr]; ptr++; + *ptr++ = 0xff; + } + break; + + case PAL_RGB5A3: + while ( ptr < end ) + { + *ptr = cc85[*ptr]; ptr++; + *ptr = cc85[*ptr]; ptr++; + *ptr = cc85[*ptr]; ptr++; + *ptr = cc83[*ptr]; ptr++; + } + break; + + default: + // never reached + ASSERT(0); + } + + + //--- convert now to palette + + err = ConvertToPALETTE(img,img,0,iform); + img->pform = pform; + if (err) + return err; + + //--- transform the palette + + DASSERT(img->pal); + const u8 *src = img->pal; + u8 *dest = img->pal; + uint n = img->n_pal; + void (*wr16) ( void*, u16 ) = img->endian->wr16; + + switch (pform) + { + case PAL_IA8: + img->is_grayed = true; + while ( n-- > 0 ) + { + wr16( dest, src[0] | src[3] << 8 ); + dest += 2; + src += 4; + } + break; + + case PAL_RGB565: + while ( n-- > 0 ) + { + wr16( dest, ( src[0] & 0xfe ) << 10 // already shifted by 1 + | src[1] << 5 + | src[2] >> 1 // already shifted by 1 + ); + dest += 2; + src += 4; + } + break; + + case PAL_RGB5A3: + while ( n-- > 0 ) + { + if ( src[3] == 0x07 ) + wr16( dest, src[0] << 10 + | src[1] << 5 + | src[2] + | 0x8000 ); + else + wr16( dest, ( src[0] & 0xfe ) << 7 + | ( src[0] & 0xfe ) << 3 + | src[0] >> 1 + | src[3] << 12 ); + dest += 2; + src += 4; + } + break; + + default: + // never reached + ASSERT(0); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_C4 +( + Image_t * img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format +) +{ + DASSERT(img); + DASSERT(src_img); + PRINT("conv_to_C8()\n"); + + enumError err = create_C_palette(img,src_img,IMG_X_PAL4,pform); + if (err) + return err; + + const uint bits_per_pixel = 4; + const uint block_width = 8; + const uint block_height = 8; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock(img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + const uint xwidth = EXPAND8(img->width); + //const uint xheight = EXPAND8(img->height); + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u16 *src1 = (u16*)img->data; + + while ( v_blocks-- > 0 ) + { + const u16 * src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u16 * src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width / 2; + while ( ih-- > 0 ) + { + *dest++ = src3[0] << 4 | src3[1]; + src3 += 2; + } + src3 += xwidth - block_width; + } + src2 += block_width; + } + src1 += xwidth * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( (u8*)src1 <= img->data + img->data_size ); + + img->data = data; + img->data_alloced = true; + img->data_size = img_size; + img->xwidth = EXPAND8(img->width); + img->xheight = EXPAND8(img->height); + img->iform = IMG_C4; + img->alpha_status = 0; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_C8 +( + Image_t * img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format +) +{ + DASSERT(img); + DASSERT(src_img); + PRINT("conv_to_C8()\n"); + + enumError err = create_C_palette(img,src_img,IMG_X_PAL8,pform); + if (err) + return err; + + const uint bits_per_pixel = 8; + const uint block_width = 8; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock(img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + const uint xwidth = EXPAND8(img->width); + //const uint xheight = EXPAND8(img->height); + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u16 *src1 = (u16*)img->data; + + while ( v_blocks-- > 0 ) + { + const u16 * src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u16 * src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + *dest++ = *src3++; + src3 += xwidth - block_width; + } + src2 += block_width; + } + src1 += xwidth * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( (u8*)src1 <= img->data + img->data_size ); + + img->data = data; + img->data_alloced = true; + img->data_size = img_size; + img->xwidth = EXPAND8(img->width); + img->xheight = EXPAND8(img->height); + img->iform = IMG_C8; + img->alpha_status = 0; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_C14X2 +( + Image_t * img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format +) +{ + DASSERT(img); + DASSERT(src_img); + PRINT("conv_to_C14X2()\n"); + + enumError err = create_C_palette(img,src_img,IMG_X_PAL14,pform); + if (err) + return err; + + const uint bits_per_pixel = 16; + const uint block_width = 4; + const uint block_height = 4; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock(img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + const uint xwidth = EXPAND8(img->width); + //const uint xheight = EXPAND8(img->height); + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u16 *src1 = (u16*)img->data; + void (*wr16) ( void*, u16 ) = img->endian->wr16; + + while ( v_blocks-- > 0 ) + { + const u16 * src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + const u16 * src3 = src2; + uint iv = block_height; + while ( iv-- > 0 ) + { + uint ih = block_width; + while ( ih-- > 0 ) + { + wr16(dest,*src3++); + dest += 2; + } + src3 += xwidth - block_width; + } + src2 += block_width; + } + src1 += xwidth * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( (u8*)src1 <= img->data + img->data_size ); + + img->data = data; + img->data_alloced = true; + img->data_size = img_size; + img->xwidth = EXPAND8(img->width); + img->xheight = EXPAND8(img->height); + img->iform = IMG_C14X2; + img->alpha_status = 0; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// conv_to_CMPR() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint calc_distance ( const u8 * v1, const u8 * v2 ) +{ + const int d0 = (int)*v1++ - (int)*v2++; + const int d1 = (int)*v1++ - (int)*v2++; + const int d2 = (int)*v1++ - (int)*v2++; + return abs(d0) + abs(d1) + abs(d2); +} + +//----------------------------------------------------------------------------- + +static uint calc_distance_square ( const u8 * v1, const u8 * v2 ) +{ + const int d0 = (int)*v1++ - (int)*v2++; + const int d1 = (int)*v1++ - (int)*v2++; + const int d2 = (int)*v1++ - (int)*v2++; + return d0*d0 + d1*d1 + d2*d2; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CMPR_close_info +( + const u8 *data, // source data + cmpr_info_t *info, // info data structure + u8 *dest, // store destination data here (never 0) + bool fill_info // true: fill info with statistics +) +{ + DASSERT(info); + DASSERT(dest); + + u8 *pal0 = info->p[0]; + u8 *pal1 = info->p[1]; + + if (!info->opaque_count) + { + // all pixel are transparent + + memcpy(dest,info->default_vector,sizeof(info->default_vector)); + if (fill_info) + memset(info->index,3,sizeof(info->index)); + return; + } + else if ( info->opaque_count < CMPR_MAX_COL ) + { + // we have at least one transparent pixel + + u16 p0 = cc85[ pal0[0] ] << 11 + | cc86[ pal0[1] ] << 5 + | cc85[ pal0[2] ]; + u16 p1 = cc85[ pal1[0] ] << 11 + | cc86[ pal1[1] ] << 5 + | cc85[ pal1[2] ]; + if ( p0 == p1 ) + { + // make p0 < p1 + p0 &= ~0x0020; // modify least significant bit of green + p1 |= 0x0020; + } + else if ( p0 > p1 ) + { + u16 ptemp = p0; + p0 = p1; + p1 = ptemp; + } + DASSERT( p0 < p1 ); + write_be16(dest,p0); dest += 2; + write_be16(dest,p1); dest += 2; + + // re calculate palette colors + + pal0[0] = cc58[ p0 >> 11 ]; + pal0[1] = cc68[ p0 >> 5 & 0x3f ]; + pal0[2] = cc58[ p0 & 0x1f ]; + pal0[3] = 0xff; + + pal1[0] = cc58[ p1 >> 11 ]; + pal1[1] = cc68[ p1 >> 5 & 0x3f ]; + pal1[2] = cc58[ p1 & 0x1f ]; + pal1[3] = 0xff; + + // calculate median palette value + + u8 *pal2 = info->p[2]; + pal2[0] = ( pal0[0] + pal1[0] ) / 2; + pal2[1] = ( pal0[1] + pal1[1] ) / 2; + pal2[2] = ( pal0[2] + pal1[2] ) / 2; + pal2[3] = 0xff; + memcpy(info->p[3],pal2,4); + + uint i; + for ( i = 0; i < 4; i++ ) + { + u8 val = 0; + uint j; + for ( j = 0; j < 4; j++, data += 4 ) + { + val <<= 2; + if ( data[3] & 0x80 ) + { + const uint d0 = calc_distance(data,pal0); + const uint d1 = calc_distance(data,pal1); + const uint d2 = calc_distance(data,pal2); + if ( d1 <= d2 ) + val |= d1 <= d0; + else if ( d2 < d0 ) + val |= 2; + } + else + val |= 3; + } + *dest++ = val; + } + } + else + { + // we haven't any transparent pixel + + u16 p0 = cc85[ pal0[0] ] << 11 + | cc86[ pal0[1] ] << 5 + | cc85[ pal0[2] ]; + u16 p1 = cc85[ pal1[0] ] << 11 + | cc86[ pal1[1] ] << 5 + | cc85[ pal1[2] ]; + if ( p0 == p1 ) + { + // make p0 > p1 + p0 |= 1; + p1 &= ~(u16)1; + } + else if ( p0 < p1 ) + { + u16 ptemp = p0; + p0 = p1; + p1 = ptemp; + } + DASSERT( p0 > p1 ); + write_be16(dest,p0); dest += 2; + write_be16(dest,p1); dest += 2; + + // re calculate palette colors + + pal0[0] = cc58[ p0 >> 11 ]; + pal0[1] = cc68[ p0 >> 5 & 0x3f ]; + pal0[2] = cc58[ p0 & 0x1f ]; + pal0[3] = 0xff; + + pal1[0] = cc58[ p1 >> 11 ]; + pal1[1] = cc68[ p1 >> 5 & 0x3f ]; + pal1[2] = cc58[ p1 & 0x1f ]; + pal1[3] = 0xff; + + // calculate median palette values + + u8 *pal2 = info->p[2]; + pal2[0] = ( 2 * pal0[0] + pal1[0] ) / 3; + pal2[1] = ( 2 * pal0[1] + pal1[1] ) / 3; + pal2[2] = ( 2 * pal0[2] + pal1[2] ) / 3; + pal2[3] = 0xff; + + u8 *pal3 = info->p[3]; + pal3[0] = ( pal0[0] + 2 * pal1[0] ) / 3; + pal3[1] = ( pal0[1] + 2 * pal1[1] ) / 3; + pal3[2] = ( pal0[2] + 2 * pal1[2] ) / 3; + pal3[3] = 0xff; + + uint i; + for ( i = 0; i < 4; i++ ) + { + u8 val = 0; + uint j; + for ( j = 0; j < 4; j++, data += 4 ) + { + val <<= 2; + const uint d0 = calc_distance(data,pal0); + const uint d1 = calc_distance(data,pal1); + const uint d2 = calc_distance(data,pal2); + const uint d3 = calc_distance(data,pal3); + if ( d0 <= d1 ) + { + if ( d2 <= d3 ) + val |= d0 <= d2 ? 0 : 2; + else + val |= d0 <= d3 ? 0 : 3; + } + else + { + if ( d2 <= d3 ) + val |= d1 <= d2 ? 1 : 2; + else + val |= d1 <= d3 ? 1 : 3; + } + } + *dest++ = val; + } + } + + if (fill_info) + { + uint i; + for ( i = 0; i < 4; i++ ) + { + info->p[i][0] = cc58[cc85[info->p[i][0]]]; + info->p[i][1] = cc68[cc86[info->p[i][1]]]; + info->p[i][2] = cc58[cc85[info->p[i][2]]]; + } + + memcpy(info->cmpr,dest-8,8); + dest -= 4; + data -= CMPR_DATA_SIZE; + + uint val = 0; + for ( i = 0; i < CMPR_MAX_COL; i++ ) + { + if (!(i&3)) + val = *dest++; + uint dist = 0; + switch ( val & 0xc0 ) + { + case 0x00: + info->index[i] = 0; + dist = calc_distance(data,info->p[0]); + break; + + case 0x40: + info->index[i] = 1; + dist = calc_distance(data,info->p[1]); + break; + + case 0x80: + info->index[i] = 2; + dist = calc_distance(data,info->p[2]); + break; + + case 0xc0: + info->index[i] = 3; + if ( info->opaque_count == CMPR_MAX_COL ) + dist = calc_distance(data,info->p[3]); + break; + } + info->total_dist += dist; + info->dist[i] = dist; + data += 4; + val <<= 2; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// + +void CMPR_wiimm +( + const u8 *data, // source data + cmpr_info_t *info // info data structure +) +{ + DASSERT(info); + InitializeCmprInfo(info); + info->name = "Wiimm"; + + typedef struct sum_t + { + u8 col[4]; + uint count; + } sum_t; + + sum_t sum[CMPR_MAX_COL]; + uint n_sum = 0, opaque_count = 0; + uint col[3] = {0,0,0}; + + const u8 *data_end = data + CMPR_DATA_SIZE; + const u8 *dat; + for ( dat = data; dat < data_end; dat += 4 ) + { + col[0] += dat[0]; + col[1] += dat[1]; + col[2] += dat[2]; + + if ( dat[3] & 0x80 ) + { + opaque_count++; + u8 col[4]; + col[0] = cc58[cc85[dat[0]]]; + col[1] = cc68[cc86[dat[1]]]; + col[2] = cc58[cc85[dat[2]]]; + + uint s; + for ( s = 0; s < n_sum; s++ ) + { + if (!memcmp(sum[s].col,col,3)) + { + sum[s].count++; + goto abort_s; + } + } + col[3] = 0xff; + memcpy(sum[n_sum].col,col,4); + sum[n_sum].count = 1; + n_sum++; + abort_s:; + } + } + + if (!opt_cmpr_valid) + { + const u32 rgb = col[0] / CMPR_MAX_COL << 16 + | col[1] / CMPR_MAX_COL << 8 + | col[2] / CMPR_MAX_COL; + + // modify least significant bit of green + const u16 c1 = RGB_to_RGB565(rgb) & ~0x0020; + const u16 c2 = c1 | 0x0020; + write_be16(info->default_vector,c1); + write_be16(info->default_vector+2,c2); + } + + info->opaque_count = opaque_count; + if (!opaque_count) + return; + + DASSERT(n_sum); + if ( n_sum < 3 ) + { + memcpy(info->p[0],sum[0].col,4); + memcpy(info->p[1],sum[n_sum-1].col,4); + return; + } + + DASSERT( opaque_count >= 3 ); + //HEXDUMP16(0,0,sum,sizeof(sum)); + + uint best0 = 0, best1 = 0, max_dist = UINT_MAX; + if ( info->opaque_count < CMPR_MAX_COL ) + { + // we have transparent points -> 1 middle point + + uint s0; + for ( s0 = 0; s0 < n_sum; s0++ ) + { + u8 *pal0 = sum[s0].col; + uint s1; + for ( s1 = s0+1; s1 < n_sum; s1++ ) + { + u8 *pal1 = sum[s1].col; + u8 pal2[4]; + pal2[0] = ( pal0[0] + pal1[0] ) / 2; + pal2[1] = ( pal0[1] + pal1[1] ) / 2; + pal2[2] = ( pal0[2] + pal1[2] ) / 2; + + uint dist = 0; + const u8 * dat; + for ( dat = data; dat < data_end && dist < max_dist; dat += 4 ) + { + if ( dat[3] & 0x80 ) + { + const uint d0 = calc_distance(dat,pal0); + const uint d1 = calc_distance(dat,pal1); + const uint d2 = calc_distance(dat,pal2); + if ( d0 <= d1 ) + dist += d0 < d2 ? d0 : d2; + else + dist += d1 < d2 ? d1 : d2; + } + } + if ( max_dist > dist ) + { + max_dist = dist; + best0 = s0; + best1 = s1; + } + } + } + } + else + { + // no transparent points -> 2 middle point + + uint s0; + for ( s0 = 0; s0 < n_sum; s0++ ) + { + u8 *pal0 = sum[s0].col; + uint s1; + for ( s1 = s0+1; s1 < n_sum; s1++ ) + { + u8 *pal1 = sum[s1].col; + u8 pal2[4]; + pal2[0] = ( 2 * pal0[0] + pal1[0] ) / 3; + pal2[1] = ( 2 * pal0[1] + pal1[1] ) / 3; + pal2[2] = ( 2 * pal0[2] + pal1[2] ) / 3; + u8 pal3[4]; + pal3[0] = ( pal0[0] + 2 * pal1[0] ) / 3; + pal3[1] = ( pal0[1] + 2 * pal1[1] ) / 3; + pal3[2] = ( pal0[2] + 2 * pal1[2] ) / 3; + + uint dist = 0; + const u8 * dat; + for ( dat = data; dat < data_end && dist < max_dist; dat += 4 ) + { + const uint d0 = calc_distance(dat,pal0); + const uint d1 = calc_distance(dat,pal1); + const uint d2 = calc_distance(dat,pal2); + const uint d3 = calc_distance(dat,pal3); + if ( d0 <= d1 ) + { + if ( d2 <= d3 ) + dist += d0 < d2 ? d0 : d2; + else + dist += d0 < d3 ? d0 : d3; + } + else + { + if ( d2 <= d3 ) + dist += d1 <= d2 ? d1 : d2; + else + dist += d1 <= d3 ? d1 : d3; + } + } + if ( max_dist > dist ) + { + max_dist = dist; + best0 = s0; + best1 = s1; + } + } + } + } + + memcpy(info->p[0],sum[best0].col,4); + memcpy(info->p[1],sum[best1].col,4); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError conv_to_CMPR +( + Image_t * dest_img, // valid destination + const Image_t * src_img, // valid source + palette_format_t pform // wanted palette format => ignored +) +{ + DASSERT(dest_img); + DASSERT(src_img); + DASSERT( src_img->iform >= IMG_X__MIN && src_img->iform <= IMG_X__MAX ); + + + //--- first convert to IMG_X_RGB + + if ( src_img->iform != IMG_X_RGB ) + { + enumError err = ConvertIMG(dest_img,false,src_img,IMG_X_RGB,PAL_INVALID); + if (err) + return err; + } + else + CopyIMG(dest_img,false,src_img,true); + DASSERT( dest_img->iform == IMG_X_RGB ); + NormalizeFrameIMG(dest_img); + + + //--- and now convert IMG_X_RGB -> CMPR + + const uint bits_per_pixel = 4; + const uint block_width = 8; + const uint block_height = 8; + + uint h_blocks, v_blocks, img_size; + CalcImageBlock( dest_img, bits_per_pixel, block_width, block_height, + &h_blocks, &v_blocks, &img_size, true ); + + u8 *data = CALLOC(1,img_size); + u8 *dest = data; + const u8 *src1 = dest_img->data; + + const uint block_size = block_width * 4; + DASSERT( EXPAND8(dest_img->width) == dest_img->xwidth ); + const uint line_size = dest_img->xwidth * 4; + const uint delta[] = { 0, 16, 4*line_size, 4*line_size+16 }; + + while ( v_blocks-- > 0 ) + { + const u8 *src2 = src1; + uint hblk = h_blocks; + while ( hblk-- > 0 ) + { + uint subb; + for ( subb = 0; subb < 4; subb++ ) + { + //---- first collect the data of the 16 pixel + + u8 vector[16*4], *vect = vector; + const u8 *src3 = src2 + delta[subb]; + uint i; + for ( i = 0; i < 4; i++ ) + { + memcpy(vect,src3,16); + vect += 16; + src3 += line_size; + } + DASSERT( vect == vector + sizeof(vector) ); + + + //--- analyze data + + cmpr_info_t info; + CMPR_wiimm(vector,&info); + PRINT("CMPR: no=%u, %08x %08x %08x %08x\n", + info.opaque_count, + *(u32*)info.p[0], *(u32*)info.p[1], + *(u32*)info.p[2], *(u32*)info.p[3] ); + CMPR_close_info(vector,&info,dest,false); + dest += 8; + } + src2 += block_size; + } + src1 += line_size * block_height; + } + + DASSERT( dest == data + img_size ); + DASSERT( src1 <= dest_img->data + dest_img->data_size ); + + AssignData(dest_img,dest_img,data,img_size,IMG_CMPR); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IMG conversions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ConvertIMG +( + Image_t * dest, // destination of conversion + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source of conversion. + // if src==NULL or src==dest + // -> inplace conversion of 'dest' + image_format_t iform, // new image format + palette_format_t pform // new palette format +) +{ + DASSERT(dest); + if (init_dest) + InitializeIMG(dest); + if (!src) + src = dest; + + + //--- base setup + + image_format_t temp_iform; + iform = NormalizeIF(&temp_iform,iform,src->iform,pform); + pform = NormalizePF(iform,pform,src->pform); + PRINT("%*sCONVERT-IMG: [ %-6s -> %-6s -> %-6s ] [%s->%s] mm=%u {%u,%u}\n", + convert_depth*2, "", + GetImageFormatName(src->iform,"?"), + GetImageFormatName(temp_iform,"?"), + GetImageFormatName(iform,"?"), + GetPaletteFormatName(src->pform,"?"), + GetPaletteFormatName(pform,"?"), + CountMipmapsIMG(src), + src->conv_count, src->seq_num ); + + convert_depth++; + + + //--- mipmap support + + if (src->mipmap) // --> do the conversion for each image standalone! + { + if ( src != dest ) + CopyIMG(dest,false,src,true); // this makes it easier to handle + RemoveContainerIMG(dest); + DASSERT(!dest->container); + + while (dest) + { + Image_t *next = dest->mipmap; + dest->mipmap = 0; + enumError err = ConvertIMG(dest,false,dest,iform,pform); + dest->mipmap = next; + if (err) + { + convert_depth--; + return err; + } + dest = next; + } + convert_depth--; + return ERR_OK; + } + + + //--- some more tests + + if ( convert_depth > 10 + || src->iform == IMG_INVALID + || iform == src->iform + && ( src->pform == PAL_INVALID || pform == src->pform )) + { + convert_depth--; + CopyIMG(dest,false,src,true); + return ERR_OK; + } + + + //--- select sub conversions I + + enumError (*func1) ( Image_t*, const Image_t*, image_format_t ) = 0; + + switch (src->iform) + { + case IMG_I4: func1 = conv_from_I4; break; + case IMG_I8: func1 = conv_from_I8; break; + case IMG_IA4: func1 = conv_from_IA4; break; + case IMG_IA8: func1 = conv_from_IA8; break; + case IMG_RGB565: func1 = conv_from_RGB565; break; + case IMG_RGB5A3: func1 = conv_from_RGB5A3; break; + case IMG_RGBA32: func1 = conv_from_RGBA32; break; + case IMG_C4: func1 = conv_from_C4; break; + case IMG_C8: func1 = conv_from_C8; break; + case IMG_C14X2: func1 = conv_from_C14X2; break; + case IMG_CMPR: func1 = conv_from_CMPR; break; + + case IMG_X_GRAY: break; + case IMG_X_RGB: break; + case IMG_X_PAL4: break; + case IMG_X_PAL8: break; + case IMG_X_PAL14: break; + + default: + convert_depth--; + return ERROR0(ERR_INVALID_IFORM, + "Image format 0x%02x [%s] not supported: %s\n", + src->iform, GetImageFormatName(src->iform,"?"), src->path ); + } + + if (func1) + { + const enumError err = func1(dest,src,temp_iform); + if (err) + { + convert_depth--; + return err; + } + src = dest; + } + + + //--- select sub conversions II + + if ( temp_iform != src->iform ) + { + enumError (*func2) ( Image_t*, const Image_t*, palette_format_t ) = 0; + switch(temp_iform) + { + case IMG_X_GRAY: func2 = ConvertToGRAY; break; + case IMG_X_RGB: func2 = ConvertToRGB; break; + case IMG_X_PAL: func2 = conv_to_palette_auto; break; + case IMG_X_PAL4: func2 = conv_to_palette_4; break; + case IMG_X_PAL8: func2 = conv_to_palette_8; break; + case IMG_X_PAL14: func2 = conv_to_palette_14; break; + + default: return ERROR0(ERR_INTERNAL,0); + } + + if (func2) + { + const enumError err = func2(dest,src,pform); + if (err) + { + convert_depth--; + return err; + } + src = dest; + } + } + + + //--- select sub conversions III + + enumError (*func2) ( Image_t*, const Image_t*, palette_format_t ) = 0; + switch(iform) + { + case IMG_I4: func2 = conv_to_I4; break; + case IMG_I8: func2 = conv_to_I8; break; + case IMG_IA4: func2 = conv_to_IA4; break; + case IMG_IA8: func2 = conv_to_IA8; break; + case IMG_RGB565: func2 = conv_to_RGB565; break; + case IMG_RGB5A3: func2 = conv_to_RGB5A3; break; + case IMG_RGBA32: func2 = conv_to_RGBA32; break; + case IMG_CMPR: func2 = conv_to_CMPR; break; + case IMG_X_GRAY: func2 = ConvertToGRAY; break; + case IMG_X_RGB: func2 = ConvertToRGB; break; + case IMG_X_PAL: func2 = conv_to_palette_auto; break; + case IMG_X_PAL4: func2 = conv_to_palette_4; break; + case IMG_X_PAL8: func2 = conv_to_palette_8; break; + case IMG_X_PAL14: func2 = conv_to_palette_14; break; + + case IMG_C4: func2 = conv_to_C4; break; + case IMG_C8: func2 = conv_to_C8; break; + case IMG_C14X2: func2 = conv_to_C14X2; break; + + default: + convert_depth--; + return ERROR0(ERR_INVALID_IFORM, + "Conversion to image format 0x%02x [%s] not supported: %s\n", + iform, GetImageFormatName(iform,"?"), src->path ); + } + + enumError err = func2 ? func2(dest,src,pform) : ERR_OK; + convert_depth--; + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Normalize IF + PF /////////////// +/////////////////////////////////////////////////////////////////////////////// + +image_format_t NormalizeIF +( + image_format_t * xform, // not NULL: store the best IMG_X_* here + image_format_t iform, // wanted image format + image_format_t x_default, // default format, use if iform == AUTO + // and return a X-format + palette_format_t pform // related palette format +) +{ + switch (iform) + { + case IMG_I4: // known gray formats + case IMG_I8: + case IMG_IA4: + case IMG_IA8: + case IMG_X_GRAY: + if (xform) + *xform = IMG_X_GRAY; + return iform; + + case IMG_RGB565: // known RGB formats + case IMG_RGB5A3: + case IMG_RGBA32: + case IMG_CMPR: + case IMG_X_RGB: + case IMG_C4: + case IMG_C8: + case IMG_C14X2: + if (xform) + *xform = IMG_X_RGB; + return iform; + + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + if (xform) + *xform = iform; + return iform; + + case IMG_X_PAL: + switch(x_default) + { + case IMG_I4: + if (xform) + *xform = IMG_X_PAL4; + return IMG_X_PAL4; + + case IMG_I8: + case IMG_IA4: + if (xform) + *xform = IMG_X_PAL8; + return IMG_X_PAL8; + + default: + if (xform) + *xform = IMG_X_PAL14; + return IMG_X_PAL14; + } + + case IMG_X_AUTO: + switch(x_default) + { + case IMG_I4: + case IMG_I8: + case IMG_IA4: + case IMG_IA8: + case IMG_X_GRAY: + if (xform) + *xform = IMG_X_GRAY; + return IMG_X_GRAY; + + case IMG_C4: + case IMG_X_PAL4: + if (xform) + *xform = IMG_X_PAL4; + return x_default; + + case IMG_C8: + case IMG_X_PAL8: + if (xform) + *xform = IMG_X_PAL8; + return x_default; + + case IMG_C14X2: + case IMG_X_PAL14: + if (xform) + *xform = IMG_X_PAL14; + return x_default; + + default: + if (xform) + *xform = IMG_X_RGB; + return IMG_X_RGB; + } + + default: + return NormalizeIF(xform,x_default,IMG_X_RGB,pform); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +palette_format_t NormalizePF +( + image_format_t iform, // related image format + palette_format_t pform, // wanted palette format + palette_format_t default_pform // palette format for auto +) +{ + noPRINT("NormalizePF(%s,%s,%s)\n", + GetImageFormatName(iform,"?"), + GetPaletteFormatName(pform,"?"), + GetPaletteFormatName(default_pform,"?") ); + + switch (iform) + { + case IMG_C4: + case IMG_C8: + case IMG_C14X2: + switch(pform) + { + case PAL_IA8: + case PAL_RGB565: + case PAL_RGB5A3: + return pform; + + default: + switch(default_pform) + { + case PAL_IA8: + case PAL_RGB565: + case PAL_RGB5A3: + return default_pform; + + case PAL_AUTO: + return PAL_DEFAULT; + + default: + break; + } + break; + } + break; + + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + case IMG_X_PAL: + return PAL_X_RGB; + + default: + break; + } + + return PAL_INVALID; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-image2.c b/project/src/lib-image2.c new file mode 100644 index 0000000..614838d --- /dev/null +++ b/project/src/lib-image2.c @@ -0,0 +1,2992 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-std.h" +#include +#include "lib-image.h" +#include "lib-breff.h" + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef TEST + #define ENABLE_IMAGE_TYPE_LOG 0 // 0|1 + #define ENABLE_EXPORT_TIMER 0 // 0|1|2 +#else + #define ENABLE_IMAGE_TYPE_LOG 0 + #define ENABLE_EXPORT_TIMER 0 +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AssignIMG(), LoadIMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError AssignIMG +( + Image_t * img, // pointer to valid img + int init_img, // <0:none, =0:reset, >0:init + const u8 * data, // source data + uint data_size, // soze of 'data' + uint img_index, // index of sub image, 0:main, >0:mipmaps + bool mipmaps, // true: assign mipmaps + const endian_func_t * endian, // endian functions to read data + ccp fname // object name, assigned +) +{ + DASSERT(img); + DASSERT(data); + noPRINT("ASSIGN-IMG: idx=%d mm=%d siz=%x %s\n", + img_index, mipmaps, data_size, fname ); + + if ( init_img > 0 ) + InitializeIMG(img); + else if (!init_img) + ResetIMG(img); + +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + + image_format_t iform = IMG_INVALID, pform = PAL_INVALID; + uint width = 0, height = 0, n_pal = 0, psize = 0, n_img = 0; + const u8 *idata = 0, *pdata = 0; + bool calc_geo = false; // true: calculate geometry for mipmaps + + switch (fform) + { + case FF_TPL: + { + const tpl_header_t * tpl; + const tpl_pal_header_t * tp; + const tpl_img_header_t * ti; + + if (SetupPointerTPL( data, data_size, img_index, + &tpl, 0, &tp, &ti, + &pdata, &idata, &be_func )) + { + iform = be32(&ti->iform); + width = be16(&ti->width); + height = be16(&ti->height); + n_img = be32(&tpl->n_image); + + if (tp) + { + pform = be32(&tp->pform); + n_pal = be16(&tp->n_entry); + psize = pdata < idata + ? idata - pdata + : data + data_size - pdata; + noPRINT("PALETTE: %u*%02x[%s], off = 0x%zx, size = 0x%x, n= %u\n", + n_pal, pform, GetImageFormatName(pform,"?"), + pdata - data, psize, n_pal ); + } + } + } + break; + + case FF_BTI: + { + const bti_header_t *bti = (bti_header_t*)data; + iform = bti->iform; + idata = data + be32(&bti->data_off); + width = be16(&bti->width); + height = be16(&bti->height); + n_img = bti->n_image; + calc_geo = true; + + const u32 pal_off = be32(&bti->pal_off); + if ( pal_off && pal_off < data_size ) + { + pform = be16(&bti->pform); + pdata = data + pal_off; + psize = pdata < idata ? idata - pdata : data_size - pal_off; + } + } + break; + + case FF_TEX: + case FF_TEX_CT: // ??? [[CTCODE]] add ctcode info + { + const brsub_header_t *bh = (brsub_header_t*)data; + const uint n_grp = GetSectionNumBRSUB(data,data_size,endian); + const tex_info_t *ti = (tex_info_t*)( bh->grp_offset + n_grp ); + uint grp_off = endian->rd32(&bh->grp_offset); + + if ( grp_off < data_size ) + { + iform = endian->rd32(&ti->iform); + width = endian->rd16(&ti->width); + height = endian->rd16(&ti->height); + n_img = endian->rd32(&ti->n_image); + calc_geo= true; + idata = (u8*)data + grp_off; + } + } + break; + + case FF_BREFT_IMG: + if ( data_size > sizeof(breft_image_t) ) + { + const breft_image_t * bi = (breft_image_t*)data; + iform = bi->iform; + width = be16(&bi->width); + height = be16(&bi->height); + idata = (u8*)data + sizeof(*bi); + n_img = bi->n_image ? bi->n_image : 1; + calc_geo = true; + } + break; + + default: + return opt_ignore || fform == FF_UNKNOWN + ? ERR_WARNING + : ERROR0(ERR_INVALID_IFORM, + "No (supported) image file [file type=%s]: %s\n", + GetNameFF(0,fform), fname); + } + + const ImageGeometry_t *geo = GetImageGeometry(iform); + if ( geo && calc_geo ) + { + noPRINT_IF(img_index,"BASE IMAGE: %3u*%-3u %6zu\n", + width, height, data+data_size-idata); + uint n; + for ( n = img_index; n > 0; n-- ) + { + uint img_size; + CalcImageGeometry(iform,width,height,0,0,0,0,&img_size); + idata += img_size; + width /= 2; + height /= 2; + noPRINT("NEXT IMAGE: %3u*%-3u %6zu %6u\n", + width, height, data+data_size-idata, img_size ); + } + } + + if ( !idata && data_size < 0x40 ) + { + // small => only a fragment (e.g. header) => be silent + return ERR_INVALID_IFORM; + } + + uint delta = idata - data; + if ( !idata || delta >= data_size ) + return ERROR0(ERR_INVALID_IFORM, + "Invalid image format [file type=%s]: %s\n", + GetNameFF(0,fform), fname ); + + img->width = width; + img->height = height; + img->xwidth = geo ? ALIGN32(width,geo->block_width) : EXPAND8(width); + img->xheight = geo ? ALIGN32(height,geo->block_height) : EXPAND8(height); + img->alpha_status = geo && !geo->has_alpha ? -1 : 0; + + //img->container = set by caller if needed/wanted! + img->data = (u8*)idata; + img->info_size = data_size - delta; + img->data_size = geo ? img->xwidth * img->xheight * geo->bits_per_pixel / 8 + : img->info_size; + img->data_alloced = false; + img->iform = iform; + img->info_iform = iform; + img->info_fform = fform; + img->info_n_image = n_img; + + img->pal = (u8*)pdata; + img->pal_size = psize; + img->pal_alloced = false; + img->n_pal = n_pal; + img->pform = pform; + img->info_pform = pform; + + img->endian = endian; + img->path = fname; + img->seq_num = ++image_seq_num; + + noPRINT("-> %u*%u->%u*%u [%u=0x%x]\n", + img->width, img->height, img->xwidth, img->xheight, + img->data_size, img->data_size ); + + if ( mipmaps && ++img_index < n_img ) + { + DASSERT(!img->mipmap); + img->mipmap = MALLOC(sizeof(*img->mipmap)); + AssignIMG(img->mipmap,true,data,data_size,img_index,mipmaps,endian,fname); + } + + return PatchListIMG(img); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadIMG +( + Image_t * img, // pointer to valid img + bool init_img, // true: initialize 'img' + ccp fname, // filename of source + uint img_index, // index of sub image, 0:main, >0:mipmaps + bool mipmaps, // true: load and assign mipmaps + bool allow_subfile, // allow to extract szs sub files + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(img); + DASSERT(fname); + TRACE("LoadIMG(%p,%d,%d) fname=%s\n",img,init_img,ignore_no_file,fname); + + if (init_img) + InitializeIMG(img); + else + ResetIMG(img); + + szs_extract_t eszs; + if (allow_subfile) + { + enumError err = ExtractSZS(&eszs,true,fname,0,ignore_no_file); + if (err) + return err; + } + else + InitializeExtractSZS(&eszs); + + if (!eszs.data) + { + File_t F; + enumError err = OpenFILE(&F,true,fname,ignore_no_file,false); + if ( err || !F.f ) + return err; + + if ( ignore_no_file && !S_ISREG(F.st.st_mode) ) + { + ResetFile(&F,0); + return ERR_WARNING; + } + + u8 buf[0x200]; + size_t read_stat = fread(buf,1,sizeof(buf),F.f); +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(buf,read_stat,0); + if ( fform == FF_PNG ) + { + err = ReadPNG(img,mipmaps,&F,buf,read_stat); + img->path = F.fname; + F.fname = 0; + ResetFile(&F,0); + return err; + } + + // use 'eszs' data structure to hold dynamic data + eszs.data_size = F.st.st_size; + eszs.data = MALLOC(eszs.data_size); + eszs.data_alloced = true; + + memcpy(eszs.data,buf,read_stat); + if ( read_stat < eszs.data_size ) + { + const uint read_len = eszs.data_size - read_stat; + read_stat = fread(eszs.data+read_stat,1,read_len,F.f); + if ( read_stat != read_len ) + { + ERROR1(ERR_READ_FAILED,"Can't read file: %s\n",fname); + ResetFile(&F,0); + return ERR_READ_FAILED; + } + } + ResetFile(&F,0); + FreeString(eszs.fname); + eszs.fname = STRDUP(fname); + } + + enumError err = AssignIMG( img,-1, eszs.data, eszs.data_size, + img_index, mipmaps, eszs.endian, eszs.fname ); + if (!err) + { + if (eszs.data_alloced) + { + eszs.data_alloced = false; + img->container = eszs.data; + } + img->path_alloced = true; + eszs.fname = 0; + } + + ResetExtractSZS(&eszs); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MipmapOptions_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupMipmapOptions ( MipmapOptions_t *mmo ) +{ + DASSERT(mmo); + + //memset(mmo,0,sizeof(*mmo)); + mmo->valid = true; + + if (opt_n_images) + { + mmo->force = true; + mmo->n_mipmap = opt_n_images - 1; + mmo->n_image = opt_n_images; + mmo->min_size = 1; + } + else + { + mmo->force = false; + mmo->n_mipmap = opt_max_images - 1; + mmo->n_image = opt_max_images; + mmo->min_size = opt_min_mipmap_size; + } + + PRINT("MM-OPT/SETUP: %s\n",TextMipmapOptions(mmo)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupMipmapOptions1 ( MipmapOptions_t *mmo ) +{ + DASSERT(mmo); + + //memset(mmo,0,sizeof(*mmo)); + mmo->valid = true; + mmo->force = true; + mmo->n_mipmap = 0; + mmo->n_image = 1; + mmo->min_size = 1; + + PRINT("MM-OPT/SETUP1: %s\n",TextMipmapOptions(mmo)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyMipmapOptions ( MipmapOptions_t *dest, const MipmapOptions_t *src ) +{ + DASSERT(dest); + if (src) + { + memcpy(dest,src,sizeof(*dest)); + PRINT("MM-OPT/COPY: %s\n",TextMipmapOptions(dest)); + } + else + SetupMipmapOptions(dest); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MipmapOptionsByImage ( MipmapOptions_t *mmo, const Image_t *img ) +{ + DASSERT(mmo); + if (!mmo->valid) + SetupMipmapOptions(mmo); + + if ( !mmo->force && img ) + { + int n_image = img->mipmap ? CountMipmapsIMG(img) + 1 : mmo->n_image; + if ( n_image < img->info_n_image ) + n_image = img->info_n_image; + + if ( n_image > MAX_MIPMAPS ) + n_image = MAX_MIPMAPS+1; + else if ( n_image < 1 ) + n_image = opt_max_images > 0 ? opt_max_images : 1; + + mmo->n_image = n_image; + mmo->n_mipmap = n_image - 1; + } + + PRINT("MM-OPT/IMG: %s\n",TextMipmapOptions(mmo)); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp InfoMipmapOptions ( const MipmapOptions_t *mmo ) +{ + if (!mmo) + return "--"; + + if (!mmo->valid) + return "!!"; + + char buf[20]; + int len = snprintf(buf,sizeof(buf),"%s%d/%d", + mmo->force ? "f" : "m", mmo->n_mipmap, mmo->min_size ); + + char *res = GetCircBuf(++len); + memcpy(res,buf,len); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp TextMipmapOptions ( const MipmapOptions_t *mmo ) +{ + if (!mmo) + return "--"; + + if (!mmo->valid) + return "INVALID!"; + + char buf[100]; + int len = snprintf(buf,sizeof(buf),"force=%d, nm=%d, ni=%d, minsize=%d", + mmo->force, mmo->n_mipmap, mmo->n_image, mmo->min_size ); + + char *res = GetCircBuf(++len); + memcpy(res,buf,len); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintMipmapOptions ( FILE *f, int indent, const MipmapOptions_t *mmo ) +{ + DASSERT(f); + DASSERT(mmo); + + indent = NormalizeIndent(indent); + fprintf(f,"%*sMM-OPT: %s\n",indent, "", TextMipmapOptions(mmo)); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// mipmap helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mipmap_info_t]] + +typedef struct mipmap_info_t +{ + MipmapOptions_t mmo; // mipmap options + uint n_mipmap; // number of mipmaps to write + uint image_size; // calculated size of all images + Image_t img; // transformed image + const Image_t * src_img; // pointer to source image + + // tpl helpers + + uint img_head_off; // offset of image header + uint img_data_off; // offset of image data + uint img_data_size; // size of image data + + uint pal_head_off; // offset of palette header + uint pal_data_off; // offset of palette header + uint pal_data_size; // size of palette data + +} mipmap_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMMI ( mipmap_info_t * mmi ) +{ + DASSERT(mmi); + ResetIMG(&mmi->img); + memset(mmi,0,sizeof(*mmi)); + SetupMipmapOptions(&mmi->mmo); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError PrepareImages +( + mipmap_info_t * mmi, // valid mipmap info + const Image_t * src_img, // pointer to source image + const MipmapOptions_t *mmo // NULL or mipmap options + + // RETURNS (if return value == ERR_OK): + // mmi->n_mipmap : number of mipmaps to store + // mmi->image_size : total image size (main+mipmaps) + // mmi->img.info_size : data size of main image +) +{ + DASSERT(mmi); + DASSERT(src_img); + + memset(mmi,0,sizeof(*mmi)); + mmi->src_img = src_img; + + CopyMipmapOptions(&mmi->mmo,mmo); + MipmapOptionsByImage(&mmi->mmo,src_img); + PRINT("PrepareImages() MMO: %s\n",TextMipmapOptions(&mmi->mmo)); + + + //--- copy & convert images + + CopyIMG(&mmi->img,true,src_img,false); + Transform2InternIMG(&mmi->img); + enumError err = ExecTransformIMG(&mmi->img); + if (err) + return err; + + + //--- calculate image size + + const ImageGeometry_t *geo = GetImageGeometry(mmi->img.iform); + if ( !geo || geo->is_x ) + return ERROR0(ERR_INTERNAL,0); + + uint ni, size = 0; + uint wd = mmi->img.width; + uint ht = mmi->img.height; + uint n_image = mmi->mmo.n_image; + + for ( ni = 0; ni < n_image; ni++ ) + { + size += CalcImageSize( wd, ht, geo->bits_per_pixel, + geo->block_width, geo->block_height, 0,0,0,0 ); + if (!ni) + mmi->img.info_size = size; + wd /= 2; + ht /= 2; + DASSERT( opt_min_mipmap_size >= 1 ); + if ( wd < mmi->mmo.min_size || ht < mmi->mmo.min_size ) + { + n_image = ni + 1; + break; + } + } + + mmi->n_mipmap = n_image - 1; + mmi->image_size = size; + + PRINT("SETUP IMAGES: N=1+%u, size=%u=0x%x, m0=%s\n", + n_image-1, size, size, InfoMipmapOptions(&mmi->mmo) ); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError PrepareImages1 +( + mipmap_info_t * mmi, // valid mipmap info + const Image_t * src_img // pointer to source image + + // RETURNS (if return value == ERR_OK): + // mmi->n_mipmap : number of mipmaps to store + // mmi->image_size : total image size (main+mipmaps) + // mmi->img.info_size : data size of main image +) +{ + DASSERT(mmi); + DASSERT(src_img); + + MipmapOptions_t mmo; + SetupMipmapOptions1(&mmo); + return PrepareImages(mmi,src_img,&mmo); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError WriteImageData +( + mipmap_info_t * mmi, // valid mipmap info + u8 * data // destination buffer +) +{ + DASSERT(mmi); + DASSERT(data); + + const Image_t *img = &mmi->img; + memcpy(data,img->data,img->info_size); + if (!mmi->n_mipmap) + return ERR_OK; + + const ImageGeometry_t *geo = GetImageGeometry(img->iform); + if ( !geo || geo->is_x ) + return ERROR0(ERR_INTERNAL,0); + + Image_t temp; + InitializeIMG(&temp); + + enumError err = ERR_OK; + u8 * dest = data + img->info_size; + img = mmi->src_img; + DASSERT(img); + + uint wd = img->width; + uint ht = img->height; + uint ni; + for ( ni = 0; ni < mmi->n_mipmap; ni++ ) + { + if (img->mipmap) + img = img->mipmap; + + wd /= 2; + ht /= 2; + DASSERT( wd && ht ); + err = ResizeIMG(&temp,false,img,wd,ht); + if (err) + break; + //PRINT("RESIZE %3u*%-3u ",wd,ht); HEXDUMP16(0,0,temp.data,16); + + err = ConvertIMG(&temp,false,0,mmi->img.iform,mmi->img.pform); + if (err) + break; + //PRINT("CONVERT %3u*%-3u ",wd,ht); HEXDUMP16(0,0,temp.data,16); + + DASSERT( dest + temp.data_size <= data + mmi->image_size ); + memcpy(dest,temp.data,temp.data_size); + dest += temp.data_size; + } + + ResetIMG(&temp); + ResetIMG(&mmi->img); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveIMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveIMG +( + Image_t * img, // pointer to valid img + file_format_t fform, // file format + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite // true: force overwriting +) +{ + DASSERT(img); + DASSERT(fname); + + PRINT("SaveIMG(N=%u,o=%d) %s, mo=%s, %s\n", + CountMipmapsIMG(img), overwrite, + PrintFormat3(fform,img->iform,img->pform), + InfoMipmapOptions(mmo), fname ); + + switch(fform) + { + case FF_TPL: return SaveTPL(img,fname,overwrite); + case FF_BTI: return SaveBTI(img,mmo,fname,overwrite); + case FF_TEX: return SaveTEX(img,mmo,fname,overwrite,false); + case FF_TEX_CT: return SaveTEX(img,mmo,fname,overwrite,true); + case FF_BREFT: + case FF_BREFT_IMG: return SaveBREFTIMG(img,mmo,fname,overwrite); + case FF_PNG: return SavePNG(img,true,fname,0,0,overwrite,0); + + default: + return ERROR0(ERR_INVALID_IFORM, + "Can_t create image [file type=%s]: %s\n", + GetNameFF(0,fform), fname); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTPL +( + Image_t * src_img, // pointer to valid source img + ccp fname, // filename of source + bool overwrite // true: force overwriting +) +{ + DASSERT(src_img); + DASSERT(fname); + + PRINT("SaveTPL(o=%d) %s\n", overwrite, fname ); + + + //--- setup images + + u8 *data = 0; + const int n_image = CountMipmapsIMG(src_img) + 1; + mipmap_info_t *mmi = CALLOC(n_image,sizeof(*mmi)); + enumError err = ERR_OK; + + const uint align = 0x20; + const uint tab_off = sizeof(tpl_header_t); + uint data_off = tab_off + n_image * sizeof(tpl_imgtab_t); + + mipmap_info_t *m = mmi; + const Image_t *img = src_img; + + + //--- first loop: setup header offsets + + int i; + for ( i = 0; i < n_image; i++, m++, img = img->mipmap ) + { + DASSERT(img); + err = PrepareImages1(m,img); + if (err) + goto abort; + + m->pal_head_off = 0; + m->img_head_off = data_off; + + if (m->img.n_pal) + { + m->pal_head_off = data_off; + m->pal_data_off = m->pal_head_off + sizeof(tpl_pal_header_t); + m->pal_data_size = 2 * m->img.n_pal; + m->img_head_off = ALIGN32( m->pal_data_off + m->pal_data_size, 4 ); + } + data_off = ALIGN32( m->img_head_off + sizeof(tpl_img_header_t), 4 ); + } + data_off = ALIGN32(data_off,align); + + + //--- second loop: setup data offsets + + for ( i = 0, m = mmi; i < n_image; i++, m++ ) + { + m->img_data_off = data_off; + m->img_data_size = m->image_size; + data_off = ALIGN32( m->img_data_off + m->img_data_size, align ); + + PRINT("%6x %6x %6x | %6x %6x %6x | %6x = %6u\n", + m->pal_head_off, m->pal_data_off, m->pal_data_size, + m->img_head_off, m->img_data_off, m->img_data_size, + data_off, data_off ); + } + + + //--- alloc data & setup file header + + data = CALLOC(1,data_off); + const endian_func_t * endian = src_img->endian; + + tpl_header_t * tpl = (tpl_header_t*)data; + endian->wr32(tpl->magic,TPL_MAGIC_NUM); + endian->wr32(&tpl->n_image,n_image); + endian->wr32(&tpl->imgtab_off,tab_off); + + tpl_imgtab_t * tab = (tpl_imgtab_t*)( data + tab_off ); + + + //--- third loop: copy data + + for ( i = 0, m = mmi; i < n_image; i++, m++ ) + { + endian->wr32( &tab[i].image_off, m->img_head_off ); + endian->wr32( &tab[i].palette_off, m->pal_head_off ); + + if (m->img.n_pal) + { + tpl_pal_header_t * tp = (tpl_pal_header_t*)( data + m->pal_head_off ); + endian->wr16( &tp->n_entry, m->img.n_pal ); + endian->wr32( &tp->pform, m->img.pform ); + endian->wr32( &tp->data_off, m->pal_data_off ); + memcpy( data + m->pal_data_off, m->img.pal, 2*m->img.n_pal ); + } + + tpl_img_header_t * ti = (tpl_img_header_t*)( data + m->img_head_off ); + endian->wr16( &ti->width, m->img.width ); + endian->wr16( &ti->height, m->img.height ); + endian->wr32( &ti->iform, m->img.iform ); + endian->wr32( &ti->data_off, m->img_data_off ); + endian->wr32( &ti->min_filter, 1 ); + endian->wr32( &ti->mag_filter, 1 ); + + err = WriteImageData( m, data + m->img_data_off ); + if (err) + goto abort; + } + + + //--- save to file & clean + + err = SaveFILE(fname,0,overwrite,data,data_off,0); + + abort: + for ( i = 0; i < n_image; i++ ) + ResetMMI(mmi+i); + FREE(mmi); + FREE(data); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveBTI +( + Image_t * src_img, // pointer to valid source img + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite // true: force overwriting +) +{ + DASSERT(src_img); + DASSERT(fname); + + PRINT0("SaveBTI(o=%d) mo=%s, %s\n", overwrite, InfoMipmapOptions(mmo), fname ); + + + //--- setup images + + u8 *data = 0; + // don't know how to store palettes [[2do]] + const bool no_pal_stat = Transform2NoPaletteIMG(src_img); + + mipmap_info_t mmi; + enumError err = PrepareImages(&mmi,src_img,mmo); + if (err) + goto abort; + + static int warn_count = 3; + if ( no_pal_stat && warn_count > 0 ) + { + warn_count--; + ERROR0(ERR_WARNING, + "BTI files with palettes not supported yet. Image converted to '%s'.", + PrintFormat3(0,mmi.img.iform,mmi.img.pform)); + } + + + //--- calculate image size + + const uint data_off = sizeof(bti_header_t); + const uint total_size = data_off + mmi.image_size; + PRINT("TOTAL-SIZE: %x = %u\n",total_size,total_size); + PRINT("IMAGE-SIZE: %x = %u, %u*%u, N=1+%u\n", + mmi.img.data_size, mmi.img.data_size, + mmi.img.width, mmi.img.height, mmi.n_mipmap ); + + + //--- alloc and setup data + + data = CALLOC(1,total_size); + const endian_func_t * endian = &be_func; + + bti_header_t *bti = (bti_header_t*)data; + bti->iform = mmi.img.iform; + bti->n_image = mmi.n_mipmap+1; + bti->unknown_14 = 1; + bti->unknown_15 = 1; + endian->wr16(&bti->width,mmi.img.width); + endian->wr16(&bti->height,mmi.img.height); + endian->wr32(&bti->data_off,data_off); + + ccp point = strrchr(fname,'.'); + const bool is_special = point && ( !strcasecmp(point,".btiEnv") + || !strcasecmp(point,".btiMat")); + if (!is_special) + { + bti->unknown_01 = 2; + bti->wrap_s = 1; + bti->wrap_t = 1; + } + + err = WriteImageData(&mmi,data+data_off); + if (!err) + err = SaveFILE(fname,0,overwrite,data,total_size,0); + + abort: + FREE(data); + ResetMMI(&mmi); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTEX +( + Image_t * src_img, // pointer to valid source img + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite, // true: force overwriting + bool ctcode_support // true: include a CT-CODE file +) +{ + DASSERT(src_img); + DASSERT(fname); + + PRINT("SaveTEX(o=%d,ct=%d) mo=%s, %s\n", + overwrite, ctcode_support, InfoMipmapOptions(mmo), fname ); + + + //--- setup images + + u8 *data = 0; + const bool no_pal_stat = Transform2NoPaletteIMG(src_img); + + mipmap_info_t mmi; + enumError err = PrepareImages(&mmi,src_img,mmo); + if (err) + goto abort; + + static int warn_count = 3; + if ( no_pal_stat && warn_count > 0 ) + { + warn_count--; + ERROR0(ERR_WARNING, + "TEX0 files don't support palettes, image converted to '%s'.", + PrintFormat3(0,mmi.img.iform,mmi.img.pform)); + } + + + //--- calculate image size + + ccp realfile = strrchr(fname,'/'); + realfile = realfile ? realfile + 1 : fname; + const uint filelen = strlen(realfile); + const uint grp_off = 0x40; + const uint data_size = grp_off + mmi.image_size; + const uint total_size = data_size + 4 + ALIGN32(filelen+1,4); + + PRINT("TOTAL-SIZE: %x = %u\n",total_size,total_size); + PRINT("IMAGE-SIZE: %x = %u, %u*%u, N=1+%u\n", + mmi.img.data_size, mmi.img.data_size, + mmi.img.width, mmi.img.height, mmi.n_mipmap ); + + + //--- alloc and setup data + + data = CALLOC(1,total_size); + const endian_func_t * endian = mmi.img.endian; + + brsub_header_t *bh = (brsub_header_t*)data; + memcpy(bh->magic,TEX_MAGIC,sizeof(bh->magic)); + endian->wr32(&bh->size,data_size); + endian->wr32(&bh->version,3); + endian->wr32(&bh->grp_offset,grp_off); + + const uint n_grp = GetSectionNumBRSUB(data,data_size,endian); + tex_info_t *ti = (tex_info_t*)( bh->grp_offset + n_grp ); + endian->wr32(&ti->name_off,data_size+4); + endian->wr32(data+data_size,filelen); + memcpy(data+data_size+4,realfile,filelen); + endian->wr16(&ti->width,mmi.img.width); + endian->wr16(&ti->height,mmi.img.height); + endian->wr32(&ti->iform,mmi.img.iform); + endian->wr32(&ti->n_image,mmi.n_mipmap+1); + endian->wrf4(&ti->image_val,mmi.n_mipmap); + + err = WriteImageData(&mmi,data+grp_off); + if (!err) + err = SaveFILE(fname,0,overwrite,data,total_size,0); + + abort: + FREE(data); + ResetMMI(&mmi); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveBREFTIMG +( + Image_t * src_img, // pointer to valid img + const MipmapOptions_t *mmo, // NULL or mipmap options + ccp fname, // filename of source + bool overwrite // true: force overwriting +) +{ + DASSERT(src_img); + DASSERT(fname); + + PRINT("SaveBREFTIMG(o=%d) mo=%s, %s\n", + overwrite, InfoMipmapOptions(mmo), fname ); + + + //--- setup images + + u8 *data = 0; + const bool no_pal_stat = Transform2NoPaletteIMG(src_img); + + mipmap_info_t mmi; + enumError err = PrepareImages(&mmi,src_img,mmo); + if (err) + goto abort; + + static int warn_count = 3; + if ( no_pal_stat && warn_count > 0 ) + { + warn_count--; + ERROR0(ERR_WARNING, + "BREFT files don't support palettes, image converted to '%s'.", + PrintFormat3(0,mmi.img.iform,mmi.img.pform)); + } + + + //--- calculate image size + + const uint img_off = 0x20; + const uint total_size = img_off + mmi.image_size; + + PRINT("TOTAL-SIZE: %x = %u\n",total_size,total_size); + PRINT("IMAGE-SIZE: %x = %u, %u*%u, N=1+%u\n", + mmi.img.data_size, mmi.img.data_size, + mmi.img.width, mmi.img.height, mmi.n_mipmap ); + + + //--- alloc and setup data + + data = CALLOC(1,total_size); + const endian_func_t * endian = mmi.img.endian; + + breft_image_t * bi = (breft_image_t*)data; + endian->wr16(&bi->width,mmi.img.width); + endian->wr16(&bi->height,mmi.img.height); + endian->wr32(&bi->img_size,mmi.image_size); + bi->iform = mmi.img.iform; + bi->n_image = mmi.n_mipmap + 1; + + err = WriteImageData(&mmi,data+img_off); + if (!err) + err = SaveFILE(fname,0,overwrite,data,total_size,0); + + abort: + FREE(data); + ResetMMI(&mmi); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PNG callback functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct png_info_t +{ + ccp head_msg; // header for error messages + File_t *file; // file pointer + u8 * cache; // cached data + uint cache_size; // size of 'cache' data + +} png_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void print_png_message ( enumError err, png_structp png_ptr, ccp message ) +{ + const png_info_t * pinfo = png_get_error_ptr(png_ptr); + if (!pinfo) + ERROR0(err,"PNG error: %s",message); + else if (pinfo->file) + { + ERROR0(err,"%s: %s: %s",pinfo->head_msg,message,pinfo->file->fname); + RegisterFileError(pinfo->file,err); + ResetFile(pinfo->file,false); + } + else + ERROR0(err,"%s: %s",pinfo->head_msg,message); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void png_warning_func ( png_structp png_ptr, ccp message ) +{ + print_png_message(ERR_PNG,png_ptr,message); + longjmp(png_jmpbuf(png_ptr),1); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void png_error_func ( png_structp png_ptr, ccp message ) +{ + print_png_message(ERR_PNG,png_ptr,message); + longjmp(png_jmpbuf(png_ptr),1); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void png_read_func + ( png_structp png_ptr, png_bytep dest, png_size_t size ) +{ + png_info_t * pinfo = png_get_io_ptr(png_ptr); + DASSERT(pinfo); + if ( pinfo->cache_size ) + { + const uint max_read = size < pinfo->cache_size ? size : pinfo->cache_size; + memcpy(dest,pinfo->cache,max_read); + pinfo->cache += max_read; + pinfo->cache_size -= max_read; + dest += max_read; + size -= max_read; + errno = 0; + } + + if ( size > 0 ) + fread(dest,1,size,pinfo->file->f); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LoadPNG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadPNG +( + Image_t * img, // destination image + bool init_img, // true: initialize 'img' first + bool mipmaps, // true: try to load mipmaps + + ccp path1, // NULL or part #1 of path + ccp path2 // NULL or part #2 of path +) +{ + DASSERT(img); + PRINT("LoadPNG(mm=%d) %s / %s\n", + mipmaps, path1?path1:"", path2?path2:"" ); + + if (init_img) + InitializeIMG(img); + else + ResetIMG(img); + + + //--- open file + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + + File_t f; + enumError err = OpenFILE(&f,true,path,false,false); + if (err) + { + ResetFile(&f,false); + return err; + } + + err = ReadPNG(img,mipmaps,&f,0,0); + + img->path = f.fname; + f.fname = 0; + img->path_alloced = true; + memcpy(&img->fatt,&f.fatt,sizeof(img->fatt)); + + ResetFile(&f,false); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// + +enumError ReadPNG +( + Image_t * img, // destination image + bool mipmaps, // true: try to load mipmaps + File_t * f, // valid opened file + u8 * cache, // pre read data + uint cache_size // size of 'cache' +) +{ + DASSERT(img); + DASSERT(f); + DASSERT(f->f); + + + //--- setup png + + png_info_t pinfo; + memset(&pinfo,0,sizeof(pinfo)); + pinfo.head_msg = "Open PNG"; + pinfo.file = f; + pinfo.cache = cache; + pinfo.cache_size = cache_size; + + png_structp png_ptr + = png_create_read_struct( PNG_LIBPNG_VER_STRING, + &pinfo, png_error_func, png_warning_func ); + if (!png_ptr) + goto abort_init; + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr,0,0); + goto abort_init; + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr,&info_ptr,0); + goto abort_init; + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); + PRINT("ERR=%u\n",f->max_err); + return f->max_err; + } + + if (pinfo.cache_size) + { + DASSERT(pinfo.cache); + png_set_read_fn(png_ptr,&pinfo,png_read_func); + } + else + png_init_io(png_ptr,f->f); + + png_read_info(png_ptr, info_ptr); + + u32 width, height; + int bit_depth, color_type, interlace; + png_get_IHDR( png_ptr, info_ptr, &width, &height, + &bit_depth, &color_type, &interlace, 0, 0 ); + noPRINT("--> %u*%u*%u, ct=%d, il=%d\n", + width, height, bit_depth, color_type, interlace ); + + if ( interlace != PNG_INTERLACE_NONE ) + { + png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); + RegisterFileError(f,ERR_INVALID_IFORM); + return ERROR0(ERR_INVALID_IFORM, + "Interlaced PNG not supported: %s\n",f->fname); + } + + uint data_size; + bool is_gray; + img->alpha_status = -1; + + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY_ALPHA: + img->alpha_status = 0; + case PNG_COLOR_TYPE_GRAY: + is_gray = true; + data_size = EXPAND8(width) * EXPAND8(height) * 2; + img->iform = IMG_X_GRAY; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + case PNG_COLOR_TYPE_PALETTE: + img->alpha_status = 0; + case PNG_COLOR_TYPE_RGB: + is_gray = false; + data_size = EXPAND8(width) * EXPAND8(height) * 4; + img->iform = IMG_X_RGB; + break; + + default: + png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); + ERROR0(ERR_INVALID_IFORM, + "Unsupported PNG color type: %s\n",f->fname); + RegisterFileError(f,ERR_INVALID_IFORM); + return RegisterFileError(f,ERR_INVALID_IFORM); + } + + if ( color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_palette_to_rgb(png_ptr); + + if ( png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ) + png_set_tRNS_to_alpha(png_ptr); + + if ( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 ) + png_set_expand_gray_1_2_4_to_8(png_ptr); + + if ( bit_depth == 16 ) + png_set_strip_16(png_ptr); + + png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); + png_read_update_info(png_ptr,info_ptr); + + u8 *data = MALLOC(data_size); + + img->data = data; + img->data_alloced = true; + img->data_size = data_size; + img->width = width; + img->height = height; + img->xwidth = EXPAND8(width); + img->xheight = EXPAND8(height); + img->info_iform = img->iform; + img->info_fform = FF_PNG; + + + //--- read data + + const uint rowlen = ( is_gray ? 2 : 4 ) * img->xwidth; + + #if HAVE_PRINT + png_get_IHDR( png_ptr, info_ptr, &width, &height, + &bit_depth, &color_type, &interlace, 0, 0 ); + noPRINT("--> %u*%u*%u, ct=%d, il=%d\n", + width, height, bit_depth, color_type, interlace ); + + const uint channels = png_get_channels(png_ptr,info_ptr); + const uint rowbytes = png_get_rowbytes(png_ptr,info_ptr); + + PRINT("ReadPNG() %u*%u, ch=%u, gray=%d, alpha=%d, rl=%u,%u\n", + width, height, channels, is_gray, img->alpha_status, + rowbytes, rowlen ); + #endif + + while ( height-- > 0 ) + { + png_read_row(png_ptr,(png_bytep)data,NULL); + data += rowlen; + } + DASSERT( data <= img->data + data_size ); + + + //--- close png and file + + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); + + + //--- mipmap support + + if (mipmaps) + { + ccp ext = strrchr(f->fname,'.'); + ccp file = strrchr(f->fname,'/'); + if ( !ext || file && ext < file ) + ext = f->fname + strlen(f->fname); + + Image_t * mm_img = img; + uint count; + for ( count = 1; ; count++ ) + { + char mm_path[PATH_MAX]; + snprintf(mm_path,sizeof(mm_path),"%.*s.mm%u%s", + (int)(ext-f->fname), f->fname, count, ext ); + PRINT("Try open PNG: %s\n",mm_path); + File_t F; + enumError err = OpenFILE(&F,true,mm_path,true,false); // local err + if (err) + { + ResetFile(&F,false); + if ( err != ERR_NOT_EXISTS ) + return err; + break; + } + + mm_img->mipmap = MALLOC(sizeof(*mm_img->mipmap)); + mm_img = mm_img->mipmap; + InitializeIMG(mm_img); + err = ReadPNG(mm_img,false,&F,0,0); + memcpy(&mm_img->fatt,&F.fatt,sizeof(mm_img->fatt)); + ResetFile(&F,false); + if (err) + return err; + } + MM_COUNT(img); + } + + return PatchListIMG(img); + + + //--- abort + + abort_init: + ERROR0(ERR_READ_FAILED,"Error while initializing PNG data: %s\n",f->fname); + return RegisterFileError(f,ERR_READ_FAILED); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SavePNG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SavePNG +( + Image_t * img, // valid image + bool mipmaps, // save: save mipmaps (auto file name) + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + int store_alpha, // <0:no alpha, =0:auto alpha, >0:store alpha + bool overwrite, // true: force overwriting + StringField_t *file_list // not NULL: store filenames of created png files + +) +{ + DASSERT(img); + + //--- setup path & ... + + if ( !path2 || !*path2 ) + { + path2 = path1; + path1 = 0; + } + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + PRINT("SavePNG(mm=%d/%u) {%u,%u} alpha=%d : %s\n", + mipmaps, CountMipmapsIMG(img), + img->conv_count, img->seq_num, store_alpha, path ); + + Transform2XIMG(img); + enumError err = ExecTransformIMG(img); + if (err) + return err; + + if (!store_alpha) + store_alpha = CheckAlphaIMG(img,false); + + if ( GetPaletteCountIF(img->iform) && ( store_alpha > 0 || img->n_pal > 0x100 )) + { + err = ConvertToRGB(img,img,PAL_AUTO); + if (err) + return err; + } + + //--- setup export mode + + enum export_mode + { + MD_F_ALPHA = 1, + MD_F_RGB = 2, + MD_F_PAL = 4, + + MD_G = 0, + MD_GA = MD_F_ALPHA, + MD_RGB = MD_F_RGB, + MD_RGBA = MD_F_RGB | MD_F_ALPHA, + MD_PAL = MD_F_PAL, + + } export_mode; + + switch (img->iform) + { + case IMG_X_GRAY: + export_mode = store_alpha < 0 ? MD_G : MD_GA; + break; + + case IMG_X_RGB: + export_mode = store_alpha < 0 ? MD_RGB : MD_RGBA; + break; + + case IMG_X_PAL: + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + export_mode = MD_PAL; + break; + + default: + return ERROR0(ERR_INVALID_IFORM, + "Image format 0x%02x [%s] not supported for PNG export: %s\n", + img->iform, GetImageFormatName(img->iform,"?"), path ); + } + + + //--- create file + + if (file_list) + { + ccp str = path + ( path1 ? strlen(path1) : 0 ); + if ( *str == '/' ) + str++; + InsertStringField(file_list,str,false); + } + + File_t f; + err = CreateFileOpt(&f,true,path,testmode, overwrite ? path : 0 ); + if ( err || !f.f ) + { + ResetFile(&f,0); + return err; + } + + + //--- setup png + + png_info_t pinfo; + memset(&pinfo,0,sizeof(pinfo)); + pinfo.head_msg = "Create PNG"; + pinfo.file = &f; + + png_structp png_ptr + = png_create_write_struct( PNG_LIBPNG_VER_STRING, + &pinfo, png_error_func, png_warning_func ); + if (!png_ptr) + goto abort_init; + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr,0); + goto abort_init; + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr,&info_ptr); + goto abort; + } + + png_init_io(png_ptr,f.f); + + DASSERT( EXPAND8(img->width) == img->xwidth ); + DASSERT( EXPAND8(img->height) == img->xheight ); + + switch (export_mode) + { + case MD_G: + png_set_IHDR( png_ptr, info_ptr, + img->width, img->height, 8, + PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + break; + + case MD_GA: + png_set_IHDR( png_ptr, info_ptr, + img->width, img->height, 8, + PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + break; + + case MD_RGB: + png_set_IHDR( png_ptr, info_ptr, + img->width, img->height, 8, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + break; + + case MD_RGBA: + png_set_IHDR( png_ptr, info_ptr, + img->width, img->height, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + break; + + + case MD_PAL: + png_set_IHDR( png_ptr, info_ptr, + img->width, img->height, 8, + PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + + // iobuf to transform palette into 'no alpha' + { + png_colorp dest = (png_colorp)iobuf; + DASSERT( img->n_pal * sizeof(*dest) <= sizeof(iobuf) ); + const u8 * src = img->pal; + uint c = img->n_pal; + while ( c-- > 0 ) + { + dest->red = *src++; + dest->green = *src++; + dest->blue = *src++; + src++; + dest++; + } + png_set_PLTE( png_ptr, info_ptr, (png_colorp)iobuf, img->n_pal ); + } + break; + } + + + //---- write strings + + png_text ptext[5], *pt = ptext; + memset(ptext,0,sizeof(ptext)); + + if (!opt_strip) + { + pt->compression = PNG_TEXT_COMPRESSION_NONE; + pt->key = "creator"; + pt->text = TOOLSET_LONG; + pt->text_length = strlen(pt->text); + pt++; + + if ( img->info_fform > FF_UNKNOWN ) + { + pt->compression = PNG_TEXT_COMPRESSION_NONE; + pt->key = "file-format"; + pt->text = (char*)GetNameFF(0,img->info_fform); + pt->text_length = strlen(pt->text); + pt++; + } + + ccp info_text = GetImageFormatName(img->info_iform,0); + if (info_text) + { + pt->compression = PNG_TEXT_COMPRESSION_NONE; + pt->key = "image-format"; + pt->text = (char*)info_text; + pt->text_length = strlen(pt->text); + pt++; + } + + info_text = GetPaletteFormatName(img->info_pform,0); + if (info_text) + { + pt->compression = PNG_TEXT_COMPRESSION_NONE; + pt->key = "palette-format"; + pt->text = (char*)info_text; + pt->text_length = strlen(pt->text); + pt++; + } + + } + + DASSERT( pt - ptext <= sizeof(ptext)/sizeof(*ptext) ); + png_set_text(png_ptr,info_ptr,ptext,pt-ptext); + png_write_info(png_ptr,info_ptr); + + //--- write image data + + switch (export_mode) + { + case MD_G: + { + if ( img->xwidth > sizeof(iobuf) ) + goto abort_init; + + const u8 * data = img->data; + uint ih = img->height; + while ( ih-- > 0 ) + { + u8 * dest = (u8*)iobuf; + uint iw = img->xwidth; + while ( iw-- > 0 ) + { + *dest++ = *data++; + data++; + } + png_write_row(png_ptr,(png_bytep)iobuf); + } + } + break; + + case MD_GA: + { + const uint rowlen = img->xwidth * 2; + const u8 * data = img->data; + uint ih = img->height; + while ( ih-- > 0 ) + { + png_write_row(png_ptr,(png_bytep)data); + data += rowlen; + } + } + break; + + case MD_RGB: + { + if ( img->xwidth * 3 > sizeof(iobuf) ) + goto abort_init; + + const u8 * data = img->data; + uint ih = img->height; + while ( ih-- > 0 ) + { + u8 * dest = (u8*)iobuf; + uint iw = img->xwidth; + while ( iw-- > 0 ) + { + *dest++ = *data++; + *dest++ = *data++; + *dest++ = *data++; + data++; + } + png_write_row(png_ptr,(png_bytep)iobuf); + } + } + break; + + case MD_RGBA: + { + const uint rowlen = img->xwidth * 4; + const u8 * data = img->data; + uint ih = img->height; + while ( ih-- > 0 ) + { + png_write_row(png_ptr,(png_bytep)data); + data += rowlen; + } + } + break; + + case MD_PAL: + { + u8 * dest_end = (u8*)iobuf + img->xwidth; + const u16 *data = (u16*)img->data; + uint ih = img->height; + while ( ih-- > 0 ) + { + // transform index into single byte + u8 * dest = (u8*)iobuf; + while ( dest < dest_end ) + *dest++ = *data++; + png_write_row(png_ptr,(png_bytep)iobuf); + } + } + break; + } + + + //--- close png and file + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr,&info_ptr); + goto abort; + } + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr,&info_ptr); + + if (opt_preserve) + memcpy(&f.fatt,&img->fatt,sizeof(f.fatt)); + err = ResetFile(&f,opt_preserve); + + if ( mipmaps && img->mipmap ) + { + ccp ext = strrchr(path2,'.'); + ccp file = strrchr(path2,'/'); + if ( !ext || file && ext < file ) + ext = path2 + strlen(path2); + + uint count; + for ( count = 1; !err; count++ ) + { + img = img->mipmap; + if (!img) + break; + + char mm_path[PATH_MAX]; + snprintf(mm_path,sizeof(mm_path),"%.*s.mm%u%s", + (int)(ext-path2), path2, count, ext ); + + PRINT("## SAVE -> %s\n",mm_path); + err = SavePNG(img,false,path1,mm_path,store_alpha,overwrite,file_list); + } + } + return err; + + + //--- abort + + abort_init: + ERROR0(ERR_WRITE_FAILED,"Error while initializing PNG data: %s\n",path); + abort: + RegisterFileError(&f,ERR_WRITE_FAILED); + return ResetFile(&f,false); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ExportPNG /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ExportPNG +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + FileAttrib_t * fatt, // NULL or file attributes + const void * img_data, // image data + uint img_size, // image size, needed for validation + uint img_index, // index of sub image, 0:main, >0:mipmaps + bool mipmaps, // true: load and export mipmaps + uint * n_image, // not null: store detected image count + const endian_func_t * endian, // endian functions to read data + FormatFieldItem_t * ffi, // not null: store detected image+pal format + bool create_png, // false: do some calculations but don't create png + StringField_t *file_list // not NULL: store filenames of created png files +) +{ + DASSERT(img_data); + DASSERT(endian); + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2); + TRACE("ExportPNG(size=%u) %s\n", img_size, path ); + + + #if ENABLE_EXPORT_TIMER + static u64 total_time = 0; + u64 start_time = GetTimerUSec(); + #endif + + Image_t img; + enumError err = AssignIMG( &img, 1, img_data, img_size, + img_index, mipmaps, endian, path ); + if (n_image) + *n_image = img.info_n_image; + if (ffi) + { + ffi->iform = img.iform; + ffi->pform = img.pform; + } + if (fatt) + memcpy(&img.fatt,fatt,sizeof(img.fatt)); + + if (!err) + { + #if ENABLE_IMAGE_TYPE_LOG + { + static bool log_opened = false; + static FILE * log = 0; + if (!log_opened) + { + log_opened = true; + ccp fname = IsDirectory("pool",false) + ? "pool/_image-format.log" + : "_image-format.log"; + log = fopen(fname,"wb"); + if (log) + printf(">>> IMAGE LOG OPENED: %s <<<\n",fname); + } + if (log) + fprintf(log,"%02x [%-5s %-6s] : %4u * %4u : %s\n", + iform, GetNameFF(0,fform), GetImageFormatName(iform,"?"), + img.width, img.height, path ); + } + #endif + + if (create_png) + { + err = ConvertIMG(&img,false,0,IMG_X_AUTO,PAL_INVALID); + if (!err) + err = SavePNG(&img,mipmaps,path1,path2,0,false,file_list); + + #if ENABLE_EXPORT_TIMER + start_time = GetTimerUSec() - start_time; + total_time += start_time; + printf("\t\t--> TIME: %s [%s total]\n", + PrintUSec(0,0,start_time,ENABLE_EXPORT_TIMER), + PrintUSec(0,0,total_time,ENABLE_EXPORT_TIMER) ); + #endif + } + } + ResetIMG(&img); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan file and image format /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum transform_mode_t +{ + TM_IDX_FILE, // file format (=byte index) + TM_IDX_IMG, // image format (=byte index) + TM_IDX_PAL, // palette format (=byte index) + TM_IDX_PALETTE, // switch PALETTE + TM_IDX_COLOR, // switch COLOR + TM_IDX_ALPHA, // switch ALPHA + + TM_IDX_N, // number of modes + TM_IDX_MASK = 7, + + TM_F_PAL = 0x10, // flag: Palette support + + TF_ON = 1, + TF_OFF = 2, + +} transform_mode_t; + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t cmdtab_transform[] = +{ + //--- file formats + + { FF_TPL, "TPL", 0, TM_IDX_FILE|TM_F_PAL }, + { FF_BTI, "BTI", 0, TM_IDX_FILE|TM_F_PAL }, + { FF_TEX, "TEX", "TEX0", TM_IDX_FILE }, + { FF_BREFT_IMG, "BREFT-IMG", "BREFTIMG", TM_IDX_FILE }, + { FF_BREFT_IMG,"REFT-IMG", "REFTIMG", TM_IDX_FILE }, + { FF_BREFT_IMG,"BT-IMG", "BTIMG", TM_IDX_FILE }, + { FF_PNG, "PNG", 0, TM_IDX_FILE }, + + + //--- image formats + + { IMG_I4, "I4", 0, TM_IDX_IMG }, + { IMG_I8, "I8", 0, TM_IDX_IMG }, + { IMG_IA4, "IA4", 0, TM_IDX_IMG }, + { IMG_IA8, "IA8", 0, TM_IDX_IMG }, + { IMG_RGB565, "RGB565", "R565", TM_IDX_IMG }, + { IMG_RGB5A3, "RGB5A3", "R3", TM_IDX_IMG }, + { IMG_RGBA32, "RGBA32", "RGBA8", TM_IDX_IMG }, + { IMG_RGBA32, "R32", "R8", TM_IDX_IMG }, + { IMG_C4, "C4", "CI4", TM_IDX_IMG|TM_F_PAL }, + { IMG_C8, "C8", "CI8", TM_IDX_IMG|TM_F_PAL }, + { IMG_C14X2, "C14X2", "CI14X2", TM_IDX_IMG|TM_F_PAL }, + { IMG_CMPR, "CMPR", 0, TM_IDX_IMG }, + + + //--- palette formats + + { PAL_IA8, "PIA8", "P-IA8", TM_IDX_PAL }, + { PAL_IA8, "P8", "P-8", TM_IDX_PAL }, + { PAL_RGB565, "PRGB565", "P-RGB565", TM_IDX_PAL }, + { PAL_RGB565, "P565", "P-565", TM_IDX_PAL }, + { PAL_RGB5A3, "PRGB5A3", "P-RGB5A3", TM_IDX_PAL }, + { PAL_RGB5A3, "P3", "P-3", TM_IDX_PAL }, + + + //--- switch PALETTE + + { TF_ON, "PALETTE", 0, TM_IDX_PALETTE }, + { TF_OFF, "-PALETTE", "NOPALETTE", TM_IDX_PALETTE }, + + + //--- switch COLOR + + { TF_ON, "COLOR", 0, TM_IDX_COLOR }, + { TF_OFF, "GRAY", "GREY", TM_IDX_COLOR }, + + + //--- switch ALPHA + + { TF_ON, "ALPHA", 0, TM_IDX_ALPHA }, + { TF_OFF, "-ALPHA", "NOALPHA", TM_IDX_ALPHA }, + + + //--- end of table + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct transform_t +{ + char src[TM_IDX_N]; + char dest[TM_IDX_N]; + +} transform_t; + +#define MAX_TRANSFORM 100 + +static uint n_transform = 0; +static transform_t transform[MAX_TRANSFORM]; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct transform_term_t +{ + ccp arg; + char res[TM_IDX_N]; + uint opt[TM_IDX_N]; + +} transform_term_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTransformKeyword ( transform_term_t * term ) +{ + DASSERT(term); + ccp name = term->arg; + + while ( *name > 0 && *name <= ' ' ) + name++; + + char namebuf[20], *end = namebuf + sizeof(namebuf) - 1, *dest = namebuf; + while ( *name >= '0' && *name <= '9' + || *name >= 'a' && *name <= 'z' + || *name >= 'A' && *name <= 'Z' + || *name == '-' ) + { + if ( dest < end ) + *dest++ = *name; + name++; + } + while ( *name > 0 && *name <= ' ' ) + name++; + term->arg = name; + + if ( dest == namebuf ) + return ERR_OK; + *dest = 0; + + const KeywordTab_t * cmd = ScanKeyword(0,namebuf,cmdtab_transform); + if (!cmd) + return ERROR0(ERR_SYNTAX, + "Invalid keyword for option --transform: %s\n",namebuf); + + uint idx = cmd->opt & TM_IDX_MASK; + DASSERT( idx < TM_IDX_N ); + term->res[idx] = cmd->id; + term->opt[idx] = cmd->opt; + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTransformTerm ( transform_term_t * term, ccp arg ) +{ + DASSERT(term); + memset(term,0,sizeof(*term)); + memset(term->res,-1,sizeof(term->res)); + if (!arg) + return ERR_OK; + + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == '.' ) + arg++; + term->arg = arg; + enumError err = ScanTransformKeyword(term); + if (err) + return err; + arg = term->arg; + if ( *arg != '.' ) + return ERR_OK; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTransform ( ccp arg ) +{ + n_transform = 0; + if (!arg) + return 0; + + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == ',' ) + arg++; + if (!*arg) + break; + + ccp src = 0, dest = arg; + while ( *arg && *arg != ',' && *arg != '=' ) + arg++; + if ( *arg == '=' ) + { + src = dest; + arg++; + while ( *arg > 0 && *arg <= ' ' ) + arg++; + dest = arg; + while ( *arg && *arg != ',' && *arg != '=' ) + arg++; + } + + if (!src && arg == dest ) + goto err_abort; + + transform_term_t dterm; + enumError err = ScanTransformTerm(&dterm,dest); + if (err) + return err; + if ( dterm.arg != arg ) + { + arg = dterm.arg; + goto err_abort; + } + PRINT0("DEST: %d,%d,%d [%u,%u,%u]\n", + dterm.res[0], dterm.res[1], dterm.res[2], + dterm.opt[0], dterm.opt[1], dterm.opt[2] ); + + for(;;) + { + transform_term_t sterm; + enumError err = ScanTransformTerm(&sterm,src); + if (err) + return err; + PRINT0("SRC: %d,%d,%d [%u,%u,%u]\n", + sterm.res[0], sterm.res[1], sterm.res[2], + sterm.opt[0], sterm.opt[1], sterm.opt[2] ); + + if ( n_transform == MAX_TRANSFORM ) + { + ERROR0(ERR_SYNTAX, + "Option --transform: Only %u terms allowed!\n",MAX_TRANSFORM); + return 1; + } + + transform_t *t = transform + n_transform++; + memcpy(t->src,sterm.res,sizeof(t->src)); + memcpy(t->dest,dterm.res,sizeof(t->dest)); + + src = sterm.arg; + if ( !src ) + break; + if ( *src == '+' ) + src++; + else if ( *src == '=' ) + break; + else + { + arg = src; + goto err_abort; + } + } + } + return 0; + + err_abort: + ERROR0(ERR_SYNTAX, + "Invalid parameter for option --transform: %.20s\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetTransformName ( uint index, int mode, ccp not_found_text ) +{ + if ( mode != -1 ) + { + const KeywordTab_t * ct; + for ( ct = cmdtab_transform; ct->name1; ct++ ) + if ( ct->id == mode && ( ct->opt & TM_IDX_MASK ) == index ) + return ct->name1; + } + return not_found_text; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpTransformList ( FILE * f, int indent, bool force ) +{ + DASSERT(f); + if (!n_transform) + return; + + fprintf(f,"\n" + "%*s source formats -> destination formats\n" + "%*s file image palette pal color alph -> file image palette pal color alph\n" + "%*s--------------------------------------------------------------------------------\n", + indent, "", indent, "", indent, "" ); + uint i; + for ( i = 0; i < n_transform; i++ ) + { + const transform_t *t = transform + i; + fprintf(f, + "%*s %-6s %-6s %-7s %-4.4s %-5s %-4.4s" + " -> %-6s %-6s %-7s %-4.4s %-5s %-4.4s\n", + indent, "", + GetTransformName(0,t->src[0],"*"), + GetTransformName(1,t->src[1],"*"), + GetTransformName(2,t->src[2],"*"), + GetTransformName(3,t->src[3],"*"), + GetTransformName(4,t->src[4],"*"), + GetTransformName(5,t->src[5],"*"), + GetTransformName(0,t->dest[0],"*"), + GetTransformName(1,t->dest[1],"*"), + GetTransformName(2,t->dest[2],"*"), + GetTransformName(3,t->dest[3],"*"), + GetTransformName(4,t->dest[4],"*"), + GetTransformName(5,t->dest[5],"*") ); + } + fprintf(f,"\n"); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTransformTuple ( ccp tuple ) +{ + const uint bufsize = 50; + char *buf = GetCircBuf(bufsize); + char *dest = buf, *bufend = buf + bufsize - TM_IDX_N; + + uint idx; + for ( idx = 0; idx < TM_IDX_N; idx++ ) + { + if ( tuple[idx] != -1 ) + { + ccp name = GetTransformName(idx,tuple[idx],0); + if (name) + { + *dest++ = '.'; + dest = StringCopyE(dest,bufend++,name); + } + } + } + *dest = 0; + return dest == buf ? "*" : buf+1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintFormat3 +( + file_format_t fform, // file format + image_format_t iform, // image format + palette_format_t pform // palette format +) +{ + char tuple[TM_IDX_N]; + memset(tuple,-1,sizeof(tuple)); + tuple[TM_IDX_FILE] = fform == FF_BREFT ? FF_BREFT_IMG : fform; + if ( fform != FF_PNG ) + { + tuple[TM_IDX_IMG] = iform; + if (GetPaletteCountIF(iform)) + tuple[TM_IDX_PAL] = pform; + } + return PrintTransformTuple(tuple); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// transformation functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupTransformIMG ( Image_t * img ) +{ + DASSERT(img); + + if (!img->tform_valid) + { + img->tform_valid = true; + img->tform_exec = false; + img->tform_gray = false; + img->tform_noalpha = false; + img->tform_fform0 = img->tform_fform = FF_INVALID; + img->tform_iform0 = img->tform_iform = img->iform; + img->tform_pform0 = img->tform_pform = img->pform; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2InternIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + + switch (img->tform_iform) + { + case IMG_X_GRAY: + img->tform_iform = img->tform_noalpha ? IMG_I8 : IMG_IA4; + return img->tform_exec = true; + + case IMG_X_RGB: + img->tform_iform = img->tform_noalpha ? IMG_RGB565 : IMG_RGB5A3; + return img->tform_exec = true; + + case IMG_X_PAL4: + img->tform_iform = IMG_C4; + return img->tform_exec = true; + + case IMG_X_PAL8: + img->tform_iform = IMG_C8; + return img->tform_exec = true; + + case IMG_X_PAL: + case IMG_X_PAL14: + img->tform_iform = IMG_C14X2; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2XIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + + switch (img->tform_iform) + { + case IMG_I4: + case IMG_I8: + case IMG_IA4: + case IMG_IA8: + img->tform_iform = IMG_X_GRAY; + return img->tform_exec = true; + + case IMG_RGB565: + case IMG_RGB5A3: + case IMG_RGBA32: + case IMG_CMPR: + img->tform_iform = IMG_X_RGB; + return img->tform_exec = true; + + case IMG_C4: + img->tform_iform = IMG_X_PAL4; + return img->tform_exec = true; + + case IMG_C8: + img->tform_iform = IMG_X_PAL8; + return img->tform_exec = true; + + case IMG_C14X2: + img->tform_iform = IMG_X_PAL14; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2XRGB ( Image_t * img ) +{ + SetupTransformIMG(img); + if ( img->tform_iform == IMG_X_RGB ) + return false; + + img->tform_iform = IMG_X_RGB; + return img->tform_exec = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2PaletteIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + noPRINT("Transform2PaletteIMG(%s)\n",GetImageFormatName(img->tform_iform,"?")); + + switch (img->tform_iform) + { + case IMG_I4: + img->tform_iform = IMG_C4; + img->tform_pform = PAL_IA8; + return img->tform_exec = true; + + case IMG_I8: + case IMG_IA4: + case IMG_IA8: + img->tform_iform = IMG_C8; + img->tform_pform = PAL_IA8; + return img->tform_exec = true; + + case IMG_RGB565: + img->tform_iform = IMG_C8; + img->tform_pform = PAL_RGB565; + return img->tform_exec = true; + + case IMG_RGB5A3: + case IMG_CMPR: + img->tform_iform = IMG_C8; + img->tform_pform = img->tform_noalpha ? PAL_RGB565 : PAL_RGB5A3; + return img->tform_exec = true; + + case IMG_RGBA32: + img->tform_iform = IMG_C14X2; + img->tform_pform = img->tform_noalpha ? PAL_RGB565 : PAL_RGB5A3; + return img->tform_exec = true; + + case IMG_X_GRAY: + case IMG_X_RGB: + img->tform_iform = IMG_X_PAL; + img->tform_pform = PAL_X_RGB; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2NoPaletteIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + + switch (img->tform_iform) + { + case IMG_C4: + case IMG_C8: + case IMG_C14X2: + img->tform_iform = PaletteToImageFormat(img->tform_pform,IMG_X_RGB); + return img->tform_exec = true; + + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + case IMG_X_PAL: + img->tform_iform = IMG_X_RGB; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2GrayIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + PRINT("TRANSFORM GRAY: %s\n", + PrintFormat3(0,img->tform_iform,img->tform_pform)); + + switch (img->tform_iform) + { + case IMG_RGB565: + case IMG_RGB5A3: + case IMG_CMPR: + img->tform_gray = true; + img->tform_iform = IMG_I8; + return img->tform_exec = true; + + case IMG_RGBA32: + img->tform_gray = true; + img->tform_iform = img->tform_noalpha ? IMG_I8 : IMG_IA8; + return img->tform_exec = true; + + case IMG_C4: + case IMG_C8: + case IMG_C14X2: + img->tform_gray = true; + img->tform_pform = PAL_IA8; + return img->tform_exec = true; + + case IMG_X_RGB: + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + case IMG_X_PAL: + img->tform_gray = true; + img->tform_iform = IMG_X_GRAY; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2ColorIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + + switch (img->tform_iform) + { + case IMG_I4: + case IMG_I8: + img->tform_iform = IMG_RGB565; + return img->tform_exec = true; + + case IMG_IA4: + case IMG_IA8: + img->tform_iform = IMG_RGB5A3; + return img->tform_exec = true; + + case IMG_C4: + case IMG_C8: + case IMG_C14X2: + if ( img->tform_pform != PAL_RGB565 ) + img->tform_pform = PAL_RGB5A3; + return img->tform_exec = true; + + case IMG_X_GRAY: + img->tform_iform = IMG_X_RGB; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2AlphaIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + + switch (img->tform_iform) + { + case IMG_I4: + img->tform_iform = IMG_IA4; + return img->tform_exec = true; + + case IMG_I8: + img->tform_iform = IMG_IA8; + return img->tform_exec = true; + + case IMG_RGB565: + img->tform_iform = IMG_RGB5A3; + return img->tform_exec = true; + + case IMG_C4: + case IMG_C8: + case IMG_C14X2: + if ( img->tform_pform != PAL_IA8 ) + img->tform_pform = PAL_RGB5A3; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool Transform2NoAlphaIMG ( Image_t * img ) +{ + SetupTransformIMG(img); + + switch (img->tform_iform) + { + case IMG_IA4: + img->tform_iform = IMG_I4; + img->tform_noalpha = true; + return img->tform_exec = true; + + case IMG_IA8: + img->tform_iform = IMG_I8; + img->tform_noalpha = true; + return img->tform_exec = true; + + case IMG_RGB5A3: + case IMG_RGBA32: + img->tform_iform = IMG_RGB565; + img->tform_noalpha = true; + return img->tform_exec = true; + + case IMG_C4: + case IMG_C8: + case IMG_C14X2: + if ( img->tform_pform != PAL_IA8 ) + img->tform_iform = IMG_RGB565; + img->tform_noalpha = true; + return img->tform_exec = true; + + case IMG_X_GRAY: + case IMG_X_RGB: + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + case IMG_X_PAL: + img->tform_noalpha = true; + return img->tform_exec = true; + + default: + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool TransformIMG +( + // The image is NOT converted, only the planned transformations + // are calculated. Use ExecTransformIMG() to execute transformation + // -> returns TRUE if a rule match + + Image_t * img, // destination of transforming + int indent // <0: no logging + // >=0: log transform with indent +) +{ + SetupTransformIMG(img); + + uint i; + for ( i = 0; i < n_transform; i++ ) + { + const transform_t *t = transform + i; + ccp s = t->src; + if ( s[TM_IDX_FILE] != FF_INVALID && s[TM_IDX_FILE] != img->tform_fform0 + || s[TM_IDX_IMG] != FF_INVALID && s[TM_IDX_IMG] != img->tform_iform0 + || s[TM_IDX_PAL] != FF_INVALID && s[TM_IDX_PAL] != img->tform_pform0 ) + { + continue; + } + + PRINT("TFORM-1: %s -> %s\n", + PrintTransformTuple(t->src), + PrintTransformTuple(t->dest) ); + + const int palette = s[TM_IDX_PALETTE]; + if ( palette > 0 ) + { + if ( ( palette == TF_OFF ) == ( GetPaletteCountIF(img->iform) != 0 ) ) + continue; + } + + const int color = s[TM_IDX_COLOR]; + if ( color > 0 ) + { + if ( ( color == TF_ON ) == IsGrayIMG(img) ) + continue; + } + + const int alpha = s[TM_IDX_ALPHA]; + if ( alpha > 0 ) + { + if ( ( alpha == TF_ON ? -1 : 1 ) == CheckAlphaIMG(img,false) ) + continue; + } + + noPRINT("TFORM-2: %s -> %s\n", + PrintTransformTuple(t->src), + PrintTransformTuple(t->dest) ); + + if ( indent >= 0 ) + { + printf("%*s- Transform: %s -> %s\n", + indent, "", + PrintTransformTuple(t->src), + PrintTransformTuple(t->dest) ); + } + + if ( t->dest[TM_IDX_PALETTE] == TF_ON ) + Transform2PaletteIMG(img); + else if ( t->dest[TM_IDX_PALETTE] == TF_OFF ) + Transform2NoPaletteIMG(img); + + if ( t->dest[TM_IDX_COLOR] == TF_ON ) + Transform2ColorIMG(img); + else if ( t->dest[TM_IDX_COLOR] == TF_OFF ) + Transform2GrayIMG(img); + + if ( t->dest[TM_IDX_ALPHA] == TF_ON ) + Transform2AlphaIMG(img); + else if ( t->dest[TM_IDX_ALPHA] == TF_OFF ) + Transform2NoAlphaIMG(img); + + if ( t->dest[TM_IDX_FILE] != FF_INVALID ) + img->tform_fform = t->dest[TM_IDX_FILE]; + + if ( t->dest[TM_IDX_IMG] != IMG_INVALID ) + img->tform_iform = t->dest[TM_IDX_IMG]; + + if ( t->dest[TM_IDX_PAL] != PAL_INVALID ) + img->tform_pform = t->dest[TM_IDX_PAL]; + + return img->tform_exec = true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool Transform3IMG +( + Image_t * img, // valid image + file_format_t fform, // file format + image_format_t iform, // image format + palette_format_t pform, // palette format + bool def_base // define *form values as base +) +{ + SetupTransformIMG(img); + bool stat = false; + + fform = IsImageFF(fform,true); + if ( fform != FF_UNKNOWN && img->tform_fform != fform ) + { + stat = true; // do not set img->tform_exec + img->tform_fform = fform; + } + + if ( iform != IMG_INVALID && img->tform_iform != iform ) + { + img->tform_exec = stat = true; + img->tform_iform = iform; + } + + if ( pform != PAL_INVALID && img->tform_pform != pform ) + { + img->tform_exec = stat = true; + img->tform_pform = pform; + } + + if (def_base) + { + img->tform_fform0 = fform; + img->tform_iform0 = iform; + img->tform_pform0 = pform; + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ExecTransformIMG +( + Image_t * img // image to transform +) +{ + DASSERT(img); + + enumError err = ERR_OK; + if ( !img->tform_valid || !img->tform_exec ) + goto abort; + + PRINT("ExecTransformIMG() %s -> %s\n", + PrintFormat3(0,img->iform,img->pform), + PrintFormat3(0,img->tform_iform,img->tform_pform) ); + + if ( img->tform_gray && !img->is_grayed ) + { + err = ConvertIMG( img, false, 0, IMG_X_GRAY, PAL_AUTO ); + if (err) + goto abort; + } + + if ( img->tform_noalpha && CheckAlphaIMG(img,false) >= 0 ) + { + err = ConvertIMG( img, false, 0, IMG_X_AUTO, PAL_AUTO ); + if (err) + goto abort; + + switch(img->iform) + { + case IMG_X_GRAY: + { + uint n = img->xwidth * img->xheight; + u8 * data = img->data + 1; + while ( n-- > 0 ) + { + *data = 0xff; + data += 2; + } + } + break; + + case IMG_X_RGB: + { + uint n = img->xwidth * img->xheight; + u8 * data = img->data + 3; + while ( n-- > 0 ) + { + *data = 0xff; + data += 4; + } + } + break; + + case IMG_X_PAL: + case IMG_X_PAL4: + case IMG_X_PAL8: + case IMG_X_PAL14: + if (img->n_pal) + { + DASSERT(img->pal); + uint n = img->n_pal; + u8 * data = img->data + 1; + while ( n-- > 0 ) + { + *data = 0xff; + data += 2; + } + } + break; + + default: + return ERROR0(ERR_INTERNAL,0); + } + img->alpha_status = -1; + } + + err = ConvertIMG( img, false, 0, img->tform_iform, img->tform_pform ); + + abort: + img->tform_valid = false; + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TPL support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint GetNImagesTPL +( + const u8 * data, // TPL data + uint data_size, // size of tpl data + const endian_func_t * endian // endian functions +) +{ + DASSERT(data); + DASSERT(endian); + + return data_size >= sizeof(tpl_header_t) && endian->rd32(data) == TPL_MAGIC_NUM + ? endian->rd32(data+4) + : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SetupPointerTPL +( + const u8 * data, // TPL data + uint data_size, // size of tpl data + uint img_index, // index of image to extract + const tpl_header_t ** tpl_head, // not NULL: store pointer here + const tpl_imgtab_t ** tpl_tab, // not NULL: store pointer here + const tpl_pal_header_t ** tpl_pal, // not NULL: store pointer here + const tpl_img_header_t ** tpl_img, // not NULL: store pointer here + const u8 ** pal_data, // not NULL: store pointer to pal data + const u8 ** img_data, // not NULL: store pointer to img data + const endian_func_t * endian // endian functions +) +{ + DASSERT(data); + DASSERT(endian); + + if ( data_size >= sizeof(tpl_header_t) && endian->rd32(data) == TPL_MAGIC_NUM ) + { + const tpl_header_t *tpl = (tpl_header_t*)data; + const uint n_img = endian->rd32(&tpl->n_image); + const u32 tab_off = endian->rd32(&tpl->imgtab_off); + if ( img_index < n_img && tab_off + n_img*sizeof(tpl_imgtab_t) <= data_size ) + { + const tpl_imgtab_t *tab = (tpl_imgtab_t*)( data + tab_off ) + img_index; + const u32 img_off = endian->rd32(&tab->image_off); + if ( img_off && img_off + sizeof(tpl_img_header_t) <= data_size ) + { + const tpl_img_header_t *img = (tpl_img_header_t*)(data+img_off); + const u32 img_data_off = endian->rd32(&img->data_off); + if ( !img_data_off || img_data_off >= data_size ) + goto abort; + + if (tpl_head) *tpl_head = tpl; + if (tpl_tab) *tpl_tab = tab; + if (tpl_pal) *tpl_pal = 0; + if (tpl_img) *tpl_img = img; + if (pal_data) *pal_data = 0; + if (img_data) *img_data = data + img_data_off; + + const u32 pal_off = endian->rd32(&tab->palette_off); + if (pal_off) + { + if ( pal_off > sizeof(tpl_pal_header_t) >= data_size ) + goto abort; + + const tpl_pal_header_t *pal = (tpl_pal_header_t*)(data+pal_off); + const u32 pal_data_off = endian->rd32(&pal->data_off); + if ( !pal_data_off || pal_data_off >= data_size ) + goto abort; + + if (tpl_pal) *tpl_pal = pal; + if (pal_data) *pal_data = data + pal_data_off; + } + return true; + } + } + } + + abort: + if (tpl_head) *tpl_head = 0; + if (tpl_tab) *tpl_tab = 0; + if (tpl_pal) *tpl_pal = 0; + if (tpl_img) *tpl_img = 0; + if (pal_data) *pal_data = 0; + if (img_data) *img_data = 0; + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BTI support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint GetNImagesBTI +( + const u8 * data, // bti data + uint data_size // size of bti data +) +{ + DASSERT(data); + if ( IsValidBTI(data,data_size,0,0) >= VALID_ERROR ) + return 0; + + const bti_header_t *bti = (bti_header_t*)data; + return bti->n_image; +} + +/////////////////////////////////////////////////////////////////////////////// +#if 0 + +bool SetupPointerBTI +( + const u8 * data, // BTI data + uint data_size, // size of bti data + uint img_index, // index of image to extract + const bti_header_t ** bti_head, // not NULL: store pointer here + const bti_imgtab_t ** bti_tab, // not NULL: store pointer here + const bti_pal_header_t ** bti_pal, // not NULL: store pointer here + const bti_img_header_t ** bti_img, // not NULL: store pointer here + const u8 ** pal_data, // not NULL: store pointer to pal data + const u8 ** img_data, // not NULL: store pointer to img data + const endian_func_t * endian // endian functions +) +{ + DASSERT(data); + DASSERT(endian); + + if ( data_size >= sizeof(bti_header_t) && endian->rd32(data) == BTI_MAGIC_NUM ) + { + const bti_header_t *bti = (bti_header_t*)data; + const uint n_img = endian->rd32(&bti->n_image); + const u32 tab_off = endian->rd32(&bti->imgtab_off); + if ( img_index < n_img && tab_off + n_img*sizeof(bti_imgtab_t) <= data_size ) + { + const bti_imgtab_t *tab = (bti_imgtab_t*)( data + tab_off ) + img_index; + const u32 img_off = endian->rd32(&tab->image_off); + if ( img_off && img_off + sizeof(bti_img_header_t) <= data_size ) + { + const bti_img_header_t *img = (bti_img_header_t*)(data+img_off); + const u32 img_data_off = endian->rd32(&img->data_off); + if ( !img_data_off || img_data_off >= data_size ) + goto abort; + + if (bti_head) *bti_head = bti; + if (bti_tab) *bti_tab = tab; + if (bti_pal) *bti_pal = 0; + if (bti_img) *bti_img = img; + if (pal_data) *pal_data = 0; + if (img_data) *img_data = data + img_data_off; + + const u32 pal_off = endian->rd32(&tab->palette_off); + if (pal_off) + { + if ( pal_off > sizeof(bti_pal_header_t) >= data_size ) + goto abort; + + const bti_pal_header_t *pal = (bti_pal_header_t*)(data+pal_off); + const u32 pal_data_off = endian->rd32(&pal->data_off); + if ( !pal_data_off || pal_data_off >= data_size ) + goto abort; + + if (bti_pal) *bti_pal = pal; + if (pal_data) *pal_data = data + pal_data_off; + } + return true; + } + } + } + + abort: + if (bti_head) *bti_head = 0; + if (bti_tab) *bti_tab = 0; + if (bti_pal) *bti_pal = 0; + if (bti_img) *bti_img = 0; + if (pal_data) *pal_data = 0; + if (img_data) *img_data = 0; + return false; +} + +#endif +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-image3.c b/project/src/lib-image3.c new file mode 100644 index 0000000..2f04abb --- /dev/null +++ b/project/src/lib-image3.c @@ -0,0 +1,2219 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-std.h" +#include "lib-image.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data structures /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct PatchRange1_t +{ + int dest; // destination coordinate + int src; // source coordinate + int len; // length of copy operation + int size; // total number of pixels + +} PatchRange1_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct PatchRange2_t +{ + PatchRange1_t x; // horizontal values + PatchRange1_t y; // vertical values + +} PatchRange2_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image creation + drawing /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateIMG +( + Image_t * img, // image object + bool init_img, // true: initialize 'img' first + uint width, // width a new image + uint height, // height of new image + Color_t col // fill color +) +{ + DASSERT(img); + + if (init_img) + InitializeIMG(img); + + if (!width) + width = 1; + const uint xwidth = EXPAND8(width); + + if (!height) + height = 1; + const uint xheight = EXPAND8(height); + + uint size = xwidth * xheight; + Color_t *data = MALLOC(size*sizeof(col)); + + img->width = width; + img->height = height; + AssignDataRGB(img,0,(u8*)data); + while ( size-- > 0 ) + *data++ = col; + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CopyRectIMG +( + Image_t * dest, // dest image + int dest_x, // left coordinate of dest + int dest_y, // top coordinate of dest + + const Image_t * src, // source image + int src_x, // left coordinate of src + int src_y, // top coordinate of src + + int width, // width of rect to copy + int height, // height of rect to copy + + PatchImage_t patch_mode // one of COPY, MIX, FOREGROUND, BACKGROUND +) +{ + DASSERT(dest); + DASSERT(src); + + if ( dest->iform != IMG_X_RGB ) + ConvertIMG(dest,false,0,IMG_X_RGB,PAL_AUTO); + DASSERT(dest->iform == IMG_X_RGB); + + if ( src->iform != IMG_X_RGB ) + return ERROR0(ERR_INTERNAL,0); + + + //--- normalize horizontal values + + if ( dest_x < 0 ) + { + width += dest_x; + src_x -= dest_x; + dest_x = 0; + } + + if ( src_x < 0 ) + { + width += src_x; + dest_x -= src_x; + src_x = 0; + } + + int max = dest->width - dest_x; + if ( width > max ) + width = max; + + max = src->width - src_x; + if ( width > max ) + width = max; + + + //--- normalize vertical values + + if ( dest_y < 0 ) + { + height += dest_y; + src_y -= dest_y; + dest_y = 0; + } + + if ( src_y < 0 ) + { + height += src_y; + dest_y -= src_y; + src_y = 0; + } + + max = dest->height - dest_y; + if ( height > max ) + height = max; + + max = src->height - src_y; + if ( height > max ) + height = max; + + + //--- copy rect + + if ( width > 0 && height > 0 ) + { + DASSERT( dest_x >= 0 && dest_x + width <= dest->width ); + DASSERT( dest_y >= 0 && dest_y + height <= dest->height ); + DASSERT( src_x >= 0 && src_x + width <= src->width ); + DASSERT( src_y >= 0 && src_y + height <= src->height ); + + Color_t *dcol = (Color_t*)dest->data + dest_y * dest->xwidth + dest_x; + const Color_t *scol = (Color_t*)src->data + src_y * src->xwidth + src_x; + + const uint dest_skip = dest->xwidth - width; + const uint src_skip = src->xwidth - width; + + switch ( patch_mode & PIM_M_COPY ) + { + case PIM_BACKGROUND: + while ( height-- > 0 ) + { + uint n = width; + while ( n-- > 0 ) + { + const uint dalpha = dcol->a; + const uint salpha = 0xff - dalpha; + dcol->r = ( dalpha * dcol->r + salpha * scol->r + 0x80 ) / 0xff; + dcol->g = ( dalpha * dcol->g + salpha * scol->g + 0x80 ) / 0xff; + dcol->b = ( dalpha * dcol->b + salpha * scol->b + 0x80 ) / 0xff; + dcol->a = ( 0xfe01 - ( 0xff - dcol->a ) * ( 0xff - scol->a )) / 0xff; + dcol++; + scol++; + } + DASSERT( (u8*)dcol <= dest->data + dest->data_size ); + DASSERT( (u8*)scol <= src->data + src->data_size ); + dcol += dest_skip; + scol += src_skip; + } + break; + + case PIM_FOREGROUND: + while ( height-- > 0 ) + { + uint n = width; + while ( n-- > 0 ) + { + const uint salpha = scol->a; + const uint dalpha = 0xff - salpha; + dcol->r = ( dalpha * dcol->r + salpha * scol->r + 0x80 ) / 0xff; + dcol->g = ( dalpha * dcol->g + salpha * scol->g + 0x80 ) / 0xff; + dcol->b = ( dalpha * dcol->b + salpha * scol->b + 0x80 ) / 0xff; + dcol->a = ( 0xfe01 - ( 0xff - dcol->a ) * ( 0xff - scol->a )) / 0xff; + dcol++; + scol++; + } + DASSERT( (u8*)dcol <= dest->data + dest->data_size ); + DASSERT( (u8*)scol <= src->data + src->data_size ); + dcol += dest_skip; + scol += src_skip; + } + break; + + case PIM_MIX: + while ( height-- > 0 ) + { + uint n = width; + while ( n-- > 0 ) + { + const uint alpha = dcol->a + scol->a; + if (alpha) + { + const uint alpha2 = alpha/2; + dcol->r = ( dcol->a * dcol->r + scol->a * scol->r + alpha2 ) / alpha; + dcol->g = ( dcol->a * dcol->g + scol->a * scol->g + alpha2 ) / alpha; + dcol->b = ( dcol->a * dcol->b + scol->a * scol->b + alpha2 ) / alpha; + dcol->a = ( 0xfe01 - ( 0xff - dcol->a ) * ( 0xff - scol->a )) / 0xff; + } + dcol++; + scol++; + } + DASSERT( (u8*)dcol <= dest->data + dest->data_size ); + DASSERT( (u8*)scol <= src->data + src->data_size ); + dcol += dest_skip; + scol += src_skip; + } + break; + + default: // PIM_COPY: + while ( height-- > 0 ) + { + uint n = width; + while ( n-- > 0 ) + *dcol++ = *scol++; + DASSERT( (u8*)dcol <= dest->data + dest->data_size ); + DASSERT( (u8*)scol <= src->data + src->data_size ); + dcol += dest_skip; + scol += src_skip; + } + break; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DrawPointIMG +( + Image_t * img, // image + int x, // x position (robust) + int y, // y position (robust) + Color_t col, // draw color + bool combine // true: combine 'col' with current one +) +{ + DASSERT(img); + if ( img->iform != IMG_X_RGB ) + ConvertIMG(img,false,0,IMG_X_RGB,PAL_AUTO); + + if ( x >= 0 && x < img->width && y >= 0 && y < img->height && col.a ) + { + Color_t *dest = (Color_t*)img->data + y * img->xwidth + x; + + if ( combine && col.a != 0xff ) + { + const uint da = ( 0xff - col.a ) * dest->a; + dest->r = ( da * dest->r + 0xff * col.a * col.r ) / 0xfe01; + dest->g = ( da * dest->g + 0xff * col.a * col.g ) / 0xfe01; + dest->b = ( da * dest->b + 0xff * col.a * col.b ) / 0xfe01; + dest->a = 0xff - (0xff-dest->a)*(0xff-col.a)/0xff; + } + else + *dest = col; + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DrawHLineIMG +( + Image_t * img, // image + int x1, // start point (robust) + int x2, // end point (not drawn, robust) + int y, // common y coordinate (robust) + Color_t col // draw color +) +{ + DASSERT(img); + if ( img->iform != IMG_X_RGB ) + ConvertIMG(img,false,0,IMG_X_RGB,PAL_AUTO); + + if ( x1 > x2 ) + { + const uint temp = x1; + x1 = x2 - 1; + x2 = temp + 1; + } + + if ( x1 < 0 ) + x1 = 0; + if ( x2 > img->width ) + x2 = img->width; + + if ( x1 < x2 && y >= 0 && y < img->height ) + { + Color_t *dest = (Color_t*)img->data + y * img->xwidth + x1; + uint n = x2 - x1; + while ( n-- > 0 ) + *dest++ = col; + DASSERT( (u8*)dest <= img->data + img->data_size ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DrawVLineIMG +( + Image_t * img, // image + int x, // common x coordinate (robust) + int y1, // start point (robust) + int y2, // end point (not drawn, robust) + Color_t col // draw color +) +{ + DASSERT(img); + if ( img->iform != IMG_X_RGB ) + ConvertIMG(img,false,0,IMG_X_RGB,PAL_AUTO); + + if ( y1 > y2 ) + { + const uint temp = y1; + y1 = y2 - 1; + y2 = temp + 1; + } + + if ( y1 < 0 ) + y1 = 0; + if ( y2 > img->height ) + y2 = img->height; + + if ( y1 < y2 && x >= 0 && x < img->width ) + { + Color_t *dest = (Color_t*)img->data + y1 * img->xwidth + x; + uint n = y2 - y1; + while ( n-- > 0 ) + { + *dest = col; + dest += img->xwidth; + } + DASSERT( (u8*)dest <= img->data + img->data_size ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DrawFrameIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col // draw color +) +{ + DASSERT(img); + DrawHLineIMG(img,x1,x2,y1,col); + DrawVLineIMG(img,x2,y1,y2,col); + DrawHLineIMG(img,x2,x1,y2,col); + DrawVLineIMG(img,x1,y2,y1,col); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError FillRectIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col, // draw color + bool combine // true: combine 'col' with current one +) +{ + DASSERT(img); + if ( img->iform != IMG_X_RGB ) + ConvertIMG(img,false,0,IMG_X_RGB,PAL_AUTO); + + if ( x1 > x2 ) + { + const uint temp = x1; + x1 = x2 - 1; + x2 = temp + 1; + } + + if ( x1 < 0 ) + x1 = 0; + if ( x2 > img->width ) + x2 = img->width; + + if ( y1 > y2 ) + { + const uint temp = y1; + y1 = y2 - 1; + y2 = temp + 1; + } + + if ( y1 < 0 ) + y1 = 0; + if ( y2 > img->height ) + y2 = img->height; + + if ( x1 < x2 && y1 < y2 ) + { + Color_t *dest = (Color_t*)img->data + y1 * img->xwidth + x1; + const uint skip = img->xwidth - (x2-x1); + uint yn = y2 - y1; + if ( combine && col.a != 0xff ) + { + const uint r = 0xff * col.a * col.r; + const uint g = 0xff * col.a * col.g; + const uint b = 0xff * col.a * col.b; + + while ( yn-- > 0 ) + { + uint xn = x2 - x1; + while ( xn-- > 0 ) + { + const uint da = ( 0xff - col.a ) * dest->a; + dest->r = ( da * dest->r + r ) / 0xfe01; + dest->g = ( da * dest->g + g ) / 0xfe01; + dest->b = ( da * dest->b + b ) / 0xfe01; + dest->a = 0xff - (0xff-dest->a)*(0xff-col.a)/0xff; + dest++; + } + DASSERT( (u8*)dest <= img->data + img->data_size ); + dest += skip; + } + } + else + { + while ( yn-- > 0 ) + { + uint xn = x2 - x1; + while ( xn-- > 0 ) + *dest++ = col; + DASSERT( (u8*)dest <= img->data + img->data_size ); + dest += skip; + } + } + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError FillHGradientIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col1, // draw color left + Color_t col2 // draw color right +) +{ + DASSERT(img); + if ( img->iform != IMG_X_RGB ) + ConvertIMG(img,false,0,IMG_X_RGB,PAL_AUTO); + + if ( x1 > x2 ) + { + const uint temp = x1; + x1 = x2 - 1; + x2 = temp + 1; + + const Color_t col = col1; + col1 = col2; + col2 = col; + } + + if ( x1 < 0 ) + x1 = 0; + if ( x2 > img->width ) + x2 = img->width; + + if ( y1 > y2 ) + { + const uint temp = y1; + y1 = y2 - 1; + y2 = temp + 1; + } + + if ( y1 < 0 ) + y1 = 0; + if ( y2 > img->height ) + y2 = img->height; + + if ( x1 < x2 && y1 < y2 ) + { + Color_t *dest1 = (Color_t*)img->data + y1 * img->xwidth + x1; + + const uint xn = x2 - x1, xn2 = xn/2; + uint xi; + for ( xi = 0; xi < xn; xi++, dest1++ ) + { + Color_t col; + col.r = ( xi * col2.r + (xn-xi) * col1.r + xn2 ) / xn; + col.g = ( xi * col2.g + (xn-xi) * col1.g + xn2 ) / xn; + col.b = ( xi * col2.b + (xn-xi) * col1.b + xn2 ) / xn; + col.a = ( xi * col2.a + (xn-xi) * col1.a + xn2 ) / xn; + + Color_t *dest2 = dest1; + uint yn = y2 - y1; + while ( yn-- > 0 ) + { + *dest2 = col; + dest2 += img->xwidth; + } + } + DASSERT( (u8*)dest1 <= img->data + img->data_size ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError FillVGradientIMG +( + Image_t * img, // image + int x1, // left coordinate (robust) + int y1, // top coordinate (robust) + int x2, // right coordinate (robust) + int y2, // bottom coordinate (robust) + Color_t col1, // draw color top + Color_t col2 // draw color bottom +) +{ + DASSERT(img); + if ( img->iform != IMG_X_RGB ) + ConvertIMG(img,false,0,IMG_X_RGB,PAL_AUTO); + + if ( x1 > x2 ) + { + const uint temp = x1; + x1 = x2 - 1; + x2 = temp + 1; + } + + if ( x1 < 0 ) + x1 = 0; + if ( x2 > img->width ) + x2 = img->width; + + if ( y1 > y2 ) + { + const uint temp = y1; + y1 = y2 - 1; + y2 = temp + 1; + + const Color_t col = col1; + col1 = col2; + col2 = col; + } + + if ( y1 < 0 ) + y1 = 0; + if ( y2 > img->height ) + y2 = img->height; + + if ( x1 < x2 && y1 < y2 ) + { + Color_t *dest = (Color_t*)img->data + y1 * img->xwidth + x1; + const uint skip = img->xwidth - (x2-x1); + + const uint yn = y2 - y1, yn2 = yn/2; + uint yi; + for ( yi = 0; yi < yn; yi++ ) + { + Color_t col; + col.r = ( yi * col2.r + (yn-yi) * col1.r + yn2 ) / yn; + col.g = ( yi * col2.g + (yn-yi) * col1.g + yn2 ) / yn; + col.b = ( yi * col2.b + (yn-yi) * col1.b + yn2 ) / yn; + col.a = ( yi * col2.a + (yn-yi) * col1.a + yn2 ) / yn; + + uint xn = x2 - x1; + while ( xn-- > 0 ) + *dest++ = col; + dest += skip; + } + DASSERT( (u8*)dest <= img->data + img->data_size ); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image resizing /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool fast_resize_enabled = false; + +/////////////////////////////////////////////////////////////////////////////// + +enumError ResizeIMG +( + Image_t * dest, // dest image (may same as src) + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source image (if NULL: use dest) + uint width, // new width of image + uint height // new height of image + // if one of 'width' or 'height' is NULL + // -> keep aspect ratio +) +{ + return fast_resize_enabled + ? FastResizeIMG(dest,init_dest,src,width,height) + : SmartResizeIMG(dest,init_dest,src,width,height); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError FastResizeIMG +( + Image_t * dest, // dest image (may same as src) + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source image (if NULL: use dest) + uint width, // new width of image + uint height // new height of image + // if one of 'width' or 'height' is NULL + // -> keep aspect ratio +) +{ + DASSERT(dest); + if (init_dest) + InitializeIMG(dest); + if (!src) + src = dest; + + + //--- analyze width and height + + if ( !src->width || !src->height ) + { + CopyIMG(dest,false,src,true); + return ERR_OK; + } + + if (!width) + { + if (!height) + { + CopyIMG(dest,false,src,true); + NormalizeFrameIMG(dest); + return ERR_OK; + } + width = ( src->width * height + src->height/2 ) / src->height; + } + else if (!height) + height = ( src->height * width + src->width/2 ) / src->width; + + if ( width == src->width && height == src->height ) + { + //--- no resize needed + + CopyIMG(dest,false,src,true); + NormalizeFrameIMG(dest); + return ERR_OK; + } + + if ( 2*width == src->width && 2*height == src->height ) + { + //--- use fast HalfIMG() + + if ( src != dest ) + CopyIMG(dest,false,src,true); + return HalfIMG(dest); + } + + + //--- convert to IMG_X_RGB + + PRINT("RESIZE: %u*%u -> %u*%u\n", + src->width, src->height, width, height ); + + if ( src->iform != IMG_X_RGB ) + { + noPRINT("FastResizeIMG: src= %s [%x], dest= %s [%x]\n", + PrintFormatIMG(src), src->iform, + PrintFormatIMG(dest), dest->iform ); + + if (IsGrayIMG(src)) + { + //--- convert temporary to IMG_X_RGB + + enumError err = ConvertIMG(dest,false,src,IMG_X_RGB,PAL_AUTO); + noPRINT("FastResizeIMG: gray= %s [%x]\n",PrintFormatIMG(dest),dest->iform); + if (!err) + { + err = FastResizeIMG(dest,false,dest,width,height); + if (!err) + err = ConvertToGRAY(dest,dest,PAL_AUTO); + } + return err; + } + + enumError err = ConvertIMG(dest,false,src,IMG_X_RGB,PAL_AUTO); + if (err) + return err; + src = dest; + noPRINT("FastResizeIMG: conv= %s [%x]\n", + PrintFormatIMG(dest), dest->iform ); + + if ( src->iform != IMG_X_RGB ) + return ERROR0(ERR_INTERNAL, + "Image format 'X-RGB' expected, but '%s' found\n", + PrintFormatIMG(src)); + } + + + //--- setup new image data + + DASSERT( src->iform == IMG_X_RGB ); + + const uint bytes_per_pixel = 4; + const uint xwidth = EXPAND8(width); + const uint xheight = EXPAND8(height); + const uint data_size = xwidth * xheight * bytes_per_pixel; + u8 *data = MALLOC(data_size); + memset(data,0xff,data_size); + PRINT(" - new image: %u*%u -> %u*%u -> %u bytes\n", + width, height, xwidth, xheight, data_size ); + + u8 * dest1 = data; + const u8 * src1 = src->data; + uint src_line_size = bytes_per_pixel * src->xwidth; + uint dest_line_size = bytes_per_pixel * xwidth; + + + //--- main loop + // naming scheme: abx: a=Height|Width, b=Src|Dest, x=1|2|N|MAX + + const uint wmax = width < src->width ? width : src->width; + const uint hmax = height < src->height ? height : src->height; + uint ht, hs2 = 0, hd2 = 0; + for ( ht = 1; ht <= hmax; ht++ ) + { + const uint hs1 = hs2; + hs2 = ( ht * src->height + hmax/2 ) / hmax; + const uint hsn = hs2 - hs1; + + const uint hd1 = hd2; + hd2 = ( ht * height + hmax/2 ) / hmax; + const uint hdn = hd2 - hd1; + + noPRINT(" LINE: %5u .. %5u -> %5u .. %5u [ %3u -> %3u ]\n", + hs1, hs2, hd1, hd2, hsn, hdn ); + DASSERT( hsn >= 1 && hdn >= 1 ); + DASSERT( hsn == 1 || hdn == 1 ); + + if ( hsn == 1 && width == src->width ) + { + //--- copy complete line without any calculations + memcpy(dest1,src1,width*bytes_per_pixel); + } + else + { + //--- mix each cell + + u8 * dest2 = dest1; + const u8 * src2 = src1; + + uint wd, ws2 = 0, wd2 = 0; + for ( wd = 1; wd <= wmax; wd++ ) + { + const uint ws1 = ws2; + ws2 = ( wd * src->width + wmax/2 ) / wmax; + const uint wsn = ws2 - ws1; + + const uint wd1 = wd2; + wd2 = ( wd * width + wmax/2 ) / wmax; + const uint wdn = wd2 - wd1; + + noPRINT("COPY: %4u..%4u*%4u..%4u -> %4u..%4u*%4u..%4u [ %2u*%2u -> %2u*%2u ]\n", + ws1, ws2, hs1, hs2, + wd1, wd2, hd1, hd2, + wsn, wdn, hsn, hdn ); + DASSERT( wsn >= 1 && wdn >= 1 ); + DASSERT( wsn == 1 || wdn == 1 ); + + DASSERT(bytes_per_pixel==4); + uint sum[4]; + memset(sum,0,sizeof(sum)); + + if ( hsn == 1 ) + { + uint nw = wsn; + while ( nw-- > 0 ) + { + sum[0] += *src2++; + sum[1] += *src2++; + sum[2] += *src2++; + sum[3] += *src2++; + } + } + else if ( hsn == 2 ) + { + uint nw = wsn; + while ( nw-- > 0 ) + { + sum[0] += (uint)*src2 + src2[src_line_size]; src2++; + sum[1] += (uint)*src2 + src2[src_line_size]; src2++; + sum[2] += (uint)*src2 + src2[src_line_size]; src2++; + sum[3] += (uint)*src2 + src2[src_line_size]; src2++; + } + } + else + { + uint nh = hsn; + while ( nh-- > 0 ) + { + const u8 * src3 = src2 + nh * src_line_size; + uint nw = wsn; + while ( nw-- > 0 ) + { + sum[0] += *src3++; + sum[1] += *src3++; + sum[2] += *src3++; + sum[3] += *src3++; + } + } + src2 += wsn * bytes_per_pixel; + } + + uint n = wdn, div = hsn * wsn; + while ( n-- > 0 ) + { + *dest2++ = sum[0] / div; + *dest2++ = sum[1] / div; + *dest2++ = sum[2] / div; + *dest2++ = sum[3] / div; + } + } + } + + //--- duplicate lines + + uint i; + for ( i = 1; i < hdn; i++ ) + { + memcpy(dest1+dest_line_size,dest1,dest_line_size); + dest1 += dest_line_size; + } + + //--- adjust first level pointers + + dest1 += dest_line_size; + src1 += src_line_size * hsn; + } + + + //--- assign data + + FreeIMG(dest,src); + + dest->data = data; + dest->data_alloced = true; + dest->width = width; + dest->height = height; + dest->xwidth = xwidth; + dest->xheight = xheight; + dest->data_size = dest->xwidth * dest->xheight * 4; + dest->iform = IMG_X_RGB; + NormalizeFrameIMG(dest); + + PRINT(" - new image: %u*%u -> %u*%u -> %u bytes\n", + dest->width, dest->height, dest->xwidth, dest->xheight, dest->data_size ); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SmartResizeIMG +( + Image_t * dest, // dest image (may same as src) + bool init_dest, // true: initialize 'dest' first + const Image_t * src, // source image (if NULL: use dest) + uint width, // new width of image + uint height // new height of image + // if one of 'width' or 'height' is NULL + // -> keep aspect ratio +) +{ + DASSERT(dest); + if (init_dest) + InitializeIMG(dest); + if (!src) + src = dest; + + + //--- analyze width and height + + if ( !src->width || !src->height ) + { + CopyIMG(dest,false,src,true); + return ERR_OK; + } + + if (!width) + { + if (!height) + { + CopyIMG(dest,false,src,true); + NormalizeFrameIMG(dest); + return ERR_OK; + } + width = ( src->width * height + src->height/2 ) / src->height; + } + else if (!height) + height = ( src->height * width + src->width/2 ) / src->width; + + if ( width == src->width && height == src->height ) + { + //--- no resize needed + + CopyIMG(dest,false,src,true); + NormalizeFrameIMG(dest); + return ERR_OK; + } + + if ( 2*width == src->width && 2*height == src->height ) + { + //--- use fast HalfIMG() + + if ( src != dest ) + CopyIMG(dest,false,src,true); + return HalfIMG(dest); + } + + + //--- convert to IMG_X_RGB + + PRINT("RESIZE: %u*%u -> %u*%u\n", + src->width, src->height, width, height ); + + if ( src->iform != IMG_X_RGB ) + { + noPRINT("SmartResizeIMG: src= %s [%x], dest= %s [%x]\n", + PrintFormatIMG(src), src->iform, + PrintFormatIMG(dest), dest->iform ); + + if (IsGrayIMG(src)) + { + //--- convert temporary to IMG_X_RGB + + enumError err = ConvertIMG(dest,false,src,IMG_X_RGB,PAL_AUTO); + noPRINT("SmartResizeIMG: gray= %s [%x]\n",PrintFormatIMG(dest),dest->iform); + if (!err) + { + err = SmartResizeIMG(dest,false,dest,width,height); + if (!err) + err = ConvertToGRAY(dest,dest,PAL_AUTO); + } + return err; + } + + enumError err = ConvertIMG(dest,false,src,IMG_X_RGB,PAL_AUTO); + if (err) + return err; + src = dest; + noPRINT("SmartResizeIMG: conv= %s [%x]\n", + PrintFormatIMG(dest), dest->iform ); + + if ( src->iform != IMG_X_RGB ) + return ERROR0(ERR_INTERNAL, + "Image format 'X-RGB' expected, but '%s' found\n", + PrintFormatIMG(src)); + } + + + //--- setup new image data + + DASSERT( src->iform == IMG_X_RGB ); + + const uint bytes_per_pixel = 4; + const uint xwidth = EXPAND8(width); + const uint xheight = EXPAND8(height); + const uint data_size = xwidth * xheight * bytes_per_pixel; + u8 *data = MALLOC(data_size); + memset(data,0xff,data_size); + PRINT(" - new image: %u*%u -> %u*%u -> %u bytes\n", + width, height, xwidth, xheight, data_size ); + + u8 * dest1 = data; + const uint dest_line_skip = bytes_per_pixel * ( xwidth - width ); + + //const u8 * src1 = src->data; + const uint src_line_size = bytes_per_pixel * src->xwidth; + u8 *temp_src = MALLOC(src_line_size); + //const uint dest_line_size = bytes_per_pixel * xwidth; + + ResizeHelper_t rh, rv; + InitializeResize(&rh,src->width,width); + InitializeResize(&rv,src->height,height); + + bool v_ok; + for ( v_ok = FirstResize(&rv); v_ok; v_ok = NextResize(&rv) ) + { + //--- proccess vertical points + + const u8 *src2 = src->data + src_line_size * rv.src_idx; + u8 *temp = temp_src; + uint n = src->width; + while ( n-- > 0 ) + { + uint sum0 = rv.half_factor; + uint sum1 = rv.half_factor; + uint sum2 = rv.half_factor; + uint sum3 = rv.half_factor; + + const u8 *src3 = src2; + const ResizeElement_t *re; + for ( re = rv.elem; re->n_elem; re++ ) + { + uint ne; + for ( ne = re->n_elem; ne > 0; ne-- ) + { + noPRINT("COPY %u, %u: %zu / %zu / %u\n", + rv.src_idx, ne, + ( src2 - src->data ) / src_line_size, + ( src3 - src->data ) / src_line_size, + src->height ); + DASSERT( src3 < src->data + src->data_size ); + sum0 += src3[0] * re->factor; + sum1 += src3[1] * re->factor; + sum2 += src3[2] * re->factor; + sum3 += src3[3] * re->factor; + src3 += src_line_size; + } + } + *temp++ = sum0 / rv.sum_factor; + *temp++ = sum1 / rv.sum_factor; + *temp++ = sum2 / rv.sum_factor; + *temp++ = sum3 / rv.sum_factor; + DASSERT( temp <= temp_src + src_line_size ); + src2 += bytes_per_pixel; + DASSERT( src2 <= src->data + src->data_size ); + } + DASSERT( temp == temp_src + bytes_per_pixel * src->width ); + + #if HAVE_PRINT0 + { + static bool done = false; + if (!done) + { + done = true; + HEXDUMP16(4,0,temp_src,temp-temp_src); + } + } + #endif + + bool h_ok; + for ( h_ok = FirstResize(&rh); h_ok; h_ok = NextResize(&rh) ) + { + uint sum0 = rh.half_factor; + uint sum1 = rh.half_factor; + uint sum2 = rh.half_factor; + uint sum3 = rh.half_factor; + + u8 *temp = temp_src + rh.src_idx * bytes_per_pixel; + + const ResizeElement_t *re; + for ( re = rh.elem; re->n_elem; re++ ) + { + uint ne; + for ( ne = re->n_elem; ne > 0; ne-- ) + { + sum0 += *temp++ * re->factor; + sum1 += *temp++ * re->factor; + sum2 += *temp++ * re->factor; + sum3 += *temp++ * re->factor; + } + } + + *dest1++ = sum0 / rh.sum_factor; + *dest1++ = sum1 / rh.sum_factor; + *dest1++ = sum2 / rh.sum_factor; + *dest1++ = sum3 / rh.sum_factor; + } + dest1 += dest_line_skip; + noPRINT("DEST1: %u/4 = %u, line %u.%u/%u\n", + (int)(dest1-data), + (int)(dest1-data)/bytes_per_pixel, + (int)(dest1-data)/(bytes_per_pixel*xwidth), + (int)(dest1-data)%(bytes_per_pixel*xwidth), height ) ; + } + DASSERT_MSG( dest1 == data + (bytes_per_pixel*xwidth) * height, + "%p - %p %zd, dest= %lu, col = %lu.%lu/%u line\n", + dest1, data + data_size, dest1 - data - data_size, + (dest1-data)/bytes_per_pixel, + (dest1-data)/(bytes_per_pixel*xwidth), + (dest1-data)%(bytes_per_pixel*xwidth), height ) ; + FREE(temp_src); + + + //--- assign data + + FreeIMG(dest,src); + + dest->data = data; + dest->data_alloced = true; + dest->width = width; + dest->height = height; + dest->xwidth = xwidth; + dest->xheight = xheight; + dest->data_size = dest->xwidth * dest->xheight * 4; + dest->iform = IMG_X_RGB; + NormalizeFrameIMG(dest); + + PRINT(" - new image: %u*%u -> %u*%u -> %u bytes\n", + dest->width, dest->height, dest->xwidth, dest->xheight, dest->data_size ); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError HalfIMG +( + Image_t * img // source and dest +) +{ + DASSERT(img); + Transform2XIMG(img); + enumError err = ExecTransformIMG(img); + const uint newwd = img->width/2; + const uint newht = img->height/2; + if ( err || !newwd || !newht ) + return err; + + NormalizeFrameIMG(img); + const uint newxwd = EXPAND8(newwd); + const uint newxht = EXPAND8(newht); + PRINT("HalfIMG() %u*%u -> %u*%u -> %u*%u\n", + img->width, img->height, newwd, newht, newxwd, newxht ); + + if ( img->iform == IMG_X_GRAY ) + { + const uint pixel_bytes = 2; + const uint src_add = pixel_bytes * img->xwidth; + const uint dest_add = pixel_bytes * newxwd; + const uint data_size = pixel_bytes * newxwd * newxht; + + u8 *data = MALLOC(data_size); + u8 *dest1 = data; + const u8 *src1 = img->data; + uint ih = newht; + while ( ih-- > 0 ) + { + u8 *dest2 = dest1; + const u8 *src2 = src1; + uint iw = newwd; + while ( iw-- > 0 ) + { + *dest2++ = ( *src2 + + src2[pixel_bytes] + + src2[src_add] + + src2[src_add+pixel_bytes] + + 2 ) / 4; + src2++; + *dest2++ = ( *src2 + + src2[pixel_bytes] + + src2[src_add] + + src2[src_add+pixel_bytes] + + 2 ) / 4; + src2 += pixel_bytes + 1; + DASSERT( dest2 <= data + data_size ); + DASSERT( src2 <= img->data + img->data_size ); + } + noPRINT("%3u: %u*%u %5zx..%5zx/%x -> %u*%u %5zx..%5zx/%x\n", + newht - ih - 1, + img->width, img->height, src1-img->data, src2-img->data, img->data_size, + newwd, newht, dest1-data, dest2-data, data_size ); + + dest1 += dest_add; + src1 += 2 * src_add; + DASSERT( dest1 <= data + data_size ); + DASSERT( src1 <= img->data + img->data_size ); + } + + img->width = newwd; + img->height= newht; + AssignDataGRAY(img,0,data); + return ERR_OK; + } + + if ( img->iform >= IMG_X_PAL__MIN && img->iform <= IMG_X_PAL__MAX ) + { + enumError err = ConvertToRGB(img,img,PAL_AUTO); + if (err) + return err; + } + + if ( img->iform == IMG_X_RGB ) + { + const uint pixel_bytes = 4; + const uint src_add = pixel_bytes * img->xwidth; + const uint dest_add = pixel_bytes * newxwd; + const uint data_size = pixel_bytes * newxwd * newxht; + + u8 *data = MALLOC(data_size); + u8 *dest1 = data; + const u8 *src1 = img->data; + uint ih = newht; + while ( ih-- > 0 ) + { + u8 *dest2 = dest1; + const u8 *src2 = src1; + uint iw = newwd; + while ( iw-- > 0 ) + { + *dest2++ = ( *src2 + + src2[pixel_bytes] + + src2[src_add] + + src2[src_add+pixel_bytes] + + 2 ) / 4; + src2++; + *dest2++ = ( *src2 + + src2[pixel_bytes] + + src2[src_add] + + src2[src_add+pixel_bytes] + + 2 ) / 4; + src2++; + *dest2++ = ( *src2 + + src2[pixel_bytes] + + src2[src_add] + + src2[src_add+pixel_bytes] + + 2 ) / 4; + src2++; + *dest2++ = ( *src2 + + src2[pixel_bytes] + + src2[src_add] + + src2[src_add+pixel_bytes] + + 2 ) / 4; + src2 += pixel_bytes + 1; + DASSERT( dest2 <= data + data_size ); + DASSERT( src2 <= img->data + img->data_size ); + } + noPRINT("%3u: %u*%u %5zx..%5zx/%x -> %u*%u %5zx..%5zx/%x\n", + newht - ih - 1, + img->width, img->height, src1-img->data, src2-img->data, img->data_size, + newwd, newht, dest1-data, dest2-data, data_size ); + + dest1 += dest_add; + src1 += 2 * src_add; + DASSERT( dest1 <= data + data_size ); + DASSERT( src1 <= img->data + img->data_size ); + } + + img->width = newwd; + img->height = newht; + AssignDataRGB(img,0,data); + } + else + return ERROR0(ERR_INTERNAL,0); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patching helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static PatchImage_t calc_copy_range +( + PatchRange1_t *dest, + PatchRange1_t *src1, + PatchRange1_t *src2, + PatchImage_t patch_mode, + PatchImage_t align_mode +) +{ + DASSERT(dest); + DASSERT(src1); + DASSERT(src2); + + switch ( patch_mode & PIM_M_SIZE ) + { + case PIM_GROW: + dest->size = src1->size > src2->size ? src1->size : src2->size; + break; + + case PIM_SHRINK: + dest->size = src1->size < src2->size ? src1->size : src2->size; + break; + + default: // PIM_LEAVE + dest->size = src1->size; + break; + }; + + switch ( align_mode & PIM_M_V ) + { + case PIM_TOP: + src1->dest = 0; + src1->src = 0; + src1->len = dest->size < src1->size ? dest->size : src1->size; + + src2->dest = 0; + src2->src = 0; + src2->len = dest->size < src2->size ? dest->size : src2->size; + break; + + case PIM_BOTTOM: + if ( dest->size >= src1->size ) + { + src1->dest = dest->size - src1->size; + src1->src = 0; + src1->len = src1->size; + } + else + { + src1->dest = 0; + src1->src = src1->size - dest->size; + src1->len = dest->size; + } + + if ( dest->size >= src2->size ) + { + src2->dest = dest->size - src2->size; + src2->src = 0; + src2->len = src2->size; + } + else + { + src2->dest = 0; + src2->src = src2->size - dest->size; + src2->len = dest->size; + } + break; + + case PIM_INS_TOP: + // override the local size and global copy mode! + patch_mode = patch_mode & ~PIM_M_COPY | PIM_COPY; + dest->size = src1->size + src2->size; + + src1->dest = src2->size; + src1->src = 0; + src1->len = src1->size; + + src2->dest = 0; + src2->src = 0; + src2->len = src2->size; + break; + + case PIM_INS_BOTTOM: + // override the local size and global copy mode! + patch_mode = patch_mode & ~PIM_M_COPY | PIM_COPY; + dest->size = src1->size + src2->size; + + src1->dest = 0; + src1->src = 0; + src1->len = src1->size; + + src2->dest = src1->size; + src2->src = 0; + src2->len = src2->size; + break; + + default: // PIM_VCENTER: + if ( dest->size >= src1->size ) + { + src1->dest = ( dest->size - src1->size ) / 2; + src1->src = 0; + src1->len = src1->size; + } + else + { + src1->dest = 0; + src1->src = ( src1->size - dest->size ) / 2; + src1->len = dest->size; + } + + if ( dest->size >= src2->size ) + { + src2->dest = ( dest->size - src2->size ) / 2; + src2->src = 0; + src2->len = src2->size; + } + else + { + src2->dest = 0; + src2->src = ( src2->size - dest->size ) / 2; + src2->len = dest->size; + } + break; + } + + return patch_mode; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchIMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError PatchIMG +( + Image_t * dest_img, // destination image, can be one of the sources + Image_t * src_img1, // base source + Image_t * src_img2, // patch source + PatchImage_t patch_mode // patch mode +) +{ + DASSERT(dest_img); + DASSERT(src_img1); + DASSERT(src_img2); + + if ( src_img1->iform != IMG_X_RGB ) + ConvertIMG(src_img1,false,0,IMG_X_RGB,PAL_AUTO); + if ( src_img2->iform != IMG_X_RGB ) + ConvertIMG(src_img2,false,0,IMG_X_RGB,PAL_AUTO); + + + //--- calculate copy range + + PatchRange2_t dest, src1, src2; + src1.x.size = src_img1->width; + src1.y.size = src_img1->height; + src2.x.size = src_img2->width; + src2.y.size = src_img2->height; + + patch_mode = calc_copy_range( &dest.x, &src1.x, &src2.x, + patch_mode, patch_mode >> PIM_SHIFT_V_H ); + patch_mode = calc_copy_range( &dest.y, &src1.y, &src2.y, + patch_mode, patch_mode ); + + noPRINT(" - COPY-X: %3d..%-3d -> %3d..%-3d | %3d..%-3d -> %3d..%-3d | %3d %3d -> %3d\n", + src1.x.src, src1.x.src+src1.x.len, src1.x.dest, src1.x.dest+src1.x.len, + src2.x.src, src2.x.src+src2.x.len, src2.x.dest, src2.x.dest+src2.x.len, + src1.x.size, src2.x.size, dest.x.size ); + noPRINT(" - COPY-Y: %3d..%-3d -> %3d..%-3d | %3d..%-3d -> %3d..%-3d | %3d %3d -> %3d\n", + src1.y.src, src1.y.src+src1.y.len, src1.y.dest, src1.y.dest+src1.y.len, + src2.y.src, src2.y.src+src2.y.len, src2.y.dest, src2.y.dest+src2.y.len, + src1.y.size, src2.y.size, dest.y.size ); + + + //--- create the new image + + if ( dest_img == src_img1 + && dest.x.size == src1.x.size + && dest.y.size == src1.y.size ) + { + //--- one direct operation + + PRINT("DIRECT IMAGE OPERATION\n"); + CopyRectIMG( dest_img, src2.x.dest, src2.y.dest, + src_img2, src2.x.src, src2.y.src, + src2.x.len, src2.y.len, + patch_mode ); + } + else + { + //--- we need a temporary image + + Color_t transparent; + SETCOLOR(transparent,0xff,0xff,0xff,0x00); + Image_t temp_img; + CreateIMG(&temp_img,true,dest.x.size,dest.y.size,transparent); + + CopyRectIMG( &temp_img, src1.x.dest, src1.y.dest, + src_img1, src1.x.src, src1.y.src, + src1.x.len, src1.y.len, + PIM_COPY ); + + CopyRectIMG( &temp_img, src2.x.dest, src2.y.dest, + src_img2, src2.x.src, src2.y.src, + src2.x.len, src2.y.len, + patch_mode ); + + MoveDataIMG(dest_img,&temp_img); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool pimg_valid = false; +static uint n_pimg = 0; +static Image_t *pimg = 0; + +/////////////////////////////////////////////////////////////////////////////// + +bool SetupPatchListIMG() +{ + static bool active = false; + if ( active || !patch_image_list.used ) + return false; + + if (pimg_valid) + return n_pimg > 0; + pimg_valid = active = true; + + TRACE("-\n"); + TRACE_SIZEOF(PatchRange1_t); + TRACE_SIZEOF(PatchRange2_t); + TRACE("-\n"); + + DASSERT(!pimg); + pimg = CALLOC(patch_image_list.used,sizeof(*pimg)); + n_pimg = 0; + + uint ip; + for ( ip = 0; ip < patch_image_list.used; ip++ ) + { + const FormatFieldItem_t * ffi = patch_image_list.list + ip; + Image_t *img = pimg + n_pimg; + if (LoadIMG(img,true,ffi->key,0,false,true,false)) + ResetIMG(img); + else + { + n_pimg++; + img->patch_mode = ffi->patch_mode; + ConvertIMG(img,false,0,IMG_X_RGB,PAL_AUTO); + PRINT("PATCH-IMG LOADED: %04x = %s\n",img->patch_mode,img->path); + } + } + + active = false; + return n_pimg > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPatchListIMG() +{ + uint ip; + for ( ip = 0; ip < n_pimg; ip++ ) + ResetIMG( pimg + ip ); + FREE(pimg); + + pimg_valid = false; + n_pimg = 0; + pimg = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PatchListIMG +( + Image_t * img // image to transform +) +{ + DASSERT(img); + if ( img->patch_done || !SetupPatchListIMG() ) + return ERR_OK; + + FreeMipmapsIMG(img); + Transform2XIMG(img); + enumError err = ExecTransformIMG(img); + if ( err || img->iform != IMG_X_RGB ) + return err; + + PRINT("PATCH %s\n",img->path); + + uint ip; + for ( ip = 0; ip < n_pimg && err == ERR_OK; ip++ ) + { + Image_t *pat = pimg + ip; + if ( verbose > 1 ) + fprintf(stdlog," - PATCH %04x = %s\n",pat->patch_mode,pat->path); + err = PatchIMG(img,img,pat,pat->patch_mode); + } + + img->patch_done = true; + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image lists /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintImageHead +( + int line_indent, // indention of complete line + uint long_count // set the verbosity +) +{ + if ( long_count > 1 ) + printf( "\n" + "%*sfile image mip width image attributes(Gray,Alpha,Palette)\n" + "%*stype type map height size ^ block file path\n" + "%*s%.79s\n", + line_indent,"", line_indent,"", line_indent,"", Minus300 ); + else if ( long_count ) + printf( "\n" + "%*sfile image mip width image\n" + "%*stype type map height size file path\n" + "%*s%.79s\n", + line_indent,"", line_indent,"", line_indent,"", Minus300 ); + else + printf( "\n" + "%*sfile image mip\n" + "%*stype type map file path\n" + "%*s%.79s\n", + line_indent,"", line_indent,"", line_indent,"", Minus300 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp SizeColor ( u32 size ) +{ + if (!size) + return colset->warn; + + u32 m = 1; + while ( !( m & size ) ) + m <<= 1; + + return m != size + ? colset->warn + : m > 1024 + ? colset->hint + : ""; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintImage +( + const void * data, // valid data pointer + // if NULL: force data line + uint data_size, // size of 'data' + ccp fname, // file name for listing + int line_indent, // indention of complete line + int fname_indent, // indention of 'fname' + uint long_count, // set the verbosity + bool ignore // true: ignore non images +) +{ + DASSERT(fname); + + Image_t img; + InitializeIMG(&img); + const ImageGeometry_t *geo = 0; + + if ( data && data_size ) + { + ScanDataIMG(&img,true,data,data_size); + geo = GetImageGeometry(img.iform); + + if ( img.info_fform == FF_PNG && long_count ) + LoadPNG(&img,false,0,fname,0); + } + else + ignore = false; + + if (geo) + { + printf("%-6s %02x %-6s", + GetNameFF(0,img.info_fform), + img.iform, GetImageFormatName(img.iform,"?") ); + + if ( img.info_n_image < 1 || img.info_n_image == 1 && ignore ) + fputs(" -",stdout); + else + printf(" %2u",img.info_n_image-1); + + if (long_count) + { + ccp wcol, hcol; + if (colorize_stdout) + { + wcol = SizeColor(img.width); + hcol = SizeColor(img.height); + } + else + wcol = hcol = ""; + + printf(" %s%4u%s*%s%-4u%s %7u", + wcol, img.width, *wcol ? TermTextModeReset : "", + hcol, img.height, *hcol ? TermTextModeReset : "", + img.xwidth * img.xheight * geo->bits_per_pixel / 8 ); + } + + if (long_count>1) + printf(" %c%c%c %u*%u", + geo->is_gray ? 'G' :'-', + geo->has_alpha ? 'A' :'-', + geo->max_palette ? 'P' :'-', + geo->block_width, geo->block_height ); + + printf(" %*s%s\n",fname_indent,"",fname); + } + else if ( !ignore || img.width && img.height ) + { + printf("%-6s - - -", GetNameFF(0,img.info_fform) ); + if (long_count) + { + if ( img.width && img.height ) + printf(" %4u*%-4u -",img.width,img.height); + else + fputs(" - -",stdout); + } + if (long_count>1) + fputs(" --- - ",stdout); + printf(" %*s%s\n", fname_indent,"", fname ); + } + + ResetIMG(&img); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Median Cut: data structures /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef CHAN +#define CHAN 4 + +#if 0 + #define mcPRINT PRINT +#else + #define mcPRINT noPRINT +#endif + +//----------------------------------------------------------------------------- + +typedef struct mc_elem_t +{ + u8 val[CHAN]; // channel tuple + u32 index; // index of element + +} mc_elem_t; + +//----------------------------------------------------------------------------- + +typedef struct mc_block_t +{ + u8 min[CHAN]; // minimum value of each channel + u8 max[CHAN]; // maximum value of each channel + uint max_len; // maximum length of all channels + + mc_elem_t * elem_beg; // pointer to first element + mc_elem_t * elem_end; // pointer to end of element list + + struct mc_block_t * next; // pointer to next block + +} mc_block_t; + +//----------------------------------------------------------------------------- + +typedef struct mc_t +{ + uint n_elem; // number of element + mc_elem_t * elem; // list with all elements, alloced + + uint n_block; // number of used blocks + uint max_block; // max number of blocks + mc_block_t * block_head; // head of sorted list of blocks + mc_block_t * block_data; // alloced blocked data + mc_block_t last_block; // special last block + +} mc_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Median Cut: Debugging /////////////// +/////////////////////////////////////////////////////////////////////////////// +#if HAVE_PRINT +/////////////////////////////////////////////////////////////////////////////// + +static void DumpBlock ( const mc_t *mc, const mc_block_t *blk, ccp title ) +{ + DASSERT(mc); + DASSERT(blk); + DASSERT(CHAN==4); + + mcPRINT("BLOCK %u/%u/%u [%s]\n", + (int)( blk - mc->block_data ), mc->n_block, mc->max_block, + title ? title : "" ); + + mcPRINT(" ** range: %02x..%02x %02x..%02x %02x..%02x %02x..%02x" + " ** len: %3u=0x%03x ** elem: %u..%u %3u/%u **\n", + blk->min[0], blk->max[0], + blk->min[1], blk->max[1], + blk->min[2], blk->max[2], + blk->min[3], blk->max[3], + blk->max_len, blk->max_len, + (int)( blk->elem_beg - mc->elem ), + (int)( blk->elem_end - mc->elem ), + (int)( blk->elem_end - blk->elem_beg ), + mc->n_elem ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void DumpBlockList ( const mc_t *mc, ccp title ) +{ + DASSERT(mc); + + mcPRINT("BLOCK-LIST %u/%u [%s]\n", + mc->n_block, mc->max_block, title ? title : "" ); + + mc_block_t * blk = mc->block_head; + uint cnt; + for ( cnt = 0; blk; cnt++, blk = blk->next ) + { +//DEL char buf[20]; +//DEL ccp info = "LAST"; +//DEL if ( blk != &mc->last_block ) +//DEL { +//DEL snprintf(buf,sizeof(buf),"%4u",(int)(blk - mc->block_data)); +//DEL info = buf; +//DEL } + + mcPRINT("%4u %s: len: %3u=0x%03x ** elem: %u..%u %3u/%u\n", + cnt, info, blk->max_len, blk->max_len, + (int)( blk->elem_beg - mc->elem ), + (int)( blk->elem_end - mc->elem ), + (int)( blk->elem_end - blk->elem_beg ), + mc->n_elem ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#define DUMPBLOCK(m,b,t) DumpBlock(m,b,t) +#define DUMPBLOCKLIST(m,t) DumpBlockList(m,t) + +#else // !HAVE_PRINT + #define DUMPBLOCK(m,b,t) + #define DUMPBLOCKLIST(m,t) +#endif + +#define noDUMPBLOCK(m,b,t) +#define noDUMPBLOCKLIST(m,t) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Median Cut: Helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint CalcMinMax ( mc_block_t *blk ) +{ + DASSERT(blk); + memset(blk->min,~0,sizeof(blk->min)); + memset(blk->max, 0,sizeof(blk->max)); + + const mc_elem_t *elem; + for ( elem = blk->elem_beg; elem < blk->elem_end; elem++ ) + { + uint c; + for ( c = 0; c < CHAN; c++ ) + { + const u8 val = elem->val[c]; + noPRINT("C: %02x -> %02x..%02x\n", val, blk->min[c], blk->max[c] ); + if ( blk->min[c] > val ) + blk->min[c] = val; + if ( blk->max[c] < val ) + blk->max[c] = val; + } + } + + uint max_len = 0, c; + for ( c = 0; c < CHAN; c++ ) + { + const uint ml = blk->max[c] - blk->min[c]; + if ( max_len < ml ) + max_len = ml; + } + + return blk->max_len = max_len; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Median Cut: Interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint MedianCut +( + const u32 * data, // input: 4-tuple aka RGBA + // data is read at the very beginning + // and may overlay with 'index' and/or 'pal' + u16 * index, // not NULL: store index into palette, + // can be the space as 'data' + + // The follwing 3 parameters are used for 'data' and 'index'. + // They allow to skip unused pixel at the end or each row. + uint width, // used pixels per row + uint xwidth, // >width: pixels per row + uint height, // number or rows + + // pallette data + u32 * pal, // not NULL: store calculated palette + uint pal_elem // number of wanted palette entries + + // return value = number of calculated palette values +) +{ + DASSERT(data); + DASSERT(width); + DASSERT(height); + DASSERT(pal); + DASSERT(pal_elem); + + PRINT("MedianCut(), %d,%dx%d, pal=%d, n_pal=%u\n", + width, xwidth, height, pal!=0, pal_elem ); + + #if HAVE_PRINT || DEBUG + u64 usec = GetTimerUSec(); + #endif + + static bool done = false; + if (!done) + { + done = true; + TRACE("- median cut\n"); + TRACE_SIZEOF(mc_t); + TRACE_SIZEOF(mc_block_t); + TRACE_SIZEOF(mc_elem_t); + TRACE("-\n"); + } + + + //--- setup data structure + + const uint n_elem = width * height; + if ( xwidth <= width ) + { + // normalization and a little optimization + width = xwidth = n_elem; + height = 1; + } + + if ( pal_elem > 0x10000 ) + pal_elem = 0x10000; + + mc_t mc; + memset(&mc,0,sizeof(mc)); + mc.n_elem = n_elem; + mc.elem = CALLOC(sizeof(*mc.elem),n_elem); + mc.n_block = 1; + mc.max_block = pal_elem; + mc.block_data = CALLOC(sizeof(*mc.block_data),pal_elem); + mc.block_head = mc.block_data; + mc.last_block.elem_beg = mc.elem; + mc.last_block.elem_end = mc.elem; + + //--- setup first block + + { + mc_block_t * block = mc.block_head; + block->next = &mc.last_block; + block->elem_beg = mc.elem; + block->elem_end = mc.elem + n_elem; + + const uint delta = xwidth - width; + mc_elem_t * elem = mc.elem; + uint h = height, i = 0; + while ( h-- > 0 ) + { + uint w = width; + while ( w-- > 0 ) + { + elem->index = i++; + memcpy(elem->val,data,sizeof(elem->val)); + elem++; + data++; + } + data += delta; + i += delta; + } + CalcMinMax(block); + DUMPBLOCK(&mc,block,"startup"); + } + + + //--- median cut + + while ( mc.n_block < mc.max_block ) + { + //--- pick first block + + mc_block_t * b1 = mc.block_head; + DASSERT( b1 ); + DASSERT( b1->next ); + DASSERT( b1->max_len >= b1->next->max_len ); + + if (!b1->max_len) + break; + + mc.block_head = b1->next; + + + //--- find channel with max length + + uint c; + for ( c = 0; b1->max[c] - b1->min[c] != b1->max_len; c++ ) + ; + DASSERT( c < CHAN ); + + + //--- split block + + DASSERT( mc.n_block < mc.max_block ); + mc_block_t *b2 = mc.block_data + mc.n_block++; + memcpy(b2,b1,sizeof(*b2)); + + const u8 split_val = ( b1->min[c] + b1->max[c] + 1 ) / 2; + mcPRINT("\n----- SPLIT b=%zu,%zu, c#%u at %02x, len=%02x -----\n", + b1 - mc.block_data, b2 - mc.block_data, + c, split_val, b1->max_len ); + //b1->max[c] = b1->min[c]; + //b2->min[c] = b2->max[c]; + + mc_elem_t * e1 = b1->elem_beg; + mc_elem_t * e2 = b1->elem_end - 1; + for(;;) + { + //--- find elements to swap + + while ( e1 < e2 && e1->val[c] < split_val ) + e1++; + + while ( e1 < e2 && e2->val[c] >= split_val ) + e2--; + + if ( e1 >= e2 ) + break; + + //--- swap elements + + noPRINT("SWAP: %zu,%zu : #%u %02x >= %02x > %02x\n", + e1-mc.elem,e2-mc.elem, + c, e1->val[c], split_val, e2->val[c] ); + mc_elem_t temp; + memcpy(&temp,e1,sizeof(temp)); + memcpy(e1,e2,sizeof(temp)); + memcpy(e2,&temp,sizeof(temp)); + } + mcPRINT(" %zu,%zu,%zu,%zu : #%u %02x < %02x <= %02x\n", + b1->elem_beg - mc.elem, e1 - mc.elem, + e2 - mc.elem, b1->elem_end - mc.elem, + c, e1[-1].val[c], split_val, e1[0].val[c] ); + DASSERT( e1 > b1->elem_beg ); + DASSERT( e1 < b1->elem_end ); + DASSERT ( e1[-1].val[c] < split_val ); + DASSERT ( e1[ 0].val[c] >= split_val ); + + b1->elem_end = b2->elem_beg = e1; + CalcMinMax(b1); + CalcMinMax(b2); + + DUMPBLOCK(&mc,b1,"split 1"); + DUMPBLOCK(&mc,b2,"split 2"); + + + //--- insert both blocks into list + + noDUMPBLOCKLIST(&mc,"0"); + + if ( b1->max_len < b2->max_len ) + { + mc_block_t *temp = b1; + b1 = b2; + b2 = temp; + } + + mc_block_t **ptr = &mc.block_head; + while ( (*ptr)->max_len > b1->max_len ) + ptr = &(*ptr)->next; + b1->next = *ptr; + *ptr = b1; + noDUMPBLOCKLIST(&mc,"b1"); + + while ( (*ptr)->max_len > b2->max_len ) + ptr = &(*ptr)->next; + b2->next = *ptr; + *ptr = b2; + noDUMPBLOCKLIST(&mc,"b2"); + } + DUMPBLOCKLIST(&mc,"END"); + + + //--- fill index table + + if (index) + { + const uint max_index = xwidth * height; + memset(index,0,max_index*sizeof(*index)); + uint blk_idx = 0; + const mc_block_t * blk; + for ( blk = mc.block_head; blk != &mc.last_block; blk = blk->next, blk_idx++ ) + { + const mc_elem_t * elem; + for ( elem = blk->elem_beg; elem < blk->elem_end; elem++ ) + { + DASSERT( elem->index < max_index ); + index[elem->index] = blk_idx; + } + } + } + + + //--- fill palette table + + if (pal) + { + u8 *dest = (u8*)pal; + const mc_block_t * blk; + for ( blk = mc.block_head; blk != &mc.last_block; blk = blk->next ) + { + uint val[CHAN]; + memset(val,0,sizeof(val)); + + const mc_elem_t * elem; + for ( elem = blk->elem_beg; elem < blk->elem_end; elem++ ) + { + uint c; + for ( c = 0; c < CHAN; c++ ) + val[c] += elem->val[c]; + } + + const uint n = blk->elem_end - blk->elem_beg; + DASSERT(n); + uint c; + for ( c = 0; c < CHAN; c++ ) + *dest++ = ( val[c] + n/2 ) / n; + } + } + + + //--- clean & return + + FREE(mc.elem); + FREE(mc.block_data); + + + #if HAVE_PRINT + usec = GetTimerUSec() - usec; + PRINT("MEDIAN-CUT: elem=%u, pal=%u/%u, %llu µsec\n", + n_elem, mc.n_block, pal_elem, usec ); + #elif DEBUG + usec = GetTimerUSec() - usec; + TRACE("MEDIAN-CUT: elem=%u, pal=%u/%u, %llu µsec\n", + n_elem, mc.n_block, pal_elem, usec ); + #else + PRINT("MEDIAN-CUT: elem=%u, pal=%u/%u\n", n_elem, mc.n_block, pal_elem ); + #endif + + return mc.n_block; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-itemslot.c b/project/src/lib-itemslot.c new file mode 100755 index 0000000..20d0060 --- /dev/null +++ b/project/src/lib-itemslot.c @@ -0,0 +1,790 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include + +#include "dclib-xdump.h" +#include "lib-szs.h" +#include "lib-common.h" +#include "lib-numeric.h" +#include "lib-bzip2.h" + +#include "itemslot.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + RACE_PARAM = 0, + BATTLE_PARAM = 1, +}; + +static const KeywordTab_t itemslot_table_list[] = +{ + { 0x001, "GRAND-PRIX-PLAYER", 0, RACE_PARAM }, + { 0x0e7, "GRAND-PRIX-ENEMY", 0, RACE_PARAM }, + { 0x1cd, "VERSUS-PLAYER", 0, RACE_PARAM }, + { 0x2b3, "VERSUS-ENEMY", 0, RACE_PARAM }, + { 0x399, "VERSUS-ONLINE", 0, RACE_PARAM }, + { 0x47f, "SPECIAL", 0, RACE_PARAM }, + + // typo "BALLON" will be [[obsolete]], "BALLOON" since 2020-01 (3x) + { 0x5b1, "BALLOON-PLAYER", "BALLON-PLAYER", BATTLE_PARAM }, + { 0x5ec, "BALLOON-ENEMY", "BALLON-ENEMY", BATTLE_PARAM }, + { 0x627, "COIN-PLAYER", 0, BATTLE_PARAM }, + { 0x662, "COIN-ENEMY", 0, BATTLE_PARAM }, + { 0x69d, "BALLOON-ONLINE", "BALLON-ONLINE", BATTLE_PARAM }, + { 0x6d8, "COIN-ONLINE", 0, BATTLE_PARAM }, + + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +static ccp item_info[] = +{ + "Green Shell", + "Red Shell", + "Banana", + "Fake Item Box", + "Mushroom", + "Triple Mushroom", + "Bob-omb", + "Blue Shell", + "Lightning", + "Star", + "Golden Mushroom", + "Mega Mushroom", + "Blooper", + "POW Block", + "Thundercloud", + "Bullet Bill", + "Triple Green Shells", + "Triple Red Shells", + "Triple Bananas", +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ITEMSLOT setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeITEMSLOT ( itemslot_t * itemslot, file_format_t fform ) +{ + DASSERT(itemslot); + memset(itemslot,0,sizeof(*itemslot)); + itemslot->fname = EmptyString; + itemslot->fform_source = fform; + itemslot->add_battle = true; + SetupBZ2MgrITEMSLOT(itemslot,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetITEMSLOT ( itemslot_t * itemslot ) +{ + DASSERT(itemslot); + const typeof(itemslot->fform_source) fform = itemslot->fform_source; + FreeString(itemslot->fname); + InitializeITEMSLOT(itemslot,fform); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GetOriginalITEMSLOT ( itemslot_bin_t *dest ) +{ + DASSERT(dest); + BZ2Manager_t *bz = GetCommonBZ2Manager(FF_ITEMSLT); + DASSERT(bz); + DASSERT( bz->size == sizeof(*dest) ); + if ( bz && bz->size == sizeof(*dest) ) + { + memcpy(dest,bz->data,sizeof(*dest)); + return true; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupBZ2MgrITEMSLOT ( itemslot_t * itemslot, bool reload_bin ) +{ + DASSERT(itemslot); + if ( reload_bin || !itemslot->data.n_table ) + GetOriginalITEMSLOT(&itemslot->data); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawITEMSLOT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(itemslot); + if (init_itemslot) + InitializeITEMSLOT(itemslot,FF_ITEMSLT); + + SetupBZ2MgrITEMSLOT(itemslot,true); + if ( !data || data_size != ITEMSLT_SIZE6 && data_size != ITEMSLT_SIZE12 ) + return ERROR0(ERR_INVALID_DATA, + "Invalid file size (%u but not %zu bytes: %s\n", + data_size, sizeof(itemslot->data), itemslot->fname ); + + memcpy(&itemslot->data,data,data_size); + itemslot->add_battle = data_size == ITEMSLT_SIZE12; + + + //--- fix table headers + + BZ2Manager_t *bz = GetCommonBZ2Manager(FF_ITEMSLT); + const KeywordTab_t *par; + for ( par = itemslot_table_list; par->name1; par++ ) + { + const uint offset = par->id; + u8 *val = (u8*)&itemslot->data + offset; + const u8 *orig = bz->data + offset; + *val++ = *orig++; + *val++ = *orig++; + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextITEMSLOT() helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + ITEMSLOT_IGNORE, + ITEMSLOT_SETUP, + ITEMSLOT_PARAMETERS, +}; + +//----------------------------------------------------------------------------- + +static const KeywordTab_t itemslot_section_name[] = +{ + { ITEMSLOT_IGNORE, "END", 0, 0 }, + { ITEMSLOT_SETUP, "SETUP", 0, 0 }, + { ITEMSLOT_PARAMETERS, "PARAMETERS", 0, 0 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextSETUP +( + itemslot_t * itemslot, // ITEMSLOT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(itemslot); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + ScanU32SI(si,(u32*)&itemslot->revision,1,0); + si->init_revision = si->cur_file->revision = itemslot->revision; + DefineIntVar(&si->gvar,"REVISION$SETUP",itemslot->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",itemslot->revision); + } + else + GotoEolSI(si); + CheckEolSI(si); + } + + CheckLevelSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextPARAMETERS +( + itemslot_t * itemslot, // ITEMSLOT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(itemslot); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + si->cur_file->ptr++; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + PRINT0("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + u32 rev; + ScanU32SI(si,&rev,1,0); + si->cur_file->revision = rev; + DefineIntVar(&si->gvar,"REVISION$ACTIVE",rev); + } + else if (!strcmp(name,"ADD-BATTLE")) + { + int val; + const enumError err = ScanAssignIntSI(si,&val); + if (!err) + itemslot->add_battle = val > 0 ; + } + else if (!strcmp(name,"USE-SLT")) + { + int val; + const enumError err = ScanAssignIntSI(si,&val); + if (!err) + itemslot->use_slt = val > 0 ; + } + CheckEolSI(si); + } + + CheckLevelSI(si); + return ERR_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextTABLE +( + itemslot_t * itemslot, // ITEMSLOT data structure + ScanInfo_t * si, // valid data + const KeywordTab_t * par // valid section pointer +) +{ + DASSERT(itemslot); + DASSERT(si); + DASSERT(par); + + const uint offset = par->id; + u8 *val = (u8*)&itemslot->data + offset; + const uint n_col = *val++; + const uint n_row = *val++; + + PRINT0(">> SCAN offset 0x%03x, size %2u*%u %s\n", + offset, n_col, n_row, par->name1 ); + si->point_is_null++; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,0); + continue; + } + + ScanFile_t *sf = si->cur_file; + + long item; + enumError err = ScanUValueSI(si,&item,0); + if (err) + break; + if ( item >= n_row ) + { + ccp eol = FindNextLineFeedSI(si,true); + if ( si->no_warn <= 0 ) + ERROR0(ERR_WARNING, + "Item ID is %lu but must be between 0 and %u: [%s @%u]: %.*s\n", + item, n_row-1, sf->name, sf->line, (int)(eol-sf->ptr), sf->ptr ); + sf->ptr = eol; + continue; + } + + u8 *dest = val + item * n_col;; + uint idx; + for ( idx = 0; idx < n_col; idx++ ) + { + ch = NextCharSI(si,false); + if (!ch) + break; + + long val; + err = ScanUValueSI(si,&val,0); + if (err) + break; + *dest++ = val; + } + CheckEolSI(si); + } + + si->point_is_null--; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextITEMSLOT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // file format +) +{ + DASSERT(itemslot); + PRINT("ScanTextITEMSLOT(init=%d)\n",init_itemslot); + + if (init_itemslot) + InitializeITEMSLOT(itemslot,FF_ITEMSLT_TXT); + else + { + itemslot->fform_source = FF_ITEMSLT_TXT; + SetupBZ2MgrITEMSLOT(itemslot,true); + } + + ScanInfo_t si; + InitializeSI(&si,data,data_size,itemslot->fname,itemslot->revision); + si.predef = SetupVarsCOMMON(); + + enumError max_err = ERR_OK; + itemslot->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN ITEMSLOT SECTIONS, PASS%u ...\n",itemslot->is_pass2+1); + + max_err = ERR_OK; + si.no_warn = !itemslot->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", itemslot->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,itemslot->revision); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + noPRINT("--> pass=%u: #%04u: %s\n",itemslot->is_pass2+1,si.cur_file->line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,itemslot_section_name); + if ( !cmd || abbrev_count ) + { + cmd = ScanKeyword(&abbrev_count,name,itemslot_table_list); + if ( cmd && !abbrev_count ) + { + while ( cmd > itemslot_table_list && cmd[-1].id == cmd->id ) + cmd--; + NextLineSI(&si,false,false); + enumError err = ScanTextTABLE(itemslot,&si,cmd); + if ( max_err < err ) + max_err = err; + } + continue; + } + + NextLineSI(&si,false,false); + noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case ITEMSLOT_SETUP: + err = ScanTextSETUP(itemslot,&si); + break; + + case ITEMSLOT_PARAMETERS: + err = ScanTextPARAMETERS(itemslot,&si); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (itemslot->is_pass2) + break; + + itemslot->is_pass2 = true; + + RestartSI(&si); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextITEMSLOT) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ITEMSLOT: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + const void * data, // data to scan + uint data_size, // size of 'data' + ccp fname // not NULL: Analyse file extention for '.slt' +) +{ + DASSERT(itemslot); + + enumError err; + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch (fform) + { + case FF_ITEMSLT: + err = ScanRawITEMSLOT(itemslot,init_itemslot,data,data_size); + if (fname) + { + const uint fnlen = strlen(fname); + itemslot->use_slt = fnlen > 4 && !strcasecmp(fname+fnlen-4,".slt"); + PRINT("USE_SLT=%d, ADD_BATTLE=%d\n",itemslot->use_slt,itemslot->add_battle); + } + break; + + case FF_ITEMSLT_TXT: + err = ScanTextITEMSLOT(itemslot,init_itemslot,data,data_size,fform); + break; + + default: + if (init_itemslot) + InitializeITEMSLOT(itemslot,0); + return ERROR0(ERR_INVALID_DATA, + "No ITEMSLOT file: %s\n", itemslot->fname ? itemslot->fname : "?"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadITEMSLOT +( + itemslot_t * itemslot, // ITEMSLOT data structure + bool init_itemslot, // true: initialize 'itemslot' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(itemslot); + DASSERT(fname); + if (init_itemslot) + InitializeITEMSLOT(itemslot,0); + else + ResetITEMSLOT(itemslot); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + itemslot->fname = raw.fname; + raw.fname = 0; + err = ScanITEMSLOT(itemslot,false,raw.data,raw.data_size,fname); + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawITEMSLOT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawITEMSLOT +( + itemslot_t * itemslot, // pointer to valid ITEMSLOT + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(itemslot); + DASSERT(fname); + + PRINT("SaveRawITEMSLOT(%s,%d)\n",fname,set_time); + + //--- write to file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&itemslot->fatt,0); + + const uint size = itemslot->add_battle ? ITEMSLT_SIZE12 : ITEMSLT_SIZE6; + itemslot->data.n_table = itemslot->add_battle ? 12 : 6; + if ( fwrite(&itemslot->data,1,size,F.f) != size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextITEMSLOT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextITEMSLOT +( + const itemslot_t * itemslot, // pointer to valid ITEMSLOT + ccp fname, // filename of destination + bool set_time, // true: set time stamps +// [[minimize]] + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >1: write only modified records + // >9: minimize output +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(itemslot); + DASSERT(fname); + PRINT("SaveTextITEMSLOT(%s,%d)\n",fname,set_time); + + BZ2Manager_t *bz = GetCommonBZ2Manager(FF_ITEMSLT); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,itemslot->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&itemslot->fatt,0); + + + //--- print header + syntax info + + const bool explain = print_header && !brief_count && !export_count; + if (explain) + fputs(text_itemslot_info_cr,F.f); + else + fputs(text_itemslot_head_cr,F.f); + + const bool tiny = brief_count > 1 || export_count; + if (!tiny) + fprintf(F.f,text_itemslot_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + + + //--- print parameters + + if ( minimize < 10 ) + { + fputs( brief_count < 2 ? section_sep : "\r\n", F.f ); + fprintf(F.f,text_itemslot_param_cr, + REVISION_NUM, itemslot->add_battle, itemslot->use_slt ); + } + + + //--- print tables + + uint active_offset = 0; + const uint N_SUM = 20; + uint sum[N_SUM]; + + const KeywordTab_t *par; + for ( par = itemslot_table_list; par->name1; par++ ) + { + const uint offset = par->id; + if ( active_offset == offset ) // skip entries for same section + continue; + active_offset = offset; + + const bool is_battle = par->opt == BATTLE_PARAM; + if ( is_battle && !itemslot->add_battle ) + continue; + + const u8 *val = (u8*)&itemslot->data + offset + 2; + const u8 *orig = bz->data + offset; + const uint n_col = *orig++; + const uint n_row = *orig++; + const uint table_size = n_row * n_col; + DASSERT( n_col <= N_SUM ); + + if ( minimize < 1 || memcmp(val,orig,table_size) ) + { + uint row, col; + memset(sum,0,sizeof(sum)); + + fputs( brief_count < 2 ? section_sep : "\r\n", F.f ); + fprintf(F.f, text_itemslot_table_cr, + par->name2 ? par->name2 : par->name1, + is_battle ? "battle" : "racing", + offset, n_row, n_col, REVISION_NUM ); + // [[2do]] ??? table dependent info + + const uint fw1 = 7 + 4*n_col; + const uint fw2 = 25; + fprintf(F.f,"#%.*s#%.*s\r\n# item",fw1,Minus300,fw2,Minus300); + for ( col = 0; col < n_col; col++ ) + fprintf(F.f,"%3u.",col+1); + fprintf(F.f," # sum item name\r\n#%.*s#%.*s\r\n",fw1,Minus300,fw2,Minus300); + + for ( row = 0; row < n_row; row++ ) + { + const int differ = memcmp(val,orig,n_col); + if ( delta_count < 2 || differ ) + { + fprintf(F.f,"%5u ",row); + uint row_sum = 0; + for ( col = 0; col < n_col; col++ ) + { + const uint num = val[col]; + if (num) + { + row_sum += num; + sum[col] += num; + fprintf(F.f,"%4u",num); + } + else + fputs(" .",F.f); + } + fprintf(F.f," #%4u %s\r\n",row_sum,item_info[row]); + + if (differ) + { + fputs("#ORIG:",F.f); + for ( col = 0; col < n_col; col++ ) + if ( val[col] == orig[col] ) + fputs(" .",F.f); + else + fprintf(F.f,"%4d",orig[col]); + fputs("\r\n",F.f); + if ( minimize < 2 ) + fputs("\r\n",F.f); + } + } + val += n_col; + orig += n_col; + } + if ( minimize < 2 ) + { + fprintf(F.f,"#%.*s#%.*s\r\n#SUM: ",fw1,Minus300,fw2,Minus300); + for ( col = 0; col < n_col; col++ ) + fprintf(F.f,"%4d",sum[col]); + fputs("\r\n",F.f); + } + } + } + + //--- terminate + + fputs( brief_count < 2 ? section_end :"\r\n", F.f ); + ResetFile(&F,set_time); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-kcl-image.c b/project/src/lib-kcl-image.c new file mode 100644 index 0000000..161e3cc --- /dev/null +++ b/project/src/lib-kcl-image.c @@ -0,0 +1,634 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kcl.h" +#include "lib-kmp.h" +#include "lib-image.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanOptPng() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool opt_png = false; // if true: PNG drawing is enabled +uint opt_png_pix_size = 0; // pixel size +uint opt_png_aalise = 0; // a factor for antialising +int opt_png_x1 = 0; // x1ptr) + { + case 'u': case 'U': + sf->ptr++; + //fall through + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + opt_png_pix_size = ScanUIntSI(&si,&err); + if ( err || !opt_png_pix_size ) + goto syntax_err; + break; + + case 'a': case 'A': case '/': + sf->ptr++; + opt_png_aalise = ScanUIntSI(&si,&err); + if (err) + goto syntax_err; + if (!opt_png_aalise) + opt_png_aalise = 1; + break; + case 'r': case 'R': + sf->ptr++; + opt_png_rm = true; + break; + + case 't': case 'T': + sf->ptr++; + opt_png_type_mask = ScanIntSI(&si,&err); + if (err) + goto syntax_err; + break; + + case 'y': case 'Y': + opt1 = &opt_png_y1; + opt2 = &opt_png_y2; + //fall through + case 'x': case 'X': + sf->ptr++; + *opt1 = ScanIntSI(&si,&err); + if (err) + goto syntax_err; + if ( NextCharSI(&si,false) == ':' ) + { + sf->ptr++; + *opt2 = ScanIntSI(&si,&err); + if (err) + goto syntax_err; + } + else + { + if ( *opt1 > 0 ) + *opt1 = -*opt1; + *opt2 = -*opt1; + } + break; + + case 'p': case 'P': + if (ScanExprSI(&si,&opt_png_param)) + goto syntax_err; + break; + + default: + sf->prev_ptr = sf->ptr; + goto syntax_err; + } + } + + opt_png = opt_png_pix_size > 0; + ResetSI(&si); + return 0; + + syntax_err: + ERROR0(ERR_SYNTAX, + "Invalid parameter for option --png: %s\n", + si.cur_file->prev_ptr ); + ResetSI(&si); + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveImageKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct png_param_t +{ + kcl_t * kcl; // pointer to valid KCL + + Image_t img; // image + Color_t col_bg; // background color + u32 type_mask; // kcl type selector (bit field) + + int x1; // left side of world + int x2; // right side of world + int y; // start height + int z1; // top side of world + int z2; // bottom side of world + + int pix_size; // size of a quadratic pixel + + Color_t kclcol1[N_KCL_FLAG]; // array with kcl colors + Color_t *kclcol2; // pointer to extended colors +} +png_param_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct png_object_t +{ + int xpos; // current x pos + int ypos; // current y pos + int zpos; // current z pos + int pix_size; // current pix_size + + uint ximg; // image x pos + uint yimg; // image y pos +} +png_object_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void ImageFall ( png_param_t *p, const png_object_t *o, uint multi ) +{ + noTRACE("FALL: *%u: %4u %4u / %6d %6d %6d / %u\n", + multi, o->ximg, o->yimg, o->xpos, o->ypos, o->zpos, o->pix_size ); + + + //--- fall + + double3 pos; + pos.x = o->xpos; + pos.y = o->ypos; + pos.z = o->zpos; + + int res_kcl_flag; + double res = FallKCL(p->kcl,stdout,pos,o->pix_size,0,p->type_mask,&res_kcl_flag); + if ( res_kcl_flag < 0 ) + return; // nothing found + + + //--- draw + + if ( multi == 1 ) + { + Color_t col; + if ( res_kcl_flag < N_KCL_FLAG ) + col = p->kclcol1[res_kcl_flag]; + else if ( res_kcl_flag < NN_KCL_FLAG ) + col.val = p->kclcol2[res_kcl_flag].val; + else + { + uint idx = res_kcl_flag - KCL_FLAG_USER_0; + col.val = idx < n_user_color + ? user_color[idx].val + : 0; + } + col.a = 0xff; + TRACE("DRAW: %05x -> %08x\n",res_kcl_flag,col.val); + noPRINT_IF( res_kcl_flag >= N_KCL_FLAG, + "DRAW: %05x -> %08x\n", res_kcl_flag, col.val ); + DrawPointIMG(&p->img,o->ximg,o->yimg,col,false); + return; + } + + + //--- 2x2 split + + multi /= 2; + png_object_t obj; + obj.pix_size = o->pix_size / 2; + const int half = obj.pix_size / 2; + obj.xpos = o->xpos - half; + obj.ypos = double2int(res) + half; + obj.zpos = o->zpos - half; + + obj.ximg = o->ximg; + obj.yimg = o->yimg; + + ImageFall(p,&obj,multi); + + obj.xpos += obj.pix_size; + obj.ximg += multi; + ImageFall(p,&obj,multi); + + obj.zpos += obj.pix_size; + obj.yimg += multi; + ImageFall(p,&obj,multi); + + obj.xpos -= obj.pix_size; + obj.ximg -= multi; + ImageFall(p,&obj,multi); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveImageKCL +( + kmp_t * kmp, // pointer to valid KMP + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(kmp); + DASSERT(kcl); + DASSERT(fname); + + + //--- calc pix_size & pix_aalise + + uint pix_aalise = opt_png_aalise ? opt_png_aalise : 1; + uint pix_size = opt_png_pix_size / opt_png_aalise; + if (!pix_size) + { + pix_size = 1; + pix_aalise = opt_png_pix_size; + } + + const uint multi = 8; + + + //--- remove unneeded faces and recalc octree + + CalcMinMaxKCL(kcl); + const double orig_max_ht = kcl->max.y; + + disable_transformation--; + TransformKCL(kcl); + disable_transformation++; + + if (opt_png_rm) + { + uint n_tri = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + + for ( ; n_tri-- > 0; td++ ) + { + if ( td->cur_flag < N_KCL_FLAG + && !( 1u << ( td->cur_flag & 0x1f ) & opt_png_type_mask ) ) + { + td->status |= TD_REMOVED; + } + } + DropSortHelper(kcl,TD_REMOVED,0); + } + + #ifdef TEST + + CalcMinMaxKCL(kcl); + float raise = kcl->min.y < 10.0 ? 10.0 - kcl->min.y : 0.0; + float perspective = 50000.0; + + if ( raise > 0.0 || perspective != 0.0 ) + { + uint n_tri = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + for ( ; n_tri-- > 0; td++ ) + { + td->pt[0].y += raise; + td->pt[1].y += raise; + td->pt[2].y += raise; + td->status |= TD_INVALID_NORM; + + uint i; + for ( i =0; i < 3; i++ ) + { + //td->pt[i].x = td->pt[i].x / ( 50000.0 - td->pt[i].y ) * 1000; + //td->pt[i].z = td->pt[i].z / ( 50000.0 - td->pt[i].y ) * 1000; + float f = td->pt[i].y / 50000; + td->pt[i].x = td->pt[i].x * f; + td->pt[i].z = td->pt[i].z * f; + } + } + } + #else + double raise = 0.0; + CalcMinMaxKCL(kcl); + if ( kcl->min.y < 10.0 ) + { + raise = 10.0 - kcl->min.y; + + uint n_tri = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + for ( ; n_tri-- > 0; td++ ) + { + td->pt[0].y += raise; + td->pt[1].y += raise; + td->pt[2].y += raise; + td->status |= TD_INVALID_NORM; + } + } + #endif + +#if 1 + const uint min_cube_size = multi * pix_size / 2; + if ( kcl->min_cube_size < min_cube_size ) + kcl->min_cube_size = min_cube_size; +#endif + + kcl->min_max_valid = kcl->stat_valid = kcl->octree_valid = false; + CreateOctreeKCL(kcl); + KCL_ACTION_LOG("SaveImageKCL(%s) N=%u, model_modified=%d\n", + fname, kcl->tridata.used, kcl->model_modified ); + + + //--- find view port + + float xmin, xmax, zmin, zmax; + + const uint n_ckpt = kmp->dlist[KMP_CKPT].used; + if (n_ckpt) + { + const kmp_ckpt_entry_t *ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + xmin = xmax = ckpt->left[0]; + zmin = zmax = ckpt->left[1]; + + uint i; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + { + if ( xmin > ckpt->left [0] ) xmin = ckpt->left [0]; + if ( xmax < ckpt->left [0] ) xmax = ckpt->left [0]; + if ( xmin > ckpt->right[0] ) xmin = ckpt->right[0]; + if ( xmax < ckpt->right[0] ) xmax = ckpt->right[0]; + + if ( zmin > ckpt->left [1] ) zmin = ckpt->left [1]; + if ( zmax < ckpt->left [1] ) zmax = ckpt->left [1]; + if ( zmin > ckpt->right[1] ) zmin = ckpt->right[1]; + if ( zmax < ckpt->right[1] ) zmax = ckpt->right[1]; + } + PRINT("SETUP: %2.0f .. %2.0f / %2.0f .. %2.0f\n",xmin,xmax,zmin,zmax); + } + else + { + CalcMinMaxKCL(kcl); + xmin = kcl->min.x - 10.0; + xmax = kcl->max.x + 10.0; + zmin = kcl->min.z - 10.0; + zmax = kcl->max.z + 10.0; + } + + if (transform_active) + { + // create a surrounding cuboid, ... + float3 tab[8]; + tab[0].x = tab[2].x = tab[4].x = tab[5].x = xmin; + tab[1].x = tab[3].x = tab[5].x = tab[7].x = xmax; + tab[0].y = tab[1].y = tab[4].y = tab[5].y = 0.0; + tab[2].y = tab[3].y = tab[6].y = tab[7].y = orig_max_ht; + tab[0].z = tab[1].z = tab[2].z = tab[3].z = zmin; + tab[4].z = tab[5].z = tab[6].z = tab[7].z = zmax; + + // ... transform ... + TransformPosFloat3D(tab->v,8,sizeof(*tab)); + + // ... and get min/max again + xmin = xmax = tab[0].x; + zmin = zmax = tab[0].z; + uint i; + for ( i = 1; i < 8; i++ ) + { + if ( xmin > tab[i].x ) xmin = tab[i].x; + if ( xmax < tab[i].x ) xmax = tab[i].x; + if ( zmin > tab[i].z ) zmin = tab[i].z; + if ( zmax < tab[i].z ) zmax = tab[i].z; + } + PRINT("SETUP: %2.0f .. %2.0f / %2.0f .. %2.0f [TRANSFORM]\n", + xmin, xmax, zmin, zmax ); + } + + //--- user settings override calcualted settings + + if ( opt_png_x1 < opt_png_x2 ) + { + xmin = opt_png_x1; + xmax = opt_png_x2; + } + + if ( opt_png_y1 < opt_png_y2 ) + { + zmin = opt_png_y1; + zmax = opt_png_y2; + } + PRINT("SETUP: %2.0f .. %2.0f / %2.0f .. %2.0f\n",xmin,xmax,zmin,zmax); + + + //--- limit the output to kcl range + + CalcMinMaxKCL(kcl); + if ( xmin < kcl->min.x ) xmin = kcl->min.x; + if ( xmax > kcl->max.x ) xmax = kcl->max.x; + if ( zmin < kcl->min.z ) zmin = kcl->min.z; + if ( zmax > kcl->max.z ) zmax = kcl->max.z; + + uint x_total = (int)ceil(xmax-xmin); + uint z_total = (int)ceil(zmax-zmin); + PRINT("SETUP: %2.0f .. %2.0f / %2.0f .. %2.0f => %u * %u\n", + xmin, xmax, zmin, zmax, x_total, z_total ); + + + //--- setup parameters + + png_param_t param; + memset(¶m,0,sizeof(param)); + param.kcl = kcl; + param.type_mask = opt_png_type_mask; + InitializeIMG(¶m.img); + SETCOLOR(param.col_bg,0,0,0x80,0xff); + + + //--- pixel coordinates + + param.pix_size = pix_size; + + uint xn = ALIGN32( (x_total+multi+1) / pix_size, multi ); + uint zn = ALIGN32( (z_total+multi+1) / pix_size, multi ); + param.x1 = double2int((xmax+xmin)/2) - xn * pix_size / 2; + param.x2 = param.x1 + xn * pix_size; + param.z1 = double2int((zmax+zmin)/2) - zn * pix_size / 2; + param.z2 = param.z1 + zn * pix_size; + param.y = double2int(kcl->max.y) + 100; + + TRACE("SETUP: %d .. %d / %d / %d .. %d => %u*%u *%u /%u\n", + param.x1, param.x2, param.y, param.z1, param.z2, + xn, zn, pix_aalise, pix_size ); + if ( verbose >= 0 && !(KCL_MODE & KCLMD_SILENT) ) + fprintf(stdlog, + " - png draw: x=%d..%d, y=%d, z=%d..%d" + " => size=%u*%u [a.alising %u], units/pix=%u\n", + param.x1, param.x2, param.y, param.z1, param.z2, + xn, zn, pix_aalise > 1 ? pix_aalise : 0, pix_size ); + fflush(stdlog); + + CreateIMG( ¶m.img, true, xn, zn, param.col_bg ); + + + //--- calc colors + + Color_t *kclcol2 = (Color_t*)kcl_color; + param.kclcol2 = kclcol2 - N_KCL_FLAG; + + memset(param.kclcol1,0x00,sizeof(param.kclcol1)); + uint *flag_count = AllocFlagCount(); + CountFlagsKCL(kcl,flag_count); + + uint i, class_idx[N_KCL_CLASS] = {0}; + for ( i = 0; i < N_KCL_TYPE; i++ ) + { + const kcl_class_t *cls = kcl_class + kcl_type[i].cls; + uint col_idx = class_idx[cls->cls]; + uint col_max = cls->ncol; + + uint j; + for ( j = i ; j < N_KCL_FLAG; j += N_KCL_TYPE ) + { + if (!flag_count[j]) + continue; + + param.kclcol1[j].val = kclcol2[cls->color_index+col_idx].val; + if ( ++col_idx >= col_max ) + col_idx = 0; + } + class_idx[cls->cls] = col_idx ; + } + FREE(flag_count); + + + //--- setup object and iterate + + png_object_t object; + memset(&object,0,sizeof(object)); + + object.pix_size = multi * param.pix_size; + object.xpos = param.x1 + object.pix_size / 2; + object.ypos = param.y; + object.ximg = 0; + + while ( object.xpos < param.x2 ) + { + object.zpos = param.z1 + object.pix_size / 2; + object.yimg = 0; + while ( object.zpos < param.z2 ) + { + ImageFall(¶m,&object,multi); + object.zpos += object.pix_size; + object.yimg += multi; + } + object.xpos += object.pix_size; + object.ximg += multi; + } + + + //--- terminate + + if ( pix_aalise > 1 ) + { + PRINT("RESIZE: %u x %u -> %u x %u\n", + param.img.width, param.img.height, xn/pix_aalise, zn/pix_aalise ); + SmartResizeIMG(¶m.img,false,0,xn/pix_aalise,zn/pix_aalise); + } + + enumError err = SavePNG(¶m.img,true,fname,0,0,true,0); + ResetIMG(¶m.img); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-kcl.c b/project/src/lib-kcl.c new file mode 100644 index 0000000..090abc5 --- /dev/null +++ b/project/src/lib-kcl.c @@ -0,0 +1,8955 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kcl.h" +#include "lib-szs.h" +#include "lib-image.h" +#include "lib-bzip2.h" +#include "kcl.inc" +#include "obj-mtl-bz2.inc" +#include +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL action log /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool KCL_ACTION_LOG ( const char * format, ... ) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[KCL]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + + if ( verbose > 2 || KCL_MODE & KCLMD_LOG ) + { + fflush(stdout); + fprintf(stdlog," %s>[KCL]%s ",colset->heading,colset->info); + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool drop_sort_triangles = false; + +int enable_kcl_drop_auto = 0; +kcl_mode_t KCL_MODE = KCLMD_M_DEFAULT; + +kcl_t * kcl_ref = 0; // NULL or reference KCL for calculations +kcl_t * kcl_ref_prio = 0; // NULL or prioreference KCL for calculations + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_kcl_tab[] = +{ + { 0, "CLEAR", "RESET", KCLMD_M_ALL | KCLMD_F_HIDE }, + { KCLMD_M_DEFAULT, "DEFAULT", 0, KCLMD_M_ALL | KCLMD_F_HIDE }, + + { KCLMD_FAST, "FAST", 0, 0 }, + { KCLMD_ROUND, "ROUND", 0, 0 }, + { KCLMD_NORMALS, "NORMALS", 0, 0 }, + { KCLMD_MTL, "MTL", "MATERIALS", 0 }, + { KCLMD_WIIMM, "WIIMM", 0, 0 }, + { KCLMD_TRIANGLES, "TRIANGLES", 0, 0 }, + { KCLMD_OUT_SWAP, "OUT-SWAP", "OUTSWAP", 0 }, + { KCLMD_OUT_SWAP, "OSWAP", 0, 0 }, + + { KCLMD_G, "G", 0, 0 }, + { KCLMD_USEMTL, "USEMTL", 0, 0 }, + { KCLMD_CLIP, "CLIP", 0, 0 }, + { KCLMD_IN_SWAP, "IN-SWAP", "INSWAP", 0 }, + { KCLMD_IN_SWAP, "ISWAP", 0, 0 }, + { KCLMD_AUTO, "AUTO", 0, 0 }, + + { KCLMD_CENTER, "CENTER", 0, 0 }, + { KCLMD_ADD_ROAD, "ADD-ROAD", "ADDROAD", 0 }, + + { KCLMD_HEX4, "HEX4", "H4", 0 }, + { KCLMD_HEX23, "HEX23", "H23", 0 }, + { KCLMD_M_HEX, "HEX", 0, KCLMD_M_HEX | KCLMD_F_HIDE }, + { 0, "-HEX", 0, KCLMD_M_HEX | KCLMD_F_HIDE }, + + { KCLMD_CUBE, "CUBE", 0, 0 }, + + { KCLMD_SMALL, "SMALL", 0, KCLMD_M_PRESET }, + { KCLMD_CHARY, "CHARY", "NINTENDO", KCLMD_M_PRESET }, + { KCLMD_DRAW, "DRAW", 0, KCLMD_M_PRESET }, + { 0, "-SMALL", 0, KCLMD_SMALL | KCLMD_F_HIDE }, + { 0, "-CHARY", "-NINTENDO", KCLMD_CHARY | KCLMD_F_HIDE }, + { 0, "MEDIUM", 0, KCLMD_M_PRESET | KCLMD_F_HIDE }, + + { KCLMD_DROP_AUTO, "DROP-AUTO", "DROPAUTO", KCLMD_M_DROP }, + { 0, "-DROP-AUTO", "-DROPAUTO", KCLMD_DROP_AUTO|KCLMD_F_HIDE }, + { KCLMD_DROP_UNUSED + |KCLMD_DROP_AUTO, "DROP-UNUSED", "DROPUNUSED", 0 }, + { KCLMD_DROP_FIXED + |KCLMD_DROP_AUTO, "DROP-FIXED", "DROPFIXED", 0 }, + { KCLMD_DROP_INVALID + |KCLMD_DROP_AUTO, "DROP-INVALID", "DROPINVALID", 0 }, + { KCLMD_M_DROP, "DROP", "DROP-ALL", KCLMD_M_DROP | KCLMD_F_HIDE }, + { 0, "-DROP", "-DROP-ALL", KCLMD_M_DROP | KCLMD_F_HIDE }, + + { KCLMD_RM_FACEDOWN, "RM-FACEDOWN", "RMFACEDOWN", 0 }, + { KCLMD_RM_FACEUP, "RM-FACEUP", "RMFACEUP", 0 }, + { KCLMD_M_FIX_ALL, "FIX-ALL", "FIXALL", KCLMD_M_FIX_ALL | KCLMD_F_HIDE }, + + { KCLMD_CONV_FACEUP, "CONV-FACEUP", "CONVFACEUP", 0 }, + { KCLMD_WEAK_WALLS, "WEAK-WALLS", "WEAKWALLS", 0 }, + + { KCLMD_NEW, "NEW", 0, 0 }, + { KCLMD_SORT, "SORT", 0, 0 }, + + { KCLMD_TINY_0, "TINY-0", "TINY0", KCLMD_M_TINY }, + { KCLMD_TINY_1, "TINY-1", "TINY1", KCLMD_M_TINY }, + { KCLMD_TINY_2, "TINY-2", "TINY2", KCLMD_M_TINY }, + { KCLMD_TINY_3, "TINY-3", "TINY3", KCLMD_M_TINY }, + { KCLMD_TINY_4, "TINY-4", "TINY4", KCLMD_M_TINY }, + { KCLMD_TINY_5, "TINY-5", "TINY5", KCLMD_M_TINY }, + { KCLMD_TINY_6, "TINY-6", "TINY6", KCLMD_M_TINY }, + { KCLMD_TINY_7, "TINY-7", "TINY7", KCLMD_M_TINY }, + + { KCLMD_LOG, "LOG", 0, 0 }, + { KCLMD_SILENT, "SILENT", 0, 0 }, + { KCLMD_POSLEN, "POSLEN", 0, 0 }, + { KCLMD_INPLACE, "INPLACE", 0, 0 }, + + { KCLMD_F_SCRIPT, "SCRIPT", 0, 0 }, + { KCLMD_TEST, "TEST", "T", 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +void SetKclMode ( kcl_mode_t new_mode ) +{ + PRINT("KCL-MODE-IN: PC=%d, KPC=%d, DS=%d\n", + have_patch_count, have_kcl_patch_count, drop_sort_triangles ); + + // disable DROP_AUTO mode if any other DROP is set + if ( (new_mode & KCLMD_M_DROP ) != KCLMD_DROP_AUTO ) + new_mode &= ~KCLMD_DROP_AUTO; + + if (drop_sort_triangles) + have_patch_count--, have_kcl_patch_count--; + + KCL_MODE = new_mode & KCLMD_M_ALL; + + const kcl_mode_t tiny = KCL_MODE & KCLMD_M_TINY; + if ( tiny >= KCLMD_TINY_1 ) + { + KCL_MODE = KCL_MODE & ~KCLMD_M_PRESET | (KCLMD_SMALL|KCLMD_ROUND|KCLMD_SORT); + if ( tiny >= KCLMD_TINY_2 ) + KCL_MODE |= KCLMD_NEW; + } + + if ( PATCH_FILE_MODE & PFILE_F_LOG_ALL ) + KCL_MODE |= KCLMD_LOG; + + drop_sort_triangles = ( KCL_MODE & KCLMD_M_PATCH ) != 0; + if (drop_sort_triangles) + have_patch_count++, have_kcl_patch_count++; + + PRINT("KCL-MODE-OUT: PC=%d, KPC=%d, DS=%d\n", + have_patch_count, have_kcl_patch_count, drop_sort_triangles ); +} + +//----------------------------------------------------------------------------- + +int ScanOptKcl ( ccp arg ) +{ + if (!arg) + return 0; + + s64 stat = ScanKeywordList(arg,opt_kcl_tab,0,true,0,KCL_MODE, + "Option --kcl",ERR_SYNTAX); + if ( stat != -1 ) + { + SetKclMode(stat); + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintKclMode ( char *buf, uint bufsize, kcl_mode_t mode ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + char *dest = buf; + char *end = buf + bufsize - 1; + + mode = mode & KCLMD_M_ALL | KCLMD_F_HIDE; + kcl_mode_t mode1 = mode; + + const KeywordTab_t *ct; + for ( ct = opt_kcl_tab; ct->name1 && dest < end; ct++ ) + { + if ( ct->opt & KCLMD_F_HIDE ) + continue; + + if ( ct->opt ? (mode & ct->opt) == ct->id : mode & ct->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,ct->name1); + mode &= ~(ct->id|ct->opt); + } + } + + if ( mode1 == (KCLMD_M_DEFAULT|KCLMD_F_HIDE) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + return dest-buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetKclMode() +{ + static char buf[200] = {0}; + if (!*buf) + PrintKclMode(buf,sizeof(buf),KCL_MODE); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupKCL() +{ + if ( opt_tiny > 0 ) + { + const kcl_mode_t tiny + = ( (kcl_mode_t)opt_tiny << KCLMD_S_TINY ) & KCLMD_M_TINY; + if ( tiny > ( KCL_MODE & KCLMD_M_TINY )) + SetKclMode( KCL_MODE & ~KCLMD_M_TINY | tiny ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum CommandKCL_t +{ + KCMD_NONE = 0, // definitiv NULL + + KCMD_V, + KCMD_VN, + KCMD_F, + KCMD_G, + KCMD_USEMTL, + KCMD_TRI, + +} CommandKCL_t; + +/////////////////////////////////////////////////////////////////////////////// + +CommandKCL_t GetCommandKCL +( + ccp name // name to scan, upper case is assumed +) +{ + static FormatField_t cmdtab = {0}; + if (!cmdtab.used) + { + struct kcmdtab_t { ccp name; int id; }; + static const struct kcmdtab_t def_tab[] = + { + { "V", KCMD_V }, + { "VN", KCMD_VN }, + { "F", KCMD_F }, + { "G", KCMD_G }, + { "USEMTL", KCMD_USEMTL }, + { "TRI", KCMD_TRI }, + + {0,0} + }; + + const struct kcmdtab_t * cp; + for ( cp = def_tab; cp->name; cp++ ) + InsertFormatField(&cmdtab,cp->name,0,0,0)->num = cp->id; + } + + FormatFieldItem_t * item = FindFormatField(&cmdtab,name); + return item ? item->num : KCMD_NONE; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// const parameters /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// create octree params +uint KCL_BITS = 0; // 0 = generic +uint KCL_BLOW = 400; +uint KCL_MAX_DEPTH = 10; +uint KCL_MAX_TRI = 30; +uint KCL_MIN_SIZE = 512; +uint KCL_MAX_SIZE = 0x100000; +double3 KCL_MIN; // -inf +double3 KCL_MAX; // +inf +uint KCL_TRI_SPLIT = 1024; // >0: split triangles (recures) if side length ># + +// export obj param +static double3 KCL_CLIP; // = 1.0 + +/////////////////////////////////////////////////////////////////////////////// + +static bool DefineVector ( double3 *val, ccp name ) +{ + bool stat = false; + + const Var_t *var = FindConst(name); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->x = GetXDoubleV(var); + val->y = GetYDoubleV(var); + val->z = GetZDoubleV(var); + } + + char varname[100]; + snprintf(varname,sizeof(varname),"%s.X",name); + var = FindConst(varname); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->x = GetXDoubleV(var); + } + + snprintf(varname,sizeof(varname),"%s.Y",name); + var = FindConst(varname); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->y = GetYDoubleV(var); + } + + snprintf(varname,sizeof(varname),"%s.Z",name); + var = FindConst(varname); + if ( var && var->mode != VAR_UNSET ) + { + stat = true; + val->z = GetZDoubleV(var); + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +void LoadParametersKCL +( + ccp log_prefix // not NULL: + // print log message with prefix +) +{ + static bool done = false; + if (done) + return; + done = true; + + + //--- --kcl small,chary,draw ==> change defaults + + SetKclMode(KCL_MODE); + kcl_mode_t preset = KCL_MODE & KCLMD_M_PRESET; + + if ( preset == KCLMD_DRAW ) + { + KCL_MODE = KCL_MODE | KCLMD_FAST | KCLMD_ROUND; + KCL_BLOW = 200; + KCL_MAX_TRI = 50; + } + else if ( preset == KCLMD_SMALL ) + { + KCL_MODE = KCL_MODE & ~KCLMD_FAST | KCLMD_ROUND; + KCL_BLOW = 200; + KCL_MAX_TRI = 40; + } + else if ( preset == KCLMD_CHARY ) + { + KCL_BLOW = 600; + KCL_MAX_TRI = 20; + } + + + //--- KCL_BITS + + const Var_t *var = FindConst("KCL_BITS"); + if (var) + { + const uint num = GetIntV(var); + if ( num >= 0 ) + KCL_BITS = num < 3 ? 3 : num > 20 ? 20 : num; + } + + + //--- KCL_BLOW + + var = FindConst("KCL_BLOW"); + if (var) + { + const uint num = GetIntV(var); + if ( num >= 0 ) + KCL_BLOW = num < 10000 ? num : 10000; + } + + + //--- KCL_CLIP + + KCL_CLIP.x = KCL_CLIP.y = KCL_CLIP.z = 1.0; + DefineVector(&KCL_CLIP,"KCL_CLIP"); + if ( KCL_CLIP.x <= 0.0 ) KCL_CLIP.x = 1.0; + if ( KCL_CLIP.y <= 0.0 ) KCL_CLIP.y = 1.0; + if ( KCL_CLIP.z <= 0.0 ) KCL_CLIP.z = 1.0; + + + //--- KCL_MAX_DEPTH + + var = FindConst("KCL_MAX_DEPTH"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MAX_DEPTH = num; + } + + + //--- KCL_MAX_TRI + + var = FindConst("KCL_MAX_TRI"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MAX_TRI = num < 5 ? 5 : num > 1000 ? 1000 : num; + } + + + //--- KCL_MAX_SIZE, KCL_MIN_SIZE + + var = FindConst("KCL_MAX_SIZE"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MAX_SIZE = num < 0x100 ? 0x100 : num > 0x100000 ? 0x100000 : num; + } + + var = FindConst("KCL_MIN_SIZE"); + if (var) + { + const uint num = GetIntV(var); + if ( num > 0 ) + KCL_MIN_SIZE = num < KCL_MAX_SIZE ? num : KCL_MAX_SIZE; + } + + + //--- KCL_MIN, KCL_MAX + + KCL_MIN.x = KCL_MIN.y = KCL_MIN.z = -INFINITY; + DefineVector(&KCL_MIN,"KCL_MIN"); + + KCL_MAX.x = KCL_MAX.y = KCL_MAX.z = INFINITY; + DefineVector(&KCL_MAX,"KCL_MAX"); + + if ( KCL_MAX.x < KCL_MIN.x ) KCL_MAX.x = KCL_MIN.x; + if ( KCL_MAX.y < KCL_MIN.y ) KCL_MAX.y = KCL_MIN.y; + if ( KCL_MAX.z < KCL_MIN.z ) KCL_MAX.z = KCL_MIN.z; + + + //--- KCL_TRI_SPLIT, + + var = FindConst("KCL_TRI_SPLIT"); + if (var) + { + KCL_TRI_SPLIT = GetIntV(var); + if ( KCL_TRI_SPLIT && KCL_TRI_SPLIT < 512 ) + KCL_TRI_SPLIT = 512; + } + + + //--- logging + + if ( !KCL_ACTION_LOG("Global KCL Modes: %s\n",GetKclMode()) + && log_prefix + && KCL_MODE != KCLMD_M_DEFAULT + && !(KCL_MODE & KCLMD_SILENT) ) + { + fprintf(stdlog,"%sglobal kcl modes: %s\n",log_prefix,GetKclMode()); + fflush(stdlog); + } + + #if HAVE_PRINT + PRINT("KCL-PARAM: MODE= [%llx] %s\n",(u64)KCL_MODE,GetKclMode()); + + PRINT(" > BITS=%u, MAX_TRI=%u, MAX_DEPTH=%u, CUBE_SIZE=%u..%u\n", + KCL_BITS, KCL_MAX_TRI, KCL_MAX_DEPTH, KCL_MIN_SIZE, KCL_MAX_SIZE ); + PRINT(" > CLIP: %11.3f %11.3f %11.3f, enabled=%d \n", + KCL_CLIP.x, KCL_CLIP.y, KCL_CLIP.z, (KCL_MODE&KCLMD_CLIP)!=0 ); + PRINT(" > MIN: %11.3f %11.3f %11.3f\n", + KCL_MIN.x, KCL_MIN.y, KCL_MIN.z ); + PRINT(" > MAX: %11.3f %11.3f %11.3f\n", + KCL_MAX.x, KCL_MAX.y, KCL_MAX.z ); + + if ( KCL_MODE & KCLMD_CUBE ) + PRINT(" > Use CUBE mode (experimental), split triangles @ %u\n", + KCL_TRI_SPLIT ); + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +Color_t user_color[N_KCL_USER_FLAGS]; +uint n_user_color = 0; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeKCL ( kcl_t * kcl ) +{ + //--- one time setup + + static int done = 0; + if (!done) + { + ++done; + if ( opt_slot & (SLOTMD_ICE_TO_WATER|SLOTMD_WATER_TO_ICE) ) + { + SetupPatchKclFlag(); + const u16 search = opt_slot & SLOTMD_ICE_TO_WATER ? 0x0070 : 0x0030; + + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + if ( ( patch_kcl_flag[i] & 0x00ff ) == search ) + patch_kcl_flag[i] = patch_kcl_flag[i] ^ 0x0040; + + PurgePatchKclFlag(); + } + + if ( KCL_MODE & KCLMD_WEAK_WALLS ) + { + SetupPatchKclFlag(); + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + { + const int type = GET_KCL_TYPE(i); + if ( type >= 0 && kcl_type[type].attrib & KCLT_C_WALL ) + patch_kcl_flag[i] |= 0x8000; + } + PurgePatchKclFlag(); + } + } + + //--- object setup + + DASSERT(kcl); + memset(kcl,0,sizeof(*kcl)); + + kcl->fname = EmptyString; + kcl->revision = REVISION_NUM; + + InitializeList(&kcl->tridata,sizeof(kcl_tridata_t)); + InitializeFormatField(&kcl->flag_name); + InitializeFormatField(&kcl->flag_pattern); + InitializeFormatField(&kcl->flag_missing); + + // default header values + kcl->unknown_0x10 = 300.0; + kcl->unknown_0x38 = 250.0; + + kcl->tri_minval.x = -1e9; + kcl->tri_minval.y = -1e9; + kcl->tri_minval.z = -1e9; + + kcl->tri_maxval.x = 1e9; + kcl->tri_maxval.y = 1e9; + kcl->tri_maxval.z = 1e9; + + LoadParametersKCL( verbose > 0 ? " - " : 0 ); + + kcl->fast = ( KCL_MODE & KCLMD_FAST ) != 0; + kcl->accept_hex4 = ( KCL_MODE & KCLMD_HEX4 ) != 0; + kcl->accept_hex23 = ( KCL_MODE & KCLMD_HEX23 ) != 0; + + kcl->min_cube_size = KCL_MIN_SIZE; + kcl->max_cube_size = KCL_MAX_SIZE; + kcl->cube_blow = KCL_BLOW; + kcl->max_cube_triangles = KCL_MAX_TRI; + kcl->max_octree_depth = KCL_MAX_DEPTH; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetKCL ( kcl_t * kcl ) +{ + DASSERT(kcl); + + const file_format_t fform_outfile = kcl->fform_outfile; + + ResetStatisticsKCL(kcl); + FreeString(kcl->fname); + FreeString(kcl->flag_fname); + if (kcl->octree_alloced) + FREE(kcl->octree); + if (kcl->raw_data_alloced) + FREE(kcl->raw_data); + ResetList(&kcl->tridata); + ResetFormatField(&kcl->flag_name); + ResetFormatField(&kcl->flag_pattern); + ResetFormatField(&kcl->flag_missing); + + InitializeKCL(kcl); + kcl->fform_outfile = fform_outfile; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearKCL ( kcl_t * kcl, bool init_kcl ) +{ + DASSERT(kcl); + if (init_kcl) + InitializeKCL(kcl); + else + { + ccp fname = kcl->fname; + kcl->fname = 0; + ResetKCL(kcl); + kcl->fname = fname; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyKCL ( kcl_t * kcl, bool init_kcl, const kcl_t * src ) +{ + DASSERT(kcl); + DASSERT(src); + + if ( !kcl || kcl == src ) + return; + + ClearKCL(kcl,init_kcl); + if (!src) + return; + + const uint n_tri = src->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)src->tridata.list; + uint i; + for ( i = 0; i < n_tri; i++, td++ ) + AppendTriangleKCLp( kcl, td->cur_flag, td->pt, td->pt+1, td->pt+2 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_tridata_t helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CopyTriData_F2D ( kcl_tridata_dbl_t *dest, const kcl_tridata_flt_t *src ) +{ + DASSERT(src); + DASSERT(dest); + memcpy(dest->pt,src->pt,sizeof(dest->pt)); + + const int n = SIZEOFRANGE(kcl_tridata_dbl_t,length,normal)/sizeof(double); + const float *s = &src->length; + double *d = &dest->length; + int i; + for ( i = 0; i < n; i++ ) + *d++ = *s++; +} + +//----------------------------------------------------------------------------- + +void CopyTriData_D2F ( kcl_tridata_flt_t *dest, const kcl_tridata_dbl_t *src ) +{ + DASSERT(src); + DASSERT(dest); + memcpy(dest->pt,src->pt,sizeof(dest->pt)); + + const int n = SIZEOFRANGE(kcl_tridata_dbl_t,length,normal)/sizeof(double); + const double *s = &src->length; + float *d = &dest->length; + int i; + for ( i = 0; i < n; i++ ) + *d++ = *s++; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +uint CheckMetricsTriData +( + // check length and minimal height of triangles + + kcl_tridata_t *td, // valid pointer to first record + uint n, // number of records to process + kcl_t *kcl // not NULL: store statistics +) +{ + DASSERT( td || !n ); + #if HAVE_PRINT + const u_usec_t start_time = GetTimerUSec(); + #endif + + uint invalid_count = 0; + kcl_tridata_t *end = td + n; + for ( ; td < end; td++ ) + { + if ( td->status & TD_INVALID ) + { + invalid_count++; + continue; + } + + if ( !( td->status & TD_INVALID_NORM ) && + ( !isnormal(td->length) || fabs(td->length) < opt_tri_height )) + { + PRINT0("CHECK-TRI/INVALID LENGTH %g\n",td->length); + td->status |= TD_INVALID|TD_INVALID_NORM; + invalid_count++; + continue; + } + + if ( opt_tri_area > 0 || opt_tri_height > 0 ) + { + tri_metrics_t met; + CalcTriMetricsByPoints(&met,td->pt,td->pt+1,td->pt+2); + if ( met.area < opt_tri_area || met.min_ht < opt_tri_height ) + { + #if HAVE_PRINT + { + ccp sep = " "; + char hbuf[30], abuf[30]; + if ( met.min_ht < opt_tri_height ) + { + snprintf(hbuf,sizeof(hbuf)," HEIGHT %g",met.min_ht); + sep = ", "; + } + else + *hbuf = 0; + + if ( met.area < opt_tri_area ) + snprintf(abuf,sizeof(abuf),"%sAREA %g",sep,met.area); + else + *abuf = 0; + + PRINT0("CHECK-TRI/INVALID%s%s\n",hbuf,abuf); + } + #endif + + td->status |= TD_INVALID; + invalid_count++; + continue; + } + } + } + + #if HAVE_PRINT + const u_usec_t duration = GetTimerUSec() - start_time; + #endif + + if (kcl) + { + PRINT("CheckMetricsTriData(%u), invalid: %u -> %u, done in %s\n", + n, kcl->tri_inv_count, invalid_count, + PrintTimerUSec(0,0,duration,6) ); + kcl->tri_inv_count = invalid_count; + } + else + PRINT("CheckMetricsTriData(%u), invalid: %u, done in %s\n", + n, invalid_count, PrintTimerUSec(0,0,duration,6) ); + return invalid_count; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +uint CalcPointsTriData +( + // calculate 'pt' using 'length' & 'normal' + // returns the number of fixes + + kcl_tridata_t *td, // valid pointer to first record + uint n, // number of records to process + kcl_t *kcl, // not NULL: store statistics + bool use_minmax // true & 'kcl': use 'tri_minval' and 'tri_maxval' +) +{ + DASSERT( td || !n ); + + if (!kcl) + use_minmax = false; + + const double min_divisor = 1e-9; + uint fix_count = 0, invalid_count = 0; + + for ( ; n-- > 0; td++ ) + { + if ( td->status & TD_INVALID + || !isnormal(td->length) + || fabs(td->length) < opt_tri_height + ) + { + invalid: + PRINT0("TRI INVALID %g\n",td->length); + td->length = 0.0; +// memcpy(td->pt+1,td->pt,sizeof(*td->pt)); +// memcpy(td->pt+2,td->pt,sizeof(*td->pt)); + td->status |= TD_INVALID|TD_INVALID_NORM; + invalid_count++; + continue; + } + + double3 cross_1, cross_2; + double dot_1, dot_2; + + CrossProd3(cross_1,td->normal[1],td->normal[0]); + dot_1 = DotProd3(cross_1,td->normal[3]); + if (!isfinite(dot_1)) + goto invalid; + + if ( fabs(dot_1) >= min_divisor ) + { + const double factor_1 = td->length / dot_1; + double3 *d3 = td->pt + 2; + d3->x = td->pt[0].x + cross_1.x * factor_1; + d3->y = td->pt[0].y + cross_1.y * factor_1; + d3->z = td->pt[0].z + cross_1.z * factor_1; + + if ( use_minmax && + ( d3->x < kcl->tri_minval.x || d3->x > kcl->tri_maxval.x + || d3->y < kcl->tri_minval.y || d3->y > kcl->tri_maxval.y + || d3->z < kcl->tri_minval.z || d3->z > kcl->tri_maxval.z )) + { + TRACE("VERTEX-1: %12.6g %12.6g %12.6g\n", + d3->x, d3->y, d3->z ); + goto abort_1; + } + } + else + { + abort_1: + noPRINT("INVALID DOT_1\n"); + memcpy(td->pt+2,td->pt,sizeof(*td->pt)); + td->status |= TD_FIXED_PT|TD_INVALID_NORM; + } + + CrossProd3(cross_2,td->normal[2],td->normal[0]); + dot_2 = DotProd3(cross_2,td->normal[3]); + if (!isfinite(dot_2)) + goto invalid; + + if ( fabs(dot_2) >= min_divisor ) + { + const double factor_2 = td->length / dot_2; + double3 *d3 = td->pt + 1; + d3->x = td->pt[0].x + cross_2.x * factor_2; + d3->y = td->pt[0].y + cross_2.y * factor_2; + d3->z = td->pt[0].z + cross_2.z * factor_2; + + if ( use_minmax && + ( d3->x < kcl->tri_minval.x || d3->x > kcl->tri_maxval.x + || d3->y < kcl->tri_minval.y || d3->y > kcl->tri_maxval.y + || d3->z < kcl->tri_minval.z || d3->z > kcl->tri_maxval.z )) + { + TRACE("VERTEX-2: %12.6g %12.6g %12.6g\n", + d3->x, d3->y, d3->z ); + goto abort_2; + } + } + else + { + abort_2: + noPRINT("INVALID DOT_2\n"); + memcpy(td->pt+1,td->pt,sizeof(*td->pt)); + td->status |= TD_FIXED_PT|TD_INVALID_NORM; + } + + if ( td->status & TD_FIXED_PT ) + fix_count++; + } + + if (kcl) + { + kcl->tri_inv_count = invalid_count; + kcl->tri_fix_count = fix_count; + } + + return invalid_count + fix_count; +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +void CalcNormalsTriData +( + // calculate 'length' & 'normal' using 'pt' + // the calculation is only done, if 'TD_INVALID_NORM' + + kcl_t *kcl, // valid KCL + kcl_tridata_t *td, // valid pointer to first record + uint n // number of records to process +) +{ + DASSERT( td || !n ); + + #undef XPRINT + #define XPRINT noPRINT + + const bool pos_length = ( KCL_MODE & KCLMD_POSLEN ) != 0; + + for ( ; n-- > 0; td++ ) + { + if ( !( td->status & TD_INVALID_NORM ) || td->status & TD_REMOVED ) + continue; + + XPRINT("-----\n"); + XPRINT("PT0: %6.2f %6.2f %6.2f\n", td->pt[0].x, td->pt[0].y, td->pt[0].z ); + XPRINT("PT1: %6.2f %6.2f %6.2f\n", td->pt[1].x, td->pt[1].y, td->pt[1].z ); + XPRINT("PT2: %6.2f %6.2f %6.2f\n", td->pt[2].x, td->pt[2].y, td->pt[2].z ); + + double3 diff_2_0, diff_1_0, diff_1_2, normal[4]; + + Sub3(diff_2_0,td->pt[2],td->pt[0]); + Sub3(diff_1_0,td->pt[1],td->pt[0]); + Sub3(diff_1_2,td->pt[1],td->pt[2]); + XPRINT("D-10: %6.2f %6.2f %6.2f\n", diff_1_0.x, diff_1_0.y, diff_1_0.z ); + XPRINT("D-20: %6.2f %6.2f %6.2f\n", diff_2_0.x, diff_2_0.y, diff_2_0.z ); + XPRINT("D-12: %6.2f %6.2f %6.2f\n", diff_1_2.x, diff_1_2.y, diff_1_2.z ); + + CrossProd3( normal[0], diff_1_0, diff_2_0 ); Unit3(normal[0]); + CrossProd3( normal[1], normal[0], diff_2_0 ); Unit3(normal[1]); + CrossProd3( normal[2], diff_1_0, normal[0] ); Unit3(normal[2]); + CrossProd3( normal[3], normal[0], diff_1_2 ); Unit3(normal[3]); + + double length = DotProd3(diff_1_0,normal[3]); + if ( pos_length && length < 0.0 ) + { + length = -length; + Neg3(normal[3]); + } + + XPRINT("A: %6.2f %6.2f %6.2f\n",normal[1].x,normal[1].y,normal[1].z); + XPRINT("B: %6.2f %6.2f %6.2f\n",normal[2].x,normal[2].y,normal[2].z); + XPRINT("C: %6.2f %6.2f %6.2f\n",normal[3].x,normal[3].y,normal[3].z); + XPRINT("LEN: %6.2f\n",length); + + //--- now we assign the double values to the float members + + // need cast to avoid gcc warning 'above array bounds' + double *d = (double*)&normal->x; + float *f = td->normal->v; + float *f_end = (float*)( (u8*)f + sizeof(td->normal) ); + + if ( KCL_MODE & KCLMD_M_TINY ) + { + while ( f < f_end ) + *f++ = ldexp(round(ldexp(*d++,15)),-15); + } + else if ( KCL_MODE & KCLMD_ROUND ) + { + while ( f < f_end ) + *f++ = ldexp(round(ldexp(*d++,17)),-17); + } + else + while ( f < f_end ) + *f++ = *d++; + + td->length = length; + td->status &= ~TD_INVALID_NORM; + + if ( !isnormal(td->length) + || fabs(td->length) < opt_tri_height + || !isfinite(normal[0].x) + || !isfinite(normal[0].y) + || !isfinite(normal[0].z) + || !isfinite(normal[1].x) + || !isfinite(normal[1].y) + || !isfinite(normal[1].z) + || !isfinite(normal[2].x) + || !isfinite(normal[2].y) + || !isfinite(normal[2].z) + || !isfinite(normal[3].x) + || !isfinite(normal[3].y) + || !isfinite(normal[3].z) + ) + { + if (kcl) + { + PRINT("INVALID TRIANGLE #%u! [len=%5.3g]\n", + (int)( td - (kcl_tridata_t*)kcl->tridata.list ), td->length ); + if (!(td->status & TD_INVALID)) + kcl->tri_inv_count++; + } + td->status |= TD_INVALID; + td->length = 0.0; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcNormalsKCL +( + // calculate 'length' & 'normal' using 'pt' + // the calculation is only done, if '!kcl->norm_valid' + + kcl_t *kcl, // valid KCL + bool force // true: ignore 'kcl->norm_valid' +) +{ + DASSERT(kcl); + + if ( !kcl->norm_valid || force ) + { + CalcNormalsTriData( kcl, (kcl_tridata_t*)kcl->tridata.list, + kcl->tridata.used ); + kcl->norm_valid = true; + + #ifdef TEST0 + // verify valid triangels + PRINT(" verify valid triangels\n"); + CalcPointsTriData( (kcl_tridata_t*)kcl->tridata.list, + kcl->tridata.used, 0,false ); + #endif + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +void RoundPointsKCL +( + // round the normal values + + kcl_t *kcl, // valid KCL + int round_pow2 // round to '1/(2^round_pow2)' +) +{ + DASSERT(kcl); + + uint n = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + + for ( ; n-- > 0; td++ ) + { + double *d = &td->pt->x; + uint i; + for ( i = 0; i < sizeof(td->pt)/sizeof(*d); i++, d++ ) + *d = ldexp(round(ldexp(*d,round_pow2)),-round_pow2); + td->status |= TD_INVALID_NORM; + } + + CalcNormalsKCL(kcl,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +void RoundNormalsKCL +( + // round the normal values + + kcl_t *kcl, // valid KCL + int round_pow2 // round to '1/(2^round_pow2)' +) +{ + DASSERT(kcl); + + uint n = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + + if (!kcl->norm_valid) + { + CalcNormalsTriData(kcl,td,n); + kcl->norm_valid = true; + } + + for ( ; n-- > 0; td++ ) + { + float *f = &td->normal->x; + uint i; + for ( i = 0; i < sizeof(td->normal)/sizeof(*f); i++, f++ ) + *f = ldexpf(roundf(ldexpf(*f,round_pow2)),-round_pow2); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *GetTridataKCL +( + kcl_t *kcl, // NULL or valid kcl + uint tri_index // triangle index +) +{ + return kcl && tri_index < kcl->tridata.used + ? (kcl_tridata_t*)kcl->tridata.list + tri_index + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// append triangles /////////////// +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t * PrepareAppendTrianglesKCL +( + // Grows the triangle field and the 'used' count. + // Returns a pointer to the first triangle. + // If to many triangles: + // => a warning is printed, but only once for a kcl. NULL is returned + + kcl_t *kcl, // valid KCL data structure + uint n_append // number of triangles to append +) +{ + DASSERT(kcl); + if ( kcl->no_limit || n_append + kcl->tridata.used <= KCL_MAX_TRIANGLES ) + { + kcl_tridata_t *td = GrowListSize(&kcl->tridata,n_append,1000); + DASSERT(td); + memset(td,0,n_append*sizeof(*td)); + return td; + } + + if (!kcl->limit_warning) + { + kcl->limit_warning = true; + ERROR0(ERR_SEMANTIC, + "Can't add new triangles, because limit (%u) reached!", + KCL_MAX_TRIANGLES ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendTriangleKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3 // pointer to point #3 +) +{ + DASSERT(kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + return AppendTriangleKCL(&par,pt1,pt2,pt3); +} + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTriangleKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3 // pointer to point #3 +) +{ + DASSERT(par); + DASSERT(par->kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(par->kcl,1); + if (td) + { + memcpy(td->pt+0,pt1,sizeof(*td->pt)); + memcpy(td->pt+1,pt2,sizeof(*td->pt)); + memcpy(td->pt+2,pt3,sizeof(*td->pt)); + td->in_flag = td->cur_flag = par->kcl_flag; + td->status = TD_ADDED|TD_INVALID_NORM; + par->kcl->model_modified = true; + + #if SUPPORT_KCL_DELTA + { + float d1 = LengthD(pt1->v,pt2->v); + float d2 = LengthD(pt2->v,pt3->v); + float d3 = LengthD(pt3->v,pt1->v); + float dmin, dmax; + if ( d1 < d2 ) + { + dmin = d1; + dmax = d2; + } + else + { + dmin = d2; + dmax = d1; + } + td->delta_min = dmin < d3 ? dmin : d3; + td->delta_max = dmax > d3 ? dmax : d3; + } + #endif + + if (par->norm) + { + const double norm_len = Length3(*par->norm); + if ( norm_len > 1e-3 ) + { + // first calc the normal of the direction + + double3 norm_unit; + Div3f(norm_unit,*par->norm,norm_len); + PRINT("NORM/DIR: %8.5f %8.5f %8.5f | %11.3f\n", + norm_unit.x, norm_unit.y, norm_unit.z, norm_len ); + + // now calc a normal by the points + + double3 diff_2_0, diff_1_0, pt_norm; + Sub3(diff_2_0,td->pt[2],td->pt[0]); + Sub3(diff_1_0,td->pt[1],td->pt[0]); + CrossProd3(pt_norm,diff_1_0,diff_2_0); + Unit3(pt_norm); + PRINT("NORM/PTS: %8.5f %8.5f %8.5f\n", + pt_norm.x, pt_norm.y, pt_norm.z ); + + // comparing + double dp = DotProd3(norm_unit,pt_norm); + if ( dp < -0.2 ) + { + PRINT(" -> SWAP POINTS, dp=%7.5f\n",dp); + memcpy(td->pt+1,pt3,sizeof(*td->pt)); + memcpy(td->pt+2,pt2,sizeof(*td->pt)); + par->swap_count++; + } + } + } + + #if SUPPORT_KCL_XTRIDATA + if (opt_xtridata) + { + CopyTriData_T2D(&td->dbl,td); + if (par->is_orig) + { + td->status |= TDX_ORIG_PT; + memcpy(&td->orig,&td->dbl,sizeof(td->orig)); + } + } + #endif + +// [[tridata]] [[2do]] ??? drop invalid triangles + } + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendQuadrilateralKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3, // pointer to point #3 + const double3 *pt4 // pointer to point #4 +) +{ + DASSERT(kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + DASSERT(pt4); + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + return AppendQuadrilateralKCL(&par,pt1,pt2,pt3,pt4); +} + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendQuadrilateralKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3, // pointer to point #3 + const double3 *pt4 // pointer to point #4 +) +{ + DASSERT(par); + DASSERT(par->kcl); + DASSERT(pt1); + DASSERT(pt2); + DASSERT(pt3); + DASSERT(pt4); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(par->kcl,2); + if (!td) + return 0; + par->kcl->tridata.used -= 2; + + if ( Length2D(pt1->v,pt3->v) <= 1.1 * Length2D(pt2->v,pt4->v) ) + { + // this variant is prioritized to force + // 'pt1' as vertex at nearly same length + + AppendTriangleKCL( par, pt1, pt2, pt3 ); + AppendTriangleKCL( par, pt1, pt3, pt4 ); + } + else + { + AppendTriangleKCL( par, pt2, pt4, pt1 ); + AppendTriangleKCL( par, pt2, pt3, pt4 ); + } + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendTrianglesKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to first point + int pt_delta, // delta in bytes to next point, <0 possible + uint n_pt // number of points +) +{ + DASSERT(kcl); + DASSERT(pt1); + DASSERT( pt_delta >= sizeof(double3) || pt_delta <= -(int)sizeof(double3) ); + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + return AppendTrianglesKCL(&par,pt1,pt_delta,n_pt); +} + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTrianglesKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to first point + int pt_delta, // delta in bytes to next point, <0 possible + uint n_pt // number of points +) +{ + DASSERT(par); + DASSERT(par->kcl); + DASSERT(pt1); + DASSERT( pt_delta >= sizeof(double3) || pt_delta <= -(int)sizeof(double3) ); + + if ( n_pt <= 4 ) + return n_pt == 3 + ? AppendTriangleKCL( par, + pt1, + (double3*)((u8*)pt1 + pt_delta), + (double3*)((u8*)pt1 + 2*pt_delta) ) + : n_pt == 4 + ? AppendQuadrilateralKCL( + par, + pt1, + (double3*)((u8*)pt1 + pt_delta), + (double3*)((u8*)pt1 + 2*pt_delta), + (double3*)((u8*)pt1 + 3*pt_delta) ) + : 0; + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(par->kcl,n_pt-2); + if (!td) + return 0; + par->kcl->tridata.used -= n_pt-2; + + + //--- setup internal data structure + + struct tab_t + { + double3 *pt; // pointer to coordinates + double len; // distance to point+2 (square!) + }; + + struct tab_t *tab, tab_buf[KCL_MAX_PT_PER_TRI]; + if ( n_pt <= KCL_MAX_PT_PER_TRI ) + tab = tab_buf; + else + tab = MALLOC(n_pt*sizeof(*tab)); // this function allows more points + + int i; + for ( i = 0; i < n_pt; i++ ) + tab[i].pt = (double3*)( (u8*)pt1 + i * pt_delta ); + + + //--- polygons with >= 5 verteces + + DASSERT( n_pt >= 5 ); + // init differences + for ( i = 0; i < n_pt; i++ ) + tab[i].len = Length2D(tab[i].pt->v,tab[(i+2)%n_pt].pt->v); + + + //--- main loop + + while ( n_pt > 3 ) + { + // find min distance + uint found = 0; + double min = tab[0].len; + for ( i = 1; i < n_pt; i++ ) + if ( min > tab[i].len ) + { + min = tab[i].len; + found = i; + } + uint found1 = ( found + 1 ) % n_pt; + + AppendTriangleKCL(par, + tab[found].pt, + tab[found1].pt, + tab[(found+2)%n_pt].pt + ); + + //--- remove the mid point and fix the table + + n_pt--; + memmove( tab+found1, tab+found1+1, (n_pt-found1)*sizeof(*tab) ); + tab[found].len = Length2D(tab[found].pt->v,tab[(found+2)%n_pt].pt->v); + found = ( found + n_pt - 1 ) % n_pt; + tab[found].len = Length2D(tab[found].pt->v,tab[(found+2)%n_pt].pt->v); + } + + + //--- add the last triangle + + DASSERT( n_pt == 3 ); + AppendTriangleKCL(par, tab[0].pt, tab[1].pt, tab[2].pt ); + + + //--- terminate + + if ( tab != tab_buf ) + FREE(tab); + return td; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendOctahedronKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + octahedron_t *oct // valid octahedron data +) +{ + DASSERT(kcl); + DASSERT(oct); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,8); + if (!td) + return 0; + kcl->tridata.used -= 8; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + AppendTriangleKCL( &par, &oct->x1, &oct->z1, &oct->y1 ); + AppendTriangleKCL( &par, &oct->z1, &oct->x2, &oct->y1 ); + AppendTriangleKCL( &par, &oct->x2, &oct->z2, &oct->y1 ); + AppendTriangleKCL( &par, &oct->z2, &oct->x1, &oct->y1 ); + + AppendTriangleKCL( &par, &oct->x1, &oct->z2, &oct->y2 ); + AppendTriangleKCL( &par, &oct->z2, &oct->x2, &oct->y2 ); + AppendTriangleKCL( &par, &oct->x2, &oct->z1, &oct->y2 ); + AppendTriangleKCL( &par, &oct->z1, &oct->x1, &oct->y2 ); + + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendCuboidKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + cuboid_t *c // valid cuboid data +) +{ + DASSERT(kcl); + DASSERT(c); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,12); + if (!td) + return 0; + kcl->tridata.used -= 12; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + AppendQuadrilateralKCL( &par, &c->p000, &c->p010, &c->p011, &c->p001 ); + AppendQuadrilateralKCL( &par, &c->p000, &c->p001, &c->p101, &c->p100 ); + AppendQuadrilateralKCL( &par, &c->p000, &c->p100, &c->p110, &c->p010 ); + AppendQuadrilateralKCL( &par, &c->p111, &c->p101, &c->p001, &c->p011 ); + AppendQuadrilateralKCL( &par, &c->p111, &c->p110, &c->p100, &c->p101 ); + AppendQuadrilateralKCL( &par, &c->p111, &c->p011, &c->p010, &c->p110 ); + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendPyramidKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *apex, // the apex of the pyramid + const double3 *pt1, // pointer to first point + uint pt_delta, // delta in bytes to next point + uint n_pt, // number of points + bool reverse // true: reverse face orientation +) +{ + DASSERT(kcl); + DASSERT(apex); + DASSERT(pt1); + DASSERT(n_pt>=2); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,n_pt); + if (!td) + return 0; + kcl->tridata.used -= n_pt; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + if (reverse) + { + int i; + const double3 *pt = pt1; + for ( i = n_pt-1; i >= 0; i-- ) + { + const double3 *next = (double3*)((u8*)pt1 + i*pt_delta); + AppendTriangleKCL( &par, apex, pt, next ); + pt = next; + } + } + else + { + int i; + const double3 *pt = (double3*)((u8*)pt1 + (n_pt-1)*pt_delta); + for ( i = 0; i < n_pt; i++ ) + { + const double3 *next = (double3*)((u8*)pt1 + i*pt_delta); + noPRINT("%6.1f %6.1f %6.1f / %6.1f %6.1f %6.1f / %6.1f %6.1f %6.1f\n", + apex->x, apex->y, apex->z, + pt->x, pt->y, pt->z, + next->x, next->y, next->z ); + AppendTriangleKCL( &par, apex, pt, next ); + pt = next; + } + } + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t *AppendPrismKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *axis1, // first axis point + const double3 *axis2, // second axis point + const double3 *dir, // NULL or direction of first edge + double r, // radius + uint n_side, // number of sides, max=100 + uint mode, // bitfield: 0=prism, 1=antiprism + u32 base_flag, // color of base, -1:no base + double base_height, // height of base, truncated to 40% of length + // if 0.0: use a plane, otherwise a pyramid + u32 arrow_flag, // color of arror peak at axis2, -1:no arrow + double arrow_size, // relative size of arrow (multiplied by *r), + // truncated to length. no arrow if <=0.0 + bool arrow_flip // true: flip direction of arrow +) +{ + DASSERT(kcl); + DASSERT(axis1); + DASSERT(axis2); + + const uint max_side = 100; + if ( n_side < 2 ) + n_side = 2; + else if ( n_side > max_side ) + n_side = max_side; + + const uint n_tri = n_side * ( base_flag == M1(base_flag) ? 2 : 4 ); + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,n_tri); + if (!td) + return 0; + kcl->tridata.used -= n_tri; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + //--- calculations + + double3 n[3]; + double prism_len = CalcNormals(n,axis1,axis2,dir,r); + if (!prism_len) + return 0; + + if ( base_height > 0.4 * prism_len ) + base_height = 0.4 * prism_len; + base_height /= r; + + const bool draw_arrow = arrow_flag != M1(arrow_flag) && arrow_size > 0.0; + double arrow_height; + if (draw_arrow) + { + arrow_height = 1.5; + const double max = prism_len / (arrow_height*r) - 1.0; + if ( arrow_size > max ) + arrow_size = max; + } + else + arrow_height = base_height; + + double3 base1, base2; + base1.x = axis1->x + n[0].x * base_height; + base1.y = axis1->y + n[0].y * base_height; + base1.z = axis1->z + n[0].z * base_height; + base2.x = axis2->x - n[0].x * arrow_height; + base2.y = axis2->y - n[0].y * arrow_height; + base2.z = axis2->z - n[0].z * arrow_height; + + double3 tab1[max_side+1], tab2[max_side+1]; + double3 *p1 = tab1, *p2 = tab2; + + + //--- calculate the points and print the faces + + if ( mode & 1 ) + { + // anti prism + + const double add_rad = M_PI / n_side; + double rad = 0.0; + + uint i; + for ( i = 0; i < n_side; i++ ) + { + double fcos, fsin; + + fcos = cos(rad); + fsin = sin(rad); + rad += add_rad; + p1->x = base1.x + fcos * n[1].x + fsin * n[2].x; + p1->y = base1.y + fcos * n[1].y + fsin * n[2].y; + p1->z = base1.z + fcos * n[1].z + fsin * n[2].z; + p1++; + + fcos = cos(rad); + fsin = sin(rad); + rad += add_rad; + p2->x = base2.x + fcos * n[1].x + fsin * n[2].x; + p2->y = base2.y + fcos * n[1].y + fsin * n[2].y; + p2->z = base2.z + fcos * n[1].z + fsin * n[2].z; + p2++; + } + *p1 = *tab1; + *p2 = *tab2; + + for ( i = 0, p1 = tab1, p2 = tab2; i < n_side; i++, p1++, p2++ ) + { + AppendTriangleKCL( &par, p1, p1+1, p2 ); + AppendTriangleKCL( &par, p2, p1+1, p2+1 ); + } + } + else + { + const double rad_f = 2*M_PI / n_side; + + uint i; + for ( i = 0; i < n_side; i++, p1++, p2++ ) + { + const double rad = i * rad_f; + const double fcos = cos(rad); + const double fsin = sin(rad); + noPRINT("ROT=%6.2f, COS=%6.3f, SIN=%6.3f\n", rad * (180/M_PI), fcos, fsin ); + double a; + a = fcos * n[1].x + fsin * n[2].x; p1->x = base1.x + a; p2->x = base2.x + a; + a = fcos * n[1].y + fsin * n[2].y; p1->y = base1.y + a; p2->y = base2.y + a; + a = fcos * n[1].z + fsin * n[2].z; p1->z = base1.z + a; p2->z = base2.z + a; + if ( i > 0 ) + AppendQuadrilateralKCL( &par, p1-1, p1, p2, p2-1 ); + } + AppendQuadrilateralKCL( &par, p1-1, tab1, tab2, p2-1 ); + } + + //--- print base pyramids + + if ( base_flag != M1(base_flag) ) + { + if ( fabs(base_height) < 1e-9 ) + { + AppendTrianglesKCL( &par, tab1 + n_side - 1, + -(int)sizeof(*tab1), n_side ); + if (!draw_arrow) + AppendTrianglesKCL( &par, tab2, sizeof(*tab2), n_side ); + } + else + { + AppendPyramidKCL(kcl,base_flag,axis1,tab1,sizeof(*tab1),n_side,true); + if (!draw_arrow) + AppendPyramidKCL(kcl,base_flag,axis2,tab2,sizeof(*tab2),n_side,false); + } + } + + if (draw_arrow) + { + if (arrow_flip) + { + // ??? + } + else + { + uint i; + for ( i = 0, p2 = tab2; i < n_side; i++, p2++ ) + { + p2->x += ( p2->x - axis2->x ) * arrow_size; + p2->y += ( p2->y - axis2->y ) * arrow_size; + p2->z += ( p2->z - axis2->z ) * arrow_size; + } + AppendPyramidKCL(kcl,arrow_flag,axis2,tab2,sizeof(*tab2),n_side,true); + AppendPyramidKCL(kcl,arrow_flag,axis2,tab2,sizeof(*tab2),n_side,false); + } + } + + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t * AppendCylinderKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pos, // base point of cylinder + const double3 *p_scale, // scale. If NULL: assume v(1,1,1) + double scale_factor, // Scale is multiplied by this factor + const double3 *rotate, // NULL or rotation vector + uint n_side, // number of sides, max=100 + u32 base_flag // color of bases, -1:no base +) +{ + DASSERT(kcl); + DASSERT(pos); + + const uint max_side = 100; + if ( n_side < 2 ) + n_side = 2; + else if ( n_side > max_side ) + n_side = max_side; + + const uint n_tri = n_side * ( base_flag == M1(base_flag) ? 2 : 4 ); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,n_tri); + if (!td) + return 0; + kcl->tridata.used -= n_tri; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + + //--- setup pt list + + const uint n_used = 2*n_side+2; + double3 pt[2*max_side+2], scale; + double3 *tab0 = pt, *tab1 = tab0 + n_side+1; + + scale.x = scale.y = scale.z = scale_factor; + if (p_scale) + { + scale.x *= p_scale->x; + scale.y *= p_scale->y; + scale.z *= p_scale->z; + } + const double ht = 2*scale.y; + + double3 *p0 = tab0, *p1 = tab1; + const double add_rad = M_PI / n_side; + double rad = 0.0; + + uint i; + for ( i = 0; i < n_side; i++ ) + { + p0->x = scale.x * cos(rad); + p0->y = 0.0; + p0->z = scale.z * sin(rad); + p0++; + rad += add_rad; + + p1->x = scale.x * cos(rad); + p1->y = ht; + p1->z = scale.z * sin(rad); + p1++; + rad += add_rad; + } + *p0 = *tab0; + *p1 = *tab1; + + + //--- calculations + + if (rotate) + Rotate(0,rotate,pt,sizeof(*pt),n_used); + + Translate(pos,pt,sizeof(*pt),n_used); + + + //--- draw + + for ( i = 0, p0 = tab0, p1 = tab1; i < n_side; i++, p0++, p1++ ) + { + AppendTriangleKCL( &par, p0+1, p0, p1 ); + AppendTriangleKCL( &par, p0+1, p1, p1+1 ); + } + + if ( base_flag != M1(base_flag) ) + { + InitializeTriParam(&par,kcl,base_flag); + AppendTrianglesKCL(&par,tab0,sizeof(*tab0),n_side); + AppendTrianglesKCL(&par,tab1+n_side-1,-(int)sizeof(*tab1),n_side); + } + + return td; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t * AppendJoistKCL +( + kcl_t * kcl, // pointer to valid KCL + u32 kcl_flag, // parameter for AppendTrianglesKCLp() + double length, // length of corpus + const double3 *p1, // first point + const double3 *p2, // second point + const double3 *p3, // helper point to find direction + int n_mark, // >0: add marker representing the number (modulo) + u32 mark5_flag // >0: replace 5 marker by one of this color +) +{ + DASSERT(kcl); + DASSERT(p1); + DASSERT(p2); + DASSERT(p3); + + kcl_tridata_t *td = PrepareAppendTrianglesKCL(kcl,6); + if (!td) + return 0; + kcl->tridata.used -= 6; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,kcl_flag); + + + //--- calculate joist + + double3 d13, n12, n13, nx; + Sub3(n12,*p2,*p1); + Unit3(n12); + Sub3(d13,*p3,*p1); + CrossProd3(nx,n12,d13); + Unit3(nx); + CrossProd3(n13,nx,n12); + + double3 p1a, p1b, p2a, p2b; + uint p; + for ( p = 0; p < 3; p++ ) + { + const double d12 = length * n12.v[p]; + const double d13 = length * n13.v[p]; + const double dx = length * nx.v[p] * 0.3; + p1a.v[p] = p1->v[p] + d12 + d13 - dx; + p1b.v[p] = p1->v[p] + d12 + d13 + dx; + p2a.v[p] = p2->v[p] - d12 + d13 - dx; + p2b.v[p] = p2->v[p] - d12 + d13 + dx; + } + + + //--- draw joist + + AppendQuadrilateralKCL( &par, p1, &p1a, &p2a, p2 ); + AppendQuadrilateralKCL( &par, &p1a, &p1b, &p2b, &p2a ); + AppendQuadrilateralKCL( &par, &p1b, p1, p2, &p2b ); + + if ( n_mark <= 0 ) + return td; + + + //--- calc marker counter + + const uint n_mark5 = mark5_flag > 0 ? n_mark/5 : 0; + n_mark -= n_mark5 * 4; + + + //--- calc marker basics + + length *= 2; + + double3 pt1, pt2, delta[4]; + for ( p = 0; p < 3; p++ ) + { + const double d12 = length * n12.v[p]; + const double d13 = length * n13.v[p]; + const double dx = length * nx.v[p]; + + pt1.v[p] = p1->v[p] + d12 + 0.25 * d13; + pt2.v[p] = p2->v[p] - d12 + 0.25 * d13; + + delta[0].v[p] = -d13 - dx; + delta[1].v[p] = -d13 + dx; + delta[2].v[p] = d13 + dx; + delta[3].v[p] = d13 - dx; + noPRINT("DELTA[%u]: %6.1f %6.1f %6.1f %6.1f\n", + p, delta[0].v[p], delta[1].v[p], delta[2].v[p], delta[3].v[p] ); + } + + + //--- calc axis positions + + const double factor = 2 * n_mark + 0.51; + + double dist = LengthD(pt1.v,pt2.v); + double need = length * factor; + if ( need <= dist ) + { + need = (1.0-need/dist)/2; + for ( p = 0; p < 3; p++ ) + { + double d = ( pt2.v[p] - pt1.v[p] ) * need; + pt1.v[p] += d; + pt2.v[p] -= d; + } + } + + double3 dx; + dx.x = ( pt2.x - pt1.x ) / factor; + dx.y = ( pt2.y - pt1.y ) / factor; + dx.z = ( pt2.z - pt1.z ) / factor; + + + //--- append markers + + uint i; + for ( i = 0; i < n_mark; i++ ) + { + if ( i < n_mark5 ) + { + double3 base1[4], base2[4]; + for ( p = 0; p < 3; p++ ) + { + pt2.v[p] = pt1.v[p] + 0.5 * dx.v[p]; + pt1.v[p] = pt2.v[p] + 1.5 * dx.v[p]; + + uint j; + for ( j = 0; j < 4; j++ ) + { + base1[j].v[p] = pt1.v[p] + delta[j].v[p]; + base2[j].v[p] = pt2.v[p] + delta[j].v[p]; + } + } + + AppendPyramidKCL(kcl,mark5_flag,&pt2,base1,sizeof(double3),4,true); + AppendPyramidKCL(kcl,mark5_flag,&pt1,base2,sizeof(double3),4,true); + } + else + { + double3 base[4]; + for ( p = 0; p < 3; p++ ) + { + pt2.v[p] = pt1.v[p] + 0.5 * dx.v[p]; + pt1.v[p] = pt2.v[p] + 1.5 * dx.v[p]; + + const double base1 = ( pt1.v[p] + pt2.v[p] ) / 2; + uint j; + for ( j = 0; j < 4; j++ ) + base[j].v[p] = base1 + delta[j].v[p]; + } + + AppendPyramidKCL(kcl,kcl_flag,&pt2,base,sizeof(double3),4,false); + AppendPyramidKCL(kcl,kcl_flag,&pt1,base,sizeof(double3),4,true); + } + } + + return td; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// statistics /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetStatisticsKCL ( kcl_t * kcl ) +{ + DASSERT(kcl); + + FREE(kcl->o_depth); + FREE(kcl->o_count); + FREE(kcl->l_count); + + u8 *stat_beg = (u8*)&kcl->stat_valid; + u8 *stat_end = (u8*)kcl + sizeof(*kcl); + memset(stat_beg,0,stat_end-stat_beg); + + DASSERT(!kcl->stat_valid); + DASSERT(!kcl->o_depth); + DASSERT(!kcl->o_count); + DASSERT(!kcl->l_count); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u32 calc_stat_oct +( + // returns next offset + + kcl_t *kcl, // valid KCL + u32 offset, // offset check + uint n // number of links to check +) +{ + DASSERT(kcl); + noPRINT("calc_stat_oct: off=%x, n=%x\n",offset,n); + + if ( offset >= kcl->octree_size ) + return kcl->octree_size; + const u32 baseoff = offset; + + const uint max_n = ( kcl->octree_size - offset ) / 4; + if ( n > max_n ) + n = max_n; + + while ( n-- > 0 ) + { + u32 val = be32(kcl->octree+offset); + offset += 4; + + if ( val & 0x80000000 ) + { + val = ( val & 0x7ffffff ) + baseoff + 2; + if ( kcl->min_l_offset > val ) + kcl->min_l_offset = val; + if ( kcl->max_l_offset < val ) + kcl->max_l_offset = val; + } + else + { + val += baseoff + 0x20; + if ( kcl->max_o_offset < val ) + kcl->max_o_offset = val; + } + } + return offset; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u32 calc_stat_octlist +( + // returns next offset + + kcl_t *kcl, // valid KCL + u32 offset, // offset check + uint n, // number of links to check + uint depth // current depth +) +{ + DASSERT(kcl); + noPRINT("calc_stat_oct: off=%x, n=%x\n",offset,n); + + if ( offset >= kcl->octree_size ) + return kcl->octree_size; + const u32 baseoff = offset; + + const uint max_n = ( kcl->octree_size - offset ) / 4; + if ( n > max_n ) + n = max_n; + + while ( n-- > 0 ) + { + u32 val = be32(kcl->octree+offset); + offset += 4; + + if ( val & 0x80000000 ) + { + u32 off = ( val & 0x7ffffff ) + baseoff + 2; + val = ( off - kcl->min_l_offset ) / 2; + if ( val < kcl->n_trilist ) + { + u32 len = 0; + while (be16(kcl->octree+off)) + off+=2, len++; + if (len) + { + kcl->sum_tri_len += len; + if ( kcl->max_tri_len < len ) + kcl->max_tri_len = len; + if ( depth < KCL_MAX_STAT_DEPTH + && kcl->max_depth_tri_len[depth] < len ) + { + kcl->max_depth_tri_len[depth] = len; + } + kcl->n_tri_link++; + if (!kcl->l_count[val]++) + kcl->n_tri_list++; + } + else + { + kcl->n_0_link++; + if (!kcl->l_count[val]++) + kcl->n_0_list++; + } + } + } + else + { + val = ( val + baseoff - kcl->min_o_offset ) / 0x20; + if ( val < kcl->n_cube_nodes ) + { + kcl->o_count[val]++; + if ( kcl->o_depth[val] < depth ) + kcl->o_depth[val] = depth; + } + } + } + return offset; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CalcStatisticsKCL +( + kcl_t *kcl // valid KCL +) +{ + DASSERT(kcl); + + if (kcl->stat_valid) + return; + + DASSERT(kcl); + ResetStatisticsKCL(kcl); + KCL_ACTION_LOG("CalcStatisticsKCL()\n"); + CreateOctreeKCL(kcl); + + + //--- setup + + kcl->hash_bits = 0; + kcl->total_bcubes = 1; + kcl->bcube_width = 1 << kcl->coord_rshift; + + + //--- axis dependent calculations + + uint p; + for ( p = 0; p < 3; p++ ) + { + u32 val = kcl->mask[p]; + uint count; + if (!val) + count = 32; + else + { + count = 0; + for ( ; !(val&1); val >>= 1 ) + count++; + } + kcl->mask_bits[p] = count; + count = count > kcl->coord_rshift ? count - kcl->coord_rshift : 0; + kcl->bcube_bits[p] = count; + kcl->hash_bits += count; + kcl->n_bcubes[p] = 1 << count; + kcl->total_bcubes *= 1 << count; + kcl->max_octree.v[p] = kcl->min_octree.v[p] + + kcl->bcube_width * kcl->n_bcubes[p]; + } + + //--- logging + + noPRINT("KCL BASE STATS:\n" + "\t\tH.mask: 0x%08x 0x%08x 0x%08x\n" + "\t\tH.shift: %11u %11u %11u\n" + "\t\tS.mask_bits: %11u %11u %11u\n" + "\t\tS.bcube_bits: %11u %11u %11u =>%5u\n" + "\t\tS.n_bcubes: %11u %11u %11u =>%5u\n" + "\t\tS.bcube_width: %19u\n" + "\t\tH.min_octree: %11.3f %11.3f %11.3f\n" + "\t\tS.max_octree: %11.3f %11.3f %11.3f\n" + "\t\t---\n", + kcl->mask[0], kcl->mask[1], kcl->mask[2], + kcl->coord_rshift, kcl->y_lshift, kcl->z_lshift, + kcl->mask_bits[0], kcl->mask_bits[1], kcl->mask_bits[2], + kcl->bcube_bits[0], kcl->bcube_bits[1], kcl->bcube_bits[2], kcl->hash_bits, + kcl->n_bcubes[0], kcl->n_bcubes[1], kcl->n_bcubes[2], kcl->total_bcubes, + kcl->bcube_width, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + kcl->max_octree.x, kcl->max_octree.y, kcl->max_octree.z + ); + + + //--- octree setup + + kcl->max_o_offset = kcl->total_bcubes * 4; + kcl->max_l_offset = 0; + kcl->min_l_offset = kcl->octree_size; + + u32 offset = calc_stat_oct(kcl,0,kcl->total_bcubes); + kcl->min_o_offset = offset; + while ( offset < kcl->max_o_offset && offset < kcl->octree_size ) + offset = calc_stat_oct(kcl,offset,8); + + kcl->n_cube_nodes = ( kcl->max_o_offset - kcl->min_o_offset ) / 0x20; + kcl->n_trilist = ( kcl->octree_size - kcl->min_l_offset ) / 2 + 1; + + + //--- fix max_l_offset to end of list + + offset = kcl->max_l_offset; + while ( offset < kcl->octree_size ) + { + const u16 val = be16(kcl->octree+offset); + offset += 2; + if (!val) + break; + } + kcl->max_l_offset = offset; + + + //--- logging + + noPRINT("KCL OCTREE STATS:\n" + "\t\tS.o_offset: %#9x .. %#9x, N:%8u\n" + "\t\tS.l_offset: %#9x .. %#9x, N:%8u\n" + "\t\t---\n", + kcl->min_o_offset, kcl->max_o_offset, kcl->n_cube_nodes, + kcl->min_l_offset, kcl->max_l_offset, kcl->n_trilist ); + + + //--- setup octree lists + + FREE(kcl->o_depth); + FREE(kcl->o_count); + FREE(kcl->l_count); + kcl->o_depth = CALLOC(kcl->n_cube_nodes,sizeof(*kcl->o_depth)); + kcl->o_count = CALLOC(kcl->n_cube_nodes,sizeof(*kcl->o_count)); + kcl->l_count = CALLOC(kcl->n_trilist,sizeof(*kcl->l_count)); + + kcl->n_0_list = 0; + kcl->n_0_link = 0; + kcl->n_tri_list = 0; + kcl->n_tri_link = 0; + kcl->max_tri_len = 0; + kcl->sum_tri_len = 0; + memset(kcl->max_depth_tri_len,0,sizeof(kcl->max_depth_tri_len)); + + + //--- fill octree lists + + uint max_depth = 0, sum_depth = 0; + offset = calc_stat_octlist(kcl,0,kcl->total_bcubes,1); + + while ( offset < kcl->max_o_offset && offset < kcl->octree_size ) + { + const uint depth = kcl->o_depth[(offset-kcl->min_o_offset)/0x20]; + sum_depth += 7*depth + 1; + if ( max_depth < depth ) + max_depth = depth; + offset = calc_stat_octlist(kcl,offset,8,depth+1); + } + kcl->max_depth = max_depth; + kcl->total_cubes = kcl->n_cube_nodes * 8 + kcl->total_bcubes; + kcl->ave_depth = (double)sum_depth/kcl->total_cubes; + + kcl->min_depth = 0; + uint d; + for ( d = 0; d < KCL_MAX_STAT_DEPTH; d++ ) + if ( kcl->max_depth_tri_len[d] ) + { + kcl->min_depth = d - 1; + break; + } + + + //--- logging + + PRINT("KCL OCTREE STATS: 0=%u,%u, tri=%u,%u, len=%u,%u, depth=%u..%u\n", + kcl->n_0_list, kcl->n_0_link, + kcl->n_tri_list, kcl->n_tri_link, + kcl->max_tri_len, kcl->sum_tri_len, + kcl->min_depth, kcl->max_depth ); + + + //--- setup triangle usage + + const int n_tri = kcl->tridata.used; + u8 *tri_used = CALLOC(n_tri,sizeof(*tri_used)); + kcl->n_invalid_tri_ref = 0; + kcl->n_tri_used = 0; + + + //--- calc triangle usage + + noPRINT("OFF: %x .. %x\n",kcl->min_l_offset,kcl->max_l_offset); + for ( offset = kcl->min_l_offset; offset < kcl->max_l_offset; offset += 2 ) + { + uint val = be16( kcl->octree + offset ); + if (val) + { + if ( --val >= n_tri ) + kcl->n_invalid_tri_ref++; + else if (!tri_used[val]) + { + tri_used[val] = 1; + kcl->n_tri_used++; + } + } + } + + + //--- set TD_UNUSED + + kcl->used_valid = true; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + uint i; + for ( i = 0; i < n_tri; i++, td++ ) + if (tri_used[i]) + td->status &= ~TD_UNUSED; + else + td->status |= TD_UNUSED; + + FREE(tri_used); + + + //--- terminate & logging + + kcl->stat_valid = true; + noPRINT("KCL TRIANGLE STATS: used=%u/%u, invalid link=%u\n", + kcl->n_tri_used, n_tri, kcl->n_invalid_tri_ref ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CalcMinMaxKCL +( + kcl_t * kcl // KCL data structure +) +{ + DASSERT(kcl); + + if (!kcl->min_max_valid) + { + KCL_ACTION_LOG("CalcMinMaxKCL()\n"); + kcl->min_max_valid = true; + + const uint n_tri = kcl->tridata.used; + if (n_tri) + { + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + const kcl_tridata_t *td_end = td + n_tri; + + memcpy(&kcl->min,td->pt,sizeof(kcl->min)); + memcpy(&kcl->max,td->pt,sizeof(kcl->max)); + double tmin = 1e99; + double tmax = 0.0; + double3 sum; + sum.x = sum.y = sum.z = 0.0; + + for ( ; td < td_end; td++ ) + { + const double3 *d3 = td->pt; + MinMax3(&kcl->min,&kcl->max,d3,3); + + sum.x += d3[0].x + d3[1].x + d3[2].x; + sum.y += d3[0].y + d3[1].y + d3[2].y; + sum.z += d3[0].z + d3[1].z + d3[2].z; + + double dist; + double3 diff; + + Sub3(diff,d3[0],d3[1]); + dist = LengthSqare3(diff); + if ( tmin > dist ) tmin = dist; + if ( tmax < dist ) tmax = dist; + + Sub3(diff,d3[0],d3[2]); + dist = LengthSqare3(diff); + if ( tmin > dist ) tmin = dist; + if ( tmax < dist ) tmax = dist; + + Sub3(diff,d3[1],d3[2]); + dist = LengthSqare3(diff); + if ( tmin > dist ) tmin = dist; + if ( tmax < dist ) tmax = dist; + } + kcl->tmin_dist = sqrt(tmin); + kcl->tmax_dist = sqrt(tmax); + + const double factor = 1 / ( n_tri * 3.0 ); + kcl->mean.x = sum.x * factor; + kcl->mean.y = sum.y * factor; + kcl->mean.z = sum.z * factor; + } + else + { + memset(&kcl->min,0,sizeof(kcl->min)); + memset(&kcl->max,0,sizeof(kcl->max)); + memset(&kcl->mean,0,sizeof(kcl->max)); + kcl->tmin_dist = 0.0; + kcl->tmax_dist = 0.0; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateOctreeKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[oct_info_t]] + +typedef struct oct_info_t +{ + kcl_t *kcl; // current KCL + uint n_tri; // number of triangles + kcl_tri_t *tri; // triangle values + #if SUPPORT_KCL_CUBE + kcl_cube_list_t *cubelist; // NULL or a cube list for each 'tri' + #endif + + u32 *olist; // octree list + uint oused; // number of used elements in 'olist' + uint osize; // number of alloced elements in 'olist' + + u16 *tlist; // triangle list + uint tused; // number of used elements in 'tlist' + uint tsize; // number of alloced elements in 'tlist' + +} oct_info_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void setup_oct_tri +( + kcl_t *kcl, // valid kcl + kcl_tri_t *tri, // valid pointer -> dest + #if SUPPORT_KCL_CUBE + kcl_cube_list_t *cl, // NULL or pointer to a cube list + #endif + const kcl_tridata_t *td // valid data -> source +) +{ + DASSERT(kcl); + DASSERT(tri); + DASSERT(td); + + uint p; + const double3 *d3 = td->pt; + for ( p = 0; p < 3; p++ ) + { + s32 min = (u32)round( d3[0].v[p] - kcl->min_octree.v[p] ); + s32 max = min; + tri->pt[0][p] = min; + + s32 temp = (u32)round( d3[1].v[p] - kcl->min_octree.v[p] ); + tri->pt[1][p] = temp; + if ( min > temp ) + min = temp; + if ( max < temp ) + max = temp; + + temp = (u32)round( d3[2].v[p] - kcl->min_octree.v[p] ); + tri->pt[2][p] = temp; + + tri->cube.min[p] = min < temp ? min : temp; + tri->cube.max[p] = max > temp ? max : temp; + noPRINT(" %u: min=%6d, max=%6d, t=%6d %6d %6d\n", + p, tri->cube.min[p], tri->cube.max[p], + tri->pt[0][p], tri->pt[1][p], tri->pt[0][p] ); + + #if SUPPORT_KCL_CUBE + if (cl) + { + // [[2do]] [[cube]] KCL_TRI_SPLIT + + cl->n = 1; + cl->cube[0] = tri->cube; + } + #endif + } + tri->select = td->status & TD_INVALID ? M1(tri->select) : 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//======================== X-tests ======================== + +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + if(p0 rad || max <- rad ) return 0; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + if(p0rad || max<-rad) return 0; + +//======================== Y-tests ======================== + +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + if(p0rad || max<-rad) return 0; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + if(p0rad || max<-rad) return 0; + +//======================== Z-tests ======================== + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + if(p2rad || max<-rad) return 0; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + if(p0rad || max<-rad) return 0; + +//========================================================== + +static bool OctCubeTriangleOverlaped +( + kcl_cube_t *cube, // cube info + kcl_tri_t *tri // triangle info + #if SUPPORT_KCL_CUBE + , + kcl_cube_list_t *cl // NULL or cube list + #endif +) +{ + DASSERT( cube && tri ); + + //------------------------------------------------------------ + // we use this algorithm: + // http://jgt.akpeters.com/papers/AkenineMoller01/tribox.html + //------------------------------------------------------------ + + //--- Bullet 1: test the cube, that surrounds the triangle + + if ( tri->select == M1(tri->select) + || tri->cube.min[0] > cube->max[0] || tri->cube.max[0] < cube->min[0] + || tri->cube.min[1] > cube->max[1] || tri->cube.max[1] < cube->min[1] + || tri->cube.min[2] > cube->max[2] || tri->cube.max[2] < cube->min[2] ) + { + return false; + } + + + #if SUPPORT_KCL_CUBE // [[cube]] + if (cl) + { + if ( cl->n == 1 ) + return true; + + // use [[cube]] param? + } + #endif + + + //--- center cube and triangle + + const int cubehalf = ( cube->max[0] - cube->min[0] ) / 2; + DASSERT( cubehalf == ( cube->max[1] - cube->min[1] ) / 2 ); + DASSERT( cubehalf == ( cube->max[2] - cube->min[2] ) / 2 ); + + uint p; + double3 v0, v1, v2; + for ( p = 0; p < 3; p++ ) + { + const int cubemid = cube->min[p] + cubehalf; + v0.v[p] = tri->pt[0][p] - cubemid; + v1.v[p] = tri->pt[1][p] - cubemid; + v2.v[p] = tri->pt[2][p] - cubemid; + } + + double3 e0,e1,e2; + Sub3(e0,v1,v0); // tri edge 0 + Sub3(e1,v2,v1); // tri edge 1 + Sub3(e2,v0,v2); // tri edge 2 + + //--- Bullet 3: + + double min, max, p0,p1,p2, rad, fex,fey,fez; + + fex = fabs(e0.x); + fey = fabs(e0.y); + fez = fabs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = fabs(e1.x); + fey = fabs(e1.y); + fez = fabs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0 (e1.y, e1.x, fey, fex); + + fex = fabs(e2.x); + fey = fabs(e2.y); + fez = fabs(e2.z); + AXISTEST_X2 (e2.z, e2.y, fez, fey); + AXISTEST_Y1 (e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + //--- Bullet 2: + // test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + + double3 normal; + CrossProd3(normal,e0,e1); + + double3 vmin, vmax; + for ( p = 0; p < 3; p++ ) + { + double v = v0.v[p]; + if ( normal.v[p] > 0.0 ) + { + vmin.v[p] = -cubehalf - v; + vmax.v[p] = cubehalf - v; + } + else + { + vmin.v[p] = cubehalf - v; + vmax.v[p] = -cubehalf - v; + } + } + + return DotProd3(normal,vmin) <= 0.0 + && DotProd3(normal,vmax) >= 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u32 CalcCube +( + oct_info_t *oi, // base data + kcl_cube_t *cube, // cube info + uint depth // current depth, also used for select level +) +{ + DASSERT(oi); + DASSERT(oi->kcl); + DASSERT(cube); + + noPRINT("CUBE*: min=%6u,%6u,%6u, max=%6u,%6u,%6u\n", + cube->min[0], cube->min[1], cube->min[2], + cube->max[0], cube->max[1], cube->max[2] ); + + uint tri_count = 0; + + const uint cube_blow = oi->kcl->cube_blow; + cube->min[0] -= cube_blow; + cube->min[1] -= cube_blow; + cube->min[2] -= cube_blow; + cube->max[0] += cube_blow; + cube->max[1] += cube_blow; + cube->max[2] += cube_blow; + + #if SUPPORT_KCL_CUBE + kcl_tri_t *tri, *end = oi->tri + oi->n_tri; + kcl_cube_list_t *cl = oi->cubelist; + for ( tri = oi->tri ; tri < end; tri++ ) + { + if ( tri->select == depth && OctCubeTriangleOverlaped(cube,tri,cl) ) + { + tri->select++; + tri_count++; + } + if (cl) + cl++; + } + #else + kcl_tri_t *tri, *end = oi->tri + oi->n_tri; + for ( tri = oi->tri ; tri < end; tri++ ) + if ( tri->select == depth && OctCubeTriangleOverlaped(cube,tri) ) + { + tri->select++; + tri_count++; + } + #endif + + cube->min[0] += cube_blow; + cube->min[1] += cube_blow; + cube->min[2] += cube_blow; + cube->max[0] -= cube_blow; + cube->max[1] -= cube_blow; + cube->max[2] -= cube_blow; + + depth++; + uint cube_size = cube->max[0] - cube->min[0]; + + if ( cube_size <= oi->kcl->max_cube_size && tri_count <= oi->kcl->max_cube_triangles + || cube_size <= oi->kcl->min_cube_size + || depth > oi->kcl->max_octree_depth ) + { + noPRINT("--> TLIST: n-tri =%5u, width =%6u, base = %6u,%6u,%6u, depth=%u/%u\n", + tri_count, cube_size, + cube->min[0], cube->min[1], cube->min[2], + depth, kcl->max_octree_depth ); + + if (!tri_count) + return 0; + + if ( oi->tused + tri_count + 1 > oi->tsize ) + { + oi->tsize *= 2; + TRACE("REALLOC(tlist), new size = %u\n",oi->tsize); + oi->tlist = REALLOC( oi->tlist, oi->tsize * sizeof(*oi->tlist) ); + } + u32 result = 0x80000000 | oi->tused * sizeof(*oi->tlist); + + // create the list + u16 *tdata = oi->tlist + oi->tused; + u16 *tptr = tdata; + for ( tri = oi->tri ; tri < end; tri++ ) + if ( tri->select == depth ) + { + tri->select--; + write_be16( tptr++, tri - oi->tri + 1 ); + } + write_be16( tptr++, 0); + DASSERT( tptr - oi->tlist <= oi->tsize ); + DASSERT( tptr - oi->tlist == oi->tused + tri_count + 1 ); + + if (!oi->kcl->fast) + { + // try to find a duplicate + u16 *tmax = tdata - tri_count - 1; + const uint tsize = ( tri_count + 1 ) * sizeof(*oi->tlist); + for ( tptr = oi->tlist; tptr <= tmax; tptr++ ) + if (!memcmp(tptr,tdata,tsize)) + return ( tptr - oi->tlist ) * sizeof(*oi->tlist) | 0x80000000; + } + + // no duplicate found + oi->tused += tri_count + 1; + return result; + } + else + { + noPRINT("--> SUBCUBE: n-tri =%5u, width =%6u, base = %6u,%6u,%6u, depth=%u/%u\n", + tri_count, cube_size, + cube->min[0], cube->min[1], cube->min[2], + depth, kcl->max_octree_depth ); + + if ( oi->oused + 8 > oi->osize ) + { + oi->osize *= 2; + TRACE("REALLOC(olist), new size = %u\n",oi->osize); + oi->olist = REALLOC( oi->olist, oi->osize * sizeof(*oi->olist) ); + } + u32 result = oi->oused * sizeof(*oi->olist); + uint ores = oi->oused; + oi->oused += 8; + DASSERT( oi->oused <= oi->osize ); + + cube_size /= 2; + uint zi; + for ( zi = 0; zi < 2; zi++ ) + { + uint yi; + for ( yi = 0; yi < 2; yi++ ) + { + uint xi; + for ( xi = 0; xi < 2; xi++ ) + { + kcl_cube_t subcube; + subcube.min[0] = cube->min[0] + xi * cube_size; + subcube.max[0] = subcube.min[0] + cube_size; + subcube.min[1] = cube->min[1] + yi * cube_size; + subcube.max[1] = subcube.min[1] + cube_size; + subcube.min[2] = cube->min[2] + zi * cube_size; + subcube.max[2] = subcube.min[2] + cube_size; + + // 2 steps, because oi->olist may be realloced + const u32 result = CalcCube(oi,&subcube,depth); + oi->olist[ores++] = result; + } + } + } + + for ( tri = oi->tri ; tri < end; tri++ ) + if ( tri->select == depth ) + tri->select--; + return result; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateOctreeKCL +( + kcl_t * kcl // KCL data structure +) +{ + DASSERT(kcl); + if ( kcl->octree_valid && !kcl->recreate_octree ) + return ERR_NOTHING_TO_DO; + + TRACE_SIZEOF(kcl_mode_t); + TRACE_SIZEOF(kcl_cube_t); + #if SUPPORT_KCL_CUBE + TRACE_SIZEOF(kcl_cube_list_t); + #endif + TRACE_SIZEOF(kcl_tri_t); + TRACE_SIZEOF(oct_info_t); + + if (!kcl->silent_octree) + KCL_ACTION_LOG("Create KCL Octree [valid=%d,recreate=%d]\n", + kcl->octree_valid, kcl->recreate_octree ); + + CalcMinMaxKCL(kcl); + + if (kcl->octree_alloced) + FREE(kcl->octree); + kcl->octree = 0; + kcl->octree_size = 0; + kcl->octree_alloced = false; + kcl->recreate_octree = false; + + if (kcl->raw_data_alloced) + FREE(kcl->raw_data); + kcl->raw_data = 0; + kcl->raw_data_size = 0; + kcl->raw_data_alloced = false; + kcl->model_modified = true; + + + //--- calculate relevant bits + + uint p, relevant_bits[3], max_relevant = 0; + double max[3]; + for ( p = 0; p < 3; p++ ) + { + kcl->min_octree.v[p] + = ( IsNormalD(KCL_MIN.v[p]) ? KCL_MIN.v[p] : floor(kcl->min.v[p]) ) + - kcl->cube_blow; + + int exp; + max[p] = ( IsNormalD(KCL_MAX.v[p]) ? KCL_MAX.v[p] : kcl->max.v[p] ) + + kcl->cube_blow; + + frexp( max[p] - kcl->min_octree.v[p], &exp ); + relevant_bits[p] = exp < 0 ? 0 : exp; + kcl->mask[p] = ~(u32)0 << relevant_bits[p]; + if ( max_relevant < relevant_bits[p] ) + max_relevant = relevant_bits[p]; + } + noPRINT(" *MIN: %11.3f %11.3f %11.3f\n", kcl->min.x, kcl->min.y, kcl->min.z ); + noPRINT(" *MAX: %11.3f %11.3f %11.3f\n", kcl->max.x, kcl->max.y, kcl->max.z ); + noPRINT(" BASE: %11.3f %11.3f %11.3f\n", + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z ); + + + //--- maximum allowed bits in base cube selection + + int max_bits; + if (KCL_BITS) + max_bits = KCL_BITS; + else + { + max_bits = log(kcl->tridata.used)/log(2); + if ( max_bits < 5 ) + max_bits = 5; + #if 0 + else if ( max_bits >= 10 ) + max_bits = max_relevant > 10 + ? 10 + ( max_relevant - 10 ) / 2 + : 10; + #else + else if ( max_bits > 10 ) + max_bits = 10; + #endif + } + PRINT("KCL-PARAM: BITS=%u, MAX_TRI=%u, MAX_DEPTH=%u, CUBE_SIZE=%u..%u, BLOW=%u\n", + max_bits, kcl->max_cube_triangles, kcl->max_octree_depth, + kcl->min_cube_size, kcl->max_cube_size, kcl->cube_blow ); + + + //--- calculate shifts + + uint rshift = 1, nbits[3]; + while ( 1 << rshift < kcl->min_cube_size ) + rshift++; + for(;;) + { + uint sumbits = 0; + for ( p = 0; p < 3; p++ ) + { + nbits[p] = relevant_bits[p] > rshift ? relevant_bits[p] - rshift : 0; + sumbits += nbits[p]; + } + noPRINT("RSHIFT=%2u, NBITS= %2d + %2d + %2d = %2d / %2d\n", + rshift, + nbits[0], nbits[1], nbits[2], + sumbits, max_bits ); + if ( sumbits <= max_bits ) + break; + rshift++; + } + + const uint xn = 1 << nbits[0]; + const uint yn = 1 << nbits[1]; + const uint zn = 1 << nbits[2]; + const uint n_cubes = xn * yn * zn; + + PRINT("RSHIFT=%u, NBITS=%u,%u,%u, RELBITS=%u,%u,%u, N_CUBES=%u\n", + rshift, + nbits[0], nbits[1], nbits[2], + relevant_bits[0], relevant_bits[1], relevant_bits[2], + n_cubes ); + + kcl->coord_rshift = rshift; + kcl->y_lshift = nbits[0]; + kcl->z_lshift = nbits[0] + nbits[1]; + + + //--- adjust min_coord + + if ( KCL_MODE & KCLMD_CENTER ) + { + for ( p = 0; p < 3; p++ ) + { + const double space = ( 1 << rshift + nbits[p] ) + + kcl->min_octree.v[p] - max[p]; + noPRINT("ADJUST[%u] MIN=%11.3f, SPC=%11.3f\n",p,kcl->min_octree.v[p],space); + if ( space > 0.0 ) + { + PRINT("ADJUST[%u] %11.3f -> %11.3f\n", + p, kcl->min_octree.v[p], + kcl->min_octree.v[p] - floor(space/2) ); + kcl->min_octree.v[p] -= floor(space/2); + } + } + } + + + //--- setup data for octree calculations + + oct_info_t oi; + memset(&oi,0,sizeof(oi)); + oi.kcl = kcl; + oi.n_tri = kcl->tridata.used; + oi.tri = CALLOC(oi.n_tri,sizeof(*oi.tri)); + #ifdef TEST // [[cube]] + if ( KCL_MODE & KCLMD_CUBE ) + oi.cubelist = CALLOC(oi.n_tri,sizeof(*oi.cubelist)); + #endif + + oi.osize = oi.n_tri + n_cubes; + oi.oused = n_cubes; + oi.olist = CALLOC(oi.osize,sizeof(*oi.olist)); + uint ores = 0; + + oi.tsize = 2 + oi.n_tri; + oi.tused = 0; + oi.tlist = CALLOC(oi.tsize,sizeof(*oi.tlist)); + + if ( verbose >= 0 && !(KCL_MODE & KCLMD_SILENT) && !kcl->silent_octree ) + fprintf(stdlog, + " - create octree: rshift=%u, n_bcube=%u, cube_size=%u..%u, blow=%u," + " max_tri=%u, max_depth=%u, fast=%u\n", + rshift, n_cubes, + kcl->min_cube_size, kcl->max_cube_size, kcl->cube_blow, + kcl->max_cube_triangles, kcl->max_octree_depth, kcl->fast); + + uint t; + kcl_tri_t *tri = oi.tri; + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + for ( t = 0; t < oi.n_tri; t++, tri++, td++ ) + { + #if SUPPORT_KCL_CUBE + setup_oct_tri( kcl, tri, oi.cubelist ? oi.cubelist + t : 0 , td ); + #else + setup_oct_tri(kcl,tri,td); + #endif + noPRINT("%5u: min=%5d,%5d,%5d, max=%5d,%5d,%5d, select=%d\n", + t, + tri->min[0], tri->min[1], tri->min[2], + tri->max[0], tri->max[1], tri->max[2], + tri->select ); + } + + + //--- now create the octree (start recursion) + + const uint cube_size = 1 << rshift; + + uint zi; + for ( zi = 0; zi < zn; zi++ ) + { + uint yi; + for ( yi = 0; yi < yn; yi++ ) + { + uint xi; + for ( xi = 0; xi < xn; xi++ ) + { + kcl_cube_t cube; + cube.min[0] = xi * cube_size; + cube.max[0] = cube.min[0] + cube_size; + cube.min[1] = yi * cube_size; + cube.max[1] = cube.min[1] + cube_size; + cube.min[2] = zi * cube_size; + cube.max[2] = cube.min[2] + cube_size; + + // 2 steps, because oi.olist may be realloced + const u32 result = CalcCube(&oi,&cube,0); + oi.olist[ores++] = result; + } + } + } + + PRINT("*** O-USED = %u, T-USED = %u, n_CUBES = %u\n",oi.oused,oi.tused,n_cubes); + //HEXDUMP16(0,0,oi.olist,oi.oused*sizeof(*oi.olist)); putchar('\n'); + //HEXDUMP16(0,0,oi.tlist,oi.tused*sizeof(*oi.tlist)); putchar('\n'); + + + //--- concatenate both data areas + + if (!oi.tused) + { + // be sure that at least one 0 element is in the list + oi.tused++; + *oi.tlist = 0; + } + + u32 toffset = oi.oused * sizeof(*oi.olist); + u32 tsize = oi.tused * sizeof(*oi.tlist); + kcl->octree_size = toffset + tsize; + kcl->octree = REALLOC(oi.olist,kcl->octree_size); + kcl->octree_alloced = true; + oi.olist = (u32*)kcl->octree; + memcpy( kcl->octree + toffset, oi.tlist, tsize ); + + + //--- we have an absolute offset, but need big endian relative -> fix it + + u32 null_offset = tsize + toffset - 4 | 0x80000000; + toffset -= 2; // all tri offsets are 2 bytes to short! + + uint idx = 0, end = n_cubes; + while ( idx < oi.oused ) + { + u32 baseoff = idx * 4; + noPRINT("ADJUST %x..%x, baseoff=%x\n",idx,end,baseoff); + for ( ; idx < end; idx++ ) + { + u32 val = oi.olist[idx]; + if (!val) + val = null_offset - baseoff; + else if ( val & 0x80000000 ) + val += toffset - baseoff; + else + val -= baseoff; + write_be32( oi.olist + idx, val ); + } + end = idx + 8; + } + + //HEXDUMP16(0,0,kcl->octree,kcl->octree_size); putchar('\n'); + + + //--- clean up and terminate + + //FREE(oi.olist); don't free this. it's already realloced! + FREE(oi.tlist); + #if SUPPORT_KCL_CUBE + FREE(oi.cubelist); + #endif + kcl->octree_valid = true; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// flag support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanFlagFile +( + kcl_t * kcl, // pointer to valid KCL + ccp fname // filename to scan +) +{ + DASSERT(kcl); + DASSERT(fname); + + static u8 *data = 0; + static uint data_size = 0; + enumError err = OpenReadFILE(fname,0,true,&data,&data_size,0,0); + if (!err) + { + KCL_ACTION_LOG("Scan flag file: %s\n",fname); + FreeString(kcl->flag_fname); + kcl->flag_fname = STRDUP(fname); + + ScanInfo_t si; + InitializeSI(&si,(ccp)data,data_size,kcl->fname,kcl->revision); + si.predef = SetupVarsKCL(); + + while (NextCharSI(&si,true)) + { + //-- scan the group name + + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + char *ptr = (char*)sf->ptr; + char *name = ptr; + bool have_wildcard = false; + while ( *ptr > ' ' && *ptr != '=' && *ptr != '#' ) + { + if ( !have_wildcard && strchr(PATTERN_WILDCARDS,*ptr) ) + have_wildcard = true; + *ptr = toupper((int)*ptr); + ptr++; + } + const uint namelen = ptr - name; + sf->ptr = ptr; + + if (!namelen) + { + CheckEolSI(&si); + continue; + } + + DEFINE_VAR(flag); + if ( CheckWarnSI(&si,'=',0) || ScanExprSI(&si,&flag) ) + { + GotoEolSI(&si); + continue; + } + CheckEolSI(&si); + int num = GetIntV(&flag); + if ( num < 0 ) + num = -1; // -1 is the only allowed negative value + + + //--- here we have all data! + + name[namelen] = 0; + noPRINT("FLAG-FOUND[%d]: |%s| = 0x%04x\n",have_wildcard,name,num); + + FormatFieldItem_t *it + = InsertFormatField(&kcl->flag_name,name,false,false,0); + DASSERT(it); + it->num = num; + + if (have_wildcard) + { + it = AppendFormatField(&kcl->flag_pattern,name,false,false); + DASSERT(it); + it->num = num; + } + } + + const Var_t *var = FindVarSI(&si,"HEX4",false); + if ( var && var->mode != VAR_UNSET ) + kcl->accept_hex4 = GetBoolV(var); + + var = FindVarSI(&si,"HEX23",false); + if ( var && var->mode != VAR_UNSET ) + kcl->accept_hex23 = GetBoolV(var); + + ResetSI(&si); + } + + FREE(data); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +void LoadFlagFileKCL +( + kcl_t * kcl // pointer to valid KCL +) +{ + DASSERT(kcl); + + if ( !kcl->flag_db_loaded && kcl->fname ) + { + kcl->flag_db_loaded = true; + if ( !opt_flag_file || *opt_flag_file ) + { + enumError err = ERR_WARNING; + if (opt_flag_file) + { + SubstDest(iobuf,sizeof(iobuf),kcl->fname,opt_flag_file, + 0,KCL_FLAG_EXT,false); + err = ScanFlagFile(kcl,iobuf); + } + + if (err) + { + SubstDest(iobuf,sizeof(iobuf),kcl->fname,KCL_FLAG_FILE1, + 0,KCL_FLAG_EXT,false); + err = ScanFlagFile(kcl,iobuf); + } + + if (err) + { + SubstDest(iobuf,sizeof(iobuf),kcl->fname,KCL_FLAG_FILE2, + 0,KCL_FLAG_EXT,false); + err = ScanFlagFile(kcl,iobuf); + } + + if (err) + { + FreeString(kcl->flag_fname); + kcl->flag_fname = 0; + KCL_ACTION_LOG("No flag file found for: %s\n",kcl->fname); + } + + PRINT("KCL.FLAGS loaded, N=%u+%u, hex4=%d, hex23=%d\n", + kcl->flag_name.used, kcl->flag_pattern.used, + kcl->accept_hex4, kcl->accept_hex23 ); + } + else + KCL_ACTION_LOG("No flag file searched for: %s\n",kcl->fname); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetFlagByNameKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp name, // upper case name to search + int new_value // return value if not found + // >0: used as new value + + // The name is searched in 4 steps: + // 1. Search literal in 'flag_name' (ignore case) + // 2. Search pattern in 'flag_pattern' (ignore case) + // 3. Search pattern in 'flag_missing' (ignore case) + // 4. Analyze the last 5 characters for '_ffff' + // 5. Analyze the last 7 characters for '_tt_vvv' + // 6. Use 'new_value' +) +{ + DASSERT(kcl); + DASSERT(name); + + if (!kcl->flag_db_loaded) + LoadFlagFileKCL(kcl); + + + //--- 1. Search literal in 'flag_name' (ignore case) + + if ( kcl->flag_name.used ) + { + FormatFieldItem_t *ptr = FindFormatField(&kcl->flag_name,name); + if (ptr) + { + noPRINT("GetFlagByNameKCL(%s) FOUND NAME -> %04x\n",name,ptr->num); + return (int)ptr->num; + } + } + + + //--- 2. Search pattern in 'flag_pattern' (ignore case) + + if ( kcl->flag_pattern.used ) + { + FormatFieldItem_t *ptr = MatchFormatField(&kcl->flag_pattern,name); + if (ptr) + { + noPRINT("GetFlagByNameKCL(%s) FOUND PATTERN-> %04x\n",name,ptr->num); + return (int)ptr->num; + } + } + + + //--- 3. Search literal in 'flag_missing' (ignore case) + + if ( kcl->flag_missing.used ) + { + FormatFieldItem_t *ptr = FindFormatField(&kcl->flag_missing,name); + if (ptr) + { + noPRINT("GetFlagByNameKCL(%s) FOUND MISSING -> %04x %s\n",name,ptr->num,ptr->key); + return (int)ptr->num; + } + } + + + //--- flag is missed in flag file + + FormatFieldItem_t *it = InsertFormatField(&kcl->flag_missing,name,false,false,0); + DASSERT(it); + it->num = new_value > 0 ? new_value : 0x10000; + PRINT("GetFlagByNameKCL(%s) INSERT INTO MISSING := %04x\n",name,it->num); + + + //--- 4. Analyze the last 5-6 characters for '_ffff' or '_Fffff + + const uint len = strlen(name); + if ( kcl->accept_hex4 && len >= 5 && name[len-5] == '_' + || len >= 6 && name[len-6] == '_' && name[len-5] == 'F' ) + { + char *end; + const uint num = strtoul(name+len-4,&end,16); + if (!*end) + { + noPRINT("GetFlagByNameKCL(%s) FOUND '_FFFF' -> %04x\n",name,num); + it->num = num; + return num; + } + } + + + //--- 5. Analyze the last 7 characters for '_tt_vvv' + + if ( kcl->accept_hex23 + && len >= 7 && name[len-7] == '_' && name[len-4] == '_' ) + { + char *end1, *end2; + const uint num1 = strtoul(name+len-6,&end1,16); + const uint num2 = strtoul(name+len-3,&end2,16); + if ( end1 == name+len-4 && !*end2 && num1 < 0x20 && num2 < 0x800 ) + { + noPRINT("GetFlagByNameKCL(%s) FOUND '_TT_VVV' -> %02x,%03x=%04x\n", + name, num1, num2, num1 | num2 << 5 ); + it->num = num1 | num2 << 5; + return it->num; + } + } + + + //--- 6. Use 'new_value' + + return new_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint * AllocFlagCount() +{ + return CALLOC(NNN_KCL_FLAG,sizeof(uint)); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountFlagsKCL +( + // returns the total number of flag groups + + const kcl_t * kcl, // pointer to valid KCL + uint * count // pointer to field with NNN_KCL_FLAG elements +) +{ + DASSERT(kcl); + DASSERT(count); + + uint total = 0; + memset(count,0,N_KCL_FLAG*sizeof(*count)); + + const uint n = kcl->tridata.used; + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + const kcl_tridata_t *end = td + n; + for ( ; td < end; td++ ) + { + uint idx = td->cur_flag < NNN_KCL_FLAG ? td->cur_flag : KCL_FLAG_UNKNOWN; + if (!count[idx]++) + total++; + } + return total; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetGroupNameKCL ( uint kcl_flag, bool add_info ) +{ + if ( kcl_flag < N_KCL_FLAG ) + { + const uint bufsize = add_info ? 60 : 40; + char *buf = GetCircBuf(bufsize); + + const uint type = kcl_flag & 0x1f; + const kcl_class_t *cls = kcl_class + kcl_type[type].cls; + if (add_info) + snprintf(buf,bufsize,"%s_%02x_F%04x (%s)", + cls->name, type, kcl_flag, kcl_type[type].info ); + else + snprintf(buf,bufsize,"%s_%02x_F%04x", + cls->name, type, kcl_flag ); + return buf; + } + + if ( kcl_flag >= KCL_FLAG_AUTO_0 && kcl_flag <= KCL_FLAG_AUTO_MAX ) + { + const uint bufsize = 9; + char *buf = GetCircBuf(bufsize); + snprintf( buf, bufsize, "auto_%03u",kcl_flag-KCL_FLAG_AUTO_0); + return buf; + } + + if ( kcl_flag >= KCL_FLAG_USER_0 && kcl_flag <= KCL_FLAG_USER_MAX ) + { + Color_t col = user_color[kcl_flag-KCL_FLAG_USER_0]; + const uint bufsize = 14; + char *buf = GetCircBuf(bufsize); + snprintf( buf, bufsize, "user_%02x%02x%02x%02x", + col.a, col.r, col.g, col.b ); + return buf; + } + + return GetFlagGroupName(kcl_flag); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetExportFlagKCL ( uint kcl_flag ) +{ + if ( kcl_flag < NN_KCL_FLAG ) + return kcl_flag; + + if ( kcl_flag >= KCL_FLAG_USER_0 && kcl_flag <= KCL_FLAG_USER_MAX ) + return kcl_flag - KCL_FLAG_USER_0 + 0x1000000; + + if ( kcl_flag >= KCL_FLAG_AUTO_0 && kcl_flag <= KCL_FLAG_AUTO_MAX ) + return kcl_flag - KCL_FLAG_AUTO_0 + 0x2000000; + + return kcl_flag | 0x8000000; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // FF_UNKNOWN or file format +) +{ + TRACE("ScanTextKCL()\n"); + + DASSERT(kcl); + ClearKCL(kcl,init_kcl); +// [[analyse-magic]] + kcl->fform = fform != FF_UNKNOWN ? fform : GetByMagicFF(data,data_size,data_size); + + bool use_g = 0 != ( KCL_MODE & KCLMD_G ); + bool use_usemtl = 0 != ( KCL_MODE & KCLMD_USEMTL ); + if ( !use_g && !use_usemtl ) + { + if ( kcl->fform == FF_SKP_OBJ ) + use_usemtl = true; + else + use_g = true; + } + DASSERT( use_g || use_usemtl ); + KCL_ACTION_LOG("ScanTextKCL() %s [%s,%s]\n", + kcl->fname,GetNameFF(kcl->fform,0), + use_g && use_usemtl ? "'g'+'usemtl'" : use_g ? "'g'" : "'usemtl'" ); + + + //--- setup data + + double3List_t vertex, normal; + InitializeD3L(&vertex); + InitializeD3L(&normal); + + + //--- scan text file + + ScanInfo_t si; + InitializeSI(&si,data,data_size,kcl->fname,kcl->revision); + si.predef = SetupVarsKCL(); + enumError max_err = ERR_OK; + + int auto_flag = KCL_FLAG_AUTO_0; + char buf[200]; // for commands and group names + + struct tab_t + { + uint idx; + double3 pt; + } tab[KCL_MAX_PT_PER_TRI]; + + kcl_tri_param_t par; + InitializeTriParam(&par,kcl,0); + par.is_orig = true; + uint bad_count = 0; + + static const uint norm_assign_23[] = { 0, 1, 2 }; + static const uint norm_assign_32[] = { 0, 2, 1 }; + const uint *norm_assign = KCL_MODE & KCLMD_IN_SWAP + ? norm_assign_32 : norm_assign_23; + + while (NextCharSI(&si,true)) + { + if (!ScanNameSI(&si,buf,sizeof(buf),true,true,0)) + { + ccp eol = FindNextLineFeedSI(&si,true); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + ERROR0(ERR_WARNING, + "Missing name [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + if ( max_err < ERR_WARNING ) + max_err = ERR_WARNING; + GotoEolSI(&si); + continue; + } + + enumError err = ERR_OK; + CommandKCL_t cmd = GetCommandKCL(buf); + switch (cmd) + { + case KCMD_V: + noPRINT("V\n"); + { + double3 temp; + err = ScanDoubleV3SI(&si,temp.v,1); + if (!err) + { + double3 *dest = AppendD3L(&vertex); + DASSERT(dest); + memcpy(dest,&temp,sizeof(*dest)); + } + } + break; + + case KCMD_VN: + noPRINT("Vn\n"); + { + double3 temp; + err = ScanDoubleV3SI(&si,temp.v,1); + if (!err) + { + double3 *dest = AppendD3L(&normal); + DASSERT(dest); + memcpy(dest,&temp,sizeof(*dest)); + } + } + break; + + case KCMD_F: + { + noPRINT("F\n"); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + //--- scan points + + uint k, norm = ~0; + for ( k = 0; k < KCL_MAX_PT_PER_TRI; k++ ) + { + if ( k >= 3 && !NextCharSI(&si,false) ) + break; + + u32 idx; + err = ScanU32SI(&si,&idx,1,0); + if (err) + break; + // transform to 0-based + tab[ k < 3 ? norm_assign[k] : k ].idx = idx - 1; + + if ( *sf->ptr == '/' ) + { + // texture index found => ignore + sf->ptr++; + while ( *sf->ptr >= '0' && *sf->ptr <= '9' ) + sf->ptr++; + + if ( *sf->ptr == '/' ) + { + // normal index found + sf->ptr++; + if ( *sf->ptr >= '0' && *sf->ptr <= '9' + && !ScanU32SI(&si,&idx,1,0) + && !k + && (KCL_MODE & KCLMD_AUTO) ) + { + noPRINT("NORMAL FOUND: %u\n",idx-1); + norm = idx - 1; + } + } + } + } + + //--- add point + + if ( !err && par.kcl_flag >= 0 ) + { + bool valid = true; + uint i; + for ( i = 0; i < k; i++ ) + if ( tab[i].idx > vertex.used ) + { + PRINT("INVALID VERTEX INDEX\n"); + bad_count++; + valid = false; + break; + } + else + tab[i].pt = vertex.list[tab[i].idx]; + + if (valid) + { + par.norm = norm < normal.used ? normal.list + norm : 0; + AppendTrianglesKCL( &par, &tab[0].pt, sizeof(*tab), k ); + } + } + } + break; + + case KCMD_G: + PRINT("G %.10s\n",si.cur_file->ptr); + if (use_g) + { + scan_group: + ScanNameSI(&si,buf,sizeof(buf),true,true,0); + par.kcl_flag = GetFlagByNameKCL(kcl,buf,auto_flag); + if ( par.kcl_flag == auto_flag ) + { + if ( ++auto_flag > KCL_FLAG_AUTO_MAX ) + auto_flag = KCL_FLAG_AUTO_0; + PRINT("KCL-FLAG set to %04x: %s\n",par.kcl_flag,buf); + } + } + GotoEolSI(&si); + break; + + case KCMD_USEMTL: + noPRINT("USEMTL\n"); + if (use_usemtl) + goto scan_group; + GotoEolSI(&si); + break; + + case KCMD_TRI: + { + noPRINT("TRI\n"); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + //--- scan flag + + u32 flag; + err = ScanU32SI(&si,&flag,1,0); + + + //--- scan points + + uint k; + for ( k = 0; k < KCL_MAX_PT_PER_TRI && !err; k++ ) + { + noPRINT(">> %u: %.8s\n",k,sf->ptr); + char ch = NextCharSI(&si,false); + if ( ch == '|' ) + { + sf->ptr++; + ch = NextCharSI(&si,false); + } + if (!ch) + break; + err = ScanDoubleSI(&si, tab[ k < 3 ? norm_assign[k] : k ].pt.v, 3 ); + } + + + //--- add point + + if ( !err && k >= 3 ) + { + par.norm = 0; + AppendTrianglesKCL( &par, &tab[0].pt, sizeof(*tab), k ); + } + } + break; + + default: + TRACE("IGNORE KCL/OBJ COMMAND %s\n",buf); + GotoEolSI(&si); + break; + } + + if (err) + { + if ( max_err < err ) + max_err = err; + GotoEolSI(&si); + } + else + CheckEolSI(&si); + } + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextKCL) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + + //--- logging + + if ( !(KCL_MODE & KCLMD_SILENT) + && ( verbose > 0 || verbose >=0 && par.swap_count ) ) + { + fprintf(stdlog, + " - %u triangles read," + " points #2 and #3 are swapped for %u triangles.\n", + kcl->tridata.used, par.swap_count ); + } + + if (bad_count) + { + const uint total = bad_count + kcl->tridata.used; + ERROR0(ERR_WARNING, + "%u of %u (%4.2f%%) triangles ignored because of wrong indices: %s\n", + bad_count, total, + 100.0 * bad_count / total, + kcl->fname ); + if ( max_err < ERR_WARNING ) + max_err = ERR_WARNING; + } + + + //--- terminate + + ResetD3L(&vertex); + ResetD3L(&normal); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + bool use_data // true: data is valid on 'kcl' live time +) +{ + DASSERT(kcl); + ClearKCL(kcl,init_kcl); + + kcl_analyze_t ka; + if ( IsValidKCL(&ka,data,data_size,data_size,kcl->fname) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid KCL file: %s\n", kcl->fname ? kcl->fname : "?"); + } + + KCL_ACTION_LOG("ScanRawKCL() %s\n",kcl->fname); + kcl->fform = FF_KCL; + + if (!ka.order_ok) + ERROR0(ERR_WARNING, + "KCL: Unusual section order: %d\n",ka.order_value); + + + //--- save raw data for optimized handling + + const bool store_raw_data + = ( KCL_MODE & (KCLMD_NEW|KCLMD_DROP_UNUSED) ) != KCLMD_NEW; + PRINT("STORE_RAW_DATA=%d\n",store_raw_data); + if (store_raw_data) + { + if (use_data) + { + kcl->raw_data = (u8*)data; + kcl->raw_data_alloced = false; + } + else + { + kcl->raw_data = MEMDUP(data,data_size); + kcl->raw_data_alloced = true; + } + kcl->raw_data_size = data_size; + } + + + //--- transfer header + + const kcl_head_t *kclhead = data; + kcl->min_octree.x = bef4(kclhead->min_octree+0); + kcl->min_octree.y = bef4(kclhead->min_octree+1); + kcl->min_octree.z = bef4(kclhead->min_octree+2); + kcl->mask[0] = be32(kclhead->mask+0); + kcl->mask[1] = be32(kclhead->mask+1); + kcl->mask[2] = be32(kclhead->mask+2); + kcl->coord_rshift = be32(&kclhead->coord_rshift); + kcl->y_lshift = be32(&kclhead->y_lshift); + kcl->z_lshift = be32(&kclhead->z_lshift); + kcl->unknown_0x10 = bef4(&kclhead->unknown_0x10); + kcl->unknown_0x38 = bef4(&kclhead->unknown_0x38); + + + //--- store triangles + + const uint n_vert = ka.n[0]; + const uint n_norm = ka.n[1]; + const uint n_tri = ka.n[2]; + PRINT("N(vert)=%u, N(norm)=%u, N(tri)=%u\n",n_vert,n_norm,n_tri); + + const float3 *vert = (float3*)( data + ka.off[0] ); + const float3 *norm = (float3*)( data + ka.off[1] ); + const kcl_triangle_t *tri = (kcl_triangle_t*)( data + ka.off[2] ); + + kcl_tridata_t *td = GrowListSize(&kcl->tridata,n_tri,100); + DASSERT( kcl->tridata.used == n_tri ); + memset(td,0,sizeof(*td)*n_tri); + + double3 min, max; // min and max values of all incomming vertex points + min.x = min.y = min.z = -1000.0; + max.x = min.y = max.z = 1000.0; + + uint ti; + for ( ti = 0; ti < n_tri; ti++, td++, tri++ ) + { + u16 in[6]; + be16n(in,&tri->idx_vertex,6); + + if ( in[0] < n_vert ) + { + const float *flt = vert[in[0]].v; + td->pt[0].x = bef4(flt++); + td->pt[0].y = bef4(flt++); + td->pt[0].z = bef4(flt); + MinMax3(&min,&max,td->pt,1); + // [[xtridata]] + } + + uint p; + for ( p = 0; p < 4; p++ ) + if ( in[p+1] < n_norm ) + bef4n(td->normal[p].v,norm[in[p+1]].v,3); + + td->length = bef4(&tri->length); + + td->in_flag = in[5]; + td->cur_flag = patch_kcl_flag ? patch_kcl_flag[in[5]] : in[5]; + PRINT_IF ( td->in_flag!=td->cur_flag, + "IN/FLAGS: %04x -> %04x\n",td->in_flag,td->cur_flag); + } + + + //--- calc 'tri_minval' and 'tri_maxval' + + const bool clip = ( KCL_MODE & KCLMD_CLIP ) != 0; + if (clip) + { + double temp; + temp = ( max.x - min.x ) * KCL_CLIP.x; + kcl->tri_minval.x = min.x - temp; + kcl->tri_maxval.x = max.x + temp; + + temp = ( max.y - min.y ) * KCL_CLIP.y; + kcl->tri_minval.y = min.y - temp; + kcl->tri_maxval.y = max.y + temp; + + temp = ( max.z - min.z ) * KCL_CLIP.z; + kcl->tri_minval.z = min.z - temp; + kcl->tri_maxval.z = max.z + temp; + + TRACE("VERTEX-MIN: %12.6g %12.6g %12.6g\n", + kcl->tri_minval.x, kcl->tri_minval.y, kcl->tri_minval.z ); + TRACE("VERTEX-MAX: %12.6g %12.6g %12.6g\n", + kcl->tri_maxval.x, kcl->tri_maxval.y, kcl->tri_maxval.z ); + } + + + //--- calc triangle points + + CalcPointsTriData( (kcl_tridata_t*)kcl->tridata.list, n_tri, kcl, clip ); + kcl->norm_valid = true; + + + //--- test signature + + { + ccp ptr = (ccp)&vert->x; + ccp end = ptr + 8; + for(;;) + { + char ch = *ptr++; + noPRINT("|%02x|\n",ch); + if ( ch <= 0x20 || ch >= 0x7f ) + break; + + if ( ptr == end ) + { + float sig = bef4(&vert->z); + if ( sig >= 0.01 && sig <= 1000.0 ) + { + kcl->signature = sig; + memcpy(kcl->signature_info,&vert->x,8); + } + break; + } + } + } + + + //--- store octree + + if ( store_raw_data && ka.off[3] < data_size ) + { + kcl->octree_size = data_size - ka.off[3]; + if (kcl->raw_data) + { + kcl->octree = kcl->raw_data + ka.off[3]; + kcl->octree_alloced = false; + } + else + { + kcl->octree = MEMDUP( data+ka.off[3], kcl->octree_size ); + kcl->octree_alloced = true; + } + kcl->octree_valid = true; + } + + + //--- terminate + + //PRINT("KCL-N: %u %u %u\n",n_vert,n_norm,kcl->triangle.used); + kcl->min_max_valid = false; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + bool use_data, // true: data is valid on 'kcl' live time + CheckMode_t mode // not NULL: call CheckKCL(mode) +) +{ + DASSERT(kcl); + PRINT("ScanKCL(mode=%x) size=%u\n",mode,data_size); + //HEXDUMP16(0,0,data,16); + + enumError err; +// [[analyse-magic]] +// const file_format_t fform = GetByMagicFF(data,data_size,data_size); + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch (fform) + { + case FF_KCL: + err = ScanRawKCL(kcl,init_kcl,data,data_size,use_data); + break; + + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + err = ScanTextKCL(kcl,init_kcl,data,data_size,fform); + break; + + default: + ClearKCL(kcl,init_kcl); + return ERROR0(ERR_INVALID_DATA, + "No KCL or OBJ file: %s\n", kcl->fname ? kcl->fname : "?"); + } + kcl->fform = fform; + const bool rm_octree = kcl->octree_valid && KCL_MODE & KCLMD_NEW; + PRINT("NEW->RM(OCTREE): %u && %u => %u\n", + kcl->octree_valid, (KCL_MODE & KCLMD_NEW)!=0, rm_octree ); + + + CheckMetricsTriData( (kcl_tridata_t*)kcl->tridata.list, kcl->tridata.used, kcl ); + + if ( disable_patch_on_load <= 0 ) + PatchKCL(kcl); + + if ( err <= ERR_WARNING && mode ) + CheckKCL(kcl,mode); + + if (rm_octree) + { + KCL_ACTION_LOG("Remove Octree\n"); + + if (kcl->octree_alloced) + FREE(kcl->octree); + kcl->octree = 0; + kcl->octree_size = 0; + kcl->octree_alloced = false; + kcl->octree_valid = false; + } + kcl->recreate_octree = ( KCL_MODE & KCLMD_NEW ) != 0; + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + struct raw_data_t * raw, // valid raw data + bool move_data, // true: move 'raw.data' to 'kcl' + CheckMode_t mode // not NULL: call CheckKCL(mode) +) +{ + DASSERT(kcl); + DASSERT(raw); + if (init_kcl) + InitializeKCL(kcl); + else + ResetKCL(kcl); + + kcl->fname = raw->fname; + raw->fname = 0; + + if ( raw->is_0 ) + { + if ( disable_patch_on_load <= 0 ) + PatchKCL(kcl); + return ERR_OK; + } + + enumError err = ScanKCL(kcl,false,raw->data,raw->data_size,mode,move_data); + + if ( kcl->raw_data == raw->data ) + { + // move the data + kcl->raw_data_alloced = raw->data_alloced; + raw->data_alloced = false; + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckKCL(mode) +) +{ + DASSERT(kcl); + DASSERT(fname); + if (init_kcl) + InitializeKCL(kcl); + else + ResetKCL(kcl); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + kcl->fname = raw.fname; + raw.fname = 0; + err = ScanKCL(kcl,false,raw.data,raw.data_size,mode,true); + if ( kcl->raw_data == raw.data ) + { + // move the data + kcl->raw_data_alloced = raw.data_alloced; + raw.data_alloced = false; + } + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Reference KCL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp kcl_ref_fname = 0; +ccp kcl_auto_ref_fname = 0; + +int ScanOptLoadKcl ( ccp arg ) +{ + kcl_ref_fname = arg; + SetupReferenceKCL(0); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UnloadAutoReferenceKCL() +{ + if ( !kcl_ref_fname && kcl_ref ) + { + // unload only auto loaded kcl + PRINT("UNLOAD AUTO KCL\n"); + ResetKCL(kcl_ref); + FREE(kcl_ref); + kcl_ref = 0; + } + + FreeString(kcl_auto_ref_fname); + kcl_auto_ref_fname = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineAutoLoadReferenceKCL ( ccp fname, ccp ext ) +{ + DASSERT(fname); + if ( kcl_ref_fname || !fname ) + return; + + UnloadAutoReferenceKCL(); + + char buf[PATH_MAX]; + char *dest = StringCopyS(buf,sizeof(buf),fname); + bool done = false; + if (ext) + { + uint elen = strlen(ext); + if ( elen < dest - buf && !strcasecmp(dest-elen,ext) ) + { + dest -= elen; + done = true; + } + } + + if (!done) + { + char *pt = strrchr(buf,'.'); + if (pt) + dest = pt; + } + + StringCopyE(dest,buf+sizeof(buf),".kcl"); + kcl_auto_ref_fname = STRDUP(buf); + PRINT("NEW AUTO KCL: %s\n",kcl_auto_ref_fname); +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_t * LoadReferenceKCL() +{ + bool silent; + ccp path; + if (kcl_ref_fname) + { + path = kcl_ref_fname; + silent = false; + } + else + { + path = kcl_auto_ref_fname; + silent = true; + } + + if ( kcl_ref || !path || !*path ) + return kcl_ref; + + PRINT("GetReferenceKCL() %s\n",path); + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,path,"course.kcl",silent,0); + if (err) + { + ResetRawData(&raw); + FreeString(kcl_auto_ref_fname); + kcl_ref_fname = kcl_auto_ref_fname = 0; + return 0; + } + + kcl_t kcl; + InitializeKCL(&kcl); + //kcl.fform_outfile = FF_KCL; + err = ScanRawDataKCL(&kcl,false,&raw,0,false); + ResetRawData(&raw); + if (err) + { + ResetKCL(&kcl); + FreeString(kcl_auto_ref_fname); + kcl_ref_fname = kcl_auto_ref_fname = 0; + return 0; + } + + PRINT("REF-KCL loaded!\n"); + kcl_ref = MALLOC(sizeof(*kcl_ref)); + memcpy(kcl_ref,&kcl,sizeof(*kcl_ref)); + + return kcl_ref; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_t * CreateReferenceKCL() +{ + kcl_t *kcl = LoadReferenceKCL(); + if (kcl) + { + kcl_ref = 0; + return kcl; + } + + kcl = MALLOC(sizeof(*kcl)); + InitializeKCL(kcl); + return kcl; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int kcl_fall_flag = -1; + +static enumError F_kcl_fall +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + ToVectorV(param); + double y = param->y; + kcl_t *kcl = GetReferenceKCL(); + if ( !kcl && si ) + kcl = si->kcl; + if (kcl) + { + u32 typemask = n_param > 2 && param[2].mode != VAR_UNSET + ? GetIntV(param+2) + : M1(typemask); + double temp = FallKCL(kcl,0,param->d3, + GetDoubleV(param+1),0,typemask,&kcl_fall_flag); + if ( temp >= 0.0 ) + y = temp; + } + + res->x = param->x; + res->y = y; + res->z = param->z; + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_kcl_fall_flag +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + res->i = kcl_fall_flag; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isKCL +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + uint result = 0; + if ( si && si->kcl ) + { + result = 1; + + if (si->kcl->fname) + { + ccp fname = strrchr(si->kcl->fname,'/'); + if (fname) + fname++; + else + fname = si->kcl->fname; + + if ( !strcasecmp(fname,"course.kcl") + || !strcasecmp(fname,"course.txt") + || !strcasecmp(fname,"course.txt.kcl") ) + { + result = 2; + } + } + } + + res->i = result; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const struct FuncTable_t kcl_ref_func_tab[] = +{ + { 2, 3, "KCL$FALL", F_kcl_fall, 0, + "vector", "kcl$fall(pt,width[,typemask])", + " If a reference KCL is defined, search the lowest point" + " below 'pt', that can be reached without collision." + " The point is expanded to a cube with edge length 'width'" + " for the collision tests." + " If 'typemask' is set, only KCL types with related bit number set" + " are recognized." + " Use kcl$fallFlag() to get the corespondent KCL flag." }, + + { 0, 0, "KCL$FALLFLAG", F_kcl_fall_flag, 0, + "int", "kcl$fallFlag()", + " This functions returns the corespondent KCL flag of the last" + " call of kcl$fall()." + " The return value is the KCL flag of the colliding triangle," + " or -1 if no collision was found." }, + + { 0, 0, "ISKCL", F_isKCL, 0, // replace only function call + "int", "isKCL()", 0 }, // but not info + + {0,0,0,0,0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +void SetupReferenceKCL +( + VarMap_t *vm // not NULL: define KCL variables here +) +{ + static bool done = false; + if (!done) + { + done = true; + DefineParserFuncTab(kcl_ref_func_tab,0); + } + + if (vm) + { + char name[50]; + + const kcl_attrib_name_t *an; + for ( an = kcl_attrib_name; an->name; an++ ) + { + u32 typemask = 0; + const kcl_type_t *kt; + for ( kt = kcl_type; kt->info; kt++ ) + if ( kt->attrib & an->attrib ) + typemask |= 1ul << kt->type; + + StringCat2S(name,sizeof(name),"KCL$",an->name); + DefineIntVar(vm,name,typemask); + } + + uint i; + for ( i = N_KCL_FLAG; i < NN_KCL_FLAG; i++ ) + { + StringCat2S(name,sizeof(name),"FLAG$",GetFlagName(i)); + DefineIntVar(vm,name,i); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError FastCreateRawKCL +( + kcl_t * kcl, // pointer to valid KCL + bool add_sig // true: add signature +) +{ + DASSERT(kcl); + DASSERT(!kcl->model_modified); + DASSERT(kcl->raw_data); + DASSERT(kcl->raw_data_size); + + + //--- setup section offsets + + const kcl_head_t * kclhead = (kcl_head_t*)kcl->raw_data; + u32 sect_off[N_KCL_SECT]; + DASSERT( sizeof(sect_off) == sizeof(kclhead->sect_off) ); + + uint i; + for ( i = 0; i < N_KCL_SECT; i++ ) + sect_off[i] = be32(kclhead->sect_off+i); + sect_off[2] += 0x10; + + + //--- compare number triangles + + const uint n_tri = ( sect_off[3] - sect_off[2] ) / sizeof(kcl_triangle_t); + KCL_ACTION_LOG("FastCreateRawKCL() N=%u\n",n_tri); + if ( n_tri != kcl->tridata.used ) + { + PRINT("N-TRI: %u != %u\n",n_tri,kcl->tridata.used ); + kcl->model_modified = true; + return CreateRawKCL(kcl,add_sig); + } + + + //--- store modified flags + + kcl_triangle_t *tri = (kcl_triangle_t*)( kcl->raw_data + sect_off[2] ); + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *end = td + n_tri; + + for ( ; td < end; td++, tri++ ) + write_be16(&tri->flag,td->cur_flag); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AddSignature ( float3List_t *vertex, bool fast ) +{ + // the first normal is always a signature + float3 sig; + memcpy(&sig,KCL_SIGNATURE,8); + sig.x = bef4(&sig.x); + sig.y = bef4(&sig.y); + sig.z = strtod(VERSION_NUM,0); + FindInsertFloatF3L(vertex,&sig,fast); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindInsertFloatWithMask ( float3List_t *f3l, float3 *val, u32 mask ) +{ + float3 temp; + temp.u[0] = val->u[0] & mask; + temp.u[1] = val->u[1] & mask; + temp.u[2] = val->u[2] & mask; + return FindInsertFloatF3L(f3l,&temp,false); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawKCL +( + kcl_t * kcl, // pointer to valid KCL + bool add_sig // true: add signature +) +{ + DASSERT(kcl); + TRACE("CreateRawKCL()\n"); + + if ( !kcl->model_modified + && kcl->raw_data + && kcl->raw_data_size + && kcl->octree_valid ) + { + return FastCreateRawKCL(kcl,add_sig); + } + + if ( CreateOctreeKCL(kcl) == ERR_OK ) + add_sig = true; + + KCL_ACTION_LOG("CreateRawKCL()\n"); + + //--- calculate vetex and normal lists + + CalcNormalsKCL(kcl,false); + + static const int round_tab[] = { 17, 14, 11, 8, 6, 4, 3, 2, 1, 0 }; + const int *round_ptr = KCL_MODE & KCLMD_ROUND ? round_tab+1 : round_tab; + uint start_normals = 0, round_steps = 0; + + const int tiny_level = ( KCL_MODE & KCLMD_M_TINY ) >> KCLMD_S_TINY; + const tiny_param_t *tp = TinyParam + tiny_level; + const uint n_tri = kcl->tridata.used; + const bool fast = kcl->fast && n_tri < 0x4000 && !tp->kcl_round; + float3List_t vertex, normal; + + // vertex + 4*normal + flag + typedef u16 vertex_index_t; + vertex_index_t *index_list = CALLOC(6*n_tri,sizeof(*index_list)); + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + for(;;) + { + InitializeF3L(&vertex,n_tri); + InitializeF3L(&normal,n_tri*4); + + vertex_index_t *idx = index_list; + if (tp->kcl_mask) + { + for ( td = td_base; td < td_end; td++ ) + { + float3 temp; + temp.x = RoundF(td->pt->x,tp->kcl_round); + temp.y = RoundF(td->pt->y,tp->kcl_round); + temp.z = RoundF(td->pt->z,tp->kcl_round); + *idx++ = FindInsertFloatF3L(&vertex,&temp,false); + + *idx++ = FindInsertFloatWithMask(&normal,td->normal+0,tp->kcl_mask); + *idx++ = FindInsertFloatWithMask(&normal,td->normal+1,tp->kcl_mask); + *idx++ = FindInsertFloatWithMask(&normal,td->normal+2,tp->kcl_mask); + *idx++ = FindInsertFloatWithMask(&normal,td->normal+3,tp->kcl_mask); + + *idx++ = td->cur_flag; + } + } + else + { + if ( add_sig && !tiny_level ) + AddSignature(&vertex,fast); + + for ( td = td_base; td < td_end; td++ ) + { + float3 temp; + temp.x = td->pt->x; + temp.y = td->pt->y; + temp.z = td->pt->z; + *idx++ = FindInsertFloatF3L(&vertex,&temp,fast); + + *idx++ = FindInsertFloatF3L(&normal,td->normal+0,fast); + *idx++ = FindInsertFloatF3L(&normal,td->normal+1,fast); + *idx++ = FindInsertFloatF3L(&normal,td->normal+2,fast); + *idx++ = FindInsertFloatF3L(&normal,td->normal+3,fast); + + *idx++ = td->cur_flag; + } + } + + if ( normal.used < 0x10000 ) + { + if (start_normals) + { + KCL_ACTION_LOG("Number of normals reduced from %u to %u [steps=%u]\n", + start_normals, normal.used, round_steps ); + } + break; + } + + if (!start_normals) + start_normals = normal.used; + round_steps++; + + const int round = *round_ptr++; + if (!round) + { + return ERROR0(ERR_INVALID_DATA, + "Unable to limit normals list to <65536 elements (have %u): %s\n", + normal.used, kcl->fname ); + } + + #if defined(TEST) || 0 + KCL_ACTION_LOG("Have %u normals: try to reduce to 65535 (level %u)\n", + normal.used, round ); + #endif + + for ( td = td_base; td < td_end; td++ ) + { + // need cast to avoid gcc warning 'above array bounds' + float *f = (float*)&td->normal->x; + float *f_end = (float*)( (u8*)f + sizeof(td->normal) ); + while ( f < f_end ) + { + *f = ldexpf(truncf(ldexpf(*f,round)),-round); + f++; + } + } + } + + + //--- calculate data size + + const uint vertex_size = sizeof(float3) * vertex.used; + const uint normal_size = sizeof(float3) * normal.used; + const uint triangle_size = sizeof(kcl_triangle_t) * n_tri; + + uint data_size = sizeof(kcl_head_t) + + vertex_size + + normal_size + + triangle_size + + kcl->octree_size; + + + //--- alloc data + + u8 *old_data = 0; + if (kcl->raw_data_alloced) + old_data = kcl->raw_data; // octree may use this data + + kcl_head_t * kclhead = CALLOC(1,data_size); + kcl->raw_data = (u8*)kclhead; + kcl->raw_data_size = data_size; + kcl->raw_data_alloced = true; + + PRINT("CREATE KCL: size = %#x = %u\n",data_size,data_size); + + + //--- write header + + const uint vertex_offset = sizeof(kcl_head_t); + const uint normal_offset = vertex_offset + vertex_size; + const uint triangle_offset = normal_offset + normal_size; + const uint octree_offset = triangle_offset + triangle_size; + + write_be32(kclhead->sect_off+0, vertex_offset); + write_be32(kclhead->sect_off+1, normal_offset); + write_be32(kclhead->sect_off+2, triangle_offset - 0x10 ); + write_be32(kclhead->sect_off+3, octree_offset); + + write_bef4(kclhead->min_octree+0, kcl->min_octree.x); + write_bef4(kclhead->min_octree+1, kcl->min_octree.y); + write_bef4(kclhead->min_octree+2, kcl->min_octree.z); + write_be32(kclhead->mask+0, kcl->mask[0]); + write_be32(kclhead->mask+1, kcl->mask[1]); + write_be32(kclhead->mask+2, kcl->mask[2]); + write_be32(&kclhead->coord_rshift, kcl->coord_rshift); + write_be32(&kclhead->y_lshift, kcl->y_lshift); + write_be32(&kclhead->z_lshift, kcl->z_lshift); + write_bef4(&kclhead->unknown_0x10, kcl->unknown_0x10); + write_bef4(&kclhead->unknown_0x38, kcl->unknown_0x38); + + + //--- transfer verticies + + float *src = vertex.list->v; + float *dest = (float*)(kcl->raw_data + vertex_offset); + write_bef4n(dest,src,3*vertex.used); + + + //--- transfer normals + + src = normal.list->v; + dest = (float*)(kcl->raw_data + normal_offset); + write_bef4n(dest,src,3*normal.used); + + + //--- transfer triangles + + const vertex_index_t *idx = index_list; + kcl_triangle_t *tdest = (kcl_triangle_t*)( kcl->raw_data + triangle_offset ); + for ( td = td_base; td < td_end; td++, tdest++, idx+=6 ) + { + write_bef4(&tdest->length,td->length); + write_be16n(&tdest->idx_vertex,idx,6); + } + + + //--- transfer octree + + DASSERT( octree_offset + kcl->octree_size == kcl->raw_data_size ); + memcpy( kcl->raw_data+octree_offset, kcl->octree, kcl->octree_size ); + + + //--- terminate + + ResetF3L(&vertex); + ResetF3L(&normal); + FREE(index_list); + + kcl->model_modified = false; + + if (!kcl->octree_alloced) + { + // is part of 'old_data' + kcl->octree = 0; + kcl->octree_size = 0; + kcl->octree_alloced = false; + } + FREE(old_data); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(kcl); + DASSERT(fname); + PRINT("SaveRawKCL(%s,%d)\n",fname,set_time); + + //--- create raw data + + enumError err = CreateRawKCL(kcl,true); + if (err) + return err; + DASSERT(kcl->raw_data); + DASSERT(kcl->raw_data_size); + KCL_ACTION_LOG("SaveRawKCL(%s) N=%u, model_modified=%d\n", + fname, kcl->tridata.used, kcl->model_modified ); + + //--- write to file + + File_t F; + err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + if ( fwrite(kcl->raw_data,1,kcl->raw_data_size,F.f) != kcl->raw_data_size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + // use DOS/Windows line format -> unix can handle it ;) + // obj file format: + // http://en.wikipedia.org/wiki/Wavefront_.obj_file + // http://www.martinreddy.net/gfx/3d/OBJ.spec + + DASSERT(kcl); + DASSERT(fname); + + KCL_ACTION_LOG("SaveTextKCL(%s) N=%u, model_modified=%d\n", + fname, kcl->tridata.used, kcl->model_modified ); + + static ccp sep = "#\f\r\n#-----------------------------------------------\r\n"; + static ccp head = "\r\n%s# %u %s\r\n\r\n"; + + if ( KCL_MODE & KCLMD_NORMALS ) + CalcNormalsKCL(kcl,false); + + const uint n_tri = kcl->tridata.used; + uint p, n_vert, n_norm, n_valid_tri = 0; + + uint *flag_count = AllocFlagCount(); + typedef u32 vertex_index_t; + vertex_index_t *index_list = CALLOC(4*n_tri,sizeof(*index_list)); + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + float3List_t vertex, normal; + const bool print_tri = ( KCL_MODE & KCLMD_TRIANGLES ) != 0; + if ( print_tri || kcl->fast ) + { + InitializeF3L(&vertex,0); + InitializeF3L(&normal,0); + + // setup of 'index_list' is delayed; + + for ( td = td_base; td < td_end; td++ ) + if ( !(td->status & TD_INVALID) ) + n_valid_tri++; + + n_vert = print_tri ? 0 : 3 * n_valid_tri; + n_norm = !print_tri && KCL_MODE & KCLMD_NORMALS ? n_valid_tri : 0; + } + else + { + InitializeF3L(&vertex,n_tri*3); + InitializeF3L(&normal,n_tri); + + vertex_index_t *idx = index_list; + for ( td = td_base; td < td_end; td++ ) + { + if ( !(td->status & TD_INVALID) ) + { + n_valid_tri++; + for ( p = 0; p < 3; p++ ) + { + float3 temp; + temp.x = td->pt[p].x; + temp.y = td->pt[p].y; + temp.z = td->pt[p].z; + *idx++ = FindInsertFloatF3L(&vertex,&temp,false) + 1; + } + *idx++ = ( KCL_MODE & KCLMD_NORMALS ) + ? FindInsertFloatF3L(&normal,td->normal+0,false) + 1 + : 0; + } + } + n_vert = vertex.used; + n_norm = normal.used; + } + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + goto abort; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + const uint n_grp = CountFlagsKCL(kcl,flag_count); + PRINT("n_vert=%u, n_norm=%u, n_tri=%u/%u, n_grp=%u\n", + n_vert, n_norm, n_valid_tri, n_tri, n_grp ); + + + //--- print header + + fprintf(F.f, text_kcl_head_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, GetKclMode(), + n_vert, n_norm, n_valid_tri, n_grp ); + + uint i; + for ( i = 0; i <= N_KCL_TYPE; i++ ) + { + int j, j_max, j_inc; + if ( i < N_KCL_TYPE ) + { + j = i; + j_max = N_KCL_FLAG; + j_inc = N_KCL_TYPE; + } + else + { + j = N_KCL_FLAG; + j_max = KCL_FLAG_USER_0 + n_user_color; + j_inc = 1; + } + for ( ; j < j_max; j += j_inc ) + if (flag_count[j]) + fprintf(F.f,"# %8u triangles in group %s\r\n", + flag_count[j], GetGroupNameKCL(j,true) ); + } + + char mtl_fname[PATH_MAX]; + const bool have_mtl = !print_tri && !F.is_stdio + && KCL_MODE & (KCLMD_MTL|KCLMD_WIIMM); + if (have_mtl) + { + char *dest = StringCopyS(mtl_fname,sizeof(mtl_fname),fname); + if ( KCL_MODE & KCLMD_WIIMM ) + { + dest = strrchr(mtl_fname,'/'); + dest = dest ? dest+1 : mtl_fname; + StringCopyE(dest,mtl_fname+sizeof(mtl_fname),"wiimm" KCL_MTL_EXT); + fprintf(F.f,"\r\n\r\nmtllib %s\r\n",dest); + } + else + { + char *found = strrchr(mtl_fname,'.'); + if ( found && !strcasecmp(found,".obj") ) + dest = found; + StringCopyE(dest,mtl_fname+sizeof(mtl_fname),KCL_MTL_EXT); + + found = strrchr(mtl_fname,'/'); + found = found ? found+1 : mtl_fname; + fprintf(F.f,"\r\n\r\nmtllib %s\r\n",found); + } + } + + + //--- print vertices + + if (n_vert) + { + fprintf(F.f,head,sep,n_vert,"vertices"); + + if (kcl->fast) + { + vertex_index_t *idx = index_list; + uint vidx = 1, nidx = 1; + for ( td = td_base; td < td_end; td++ ) + { + if ( !(td->status & TD_INVALID) ) + { + fprintf(F.f,"\r\n" + "v %11.3f %11.3f %11.3f\r\n" + "v %11.3f %11.3f %11.3f\r\n" + "v %11.3f %11.3f %11.3f\r\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->pt[1].x, td->pt[1].y, td->pt[1].z, + td->pt[2].x, td->pt[2].y, td->pt[2].z ); + *idx++ = vidx++; + *idx++ = vidx++; + *idx++ = vidx++; + *idx++ = nidx++; + } + } + } + else + { + float3 *f3 = vertex.list; + float3 *end = f3 + vertex.used; + for ( ; f3 < end; f3++ ) + fprintf(F.f, "v %11.3f %11.3f %11.3f\r\n", f3->x, f3->y, f3->z ); + } + } + + //--- print normals + + if (n_norm) + { + fprintf(F.f,head,sep,n_norm,"normals"); + + if (kcl->fast) + { + for ( td = td_base; td < td_end; td++ ) + if ( !(td->status & TD_INVALID) ) + fprintf(F.f, + "vn %11.3f %11.3f %11.3f\r\n", + td->normal->x, td->normal->y, td->normal->z ); + } + else + { + float3 *f3 = normal.list; + float3 *end = f3 + normal.used; + for ( ; f3 < end; f3++ ) + fprintf(F.f, + "vn %11.3f %11.3f %11.3f\r\n", + f3->x, f3->y, f3->z ); + } + } + + + //--- print groups and faces + + uint idx2, idx3; + if ( KCL_MODE & KCLMD_OUT_SWAP ) + idx2 = 2, idx3 = 1; + else + idx2 = 1, idx3 = 2; + + uint class_idx[N_KCL_CLASS] = {0}; + for ( i = 0; i <= N_KCL_TYPE; i++ ) + { + const kcl_class_t *cls = kcl_class + kcl_type[i].cls; + uint col_idx = class_idx[cls->cls]; + uint col_max = cls->ncol; + + int j, j_max, j_inc; + if ( i < N_KCL_TYPE ) + { + j = i; + j_max = N_KCL_FLAG; + j_inc = N_KCL_TYPE; + } + else + { + j = N_KCL_FLAG; + j_max = KCL_FLAG_USER_0 + n_user_color; + j_inc = 1; + } + + for ( ; j < j_max; j += j_inc ) + { + if (!flag_count[j]) + continue; + + ccp grp_name = GetGroupNameKCL(j,false); + fprintf(F.f, + "\r\n%s" + "# %u triangles in group '%s'\r\n" + "# -> flag 0x%04x => type 0x%02x, variant 0x%03x\r\n" + "\r\n" + "g %s\r\n" + "s off\r\n" + , sep + , flag_count[j], grp_name + , j, i < N_KCL_TYPE ? i : 0x100, i < N_KCL_TYPE ? j >> 5 : j & 0xffff + , grp_name + ); + + if (have_mtl) + { + if ( i < N_KCL_TYPE ) + { + fprintf(F.f,"usemtl %s_%u\r\n\r\n",cls->name,++col_idx); + if ( col_idx >= col_max ) + col_idx = 0; + } + else + fprintf(F.f,"usemtl %s\r\n\r\n",grp_name); + } + else + fputs("\r\n",F.f); + + if (print_tri) + { + for ( td = td_base; td < td_end; td++ ) + if ( td->cur_flag == j && !(td->status & TD_INVALID) ) + { + fprintf(F.f, + "TRI 0x%04x | %11.3f %11.3f %11.3f" + " | %11.3f %11.3f %11.3f" + " | %11.3f %11.3f %11.3f\r\n", + GetExportFlagKCL(td->cur_flag), + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->pt[1].x, td->pt[1].y, td->pt[1].z, + td->pt[2].x, td->pt[2].y, td->pt[2].z ); + } + #ifdef TEST0 + else + fprintf(F.f,"# flag=%x,%x, j=%x\n",td->in_flag,td->cur_flag,j); + #endif + } + else if (n_norm) + { + for ( td = td_base; td < td_end; td++ ) + if ( td->cur_flag == j && !(td->status & TD_INVALID) ) + { + vertex_index_t *idx = index_list + 4*(td-td_base); + fprintf(F.f, + "f %u//%u %u//%u %u//%u\r\n", + idx[0], idx[3], + idx[idx2], idx[3], + idx[idx3], idx[3] ); + } + } + else + { + for ( td = td_base; td < td_end; td++ ) + if ( td->cur_flag == j && !(td->status & TD_INVALID) ) + { + vertex_index_t *idx = index_list + 4*(td-td_base); + fprintf(F.f, + "f %u %u %u\r\n", + idx[0], idx[idx2], idx[idx3] ); + } + } + } + class_idx[cls->cls] = col_idx ; + } + + + //--- terminate + + fprintf(F.f,"\r\n%s# END\r\n" + "# define an empty last group as workaround for a SketchUp importer bug\r\n" + "g empty_group\r\n" + "\r\n" + ,sep); + ResetFile(&F,set_time); + + //--- create mtl file + + if (have_mtl) + { + enumError err = CreateFileOpt(&F,true,mtl_fname,testmode,0); + if ( err <= ERR_WARNING && F.f ) + { + SetFileAttrib(&F.fatt,&kcl->fatt,0); + #if 1 + u8 *data; + uint size; + if (!DecodeBZIP2(&data,&size,0,text_obj_mtl_bz2,sizeof(text_obj_mtl_bz2))) + fwrite(data,size,1,F.f); + FREE(data); + #else + fputs(kcl_mtl,F.f); + #endif + + uint ci; + for ( ci = 0; ci < n_user_color; ci++ ) + { + fprintf(F.f,"newmtl %s\r\n",GetGroupNameKCL(ci+KCL_FLAG_USER_0,0)); + Color_t col = user_color[ci]; + if ( col.a != 0xff ) + { + const float a = col.a / 255.0; + fprintf(F.f,"\td %5.3f\r\n\tTr %5.3f\r\n",a,1.0-a); + } + + const float r = col.r / 255.0; + const float g = col.g / 255.0; + const float b = col.b / 255.0; + fprintf(F.f, + "\tKa %5.3f %5.3f %5.3f\r\n" + "\tKd %5.3f %5.3f %5.3f\r\n" + "\tKs %5.3f %5.3f %5.3f\r\n" + "\r\n", + r, g, b, + r, g, b, + r, g, b ); + } + } + ResetFile(&F,set_time); + } + + abort: + FREE(flag_count); + FREE(index_list); + ResetF3L(&vertex); + ResetF3L(&normal); + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrintTriangles() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool PrintTriangles ( File_t *F, kcl_t *kcl, bool have_used ) +{ + DASSERT(F); + DASSERT(kcl); + + const uint n_tri = kcl->tridata.used; + if (!n_tri) + return false; + + FILE *f = F->f; + fprintf(f,text_kcl_dump_tridata_cr,n_tri); + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + for ( td = td_base; td < td_end; td++ ) + { + fprintf(f,"#%u\n",(int)(td-td_base)); + + const float3 *f3 = td->normal; + fprintf(f,"T.N0 %11.6f %11.6f %11.6f | 0x%04x -> 0x%04x\r\n", + f3->x, f3->y, f3->z, + td->in_flag, td->cur_flag ); + + f3++; + fprintf(f,"T.N1 %11.6f %11.6f %11.6f | %s\r\n", + f3->x, f3->y, f3->z, + td->status & TD_FIXED_PT ? "fixed" : "-" ); + + f3++; + fprintf(f,"T.N2 %11.6f %11.6f %11.6f | %svalid", + f3->x, f3->y, f3->z, + td->status & TD_INVALID ? "in" : "" ); + + if (have_used) + fprintf(f,", %sused", td->status & TD_UNUSED ? "un" : "" ); + + f3++; + fprintf(f,"\r\nT.N3 %11.6f %11.6f %11.6f |\r\n", + f3->x, f3->y, f3->z ); + + #if SUPPORT_KCL_DELTA + fprintf(f,"T.P1 %11.3f %11.3f %11.3f | len %g%s, delta %g/%g = %g\r\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->length, + td->length, < opt_tri_height ? " (too small)" : "", + td->delta_max, td->delta_min, + td->delta_min > 0 ? td->delta_max / td->delta_min : 0.0 ); + #else + fprintf(f,"T.P1 %11.3f %11.3f %11.3f | len %g%s\r\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, + td->length, + td->length < opt_tri_height ? " (too small)" : "" ); + #endif + + int p; + for ( p = 1; p < 3; p++ ) + fprintf(f,"T.P%u %11.3f %11.3f %11.3f | %10.3f %10.3f %10.3f \r\n", + p+1, td->pt[p].x, td->pt[p].y, td->pt[p].z, + td->pt[p].x - td->pt[0].x, + td->pt[p].y - td->pt[0].y, + td->pt[p].z - td->pt[0].z ); + + tri_metrics_t met; + CalcTriMetricsByPoints(&met,td->pt,td->pt+1,td->pt+2); + fprintf(f,"T.LEN %11.3f %11.3f %11.3f | area %g%s\r\n", + met.l1, met.l2, met.l3, met.area, + met.area < opt_tri_area ? " (too small)" : "" ); + fprintf(f,"T.HT %11.3f %11.3f %11.3f | min %g%s, max %g\r\n", + met.h1, met.h2, met.h3, met.min_ht, + met.min_ht < opt_tri_height ? " (too small)" : "", + met.max_ht ); + + fputs("\r\n",f); + } + + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DumpKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u32 dump_o_table +( + // returns next offset + + FILE *f, // output file + kcl_t *kcl, // valid kcl + u32 offset, // offset to print + u32 empty_offset, // offset of empty list + uint n, // number of elements to print + int count // >=0: print usage counter +) +{ + DASSERT(f); + DASSERT(kcl); + DASSERT(kcl->octree); + DASSERT(kcl->o_depth); + DASSERT(kcl->o_count); + DASSERT(kcl->l_count); + + noPRINT("dump_o_table: off=%x, n=%x\n",offset,n); + + if ( offset >= kcl->octree_size ) + return kcl->octree_size; + + const uint max_n = ( kcl->octree_size - offset ) / 4; + if ( n > max_n ) + n = max_n; + + const u32 baseoff = offset; + + while ( n > 0 ) + { + fprintf(f,"O.%06x:",offset); + + uint i, max = n < 8 ? n : 8; + n -= max; + for ( i = 0; i < max; i++ ) + { + if ( i == 4 ) + fputc(' ',f); + u32 val = be32(kcl->octree+offset); + offset += 4; + if ( val & 0x80000000 ) + { + val = ( val & 0x7ffffff ) + baseoff + 2; + if ( val == empty_offset ) + fprintf(f," -"); + else + fprintf(f," L.%06x",val); + } + else + { + val += baseoff; + fprintf(f," %8x",val & 0x7ffffff); + } + } + if ( count >= 0 ) + { + fprintf(f," # %2u*\r\n",count); + count = -1; + } + else + fputs("\r\n",f); + } + return offset; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool print_tridata, // true: triangle list + uint print_octree // octree print level (0..2) +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(kcl); + DASSERT(fname); + + CalcNormalsKCL(kcl,false); + CalcMinMaxKCL(kcl); + CreateOctreeKCL(kcl); + CalcStatisticsKCL(kcl); + CreateRawKCL(kcl,false); + //const uint blow_size = CalcBlowSizeKCL(kcl); + KCL_ACTION_LOG("DumpKCL(%s,%d,%d)\n",fname,print_tridata,print_octree); + + PRINT("DumpKCL(%s,PT=%d,PO=%d) N(td)=%u\n", + fname, print_tridata, print_octree, kcl->tridata.used ); + + + //--- calculate number of verteces and normals + + const uint n_tri = kcl->tridata.used; + uint n_vert, n_norm; + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + if (kcl->fast) + { + n_vert = 3 * n_tri; + n_norm = KCL_MODE & KCLMD_NORMALS ? n_tri : 0; + } + else + { + #if HAVE_PRINT + u_usec_t start_time = GetTimerUSec(); + #endif + float3List_t vertex, normal; + InitializeF3L(&vertex,n_tri); + InitializeF3L(&normal,n_tri*4); + //AddSignature(&vertex,false); + + for ( td = td_base; td < td_end; td++ ) + { + float3 temp; + temp.x = td->pt->x; + temp.y = td->pt->y; + temp.z = td->pt->z; + FindInsertFloatF3L(&vertex,&temp,false); + + FindInsertFloatF3L(&normal,td->normal+0,false); + FindInsertFloatF3L(&normal,td->normal+1,false); + FindInsertFloatF3L(&normal,td->normal+2,false); + FindInsertFloatF3L(&normal,td->normal+3,false); + } + n_vert = vertex.used; + n_norm = normal.used; + #if HAVE_PRINT + uint end_time = GetTimerUSec(); + PRINT(">> %u/%u/%u verteces and %u/%u/%u normals inserted in %6.4f sec.\n", + vertex.used, vertex.size, n_tri, + normal.used, normal.size, 4*n_tri, + 1e-6 * (end_time-start_time) ); + #endif + ResetF3L(&vertex); + ResetF3L(&normal); + } + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + + //--- print header + + if (kcl->signature > 0.0 ) + fprintf(F.f,"\r\nH.signature = %s v%4.2f\r\n", + kcl->signature_info, kcl->signature ); + + fprintf(F.f,text_kcl_dump_head_cr, + n_vert, n_norm, n_tri, + n_tri - kcl->n_tri_used, kcl->tri_fix_count, kcl->tri_inv_count, + kcl->octree_size, kcl->raw_data_size, + + kcl->unknown_0x10, kcl->unknown_0x38, + + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + kcl->min.x, kcl->min.y, kcl->min.z, + kcl->mean.x, kcl->mean.y, kcl->mean.z, + kcl->max.x, kcl->max.y, kcl->max.z, + kcl->max_octree.x, kcl->max_octree.y, kcl->max_octree.z, + + kcl->mask[0], kcl->mask[1], kcl->mask[2], + kcl->coord_rshift, kcl->y_lshift, kcl->z_lshift, + kcl->mask_bits[0], kcl->mask_bits[1], kcl->mask_bits[2], + kcl->bcube_bits[0], kcl->bcube_bits[1], kcl->bcube_bits[2], + kcl->n_bcubes[0], kcl->n_bcubes[1], kcl->n_bcubes[2], + + kcl->tmin_dist, + kcl->tmax_dist, + + kcl->bcube_width, + kcl->bcube_width >> kcl->min_depth, + kcl->bcube_width >> kcl->max_depth, + //blow_size, + kcl->total_bcubes, + kcl->n_cube_nodes, + kcl->total_cubes, + kcl->max_depth, + kcl->ave_depth, + + kcl->n_0_list, + kcl->n_tri_list, + kcl->n_0_link, + kcl->n_tri_link + ); + + uint di; + //for ( di = 0; di < KCL_MAX_STAT_DEPTH && di <= kcl->max_depth; di++ ) + for ( di = 0; di < KCL_MAX_STAT_DEPTH; di++ ) + if (kcl->max_depth_tri_len[di]) + fprintf(F.f,"S.max_tri_size.%-2u= %8u" + " # max number of triangles in all lists of depth %u\r\n", + di-1, kcl->max_depth_tri_len[di], di-1 ); + + fprintf(F.f, + "S.max_tri_size = %8u # maximum number of triangles in all lists\r\n" + "S.ave_tri_size = %11.2f # average number of triangles in all lists\r\n\r\n", + kcl->max_tri_len, // max_tri_size + (double)kcl->sum_tri_len/kcl->n_tri_list // ave_tri_size + ); + + + //--- print triangles + + if (print_tridata) + { + fputs("#\f\r\n" + "########################################" + "#######################################\r\n",F.f); + + PrintTriangles(&F,kcl,true); + } + + + //--- print octree + + if ( print_octree && kcl->octree_valid && kcl->octree ) + { + fprintf(F.f,text_kcl_dump_octree_cr, + kcl->total_bcubes, kcl->total_cubes - kcl->total_bcubes, + kcl->n_tri_list ); + + + u32 empty_offset = kcl->octree_size >= 2 + && !be16(kcl->octree+kcl->octree_size-2) + ? kcl->octree_size - 2 : 0; + + u32 offset = dump_o_table(F.f,kcl,0,empty_offset,kcl->total_bcubes,-1); + fputs("\r\n",F.f); + + while ( offset < kcl->max_o_offset && offset < kcl->octree_size ) + offset = dump_o_table(F.f,kcl,offset,empty_offset,8, + kcl->o_count[(offset-kcl->min_o_offset)/0x20]); + + if ( print_octree > 1 ) + { + fprintf(F.f,text_kcl_dump_octlist_cr); + + uint li; + for ( li = 0; li < kcl->n_trilist; li++ ) + { + if ( kcl->l_count[li] ) + { + offset = li * 2 + kcl->min_l_offset; + fprintf(F.f,"L.%06x [%2u*]:",offset,kcl->l_count[li]); + for(;;) + { + const u16 tidx = be16(kcl->octree+offset); + if (!tidx) + break; + fprintf(F.f," %u",tidx-1); + offset += 2; + } + fputs("\r\n",F.f); + } + } + fputs("\r\n",F.f); + } + } + + + //--- terminate + + ResetFile(&F,false); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DumpTrianglesKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpTrianglesKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname // filename of destination +) +{ + DASSERT(kcl); + DASSERT(fname); + + KCL_ACTION_LOG("DumpTrianglesKCL(%s)\n",fname); + PRINT("DumpTrianglesKCL(%s)\n",fname); + CalcNormalsKCL(kcl,false); + + if (kcl->octree_valid) + CalcStatisticsKCL(kcl); + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + PrintTriangles(&F,kcl,kcl->octree_valid); + ResetFile(&F,false); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ListTrianglesKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ListTrianglesKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool print_normals, // true: print large tables + uint precision // floating point pre: 0=+0, .. 3=+6 +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(kcl); + DASSERT(fname); + + CalcNormalsKCL(kcl,false); + CalcStatisticsKCL(kcl); + + KCL_ACTION_LOG("ListTrianglesKCL(%s,%d,%u)\n",fname,print_normals,precision); + + PRINT("ListTrianglesKCL(%s,PN=%d,PREC=%u) N=%u\n", + fname, print_normals, precision, kcl->tridata.used ); + + if ( precision > 5 ) + precision = 5; + const uint prec_pt = precision; + const uint prec_len = precision + 1; + const uint prec_norm = precision + 2; + const uint fw_pt = 7 + prec_pt + (prec_pt>0); + const uint fw_len = 6 + prec_len; + const uint fw_norm = 3 + prec_norm; + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kcl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kcl->fatt,0); + + + //--- print data + + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + uint i; + for ( i = 0; i < kcl->tridata.used; i++, td++ ) + { + fprintf(F.f, + "%04x %c %*.*f", + td->cur_flag, + !kcl->used_valid ? '?' : td->status & TD_UNUSED ? '-' : '+', + fw_len, prec_len, td->length + ); + + uint ti; + const double3 *d3 = td->pt; + for ( ti = 0; ti < 3; ti++, d3++ ) + fprintf(F.f,"%s %*.*f %*.*f %*.*f", + ti ? "," : " |", + fw_pt, prec_pt, d3->x, + fw_pt, prec_pt, d3->y, + fw_pt, prec_pt, d3->z ); + + if (print_normals) + { + uint ni; + const float3 *f3 = td->normal; + for ( ni = 0; ni < 4; ni++, f3++ ) + { + fprintf(F.f,"%s %*.*f %*.*f %*.*f", + ni ? "," : " |", + fw_norm, prec_norm, f3->x, + fw_norm, prec_norm, f3->y, + fw_norm, prec_norm, f3->z ); + } + } + fputs("\r\n",F.f); + } + + + //--- terminate + + ResetFile(&F,false); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// TraverseOctreeKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int TraverseOctree +( + // returns -2: octree invalid + // -1: octree data invalid + // >=0: number of triangles + + kcl_t *kcl, // pointer to valid KCL + FILE *f, // log file + double3 pt, // point to analyze + int verbosity, // log verbose level + // <=0: silent + // >=1: print warnings + // >=2: print base calculations + // >=3: print step calculations + double3 *cube_min, // not NULL: store cube data here + uint *cube_width, // not NULL: store cube width here + u16 **tri_list // not NULL: store pointer to + // triangle list here +) +{ + DASSERT(kcl); + if ( !kcl->octree || !kcl->octree_valid ) + return -1; + + if (cube_min) + memset(cube_min,0,sizeof(*cube_min)); + if (cube_width) + *cube_width = 0.0; + if (tri_list) + *tri_list = 0; + + + //--- test 'min_octree' + + if ( pt.x < kcl->min_octree.x + || pt.y < kcl->min_octree.y + || pt.z < kcl->min_octree.z ) + { + if ( verbosity > 0 ) + fprintf(f, + "# No triangles: At least one coordinate is too small.\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * min border: %11.3f %11.3f %11.3f\n" + "#\n", + pt.x, pt.y, pt.z, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z ); + return 0; + } + + double3 basept; + Sub3(basept,pt,kcl->min_octree); + + if ( basept.x > 2e9 || basept.y > 2e9 || basept.z > 2e9 ) + { + if ( verbosity > 0 ) + fprintf(f, + "# No triangles: At least one coordinate is too large (diff>10^9).\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * point - min: %11.3f %11.3f %11.3f\n" + "#\n", + pt.x, pt.y, pt.z, + basept.x, basept.y, basept.z ); + return 0; + } + + + //--- create and test integer coordinates + + u32 idx_mask = 1 << kcl->coord_rshift; // same as current cube width! + + double3 cmin; + uint p, failed = 0; + u32 base[3], relevant[3], nmask[3], nbits[3]; + for ( p = 0; p < 3; p++ ) + { + cmin.v[p] = trunc( basept.v[p] / idx_mask ) * idx_mask + kcl->min_octree.v[p]; + base[p] = round(basept.v[p]); + u32 mask = kcl->mask[p]; + relevant[p] = ( base[p] & ~mask ) >> kcl->coord_rshift << kcl->coord_rshift; + if ( base[p] & mask ) + failed++; + + if (!mask) + nmask[p] = 32; + else + { + nmask[p] = 0; + for ( ; !(mask&1); mask >>= 1 ) + nmask[p]++; + } + nbits[p] = nmask[p] > kcl->coord_rshift + ? nmask[p] - kcl->coord_rshift + : 0; + } + + if (failed) + { + if ( verbosity > 0 ) + fprintf(f, + "# No triangles: Mask test failed.\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * min border: %11.3f %11.3f %11.3f\n" + "# * relevant pt: %11u %11u %11u\n" + "# * relevant pt: %#11x %#11x %#11x\n" + "# * mask: %#11x %#11x %#11x\n" + "#\n", + pt.x, pt.y, pt.z, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + base[0], base[1], base[2], + base[0], base[1], base[2], + kcl->mask[0], kcl->mask[1], kcl->mask[2] ); + return ERR_OK; + } + + u32 oct_index = base[0] >> kcl->coord_rshift + | base[1] >> kcl->coord_rshift << kcl->y_lshift + | base[2] >> kcl->coord_rshift << kcl->z_lshift; + + u32 baseoff = 0; + u32 offset = oct_index * 4; + + if ( verbosity > 1 ) + { + fprintf(f,"# Base values:\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * min border: %11.3f %11.3f %11.3f\n" + "# * relevant pt: %11u %11u %11u\n" + "# * relevant pt: %#11x %#11x %#11x\n" + "# * mask: %#11x %#11x %#11x\n" + "# * relevant bits: %11u %11u %11u\n" + "# * index bits: %11u %11u %11u (rshift=%u)\n" + "# * index pt: %#11x %#11x %#11x\n" + "# * start index: %#x | %#x | %#x == %#x => offset=%#x\n" + "# * cube min: %11.3f %11.3f %11.3f (width=%u)\n" + "# * cube max: %11.3f %11.3f %11.3f\n" + "#\n", + pt.x, pt.y, pt.z, + kcl->min_octree.x, kcl->min_octree.y, kcl->min_octree.z, + base[0], base[1], base[2], + base[0], base[1], base[2], + kcl->mask[0], kcl->mask[1], kcl->mask[2], + nmask[0], nmask[1], nmask[2], + nbits[0], nbits[1], nbits[2], kcl->coord_rshift, + relevant[0], relevant[1], relevant[2], + base[0] >> kcl->coord_rshift, + base[1] >> kcl->coord_rshift << kcl->y_lshift, + base[2] >> kcl->coord_rshift << kcl->z_lshift, + oct_index, offset, + cmin.x, cmin.y, cmin.z, idx_mask, + cmin.x+idx_mask, cmin.y+idx_mask, cmin.z+idx_mask ); + } + + + //--- traverse the octree + + uint step; + for ( step=1;; step++ ) + { + if ( baseoff + 0x20 > kcl->octree_size ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because octree index out of bounds!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Size: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, kcl->octree_size ); + return -1; + } + + if ( offset & 3 ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because invalid octree index!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Low 2 bits: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, offset&3 ); + return -1; + } + + u32 val = be32( kcl->octree + offset ); + if ( val & 0x80000000 ) + { + offset = baseoff + ( val & 0x7ffffff ) + 2; + if ( offset+2 > kcl->octree_size ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because triangle list index out of bounds!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Size: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, kcl->octree_size ); + return -1; + } + + if ( offset & 1 ) + { + if ( verbosity > 0 ) + fprintf(f,"# Abort, because invalid triangle list index!\n" + "# * point: %11.3f %11.3f %11.3f\n" + "# * Offset: %#10x\n" + "# * Low bit: %#10x\n" + "#\n", + pt.x, pt.y, pt.z, + offset, offset&1 ); + return -1; + } + + if (cube_min) + *cube_min = cmin; + if (cube_width) + *cube_width = idx_mask; + if (tri_list) + *tri_list = (u16*)( kcl->octree + offset ); + + u32 off; + for ( off = offset; off < kcl->octree_size; off += 2 ) + if (!be16( kcl->octree + off )) + break; + return ( off - offset ) / 2; + } + + uint next_index = 0; + idx_mask >>= 1; + if ( idx_mask & base[0] ) + { + next_index |= 1; + cmin.x += idx_mask; + } + if ( idx_mask & base[1] ) + { + next_index |= 2; + cmin.y += idx_mask; + } + if ( idx_mask & base[2] ) + { + next_index |= 4; + cmin.z += idx_mask; + } + baseoff += val; + offset = baseoff + 4*next_index; + + if ( verbosity > 2 ) + { + fprintf(f,"# Step #%u, next octree entry found, value %#x:\n" + "# * mask %#x, index %u+%u+%u = %u, offset %#x,%#x\n" + "# * cube min: %11.3f %11.3f %11.3f (width=%u)\n" + "# * cube max: %11.3f %11.3f %11.3f\n" + "#\n", + step, val, + idx_mask, + next_index & 1, next_index & 2, next_index & 4, + next_index, baseoff, offset, + cmin.x, cmin.y, cmin.z, idx_mask, + cmin.x+idx_mask, cmin.y+idx_mask, cmin.z+idx_mask ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError TraverseOctreeKCL +( + kcl_t *kcl, // pointer to valid KCL + FILE *f, // output file, if NULL set verbosity:=0 + double3 pt, // point to analyze + bool print_tri_val, // true: print tri values instead of index + uint verbosity // verbose level + // >=1: print base calculations + // >=2: print step calculations +) +{ + DASSERT(kcl); + CreateOctreeKCL(kcl); + u16 *tri_ptr; + int stat = TraverseOctree(kcl,f,pt,verbosity+1,0,0,&tri_ptr); + if ( stat < 0 ) + return ERR_WARNING; + + const int fw_tlist = GetTermWidth(80,40) - 7; + + if (!f) + verbosity = 0; + else if (verbosity>0) + fputc('\n',f); + fprintf(f,"List with %u triangles found at offset %#x\n", + stat, (int)((u8*)tri_ptr - kcl->octree) ); + + if (!print_tri_val) + { + uint pcount = fprintf(f," - ");; + for ( ; stat > 0; stat--, tri_ptr++ ) + { + uint tidx = be16(tri_ptr); + if (!tidx--) + break; + if ( pcount > fw_tlist ) + pcount = fprintf(f,"\n "); + pcount += fprintf(f," %u%s", + tidx, + tidx <= kcl->tridata.used ? "" : "!" ); + } + } + else + { + fprintf(f,"\n * searched pt: %11.3f %11.3f %11.3f\n\n", + pt.x, pt.y, pt.z ); + + for ( ; stat > 0; stat--, tri_ptr++ ) + { + uint tidx = be16(tri_ptr); + if (!tidx--) + break; + if ( tidx < kcl->tridata.used ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list + tidx; + fprintf(f, + " * triangle pt 1: %11.3f %11.3f %11.3f (index %u)\n" + " * pt 2: %11.3f %11.3f %11.3f (flag 0x%04x)\n" + " * pt 3: %11.3f %11.3f %11.3f\n" + "\n", + td->pt[0].x, td->pt[0].y, td->pt[0].z, tidx, + td->pt[1].x, td->pt[1].y, td->pt[1].z, td->cur_flag, + td->pt[2].x, td->pt[2].y, td->pt[2].z ); + } + else + fprintf(f," * Invalid triangle, index = %u = %#x\n", + tidx, tidx ); + + } + } + fputs("\n\n",f); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// FallKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define N_FALL_LIST 10 + +typedef struct kcl_fall_t +{ + kcl_t * kcl; // related kcl + FILE *f; // log file + int verbose; // verbose level + u32 type_mask; // kcl type selector (bit field) + + kcl_cube_t cube; // cube to test + uint width; // width of cube + + u16 *tri_list[N_FALL_LIST]; // list to tri lists + uint n_tri_list; // number of valid 'tri_list' + + bool last_valid; // last values are valid + double3 last_min; // last traversed cube min + uint last_width; // last traversed cube width + +} kcl_fall_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int Collide2 +( + kcl_fall_t *kf, // valid data + double3 pt // point for octree search +) +{ + DASSERT(kf); + noPRINT("Collide2() n=%u, v=%d, pt=%.0f %.0f %.0f\n", + kf->n_tri_list, kf->last_valid, pt.x, pt.y, pt.z ); + + + //--- test if point is in previous cube + + if ( kf->last_valid + && pt.x >= kf->last_min.x && pt.x <= kf->last_min.x + kf->last_width + && pt.y >= kf->last_min.y && pt.y <= kf->last_min.y + kf->last_width + && pt.z >= kf->last_min.z && pt.z <= kf->last_min.z + kf->last_width + ) + { + return -1; + } + + //--- find tri list + + u16 *tri_list; + int stat = TraverseOctree(kf->kcl,kf->f,pt, + kf->verbose-1, &kf->last_min,&kf->last_width,&tri_list); + if ( stat < 0 ) + return -1; + + kf->last_valid = true; + if ( !tri_list || !*tri_list ) + return -1; + + uint i; + for ( i = 0; i < kf->n_tri_list; i++ ) + if (kf->tri_list[i] == tri_list ) + return -1; + + if ( kf->n_tri_list < N_FALL_LIST ) + kf->tri_list[kf->n_tri_list++] = tri_list; + + uint ti; + for ( ti = 0; ti < stat; ti++ ) + { + DASSERT(tri_list); + uint tidx = be16(tri_list+ti); + if (!tidx--) + break; + if ( tidx >= kf->kcl->tridata.used ) + continue; + + const kcl_tridata_t *td = (kcl_tridata_t*)kf->kcl->tridata.list + tidx; + if ( td->cur_flag >= N_KCL_TYPE || ( 1u << (td->cur_flag&0x1f) & kf->type_mask ) ) + { + kcl_tri_t ot; + #if SUPPORT_KCL_CUBE + setup_oct_tri(kf->kcl,&ot,0,td); // use [[cube]] list param? + if (OctCubeTriangleOverlaped(&kf->cube,&ot,0)) // [use [[cube]] list param? + return td->cur_flag; + #else + setup_oct_tri(kf->kcl,&ot,td); + if (OctCubeTriangleOverlaped(&kf->cube,&ot)) + return td->cur_flag; + #endif + } + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int Collide1 +( + kcl_fall_t *kf, // valid data + double y // y point to test +) +{ + DASSERT(kf); + kf->n_tri_list = 0; + kf->last_valid = false; + + kf->cube.min[1] = round ( y - kf->kcl->min_octree.y ); + kf->cube.max[1] = kf->cube.min[1] + kf->width; + + noPRINT("OCT=%d,%d,%d .. %d,%d,%d\n", + kf->cube.min[0], kf->cube.min[1], kf->cube.min[2], + kf->cube.max[0], kf->cube.max[1], kf->cube.max[2] ); + + double3 pt; + double3 *min = &kf->kcl->min_octree; + pt.x = min->x + ( kf->cube.min[0] + kf->cube.max[0] ) / 2; + pt.y = min->y + kf->cube.min[1]; + pt.z = min->z + ( kf->cube.min[2] + kf->cube.max[2] ) / 2; + int stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->x + kf->cube.min[0]; + pt.z = min->z + kf->cube.min[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.max[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->x + kf->cube.max[0]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.min[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.y = min->y + kf->cube.max[1]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.max[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->y + kf->cube.min[0]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.z = min->z + kf->cube.min[2]; + stat = Collide2(kf,pt); + if ( stat >= 0 ) + return stat; + + pt.x = min->x + ( kf->cube.min[0] + kf->cube.max[0] ) / 2; + pt.z = min->z + ( kf->cube.min[2] + kf->cube.max[2] ) / 2; + return Collide2(kf,pt); +} + +/////////////////////////////////////////////////////////////////////////////// + +double FallKCL +( + // returns the height or -1.0 on fail + + kcl_t *kcl, // pointer to valid KCL + FILE *f, // output file, if NULL: verbose:=0 + double3 pt, // mid point of cube to analyze + uint width, // width of cube + int verbosity, // verbose level + // <=0: silent + // >=1: log triangle search + // >=2: print base calculations + // >=3: print step calculations + u32 type_mask, // kcl type selector (bit field) + int *res_kcl_flag // not NULL: store KCL flag here +) +{ + DASSERT(kcl); + CreateOctreeKCL(kcl); + if ( !kcl->octree || !kcl->octree_valid || pt.y < 0.0 ) + return -1.0; + + if ( width < 1 ) + width = 1; + + kcl_fall_t kf; + memset(&kf,0,sizeof(kf)); + kf.kcl = kcl; + kf.f = f; + kf.verbose = f ? verbosity : 0; + kf.width = width; + kf.type_mask = type_mask; + + uint p; + for ( p = 0; p < 3; p++ ) + { + kf.cube.min[p] = round ( pt.v[p] - kcl->min_octree.v[p] - width/2); + kf.cube.max[p] = kf.cube.min[p] + width; + } + + if ( width > 1 ) + width--; + + // very first test + int stat = Collide1(&kf,pt.y); + if ( stat >= 0 ) + { + if (res_kcl_flag) + *res_kcl_flag = stat; + return pt.y; + } + + pt.y = ceil( pt.y - width ); // round only if really falling! + for(;;) + { + stat = Collide1(&kf,pt.y); + if ( stat >= 0 ) + break; + if ( pt.y <= 0.0 ) + { + if (res_kcl_flag) + *res_kcl_flag = -1; + return -1.0; + } + pt.y -= width; + if ( pt.y < 0.0 ) + pt.y = 0.0; + } + + pt.y += width; + while ( width > 1 ) + { + width = (width+1)/2; + pt.y -= width; + int stat2 = Collide1(&kf,pt.y); + if ( stat2 >= 0 ) + { + stat = stat2; + pt.y += width; + } + } + + if ( pt.y < 0.0 ) + { + pt.y = -1.0; + stat = -1; + } + + if (res_kcl_flag) + *res_kcl_flag = stat; + return pt.y; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// drop+sort unused triangles /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static kcl_t *kcl_sort = 0; + +static int sort_triangle ( const u16 *ia, const u16 *ib ) +{ + DASSERT(kcl_sort); + + kcl_tridata_t *ta = (kcl_tridata_t*)kcl_sort->tridata.list + *ia; + kcl_tridata_t *tb = (kcl_tridata_t*)kcl_sort->tridata.list + *ib; + if ( ta->length != tb->length ) + { + double absa = fabs(ta->length); + double absb = fabs(tb->length); + if ( absa != absb ) + return absa < absb ? -1 : +1; + return ta->length < tb->length ? -1 : +1; + } + + double *pa = ta->pt->v; + double *pb = tb->pt->v; + uint count; + for ( count = 0; count < 9; count++, pa++, pb++ ) + if ( *pa != *pb ) + return *pa < *pb ? -1 : +1; + + if ( ta->cur_flag != tb->cur_flag ) + return ta->cur_flag < tb->cur_flag ? -1 : +1; + + return ia < ib ? -1 : ia > ib; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DropSortHelper +( + kcl_t *kcl, // valid KCL + uint drop_mask, // not 0: drop triangles with set bits + bool sort // true: sort triangles +) +{ + DASSERT(kcl); + if (sort) + CalcNormalsKCL(kcl,false); + KCL_ACTION_LOG("DropSortTriangles(,drop=%x,sort=%d)\n",drop_mask,sort); + + + //--- setup list + + const uint n_tri = kcl->tridata.used; + if (!n_tri) + return false; + u16 *list = CALLOC(n_tri,sizeof(*list)); + bool dirty = false; + + + //--- drop unreferenced triangles + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + + uint n_list = 0; + if (drop_mask) + { + uint n_invalid = 0, n_fixed = 0; + for ( td = td_base; td < td_end; td++ ) + { + if ( !(td->status & drop_mask) ) + { + list[n_list++] = td - td_base; + if ( td->status & TD_INVALID ) + n_invalid++; + else if ( td->status & TD_FIXED_PT ) + n_fixed++; + } + else if ( !(td->status & TD_UNUSED) ) + kcl->octree_valid = false; // a used triangles was removed + } + kcl->tri_inv_count = n_invalid; + kcl->tri_fix_count = n_fixed; + if ( n_list < n_tri ) + { + dirty = true; + kcl->min_max_valid = false; + } + } + else + { + for ( ; n_list < n_tri; n_list++ ) + list[n_list] = n_list; + } + + #if HAVE_PRINT + { + uint i, max = n_list < 10 ? n_list : 10; + PRINT("INDEX-LIST [%u/%u/%u,dirty=%u]:",max,n_list,n_tri,dirty); + for ( i = 0; i < max; i++ ) + fprintf(stderr," %u",list[i]); + fputc('\n',stderr); + } + #endif + + + //--- sort triangles + + if ( sort && n_list > 1 ) + { + dirty = true; // assume that sort will change anything + kcl_sort = kcl; + qsort( list, n_list, sizeof(*list), (qsort_func)sort_triangle ); + kcl_sort = 0; + } + + + //--- fix kcl->tridata + + if (dirty) + { + kcl->model_modified = true; + + kcl_tridata_t *tsrc = (kcl_tridata_t*)kcl->tridata.list; + kcl->tridata.list = 0; + ResetList(&kcl->tridata); + kcl_tridata_t *tdest = GrowList(&kcl->tridata,n_list); + + uint idx; + for ( idx = 0; idx < n_list; idx++, tdest++ ) + memcpy(tdest,tsrc+list[idx],sizeof(*tdest)); + FREE(tsrc); + } + + + //--- fix kcl->octree + + if ( dirty && kcl->octree && kcl->octree_valid ) + { + // we need a reverse list! + u16 *rlist = CALLOC(n_tri+1,sizeof(*rlist)); + uint idx; + for ( idx = 0; idx < n_list; idx++ ) + rlist[list[idx]+1] = idx+1; + + // now we fix the octree + u16 *ptr = (u16*)(kcl->octree + kcl->min_l_offset); + u16 *end = (u16*)(kcl->octree + kcl->max_l_offset); + for ( ; ptr < end; ptr++ ) + write_be16(ptr,rlist[be16(ptr)]); + + FREE(rlist); + } + + + //--- terminate + + FREE(list); + if (dirty) + ResetStatisticsKCL(kcl); + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DropSortTriangles ( kcl_t * kcl ) +{ + DASSERT(kcl); + if (!drop_sort_triangles) + return false; + + kcl_mode_t local_mode = KCL_MODE; + if ( enable_kcl_drop_auto > 0 && local_mode & KCLMD_DROP_AUTO ) + { + local_mode |= kcl->octree_valid && kcl->fform_outfile == FF_KCL + ? KCLMD_DROP_UNUSED + : KCLMD_M_DROP; + PRINT("DROP-AUTO[%d,%d] => %llx -> %llx\n", + kcl->octree_valid, kcl->fform_outfile == FF_KCL, + (u64)KCL_MODE, (u64)local_mode ); + } + + kcl_tridata_status_t mode = 0; + if ( local_mode & KCLMD_DROP_UNUSED ) mode |= TD_UNUSED; + if ( local_mode & KCLMD_DROP_FIXED ) mode |= TD_FIXED_PT; + if ( local_mode & KCLMD_DROP_INVALID ) mode |= TD_INVALID; + PRINT("DROP-MODE: %x\n",mode); + + bool sort_triangles = 0 != ( local_mode & KCLMD_SORT ); + PRINT("*** DropSortTriangles() mode=%x|%u=%u ***\n", + mode, sort_triangles, drop_sort_triangles ); + + if ( local_mode & KCLMD_DROP_UNUSED && kcl->octree_valid ) + CalcStatisticsKCL(kcl); + + if ( local_mode & (KCLMD_DROP_INVALID|KCLMD_RM_FACEDOWN + |KCLMD_RM_FACEUP|KCLMD_CONV_FACEUP) ) + { + CalcNormalsKCL(kcl,false); + if ( local_mode & (KCLMD_RM_FACEDOWN|KCLMD_RM_FACEUP|KCLMD_CONV_FACEUP) ) + { + mode |= TD_MARK; + + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + td->status &= ~TD_MARK; + } + } + + if ( local_mode & KCLMD_RM_FACEDOWN ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + { + if ( td->normal[0].y <= -0.5 ) + { + DASSERT(!(td->status&TD_INVALID_NORM)); + const int type = GET_KCL_TYPE(td->cur_flag); + if ( type >= 0 && kcl_type[type].attrib & KCLT_DRIVE ) + td->status |= TD_MARK; + } + } + } + + if ( local_mode & (KCLMD_RM_FACEUP|KCLMD_CONV_FACEUP) ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + { + if ( td->normal[0].y > 0.985 ) + { + DASSERT(!(td->status&TD_INVALID_NORM)); + const int type = GET_KCL_TYPE(td->cur_flag); + if ( type >= 0 && kcl_type[type].attrib & KCLT_C_WALL ) + { + if ( local_mode & KCLMD_CONV_FACEUP ) + td->cur_flag = 0; + else + td->status |= TD_MARK; + } + } + } + } + + if ( local_mode & KCLMD_WEAK_WALLS ) + { + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td_end = td + kcl->tridata.used; + for ( ; td < td_end; td++ ) + { + const int type = GET_KCL_TYPE(td->cur_flag); + if ( type >= 0 && kcl_type[type].attrib & KCLT_C_WALL ) + td->cur_flag |= 0x8000; + } + } + + return DropSortHelper( kcl, mode, sort_triangles ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL Script /////////////// +/////////////////////////////////////////////////////////////////////////////// + +StringField_t kcl_script_list = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKclScript ( ccp arg ) +{ + if ( arg && *arg ) + { + if (!kcl_script_list.used) + have_patch_count++, have_kcl_patch_count++; + InsertStringField(&kcl_script_list,arg,false); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool ScriptKCL +( + kcl_t *kcl, // valid KCL data + bool is_first // true: called at first place +) +{ + DASSERT(kcl); + + if ( (KCL_MODE & KCLMD_F_SCRIPT) ? !is_first : is_first ) + return false; + + PRINT("ScriptKCL(,%d)\n",is_first); + + //--- load source file once + + static uint used = 0; + static u8 **data = 0; + static uint *data_size = 0; + + if ( !used && kcl_script_list.used ) + { + used = kcl_script_list.used; + + // script files are only loaded once + data = CALLOC( kcl_script_list.used, sizeof(*data) ); + data_size = CALLOC( kcl_script_list.used, sizeof(*data_size) ); + + uint i, active = 0; + for ( i = 0; i < used; i++ ) + { + OpenReadFILE(kcl_script_list.field[i],0,false,data+i,data_size+i,0,0); + if ( data[i] && data_size[i] ) + active++; + } + + if (!active) + { + ResetStringField(&kcl_script_list); + have_patch_count--, have_kcl_patch_count--; + used = 0; + } + } + + if (!used) + return false; + + uint i; + for ( i = 0; i < used && i < kcl_script_list.used; i++ ) + { + if ( data[i] && data_size[i] ) + { + ccp fname = kcl_script_list.field[i]; + KCL_ACTION_LOG("ScriptKCL() %s\n",fname); + + ScanInfo_t si; + InitializeSI(&si,(ccp)data[i],data_size[i],fname,REVISION_NUM); + si.kcl = kcl; + si.predef = SetupVarsKCL(); + + kcl->script_tri_removed = false; + kcl->script_tri_added = false; + kcl->script_pt_modified = false; + + while (NextLineSI(&si,false,true)) + ; //--- scan source + + ResetSI(&si); + } + } + + //--- transfer results + + bool sort_triangles = false; + + if ( kcl->script_tri_added || kcl->script_pt_modified ) + { + kcl->model_modified = true; + CalcNormalsKCL(kcl,true); + kcl->octree_valid = kcl->min_max_valid = kcl->stat_valid = false; + if ( KCL_MODE & KCLMD_SORT ) + sort_triangles = true; + } + + if (kcl->script_tri_removed) + { + if ( KCL_MODE & KCLMD_SORT ) + sort_triangles = true; + kcl->octree_valid = false; + DropSortHelper(kcl,TD_REMOVED,sort_triangles); + sort_triangles = false; + } + else if (sort_triangles) + DropSortHelper(kcl,0,true); + + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transform KCL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool TransformKCL +( + kcl_t * kcl // pointer to valid KMP +) +{ + DASSERT(kcl); + if ( have_patch_count <= 0 || !transform_active || disable_transformation > 0 ) + return false; + + KCL_ACTION_LOG("TransformKCL()\n"); + + const uint n_tri = kcl->tridata.used; + if (!n_tri) + return false; + + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + TransformPosDouble3D(td->pt[0].v,n_tri,sizeof(*td)); + TransformPosDouble3D(td->pt[1].v,n_tri,sizeof(*td)); + TransformPosDouble3D(td->pt[2].v,n_tri,sizeof(*td)); + // [[xtridata]] + + kcl_tridata_t *td_end = td + n_tri; + for ( ; td < td_end; td++ ) + td->status |= TD_INVALID_NORM; + + kcl->octree_valid = kcl->min_max_valid = kcl->stat_valid + = kcl->norm_valid = false; + + kcl->model_modified = true; + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Patch KCL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u16 *patch_kcl_flag = 0; // NULL or 'N_KCL_FLAG' elements + +/////////////////////////////////////////////////////////////////////////////// + +void RemovePatchKclFlag() +{ + if (patch_kcl_flag) + { + have_patch_count--, have_kcl_patch_count--; + FREE(patch_kcl_flag); + patch_kcl_flag = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PurgePatchKclFlag() +{ + if (patch_kcl_flag) + { + uint i; + bool have_patch = false; + for ( i = 0; i < N_KCL_FLAG; i++ ) + if ( patch_kcl_flag[i] != i ) + { + have_patch = true; + break; + } + if (!have_patch) + RemovePatchKclFlag(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPatchKclFlag() +{ + if (!patch_kcl_flag) + { + have_patch_count++, have_kcl_patch_count++; + patch_kcl_flag = MALLOC( N_KCL_FLAG * sizeof(*patch_kcl_flag) ); + } + + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + patch_kcl_flag[i] = i; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupPatchKclFlag() +{ + if (!patch_kcl_flag) + ResetPatchKclFlag(); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKclFlag ( ccp arg ) +{ + if (!arg) + { + RemovePatchKclFlag(); + return 0; + } + + ResetPatchKclFlag(); + for(;;) + { + //--- skip spaces & ',' + + while ( *arg && (uchar)*arg <= ' ' || *arg == ',' ) + arg++; + if (!*arg) + break; + + //--- find '=' + + ccp old = arg; + for ( ; *arg != '='; arg++ ) + { + if ( !*arg || *arg == ',' ) + { + ERROR0(ERR_SYNTAX, + "Missing '=' in parameter of --kcl-flag: %s\n",old); + goto abort; + } + } + DASSERT( *arg == '=' ); + arg++; + + //--- scan new value + + u32 new_val, valid; + arg = ScanNumU32(arg,&valid,&new_val,0,N_KCL_FLAG-1); + if (!valid) + { + ERROR0(ERR_SYNTAX, + "Missing number in parameter of --kcl-flag: %s\n",arg); + goto abort; + } + PRINT("NEW-VAL: - %04x\n",new_val); + + + //--- now scan old values (=index) and insert new value into table + + for(;;) + { + //--- skip spaces & '+' + + while ( *old && (uchar)*old <= ' ' || *old == '+' ) + old++; + if ( *old == '=' ) + break; + + + //--- test for 'T' prefix + + const bool have_t = *old == 't' || *old == 'T'; + if (have_t) + { + old++; + while ( *old && (uchar)*old <= ' ' ) + old++; + } + + + //--- scan range + + u32 old1, old2; + old = ScanRangeU32(old,&valid,&old1,&old2,0, + have_t ? N_KCL_TYPE-1 : N_KCL_FLAG-1 ); + if (!valid) + { + ERROR0(ERR_SYNTAX, + "Missing number in parameter of --kcl-flag: %s\n",old); + goto abort; + } + DASSERT( old1 >= 0 ); + DASSERT( old2 < N_KCL_FLAG ); + + while ( *old && (uchar)*old <= ' ' ) + old++; + + + //--- scan mask + + u32 mask = 0xffff; + if (have_t) + mask = N_KCL_TYPE-1; + else if ( *old == '/' ) + { + old++; + old = ScanNumU32(old,&valid,&mask,0,N_KCL_FLAG-1); + + while ( *old && (uchar)*old <= ' ' ) + old++; + } + + if ( !valid || *old != '+' && *old != '=' ) + { + ERROR0(ERR_SYNTAX, + "Missing '=' in parameter of --kcl-flag: %s\n",old); + goto abort; + } + + + //--- normalize params + + if (mask) + { + const u32 xmask = (u16)~mask; + u32 val = old1 & mask; + const u32 end = old2 | xmask; + const u32 newv = new_val & mask; + + PRINT("OLD-VAL: %s %04x..%04x / %04x => %04x..%04x := %04x\n", + have_t ? "T" : " ", old1, old2, mask, val, end, newv ); + + for ( ; val <= end; val++ ) + { + const u16 mval = val & mask; + if ( mval >= old1 && mval <= old2 ) + patch_kcl_flag[val] = val & xmask | newv; + } + } + } + } + PurgePatchKclFlag(); + + #if defined(DEBUG) && defined(TEST) + if (patch_kcl_flag) + { + PRINT("## HAVE KCL PATCH [%d,%d]\n",have_patch_count,have_kcl_patch_count); + #if 0 + for ( i = 0; i < N_KCL_FLAG; i++ ) + if ( patch_kcl_flag[i] != i ) + PRINT(" %04x := %04x\n", i, patch_kcl_flag[i] ); + #endif + } + #endif + + return 0; + + abort: + PRINT("ABORT KCL-FLAG\n"); + RemovePatchKclFlag(); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTriArea ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --tri-area",0); + si.float_div++; + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + if (!err) + { + const double d = GetDoubleV(&temp); + if ( d >= 0 ) + opt_tri_area = d; + CheckEolSI(&si); + ResetSI(&si); + } + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTriHeight ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --tri-height",0); + si.float_div++; + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + if (!err) + { + const double d = GetDoubleV(&temp); + if ( d >= 0 ) + opt_tri_height = d; + CheckEolSI(&si); + ResetSI(&si); + } + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRawDataKCL +( + void * data, // data to scan + uint data_size, // size of 'data' + ccp fname // file name for error messages +) +{ + if ( have_patch_count <= 0 || have_kcl_patch_count <= 0 && !transform_active ) + return false; + kcl_analyze_t ka; + if ( IsValidKCL(&ka,data,data_size,data_size,0) >= VALID_ERROR ) + return false; + + KCL_ACTION_LOG("PatchRawDataKCL: %s\n",fname); + PRINT("** PatchRawDataKCL() **\n"); + + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = FF_KCL; + enumError err = ScanRawKCL(&kcl,false,data,data_size,true); + kcl.fast = false; + + bool stat = false; + if ( !err && PatchKCL(&kcl) ) + { + enumError err = CreateRawKCL(&kcl,false); + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => round normals.\n", + kcl.raw_data_size - data_size ); + + RoundNormalsKCL(&kcl,16); + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + #if 0 // this has sometimes negative effect (enlarge the data) + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => round points.\n", + kcl.raw_data_size - data_size ); + + RoundPointsKCL(&kcl,1); + RoundNormalsKCL(&kcl,15); + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + #endif + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => enlarge triangle lists.\n", + kcl.raw_data_size - data_size ); + + RoundNormalsKCL(&kcl,15); + if ( kcl.cube_blow > 400 ) + kcl.cube_blow = 400; + kcl.max_cube_triangles *= 2; + kcl.octree_valid = false; + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => new octree param.\n", + kcl.raw_data_size - data_size ); + + if ( kcl.cube_blow > 250 ) + kcl.cube_blow = 250; + kcl.min_cube_size *= 2; + kcl.max_cube_triangles *= 2; + kcl.max_octree_depth -= 1; + kcl.octree_valid = false; + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + if ( !err && kcl.raw_data_size > data_size ) + { + KCL_ACTION_LOG( + "New KCL is %u bytes too large => new octree param again.\n", + kcl.raw_data_size - data_size ); + + RoundNormalsKCL(&kcl,14); + if ( kcl.cube_blow > 150 ) + kcl.cube_blow = 150; + kcl.octree_valid = false; + kcl.model_modified = true; + err = CreateRawKCL(&kcl,false); + } + + if (err) + ERROR0(err,"Creation of new KCL failed: %s\n",fname); + else if ( kcl.raw_data_size > data_size ) + ERROR0(ERR_WARNING, + "Creation of new KCL failed," + " because new KCL data is too large (%u>%u): %s\n", + kcl.raw_data_size, data_size, fname ); + else + { + KCL_ACTION_LOG("Replace KCL [inplace], size %u -> %u.\n", + data_size, kcl.raw_data_size ); + memcpy(data,kcl.raw_data,kcl.raw_data_size); + stat = true; + } + } + ResetKCL(&kcl); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchKCL +( + kcl_t * kcl // pointer to valid KCL +) +{ + DASSERT(kcl); + if ( have_patch_count <= 0 || have_kcl_patch_count <= 0 && !transform_active ) + return false; + + KCL_ACTION_LOG("PatchKCL() KCL=%d\n",kcl->fform_outfile == FF_KCL); + + const bool stat + = ScriptKCL(kcl,true) + | DropSortTriangles(kcl) + | TransformKCL(kcl) + | ScriptKCL(kcl,false) + | patch_kcl_flag != 0 + | ( !kcl->octree_valid && kcl->fform_outfile == FF_KCL ); + + CheckMetricsTriData( (kcl_tridata_t*)kcl->tridata.list, kcl->tridata.used, kcl ); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// octree iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct oct_iter_t +{ + kcl_t *kcl; // pointer to valid KCL + void *param; // user parameter + oct_func func; // call back function + +} oct_iter_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int iterate_oct8 +( + const oct_iter_t *iter, // iteration parameter + u32 baseoff, // offset of current table + u32 offset, // offset to handle + const kcl_cube_t *cube // current octree +) +{ + DASSERT(iter); + DASSERT(iter->kcl); + DASSERT(iter->func); + DASSERT(cube); + + + if ( offset + 4 > iter->kcl->octree_size ) + return 0; + + u32 val = be32( iter->kcl->octree + offset ); + noPRINT("\t\t\t>>> %8x %8x [%08x]\n",baseoff,offset,val); + + if ( val & 0x80000000 ) + { + offset = baseoff + ( val & 0x7ffffff ) + 2; + if ( offset < iter->kcl->octree_size ) + { + const u16 *ptr, *list = (u16*)(iter->kcl->octree + offset); + for ( ptr = list; be16(ptr); ptr++ ) + ; + return iter->func(iter->kcl,iter->param,cube,list,ptr-list); + } + return 0; + } + + baseoff += val; + if ( val && baseoff + 0x20 <= iter->kcl->octree_size ) + { + const uint cube_size = ( cube->max[0] - cube->min[0] ) / 2; + + uint i; + for ( i = 0; i < 8; i++ ) + { + kcl_cube_t subcube; + subcube.min[0] = cube->min[0] + ( i&1 ? cube_size : 0 ); + subcube.max[0] = subcube.min[0] + cube_size; + subcube.min[1] = cube->min[1] + ( i&2 ? cube_size : 0 ); + subcube.max[1] = subcube.min[1] + cube_size; + subcube.min[2] = cube->min[2] + ( i&4 ? cube_size : 0 ); + subcube.max[2] = subcube.min[2] + cube_size; + const int stat = iterate_oct8(iter,baseoff,baseoff+i*4,&subcube); + if (stat) + return stat; + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int OctreeIteratorKCL +( + kcl_t *kcl, // pointer to valid KCL + oct_func func, // call back function + void *param // user parameter +) +{ + DASSERT(kcl); + DASSERT(func); + + CalcStatisticsKCL(kcl); + if (!kcl->octree_valid) + return -1; + + const uint cube_size = 1 << kcl->coord_rshift; + + const uint xn = 1 << kcl->bcube_bits[0]; + const uint yn = 1 << kcl->bcube_bits[1]; + const uint zn = 1 << kcl->bcube_bits[2]; + + oct_iter_t iter; + iter.kcl = kcl; + iter.func = func; + iter.param = param; + + u32 offset = 0; + + uint zi; + for ( zi = 0; zi < zn; zi++ ) + { + uint yi; + for ( yi = 0; yi < yn; yi++ ) + { + uint xi; + for ( xi = 0; xi < xn; xi++ ) + { + kcl_cube_t cube; + cube.min[0] = xi * cube_size; + cube.max[0] = cube.min[0] + cube_size; + cube.min[1] = yi * cube_size; + cube.max[1] = cube.min[1] + cube_size; + cube.min[2] = zi * cube_size; + cube.max[2] = cube.min[2] + cube_size; + + iterate_oct8(&iter,0,offset,&cube); + offset += 4; + } + } + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// calculate blow level /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kcl_blow_t +{ + uint min_blow; // minimum blow value + + uint n_tri; // number of triangles + kcl_tri_t *tlist; // list of triangles + +} kcl_blow_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int calc_blow +( + kcl_t *kcl, // pointer to valid KCL + void *param, // user parameter + const kcl_cube_t *cube, // pointer to current cube geometry + // relative to kcl->min_octree; + const u16 *tri_list, // NULL or pointer to triangle index list + uint n_list // number of elements in 'tri_list' +) +{ + DASSERT(kcl); + DASSERT(cube); + DASSERT( tri_list || !n_list ); + + kcl_blow_t *blow = param; + DASSERT(blow); + + noPRINT("** %7u,%7u,%7u .. %7u,%7u,%7u WD=%7u, N=%5u, D=%6zx, BLOW=%u\n", + cube->min[0], cube->min[1], cube->min[2], + cube->max[0], cube->max[1], cube->max[2], + cube->max[0] - cube->min[0], n_list, + tri_list ? (u8*)tri_list - kcl->octree : 0, blow->min_blow ); + + kcl_cube_t xcube; + xcube.min[0] = cube->min[0] - blow->min_blow; + xcube.min[1] = cube->min[1] - blow->min_blow; + xcube.min[2] = cube->min[2] - blow->min_blow; + xcube.max[0] = cube->max[0] + blow->min_blow; + xcube.max[1] = cube->max[1] + blow->min_blow; + xcube.max[2] = cube->max[2] + blow->min_blow; + + while ( n_list-- > 0 ) + { + const uint tidx = be16(tri_list++) - 1; + + #if SUPPORT_KCL_CUBE + // use [[cube]] list param? + noPRINT("%d/%u -> %d\n",tidx,blow->n_tri, + OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx,0)); + + // use [[cube]] list param? + if ( tidx >= blow->n_tri + || OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx,0) ) + { + continue; + } + #else + noPRINT("%d/%u -> %d\n",tidx,blow->n_tri, + OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx)); + if ( tidx >= blow->n_tri + || OctCubeTriangleOverlaped(&xcube,blow->tlist+tidx) ) + { + continue; + } + + #endif + + + //--- enlarge the box until all triangles are inside + + const uint grow_size = 512; + const int max_grow = 99999; + + uint grow, good_grow = blow->min_blow; + for ( grow = grow_size; ; grow *= 2 ) + { + uint grow_val = blow->min_blow + grow; + if ( grow_val > max_grow ) + grow_val = max_grow; + noPRINT("%5u : %6u + %6u = %6u\n", n_list, blow->min_blow, grow, grow_val ); + + kcl_cube_t ycube; + ycube.min[0] = cube->min[0] - grow_val; + ycube.min[1] = cube->min[1] - grow_val; + ycube.min[2] = cube->min[2] - grow_val; + ycube.max[0] = cube->max[0] + grow_val; + ycube.max[1] = cube->max[1] + grow_val; + ycube.max[2] = cube->max[2] + grow_val; + + #if SUPPORT_KCL_CUBE + // use [[cube]] list param? + if (OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx,0)) + break; + #else + if (OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx)) + break; + #endif + + if ( grow_val >= 99999 ) + { + blow->min_blow = 99999; + return 1; + } + good_grow = grow_val; + } + noPRINT("GROW (A) TO %u\n",blow->min_blow); + blow->min_blow = good_grow; + + + //--- now use a binary method to find the border + + for ( grow = grow_size / 2; grow; grow /= 2 ) + { + const uint grow_val = blow->min_blow + grow; + + kcl_cube_t ycube; + ycube.min[0] = cube->min[0] - grow_val; + ycube.min[1] = cube->min[1] - grow_val; + ycube.min[2] = cube->min[2] - grow_val; + ycube.max[0] = cube->max[0] + grow_val; + ycube.max[1] = cube->max[1] + grow_val; + ycube.max[2] = cube->max[2] + grow_val; + + #if SUPPORT_KCL_CUBE + // use [[cube]] list param? + if (!OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx,0)) + blow->min_blow = grow_val; + #else + if (!OctCubeTriangleOverlaped(&ycube,blow->tlist+tidx)) + blow->min_blow = grow_val; + #endif + } + noPRINT("GROW (B) TO %u\n",blow->min_blow); + + + //--- fix the xcube + + xcube.min[0] = cube->min[0] - blow->min_blow; + xcube.min[1] = cube->min[1] - blow->min_blow; + xcube.min[2] = cube->min[2] - blow->min_blow; + xcube.max[0] = cube->max[0] + blow->min_blow; + xcube.max[1] = cube->max[1] + blow->min_blow; + xcube.max[2] = cube->max[2] + blow->min_blow; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CalcBlowSizeKCL ( kcl_t *kcl ) +{ + DASSERT(kcl); + + KCL_ACTION_LOG("CalcBlowSizeKCL()\n"); + + kcl_blow_t blow; + memset(&blow,0,sizeof(blow)); + blow.n_tri = kcl->tridata.used; + blow.tlist = CALLOC(blow.n_tri,sizeof(*blow.tlist)); + + uint t; + kcl_tri_t *tri = blow.tlist; + const kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + for ( t = 0; t < blow.n_tri; t++, tri++, td++ ) + #if SUPPORT_KCL_CUBE + setup_oct_tri(kcl,tri,0,td); // use [[cube]] list param? + #else + setup_oct_tri(kcl,tri,td); + #endif + + OctreeIteratorKCL(kcl,calc_blow,&blow); + + FREE(blow.tlist); + return blow.min_blow; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CkeckKCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct check_kcl_t +{ + //--- param + + const kcl_t * kcl; // pointer to valid KCL + CheckMode_t mode; // print mode + + //--- status + + int warn_count; // number of warnings + int hint_count; // number of hints + int info_count; // number of infos + bool head_printed; // true: header line printed + bool sig_printed; // true: signature line printed + + //--- misc data + + ColorSet_t col; // color setup + +} check_kcl_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +// predefine function to avoid a warning because of __attribute__ usage +static void print_check_error +( + check_kcl_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_WARNING | CMOD_HINT | CMOD_INFO + ccp format, // format of message + ... // arguments + +) __attribute__ ((__format__(__printf__,3,4))); + +static void print_check_error +( + check_kcl_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_WARNING | CMOD_HINT | CMOD_INFO + ccp format, // format of message + ... // arguments + +) +{ + DASSERT(chk); + DASSERT(chk->kcl); + + ccp col; // message color + ccp bol; // begin of line + switch (print_mode) + { + case CMOD_WARNING: + if (!( chk->mode & CMOD_WARNING )) + return; + col = chk->col.warn; + bol = check_bowl; + chk->warn_count++; + break; + + case CMOD_HINT: + if (!( chk->mode & CMOD_HINT )) + return; + col = chk->col.hint; + bol = check_bohl; + chk->hint_count++; + break; + + case CMOD_INFO: + if (!( chk->mode & CMOD_INFO )) + return; + col = chk->col.info; + bol = check_boil; + chk->info_count++; + break; + + default: + col = 0; + bol = 0; // print head only + break; + } + + if (!chk->head_printed) + { + chk->head_printed = true; + if (chk->mode & (CMOD_HEADER|CMOD_FORCE_HEADER) ) + fprintf(stdlog,"%s* CHECK %s:%s%s\n", + chk->col.heading, GetNameFF(chk->kcl->fform,0), + chk->kcl->fname, chk->col.reset ); + } + + if ( !chk->sig_printed && chk->kcl->signature > 0.0 ) + { + chk->sig_printed = true; + chk->info_count++; + fprintf(stdlog,"%s%s%sSignature: %s v%4.2f\n", + chk->col.info, check_boil, chk->col.reset, + chk->kcl->signature_info, chk->kcl->signature ); + } + + if (!bol) + return; + + if (format) + { + fputs(col,stdlog); + fputs(bol,stdlog); + fputs(chk->col.reset,stdlog); + + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[xtridata]] + +int CheckKCL +( + // returns number of errors + + kcl_t * kcl, // pointer to valid KCL + CheckMode_t mode // print mode +) +{ + DASSERT(kcl); + PRINT("CheckKCL(%d)\n",mode); + + + //--- setup + + check_kcl_t chk; + memset(&chk,0,sizeof(chk)); + SetupColorSet(&chk.col,stdlog); + chk.kcl = kcl; + chk.mode = mode; + + if ( mode & CMOD_FORCE_HEADER || kcl->signature > 0.0 ) + print_check_error(&chk,0,0); + + + //--- special statistics + + uint count_0 = 0, count_30 = 0, total = 0, n = kcl->tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl->tridata.list; + + for ( ; n-- > 0; td++ ) + if ( td->cur_flag < N_KCL_FLAG + && kcl_type[ td->cur_flag & 0x1f ].attrib & KCLT_DRIVE ) + { + total++; + if ( td->status & TD_INVALID_NORM ) + CalcNormalsTriData(kcl,td,1); + if ( td->normal[0].y < 0.0 ) + { + count_0++; + if ( td->normal[0].y < -0.5 ) + count_30++; + } + } + if (count_0) + print_check_error(&chk,CMOD_HINT, + "%u of %u drivable triangle%s %s face down" + " => --kcl=RM-FACEDOWN\n", + count_0, total, + total == 1 ? "" : "s", + count_0 == 1 ? "is" : "are" ); + if (count_30) + print_check_error(&chk,CMOD_HINT, + "%u of %u drivable triangle%s %s face down (>30°).\n", + count_30, total, + total == 1 ? "" : "s", + count_30 == 1 ? "is" : "are" ); + + + //--- analyze statistics + + if (kcl->octree_valid) + { + CalcStatisticsKCL(kcl); + if ( kcl->n_tri_used < kcl->tridata.used ) + { + uint unused = kcl->tridata.used - kcl->n_tri_used; + print_check_error(&chk,CMOD_HINT, + "%u of %u triangle%s obsolete" + " (not referenced by the octree)" + " => --kcl=DROP-UNUSED\n", + unused, kcl->tridata.used, + unused == 1 ? " is" : "s are" ); + } + } + + if (kcl->tri_fix_count) + print_check_error(&chk,CMOD_HINT, + "%u of %u triangle%s had extreme values" + " => --kcl=DROP-FIXED\n", + kcl->tri_fix_count, kcl->tridata.used, + kcl->tridata.used == 1 ? "" : "s" ); + + if (kcl->tri_inv_count) + print_check_error(&chk,CMOD_WARNING, + "%u of %u triangle%s invalid values" + " => --kcl=DROP-INVALID\n", + kcl->tri_inv_count, kcl->tridata.used, + kcl->tridata.used == 1 ? " has" : "s have" ); + + if (kcl->n_invalid_tri_ref) + print_check_error(&chk,CMOD_WARNING, + "The octree contains %u invalid triangle reference%s!\n", + kcl->n_invalid_tri_ref, + kcl->n_invalid_tri_ref == 1 ? "" : "s" ); + + + //--- terminate + + if ( mode & CMOD_GLOBAL_STAT ) + { + global_warn_count += chk.warn_count; + global_hint_count += chk.hint_count; + global_info_count += chk.info_count; + } + + if ( mode & (CMOD_FOOTER|CMOD_FORCE_FOOTER) ) + { + static ccp hint + = " => see https://szs.wiimm.de/cmd/wkclt/check#desc for more info."; + + ccp bind; + char hbuf[60], ibuf[60]; + if (chk.info_count) + { + snprintf(ibuf,sizeof(ibuf)," and %s%u info%s%s", + chk.col.info, + chk.info_count, chk.info_count>1 ? "s" : "", + chk.col.heading ); + bind = ","; + } + else + { + *ibuf = 0; + bind = " and"; + } + + if (chk.hint_count) + snprintf(hbuf,sizeof(hbuf),"%s %s%u hint%s%s", + bind, chk.col.hint, + chk.hint_count, chk.hint_count>1 ? "s" : "", + chk.col.heading ); + else + *hbuf = 0; + + if (chk.warn_count) + fprintf(stdlog, + " %s=> %s%u warning%s%s%s%s for %s:%s\n%s%s%s\n\n", + chk.col.heading, chk.col.warn, + chk.warn_count, chk.warn_count == 1 ? "" : "s", + chk.col.heading, + hbuf, ibuf, GetNameFF(kcl->fform,0), kcl->fname, + chk.col.heading, hint, chk.col.reset ); + else if ( mode & CMOD_FORCE_FOOTER || chk.hint_count ) + fprintf(stdlog," %s=> No warnings%s%s for %s:%s\n%s%s%s\n\n", + chk.col.heading, hbuf, ibuf, GetNameFF(kcl->fform,0), kcl->fname, + chk.col.heading, hint, chk.col.reset ); + } + + return chk.warn_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl parser functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_n +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->kcl ? si->kcl->tridata.used : 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_pt +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + if (si) + { + const int pt_index = GetIntV(param+1); + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if ( td && pt_index >= 0 && pt_index <= 2 ) + { + AssignVectorV(res,td->pt+pt_index); + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_length +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + if ( td->status & TD_INVALID_NORM ) + CalcNormalsTriData(si->kcl,td,1); + res->d = td->length; + res->mode = VAR_DOUBLE; + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_normal +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + if (si) + { + const int pt_index = GetIntV(param+1); + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if ( td && pt_index >= 0 && pt_index <= 3 ) + { + if ( td->status & TD_INVALID_NORM ) + CalcNormalsTriData(si->kcl,td,1); + float *f = td->normal[pt_index].v; + res->x = *f++; + res->y = *f++; + res->z = *f; + res->mode = VAR_VECTOR; + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_setPt +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + if ( n_param > 3 ) + { + td->pt[0] = GetVectorV(param+1); + td->pt[1] = GetVectorV(param+2); + td->pt[2] = GetVectorV(param+3); + status = 1; + } + else + { + const int pt_index = GetIntV(param+1); + if ( pt_index >= 0 && pt_index <= 2 ) + { + td->pt[pt_index] = GetVectorV(param+2); + status = 1; + } + } + if (status>0) + { + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + } + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_scale +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + Var_t *v = ToVectorV(param+1); + if ( n_param > 2 ) + { + Var_t *origin = ToVectorV(param+2); + uint p; + for ( p = 0 ; p < 3; p++ ) + { + td->pt[p].x = ( td->pt[p].x - origin->x ) * v->x + origin->x; + td->pt[p].y = ( td->pt[p].y - origin->y ) * v->y + origin->y; + td->pt[p].z = ( td->pt[p].z - origin->z ) * v->z + origin->z; + } + } + else + { + uint p; + for ( p = 0 ; p < 3; p++ ) + { + td->pt[p].x *= v->x; + td->pt[p].y *= v->y; + td->pt[p].z *= v->z; + } + } + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + status = 1; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_shift +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + Var_t *v = ToVectorV(param+1); + uint p; + for ( p = 0 ; p < 3; p++ ) + { + td->pt[p].x += v->x; + td->pt[p].y += v->y; + td->pt[p].z += v->z; + } + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + status = 1; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_hRot +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + const double hrot_rad = GetDoubleV(param+1) * (M_PI/180.0); + + if ( n_param > 2 ) + { + Var_t *origin = ToVectorV(param+2); + + uint p; + double3 *d3 = td->pt; + for ( p = 0 ; p < 3; p++, d3++ ) + { + const double dx = d3->x - origin->x; + const double dz = d3->z - origin->z; + const double rad = atan2(dx,dz) + hrot_rad; + const double len = sqrt(dx*dx+dz*dz); + + d3->x = sin(rad) * len; + d3->z = cos(rad) * len; + } + } + else + { + + uint p; + double3 *d3 = td->pt; + for ( p = 0 ; p < 3; p++, d3++ ) + { + const double rad = atan2(d3->x,d3->z) + hrot_rad; + const double len = sqrt(d3->x*d3->x+d3->z*d3->z); + + d3->x = sin(rad) * len; + d3->z = cos(rad) * len; + } + } + td->status |= TD_INVALID_NORM; + si->kcl->script_pt_modified = true; + status = 1; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_remove +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = ( td->status & TD_REMOVED ) != 0; + td->status |= TD_REMOVED; + si->kcl->script_tri_removed = true; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_unremove +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = ( td->status & TD_REMOVED ) != 0; + td->status &= ~TD_REMOVED; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_isRemoved +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + status = ( td->status & TD_REMOVED ) != 0; + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_create +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + int status = -1; + if ( si && si->kcl ) + { + uint i; + for ( i = 1; i < n_param; i++ ) + ToVectorV(param+i); + + kcl_tridata_t *td = AppendTrianglesKCLp( + si->kcl, + GetIntV(param), + ¶m[1].d3, + sizeof(*param), + n_param-1 ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createOctahedron +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if ( si && si->kcl ) + { + ToVectorV(param+1); + ToVectorV(param+2); + octahedron_t oct; + if ( n_param > 3 ) + { + ToVectorV(param+3); + CreateOctahedron( &oct, ¶m[2].d3, ¶m[3].d3, ¶m[1].d3 ); + } + else + CreateOctahedron( &oct, ¶m[2].d3, 0, ¶m[1].d3 ); + + kcl_tridata_t *td = AppendOctahedronKCL( si->kcl, GetIntV(param), &oct ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createCuboid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if ( si && si->kcl ) + { + ToVectorV(param+1); + ToVectorV(param+2); + cuboid_t cub; + if ( n_param > 3 ) + { + ToVectorV(param+3); + CreateCuboid( &cub, ¶m[2].d3, ¶m[3].d3, ¶m[1].d3 ); + } + else + CreateCuboid( &cub, ¶m[2].d3, 0, ¶m[1].d3 ); + + kcl_tridata_t *td = AppendCuboidKCL( si->kcl, GetIntV(param), &cub ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createPyramid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + int status = -1; + if ( si && si->kcl ) + { + uint i; + for ( i = 1; i < n_param; i++ ) + ToVectorV(param+i); + + kcl_tridata_t *td = AppendPyramidKCL( + si->kcl, + GetIntV(param), + ¶m[1].d3, + ¶m[2].d3, + sizeof(*param), + n_param-2, + false ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createPrism +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter + // * fpar->user_id (bit field) + // bit 0: anti prism + // bit 1: arrow +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=6); + DASSERT(fpar); + + int status = -1; + if ( si && si->kcl ) + { + + // tri$createPrism( kcl_flag, pt1,pt2, ptX, r,n [,base_flag] [,base_height] ) + // tri$createArrow( kcl_flag, pt1,pt2, ptX, r,n, + // arrow_flag [,arrow_size] [,base_flag] [,base_height] ) + + ToVectorV(param+1); + ToVectorV(param+2); + ToVectorV(param+3); + + int base_flag, arrow_flag; + double base_height, arrow_size; + if ( fpar->user_id & 2 ) + { + DASSERT( n_param >= 7 ); + arrow_flag = GetIntV(param+6); + arrow_size = n_param > 7 ? GetDoubleV(param+7) : 1.0; + base_flag = n_param > 8 ? GetIntV(param+8) : -1; + base_height = n_param > 9 ? GetDoubleV(param+9) : 0.0; + } + else + { + arrow_flag = -1; + arrow_size = 0.0; + base_flag = n_param > 6 ? GetIntV(param+6) : -1; + base_height = n_param > 7 ? GetDoubleV(param+7) : 0.0; + } + + kcl_tridata_t *td + = AppendPrismKCL( si->kcl, + GetIntV(param), + ¶m[1].d3, + ¶m[2].d3, + ¶m[3].d3, + GetDoubleV(param+4), + GetIntV(param+5), + fpar->user_id & 1, + base_flag, + base_height, + arrow_flag, + arrow_size, + false ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_createJoist +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=5); + + int status = -1; + if ( si && si->kcl ) + { + ToVectorV(param+2); + ToVectorV(param+3); + ToVectorV(param+4); + + kcl_tridata_t *td = AppendJoistKCL( + si->kcl, + GetIntV(param), + GetDoubleV(param+1), + ¶m[2].d3, + ¶m[3].d3, + ¶m[4].d3, + n_param > 5 ? GetIntV(param+5) : 0, + n_param > 6 ? GetIntV(param+6) : 0 ); + if (td) + { + si->kcl->script_tri_added = true; + status = td - (kcl_tridata_t*)si->kcl->tridata.list; + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_ptsInCuboid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = 0; + ToVectorV(param+1); + ToVectorV(param+2); + + double3 *d3 = td->pt; + uint p; + for ( p = 0; p < 3; p++, d3++ ) + { + if ( d3->x >= param[1].x && d3->x <= param[2].x + && d3->y >= param[1].y && d3->y <= param[2].y + && d3->z >= param[1].z && d3->z <= param[2].z + ) + { + status++; + } + } + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_OverlapCube +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = OverlapCubeTri( + &ToVectorV(param+1)->d3, + GetDoubleV(param+2), + td->pt+0, + td->pt+1, + td->pt+2 ); + } + } + + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_flag +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT(fpar); + + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + res->i = td->cur_flag; + if ( n_param > 1 ) + { + const int mode = GetIntV(param+1); + if ( mode == 2 && patch_kcl_flag ) + res->i = patch_kcl_flag[td->in_flag]; + else if ( mode == 1 || mode == 2 ) + res->i = td->in_flag; + } + if (fpar->user_id) + res->i = GET_KCL_TYPE(res->i); + res->mode = VAR_INT; + return ERR_OK; + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_setFlag +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int status = -1; + if (si) + { + kcl_tridata_t *td = GetTridataKCL(si->kcl,GetIntV(param)); + if (td) + { + status = td->cur_flag; + td->cur_flag = GetIntV(param+1); + // no 'flag changed' status needed + } + } + res->i = status; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_defColor +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + const u32 col0 = GetIntV(param); + Color_t col; + col.a = col0 >> 24; + col.r = col0 >> 16; + col.g = col0 >> 8; + col.b = col0; + + switch (n_param) + { + case 1: + if (!col.a) + col.a = 0xff; + break; + + case 2: + col.a = GetIntV(param+1); + break; + + case 4: + col.a = GetIntV(param+3); + case 3: + col.r = GetIntV(param+0); + col.g = GetIntV(param+1); + col.b = GetIntV(param+2); + break; + } + + int ci; + for ( ci = 0; ci < n_user_color; ci++ ) + if ( user_color[ci].val == col.val ) + break; + + if ( ci >= N_KCL_USER_FLAGS ) + res->i = -1; + else + { + if ( ci == n_user_color ) + user_color[n_user_color++] = col; + res->i = ci + KCL_FLAG_USER_0; + } + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tri_getColor +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + const uint idx = GetIntV(param); + if ( idx >= KCL_FLAG_USER_0 && idx <= KCL_FLAG_USER_MAX ) + { + Color_t col = user_color[ idx - KCL_FLAG_USER_0]; + res->i = col.a << 24 | col.r << 16 | col.g << 8 | col.b; + res->mode = VAR_INT; + } + else + res->mode = VAR_UNSET; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const struct FuncTable_t func_tab[] = +{ + { 0, 0, "TRI$N", F_tri_n, 0, + "vector", "tri$n()", + "This function returns the number of triangles." }, + + { 2, 2, "TRI$PT", F_tri_pt, 0, + "vector", "tri$pt(tri_index,pt_index)", + "This function returns the point with index 'pt_index' (0..2)" + " of triangle 'tri_index' (0..)." }, + + { 1, 1, "TRI$LENGTH", F_tri_length, 0, + "float", "tri$length(tri_index)", + "This function returns the length of triangle 'tri_index' (0..)." }, + + { 2, 2, "TRI$NORMAL", F_tri_normal, 0, + "vector", "tri$normal(tri_index,norm_index)", + "This function returns the normal with index 'norm_index' (0..3)" + " of triangle 'tri_index' (0..)." }, + + { 3, 4, "TRI$SETPT", F_tri_setPt, 0, + "int", "tri$setPt(tri_index,pt0,pt1,pt2)", + "This function sets the coordinates of all 3 points" + " of the triangle 'tri_index' (0..) to the vectors 'pt*'." }, + { 0, 0, 0, F_tri_setPt, 0, + "int", "tri$setPt(tri_index,pt_index,pt)", + "This function sets the coordinates of the single point" + " with index 'pt_index' (0..2) of triangle 'tri_index' (0..)." + " to the vector 'pt'." }, + + { 2, 3, "TRI$SCALE", F_tri_scale, 0, + "int", "tri$scale(tri_index,scale[,origin])", + "This function scales the triangle 'tri_index' (0..)" + " by the vector 'scale' relative to point 'origin'." + " If the origin is not set, v(0,0,0) is used instead." }, + + { 2, 2, "TRI$SHIFT", F_tri_shift, 0, + "int", "tri$shift(tri_index,shift)", + "This function adds the vector 'shift' to all 3 points" + " of the triangle 'tri_index' (0..)." }, + + { 2, 3, "TRI$HROT", F_tri_hRot, 0, + "int", "tri$hRot(tri_index,degree[,origin])", + "This function rotates the triangle 'tri_index' (0..)" + " horizontal counterclockwise by the angle 'degree'" + " around the point 'origin'." + " If the origin is not set, v(0,0,0) is used instead." }, + + { 1, 1, "TRI$REMOVE", F_tri_remove, 0, + "int", "tri$remove(tri_index)", + "This function marks the triangle 'tri_index' (0..) as 'REMOVED'." + " The return value is -1 for an invalid index," + " 1 if the triangle was already removed or 0 if not." }, + + { 1, 1, "TRI$UNREMOVE", F_tri_unremove, 0, + "int", "tri$unremove(tri_index)", + "This function clears the 'REMOVED' marker of the triangle 'tri_index' (0..)." + " The return value is -1 for an invalid index," + " 1 if the triangle was removed or 0 if not." }, + + { 1, 1, "TRI$ISREMOVED", F_tri_isRemoved, 0, + "int", "tri$isRemoved(tri_index)", + "This returns the status of the 'REMOVED' marker" + " of the triangle 'tri_index' (0..)." + " The return value is -1 for an invalid index," + " 1 if the triangle is marked as removed or 0 if not." }, + + { 4, MAX_FUNC_PARAM, "TRI$CREATE", F_tri_create, 0, + "int", "tri$create(kcl_flag,pt0,pt1,pt2,...)", + "This function appends new triangles at the end of the triangle list" + " with the entered parameters." + " If more than 3 points are entered, all points must be in the same plane" + " and the polygon is split into multiple (N-2) triangles." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 3, 4, "TRI$CREATEOCTAHEDRON", F_tri_createOctahedron, 0, + "int", "tri$createOctahedron(kcl_flag,pos,radius[,rot])", + "This function creates a octahedron by adding 8 triangles." + " If 'radius' is a scalar, a regular octahedron created." + " Otherwise it is a vector and define different radiuses for each axis." + " If 'rot' is set, the octahedron is rotated." + " As last operation the center (origin) of the cube is shifted to 'pos'." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 3, 4, "TRI$CREATECUBOID", F_tri_createCuboid, 0, + "int", "tri$createCuboid(kcl_flag,pos,size[,rot])", + "This function creates a cuboid by adding 12 triangles." + " If 'size' is a scalar, a cube with the entered edge length is created." + " If 'rot' is set, the cube is rotated." + " As last operation the center (origin) of the cube is shifted to 'pos'." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 4, MAX_FUNC_PARAM, "TRI$CREATEPYRAMID", F_tri_createPyramid, 0, + "int", "tri$createPyramid(kcl_flag,apex,pt1,pt2,...)", + "This function creates a pyramid by adding N triangles." + " 'pt1'..'ptN' descibribes the base polygon, which is not created." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 6, 8, "TRI$CREATEPRISM", F_tri_createPrism, 0, + "int", "tri$createPrism(kcl_flag,pt1,pt2,ptX,r,n[,base_flag][,base_height])", + "This function creates a prism with radius 'r' and N faces" + " from 'pt1' to 'pt2' by adding 2*N triangles." + " The point 'ptX' is used for the orientation (direction of first edge)." + " If 'base_flag' is set and not @-1@, the base is printed with ths kcl flag value." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 6, 8, "TRI$CREATEANTIPRISM", F_tri_createPrism, 1, + "int", "tri$createAntiPrism(kcl_flag,pt1,pt2,ptX,r,n[,base_flag][,base_height])", + "This function creates an antiprism with radius 'r' and N faces" + " from 'pt1' to 'pt2' by adding 2*N triangles." + " The point 'ptX' is used for the orientation (direction of first edge)." + " If 'base_flag' is set and not @-1@, the base is printed with ths kcl flag value." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 7, 10, "TRI$CREATEARROW", F_tri_createPrism, 2, + "int", "tri$createArrow(flag,pt1,pt2,ptX,r,n,a_flag[,a_size][,b_flag][,b_ht])", + "This function creates a prism based arrow with radius 'r' and N faces" + " from 'pt1' to 'pt2', where 'pt2' is the place of the arrowhead." + " The point 'ptX' is used for the orientation (direction of first edge)." + " 'flag', 'a_flag' and 'b_flag' are KCL flags to define the color." + " 'a_size' is the addition size factor of the arrowhead, the default is 1.0." + " If 'b_flag' is not set or @-1@, no base is drawn." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 7, 10, "TRI$CREATEANTIARROW", F_tri_createPrism, 3, + "int", "tri$createAntiArrow(flag,pt1,pt2,ptX,r,n,a_flag[,a_size][,b_flag][,b_ht])", + "This function creates an antiprism based arrow with radius 'r' and N faces" + " from 'pt1' to 'pt2', where 'pt2' is the place of the arrowhead." + " The point 'ptX' is used for the orientation (direction of first edge)." + " 'flag', 'a_flag' and 'b_flag' are KCL flags to define the color." + " 'a_size' is the addition size factor of the arrowhead, the default is 1.0." + " If 'b_flag' is not set or @-1@, no base is drawn." + " If 'base_height' is set, a pyramid is drawn as base." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 5, 6, "TRI$CREATEJOIST", F_tri_createJoist, 0, + "int", "tri$createJoist(kcl_flag,length,pt1,pt2,pt_dir[,n_marker][,mark5_flag])", + "This function appends a triangular joist from 'p1' to 'p2'" + " to at the end of the triangle list." + " 'length' the length of the body" + " and 'pt_dir' is a helper point for its direction." + " If 'n_marker' is >0, markers representing the number are added." + " If 'mark5_flag' is >0, every 5 markers are replaced by one of the entered flag." + " The function returns the index of the first new triangle or -1 if failed." }, + + { 3, 3, "TRI$PTSINCUBOID", F_tri_ptsInCuboid, 0, + "int", "tri$ptsInCuboid(tri_index,cube_min,cube_max)", + " Parameters 'cube_*' are converted to vectors." + " They describe 2 diagonal corners of a rectangular cuboid," + " assuming that 'cube_min<=cube_max' is true for each coordinate." + " The functions returns the number of points" + " of the triangle 'tri_index' (0..)," + " that are inside of the cube including the border." + " If 'tri_index' is invalid, -1 is returned." }, + + #ifdef TEST // [[2do]] + { 3, 3, "TRI$OVERLAPCUBE", F_tri_OverlapCube, 1, + "int", "tri$OverlapCube(tri_index,cube_mid,cube_width)", + " 'tri_index' (0..) is a triangle index." + " 'cube_mid' is the middle point of a cube" + " and 'cube_width' the length of its edges." + " The function returns -1 for an invalid triangle index," + " or 1, if the cube and the triangle are overlapped." + " Otherwise 0 is returned." }, + #endif + + { 1, 2, "TRI$FLAG", F_tri_flag, 0, + "int", "tri$flag(tri_index[,mode])", + "This function returns the KCL flag of the triangle 'tri_index' (0..)." + " If mode is '1', the original source value is returned." + " If mode is '2', the original source value " + " transformed by --kcl-flag is returned;" + " if --kcl-flag is not set, it is the same as mode '1'." + " Otherwise the current value is returned." + " The current value is initialized by the value of mode '2'." + " Values >0xffff are possible and represent internal drawings." }, + + { 1, 2, "TRI$TYPE", F_tri_flag, 1, + "int", "tri$type(tri_index[,mode])", + "This function returns the KCL type of the triangle 'tri_index' (0..)." + " It works like tri$flag(), but the flag is transformed to a type." + " The return value is in the range of 0x00..0x1f (5 lowest bits of the flag)," + " if it is a real KCL flag (<0x10000), or -1 otherwise (special type)." }, + + { 2, 2, "TRI$SETFLAG", F_tri_setFlag, 0, + "int", "tri$setFlag(tri_index,new_flag)", + "This function sets the new KCL flag of triangle 'tri_index' (0..)." + " The function returns -1 for an invalid index" + " or the previous value of the KCL flag." }, + + { 1, 4, "TRI$DEFCOLOR", F_tri_defColor, 0, + "int", "tri$defColor(argb[,alpha])", + "Define a new color." + " 'ARGB' is a bytes coded color: alpha,red,green,blue." + " If 'ALPHA' is not set, the highest byte of 'ARGB' is used." + " If this highest byte is null, 0xff (no transparency) is used." + " The function returns -1, if already 200 colors defined." + " Otherwise it returns the virtual KCL flag for the user defined color." }, + { 0, 0, 0, F_tri_defColor, 0, + "int", "tri$defColor(red,green,blue[,alpha])", + "Define a new color." + " If ALPHA is not set, 0xff (no transparency) is used." + " For the 3 colors, values between 0x00 and 0xff are expected." + " The function returns -1, if already 200 colors defined." + " Otherwise it returns the virtual KCL flag for the user defined color." }, + + { 1, 1, "TRI$GETCOLOR", F_tri_getColor, 0, + "int", "tri$getColor(index)", + "This function returns the color defined by a previous tri$defColor() call." + " INDEX is the virtual KCL flag for the user defined color." + " On error, the value NONE (type 'undefined') is returned." }, + + {0,0,0,0,0,0,0,0} +}; + +// [[2do]] ideas: +// tri$isAdded(tindex) : true, if triangle is marked as 'added' +// tri$isFixed(tindex) : true, if triangle is marked as 'fixed' +// tri$isUsed(tindex) : true, if triangle is marked as 'used' + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup kcl parser /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsKCL() +{ + static VarMap_t vm = {0}; + if (!vm.used) + { + //---- define functions + + DefineParserFuncTab(func_tab,FF_KCL); + SetupReferenceKCL(&vm); + + #if 0 + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(&vm,ip->name,ip->val); + #endif + + //--- setup basic parser variables + + DefineParserVars(&vm); + } + + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-kcl.h b/project/src/lib-kcl.h new file mode 100644 index 0000000..9a73043 --- /dev/null +++ b/project/src/lib-kcl.h @@ -0,0 +1,1221 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_KCL_H +#define SZS_LIB_KCL_H 1 + +#include "lib-std.h" +#include "db-kcl.h" +#include "lib-image.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef TEST // [[cube]] + #define SUPPORT_KCL_CUBE 1 + #define SUPPORT_KCL_XTRIDATA 0 // 0=off, 1=extra + #define SUPPORT_KCL_DELTA 0 // 0|1 +#else + #define SUPPORT_KCL_CUBE 0 + #define SUPPORT_KCL_XTRIDATA 0 + #define SUPPORT_KCL_DELTA 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#define KCL_TEXT_MAGIC "#KCL" +#define KCL_TEXT_MAGIC_NUM 0x234b434c + +#define KCL_SIGNATURE "WiimmSZS" + +#define KCL_MTL_EXT ".mtl" +#define KCL_FLAG_FILE1 "\1P/\1N\1T" +#define KCL_FLAG_FILE2 "\1P/\1F\1T" +#define KCL_FLAG_EXT ".flag" + +#define N_KCL_SECT 4 + +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_analyze_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_analyze_t]] + +typedef struct kcl_analyze_t +{ + valid_t valid; // valid status + u16 order_value; // digits 1, 2, 3, 4 as dec to reflect order + bool order_ok; // true: regular order (order_value==1234) + u32 head_size; // size of head + u32 file_size; // not NULL: size of file + u32 off[N_KCL_SECT]; // offsets of KCL sections + u32 size[N_KCL_SECT]; // max size of KCL sections (U32_MAX=unknown) + u32 n[N_KCL_SECT]; // number of KCL entries based on 'size' +} +kcl_analyze_t; + +ccp GetValidInfoKCL ( kcl_analyze_t * ka ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_head_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kcl_head_t +{ + /*0x00*/ u32 sect_off[N_KCL_SECT]; // offset of the 4 sections + /*0x10*/ float32 unknown_0x10; + /*0x14*/ float32 min_octree[3]; // minimum values + /*0x20*/ u32 mask[3]; // masks for spatial index + /*0x2c*/ u32 coord_rshift; // right shift for all coord + /*0x30*/ u32 y_lshift; // left shift for y values + /*0x34*/ u32 z_lshift; // left shift for z values + /*0x38*/ float32 unknown_0x38; + /*0x3c*/ +} +__attribute__ ((packed)) kcl_head_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_triangle_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kcl_triangle_t +{ + /*0x00*/ float32 length; // + /*0x04*/ u16 idx_vertex; // + /*0x06*/ u16 idx_normal[4]; // + /*0x0e*/ u16 flag; // KCL flag (includes the type) + /*0x10*/ +} +__attribute__ ((packed)) kcl_triangle_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_tridata_status_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_tridata_status_t]] + +typedef enum kcl_tridata_status_t +{ + TD_INVALID_NORM = 0x01, // 'length' & 'normal' are invalid + TD_INVALID = 0x02, // invalid data + TD_FIXED_PT = 0x04, // triangle was fixed/repaired + TD_UNUSED = 0x08, // triangle not used in octree + TD_REMOVED = 0x10, // triangle is marked as 'removed' + TD_ADDED = 0x20, // triangle was added + TD_MARK = 0x40, // temporary marker for calculations + + TDX_ORIG_PT = 0x1000, // original 'pt' valid + TDX_ORIG_NORM = 0x2000, // original 'length' & 'normal' valid + +} kcl_tridata_status_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_tridata*_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_tridata_flt_t]] [[xtridata]] + +typedef struct kcl_tridata_flt_t +{ + double3 pt[3]; // 3 points + float length; // length + float3 normal[4]; // 4 normal values +} +__attribute__ ((packed)) kcl_tridata_flt_t; + +//----------------------------------------------------------------------------- +// [[kcl_tridata_dbl_t]] [[xtridata]] + +typedef struct kcl_tridata_dbl_t +{ + double3 pt[3]; // 3 points + double length; // length + double3 normal[4]; // 4 normal values +} +__attribute__ ((packed)) kcl_tridata_dbl_t; + +//----------------------------------------------------------------------------- +// [[kcl_tridata0_t]] + +typedef struct kcl_tridata0_t +{ + double3 pt[3]; // 3 points + float length; // length -> TD_INVALID_NORM + float3 normal[4]; // 4 normal values -> TD_INVALID_NORM + + #if SUPPORT_KCL_DELTA + float delta_min; // minimal distance of 3 points + float delta_max; // maximal distance of 3 points + #endif + + u16 status; // bit field: kcl_tridata_status_t + u16 in_flag; // original flag of input + u32 cur_flag; // current flag + +} kcl_tridata0_t; + +//----------------------------------------------------------------------------- +// [[kcl_tridata1_t]] [[xtridata]] + +typedef struct kcl_tridata1_t +{ + double3 pt[3]; // 3 points + float length; // length -> TD_INVALID_NORM + float3 normal[4]; // 4 normal values -> TD_INVALID_NORM + + #if SUPPORT_KCL_DELTA + float delta_min; // minimal distance of 3 points + float delta_max; // maximal distance of 3 points + #endif + + u16 status; // bit field: kcl_tridata_status_t + u16 in_flag; // original flag of input + u32 cur_flag; // current flag + + //-- extra data: input + + kcl_tridata_dbl_t dbl; // parts invalid on TD_INVALID_NORM + kcl_tridata_dbl_t orig; // valid on TDX_ORIG_PT, TDX_ORIG_NORM + +} kcl_tridata1_t; + +//----------------------------------------------------------------------------- +// [[kcl_tridata_t]] [[xtridata]] + +#if SUPPORT_KCL_XTRIDATA > 0 + typedef kcl_tridata1_t kcl_tridata_t; +#else + typedef kcl_tridata0_t kcl_tridata_t; +#endif + +//----------------------------------------------------------------------------- + +void CopyTriData_F2D ( kcl_tridata_dbl_t *dest, const kcl_tridata_flt_t *src ); +void CopyTriData_D2F ( kcl_tridata_flt_t *dest, const kcl_tridata_dbl_t *src ); + +static inline void CopyTriData_T2D ( kcl_tridata_dbl_t *dest, const kcl_tridata_t *src ) + { CopyTriData_F2D(dest,(const kcl_tridata_flt_t*)src); } +static inline void CopyTriData_D2T ( kcl_tridata_t *dest, const kcl_tridata_dbl_t *src ) + { CopyTriData_D2F((kcl_tridata_flt_t*)dest,src); } + +static inline void CopyTriData_T2F ( kcl_tridata_flt_t *dest, const kcl_tridata_t *src ) + { memcpy(dest,src,sizeof(*dest)); } +static inline void CopyTriData_F2T ( kcl_tridata_t *dest, const kcl_tridata_flt_t *src ) + { memcpy(dest,src,sizeof(*src)); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_mode_t]] + +typedef enum kcl_mode_t +{ + //--- general options + + KCLMD_FAST = 0x00000001, // fast mode: don't find duplicates + KCLMD_ROUND = 0x00000002, // round normals toward zero + KCLMD_NORMALS = 0x00000004, // print normals into OBJ + KCLMD_MTL = 0x00000008, // print materials on OBJ export + KCLMD_WIIMM = 0x00000010, // print materials, use "wiimm.mtl" as file + KCLMD_TRIANGLES = 0x00000020, // export as triangle list + KCLMD_OUT_SWAP = 0x00000040, // swap points #2 and #3 while generating OBJ + + KCLMD_USEMTL = 0x00000080, // use 'usemtl' for groups if reading OBJ + KCLMD_G = 0x00000100, // use 'g' for groups if reading OBJ + KCLMD_CLIP = 0x00000200, // enable clip function => const 'KCL_CLIP' + KCLMD_IN_SWAP = 0x00000400, // swap points #2 and #3 while reading OBJ + KCLMD_AUTO = 0x00000800, // enable auto swap while reading OBJ + + KCLMD_CENTER = 0x00001000, // center world in cube area on octree creation + KCLMD_ADD_ROAD = 0x00002000, // for 'kcl_ref' only: Add DMD_ROADOBJ objects + + KCLMD_HEX4 = 0x00004000, // enable flag _XXXX detection in group names + KCLMD_HEX23 = 0x00008000, // enable flag _XX_XXX detection in group names + KCLMD_M_HEX = KCLMD_HEX4 + | KCLMD_HEX23, + + KCLMD_CUBE = 0x00010000, // use cubes around triangles for octree calculation + + KCLMD_SMALL = 0x01000000, // aggressive preset to get a smaller kcl + KCLMD_CHARY = 0x02000000, // chary preset, Nintendo like + KCLMD_DRAW = 0x04000000, // preset for drawing + KCLMD_M_PRESET = KCLMD_SMALL + | KCLMD_CHARY + | KCLMD_DRAW, + + KCLMD_M_GENERAL = KCLMD_FAST + | KCLMD_ROUND + | KCLMD_NORMALS + | KCLMD_MTL + | KCLMD_WIIMM + | KCLMD_TRIANGLES + | KCLMD_OUT_SWAP + | KCLMD_G + | KCLMD_USEMTL + | KCLMD_CLIP + | KCLMD_IN_SWAP + | KCLMD_AUTO + | KCLMD_CENTER + | KCLMD_M_HEX + | KCLMD_CUBE + | KCLMD_M_PRESET, + + + //--- KCL patching options + + KCLMD_DROP_AUTO = 0x10000000, // enable DROP auto mode + KCLMD_DROP_UNUSED = 0x20000000, // remove unreferenced triangles + KCLMD_DROP_FIXED = 0x40000000, // remove fixed triangles + KCLMD_DROP_INVALID = 0x80000000, // remove invalid triangles + KCLMD_M_DROP = KCLMD_DROP_AUTO + | KCLMD_DROP_UNUSED + | KCLMD_DROP_FIXED + | KCLMD_DROP_INVALID, + + KCLMD_RM_FACEDOWN = 1ull<<32, // remove face down drivable triangles + KCLMD_RM_FACEUP = 1ull<<33, // remove face up walls + KCLMD_M_RM = KCLMD_RM_FACEDOWN + | KCLMD_RM_FACEUP, + + KCLMD_CONV_FACEUP = 1ull<<34, // convert face up walls to road + KCLMD_WEAK_WALLS = 1ull<<35, // OR 0x8000 to KCL flags of walls + KCLMD_NEW = 1ull<<36, // create a new KCL file even if nothing changed + KCLMD_SORT = 1ull<<37, // sort triangles + KCLMD_M_PATCH = KCLMD_M_DROP + | KCLMD_M_RM + | KCLMD_CONV_FACEUP + | KCLMD_WEAK_WALLS + | KCLMD_NEW + | KCLMD_SORT, + + //--- tiny modes + + KCLMD_S_TINY = 40, + KCLMD_TINY_0 = 0ull << KCLMD_S_TINY, // TINY levels + KCLMD_TINY_1 = 1ull << KCLMD_S_TINY, + KCLMD_TINY_2 = 2ull << KCLMD_S_TINY, + KCLMD_TINY_3 = 3ull << KCLMD_S_TINY, + KCLMD_TINY_4 = 4ull << KCLMD_S_TINY, + KCLMD_TINY_5 = 5ull << KCLMD_S_TINY, + KCLMD_TINY_6 = 6ull << KCLMD_S_TINY, + KCLMD_TINY_7 = 7ull << KCLMD_S_TINY, + KCLMD_M_TINY = KCLMD_TINY_7, + + + //--- hidden options + + KCLMD_LOG = 1ull<<43, // enable kcl trace log to stdout + KCLMD_SILENT = 1ull<<44, // suppress kcl messages + KCLMD_POSLEN = 1ull<<45, // force length>=0 in CalcPointsTriData() + KCLMD_INPLACE = 1ull<<46, // force inplace patching + KCLMD_M_HIDDEN = KCLMD_LOG + | KCLMD_SILENT + | KCLMD_POSLEN + | KCLMD_INPLACE, + + KCLMD_F_SCRIPT = 1ull<<50, // execute script first + KCLMD_F_HIDE = 1ull<<51, // help flag to hide modes in PrintKclMode() + + + //--- test mode + + KCLMD_TEST = 1ull<<60, // reserved for testing + + + //--- summaries + + KCLMD_M_DEFAULT = KCLMD_MTL // default settings + | KCLMD_DROP_AUTO, + + KCLMD_M_FIX_ALL = KCLMD_M_DROP // general kcl fix mode + | KCLMD_M_RM + | KCLMD_NEW, + + KCLMD_M_ALL = KCLMD_M_GENERAL // all relevant bits + | KCLMD_M_PATCH + | KCLMD_M_HIDDEN + | KCLMD_ADD_ROAD + | KCLMD_F_SCRIPT + | KCLMD_M_TINY + | KCLMD_TEST, + + KCLMD_M_PRINT = KCLMD_M_ALL // all relevant bits for obj output + % ~KCLMD_ADD_ROAD + & ~KCLMD_IN_SWAP + & ~KCLMD_AUTO + & ~KCLMD_M_HEX + & ~KCLMD_M_HIDDEN, + +} kcl_mode_t; + +//----------------------------------------------------------------------------- + +extern int enable_kcl_drop_auto; +extern kcl_mode_t KCL_MODE; + +void SetKclMode ( kcl_mode_t new_mode ); +int ScanOptKcl ( ccp arg ); +uint PrintKclMode( char *buf, uint bufsize, kcl_mode_t mode ); +ccp GetKclMode(); +void SetupKCL(); + +extern Color_t user_color[N_KCL_USER_FLAGS]; +extern uint n_user_color; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_t]] + +#define KCL_MAX_STAT_DEPTH 32 +#define KCL_MAX_TRIANGLES 0xffff +#define KCL_MAX_PT_PER_TRI 50 + +typedef struct kcl_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // FF_KCL | FF_KCL_TXT + file_format_t fform_outfile; // planned output format + int revision; // tool revision + char signature_info[9]; // info text of signature, found in vertex #0 + float signature; // >0.0: signature number, found in vertex #0 + + //--- base settings + + bool silent_octree; // true: create octree without comments + // always init with false + bool fast; // true: don't find duplicates + // init by 'KCL_MODE & KCLMD_FAST' + uint min_cube_size; // minimum cube size + // init by 'KCL_MIN_SIZE' + uint max_cube_size; // maximum cube size + // init by 'KCL_MAX_SIZE' + uint cube_blow; // blow up the cube to find tirangles + // init by 'KCL_BLOW' + uint max_cube_triangles; // maximum triangles per cube + // init by 'KCL_MAX_TRI' + uint max_octree_depth; // maximum octree depth + // init by 'KCL_MAX_DEPTH' + + //--- file header data + + double3 min_octree; // minimum coordinate values + u32 mask[3]; // masks for spatial index + u32 coord_rshift; // right shift for all coord + u32 y_lshift; // left shift for y values + u32 z_lshift; // left shift for z values + float unknown_0x10; // unknown header value + float unknown_0x38; // unknown header value + + //--- triangle data + + List_t tridata; // triangle data base + bool norm_valid; // true: all normal data is valid + bool used_valid; // true: used markers are valid + bool no_limit; // true: no triangle limit + bool limit_warning; // true: limit warning is already printed + double3 tri_minval; // minimum allowed triangle value, + // initialized with -1e9 + double3 tri_maxval; // maximum allowed triangle value, + // initialized with +1e9 + uint tri_inv_count; // number of invalid triangles + // all invalid are also fixed + // but not counted as 'tri_fix_count' + uint tri_fix_count; // number of fixed triangles + // this excludes 'tri_inv_count' + + //--- octree data + + u8 *octree; // NULL or pointer to octree + uint octree_size; // size of 'octree' + bool octree_valid; // octree & header data are valid + bool octree_alloced; // true: FREE(octree) + bool recreate_octree; // true: recreate octree before storing + + + //--- statistics about triangle points + + double3 min; // min coord of all triangle points + double3 mean; // mean coord of all triangle points + double3 max; // max coord of all triangle points + double tmin_dist; // minimum triangle point distance + double tmax_dist; // maximum triangle point distance + bool min_max_valid; // true: all 4 vars of this part are valid + + //--- raw data, created by CreateRawKCL() + + u8 * raw_data; // NULL or data + uint raw_data_size; // size of 'raw_data' + bool raw_data_alloced; // true: FREE(raw_data) + bool model_modified; // the model was modified + // raw data is invalid + + //--- flag database + + ccp flag_fname; // path of flag file, alloced + bool flag_db_loaded; // true: flag db is loaded + bool accept_hex4; // true: accept '_FFFF' + bool accept_hex23; // true: accept '_TT_VVV' + FormatField_t flag_name; // list with names + FormatField_t flag_pattern; // list with patterns + FormatField_t flag_missing; // list with unresolved group names + + + //--- script flags + + bool script_tri_removed; // true: triangle removed + bool script_tri_added; // true: triangle added + bool script_pt_modified; // true: points modified + + + /////////////////////////////////////////////////////////////////////////// + /////////////// Statistics ==> LAST KCL DATA! /////////////// + /////////////////////////////////////////////////////////////////////////// + + //--- statistics: base + + bool stat_valid; // IMPORTANT: First statsitics data! + // true: all statistics are valid + + //--- base statistics + + uint mask_bits[3]; // number of 0 bits in mask + uint bcube_bits[3]; // number of base cube relevant bits + uint hash_bits; // number of bits in hash value + uint n_bcubes[3]; // number of base cubes per axis + uint total_bcubes; // total number of base cubes + uint bcube_width; // edge length of base cubes + double3 max_octree; // maximum coordinate values + + + //--- statistics: octree + + u32 min_o_offset; // first cube node + u32 max_o_offset; // end of last cube node + u32 min_l_offset; // min found list offset + u32 max_l_offset; // max found list offset + uint n_cube_nodes; // number of octree cube nodes + // (each with 8 links) + uint total_cubes; // total number of cubes + uint n_trilist; // number of tri-list links + + + //--- statistics: octree lists + + u8 *o_depth; // octree depth, N=n_octree + u16 *o_count; // octree ref counts, N=n_octree + u16 *l_count; // trilist ref counts, N=n_trilist + + uint n_0_list; // num of triangle lists + uint n_0_link; // num of triangle links + uint n_tri_list; // num of triangle lists + uint n_tri_link; // num of triangle links + uint max_tri_len; // max triangles in a list + uint min_depth; // min octree depth with triangles + uint max_depth; // max octree depth + uint max_depth_tri_len[KCL_MAX_STAT_DEPTH]; + // max triangles in a list, + // depth dependent + float ave_depth; // average depth + uint sum_tri_len; // sum of all list length's + + + //--- statistics: usage + + uint n_invalid_tri_ref; // num of invalid tri references + uint n_tri_used; // num of used triangles + +} kcl_t; + +//----------------------------------------------------------------------------- + +extern kcl_t * kcl_ref; // NULL or reference KCL for calculations +extern kcl_t * kcl_ref_prio; // NULL or prioreference KCL for calculations + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsKCL(); + +void SetupReferenceKCL +( + VarMap_t *vm // not NULL: define KCL variables here +); + +void InitializeKCL ( kcl_t * kcl ); +void ResetKCL ( kcl_t * kcl ); +void ClearKCL ( kcl_t * kcl, bool init_kcl ); +void CopyKCL ( kcl_t * kcl, bool init_kcl, const kcl_t * src ); + +void LoadParametersKCL +( + ccp log_prefix // not NULL: + // print log message with prefix +); + +bool TransformKCL ( kcl_t * kcl ); +enumError CreateOctreeKCL ( kcl_t * kcl ); + +void ResetStatisticsKCL ( kcl_t * kcl ); +void CalcStatisticsKCL ( kcl_t * kcl ); +void CalcMinMaxKCL ( kcl_t * kcl ); +uint CalcBlowSizeKCL ( kcl_t * kcl ); +bool DropSortTriangles ( kcl_t * kcl ); + +bool DropSortHelper +( + kcl_t *kcl, // valid KCL + uint drop_mask, // not 0: drop triangles with set bits + bool sort // true: sort triangles +); + +bool KCL_ACTION_LOG ( const char * format, ... ) + __attribute__ ((__format__(__printf__,1,2))); + +/////////////////////////////////////////////////////////////////////////////// + +uint CheckMetricsTriData +( + // check length and minimal height of triangles + + kcl_tridata_t *td, // valid pointer to first record + uint n, // number of records to process + kcl_t *kcl // not NULL: store statistics +); + +//----------------------------------------------------------------------------- + +uint CalcPointsTriData +( + // calculate 'pt' using 'length' & 'normal' + // returns the number of fixes + + kcl_tridata_t *td, // valid pointer to first record + uint n, // number of records to process + kcl_t *kcl, // not NULL: store statistics + bool use_minmax // true & 'kcl': use 'tri_minval' and 'tri_maxval' +); + +//----------------------------------------------------------------------------- + +void CalcNormalsTriData +( + // calculate 'length' & 'normal' using 'pt' + // the calculation is only done, if 'TD_INVALID_NORM' + + kcl_t *kcl, // valid KCL + kcl_tridata_t *td, // valid pointer to first record + uint n // number of records to process +); + +//----------------------------------------------------------------------------- + +void CalcNormalsKCL +( + // calculate 'length' & 'normal' using 'pt' + // the calculation is only done, if '!kcl->norm_valid' + + kcl_t *kcl, // valid KCL + bool force // true: ignore 'kcl->norm_valid' +); + +//----------------------------------------------------------------------------- + +void RoundNormalsKCL +( + // round the normal values + + kcl_t *kcl, // valid KCL + int round_pow2 // round to '1/(2^round_pow2)' +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t *GetTridataKCL +( + kcl_t *kcl, // valid KCL + uint tri_index // triangle index +); + +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_tri_param_t]] + +typedef struct kcl_tri_param_t +{ + kcl_t *kcl; // valid KCL data structure + u32 kcl_flag; // value of kcl flag + bool is_orig; // true: is original data + + const double3 *norm; // NULL or pointer to normal + // => auto detection to swap pt2,pt3 + uint swap_count; // not NULL: increment on swap +} +kcl_tri_param_t; + +//----------------------------------------------------------------------------- + +static inline void InitializeTriParam + ( kcl_tri_param_t *tp, kcl_t *kcl, u32 kcl_flag ) +{ + DASSERT(tp); + DASSERT(kcl); + memset(tp,0,sizeof(*tp)); + tp->kcl = kcl; + tp->kcl_flag = kcl_flag; +} + +/////////////////////////////////////////////////////////////////////////////// + +kcl_tridata_t * PrepareAppendTrianglesKCL +( + // Grows the triangle field and the 'used' count. + // Returns a pointer to the first triangle. + // If to many triangles: + // => a warning is printed, but only once for a kcl. NULL is returned + + kcl_t *kcl, // valid KCL data structure + uint n_append // number of triangles to append +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTriangleKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3 // pointer to point #3 +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTriangleKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3 // pointer to point #3 +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendQuadrilateralKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3, // pointer to point #3 + const double3 *pt4 // pointer to point #4 +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendQuadrilateralKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to point #1 + const double3 *pt2, // pointer to point #2 + const double3 *pt3, // pointer to point #3 + const double3 *pt4 // pointer to point #4 +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTrianglesKCL +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_tri_param_t *par, // valid data structure + const double3 *pt1, // pointer to first point + int pt_delta, // delta in bytes to next point, <0 possible + uint n_pt // number of points +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendTrianglesKCLp +( + // returns the new triangle, or NULL if failed (to many triangles) + + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pt1, // pointer to first point + int pt_delta, // delta in bytes to next point, <0 possible + uint n_pt // number of points +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendOctahedronKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + octahedron_t *oct // valid octahedron data +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendCuboidKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + cuboid_t *c // valid cuboid data +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendPyramidKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *apex, // the apex of the pyramid + const double3 *pt1, // pointer to first point + uint pt_delta, // delta in bytes to next point + uint n_pt, // number of points + bool reverse // true: reverse face orientation +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t *AppendPrismKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *axis1, // first axis point + const double3 *axis2, // second axis point + const double3 *dir, // NULL or direction of first edge + double r, // radius + uint n_side, // number of sides, max=100 + uint mode, // bitfield: 0=prism, 1=antiprism + u32 base_flag, // color of base, -1:no base + double base_height, // height of base, truncated to 40% of length + // if 0.0: use a plane, otherwise a pyramid + u32 arrow_flag, // color of arror peak at axis2, -1:no arrow + double arrow_size, // relative size of arrow (multiplied by *r), + // truncated to length. no arrow if <=0.0 + bool arrow_flip // true: flip direction of arrow +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendCylinderKCL +( + kcl_t *kcl, // valid KCL data structure + u32 kcl_flag, // value of kcl flag + const double3 *pos, // base point of cylinder + const double3 *p_scale, // scale. If NULL: assume v(1,1,1) + double scale_factor, // Scale is multiplied by this factor + const double3 *rotate, // NULL or rotation vector + uint n_side, // number of sides, max=100 + u32 base_flag // color of bases, -1:no base +); + +//----------------------------------------------------------------------------- + +kcl_tridata_t * AppendJoistKCL +( + kcl_t * kcl, // pointer to valid KCL + u32 kcl_flag, // parameter for AppendTrianglesKCLp() + double length, // length of corpus + const double3 *p1, // first point + const double3 *p2, // second point + const double3 *p3, // helper point to find direction + int n_mark, // >0: add marker representing the number (modulo) + u32 mark5_flag // >0: replace 5 marker by one of this color +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + bool use_data // true: data is valid on 'kcl' live time +); + +//----------------------------------------------------------------------------- + +enumError ScanTextKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // FF_UNNOWN or file format +); + +//----------------------------------------------------------------------------- + +enumError ScanKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + const void * data, // data to scan + uint data_size, // size of 'data' + bool use_data, // true: data is valid on 'kcl' live time + CheckMode_t mode // not NULL: call CheckKCL(mode) +); + +//----------------------------------------------------------------------------- + +struct raw_data_t; + +enumError ScanRawDataKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + struct raw_data_t * raw, // valid raw data + bool move_data, // true: move 'raw.data' to 'kcl' + CheckMode_t mode // not NULL: call CheckKCL(mode) +); + +//----------------------------------------------------------------------------- + +enumError LoadKCL +( + kcl_t * kcl, // KCL data structure + bool init_kcl, // true: initialize 'kcl' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckKCL(mode) +); + +//----------------------------------------------------------------------------- + +int ScanOptLoadKcl ( ccp arg ); +kcl_t * LoadReferenceKCL(); +kcl_t * CreateReferenceKCL(); +void UnloadAutoReferenceKCL(); +void DefineAutoLoadReferenceKCL ( ccp fname, ccp ext ); + +static inline kcl_t * GetReferenceKCL() + { return kcl_ref_prio ? kcl_ref_prio : LoadReferenceKCL(); } + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawKCL +( + kcl_t * kcl, // pointer to valid KCL + bool add_sig // true: add signature +); + +//----------------------------------------------------------------------------- + +enumError SaveRawKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError DumpKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool print_tridata, // true: triangle list + uint print_octree // octree print level (0..2) +); + +//----------------------------------------------------------------------------- + +enumError DumpTrianglesKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname // filename of destination +); + +//----------------------------------------------------------------------------- + +enumError ListTrianglesKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp fname, // filename of destination + bool print_normals, // true: print large tables + uint precision // floating point pre: 0=+0, .. 3=+6 +); + +//----------------------------------------------------------------------------- + +enumError TraverseOctreeKCL +( + kcl_t *kcl, // pointer to valid KCL + FILE *f, // output file, if NULL set verbosity:=0 + double3 pt, // point to analyze + bool print_tri_val, // true: print tri values instead of index + uint verbosity // verbose level + // >=1: print base calculations + // >=2: print step calculations +); + +//----------------------------------------------------------------------------- + +double FallKCL +( + // returns the height or -1.0 on fail + + kcl_t *kcl, // pointer to valid KCL + FILE *f, // output file, if NULL: verbose:=0 + double3 pt, // mid point of cube to analyze + uint width, // width of cube + int verbosity, // verbose level + // <=0: silent + // >=1: log triangle search + // >=2: print base calculations + // >=3: print step calculations + u32 type_mask, // kcl type selector (bit field) + int *res_kcl_flag // not NULL: store KCL flag here +); + +/////////////////////////////////////////////////////////////////////////////// + +int CheckKCL +( + // returns number of errors + + kcl_t * kcl, // pointer to valid KCL + CheckMode_t mode // print mode +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL flags /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void LoadFlagFileKCL +( + kcl_t * kcl // pointer to valid KCL +); + +//----------------------------------------------------------------------------- + +int GetFlagByNameKCL +( + kcl_t * kcl, // pointer to valid KCL + ccp name, // upper case name to search + int new_value // return value if not found + // >0: used as new value + + // The name is searched in 4 steps: + // 1. Search literal in 'flag_name' (ignore case) + // 2. Search pattern in 'flag_pattern' (ignore case) + // 3. Search pattern in 'flag_missing' (ignore case) + // 4. Analyze the last 5 characters for '_ffff' + // 5. Analyze the last 7 characters for '_tt_vvv' + // 6. Use 'new_value' +); + +//----------------------------------------------------------------------------- + +uint * AllocFlagCount(); + +uint CountFlagsKCL +( + // returns the total number of flag groups + + const kcl_t * kcl, // pointer to valid KCL + uint * count // pointer to field with NNN_KCL_FLAG elements +); + +ccp GetGroupNameKCL ( uint kcl_flag, bool add_info ); +uint GetExportFlagKCL ( uint kcl_flag ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KCL Type transformations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern int have_kcl_patch_count; +extern StringField_t kcl_script_list; +extern u16 *patch_kcl_flag; // NULL or 'N_KCL_FLAG' elements + +//----------------------------------------------------------------------------- + +int ScanOptKclScript ( ccp arg ); + +void SetupPatchKclFlag(); +void ResetPatchKclFlag(); +void PurgePatchKclFlag(); +void RemovePatchKclFlag(); +int ScanOptKclFlag ( ccp arg ); +int ScanOptTriArea ( ccp arg ); +int ScanOptTriHeight ( ccp arg ); + +//----------------------------------------------------------------------------- + +bool PatchRawDataKCL +( + void * data, // data to scan + uint data_size, // size of 'data' + ccp fname // file name for error messages +); + +//----------------------------------------------------------------------------- + +bool PatchKCL +( + kcl_t * kcl // pointer to valid KCL +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// octree iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_cube_t]] + +typedef struct kcl_cube_t +{ + s32 min[3]; // minimum cube coordinates + s32 max[3]; // maximum cube coordinates + +} kcl_cube_t; + +//----------------------------------------------------------------------------- +// [[kcl_cube_list_t]] + +#if SUPPORT_KCL_CUBE +#define MAX_CUBE_TRI 10 + +typedef struct kcl_cube_list_t +{ + uint n; // number of used cubes + kcl_cube_t cube[MAX_CUBE_TRI]; // list of cubes + +} kcl_cube_list_t; + +#endif +//----------------------------------------------------------------------------- +// [[kcl_tri_t]] + +typedef struct kcl_tri_t +{ + s32 pt[3][3]; // normalized positions of 3 points + kcl_cube_t cube; // min and max coordinates + u32 select; // select level + +} kcl_tri_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef int (*oct_func) +( + kcl_t *kcl, // pointer to valid KCL + void *param, // user parameter + const kcl_cube_t *cube, // pointer to current cube geometry + // relative to kcl->min_octree; + const u16 *tri_list, // NULL or pointer to triangle index list + uint n_list // number of elements in 'tri_list' +); + +/////////////////////////////////////////////////////////////////////////////// + +int OctreeIteratorKCL +( + kcl_t *kcl, // pointer to valid KCL + oct_func func, // call back function + void *param // user parameter +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// draw png /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern bool opt_png; // if true: PNG drawing is enabled +extern uint opt_png_pix_size; // pixel size, =0: disabled +extern uint opt_png_aalise; // a factor for antialising +extern int opt_png_x1; // x1 * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kmp.h" +//#include "lib-szs.h" +//#include "dclib-xdump.h" + +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define COL_OK reset +#define COL_HINT yellow +#define COL_WARN red +#define COL_DIFF magenta +#define COL_SRC1 cyan +#define COL_SRC2 b_blue + +#define OK_WARN0(a,b,n) a->n == b->n ? ok : warn +#define OK_WARN1(a,b,n) a->n == b->n ? ok : warn, a->n + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// section_info_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[section_info_t]] + +typedef struct section_info_t +{ + kmp_entry_t sect; // section to compare + + kmp_t *kmp1; // first kmp to compare + kmp_t *kmp2; // first kmp to compare + uint verbose; // 0:quiet, 1:summary only, 2:full report + + char sect_name[5]; // section name + char subject[16]; // subject for status messages +} +section_info_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helper functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_float + ( uint n, const float *f1, const float *f2, double epsilon ) +{ + while ( n-- > 0 ) + { + printf(" %s%11.3f", + *f1 == *f2 ? colout->COL_OK + : fabsf(*f1-*f2) > epsilon ? colout->COL_WARN : colout->COL_HINT, + *f1 ); + f1++; + f2++; + } +} + +//----------------------------------------------------------------------------- + +static void print_rot ( uint n, const float *f1, const float *f2 ) +{ + while ( n-- > 0 ) + { + printf(" %s%7.2f", + *f1 == *f2 ? colout->COL_OK + : fabsf(*f1-*f2) > epsilon_rot ? colout->COL_WARN : colout->COL_HINT, + *f1 ); + f1++; + f2++; + } +} + +//----------------------------------------------------------------------------- + +static void print_hex16 ( uint n, const u16 *u1, const u16 *u2 ) +{ + while ( n-- > 0 ) + { + printf(" %s%#6x", + *u1 == *u2 ? colout->COL_OK : colout->COL_WARN, *u1 ); + u1++; + u2++; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ph: helper for CKPH, ENPH, ITPH /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sort_ph_t]] + +typedef struct sort_ph_t +{ + int rank; // previous rank order + kmp_enph_entry_t data; // original object data + +} sort_ph_t; + +//----------------------------------------------------------------------------- + +static sort_ph_t * sort_ph ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_ph_t *res = CALLOC(list->used,sizeof(*res)); + sort_ph_t *ptr = res; + kmp_enph_entry_t *e = (kmp_enph_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_ph ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static void print_ph + ( char mode, const kmp_enph_entry_t *p1, const kmp_enph_entry_t *p2 ) +{ + if ( p2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + ccp warn = colout->COL_WARN; + + const uint end1 = p1->pt_start + p1->pt_len - 1; + const uint end2 = p2->pt_start + p2->pt_len - 1; + printf("%c %s%3u %s%3u %s%3u %s:", + mode, + p1->pt_start == p2->pt_start ? ok : warn, p1->pt_start, + end1 == end2 ? ok : colout->COL_HINT, end1, + p1->pt_len == p2->pt_len ? ok : warn, p1->pt_len, + colout->reset ); + + uint j; + const u8 *d1 = p1->prev; + const u8 *d2 = p2->prev; + for ( j = 0; j < 12; j++, d1++, d2++ ) + { + if ( j == 6 ) + printf(" %s:",colout->reset); + fputs( *d1 == *d2 ? ok : warn, stdout ); + if ( *d1 == 0xff ) + fputs(" -",stdout); + else + printf("%4u",*d1); + } + putchar('\n'); + } + else + { + printf("%c %3u %3u %3u :", + mode, p1->pt_start, p1->pt_start + p1->pt_len - 1, p1->pt_len ); + + uint j; + const u8 *d1 = p1->prev; + for ( j = 0; j < 12; j++, d1++ ) + { + if ( j == 6 ) + fputs(" :",stdout); + if ( *d1 == 0xff ) + fputs(" -",stdout); + else + printf("%4u",*d1); + } + putchar('\n'); + } +} + +//----------------------------------------------------------------------------- + +static sort_ph_t * print_ph_single ( const section_info_t *si, + char mode, const sort_ph_t *e, sort_ph_t *end ) +{ + DASSERT(si); + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%s%s: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + si->sect_name, mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_ph(mode,&e->data,0); + e++; + } + return (sort_ph_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_ph ( const sort_ph_t *e1, const sort_ph_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + uint count = ( e1->data.pt_start != e2->data.pt_start ) + + ( e1->data.pt_len != e2->data.pt_len ); + + if (memcmp(e1->data.prev,e2->data.prev,sizeof(e1->data.prev))) + count++; + if (memcmp(e1->data.next,e2->data.next,sizeof(e1->data.next))) + count++; + if (memcmp(e1->data.setting,e2->data.setting,sizeof(e1->data.setting))) + count++; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_ph_si ( const section_info_t *si ) +{ + DASSERT(si); + DASSERT(si->kmp1); + DASSERT(si->kmp2); + + uint diff_count = 0; + List_t *l1 = si->kmp1->dlist + si->sect; + List_t *l2 = si->kmp2->dlist + si->sect; + + if ( l1->used != l2->used ) + { + if (!si->verbose) + return 1; + printf("%s%s: Number of %s groups differ: Have %u and %u.%s\n", + colout->COL_DIFF, + si->sect_name, si->subject, l1->used, l2->used, + colout->reset ); + if (si->verbose==1) + return 1; + diff_count++; + } + + sort_ph_t *s1 = sort_ph(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_ph_t *s2 = sort_ph(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_ph(e1,e2); + if (stat) + { + if ( si->verbose < 2 ) + { + if (si->verbose) + printf("%s%s: Data differ%s\n", + colout->COL_DIFF, si->sect_name, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_ph_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_ph(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_ph_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_ph(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_ph_single(si,'<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_ph_single(si,'>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%s%s: %c%s group @%u:%u differ%s\n", + colout->COL_DIFF, + si->sect_name, + toupper((int)si->subject[0]), si->subject+1, + e1->rank, e2->rank, colout->reset ); + print_ph('<',&e1->data,&e2->data); + print_ph('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_ph_single(si,'<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_ph_single(si,'>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (si->verbose) + printf("%s%s: Order of groups differ%s\n", + colout->COL_DIFF, si->sect_name, colout->reset ); + if ( si->verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// pt: helper for ENPT, ITPT /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sort_pt_t]] + +typedef struct sort_pt_t +{ + int rank; // previous rank order + kmp_enpt_entry_t data; // original object data + +} sort_pt_t; + +//----------------------------------------------------------------------------- + +static sort_pt_t * sort_pt ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_pt_t *res = CALLOC(list->used,sizeof(*res)); + sort_pt_t *ptr = res; + kmp_enpt_entry_t *e = (kmp_enpt_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_pt ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static void print_pt + ( char mode, const kmp_enpt_entry_t *p1, const kmp_enpt_entry_t *p2 ) +{ + if ( p2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + ccp warn = colout->COL_WARN; + + putchar(mode); + putchar(' '); + print_float(3,p1->position,p2->position,epsilon_pos); + + printf(" %s%7.3f %s%#6x %s%#6x%s\n", + p1->scale == p2->scale ? ok + : fabsf(p1->scale-p2->scale) > epsilon_scale ? colout->COL_WARN + : colout->COL_HINT, + p1->scale, + p1->prop[0] == p2->prop[0] ? ok : warn, p1->prop[0], + p1->prop[1] == p2->prop[1] ? ok : warn, p1->prop[1], + colout->reset ); + } + else + { + printf("%c %11.3f %11.3f %11.3f %7.3f %#6x %#6x\n", + mode, + p1->position[0], p1->position[1], p1->position[2], + p1->scale, + p1->prop[0], p1->prop[1] ); + } +} + +//----------------------------------------------------------------------------- + +static sort_pt_t * print_pt_single ( const section_info_t *si, + char mode, const sort_pt_t *e, sort_pt_t *end ) +{ + DASSERT(si); + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%s%s: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + si->sect_name, mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_pt(mode,&e->data,0); + e++; + } + return (sort_pt_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_pt ( const sort_pt_t *e1, const sort_pt_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + uint count = ( e1->data.prop[0] != e2->data.prop[0] ) + + ( e1->data.prop[1] != e2->data.prop[1] ); + + count += fabsf( e1->data.position[0] - e2->data.position[0] ) > epsilon_pos + || fabsf( e1->data.position[1] - e2->data.position[1] ) > epsilon_pos + || fabsf( e1->data.position[2] - e2->data.position[2] ) > epsilon_pos; + + count += fabsf( e1->data.scale - e2->data.scale ) > epsilon_scale; + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_pt_si ( const section_info_t *si ) +{ + DASSERT(si); + DASSERT(si->kmp1); + DASSERT(si->kmp2); + + uint diff_count = 0; + List_t *l1 = si->kmp1->dlist + si->sect; + List_t *l2 = si->kmp2->dlist + si->sect; + + if ( l1->used != l2->used ) + { + if (!si->verbose) + return 1; + printf("%s%s: Number of %ss differ: Have %u and %u.%s\n", + colout->COL_DIFF, + si->sect_name, si->subject, l1->used, l2->used, + colout->reset ); + if (si->verbose==1) + return 1; + diff_count++; + } + + sort_pt_t *s1 = sort_pt(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_pt_t *s2 = sort_pt(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_pt(e1,e2); + if (stat) + { + if ( si->verbose < 2 ) + { + if (si->verbose) + printf("%s%s: Data differ%s\n", + colout->COL_DIFF, si->sect_name, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_pt_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_pt(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_pt_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_pt(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_pt_single(si,'<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_pt_single(si,'>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%s%s: %s @%u:%u differ%s\n", + colout->COL_DIFF, + si->sect_name, si->subject, + e1->rank, e2->rank, colout->reset ); + print_pt('<',&e1->data,&e2->data); + print_pt('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_pt_single(si,'<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_pt_single(si,'>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (si->verbose) + printf("%s%s: Order of %ss differ%s\n", + colout->COL_DIFF, si->sect_name, + si->subject, colout->reset ); + if ( si->verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// pt2: helper for CNPT, JGPT, MSPT /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sort_pt2_t]] + +typedef struct sort_pt2_t +{ + int rank; // previous rank order + kmp_jgpt_entry_t data; // original object data + +} sort_pt2_t; + +//----------------------------------------------------------------------------- + +static sort_pt2_t * sort_pt2 ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_pt2_t *res = CALLOC(list->used,sizeof(*res)); + sort_pt2_t *ptr = res; + kmp_jgpt_entry_t *e = (kmp_jgpt_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_pt2 ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static void print_pt2 + ( char mode, const kmp_jgpt_entry_t *p1, const kmp_jgpt_entry_t *p2 ) +{ + if ( p2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + + putchar(mode); + print_float(3,p1->position,p2->position,epsilon_pos); + putchar(' '); + print_rot(3,p1->rotation,p2->rotation); + + printf(" %s%6d %s%#6x%s\n", + p1->id == p2->id ? ok : colout->COL_HINT, p1->id, + p1->effect == p2->effect ? ok : colout->COL_WARN, p1->effect, + colout->reset ); + } + else + { + printf("%c %11.3f %11.3f %11.3f %7.2f %7.2f %7.2f %6d %#6x\n", + mode, + p1->position[0], p1->position[1], p1->position[2], + p1->rotation[0], p1->rotation[1], p1->rotation[2], + p1->id, p1->effect ); + } +} + +//----------------------------------------------------------------------------- + +static sort_pt2_t * print_pt2_single ( const section_info_t *si, + char mode, const sort_pt2_t *e, sort_pt2_t *end ) +{ + DASSERT(si); + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%s%s: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + si->sect_name, mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_pt2(mode,&e->data,0); + e++; + } + return (sort_pt2_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_pt2 ( const sort_pt2_t *e1, const sort_pt2_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + // 'data.id' ignored! + uint count = e1->data.effect != e2->data.effect; + + count += fabsf( e1->data.position[0] - e2->data.position[0] ) > epsilon_pos + || fabsf( e1->data.position[1] - e2->data.position[1] ) > epsilon_pos + || fabsf( e1->data.position[2] - e2->data.position[2] ) > epsilon_pos; + + count += fabsf( e1->data.rotation[0] - e2->data.rotation[0] ) > epsilon_rot + || fabsf( e1->data.rotation[1] - e2->data.rotation[1] ) > epsilon_rot + || fabsf( e1->data.rotation[2] - e2->data.rotation[2] ) > epsilon_rot; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_pt2_si ( const section_info_t *si ) +{ + DASSERT(si); + DASSERT(si->kmp1); + DASSERT(si->kmp2); + + uint diff_count = 0; + List_t *l1 = si->kmp1->dlist + si->sect; + List_t *l2 = si->kmp2->dlist + si->sect; + + if ( l1->used != l2->used ) + { + if (!si->verbose) + return 1; + printf("%s%s: Number of %s points differ: Have %u and %u.%s\n", + colout->COL_DIFF, + si->sect_name, si->subject, l1->used, l2->used, + colout->reset ); + if (si->verbose==1) + return 1; + diff_count++; + } + + sort_pt2_t *s1 = sort_pt2(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_pt2_t *s2 = sort_pt2(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_pt2(e1,e2); + if (stat) + { + if ( si->verbose < 2 ) + { + if (si->verbose) + printf("%s%s: Data differ%s\n", + colout->COL_DIFF, si->sect_name, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_pt2_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_pt2(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_pt2_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_pt2(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_pt2_single(si,'<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_pt2_single(si,'>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%s%s: %s group @%u:%u differ%s\n", + colout->COL_DIFF, + si->sect_name, si->subject, + e1->rank, e2->rank, colout->reset ); + print_pt2('<',&e1->data,&e2->data); + print_pt2('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_pt2_single(si,'<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_pt2_single(si,'>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (si->verbose) + printf("%s%s: Order of %s points differ%s\n", + colout->COL_DIFF, si->sect_name, + si->subject, colout->reset ); + if ( si->verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_AREA() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct sort_area_t +{ + int rank; // previous rank order + kmp_area_entry_t data; // original object data + +} sort_area_t; + +//----------------------------------------------------------------------------- + +static sort_area_t * sort_area ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_area_t *res = CALLOC(list->used,sizeof(*res)); + sort_area_t *ptr = res; + kmp_area_entry_t *e = (kmp_area_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_area ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static void print_area + ( char mode, const kmp_area_entry_t *e1, const kmp_area_entry_t *e2 ) +{ + if ( e2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + ccp warn = colout->COL_WARN; + + printf("%c %s%5u %s%5u", + mode, OK_WARN1(e1,e2,mode), OK_WARN1(e1,e2,type) ); + print_float(3,e1->position,e2->position,epsilon_pos); + putchar(' '); + print_hex16(4,e1->setting,e2->setting); + + printf("%s\n%c %s%5u %s%5u", + colout->reset, mode, + OK_WARN1(e1,e2,dest_id), OK_WARN1(e1,e2,unknown) ); + print_float(3,e1->rotation,e2->rotation,epsilon_rot); + + printf("%s\n%-14c", colout->reset, mode ); + print_float(3,e1->scale,e2->scale,epsilon_scale); + + printf("%s\n",colout->reset); + } + else + { + printf( "%c %5u %5u %11.3f %11.3f %11.3f %#6x %#6x %#6x %#6x\n" + "%c %5u %5u %11.3f %11.3f %11.3f\n" + "%c %24.3f %11.3f %11.3f\n", + mode, + e1->mode, e1->type, + e1->position[0], e1->position[1], e1->position[2], + e1->setting[0], e1->setting[1], e1->setting[2], e1->setting[3], + mode, + e1->dest_id, e1->unknown, + e1->rotation[0], e1->rotation[1], e1->rotation[2], + mode, + e1->scale[0], e1->scale[1], e1->scale[2] ); + } +} + +//----------------------------------------------------------------------------- + +static sort_area_t * print_area_single + ( char mode, const sort_area_t *e, sort_area_t *end ) +{ + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%sAREA: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_area(mode,&e->data,0); + e++; + } + return (sort_area_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_area ( const sort_area_t *e1, const sort_area_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + uint count = ( e1->data.mode != e2->data.mode ) + + ( e1->data.type != e2->data.type ) + + ( e1->data.dest_id != e2->data.dest_id ) + + ( e1->data.unknown != e2->data.unknown ); + + uint i; + for ( i = 0; i < 4; i++ ) + count += e1->data.setting[i] != e2->data.setting[i]; + + count += fabsf( e1->data.position[0] - e2->data.position[0] ) > epsilon_pos + || fabsf( e1->data.position[1] - e2->data.position[1] ) > epsilon_pos + || fabsf( e1->data.position[2] - e2->data.position[2] ) > epsilon_pos; + + count += fabsf( e1->data.rotation[0] - e2->data.rotation[0] ) > epsilon_rot + || fabsf( e1->data.rotation[1] - e2->data.rotation[1] ) > epsilon_rot + || fabsf( e1->data.rotation[2] - e2->data.rotation[2] ) > epsilon_rot; + + count += fabsf( e1->data.scale[0] - e2->data.scale[0] ) > epsilon_scale + || fabsf( e1->data.scale[1] - e2->data.scale[1] ) > epsilon_scale + || fabsf( e1->data.scale[2] - e2->data.scale[2] ) > epsilon_scale; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_KMP_AREA +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + uint diff_count = 0; + List_t *l1 = kmp1->dlist + KMP_AREA; + List_t *l2 = kmp2->dlist + KMP_AREA; + + if ( l1->used != l2->used ) + { + if (!verbose) + return 1; + printf("%sAREA: Number of areas differ: Have %u and %u.%s\n", + colout->COL_WARN, l1->used, l2->used, colout->reset ); + if (verbose==1) + return 1; + diff_count++; + } + + sort_area_t *s1 = sort_area(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_area_t *s2 = sort_area(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_area(e1,e2); + if (stat) + { + if ( verbose < 2 ) + { + if (verbose) + printf("%sAREA: Data differ%s\n", + colout->COL_DIFF, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_area_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_area(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_area_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_area(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_area_single('<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_area_single('>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%sAREA: Area @%u:%u differ%s\n", + colout->COL_DIFF, e1->rank,e2->rank, + colout->reset ); + print_area('<',&e1->data,&e2->data); + print_area('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_area_single('<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_area_single('>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (verbose) + printf("%sAREA: Order of areas differ%s\n", + colout->COL_DIFF, colout->reset ); + if ( verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_CAME() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct sort_came_t +{ + int rank; // previous rank order + kmp_came_entry_t data; // original object data + +} sort_came_t; + +//----------------------------------------------------------------------------- + +static sort_came_t * sort_came ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_came_t *res = CALLOC(list->used,sizeof(*res)); + sort_came_t *ptr = res; + kmp_came_entry_t *e = (kmp_came_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_came ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static void print_came + ( char mode, const kmp_came_entry_t *e1, const kmp_came_entry_t *e2 ) +{ + if ( e2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + ccp warn = colout->COL_WARN; + + printf("%c %s%5u %s%5u ", + mode, + OK_WARN1(e1,e2,type), + OK_WARN1(e1,e2,came_speed) ); + print_float(3,e1->position,e2->position,epsilon_pos); + putchar(' '); + print_float(1,&e1->zoom_begin,&e2->zoom_begin,epsilon_pos); + + printf("%s\n%c %s%5s %s%5u ", + colout->reset, + mode, + OK_WARN0(e1,e2,next),PrintU8M1(e1->next), + OK_WARN1(e1,e2,zoom_speed) ); + print_float(3,e1->rotation,e2->rotation,epsilon_pos); + putchar(' '); + print_float(1,&e1->zoom_end,&e2->zoom_end,epsilon_pos); + + printf("%s\n%c %s%5u %s%5u ", + colout->reset, + mode, + OK_WARN1(e1,e2,unknown_0x02), + OK_WARN1(e1,e2,viewpt_speed) ); + print_float(3,e1->viewpt_begin,e2->viewpt_begin,epsilon_pos); + + printf("%s\n%c %s%5s %s%5u ", + colout->reset, + mode, + OK_WARN0(e1,e2,route),PrintU8M1(e1->route), + OK_WARN1(e1,e2,unknown_0x0a) ); + print_float(3,e1->viewpt_end,e2->viewpt_end,epsilon_pos); + putchar(' '); + print_float(1,&e1->time,&e2->time,epsilon_time); + + printf("%s\n",colout->reset); + } + else + { + printf( "%c %5u %5u %11.3f %11.3f %11.3f %11.3f\n" + "%c %5s %5u %11.3f %11.3f %11.3f %11.3f\n" + "%c %5u %5u %11.3f %11.3f %11.3f\n" + "%c %5s %5u %11.3f %11.3f %11.3f %11.3f\n", + mode, + e1->type, e1->came_speed, + e1->position[0], e1->position[1], e1->position[2], + e1->zoom_begin, + mode, + PrintU8M1(e1->next), e1->zoom_speed, + e1->rotation[0], e1->rotation[1], e1->rotation[2], + e1->zoom_end, + mode, + e1->unknown_0x02, e1->viewpt_speed, + e1->viewpt_begin[0], e1->viewpt_begin[1], e1->viewpt_begin[2], + mode, + PrintU8M1(e1->route), e1->unknown_0x0a, + e1->viewpt_end[0], e1->viewpt_end[1], e1->viewpt_end[2], + e1->time ); + } +} + +//----------------------------------------------------------------------------- + +static sort_came_t * print_came_single + ( char mode, const sort_came_t *e, sort_came_t *end ) +{ + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%sCAME: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_came(mode,&e->data,0); + e++; + } + return (sort_came_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_came ( const sort_came_t *e1, const sort_came_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + uint count = ( e1->data.type != e2->data.type ) + + ( e1->data.next != e2->data.next ) + + ( e1->data.unknown_0x02 != e2->data.unknown_0x02 ) + + ( e1->data.route != e2->data.route ) + + ( e1->data.came_speed != e2->data.came_speed ) + + ( e1->data.zoom_speed != e2->data.zoom_speed ) + + ( e1->data.viewpt_speed != e2->data.viewpt_speed ) + + ( e1->data.unknown_0x0a != e2->data.unknown_0x0a ); + + count += fabsf( e1->data.position[0] - e2->data.position[0] ) > epsilon_pos + || fabsf( e1->data.position[1] - e2->data.position[1] ) > epsilon_pos + || fabsf( e1->data.position[2] - e2->data.position[2] ) > epsilon_pos; + + count += fabsf( e1->data.rotation[0] - e2->data.rotation[0] ) > epsilon_rot + || fabsf( e1->data.rotation[1] - e2->data.rotation[1] ) > epsilon_rot + || fabsf( e1->data.rotation[2] - e2->data.rotation[2] ) > epsilon_rot; + + count += fabsf( e1->data.zoom_begin - e2->data.zoom_begin ) > epsilon_scale + || fabsf( e1->data.zoom_end - e2->data.zoom_end ) > epsilon_scale; + + count += fabsf( e1->data.viewpt_begin[0] - e2->data.viewpt_begin[0] ) > epsilon_pos + || fabsf( e1->data.viewpt_begin[1] - e2->data.viewpt_begin[1] ) > epsilon_pos + || fabsf( e1->data.viewpt_begin[2] - e2->data.viewpt_begin[2] ) > epsilon_pos; + + + count += fabsf( e1->data.viewpt_end[0] - e2->data.viewpt_end[0] ) > epsilon_pos + || fabsf( e1->data.viewpt_end[1] - e2->data.viewpt_end[1] ) > epsilon_pos + || fabsf( e1->data.viewpt_end[2] - e2->data.viewpt_end[2] ) > epsilon_pos; + + count += fabsf( e1->data.time - e2->data.time ) > epsilon_time; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_KMP_CAME +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + uint diff_count = 0; + List_t *l1 = kmp1->dlist + KMP_CAME; + List_t *l2 = kmp2->dlist + KMP_CAME; + + if ( l1->used != l2->used ) + { + if (!verbose) + return 1; + printf("%sCAME: Number of cameras differ: Have %u and %u.%s\n", + colout->COL_WARN, l1->used, l2->used, colout->reset ); + if (verbose==1) + return 1; + diff_count++; + } + + sort_came_t *s1 = sort_came(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_came_t *s2 = sort_came(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_came(e1,e2); + if (stat) + { + if ( verbose < 2 ) + { + if (verbose) + printf("%sCAME: Data differ%s\n", + colout->COL_DIFF, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_came_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_came(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_came_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_came(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_came_single('<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_came_single('>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%sCAME: Camera @%u:%u differ%s\n", + colout->COL_DIFF, e1->rank,e2->rank, + colout->reset ); + print_came('<',&e1->data,&e2->data); + print_came('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_came_single('<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_came_single('>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (verbose) + printf("%sCAME: Order of cameras differ%s\n", + colout->COL_DIFF, colout->reset ); + if ( verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_CKPH() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_CKPH +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_CKPH,kmp1,kmp2,verbose,"CKPH","check point"}; + return diff_ph_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_CKPT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct sort_ckpt_t +{ + int rank; // previous rank order + kmp_ckpt_entry_t data; // original object data + +} sort_ckpt_t; + +//----------------------------------------------------------------------------- + +static sort_ckpt_t * sort_ckpt ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_ckpt_t *res = CALLOC(list->used,sizeof(*res)); + sort_ckpt_t *ptr = res; + kmp_ckpt_entry_t *e = (kmp_ckpt_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_ckpt ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static void print_ckpt + ( char mode, const kmp_ckpt_entry_t *e1, const kmp_ckpt_entry_t *e2 ) +{ + if ( e2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + ccp warn = colout->COL_WARN; + + putchar(mode); + print_float(2,e1->left,e2->left,epsilon_pos); + putchar(' '); + print_float(2,e1->right,e2->right,epsilon_pos); + + printf(" %s%3d %s%3s %s%3s %s%3s%s\n", + OK_WARN1(e1,e2,respawn), + OK_WARN0(e1,e2,mode), PrintU8M1(e1->mode), + OK_WARN0(e1,e2,prev), PrintU8M1(e1->prev), + OK_WARN0(e1,e2,next), PrintU8M1(e1->next), + colout->reset ); + } + else + { + printf("%c %11.3f %11.3f %11.3f %11.3f %3d %3s %3s %3s\n", + mode, + e1->left[0], e1->left[1], + e1->right[0], e1->right[1], + e1->respawn, + PrintU8M1(e1->mode), + PrintU8M1(e1->prev), + PrintU8M1(e1->next) ); + } +} + +//----------------------------------------------------------------------------- + +static sort_ckpt_t * print_ckpt_single + ( char mode, const sort_ckpt_t *e, sort_ckpt_t *end ) +{ + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%sCKPT: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_ckpt(mode,&e->data,0); + e++; + } + return (sort_ckpt_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_ckpt ( const sort_ckpt_t *e1, const sort_ckpt_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + uint count = ( e1->data.respawn != e2->data.respawn ) + + ( e1->data.mode != e2->data.mode ) + + ( e1->data.prev != e2->data.prev ) + + ( e1->data.next != e2->data.next ); + + count += fabsf( e1->data.left[0] - e2->data.left[0] ) > epsilon_pos + || fabsf( e1->data.left[1] - e2->data.left[1] ) > epsilon_pos; + + count += fabsf( e1->data.right[0] - e2->data.right[0] ) > epsilon_pos + || fabsf( e1->data.right[1] - e2->data.right[1] ) > epsilon_pos; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_KMP_CKPT +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + uint diff_count = 0; + List_t *l1 = kmp1->dlist + KMP_CKPT; + List_t *l2 = kmp2->dlist + KMP_CKPT; + + if ( l1->used != l2->used ) + { + if (!verbose) + return 1; + printf("%sCKPT: Number of check points differ: Have %u and %u.%s\n", + colout->COL_WARN, l1->used, l2->used, colout->reset ); + if (verbose==1) + return 1; + diff_count++; + } + + sort_ckpt_t *s1 = sort_ckpt(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_ckpt_t *s2 = sort_ckpt(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_ckpt(e1,e2); + if (stat) + { + if ( verbose < 2 ) + { + if (verbose) + printf("%sCKPT: Data differ%s\n", + colout->COL_DIFF, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_ckpt_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_ckpt(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_ckpt_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_ckpt(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_ckpt_single('<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_ckpt_single('>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%sCKPT: CKPT @%u:%u differ%s\n", + colout->COL_DIFF, e1->rank,e2->rank, + colout->reset ); + print_ckpt('<',&e1->data,&e2->data); + print_ckpt('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_ckpt_single('<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_ckpt_single('>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (verbose) + printf("%sCKPT: Order of check points differ%s\n", + colout->COL_DIFF, colout->reset ); + if ( verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_CNPT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_CNPT +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_CNPT,kmp1,kmp2,verbose,"CNPT","cannon"}; + return diff_pt2_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_ENPH() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_ENPH +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_ENPH,kmp1,kmp2,verbose,"ENPH","enemy point"}; + return diff_ph_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_ENPT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_ENPT +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_ENPT,kmp1,kmp2,verbose,"ENPT","enemy point"}; + return diff_pt_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_GOBJ() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sort_gobj_t]] + +typedef struct sort_gobj_t +{ + int obj_id; // relevant object id + int rank; // previous rank order + kmp_gobj_entry_t data; // original object data + +} sort_gobj_t; + +//----------------------------------------------------------------------------- + +static int compare_sort_gobj ( const sort_gobj_t * a, const sort_gobj_t * b ) +{ + int stat = a->obj_id - b->obj_id; + if (!stat) + stat = a->rank - b->rank; + return stat; +} + +//----------------------------------------------------------------------------- + +static sort_gobj_t * sort_gobj ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_gobj_t *res = CALLOC(list->used,sizeof(*res)); + sort_gobj_t *sg = res; + kmp_gobj_entry_t *e = (kmp_gobj_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, sg++ ) + { + sg->obj_id = e->obj_id & ~GOBJ_M_LECODE; + sg->rank = i; + memcpy(&sg->data,e,sizeof(sg->data)); + } + + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_gobj ); + return res; +} + +//----------------------------------------------------------------------------- + +static void print_gobj ( char mode, const kmp_gobj_entry_t *g1, + const kmp_gobj_entry_t *g2, const ObjectInfo_t *oi ) +{ + char buf1[32], buf2[32]; + if (!oi) + oi = GetObjectInfo(g1->obj_id); + DASSERT(oi); + + if ( g2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + ccp warn = colout->COL_WARN; + + printf("%c %s%#6x", + mode, g1->obj_id == g2->obj_id ? ok : warn, g1->obj_id ); + print_float(3,g1->position,g2->position,epsilon_pos); + + fputs(" ",stdout); + printf( oi->format1 ? oi->format1->col : settings_format_uuuu.col, + g1->setting[0] == g2->setting[0] ? ok : warn , g1->setting[0], + g1->setting[1] == g2->setting[1] ? ok : warn , g1->setting[1], + g1->setting[2] == g2->setting[2] ? ok : warn , g1->setting[2], + g1->setting[3] == g2->setting[3] ? ok : warn , g1->setting[3] ); + + printf(" %s%6s%s\n%c %s%#6x", + g1->route_id == g2->route_id ? ok : warn, PrintU16M1(g1->route_id), + colout->reset, + mode, + g1->ref_id == g2->ref_id ? ok : warn, g1->ref_id ); + print_float(3,g1->rotation,g2->rotation,epsilon_rot); + + fputs(" ",stdout); + printf( oi->format2 ? oi->format2->col : settings_format_uuuu.col, + g1->setting[4] == g2->setting[4] ? ok : warn , g1->setting[4], + g1->setting[5] == g2->setting[5] ? ok : warn , g1->setting[5], + g1->setting[6] == g2->setting[6] ? ok : warn , g1->setting[6], + g1->setting[7] == g2->setting[7] ? ok : warn , g1->setting[7] ); + + printf(" %s%#6x%s\n%c ", + g1->pflags == g2->pflags ? ok : warn, g1->pflags, + colout->reset, mode ); + print_float(3,g1->scale,g2->scale,epsilon_scale); + + printf( "%s\n",colout->reset); + } + else + { + snprintf( buf1, sizeof(buf1), + oi->format1 ? oi->format1->std : settings_format_uuuu.std, + g1->setting[0], g1->setting[1], g1->setting[2], g1->setting[3] ); + + snprintf( buf2, sizeof(buf2), + oi->format2 ? oi->format2->std : settings_format_uuuu.std, + g1->setting[4], g1->setting[5], g1->setting[6], g1->setting[7] ); + + printf( "%c %#6x %11.3f %11.3f %11.3f %s %6s\n" + "%c %#6x %11.3f %11.3f %11.3f %s %#6x\n" + "%c %11.3f %11.3f %11.3f\n", + mode, g1->obj_id, + g1->position[0], g1->position[1], g1->position[2], + buf1, PrintU16M1(g1->route_id), + mode, g1->ref_id, + g1->rotation[0], g1->rotation[1], g1->rotation[2], + buf2, g1->pflags, + mode, g1->scale[0], g1->scale[1], g1->scale[2] + ); + } +} + +//----------------------------------------------------------------------------- + +static sort_gobj_t * print_gobj_single + ( char mode, const sort_gobj_t *e, sort_gobj_t *end ) +{ + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + const ObjectInfo_t *oi = GetObjectInfo(e->data.obj_id); + printf("%sGOBJ: Only found in source %c (@%u,%s)%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + mode == '<' ? '1' : '2', e->rank, + oi->name ? oi->name : "?", + colout->reset ); + print_gobj(mode,&e->data,0,0); + e++; + } + return (sort_gobj_t*)e; +} + +//----------------------------------------------------------------------------- +// return 0:equal, >0:number of different elements, 99:different relevant obj_id + +static int diff_gobj ( const sort_gobj_t *e1, const sort_gobj_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + if ( e1->obj_id != e2->obj_id ) + return 99; + + uint count = ( e1->data.obj_id != e2->data.obj_id ) + + ( e1->data.ref_id != e2->data.ref_id ) + + ( e1->data.route_id != e2->data.route_id ) + + ( e1->data.pflags != e2->data.pflags ); + + uint i; + for ( i = 0; i < 8; i++ ) + count += e1->data.setting[i] != e2->data.setting[i]; + + count += fabsf( e1->data.position[0] - e2->data.position[0] ) > epsilon_pos + || fabsf( e1->data.position[1] - e2->data.position[1] ) > epsilon_pos + || fabsf( e1->data.position[2] - e2->data.position[2] ) > epsilon_pos; + + count += fabsf( e1->data.rotation[0] - e2->data.rotation[0] ) > epsilon_rot + || fabsf( e1->data.rotation[1] - e2->data.rotation[1] ) > epsilon_rot + || fabsf( e1->data.rotation[2] - e2->data.rotation[2] ) > epsilon_rot; + + count += fabsf( e1->data.scale[0] - e2->data.scale[0] ) > epsilon_scale + || fabsf( e1->data.scale[1] - e2->data.scale[1] ) > epsilon_scale + || fabsf( e1->data.scale[2] - e2->data.scale[2] ) > epsilon_scale; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_KMP_GOBJ +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + uint diff_count = 0; + List_t *l1 = kmp1->dlist + KMP_GOBJ; + List_t *l2 = kmp2->dlist + KMP_GOBJ; + + if ( l1->used != l2->used ) + { + if (!verbose) + return 1; + printf("%sGOBJ: Number of objects differ: Have %u and %u.%s\n", + colout->COL_DIFF, l1->used, l2->used, colout->reset ); + if (verbose==1) + return 1; + diff_count++; + } + + sort_gobj_t *sg1 = sort_gobj(l1), *e1 = sg1, *end1 = e1 + l1->used; + sort_gobj_t *sg2 = sort_gobj(l2), *e2 = sg2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + if ( e1->obj_id < e2->obj_id ) + { + e1 = print_gobj_single('<',e1,e1+1); + diff_count++; + continue; + } + + if ( e2->obj_id < e1->obj_id ) + { + e2 = print_gobj_single('>',e2,e2+1); + diff_count++; + continue; + } + + const int stat = diff_gobj(e1,e2); + if (stat) + { + if ( verbose < 2 ) + { + if (verbose) + printf("%sGOBJ: Data differ%s\n", + colout->COL_DIFF, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_gobj_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_gobj(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_gobj_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_gobj(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + //e1 = print_gobj_single('<',e1,p1); + e1 = print_gobj_single('<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + //e2 = print_gobj_single('>',e2,p2); + e2 = print_gobj_single('>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + const ObjectInfo_t *oi = GetObjectInfo(e1->data.obj_id); + printf("%sGOBJ: Object @%u:%u (%s) differ%s\n", + colout->COL_DIFF, e1->rank,e2->rank, + oi->name ? oi->name : "?", colout->reset ); + print_gobj('<',&e1->data,&e2->data,oi); + print_gobj('>',&e2->data,&e1->data,oi); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_gobj_single('<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_gobj_single('>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (verbose) + printf("%sGOBJ: Order of objects differ%s\n", + colout->COL_DIFF, colout->reset ); + if ( verbose < 2 ) + return 1; + diff_count++; + } + + FREE(sg1); + FREE(sg2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_ITPH() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_ITPH +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_ITPH,kmp1,kmp2,verbose,"ITPH","item point"}; + return diff_ph_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_ITPT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_ITPT +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_ITPT,kmp1,kmp2,verbose,"ITPT","item point"}; + return diff_pt_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_JGPT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_JGPT +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_JGPT,kmp1,kmp2,verbose,"JGPT","respawn"}; + return diff_pt2_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_KTPT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct sort_ktpt_t +{ + int rank; // previous rank order + kmp_ktpt_entry_t data; // original object data + +} sort_ktpt_t; + +//----------------------------------------------------------------------------- + +static sort_ktpt_t * sort_ktpt ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_ktpt_t *res = CALLOC(list->used,sizeof(*res)); + sort_ktpt_t *ptr = res; + kmp_ktpt_entry_t *e = (kmp_ktpt_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_ktpt ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static inline void print_ktpt + ( char mode, const kmp_ktpt_entry_t *e1, const kmp_ktpt_entry_t *e2 ) +{ + print_pt2(mode,(kmp_jgpt_entry_t*)e1,(kmp_jgpt_entry_t*)e2); +} + +//----------------------------------------------------------------------------- + +static sort_ktpt_t * print_ktpt_single + ( char mode, const sort_ktpt_t *e, sort_ktpt_t *end ) +{ + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%sKTPT: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_ktpt(mode,&e->data,0); + e++; + } + return (sort_ktpt_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_ktpt ( const sort_ktpt_t *e1, const sort_ktpt_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + uint count = ( e1->data.player_index != e2->data.player_index ) + + ( e1->data.unknown != e2->data.unknown ); + + count += fabsf( e1->data.position[0] - e2->data.position[0] ) > epsilon_pos + || fabsf( e1->data.position[1] - e2->data.position[1] ) > epsilon_pos + || fabsf( e1->data.position[2] - e2->data.position[2] ) > epsilon_pos; + + count += fabsf( e1->data.rotation[0] - e2->data.rotation[0] ) > epsilon_rot + || fabsf( e1->data.rotation[1] - e2->data.rotation[1] ) > epsilon_rot + || fabsf( e1->data.rotation[2] - e2->data.rotation[2] ) > epsilon_rot; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_KMP_KTPT +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + uint diff_count = 0; + List_t *l1 = kmp1->dlist + KMP_KTPT; + List_t *l2 = kmp2->dlist + KMP_KTPT; + + if ( l1->used != l2->used ) + { + if (!verbose) + return 1; + printf("%sKTPT: Number of start positions differ: Have %u and %u.%s\n", + colout->COL_WARN, l1->used, l2->used, colout->reset ); + if (verbose==1) + return 1; + diff_count++; + } + + sort_ktpt_t *s1 = sort_ktpt(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_ktpt_t *s2 = sort_ktpt(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_ktpt(e1,e2); + if (stat) + { + if ( verbose < 2 ) + { + if (verbose) + printf("%sKTPT: Data differ%s\n", + colout->COL_DIFF, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_ktpt_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_ktpt(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_ktpt_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_ktpt(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_ktpt_single('<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_ktpt_single('>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%sKTPT: KTPT @%u:%u differ%s\n", + colout->COL_DIFF, e1->rank,e2->rank, + colout->reset ); + print_ktpt('<',&e1->data,&e2->data); + print_ktpt('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_ktpt_single('<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_ktpt_single('>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (verbose) + printf("%sKTPT: Order of start positions differ%s\n", + colout->COL_DIFF, colout->reset ); + if ( verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_MSPT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint diff_KMP_MSPT +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + section_info_t si = {KMP_MSPT,kmp1,kmp2,verbose,"MSPT","mission success"}; + return diff_pt2_si(&si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_POTI() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int diff_poti_group ( const kmp_poti_group_t *e1, const kmp_poti_group_t *e2 ) +{ + if (!memcmp(e1,e2,sizeof(*e1))) + return 0; + + return ( e1->n_point != e2->n_point ) + + ( e1->smooth != e2->smooth ) + + ( e1->back != e2->back ); +} + +//----------------------------------------------------------------------------- + +static int diff_poti_point ( const kmp_poti_point_t *e1, const kmp_poti_point_t *e2 ) +{ + if (!memcmp(e1,e2,sizeof(*e1))) + return 0; + + uint count = ( e1->speed != e2->speed ) + + ( e1->unknown != e2->unknown ); + + count += fabsf( e1->position[0] - e2->position[0] ) > epsilon_pos + || fabsf( e1->position[1] - e2->position[1] ) > epsilon_pos + || fabsf( e1->position[2] - e2->position[2] ) > epsilon_pos; + + return count; +} + +//----------------------------------------------------------------------------- + +static uint diff_KMP_POTI +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + uint diff_count = 0; + List_t *gl1 = kmp1->dlist + KMP_POTI; + List_t *pl1 = &kmp1->poti_point; + List_t *gl2 = kmp2->dlist + KMP_POTI; + List_t *pl2 = &kmp2->poti_point; + + + if ( gl1->used != gl2->used ) + { + if (!verbose) + return 1; + printf("POTI: Number of routes differ: Have %u and %u.\n", + gl1->used, gl2->used ); + if (verbose==1) + return 1; + diff_count++; + } + + if ( pl1->used != pl2->used ) + { + if (!verbose) + return 1; + printf("POTI: Number of total route points differ: Have %u and %u.\n", + pl1->used, pl2->used ); + if (verbose==1) + return 1; + diff_count++; + } + +// [[2do]] ??? qqq + + uint i; + const uint gn = gl1->used < gl2->used ? gl1->used : gl2->used; + kmp_poti_group_t *ge1 = (kmp_poti_group_t*)gl1->list; + kmp_poti_group_t *ge2 = (kmp_poti_group_t*)gl2->list; + + for ( i = 0; i < gn; i++, ge1++, ge2++ ) + { + if (diff_poti_group(ge1,ge2)) + { + if ( verbose < 2 ) + { + if (verbose) + fputs("POTI: Groups differ\n",stdout); + return 1; + } + + printf("POTI: Group #%u differ\n",i); + diff_count++; + } + } + + +// [[2do]] ??? qqq + + const uint pn = pl1->used < pl2->used ? pl1->used : pl2->used; + kmp_poti_point_t *pe1 = (kmp_poti_point_t*)pl1->list; + kmp_poti_point_t *pe2 = (kmp_poti_point_t*)pl2->list; + + for ( i = 0; i < pn; i++, pe1++, pe2++ ) + { + if (diff_poti_point(pe1,pe2)) + { + if ( verbose < 2 ) + { + if (verbose) + fputs("POTI: Route points differ\n",stdout); + return 1; + } + + printf("POTI: Route point #%u differ\n",i); + diff_count++; + } + } + + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// diff_KMP_STGI() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct sort_stgi_t +{ + int rank; // previous rank order + kmp_stgi_entry_t data; // original object data + +} sort_stgi_t; + +//----------------------------------------------------------------------------- + +static sort_stgi_t * sort_stgi ( const List_t *list ) +{ + DASSERT(list); + if (!list->used) + return 0; + + sort_stgi_t *res = CALLOC(list->used,sizeof(*res)); + sort_stgi_t *ptr = res; + kmp_stgi_entry_t *e = (kmp_stgi_entry_t*)list->list; + const uint n = list->used; + + uint i; + for ( i = 0; i < n; i++, e++, ptr++ ) + { + ptr->rank = i; + memcpy(&ptr->data,e,sizeof(ptr->data)); + } + + #if 0 + if ( n > 1 && KMP_DIFF_MODE & KDMD_SORT ) + qsort( res, n, sizeof(*res), (qsort_func)compare_sort_stgi ); + #endif + return res; +} + +//----------------------------------------------------------------------------- + +static inline void print_stgi + ( char mode, const kmp_stgi_entry_t *e1, const kmp_stgi_entry_t *e2 ) +{ + if ( e2 && colout->colorize ) + { + ccp ok = colout->COL_OK; + ccp warn = colout->COL_WARN; + + printf("%c %s%5u %s%4u %s%5u %s%5u %s%#10x %s0x%02x %s%#5x %s%#6x", + mode, + OK_WARN1(e1,e2,lap_count), + OK_WARN1(e1,e2,pole_pos), + OK_WARN1(e1,e2,narrow_start), + OK_WARN1(e1,e2,enable_lens_flare), + OK_WARN1(e1,e2,flare_color), + OK_WARN1(e1,e2,flare_alpha), + OK_WARN1(e1,e2,unknown_09), + OK_WARN1(e1,e2,speed_mod) ); + + if (e1->speed_mod) + printf(" (%5.3f)",SpeedMod2float(e1->speed_mod)); + printf("%s\n",colout->reset); + } + else + { + printf("%c %5u %4u %5u %5u %#10x 0x%02x %#5x %#6x", + mode, + e1->lap_count, e1->pole_pos, + e1->narrow_start, e1->enable_lens_flare, + e1->flare_color, + e1->flare_alpha, e1->unknown_09, + e1->speed_mod ); + if (e1->speed_mod) + printf(" (%5.3f)\n",SpeedMod2float(e1->speed_mod)); + else + putchar('\n'); + } +} + +//----------------------------------------------------------------------------- + +static sort_stgi_t * print_stgi_single + ( char mode, const sort_stgi_t *e, sort_stgi_t *end ) +{ + DASSERT(e); + DASSERT(end); + + while ( e < end ) + { + printf("%sSTGI: Only found in source %c @%u%s\n", + mode == '<' ? colout->COL_SRC1 : colout->COL_SRC2, + mode == '<' ? '1' : '2', e->rank, + colout->reset ); + print_stgi(mode,&e->data,0); + e++; + } + return (sort_stgi_t*)e; +} + +//----------------------------------------------------------------------------- + +static int diff_stgi ( const sort_stgi_t *e1, const sort_stgi_t *e2 ) +{ + if (!memcmp(&e1->data,&e2->data,sizeof(e1->data))) + return 0; + + return ( e1->data.lap_count != e2->data.lap_count ) + + ( e1->data.pole_pos != e2->data.pole_pos ) + + ( e1->data.narrow_start != e2->data.narrow_start ) + + ( e1->data.enable_lens_flare != e2->data.enable_lens_flare ) + + ( e1->data.flare_color != e2->data.flare_color ) + + ( e1->data.flare_alpha != e2->data.flare_alpha ) + + ( e1->data.unknown_09 != e2->data.unknown_09 ) + + ( e1->data.speed_mod != e2->data.speed_mod ); +} + +//----------------------------------------------------------------------------- + +static uint diff_KMP_STGI +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + + uint diff_count = 0; + List_t *l1 = kmp1->dlist + KMP_STGI; + List_t *l2 = kmp2->dlist + KMP_STGI; + + if ( l1->used != l2->used ) + { + if (!verbose) + return 1; + printf("%sSTGI: Number of stage info records differ: Have %u and %u.%s\n", + colout->COL_WARN, l1->used, l2->used, colout->reset ); + if (verbose==1) + return 1; + diff_count++; + } + + sort_stgi_t *s1 = sort_stgi(l1), *e1 = s1, *end1 = e1 + l1->used; + sort_stgi_t *s2 = sort_stgi(l2), *e2 = s2, *end2 = e2 + l2->used; + uint order_differ = 0; + + while ( e1 < end1 && e2 < end2 ) + { + const int stat = diff_stgi(e1,e2); + if (stat) + { + if ( verbose < 2 ) + { + if (verbose) + printf("%sSTGI: Data differ%s\n", + colout->COL_DIFF, colout->reset ); + return 1; + } + + const int trigger = 2; + if ( stat >= trigger ) + { + //--- try to sync + + int dist1 = 99999; + sort_stgi_t *p1; + for ( p1 = e1+1; p1 < end1; p1++ ) + if ( diff_stgi(p1,e2) < trigger ) + { + dist1 = p1 - e1; + break; + } + + int dist2 = 99999; + sort_stgi_t *p2; + for ( p2 = e2+1; p2 < end2; p2++ ) + if ( diff_stgi(p2,e1) < trigger ) + { + dist2 = p2 - e2; + break; + } + + PRINT0("DIST: %d %d\n",dist1,dist2); + + if ( dist1 < 99999 && dist1 < dist2 ) + { + e1 = print_stgi_single('<',e1,e1+1); + diff_count++; + continue; + } + else if ( dist2 < 99999 && dist2 < dist1 ) + { + e2 = print_stgi_single('>',e2,e2+1); + diff_count++; + continue; + } + } + + //--- print data + + printf("%sSTGI: Stage info records @%u:%u differ%s\n", + colout->COL_DIFF, e1->rank,e2->rank, + colout->reset ); + print_stgi('<',&e1->data,&e2->data); + print_stgi('>',&e2->data,&e1->data); + diff_count++; + } + + if ( e1->rank != e2->rank ) + order_differ++; + + e1++; + e2++; + } + + if ( e1 < end1 ) + { + print_stgi_single('<',e1,end1); + diff_count++; + } + + if ( e2 < end2 ) + { + print_stgi_single('>',e2,end2); + diff_count++; + } + + if (order_differ) + { + if (verbose) + printf("%sSTGI: Order of stage info records differ%s\n", + colout->COL_DIFF, colout->reset ); + if ( verbose < 2 ) + return 1; + diff_count++; + } + + FREE(s1); + FREE(s2); + return diff_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DiffKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DiffKMP +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +) +{ + DASSERT(kmp1); + DASSERT(kmp2); + PRINT("DiffKMP( %s, %s, verbose=%d )\n",kmp1->fname,kmp2->fname,verbose); + + //--- compare sections + + enumError diff_count = 0; + + if ( KMP_DIFF_MODE & KDMD_AREA && ( verbose || !diff_count )) + diff_count += diff_KMP_AREA(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_CAME && ( verbose || !diff_count )) + diff_count += diff_KMP_CAME(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_CKPH && ( verbose || !diff_count )) + diff_count += diff_KMP_CKPH(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_CKPT && ( verbose || !diff_count )) + diff_count += diff_KMP_CKPT(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_CNPT && ( verbose || !diff_count )) + diff_count += diff_KMP_CNPT(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_ENPH && ( verbose || !diff_count )) + diff_count += diff_KMP_ENPH(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_ENPT && ( verbose || !diff_count )) + diff_count += diff_KMP_ENPT(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_GOBJ && ( verbose || !diff_count )) + diff_count += diff_KMP_GOBJ(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_ITPH && ( verbose || !diff_count )) + diff_count += diff_KMP_ITPH(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_ITPT && ( verbose || !diff_count )) + diff_count += diff_KMP_ITPT(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_JGPT && ( verbose || !diff_count )) + diff_count += diff_KMP_JGPT(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_KTPT && ( verbose || !diff_count )) + diff_count += diff_KMP_KTPT(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_MSPT && ( verbose || !diff_count )) + diff_count += diff_KMP_MSPT(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_POTI && ( verbose || !diff_count )) + diff_count += diff_KMP_POTI(kmp1,kmp2,verbose); + + if ( KMP_DIFF_MODE & KDMD_STGI && ( verbose || !diff_count )) + diff_count += diff_KMP_STGI(kmp1,kmp2,verbose); + + return diff_count ? ERR_DIFFER : ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-kmp-draw.c b/project/src/lib-kmp-draw.c new file mode 100644 index 0000000..b967a9f --- /dev/null +++ b/project/src/lib-kmp-draw.c @@ -0,0 +1,2493 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kmp.h" +#include "lib-kcl.h" +#include "db-object.h" +#include "db-file.h" + +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP draw support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_draw_tab[] = +{ + { 0, "NONE", 0, DMD_M_RESET }, + { 0, "-ALL", 0, DMD_M_RESET }, + { DMD_M_ALL, "ALL", 0, DMD_M_RESET }, + { 0, "-POINTS", "-PTS", DMD_M_PTS }, + { DMD_M_PTS, "POINTS", "PTS", DMD_M_PTS }, + { 0, "-OBJECTS", "-GOBJ", DMD_M_GOBJ }, + { DMD_M_GOBJ, "OBJECTS", "GOBJ", DMD_M_GOBJ }, + { DMD_M_RESPAWN, "RESPAWN", 0, DMD_M_RESPAWN }, + { DMD_M_DEFAULT, "DEFAULT", 0, DMD_M_ALL }, + + { 0, "-OFFLINE", 0, DMD_M_OFFLINE }, + { DMD_M_OFFLINE, "OFFLINE", 0, DMD_M_OFFLINE }, + { 0, "-ONLINE", 0, DMD_M_ONLINE }, + { DMD_M_ONLINE, "ONLINE", 0, DMD_M_ONLINE }, + + { DMD_1OFFLINE, "1OFFLINE", 0, 0 }, + { DMD_2OFFLINE, "2OFFLINE", 0, 0 }, + { DMD_3OFFLINE, "3OFFLINE", "4OFFLINE", 0 }, + { DMD_1ONLINE, "1ONLINE", 0, 0 }, + { DMD_2ONLINE, "2ONLINE", 0, 0 }, + { DMD_3ONLINE, "3ONLINE", "4ONLINE", 0 }, + + { DMD_ENPT, "ENPT", 0, 0 }, + { DMD_ITPT, "ITPT", 0, 0 }, + { DMD_CKPT, "CKPT", 0, 0 }, + { DMD_CJGPT, "CJGPT", 0, 0 }, + { DMD_JGPT, "JGPT", 0, 0 }, + { DMD_KTPT, "KTPT", 0, 0 }, + { DMD_CNPT, "CNPT", 0, 0 }, + { DMD_POTI, "POTI", 0, 0 }, + { DMD_AREA, "AREA", 0, 0 }, + { DMD_ITEMBOX, "ITEMBOXES", 0, 0 }, + { DMD_COIN, "COINS", 0, 0 }, + { DMD_ROADOBJ, "ROADOBJECTS", 0, 0 }, + { DMD_SOLIDOBJ, "SOLIDOBJECTS", 0, 0 }, + { DMD_DECORATION, "DECORATION", 0, 0 }, + { DMD_BLACK, "BLACK", 0, 0 }, + { DMD_WHITE, "WHITE", 0, 0 }, + { DMD_KCL, "KCL", 0, 0 }, + + { DMD_DETAILED, "DETAILED", 0, 0 }, + { DMD_DISPATCH, "DISPATCH", 0, 0 }, + { DMD_WARNINGS, "WARNINGS", 0, 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +draw_mode_t KMP_DRAW = DMD_M_DEFAULT; + +int ScanOptDraw ( ccp arg ) +{ + if (!arg) + return 0; + + s64 stat = ScanKeywordList( arg, opt_draw_tab, 0, true, 0, + *arg == '-' ? DMD_M_ALL : (DMD_M_ALL&~DMD_M_RESET), + "Option --draw", ERR_SYNTAX ); + if ( stat != M1(stat) ) + { + KMP_DRAW = stat & DMD_M_ALL; + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetDrawMode() +{ + static char buf[200] = {0}; + if (!*buf) + PrintKeywordList( buf, sizeof(buf), 0, opt_draw_tab, + KMP_DRAW, DMD_M_DEFAULT, 0 ); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define OBJ_ROAD_COL 0x0000 + +/////////////////////////////////////////////////////////////////////////////// + +static void TransformByGOBJ +( + const + kmp_gobj_entry_t *obj, // object to draw + DbFlags_t flags, // flags DBF_SCALE and DBF_ROTATE + + double3 *pt, // pointer to first point to transform + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +) +{ + DASSERT(obj); + DASSERT(pt||!n_pt); + + double3 temp; + + if ( flags & DBF_SCALE ) + { + temp.x = obj->scale[0]; + temp.y = obj->scale[1]; + temp.z = obj->scale[2]; + Scale(0,&temp,pt,pt_delta,n_pt); + } + + if ( flags & DBF_ROTATE) + { + temp.x = obj->rotation[0]; + temp.y = obj->rotation[1]; + temp.z = obj->rotation[2]; + Rotate(0,&temp,pt,pt_delta,n_pt); + } + + temp.x = obj->position[0]; + temp.y = obj->position[1]; + temp.z = obj->position[2]; + Translate(&temp,pt,pt_delta,n_pt); +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rot_shape_t { int x; int y; } rot_shape_t; + +//----------------------------------------------------------------------------- + +static double3 * CreateRotatedShape +( + // returns an alloced list or NULL on error -> call FREE() + + const rot_shape_t *shape, // pointer to the shape + int n_shape, // number of elements of 'shape' + int n_segments, // number of segments + + const + kmp_gobj_entry_t *obj, // NULL or object for transformation + DbFlags_t flags // flags DBF_SCALE and DBF_ROTATE +) +{ + if ( n_shape < 1 || n_segments < 3 ) + return 0; + DASSERT(shape); + + const uint n_total = n_shape * n_segments; + double3 *res = MALLOC(n_total*sizeof(*res)); + + //--- create first segment + + uint i; + for ( i = 0; i < n_shape; i++ ) + { + res[i].x = shape[i].x; + res[i].y = shape[i].y; + res[i].z = 0.0; + } + + double3 rot; + rot.x = rot.y = rot.z = 0.0; + + //--- create additonal segments + + uint s; + for ( s = 1; s < n_segments; s++ ) + { + double3 *dest = res + s * n_shape; + memcpy( dest, res, sizeof(*dest) * n_shape ); + rot.y = 360.0 * s / n_segments; + Rotate(0,&rot,dest,sizeof(double3),n_shape); + } + + if (obj) + TransformByGOBJ(obj,flags,res,sizeof(double3),n_total); + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int DrawRotatedShape +( + struct kcl_t *dk, // pointer to valid KCL + + const rot_shape_t *shape, // pointer to the shape + int n_shape, // number of elements of 'shape' + int n_segments, // number of segments + + const + kmp_gobj_entry_t *obj, // NULL or object for transformation + DbFlags_t flags, // flags DBF_SCALE and DBF_ROTATE + u32 col // print color +) +{ + DASSERT(dk); + + double3 *pt = CreateRotatedShape(shape,n_shape,n_segments,obj,flags); + if (!pt) + return 0; + +// [[append-p]] + + double3 *s2 = pt + n_shape * (n_segments-1); + uint s; + for ( s = 0; s < n_segments; s++ ) + { + double3 *s1 = s2; + s2 = pt + n_shape * s; + + double3 *p1 = s1, *p2 = s2; + uint i; + for ( i = 0; i < n_shape-1; i++, p1++, p2++ ) + { + if (!shape[i+1].x) + { + if ( shape[i+1].y == -1 ) + i++, p1++, p2++; + else if (shape[i].x) + AppendTriangleKCLp ( dk,col, p1, p1+1, p2 ); + } + else if (!shape[i].x) + AppendTriangleKCLp ( dk,col, p1, p1+1, p2+1 ); + else + AppendQuadrilateralKCLp( dk,col, p1, p1+1, p2+1, p2 ); + } + } + + FREE(pt); + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddAreas2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddAreas2KCL +( + DrawKCL_t * dk // pointer to initalized object +) +{ + const double width = 150; + const double length = 1500; + + const uint n = dk->kmp->dlist[KMP_AREA].used; + const kmp_area_entry_t *area = (kmp_area_entry_t*)dk->kmp->dlist[KMP_AREA].list; + + uint i; + for ( i = 0; i < n; i++, area++ ) + { + double3 pos, rot, scale, base; + pos.x = area->position[0]; + pos.y = area->position[1]; + pos.z = area->position[2]; + rot.x = area->rotation[0]; + rot.y = area->rotation[1]; + rot.z = area->rotation[2]; + scale.x = area->scale[0]; + scale.y = area->scale[1]; + scale.z = area->scale[2]; + base.x = pos.x; + base.y = pos.y; + base.z = pos.z - length; + + Rotate(&pos,&rot,&base,sizeof(base),1); + + if (dk->detailed) + AppendPrismKCL( dk->draw, KCL_FLAG_AREA_ARROW, + &base, &pos, 0, width, 6, 0, + KCL_FLAG_AREA_ARROW, width, + KCL_FLAG_AREA_ARROW, 1.8, false ); + else + AppendPrismKCL( dk->draw, KCL_FLAG_AREA_ARROW, + &base, &pos, 0, width, 4, 1, + ~0, 0.0, ~0, 0.0, false ); + + if ( area->mode == 1 ) + { + AppendCylinderKCL( dk->draw, KCL_FLAG_AREA_BORDER, + &pos, &scale, 5000, &rot, 10, KCL_FLAG_AREA_BORDER ); + } + else + { + double3 size, shift; + size.x = size.y = size.z = 10000; + shift.x = shift.z = 0; + shift.y = 5000; + + cuboid_t cub; + CreateCuboid(&cub,&size,0,&shift); + ScaleCuboid(&cub,0,&scale); + RotateCuboid(&cub,0,&rot); + ShiftCuboid(&cub,&pos); + AppendCuboidKCL(dk->draw,KCL_FLAG_AREA_BORDER,&cub); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddCheckPoints2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void add_ckpt_joist +( + kcl_t *kcl, + const kmp_ckpt_entry_t *ckpt, + const kmp_ckpt_entry_t *c1, + uint next, + double min, + double max, + double width, + u32 kcl_flag_lt, + u32 kcl_flag_rt +) +{ + const kmp_ckpt_entry_t *c2 = ckpt + next; + + double3 d[3]; + d[0].y = d[1].y = max; + d[2].y = min; + + d[0].x = d[2].x = c1->left[0]; + d[0].z = d[2].z = c1->left[1]; + d[1].x = c2->left[0]; + d[1].z = c2->left[1]; + AppendJoistKCL(kcl,kcl_flag_lt,width,d,d+1,d+2,0,0); + + d[0].x = d[2].x = c1->right[0]; + d[0].z = d[2].z = c1->right[1]; + d[1].x = c2->right[0]; + d[1].z = c2->right[1]; + AppendJoistKCL(kcl,kcl_flag_rt,width,d,d+1,d+2,0,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddCheckPoints2KCL +( + DrawKCL_t * dk, // pointer to initalized object + bool draw_jgpt // true: draw connections to JGPT +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw || !dk->kmp ) + return; + + const double lc_width = 600.0; + const double std_width = 300.0; + const double tie_width = 200.0; + const double next_width = 150.0; + const double jgpt_width = 150.0; + + + //--- find min and max heigth + + float min = 1000000; + float max = 0; + + uint n = dk->kmp->dlist[KMP_ENPT].used; + const kmp_enpt_entry_t * pt = (kmp_enpt_entry_t*)dk->kmp->dlist[KMP_ENPT].list; + for ( ; n > 0; n--, pt++ ) + { + if ( min > pt->position[1] ) + min = pt->position[1]; + if ( max < pt->position[1] ) + max = pt->position[1]; + } + if ( min > max ) + min = max; + min = round( min - 1500.0 ); + max = round( max + 4000.0 ); + + + //--- create rectangles for the check points + + n = dk->kmp->dlist[KMP_CKPT].used; + const kmp_ckpt_entry_t *ckpt = (kmp_ckpt_entry_t*)dk->kmp->dlist[KMP_CKPT].list; + const kmp_ckpt_entry_t *c1 = ckpt; + uint i; + for ( i = 0; i < n; i++, c1++ ) + { + //--- predefine params for check point gate + + uint kcl_flag, n_mark = 0; + double width = std_width; + + if ( !c1->mode ) + { + kcl_flag = KCL_FLAG_CKPT_LC; // KCL_FLAG_CKPT_LC_TT is used by 'kcl_flag+2' + width = lc_width; + } + else if ( c1->mode != M1(c1->mode) ) + { + kcl_flag = KCL_FLAG_CKPT_MD; // KCL_FLAG_CKPT_MD_TT is used by 'kcl_flag+2' + n_mark = (c1->mode-1) % 50 + 1; + } + else + kcl_flag = KCL_FLAG_CKPT_ETC; // KCL_FLAG_CKPT_ETC_TT is used by 'kcl_flag+2' + + + //--- iterate all links and draw them + + int std_next = GetNextPointKMP(dk->kmp,KMP_CKPT,c1-ckpt,-1,false,-1,0,0,0,0); + int link; + for ( link = 0; link < KMP_MAX_PH_LINK; link++ ) + { + bool new_group; + int next = GetNextPointKMP(dk->kmp,KMP_CKPT,c1-ckpt,link,true,-1,0,0,&new_group,0); + if ( next >= 0 ) + { + if ( std_next == next ) + std_next = -1; + + u32 flag_lt = new_group ? KCL_FLAG_CKPT_TIE_GROUP_LT : KCL_FLAG_CKPT_TIE_LT; + u32 flag_rt = new_group ? KCL_FLAG_CKPT_TIE_GROUP_RT : KCL_FLAG_CKPT_TIE_RT; + + if (dk->warnings) + { + double min_dist; + const int stat = CheckConvexCKPT(dk->kmp,i,next,&min_dist); + if ( stat || min_dist < KMP_DIST_WARN ) + { + flag_lt = flag_rt = kcl_flag = KCL_FLAG_CKPT_WARNING; + // KCL_FLAG_CKPT_WARNING_TT is used by 'kcl_flag+2' + } + } + + add_ckpt_joist(dk->draw,ckpt,c1,next,min,max,tie_width,flag_lt,flag_rt); + } + } + + if ( std_next >= 0 ) + add_ckpt_joist( dk->draw, ckpt, c1, std_next, min, max, next_width, + KCL_FLAG_CKPT_TIE_NEXT_LT, KCL_FLAG_CKPT_TIE_NEXT_RT ); + + + //--- draw check point gate + + double3 d[4]; + d[0].x = d[1].x = c1->left[0]; + d[0].z = d[1].z = c1->left[1]; + d[2].x = d[3].x = c1->right[0]; + d[2].z = d[3].z = c1->right[1]; + d[0].y = d[3].y = max; + d[1].y = d[2].y = min; + + AppendJoistKCL(dk->draw,kcl_flag,width,d+0,d+1,d+3,0,0); + AppendJoistKCL(dk->draw,kcl_flag,width,d+1,d+2,d+0,0,0); + AppendJoistKCL(dk->draw,kcl_flag,width,d+2,d+3,d+1,0,0); + AppendJoistKCL(dk->draw,kcl_flag,width,d+3,d+0,d+2,n_mark,KCL_FLAG_CKPT_MD5); + AppendTrianglesKCLp(dk->draw,kcl_flag+2,d,sizeof(double3),4); + + + //--- draw respawn point links + + if ( draw_jgpt && c1->respawn < dk->kmp->dlist[KMP_JGPT].used ) + { + const kmp_jgpt_entry_t *jgpt + = (kmp_jgpt_entry_t*)dk->kmp->dlist[KMP_JGPT].list + c1->respawn; + + d[0].y = max; + d[2].y = min; + d[0].x = d[2].x = c1->left[0]; + d[0].z = d[2].z = c1->left[1]; + d[1].x = jgpt->position[0]; + d[1].y = jgpt->position[1]; + d[1].z = jgpt->position[2]; + AppendJoistKCL(dk->draw,KCL_FLAG_CKPT_JGPT,jgpt_width,d,d+1,d+2,0,0); + + d[0].x = d[2].x = c1->right[0]; + d[0].z = d[2].z = c1->right[1]; + AppendJoistKCL(dk->draw,KCL_FLAG_CKPT_JGPT,jgpt_width,d,d+1,d+2,0,0); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddArrow2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddArrow2KCL +( + DrawKCL_t * dk, // pointer to initalized object + uint sect, // KMP section + uint arrow_color, // color of arrow + uint area_color, // color of area behind arrow + double area_len, // >0.0: width of area behind arrow + double area_wd // >0.0: length of area behind arrow +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw || !dk->kmp ) + return; + + const bool have_area = area_len > 0.0 && area_wd > 0.0; + const double length = have_area ? area_len : 1500; + //const double width = length/2 < 200 ? length/2 : 200; + const double width = 150; + area_wd /= 2; + + const uint n = dk->kmp->dlist[sect].used; + const kmp_jgpt_entry_t * pt = (kmp_jgpt_entry_t*)dk->kmp->dlist[sect].list; + +// [[append-p]] + + uint i; + for ( i = 0; i < n; i++, pt++ ) + { + double3 pos, rot, base; + pos.x = pt->position[0]; + pos.y = pt->position[1]; + pos.z = pt->position[2]; + rot.x = pt->rotation[0]; + rot.y = pt->rotation[1]; + rot.z = pt->rotation[2]; + base.x = pos.x; + base.y = pos.y; + base.z = pos.z - length; + + Rotate(&pos,&rot,&base,sizeof(base),1); + + if (dk->detailed) + AppendPrismKCL( dk->draw, arrow_color, + &base, &pos, 0, width, 6, 0, + arrow_color, width, + arrow_color, 1.8, false ); + else + AppendPrismKCL( dk->draw, arrow_color, + &base, &pos, 0, width, 4, 1, + ~0, 0.0, ~0, 0.0, false ); + + if ( area_len > 0.0 && area_wd > 0.0 ) + { + double3 p[4]; + p[0].x = p[1].x = pos.x + area_wd; + p[2].x = p[3].x = pos.x - area_wd; + p[0].y = p[1].y = p[2].y = p[3].y = pos.y + 20; + p[0].z = p[3].z = pos.z; + p[1].z = p[2].z = pos.z - area_len; + + Rotate(&pos,&rot,p,sizeof(*p),4); + AppendQuadrilateralKCLp( dk->draw, area_color, p, p+1, p+2, p+3 ); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddPotiRoute2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void AddPotiRoute2KCL +( + struct kcl_t *kcl, // pointer to valid KCL + const u32 color, // color + double width, // width of pyramid + bool detailed, // print end points detailed + const kmp_poti_group_t *pg, // pointer to group + const kmp_poti_point_t *pp, // pointer to first point + int limit // max num of points to draw + +) +{ + uint en = pg->n_point; + if ( en > (uint)limit ) + en = limit; + if ( !en || limit <= 0 ) + return; + + const kmp_poti_point_t *pp0 = pp; + + double3 pt2; + pt2.x = pp->position[0]; + pt2.y = pp->position[1]; + pt2.z = pp->position[2]; + pp++; + + if ( en == 1 ) + { + double3 pt[4]; + pt[0] = pt2; pt[0].z += width; + pt[1] = pt2; pt[1].x += width; + pt[2] = pt2; pt[2].z -= width; + pt[3] = pt2; pt[3].x -= width; + + double3 apex; + apex = pt2; apex.y += width; + AppendPyramidKCL(kcl,color,&apex,pt,sizeof(*pt),4,false); + apex.y = pt2.y - width; + AppendPyramidKCL(kcl,color,&apex,pt,sizeof(*pt),4,false); + + return; + } + + uint ei; + for ( ei = 1; ei < en; ei++ ) + { + double3 pt1 = pt2; + pt2.x = pp->position[0]; + pt2.y = pp->position[1]; + pt2.z = pp->position[2]; + pp++; + + double3 helper = pt1; + helper.y += 1000; + + if (detailed) + AppendPrismKCL( kcl, color, &pt1, &pt2, &helper, + width, 6, 0, color, width, ~0, 0.0, false ); + else + AppendPrismKCL( kcl, color, &pt1, &pt2, &helper, + width, 4, 1, ~0, 0.0, ~0, 0.0, false ); + } + + if ( !pg->back && en > 2 ) // is cyclic + { + + double3 pt0; + pt0.x = pp0->position[0]; + pt0.y = pp0->position[1]; + pt0.z = pp0->position[2]; + + double3 helper = pt0; + helper.y += 1000; + + if (detailed) + AppendPrismKCL( kcl, color, &pt2, &pt0, &helper, + width, 6, 0, color, width, ~0, 0.0, false ); + else + AppendPrismKCL( kcl, color, &pt2, &pt0, &helper, + width, 4, 1, ~0, 0.0, ~0, 0.0, false ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddRoutes2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddRoutes2KCL +( + DrawKCL_t * dk, // pointer to initalized object + uint sect_pt, // KMP section + uint color_std // standart color index +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw || !dk->kmp ) + return; + DASSERT( sect_pt == KMP_ENPT || sect_pt == KMP_ITPT ); + + //--- setup + + const uint color_single = color_std + ( KCL_FLAG_ENPT_SINGLE - KCL_FLAG_ENPT ); + const uint color_tie_bi = color_std + ( KCL_FLAG_ENPT_TIE_BI - KCL_FLAG_ENPT ); + const uint color_tie_prev = color_std + ( KCL_FLAG_ENPT_TIE_PREV - KCL_FLAG_ENPT ); + const uint color_tie_next = color_std + ( KCL_FLAG_ENPT_TIE_NEXT - KCL_FLAG_ENPT ); + const uint color_dispatch = color_std + ( KCL_FLAG_ENPT_DISPATCH - KCL_FLAG_ENPT ); + + double3 max_pt; + max_pt.x = max_pt.y = max_pt.z = 0; + uint col1_used = 0, col2_used = 0; + + double3 pos_beg[KMP_MAX_LINFO], pos_end[KMP_MAX_LINFO]; + memset(pos_beg,0,sizeof(pos_beg)); + memset(pos_end,0,sizeof(pos_end)); + + const double width_std = 200.0; + const double width_tie2 = 120.0; + const double width_next = 70.0; + const double width_prev = 40.0; + const double width_oct = 1.8 * width_std; + const double dist_oct = 1.9 * width_std; + + kmp_linfo_t *li = SetupRouteLinksKMP(dk->kmp,sect_pt,false); + + const uint n = dk->kmp->dlist[li->sect_pt].used; + const kmp_enpt_entry_t *pt = (kmp_enpt_entry_t*)dk->kmp->dlist[li->sect_pt].list; + + + //--- draw dispatch points and sub routes, but no links + + uint i; + double3 pt0; + bool pt0_valid = false; + + for ( i = 0; i < n; i++ ) + { + //--- calculate position of point + + double3 pt1, helper; + pt1.x = helper.x = pt[i].position[0]; + pt1.y = pt[i].position[1]; + pt1.z = helper.y = pt[i].position[2]; + helper.y = pt1.y + 10000; + + if (i) + { + if ( max_pt.x < pt1.x ) max_pt.x = pt1.x; + if ( max_pt.y < pt1.y ) max_pt.y = pt1.y; + if ( max_pt.z < pt1.z ) max_pt.z = pt1.z; + } + else + max_pt = pt1; + + + //--- store postions of first and last point of a route + + int ph_index; + const kmp_enph_entry_t *ph = FindPH(dk->kmp,li->sect_ph,i,&ph_index); + if (!ph) + { + pt0_valid = false; + continue; + } + + DASSERT( ph_index >= 0 ); + if ( i == ph->pt_start ) + pos_beg[ph_index] = pt1; + + const bool is_last_pt = i == ph->pt_start + ph->pt_len - 1; + if ( is_last_pt ) + pos_end[ph_index] = pt1; + + + //-- draw dispatch point + + if ( li->gmode[i] & LINFOG_DISPATCH || li->n_point[i] == 1 ) + { + octahedron_t oct; + CreateRegularOctahedron(&oct,width_oct,0,&pt1); + + if ( !( li->gmode[i] & LINFOG_DISPATCH )) + AppendOctahedronKCL(dk->draw,color_single,&oct); + else if ( KMP_DRAW & DMD_DISPATCH ) + { + double3 shift; + shift.x = -dist_oct/2; + shift.y = width_tie2; + shift.z = 0.0; + ShiftOctahedron(&oct,&shift); + uint color = ph->setting[0] & 7; + col1_used |= 1 << color; + AppendOctahedronKCL(dk->draw,KCL_FLAG_TEST0+3*color,&oct); + + shift.x = dist_oct; + shift.y = 0.0; + ShiftOctahedron(&oct,&shift); + color = ph->setting[1] >> 5 & 7; + col2_used |= 1 << color; + AppendOctahedronKCL(dk->draw,KCL_FLAG_TEST0+3*color,&oct); + } + else + AppendOctahedronKCL(dk->draw,color_dispatch,&oct); + pt0_valid = false; + } + else + { + if ( pt0_valid ) + { + if (dk->detailed) + AppendPrismKCL( dk->draw, color_std, &pt0, &pt1, &helper, + width_std, 6, 0, color_std, + width_std, ~0, 0.0, false ); + else + AppendPrismKCL( dk->draw, color_std, &pt0, &pt1, &helper, + width_std, 4, 1, ~0, + 0.0, ~0, 0.0, false ); + } + pt0_valid = !is_last_pt; + pt0 = pt1; + } + } + + + //--- draw dispatch points summary + + if ( KMP_DRAW & DMD_DISPATCH && max_pt.y > 0.0 ) + { + max_pt.x += 1500; + max_pt.z -= 7*2.2*width_oct; + + double3 shift; + shift.x = dist_oct; + shift.y = shift.z = 0.0; + + uint i; + for ( i = 0; i < 8; i++ ) + { + octahedron_t oct1, oct2; + CreateRegularOctahedron(&oct1,width_oct,0,&max_pt); + CreateRegularOctahedron(&oct2,0.4*width_oct,0,&max_pt); + + const uint color = KCL_FLAG_TEST0 + 3*i; + if ( col1_used & 1<draw,color,&oct1); + else + AppendOctahedronKCL(dk->draw,color,&oct2); + + if (!(i&1)) + { + if ( col2_used & 1<draw,color,&oct1); + } + else + { + ShiftOctahedron(&oct2,&shift); + AppendOctahedronKCL(dk->draw,color,&oct2); + } + } + max_pt.z += 2.2*width_oct; + } + } + + + //--- draw group links in 2 passes (0,1): bidirect first, others second + + li = SetupRouteLinksKMP(dk->kmp,li->sect_ph,false); + + int pass; + for ( pass = 0; pass < 2; pass++ ) + { + for ( i = 0; i < li->used; i++ ) + { + const kmp_linfo_mode *lm = li->list[i]; + uint j0; + for ( j0 = 0; j0 < li->used; j0++ ) + { + kmp_linfo_mode mode = *lm++; + if (!mode) + continue; // fast skip + + uint j = j0; + uint color = 0; + double width = 0.0; + double3 *pt1 = pos_end+i; + double3 *pt2 = pos_beg+j; + u32 arrow_col = ~0; + bool arrow_flip = false; + + if ( mode & LINFO_G_NEXT && li->gmode[i] & LINFOG_DISPATCH ) + { + double3 *pt3 = pos_end+j; + if ( Length2D(pt1->v,pt3->v) < Length2D(pt1->v,pt2->v) ) + { + pt2 = pt3; + if ( mode & LINFO_G_BY_NEXT ) + mode |= LINFO_G_BY_PREV; + } + } + + if ( mode & LINFO_G_NEXT ) + { + if ( mode & LINFO_G_BY_PREV || + mode & LINFO_G_BY_NEXT && li->gmode[i] & LINFOG_DISPATCH ) + { + li->list[i][j] = li->list[j][i] = 0; // reset connection info + color = color_tie_bi; + width = width_tie2; + } + else if (pass) + { + color = arrow_col = color_tie_next; + width = width_next; + } + } + else if ( pass && mode & LINFO_G_BY_PREV ) + { + color = arrow_col = color_tie_prev; + width = width_prev; + double3 *temp = pt1; + pt1 = pt2; + pt2 = temp; + } + + if (color) + { + double3 helper; + helper.x = pt1->x; + helper.y = pt1->y + 10000; + helper.z = pt1->z; + + double arrow_size = 0.0; + if ( arrow_col != M1(arrow_col) ) + { + arrow_size = 1.5; + } + + if (dk->detailed) + AppendPrismKCL( dk->draw, color, pt1, pt2, &helper, + width, 6, 0, color, width, + arrow_col, arrow_size, arrow_flip ); + else + AppendPrismKCL( dk->draw, color, pt1, pt2, &helper, + width, 4, 1, ~0, 0.0, + arrow_col, arrow_size, arrow_flip ); + } + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddPotiRoutes2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddPotiRoutes2KCL +( + DrawKCL_t * dk // pointer to initalized object +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw || !dk->kmp ) + return; + + const double width = 200.0; + + const uint ng = dk->kmp->dlist[KMP_POTI].used; + const kmp_poti_group_t * pg = (kmp_poti_group_t*)dk->kmp->dlist[KMP_POTI].list; + const uint np = dk->kmp->poti_point.used; + const kmp_poti_point_t * pp = (kmp_poti_point_t*)dk->kmp->poti_point.list; + + uint gi, i; + for ( gi = i = 0; gi < ng && i < np; gi++, pg++ ) + { + u32 color = KCL_FLAG_POTI0 + gi%4; + if ( gi < MAX_ROUTE ) + switch (dk->route[gi]) + { + case DRTM_OFF: + color = 0; + break; + + case DRTM_POTI: + //case DRTM_CAME: + // use default + break; + + case DRTM_GOBJ: + color = KCL_FLAG_SOLID_ROUTE; + break; + + case DRTM_ITEMBOX: + color = KCL_FLAG_ITEMROUTE; + break; + + case DRTM_ITEMBOX_ENEMY: + color = KCL_FLAG_ITEMROUTE_ENEMY; + break; + + case DRTM_ITEMBOX_PLAYER: + color = KCL_FLAG_ITEMROUTE_PLAYER; + break; + } + + if (color) + AddPotiRoute2KCL( dk->draw, color, width, dk->detailed, pg, pp, np-i ); + pp += pg->n_point; + i += pg->n_point; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddCannons2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const double cannon_aura = 50.0; + +//----------------------------------------------------------------------------- + +static uint td_minmax +( + const kcl_tridata_t *td, // valid tridata + double rot_rad, // rotation in radiant + double *res_min, // store min data here + double *res_max, // store max data here + double3 *res_sum // add points here +) +{ + DASSERT(td); + DASSERT(res_min); + DASSERT(res_max); + DASSERT(res_sum); + + const double3 *pt = td->pt; + double rad = atan2(pt->x,pt->z) + rot_rad; + double min, max = sin(rad) * sqrt( pt->x * pt->x + pt->z * pt->z ); + + pt++; + rad = atan2(pt->x,pt->z) + rot_rad; + double temp = sin(rad) * sqrt( pt->x * pt->x + pt->z * pt->z ); + if ( temp <= max ) + min = temp; + else + { + min = max; + max = temp; + } + + pt++; + rad = atan2(pt->x,pt->z) + rot_rad; + temp = sin(rad) * sqrt( pt->x * pt->x + pt->z * pt->z ); + *res_min = ( temp < min ? temp : min ) - cannon_aura; + *res_max = ( temp > min ? temp : max ) + cannon_aura; + + res_sum->x += td->pt[0].x + td->pt[1].x + td->pt[2].x; + res_sum->y += td->pt[0].y + td->pt[1].y + td->pt[2].y; + res_sum->z += td->pt[0].z + td->pt[1].z + td->pt[2].z; + return 3; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddCannons2KCL +( + DrawKCL_t * dk // pointer to initalized object +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw || !dk->kmp || !dk->kcl ) + return; + + const uint ctype = 0x11; + const double cube_size = 500.0; + //const double way_size = 1500.0; + + //--- set td markers for unresolved cannon triangles + //--- and find first and last cannon (for optimization) + + const uint n_tri = dk->kcl->tridata.used; + uint i, first_can = n_tri+1, last_can = 0; + kcl_tridata_t *td = (kcl_tridata_t*)dk->kcl->tridata.list; + for ( i = 0; i < n_tri; i++, td++ ) + { + td->status &= ~TD_MARK; + if (IS_KCL_TYPE(td->cur_flag,ctype)) + { + td->status |= TD_MARK; + if ( first_can > i ) + first_can = i; + last_can = i; + } + } + PRINT("CANNONS: %u .. %u / %u\n",first_can,last_can,n_tri); + if ( first_can > last_can ) + return; + const uint n_can = last_can + 1 - first_can; + + + //--- main loop + + const uint nc = dk->kmp->dlist[KMP_CNPT].used; + const kmp_cnpt_entry_t * cnpt = (kmp_cnpt_entry_t*)dk->kmp->dlist[KMP_CNPT].list; + uint ic = 0; + while ( ic < nc ) + { + //--- find first cannon + + const uint ftype = ctype | ic << 5; + const double rot_rad = -cnpt->rotation[1] * (M_PI/180.0); + + bool retry = false; + kcl_tridata_t *td = (kcl_tridata_t*)dk->kcl->tridata.list + first_can; + kcl_tridata_t *td_end = td + n_can; + for ( ; td < td_end; td++ ) + if ( td->status & TD_MARK && td->cur_flag == ftype ) + { + double min, max; + double3 sum; + sum.x = sum.y = sum.z = 0.0; + uint n = td_minmax(td,rot_rad,&min,&max,&sum); + td->status &= ~TD_MARK; + for ( ; td < td_end; td++ ) + if ( td->status & TD_MARK && td->cur_flag == ftype ) + { + double min2, max2; + n += td_minmax(td,rot_rad,&min2,&max2,&sum); + if ( min2 <= max && max2 >= min ) + { + if ( min > min2 ) + min = min2; + if ( max < max2 ) + max = max2; + td->status &= ~TD_MARK; + } + } + min += cannon_aura; + max -= cannon_aura; + + PRINT("CANNON #%u found: %3.1f..%3.1f\n", ic, min, max ); + + double3 size; + size.x = max - min; + size.y = size.z = cube_size; + + double3 shift; + shift.x = ( min + max ) /2; + shift.y = cnpt->position[1]; + shift.z = cos( atan2(cnpt->position[0],cnpt->position[2]) + rot_rad ) + * sqrt( cnpt->position[0]*cnpt->position[0] + + cnpt->position[2]*cnpt->position[2] ); + + cuboid_t cub; + CreateCuboid(&cub,&size,0,&shift); + + double3 rotate; + rotate.x = rotate.z = 0.0; + rotate.y = cnpt->rotation[1]; + RotateCuboid(&cub,0,&rotate); + + double f = cnpt->rotation[0]; + if ( f > 0.0 && f <= 100.0 ) + { + shift.x = ( cub.p000.x + cub.p111.x ) / 2; + shift.y = ( cub.p000.y + cub.p111.y ) / 2; + shift.z = ( cub.p000.z + cub.p111.z ) / 2; + f /= 100.0; + shift.x = ( cnpt->position[0] - shift.x ) * f; + shift.y = ( cnpt->position[1] - shift.y ) * f; + shift.z = ( cnpt->position[2] - shift.z ) * f; + ShiftCuboid(&cub,&shift); + } + + AppendCuboidKCL(dk->draw,KCL_FLAG_CNPT_LANDING,&cub); + + double3 dest, helper; + dest.x = helper.x = ( cub.p000.x + cub.p011.x ) / 2; + dest.y = ( cub.p000.y + cub.p011.y ) / 2; + dest.z = helper.z = ( cub.p000.z + cub.p011.z ) / 2; + helper.y = dest.y + 10000; + sum.x /= n; + sum.y /= n; + sum.z /= n; + + if (dk->detailed) + AppendPrismKCL( dk->draw, KCL_FLAG_CNPT_WAY, + &sum, &dest, &helper, cube_size/4, 6, 0, + KCL_FLAG_CNPT_WAY, cube_size, + KCL_FLAG_CNPT_WAY, 2.0, false ); + else + AppendPrismKCL( dk->draw, KCL_FLAG_CNPT_WAY, + &sum, &dest, &helper, cube_size/4, 4, 1, + ~0, 0.0, ~0, 0.0, false ); + + #ifdef TEST0 + size.y = cube_size/3; + size.z = way_size; + shift.z -= (cube_size+way_size)/2; + CreateCuboid(&cub,&size,0,&shift); + RotateCuboid(&cub,0,&rotate); + AppendCuboidKCL(dk->draw,KCL_FLAG_CNPT_WAY,&cub); + #endif + + #ifdef TEST0 + CreateCube(&cub,10000,0,0); + AppendCuboidKCL(dk->draw,KCL_FLAG_BLUE,&cub); + #endif + + retry = true; + break; + } + + if (!retry) + { + ic++; + cnpt++; + continue; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddObject helper /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void SetupMatrixD +( + MatrixD_t *mat, // will be initialized + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *raise // NULL or value to raise +) +{ + DASSERT(mat); + DASSERT(op); + + InitializeMatrixD(mat); + + if ( raise && *raise ) + { + double3 shift; + shift.x = shift.z = 0.0; + shift.y = *raise; + SetShiftMatrixD(mat,&shift); + } + + if ( flags & DBF_SCALE ) + { + double3 scale; + scale.x = op->scale[0]; + scale.y = op->scale[1]; + scale.z = op->scale[2]; + SetScaleMatrixD(mat,&scale,0); + } + + if ( flags & DBF_ROTATE ) + { + SetRotateMatrixD(mat,0,op->rotation[0],0.0,0); + SetRotateMatrixD(mat,1,op->rotation[1],0.0,0); + SetRotateMatrixD(mat,2,op->rotation[2],0.0,0); + } + + double3 trans; + trans.x = op->position[0]; + trans.y = op->position[1]; + trans.z = op->position[2]; + SetTranslateMatrixD(mat,&trans); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddObject: Add_*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef uint (*add_func) +( + struct kcl_t *dk, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +); + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_RECTANGLE +( + struct kcl_t *dk, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(dk); + DASSERT(op); + DASSERT(data); + + // ==> color x-len z-len x-rot y-rot z-rot + + if ( data_size < 6 ) + return 0; + + u32 col = *data++ + N_KCL_FLAG; + if (as_road) + col = OBJ_ROAD_COL; + + double xlen = *data++; + double zlen = *data++; + double3 rot; + rot.x = *data++; + rot.y = *data++; + rot.z = *data++; + + double3 pt[4]; + pt[0].x = pt[1].x = -xlen; + pt[2].x = pt[3].x = +xlen; + pt[0].y = pt[1].y = pt[2].y = pt[3].y = 25.0; // make it a little bit higher + pt[0].z = pt[3].z = -zlen; + pt[1].z = pt[2].z = +zlen; + + Rotate(0,&rot,pt,sizeof(double3),4); + TransformByGOBJ(op,flags,pt,sizeof(double3),4); + AppendQuadrilateralKCLp(dk,col,pt,pt+1,pt+2,pt+3); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_CUBE +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + DASSERT(data); + + // ==> color width [raise] + + if ( data_size < 2 ) + return 0; + + u32 col = *data++ + N_KCL_FLAG; + double wd = *data++; + double raise = data_size >= 3 ? *data : 0.0; + + float3 pos; + pos.x = op->position[0]; + pos.y = op->position[1] + raise; + pos.z = op->position[2]; + + float3 size; + if ( flags & DBF_SCALE ) + { + size.x = wd * op->scale[0]; + size.y = wd * op->scale[1]; + size.z = wd * op->scale[2]; + } + else + size.x = size.y = size.z = wd; + + cuboid_t cub; + CreateCuboidF( &cub, size.v, flags & DBF_ROTATE ? op->rotation : 0, pos.v ); + AppendCuboidKCL(draw,col,&cub); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_CUBOID +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + DASSERT(data); + + // ==> color width [raise] + + if ( data_size < 4 ) + return 0; + + u32 col = *data++ + N_KCL_FLAG; + float3 size; + size.x = *data++; + size.y = *data++; + size.z = *data++; + + float3 shift; + if ( data_size >= 7 ) + { + shift.x = *data++; + shift.y = *data++; + shift.z = *data++; + } + else + shift.x = shift.y = shift.z = 0.0; + + if ( flags & DBF_SCALE ) + { + size.x *= op->scale[0]; + size.y *= op->scale[1]; + size.z *= op->scale[2]; + + shift.x *= op->scale[0]; + shift.y *= op->scale[1]; + shift.z *= op->scale[2]; + } + + float3 pos; + pos.x = op->position[0] + shift.x; + pos.y = op->position[1] + shift.y; + pos.z = op->position[2] + shift.z; + + cuboid_t cub; + CreateCuboidF( &cub, size.v, flags & DBF_ROTATE ? op->rotation : 0, pos.v ); + AppendCuboidKCL(draw,col,&cub); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_CYLINDER_Y +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + DASSERT(data); + + // ==> color length radius [raise] + + if ( data_size < 3 ) + return 0; + + u32 col = *data++ + N_KCL_FLAG; + double len = *data++; + double xr = *data++; + double zr = xr; + double raise = data_size >= 4 ? *data : 0.0; + len += raise; + + if ( flags & DBF_SCALE ) + { + xr *= op->scale[0]; + len *= op->scale[1]; + raise *= op->scale[1]; + zr *= op->scale[2]; + } + + const uint max_sides = 8; + double3 pt[2*max_sides+4]; + + const uint n_sides = detailed ? max_sides : 6; + const uint n_used = 2*n_sides+4; + double3 *tab0 = pt+2, *tab1 = tab0 + n_sides + 1; + + pt[0].x = pt[0].z = pt[1].x = pt[1].z = 0.0; + pt[0].y = raise; + pt[1].y = len; + + double3 *p0 = tab0, *p1 = tab1; + const double add_rad = M_PI / n_sides; + double rad = 0.0; + + uint i; + for ( i = 0; i < n_sides; i++ ) + { + p0->x = cos(rad) * xr; + p0->y = raise; + p0->z = sin(rad) * zr; + p0++; + rad += add_rad; + + p1->x = cos(rad) * xr; + p1->y = len; + p1->z = sin(rad) * zr; + p1++; + rad += add_rad; + } + *p0 = *tab0; + *p1 = *tab1; + + if ( flags & DBF_ROTATE ) + { + double3 rot; + rot.x = op->rotation[0]; + rot.y = op->rotation[1]; + rot.z = op->rotation[2]; + Rotate(0,&rot,pt,sizeof(*pt),n_used); + } + + double3 pos; + pos.x = op->position[0]; + pos.y = op->position[1]; + pos.z = op->position[2]; + Translate(&pos,pt,sizeof(*pt),n_used); + + for ( i = 0, p0 = tab0, p1 = tab1; i < n_sides; i++, p0++, p1++ ) + { + AppendTriangleKCLp( draw, col, p0+1, p0, p1 ); + AppendTriangleKCLp( draw, col, p0+1, p1, p1+1 ); + } + + AppendTrianglesKCLp(draw,col,tab0,sizeof(*tab0),n_sides); + AppendTrianglesKCLp(draw,col,tab1+n_sides-1,-(int)sizeof(*tab1),n_sides); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_CYLINDER_Z +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + DASSERT(data); + + // ==> color length radius [raise] + + if ( data_size < 3 ) + return 0; + + u32 col = *data++ + N_KCL_FLAG; + double len = *data++; + double xr = *data++; + double yr = xr; + double raise = data_size >= 4 ? *data : 0.0; + len += raise; + + if ( flags & DBF_SCALE ) + { + xr *= op->scale[0]; + yr *= op->scale[1]; + len *= op->scale[2]; + raise *= op->scale[2]; + } + + const uint max_sides = 8; + double3 pt[2*max_sides+4]; + + const uint n_sides = detailed ? max_sides : 6; + const uint n_used = 2*n_sides+4; + double3 *tab0 = pt+2, *tab1 = tab0 + n_sides + 1; + + pt[0].x = pt[0].y = pt[1].x = pt[1].y = 0.0; + pt[0].z = raise; + pt[1].z = len; + + double3 *p0 = tab0, *p1 = tab1; + const double add_rad = M_PI / n_sides; + double rad = 0.0; + + uint i; + for ( i = 0; i < n_sides; i++ ) + { + p0->x = sin(rad) * xr; + p0->y = cos(rad) * yr + yr; + p0->z = raise; + p0++; + rad += add_rad; + + p1->x = sin(rad) * xr; + p1->y = cos(rad) * yr + yr; + p1->z = len; + p1++; + rad += add_rad; + } + *p0 = *tab0; + *p1 = *tab1; + + if ( flags & DBF_ROTATE ) + { + double3 rot; + rot.x = op->rotation[0]; + rot.y = op->rotation[1]; + rot.z = op->rotation[2]; + Rotate(0,&rot,pt,sizeof(*pt),n_used); + } + + double3 pos; + pos.x = op->position[0]; + pos.y = op->position[1]; + pos.z = op->position[2]; + Translate(&pos,pt,sizeof(*pt),n_used); + + for ( i = 0, p0 = tab0, p1 = tab1; i < n_sides; i++, p0++, p1++ ) + { + AppendTriangleKCLp( draw, col, p0+1, p0, p1 ); + AppendTriangleKCLp( draw, col, p0+1, p1, p1+1 ); + } + + AppendTrianglesKCLp(draw,col,tab0,sizeof(*tab0),n_sides); + AppendTrianglesKCLp(draw,col,tab1+n_sides-1,-(int)sizeof(*tab1),n_sides); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_ITEMBOX +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + + float3 pos; + pos.x = op->position[0]; + pos.y = op->position[1] + 140.0; + pos.z = op->position[2]; + + float3 rot; + rot.x = 45.000; + rot.y = 0.000; + rot.z = 35.264; + + cuboid_t cub; + CreateCubeF(&cub,240.0,rot.v,pos.v); + + const u32 col = op->setting[1] + ? KCL_FLAG_ITEMBOX_PLAYER + : op->setting[2] + ? KCL_FLAG_ITEMBOX_ENEMY + : KCL_FLAG_ITEMBOX; + AppendCuboidKCL(draw,col,&cub); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_COIN +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + + u32 col; + double size; + if ( op->setting[1] == M1(op->setting[1]) ) + { + col = KCL_FLAG_COIN_RESPAWN; + size = 150; + } + else if ( op->setting[1] < KMP_COIN_IS_INVALID ) + { + col = KCL_FLAG_COIN_START; + size = 220; + } + else + { + col = KCL_FLAG_COIN_INVALID; + size = 220; + } + + float3 pos; + pos.x = op->position[0]; + pos.y = op->position[1] + 150.0; + pos.z = op->position[2]; + + cuboid_t cub; + CreateCubeF(&cub,size,0,pos.v); + AppendCuboidKCL(draw,col,&cub); + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_KINOKO +( + struct kcl_t *dk, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(dk); + DASSERT(op); + + static const rot_shape_t shape[] = + { + { 0, 545 }, + { 760, 530 }, + { 1400, 420 }, + { 2050, 420 }, + { 2050, -50 }, + #ifdef TEST0 + { 100, -50 }, + { 100,-1000 }, + { 0,-1000 }, + { 0, -1 }, + { 2500, 500 }, + { 3000, 500 }, + { 3000, 0 }, + { 2500, 0 }, + #endif + }; + + const uint n_shp = sizeof(shape)/sizeof(*shape); + const uint n_seg = detailed ? 10 : 6; + const u32 col = as_road + ? OBJ_ROAD_COL + : op->obj_id == 0x1fa + ? KCL_FLAG_KINOKO_GREEN + : KCL_FLAG_KINOKO_RED; + + return DrawRotatedShape(dk,shape,n_shp,n_seg,op,flags,col); +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_PYRAMID +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + DASSERT(data); + + // => color N height radius [raise] + + if ( data_size < 4 ) + return 0; + + u32 col = *data++ + N_KCL_FLAG; + uint n_side = *data++; + double ht = *data++; + double r = *data++; + const s16 *raise = data_size >= 4 ? data : 0; + + const uint max_side = 10; + if ( n_side > max_side ) + n_side = max_side; + + double3 pt[max_side+1]; // pt[0] is apex + pt[0].x = pt[0].z = 0.0; + pt[0].y = ht; + + const double rad_f = 2*M_PI / n_side; + double3 *p = pt + 1; + uint i; + for ( i = 0; i < n_side; i++, p++ ) + { + const double rad = i * rad_f; + p->x = cos(rad) * r; + p->y = 0.0; + p->z = sin(rad) * r; + } + + MatrixD_t mat; + SetupMatrixD(&mat,op,flags,raise); + TransformD3NMatrixD(&mat,pt,n_side+1,sizeof(*pt)); + + AppendPyramidKCL(draw,col,pt,pt+1,sizeof(*pt),n_side,true); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint Add_TREE +( + struct kcl_t *draw, // pointer to valid KCL + const + kmp_gobj_entry_t *op, // object to draw + DbFlags_t flags, // file flags + const s16 *data, // valid pointer to data + uint data_size, // size of data + bool detailed, // draw object detailed + bool as_road // draw as road +) +{ + DASSERT(draw); + DASSERT(op); + + // => trunk_wd trunk_dp trunk_ht crown_wd crown_dp crown_ht + // => data[0] data[1] data[2] data[3] data[4] data[5] + + if ( data_size < 6 ) + return 0; + + s16 d1[] = { KCL_FLAG_TREE_TRUNK - N_KCL_FLAG, + data[1] + data[2], data[0]/2, -data[1] }; + Add_CYLINDER_Y(draw,op,flags,d1,4,false,false); + + s16 d2[] = { KCL_FLAG_TREE_CROWN - N_KCL_FLAG, + 4, data[4] + data[5], data[3]/2, data[2] - data[4] }; + Add_PYRAMID(draw,op,flags,d2,5,false,false); + + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddObject*2KCL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddObject2KCL +( + DrawKCL_t * dk, // pointer to initalized object + const + kmp_gobj_entry_t * op // object to draw +) +{ + DASSERT(dk); + DASSERT(dk->draw); + DASSERT(op); + if ( !dk || !dk->draw || !op ) + return; + + uint draw_count = 0; + if ( op->obj_id < N_KMP_GOBJ ) + { + const ObjectInfo_t *oi = ObjectInfo + op->obj_id; + if ( oi->fname ) + { + int i; + for ( i = 0; i < sizeof(oi->group_idx)/sizeof(*oi->group_idx); i++ ) + { + uint gid = oi->group_idx[i]; + if ( gid >= N_DB_FILE_GROUP ) + break; + + const s16 *gp = DbFileRefGROUP + DbFileGROUP[gid].ref; + while ( *gp >= 0 ) + { + const DbFileFILE_t *f = DbFileFILE + *gp++; + if (f->draw_index) + { + const s16 *ptr = DbFileDraw + f->draw_index; + for(;;) + { + if ( *ptr < 2 ) + break; + + add_func func = 0; + switch(ptr[1]) + { + case DFD_RECTANGLE: func = Add_RECTANGLE; break; + case DFD_CUBE: func = Add_CUBE; break; + case DFD_CUBOID: func = Add_CUBOID; break; + case DFD_CYLINDER_Y: func = Add_CYLINDER_Y; break; + case DFD_CYLINDER_Z: func = Add_CYLINDER_Z; break; + case DFD_ITEMBOX: func = Add_ITEMBOX; break; + case DFD_KINOKO: func = Add_KINOKO; break; + case DFD_COIN: func = Add_COIN; break; + case DFD_PYRAMID: func = Add_PYRAMID; break; + case DFD_TREE: func = Add_TREE; break; + } + if (func) + draw_count += func( dk->draw, op, f->flags, ptr+2, *ptr-2, + dk->detailed, dk->as_road ); + ptr += *ptr; + } + } + } + } + } + } + + // fall back + + if (!draw_count) + { + const float radius = 150; + float3 pos; + pos.x = op->position[0]; + pos.y = op->position[1] + radius; + pos.z = op->position[2]; + + octahedron_t oct; + CreateRegularOctahedronF(&oct,radius,0,pos.v); + AppendOctahedronKCL(dk->draw,KCL_FLAG_SOLIDOBJ,&oct); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddObjectsById2KCL +( + DrawKCL_t * dk, // pointer to initalized object + u16 obj_id // object id to draw +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw || !dk->kmp ) + return; + + uint n = dk->kmp->dlist[KMP_GOBJ].used; + const kmp_gobj_entry_t *op = (kmp_gobj_entry_t*)dk->kmp->dlist[KMP_GOBJ].list; + for ( ; n > 0; n--, op++ ) + if ( op->obj_id == obj_id ) + AddObject2KCL(dk,op); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddObjectsByFlag2KCL +( + DrawKCL_t * dk, // pointer to initalized object + u32 flag_mask, // mask the object flag with this ... + u32 flag_result // ... and draw it, if the result is this. +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw || !dk->kmp ) + return; + + const draw_mode_t pflags = ( KMP_DRAW & DMD_M_PFLAGS ) >> DMD_SHIFT_PFLAGS; + + uint n = dk->kmp->dlist[KMP_GOBJ].used; + const kmp_gobj_entry_t *op = (kmp_gobj_entry_t*)dk->kmp->dlist[KMP_GOBJ].list; + for ( ; n > 0; n--, op++ ) + { + if ( op->obj_id < N_KMP_GOBJ && ( !pflags || pflags & op->pflags ) ) + { + const u32 flags = ObjectInfo[op->obj_id].flags; + if ( (flags & flag_mask) == flag_result ) + AddObject2KCL(dk,op); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddZeroGround2KCL +( + DrawKCL_t * dk, // pointer to initalized object + uint color // color of ground +) +{ + DASSERT(dk); + DASSERT(dk->draw); + if ( !dk || !dk->draw ) + return; + + const uint n_tri = dk->draw->tridata.used; + if (!n_tri) + return; + + uint i, n = 0; + double xmin = 0, xmax = 0, zmin = 0, zmax = 0; + const kcl_tridata_t *td = (kcl_tridata_t*)dk->draw->tridata.list; + + for ( i = 0; i < n_tri; i++, td++ ) + { + #if HAVE_PRINT0 + if ( td->cur_flag >= KCL_FLAG_TEST0 && td->cur_flag <= KCL_FLAG_TEST7 ) + printf("%6x: %04x %04x %7.1f\n", td->cur_flag, td->status, + td->status & (TD_REMOVED|TD_INVALID), td->pt[0].x ); + #endif + + if ( td->status & (TD_REMOVED|TD_INVALID) ) + continue; + + const double3 *pt = td->pt; + if (!n++) + { + xmin = xmax = pt[0].x; + zmin = zmax = pt[0].z; + } + + if ( xmin > pt[0].x ) xmin = pt[0].x; + if ( xmin > pt[1].x ) xmin = pt[1].x; + if ( xmin > pt[2].x ) xmin = pt[2].x; + + if ( xmax < pt[0].x ) xmax = pt[0].x; + if ( xmax < pt[1].x ) xmax = pt[1].x; + if ( xmax < pt[2].x ) xmax = pt[2].x; + + if ( zmin > pt[0].z ) zmin = pt[0].z; + if ( zmin > pt[1].z ) zmin = pt[1].z; + if ( zmin > pt[2].z ) zmin = pt[2].z; + + if ( zmax < pt[0].z ) zmax = pt[0].z; + if ( zmax < pt[1].z ) zmax = pt[1].z; + if ( zmax < pt[2].z ) zmax = pt[2].z; + } + + if (n) + { + PRINTF("xmax= %6.1f\n",xmax); + double3 pt1, pt2, pt3, pt4; + pt2.x = pt3.x = xmin - 1000; + pt1.x = pt4.x = xmax + 1000; + pt3.z = pt4.z = zmin - 1000; + pt1.z = pt2.z = zmax + 1000; + pt1.y = pt2.y = pt3.y = pt4.y = 0.0; + + AppendQuadrilateralKCLp(dk->draw,color,&pt1,&pt2,&pt3,&pt4); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// pos file support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +pos_param_t default_pos_param = {0}; +pos_file_t current_pos_param = {0}; +pos_file_t *first_pos_file = 0; + +static ParamField_t pos_autocolor = {0}; +static u32 pos_colors[] = +{ + KCL_FLAG_ORANGE, + KCL_FLAG_BLUE, + KCL_FLAG_GREEN, + KCL_FLAG_MAGENTA, + KCL_FLAG_YELLOW, + KCL_FLAG_RED, + KCL_FLAG_CYAN, + + KCL_FLAG_GOLD, + KCL_FLAG_SILVER, + KCL_FLAG_BRONZE, + + KCL_FLAG_WHITE, + KCL_FLAG_GREY, + KCL_FLAG_BROWN, + KCL_FLAG_BLACK, + + KCL_FLAG_ORANGE_YELLOW, + KCL_FLAG_BLUE_MAGENTA, + KCL_FLAG_GREEN_CYAN, + KCL_FLAG_MAGENTA_RED, + KCL_FLAG_YELLOW_GREEN, + KCL_FLAG_RED_ORANGE, + KCL_FLAG_CYAN_BLUE, +}; + +const uint n_pos_colors = sizeof(pos_colors)/sizeof(*pos_colors); + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupPosMode() +{ + if (!default_pos_param.color) + { + memset(&default_pos_param,0,sizeof(default_pos_param)); + default_pos_param.color = KCL_FLAG_SILVER; + default_pos_param.idx_x = 8; + default_pos_param.idx_d = 16; + + memset(¤t_pos_param,0,sizeof(current_pos_param)); + current_pos_param.n_param = 1; + current_pos_param.param[0] = default_pos_param; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void NormalizePosMode ( pos_file_t *pf ) +{ + DASSERT(pf); + + uint i; + pos_param_t *pp; + for ( i = 0, pp = pf->param; i < pf->n_param; i++, pp++ ) + { + if (!pp->idx_y) pp->idx_y = pp->idx_x + 1; + if (!pp->idx_z) pp->idx_z = pp->idx_y + 1; + if ( pp->transp > 0 ) + { + pp->color += pp->transp; + pp->transp = 0; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void CopyPosMode() +{ + SetupPosMode(); + + pos_file_t *pf; + for ( pf = first_pos_file; pf; pf = pf->next ) + if (!pf->n_param) + { + ccp save_fname = pf->fname; + memcpy(pf,¤t_pos_param,sizeof(*pf)); + pf->fname = save_fname; + NormalizePosMode(pf); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPosMode ( ccp arg ) +{ + if (!arg) + return 0; + + enum { SC_XPOS, SC_YPOS, SC_ZPOS, SC_DIR, SC_COLOR, SC_TRANSP, SC_FILTER }; + + static const KeywordTab_t keytab[] = + { + { SC_XPOS, "X", "XPOS", 0 }, + { SC_YPOS, "Y", "YPOS", 0 }, + { SC_ZPOS, "Z", "ZPOS", 0 }, + { SC_DIR, "D", "DIR", 0 }, + + { SC_COLOR, "C", "COLOR", 0 }, + { SC_TRANSP, "T", "TRANSPARENT", 0 }, + + { SC_FILTER, "FA", "FILTER-A", 0 }, + { SC_FILTER, "FB", "FILTER-B", 1 }, + { SC_FILTER, "FC", "FILTER-C", 2 }, + + {0,0,0,0} + }; + + SetupPosMode(); + pos_param_t *pp = current_pos_param.param; + + char skip_plus = '+'; + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == ',' || *arg == skip_plus ) + arg++; + if (!*arg) + break; + + if ( *arg == '+' ) + { + if ( current_pos_param.n_param == MAX_POS_PARAM ) + return ERROR0(ERR_SYNTAX, + "Too many '+' terms for option --pos-mode: %s\n",arg); + memcpy(pp+1,pp,sizeof(*pp)); + pp++; + current_pos_param.n_param++; + skip_plus = '+'; + continue; + } + skip_plus = ' '; + + char name[50]; + uint i; + for ( i = 0; i < sizeof(name) - 1; i++ ) + { + char ch = toupper((int)(arg[i])); + if ( ch < 'A' || ch > 'Z' ) + break; + name[i] = ch; + } + if (!i) + return ERROR0(ERR_SYNTAX, + "Option --pos-mode: Missing sub command: %s\n",arg); + name[i] = 0; + //ccp subcmd = arg; + arg += i; + + int stat; + const KeywordTab_t *key = ScanKeyword(&stat,name,keytab); + if (!key) + { + int cstat; + const KeywordTab_t *ckey = ScanKeyword(&cstat,name,kcl_color_key_tab); + if (ckey) + { + pp->color = ckey->id; + pp->transp = ckey->opt & 1 ? 0 : -1; + goto next_parm; + } + if ( stat < 1 && cstat > 1 ) + return PrintKeywordError(kcl_color_key_tab,name,cstat,0, + "Color of --pos-mode:"); + return PrintKeywordError(keytab,name,stat,0, + "Sub command of --pos-mode:"); + } + + char *end = 0; + switch(key->id) + { + case SC_XPOS: pp->idx_x = str2ul(arg,&end,10); break; + case SC_YPOS: pp->idx_y = str2ul(arg,&end,10); break; + case SC_ZPOS: pp->idx_z = str2ul(arg,&end,10); break; + case SC_DIR: pp->idx_d = str2ul(arg,&end,10); break; + + case SC_COLOR: + { + const uint num = str2ul(arg,&end,10); + if ( num <= MAX_POS_FILE_INDEX ) + pp->autocolor = num; + } + break; + + case SC_TRANSP: + { + const uint num = str2ul(arg,&end,10); + if ( pp->transp >= 0 && num <= 2 ) + pp->transp = num; + } + break; + + case SC_FILTER: + { + uint idx = str2ul(arg,&end,10); + if ( (ccp)end > arg ) + { + if ( *end == '=' ) + end++; + arg = end; + pp->idx_f[key->opt] = idx; + while ( *arg && *arg != ',' && *arg != '+' ) + arg++; + StringCopySM( pp->filter[key->opt], sizeof(*pp->filter), + end, arg - end ); + end = 0; + PRINT("FILTER[%u.%llu]: %u = %s\n", + current_pos_param.n_param, key->opt, + pp->idx_f[key->opt], pp->filter[key->opt]); + } + } + break; + } + + if (end) + { + if ( arg == (ccp)end ) + return ERROR0(ERR_SYNTAX, + "Option --pos-mode: Missing parameter for '%s': %s\n", + key->name1,arg); + arg = end; + } + + next_parm: + while ( *arg > 0 && *arg <= ' ' ) + arg++; + if (!*arg) + break; + if ( *arg != ',' && *arg != '+' ) + return ERROR0(ERR_SYNTAX, + "Option --pos-mode: Comma or plus expected: %s\n",arg); + } + + #if HAVE_PRINT0 + { + pos_param_t *pp = current_pos_param.param; + PRINT(" - FA %u = %s\n",pp->idx_f[0],pp->filter[0]); + PRINT(" - FB %u = %s\n",pp->idx_f[1],pp->filter[1]); + PRINT(" - FC %u = %s\n",pp->idx_f[2],pp->filter[2]); + } + #endif + CopyPosMode(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPosFile ( ccp arg ) +{ + if ( !arg || !*arg ) + return 0; + + //SetupPosMode(); + + pos_file_t *pf = MALLOC(sizeof(*pf)); + DASSERT(pf); + memcpy(pf,¤t_pos_param,sizeof(*pf)); + pf->fname = STRDUP(arg); + NormalizePosMode(pf); + + pf->next = first_pos_file; + first_pos_file = pf; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DrawPosFiles ( DrawKCL_t *draw ) +{ + CopyPosMode(); + + pos_file_t *pf; + for ( pf = first_pos_file; pf; pf = pf->next ) + DrawPosFile(draw,pf); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DrawPosFile ( DrawKCL_t *draw, pos_file_t *pf ) +{ + DASSERT(draw); + + if ( !pf || !pf->fname || !*pf->fname ) + return ERR_NOTHING_TO_DO; + + SetupPosMode(); + + File_t F; + enumError err = OpenFile(&F,true,pf->fname,FM_STDIO,0,0); + if (err) + { + FreeString(pf->fname); + pf->fname = 0; + return err; + } + + PRINT("scan [N=%u] %s\n",n_param,pf->fname); + PRINT(" - FA %u = %s\n",pf->param->idx_f[0],pf->param->filter[0]); + PRINT(" - FB %u = %s\n",pf->param->idx_f[1],pf->param->filter[1]); + PRINT(" - FC %u = %s\n",pf->param->idx_f[2],pf->param->filter[2]); + + double3 pos[3]; + pos[0].x = pos[0].y = pos[0].z = 0.0; + double3 rot; + rot.x = rot.y = rot.z = 0.0; + + char buf[1000]; + ccp cols[MAX_POS_FILE_INDEX+1]; + cols[0] = buf; + + while (fgets(buf,sizeof(buf)-1,F.f)) + { + char *ptr = buf; + + int idx; + for ( idx = 1; idx <= MAX_POS_FILE_INDEX; ) + { + while ( *ptr == ' ' || *ptr == '\t' ) + ptr++; + if ( (uchar)*ptr < ' ' || *ptr == '#' ) + break; + + cols[idx++] = ptr; + while ( (uchar)*ptr > ' ' ) + ptr++; + if (!*ptr) + break; + *ptr++ = 0; + } + + uint i; + pos_param_t *pp; + for ( i = 0, pp = pf->param; i < pf->n_param; i++, pp++ ) + { + uint fi, ok = 1; + for ( fi = 0; fi < MAX_POS_FILTER; fi++ ) + { + const uint fidx = pp->idx_f[fi]; + if (!fidx) + continue; + if ( fidx >= idx || strcmp(pp->filter[fi],cols[fidx]) ) + { + ok = 0; + break; + } + } + if (!ok) + continue; + + if ( pp->idx_x < idx && pp->idx_y < idx && pp->idx_z < idx ) + { + pos[0].x = strtod(cols[pp->idx_x],0); + pos[0].y = strtod(cols[pp->idx_y],0); + pos[0].z = strtod(cols[pp->idx_z],0); + } + else + continue; + + if ( pp->idx_d && pp->idx_d < idx ) + rot.y = strtod(cols[pp->idx_d],0); + + //printf("%9.1f %8.1f %9.1f | %4.0f\n",pos[0].x,pos[0].y,pos[0].z,rot.y); + + pos[1].x = pos[0].x + 40; + pos[2].x = pos[0].x - 40; + pos[1].y = pos[2].y = pos[0].y; + pos[1].z = pos[2].z = pos[0].z - 120; + + u32 col = pp->color; + if ( pp->autocolor && pp->autocolor < idx && cols[pp->autocolor][0] ) + { + if (!pos_autocolor.used) + InitializeParamField(&pos_autocolor); + + bool old; + ParamFieldItem_t *it + = FindInsertParamField(&pos_autocolor,cols[pp->autocolor],false,&old); + if (it) + { + if (!old) + it->num = ( pos_autocolor.used -1 ) % n_pos_colors; + col = pos_colors[it->num]; + if ( pp->transp >= 0 ) + col += pp->transp; + } + } + + Rotate(pos,&rot,pos+1,sizeof(double3),2); + AppendTriangleKCLp(draw->draw,col,pos+0,pos+1,pos+2); + } + } + + CloseFile(&F,0); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-kmp-pflags.c b/project/src/lib-kmp-pflags.c new file mode 100644 index 0000000..07be6c6 --- /dev/null +++ b/project/src/lib-kmp-pflags.c @@ -0,0 +1,1965 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kmp.h" +#include "lib-szs.h" +#include "dclib-xdump.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +opt_gamemode_t opt_gamemodes = GMD_M_DEFAULT; + +static const KeywordTab_t opt_gamemodes_tab[] = +{ + { 0, "CLEAR", "RESET", GMD_M_ALL | GMD_F_HIDE }, + { GMD_M_DEFAULT, "DEFAULT", 0, GMD_M_ALL | GMD_F_HIDE }, + + { GMD_STD, "STANDARD", "STD", 0 }, + { GMD_BALLOON, "BALLOON", 0, 0 }, + { GMD_COIN, "COIN", 0, 0 }, + { GMD_MD_BATTLE, "BATTLE", 0, 0 }, + { GMD_VERSUS, "VERSUS", "VS", 0 }, + { GMD_ITEMRAIN, "ITEMRAIN", 0, 0 }, + { GMD_MD_RACE, "RACE", 0, 0 }, + { GMD_MD_ALL, "ALL", 0, 0 }, + + { GMD_NO_MODES, "NO-MODES", "NOMODES", 0 }, + { GMD_OFFLINE, "OFFLINE", 0, 0 }, + { GMD_ONLINE, "ONLINE", 0, 0 }, + + { GMD_AUTO, "AUTO", 0, 0 }, + { GMD_STD_FIRST, "1STANDARD", "1STD", 0 }, + { GMD_ENGINE, "ENGINE", 0, 0 }, + { 0, "NO-ENGINE", "NOENGINE", GMD_ENGINE }, + { GMD_RANDOM, "RANDOM", 0, 0 }, + { 0, "NO-RANDOM", "NORANDOM", GMD_RANDOM }, + { GMD_NO_TT, "NO-TT", "NOTT", 0 }, + { GMD_FORCE_TT, "TT", 0, 0 }, + { GMD_FULL, "FULL", 0, 0 }, + + { GMD_NSORT, "SORT", "NSORT", GMD_M_SORT }, + { GMD_ISORT, "ISORT", 0, GMD_M_SORT }, + { 0, "NO-SORT", "NOSORT", GMD_M_SORT }, + + { GMD_DEBUG, "DEBUG", 0, 0 }, + { GMD_DUMP, "DUMP", 0, 0 }, + + {0,0,0,0} +}; + +//----------------------------------------------------------------------------- + +int ScanOptGamemodes ( ccp arg ) +{ + if (!arg) + return 0; + + s64 stat = ScanKeywordList(arg,opt_gamemodes_tab,0,true,0,GMD_M_OPTIONS, + "Option --gamemodes",ERR_SYNTAX); + if ( stat != -1 ) + { + opt_gamemodes = stat; + if (!(opt_gamemodes&GMD_MD_ALL)) + opt_gamemodes |= GMD_MD_DEFAULT; + if (!(opt_gamemodes&GMD_FI_ALL)) + opt_gamemodes |= GMD_FI_DEFAULT; + + // do this after evaluating GMD_MD_ALL! + if (opt_gamemodes&GMD_STD_FIRST) + opt_gamemodes |= GMD_STD; + + if ( opt_gamemodes & GMD_DEBUG ) + { + char buf[200] = {0}; + PrintKeywordList( buf, sizeof(buf), 0, opt_gamemodes_tab, + opt_gamemodes, GMD_M_DEFAULT, GMD_F_HIDE ); + fflush(stdout); + fprintf(stderr,"GAMEMODES: %s\n",buf); + fflush(stderr); + } + return 0; + } + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_pflags_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint CountPresenceFlags ( const kmp_t *kmp, kmp_pflags_t *pf ) +{ + DASSERT(pf); + DASSERT(kmp); + + memset(pf,0,sizeof(*pf)); + + const uint n = kmp->dlist[KMP_GOBJ].used; + pf->n_gobj = n; + + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint i; + for ( i = 0; i < n; i++, gobj++ ) + { + const u16 pflags = gobj->pflags; + const u16 std = pflags & GOBJ_M_PF_STD; + const u16 mode = ( pflags & GOBJ_M_PF_MODE ) >> GOBJ_S_PF_MODE; + + if ( std == pflags ) + pf->n_standard++; + + if ( mode > 0 ) + pf->n_xpflags++; + + if (!pf->n_mode[mode]++) + pf->n_cat_mode++; + + if (!pf->n_std[std]++) + pf->n_cat_std++; + + if ( mode == 1 ) + { + if (IsConditionRefRelevantRandomId(gobj->ref_id)) + pf->n_cond_rnd++; + else if (IsConditionRefId(gobj->ref_id)) + pf->n_cond_ref++; + + if (IsDefinitionObjectId(gobj->ref_id)) + pf->n_obj_ref++; + } + + if ( IsDefinitionObjectIdBITS(gobj->obj_id) ) + pf->n_defobj_bits++; + else if ( IsDefinitionObjectIdOR(gobj->obj_id) ) + pf->n_defobj_or++; + else if ( IsDefinitionObjectIdAND(gobj->obj_id) ) + pf->n_defobj_and++; + } + + pf->n_defobj = pf->n_defobj_bits + pf->n_defobj_or + pf->n_defobj_and; + return n; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// detect, fix and sort definition-objects /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyseDefObject ( kmp_ana_defobj_t *ad, const kmp_gobj_entry_t *gobj ) +{ + DASSERT(ad); + DASSERT(gobj); + memset(ad,0,sizeof(*ad)); + + if (IsDefinitionObjectId(gobj->obj_id)) + { + ad->is_defobj = true; + ad->is_pure_defobj = + ( gobj->ref_id == 0 || IsConditionRefId(gobj->ref_id) ) + && gobj->position[0] == 0.0 + && gobj->position[1] == 0.0 + && gobj->position[2] == 0.0 + && gobj->rotation[0] == 0.0 + && gobj->rotation[1] == 0.0 + && gobj->rotation[2] == 0.0 + && gobj->scale[0] == 0.0 + && gobj->scale[1] == 0.0 + && gobj->scale[2] == 0.0 + && gobj->route_id == 0xffff + && ( gobj->pflags == 0 + || gobj->pflags == GOBJ_PF_MODE1 ); + } + + if ( IsDefinitionObjectIdOR(gobj->obj_id) ) + { + ad->cmd_name = "OR"; + logical: + ad->par_info = "obj_id, condition_1 [,conditions_2_8]..."; + ad->is_logical = true; + ad->index_beg = 0; + ad->index_end = 8; + return; + } + else if ( IsDefinitionObjectIdAND(gobj->obj_id) ) + { + ad->cmd_name = "AND"; + goto logical; + } + + u16 set0 = gobj->setting[0]; + uint idx = 1; + while ( idx < 8 && gobj->setting[idx] == set0 ) + idx++; + + if ( idx > 2 ) + { + ad->cmd_name = "RACE"; + ad->par_info = "obj_id, versus, itemrain"; + ad->index_beg = 2; + } + else + { + ad->cmd_name = "BATTLE"; + ad->par_info = "obj_id, balloon, coin, versus, itemrain"; + ad->index_beg = 0; + } + + while ( idx < 8 ) + { + const u16 set1 = gobj->setting[idx++]; + if ( set0 != set1 ) + { + ad->index_end = idx; + set0 = set1; + } + } + + if ( ad->index_end <= ad->index_beg ) + ad->index_end = ad->index_beg + 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NormalizeDefObj ( kmp_gobj_entry_t *gobj ) +{ + DASSERT(gobj); + bool stat = false; + if (IsDefinitionObjectId(gobj->obj_id)) + { + kmp_gobj_entry_t temp; + memcpy(&temp,gobj,sizeof(temp)); + + gobj->position[0] = 0.0; + gobj->position[1] = 0.0; + gobj->position[2] = 0.0; + gobj->rotation[0] = 0.0; + gobj->rotation[1] = 0.0; + gobj->rotation[2] = 0.0; + gobj->scale[0] = 0.0; + gobj->scale[1] = 0.0; + gobj->scale[2] = 0.0; + gobj->route_id = 0xffff; + gobj->pflags = 0; + + if (IsDefinitionObjectIdBITS(gobj->obj_id)) + { + const u16 val = gobj->setting[2]; + gobj->setting[4] = val; + gobj->setting[5] = val; + gobj->setting[6] = val; + gobj->setting[7] = val; + } + + stat = memcmp(&temp,gobj,sizeof(temp)) != 0; + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountDefObjUsage ( const kmp_t *kmp, u16 obj_id ) +{ + DASSERT(kmp); + + uint i, count = 0; + const List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + const kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + for ( i = 0; i < n; i++, gobj++ ) + { + if ( (gobj->ref_id & ~GOBJ_DEF_F_NOT) == obj_id ) + { + kmp_ana_ref_t ar; + AnalyseRefObject(&ar,gobj); + if (ar.is_valid) + count++; + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetDefObjConditionName ( u16 cond ) +{ + const gobj_cond_ref_t *rc; + for ( rc = cond_ref_tab; rc->ref_id; rc++ ) + if ( rc->cond_mask == cond && !(rc->option & 4) ) + return PrintCircBuf("C$%s",rc->name); + + return PrintCircBuf("0x%04x",cond); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FixDefObj +( + kmp_t * kmp, // pointer to valid KMP + int fix_mode // >0: clear unused parameters +) +{ + DASSERT(kmp); + if ( fix_mode < 1 ) + return false; + + uint i, fix_count = 0; + List_t *dlist = kmp->dlist + KMP_GOBJ; + kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + for ( i = 0; i < dlist->used; i++, gobj++ ) + if (NormalizeDefObj(gobj)) + fix_count++; + + return fix_count>0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int sort_defobj_begin + ( const kmp_gobj_entry_t *a, const kmp_gobj_entry_t *b ) +{ + if (IsDefinitionObjectId(a->obj_id)) + { + if (!IsDefinitionObjectId(b->obj_id)) + return -1; + + return a->obj_id < b->obj_id ? -1 + : a->obj_id > b->obj_id ? 1 + : a < b ? -1 : 1; + } + + if (IsDefinitionObjectId(b->obj_id)) + return 1; + return a < b ? -1 : 1; +} + +//----------------------------------------------------------------------------- + +static int sort_defobj_end + ( const kmp_gobj_entry_t *a, const kmp_gobj_entry_t *b ) +{ + if (IsDefinitionObjectId(a->obj_id)) + { + if (!IsDefinitionObjectId(b->obj_id)) + return 1; + + return a->obj_id < b->obj_id ? -1 + : a->obj_id > b->obj_id ? 1 + : a < b ? -1 : 1; + } + + if (IsDefinitionObjectId(b->obj_id)) + return -1; + return a < b ? -1 : 1; +} + +//----------------------------------------------------------------------------- + +bool SortDefObj +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // sort if KMP_DOB_BEGIN || KMP_DOB_END +) +{ + DASSERT(kmp); + if ( sort_mode != KMP_DOB_BEGIN && sort_mode != KMP_DOB_END ) + return false; + + List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + if ( n < 2 ) + return false; + + kmp_gobj_entry_t *base = (kmp_gobj_entry_t*)dlist->list; + if ( sort_mode == KMP_DOB_BEGIN ) + qsort( base, n, sizeof(*base), (qsort_func)sort_defobj_begin ); + else + qsort( base, n, sizeof(*base), (qsort_func)sort_defobj_end ); + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// detect, fix and sort references /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyseRefObject ( kmp_ana_ref_t *ar, const kmp_gobj_entry_t *gobj ) +{ + DASSERT(ar); + DASSERT(gobj); + memset(ar,0,sizeof(*ar)); + + ar->mode = GetPFlagsMode(gobj->pflags); + ar->mode_valid = ar->mode == 1; + + ar->condref_valid = IsConditionRefId(gobj->ref_id); + ar->is_condref = ar->mode_valid && ar->condref_valid; + + ar->condeng_valid = IsConditionRefEngineId(gobj->ref_id); + ar->is_condeng = ar->mode_valid && ar->condeng_valid; + + ar->condrnd_valid = IsConditionRefRandomId(gobj->ref_id); + ar->is_condrnd = ar->mode_valid && ar->condrnd_valid; + + ar->condtest_valid = IsConditionRefTestId(gobj->ref_id); + ar->is_condtest = ar->mode_valid && ar->condtest_valid; + + ar->objref_valid = IsDefinitionObjectId(gobj->ref_id); + ar->is_objref = ar->mode_valid && ar->objref_valid; + + ar->is_valid = ar->objref_valid || ar->is_objref; +} + +/////////////////////////////////////////////////////////////////////////////// + +kmp_gobj_entry_t * FindRefObjectByID ( const kmp_t *kmp, u16 ref_id ) +{ + DASSERT(kmp); + ref_id &= ~GOBJ_DEF_F_NOT; + if (!IsDefinitionObjectId(ref_id)) + return 0; + + const List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + uint i; + for ( i = 0; i < n; i++, gobj++ ) + if ( gobj->obj_id == ref_id ) + return gobj; + return 0; +} + +//----------------------------------------------------------------------------- + +kmp_gobj_entry_t * FindRefObject ( const kmp_t *kmp, const kmp_gobj_entry_t *gobj ) +{ + DASSERT(kmp); + DASSERT(gobj); + + kmp_ana_ref_t ar; + AnalyseRefObject(&ar,gobj); + return ar.is_valid ? FindRefObjectByID(kmp,gobj->ref_id) : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FixReference +( + kmp_t * kmp, // pointer to valid KMP + int fix_mode // >0: fix if valid mode + // >1: fix for all modes +) +{ + DASSERT(kmp); + if ( fix_mode < 1 ) + return false; + + uint i, fix_count = 0; + List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + for ( i = 0; i < n; i++, gobj++ ) + { + kmp_ana_ref_t ar; + AnalyseRefObject(&ar,gobj); + if ( !ar.is_valid && ( fix_mode > 1 || ar.mode_valid )) + { + gobj->ref_id = 0; + fix_count++; + } + } + return fix_count>0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int sort_reference ( const u32 *a, const u32 *b ) +{ + return (s32)*a - (s32)*b; +} + +//----------------------------------------------------------------------------- + +bool SortReference +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // sort if KMP_DOB_BEFORE || KMP_DOB_BEHIND +) +{ + DASSERT(kmp); + if ( sort_mode != KMP_DOB_BEFORE && sort_mode != KMP_DOB_BEHIND ) + return false; + + List_t *dlist = kmp->dlist + KMP_GOBJ; + const uint n = dlist->used; + if ( n < 2 ) + return false; + + kmp_gobj_entry_t *base = (kmp_gobj_entry_t*)dlist->list; + u32 *keys = MALLOC(n*sizeof(*keys)); + uint i; + const kmp_gobj_entry_t *gobj = base; + if ( sort_mode == KMP_DOB_BEFORE ) + { + for ( i = 0; i < n; i++, gobj++ ) + { + const kmp_gobj_entry_t *found = FindRefObject(kmp,gobj); + if (!found) + keys[i] = i << 16 | 0x8000 | i; + else + keys[i] = (uint)(found-base) << 16 | i; + } + } + else + { + for ( i = 0; i < n; i++, gobj++ ) + { + const kmp_gobj_entry_t *found = FindRefObject(kmp,gobj); + if (!found) + keys[i] = i << 16 | i; + else + keys[i] = (uint)(found-base) << 16 | 0x8000 | i; + } + } + + qsort ( keys, n, sizeof(*keys), (qsort_func)sort_reference ); + + kmp_gobj_entry_t *temp = MALLOC(n*sizeof(*temp)); + for ( i = 0; i < n; i++ ) + { + const kmp_gobj_entry_t *src = base + ( keys[i] & 0x7fff ); + memcpy(temp+i,src,sizeof(*temp)); + } + memcpy(base,temp,n*sizeof(*temp)); + FREE(temp); + FREE(keys); + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u16 GetActiveObjectId ( const kmp_gobj_entry_t *g ) +{ + if ( !g || g->obj_id >= GOBJ_MIN_DEF ) + return 0; + + const u16 obj_id = g->obj_id & GOBJ_M_OBJECT; + return obj_id < N_KMP_GOBJ + && ( g->pflags & GOBJ_M_PF_STD + || (g->pflags & GOBJ_M_PF_MODE) == GOBJ_PF_MODE1 ) + ? obj_id : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Convert conditions to text /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const gobj_condition_t gobj_condition[] = +{ + { 0xffff,0xff,-1, 1,4, 0,99, + "All conditions." }, + { 0x3fff,0xfb,-1, 1,4, 0,99, + "All conditions except reserved." }, + { 0x7fff,0x04,-1, 1,4, 0,99, + "All conditions except reserved 2." }, + { 0x3fff,0xff,-1, 1,4, 0,99, + "All conditions except time trial and reserved." }, + + //--- offline players + + { 0x000f,0xff,-1, 1,4, 0,0, + "Offline race with 1-4 human players." }, + { 0x0007,0xff,-1, 1,3, 0,0, + "Offline race with 1-3 human players." }, + { 0x0003,0xff,-1, 1,2, 0,0, + "Offline race with 1-2 human players." }, + { 0x0001,0xff, 0, 1,1, 0,0, + "Offline race with 1 human player." }, + + { 0x000e,0xff,-1, 2,4, 0,0, + "Offline race with 2-4 human players." }, + { 0x0006,0xff,-1, 2,3, 0,0, + "Offline race with 2-3 human players." }, + { 0x0002,0xff, 1, 2,2, 0,0, + "Offline race with 2 human players." }, + + { 0x000c,0xff,-1, 3,4, 0,0, + "Offline race with 3-4 human players." }, + { 0x0004,0xff, 2, 3,3, 0,0, + "Offline race with 3 human players." }, + { 0x0008,0xff, 3, 4,4, 0,0, + "Offline race with 4 human players." }, + + + //--- online, 1-N players total + + { 0x3ff0,0xff,-1, 1,2, 1,99, + "Online race with any number of players." }, + { 0x1550,0xff,-1, 1,1, 1,99, + "Online race with 1 human player at Wii." }, + { 0x2aa0,0xff,-1, 2,2, 1,99, + "Online race with 2 human players at Wii." }, + + { 0x0ff0,0xff,-1, 1,2, 1,18, + "Online race with 1-18 players total." }, + { 0x0550,0xff,-1, 1,1, 1,18, + "Online race with 1-18 players total and 1 human at Wii." }, + { 0x0aa0,0xff,-1, 2,2, 1,18, + "Online race with 1-18 players total and 2 humans at Wii." }, + + { 0x03f0,0xff,-1, 1,2, 1,12, + "Online race with 1-12 players total." }, + { 0x0150,0xff,-1, 1,1, 1,12, + "Online race with 1-12 players total and 1 human at Wii." }, + { 0x02a0,0xff,-1, 2,2, 1,12, + "Online race with 1-12 players total and 2 humans at Wii." }, + + { 0x00f0,0xff,-1, 1,2, 1,9, + "Online race with 1-9 players total." }, + { 0x0050,0xff,-1, 1,1, 1,9, + "Online race with 1-9 players total and 1 human at Wii." }, + { 0x00a0,0xff,-1, 2,2, 1,9, + "Online race with 1-9 players total and 2 humans at Wii." }, + + { 0x0030,0xff,-1, 1,2, 1,9, + "Online race with 1-6 players total." }, + { 0x0010,0xff, 4, 1,1, 1,6, + "Online race with 1-6 players total and 1 human at Wii." }, + { 0x0020,0xff, 5, 2,2, 1,6, + "Online race with 1-6 players total and 2 humans at Wii." }, + + + //--- online, 7-N players total + + { 0x3fc0,0xff,-1, 1,2, 7,99, + "Online race with 7 or more players total." }, + { 0x1540,0xff,-1, 1,1, 7,99, + "Online race with 7 or more players total and 1 human at Wii." }, + { 0x2a80,0xff,-1, 2,2, 7,99, + "Online race with 7 or more players total and 2 humans at Wii." }, + + { 0x0fc0,0xff,-1, 1,2, 7,18, + "Online race with 7-18 players total." }, + { 0x0540,0xff,-1, 1,1, 7,18, + "Online race with 7-18 players total and 1 human at Wii." }, + { 0x0a80,0xff,-1, 2,2, 7,18, + "Online race with 7-18 players total and 2 humans at Wii." }, + + { 0x03c0,0xff,-1, 1,2, 7,12, + "Online race with 7-12 players total." }, + { 0x01e0,0xff,-1, 1,1, 7,12, + "Online race with 7-12 players total and 1 human at Wii." }, + { 0x0280,0xff,-1, 2,2, 7,12, + "Online race with 7-12 players total and 2 humans at Wii." }, + + { 0x00c0,0xff,-1, 1,2, 7,9, + "Online race with 7-9 players total." }, + { 0x0040,0xff, 6, 1,1, 7,9, + "Online race with 7-9 players total and 1 human at Wii." }, + { 0x0080,0xff, 7, 2,2, 7,9, + "Online race with 7-9 players total and 2 humans at Wii." }, + + + //--- online, 10-N players total + + { 0x3f00,0xff,-1, 1,2, 10,99, + "Online race with 10 or more players total." }, + { 0x1500,0xff,-1, 1,1, 10,99, + "Online race with 10 or more players total and 1 human at Wii." }, + { 0x2a00,0xff,-1, 2,2, 10,99, + "Online race with 10 or more players total and 2 humans at Wii." }, + + { 0x0f00,0xff,-1, 1,2, 10,18, + "Online race with 10-18 players total." }, + { 0x0500,0xff,-1, 1,1, 10,18, + "Online race with 10-18 players total and 1 human at Wii." }, + { 0x0a00,0xff,-1, 2,2, 10,18, + "Online race with 10-18 players total and 2 humans at Wii." }, + + { 0x0300,0xff,-1, 1,2, 10,12, + "Online race with 10-12 players total." }, + { 0x0100,0xff, 8, 1,1, 10,12, + "Online race with 10-12 players total and 1 human at Wii." }, + { 0x0200,0xff, 9, 2,2, 10,12, + "Online race with 10-12 players total and 2 humans at Wii." }, + + + //--- online, 13-N players total + + { 0x3c00,0xff,-1, 1,2, 13,99, + "Online race with 13 or more players total." }, + { 0x1400,0xff,-1, 1,1, 13,99, + "Online race with 13 or more players total and 1 human at Wii." }, + { 0x2800,0xff,-1, 2,2, 13,99, + "Online race with 13 or more players total and 2 humans at Wii." }, + + { 0x0c00,0xff,-1, 1,2, 13,18, + "Online race with 13-18 players total." }, + { 0x0400,0xff,10, 1,1, 13,18, + "Online race with 13-18 players total and 1 human at Wii." }, + { 0x0800,0xff,11, 2,2, 13,18, + "Online race with 13-18 players total and 2 humans at Wii." }, + + + //--- online, 17-N players total + + { 0x3000,0xff,-1, 1,2, 19,99, + "Online race with 19 or more players total." }, + { 0x1000,0xff,12, 1,1, 19,99, + "Online race with 19 or more players total and 1 human at Wii." }, + { 0x2000,0xff,13, 2,2, 19,99, + "Online race with 19 or more players total and 2 humans at Wii." }, + + + //--- reserved + + { 0x4000,0x04,14, 1,1, -1,-1, + "Time Trial (Reserved 1)." }, + { 0xc000,0xfb,-1, -1,-1, -1,-1, + "Reserved 1 and 2." }, + { 0x4000,0xfb,14, -1,-1, -1,-1, + "Reserved 1." }, + { 0x8000,0xff,15, -1,-1, -1,-1, + "Reserved 2." }, + {0,0,-1,-1,-1,-1,-1,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +uint GetGobjConditions ( gobj_condition_set_t *gcs, int setting, u16 mask ) +{ + DASSERT(gcs); + const u8 setting_mask = setting >= 0 && setting < 8 ? 1<mask = mask; + gcs->setting_mask = setting_mask; + + const gobj_condition_t *src = gobj_condition; + gobj_condition_t *dest = gcs->cond; + while (src->info) + { + if ( setting_mask & src->setting + && ( mask & src->mask ) == src->mask + && dest - gcs->cond < GOBJ_COND_SET_MAX + ) + { + memcpy(dest++,src,sizeof(*dest)); + mask &= ~src->mask; + DASSERT( dest - gcs->cond <= GOBJ_COND_SET_MAX ); + } + src++; + } + memcpy(dest,src,sizeof(*dest)); + return gcs->n = dest - gcs->cond; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Analyse GOBJ elements /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetAnaGobj ( kmp_ana_gobj_t *ag ) +{ + if (ag) + { + FREE(ag->info); + memset(ag,0,sizeof(*ag)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static kmp_gobj_status_t CheckRefStatus ( u16 ref_id ) +{ + if (IsConditionRefEngineId(ref_id)) + return KGST_REF_VALID | KGST_ENGINE; + + if (IsConditionRefRandomId(ref_id)) + return KGST_REF_VALID | KGST_RANDOM; + + const gobj_cond_ref_t *cr = FindConditionRef(ref_id); + return cr ? KGST_REF_VALID : 0; +} + +//----------------------------------------------------------------------------- + +void AnalyseGobj ( kmp_ana_gobj_t *ag, const kmp_t *kmp ) +{ + DASSERT(ag); + DASSERT(kmp); + + const u_usec_t start_time = GetTimerUSec(); + + memset(ag,0,sizeof(*ag)); + ag->kmp = kmp; + ag->gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + ag->n_gobj = kmp->dlist[KMP_GOBJ].used; + if ( ag->n_gobj > 0x7fff ) + ag->n_gobj = 0x7fff; + + ag->info = CALLOC(ag->n_gobj,sizeof(*ag->info)); + + + //--- first loop + + uint i; + kmp_gobj_info_t *info; + const kmp_gobj_entry_t *gobj; + for ( i = 0, info = ag->info, gobj = ag->gobj; i < ag->n_gobj; i++, info++, gobj++ ) + { + info->defobj = -1; + if (!gobj->obj_id) + { + info->type = KGTY_NULL; + continue; + } + + info->type = KGTY_INVALID; + if ( gobj->obj_id < GOBJ_MIN_DEF ) + { + const u16 relevant_id = GetRelevantObjectId(gobj->obj_id); + if ( relevant_id >= N_KMP_GOBJ || !ObjectInfo[relevant_id].name ) + continue; + + ag->n_object++; + info->obj_id = relevant_id; + info->type = KGTY_OBJECT; + info->status |= KGST_USED; + + if ( gobj->obj_id < GOBJ_M_OBJECT ) + { + ag->n_enabled++; + info->status |= KGST_ENABLED; + } + else + ag->n_disabled++; + + kmp_gobj_entry_t *defobj = FindRefObjectByID(kmp,gobj->ref_id); + if (defobj) + { + info->ref_id = gobj->ref_id; + info->status |= KGST_REF_VALID; + info->defobj = defobj - ag->gobj; + ag->info[info->defobj].ref_count++; + ag->info[info->defobj].status |= KGST_USED; + } + } + else + { + if ( IsDefinitionObjectIdBITS(gobj->obj_id) ) + { + ag->n_defobj_bits++; + info->type = KGTY_DEFOBJ_BITS; + } + else if ( IsDefinitionObjectIdOR(gobj->obj_id) ) + { + ag->n_defobj_or++; + info->type = KGTY_DEFOBJ_OR; + } + else if ( IsDefinitionObjectIdAND(gobj->obj_id) ) + { + ag->n_defobj_and++; + info->type = KGTY_DEFOBJ_AND; + } + else + continue; + + + // this is a valid def-object + if ( info->type == KGTY_DEFOBJ_OR || info->type == KGTY_DEFOBJ_AND ) + { + kmp_gobj_status_t rs =0; + uint i; + for ( i = 0; i < 8; i++ ) + rs = CheckRefStatus(gobj->setting[i]); + + if (rs) + { + info->status |= rs; + info->ref_id = gobj->ref_id; + } + } + } + + //--- analyse ref_id + + if ( gobj->ref_id ) + { + const kmp_gobj_status_t rs = CheckRefStatus(gobj->ref_id); + if (rs) + { + info->status |= rs; + info->ref_id = gobj->ref_id; + } + else + info->status |= KGST_DISABLED_BY; + } + } + + + //--- second loop + + for ( i = 0, info = ag->info; i < ag->n_gobj; i++, info++ ) + { + if ( info->type == KGTY_OBJECT ) + { + if ( info->defobj >= 0 ) + info->status |= ag->info[info->defobj].status & KGST_M_DERIVE; + + if ( info->status & KGST_ENGINE ) + ag->n_engine++; + if ( info->status & KGST_RANDOM ) + ag->n_random++; + } + } + + + //--- finalize + + ag->n_object = ag->n_disabled + ag->n_enabled; + ag->n_defobj = ag->n_defobj_bits + ag->n_defobj_or + ag->n_defobj_and; + ag->dur_usec = GetTimerUSec() - start_time; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Analyse PFlag Scenarios /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct xpf_bits_t +{ + u16 mask; // mask to select bit + u8 bitnum; // bit number + u8 pflag; // mask to selct presence_flag + u8 n_offline; // num of human players at Wii + u8 n_online; // num of total online players + u16 relevant; // bit field: relevant for setting + bool no_prefix; // true: suppress game mode prefix + char name[5]; // name +} +xpf_bits_t; + +static const xpf_bits_t xpf_bits[16+1] = +{ + { 0x0001, 0, 1, 1, 0, 0x800f, 0, "1" }, + { 0x0002, 1, 2, 2, 0, 0x800f, 0, "2" }, + { 0x0004, 2, 4, 3, 0, 0x800f, 0, "3" }, + { 0x0008, 3, 4, 4, 0, 0x000f, 0, "4" }, + { 0x0010, 4, 1, 1, 2, 0x000f, 0, "1,2" }, + { 0x0020, 5, 2, 2, 2, 0x000f, 0, "2,2" }, + { 0x0040, 6, 1, 1, 7, 0x000f, 0, "1,7" }, + { 0x0080, 7, 2, 2, 7, 0x000f, 0, "2,7" }, + { 0x0100, 8, 1, 1, 10, 0x000f, 0, "1,10" }, + { 0x0200, 9, 2, 2, 10, 0x000f, 0, "2,10" }, + { 0x0400, 10, 1, 1, 13, 0x000f, 0, "1,13" }, + { 0x0800, 11, 2, 2, 13, 0x000f, 0, "2,13" }, + { 0x1000, 12, 1, 1, 19, 0x000f, 0, "1,19" }, + { 0x2000, 12, 2, 2, 19, 0x000f, 0, "2,19" }, + { 0x4000, 14, 1, 1, 0, 0x0004, 1, "TT" }, + { 0x8000, 15, 1, 0, 0, 0x0000, 0, "?" }, + { 0, 0, 0, 0, 0, 0, 0, "" }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPFlagScenarios ( kmp_ana_pflag_t *ap ) +{ + if (ap) + { + ResetAnaGobj(&ap->ag); + + uint i; + for ( i = 0; i < ap->n_res; i++ ) + FreeString(ap->res_list[i].status); + FREE(ap->res_list); + memset(ap,0,sizeof(*ap)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +kmp_ana_pflag_res_t * AppendPFlagScenario ( kmp_ana_pflag_t *ap ) +{ + DASSERT(ap); + if ( ap->n_res == ap->res_size ) + { + ap->res_size = 2 * ap->res_size + 50; + ap->res_list = REALLOC( ap->res_list, ap->res_size*sizeof(*ap->res_list) ); + } + + kmp_ana_pflag_res_t *ptr = ap->res_list + ap->n_res++; + memset(ptr,0,sizeof(*ptr)); + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef SUPPORT_SEP + +#if HAVE_WIIMM_EXT + #define SUPPORT_SEP 1 +#else + #define SUPPORT_SEP 0 +#endif + +// helper consts + +enum +{ + X_INVALID_MODE = -16, + X_INVALID_CONDREF, + X_INVALID_REF, + X_NOTFOUND, + X_DISABLED, + X_ENABLED, + X_DEFOBJ_BITS, + X_DEFOBJ_OR, + X_DEFOBJ_AND, + X_CONDENG, + X_CONDRND, + X_CONDREF_BEG = -0x2000, + X_CONDREF_END = X_CONDREF_BEG + 0xfff, + + CH_DISABLED = '-', + CH_HIDDEN = '=', + CH_ENABLED = '+', + CH_ENGINE = 'e', + CH_RANDOM = 'r', + CH_DEFOBJ_BITS = 'b', + CH_DEFOBJ_OR = 'o', + CH_DEFOBJ_AND = 'a', + CH_SEP = ' ' +}; + +//----------------------------------------------------------------------------- + +static int sort_apf_name + ( const kmp_ana_pflag_res_t * a, const kmp_ana_pflag_res_t * b ) +{ + return strcasecmp(a->name,b->name); +} + +//----------------------------------------------------------------------------- + + +static int sort_apf_index ( const kmp_ana_pflag_res_t * a, const kmp_ana_pflag_res_t * b ) +{ + int stat = (int)a->version - (int)b->version; + return stat ? stat : strcasecmp(a->name,b->name);; +} + +//----------------------------------------------------------------------------- + +static void analyze_helper +( + kmp_ana_pflag_t *ap, // valid struct + const s16 *ref_list, // list with 'ap->ag.n_gobj' elements + char *res, // vector with 'ap->status_len+1' elements + + int gamemode, // selected gamemode + ccp prefix, // gamemode related prefix to use + const xpf_bits_t *xpf, // selected condition + int engine, // <0: off, 0..: select engine + uint random // 0: off, 1-GOBJ_MAX_RANDOM: select random scenario +) +{ + char *res_ptr = res; + const bool is_std = gamemode == KMP_GMODE_STD; + const bool is_tt = gamemode == KMP_GMODE_VERSUS && xpf->bitnum == 14; + + if ( is_std ) + { + if ( random > 1 || engine != -1 && engine != ENGINE_150 ) + return; + engine = -1; + random = 0; + } + else if ( is_tt ) + { + if ( random > 1 || engine != -1 && engine != ENGINE_150 ) + return; + engine = ENGINE_150; + random = 1; + } + else if ( gamemode == KMP_GMODE_BALLOON || gamemode == KMP_GMODE_COIN ) + { + if ( engine != -1 && engine != ENGINE_BATTLE ) + return; + } + else + { + if ( engine == ENGINE_BATTLE ) + return; + } + + if ( ap->opt_gamemode & GMD_DUMP ) + { + printf("gm %2d, bit %2d, engine %2d, random %d\n", + gamemode, xpf->bitnum, engine, random ); + return; + } + + gobj_cond_mask_t cmask = {0}; + cmask.bit_mask = xpf->mask; + cmask.game_mask = 1 << gamemode; + cmask.engine_mask = 1 << ( engine < 0 ? ENGINE_150 : engine ); + cmask.random_mask = 1 << ( random ? random-1 : 0 ); + + int i; + const kmp_gobj_entry_t *gobj; + for ( i = 0, gobj = ap->ag.gobj; i < ap->ag.n_gobj; i++, gobj++ ) + { + u16 obj_id = gobj->obj_id; + u16 pflags = gobj->pflags; + char stat; + + if ( ref_list[i] == X_DEFOBJ_BITS ) + stat = CH_DEFOBJ_BITS; + else if ( ref_list[i] == X_DEFOBJ_OR ) + stat = CH_DEFOBJ_OR; + else if ( ref_list[i] == X_DEFOBJ_AND ) + stat = CH_DEFOBJ_AND; + else if (is_std) + stat = xpf->pflag & pflags ? CH_ENABLED : CH_HIDDEN; + else + { + stat = CH_DISABLED; + switch(ref_list[i]) + { + case X_INVALID_MODE: + case X_INVALID_REF: + stat = xpf->pflag & pflags ? CH_ENABLED : CH_HIDDEN; + break; + + case X_INVALID_CONDREF: + case X_NOTFOUND: + case X_DISABLED: + obj_id = 0; + break; + + case X_ENABLED: + stat = CH_ENABLED; + obj_id = obj_id & ~GOBJ_M_LECODE; + break; + + case X_CONDENG: + if ( engine < 0 ) + { + stat = CH_ENGINE; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + else if ( gobj->ref_id & cmask.engine_mask ) + { + stat = CH_ENABLED; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + break; + + case X_CONDRND: + if ( !random ) + { + stat = CH_RANDOM; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + else if ( gobj->ref_id & cmask.random_mask ) + { + stat = CH_ENABLED; + obj_id = obj_id & ~GOBJ_M_LECODE; + } + break; + + default: + if ( ref_list[i] >= X_CONDREF_BEG && ref_list[i] <= X_CONDREF_END ) + { + const gobj_cond_ref_t *cref + = cond_ref_tab + ( ref_list[i] - X_CONDREF_BEG ); + if (IsConditionRefEnabled(&cmask,gobj->ref_id,cref)) + stat = CH_ENABLED; + } + else + { + bool enabled = false; + const kmp_gobj_entry_t *dob = ap->ag.gobj + ref_list[i]; + if ( !dob->ref_id || IsConditionRefEnabled(&cmask,dob->ref_id,0) ) + { + if (IsDefinitionObjectIdBITS(dob->obj_id)) + { + if ( dob->setting[gamemode] & cmask.bit_mask ) + enabled = true; + } + else if (IsDefinitionObjectIdOR(dob->obj_id)) + { + uint i; + for ( i = 0; i < 8; i++ ) + if ( dob->setting[i] + && IsConditionRefEnabled(&cmask,dob->setting[i],0) ) + { + enabled = true; + break; + } + } + else if (IsDefinitionObjectIdAND(dob->obj_id)) + { + enabled = true; + uint i; + for ( i = 0; i < 8; i++ ) + if ( dob->setting[i] + && !IsConditionRefEnabled(&cmask,dob->setting[i],0) ) + { + enabled = false; + break; + } + } + } + + if ( gobj->ref_id & GOBJ_DEF_F_NOT ) + enabled = !enabled; + if (enabled) + stat = CH_ENABLED; + } + if ( stat == CH_ENABLED || stat == CH_RANDOM ) + obj_id = obj_id & ~GOBJ_M_LECODE; + break; + } + } + + if ( stat != CH_DEFOBJ_BITS && stat != CH_DEFOBJ_OR && stat != CH_DEFOBJ_AND + && (obj_id >= N_KMP_GOBJ || !ObjectInfo[obj_id].name )) + { + stat = CH_DISABLED; + } + + #if SUPPORT_SEP + if ( res_ptr > res + && ( gobj->pflags & 0x100 || ap->auto_sep && !(i%5) )) + { + *res_ptr++ = CH_SEP; + } + #endif + *res_ptr++ = stat; + DASSERT( res_ptr <= res + ap->status_len ); + } + ASSERT( res_ptr <= res + ap->status_len ); + + int ri, found_ri = -1; + for ( ri = 0; ri < ap->n_res; ri++ ) + if (!memcmp(ap->res_list[ri].status,res,ap->status_len)) + { + found_ri = ri; + kmp_ana_pflag_res_t *found = ap->res_list + ri; + if ( is_std && !found->n_std++ ) + { + ap->n_res_std++; + StringCat2S(found->name,sizeof(found->name),found->name,"+"); + } + if ( !is_std && !found->n_ext++ ) + { + ap->n_res_ext++; + StringCat2S(found->name,sizeof(found->name),found->name,"+"); + } + break; + } + + if ( found_ri < 0 + || ap->opt_gamemode & GMD_FULL + || is_tt && ap->opt_gamemode & GMD_FORCE_TT + ) + { + kmp_ana_pflag_res_t *pr = AppendPFlagScenario(ap); + kmp_ana_pflag_res_t *found = found_ri < 0 ? 0 : ap->res_list + found_ri; + ccp pre = xpf->no_prefix || !prefix ? "" : prefix; + pr->status = ALLOCDUP(res,ap->status_len+1); + pr->version = found ? found->version : ap->n_version++; + + memcpy(&pr->lex_test,TestDataLEX,sizeof(pr->lex_test)); + char *name_end = pr->name + sizeof(pr->name); + char *dest = snprintfE(pr->name,name_end,"%s%s",pre,xpf->name); + + if ( engine > 0 ) + { + static const char tab[][5] = { "bat","50c","100c","150c","200c","150m","200m" }; + dest = StringCat2E(dest,name_end,",",tab[engine]); + pr->lex_test.engine = engine; + } + + if ( random && !is_tt ) + { + dest = snprintfE(dest,name_end,",r%u",random); + pr->lex_test.random = random; + } + + pr->lex_test.offline_online = xpf->n_online > 0 + ? LEX_OO_ONLINE : LEX_OO_OFFLINE; + pr->lex_test.n_online = xpf->n_online; + pr->lex_test.n_offline = xpf->n_offline; + if (is_std) + { + pr->lex_test.cond_bit = -1; + pr->lex_test.game_mode = LEX_GMODE_STD; + } + else + { + pr->lex_test.cond_bit = xpf->bitnum; + pr->lex_test.game_mode = gamemode + LEX_GMODE__BASE; + } + + if (found) + { + if ( !is_std && !found->n_ext++ ) + ap->n_res_ext++; + } + else if ( is_std && !pr->n_std++ ) + ap->n_res_std++; + else if ( !is_std && !pr->n_ext++ ) + ap->n_res_ext++; + } +} + +//----------------------------------------------------------------------------- + +void AnalysePFlagScenarios + ( kmp_ana_pflag_t *ap, const kmp_t *kmp, opt_gamemode_t opt_gamemode ) +{ + DASSERT(ap); + DASSERT(kmp); + + const u_usec_t start_time = GetTimerUSec(); + + memset(ap,0,sizeof(*ap)); + AnalyseGobj(&ap->ag,kmp); + ap->opt_gamemode = opt_gamemode; + + struct job_t + { + char prefix[4]; + int gamemode; + int opt_gmd; + }; + + static const struct job_t race_job_list[] = + { + { "std",KMP_GMODE_STD, GMD_STD }, + { "V", KMP_GMODE_VERSUS, GMD_VERSUS }, + { "I", KMP_GMODE_ITEMRAIN, GMD_ITEMRAIN }, + { "B", KMP_GMODE_BALLOON, GMD_BALLOON }, + { "C", KMP_GMODE_COIN, GMD_COIN }, + { "std",KMP_GMODE_STD, GMD_STD }, + { "",-1,0} + }; + + static const struct job_t arena_job_list[] = + { + { "std",KMP_GMODE_STD, GMD_STD }, + { "B", KMP_GMODE_BALLOON, GMD_BALLOON }, + { "C", KMP_GMODE_COIN, GMD_COIN }, + { "V", KMP_GMODE_VERSUS, GMD_VERSUS }, + { "I", KMP_GMODE_ITEMRAIN, GMD_ITEMRAIN }, + { "std",KMP_GMODE_STD, GMD_STD }, + { "",-1,0} + }; + + static const struct job_t auto_job_list[] = + { + { "std",KMP_GMODE_STD, GMD_STD }, + { "a", KMP_GMODE_AUTO, GMD_NO_MODES }, + { "std",KMP_GMODE_STD, GMD_STD }, + { "",-1,0} + }; + + + //--- create reference list for defobj for faster analysis + + #if SUPPORT_SEP + uint n_sep = 0; + #endif + s16 *ref_list = MALLOC(ap->ag.n_gobj*sizeof(*ref_list)); + uint i; + const kmp_gobj_entry_t *gobj; + for ( i = 0, gobj = ap->ag.gobj; i < ap->ag.n_gobj; i++, gobj++ ) + { + #if SUPPORT_SEP + if (gobj->pflags&0x100) + n_sep++; + #endif + if (IsDefinitionObjectIdBITS(gobj->obj_id)) + ref_list[i] = X_DEFOBJ_BITS; + else if (IsDefinitionObjectIdOR(gobj->obj_id)) + ref_list[i] = X_DEFOBJ_OR; + else if (IsDefinitionObjectIdAND(gobj->obj_id)) + ref_list[i] = X_DEFOBJ_AND; + else if ( GetPFlagsMode(gobj->pflags) != 1 ) + ref_list[i] = X_INVALID_MODE; + else if (IsConditionRefId(gobj->ref_id)) + { + if (IsConditionRefEngineId(gobj->ref_id)) + { + const uint engine = gobj->ref_id & GOBJ_MASK_COND_ENGINE; + if (!engine) + ref_list[i] = X_DISABLED; + else if ( engine == GOBJ_MASK_COND_ENGINE ) + ref_list[i] = X_ENABLED; + else + ref_list[i] = X_CONDENG; + } + else if (IsConditionRefRandomId(gobj->ref_id)) + { + const uint rnd = gobj->ref_id & GOBJ_MASK_COND_RND; + if (!rnd) + ref_list[i] = X_DISABLED; + else if ( rnd == GOBJ_MASK_COND_RND ) + ref_list[i] = X_ENABLED; + else + ref_list[i] = X_CONDRND; + } + else + { + const gobj_cond_ref_t *cr = FindConditionRef(gobj->ref_id); + ref_list[i] = cr ? X_CONDREF_BEG + cr-cond_ref_tab : X_INVALID_CONDREF; + } + } + else if (!IsDefinitionObjectId(gobj->ref_id)) + ref_list[i] = X_INVALID_REF; + else + { + const kmp_gobj_entry_t *found = FindRefObject(kmp,gobj); + ref_list[i] = found ? found - ap->ag.gobj : X_NOTFOUND; + } + } + + #if defined(TEST) || defined(HAVE_WIIMM_EXT) + if ( logging > 1 ) + { + XDump_t xd; + InitializeXDump(&xd); + xd.print_format = false; + xd.print_summary = false; + xd.print_text = false; + xd.format = XDUMPF_INT_2; + xd.min_width = 20; + XDump(&xd,ref_list,ap->ag.n_gobj*sizeof(*ref_list),true); + putchar('\n'); + } + #endif + + + //--- add "auto"? + + if ( opt_gamemode & (GMD_AUTO|GMD_FULL) ) + { + kmp_ana_pflag_res_t *pr = AppendPFlagScenario(ap); + StringCopyS(pr->name,sizeof(pr->name),"auto"); + pr->status = EmptyString; + memcpy(&pr->lex_test,TestDataLEX,sizeof(pr->lex_test)); + } + + + //--- separator + + #if SUPPORT_SEP + ap->auto_sep = !n_sep; + if (ap->auto_sep) + n_sep = ap->ag.n_gobj/5; + ap->status_len = ap->ag.n_gobj + n_sep; + #else + ap->status_len = ap->ag.n_gobj; + #endif + char *res = CALLOC(1,ap->status_len+1); + + + //--- engine & random + + int engine_beg, engine_end; + if ( ap->ag.n_engine && ( opt_gamemode & GMD_ENGINE )) + { + engine_beg = ENGINE_BATTLE; + engine_end = ENGINE__N-1; + } + else + engine_beg = engine_end = -1; + + int random_beg, random_end; + if ( ap->ag.n_random && ( opt_gamemode & GMD_RANDOM )) + { + random_beg = 1; + random_end = GOBJ_MAX_RANDOM; + } + else + random_beg = random_end = 0; + + + //--- main loop + + const struct job_t *job + = (opt_gamemode&GMD_NO_MODES) + ? auto_job_list + : CheckBattleModeKMP(kmp) >= ARENA_FOUND + ? arena_job_list + : race_job_list; + if (!(opt_gamemode&GMD_STD_FIRST)) + job++; + + for ( ; job->gamemode >= 0; job++ ) + { + if ( !( opt_gamemode & job->opt_gmd ) ) + continue; + + const xpf_bits_t *xpf; + for ( xpf = xpf_bits; xpf->mask; xpf++ ) + { + if ( job->gamemode == KMP_GMODE_AUTO ) + goto skip; + + if ( !( 1 << job->gamemode & xpf->relevant )) + continue; + + // optimized handling for time trial + const bool is_tt = job->gamemode == KMP_GMODE_VERSUS && xpf->bitnum == 14; + if (is_tt) + { + if ( opt_gamemode & GMD_NO_TT ) + continue; + if ( opt_gamemode & GMD_FORCE_TT ) + analyze_helper(ap,ref_list,res, job->gamemode,job->prefix,xpf,-1,0); + } + + if ( !(opt_gamemode & ( xpf->n_online ? GMD_ONLINE : GMD_OFFLINE )) ) + continue; + + if (is_tt) + { + analyze_helper(ap,ref_list,res, job->gamemode,job->prefix,xpf,-1,0); + continue; + } + + skip:; + int e; + for ( e = engine_beg; e <= engine_end; e++ ) + { + int r; + for ( r = random_beg; r <= random_end; r++ ) + analyze_helper(ap,ref_list,res, job->gamemode,job->prefix,xpf,e,r); + } + } // for xpf + } // for gmode + + if ( ap->n_res > 1 ) + { + if ( opt_gamemode & GMD_ISORT ) + qsort( ap->res_list, ap->n_res, sizeof(*ap->res_list), + (qsort_func)sort_apf_index ); + else if ( opt_gamemode & GMD_NSORT ) + qsort( ap->res_list, ap->n_res, sizeof(*ap->res_list), + (qsort_func)sort_apf_name ); + } + + FREE(res); + FREE(ref_list); + + ap->dur_usec = GetTimerUSec() - start_time; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: repair: global helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp repair_xpf_fname = 0; +static kmp_t *repair_kmp = 0; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRepairXPF ( ccp arg ) +{ + if (repair_xpf_fname) + have_patch_count--, have_kmp_patch_count--; + + repair_xpf_fname = arg && *arg ? arg : 0; + + if (repair_xpf_fname) + have_patch_count++, have_kmp_patch_count++; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +xpf_mode1_t CheckMode1GOBJ ( const kmp_gobj_entry_t *g ) +{ + DASSERT(g); + if (( g->pflags & GOBJ_M_PF_MODE ) != GOBJ_PF_MODE1 ) + return XMODE1_NONE; + + return IsConditionRefId(g->ref_id) ? XMODE1_CONDREF + : IsDefinitionObjectId(g->ref_id) ? XMODE1_OBJREF + : g->ref_id ? XMODE1_INVALID : XMODE1_NULL; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: repair /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[xpf_mode1_elem_t]] + +typedef struct xpf_mode1_elem_t +{ + int index; // index of element wihiin list + xpf_mode1_t mode; // mode + kmp_gobj_entry_t *obj; // pointer to object + kmp_gobj_entry_t norm; // copy of 'obj' with normalized data + + int eval; // evaluation + double diff_sum; // sum of float differences + struct xpf_mode1_elem_t + *hit; // best element found + int match_id; // for logging only: identify matching pair +} +xpf_mode1_elem_t; + +//----------------------------------------------------------------------------- +// [[xpf_mode1_info_t]] + +typedef struct xpf_mode1_info_t +{ + uint broken; // number of broken elements in 'list' + uint used; // number of used elements in 'list' + uint size; // number of available elements in 'list' + xpf_mode1_elem_t list[]; // list of elements +} +xpf_mode1_info_t; + +//----------------------------------------------------------------------------- + +static xpf_mode1_info_t * CreateXpfModelInfo ( const List_t *list ) +{ + DASSERT(list); + uint i, n = 0; + kmp_gobj_entry_t *obj; + + for ( i = 0, obj = (kmp_gobj_entry_t*)list->list; i < list->used; i++, obj++ ) + { + const xpf_mode1_t mode = CheckMode1GOBJ(obj); + if ( mode > XMODE1_NONE ) + n++; + PRINT0("%2d/%d: %u %04x %04x %04x\n", + i, list->used, n, obj->obj_id, obj->ref_id, obj->pflags ); + } + + if (!n) + return 0; + + const uint mem_size = sizeof(xpf_mode1_info_t) + n * sizeof(xpf_mode1_elem_t); + xpf_mode1_info_t *res = CALLOC(1,mem_size); + res->size = res->used = n; + + xpf_mode1_elem_t *elem = res->list; + for ( i = 0, obj = (kmp_gobj_entry_t*)list->list; i < list->used; i++, obj++ ) + { + const xpf_mode1_t mode = CheckMode1GOBJ(obj); + if ( mode > XMODE1_NONE ) + { + elem->index = i; + elem->obj = obj; + elem->mode = mode; + if ( mode <= XMODE1_INVALID ) + res->broken++; + memcpy(&elem->norm,obj,sizeof(elem->norm)); + elem->norm.obj_id &= GOBJ_M_OBJECT; + elem->norm.ref_id = 0; + elem->norm.pflags = 0; + elem++; + } + } + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void LogPrintElement ( xpf_mode1_elem_t *e1, xpf_mode1_elem_t *e2 ) +{ + if ( e2 && e2->hit == e1 ) + fprintf(stdlog," : %s%3d. %s%04x %s%04x %s%04x%s", + e1->index == e2->index ? collog->b_green : collog->b_red, e1->index, + e1->obj->obj_id == e2->obj->obj_id ? collog->b_green + : collog->b_red, e1->obj->obj_id, + e1->obj->ref_id == e2->obj->ref_id ? collog->b_green + : collog->b_red, e1->obj->ref_id, + e1->obj->pflags == e2->obj->pflags ? collog->b_green + : collog->b_red, e1->obj->pflags, + collog->reset ); + else + fprintf(stdlog," : %3d. %04x %04x %04x", + e1->index, e1->obj->obj_id, e1->obj->ref_id, e1->obj->pflags ); +} + +//----------------------------------------------------------------------------- + +static void LogListXPF + ( xpf_mode1_info_t *info, int index, ccp text, int loop, int update_eval ) +{ + int i, count = 0, match_id = 0; + xpf_mode1_elem_t *e; + for ( i = 0, e = info->list; i < info->used; i++, e++ ) + { + if ( e->eval >= 0 ) + { + if (!count++) + fprintf(stdlog,"\nRepair X-PFlags, loop %u, %s:\n",loop,text); + + fprintf(stdlog,"%12.3g %6d", e->diff_sum, e->eval ); + if ( e->eval == update_eval ) + { + LogPrintElement(e,e->hit); + LogPrintElement(e->hit,e); + + if ( index == 1 ) + { + e->match_id = ++match_id; + fprintf(stdlog," [%u]",match_id); + } + else + fprintf(stdlog," [%u]",e->hit->match_id); + } + else + LogPrintElement(e,0); + fputc('\n',stdlog); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint DiffXPF ( xpf_mode1_info_t *l1, xpf_mode1_info_t *l2, int loop ) +{ + DASSERT(l1); + DASSERT(l2); + + //--- setup + + uint i1, i2; + xpf_mode1_elem_t *e1, *e2; + + // clear second list + for ( i2 = 0, e2 = l2->list; i2 < l2->used; i2++, e2++ ) + if ( e2->eval > 0 ) + e2->eval = 0; + + + //--- compare each with each + + int max_eval = 0; + for ( i1 = 0, e1 = l1->list; i1 < l1->used; i1++, e1++ ) + { + if ( e1->eval < 0 ) + continue; + + for ( i2 = 0, e2 = l2->list; i2 < l2->used; i2++, e2++ ) + { + if ( e2->eval < 0 ) + continue; + + int eval = 0; + double diff_sum = 0.0; + + const kmp_gobj_entry_t *o1 = e1->obj; + const kmp_gobj_entry_t *o2 = e2->obj; + + if (!memcmp(o1,o2,sizeof(*o1))) + eval = 990000; + else if (!memcmp(&e1->norm,&e2->norm,sizeof(e1->norm))) + { + eval = 950000; + if ( o1->obj_id == o2->obj_id ) eval += 2000; + if ( o1->pflags == o2->pflags ) eval += 1000; + } + else + { + diff_sum = Length2F(o1->position,o2->position) + + Length2F(o1->rotation,o2->rotation) + + Length2F(o1->scale,o2->scale); + + if ( o1->obj_id == o2->obj_id ) + eval = 400000; + else if ( (o1->obj_id&GOBJ_M_PF_STD) == (o2->obj_id&GOBJ_M_PF_STD) ) + eval = 200000; + + if ( o1->pflags == o2->pflags ) + eval += 100000; + + uint i; + for ( i = 0; i < 8; i++ ) + if ( o1->setting[i] == o2->setting[i] ) + eval += 10000; + + diff_sum = Length2F(o1->position,o2->position) + + Length2F(o1->rotation,o2->rotation) + + Length2F(o1->scale,o2->scale); + + if ( diff_sum > 1.0 ) + { + int exp; + frexpl(diff_sum,&exp); + eval -= exp; + } + } + + eval += 999 - 3*abs( e1->index - e2->index ); + + if ( e1->eval < eval || e1->eval == eval && e1->diff_sum > diff_sum ) + { + e1->eval = eval; + e1->diff_sum = diff_sum; + e1->hit = e2; + } + + if ( e2->eval < eval || e2->eval == eval && e2->diff_sum > diff_sum ) + { + e2->eval = eval; + e2->diff_sum = diff_sum; + e2->hit = e1; + } + + if ( max_eval < eval ) + max_eval = eval; + + } + } + + + //--- logging + + if ( logging > 1 ) + { + LogListXPF(l1,1,"patching list",loop,max_eval); + LogListXPF(l2,2,"reference list",loop,max_eval); + } + + + //--- update + + uint update_count = 0; + if ( max_eval > 0 ) + { + for ( i1 = 0, e1 = l1->list; i1 < l1->used; i1++, e1++ ) + { + if ( e1->eval == max_eval ) + { + e2 = e1->hit; + if ( e2->eval == max_eval && e2->hit == e1 ) + { + if (!e1->obj->ref_id) + e1->obj->ref_id = e2->obj->ref_id; + e1->eval = e2->eval = -1; + update_count++; + } + } + } + } + + return update_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError RepairXPF ( kmp_t *kmp ) +{ + if (!repair_kmp) + { + if (!repair_xpf_fname) + return ERR_NOTHING_TO_DO; + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,repair_xpf_fname,"course.kmp",false,0); + repair_xpf_fname = 0; + if (err) + { + ResetRawData(&raw); + return err; + } + + disable_patch_on_load++; + repair_kmp = MALLOC(sizeof(*repair_kmp)); + err = ScanRawDataKMP(repair_kmp,true,&raw,0); + disable_patch_on_load--; + if ( err > ERR_WARNING ) + { + ResetKMP(repair_kmp); + FREE(repair_kmp); + repair_kmp = 0; + return err; + } + } + + + //--- setup lists + + xpf_mode1_info_t *rinfo = CreateXpfModelInfo( repair_kmp->dlist + KMP_GOBJ ); + xpf_mode1_info_t *kinfo = CreateXpfModelInfo( kmp->dlist + KMP_GOBJ ); + + if ( !rinfo || !kinfo || !kinfo->broken ) + { + FREE(rinfo); + FREE(kinfo); + return ERR_NOTHING_TO_DO; + } + + PRINT0("REPAIR XPF! rep=%d/%d, kmp=%d/%d\n", + rinfo->broken, rinfo->used, kinfo->broken, kinfo->used ); + + int loop = 1; + while (DiffXPF(kinfo,rinfo,loop)) + loop++; + + if ( logging > 1 ) + fputc('\n',stdlog); + + FREE(rinfo); + FREE(kinfo); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-kmp-text.c b/project/src/lib-kmp-text.c new file mode 100644 index 0000000..c30c57b --- /dev/null +++ b/project/src/lib-kmp-text.c @@ -0,0 +1,7192 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kmp.h" +#include "lib-kcl.h" +#include "lib-szs.h" +#include "lib-bzip2.h" +#include "kmp.inc" + +#include +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cond_ref table /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const gobj_cond_ref_t cond_ref_tab[COR__N+1] = +{ + { COR_NEVER, "NEVER", "", 0,"never", 0x0000,0x00,0 }, + { COR_LECODE, "LECODE", "P1234",2,"lecode", 0xffff,0xff,0 }, + { COR_SINGLE, "SINGLE", "P1", 2,"1 player @Wii", 0x5551,0x00,0 }, + { COR_MULTI, "MULTI", "P234", 2,"2-4 players @Wii", 0xaaae,0x00,0 }, + { COR_OFFLINE, "OFFLINE", "OFF", 2,"offline", 0x400f,0x00,0 }, + { COR_ONLINE, "ONLINE", "ON", 2,"online", 0xbff0,0x00,0 }, + + { COR_LE6, "LE6", "1_6", 3,"≤6 players", 0xc03f,0x00,0 }, + { COR_GE7, "GE7", "7_99", 2,"≥7 players", 0x3fc0,0x00,0 }, + { COR_LE9, "LE9", "1_9", 2,"≤9 players", 0xc0ff,0x00,0 }, + { COR_GE10, "GE10", "10_99",2,"≥10 players", 0x3f00,0x00,0 }, + { COR_LE12, "LE12", "1_12", 2,"≤12 players", 0xc3ff,0x00,0 }, + { COR_GE13, "GE13", "13_99",2,"≥13 players", 0x3c00,0x00,0 }, + { COR_LE18, "LE18", "1_18", 2,"≤18 players", 0xcfff,0x00,0 }, + { COR_GE19, "GE19", "19_99",2,"≥19 players", 0x3000,0x00,0 }, + + { COR_7_9, "7_9", "", 3,"7-9 players", 0x00c0,0x00,0 }, + { COR_NOT_7_9, "NOT_7_9", "", 2,"<7 or >9 players", 0xff3f,0x00,0 }, + { COR_7_12, "7_12", "", 2,"7-12 players", 0x03c0,0x00,0 }, + { COR_NOT_7_12, "NOT_7_12", "", 2,"<7 or >12 players", 0xfc3f,0x00,0 }, + { COR_7_18, "7_18", "", 2,"7-18 players", 0x0fc0,0x00,0 }, + { COR_NOT_7_18, "NOT_7_18", "", 2,"<7 or >18 players", 0xf03f,0x00,0 }, + { COR_10_12, "10_12", "", 2,"10-12 players", 0x0300,0x00,0 }, + { COR_NOT_10_12, "NOT_10_12", "", 2,"<10 or >12 players", 0xfcff,0x00,0 }, + { COR_10_18, "10_18", "", 2,"10-18 players", 0x0f00,0x00,0 }, + { COR_NOT_10_18, "NOT_10_18", "", 2,"<10 or >18 players", 0xf0ff,0x00,0 }, + { COR_13_18, "13_18", "", 2,"13-18 players", 0x0c00,0x00,0 }, + { COR_NOT_13_18, "NOT_13_18", "", 2,"<13 or >18 players", 0xf3ff,0x00,0 }, + + { COR_TIMETRIAL, "TIMETRIAL", "TT", 3,"time trial", 0x4000,0x04,1 }, + { COR_NO_TIMETRIAL, "NOT_TIMETRIAL","NOTT", 2,"not time trial", 0xbfff,0xfb,0 }, + { COR_BATTLE, "BATTLE", "", 2,"battle", 0x0000,0x03,0 }, + { COR_RACE, "RACE", "", 2,"race", 0x0000,0xfc,0 }, + + { COR_BALLOON, "BALLOON", "", 3,"balloon", 0x0000,0x01,0 }, + { COR_NO_BALLOON, "NOT_BALLOON", "", 2,"not balloon", 0x0000,0xfe,0 }, + { COR_COIN, "COIN", "", 2,"coin", 0x0000,0x02,0 }, + { COR_NO_COIN, "NOT_COIN", "", 2,"not coin", 0x0000,0xfd,0 }, + { COR_VERSUS, "VERSUS", "", 2,"versus", 0x0000,0x04,0 }, + { COR_NO_VERSUS, "NOT_VERSUS", "", 2,"not versus", 0x0000,0xfb,0 }, + { COR_ITEMRAIN, "ITEMRAIN", "", 2,"item rain", 0x0000,0x08,0 }, + { COR_NO_ITEMRAIN, "NOT_ITEMRAIN", "", 2,"not item rain", 0x0000,0xf7,0 }, + { COR_SETTING5, "SETTING5", "", 2,"settings 5", 0x0000,0x10,0 }, + { COR_NO_SETTING5, "NOT_SETTING5", "", 2,"not setting 5", 0x0000,0xef,0 }, + { COR_SETTING6, "SETTING6", "", 2,"settings 6", 0x0000,0x20,0 }, + { COR_NO_SETTING6, "NOT_SETTING6", "", 2,"not setting 6", 0x0000,0xdf,0 }, + { COR_SETTING7, "SETTING7", "", 2,"settings 7", 0x0000,0x40,0 }, + { COR_NO_SETTING7, "NOT_SETTING7", "", 2,"not setting 7", 0x0000,0xbf,0 }, + { COR_SETTING8, "SETTING8", "", 2,"settings 8", 0x0000,0x80,0 }, + { COR_NO_SETTING8, "NOT_SETTING8", "", 2,"not setting 8", 0x0000,0x7f,0 }, + + { COR_P2, "P2", "", 3,"2 players @Wii", 0xaaa2,0x00,0 }, + { COR_P134, "P134", "", 2,"1|3|4 players @Wii", 0x555d,0x00,0 }, + { COR_P3, "P3", "", 2,"3 players @Wii", 0x0004,0x00,0 }, + { COR_P124, "P124", "", 2,"1|2|4 players @Wii", 0xfffb,0x00,0 }, + { COR_P4, "P4", "", 2,"4 players @Wii", 0x0008,0x00,0 }, + { COR_P123, "P123", "", 2,"1-3 players @Wii", 0xfff7,0x00,0 }, + { COR_P12, "P12", "", 2,"1-2 players @Wii", 0xfff3,0x00,0 }, + { COR_P34, "P34", "", 2,"3-4 players @Wii", 0x000c,0x00,0 }, + { COR_P13, "P13", "", 2,"1|3 players @Wii", 0x5555,0x00,0 }, + { COR_P24, "P24", "", 2,"2|4 players @Wii", 0xaaaa,0x00,0 }, + { COR_P14, "P14", "", 2,"1|4 players @Wii", 0x5559,0x00,0 }, + { COR_P23, "P23", "", 2,"2-3 players @Wii", 0xaaa6,0x00,0 }, + + { CORD_DEBUG, "DEBUG", "", 5,"if LE-CODE debug", 0x0001,0x00,2 }, + { CORD_RELEASE, "RELEASE", "", 4,"if LE-CODE release", 0x0002,0x00,2 }, + { CORD_TESTVAL, "TESTVAL", "", 4,"have test values", 0x0004,0x00,2 }, + { CORD_NO_TESTVAL, "NOT_TESTVAL", "", 4,"haven't test values",0x0008,0x00,2 }, + + { 0,"","",0,"",0,0,0 }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +const gobj_cond_ref_t * FindConditionRef ( u16 ref_id ) +{ + const gobj_cond_ref_t *rc = cond_ref_tab; + if ( ref_id < COR__N ) + { + const gobj_cond_ref_t *rc2 = cond_ref_tab + ref_id - 0x1000; + if ( rc2->ref_id <= ref_id ) + rc = rc2; + } + + for ( ; rc->ref_id; rc++ ) + if ( rc->ref_id == ref_id ) + return rc; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsConditionRefEnabled + ( const gobj_cond_mask_t *cm, u16 ref_id, const gobj_cond_ref_t *cref ) +{ + DASSERT(cm); + + //--- check predefinied condition for ENGINE + + if (IsConditionRefEngineId(ref_id)) + return ref_id & cm->engine_mask; + + + //--- check predefinied condition for RANDOM + + if (IsConditionRefRandomId(ref_id)) + return ref_id & cm->random_mask; + + + //--- check hard coded condition => search list + + if (IsConditionRefId(ref_id)) + { + if (!cref) + cref = FindConditionRef(ref_id); + if (cref) + { + switch (cref->op) + { + case 0: + return cref->cond_mask & cm->bit_mask + || cref->set_mask & cm->game_mask; + + case 1: + return cref->cond_mask & cm->bit_mask + && cref->set_mask & cm->game_mask; + } + } + return false; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GetConditionRefInfo + ( cond_ref_info_t *cri, u16 ref_id, u16 obj_id, bool create_cond ) +{ + DASSERT(cri); + memset(cri,0,sizeof(*cri)); + + if ( obj_id >= GOBJ_MIN_DEF || obj_id & GOBJ_M_LECODE ) + cri->cmd_suffix = "ENABLE"; + else + { + cri->cmd_suffix = "DISABLE"; + cri->xor = 1; + } + + if (IsConditionRefTestId(ref_id)) + { + cri->is_condref = true; + if (cri->xor) + { + cri->xor = 1; + ref_id ^= 1; + } + if (create_cond) + { + snprintf( cri->cond, sizeof(cri->cond), + "IF$TEST(%u,%u)", ref_id >> 6 & 15, ref_id >> 1 & 31 ); + StringCopyS(cri->info,sizeof(cri->info), + "Test condition for LE-CODE developers" ); + } + } + else if (IsConditionRefEngineId(ref_id)) + { + cri->is_condref = true; + if (cri->xor) + { + cri->xor = GOBJ_MASK_COND_ENGINE; + ref_id ^= GOBJ_MASK_COND_ENGINE; + } + if (create_cond) + { + char *end = cri->cond + sizeof(cri->cond) - 3; + char *dest = StringCopyE(cri->cond,end,"if$engine("); + static const char name[][7] + = { "battle", "50", "100", "150", "200", "150m", "200m", "" }; + + uint i, m = ref_id; + for ( i = 0; name[i][0]; i++, m >>= 1 ) + if ( m&1 ) + { + if (dest[-1] != '(' ) + *dest++ = ','; + dest = StringCat2E(dest,end,"en$",name[i]); + } + *dest++ = ')'; + *dest = 0; + + StringCopyS(cri->info,sizeof(cri->info), + "if selected engines are active" ); + } + } + else if (IsConditionRefRandomId(ref_id)) + { + cri->is_condref = true; + if (cri->xor) + { + cri->xor = GOBJ_MASK_COND_RND; + ref_id ^= GOBJ_MASK_COND_RND; + } + if (create_cond) + { + char *end = cri->cond + sizeof(cri->cond) - 4; + char *dest = StringCopyE(cri->cond,end,"if$random("); + uint i, m; + for ( i = m = 1; i <= GOBJ_MAX_RANDOM; i++, m <<= 1 ) + if ( ref_id & m && dest < end ) + { + if (dest[-1] != '(' ) + *dest++ = ','; + *dest++ = '0'+i; + } + *dest++ = ')'; + *dest = 0; + + StringCopyS(cri->info,sizeof(cri->info), + "if selected random scenario is active" ); + } + } + else + { + cri->condref = FindConditionRef(ref_id); + if (cri->condref) + { + cri->is_condref = true; + if (create_cond) + { + const gobj_cond_ref_t *iref = cri->xor + ? FindConditionRef(ref_id^cri->xor) + : cri->condref; + if (iref) + { + if ( iref->option & 4 ) + { + snprintf( cri->cond, sizeof(cri->cond), "IFDEV$%s",iref->name); + snprintf( cri->info, sizeof(cri->info), + "DEVELOPERS: %s", iref->info ); + } + else + { + snprintf( cri->cond, sizeof(cri->cond), "IF$%s",iref->name); + snprintf( cri->info, sizeof(cri->info), "%s%s", + iref->option & 1 ? "if " : "", + iref->info ); + } + } + } + } + else + cri->is_condref = IsConditionRefId(ref_id); + + if ( cri->is_condref && !*cri->info ) + snprintf( cri->cond, sizeof(cri->cond), "%#x",ref_id^cri->xor ); + } + + return cri->is_condref; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// link info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetRouteLinksKMP ( const kmp_t *ckmp ) +{ + DASSERT(ckmp); + kmp_t *kmp = (kmp_t*)ckmp; + if (kmp->linfo) + { + memset(kmp->linfo,0,sizeof(*kmp->linfo)); + kmp->linfo_sect = ~0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +kmp_linfo_t * SetupRouteLinksKMP ( const kmp_t *ckmp, uint sect, bool force ) +{ + DASSERT(ckmp); + kmp_t *kmp = (kmp_t*)ckmp; + if (!kmp->linfo) + { + kmp->linfo = MALLOC(sizeof(*kmp->linfo)); + kmp->linfo_sect = ~0; + } + + if ( force || kmp->linfo_sect != sect ) + { + kmp->linfo_sect = sect; + AnalyseRouteKMP(kmp->linfo,kmp,sect); + noPRINT("ROUTE INFO for section #%d [%s] => %u\n", + sect, kmp_section_name[sect].name1, kmp->linfo->used ); + } + return (kmp_linfo_t*) kmp->linfo; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint AnalyseRouteKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect ) +{ + DASSERT(li); + DASSERT(kmp); + + switch(sect) + { + case KMP_CKPH: + case KMP_ENPH: + case KMP_ITPH: + return AnalyseGroupLinksKMP(li,kmp,sect); + + case KMP_CKPT: + case KMP_ENPT: + case KMP_ITPT: + return AnalyseRouteLinksKMP(li,kmp,sect); + + default: + ASSERT_MSG(0,"AnalyseRouteKMP(): Unsupported KMP section: %u",sect); + memset(li,0,sizeof(*li)); + li->sect_pt = li->sect_ph = KMP_NO_SECT; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AnalyseGroupLinksKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_ph ) +{ + DASSERT(li); + DASSERT(kmp); + memset(li,0,sizeof(*li)); + + + //--- section dependent setup + + switch(sect_ph) + { + case KMP_CKPH: + case KMP_CKPT: + li->sect_pt = KMP_CKPT; + li->sect_ph = KMP_CKPH; + break; + + case KMP_ENPH: + case KMP_ENPT: + li->sect_pt = KMP_ENPT; + li->sect_ph = KMP_ENPH; + break; + + case KMP_ITPH: + case KMP_ITPT: + li->sect_pt = KMP_ITPT; + li->sect_ph = KMP_ITPH; + break; + + default: + ASSERT_MSG(0,"AnalyseGroupLinksKMP(): Unsupported KMP section: %u",sect_ph); + li->sect_pt = li->sect_ph = KMP_NO_SECT; + return 0; + } + + + //--- setup + + uint n = kmp->dlist[li->sect_ph].used; + if ( n > KMP_MAX_LINFO ) + n = KMP_MAX_LINFO; + + + //--- iterate groups + + uint i, max = 0; + const kmp_enph_entry_t * p = (kmp_enph_entry_t*)kmp->dlist[li->sect_ph].list; + + for ( i = 0; i < n; i++, p++ ) + { + li->n_point[i] = p->pt_len; + if (IsDispatchPointKMP(p)) + li->gmode[i] |= LINFOG_DISPATCH; + + uint l; + for ( l = 0; l < KMP_MAX_PH_LINK; l++ ) + { + u8 j = p->prev[l]; + if ( j != 0xff ) + { + li->summary[i] |= LINFO_PREV; + li->list[i][j] |= LINFO_PREV; + li->summary[j] |= LINFO_BY_PREV; + li->list[j][i] |= LINFO_BY_PREV; + if ( max <= i ) max = i+1; + if ( max <= j ) max = j+1; + } + + j = p->next[l]; + if ( j != 0xff ) + { + li->summary[i] |= LINFO_NEXT; + li->list[i][j] |= LINFO_NEXT; + li->summary[j] |= LINFO_BY_NEXT; + li->list[j][i] |= LINFO_BY_NEXT; + if ( max <= i ) max = i+1; + if ( max <= j ) max = j+1; + } + } + } + + noPRINT(">>> GMODE/group\n"); + //HEXDUMP16(0,0,li->gmode,max); + return li->used = max; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint AnalyseRouteLinksPH ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_ph ) +{ + DASSERT(li); + DASSERT(kmp); + memset(li,0,sizeof(*li)); + + + //--- section dependent setup + + switch(sect_ph) + { + case KMP_CKPH: + case KMP_CKPT: + li->sect_pt = KMP_CKPT; + li->sect_ph = KMP_CKPH; + break; + + case KMP_ENPH: + case KMP_ENPT: + li->sect_pt = KMP_ENPT; + li->sect_ph = KMP_ENPH; + break; + + case KMP_ITPH: + case KMP_ITPT: + li->sect_pt = KMP_ITPT; + li->sect_ph = KMP_ITPH; + break; + + default: + ASSERT_MSG(0,"AnalyseRouteLinksPH(): Unsupported KMP section: %u",sect_ph); + li->sect_pt = li->sect_ph = KMP_NO_SECT; + return 0; + } + + + //--- setup + + uint n = kmp->dlist[li->sect_ph].used; + if ( n > KMP_MAX_LINFO ) + n = KMP_MAX_LINFO; + + const kmp_enph_entry_t *ph_list = (kmp_enph_entry_t*)kmp->dlist[li->sect_ph].list; + + + //--- iterate groups + + uint i, max = 0; + const kmp_enph_entry_t *p = ph_list; + + for ( i = 0; i < n; i++, p++ ) + { + if (!p->pt_len) + continue; + + const uint iprev = p->pt_start; + const uint inext = p->pt_start + p->pt_len - 1; + + kmp_linfo_g_mode gmode = 0; + if (IsDispatchPointKMP(p)) + gmode |= LINFOG_DISPATCH; + uint pi; + for ( pi = iprev; pi <= inext; pi++ ) + { + li->n_point[pi] = p->pt_len; + li->gmode[pi] = gmode; + } + + uint l; + for ( l = 0; l < KMP_MAX_PH_LINK; l++ ) + { + u8 j = p->prev[l]; + if ( j != 0xff && j < n ) + { + const uint link = ph_list[j].pt_start + ph_list[j].pt_len - 1; + if ( link < KMP_MAX_LINFO ) + { + li->summary[iprev] |= LINFO_G_PREV; + li->list[iprev][link] |= LINFO_G_PREV; + li->summary[link] |= LINFO_G_BY_PREV; + li->list[link][iprev] |= LINFO_G_BY_PREV; + if ( max <= iprev ) max = iprev+1; + if ( max <= link ) max = link+1; + } + } + + if ( inext < KMP_MAX_LINFO ) + { + j = p->next[l]; + if ( j != 0xff && j < n ) + { + const uint link = ph_list[j].pt_start; + if ( link < KMP_MAX_LINFO ) + { + li->summary[inext] |= LINFO_G_NEXT; + li->list[inext][link] |= LINFO_G_NEXT; + li->summary[link] |= LINFO_G_BY_NEXT; + li->list[link][inext] |= LINFO_G_BY_NEXT; + if ( max <= inext ) max = inext+1; + if ( max <= link ) max = link+1; + } + } + } + } + } + + noPRINT(">>> GMODE/points\n"); + //HEXDUMP16(0,0,li->gmode,max); + + return li->used = max; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AnalyseRouteLinksKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_pt ) +{ + DASSERT(li); + DASSERT(kmp); + + + //--- section dependent setup + + switch(sect_pt) + { + case KMP_CKPT: + case KMP_CKPH: + return AnalyseRouteLinksCKPT(li,kmp); + break; + + case KMP_ENPT: + case KMP_ENPH: + case KMP_ITPT: + case KMP_ITPH: + break; + + default: + ASSERT_MSG(0,"AnalyseRouteLinksKMP(): Unsupported KMP section: %u",sect_pt); + memset(li,0,sizeof(*li)); + li->sect_pt = li->sect_ph = KMP_NO_SECT; + return 0; + } + + + //--- setup + + uint max = AnalyseRouteLinksPH(li,kmp,sect_pt); + + uint n = kmp->dlist[li->sect_pt].used; + if ( n > KMP_MAX_LINFO ) + n = KMP_MAX_LINFO; + if ( max < n ) + max = n; + + const kmp_enpt_entry_t *p = (kmp_enpt_entry_t*)kmp->dlist[li->sect_pt].list; + + + //--- iterate points + + uint i; + for ( i = 0; i < n; i++, p++ ) + { + if ( i > 0 && !( li->summary[i] & LINFO_G_PREV ) ) + { + li->summary[i] |= LINFO_D_PREV; + li->list[i][i-1] |= LINFO_D_PREV; + li->summary[i-1] |= LINFO_D_BY_PREV; + li->list[i-1][i] |= LINFO_D_BY_PREV; + } + + if ( i < KMP_MAX_LINFO-1 && !( li->summary[i] & LINFO_G_NEXT ) ) + { + li->summary[i] |= LINFO_D_NEXT; + li->list[i][i+1] |= LINFO_D_NEXT; + li->summary[i+1] |= LINFO_D_BY_NEXT; + li->list[i+1][i] |= LINFO_D_BY_NEXT; + } + } + + return li->used = max; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AnalyseRouteLinksCKPT ( kmp_linfo_t *li, const kmp_t *kmp ) +{ + DASSERT(li); + DASSERT(kmp); + memset(li,0,sizeof(*li)); + li->sect_pt = KMP_CKPT; + li->sect_ph = KMP_CKPH; + + + //--- setup + + uint max = AnalyseRouteLinksPH(li,kmp,KMP_CKPH); + + uint n = kmp->dlist[KMP_CKPT].used; + if ( n > KMP_MAX_LINFO ) + n = KMP_MAX_LINFO; + if ( max < n ) + max = n; + + const kmp_ckpt_entry_t *p = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + + + //--- iterate points + + uint i; + for ( i = 0; i < n; i++, p++ ) + { + uint j = p->prev; + if ( j != 0xff ) + { + li->summary[i] |= LINFO_D_PREV; + li->list[i][j] |= LINFO_D_PREV; + li->summary[j] |= LINFO_D_BY_PREV; + li->list[j][i] |= LINFO_D_BY_PREV; + } + + j = p->next; + if ( j != 0xff ) + { + li->summary[i] |= LINFO_D_NEXT; + li->list[i][j] |= LINFO_D_NEXT; + li->summary[j] |= LINFO_D_BY_NEXT; + li->list[j][i] |= LINFO_D_BY_NEXT; + } + } + + return li->used = max; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintLinkInfoMode ( kmp_linfo_mode mode, int print_group, bool adjust ) +{ + // print_group: -1=never, 0:auto, 1:always + char *buf = GetCircBuf(10); + char *dest = buf; + + if (adjust) + { + *dest++ = mode & LINFO_D_PREV ? 'P' : '-'; + *dest++ = mode & LINFO_D_NEXT ? 'N' : '-'; + *dest++ = mode & LINFO_D_BY_PREV ? 'p' : '-'; + *dest++ = mode & LINFO_D_BY_NEXT ? 'n' : '-'; + + if ( print_group > 0 || !print_group && mode&LINFO_M_G ) + { + *dest++ = mode & LINFO_G_PREV ? 'P' : '-'; + *dest++ = mode & LINFO_G_NEXT ? 'N' : '-'; + *dest++ = mode & LINFO_G_BY_PREV ? 'p' : '-'; + *dest++ = mode & LINFO_G_BY_NEXT ? 'n' : '-'; + } + } + else + { + if ( mode & LINFO_D_PREV ) *dest++ = 'P'; + if ( mode & LINFO_D_NEXT ) *dest++ = 'N'; + if ( mode & LINFO_D_BY_PREV ) *dest++ = 'p'; + if ( mode & LINFO_D_BY_NEXT ) *dest++ = 'n'; + + if ( print_group >= 0 && mode&LINFO_M_G ) + { + *dest++ = ','; + if ( mode & LINFO_G_PREV ) *dest++ = 'P'; + if ( mode & LINFO_G_NEXT ) *dest++ = 'N'; + if ( mode & LINFO_G_BY_PREV ) *dest++ = 'p'; + if ( mode & LINFO_G_BY_NEXT ) *dest++ = 'n'; + } + } + *dest = 0; + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp parser function get() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum // get mode +{ + GTMD_N, // number of elements + + GTMD_HD_U16, // head 'value' as u16 + GTMD_HD_U8HI, // hight byte of head 'value' + GTMD_HD_U8LO, // low byte of head 'value' + + GTMD__DATA, // separator of header info and data info + + GTMD_U8, // unsigned 8 bit + GTMD_U8M1, // unsigned 8 bit, but 0xff is -1 + GTMD_U16, // unsigned 16 bit + GTMD_U16M1, // unsigned 16 bit, but 0xffff is -1 + + GTMD_DBL, // double value + GTMD_DBL2, // 2d vector + GTMD_DBL3, // 3d double vector +}; + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_func_t]] + +#define POTI_PT_OFF 0x10 + +typedef struct kmp_func_t +{ + char name[18]; // variable name + u16 sect; // section + u16 off; // data offset, for POTI: off>POTI_PT_OFF: route point + u16 get_mode; // see enum above + u8 n_var; // number of variables + u8 n_param; // number of function parameters + ccp get_info; // NULL or function info for get function + ccp set_info; // NULL or function info for set function + +} kmp_func_t; + +/////////////////////////////////////////////////////////////////////////////// + +static const kmp_func_t kmp_func_list[] = +{ + { "KTPT$N", KMP_KTPT, 0, GTMD_N, 0,0, + "Returns the number of start points.", + 0 }, + { "KTPT$POS", KMP_KTPT, 0x00, GTMD_DBL3, 1,1, + "Returns a start point position.", + 0 }, + { "KTPT$ROT", KMP_KTPT, 0x0c, GTMD_DBL3, 1,1, + "Returns a start point rotation.", + 0 }, + { "KTPT$ID", KMP_KTPT, 0x18, GTMD_U16M1, 1,1, + "Returns a start point ID.", + 0 }, + { "KTPT$UNKNOWN", KMP_KTPT, 0x1a, GTMD_U16, 1,1, + "Returns the unknown value of a start point.", + 0 }, + + //--------------- + + { "ENPT$N", KMP_ENPT, 0, GTMD_N, 0,0, + "Returns the number of enemy points.", + 0 }, + { "ENPT$POS", KMP_ENPT, 0x00, GTMD_DBL3, 1,1, + "Returns the position of an enemy point.", + 0 }, + { "ENPT$SCALE", KMP_ENPT, 0x0c, GTMD_DBL, 1,1, + "Returns the scale of an enemy point.", + 0 }, + { "ENPT$PROP", KMP_ENPT, 0x10, GTMD_U16, 2,2, + "Returns property 0 or 1 of an enemy point.", + 0 }, + + //--------------- + + { "ENPH$N", KMP_ENPH, 0, GTMD_N, 0,0, + "Returns the number of enemy point groups.", + 0 }, + { "ENPH$START", KMP_ENPH, 0x00, GTMD_U8, 1,1, + "Returns the enemy point start index of an enemy point group.", + 0 }, + { "ENPH$LEN", KMP_ENPH, 0x01, GTMD_U8, 1,1, + "Returns the number of enemy points of an enemy point group.", + 0 }, + { "ENPH$PREV", KMP_ENPH, 0x02, GTMD_U8M1, KMP_MAX_PH_LINK,2, + "Returns link 0..5 to the previous enemy point group.", + 0 }, + { "ENPH$NEXT", KMP_ENPH, 0x08, GTMD_U8M1, KMP_MAX_PH_LINK,2, + "Returns link 0..5 to the next enemy point group.", + 0 }, + { "ENPH$UNKNOWN", KMP_ENPH, 0x0e, GTMD_U16, 1,1, + "Returns the unknown value of an enemy point group.", + 0 }, + + //--------------- + + { "ITPT$N", KMP_ITPT, 0, GTMD_N, 0,0, + "Returns the number of item points.", + 0 }, + { "ITPT$POS", KMP_ITPT, 0x00, GTMD_DBL3, 1,1, + "Returns the position of an item point.", + "Set the position of an item point" + " and returns 1 on success ans 0 , if failed." }, + { "ITPT$SCALE", KMP_ITPT, 0x0c, GTMD_DBL, 1,1, + "Returns the scale of an item point.", + 0 }, + { "ITPT$PROP", KMP_ITPT, 0x10, GTMD_U16, 2,2, + "Returns property 0 or 1 of an item point.", + 0 }, + + //--------------- + + { "ITPH$N", KMP_ITPH, 0, GTMD_N, 0,0, + "Returns the number of item point groups.", + 0 }, + { "ITPH$START", KMP_ITPH, 0x00, GTMD_U8, 1,1, + "Returns the item point start index of an item point group.", + 0 }, + { "ITPH$LEN", KMP_ITPH, 0x01, GTMD_U8, 1,1, + "Returns the number of item points of an item point group.", + 0 }, + { "ITPH$PREV", KMP_ITPH, 0x02, GTMD_U8M1, KMP_MAX_PH_LINK,2, + "Returns link 0..5 to the previous item point group.", + 0 }, + { "ITPH$NEXT", KMP_ITPH, 0x08, GTMD_U8M1, KMP_MAX_PH_LINK,2, + "Returns link 0..5 to the next item point group.", + 0 }, + { "ITPH$UNKNOWN", KMP_ITPH, 0x0e, GTMD_U16, 1,1, + "Returns the unknown value of an item point group.", + 0 }, + + //--------------- + + { "CKPT$N", KMP_CKPT, 0, GTMD_N, 0,0, + "Returns the number of check points.", + 0 }, + { "CKPT$LEFT", KMP_CKPT, 0x00, GTMD_DBL2, 1,1, + "Returns the left position of a check point.", + 0 }, + { "CKPT$RIGHT", KMP_CKPT, 0x08, GTMD_DBL2, 1,1, + "Returns the right position of a check point.", + 0 }, + { "CKPT$RESPAWN", KMP_CKPT, 0x10, GTMD_U8, 1,1, + "Returns the respawn index of a check point.", + 0 }, + { "CKPT$MODE", KMP_CKPT, 0x11, GTMD_U8M1, 1,1, + "Returns the mode of check point.", + 0 }, + { "CKPT$PREV", KMP_CKPT, 0x12, GTMD_U8M1, 1,1, + "Returns the index of the previous check point.", + 0 }, + { "CKPT$NEXT", KMP_CKPT, 0x13, GTMD_U8M1, 1,1, + "Returns the index of the next check point.", + 0 }, + + //--------------- + + { "CKPH$N", KMP_CKPH, 0, GTMD_N, 0,0, + "Returns the number of check point groups.", + 0 }, + { "CKPH$START", KMP_CKPH, 0x00, GTMD_U8, 1,1, + "Returns the check point start index of a check point group.", + 0 }, + { "CKPH$LEN", KMP_CKPH, 0x01, GTMD_U8, 1,1, + "Returns the number of check points of a check point group.", + 0 }, + { "CKPH$PREV", KMP_CKPH, 0x02, GTMD_U8M1, KMP_MAX_PH_LINK,2, + "Returns link 0..5 to the previous check point group.", + 0 }, + { "CKPH$NEXT", KMP_CKPH, 0x08, GTMD_U8M1, KMP_MAX_PH_LINK,2, + "Returns link 0..5 to the next check point group.", + 0 }, + { "CKPH$UNKNOWN", KMP_CKPH, 0x0e, GTMD_U16, 1,1, + "Returns the unknown value of a check point group.", + 0 }, + + //--------------- + + { "GOBJ$N", KMP_GOBJ, 0, GTMD_N, 0,0, + "Returns the number of global objects.", + 0 }, + { "GOBJ$ID", KMP_GOBJ, 0x00, GTMD_U16, 1,1, + "Returns the object ID of a global object.", + 0 }, + { "GOBJ$UNKNOWN", KMP_GOBJ, 0x02, GTMD_U16, 1,1, + "Returns the unknown value of a global object.", + 0 }, + { "GOBJ$POS", KMP_GOBJ, 0x04, GTMD_DBL3, 1,1, + "Returns the position of a global object.", + 0 }, + { "GOBJ$ROT", KMP_GOBJ, 0x10, GTMD_DBL3, 1,1, + "Returns the rotation of a global object.", + 0 }, + { "GOBJ$SCALE", KMP_GOBJ, 0x1c, GTMD_DBL3, 1,1, + "Returns the scale of a global object.", + 0 }, + { "GOBJ$ROUTE", KMP_GOBJ, 0x28, GTMD_U16M1, 1,1, + "Returns -1 or the index of a related route" + " of a global object.", + 0 }, + { "GOBJ$SET", KMP_GOBJ, 0x2a, GTMD_U16, 8,2, + "Returns setting 0..7 of a global object.", + 0 }, + { "GOBJ$PFLAGS", KMP_GOBJ, 0x3a, GTMD_U16, 1,1, + "Returns the presence flags of a global object.", + 0 }, + + //--------------- + + { "POTI$N", KMP_POTI, 0, GTMD_N, 0,0, + "Returns the number of routes.", + 0 }, + { "POTI$NN", KMP_POTI, 0, GTMD_HD_U16, 0,0, + "Returns the total number of points of all routes.", + 0 }, + { "POTI$NP", KMP_POTI, 0x00, GTMD_U8, 1,1, + "Returns the number of points of a route.", + 0 }, + { "POTI$SMOOTH", KMP_POTI, 0x02, GTMD_U8, 1,1, + "Returns the smooth value of a route" + " (route header byte at offset 0x02).", + 0 }, + { "POTI$BACK", KMP_POTI, 0x03, GTMD_U8, 1,1, + "Returns the forward+backward value of a route" + " (route header byte at offset 0x03).", + 0 }, + { "POTI$POS", KMP_POTI, 0x10, GTMD_DBL3, 1,2, // off+POTI_PT_OFF + "Returns the position of a point of a route." + " The point index is relative to the route.", + 0 }, + { "POTI$SET", KMP_POTI, 0x1c, GTMD_U16, 2,3, // off+POTI_PT_OFF + "Returns setting 0 or 1 of a point of a route." + " The point index is relative to the route.", + 0 }, + + //--------------- + + { "AREA$N", KMP_AREA, 0, GTMD_N, 0,0, + "Returns the number of areas.", + 0 }, + { "AREA$MODE", KMP_AREA, 0x00, GTMD_U8M1, 1,1, + "Returns the mode of an area.", + 0 }, + { "AREA$TYPE", KMP_AREA, 0x01, GTMD_U8M1, 1,1, + "Returns the type of an area. Cameras are type 0.", + 0 }, + { "AREA$DEST", KMP_AREA, 0x02, GTMD_U8M1, 1,1, + "Returns the destination index (e.g. camera index) of an area.", + 0 }, + { "AREA$UNKNOWN", KMP_AREA, 0x03, GTMD_U8, 1,1, + "Returns the unknown value of an area.", + 0 }, + { "AREA$POS", KMP_AREA, 0x04, GTMD_DBL3, 1,1, + "Returns the position of an area.", + 0 }, + { "AREA$ROT", KMP_AREA, 0x10, GTMD_DBL3, 1,1, + "Returns the rotation of an area.", + 0 }, + { "AREA$SCALE", KMP_AREA, 0x1c, GTMD_DBL3, 1,1, + "Returns the scale of an area.", + 0 }, + { "AREA$SET", KMP_AREA, 0x28, GTMD_U16, 4,2, + "Returns setting 0..3 of an area.", + 0 }, + + //--------------- + + { "CAME$N", KMP_CAME, 0, GTMD_N, 0,0, + "Returns the number of cameras.", + 0 }, + { "CAME$OCAM", KMP_CAME, 0, GTMD_HD_U8HI, 0,0, + "Returns the index of the opening camera.", + 0 }, + { "CAME$SCAM", KMP_CAME, 0, GTMD_HD_U8LO, 0,0, + "Returns the index of the selection camera.", + 0 }, + { "CAME$TYPE", KMP_CAME, 0x00, GTMD_U8, 1,1, + "Returns the type of a camera.", + 0 }, + { "CAME$NEXT", KMP_CAME, 0x01, GTMD_U8M1, 1,1, + "Returns -1 or the link to the next camera.", + 0 }, + { "CAME$UNKNOWN_02", KMP_CAME, 0x02, GTMD_U8, 1,1, + "Returns the unknown value at offser 0x02 of a camera.", + 0 }, + { "CAME$ROUTE", KMP_CAME, 0x03, GTMD_U8M1, 1,1, + "Returns -1 or the link to the related route.", + 0 }, + { "CAME$UNKNOWN_04", KMP_CAME, 0x04, GTMD_U16, 1,1, + "Returns the unknown value at offser 0x04 of a camera.", + 0 }, + { "CAME$ZOOM_SPEED", KMP_CAME, 0x06, GTMD_U16, 1,1, + "Returns the zoom speed in units per 100/60 sec.", + 0 }, + { "CAME$VIEWPT_SPEED", KMP_CAME, 0x08, GTMD_U16, 1,1, + "Returns the view point speed in units per 100/60 sec.", + 0 }, + { "CAME$UNKNOWN_0A", KMP_CAME, 0x0a, GTMD_U16, 1,1, + "Returns the unknown value at offser 0x0a of a camera.", + 0 }, + { "CAME$POS", KMP_CAME, 0x0c, GTMD_DBL3, 1,1, + "Returns the position of a camera.", + 0 }, + { "CAME$UNKNOWN_18", KMP_CAME, 0x18, GTMD_DBL3, 1,1, + "Returns the unknown value at offser 0x18 of a camera.", + 0 }, + { "CAME$ZOOM_BEGIN", KMP_CAME, 0x24, GTMD_DBL, 1,1, + "Returns the zoom beginning angle of a camera.", + 0 }, + { "CAME$ZOOM_END", KMP_CAME, 0x28, GTMD_DBL, 1,1, + "Returns the zoom ending angle of a camera.", + 0 }, + { "CAME$VIEWPT_BEGIN", KMP_CAME, 0x2c, GTMD_DBL3, 1,1, + "Returns the start position of the view point.", + 0 }, + { "CAME$VIEWPT_END", KMP_CAME, 0x38, GTMD_DBL3, 1,1, + "Returns the end position of the view point.", + 0 }, + { "CAME$TIME", KMP_CAME, 0x44, GTMD_DBL, 1,1, + "Returns the run time of a camera in 1/60 sec.", + 0 }, + + //--------------- + + { "JGPT$N", KMP_JGPT, 0, GTMD_N, 0,0, + "Returns the number of respan points.", + 0 }, + { "JGPT$POS", KMP_JGPT, 0x00, GTMD_DBL3, 1,1, + "Returns the position of a respawn point.", + 0 }, + { "JGPT$ROT", KMP_JGPT, 0x0c, GTMD_DBL3, 1,1, + "Returns the rotation of a respawn point.", + 0 }, + { "JGPT$ID", KMP_JGPT, 0x18, GTMD_U16, 1,1, + "Returns the ID of a respawn point.", + 0 }, + { "JGPT$EFFECT", KMP_JGPT, 0x1a, GTMD_U16, 1,1, + "Returns the effect value of a respawn point.", + 0 }, + + //--------------- + + { "CNPT$N", KMP_CNPT, 0, GTMD_N, 0,0, + "Returns the number of canon point points.", + 0 }, + { "CNPT$POS", KMP_CNPT, 0x00, GTMD_DBL3, 1,1, + "Returns the position of a canon point.", + 0 }, + { "CNPT$ROT", KMP_CNPT, 0x0c, GTMD_DBL3, 1,1, + "Returns the rotation of a canon point.", + 0 }, + { "CNPT$ID", KMP_CNPT, 0x18, GTMD_U16, 1,1, + "Returns the ID of a canon point.", + 0 }, + { "CNPT$EFFECT", KMP_CNPT, 0x1a, GTMD_U16, 1,1, + "Returns the efffect value of a canon point.", + 0 }, + + //--------------- + + { "MSPT$N", KMP_MSPT, 0, GTMD_N, 0,0, + "Returns the number of battle points.", + 0 }, + { "MSPT$POS", KMP_MSPT, 0x00, GTMD_DBL3, 1,1, + "Returns the position of a battle point.", + 0 }, + { "MSPT$ROT", KMP_MSPT, 0x0c, GTMD_DBL3, 1,1, + "Returns the rotation of a battle point.", + 0 }, + { "MSPT$ID", KMP_MSPT, 0x18, GTMD_U16, 1,1, + "Returns the ID of a battle point.", + 0 }, + { "MSPT$EFFECT", KMP_MSPT, 0x1a, GTMD_U16, 1,1, + "Returns the efffect value of a battle point.", + 0 }, + + //--------------- + + { "STGI$N", KMP_STGI, 0, GTMD_N, 0,0, + "Returns the number of stage infos.", + 0 }, + { "STGI$BYTE", KMP_STGI, 0x00, GTMD_U8, 12,2, + "Returns setting byte 0..11 of a stage info.", + 0 }, +}; + +#define KMP_FUNC_LIST_N (sizeof(kmp_func_list)/sizeof(*kmp_func_list)) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_get +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(fpar); + DASSERT( fpar->user_id >= 0 && fpar->user_id < KMP_FUNC_LIST_N ); + + if ( si && si->kmp ) + { + const kmp_func_t *fp = kmp_func_list + fpar->user_id; + DASSERT( n_param >= fp->n_param ); + DASSERT( fp->sect < KMP_N_SECT ); + DASSERT( fp->get_info ); + + const List_t *dlist = si->kmp->dlist + fp->sect; + uint max_index1 = dlist->used; + int val = n_param > 0 ? GetIntV(param) : 0; + uint index1 = val < 0 ? val + max_index1 : val; + const u8 *data = dlist->list + dlist->elem_size * index1 + fp->off; + + if ( fp->sect == KMP_POTI && fp->off >= POTI_PT_OFF && index1 < max_index1 ) + { + param++; + n_param--; + + const kmp_poti_group_t *pgrp = (kmp_poti_group_t*)dlist->list; + uint pt_start = 0; + while ( index1-- > 0 ) + pt_start += (pgrp++)->n_point; + + dlist = &si->kmp->poti_point; + max_index1 = pgrp->n_point; + val = n_param > 0 ? GetIntV(param) : 0; + index1 = val < 0 ? val + max_index1 : val; + PRINT("POTI/PT: index= %u/%u +%u\n",index1,max_index1,pt_start); + data = dlist->list + + dlist->elem_size * ( pt_start + index1 ) + + fp->off - POTI_PT_OFF; + } + + val = n_param > 1 ? GetIntV(param+1) : 0; + const uint index2 = val < 0 ? val + fp->n_var : val; + + const bool valid_index12 = index1 < max_index1 && index2 < fp->n_var; + noPRINT("index1=%d/%d, index2=%d/%d, valid=%d\n", + index1, max_index1, index2, fp->n_var, valid_index12 ); + + switch(fp->get_mode) + { + case GTMD_N: + res->i = dlist->used; + res->mode = VAR_INT; + return ERR_OK; + + case GTMD_HD_U16: + res->i = si->kmp->value[fp->sect]; + res->mode = VAR_INT; + return ERR_OK; + + case GTMD_HD_U8HI: + res->i = si->kmp->value[fp->sect] >> 8; + res->mode = VAR_INT; + return ERR_OK; + + case GTMD_HD_U8LO: + res->i = si->kmp->value[fp->sect] & 0xff; + res->mode = VAR_INT; + return ERR_OK; + + case GTMD_U8: + case GTMD_U8M1: + if (valid_index12) + { + const u8 val = data[index2]; + res->i = val == 0xff && fp->get_mode == GTMD_U8M1 ? -1 : (int)val; + res->mode = VAR_INT; + return ERR_OK; + } + break; + + case GTMD_U16: + case GTMD_U16M1: + if (valid_index12) + { + const u16 val = ((u16*)data)[index2]; + res->i = val == 0xffff && fp->get_mode == GTMD_U16M1 + ? -1 : (int)val; + res->mode = VAR_INT; + return ERR_OK; + } + break; + + case GTMD_DBL: + if (valid_index12) + return ResultFloat( res, (float*)data + index2, 1 ); + break; + + case GTMD_DBL2: + if (valid_index12) + return ResultFloat( res, (float*)data + 2 * index2, 2 ); + break; + + case GTMD_DBL3: + if (valid_index12) + return ResultFloat( res, (float*)data + 3 * index2, 3 ); + break; + } + } + + res->i = 0; + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +#ifdef TEST + +static enumError F_set +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(fpar); + DASSERT( fpar->user_id >= 0 && fpar->user_id < KMP_FUNC_LIST_N ); + + if ( si && si->kmp ) + { + const kmp_func_t *fp = kmp_func_list + fpar->user_id; + DASSERT( n_param >= fp->n_param ); + DASSERT( fp->sect < KMP_N_SECT ); + DASSERT( fp->set_info ); + + PRINT(">>> F_SET <<<\n"); + // ... + } + + res->i = 0; + res->mode = VAR_UNSET; + return ERR_OK; +} + +#endif +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_group_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void InitializeGL +( + kmp_group_list_t *gl, // list to initialize + int selector, + kmp_t *kmp, // related KMP + kmp_ph_t *ph // related PH info +) +{ + DASSERT(gl); + DASSERT(kmp); + DASSERT(ph); + + memset(gl,0,sizeof(*gl)); + gl->kmp = kmp; + gl->ph = ph; + gl->selector = toupper(selector); + InitializeLine(&gl->line); +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint ScanGroupNames +( + kmp_group_list_t * gl, // group list + kmp_group_elem_t * elist, // name list + ScanInfo_t * si // valid scan info +) +{ + DASSERT(gl); + DASSERT(elist); + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + char name[KMP_MAX_NAME_LEN+3]; + + uint idx = 0; + while ( idx < KMP_MAX_PH_LINK ) + { + const kmp_oneway_mode oneway + = NextCharSI(si,false) == '>' ? ONEWAY_BOTH : ONEWAY_NONE; + if ( oneway != ONEWAY_NONE ) + sf->ptr++; + + ScanNameSI(si,name,sizeof(name),true,true,0); + if ( gl->selector && *sf->ptr == '!' ) + { + int ch = *++sf->ptr; + if (ch) + { + sf->ptr++; + if ( toupper(ch) != gl->selector ) + continue; + } + } + + uint n = 1; + if ( *sf->ptr == '*' ) + { + sf->ptr++; + const uint num = *sf->ptr - '0'; + if ( num < 10 ) + { + sf->ptr++; + if ( gl->selector == 'E' ) + n = num; + } + } + + while ( n-- > 0 && idx < KMP_MAX_PH_LINK ) + { + memcpy(elist->name,name,sizeof(elist->name)); + elist->oneway = oneway; + elist++; + idx++; + } + } + + return idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError GroupPrevGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si // valid scan info +) +{ + DASSERT(gl); + DASSERT(si); + + if ( !gl->used) + { + GotoEolSI(si); + return ERR_OK; + } + + SkipCharSI(si,':'); + kmp_group_t *grp = gl->group + gl->used - 1; + ScanGroupNames(gl,grp->prev,si); + //HEXDUMP16(0,0,grp->prev,sizeof(grp->prev)); + return CheckEolSI(si); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError DefaultClassGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si // valid scan info +) +{ + DASSERT(gl); + DASSERT(gl->ph); + DASSERT(si); + + char name[20]; + SkipCharSI(si,':'); + if (!ScanNameSI(si,name,sizeof(name),true,true,0)) + { + GotoEolSI(si); + return ERR_WARNING; + } + + int stat = InsertClassNamePH(gl->ph,name,false); + noPRINT("NEW DEFAULT CLASS: %d [%s]\n",stat,name); + if ( stat >= 0 ) + gl->ph->default_class = stat; + + return CheckEolSI(si); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ClassGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si, // valid scan info + kmp_gopt2_t * go2 // options to set +) +{ + DASSERT(gl); + DASSERT(gl->ph); + DASSERT(si); + + char name[20]; + SkipCharSI(si,':'); + if (!ScanNameSI(si,name,sizeof(name),true,true,0)) + { + GotoEolSI(si); + return ERR_WARNING; + } + + const int stat = InsertClassNamePH(gl->ph,name,false); + noPRINT("NEW CLASS: %d [%s]\n",stat,name); + if ( stat >= 0 ) + { + go2->prev.rclass = go2->prev.lclass = + go2->next.rclass = go2->next.lclass = stat; + + if ( SkipCharSI(si,',') && ScanNameSI(si,name,sizeof(name),true,true,0) ) + { + const int stat = InsertClassNamePH(gl->ph,name,false); + noPRINT("NEW NEXT CLASS: %d [%s]\n",stat,name); + if ( stat >= 0 ) + go2->next.rclass = go2->next.lclass = stat; + } + } + + return CheckEolSI(si); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ACClassGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si, // valid scan info + kmp_gopt2_t * go2, // options to set + kmp_gopt_t * gopt // prev or next +) +{ + DASSERT(gl); + DASSERT(gl->ph); + DASSERT(si); + DASSERT(gopt); + + char name[20]; + SkipCharSI(si,':'); + if (!ScanNameSI(si,name,sizeof(name),true,true,0)) + { + GotoEolSI(si); + return ERR_WARNING; + } + + int stat = InsertClassNamePH(gl->ph,name,false); + noPRINT("NEW LINK CLASS: %d [%s]\n",stat,name); + if ( stat >= 0 ) + gopt->lclass = stat; + + return CheckEolSI(si); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError GroupSettingsGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si, // valid scan info + kmp_gopt2_t * go2 // options to set +) +{ + DASSERT(gl); + DASSERT(si); + DASSERT(go2); + + if (!gl->used) + return ERR_WARNING; + + long set0 = 0, set1 = 0; + kmp_rtype_mode rtype = 0; + + SkipCharSI(si,':'); + enumError err = ScanUValueSI(si,&set0,0); + if ( !err && SkipCharSI(si,',') ) + { + err = ScanUValueSI(si,&set1,0); + if ( !err && SkipCharSI(si,',') ) + { + char name[20]; + ScanNameSI(si,name,sizeof(name),true,true,0); + int stat; + const KeywordTab_t *key = ScanKeyword(&stat,name,kmp_rtype_name); + if (!key) + { + err = ERR_WARNING; + if (!si->no_warn) + PrintKeywordError(kmp_rtype_name,name,stat,0,"$SETTINGS"); + } + else + rtype = key->id; + } + } + + go2->rtype_user = rtype; + go2->setting[0] = set0; + go2->setting[1] = set1; + + noPRINT("\e[36;1m rtype %d, g-settings: %u 0x%02x [%d]\e[0m\n", + go2->rtype_user, go2->setting[0], go2->setting[1], err ); + if (err) + { + GotoEolSI(si); + return err; + } + + return CheckEolSI(si); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError GroupOnewayGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si, // valid scan info + kmp_gopt2_t * go2 // options to set +) +{ + DASSERT(gl); + DASSERT(si); + DASSERT(go2); + + if (!gl->used) + return ERR_WARNING; + + SkipCharSI(si,':'); + + char name[20]; + ScanNameSI(si,name,sizeof(name),true,true,0); + int stat; + const KeywordTab_t *key = ScanKeyword(&stat,name,kmp_oneway_name); + if (!key) + { + if (!si->no_warn) + PrintKeywordError(kmp_oneway_name,name,stat,0,"$ONEWAY"); + GotoEolSI(si); + return ERR_WARNING; + } + + go2->ac_oneway = key->id; + return CheckEolSI(si); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError AddGroupGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si, // valid scan info + char var_prefix, // Prefix for var names + uint index, // index of first point + uint n_dim // Line_t support: number of dimensions +) +{ + DASSERT(gl); + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + char name[KMP_MAX_NAME_LEN+3]; + ScanNameSI(si,name,sizeof(name),true,true,0); + + const bool split_group = !strcmp(name,"$SPLIT"); + if (split_group) + { + if ( gl->ph && gl->ph->ac_mode != KMP_AC_PREV ) + { + si->total_err++; + sf->line_err++; + CheckEolSI(si); + if (!si->no_warn) + ERROR0(ERR_WARNING, + "Command '$SPLIT' only possible if AUTO-CONNECT is set to AC$PREV" + " => ignored [%s @%u].\n", sf->name, sf->line ); + return ERR_WARNING; + } + + if ( !gl->used || index == gl->group[gl->used-1].first_index ) + { + si->total_err++; + sf->line_err++; + CheckEolSI(si); + if (!si->no_warn) + ERROR0(ERR_WARNING, + "Command '$SPLIT' ignored [%s @%u].\n", sf->name, sf->line ); + return ERR_WARNING; + } + } + else if ( strcmp(name,"$GROUP") ) + { + if ( !strcmp(name,"$PREV") ) + return GroupPrevGL(gl,si); + if (!strcmp(name,"$LINE")) + return ScanLine(&gl->line,si,n_dim,LM_LINE); + if (!strcmp(name,"$BORDER")) + return ScanLine(&gl->line,si,n_dim,LM_BORDER); + if (!strcmp(name,"$BEZIER")) + return ScanLine(&gl->line,si,n_dim,LM_BEZIER); + + if ( gl->ph ) + { + if ( !strcmp(name,"$DEF-CLASS") ) + return DefaultClassGL(gl,si); + + if ( gl->used > 0 && gl->used <= KMP_MAX_GROUP ) + { + kmp_gopt2_t *go2 = gl->ph->gopt + gl->used - 1; + if ( !strcmp(name,"$SETTINGS") ) + return GroupSettingsGL(gl,si,go2); + if ( !strcmp(name,"$ONEWAY") ) + return GroupOnewayGL(gl,si,go2); + if ( !strcmp(name,"$CLASS") ) + return ClassGL(gl,si,go2); + if ( !strcmp(name,"$AC-PREV") ) + return ACClassGL(gl,si,go2,&go2->prev); + if ( !strcmp(name,"$AC-NEXT") ) + return ACClassGL(gl,si,go2,&go2->next); + } + } + + si->total_err++; + sf->line_err++; + CheckEolSI(si); + return ERROR0(ERR_WARNING, + "Line ignored [%s @%u]: %s ...\n", + sf->name, sf->line, name ); + } + + if ( gl->used >= KMP_MAX_GROUP ) + { + si->total_err++; + sf->line_err++; + CheckEolSI(si); + return ERROR0(ERR_WARNING, + "To many groups, new group ignored: %s @%u\n", + sf->name, sf->line ); + } + + + // before incrementing the group index + if (gl->ph) + { + noPRINT("NEW GROUP: %d, class %d\n",gl->used,gl->ph->default_class); + kmp_gopt2_t *go2 = gl->ph->gopt + gl->used; + go2->prev.rclass = go2->prev.lclass = + go2->next.rclass = go2->next.lclass = gl->ph->default_class; + } + + // create new group by incrementing group index + kmp_group_t *grp = gl->group + gl->used++; + grp->first_index = grp->last_index = index; + + if ( NextCharSI(si,false) == '-' ) + { + sf->ptr++; + grp->no_rotate = true; + } + + ScanNameSI(si,name+2,sizeof(name)-2,true,true,0); + memcpy(grp->name,name+2,sizeof(grp->name)); + name[0] = var_prefix; + name[1] = '.'; + DefineIntVar(&si->gvar,name,gl->used-1); + + if (split_group) + { + kmp_group_t *prev = gl->group + gl->used - 2; + DASSERT( prev >= gl->group ); + memcpy(grp->next,prev->next,sizeof(grp->next)); + memset(prev->next,0,sizeof(prev->next)); + memcpy(prev->next->name,name,sizeof(prev->next->name)); + + CheckEolSI(si); + return ERR_OK; + } + + for(;;) + { + char ch = NextCharSI(si,false); + noPRINT("--> %02x : %.6s\n",ch,sf->ptr); + if (!ch) + break; + sf->ptr++; + if ( ch == ':' ) + break; + } + + ScanGroupNames(gl,grp->next,si); + CheckEolSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint AddPointGL +( + kmp_group_list_t * gl, // group list + uint index // index of first point +) +{ + if (!gl->used) + gl->used++; + + kmp_group_t *grp = gl->group + gl->used - 1; + grp->last_index = index; + return index - grp->first_index; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int SearchNearestDP +( + kmp_group_list_t * gl, // group list + kmp_t * kmp, // KMP data structure + kmp_class_mode cls, // one of KCLS_* + const float * base // base position +) +{ + DASSERT(gl); + DASSERT(kmp); + const kmp_ph_t *ph = gl->ph; + DASSERT(ph); + DASSERT( ph->sect_ph == KMP_ENPH || ph->sect_ph == KMP_ITPH ); + DASSERT( ph->sect_pt == KMP_ENPT || ph->sect_pt == KMP_ITPT ); + + if ( cls == KCLS_OFF ) + return -1; + + int i, found = -1; + kmp_group_t *grp; + double found_distance = 0.0; + + List_t *list = kmp->dlist + ph->sect_ph; + kmp_enph_entry_t *ph_list = (kmp_enph_entry_t*)(list->list); + + for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ ) + { + const kmp_gopt2_t *go2 = ph->gopt + i; + if ( go2->rtype_active != KMP_RT_DISPATCH ) + continue; + + kmp_class_mode dcls = go2->prev.rclass; + if ( dcls == KCLS_OFF || cls != dcls && cls != KCLS_ANY && dcls != KCLS_ANY ) + continue; + + noPRINT("--- CANDIDATE %2u: rtype=%d>%d, class=%d,%d|%d,%d\n", + i, + go2->rtype_user, go2->rtype_active, + go2->prev.rclass, go2->prev.lclass, + go2->next.rclass, go2->next.lclass ); + + const kmp_enpt_entry_t *pt + = (kmp_enpt_entry_t*)kmp->dlist[ph->sect_pt].list + + ph_list[i].pt_start; + + const double dx = pt->position[0] - base[0]; + const double dy = pt->position[1] - base[1]; + const double dz = pt->position[2] - base[2]; + const double distance = dx*dx + dy*dy + dz*dz; + + if ( found < 0 || distance < found_distance ) + { + found = i; + found_distance = distance; + } + } + + noPRINT("SearchNearestDP - return %d\n",found); + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void TermGL +( + kmp_group_list_t * gl, // group list + ScanInfo_t * si, // not NULL: print error message + kmp_t * kmp, // KMP data structure + uint sect_ph, // PH section + int rotate_links // >0: rotate links um x positions +) +{ + DASSERT(gl); + DASSERT(gl->ph); + DASSERT(kmp); + DASSERT( sect_ph == KMP_CKPH || sect_ph == KMP_ENPH || sect_ph == KMP_ITPH ); + + CheckLevelSI(si); + ResetRouteLinksKMP(kmp); + const bool auto_prev = gl->ph->ac_mode == KMP_AC_PREV; + memset(gl->ph->gname,0,sizeof(gl->ph->gname)); + + List_t *list = kmp->dlist + sect_ph; + list->used = 0; + + uint i; + kmp_group_t *grp = gl->group; + for ( i = 0; i < gl->used; i++, grp++ ) + { + kmp_enph_entry_t * entry = AppendList(list); + entry->pt_start = grp->first_index; + entry->pt_len = grp->last_index - grp->first_index + 1; + + memset(entry->next,-1,sizeof(entry->next)); + memset(entry->prev,-1,sizeof(entry->prev)); + if ( i < KMP_MAX_GROUP ) + memcpy(gl->ph->gname+i,grp->name,sizeof(*gl->ph->gname)); + + kmp_rtype_mode rtype = gl->ph->gopt[i].rtype_user; + if ( rtype == KMP_RT_AUTO ) + rtype = IsDispatchPointKMP(entry) ? KMP_RT_DISPATCH : KMP_RT_ROUTE; + gl->ph->gopt[i].rtype_active = rtype; + } + + rotate_links = rotate_links <= 0 ? 0 : rotate_links % KMP_MAX_PH_LINK; + PRINT_IF(rotate_links,"ROTATE-LINKS %d\n",rotate_links); + + for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ ) + { + //--- NEXT links + + uint l; + for ( l = 0; l < KMP_MAX_PH_LINK; l++ ) + { + const uint l2 = grp->no_rotate + ? l + : ( l + rotate_links ) % KMP_MAX_PH_LINK; + kmp_group_elem_t *elem = grp->next+l2; + ccp lname = elem->name; + if (!*lname) + continue; + + noPRINT(">> '%s' links to '%s'\n",grp->name,lname); + kmp_group_t *grp2 = gl->group; + bool found = false; + uint j; + for ( j = 0; j < gl->used; j++, grp2++ ) + { + if (!strcmp(lname,grp2->name)) + { + found = true; + + kmp_enph_entry_t * entry; + entry = (kmp_enph_entry_t*)(list->list) + i; + InsertLinkPH(entry->next,j,si,lname,true); + if (elem->oneway) + InsertOnewayPH(gl->ph,i,j); + + if ( auto_prev && !FindOnewayPH(gl->ph,i,j) ) + { + entry = (kmp_enph_entry_t*)(list->list) + j; + InsertLinkPH(entry->prev,i,si,lname,false); + } + break; + } + } + + if ( !found && !si->no_warn ) + ERROR0(ERR_WARNING, + "%s group %s: $GROUP name not found: %s", + kmp_section_name[gl->ph->sect_pt].name1, grp->name, lname); + } + + //--- PREV links + + if (!auto_prev) + { + for ( l = 0; l < KMP_MAX_PH_LINK; l++ ) + { + kmp_group_elem_t *elem = grp->prev+l; + ccp lname = elem->name; + if (!*lname) + continue; + + noPRINT(">> '%s' links to '%s'\n",grp->name,lname); + kmp_group_t *grp2 = gl->group; + bool found = false; + uint j; + for ( j = 0; j < gl->used; j++, grp2++ ) + { + if (!strcmp(lname,grp2->name)) + { + found = true; + + kmp_enph_entry_t * entry; + entry = (kmp_enph_entry_t*)(list->list) + i; + InsertLinkPH(entry->prev,j,si,lname,true); + if (elem->oneway) + InsertOnewayPH(gl->ph,i,j); + break; + } + } + + if ( !found && !si->no_warn ) + ERROR0(ERR_WARNING, + "%s group %s: $PREV name not found: %s", + kmp_section_name[gl->ph->sect_pt].name1, grp->name, lname); + } + } + } + + + //--- @AUTO-CONNECT == AC$DISPATCH + + kmp_ph_t *ph = gl->ph; + DASSERT(ph); + + if ( ph && ph->ac_mode == KMP_AC_DISPATCH + && ( sect_ph == KMP_ENPH || sect_ph == KMP_ITPH )) + { + + //--- standard routes + + const kmp_linfo_t *li = SetupRouteLinksKMP(kmp,sect_ph,true); + + for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ ) + { + const kmp_gopt2_t *go2 = ph->gopt + i; + if ( go2->rtype_active == KMP_RT_DISPATCH ) + continue; + + PRINT(">>> STDROUTE %2u: %d %s, rtype=%d>%d, oneway=%d, class=%d,%d|%d,%d\n", + i, li->gmode[i] & LINFOG_DISPATCH, + PrintLinkInfoMode(li->summary[i],1,true), + go2->rtype_user, go2->rtype_active, + go2->ac_oneway, + go2->prev.rclass, go2->prev.lclass, + go2->next.rclass, go2->next.lclass ); + + kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i; + const kmp_linfo_mode mode = li->summary[i]; + + if ( !(mode & LINFO_G_PREV) && go2->prev.lclass != KCLS_OFF ) + { + noPRINT("-> SEARCH PREV, CLASS %d\n",go2->prev.lclass); + const kmp_enpt_entry_t *pt + = (kmp_enpt_entry_t*)kmp->dlist[li->sect_pt].list + + entry->pt_start; + const int dp = SearchNearestDP(gl,kmp,go2->prev.lclass,pt->position); + if ( dp >= 0 ) + { + kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i; + InsertLinkPH(entry->prev,dp,0,0,false); + if ( go2->ac_oneway & ONEWAY_PREV ) + InsertOnewayPH(ph,i,dp); + } + } + + if ( !(mode & LINFO_G_NEXT) && go2->next.lclass != KCLS_OFF ) + { + noPRINT("-> SEARCH NEXT, CLASS %d\n",go2->prev.lclass); + const kmp_enpt_entry_t *pt + = (kmp_enpt_entry_t*)kmp->dlist[li->sect_pt].list + + entry->pt_start + entry->pt_len - 1; + const int dp = SearchNearestDP(gl,kmp,go2->prev.lclass,pt->position); + if ( dp >= 0 ) + { + kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i; + InsertLinkPH(entry->next,dp,0,0,false); + if ( go2->ac_oneway & ONEWAY_NEXT ) + InsertOnewayPH(ph,i,dp); + } + } + } + + + //--- dispatch points + + li = SetupRouteLinksKMP(kmp,sect_ph,true); + + for ( i = 0, grp = gl->group; i < gl->used; i++, grp++ ) + { + const kmp_gopt2_t *go2 = ph->gopt + i; + if ( go2->rtype_active != KMP_RT_DISPATCH || go2->next.lclass == KCLS_OFF ) + continue; + + PRINT(">>> DISPATCH %2u: %d %s, rtype=%d>%d, oneway=%d, class=%d,%d|%d,%d\n", + i, li->gmode[i] & LINFOG_DISPATCH, + PrintLinkInfoMode(li->summary[i],1,true), + go2->rtype_user, go2->rtype_active, + go2->ac_oneway, + go2->prev.rclass, go2->prev.lclass, + go2->next.rclass, go2->next.lclass ); + + const kmp_linfo_mode mode = li->summary[i]; + if ( mode & (LINFO_G_PREV|LINFO_G_NEXT) ) + continue; // mode already set + + kmp_enph_entry_t *entry = (kmp_enph_entry_t*)(list->list) + i; + const kmp_linfo_mode *l = li->list[i]; + + uint j; + for ( j = 0; j < li->used; j++, l++ ) + if ( *l & (LINFO_G_BY_PREV|LINFO_G_BY_NEXT) && !FindOnewayPH(ph,j,i) ) + InsertLinkPH(entry->next,j,0,0,false); + } + } + + + //--- fix empty link lists + + if ( ph->ac_flags & KMP_ACF_FIX ) + { + const int fix_prev = ph->ac_flags & KMP_ACF_FIX_PREV; + const int fix_next = ph->ac_flags & KMP_ACF_FIX_NEXT; + kmp_enph_entry_t *entry = (kmp_enph_entry_t*)list->list; + + for ( i = 0; i < gl->used; i++, entry++ ) + { + if ( fix_prev && entry->prev[0] == M1(entry->prev[0]) ) + entry->prev[0] = i; + if ( fix_next && entry->next[0] == M1(entry->next[0]) ) + entry->next[0] = i; + } + } + + ResetRouteLinksKMP(kmp); // invalidate link-info because modified +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// more kmp functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int get_next +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + int user_id, // user defined id + int * cur_idx // not NULL: store current index here +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT( user_id == KMP_ENPT || user_id == KMP_ITPT ); + + // [[2do]] use GetNextPointKMP() + + if (cur_idx) + *cur_idx = -1; + + int idx = GetIntV(param); + res->i = 0; + res->mode = VAR_UNSET; + + if ( si && si->kmp ) + { + const kmp_t *kmp = si->kmp; + + const List_t *ptlist, *phlist; + if ( user_id == KMP_ENPT ) + { + ptlist = kmp->dlist + KMP_ENPT; + phlist = kmp->dlist + KMP_ENPH; + } + else + { + ptlist = kmp->dlist + KMP_ITPT; + phlist = kmp->dlist + KMP_ITPH; + } + + if ( idx < 0 ) + idx += ptlist->used; + + if ( idx >= 0 && idx < ptlist->used ) + { + if (cur_idx) + *cur_idx = idx; + + const kmp_enph_entry_t *ph = (kmp_enph_entry_t*)phlist->list; + uint n = phlist->used; + while ( idx >= ph->pt_start + ph->pt_len && n-- > 0 ) + ph++; + + noPRINT(">> %u %zu[%u+%u]\n", idx, + ph-(kmp_enph_entry_t*)phlist->list,ph->pt_start,ph->pt_len); + if ( idx == ph->pt_start + ph->pt_len - 1 ) + { + idx = -1; // invalidate + uint ph_idx = ~0; + + if ( n_param > 1 ) + { + uint idx2 = GetIntV(param+1); + if ( idx2 < KMP_MAX_PH_LINK ) + ph_idx = ph->next[idx2]; + } + else + { + uint idx2; + for ( idx2 = 0; idx2 < KMP_MAX_PH_LINK; idx2++ ) + { + if (!IS_M1(ph->next[idx2])) + { + ph_idx = ph->next[idx2]; + break; + } + } + } + + if ( ph_idx < phlist->used ) + { + ph = (kmp_enph_entry_t*)phlist->list + ph_idx; + idx = ph->pt_start; + } + } + else + idx++; + + if ( idx >= 0 && idx < ptlist->used ) + return idx; + } + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_get_next +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT(fpar); + DASSERT( fpar->user_id == KMP_ENPT || fpar->user_id == KMP_ITPT ); + + int next = get_next(res,param,n_param,si,fpar->user_id,0); + if ( next >= 0 ) + { + res->i = next; + res->mode = VAR_INT; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_get_hdir +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT(fpar); + DASSERT( fpar->user_id == KMP_ENPT || fpar->user_id == KMP_ITPT ); + + int current; + int next = get_next(res,param,n_param,si,fpar->user_id,¤t); + if ( next >= 0 ) + { + DASSERT(si); + const kmp_t *kmp = si->kmp; + DASSERT(kmp); + + const kmp_enpt_entry_t *pt = (kmp_enpt_entry_t*)kmp->dlist[fpar->user_id].list; + res->d = atan2( pt[next].position[0] - pt[current].position[0], + pt[next].position[2] - pt[current].position[2] ) * (180/M_PI); + res->mode = VAR_DOUBLE; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isKMP +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + uint result = 0; + if ( si && si->kmp ) + { + result = 1; + + if (si->kmp->fname) + { + ccp fname = strrchr(si->kmp->fname,'/'); + if (fname) + fname++; + else + fname = si->kmp->fname; + + if ( !strcasecmp(fname,"course.kmp") + || !strcasecmp(fname,"course.txt") + || !strcasecmp(fname,"course.txt.kmp") ) + { + result = 2; + } + } + } + + res->i = result; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const struct FuncTable_t kmp_func_tab[] = +{ + { 1, 2, "ENPT$NEXT", F_get_next, KMP_ENPT, + "int", "enpt$next(enpt_index[,next_index])", + "Returns the index of the next enemy point." + " The 'next_index' is only used, if multiple links are available." }, + + { 1, 2, "ITPT$NEXT", F_get_next, KMP_ITPT, + "int", "itpt$next(itpt_index[,next_index])", + "Returns the index of the next item point." + " The 'next_index' is only used, if multiple links are available." }, + + { 1, 2, "ENPT$HDIR", F_get_hdir, KMP_ENPT, + "float", "enpt$hDir(enpt_index[,next_index])", + "Calculate the horizontal direction in degree of an enemy point." + " The 'next_index' is only used, if multiple links are available." }, + + { 1, 2, "ITPT$HDIR", F_get_hdir, KMP_ITPT, + "float", "itpt$hDir(itpt_index[,next_index])", + "Calculate the horizontal direction in degree of an item point." + " The 'next_index' is only used, if multiple links are available." }, + + { 0, 0, "ISKMP", F_isKMP, 0, // replace only function call + "int", "isKMP()", 0 }, // but not info + + {0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup kmp parser /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsKMP() +{ + static VarMap_t vm = {0}; + if (!vm.used) + { + //--- setup parser function get() + + uint i; + const kmp_func_t *fp = kmp_func_list; + for ( i = 0; i < KMP_FUNC_LIST_N; i++, fp++ ) + { + char syntax[80]; + if ( fp->get_info) + { + DefineParserFunc(fp->name,fp->n_param,fp->n_param,FF_KMP,F_get,i); + + ccp retval = fp->get_mode == GTMD_DBL + ? "float" + : fp->get_mode > GTMD_DBL + ? "vector" + : "int"; + char *dest = syntax; + ccp src = fp->name; + while (*src) + *dest++ = tolower((int)*src++); + *dest++ = '('; + if ( fp->n_param > 0 ) + { + src = fp->sect == KMP_POTI ? "route" : kmp_section_name[fp->sect].name1; + while (*src) + *dest++ = tolower((int)*src++); + + switch(fp->n_param) + { + case 1: + src = "_idx"; + break; + + case 2: + src = fp->sect == KMP_POTI && fp->off >= POTI_PT_OFF + ? "_idx,pt_idx" + : "_idx,val_idx"; + break; + + case 3: + src = "_idx,pt_idx,val_idx"; + break; + + }; + while (*src) + *dest++ = *src++; + } + *dest++ = ')'; + *dest = 0; + DASSERT(dest < syntax + sizeof(syntax) ); + + DefineParserFuncInfo(retval,syntax,fp->get_info); + } + + #ifdef TEST // [[2do]] + if (fp->set_info) + { + char namebuf[40]; + StringCat2S(namebuf,sizeof(namebuf),"SET_",fp->name); + DefineParserFunc(namebuf,fp->n_param+1,fp->n_param+1,FF_KMP,F_set,i); + + char *dest = syntax; + ccp src = namebuf; + while (*src) + *dest++ = tolower((int)*src++); + *dest++ = '('; + if ( fp->n_param > 0 ) + { + src = fp->sect == KMP_POTI ? "route" : kmp_section_name[fp->sect].name1; + while (*src) + *dest++ = tolower((int)*src++); + + switch(fp->n_param) + { + case 1: + src = "_idx"; + break; + + case 2: + src = fp->sect == KMP_POTI && fp->off >= POTI_PT_OFF + ? "_idx,pt_idx" + : "_idx,val_idx"; + break; + + case 3: + src = "_idx,pt_idx,val_idx"; + break; + + }; + while (*src) + *dest++ = *src++; + } + DASSERT(dest < syntax + sizeof(syntax) ); + StringCopyE(dest,syntax+sizeof(syntax),",value)"); + DefineParserFuncInfo("int",syntax,fp->set_info); + } + #endif + } + + + //--- setup additional functions + + DefineParserFuncTab(kmp_func_tab,FF_KMP); + SetupReferenceKCL(&vm); + + + //--- setup object names + + DefineObjectNameVars(&vm); + + + //--- setup KMP section names + + char varname[50]; + const KeywordTab_t *key; + for ( key = kmp_section_name; key->name2; key++ ) + { + snprintf(varname,sizeof(varname),"SECT$%s",key->name1); + DefineIntVar(&vm,varname,key->id+1); + } + + + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + { "MODE$SLOT", 0 }, + { "MODE$D", 0 }, + + { "MODE$TEST", 0 }, + { "MODE$OCAM", 0 }, + { "MODE$LAPS", 3 }, + { "MODE$SHOWRT", 0 }, + { "MODE$SHOWCK", 0 }, + { "MODE$AUTOMODE", 0 }, + + { "AC$OFF", KMP_AC_OFF }, + { "AC$PREV", KMP_AC_PREV }, + { "AC$DISPATCH", KMP_AC_DISPATCH }, + { "ACF$FIX_PREV", KMP_ACF_FIX_PREV }, + { "ACF$FIX_NEXT", KMP_ACF_FIX_NEXT }, + { "ACF$FIX", KMP_ACF_FIX }, + + { "AM$OFF", KMP_AM_OFF }, + { "AM$NORM", KMP_AM_NORM }, + { "AM$RENUMBER", KMP_AM_RENUMBER }, + { "AM$1LAP", KMP_AM_1_LAP }, + { "AM$SHORT", KMP_AM_SHORT }, + { "AM$UNLIMIT", KMP_AM_UNLIMIT }, + + { "SORT$OFF", KMP_SORT_OFF }, + { "SORT$GROUPING", KMP_SORT_GROUPING }, + { "SORT$ANGLE", KMP_SORT_ANGLE }, + { "SORT$TINY", KMP_SORT_TINY }, + + { "DOB$OFF", KMP_DOB_OFF }, + { "DOB$BEGIN", KMP_DOB_BEGIN }, + { "DOB$END", KMP_DOB_END }, + { "DOB$BEFORE", KMP_DOB_BEFORE }, + { "DOB$BEHIND", KMP_DOB_BEHIND }, + + { "EN$BATTLE", ENGM_BATTLE }, + { "EN$50", ENGM_50 }, + { "EN$100", ENGM_100 }, + { "EN$150", ENGM_150 }, + { "EN$200", ENGM_200 }, + { "EN$150M", ENGM_150M }, + { "EN$200M", ENGM_200M }, + { "EN$150X", ENGM_150M | ENGM_150 }, + { "EN$200X", ENGM_200M | ENGM_200 }, + { "EN$MIRROR", ENGM_150M | ENGM_200M }, + { "EN$ALL", ENGM__ALL }, + + { "F$FALL", KPFL_FALL }, + { "F$SNAP", KPFL_SNAP }, + { "F$JGPT", KPFL_JGPT }, + + { "ITEM$RANDOM", 0x00 }, + { "ITEM$BANANA", 0x01 }, + { "ITEM$MUSHROOM", 0x02 }, + { "ITEM$3MUSHROOMS", 0x03 }, + { "ITEM$STAR", 0x04 }, + { "ITEM$3GREEN", 0x05 }, + { "ITEM$BANANA_MUSHROOM", 0x06 }, + { "ITEM$GREEN", 0x07 }, + { "ITEM$RED", 0x08 }, + { "ITEM$MEGA", 0x0a }, + { "ITEM$CLOUD", 0x0b }, + { "ITEM$STAR_MUSHROOM", 0x0c }, + { "ITEM$MUSHROOM_GREEN", 0x0d }, + { "ITEM$3BANANAS_GREEN", 0x24 }, + + { "PF$OFFLINE1", 0x01 }, + { "PF$OFFLINE2", 0x02 }, + { "PF$OFFLINE3", 0x04 }, + { "PF$OFFLINE", 0x07 }, + { "PF$ONLINE1", 0x08 }, + { "PF$ONLINE2", 0x10 }, + { "PF$ONLINE3", 0x20 }, + { "PF$ONLINE", 0x38 }, + { "PF$ALL", 0x3f }, + + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(&vm,ip->name,ip->val); + + + //--- extended p-flags + + char name[20]; + const gobj_cond_ref_t *rc; + for ( rc = cond_ref_tab; rc->ref_id; rc++ ) + { + ccp prefix = rc->option & 4 ? "IFDEV$" : "IF$"; + StringCat2S(name,sizeof(name),prefix,rc->name); + DefineIntVar(&vm,name,rc->ref_id); + + if (rc->alias[0]) + { + StringCat2S(name,sizeof(name),prefix,rc->alias); + DefineIntVar(&vm,name,rc->ref_id); + } + + if ( rc->cond_mask && !(rc->option & 4) ) + { + StringCat2S(name,sizeof(name),"C$",rc->name); + DefineIntVar(&vm,name,rc->cond_mask); + + if (rc->alias[0]) + { + StringCat2S(name,sizeof(name),"C$",rc->alias); + DefineIntVar(&vm,name,rc->cond_mask); + } + } + } + + + //--- setup basic parser variables + + DefineParserVars(&vm); + } + + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kcl fall helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kcl_fall_t]] + +typedef struct kcl_fall_t +{ + //--- input + + kcl_t *fall_kcl; // KCL to use, never NULL if 'kf->valid' + + int width; // input for "KCL-FALL-WIDTH" + Var_t pre_add; // input for "KCL-FALL-PRE-ADD" + Var_t post_add; // input for "KCL-FALL-POST-ADD" + u32 types; // input for "KCL-FALL-TYPES" + + //---- status + + bool valid; // true: calculation can be done + +} kcl_fall_t; + +/////////////////////////////////////////////////////////////////////////////// + +#define KCL_FALL_VARS(k,v) \ + { "KCL-FALL-ADD-ROAD", SPM_BOOL, &k->enable_fall_kcl }, \ + { "KCL-FALL-WIDTH", SPM_INT, &v.width }, \ + { "KCL-FALL-PRE-ADD", SPM_VAR, &v.pre_add }, \ + { "KCL-FALL-POST-ADD", SPM_VAR, &v.post_add }, \ + { "KCL-FALL-TYPES", SPM_U32, &v.types }, + +/////////////////////////////////////////////////////////////////////////////// + +void ResetKclFall ( kcl_fall_t *kf ) +{ + DASSERT(kf); + memset(kf,0,sizeof(*kf)); + kf->types = M1(kf->types); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CheckKclFall ( kcl_fall_t *kf ) +{ + DASSERT(kf); + + kf->fall_kcl = 0; + kf->valid = kf->width > 0 && kf->types; + if (kf->valid) + { + ToVectorYV(&kf->pre_add); + ToVectorYV(&kf->post_add); + + kf->fall_kcl = GetReferenceKCL(); + if (!kf->fall_kcl) + kf->valid = false; + } + return kf->valid; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcKclFall ( kcl_fall_t *kf, float *vector ) +{ + DASSERT(kf); + if (kf->valid) + { + DASSERT(kcl_ref); + DASSERT(kf->fall_kcl); + + double3 pt; + pt.x = vector[0] + kf->pre_add.x; + pt.y = vector[1] + kf->pre_add.y; + pt.z = vector[2] + kf->pre_add.z; + double temp = FallKCL(kf->fall_kcl,0,pt,kf->width,0,kf->types,0); + if ( temp >= 0.0 && temp < pt.y ) + { + vector[0] = kf->post_add.x + pt.x; + vector[1] = kf->post_add.y + temp; + vector[2] = kf->post_add.z + pt.z; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// route object helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[rtobj_info_t]] + +typedef struct rtobj_info_t +{ + //--- input + + int route_object; // input for "ROUTE-OBJECT" + Var_t route_shift; // input for "ROUTE-OBJECT-SHIFT" + Var_t route_scale; // input for "ROUTE-OBJECT-SCALE" + Var_t route_rotate; // input for "ROUTE-OBJECT-ROTATE" + int show_route; // input for "SHOW-ROUTE" + int route_itembox; // input for "ROUTE-ITEMBOX" + + //--- data + + kmp_rtobj_t *def_obj; // default object + kmp_rtobj_t rtobj; // resulting data + bool valid; // true: rtobj is set + +} rtobj_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +#define ROUTE_OBJECT_VARS(v) \ + { "ROUTE-OBJECT", SPM_INT, &v.route_object }, \ + { "ROUTE-OBJECT-SHIFT", SPM_VAR, &v.route_shift }, \ + { "ROUTE-OBJECT-SCALE", SPM_VAR, &v.route_scale }, \ + { "ROUTE-OBJECT-ROTATE",SPM_VAR, &v.route_rotate }, \ + { "SHOW-ROUTE", SPM_INT, &v.show_route }, \ + { "ROUTE-ITEMBOX", SPM_INT, &v.route_itembox }, + +/////////////////////////////////////////////////////////////////////////////// + +void ResetRouteInfo ( rtobj_info_t *ri, kmp_rtobj_t *def_obj ) +{ + DASSERT(ri); + DASSERT(def_obj); + + memset(ri,0,sizeof(*ri)); + ri->def_obj = def_obj; + memcpy(&ri->rtobj,ri->def_obj,sizeof(ri->rtobj)); + ri->route_object = N_KMP_GOBJ; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CheckRouteInfo ( rtobj_info_t *ri ) +{ + DASSERT(ri); + DASSERT(ri->def_obj); + noPRINT_IF( ri->route_object != N_KMP_GOBJ, + ">> CheckRouteInfo() obj=%d, shrt=%d\n", + ri->route_object, ri->show_route ); + + if (!ri->route_object) + ri->rtobj.obj = 0; + else if ( ri->route_object < 0 ) + { + memcpy(&ri->rtobj,ri->def_obj,sizeof(ri->rtobj)); + ri->show_route = 1; + } + else if ( ri->route_object < N_KMP_GOBJ ) + { + ri->rtobj.obj = ri->route_object; + ri->show_route = 1; + } + ri->route_object = N_KMP_GOBJ; + + if ( ri->route_shift.mode != VAR_UNSET ) + { + ri->rtobj.shift = GetVectorFV(&ri->route_shift); + ri->route_shift.mode = VAR_UNSET; + } + + if ( ri->route_scale.mode != VAR_UNSET ) + { + ri->rtobj.scale = GetVectorFV(&ri->route_scale); + ri->route_scale.mode = VAR_UNSET; + } + + if ( ri->route_rotate.mode != VAR_UNSET ) + { + ri->rtobj.rotate = GetVectorFV(&ri->route_rotate); + ri->route_rotate.mode = VAR_UNSET; + } + + if ( ri->show_route > 0 && !ri->rtobj.obj ) + ri->rtobj.obj = ri->def_obj->obj; + + if ( ri->route_itembox > 0 && !ri->valid ) + { + ri->show_route = 1; + ri->rtobj.obj = GOBJ_ITEMBOX; + } + + ri->valid = ri->show_route > 0 && ri->rtobj.obj; + return ri->valid; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool AssignRouteInfo ( rtobj_info_t *ri, kmp_rtobj_t *dest ) +{ + DASSERT(ri); + DASSERT(dest); + if (ri->valid) + memcpy(dest,&ri->rtobj,sizeof(*dest)); + else + memset(dest,0,sizeof(*dest)); + + return ri->valid; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// transform pos helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[tform_info_t]] + +typedef struct tform_info_t +{ + //--- input + + int reset; // input for "TFORM-RESET" + Var_t scale; // input for "TFORM-SCALE" + Var_t shift; // input for "TFORM-SHIFT" + Var_t rotate; // input for "TFORM-ROTATE" + Var_t translate; // input for "TFORM-TRANSLATE" + + + //--- data + + bool active; // transformation is active + MatrixD_t matrix; // transformation matrix; + +} tform_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +#define TFORM_VARS(v) \ + { "TFORM-RESET", SPM_INC, &v.reset }, \ + { "TFORM-SCALE", SPM_VAR, &v.scale }, \ + { "TFORM-SHIFT", SPM_VAR, &v.shift }, \ + { "TFORM-ROTATE", SPM_VAR, &v.rotate }, \ + { "TFORM-TRANSLATE", SPM_VAR, &v.translate }, \ + +/////////////////////////////////////////////////////////////////////////////// + +void ResetTformInfo ( tform_info_t *ti ) +{ + DASSERT(ti); + + memset(ti,0,sizeof(*ti)); + InitializeMatrixD(&ti->matrix); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CheckTformInfo ( tform_info_t *ti ) +{ + DASSERT(ti); + + if (ti->reset) + { + PRINT("#TFORM: RESET(%d)\n",ti->reset); + ti->reset = 0; + ti->active = false; + InitializeMatrixD(&ti->matrix); + } + + if ( ti->scale.mode != VAR_UNSET ) + { + double3 d = GetVectorV(&ti->scale); + PRINT("#TFORM: SCALE %g %g %g\n",d.x,d.y,d.z); + SetScaleMatrixD(&ti->matrix,&d,0); + ti->scale.mode = VAR_UNSET; + ti->active = true; + } + + if ( ti->shift.mode != VAR_UNSET ) + { + double3 d = GetVectorV(&ti->shift); + PRINT("#TFORM: SHIFT %g %g %g\n",d.x,d.y,d.z); + SetShiftMatrixD(&ti->matrix,&d); + ti->shift.mode = VAR_UNSET; + ti->active = true; + } + + if ( ti->rotate.mode != VAR_UNSET ) + { + double3 d = GetVectorV(&ti->rotate); + PRINT("#TFORM: ROTATE %g %g %g\n",d.x,d.y,d.z); + SetRotateMatrixD(&ti->matrix,0,d.x,0.0,0); + SetRotateMatrixD(&ti->matrix,1,d.y,0.0,0); + SetRotateMatrixD(&ti->matrix,2,d.z,0.0,0); + ti->rotate.mode = VAR_UNSET; + ti->active = true; + } + + if ( ti->translate.mode != VAR_UNSET ) + { + double3 d = GetVectorV(&ti->translate); + PRINT("#TFORM: TRANSLATE %g %g %g\n",d.x,d.y,d.z); + SetTranslateMatrixD(&ti->matrix,&d); + ti->translate.mode = VAR_UNSET; + ti->active = true; + } + + return ti->active; +} + +/////////////////////////////////////////////////////////////////////////////// + +void TransformPos ( tform_info_t *ti, float32 *pos ) +{ + DASSERT(ti); + DASSERT(pos); + + if (ti->active) + *(float3*)pos = TransformF3MatrixD(&ti->matrix,(float3*)pos); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextKMP() sub functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static kmp_pt_flag_t ScanPtFlags ( ScanInfo_t *si ) +{ + DASSERT(si); + + if ( NextCharSI(si,false) == '(' ) + { + si->cur_file->ptr++; + DEFINE_VAR(val); + CheckWarnSI(si,')',ScanExprSI(si,&val)); + return GetIntV(&val); + } + + char fbuf[20]; + ScanNameSI(si,fbuf,sizeof(fbuf),true,true,0); + + kmp_pt_flag_t flags = 0; + if ( strchr(fbuf,'F') ) flags |= KPFL_FALL; + if ( strchr(fbuf,'S') ) flags |= KPFL_SNAP; + if ( strchr(fbuf,'J') ) flags |= KPFL_JGPT; + + #if 0 + if ( si->cur_file->revision < xxxx && !( flags & KPFL_UPDATE_Rxxxx ) ) + flags |= KPFL_UPDATE_Rxxxx; + #endif + return flags; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanText_PT_PH +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si, // valid data + uint sect_pt // KMP PT section +) +{ + DASSERT(kmp); + DASSERT(si); + DASSERT( sect_pt == KMP_ENPT || sect_pt == KMP_ITPT ); + + uint sect_ph; + kmp_ph_t *ph; + kmp_flag_t *kf; + + if ( sect_pt == KMP_ENPT ) + { + sect_ph = KMP_ENPH; + ph = &kmp->enph; + kf = &kmp->enpt_flag; + } + else + { + sect_ph = KMP_ITPH; + ph = &kmp->itph; + kf = &kmp->itpt_flag; + } + + ph->default_class = KCLS_DEFAULT; + + ccp pt_name = kmp_section_name[sect_pt].name1; + ccp ph_name = kmp_section_name[sect_ph].name1; + TRACE("ScanText_PT_PH(%s,%s)\n",pt_name,ph_name); + + char pt_hd_name[20], ph_hd_name[20]; + snprintf( pt_hd_name, sizeof(pt_hd_name), "%s-HEAD-VALUE", pt_name ); + snprintf( ph_hd_name, sizeof(ph_hd_name), "%s-HEAD-VALUE", ph_name ); + + rtobj_info_t rtinfo; + ResetRouteInfo(&rtinfo,&kmp->rtobj); + + tform_info_t tform_pos; + ResetTformInfo(&tform_pos); + + kcl_fall_t kfall; + ResetKclFall(&kfall); + + int auto_group_name = 0, rotate_group_links = 0; + int show_options = 0, export_flags = 0; + double auto_fill = 0.0; + u8 auto_connect = ph->ac_mode | ph->ac_flags; + u16 mask1, mask2; + mask1 = mask2 = 0xffff; + + ScanParam_t ptab[] = + { + { pt_hd_name, SPM_U16, kmp->value+sect_pt }, + { ph_hd_name, SPM_U16, kmp->value+sect_ph }, + { "AUTO-GROUP-NAME", SPM_INT, &auto_group_name }, + { "ROTATE-GROUP-LINKS", SPM_INT, &rotate_group_links }, + { "EXPORT-FLAGS", SPM_INT, &export_flags }, + { "AUTO-FILL", SPM_DOUBLE, &auto_fill }, + { "SHOW-OPTIONS", SPM_INT, &show_options }, + { "AUTO-CONNECT", SPM_U8, &auto_connect }, + { "MASK-1", SPM_U16, &mask1 }, + { "MASK-2", SPM_U16, &mask2 }, + ROUTE_OBJECT_VARS(rtinfo) + TFORM_VARS(tform_pos) + KCL_FALL_VARS(kmp,kfall) + {0} + }; + + kmp_group_list_t gl; + InitializeGL(&gl,*pt_name,kmp,ph); + List_t *ptlist = kmp->dlist + sect_pt; + ptlist->used = 0; + + enumError err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + CheckRouteInfo(&rtinfo); + CheckTformInfo(&tform_pos); + CheckKclFall(&kfall); + continue; + } + + if ( ch == '$' ) + { + AddGroupGL(&gl,si,ph_name[0],ptlist->used,3); + continue; + } + + u16 idx; + if (ScanU16SI(si,&idx,1,0)) + { + err = ERR_WARNING; + break; + } + + kmp_enpt_entry_t temp; + ScanFloatV3SI(si,temp.position,1); + ScanFloatSI(si,&temp.scale,1); + if ( si->cur_file->szs_modifier > 0 ) + ScanBE32SI_swap(si,(u32*)temp.prop,1,1); + else + ScanU16SI(si,temp.prop,2,0); + const u8 flags = NextCharSI(si,false) ? ScanPtFlags(si) : kf->mask; + CheckEolSI(si); + + const kmp_enpt_entry_t * prev = GetListElem(ptlist,-1,&temp); + if (!ptlist->used) // no previous point available + gl.line.n = 1; + else if ( auto_fill >= 1.0 + && gl.line.n == 1 + && gl.used + && ptlist->used != gl.group[gl.used-1].first_index ) + { + double3 dist3; + dist3.x = temp.position[0] - prev->position[0]; + dist3.y = temp.position[1] - prev->position[1]; + dist3.z = temp.position[2] - prev->position[2]; + const double dist = Length3(dist3); + if ( dist > auto_fill ) + { + gl.line.n = (int)trunc(dist/auto_fill) + 1; + PRINT("DIST=%8.2f, FILL=%8.2f => N=%2u\n",dist,auto_fill,gl.line.n); + } + } + + uint p; + for ( p = 1; p <= gl.line.n; p++ ) + { + AddPointGL(&gl,ptlist->used); + if ( ptlist->used < KMP_MAX_POINT ) + kf->flags[ptlist->used] = flags; + + kmp_enpt_entry_t * entry = AppendList(ptlist); + memcpy(entry,&temp,sizeof(*entry)); + CalcLine(&gl.line,entry->position,prev->position,temp.position,4,p); + TransformPos(&tform_pos,entry->position); + if ( flags & KPFL_FALL ) + CalcKclFall(&kfall,entry->position); + } + InitializeLine(&gl.line); + } + + TermGL(&gl,si,kmp,sect_ph,rotate_group_links); + + if (gl.have_options) + show_options = true; + + #if HAVE_PRINT0 + { + uint i; + for ( i = 0; i < gl.used; i++ ) + PRINT("GROUP %3u -> %s\n",i,ph->gname[i]); + } + #endif + + if (opt_export_flags) + export_flags = opt_export_flags > 0; + + kf->export = export_flags; + ph->ac_mode = auto_connect & KMP_AC_MASK; + ph->ac_flags = auto_connect & KMP_ACF_MASK; + ph->mask[0] = mask1; + ph->mask[1] = mask2; + if ( opt_route_options == OFFON_AUTO ) + ph->show_options = show_options; + + if (auto_group_name) + RenameGroupKMP( kmp, sect_ph, ph->gname, ph->ac_mode == KMP_AC_DISPATCH); + + if ( sect_pt == KMP_ENPT ) + AssignRouteInfo(&rtinfo,&kmp->rtobj_enpt); + else + AssignRouteInfo(&rtinfo,&kmp->rtobj_itpt); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextKTPT +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextKTPT()\n"); + + tform_info_t tform_pos; + ResetTformInfo(&tform_pos); + + kcl_fall_t kfall; + ResetKclFall(&kfall); + + ScanParam_t ptab[] = + { + { "KTPT-HEAD-VALUE", SPM_U16, kmp->value+KMP_KTPT }, + TFORM_VARS(tform_pos) + KCL_FALL_VARS(kmp,kfall) + {0} + }; + + List_t *dlist = kmp->dlist + KMP_KTPT; + dlist->used = 0; + + #if HAVE_PRINT0 + si->debug++; + #endif + + for(;;) + { + #if HAVE_PRINT0 + { + ccp eol = FindNextLineFeedSI(si,false); + PRINT("##LINE %4u: %.*s\n",si->line,(int)(eol-si->ptr),si->ptr); + } + #endif + + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + #if HAVE_PRINT0 + { + ccp eol = FindNextLineFeedSI(si,false); + PRINT("##LINE %4u: %.*s\n",si->line,(int)(eol-si->ptr),si->ptr); + } + #endif + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + CheckTformInfo(&tform_pos); + CheckKclFall(&kfall); + continue; + } + + u16 idx; + if (ScanU16SI(si,&idx,1,0)) + break; + + kmp_ktpt_entry_t * entry = AppendList(dlist); + ScanFloatV3SI(si,entry->position,2); + ScanU16SI(si,(u16*)&entry->player_index,1,0); + ScanU16SI(si,(u16*)&entry->unknown,1,si->cur_file->szs_modifier); + + TransformPos(&tform_pos,entry->position); + CalcKclFall(&kfall,entry->position); + CheckEolSI(si); + } + CheckLevelSI(si); + + #if HAVE_PRINT0 + si->debug--; + #endif + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextCKPT +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextCKPT()\n"); + + int auto_mode = 0, auto_next = 0, auto_respawn = 0; + int auto_group_name = 0, rotate_group_links = 0; + memset(kmp->ckpt_auto_respawn,0,sizeof(kmp->ckpt_auto_respawn)); + + ScanParam_t ptab[] = + { + { "AUTO-MODE", SPM_INT, &auto_mode }, + { "AUTO-NEXT", SPM_INT, &auto_next }, + { "AUTO-GROUP-NAME", SPM_INT, &auto_group_name }, + { "AUTO-RESPAWN", SPM_INT, &auto_respawn }, + { "ROTATE-GROUP-LINKS", SPM_INT, &rotate_group_links }, + { "ROUTE-OBJECT-MODE", SPM_INT, &kmp->ckpt_object_mode }, + { "ROUTE-OBJECT1", SPM_INT, &kmp->ckpt_object1 }, + { "ROUTE-OBJECT2", SPM_INT, &kmp->ckpt_object2 }, + { "ROUTE-OBJECT1-SCALE", SPM_VAR, &kmp->ckpt_object1_scale }, + { "ROUTE-OBJECT2-SCALE", SPM_VAR, &kmp->ckpt_object2_scale }, + { "ROUTE-OBJECT1-BASE", SPM_FLOAT, &kmp->ckpt_object1_base }, + { "ROUTE-OBJECT2-BASE", SPM_FLOAT, &kmp->ckpt_object2_base }, + { "CKPT-HEAD-VALUE", SPM_U16, kmp->value+KMP_CKPT }, + { "CKPH-HEAD-VALUE", SPM_U16, kmp->value+KMP_CKPH }, + {0} + }; + + kmp_group_list_t gl; + InitializeGL(&gl,0,kmp,&kmp->ckph); + List_t *ptlist = kmp->dlist + KMP_CKPT; + ptlist->used = 0; + uint last_mode = 0; + float max_x = 0.0, max_y = 0.0; + + const uint max_n = 0x100; + float ftemp[max_n+1][4]; // x,y,unused,width/2 + + enumError err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + if ( ch == '$' ) + { + AddGroupGL(&gl,si,'C',ptlist->used,2); + continue; + } + + u16 idx; + if (ScanU16SI(si,&idx,1,0)) + { + err = ERR_WARNING; + break; + } + + kmp_ckpt_entry_t temp; + ScanFloatV2SI(si,temp.left,2); + + if ( auto_respawn > 0 ) + { + si->no_warn++; + ScanU8SI(si,&temp.respawn,1,0); + si->no_warn--; + } + else + ScanU8SI(si,&temp.respawn,1,0); + + if ( si->cur_file->szs_modifier > 0 ) + { + ScanU8SI(si,&temp.mode,1,1); + ScanU8SI(si,&temp.prev,2,0); + } + else + ScanU8SI(si,&temp.mode,3,0); + CheckEolSI(si); + + kmp_ckpt_entry_t * prev = GetListElem(ptlist,-1,&temp); + if (!ptlist->used) // no previous point available + gl.line.n = 1; + else if ( gl.line.n > max_n ) + gl.line.n = max_n; + + float *p1 = 0, *p2 = 0; + if ( gl.line.n > 1 ) + { + if ( gl.line.line_mode == LM_BORDER ) + { + p1 = ftemp[0]; + memcpy(p1,prev->left,4*sizeof(float)); + p2 = ftemp[gl.line.n]; + memcpy(p2,temp.left,4*sizeof(float)); + } + else + { + p1 = ftemp[0]; + p1[0] = ( prev->left[0] + prev->right[0] ) / 2; + p1[1] = ( prev->left[1] + prev->right[1] ) / 2; + p1[2] = 0.0; + double dx = prev->left[0] - prev->right[0]; + double dy = prev->left[1] - prev->right[1]; + p1[3] = sqrt ( dx*dx + dy*dy ) / 2; + + p2 = ftemp[gl.line.n]; + p2[0] = ( temp.left[0] + temp.right[0] ) / 2; + p2[1] = ( temp.left[1] + temp.right[1] ) / 2; + p2[2] = 0.0; + dx = temp.left[0] - temp.right[0]; + dy = temp.left[1] - temp.right[1]; + p2[3] = sqrt ( dx*dx + dy*dy ) / 2; + + if ( gl.line.line_mode == LM_BEZIER && !gl.line.n_pt ) + { + double angle1 = CalcDirection2F(prev->left,prev->right); + double angle2 = CalcDirection2F(temp.left,temp.right); + PRINT("BINGO 2: %7.2f %7.2f -> %7.2f %7.2f\n", + angle1, angle2, + fmod(angle1+M_PI,M_PI), fmod(angle2+M_PI,M_PI) ); + + double dx = p1[0] - p2[0]; + double dy = p1[1] - p2[1]; + double range = sqrt( dx*dx + dy*dy ) * 0.707; // sin(pi/4) + + gl.line.pt[0][0] = p1[0] + cos(angle1) * range; + gl.line.pt[0][1] = p1[1] - sin(angle1) * range; + gl.line.pt[1][0] = p2[0] - cos(angle2) * range; + gl.line.pt[1][1] = p2[1] + sin(angle2) * range; + gl.line.n_pt = 2; + PRINT("\n-> %11.3f %11.3f\n" + " %11.3f %11.3f\n" + " %11.3f %11.3f\n" + " %11.3f %11.3f\n", + p1[0], p1[1], + gl.line.pt[0][0], gl.line.pt[0][1], + gl.line.pt[1][0], gl.line.pt[1][1], + p2[0], p2[1] ); + } + } + } + + uint p; + for ( p = 1; p <= gl.line.n; p++ ) + { + const uint grp_idx = AddPointGL(&gl,ptlist->used); + kmp_ckpt_entry_t * entry = AppendList(ptlist); + memcpy(entry,&temp,sizeof(*entry)); + if ( p < gl.line.n ) + CalcLine(&gl.line,ftemp[p],p1,p2,4,p); + + switch (auto_mode) + { + //case KMP_AM_NORM: // normalize modes + // --> delayed + // break; + + case KMP_AM_RENUMBER: // renumber modes + if (!entry->mode) + last_mode = 0; + else if (!IS_M1(entry->mode)) + entry->mode = ++last_mode; + break; + + case KMP_AM_1_LAP: // define modes as 1 lap race + entry->mode = ptlist->used < 7 ? ptlist->used % 2 - 1 : -1; + break; + + case KMP_AM_SHORT: // define modes to get a very short race + entry->mode = ptlist->used % 2 - 1; + break; + + case KMP_AM_UNLIMIT: // remove lap counter for unlimited race + { + static const u8 tab[] = { 0, 3,0,1,2 }; + entry->mode = ptlist->used < sizeof(tab)/sizeof(*tab) + ? tab[ptlist->used] : -1; + + if ( max_x < entry->left[0] ) + max_x = entry->left[0]; + if ( max_x < entry->right[0] ) + max_x = entry->right[0]; + + if ( max_y < entry->left[1] ) + max_y = entry->left[1]; + if ( max_y < entry->right[1] ) + max_y = entry->right[1]; + } + break; + + default: + if ( !IS_M1(entry->mode) ) + last_mode = entry->mode; + break; + } + + if (auto_next) + { + entry->next = ~0; + entry->prev = grp_idx ? ptlist->used - 2 : ~0; + if ( grp_idx > 0 && IS_M1(entry[-1].next) ) + entry[-1].next = ptlist->used - 1; + } + + if ( auto_respawn > 0 ) + { + const uint idx = ptlist->used - 1; + if ( idx < KMP_MAX_POINT ) + kmp->ckpt_auto_respawn[idx] = 1; + } + } + + if ( gl.line.line_mode == LM_BORDER ) + { + // now copy the calculated values + for ( p = 1; p < gl.line.n; p++ ) + { + kmp_ckpt_entry_t * entry = prev + p; + memcpy(entry,ftemp[p],4*sizeof(float)); + } + } + else + { + // now calculate angle and real positions + for ( p = 1; p < gl.line.n; p++ ) + { + kmp_ckpt_entry_t * entry = prev + p; + float (*f)[4] = ftemp + p-1; + double angle1 = CalcDirection2F(f[0],f[1]); + double angle2 = CalcDirection2F(f[1],f[2]); + noPRINT("%2u: %6.0f %6.0f %6.0f | %6.0f %6.0f %6.0f" + " | %6.0f %6.0f %6.0f | %7.2f %7.2f\n", + p, + f[0][0],f[0][1],f[0][3], f[1][0],f[1][1],f[1][3], f[2][0],f[2][1],f[2][3], + angle1 * 180.0 / M_PI, angle2 * 180.0 / M_PI ); + + angle1 = ( angle1 + angle2 ) / 2; + double d = cos(angle1) * f[1][3]; + entry->left [0] = f[1][0] + d; + entry->right[0] = f[1][0] - d; + d = sin(angle1) * f[1][3]; + entry->left [1] = f[1][1] - d; + entry->right[1] = f[1][1] + d; + } + } + InitializeLine(&gl.line); + } + + if ( auto_mode == KMP_AM_NORM ) + { + uint min_search = 1, next_val = 1; + kmp_ckpt_entry_t *pt_end = (kmp_ckpt_entry_t*)ptlist->list + ptlist->used; + for(;;) + { + uint min_found = 0xff; + kmp_ckpt_entry_t *pt; + for ( pt = (kmp_ckpt_entry_t*)ptlist->list; pt < pt_end; pt++ ) + if ( pt->mode >= min_search && pt->mode < min_found ) + min_found = pt->mode; + + if ( min_found == 0xff ) + break; + + for ( pt = (kmp_ckpt_entry_t*)ptlist->list; pt < pt_end; pt++ ) + if ( pt->mode == min_found ) + pt->mode = next_val; + + next_val++; + min_search = min_found + 1; + } + } + else if ( auto_mode == KMP_AM_UNLIMIT && ptlist->used > 3 ) + { + kmp_ckpt_entry_t *pt = ((kmp_ckpt_entry_t*)ptlist->list) + 3; + pt->left[0] = max_x + 20000.0; + pt->left[1] = max_y + 20000.0; + pt->right[0] = max_x + 22000.0; + pt->right[1] = max_y + 22000.0; + } + else if ( auto_mode == KMP_AM_SHORT || auto_mode == KMP_AM_1_LAP ) + kmp->multi_ckpt_mode_0_ok = true; + + TermGL(&gl,si,kmp,KMP_CKPH,rotate_group_links); + if (auto_group_name) + RenameGroupKMP(kmp,KMP_CKPH,kmp->ckph.gname,false); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int get_defobj_id ( ScanInfo_t * si, u16 min_id, ccp cmd ) +{ + u16 obj_id; + const enumError err = ScanU16SI(si,&obj_id,1,0); + if (err) + return -1; + + ScanFile_t *sf = si->cur_file; + if (min_id) + { + if ( obj_id >= min_id && obj_id <= min_id+0xfff ) + return obj_id; + } + else if (IsDefinitionObjectId(obj_id)) + return obj_id; + + sf->ptr = sf->prev_ptr; + WarnIgnoreSI(si,"Invalid id for %s (value %#x). ",cmd,obj_id); + GotoEolSI(si); + return -1; +} + +//----------------------------------------------------------------------------- + +static u16 negate_ref_id ( u16 ref_id ) +{ + if (IsConditionRefEngineId(ref_id)) + return ref_id ^ GOBJ_MASK_COND_ENGINE; + + if (IsConditionRefRandomId(ref_id)) + return ref_id ^ GOBJ_MASK_COND_RND; + +// if (IsConditionRefTestId(ref_id)) +// return ref_id ^ GOBJ_MASK_COND_RND; + + return ref_id ^ 1; +} + +//----------------------------------------------------------------------------- + +static void set_ref_id ( List_t *dlist, u16 ref_id, int mode ) +{ + // mode: <0: do nothing, =0:disable GOBJ_M_LECODE, >0:enable GOBJ_M_LECODE + + kmp_gobj_entry_t *entry = GetListElem(dlist,-1,0); + if (entry) + { + if ( !mode || IsDefinitionObjectId(entry->obj_id) ) + entry->obj_id &= ~GOBJ_M_LECODE; + else if ( mode > 0 ) + entry->obj_id |= GOBJ_M_LECODE; + + entry->ref_id = ref_id; + entry->pflags = entry->pflags & ~GOBJ_M_PF_MODE | 1 << GOBJ_S_PF_MODE; + } +} + +//----------------------------------------------------------------------------- + +static void disable_enable ( ScanInfo_t *si, List_t *dlist, bool enable ) +{ + DEFINE_VAR(var); + if (!ScanExprSI(si,&var)) + { + int ref_id = GetIntV(&var); + if ( ref_id < 0 ) + { + ref_id = -ref_id; + enable = !enable; + } + + if ( IsConditionRefId(ref_id) || IsConditionRefTestId(ref_id) ) + { + if (!enable) + ref_id = negate_ref_id(ref_id); + set_ref_id(dlist,ref_id,enable); + } + else if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ScanFile_t *sf = si->cur_file; + ERROR0(ERR_WARNING, + "Invalid ID %#x for predefinied condition [%s @%u]:\n%.*s\n", + ref_id, sf->name, sf->line, (int)(eol-sf->prev_ptr), sf->prev_ptr ); + } + } + FreeV(&var); + CheckEolSI(si); +} + +//----------------------------------------------------------------------------- + +static void disable_enable_by + ( ScanInfo_t *si, List_t *dlist, bool enable, bool not ) +{ + DEFINE_VAR(var); + if (!ScanExprSI(si,&var)) + { + int ref_id = GetIntV(&var); + if ( ref_id < 0 ) + { + ref_id = -ref_id; + enable = !enable; + } + + if (IsDefinitionObjectId(ref_id)) + { + if (not) + ref_id |= GOBJ_DEF_F_NOT; + else + ref_id &= ~GOBJ_DEF_F_NOT; + set_ref_id(dlist,ref_id,enable); + } + else if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ScanFile_t *sf = si->cur_file; + ERROR0(ERR_WARNING, + "Invalid ID %#x for a reference to a definition-object [%s @%u]:\n%.*s\n", + ref_id, sf->name, sf->line, (int)(eol-sf->prev_ptr), sf->prev_ptr ); + } + } + FreeV(&var); + CheckEolSI(si); +} + +//----------------------------------------------------------------------------- + +static enumError ScanTextGOBJ +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextGOBJ()\n"); + + tform_info_t tform_pos; + ResetTformInfo(&tform_pos); + + kcl_fall_t kfall; + ResetKclFall(&kfall); + + int auto_object_name = 0, sort_objects = 0; + int fix_objdef = 0, sort_objdef = 0; + int fix_reference = 0, sort_reference = 0; + uint auto_enemy_item = 0, auto_enemy_index = 32; + ScanParam_t ptab[] = + { + { "GOBJ-HEAD-VALUE", SPM_U16, kmp->value+KMP_GOBJ }, + { "AUTO-ENEMY-ITEM", SPM_UINT, &auto_enemy_item }, + { "AUTO-OBJECT-NAME", SPM_INT, &auto_object_name }, + { "SORT-OBJECTS", SPM_INT, &sort_objects }, + + { "FIX-OBJDEF", SPM_INT, &fix_objdef }, + { "SORT-OBJDEF", SPM_INT, &sort_objdef }, + { "FIX-REFERENCE", SPM_INT, &fix_reference }, + { "SORT-REFERENCE", SPM_INT, &sort_reference }, + + TFORM_VARS(tform_pos) + KCL_FALL_VARS(kmp,kfall) + {0} + }; + + List_t *dlist = kmp->dlist + KMP_GOBJ; + dlist->used = 0; + + bool ignore_itembox + = kmp->rtobj_enpt.obj == GOBJ_ITEMBOX + || kmp->rtobj_itpt.obj == GOBJ_ITEMBOX + || kmp->rtobj_jgpt.obj == GOBJ_ITEMBOX + || kmp->rtobj_cnpt.obj == GOBJ_ITEMBOX + || kmp->rtobj_mspt.obj == GOBJ_ITEMBOX; + if (!ignore_itembox) + { + uint pi; + for ( pi = 0; pi < kmp->n_rtobj_poti; pi++ ) + if ( kmp->rtobj_poti[pi].rtobj.obj == GOBJ_ITEMBOX ) + { + ignore_itembox = true; + break; + } + } + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + CheckTformInfo(&tform_pos); + CheckKclFall(&kfall); + continue; + } + + if ( ch == '$' ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + char name[KMP_MAX_NAME_LEN+1]; + ScanNameSI(si,name,sizeof(name),true,true,0); + int start_index = -1; + u16 min_id = 0x2000; + + if (!strcmp(name,"$DEFOBJ-RACE")) + start_index = KMP_GMODE_VERSUS; + else if (!strcmp(name,"$DEFOBJ-BATTLE")) + start_index = KMP_GMODE_BALLOON; + else if (!strcmp(name,"$DEFOBJ-OR")) + { + start_index = KMP_GMODE_BALLOON; + min_id = 0x4000; + } + else if (!strcmp(name,"$DEFOBJ-AND")) + { + start_index = KMP_GMODE_BALLOON; + min_id = 0x6000; + } + else if (!strcmp(name,"$DISABLE")) + { + disable_enable(si,dlist,false); + continue; + } + else if (!strcmp(name,"$ENABLE")) + { + disable_enable(si,dlist,true); + continue; + } + else if (!strcmp(name,"$DISABLE-BY")) + { + disable_enable_by(si,dlist,false,true); + continue; + } + else if (!strcmp(name,"$ENABLE-BY")) + { + disable_enable_by(si,dlist,true,false); + continue; + } + else if (!strcmp(name,"$DISABLE-BY-NOT")) + { + disable_enable_by(si,dlist,false,false); + continue; + } + else if (!strcmp(name,"$ENABLE-BY-NOT")) + { + disable_enable_by(si,dlist,true,true); + continue; + } + else if (!strcmp(name,"$REFERENCE")) + { + int ref_id = get_defobj_id(si,0,name); + if ( ref_id >= 0 ) + set_ref_id(dlist,ref_id,0); + continue; + } + else + { + sf->ptr = sf->prev_ptr; + WarnIgnoreSI(si,"Unknown keyword »%s«.",name); + GotoEolSI(si); + continue; + } + + //-- $DEFOBJ-RACE | $DEFOBJ-BATTLE | $DEFOBJ-OR | $DEFOBJ-AND + + int obj_id = get_defobj_id(si,min_id,name); + if ( obj_id < 0 ) + continue; + + kmp_gobj_entry_t * entry = AppendList(dlist); + entry->obj_id = obj_id; + entry->route_id = ~0; + entry->pflags = GOBJ_PF_MODE1; + + u16 val = 0; + int index = 0; + DEFINE_VAR(var); + while ( start_index < 8 ) + { + if (!ScanExprSI(si,&var)) + val = GetIntV(&var); + while ( index <= start_index ) + entry->setting[index++] = val; + start_index++; + + if (!SkipCharSI(si,',')) + break; + } + + if ( min_id == 0x2000 ) + while ( index < 8 ) + entry->setting[index++] = val; + + CheckEolSI(si); + FreeV(&var); + continue; + } + + enumError err; + ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_GOBJ,0); + if (err) + break; + + kmp_gobj_entry_t * entry = AppendList(dlist); + + if ( si->cur_file->szs_modifier > 0 ) + { + ScanU16SI(si,&entry->obj_id,1,1); + ScanU16SI(si,&entry->ref_id,1,0); + ScanFloatV3SI(si,entry->position,3); + ScanBE32SI_swap(si,(u32*)&entry->route_id,5,1); + } + else + { + ScanU16SI(si,&entry->obj_id,1,0); + ScanFloatV3SI(si,entry->position,1); + ScanU16SI(si,entry->setting,4,0); + ScanU16SI(si,&entry->route_id,1,0); + ScanU16SI(si,&entry->ref_id,1,0); + ScanFloatV3SI(si,entry->rotation,1); + ScanU16SI(si,entry->setting+4,4,0); + ScanU16SI(si,&entry->pflags,1,0); + ScanFloatV3SI(si,entry->scale,1); + } + + TransformPos(&tform_pos,entry->position); + CalcKclFall(&kfall,entry->position); + CheckEolSI(si); + + if ( ignore_itembox + && entry->obj_id < N_KMP_GOBJ + && ObjectInfo[entry->obj_id].flags & OBF_ITEMBOX ) + { + dlist->used--; + } + else if ( auto_enemy_item + && entry->obj_id == GOBJ_ITEMBOX + && !entry->setting[2] ) + { + for(;;) + { + if ( auto_enemy_index > 32 ) + auto_enemy_index = 0; + if ( auto_enemy_item & ( 1 << auto_enemy_index++ ) ) + break; + } + entry->setting[2] = auto_enemy_index - 1; + } + } + CheckLevelSI(si); + + FixDefObj(kmp,fix_objdef); + FixReference(kmp,fix_reference); + + if (SortGOBJ(kmp,sort_objects)) + auto_object_name = 1; + + if (SortDefObj(kmp,sort_objdef)) + auto_object_name = 1; + + if (SortReference(kmp,sort_reference)) + auto_object_name = 1; + + if ( auto_object_name > 0 ) + { + ResetIL(kmp->index+KMP_GOBJ); + FillIL(kmp->index+KMP_GOBJ,dlist->used); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextAREA +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextAREA()\n"); + + ScanParam_t ptab[] = + { + { "AREA-HEAD-VALUE", SPM_U16, kmp->value+KMP_AREA }, + {0} + }; + + List_t *dlist = kmp->dlist + KMP_AREA; + dlist->used = 0; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + enumError err; + ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_AREA,2); + if (err) + break; + + kmp_area_entry_t * entry = AppendList(dlist); + + if ( si->cur_file->szs_modifier > 0 ) + { + ScanBE32SI(si,(u32*)&entry->mode,1,1); + ScanFloatV3SI(si,entry->position,3); + ScanBE32SI_swap(si,(u32*)entry->setting,2,1); + } + else if ( si->cur_file->revision < 2956 ) + { + // 2011-08-26, v0.17a, r2956 -> new table layout + + ScanBE16SI(si,(u16*)&entry->mode,2,0); + ScanFloatV3SI(si,entry->position,1); + ScanU16SI(si,entry->setting,4,0); + ScanFloatV3SI(si,entry->rotation,1); + ScanFloatV3SI(si,entry->scale,1); + } + else + { + ScanU8SI(si,&entry->mode,2,0); + ScanFloatV3SI(si,entry->position,1); + ScanU16SI(si,entry->setting,4,0); + ScanU8SI(si,&entry->dest_id,2,0); + ScanFloatV3SI(si,entry->rotation,1); + ScanFloatV3SI(si,entry->scale,1); + } + CheckEolSI(si); + } + CheckLevelSI(si); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextCAME +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextCAME()\n"); + + u8 *value = (u8*)(kmp->value + KMP_CAME); + ScanParam_t ptab[] = + { + { "CAME-HEAD-VALUE", SPM_U16, (u16*)value }, + #ifdef LITTLE_ENDIAN + { "OPENING-INDEX", SPM_U8, value+1 }, + { "SELECTION-INDEX", SPM_U8, value+0 }, + #else + { "OPENING-INDEX", SPM_U8, value+0 }, + { "SELECTION-INDEX", SPM_U8, value+1 }, + #endif + {0} + }; + + List_t *dlist = kmp->dlist + KMP_CAME; + dlist->used = 0; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + enumError err; + ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_CAME,2); + if (err) + break; + + kmp_came_entry_t * entry = AppendList(dlist); + + uint zoom_speed = 0, viewpt_speed = 0; + + if ( si->cur_file->szs_modifier > 0 ) + { + ScanBE32SI(si,(u32*)&entry->type,1,1); + ScanBE32SI_swap(si,(u32*)&entry->came_speed,2,1); + ScanFloatV3SI(si,entry->position,2); + ScanFloatSI(si,&entry->zoom_begin,2); + ScanFloatV3SI(si,entry->viewpt_begin,2); + ScanFloatSI(si,&entry->time,1); + zoom_speed = entry->zoom_speed; + viewpt_speed = entry->viewpt_speed; + } + else if ( si->cur_file->revision < 2955 ) + { + // 2011-08-26, v0.17a, r2956 -> new table layout + + ScanU8SI(si,&entry->type,4,0); + ScanU16SI(si,&entry->came_speed,1,0); + ScanFloatV3SI(si,entry->position,1); + ScanFloatSI(si,&entry->time,1); + ScanU32SI(si,&zoom_speed,1,0); + ScanFloatV3SI(si,entry->rotation,1); + ScanU32SI(si,&viewpt_speed,1,0); + ScanFloatSI(si,&entry->zoom_begin,2); + ScanU16SI(si,&entry->unknown_0x0a,1,0); + ScanFloatV3SI(si,entry->viewpt_begin,2); + } + else + { + ScanU8SI(si,&entry->type,1,0); + ScanU16SI(si,&entry->came_speed,1,0); + ScanFloatV3SI(si,entry->position,1); + ScanFloatSI(si,&entry->zoom_begin,1); + ScanU8SI(si,&entry->next,1,0); + ScanU32SI(si,&zoom_speed,1,0); + ScanFloatV3SI(si,entry->rotation,1); + ScanFloatSI(si,&entry->zoom_end,1); + ScanU8SI(si,&entry->unknown_0x02,1,0); + ScanU32SI(si,&viewpt_speed,1,0); + ScanFloatV3SI(si,entry->viewpt_begin,1); + ScanU8SI(si,&entry->route,1,0); + ScanU16SI(si,&entry->unknown_0x0a,1,0); + ScanFloatV3SI(si,entry->viewpt_end,1); + ScanFloatSI(si,&entry->time,1); + } + CheckEolSI(si); + + const double zoom_len = fabs(entry->zoom_begin-entry->zoom_end); + entry->zoom_speed = zoom_speed; + if ( zoom_speed == ~(u32)0 ) + entry->zoom_speed = entry->time >= 0.001 + ? (uint)floor( 100.0 * zoom_len / entry->time ) + : 1; + + const double viewpt_len = LengthF(entry->viewpt_begin,entry->viewpt_end); + entry->viewpt_speed = viewpt_speed; + if ( viewpt_speed == ~(u32)0 ) + entry->viewpt_speed = entry->time >= 0.001 + ? (uint)floor( 100.0 * viewpt_len / entry->time ) + : 1; + + DefineIndexDouble(si,"CAME.TIME",entry->time); + DefineIndexDouble(si,"ZOOM.LEN",zoom_len); + DefineIndexDouble(si,"VIEWPT.LEN",viewpt_len); + } + CheckLevelSI(si); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextPT2 +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si, // valid data + uint sect // KMP section index +) +{ + DASSERT(kmp); + DASSERT(si); + DASSERT(sectjgpt_snap2; + snap_to_enpt = &kmp->jgpt_snap; + hsnap_to_enpt = &kmp->jgpt_hsnap; + rtobj = &kmp->rtobj_jgpt; + auto_define = &kmp->jgpt_auto_define; + auto_distance = &kmp->jgpt_auto_distance; + auto_adjust = &kmp->jgpt_auto_adjust; + auto_remove = &kmp->jgpt_auto_remove; + kf = &kmp->jgpt_flag; + break; + + case KMP_CNPT: + snap2_to_enpt = &kmp->cnpt_snap2; + snap_to_enpt = &kmp->cnpt_snap; + hsnap_to_enpt = &kmp->cnpt_hsnap; + rtobj = &kmp->rtobj_cnpt; + break; + + case KMP_MSPT: + snap2_to_enpt = &kmp->mspt_snap2; + snap_to_enpt = &kmp->mspt_snap; + hsnap_to_enpt = &kmp->mspt_hsnap; + rtobj = &kmp->rtobj_mspt; + break; + + default: + var_dummy.mode = VAR_UNSET; + snap2_to_enpt = &var_dummy; + snap_to_enpt = &var_dummy; + hsnap_to_enpt = &var_dummy; + break; + } + + rtobj_info_t rtinfo; + ResetRouteInfo(&rtinfo,&kmp->rtobj); + + tform_info_t tform_pos; + ResetTformInfo(&tform_pos); + + kcl_fall_t kfall; + ResetKclFall(&kfall); + + int auto_id = 0; + char hd_name[20]; + snprintf( hd_name, sizeof(hd_name), + "%s-HEAD-VALUE", kmp_section_name[sect].name1 ); + ScanParam_t ptab[] = + { + { hd_name, SPM_U16, kmp->value+sect }, + { "AUTO-ID", SPM_INT, &auto_id }, + { "SNAP2-TO-ENPT", SPM_VAR, snap2_to_enpt }, + { "SNAP-TO-ENPT", SPM_VAR, snap_to_enpt }, + { "HSNAP-TO-ENPT", SPM_VAR, hsnap_to_enpt }, + { "AUTO-DEFINE", SPM_INT, auto_define }, + { "AUTO-DISTANCE", SPM_FLOAT, auto_distance }, + { "AUTO-ADJUST", SPM_VAR, auto_adjust }, + { "AUTO-REMOVE", SPM_INT, auto_remove }, + { "EXPORT-FLAGS", SPM_INT, &export_flags }, + ROUTE_OBJECT_VARS(rtinfo) + TFORM_VARS(tform_pos) + KCL_FALL_VARS(kmp,kfall) + {0} + }; + + List_t *dlist = kmp->dlist + sect; + dlist->used = 0; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + CheckRouteInfo(&rtinfo); + CheckTformInfo(&tform_pos); + CheckKclFall(&kfall); + continue; + } + + enumError err; + ScanIndexSI(si,&err,dlist->used,kmp->index+sect,2); + if (err) + break; + + kmp_jgpt_entry_t * entry = AppendList(dlist); + ScanFloatV3SI(si,entry->position,2); + ScanU16SI(si,(u16*)&entry->id,1,0); + ScanU16SI(si,(u16*)&entry->effect,1,si->cur_file->szs_modifier); + + const u8 flags = !kf ? KPFL_M_ALL + : NextCharSI(si,false) ? ScanPtFlags(si) : kf->mask; + if ( kf && (uint)(dlist->used-1) < KMP_MAX_POINT ) + kf->flags[dlist->used-1] = flags; + + TransformPos(&tform_pos,entry->position); + if ( flags & KPFL_FALL ) + CalcKclFall(&kfall,entry->position); + CheckEolSI(si); + + if (auto_id) + entry->id = dlist->used - 1; + } + CheckLevelSI(si); + + PRINT_IF( snap2_to_enpt && snap2_to_enpt->mode, + "SNAP-TO-ENPT: %d -> jgpt=%d, cnpt=%d, mspt=%d\n", + snap2_to_enpt->mode, kmp->jgpt_snap2.mode, + kmp->cnpt_snap2.mode, kmp->mspt_snap2.mode ); + PRINT_IF( snap_to_enpt && snap_to_enpt->mode, + "SNAP-TO-ENPT: %d -> jgpt=%d, cnpt=%d, mspt=%d\n", + snap_to_enpt->mode, kmp->jgpt_snap.mode, + kmp->cnpt_snap.mode, kmp->mspt_snap.mode ); + + if (kf) + kf->export = opt_export_flags ? opt_export_flags>0 : export_flags; + + if (rtobj) + AssignRouteInfo(&rtinfo,rtobj); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextSTGI +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextSTGI()\n"); + + ScanParam_t ptab[] = + { + { "STGI-HEAD-VALUE", SPM_U16, kmp->value+KMP_STGI }, + {0} + }; + + List_t *dlist = kmp->dlist + KMP_STGI; + dlist->used = 0; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + enumError err; + ScanIndexSI(si,&err,dlist->used,kmp->index+KMP_STGI,2); + if (err) + break; + + kmp_stgi_entry_t * entry = AppendList(dlist); + if ( si->cur_file->revision < 3379 ) + { + // 2012-01-28, v0.26a, r3379 + ScanBE16SI(si,(u16*)&entry->lap_count,6,0); + entry->flare_color = be32(&entry->flare_color); + } + else + { + ScanU8SI(si,&entry->lap_count,4,0); + ScanU32SI(si,&entry->flare_color,1,0); + ScanU8SI(si,&entry->flare_alpha,2,0); + + DEFINE_VAR(var); + ScanExprSI(si,&var); + if ( var.mode == VAR_DOUBLE || var.mode == VAR_VECTOR ) + entry->speed_mod = SpeedMod2u16(GetDoubleV(&var)); + else + { + u8 v2; + ScanU8SI(si,&v2,1,0); + entry->speed_mod = GetIntV(&var) << 8 | v2; + } + } + CheckEolSI(si); + } + CheckLevelSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextSETUP +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + rtobj_info_t rtinfo; + ResetRouteInfo(&rtinfo,&kmp->rtobj); + + ScanParam_t ptab[] = + { + ROUTE_OBJECT_VARS(rtinfo) + {0} + }; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + CheckRouteInfo(&rtinfo); + continue; + } + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + ScanU32SI(si,(u32*)&kmp->revision,1,0); + si->init_revision = si->cur_file->revision = kmp->revision; + DefineIntVar(&si->gvar,"REVISION$SETUP",kmp->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",kmp->revision); + } + else if (!strcmp(name,"KCL-FALL-ADD-ROAD")) + { + DEFINE_VAR(val); + if ( ScanExprSI(si,&val) <= ERR_WARNING ) + kmp->enable_fall_kcl = GetBoolV(&val); + } + else if (!strcmp(name,"KMP-WIM0")) + { + DEFINE_VAR(val); + if ( ScanExprSI(si,&val) <= ERR_WARNING ) + kmp->wim0_export = GetBoolV(&val) || opt_wim0 > 0; + } + // "UNKNOWN-0X0C" replaced by "KMP-VERSION" in 2020-01 + else if ( !strcmp(name,"KMP-VERSION") || !strcmp(name,"UNKNOWN-0X0C") ) + ScanU32SI(si,&kmp->kmp_version,1,0); + else if (!strcmp(name,"INFO")) + ConcatStringsSI(si,&kmp->info,0); + else + GotoEolSI(si); + CheckEolSI(si); + } + CheckLevelSI(si); + if (rtinfo.valid) + AssignRouteInfo(&rtinfo,&kmp->rtobj); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextKMP() route functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct route_info_t +{ + int index; // index of route + + int f_xss; + int f_yss; + int f_zss; + int f_scale; + int f_shift; + int f_hrotate; // [[[hrot]] [[2do]] + + rtobj_info_t rtinfo; + kcl_fall_t kfall; + + double xss[4]; + double yss[4]; + double zss[4]; + Var_t scale[2]; + Var_t shift; + Var_t hrotate[2]; // [[[hrot]] [[2do]] + int reverse_order; + + double route_time; // >0.0: recalc setting 1 + + int show_route; // >0: enable itemboxes for this route + +} route_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void CloseRoute +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si, // valid data + const kmp_poti_group_t + * grp, // group to close + route_info_t * ri // route info data +) +{ + DASSERT(kmp); + DASSERT(grp); + DASSERT(ri); + + const uint np = grp->n_point; + DASSERT( np <= kmp->poti_point.used ); + + kmp_poti_point_t *pp0 = (kmp_poti_point_t*)kmp->poti_point.list + + kmp->poti_point.used - np; + + //--- xss/yss/zss + + if ( ri->f_xss && fabs(ri->xss[0]-ri->xss[2]) < 1e-6 ) + ri->f_xss = 0; + + if ( ri->f_yss && fabs(ri->yss[0]-ri->yss[2]) < 1e-6 ) + ri->f_yss = 0; + + if ( ri->f_zss && fabs(ri->zss[0]-ri->zss[2]) < 1e-6 ) + ri->f_zss = 0; + + if ( ri->f_xss || ri->f_yss || ri->f_zss ) + { + ri->f_shift = 1; + ri->f_scale = 1; + AssignVectorV3(&ri->shift,0.0,0.0,0.0); + AssignVectorV3(ri->scale,1.0,1.0,1.0); + + if (ri->f_xss) + { + double *d = ri->xss; + PRINT("XSS[%d]: %11.3f %11.3f | %11.3f %11.3f\n", + ri->f_xss, d[0], d[1], d[2], d[3] ); + const double scale = ( d[3] - d[1] ) / ( d[2] - d[0] ); + ri->scale[0].x = scale; + ri->shift.x = d[1] - d[0] * scale; + } + + if (ri->f_yss) + { + double *d = ri->yss; + PRINT("YSS[%d]: %11.3f %11.3f | %11.3f %11.3f\n", + ri->f_yss, d[0], d[1], d[2], d[3] ); + const double scale = ( d[3] - d[1] ) / ( d[2] - d[0] ); + ri->scale[0].y = scale; + ri->shift.y = d[1] - d[0] * scale; + } + + if (ri->f_zss) + { + double *d = ri->zss; + PRINT("ZSS[%d]: %11.3f %11.3f | %11.3f %11.3f\n", + ri->f_zss, d[0], d[1], d[2], d[3] ); + const double scale = ( d[3] - d[1] ) / ( d[2] - d[0] ); + ri->scale[0].z = scale; + ri->shift.z = d[1] - d[0] * scale; + } + } + + + //--- scale + + if ( ri->f_scale > 1 ) + { + Var_t *d = ri->scale; + ToVectorV(d+0); + ToVectorV(d+1); + PRINT("SCALE[%d]: %11.3f %11.3f %11.3f | %11.3f %11.3f %11.3f\n", + ri->f_scale, d[0].x, d[0].y, d[0].z, d[1].x, d[1].y, d[1].z ); + + kmp_poti_point_t *pp = pp0; + uint i; + for ( i = 0; i < np; i++, pp++ ) + { + pp->position[0] = ( pp->position[0] - d[1].x ) * d[0].x + d[1].x; + pp->position[1] = ( pp->position[1] - d[1].y ) * d[0].y + d[1].y; + pp->position[2] = ( pp->position[2] - d[1].z ) * d[0].z + d[1].z; + } + } + else if ( ri->f_scale > 0 ) + { + Var_t *d = ri->scale; + ToVectorV(d+0); + PRINT("SCALE[%d]: %11.3f %11.3f %11.3f\n", + ri->f_scale, d->x, d->y, d->z ); + + kmp_poti_point_t *pp = pp0; + uint i; + for ( i = 0; i < np; i++, pp++ ) + { + pp->position[0] *= d->x; + pp->position[1] *= d->y; + pp->position[2] *= d->z; + } + } + + + //--- shift + + if ( ri->f_shift ) + { + Var_t *d = &ri->shift; + ToVectorV(d); + PRINT("SHIFT[%d]: %11.3f %11.3f %11.3f\n", + ri->f_shift, d->x, d->y, d->z ); + + kmp_poti_point_t *pp = pp0; + uint i; + for ( i = 0; i < np; i++, pp++ ) + { + pp->position[0] += d->x; + pp->position[1] += d->y; + pp->position[2] += d->z; + } + } + + + //--- hrotate // [[[hrot]] [[2do]] + + if ( ri->f_hrotate ) + { + Var_t *d = ri->hrotate; + ToVectorV(d+1); + double rot = GetDoubleV(d++) * (M_PI/180.0); + PRINT("HROTATE[%d]: %11.3f %11.3f %11.3f -> %6.3f\n", + ri->f_hrotate, d->x, d->y, d->z, rot ); + + kmp_poti_point_t *pp = pp0; + uint i; + for ( i = 0; i < np; i++, pp++ ) + { + const double dx = pp->position[0] - d->x; + const double dz = pp->position[2] - d->z; + const double width = sqrt ( dx*dx + dz*dz ); + if ( width >= 1e-6 ) + { + const double new_rot = atan2(dx,dz) + rot; + pp->position[0] = d->x + sin(new_rot) * width; + pp->position[2] = d->y + cos(new_rot) * width; + } + } + } + + + //--- reverse order + + if (ri->reverse_order) + { + kmp_poti_point_t *pp1 = pp0; + kmp_poti_point_t *pp2 = pp0 + np - 1; + while ( pp1 < pp2 ) + { + noPRINT("P: %p %p / %zd %zd\n",pp1,pp2,pp1-pp0,pp2-pp0); + kmp_poti_point_t temp; + memcpy(&temp,pp1, sizeof(temp)); + memcpy(pp1++,pp2, sizeof(*pp1)); + memcpy(pp2--,&temp,sizeof(*pp2)); + } + } + + + //--- define variables + + uint i; + double total_len = 0.0, total_tim = 0.0; + kmp_poti_point_t *pp = pp0; + for ( i = 0; i < np-1; i++, pp++ ) + { + const double len = LengthF(pp->position,pp[1].position); + total_len += len; + if ( pp->speed > 0 ) + total_tim += len / pp->speed; + } + + DefineIndexDouble(si,"LEN",total_len); + DefineIndexDouble(si,"TIME",total_tim); + DefineIndexDouble(si,"TIME.SET",total_tim); + + + //--- set route time + + if ( ri->route_time > 0.0 && total_tim > 0.0 ) + { + PRINT("SET ROUTE TIME %s, N=%d+%u, time=%4.2f, len=%5.3f, set=%4.2f\n", + last_index_name, kmp->poti_point.used - np, np, + total_tim, total_len, ri->route_time ); + + + const double factor = ri->route_time / total_tim; + double sum_src = 0.0, sum_dest = 0.0; + pp = pp0; + for ( i = 0; i < np-1; i++, pp++ ) + { + if ( pp->speed > 0 ) + { + const double len = LengthF(pp->position,pp[1].position); + const double tim = len / pp->speed; + sum_src += tim; + const double wanted = sum_src * factor - sum_dest; + DASSERT(wanted>=0.0); + pp->speed = wanted > 0.0 ? floor( len / wanted + 0.5 ) : 0.0; + if (!pp->speed) + pp->speed = 1; + sum_dest += len / pp->speed; + noPRINT("%6.2f/%4.2f - %6.2f/%4.2f - %6.2f -> %5u\n", + sum_src/60.0, total_tim, + sum_dest/60.0, total_tim*factor, + wanted, pp->speed ); + } + } + DefineIndexDouble(si,"TIME.SET",sum_dest); + } + + + //--- enable ruote itemboxes + + kmp_rtobj_t rtobj; + if (AssignRouteInfo(&ri->rtinfo,&rtobj)) + { + const uint max = sizeof(kmp->rtobj_poti)/sizeof(*kmp->rtobj_poti); + uint i; + for ( i = 0; i <= kmp->n_rtobj_poti; i++ ) + { + if ( i >= max ) + { + ERROR0(ERR_WARNING, + "Route list full -> @ROUTE-OBJECT ignored for route #%u",ri->index); + break; + } + + kmp_rtobj_list_t *rptr = kmp->rtobj_poti + i; + if ( i == kmp->n_rtobj_poti ) + { + rptr->index = ri->index; + kmp->n_rtobj_poti++; + + } + + if ( rptr->index == ri->index ) + { + AssignRouteInfo(&ri->rtinfo,&rptr->rtobj); + PRINT("@ROUTE-OBJECT[%u] = 0x%x -> %u\n", + i, ri->index, rptr->rtobj.obj ); + break; + } + } + } + + memset(ri,0,sizeof(*ri)); + ResetRouteInfo(&ri->rtinfo,&kmp->rtobj); + ResetKclFall(&ri->kfall); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextPOTI +( + kmp_t * kmp, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(kmp); + DASSERT(si); + TRACE("ScanTextPOTI()\n"); + + List_t *glist = kmp->dlist + KMP_POTI; + List_t *plist = &kmp->poti_point; + glist->used = plist->used = 0; + + Line_t line; + InitializeLine(&line); + kmp_poti_group_t *grp = 0; + u8 g_smooth = 0, g_back = 0; + + int auto_route_name = 0; + route_info_t ri = {0}; + ResetRouteInfo(&ri.rtinfo,&kmp->rtobj); + ResetKclFall(&ri.kfall); + + ScanParam_t ptab[] = + { + { "AUTO-ROUTE-NAME", SPM_INT, &auto_route_name }, + { "XSS", SPM_DOUBLE, ri.xss, 4,4, &ri.f_xss }, + { "YSS", SPM_DOUBLE, ri.yss, 4,4, &ri.f_yss }, + { "ZSS", SPM_DOUBLE, ri.zss, 4,4, &ri.f_zss }, + { "SCALE", SPM_VAR, ri.scale, 1,2, &ri.f_scale }, + { "SHIFT", SPM_VAR, &ri.shift, 1,1, &ri.f_shift }, +// [[[hrot]] [[2do]] + { "HROTATE", SPM_VAR, ri.hrotate, 1,2, &ri.f_hrotate }, + { "REVERSE-ORDER", SPM_INT, &ri.reverse_order }, + { "SET-ROUTE-TIME", SPM_DOUBLE, &ri.route_time }, + ROUTE_OBJECT_VARS(ri.rtinfo) + KCL_FALL_VARS(kmp,ri.kfall) + {0} + }; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + CheckRouteInfo(&ri.rtinfo); + CheckKclFall(&ri.kfall); + continue; + } + + if ( ch == '$' ) + { + char name[KMP_MAX_NAME_LEN+1]; + ScanNameSI(si,name,sizeof(name),true,true,0); + + const int is_bezier = !strcmp(name,"$BEZIER"); + if ( is_bezier || !strcmp(name,"$LINE") ) + { + enumError err = ScanLine(&line,si,3,is_bezier); + if (err) + return err; + continue; + } + + if ( !strcmp(name,"$COPY") ) + { + uint r_idx; + if (ScanU32SI(si,&r_idx,1,0)) + continue; + + if ( r_idx >= glist->used ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + si->total_err++; + sf->line_err++; + ERROR0(ERR_WARNING, + "Can't copy unknown route #%u [%s @%u]\n", + r_idx, sf->name, sf->line ); + GotoEolSI(si); + } + + uint r_beg = 0, r_end = UINT_MAX; + if (NextCharSI(si,false)) + { + if (ScanU32SI(si,&r_beg,1,0)) + continue; + + if (NextCharSI(si,false)) + { + if (ScanU32SI(si,&r_end,1,0)) + continue; + r_end++; + } + } + + const kmp_poti_group_t *srcgrp = GetListElem(glist,r_idx,0); + DASSERT(srcgrp); + if ( r_end > srcgrp->n_point ) + r_end = srcgrp->n_point; + PRINT("COPY ROUTE: %u,%u..%u\n",r_idx,r_beg,r_end); + if ( r_beg >= r_end ) + continue; + + const kmp_poti_point_t * psrc = GetListElem(plist,0,0); + psrc += r_beg; + while ( r_idx > 0 ) + { + r_idx--; + const kmp_poti_group_t *g = GetListElem(glist,r_idx,0); + DASSERT(g); + psrc += g->n_point; + } + DASSERT( psrc < (kmp_poti_point_t*)GetListElem(plist,0,0) + plist->used ); + + if (!grp) + { + grp = AppendList(glist); + grp->smooth = g_smooth; + grp->back = g_back; + } + DASSERT(grp); + grp->n_point += r_end - r_beg; + + for ( ; r_beg < r_end; r_beg++, psrc++ ) + { + kmp_poti_point_t * pdest = AppendList(plist); + memcpy(pdest,psrc,sizeof(*pdest)); + } + kmp->value[KMP_POTI] = plist->used; + continue; + } + + const bool cmd_close = !strcmp(name,"$CLOSE"); + if ( !cmd_close && strcmp(name,"$ROUTE") && strcmp(name,"$GROUP") ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + si->total_err++; + sf->line_err++; + CheckEolSI(si); + ERROR0(ERR_WARNING, + "Line ignored [%s @%u]: %s ...\n", + sf->name, sf->line, name ); + continue; + } + + + //--- close group + + if (grp) + CloseRoute(kmp,si,grp,&ri); + grp = 0; + + if (cmd_close) + continue; + + ScanIndexSI(si,0,glist->used,kmp->index+KMP_POTI,2); + + for(;;) + { + char ch = NextCharSI(si,false); + noPRINT("--> %02x : %.6s\n",ch,si->cur_file->ptr); + if (!ch) + break; + si->cur_file->ptr++; + if ( ch == ':' ) + break; + } + u16 g_temp; + ScanU16SI(si,&g_temp,1,0); + if (NextCharSI(si,false)) + { + g_smooth = g_temp; + ScanU8SI(si,&g_back,1,0); + } + else + { + g_smooth = g_temp >> 8; + g_back = (u8)g_temp; + } + noPRINT("ROUTE: smooth=%u, back=%u\n",g_smooth,g_back); + CheckEolSI(si); + continue; + } + + if ( !grp || !grp->n_point ) // no previous point available + line.n = 1; + enumError err; + ScanIndexSI(si,&err, grp ? grp->n_point + line.n -1 : 0, 0,2); + if (err) + break; + + if (!grp) + { + ri.index = glist->used; + grp = AppendList(glist); + grp->smooth = g_smooth; + grp->back = g_back; + } + DASSERT(grp); + grp->n_point += line.n; + + kmp_poti_point_t temp; + ScanFloatV3SI(si,temp.position,1); + if ( si->cur_file->szs_modifier > 0 ) + ScanBE32SI_swap(si,(u32*)&temp.speed,1,1); + else + ScanU16SI(si,&temp.speed,2,0); + CheckEolSI(si); + + const kmp_poti_point_t * prev = GetListElem(plist,-1,&temp); + + uint p; + for ( p = 1; p <= line.n; p++ ) + { + kmp_poti_point_t * ppos = AppendList(plist); + memcpy(ppos,&temp,sizeof(*ppos)); + CalcLine(&line,ppos->position,prev->position,temp.position,3,p); + CalcKclFall(&ri.kfall,ppos->position); + } + kmp->value[KMP_POTI] = plist->used; + InitializeLine(&line); + } + if (grp) + CloseRoute(kmp,si,grp,&ri); + CheckLevelSI(si); + + if ( auto_route_name > 0 ) + { + ResetIL(kmp->index+KMP_POTI); + FillIL(kmp->index+KMP_POTI,glist->used); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static kmp_gobj_entry_t * AddObject +( + kmp_t * kmp, // KMP data structure + const float * pos, // 3D vector with position + const float * rot, // NULL or a rotation 3D vector + kmp_rtobj_t * rtobj // object to insert +) +{ + DASSERT(kmp); + DASSERT(pos); + DASSERT(rtobj); + DASSERT( rtobj->obj > 0 && rtobj->obj < N_KMP_GOBJ ); + + List_t *glist = kmp->dlist + KMP_GOBJ; + kmp_gobj_entry_t *gobj = AppendList(glist); + gobj->obj_id = rtobj->obj; + gobj->position[0] = pos[0] + rtobj->shift.x; + gobj->position[1] = pos[1] + rtobj->shift.y; + gobj->position[2] = pos[2] + rtobj->shift.z; + memcpy(gobj->scale,rtobj->scale.v,sizeof(gobj->scale)); + memcpy(gobj->rotation,rtobj->rotate.v,sizeof(gobj->rotation)); + gobj->route_id = (typeof(gobj->route_id))~0; + gobj->pflags = 0x3f; + if (rot) + { + gobj->rotation[0] = NormDegreeF( gobj->rotation[0] + rot[0] ); + gobj->rotation[1] = NormDegreeF( gobj->rotation[1] + rot[1] ); + gobj->rotation[2] = NormDegreeF( gobj->rotation[2] + rot[2] ); + } + return gobj; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AddEnemyRouteObjects +( + kmp_t * kmp, // KMP data structure + uint sect, // KMP section index + kmp_rtobj_t *rtobj // object to insert +) +{ + DASSERT(kmp); + DASSERT(rtobj); + DASSERT( sect == KMP_ENPT || sect == KMP_ITPT ); + + if (rtobj->obj) + { + PRINT("AddEnemyRouteObjects(%s)\n",kmp_section_name[sect].name1); + const uint n = kmp->dlist[sect].used; + const kmp_enpt_entry_t * pt = (kmp_enpt_entry_t*)kmp->dlist[sect].list; + + uint i; + for ( i = 0; i < n; i++ ) + { + int last = -1, link; + for ( link = 0; link < KMP_MAX_PH_LINK; link++ ) + { + int next = GetNextPointKMP(kmp,sect,i,link,false,-1,0,0,0,0); + if ( next >= 0 && next != last ) + { + last = next; + float3 rot = CalcDirection3F(pt[i].position,pt[next].position); + AddObject(kmp,pt[i].position,rot.v,rtobj); + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AddJugemRouteObjects +( + kmp_t * kmp, // KMP data structure + uint sect, // KMP section index + kmp_rtobj_t *rtobj // object to insert +) +{ + DASSERT(kmp); + DASSERT(rtobj); + DASSERT( sect == KMP_JGPT || sect == KMP_CNPT || sect == KMP_MSPT ); + + if (rtobj->obj) + { + PRINT("AddJugemRouteObjects(%s)\n",kmp_section_name[sect].name1); + const uint n = kmp->dlist[sect].used; + const kmp_jgpt_entry_t * pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list; + + uint i; + for ( i = 0; i < n; i++, pt++ ) + AddObject(kmp,pt->position,pt->rotation,rtobj); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AddRouteObjects +( + kmp_t * kmp // KMP data structure +) +{ + DASSERT(kmp); + PRINT("AddRouteObjects()\n"); + + const uint ng = kmp->dlist[KMP_POTI].used; + uint ilist; + kmp_rtobj_list_t *rptr = kmp->rtobj_poti; + for ( ilist = 0; ilist < kmp->n_rtobj_poti; ilist++, rptr++ ) + { + int route_idx = rptr->index; + if ( route_idx < 0 || route_idx >= ng || !rptr->rtobj.obj ) + continue; + + PRINT("ROUTE-ITEMBOX %u/%u -> %d/%u\n", + ilist, kmp->n_rtobj_poti, route_idx, ng ); + const kmp_poti_group_t *grp = (kmp_poti_group_t*)kmp->dlist[KMP_POTI].list; + kmp_poti_point_t *pt = (kmp_poti_point_t*)kmp->poti_point.list; + while ( route_idx-- > 0 ) + { + pt += grp->n_point; + grp++; + } + + uint i; + for ( i = 0; i < grp->n_point; i++ ) + { + float3 rot = CalcDirection3F( pt[i].position, + pt[ (i+1) % grp->n_point ].position ); + AddObject( kmp, pt[i].position, rot.v, &rptr->rtobj ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void setup_scale ( Var_t *dest, const Var_t *src ) +{ + DASSERT(dest); + DASSERT(src); + + if ( src->mode == VAR_VECTOR ) + AssignVar(dest,src); + else + AssignVectorV3(dest,0.0,GetYDoubleV(src),0.0); + DASSERT( dest->mode == VAR_VECTOR ); + + if ( dest->x <= 0.0 ) + dest->x = RTOBJ_SCALE_X; + else if ( dest->x < RTOBJ_SCALE_MIN ) + dest->y = RTOBJ_SCALE_MIN; + + if ( dest->y <= 0.0 ) + dest->y = RTOBJ_SCALE_Y; + else if ( dest->y < RTOBJ_SCALE_MIN ) + dest->y = RTOBJ_SCALE_MIN; + + if ( dest->z <= 0.0 ) + dest->z = RTOBJ_SCALE_Z; + else if ( dest->z < RTOBJ_SCALE_MIN ) + dest->y = RTOBJ_SCALE_MIN; +} + +//----------------------------------------------------------------------------- + +static void AddCheckpointObject +( + kmp_t * kmp // KMP data structure +) +{ + DASSERT(kmp); + PRINT("AddCheckpointObject()\n"); + + struct obj_t + { + int object; + Var_t scale; + float base; + + } obj1, obj2; + + obj1.object = kmp->ckpt_object1 > 0 && kmp->ckpt_object1 < N_KMP_GOBJ + ? kmp->ckpt_object1 : 0; + setup_scale(&obj1.scale,&kmp->ckpt_object1_scale); + obj1.base = kmp->ckpt_object1_base; + + obj2.object = kmp->ckpt_object2 > 0 && kmp->ckpt_object2 < N_KMP_GOBJ + ? kmp->ckpt_object2 : 0; + setup_scale(&obj2.scale,&kmp->ckpt_object2_scale); + obj2.base = kmp->ckpt_object2_base; + + const struct obj_t *obj_lap_lt = 0, *obj_lap_rt = 0; + const struct obj_t *obj_man_lt = 0, *obj_man_rt = 0; + const struct obj_t *obj_std_lt = 0, *obj_std_rt = 0; + + switch (kmp->ckpt_object_mode) + { + case 1: + obj_lap_lt = &obj2; + obj_lap_rt = &obj2; + obj_man_lt = &obj1; + obj_man_rt = &obj1; + break; + + case 2: + obj_lap_lt = &obj2; + obj_lap_rt = &obj2; + obj_man_lt = &obj2; + obj_man_rt = &obj2; + obj_std_lt = &obj1; + obj_std_rt = &obj1; + break; + + case 3: + obj_lap_lt = &obj1; + obj_lap_rt = &obj2; + obj_man_lt = &obj1; + obj_man_rt = &obj2; + obj_std_lt = &obj1; + obj_std_rt = &obj2; + break; + + default: + return; + } + + opt_auto_add = true; + + List_t *glist = kmp->dlist + KMP_GOBJ; + const kmp_ckpt_entry_t *pt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + uint i, n = kmp->dlist[KMP_CKPT].used; + for ( i = 0; i < n; i++, pt++ ) + { + double direction, factor = IS_M1(pt->mode) ? 1.0 : 3.0; + WidthAndDirection(0,&direction,true,pt->left,pt->right); + + const struct obj_t *obj; + obj = IS_M1(pt->mode) ? obj_std_lt : pt->mode ? obj_man_lt : obj_lap_lt; + if (obj) + { + kmp_gobj_entry_t *go = AppendList(glist); + go->obj_id = obj->object; + go->position[0] = pt->left[0]; + go->position[1] = obj->base; + go->position[2] = pt->left[1]; + go->rotation[1] = direction; + go->scale[0] = obj->scale.x * factor; + go->scale[1] = obj->scale.y; + go->scale[2] = obj->scale.z * factor; + go->route_id = (typeof(go->route_id))~0; + go->pflags = 0x3f; + } + + obj = IS_M1(pt->mode) ? obj_std_rt : pt->mode ? obj_man_rt : obj_lap_rt; + if (obj) + { + kmp_gobj_entry_t *go = AppendList(glist); + go->obj_id = obj->object; + go->position[0] = pt->right[0]; + go->position[1] = obj->base; + go->position[2] = pt->right[1]; + go->rotation[1] = direction; + go->scale[0] = obj->scale.x * factor; + go->scale[1] = obj->scale.y; + go->scale[2] = obj->scale.z * factor; + go->route_id = (typeof(go->route_id))~0; + go->pflags = 0x3f; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SnapToENPT +( + kmp_t * kmp, // KMP data structure + uint sect // KMP section index +) +{ + const Var_t * add2; // not NULL: enter 2D-full mode && value to add + const Var_t * add1; // not NULL: enter 1D-full mode && value to add + const Var_t * hadd; // not NULL: enter horiz-mode && value to add + const kmp_flag_t *kf = 0; + + switch(sect) + { + case KMP_JGPT: + add2 = &kmp->jgpt_snap2; + add1 = &kmp->jgpt_snap; + hadd = &kmp->jgpt_hsnap; + kf = &kmp->jgpt_flag; + break; + + case KMP_CNPT: + add2 = &kmp->cnpt_snap2; + add1 = &kmp->cnpt_snap; + hadd = &kmp->cnpt_hsnap; + break; + + case KMP_MSPT: + add2 = &kmp->mspt_snap2; + add1 = &kmp->mspt_snap; + hadd = &kmp->mspt_hsnap; + break; + + default: + ASSERT(0); + return; + } + + const Var_t *add; + enum { MODE2, MODE1, HMODE } mode; + + if ( add2 && add2->mode != VAR_UNSET ) + { + mode = MODE2; + add = add2; + } + else if ( add1 && add1->mode != VAR_UNSET ) + { + mode = MODE1; + add = add1; + } + else if ( hadd && hadd->mode != VAR_UNSET ) + { + mode = HMODE; + add = hadd; + } + else + return; + + PRINT("SnapToENPT(%s) mode=%d\n",kmp_section_name[sect].name1,mode); + + const uint n_enpt = kmp->dlist[KMP_ENPT].used; + if (!n_enpt) + return; + const kmp_enpt_entry_t *enpt1 = (kmp_enpt_entry_t*)kmp->dlist[KMP_ENPT].list; + + kmp_jgpt_entry_t *pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list; + kmp_jgpt_entry_t *pt_end = pt + kmp->dlist[sect].used; + + uint j; + for ( j = 0; pt < pt_end; j++, pt++ ) + { + if ( kf && j < KMP_MAX_POINT && !( kf->flags[j] & KPFL_SNAP ) ) + continue; + + int i, enpt_idx = -1; + double min_dist = 0.0; + const kmp_enpt_entry_t *enpt; + for ( i = 0, enpt = enpt1; i < n_enpt; i++, enpt++ ) + { + if ( i < KMP_MAX_POINT && !( kmp->enpt_flag.flags[i] & KPFL_SNAP )) + continue; + + const double dx = pt->position[0] - enpt->position[0]; + const double dy = pt->position[1] - enpt->position[1]; + const double dz = pt->position[2] - enpt->position[2]; + const double dist = dx*dx + dy*dy + dz*dz; + if ( enpt_idx < 0 || min_dist > dist ) + { + min_dist = dist; + enpt_idx = i; + } + } + if ( enpt_idx < 0 ) + break; // no usable ENPT found -> abort + + uint next_idx = GetNextPointKMP(kmp,KMP_ENPT,enpt_idx,-1,false,0,0,0,0,0); + DASSERT( next_idx < n_enpt ); + + enpt = enpt1 + enpt_idx; + + DEFINE_VAR(var); + var.mode = VAR_VECTOR; + var.x = enpt->position[0]; + var.y = mode == HMODE ? pt->position[1] : enpt->position[1]; + var.z = enpt->position[2]; + DASSERT(add); + CalcExpr(&var,add,OPI_ADD,0); + pt->position[0] = var.x; + pt->position[1] = var.y; + pt->position[2] = var.z; + + const kmp_enpt_entry_t *next = enpt1 + next_idx; + if ( mode == MODE2 ) + { + double3 p1, p2; + p1.x = enpt->position[0]; + p1.y = enpt->position[1]; + p1.z = enpt->position[2]; + p2.x = next->position[0]; + p2.y = next->position[1]; + p2.z = next->position[2]; + double3 dir = CalcDirection3D(&p1,&p2); + pt->rotation[0] = dir.x; + pt->rotation[1] = dir.y; + pt->rotation[2] = dir.z; + } + else + { + pt->rotation[0] = pt->rotation[2] = 0.0; + pt->rotation[1] = atan2( next->position[0] - enpt->position[0], + next->position[2] - enpt->position[2] ) * (180/M_PI); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static int FindNearestJGPT2 +( + kmp_t *kmp, // valid KMP + double x, // x-coordinate + double z, // z-coordinate + double *res_dist2 // not NULL: store distance^2 here +) +{ + DASSERT(kmp); + + int found = -1; + double max_dist2 = 1e30; + + const uint n_jgpt = kmp->dlist[KMP_JGPT].used; + kmp_jgpt_entry_t *jgpt = (kmp_jgpt_entry_t*)kmp->dlist[KMP_JGPT].list; + + uint i; + for ( i = 0; i < n_jgpt; i++, jgpt++ ) + { + const double dx = x - jgpt->position[0]; + const double dz = z - jgpt->position[2]; + const double dist2 = dx*dx + dz*dz; + if ( max_dist2 > dist2 ) + { + max_dist2 = dist2; + found = i; + } + } + + if ( res_dist2 ) + *res_dist2 = max_dist2; + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int FindNearestJGPT3 +( + kmp_t *kmp, // valid KMP + const float32 *pos, // 3d coordinates + double *res_dist2 // not NULL: store distance^2 here +) +{ + DASSERT(kmp); + DASSERT(pos); + + int found = -1; + double max_dist2 = 1e30; + + const uint n_jgpt = kmp->dlist[KMP_JGPT].used; + kmp_jgpt_entry_t *jgpt = (kmp_jgpt_entry_t*)kmp->dlist[KMP_JGPT].list; + + uint i; + for ( i = 0; i < n_jgpt; i++, jgpt++ ) + { + double3 d; + d.x = pos[0] - jgpt->position[0]; + d.y = pos[1] - jgpt->position[1]; + d.z = pos[2] - jgpt->position[2]; + const double dist2 = LengthSqare3(d); + if ( max_dist2 > dist2 ) + { + max_dist2 = dist2; + found = i; + } + } + + if ( res_dist2 ) + *res_dist2 = max_dist2; + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AutoRespawn ( kmp_t * kmp ) +{ + DASSERT(kmp); + + + //--- auto add respawn points + + const uint min_rm = kmp->jgpt_auto_remove > 1 ? 0 : kmp->dlist[KMP_JGPT].used; + const int sect = kmp->jgpt_auto_define - 1; + if ( sect == KMP_ENPT || sect == KMP_ITPT ) + { + const uint n = kmp->dlist[sect].used; + const kmp_enpt_entry_t *pt0 = (kmp_enpt_entry_t*)kmp->dlist[sect].list; + const kmp_enpt_entry_t *pt = pt0; + const kmp_flag_t *kf = sect == KMP_ENPT ? &kmp->enpt_flag : &kmp->itpt_flag; + + uint i; + for ( i = 0; i < n && kmp->dlist[KMP_JGPT].used < KMP_MAX_POINT; i++, pt++ ) + { + if ( i < KMP_MAX_POINT && !( kf->flags[i] & KPFL_JGPT ) ) + continue; + + double dist2; + FindNearestJGPT3(kmp,pt->position,&dist2); + if ( dist2 > kmp->jgpt_auto_distance ) + { + kmp_jgpt_entry_t * entry = AppendList(kmp->dlist+KMP_JGPT); + entry->position[0] = pt->position[0] + kmp->jgpt_auto_adjust.x; + entry->position[1] = pt->position[1] + kmp->jgpt_auto_adjust.y; + entry->position[2] = pt->position[2] + kmp->jgpt_auto_adjust.z; + entry->id = kmp->dlist[KMP_JGPT].used - 1; + entry->effect = M1(entry->effect); + + uint next_idx = GetNextPointKMP(kmp,KMP_ENPT,i,-1,false,0,0,0,0,0); + DASSERT( next_idx < n ); + const kmp_enpt_entry_t *next = pt0 + next_idx; + + double3 p1, p2; + p1.x = pt->position[0]; + p1.y = pt->position[1]; + p1.z = pt->position[2]; + p2.x = next->position[0]; + p2.y = next->position[1]; + p2.z = next->position[2]; + double3 dir = CalcDirection3D(&p1,&p2); + entry->rotation[0] = dir.x; + entry->rotation[1] = dir.y; + entry->rotation[2] = dir.z; + } + } + } + + + //--- auto connect check points to respawn points + + u8 jgpt_used[KMP_MAX_POINT]; + memset(jgpt_used,0,sizeof(jgpt_used)); + + const uint n_ckpt = kmp->dlist[KMP_CKPT].used < KMP_MAX_POINT + ? kmp->dlist[KMP_CKPT].used : KMP_MAX_POINT; + kmp_ckpt_entry_t *ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + uint i; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + { + if ( kmp->ckpt_auto_respawn[i] ) + { + const double x = ( ckpt->left[0] + ckpt->right[0] ) / 2; + const double z = ( ckpt->left[1] + ckpt->right[1] ) / 2; + ckpt->respawn = FindNearestJGPT2(kmp,x,z,0); + } + if ( (uint)ckpt->respawn < KMP_MAX_POINT ) + jgpt_used[ckpt->respawn] = 1; + } + + + //--- remove unused respawn points + + const uint n_jgpt = kmp->dlist[KMP_JGPT].used; + if ( kmp->jgpt_auto_remove > 0 && n_jgpt > min_rm ) + { + PRINT("--> JGPT: auto remove: %u..%u\n",min_rm,n_jgpt-1); + + u8 map[KMP_MAX_POINT]; + memset(map,0,sizeof(map)); + + kmp_jgpt_entry_t *base = (kmp_jgpt_entry_t*)kmp->dlist[KMP_JGPT].list; + kmp_jgpt_entry_t *src = base, *dest = base; + + uint i, new_idx = 0; + for ( i = 0; i < n_jgpt; i++, src++ ) + { + if (jgpt_used[i]) + { + new_idx = dest - base; + memcpy(dest,src,sizeof(*dest)); + dest++; + } + map[i] = new_idx; + } + kmp->dlist[KMP_JGPT].used = dest - base; + //HEXDUMP16(0,0,map,sizeof(map)); + + kmp_ckpt_entry_t *ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + ckpt->respawn = map[ckpt->respawn]; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextKMP(): reference KCL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void DefineReferenceKCLbyKMP ( kmp_t *kmp ) +{ + kcl_ref_prio = 0; + + if ( kmp && kmp->enable_fall_kcl ) + { + if (kmp->fall_kcl) + { + ResetKCL(kmp->fall_kcl); + FREE(kmp->fall_kcl); + kmp->fall_kcl = 0; + } + + DrawKCL_t dk; + memset(&dk,0,sizeof(dk)); + + const draw_mode_t pflags = ( KMP_DRAW & DMD_M_PFLAGS ) >> DMD_SHIFT_PFLAGS; + + uint n = kmp->dlist[KMP_GOBJ].used; + PRINT("\e[36;1m### DefineReferenceKCLbyKMP(), N(GOBJ) = %u\e[0m\n",n); + const kmp_gobj_entry_t *op = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + for ( ; n > 0; n--, op++ ) + { + if ( op->obj_id < N_KMP_GOBJ + && ObjectInfo[op->obj_id].flags & OBF_ROAD + && ( !pflags || pflags & op->pflags ) + ) + { + if (!kmp->fall_kcl) + { + kcl_t *ref = LoadReferenceKCL(); + PRINT("\e[36;1m### CREATE KMP, kcl_ref=%d\e[0m\n",ref!=0); + if (!ref) + break; + + kmp->fall_kcl = MALLOC(sizeof(kcl_t)); + CopyKCL(kmp->fall_kcl,true,ref); + kmp->fall_kcl->silent_octree = true; + kmp->fall_kcl->fast = true; + dk.kcl = dk.draw = kcl_ref_prio = kmp->fall_kcl; + dk.kmp = kmp; + dk.detailed = dk.as_road = true; + } + AddObject2KCL(&dk,op); + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + PRINT("ScanTextKMP(init=%d)\n",init_kmp); + KMP_ACTION_LOG(false,"ScanTextKMP() %s\n",kmp->fname); + + DASSERT(kmp); + if (init_kmp) + InitializeKMP(kmp); + else if (kmp->fname) + DefineAutoLoadReferenceKCL(kmp->fname,".kmp.txt"); + + ScanInfo_t si; + InitializeSI(&si,data,data_size,kmp->fname,kmp->revision); + si.predef = SetupVarsKMP(); + + enumError max_err = ERR_OK; + kmp->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN KMP SECTIONS, PASS%u ...\n",kmp->is_pass2+1); + + max_err = ERR_OK; + si.kmp = kmp; + si.no_warn = !kmp->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", kmp->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,kmp->revision); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + noPRINT("--> pass=%u: #%04u: %s\n",kmp->is_pass2+1,si.cur_file->line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,kmp_section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case KMP_KTPT: + err = ScanTextKTPT(kmp,&si); + break; + + case KMP_ENPT: + err = ScanText_PT_PH( kmp, &si, KMP_ENPT ); + break; + + case KMP_ITPT: + err = ScanText_PT_PH( kmp, &si, KMP_ITPT ); + break; + + case KMP_CKPT: + err = ScanTextCKPT(kmp,&si); + break; + + case KMP_GOBJ: + err = ScanTextGOBJ(kmp,&si); + break; + + case KMP_POTI: + err = ScanTextPOTI(kmp,&si); + break; + + case KMP_AREA: + err = ScanTextAREA(kmp,&si); + break; + + case KMP_CAME: + err = ScanTextCAME(kmp,&si); + break; + + case KMP_JGPT: + err = ScanTextPT2(kmp,&si,KMP_JGPT); + break; + + case KMP_CNPT: + err = ScanTextPT2(kmp,&si,KMP_CNPT); + break; + + case KMP_MSPT: + err = ScanTextPT2(kmp,&si,KMP_MSPT); + break; + + case KMP_STGI: + err = ScanTextSTGI(kmp,&si); + break; + + case KMP_N_SECT: // alias for [SETUP] + err = ScanTextSETUP(kmp,&si); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (kmp->is_pass2) + break; + + kmp->is_pass2 = true; + + DefineReferenceKCLbyKMP(kmp); + RestartSI(&si); + ClearSectionsKMP(kmp); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextKMP) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + SnapToENPT(kmp,KMP_JGPT); + SnapToENPT(kmp,KMP_CNPT); + SnapToENPT(kmp,KMP_MSPT); + + AutoRespawn(kmp); + + AddEnemyRouteObjects(kmp,KMP_ENPT,&kmp->rtobj_enpt); + AddEnemyRouteObjects(kmp,KMP_ITPT,&kmp->rtobj_itpt); + AddJugemRouteObjects(kmp,KMP_JGPT,&kmp->rtobj_jgpt); + AddJugemRouteObjects(kmp,KMP_CNPT,&kmp->rtobj_cnpt); + AddJugemRouteObjects(kmp,KMP_MSPT,&kmp->rtobj_mspt); + AddRouteObjects(kmp); + AddCheckpointObject(kmp); + + if (opt_generic) + { + uint sect; + for ( sect = 0; sect < KMP_N_SECT; sect++ ) + { + ResetIL(kmp->index+sect); + FillIL(kmp->index+sect,kmp->dlist[sect].used); + } + } + + kmp->battle_mode = CheckBattleModeKMP(kmp); + DefineReferenceKCLbyKMP(0); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextKMP() helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[print_kmp_group_t]] + +typedef struct print_kmp_group_t +{ + int index; // current group index + int max; // max group index + const kmp_enph_entry_t *ph; // pointer to next ph element + const kmp_group_name_t *gname; // list with names + +} print_kmp_group_t; + +//----------------------------------------------------------------------------- + +static void init_print_kmp_group +( + print_kmp_group_t * grp, // group element, will be initialized + const kmp_t * kmp, // KMP data structure + uint sect_ph, // related ph section index, may be invalid + const kmp_group_name_t * gname // list with names +) +{ + DASSERT(grp); + DASSERT(kmp); + + memset(grp,0,sizeof(*grp)); + + if ( sect_ph < KMP_N_SECT ) + { + grp->max = kmp->dlist[sect_ph].used; + grp->ph = (kmp_enph_entry_t*)kmp->dlist[sect_ph].list; + grp->gname = gname; + } +} + +//----------------------------------------------------------------------------- + +static ccp print_kmp_group +( + File_t * F, // output file + const kmp_t * kmp, // KMP data structure + const kmp_ph_t * ph, // related PH info + print_kmp_group_t * grp, // group element, will be initialized + uint index, // index of next element + ccp eol_text, // text, printed to close previous line + bool check_eline // true: print cross ref of GOBJ/ELINE_CONTROL +) +{ + DASSERT(F); + DASSERT(kmp); + DASSERT(ph); + DASSERT(grp); + + if ( grp->index < grp->max && index == grp->ph->pt_start ) + { + if (eol_text) + { + fputs(eol_text,F->f); + eol_text = 0; + } + + const uint idx = grp->index++; + const u8 *ptr, *end; + + fprintf(F->f,"\r\n # zero based index: %u, prev:", idx ); + for ( ptr = grp->ph->prev, end = ptr + KMP_MAX_PH_LINK; ptr < end; ptr++ ) + if ( *ptr != 0xff ) + fprintf(F->f," %u",*ptr); + + fprintf(F->f,", next:"); + for ( ptr = grp->ph->next, end = ptr + KMP_MAX_PH_LINK; ptr < end; ptr++ ) + if ( *ptr != 0xff ) + fprintf(F->f," %u",*ptr); + + + //--- $GROUP + + fprintf(F->f,"\r\n $GROUP %s, next:", grp->gname[idx] ); + + for ( ptr = grp->ph->next, end = ptr + KMP_MAX_PH_LINK; ptr < end; ) + { + const u8 *start = ptr; + const u8 code = *ptr++; + while ( ptr < end && *ptr == code ) + ptr++; + if ( code != 0xff ) + { + const uint n = ptr - start; + if ( n > 1 ) + fprintf(F->f," %s*%u",grp->gname[code],n); + else + fprintf(F->f," %s",grp->gname[code]); + } + } + + + //--- $PREV + + bool print_prev = ph->ac_mode != KMP_AC_PREV; + if ( !print_prev && idx < KMP_MAX_LINFO ) + { + SetupRouteLinksKMP(kmp,ph->sect_ph,false); + DASSERT(kmp->linfo); + DASSERT(kmp->linfo_sect==ph->sect_ph); + + uint i; + kmp_linfo_mode *ptr; + for ( i = 0, ptr = kmp->linfo->list[idx]; i < kmp->linfo->used; i++ ) + { + const kmp_linfo_mode md = *ptr++; + if ( !!(md & LINFO_PREV) == !(md & LINFO_BY_NEXT) ) + { + print_prev = true; + break; + } + } + } + + if (print_prev) + { + fprintf(F->f,"\r\n $PREV:"); + + for ( ptr = grp->ph->prev, end = ptr + KMP_MAX_PH_LINK; ptr < end; ) + { + const u8 *start = ptr; + const u8 code = *ptr++; + while ( ptr < end && *ptr == code ) + ptr++; + if ( code != 0xff ) + { + const uint n = ptr - start; + if ( n > 1 ) + fprintf(F->f," %s*%u",grp->gname[code],n); + else + fprintf(F->f," %s",grp->gname[code]); + } + } + } + + + + //--- $SETTINGS + + if ( idx < KMP_MAX_GROUP && grp->ph ) + { + const kmp_gopt2_t *go2 = ph->gopt + idx; + if ( ph->show_options || go2->setting[0] || go2->setting[1] + || go2->rtype_user || go2->rtype_active == KMP_RT_DISPATCH ) + { + const KeywordTab_t *key = GetKewordById(kmp_rtype_name,go2->rtype_user); + fprintf(F->f, "\r\n $SETTINGS: %u 0x%02x %s", + go2->setting[0], go2->setting[1], + key ? key->name1 : "AUTO" ); + if ( go2->rtype_user != go2->rtype_active ) + { + const KeywordTab_t *key = GetKewordById(kmp_rtype_name,go2->rtype_active); + if (key) + fprintf(F->f, " # >%s",key->name1); + } + } + + if ( ph->show_options ) + { +#if 0 // [[ac]] + fprintf(F->f, + "\r\n $CONNECT-PREV: %3d %3d %3d # class prio min" + "\r\n $CONNECT-NEXT: %3d %3d %3d", + go2->prev.cls, go2->prev.prio, go2->prev.min, + go2->next.cls, go2->next.prio, go2->next.min ); +#endif + } + } + + + //--- ELINE_CONTROL + + if (check_eline) + { + uint gidx; + for ( gidx = 0; gidx < kmp->dlist[KMP_GOBJ].used; gidx++ ) + { + const kmp_gobj_entry_t *op = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list + gidx; + if ( op->obj_id == GOBJ_ELINE_CONTROL && op->setting[7] == idx ) + fprintf(F->f, + "\r\n # GOBJ/ELINE_CONTROL #%u links to this group:" + " enter=%d, sec=%d, id=%d, next=%d", + gidx, op->setting[0], op->setting[1], op->setting[2], op->setting[3] ); + } + } + + fputs("\r\n\r\n",F->f); + grp->ph++; + } + + return eol_text; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp print_kmp_flags ( kmp_pt_flag_t flags, kmp_pt_flag_t pmask ) +{ + if (!opt_kmp_flags) + return EmptyString; + + static char buf[4]; + + char *d = buf; + if ( pmask & KPFL_FALL ) *d++ = flags & KPFL_FALL ? 'F' : '-'; + if ( pmask & KPFL_SNAP ) *d++ = flags & KPFL_SNAP ? 'S' : '-'; + if ( pmask & KPFL_JGPT ) *d++ = flags & KPFL_JGPT ? 'J' : '-'; + + DASSERT( d < buf + sizeof(buf) ); + *d = 0; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_kmp_pt +( + File_t * F, // output file + const kmp_t * kmp, // KMP data structure + uint sect_pt // KMP PT section index +) +{ + DASSERT(F); + DASSERT(kmp); + DASSERT( sect_pt == KMP_ENPT || sect_pt == KMP_ITPT ); + + //--- section setup + + char buf[30]; + uint sect_ph; + const kmp_ph_t *ph; + const kmp_flag_t *kf; + kmp_pt_flag_t flag_mask; + uint show_route; + + if ( sect_pt == KMP_ENPT ) + { + sect_ph = KMP_ENPH; + ph = &kmp->enph; + kf = &kmp->enpt_flag; + flag_mask = KPFL_M_ENPT; + show_route = 1 << SHRT_ENPT; + } + else + { + sect_ph = KMP_ITPH; + ph = &kmp->itph; + kf = &kmp->itpt_flag; + flag_mask = KPFL_M_ITPT; + show_route = 1 << SHRT_ITPT; + } + + ccp pt_name = kmp_section_name[sect_pt].name1; + ccp ph_name = kmp_section_name[sect_ph].name1; + + + //--- print data + + fprintf(F->f,text_kmp_pt_info_cr, + REVISION_NUM, + pt_name, kmp->value[sect_pt], // @%s-HEAD-VALUE = %#x + ph_name, kmp->value[sect_ph], // @%s-HEAD-VALUE = %#x + show_route, + opt_export_flags ? opt_export_flags>0 : kf->export ); + + if ( opt_route_options > OFFON_OFF ) + { + ccp ac_mode, ac_fix; + + switch ( ph->ac_flags & KMP_ACF_FIX ) + { + case KMP_ACF_FIX_PREV: ac_fix = " | ACF$FIX_PREV"; break; + case KMP_ACF_FIX_NEXT: ac_fix = " | ACF$FIX_NEXT"; break; + case KMP_ACF_FIX: ac_fix = " | ACF$FIX"; break; + default: ac_fix = ""; break; + } + + switch( ph->ac_mode ) + { + case KMP_AC_OFF: ac_mode = "AC$OFF"; break; + case KMP_AC_PREV: ac_mode = "AC$PREV"; break; + case KMP_AC_DISPATCH: ac_mode = "AC$DISPATCH"; break; + + default: // should never happen + snprintf(buf,sizeof(buf),"0x%0x",ph->ac_mode|ph->ac_flags); + ac_mode = buf; + ac_fix = ""; + } + + if ( sect_pt == KMP_ENPT ) + fprintf(F->f,text_kmp_enpt_ropt_cr, ph->show_options, ac_mode, ac_fix ); + else + fprintf(F->f,text_kmp_itpt_ropt_cr, ph->show_options, ac_mode, ac_fix ); + } + + if ( ph->mask[0] != 0xffff || ph->mask[1] != 0xffff ) + fprintf(F->f,text_kmp_pt_mask_cr, ph->mask[0], ph->mask[1] ); + + fprintf(F->f, text_kmp_pt_head_cr, pt_name ); + + const uint n = kmp->dlist[sect_pt].used; + const kmp_enpt_entry_t * pt = (kmp_enpt_entry_t*)kmp->dlist[sect_pt].list; + + print_kmp_group_t grp; + init_print_kmp_group(&grp,kmp,sect_ph,ph->gname); + ccp eol = ""; + + uint i, gi = 0; + for ( i = 0; i < n; i++, gi++, pt++ ) + { + eol = print_kmp_group(F,kmp,ph,&grp,i,eol,sect_pt==KMP_ENPT); + if (!eol) + { + gi = 0; + eol = ""; + } + else if ( gi > 1 ) + { + fprintf(F->f," %7.2f", + AngleVector(pt[-2].position,pt[-1].position,pt->position,true) ); + } + + fprintf(F->f,"%s%4u %11.3f %11.3f %11.3f %7.3f %#6x %#6x", + eol, i, + pt->position[0], pt->position[1], pt->position[2], + pt->scale, + pt->prop[0], pt->prop[1] ); + eol = "\r\n"; + + const kmp_pt_flag_t flags = i < KMP_MAX_POINT ? kf->flags[i] : kf->mask; + fprintf(F->f," %-3s",print_kmp_flags(flags,flag_mask)); + + if ( gi > 0 ) + fprintf(F->f," # %9.3f", LengthF(pt[-1].position,pt->position) ); + } + fputs(eol,F->f); + + + //----- list class names ----- + + if ( KMP_MODE & KMPMD_DUMP_CLASS && ph->class_name.used > KCLS_USER ) + { + fprintf(F->f,"\r\n#----- List of %u classes -----\r\n#\r\n", + ph->class_name.used); + + ParamFieldItem_t *ptr = ph->class_name.field, *end; + for ( end = ptr + ph->class_name.used; ptr < end; ptr++ ) + fprintf(F->f,"#%4d %s\r\n",ptr->num,ptr->key); + } + + + //----- list oneway links ----- + + if ( KMP_MODE & KMPMD_DUMP_ONEWAY && ph->oneway_used ) + { + fprintf(F->f,"\r\n#----- List of %u oneway link%s -----\r\n#", + ph->oneway_used, ph->oneway_used==1 ? "" : "s" ); + + SortOnewayPH(ph); + + uint max_fw = 1; + u16 *ptr, *end = ph->oneway_list + ph->oneway_used; + for ( ptr = ph->oneway_list; ptr < end; ptr++ ) + { + const uint fw = strlen(grp.gname[*ptr>>8]); + if ( max_fw < fw ) + max_fw = fw; + } + + uint prev = ~0; + for ( ptr = ph->oneway_list; ptr < end; ptr++ ) + { + uint from = *ptr>>8; + if ( prev != from ) + { + prev = from; + fprintf(F->f,"\r\n# %*s :",max_fw,grp.gname[from]); + } + fprintf(F->f," %s",grp.gname[*ptr&0xff]); + } + fputs("\r\n",F->f); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_kmp_ph +( + File_t * F, // output file + const kmp_t * kmp, // KMP data structure + uint sect_ph // KMP section index +) +{ + DASSERT(F); + DASSERT(kmp); + DASSERT( sect_ph == KMP_CKPH || sect_ph == KMP_ENPH || sect_ph == KMP_ITPH ); + + //--- section setup + + uint sect_pt; + const kmp_ph_t *ph; + + if ( sect_ph == KMP_CKPH ) + { + sect_pt = KMP_CKPT; + ph = &kmp->ckph; + } + else if ( sect_ph == KMP_ENPH ) + { + sect_pt = KMP_ENPT; + ph = &kmp->enph; + } + else + { + sect_pt = KMP_ITPT; + ph = &kmp->itph; + } + + ccp pt_name = kmp_section_name[sect_pt].name1; + ccp ph_name = kmp_section_name[sect_ph].name1; + + //--- print data + + fprintf(F->f,text_kmp_ph_cr, + pt_name, + REVISION_NUM, + ph_name, kmp->value[sect_ph], + ph_name ); + + const uint n = kmp->dlist[sect_ph].used; + const kmp_enph_entry_t * p = (kmp_enph_entry_t*)kmp->dlist[sect_ph].list; + uint i; + for ( i = 0; i < n; i++, p++ ) + { + fprintf(F->f,"%4u %3u %3u %3u ", + i, p->pt_start, p->pt_start+p->pt_len-1, p->pt_len ); + DASSERT( p->prev + KMP_MAX_PH_LINK == p->next ); // assume 'prev' before 'next' + const u8 * d = p->prev; + uint j; + for ( j = 0; j < 12; j++, d++ ) + { + if ( *d == 0xff ) + fprintf(F->f,"%s -", j==6 ? " " : "" ); + else + fprintf(F->f,"%s%4u", j==6 ? " " : "", *d ); + } + + if ( i < KMP_MAX_GROUP ) + { + const kmp_gopt2_t *go2 = ph->gopt + i; + #ifdef TEST + fprintf(F->f," %3u 0x%02x # %-6s %s\r\n", + go2->setting[0], go2->setting[1], ph->gname[i], + i < kmp->index[sect_ph].used ? kmp->index[sect_ph].list[i] : "-" ); + #else + fprintf(F->f," %3u 0x%02x # %s\r\n", + go2->setting[0], go2->setting[1], ph->gname[i] ); + #endif + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_kmp_pt2 +( + File_t * F, // output file + const kmp_t * kmp, // KMP data structure + uint sect, // KMP section index + uint * ref_count // NULL or reference counter array (len=0x100) +) +{ + DASSERT(F); + DASSERT(kmp); + DASSERT( sect < KMP_N_SECT ); + + uint i; + const uint n = kmp->dlist[sect].used; + const kmp_jgpt_entry_t * pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list; + IndexList_t *il = (IndexList_t*)kmp->index + sect; // cast because remove const + FillIL(il,n); + + bool auto_id = true; + for ( i = 0; i < n; i++, pt++ ) + if ( pt->id != i ) + { + auto_id = false; + break; + } + + ccp pt_name = kmp_section_name[sect].name1; + uint show_route = sect == KMP_JGPT + ? (1<jgpt_flag.flags : 0; + + fprintf( F->f, text_kmp_pt2_cr, + REVISION_NUM, + pt_name, kmp->value[sect], + auto_id, + show_route ); + + if ( sect == KMP_JGPT ) + fprintf( F->f, text_kmp_jgpt_auto_cr, kmp->jgpt_flag.export ); + else + fprintf( F->f, text_kmp_pt2_head_cr, pt_name ); + + pt = (kmp_jgpt_entry_t*)kmp->dlist[sect].list; + for ( i = 0; i < n; i++, pt++ ) + { + PrintNameIL(F->f,il,i,5,5,0); + fprintf(F->f," %11.3f %11.3f %11.3f %7.2f %7.2f %7.2f %6d %6s", + pt->position[0], pt->position[1], pt->position[2], + pt->rotation[0], pt->rotation[1], pt->rotation[2], + pt->id, PrintU16M1(pt->effect) ); + + if (pt_flags) + { + const kmp_pt_flag_t flags + = i < KMP_MAX_POINT ? pt_flags[i] : kmp->jgpt_flag.mask; + fprintf(F->f," %s",print_kmp_flags(flags,KPFL_M_JGPT)); + } + + fprintf(F->f,"%s\r\n", ref_count && !ref_count[i] ? " # unused" : "" ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetObjectName ( uint obj_id ) +{ + const uint bufsize = OBJ_FW_NAME + 3; + char *buf = GetCircBuf(bufsize); + + if ( obj_id < N_KMP_GOBJ && ObjectInfo[obj_id].name ) + snprintf(buf,bufsize,"o$%s",ObjectInfo[obj_id].name); + else + snprintf(buf,bufsize,"0x%x",obj_id); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint PrintDefObjInfoHelper ( FILE *f, ccp prefix, int mode, u16 mask ) +{ + DASSERT(f); + + gobj_condition_set_t gcs; + const uint n = GetGobjConditions(&gcs,mode,mask); + + uint i; + for ( i = 0; i < n; i++ ) + { + const gobj_condition_t *c = gcs.cond+i; + fprintf(f,"# %s/0x%04x: %s\n",prefix,c->mask,c->info ); + } + + return n; +} + +static void PrintDefObjInfo ( FILE *f, const kmp_gobj_entry_t * gobj ) +{ + DASSERT(f); + DASSERT(gobj); + + const u16 balloon = gobj->setting[KMP_GMODE_BALLOON]; + const u16 coin = gobj->setting[KMP_GMODE_COIN]; + const u16 versus = gobj->setting[KMP_GMODE_VERSUS]; + const u16 itemrain = gobj->setting[KMP_GMODE_ITEMRAIN]; + + uint count = 0; + if ( balloon != versus || coin != versus ) + { + if ( balloon == coin ) + count += PrintDefObjInfoHelper(f,"Battle",KMP_GMODE_BALLOON,balloon); + else + { + count += PrintDefObjInfoHelper(f,"Balloon",KMP_GMODE_BALLOON,balloon); + count += PrintDefObjInfoHelper(f,"Coin",KMP_GMODE_COIN,coin); + } + count += PrintDefObjInfoHelper(f,"Versus",KMP_GMODE_VERSUS,versus); + } + else + count += PrintDefObjInfoHelper(f,"*",-1,versus); + + if ( versus != itemrain ) + count += PrintDefObjInfoHelper(f,"Itemrain",KMP_GMODE_ITEMRAIN,itemrain); + + if (!count) + fputs("# Always disabled!\n",f); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextKMP +( + const kmp_t * kmp, // pointer to valid KMP + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(kmp); + DASSERT(fname); + PRINT("SaveTextKMP(%s,%d)\n",fname,set_time); + KMP_ACTION_LOG(false,"SaveTextKMP() %s\n",fname); + + char temp_buf[100]; + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,kmp->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kmp->fatt,0); + + + //--- print header + syntax info + + if ( print_header && !brief_count && !export_count ) + fprintf(F.f,text_kmp_head_cr); + else + fprintf(F.f, + "%s\r\n" + "# KMP syntax & semantics: https://szs.wiimm.de/info/kmp-syntax.html\r\n" + ,KMP_TEXT_MAGIC); + + + //--- var defs + + uint i, n, gi, ng, np; + char buf1[0x100], buf2[0x20]; + + + //--- print info section & section counter alphabetically + + if ( brief_count || export_count ) + { + fprintf(F.f, + "%s[SETUP]\r\n\r\n" + "TOOL = %s\r\n" + "SYSTEM = %s\r\n" + "VERSION = %s\r\n" + "REVISION = %u\r\n" + "DATE = %s\r\n" + "INFO = \"%s\"\r\n" + "\r\n" + "UNKNOWN-0x0c = %#x\r\n" + "KCL-FALL-ADD-ROAD = 0\r\n" + "BATTLE-MODE = %d\r\n" + "KMP-WIM0 = %d\r\n" + "\r\n", + section_sep, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, + kmp->info ? kmp->info : "", + kmp->kmp_version, kmp->battle_mode, kmp->wim0_export ); + } + else + { + fprintf(F.f, text_kmp_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, + kmp->info ? kmp->info : "", + kmp->kmp_version, kmp->battle_mode, kmp->wim0_export ); + + ccp last_name = ""; + for(;;) + { + ccp found_name = 0; + uint sect, found_sect = 0; + for ( sect = 0; sect < KMP_N_SECT; sect++ ) + { + ccp name = kmp_section_name[sect].name1; + if ( strcmp(name,last_name) > 0 + && ( !found_name || strcmp(name,found_name) < 0 ) ) + { + found_name = name; + found_sect = sect; + } + } + + if (!found_name) + break; + + if ( found_sect == KMP_POTI ) + fprintf(F.f, " # N-%s = %5u,%5u\r\n", + found_name, + kmp->dlist[found_sect].used, kmp->poti_point.used ); + else + fprintf(F.f, " # N-%s = %5u\r\n", + found_name, + kmp->dlist[found_sect].used ); + last_name = found_name; + } + } + + + //--- print default settings + + fprintf(F.f, text_kmp_defaults_cr, + GetObjectName(kmp->rtobj.obj), + kmp->rtobj.shift.x, kmp->rtobj.shift.y, kmp->rtobj.shift.z, + kmp->rtobj.scale.x, kmp->rtobj.scale.y, kmp->rtobj.scale.z ); + + + //--- print parameter support + + if (print_param) + { + if ( !brief_count && !export_count ) + fwrite(text_kmp_param1_cr,1,sizeof(text_kmp_param1_cr)-1,F.f); + else + fprintf(F.f,"#%.78s\r\n# Parameter support\r\n",Minus300); + + fwrite(text_kmp_param2_cr,1,sizeof(text_kmp_param2_cr)-1,F.f); + + if ( !brief_count && !export_count ) + fwrite(text_kmp_param3_cr,1,sizeof(text_kmp_param3_cr)-1,F.f); + } + + + //--- fill index lists + + IndexList_t *ill = (IndexList_t*)kmp->index; // cast because remove const + for ( i = 0; i < KMP_N_SECT; i++ ) + FillIL(ill+i,kmp->dlist[i].used); + + + //--- print macros + + fwrite(text_kmp_macros_cr,1,sizeof(text_kmp_macros_cr)-1,F.f); + + + //--- print AREA entries + + fprintf(F.f,text_kmp_area_cr,REVISION_NUM,kmp->value[KMP_AREA]); + + n = kmp->dlist[KMP_AREA].used; + const kmp_area_entry_t * area = (kmp_area_entry_t*)kmp->dlist[KMP_AREA].list; + for ( i = 0; i < n; i++, area++ ) + { + *buf1 = 0; + PrintNameIL(F.f,ill+KMP_AREA,i,6,8,0); + fprintf(F.f, + "%#4x %#4x %12.3f %11.3f %11.3f %#6x %#6x %#6x %#6x\r\n >", + area->mode, area->type, + area->position[0], area->position[1], area->position[2], + area->setting[0], area->setting[1], area->setting[2], area->setting[3] ); + + if ( area->type == 0x00 ) + PrintNameIL(F.f,ill+KMP_CAME,area->dest_id,10,10,2); + else + fprintf(F.f,"%#10x",area->dest_id); + + fprintf(F.f, + " %#4x %12.3f %11.3f %11.3f\r\n" + " > %28.3f %11.3f %11.3f\r\n" + "#%.84s\r\n", + area->unknown, + area->rotation[0], area->rotation[1], area->rotation[2], + area->scale[0], area->scale[1], area->scale[2], + Minus300 ); + } + + + //--- print CAME entries + + uint ocam = kmp->value[KMP_CAME] >> 8; + uint scam = kmp->value[KMP_CAME] & 0xff; + + fprintf(F.f,text_kmp_came_cr,REVISION_NUM, + GetNameIL(ill+KMP_CAME,ocam), + GetNameIL(ill+KMP_CAME,scam) ); + + // find opening cameras + + n = kmp->dlist[KMP_CAME].used; + const kmp_came_entry_t * came = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list; + + uint ocount = 0, oidx = ocam; + memset(buf1,0,sizeof(buf1)); + if (print_param) + while ( oidx < n && ocount < 0xff && oidx < sizeof(buf1) && !buf1[oidx] ) + { + buf1[oidx] = ++ocount; + oidx = came[oidx].next; + } + + for ( i = 0; i < n; i++, came++ ) + { + //--- AREA reference + + const kmp_area_entry_t * area = (kmp_area_entry_t*)kmp->dlist[KMP_AREA].list; + uint count, idx, nn = kmp->dlist[KMP_AREA].used; + for ( count = idx = 0; idx < nn; idx++, area++ ) + if ( area->type == 0x00 && area->dest_id == i ) + fprintf(F.f,"%s %s", + count++ ? "" : "\t# AREA reference:", + GetNameIL(ill+KMP_AREA,idx) ); + if (count) + fputs("\r\n",F.f); + + + //--- CAME reference + + count = 0; + if ( i == ocam ) + fprintf(F.f,"%s @OPENING-INDEX", + count++ ? "" : "\t# CAME reference:" ); + if ( i == scam ) + fprintf(F.f,"%s @SELECTION-INDEX", + count++ ? "" : "\t# CAME reference:" ); + + const kmp_came_entry_t * cref = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list; + nn = kmp->dlist[KMP_CAME].used; + for ( idx = 0; idx < nn; idx++, cref++ ) + if ( cref->next == i ) + fprintf(F.f,"%s %s", + count++ ? "" : "\t# CAME reference:", + GetNameIL(ill+KMP_CAME,idx) ); + if (count) + fputs("\r\n",F.f); + + + //--- CAME object + + PrintNameIL(F.f,ill+KMP_CAME,i,6,8,0); + fprintf(F.f," %3u %7u %11.3f %11.3f %11.3f %10.3f", + came->type, came->came_speed, + came->position[0], came->position[1], came->position[2], + came->zoom_begin ); + const double came_time = came->time/60.0; + if ( came_time >= 0.01 ) + fprintf(F.f," # came time:%8.2fs",came_time); + fputs("\r\n >",F.f); + + if (buf1[i]) + { + uint idx = (uchar)buf1[i]; + ccp term = "\r\n > "; + if ( idx == ocount ) + fprintf(F.f, + " ( mode$ocam == %u ? %s : mode$test ? %s : %s )%s", + idx, GetNameIL(ill+KMP_CAME,i), + GetNameIL(ill+KMP_CAME,kmp->value[KMP_CAME]>>8), + GetNameIL(ill+KMP_CAME,came->next), term ); + else + fprintf(F.f, + " ( mode$ocam == %u ? %s : %s )%s", + idx, GetNameIL(ill+KMP_CAME,i), + GetNameIL(ill+KMP_CAME,came->next), term ); + } + else + PrintNameIL(F.f,ill+KMP_CAME,came->next,10,12,2); + + fprintf(F.f," %5u %11.3f %11.3f %11.3f %10.3f", + came->zoom_speed, + came->rotation[0], came->rotation[1], came->rotation[2], + came->zoom_end ); + const double zoom_length = fabs(came->zoom_begin-came->zoom_end); + if ( zoom_length >= 0.01 && came->zoom_speed ) + fprintf(F.f," # zoom time:%8.2fs", + zoom_length / came->zoom_speed * (10.0/6.0) ); + + fprintf(F.f,"\r\n > %#9x %7u %11.3f %11.3f %11.3f", + came->unknown_0x02, came->viewpt_speed, + came->viewpt_begin[0], came->viewpt_begin[1], came->viewpt_begin[2] ); + const double viewpt_len = LengthF(came->viewpt_begin,came->viewpt_end); + if ( viewpt_len >= 0.01 && came->viewpt_speed ) + fprintf(F.f," # v.pt time:%8.2fs", + viewpt_len / came->viewpt_speed * (10.0/6.0) ); + fputs("\r\n >",F.f); + + PrintNameIL(F.f,ill+KMP_POTI,came->route,10,11,2); + fprintf(F.f, + " %#6x %11.3f %11.3f %11.3f %10.3f", + came->unknown_0x0a, + came->viewpt_end[0], came->viewpt_end[1], came->viewpt_end[2], + came->time ); + if ( viewpt_len >= 0.01 ) + fprintf(F.f," # v.pt len:%9.2f",viewpt_len); + fprintf(F.f,"\r\n#%.91s\r\n",Minus300); + } + + + //--- print CKPH entries + + if ( brief_count < 2 && !export_count ) + { + fwrite(text_kmp_ckph_cr,1,sizeof(text_kmp_ckph_cr)-1,F.f); + print_kmp_ph(&F,kmp,KMP_CKPH); + } + + + //--- print CKPT entries + + bool auto_next = true; + n = kmp->dlist[KMP_CKPT].used; + ng = kmp->dlist[KMP_CKPH].used; + const kmp_ckph_entry_t * ckph = (kmp_ckph_entry_t*)kmp->dlist[KMP_CKPH].list; + for ( gi = 0; gi < ng && auto_next; gi++, ckph++ ) + { + uint start = ckph->pt_start; + uint end = start + ckph->pt_len - 1; + if ( !ckph->pt_len || end >= n ) + { + auto_next = false; + break; + } + + const kmp_ckpt_entry_t * ckpt + = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list + start; + uint j; + for ( j = start; j <= end; j++, ckpt++ ) + { + if ( ckpt->prev != ( j == start ? -1 : j-1 ) + || ckpt->next != ( j == end ? -1 : j+1 ) ) + { + auto_next = false; + break; + } + } + } + + fprintf(F.f,text_kmp_ckpt_cr, + REVISION_NUM, + kmp->value[KMP_CKPT], kmp->value[KMP_CKPH], + auto_next, + ObjectInfo[RTOBJ_1].name, ObjectInfo[RTOBJ_2].name, + RTOBJ_SCALE_X, RTOBJ_SCALE_Y, RTOBJ_SCALE_Z, + RTOBJ_SCALE_X, RTOBJ_SCALE_Y, RTOBJ_SCALE_Z, + RTOBJ_BASE, RTOBJ_BASE ); + + const kmp_ckpt_entry_t * ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + + print_kmp_group_t grp; + init_print_kmp_group(&grp,kmp,KMP_CKPH,kmp->ckph.gname); + uint jgpt_count[0x100]; + memset(jgpt_count,0,sizeof(jgpt_count)); + + u8 prev_mode = M1(prev_mode); + for ( i = 0; i < n; i++, ckpt++ ) + { + if ( print_kmp_group(&F,kmp,&kmp->ckph,&grp,i,"",false) + && IS_M1(ckpt->mode) && !IS_M1(prev_mode) ) + { + fputs("\r\n",F.f); + } + prev_mode = ckpt->mode; + jgpt_count[(uint)ckpt->respawn]++; + + double width, direction; + WidthAndDirection(&width,&direction,true,ckpt->left,ckpt->right); + + const uint indent + = fprintf(F.f,"%4u %11.3f %11.3f %11.3f %11.3f ", + i, + ckpt->left[0], ckpt->left[1], + ckpt->right[0], ckpt->right[1] ); + PrintNameIL(F.f,ill+KMP_JGPT,ckpt->respawn,5,5,indent); + fprintf(F.f," %3d %3d %3d # %9.3f %7.2f\r\n", + IS_M1(ckpt->mode) ? -1 : (uint)ckpt->mode, + IS_M1(ckpt->prev) ? -1 : (uint)ckpt->prev, + IS_M1(ckpt->next) ? -1 : (uint)ckpt->next, + width, direction ); + } + + + //--- print CNPT entries + + fwrite(text_kmp_cnpt_cr,1,sizeof(text_kmp_cnpt_cr)-1,F.f); + print_kmp_pt2(&F,kmp,KMP_CNPT,0); + + + //--- print ENPH entries + + if ( brief_count < 2 && !export_count ) + { + fwrite(text_kmp_enph_cr,1,sizeof(text_kmp_enph_cr)-1,F.f); + print_kmp_ph(&F,kmp,KMP_ENPH); + } + + + //--- print ENPT entries + + fwrite(text_kmp_enpt_cr,1,sizeof(text_kmp_enpt_cr)-1,F.f); + print_kmp_pt(&F,kmp,KMP_ENPT); + + + //--- print GOBJ statistics + + n = kmp->dlist[KMP_GOBJ].used; + fprintf(F.f,text_kmp_gobj_cr,REVISION_NUM,kmp->value[KMP_GOBJ]); + + uint min_id = 0, n_types = 0, n_defobj = 0; + for(;;) + { + uint current_id = UINT_MAX, count = 0; + + const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + for ( i = 0; i < n; i++, gobj++ ) + { + if ( gobj->obj_id >= min_id ) + { + if ( gobj->obj_id == current_id ) + count++; + else if ( gobj->obj_id < current_id ) + { + count = 1; + current_id = gobj->obj_id; + } + } + } + + if (!count) + break; + + if (IsDefinitionObjectId(current_id)) + n_defobj += count; + else + { + const u16 relevant_id = current_id & GOBJ_M_OBJECT; + if ( relevant_id < N_KMP_GOBJ && ObjectInfo[relevant_id].name ) + { + const ObjectInfo_t *oi = ObjectInfo + relevant_id; + fprintf(F.f, + "##%4u %#6x %c%-*s %s\r\n", + count, current_id, current_id==relevant_id ? ' ' : '|', + OBJ_FW_NAME, oi->name, oi->charact ); + } + else + fprintf(F.f,"##%4u %#6x\r\n", count, current_id ); + n_types++; + } + min_id = current_id + 1; + } + + const int nstd = n - n_defobj; + fprintf(F.f,"##%.83s\r\n## GOBJ total: %u object%s, %u type%s", + Minus300, + nstd, nstd == 1 ? "" : "s", + n_types, n_types == 1 ? "" : "s" ); + if (n_defobj) + fprintf(F.f," + %u definition object%s", + n_defobj, n_defobj == 1 ? "" : "s" ); + fprintf(F.f,"\r\n##%.83s\r\n\r\n",Minus300); + + + //--- print GOBJ entries + + fwrite(text_kmp_gobj_tabhead_cr,1,sizeof(text_kmp_gobj_tabhead_cr)-1,F.f); + + u8 done[N_KMP_GOBJ]; + memset(done,0,sizeof(done)); + + const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + for ( i = 0; i < n; i++, gobj++ ) + { + ccp form1, form2; + form1 = form2 = settings_format_xxxx.std; + + const u16 relevant_id = gobj->obj_id & (GOBJ_M_OBJECT|GOBJ_M_DEFINITION); + const ObjectInfo_t *oi = GetObjectInfo(relevant_id); + DASSERT(oi); + + if (oi->name) + { + form1 = form2 = settings_format_uuuu.std; + ccp char_sep = *oi->charact ? ", " : ""; + + if (!done[relevant_id] ) + { + done[relevant_id] = 1; + + fprintf(F.f, + "## o$%s = %#x = %u%s%s\r\n", + oi->name, relevant_id, relevant_id, + char_sep, oi->charact ); + + if (oi->info) + { + fputs("## Description: ",F.f); + PutLines(F.f,0,80,13,"## ",oi->info,"\r\n"); + } + + if (oi->fname) + fprintf(F.f,"## File names: %s\r\n",oi->fname); + + uint p; + for ( p = 0; p < 8; p++ ) + if ( oi->param[p] ) + { + fprintf(F.f,"## Setting #%u: ",p+1); + PutLines(F.f,13,80,13,"## ",oi->param[p],"\r\n"); + } + fprintf(F.f,"#%.84s\r\n",Minus300); + } + + if ( relevant_id == gobj->obj_id ) + fprintf(F.f,"# o$%s, S",oi->name); + else + fprintf(F.f,"# o$%s|%#06x, S",oi->name,gobj->obj_id&GOBJ_M_FLAGS); + + uint p; + for ( p = 0; p < 8; p++ ) + fputc( oi->param[p] ? '1'+p : '-', F.f ); + fprintf(F.f,"%s%s\r\n",char_sep, oi->charact); + + if (oi->format1) + form1 = oi->format1->std; + if (oi->format2) + form2 = oi->format2->std; + } + + //--- print special features + + bool print_condition = false; + bool print_reference = false; + + kmp_ana_defobj_t ad; + AnalyseDefObject(&ad,gobj); + if ( ad.is_defobj ) + { + char usage_info_buf[40], *usage_info; + const uint usage = CountDefObjUsage(kmp,gobj->obj_id); + if (usage) + { + usage_info = usage_info_buf; + snprintf(usage_info_buf,sizeof(usage_info_buf), + "It is used by %u regular object%s.", + usage, usage == 1 ? "" : "s" ); + } + else + usage_info = "It is not used by any regular object."; + + if ( ad.is_pure_defobj && !(KMP_MODE & KMPMD_FULL_DEFOBJ) ) + { + fprintf(F.f, + "# Definition object type %s. Param: %s\r\n", + ad.cmd_name, ad.par_info ); + if (!ad.is_logical) + { + fputs("# Meaning of the conditions:\r\n",F.f); + PrintDefObjInfo(F.f,gobj); + } + fprintf(F.f,"# %s\r\n $DEFOBJ-%-7s %#06x", + usage_info,ad.cmd_name,gobj->obj_id); + uint i; + for ( i = ad.index_beg; i < ad.index_end; i++ ) + { + if (ad.is_logical) + { + if (!gobj->setting[i]) + continue; + cond_ref_info_t cri; + GetConditionRefInfo(&cri,gobj->setting[i],~0,true); + fprintf(F.f,", %s",cri.cond); + continue; + } + fprintf(F.f,", %s",GetDefObjConditionName(gobj->setting[i])); + } + fputs("\r\n",F.f); + + if ( IsConditionRefId(gobj->ref_id) ) + { + cond_ref_info_t cri; + GetConditionRefInfo(&cri,gobj->ref_id,gobj->obj_id,true); + fprintf(F.f," $%s %s%s%s\r\n", + cri.cmd_suffix, cri.cond, + cri.info ? " # " : "", cri.info ? cri.info : "" ); + } + + fprintf(F.f,"#%.84s\r\n",Minus300); + continue; + } + fprintf(F.f,"# Type %#06x: Definition object!\r\n# %s\r\n", + GOBJ_M_DEFINITION, usage_info ); + } + else + { + if ( gobj->obj_id & GOBJ_M_LECODE ) + fprintf(F.f,"# |%#x => Disabled object." + " Conditional enabled by LE-CODE.\r\n", + GOBJ_M_LECODE); + + const u16 mode = GetPFlagsMode(gobj->pflags); + if (mode) + fprintf(F.f,"# Extended presence flags, mode %u\r\n",mode); + + kmp_ana_ref_t ar; + AnalyseRefObject(&ar,gobj); + if ( ar.is_condref || ar.is_condrnd || ar.is_condtest ) + print_condition = true; + else if (ar.is_objref) + print_reference = true; + else if (ar.condref_valid) + fprintf(F.f, + "# Warning: Valid condition, but P-Flags mode is %u\r\n", + ar.mode ); + else if (ar.objref_valid) + fprintf(F.f, + "# Warning: Valid reference id, but P-Flags mode is %u\r\n", + ar.mode ); + else if ( ar.mode_valid && gobj->ref_id ) + fprintf(F.f, + "# Warning: Invalid reference id: %#06x\r\n", + gobj->ref_id); + } + + //--- print object information + + snprintf(buf1,sizeof(buf1),form1, + gobj->setting[0], gobj->setting[1], + gobj->setting[2], gobj->setting[3] ); + switch (relevant_id) + { + case GOBJ_ELINE_CONTROL: + { + const int idx = IS_M1(gobj->setting[7]) ? -1 : gobj->setting[7]; + if ( (uint)idx < KMP_MAX_GROUP && kmp->enph.gname[idx] ) + snprintf(temp_buf,sizeof(temp_buf),"E.%s",kmp->enph.gname[idx]); + else + snprintf(temp_buf,sizeof(temp_buf),"%6d",idx); + snprintf(buf2,sizeof(buf2),"%6u %6u %6u %6s", + gobj->setting[4], gobj->setting[5], + gobj->setting[6], temp_buf ); + } + break; + + default: + snprintf(buf2,sizeof(buf2),form2, + gobj->setting[4], gobj->setting[5], + gobj->setting[6], gobj->setting[7] ); + } + + PrintNameIL(F.f,ill+KMP_GOBJ,i,5,5,0); + fprintf(F.f, + " %#6x %11.3f %11.3f %11.3f %s %6s\r\n" + " > %#6x %11.3f %11.3f %11.3f %s %#6x\r\n" + " > %11.3f %11.3f %11.3f\r\n", + gobj->obj_id, + gobj->position[0], gobj->position[1], gobj->position[2], + buf1, GetNameIL(ill+KMP_POTI,gobj->route_id), + gobj->ref_id, + gobj->rotation[0], gobj->rotation[1], gobj->rotation[2], + buf2, gobj->pflags, + gobj->scale[0], gobj->scale[1], gobj->scale[2] ); + + + //--- print special definitions + + if (print_condition) + { + cond_ref_info_t cri; + GetConditionRefInfo(&cri,gobj->ref_id,gobj->obj_id,true); + if (cri.is_condref) + fprintf(F.f," $%s %s%s%s\r\n", + cri.cmd_suffix, cri.cond, + cri.info ? " # " : "", cri.info ? cri.info : "" ); + PRINT0("# CR[%#x,%#x]: is=%d, found=%d, cmd=%s, xor:%x->%#x, c=%s, i=%s\n", + gobj->ref_id, gobj->obj_id, + cri.is_condref, cri.condref!=0, cri.cmd_suffix, + cri.xor, gobj->ref_id^cri.xor, cri.cond, cri.info ); + } + else if (print_reference) + { + fprintf(F.f," $%sABLE-BY%s %#06x\r\n", + gobj->obj_id & GOBJ_M_LECODE ? "EN" : "DIS", + (gobj->obj_id ^ gobj->ref_id) & GOBJ_DEF_F_NOT ? "" : "-NOT", + gobj->ref_id & ~GOBJ_DEF_F_NOT ); + } + fprintf(F.f,"#%.84s\r\n",Minus300); + } + + + //--- print ITPH entries + + if ( brief_count < 2 && !export_count ) + { + fwrite(text_kmp_itph_cr,1,sizeof(text_kmp_itph_cr)-1,F.f); + print_kmp_ph(&F,kmp,KMP_ITPH); + } + + + //--- print ITPT entries + + fwrite(text_kmp_itpt_cr,1,sizeof(text_kmp_itpt_cr)-1,F.f); + print_kmp_pt(&F,kmp,KMP_ITPT); + + + //--- print JGPT entries + + fwrite(text_kmp_jgpt_cr,1,sizeof(text_kmp_jgpt_cr)-1,F.f); + print_kmp_pt2(&F,kmp,KMP_JGPT,jgpt_count); + + + //--- print KTPT entries + + fprintf(F.f,text_kmp_ktpt_cr,REVISION_NUM,kmp->value[KMP_KTPT]); + + n = kmp->dlist[KMP_KTPT].used; + const kmp_ktpt_entry_t * ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list; + for ( i = 0; i < n; i++, ktpt++ ) + { + fprintf(F.f,"%4u %11.3f %11.3f %11.3f %7.2f %7.2f %7.2f %5d %#6x\r\n", + i, + ktpt->position[0], ktpt->position[1], ktpt->position[2], + ktpt->rotation[0], ktpt->rotation[1], ktpt->rotation[2], + ktpt->player_index, ktpt->unknown ); + } + + + //--- print MSPT entries + + fwrite(text_kmp_mspt_cr,1,sizeof(text_kmp_mspt_cr)-1,F.f); + print_kmp_pt2(&F,kmp,KMP_MSPT,0); + + + //--- print POTI entries + + fprintf(F.f,text_kmp_poti_cr,REVISION_NUM,1<dlist[KMP_POTI].used; + const kmp_poti_group_t * pg = (kmp_poti_group_t*)kmp->dlist[KMP_POTI].list; + np = kmp->poti_point.used; + const kmp_poti_point_t * pp = (kmp_poti_point_t*)kmp->poti_point.list; + + for ( gi = i = 0; gi < ng && i < np; gi++, pg++ ) + { + uint en = pg->n_point; + DASSERT( en <= np-i ); + fprintf(F.f,"\r\n $ROUTE %s, settings: %u %u # section %u\r\n", + GetNameIL(ill+KMP_POTI,gi), + pg->smooth, pg->back, gi+1 ); + const bool is_cyclic = !pg->back; + + //--- GOBJ reference + + const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint count, idx, nn = kmp->dlist[KMP_GOBJ].used; + for ( count = idx = 0; idx < nn; idx++, gobj++ ) + if ( gobj->route_id == (uint)gi ) + { + const u16 relevant_id = gobj->obj_id & GOBJ_M_OBJECT; + fprintf(F.f,"\t# GOBJ reference: %-8s type %#6x", + GetNameIL(ill+KMP_GOBJ,idx), gobj->obj_id ); + if ( relevant_id < N_KMP_GOBJ ) + { + const ObjectInfo_t *oi = ObjectInfo + relevant_id; + if (oi->name) + { + if ( relevant_id == gobj->obj_id ) + fprintf(F.f," = %s",oi->name); + else + fprintf(F.f," = %#06x | %s",gobj->obj_id&GOBJ_M_FLAGS,oi->name); + //DEL fprintf(F.f," = %s | %#06x",oi->name,gobj->obj_id&GOBJ_M_FLAGS); + } + } + fputs("\r\n",F.f); + } + + //--- CAME reference + + ccp came_ref = 0; + const kmp_came_entry_t * came = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list; + nn = kmp->dlist[KMP_CAME].used; + for ( count = idx = 0; idx < nn; idx++, came++ ) + if ( came->route == gi ) + { + ccp came_name = GetNameIL(ill+KMP_CAME,idx); + if (!came_ref) + came_ref = came_name; + fprintf(F.f,"%s %s", + count++ ? "" : "\t# CAME reference:", + came_name ); + } + + if (came_ref) + fprintf(F.f,"\r\n\r\n" + " # @set-route-time = %s.came.time # in 1/60 sec\r\n",came_ref); + + + //--- print points + + fputs("\r\n",F.f); + double total_len = 0.0, total_tim = 0.0; + uint ei; + for ( ei = 0; ei < en; ei++, i++, pp++ ) + { + fprintf(F.f, "%4u %11.3f %11.3f %11.3f %5u %5u #", + ei, + pp->position[0], pp->position[1], pp->position[2], + pp->speed, pp->unknown ); + + if ( ei < en-1 || is_cyclic ) + { + const kmp_poti_point_t * pnext = pp + 1; + if ( ei >= en-1 ) + pnext -= en; + double len = LengthF(pp->position,pnext->position); + total_len += len; + double tim = len > 0.0 && pp->speed > 0 + ? len / (pp->speed*60) : 0.0; + if ( tim >= 0.01 && tim <= 999.99 ) + { + total_tim += tim; + fprintf(F.f," %6.2f %9.3f", tim, len ); + } + else + fprintf(F.f," - %9.3f", len ); + + if ( ei || is_cyclic ) + { + const kmp_poti_point_t * pprev = pp - 1; + if (!ei) + pprev += en; + fprintf(F.f," %7.2f", + AngleVector(pprev->position,pp->position,pnext->position,true) ); + } + } + fputs("\r\n",F.f); + } + + if ( en > 1 ) + fprintf(F.f,"%56s %6.2f %9.3f\r\n","#TOTAL#",total_tim,total_len); + } + + + //--- print STGI entries + + fprintf(F.f,text_kmp_stgi_cr,REVISION_NUM,kmp->value[KMP_STGI]); + + n = kmp->dlist[KMP_STGI].used; + const kmp_stgi_entry_t * stgi = (kmp_stgi_entry_t*)kmp->dlist[KMP_STGI].list; + + for ( i = 0; i < n; i++, stgi++ ) + { + PrintNameIL(F.f,ill+KMP_STGI,i,4,5,0); + fprintf(F.f,"%5u %4u %5u %5u %#10x 0x%02x %#5x %#6x %#4x # ", + stgi->lap_count, stgi->pole_pos, + stgi->narrow_start, stgi->enable_lens_flare, + stgi->flare_color, + stgi->flare_alpha, stgi->unknown_09, + stgi->speed_mod >> 8, stgi->speed_mod & 0xff ); + if (stgi->speed_mod) + fprintf(F.f,"%7.3f\r\n",SpeedMod2float(stgi->speed_mod)); + else + fputs(" -\r\n",F.f); + } + + + //--- terminate + + fputs(section_end,F.f); + ResetFile(&F,set_time); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-kmp.c b/project/src/lib-kmp.c new file mode 100644 index 0000000..1fb957d --- /dev/null +++ b/project/src/lib-kmp.c @@ -0,0 +1,7899 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kmp.h" +#include "lib-kcl.h" +#include "lib-szs.h" +#include "lib-bzip2.h" +#include "lib-mkw.h" +#include "ui.h" + +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// + +#define SECT_NAME(s) kmp_section_name[s].name1 + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP action log /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool KMP_ACTION_LOG ( bool is_patch, const char * format, ... ) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[KMP]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + + if ( verbose > 2 || KMP_MODE & KMPMD_LOG || is_patch && ~KMP_MODE & KMPMD_SILENT ) + { + fflush(stdout); + fprintf(stdlog," %s>[KMP]%s ", + colset->heading, + is_patch ? colset->highlight : colset->info ); + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Wim0 support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t Wim0SectionName[] = +{ + { W0ID_END, "END", 0, W0F_BINARY }, + { W0ID_VERSION, "VERSION", 0, W0F_TEXT }, +// { W0ID_AUTO_CONNECT, "AUTO-CONNECT", 0, W0F_BINARY }, + + {0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +ccp Wim0FormatName[] = +{ + "?", // W0F_UNKNOWN, + "binary", // W0F_BINARY, + "text", // W0F_TEXT, + 0 // W0F__N +}; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeWim0 ( kmp_wim0_t *w0 ) +{ + DASSERT(w0); + memset(w0,0,sizeof(*w0)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetWim0 ( kmp_wim0_t *w0 ) +{ + if (w0) + { + FREE(w0->data); + FREE(w0->bz2data); + InitializeWim0(w0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void * NeedWim0 ( kmp_wim0_t *w0, uint size, uint align ) +{ + DASSERT(w0); + if (!w0->data) + { + DASSERT(!w0->used); + DASSERT(!w0->size); + w0->size = ALIGN32(size,4) + 0x1000; + w0->data = CALLOC(w0->size,1); + } + + u32 start = w0->used; + if ( align > 0 ) + { + start = ALIGN32(start,align); + size = ALIGN32(size,align); + } + + uint need = start + size; + PRINT("NeedWim0(,%d,%d) need=%d/%d/%d\n",size,align,need,w0->used,w0->size); + if ( need > w0->used ) + { + w0->size = ALIGN32(need,4) + 0x1000; + w0->data = REALLOC(w0->data,w0->size); + memset(w0->data + w0->used, 0, w0->size - w0->used ); + } + + w0->used = need; + return w0->data + start; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CompressWim0 ( kmp_wim0_t *w0 ) +{ + DASSERT(w0); + FREE(w0->bz2data); + w0->bz2data = 0; + + if (!w0->used) + { + w0->bz2size = 0; + return ERR_NOTHING_TO_DO; + } + + enumError err = EncodeBZIP2( &w0->bz2data, &w0->bz2size, + false, 0, + w0->data, w0->used, 9 ); + + DASSERT( err || w0->bz2data ); + DASSERT( err || w0->bz2size ); + + if ( err || w0->bz2size > w0->used + 4 ) + { + PRINT("CompressWim0() use uncompressed data!\n"); + w0->bz2size = ALIGN32(w0->used,4) + 4; + FREE(w0->bz2data); + w0->bz2data = CALLOC(w0->bz2size,1); + memcpy(w0->bz2data+4,w0->data,w0->used); + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecompressWim0 ( kmp_wim0_t *w0 ) +{ + DASSERT(w0); + FREE(w0->data); + w0->data = 0; + + if ( w0->bz2size <= 4 ) + { + w0->used = w0->size = 0; + return ERR_NOTHING_TO_DO; + } + + DASSERT(w0->bz2data); + if (!*(u32*)w0->bz2data) + { + // uncompressed data + w0->used = w0->bz2size - 4; + w0->size = ALIGN32(w0->used,4); + w0->data = CALLOC(w0->size,1); + memcpy(w0->data,w0->bz2data+4,w0->used); + return ERR_OK; + } + + enumError err = DecodeBZIP2( &w0->data, &w0->size, 0, w0->bz2data, w0->bz2size ); + DASSERT( err || w0->data ); + DASSERT( err || w0->size ); + w0->used = w0->size; + PRINT_IF(err,"DecompressWim0() failed, err=%d: %p %d\n",err,w0->bz2data,w0->bz2size); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +kmp_wim0_sect_t * AppendSectionWim0 +( + kmp_wim0_t *w0, // data structure + kmp_wim0_sect_id sect_id, // section id + cvp data, // pointer to data + uint size // size of data in bytes +) +{ + DASSERT(w0); + DASSERT( data || !size ); + + if ( size > 0x1fffe ) + size = 0x1fffe; + const uint size2 = ALIGN32(size,2); + kmp_wim0_sect_t *sect = NeedWim0(w0,sizeof(kmp_wim0_sect_t)+size2,0); + DASSERT(sect); + + sect->id = htons(sect_id); + sect->words = htons(size2/2); + if (size2) + { + sect->data[size2-1] = 0; + memcpy(sect->data,data,size); + } + return sect; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool CreateWim0 +( + const u8 **res_data, // not NULL: store data pointer here + uint *res_size, // not NULL: store data size here + kmp_t *kmp // pointer to valid KMP +) +{ + DASSERT(res_data); + DASSERT(res_size); + DASSERT(kmp); + + + //--- setup + + kmp_wim0_t w0; + InitializeWim0(&w0); + AppendSectionWim0(&w0,W0ID_VERSION,BASE_VERSION,sizeof(BASE_VERSION)); + + if ( opt_wim0 >= 12 ) + { + AppendSectionWim0(&w0,0xffff,LoDigits,sizeof(LoDigits)); + AppendSectionWim0(&w0,0xffff,LoDigits,sizeof(LoDigits)); + } + + // W0ID_AUTO_CONNECT + + //--- create data + // ??? + + + //--- compress data and store result + + if ( opt_wim0 >= 11 ) + AppendSectionWim0(&w0,0xffff,LoDigits,sizeof(LoDigits)); + + AppendSectionWim0(&w0,W0ID_END,0,0); + + bool stat = w0.used > 0; + if (stat) + { + stat = CompressWim0(&w0) == ERR_OK && w0.bz2size; + if (stat) + { + *res_data = w0.bz2data; + *res_size = w0.bz2size; + w0.bz2data = 0; + } + } + ResetWim0(&w0); + + if (!stat) + { + *res_data = 0; + *res_size = 0; + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool GetSectionHelperWim0 +( + kmp_wim0_info_t *info, // store result here (never NULL) + const struct kmp_t *kmp, // pointer to valid KMP + const kmp_wim0_sect_t *sect, // next section candidate + uint index // index of next section candidate +) +{ + DASSERT(info); + DASSERT(kmp); + + memset(info,0,sizeof(*info)); + + const u8 *min_ptr = (u8*)kmp->wim0_data; + if ( !sect || !min_ptr || (u8*)sect < min_ptr ) + return false; + + const u8 *max_ptr = (u8*)min_ptr + kmp->wim0_size; + if ( (u8*)sect > max_ptr ) + return 0; + + const uint words = ntohs(sect->words); + const kmp_wim0_sect_t *next = (kmp_wim0_sect_t*)( sect->data + words ); + if ( (u8*)next > max_ptr ) + return 0; + + info->sect_id = ntohs(sect->id); + info->index = index; + info->offset = (u8*)sect - min_ptr; + info->name[0] = 0; + info->sect = sect; + info->data = (u8*)sect->data; + info->size = 2 * words; + + const KeywordTab_t *key; + for ( key = Wim0SectionName; key->name1; key++ ) + if ( key->id == info->sect_id ) + { + StringCopyS(info->name,sizeof(info->name),key->name1); + info->format = key->opt; + break; + } + if (!*info->name) + snprintf(info->name,sizeof(info->name),"0x%04x",info->sect_id); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GetFirstSectionWim0 +( + kmp_wim0_info_t *info, // store result here (never NULL) + const struct kmp_t *kmp // pointer to valid KMP +) +{ + DASSERT(info); + DASSERT(kmp); + return GetSectionHelperWim0(info,kmp,kmp->wim0_data,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GetNextSectionWim0 +( + kmp_wim0_info_t *info, // update result (never NULL) + const struct kmp_t *kmp // pointer to valid KMP +) +{ + DASSERT(info); + DASSERT(kmp); + + const kmp_wim0_sect_t *sect = info->sect; + if (sect) + { + const uint words = ntohs(sect->words); + const kmp_wim0_sect_t *next = (kmp_wim0_sect_t*)( sect->data + words ); + return GetSectionHelperWim0(info,kmp,next,info->index+1); + } + + memset(info,0,sizeof(*info)); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +const kmp_wim0_sect_t * FindSectionWim0 +( + // return NULL or a pointer to found kmp_wim0_sect_t + + kmp_wim0_info_t *info, // not NULL: store extended result here + const struct kmp_t *kmp, // pointer to valid KMP + kmp_wim0_sect_id sect_id // section id to search +) +{ + DASSERT(kmp); + + if (info) + memset(info,0,sizeof(*info)); + + const kmp_wim0_sect_t *sect = kmp->wim0_data; + if (!sect) + return 0; + + const u8 *max_ptr = (u8*)sect + kmp->wim0_size; + int index; + for( index = 0; ; index++ ) + { + if ( (u8*)sect + sizeof(*sect) > max_ptr ) + return 0; + const uint words = ntohs(sect->words); + const kmp_wim0_sect_t *next = (kmp_wim0_sect_t*)( sect->data + words ); + if ( (u8*)next > max_ptr ) + return 0; + + const u16 id = ntohs(sect->id); + PRINT("WIM0 SECTION %04x, %u words\n",id,words); + if ( id == sect_id ) + break; + if (!id) + return 0; + sect = next; + } + + // we found the section! + + if (info) + { + info->sect_id = sect_id; + info->index = index; + info->offset = (u8*)sect - (u8*)kmp->wim0_data; + info->name[0] = 0; + info->sect = sect; + info->data = (u8*)sect->data; + info->size = 2 * ntohs(sect->words); + + const KeywordTab_t *key; + for ( key = Wim0SectionName; key->name1; key++ ) + if ( key->id == sect_id ) + { + StringCopyS(info->name,sizeof(info->name),key->name1); + info->format = key->opt; + break; + } + if (!*info->name) + snprintf(info->name,sizeof(info->name),"0x%04x",sect_id); + } + + return sect; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanWim0 ( kmp_t *kmp ) +{ + DASSERT(kmp); + + #if HAVE_PRINT + { + const kmp_wim0_sect_t *sect = FindSectionWim0(0,kmp,W0ID_VERSION); + if (sect) + PRINT("WIM0 version: %s\n",(ccp)sect->data); + + kmp_wim0_info_t info; + bool avail; + for ( avail = GetFirstSectionWim0(&info,kmp); + avail; + avail = GetNextSectionWim0(&info,kmp) ) + { + printf("%5d. %04x %-10s %4u bytes (%s) %s\n", + info.index, info.offset, info.name, info.size, + Wim0FormatName[info.format], + info.format == W0F_TEXT ? (ccp)info.data : "" ); + } + } + #endif + +// [[wim0]] scan data + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +kmp_mode_t KMP_MODE = KMPMD_M_DEFAULT; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_kmp_tab[] = +{ + { 0, "CLEAR", "RESET", KMPMD_M_ALL | KMPMD_F_HIDE }, + { KMPMD_M_DEFAULT, "DEFAULT", 0, KMPMD_M_ALL | KMPMD_F_HIDE }, + + { KMPMD_FORCE, "FORCE", 0, 0 }, + { KMPMD_NEW, "NEW", 0, 0 }, + { KMPMD_RM_SPCITEM, "RM-SPCITEM", "RMSPCITEM", 0 }, + { KMPMD_REVERSE, "REVERSE", 0, 0 }, + + { KMPMD_POLE_LEFT, "LEFT", "LT", KMPMD_M_POLE }, + { KMPMD_POLE_RIGHT, "RIGHT", "RT", KMPMD_M_POLE }, + { KMPMD_SP_WIDE, "WIDE", 0, KMPMD_M_STARTPOS }, + { KMPMD_SP_NARROW, "NARROW", 0, KMPMD_M_STARTPOS }, + + { KMPMD_1_LAP, "1LAPS", "L1", KMPMD_M_LAPS }, + { KMPMD_2_LAPS, "2LAPS", "L2", KMPMD_M_LAPS }, + { KMPMD_3_LAPS, "3LAPS", "L3", KMPMD_M_LAPS }, + { KMPMD_4_LAPS, "4LAPS", "L4", KMPMD_M_LAPS }, + { KMPMD_5_LAPS, "5LAPS", "L5", KMPMD_M_LAPS }, + { KMPMD_6_LAPS, "6LAPS", "L6", KMPMD_M_LAPS }, + { KMPMD_7_LAPS, "7LAPS", "L7", KMPMD_M_LAPS }, + { KMPMD_8_LAPS, "8LAPS", "L8", KMPMD_M_LAPS }, + { KMPMD_9_LAPS, "9LAPS", "L9", KMPMD_M_LAPS }, + { KMPMD_MAX_LAPS, "MAX-LAPS", "MAXLAPS", 0 }, + + { KMPMD_FIX_CKPH, "FIX-CKPH", "FIXCKPH", 0 }, + { KMPMD_FIX_ENPH, "FIX-ENPH", "FIXENPH", 0 }, + { KMPMD_FIX_ITPH, "FIX-ITPH", "FIXITPH", 0 }, + { KMPMD_FIX_CKNEXT, "FIX-CKNEXT", "FIXCKNEXT", 0 }, + { KMPMD_FIX_CKJGPT, "FIX-CKJGPT", "FIXCKJGPT", 0 }, + + { KMPMD_M_FIX_PH, "FIX-PH", "FIXPH", KMPMD_M_FIX_PH | KMPMD_F_HIDE }, + { KMPMD_M_FIX_CK, "FIX-CK", "FIXCK", KMPMD_M_FIX_CK | KMPMD_F_HIDE }, + { KMPMD_M_FIX_ALL, "FIX-ALL", "FIXALL", KMPMD_M_FIX_ALL | KMPMD_F_HIDE }, + + { KMPMD_MASK_PFLAGS, "MASK-PFLAGS", "MASKPFLAGS", 0 }, + { KMPMD_RM_LECODE, "RM-LECODE", "RMLECODE", 0 }, + { KMPMD_PURGE_GOBJ, "PURGE-GOBJ", "PURGEGOBJ", 0 }, + { KMPMD_FULL_DEFOBJ, "FULL-DEFOBJ", "FULLDEFOBJ", 0 }, + + { KMPMD_DUMP_CLASS, "DUMP-CLASS", "DUMPCLASS", 0 }, + { KMPMD_DUMP_ONEWAY, "DUMP-ONEWAY", "DUMPONEWAY", 0 }, + { KMPMD_M_DUMP_ALL, "DUMP-ALL", "DUMPALL", KMPMD_M_DUMP_ALL | KMPMD_F_HIDE }, + + { KMPMD_TINY_0, "TINY-0", "TINY0", KMPMD_M_TINY }, + { KMPMD_TINY_1, "TINY-1", "TINY1", KMPMD_M_TINY }, + { KMPMD_TINY_2, "TINY-2", "TINY2", KMPMD_M_TINY }, + { KMPMD_TINY_3, "TINY-3", "TINY3", KMPMD_M_TINY }, + { KMPMD_TINY_4, "TINY-4", "TINY4", KMPMD_M_TINY }, + { KMPMD_TINY_5, "TINY-5", "TINY5", KMPMD_M_TINY }, + { KMPMD_TINY_6, "TINY-6", "TINY6", KMPMD_M_TINY }, + { KMPMD_TINY_7, "TINY-7", "TINY7", KMPMD_M_TINY }, + + { KMPMD_RM_EMPTY, "RM-EMPTY", "RMEMPTY", 0 }, + { KMPMD_MINIMIZE, "MINIMIZE", 0, 0 }, + + { KMPMD_TEST1, "TEST1", 0, 0 }, + { KMPMD_TEST2, "TEST2", 0, 0 }, + + { KMPMD_LOG, "LOG", 0, 0 }, + { KMPMD_SILENT, "SILENT", 0, 0 }, + { KMPMD_INPLACE, "INPLACE", 0, 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static void SetKmpMode ( kmp_mode_t new_mode ) +{ + noPRINT("SET-KMP/IN: PC=%d, KPC=%d\n", + have_patch_count, have_kmp_patch_count ); + + if ( KMP_MODE & KMPMD_M_PATCH ) + have_patch_count--, have_kmp_patch_count--; + + KMP_MODE = new_mode & KMPMD_M_ALL; + if ( PATCH_FILE_MODE & PFILE_F_LOG_ALL ) + KMP_MODE |= KMPMD_LOG; + force_kmp = ( KMP_TFORM & KMPMD_FORCE ) != 0; + + if ( KMP_MODE & KMPMD_M_TINY ) + KMP_MODE |= KMPMD_NEW | KMPMD_RM_EMPTY; + + if ( KMP_MODE & KMPMD_M_PATCH ) + have_patch_count++, have_kmp_patch_count++; + + noPRINT("SET-KMP/OUT: PC=%d, KPC=%d, force=%d\n", + have_patch_count, have_kmp_patch_count, force_kmp ); +} + +//----------------------------------------------------------------------------- + +int ScanOptKmp ( ccp arg ) +{ + if (!arg) + return 0; + + s64 stat = ScanKeywordList(arg,opt_kmp_tab,0,true,0,KMP_MODE, + "Option --kmp",ERR_SYNTAX); + if ( stat != -1 ) + { + SetKmpMode(stat); + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetKmpMode() +{ + static char buf[200] = {0}; + if (!*buf) + PrintKeywordList( buf, sizeof(buf), 0, opt_kmp_tab, + KMP_MODE, KMPMD_M_DEFAULT, KMPMD_F_HIDE ); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupKMP() +{ + if ( opt_tiny > 0 ) + { + const kmp_mode_t tiny + = ( (kmp_mode_t)opt_tiny << KMPMD_S_TINY ) & KMPMD_M_TINY; + if ( tiny > ( KMP_MODE & KMPMD_M_TINY )) + SetKmpMode( KMP_MODE & ~KMPMD_M_TINY | tiny ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_diff_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +kmp_diff_mode_t KMP_DIFF_MODE = KDMD_M_DEFAULT; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_kmp_diff_tab[] = +{ + { KDMD_NONE, "NONE", 0, KDMD_M_ALL | KDMD_F_HIDE }, + { KDMD_M_GOOD, "GOOD", 0, KDMD_M_ALL | KDMD_F_HIDE }, + { KDMD_M_DEFAULT, "DEFAULT", 0, KDMD_M_ALL | KDMD_F_HIDE }, + { KDMD_M_ALL, "ALL", 0, KDMD_M_ALL | KDMD_F_HIDE }, + + { KDMD_KTPT, "KTPT", 0, 0 }, + { KDMD_ENPT, "ENPT", 0, 0 }, + { KDMD_ENPH, "ENPH", 0, 0 }, + { KDMD_ITPT, "ITPT", 0, 0 }, + { KDMD_ITPH, "ITPH", 0, 0 }, + { KDMD_CKPT, "CKPT", 0, 0 }, + { KDMD_CKPH, "CKPH", 0, 0 }, + { KDMD_GOBJ, "GOBJ", 0, 0 }, + { KDMD_POTI, "POTI", 0, 0 }, + { KDMD_AREA, "AREA", 0, 0 }, + { KDMD_CAME, "CAME", 0, 0 }, + { KDMD_JGPT, "JGPT", 0, 0 }, + { KDMD_CNPT, "CNPT", 0, 0 }, + { KDMD_MSPT, "MSPT", 0, 0 }, + { KDMD_STGI, "STGI", 0, 0 }, + + { KDMD_SORT, "SORT", "-NOSORT", KDMD_M_SORT }, + { KDMD_NOSORT, "NOSORT", "-SORT", KDMD_M_SORT }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKmpDiff ( ccp arg ) +{ + if (!arg) + return 0; + + s64 stat = ScanKeywordList(arg,opt_kmp_diff_tab,0,true,0,KMP_MODE, + "Option --kmp",ERR_SYNTAX); + if ( stat != -1 ) + { + KMP_DIFF_MODE = stat & KDMD_M_ALL; + + if ( !(KMP_DIFF_MODE & KDMD_M_SECTION) ) + KMP_DIFF_MODE |= KDMD_M_SECTION; + + if ( !(KMP_DIFF_MODE & KDMD_M_SORT) ) + KMP_DIFF_MODE |= KDMD_SORT; + else if ( KMP_DIFF_MODE & KDMD_SORT ) + KMP_DIFF_MODE &= ~KDMD_NOSORT; + + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetKmpDiffMode() +{ + static char buf[200] = {0}; + if (!*buf) + PrintKeywordList( buf, sizeof(buf), 0, opt_kmp_diff_tab, + KMP_DIFF_MODE, KDMD_M_DEFAULT, KDMD_F_HIDE ); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_tform_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +kmp_tform_t KMP_TFORM = KMPTF_M_DEFAULT; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_tformkmp_tab[] = +{ + { 0, "NONE", "CLEAR", KMPTF_M_ALL | KMPTF_F_HIDE }, + { KMPTF_M_DEFAULT, "DEFAULT", 0, KMPTF_M_ALL | KMPTF_F_HIDE }, + { KMPTF_M_ALL, "ALL", 0, KMPTF_M_ALL }, + + { KMPTF_AREA, "AREA", 0, KMPTF_AREA }, + { 0, "-AREA", 0, KMPTF_AREA | KMPTF_F_HIDE }, + { KMPTF_AREA_POS, "AREA-POS", "AREAPOS", 0 }, + { KMPTF_AREA_ROTATE, "AREA-ROTATE", "AREAROTATE", 0 }, + { KMPTF_AREA_SCALE, "AREA-SCALE", "AREASCALE", 0 }, + + { KMPTF_CAME, "CAME", 0, 0 }, + { KMPTF_CAME_POS, "CAME-POS", "CAMEPOS", 0 }, + + { KMPTF_CKPT, "CKPT", 0, 0 }, + { KMPTF_CKPT_POS, "CKPT-POS", "CKPTPOS", 0 }, + + { KMPTF_CNPT, "CNPT", 0, KMPTF_CNPT }, + { 0, "-CNPT", 0, KMPTF_CNPT | KMPTF_F_HIDE }, + { KMPTF_CNPT_POS, "CNPT-POS", "CNPTPOS", 0 }, + { KMPTF_CNPT_ROTATE, "CNPT-ROTATE", "CNPTROTATE", 0 }, + + { KMPTF_ENPT, "ENPT", 0, KMPTF_ENPT }, + { 0, "-ENPT", 0, KMPTF_ENPT | KMPTF_F_HIDE }, + { KMPTF_ENPT_POS, "ENPT-POS", "ENPTPOS", 0 }, + { KMPTF_ENPT_SCALE, "ENPT-SCALE", "ENPTSCALE", 0 }, + + { KMPTF_GOBJ, "GOBJ", 0, KMPTF_GOBJ }, + { 0, "-GOBJ", 0, KMPTF_GOBJ | KMPTF_F_HIDE }, + { KMPTF_GOBJ_POS, "GOBJ-POS", "GOBJPOS", 0 }, + { KMPTF_GOBJ_ROTATE, "GOBJ-ROTATE", "GOBJROTATE", 0 }, + { KMPTF_GOBJ_SCALE, "GOBJ-SCALE", "GOBJSCALE", 0 }, + + { KMPTF_ITPT, "ITPT", 0, KMPTF_ITPT }, + { 0, "-ITPT", 0, KMPTF_ITPT | KMPTF_F_HIDE }, + { KMPTF_ITPT_POS, "ITPT-POS", "ITPTPOS", 0 }, + { KMPTF_ITPT_SCALE, "ITPT-SCALE", "ITPTSCALE", 0 }, + + { KMPTF_JGPT, "JGPT", 0, KMPTF_JGPT }, + { 0, "-JGPT", 0, KMPTF_JGPT | KMPTF_F_HIDE }, + { KMPTF_JGPT_POS, "JGPT-POS", "JGPTPOS", 0 }, + { KMPTF_JGPT_ROTATE, "JGPT-ROTATE", "JGPTROTATE", 0 }, + + { KMPTF_KTPT, "KTPT", 0, KMPTF_KTPT }, + { 0, "-KTPT", 0, KMPTF_KTPT | KMPTF_F_HIDE }, + { KMPTF_KTPT_POS, "KTPT-POS", "KTPTPOS", 0 }, + { KMPTF_KTPT_ROTATE, "KTPT-ROTATE", "KTPTROTATE", 0 }, + + { KMPTF_MSPT, "MSPT", 0, KMPTF_MSPT }, + { 0, "-MSPT", 0, KMPTF_MSPT | KMPTF_F_HIDE }, + { KMPTF_MSPT_POS, "MSPT-POS", "MSPTPOS", 0 }, + { KMPTF_MSPT_ROTATE, "MSPT-ROTATE", "MSPTROTATE", 0 }, + + { KMPTF_POTI, "POTI", 0, 0 }, + { KMPTF_POTI_POS, "POTI-POS", "POTIPOS", 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTformKmp ( ccp arg ) +{ + if (!arg) + return 0; + + s64 start = 0; + if ( *arg == '+' ) + start = KMP_TFORM; + else if ( *arg == '-' ) + start = KMPTF_M_ALL; + + s64 stat = ScanKeywordList(arg,opt_tformkmp_tab,0,true,0,start, + "Option --tform-kmp",ERR_SYNTAX); + if ( stat != -1 ) + { + noPRINT("TFORM: %x -> %llx\n",KMP_TFORM,stat & KMPTF_M_ALL); + KMP_TFORM = stat & KMPTF_M_ALL; + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintKmpTform ( char *buf, uint bufsize, kmp_tform_t mode ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + char *dest = buf; + char *end = buf + bufsize - 1; + + mode = mode & KMPTF_M_ALL | KMPTF_F_HIDE; + kmp_tform_t mode1 = mode; + + const KeywordTab_t *ct; + for ( ct = opt_tformkmp_tab; ct->name1 && dest < end; ct++ ) + { + if ( ct->opt & KMPMD_F_HIDE ) + continue; + + if ( ct->opt ? (mode & ct->opt) == ct->id : mode & ct->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,ct->name1); + mode &= ~(ct->id|ct->opt); + } + } + + if ( mode1 == (KMPTF_M_DEFAULT|KMPTF_F_HIDE) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + return dest-buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetKmpTform() +{ + static char buf[200] = {0}; + if (!*buf) + PrintKmpTform(buf,sizeof(buf),KMP_TFORM); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// speed modifier /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool speed_mod_active = false; +float speed_mod_factor = 0.0; +u16 speed_mod_val = 0; + +/////////////////////////////////////////////////////////////////////////////// + +u16 SpeedMod2u16 ( float speed ) +{ + u8 val[4]; + write_bef4(val,speed); + if ( val[2] >= 0x80 && !++val[1] ) + val[0]++; + return be16(val); +} + +/////////////////////////////////////////////////////////////////////////////// + +float SpeedMod2float ( u16 hex ) +{ + u16 val[2] = { htons(hex), 0 }; + return bef4(val); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptSpeedMod ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --speed-mod",0); + si.float_div++; + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + float factor = GetDoubleV(&temp); + CheckEolSI(&si); + ResetSI(&si); + + if (speed_mod_active) + have_patch_count--, have_kmp_patch_count--; + + if ( factor < 0.0 ) + speed_mod_active = false; + else + { + speed_mod_active = true; + speed_mod_val = SpeedMod2u16(factor); + speed_mod_factor = SpeedMod2float(speed_mod_val); + have_patch_count++; + have_kmp_patch_count++; + } + + return err != ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// second ktpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ktpt2_active = false; +bool ktpt2_auto = false; +double ktpt2_min = 0.0; +double3 ktpt2_pos; + +//----------------------------------------------------------------------------- + +int ScanOptKtpt2 ( ccp arg ) +{ + if (ktpt2_active) + { + ktpt2_active = false; + have_patch_count--, have_kmp_patch_count--; + } + ktpt2_auto = false; + + if ( !arg || !*arg ) + return 0; + + if (!strncasecmp(arg,"auto",4)) + { + arg += 4; + if ( *arg == ',' || *arg == '=' ) + arg++; + ktpt2_min = strtod(arg,0); + if ( !isnormal(ktpt2_min) || ktpt2_min <= 0.0 ) + ktpt2_min = 0.0; + + ktpt2_auto = ktpt2_active = true; + have_patch_count++, have_kmp_patch_count++; + return 0; + } + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --ktpt2",0); + si.float_div++; + + DEFINE_VAR(temp); + enumError err = ScanVectorExprSI(&si,&temp,-1.0,1); + double3 pos = GetVectorV(&temp); + CheckEolSI(&si); + ResetSI(&si); + + if (!err) + { + ktpt2_pos = pos; + ktpt2_active = true; + have_patch_count++, have_kmp_patch_count++; + } + + return err != ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helper functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int GetNextPointKMP +( + const kmp_t * kmp, // KMP data structure + uint sect, // KMP PT section index + int pt_index, // index of point, <0: add N + int sub_index, // index into 'next' + // -1: use 'pt_index+1' in a group + // find first valid at group end + // !-1 && KMP_CKPT: try to use 'next' link + bool unique, // return 'not_found_val' on duplicates + int not_found_val, // return this value, if next point not found + int *ret_pt_index, // not NULL: store fixed 'pt_index' here + int *ret_sub_index, // not NULL: store used sub index here + bool *ret_new_group, // not NULL: store true, if new group + const kmp_enph_entry_t **ret_ph // not NULL: store found ph-record +) +{ + DASSERT(kmp); + DASSERT( sect == KMP_CKPT || sect == KMP_ENPT || sect == KMP_ITPT ); + + if (ret_sub_index) + *ret_sub_index = -1; + if (ret_new_group) + *ret_new_group = false; + if (ret_ph) + *ret_ph = 0; + + const List_t *ptlist, *phlist; + if ( sect == KMP_CKPT ) + { + ptlist = kmp->dlist + KMP_CKPT; + phlist = kmp->dlist + KMP_CKPH; + } + else if ( sect == KMP_ENPT ) + { + ptlist = kmp->dlist + KMP_ENPT; + phlist = kmp->dlist + KMP_ENPH; + } + else + { + DASSERT( sect == KMP_ITPT ); + ptlist = kmp->dlist + KMP_ITPT; + phlist = kmp->dlist + KMP_ITPH; + } + + if ( pt_index < 0 ) + pt_index += ptlist->used; + if ( pt_index < 0 || pt_index >= ptlist->used ) + { + if (ret_pt_index) + *ret_pt_index = -1; + return not_found_val; + } + + if (ret_pt_index) + *ret_pt_index = pt_index; + + const kmp_enph_entry_t *ph = (kmp_enph_entry_t*)phlist->list; + uint n = phlist->used; + while ( pt_index >= ph->pt_start + ph->pt_len && n-- > 0 ) + ph++; + if (ret_ph) + *ret_ph = ph; + + noPRINT(">> %u %zu[%u+%u]\n", idx, + ph-(kmp_enph_entry_t*)phlist->list,ph->pt_start,ph->pt_len); + + if ( pt_index == ph->pt_start + ph->pt_len - 1 ) + { + // end of group reached + if (ret_new_group) + *ret_new_group = true; + + pt_index = -1; // invalidate + uint ph_idx = ~0; + + if ( sub_index >= 0 && sub_index < KMP_MAX_PH_LINK ) + { + ph_idx = ph->next[sub_index]; + if (unique) + { + uint i; + for ( i = 0; i < sub_index; i++ ) + if ( ph_idx == ph->next[i] ) + { + ph_idx = ~0; + break; + } + } + } + else + { + uint idx2; + for ( idx2 = 0; idx2 < KMP_MAX_PH_LINK; idx2++ ) + { + if (!IS_M1(ph->next[idx2])) + { + ph_idx = ph->next[idx2]; + if (ret_sub_index) + *ret_sub_index = idx2; + break; + } + } + } + + if ( ph_idx < phlist->used ) + { + ph = (kmp_enph_entry_t*)phlist->list + ph_idx; + pt_index = ph->pt_start; + } + } + else if ( unique && sub_index >= 1 && sub_index < KMP_MAX_PH_LINK ) + pt_index = -1; + else if ( sub_index != -1 && sect == KMP_CKPT ) + { + const kmp_ckpt_entry_t *pt = (kmp_ckpt_entry_t*)ptlist->list + pt_index; + uint next = pt->next; + pt_index = next < ptlist->used ? next : pt_index + 1; + } + else + pt_index++; + + return pt_index >= 0 && pt_index < ptlist->used ? pt_index : not_found_val; +} + +/////////////////////////////////////////////////////////////////////////////// + +const kmp_enph_entry_t * FindPH +( + const kmp_t * kmp, // KMP data structure + uint sect, // KMP PT or PH section index + int pt_index, // index of point, <0: add N + int * res_ph_index // not NULL: store ph_index or -1 here +) +{ + DASSERT(kmp); + + const List_t *ptlist, *phlist; + if ( sect == KMP_CKPT || sect == KMP_CKPH ) + { + ptlist = kmp->dlist + KMP_CKPT; + phlist = kmp->dlist + KMP_CKPH; + } + else if ( sect == KMP_ENPT || sect == KMP_ENPH ) + { + ptlist = kmp->dlist + KMP_ENPT; + phlist = kmp->dlist + KMP_ENPH; + } + else + { + DASSERT( sect == KMP_ITPT || sect == KMP_ITPH ); + ptlist = kmp->dlist + KMP_ITPT; + phlist = kmp->dlist + KMP_ITPH; + } + + if ( pt_index < 0 ) + pt_index += ptlist->used; + if ( pt_index < 0 || pt_index >= ptlist->used ) + { + if (res_ph_index) + *res_ph_index = -1; + return 0; + } + + const kmp_enph_entry_t *ph = (kmp_enph_entry_t*)phlist->list; + uint i, n = phlist->used; + for ( i = 0; i < n; i++, ph++ ) + if ( pt_index >= ph->pt_start && pt_index < ph->pt_start + ph->pt_len ) + { + if (res_ph_index) + *res_ph_index = i; + return ph; + } + + if (res_ph_index) + *res_ph_index = -1; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +u16 AnalyseItembox ( Itembox_t *ibox, const kmp_gobj_entry_t *op ) +{ + // initialze 'ibox' and returns 'ibox->obj_id' + + DASSERT(ibox); + DASSERT(op); + + memset(ibox,0,sizeof(*ibox)); + if (op) + switch (op->obj_id) + { + case 0x76: // s_itembox + case 0xd4: // w_itembox + case 0xd5: // w_itemboxline + case 0xee: // sin_itembox + ibox->unsure_player = true; + ibox->unsure_enemy = true; + // fall through + + case 0x65: // itembox + case 0xc9: // f_itembox + ibox->obj_id = op->obj_id; + ibox->obj_flags = ObjectInfo[op->obj_id].flags; + ibox->item_player = op->setting[1]; + ibox->item_enemy = op->setting[2]; + break; + } + + return ibox->obj_id; +} + +/////////////////////////////////////////////////////////////////////////////// + +void InsertLinkPH +( + u8 * vector, // insert into vector + u8 link, // link to insert + ScanInfo_t * si, // not NULL: print error message + ccp name, // NULL or name of line for error message + bool allow_dup // true: allow multiple links into same section +) +{ + DASSERT(vector); + + uint i; + for ( i = 0; i < KMP_MAX_PH_LINK; i++ ) + { + if ( vector[i] == 0xff ) + { + vector[i] = link; + return; + } + if ( !allow_dup && vector[i] == link ) + return; + } + + if ( si && !si->no_warn ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ERROR0(ERR_WARNING, + "To many links [%s @%u]: %s\n", + sf->name, sf->line, name ? name : "?" ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int kml_class_idx = 0; + +void SetupClassNamePH +( + kmp_ph_t * ph // valid PH data structure +) +{ + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + { "$OFF", KCLS_OFF }, + { "$ANY", KCLS_ANY }, + { "$DEFAULT", KCLS_DEFAULT }, + {0,0}, + }; + + if (ph->class_name.used) + ResetParamField(&ph->class_name); + + const struct inttab_t *ptr; + for ( ptr = inttab; ptr->name; ptr++ ) + { + ParamFieldItem_t *it = FindInsertParamField(&ph->class_name,ptr->name,false,0); + DASSERT(it); + it->num = ptr->val; + } + kml_class_idx = KCLS_USER; + + ph->default_class = KCLS_DEFAULT; +} + +/////////////////////////////////////////////////////////////////////////////// + +int InsertClassNamePH +( + // return 1..254 if found or set, 255 if list is full, -1 on error + + kmp_ph_t * ph, // valid PH data structure + ccp cname, // NULL or name of class + bool no_warn // TRUE: suppress warning about full list +) +{ + DASSERT(ph); + if ( !cname || !*cname ) + return -1; + + if (!ph->class_name.used) + SetupClassNamePH(ph); + + bool old_found; + ParamFieldItem_t *it = FindInsertParamField(&ph->class_name,cname,false,&old_found); + DASSERT(it); + if (!old_found) + { + it->num = kml_class_idx++; + if ( it->num > MAX_CLASS_NAMES ) + { + it->num = kml_class_idx = MAX_CLASS_NAMES; + if (!no_warn) + ERROR0(ERR_WARNING, + "Class name list is full (%u names), can't add: %s\n", + MAX_CLASS_NAMES, cname ); + } + } + return it->num; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindClassNamePH +( + // return -1, if not found, 1..254 else + + kmp_ph_t * ph, // valid PH data structure + ccp cname // NULL or name of class +) +{ + DASSERT(ph); + ParamFieldItem_t *it = cname ? FindParamField(&ph->class_name,cname) : 0; + return it ? it->num : -1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// RenameGroupKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct group_t +{ + u8 done; + u8 dispatch; + u8 index; + u8 subindex; + u8 order; +} +group_t; + +/////////////////////////////////////////////////////////////////////////////// + +static u64 IterateGroup +( + const kmp_enph_entry_t * elist, + group_t * grp, + uint idx, + uint n, + uint last_index, + u64 steps +) +{ + DASSERT(elist); + DASSERT(grp); + DASSERT(idxnext[ni]; + if ( next < n ) + { + group_t * g = grp + next; + if ( !g->done && g->index < last_index ) + { + g->index = last_index; + g->done++; + noPRINT(" -> ni=%u, next=%u/%u, last=%u\n",ni,next,n,last_index); + steps = IterateGroup(elist,grp,next,n,last_index,steps); + g->done--; + } + } + } + } + return steps; +} + +/////////////////////////////////////////////////////////////////////////////// + +void RenameGroupKMP +( + kmp_t * kmp, // KMP data structure + uint sect_ph, // ph section index + kmp_group_name_t * gname, // pointer to group name array + bool enable_dp // enable dispatch point support +) +{ + DASSERT(kmp); + DASSERT( sect_ph == KMP_CKPH || sect_ph == KMP_ENPH || sect_ph == KMP_ITPH ); + DASSERT(gname); + + const List_t *phlist = kmp->dlist + sect_ph; + const uint n = phlist->used < KMP_MAX_GROUP ? phlist->used : KMP_MAX_GROUP; + memset(gname,0,sizeof(*gname)*KMP_MAX_GROUP); + + uint last_index = 0; + group_t grp[KMP_MAX_GROUP]; + memset(grp,0,sizeof(grp)); + + const kmp_enph_entry_t *elist = (kmp_enph_entry_t*)(phlist->list); + + + //--- setup dispatch points + + uint dp_fw = 0; + if (enable_dp) + { + uint gi, dp = 0; + group_t *gp; + const kmp_enph_entry_t *p = elist; + for ( gi = 0, gp = grp; gi < n; gi++, gp++, p++ ) + if (IsDispatchPointKMP(p)) + { + gp->done++; + gp->dispatch = ++dp; + } + dp_fw = snprintf(*gname,sizeof(*gname),"%u",dp); + } + + + //--- find index number + + for(;;) + { + //--- find first non named group + + uint g1 = 0; + while ( g1 < n && grp[g1].done ) + g1++; + if ( g1 == n ) + break; + + grp[g1].done = 1; + grp[g1].index = ++last_index; + #if HAVE_PRINT0 + const uint steps = IterateGroup(elist,grp,g1,n,last_index,0); + PRINT(" SECT=%s, steps=%8u\n",kmp_section_name[sect_ph].name1,steps); + #else + IterateGroup(elist,grp,g1,n,last_index,0); + #endif + + uint gi; + group_t * gp; + for ( gi = 0, gp = grp; gi < n; gi++, gp++ ) + if (gp->index) + { + gp->done = 1; + if ( last_index < gp->index ) + last_index = gp->index; + } + } + + + //--- calc subindex + + u8 count[KMP_MAX_GROUP]; + memset(count,0,sizeof(count)); + + uint gi; + group_t *gp; + for ( gi = 0, gp = grp; gi < n; gi++, gp++ ) + gp->subindex = count[gp->index]++; + + + //--- print names + + const int fw = snprintf(*gname,sizeof(*gname),"%u",last_index); + for ( gi = 0, gp = grp; gi < n; gi++, gp++, gname++ ) + { + if (gp->dispatch) + snprintf( *gname,sizeof(*gname), "DP%0*u", dp_fw, gp->dispatch ); + else + { + uint nc = count[gp->index]; + if ( nc <= 1 ) + snprintf(*gname,sizeof(*gname), "G%0*u", fw,gp->index ); + else if ( nc <= 26 ) + snprintf(*gname,sizeof(*gname), + "G%0*u%c", fw,gp->index, 'A' + gp->subindex ); + else + snprintf(*gname,sizeof(*gname), + "G%0*u%c%c", fw,gp->index, + 'A' + gp->subindex/26, 'A' + gp->subindex%26 ); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP pathes /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kmp_link_t +{ + u8 cur; // item index of current point + u8 next; // item index of next point + u8 grp_cur; // group index of current point + u8 grp_next; // group index of next point +} +kmp_link_t; + +//----------------------------------------------------------------------------- + +typedef struct kmp_path_t +{ + uint sect_ph; // analysed PH section + uint sect_pt; // analysed PT section + + uint n_ph; // number of groups + uint n_pt; // number of items + + group_t grp[KMP_MAX_GROUP]; // group_t data + u8 grp_order[KMP_MAX_GROUP]; // order => index into 'grp' + u8 itm_grp[KMP_MAX_POINT]; // group of each item + + kmp_link_t *link; // allocated list of links + uint n_link; // number of element in 'link' +} +kmp_path_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeKmpPath ( kmp_path_t *path ) +{ + DASSERT(path); + memset(path,0,sizeof(*path)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ResetKmpPath ( kmp_path_t *path ) +{ + DASSERT(path); + if (path->link) + FREE(path->link); + InitializeKmpPath(path); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeKmpPath +( + const kmp_t *kmp, // KMP data structure + uint sect_ph, // ph section index + kmp_path_t *path, // store group list here + bool init_path, // true: initialize 'path' + uint first_grp // index of first group (robust) +) +{ + #if HAVE_PRINT + u_usec_t start_time = GetTimerUSec(); + #endif + + DASSERT(kmp); + DASSERT( sect_ph == KMP_CKPH || sect_ph == KMP_ENPH || sect_ph == KMP_ITPH ); + DASSERT(path); + + if (init_path) + InitializeKmpPath(path); + else + ResetKmpPath(path); + + DASSERT( KMP_CKPT+1 == KMP_CKPH ); + DASSERT( KMP_ENPT+1 == KMP_ENPH ); + DASSERT( KMP_ITPT+1 == KMP_ITPH ); + path->sect_ph = sect_ph; + path->sect_pt = sect_ph - 1; + + const List_t *phlist = kmp->dlist + sect_ph; + const uint n_ph = phlist->used < KMP_MAX_GROUP ? phlist->used : KMP_MAX_GROUP; + const kmp_enph_entry_t * elist = (kmp_enph_entry_t*)(phlist->list); + + const List_t *ptlist = kmp->dlist + path->sect_pt; + const uint n_pt = ptlist->used < KMP_MAX_POINT ? ptlist->used : KMP_MAX_POINT; + + uint last_index = 0; + path->n_ph = n_ph; + path->n_pt = n_pt; + + for(;;) + { + if ( first_grp >= n_ph ) + first_grp = 0; + PRINT("GRP: ---- %u ---\n",first_grp); + + for(;;) + { + uint g1 = first_grp; + while ( g1 < n_ph && path->grp[g1].done ) + g1++; + if ( g1 == n_ph ) + break; + + path->grp[g1].done = 1; + path->grp[g1].index = ++last_index; + #if HAVE_PRINT0 + const uint steps = IterateGroup(elist,path->grp,g1,n_ph,last_index,0); + PRINT(" SECT=%s, steps=%8u\n",kmp_section_name[sect_ph].name1,steps); + #else + IterateGroup(elist,path->grp,g1,n_ph,last_index,0); + #endif + + uint gi; + group_t * gp; + for ( gi = 0, gp = path->grp; gi < n_ph; gi++, gp++ ) + { + if (gp->index) + { + gp->done = 1; + if ( last_index < gp->index ) + last_index = gp->index; + noPRINT("GRP %3u: %u.%u\n",gi,gp->index,gp->subindex); + } + } + } + + if (!first_grp) + break; + first_grp = 0; + } + + + //--- calc subindex + + u8 count[KMP_MAX_GROUP]; + memset(count,0,sizeof(count)); + + uint gi; + group_t *gp; + for ( gi = 0, gp = path->grp; gi < n_ph; gi++, gp++ ) + { + gp->subindex = count[gp->index]++; + noPRINT("GRP %3u: %u.%u\n",gi,gp->index,gp->subindex); + } + + + //--- sort groups + + u8 index[KMP_MAX_GROUP]; + memset(index,0,sizeof(index)); + for ( gi = 1; gi < n_ph; gi++ ) + index[gi] = index[gi-1] + count[gi-1]; + + for ( gi = 0, gp = path->grp; gi < n_ph; gi++, gp++ ) + { + gp->done = 0; // clear done for further processing + gp->order = index[gp->index] + gp->subindex; + noPRINT("GRP %3u: %3u => %u.%u\n",gi,gp->grp_order,gp->index,gp->subindex); + path->grp_order[gp->order] = gi; + } + + + //--- setup item lists + + kmp_link_t item[KMP_MAX_PH_LINK*(KMP_MAX_GROUP+2)]; + kmp_link_t *ip = item, *iend = (kmp_link_t*)((u8*)item + sizeof(item)) - KMP_MAX_PH_LINK; + memset(item,0,sizeof(item)); + memset(path->itm_grp,0xff,sizeof(path->itm_grp)); + + uint i; + for ( i = 0; i < n_ph; i++ ) + { + uint gi = path->grp_order[i]; + + const kmp_enph_entry_t *ep = elist + gi; + uint idx = ep->pt_start; + uint end = idx + ep->pt_len; + if ( end > n_pt ) + end = n_pt; + end--; + for ( ; idx <= end && ip < iend; idx++ ) + { + if (path->itm_grp[idx] == 0xff ) + { + path->itm_grp[idx] = gi; + if ( idx == end ) + { + // end of group -> multi links + uint link; + for ( link = 0; link < KMP_MAX_PH_LINK; link++ ) + { + int next = GetNextPointKMP(kmp,path->sect_pt,idx,link,true,-1,0,0,0,0); + if ( next >= 0 ) + { + ip->cur = idx; + ip->next = next; + ip->grp_cur = gi; + ip++; + } + } + } + else + { + ip->cur = idx; + ip->next = idx + 1; + ip->grp_cur = gi; + ip++; + } + } + } + } + + + //--- calculate 'next group' + + kmp_link_t *jp; + for ( jp = item; jp < ip; jp++ ) + jp->grp_next = path->itm_grp[jp->next]; + + + //--- add points not handled by a group + + for ( i = 0; i < n_pt && ip < iend; i++ ) + if ( path->itm_grp[i] == 0xff ) + { + ip->cur = i; + ip->next = i + 1; + ip->grp_cur = 0xff; + ip->grp_next = 0xff; + ip++; + } + + path->n_link = ip - item; + path->link = MEMDUP(item,path->n_link*sizeof(*item)); + + + //--- debugging + + PRINT("AnalyzeKmpPath(%s) done in %llu µsec\n", + kmp_section_name[sect_ph].name1, GetTimerUSec()-start_time); + + #if HAVE_PRINT0 + { + PRINT("----- GROUP ORDER '%s' -----\n",kmp_section_name[sect_ph].name1); + uint i; + for ( i = 0; i < n_ph; i++ ) + { + uint gi = path->grp_order[i]; + const group_t *gp = path->grp + gi; + const kmp_enph_entry_t *ep = elist + gi; + + PRINT("%3u. GRP %3u: %3u.%u : %3u .. %3u [+%3u]\n", + gp->order, gi, gp->index, gp->subindex, + ep->pt_start, ep->pt_start + ep->pt_len-1, ep->pt_len ); + } + } + #endif + + + #if HAVE_PRINT0 + { + PRINT("----- ITEM ORDER '%s' -----\n",kmp_section_name[sect_ph].name1); + uint i; + for ( i = 0; i < path->n_link; i++ ) + { + const kmp_link_t *link = path->link + i; + PRINT("%3u. ITM %3u -> %3u : %2u -> %3u\n", + i, link->cur, link->next, link->grp_cur, link->grp_next ); + } + } + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +const kmp_link_t * FindCKPT +( + const kmp_t *kmp, // KMP data structure + kmp_path_t *ck_path, // ckeck point path data, already initialized + // data is calculated in first call + float x, // x-coordiante of point + float z, // y-coordiante of point + const kmp_link_t *start_link // NULL or start link +) +{ + DASSERT(kmp); + DASSERT(ck_path); + + const List_t *ptlist = kmp->dlist + KMP_CKPT; + const uint n_pt = ptlist->used < KMP_MAX_POINT ? ptlist->used : KMP_MAX_POINT; + if ( n_pt < 2 ) + return 0; + + if ( !ck_path->n_link || ck_path->n_pt != n_pt || ck_path->sect_ph != KMP_CKPH ) + { + AnalyzeKmpPath(kmp,KMP_CKPH,ck_path,false,0); + start_link = 0; + } + if (!start_link) + start_link = ck_path->link; + const kmp_link_t *link, *endlink = ck_path->link + ck_path->n_link; + + float pts[2]; + pts[0] = x; + pts[1] = z; + + link = start_link; + const kmp_ckpt_entry_t *pt_base = (kmp_ckpt_entry_t*)ptlist->list; + + for(;;) + { + DASSERT( link->cur < n_pt ); + DASSERT( link->next < n_pt ); + float quad[8]; + + const kmp_ckpt_entry_t *pt1 = pt_base + link->cur; + quad[0] = pt1->left[0]; + quad[1] = pt1->left[1]; + quad[2] = pt1->right[0]; + quad[3] = pt1->right[1]; + + const kmp_ckpt_entry_t *pt2 = pt_base + link->next; + quad[4] = pt2->right[0]; + quad[5] = pt2->right[1]; + quad[6] = pt2->left[0]; + quad[7] = pt2->left[1]; + + int dir; + if ( PointsInConvexPolygonF(pts,1,quad,4,true,&dir) && dir >= 0 ) + return link; + + if ( ++link == endlink ) + link = ck_path->link; + if ( link == start_link ) + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckConvexCKPT +( + // returns + // -1: illegal index + // 0: convex + // 1: concave + + const kmp_t *kmp, // KMP data structure + uint ckpt1, // index of first check point (robust) + uint ckpt2, // index of second check point (robust) + double *min_distance // not NULL: store minimum distance here +) +{ + DASSERT(kmp); + + int stat = -1; + double min_dist = INFINITY; + + const List_t *ptlist = kmp->dlist + KMP_CKPT; + const uint n_pt = ptlist->used; + + if ( ckpt1 < n_pt && ckpt2 < n_pt ) + { + float p[5][2]; + + const kmp_ckpt_entry_t *pt1 = (kmp_ckpt_entry_t*)ptlist->list + ckpt1; + p[0][0] = p[4][0] = pt1->left[0]; + p[0][1] = p[4][1] = pt1->left[1]; + p[1][0] = pt1->right[0]; + p[1][1] = pt1->right[1]; + + const kmp_ckpt_entry_t *pt2 = (kmp_ckpt_entry_t*)ptlist->list + ckpt2; + p[2][0] = pt2->right[0]; + p[2][1] = pt2->right[1]; + p[3][0] = pt2->left[0]; + p[3][1] = pt2->left[1]; + + double dir[5]; + uint j; + for ( j = 0; j < 4; j++ ) + { + noPRINT(" - p: [%8.1f,%8.1f] [%8.1f,%8.1f]\n", + p[j][0], p[j][1], p[j+1][0], p[j+1][1] ); + const double dx = p[j+1][0] - p[j][0]; + const double dz = p[j+1][1] - p[j][1]; + dir[j] = atan2(dx,dz) * (180.0/M_PI); + const double len = dx*dx + dz*dz; + if ( min_dist > len ) + min_dist = len; + noPRINT(" - DIR: %6.1f %9.1f [%8.1f,%8.1f]\n", + dir[j], len, dx, dz ); + } + dir[4] = dir[0]; + + stat = 0; + for ( j = 0; j < 4; j++ ) + { + double delta = dir[j+1] - dir[j]; + if ( delta < 0.0 ) + delta += 360.0; + else if ( delta > 360.0 ) + delta -= 360.0; + + if ( delta > 180.0 ) + { + stat = 1; + break; + } + } + } + + if (min_distance) + *min_distance = sqrt(min_dist); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const uint kmp_entry_size[KMP_N_SECT+1] = +{ + sizeof(kmp_ktpt_entry_t), + sizeof(kmp_enpt_entry_t), + sizeof(kmp_enph_entry_t), + sizeof(kmp_itpt_entry_t), + sizeof(kmp_itph_entry_t), + sizeof(kmp_ckpt_entry_t), + sizeof(kmp_ckph_entry_t), + sizeof(kmp_gobj_entry_t), + sizeof(kmp_poti_group_t), + sizeof(kmp_area_entry_t), + sizeof(kmp_came_entry_t), + sizeof(kmp_jgpt_entry_t), + sizeof(kmp_cnpt_entry_t), + sizeof(kmp_mspt_entry_t), + sizeof(kmp_stgi_entry_t), + 0 +}; + +const KeywordTab_t kmp_section_name[] = +{ + { KMP_KTPT, "KTPT", "Start position", 0 }, + { KMP_ENPT, "ENPT", "Enemy route point", 0 }, + { KMP_ENPH, "ENPH", "Enemy route group", 0 }, + { KMP_ITPT, "ITPT", "Item route point", 0 }, + { KMP_ITPH, "ITPH", "Item route group", 0 }, + { KMP_CKPT, "CKPT", "Check point", 0 }, + { KMP_CKPH, "CKPH", "Check point group", 0 }, + { KMP_GOBJ, "GOBJ", "Global object", 0 }, + { KMP_POTI, "POTI", "Route", 0 }, + { KMP_AREA, "AREA", "Area", 0 }, + { KMP_CAME, "CAME", "Camera", 0 }, + { KMP_JGPT, "JGPT", "Respawn point", 0 }, + { KMP_CNPT, "CNPT", "Canon point", 0 }, + { KMP_MSPT, "MSPT", "Battle position", 0 }, + { KMP_STGI, "STGI", "Stage info", 0 }, + + { KMP_N_SECT, "SETUP", 0, 1 }, + { KMP_WIM0, "WIM0", 0, 2 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t kmp_rtype_name[] = +{ + { KMP_RT_AUTO, "AUTO", "A", 0 }, + { KMP_RT_ROUTE, "ROUTE", "R", 0 }, + { KMP_RT_DISPATCH, "DISPATCH", "D", 0 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t kmp_oneway_name[] = +{ + { ONEWAY_NONE, "NONE", "OFF", 0 }, + { ONEWAY_PREV, "PREV", 0, 0 }, + { ONEWAY_NEXT, "NEXT", 0, 0 }, + { ONEWAY_BOTH, "BOTH", "ALL", 0 }, + { 0,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeKMP ( kmp_t * kmp ) +{ + static bool done = false; + if (!done) + { + // do this only once + done = true; + SetKmpMode(KMP_MODE); + KMP_ACTION_LOG(false,"Global KMP Modes: %s\n",GetKmpMode()); + noPRINT("Global KMP Modes: %s\n",GetKmpMode()); + } + + DASSERT(kmp); + memset(kmp,0,sizeof(*kmp)); + + kmp->fname = EmptyString; + kmp->nowarn_format = verbose <= -3; + + uint sect; + for ( sect = 0; sect < KMP_N_SECT; sect++ ) + { + InitializeList( kmp->dlist+sect, kmp_entry_size[sect] ); + InitializeIL( kmp->index+sect, 0 ); + } + InitializeList( &kmp->poti_point, sizeof(kmp_poti_point_t) ); + + kmp->index[KMP_GOBJ].prefix = "o"; + kmp->index[KMP_POTI].prefix = "r"; + kmp->index[KMP_AREA].prefix = "a"; + kmp->index[KMP_CAME].prefix = "c"; + kmp->index[KMP_JGPT].prefix = "j"; + kmp->index[KMP_CNPT].prefix = "n"; + kmp->index[KMP_MSPT].prefix = "m"; + kmp->index[KMP_STGI].prefix = "s"; + + kmp->revision = REVISION_NUM; + kmp->kmp_version = 0x9d8; + + kmp->rtobj.obj = GOBJ_OBAKE_BLOCK; + kmp->rtobj.scale.x = 0.2; + kmp->rtobj.scale.y = 0.4; + kmp->rtobj.scale.z = 1.0; + + kmp->ckpt_object1 = RTOBJ_1; + kmp->ckpt_object2 = RTOBJ_2; + kmp->ckpt_object1_base = RTOBJ_BASE; + kmp->ckpt_object2_base = RTOBJ_BASE; + + kmp->jgpt_auto_distance = 1000.0; + kmp->jgpt_auto_adjust.x = 0.0; + kmp->jgpt_auto_adjust.y = 200.0; + kmp->jgpt_auto_adjust.z = 0.0; + + InitializePH( &kmp->ckph, KMP_CKPH, KMP_CKPT, KMP_AC_PREV ); + InitializePH( &kmp->enph, KMP_ENPH, KMP_ENPT, KMP_AC_PREV ); + InitializePH( &kmp->itph, KMP_ITPH, KMP_ITPT, KMP_AC_PREV | KMP_ACF_FIX ); + + InitializeKMPFlags( &kmp->enpt_flag, KPFL_M_ENPT ); + InitializeKMPFlags( &kmp->itpt_flag, KPFL_M_ITPT ); + InitializeKMPFlags( &kmp->jgpt_flag, KPFL_M_JGPT ); + + if ( opt_wim0 >= OFFON_ON ) + kmp->wim0_export = 1; + + kmp->enable_fall_kcl = ( KCL_MODE & KCLMD_ADD_ROAD ) != 0; + + #if HAVE_PRINT0 + { + char buf[20]; + int i; + for ( i = 1; i < 9; i++ ) + { + snprintf(buf,sizeof(buf),"CNAME-%03u",i); + int f1 = FindClassNamePH(&kmp->ckph,buf); + int i1 = InsertClassNamePH(&kmp->ckph,buf,false); + int f2 = FindClassNamePH(&kmp->ckph,buf); + printf(" %s : %3d %3d %3d\n",buf,f1,i1,f2); + } + } + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearSectionsKMP ( kmp_t * kmp ) +{ + DASSERT(kmp); + + uint sect; + for ( sect = 0; sect < KMP_N_SECT; sect++ ) + { + ResetList(kmp->dlist+sect); + ResetIL(kmp->index+sect); + } + ResetList(&kmp->poti_point); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetKMP ( kmp_t * kmp ) +{ + DASSERT(kmp); + UnloadAutoReferenceKCL(); + FreeString(kmp->fname); + FreeString(kmp->info); + ClearSectionsKMP(kmp); + FREE(kmp->raw_data); + FREE(kmp->linfo); + FREE(kmp->wim0_data); + + ResetPH(&kmp->ckph); + ResetPH(&kmp->enph); + ResetPH(&kmp->itph); + + if (kmp->fall_kcl) + { + if ( kcl_ref_prio == kmp->fall_kcl ) + kcl_ref_prio = 0; + ResetKCL(kmp->fall_kcl); + FREE(kmp->fall_kcl); + } + + InitializeKMP(kmp); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct kmp_ph_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializePH ( kmp_ph_t *ph, uint sect_ph, uint sect_pt, uint ac_default ) +{ + DASSERT(ph); + memset(ph,0,sizeof(*ph)); + + ph->sect_ph = sect_ph; + ph->sect_pt = sect_pt; + ph->show_options = opt_route_options >= OFFON_ON; + ph->default_class = KCLS_DEFAULT; + ph->ac_mode = ac_default & KMP_AC_MASK; + ph->ac_flags = ac_default & KMP_ACF_MASK; + ph->mask[0] = ~0; + ph->mask[1] = ~0; + + ph->oneway_used = 0; + ph->oneway_size = 0; + ph->oneway_list = 0; + + InitializeParamField(&ph->class_name); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPH ( kmp_ph_t *ph ) +{ + DASSERT(ph); + FREE(ph->oneway_list); + ResetParamField(&ph->class_name); + InitializePH(ph,ph->sect_ph,ph->sect_pt,ph->ac_mode|ph->ac_flags); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InsertOnewayPH ( kmp_ph_t *ph, u8 from, u8 to ) +{ + DASSERT(ph); + DASSERT( ph->oneway_used <= ph->oneway_size ); + + if (!FindOnewayPH(ph,from,to)) + { + if ( ph->oneway_used == ph->oneway_size ) + { + ph->oneway_size += 100; + ph->oneway_list = REALLOC(ph->oneway_list, + ph->oneway_size * sizeof(*ph->oneway_list)); + } + DASSERT( ph->oneway_used < ph->oneway_size ); + DASSERT(ph->oneway_list); + ph->oneway_list[ph->oneway_used++] = from << 8 | to; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FindOnewayPH ( const kmp_ph_t *ph, u8 from, u8 to ) +{ + DASSERT(ph); + DASSERT( ph->oneway_used <= ph->oneway_size ); + + if (ph->oneway_used) + { + DASSERT(ph->oneway_list); + const u16 val = from << 8 | to; + const u16 *ptr = ph->oneway_list; + const u16 *end = ptr + ph->oneway_used; + while ( ptr < end ) + if ( *ptr++ == val ) + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int SortOnewayPH_helper ( const void *a, const void *b ) +{ + return *(u16*)a - *(u16*)b; +} + +//----------------------------------------------------------------------------- + +void SortOnewayPH ( const kmp_ph_t *ph ) +{ + DASSERT(ph); + if ( ph->oneway_used > 1 ) + qsort( ph->oneway_list, ph->oneway_used, sizeof(*ph->oneway_list), + SortOnewayPH_helper ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct kmp_flag_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeKMPFlags ( kmp_flag_t * kf, u8 mask ) +{ + DASSERT(kf); + memset(kf,0,sizeof(*kf)); + + kf->export = opt_export_flags >= OFFON_ON; + + kf->mask = KPFL_M_ALL & mask; + memset( kf->flags, kf->mask, sizeof(kf->flags) ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AnalyseSectionKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool AnalyseSectionKMP +( + // return true if known section found + + kmp_section_t *ks, // store result here + const void *data, // KMP raw data to scan + uint size, // size of 'data' + uint index // index of section to analyse +) +{ + DASSERT(ks); + memset(ks,0,sizeof(*ks)); + ks->sect = -1; // invalid + ks->index = index; + + if ( !data || size < sizeof(kmp_file_gen_t) ) + return false; + + const kmp_file_gen_t *kmp = data; + if ( memcmp(kmp->magic,"RKMD",sizeof(kmp->magic))) + return false; + + const uint base_off = ntohs(kmp->head_size); + const uint n_sect = ntohs(kmp->n_sect); + if ( base_off < GetFileHeadSizeKMP(n_sect) || base_off >= size || index >= n_sect ) + return false; + + const uint sect_off = base_off + ntohl(kmp->sect_off[index]); + if ( sect_off < base_off || sect_off > size - sizeof(kmp_list_head_t) ) + return false; + + + //--- find section type + + kmp_list_head_t *sect_ptr = (kmp_list_head_t*)(data+sect_off); + ks->ptr = sect_ptr; + + const KeywordTab_t *kp; + for ( kp = kmp_section_name; kp->name1; kp++ ) + if (!memcmp(sect_ptr->magic,kp->name1,4)) + break; + if (!kp->name1) + return -2; + ks->sect = kp->id; + + + //--- analyse length + + uint sect_size = sizeof(kmp_list_head_t) + + ntohs(sect_ptr->n_entry) * kmp_entry_size[ks->sect]; + if ( ks->sect == KMP_POTI ) + sect_size += sizeof(kmp_poti_point_t) * ntohs(sect_ptr->value); + + + //--- check limit + + uint next_off = base_off + + ( index+1 < n_sect ? ntohl(kmp->sect_off[index+1]) : size ); + if ( next_off > size ) + next_off = size; + if ( sect_size > next_off - sect_off ) + return false; + + ks->size = sect_size; + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP: scan helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const void * get_kmp_pointer +( + // return pointer to first element, or NULL if not found + + uint * res_n, // not NULL: store number of entries + uint * res_val, // not NULL: store 'unknown value' + uint * res_total, // not NULL: store total section size incl header + + kmp_t * kmp, // KMP data structure + const void * data, // data to scan + uint data_size, // size of 'data' + uint sect // KMP section index +) +{ + DASSERT(kmp); + DASSERT(data); + DASSERT( sect < KMP_N_SECT || sect == KMP_WIM0 ); + + kmp_head_info_t hi; + ScanHeadInfoKMP(&hi,data,data_size); + + ccp sect_name = kmp_section_name[sect].name1; + const kmp_list_head_t *list = 0; + + if ( sect < hi.n_sect ) + { + // optimization: try standard slot + const uint off = ntohl(hi.sect_off[sect]); + if ( off <= hi.max_off && !(off&3) ) + { + list = (kmp_list_head_t*) ( data + hi.head_size + off ); + if (memcmp(list->magic,sect_name,sizeof(list->magic))) + list = 0; + } + } + + if (!list) + { + // not found yet: try iteration + + noPRINT("'%s' FAILED, TRY ITERATION, ns=%d, maxoff=%x\n", + sect_name, n_sect, hi.max_off ); + + int s; + for ( s = -1; s < hi.n_sect; s++ ) + { + const uint off = ntohl(hi.sect_off[s]); + noPRINT("%3d: %x/%x\n",s,off,hi.max_off); + if ( off > hi.max_off || off&3 ) + continue; + + const kmp_list_head_t *temp + = (kmp_list_head_t*) ( data + hi.head_size + off ); + if (!memcmp(temp->magic,sect_name,sizeof(temp->magic))) + { + list = temp; + break; + } + } + + if (!list) + { + if ( sect != KMP_WIM0 && !kmp->nowarn_format ) + ERROR0(ERR_WARNING, + "KMP: Section [%s] not found: %s\n", + kmp_section_name[sect].name1, kmp->fname ); + if (res_n) *res_n = 0; + if (res_val) *res_val = 0; + if (res_total) *res_total = 0; + return 0; + } + + noPRINT("'%s' FOUND AT SLOT %u\n",sect_name,s); + } + + if ( sect == KMP_WIM0 ) + { + const uint size = be32(&list->n_entry); + if (res_n) *res_n = size; + if (res_val) *res_val = 0; + if (res_total) *res_total = size + sizeof(*list); + return list->entry; + } + + kmp->value[sect] = be16(&list->value); + const u8 * elem = list->entry; + uint n = be16(&list->n_entry); + uint max = ( (u8*)data + data_size - elem ) / kmp_entry_size[sect]; + if ( n > max ) + { + ERROR0(ERR_WARNING, + "Number of [%s] entries exceed file size (%u>%u): %s\n", + kmp_section_name[sect].name1, n, max, kmp->fname ); + n = max; + } + if (res_n) + *res_n = n; + if (res_val) + *res_val = be16(&list->value); + + const u8 *end = elem + n * kmp_entry_size[sect]; + if ( kmp->max_scanned < end ) + kmp->max_scanned = end; + + if (res_total) + { + *res_total = sizeof(kmp_list_head_t) + n * kmp_entry_size[sect]; + if ( sect == KMP_POTI ) + *res_total += sizeof(kmp_poti_point_t) * be16(&list->value); + } + return elem; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint test_unknown_sections +( + const void * data, // data to scan + uint data_size, // size of 'data' + ccp fname, // file name for error messages + bool *res_shrinked // not NULL: store status about shrinked KMP +) +{ + kmp_head_info_t hi; + ScanHeadInfoKMP(&hi,data,data_size); + + const bool is_shrinked = hi.n_sect < KMP_N_SECT; + if (res_shrinked) + *res_shrinked = is_shrinked; + if ( is_shrinked ) + ERROR0(ERR_WARNING, + "KMP: Is shrinked => some sections missed, some moved: %s\n", + fname ); + + uint err_count = 0; + int s; + for ( s = -1; s < hi.n_sect; s++ ) + { + const uint off = ntohl(hi.sect_off[s]); + noPRINT("%3d: %x/%x\n",s,off,hi.max_off); + if ( off > hi.max_off || off&3 ) + continue; + + ccp expected_sect_name = s < 0 || s >= KMP_N_SECT + ? "----" : kmp_section_name[s].name1; + const kmp_list_head_t *temp + = (kmp_list_head_t*) ( data + hi.head_size + off ); + if (memcmp(temp->magic,expected_sect_name,sizeof(temp->magic))) + { + uint i; + for ( i = 0; i < KMP_N_SECT; i++ ) + { + const uint off2 = ntohl(hi.sect_off[i]); + if ( off2 > hi.max_off || off2&3 ) + continue; + const kmp_list_head_t * temp2 + = (kmp_list_head_t*) ( data + hi.head_size + off2 ); + if (!memcmp(temp2->magic,expected_sect_name,sizeof(temp2->magic))) + break; + } + + if ( i < KMP_N_SECT && !is_shrinked ) + ERROR0(ERR_WARNING, + "KMP: Section [%s] found at index %u, but not at %d: %s\n", + expected_sect_name, i, s, fname ); + err_count++; + } + } + return err_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void scan_kmp_pt +( + kmp_t * kmp, // KMP data structure + const void * data, // data to scan + uint data_size, // size of 'data' + uint sect // KMP section index +) +{ + DASSERT( sect == KMP_ENPT || sect == KMP_ITPT ); + +//X kmp_ph_t *ph; + kmp_flag_t *kf; + if ( sect == KMP_ENPT ) + { +//X ph = &kmp->enph; + kf = &kmp->enpt_flag; + } + else + { +//X ph = &kmp->itph; + kf = &kmp->itpt_flag; + } + + uint n; + const kmp_enpt_entry_t * pt + = get_kmp_pointer(&n,0,0,kmp,data,data_size,sect); + + u32 flags_or = 0; + u32 flags_and = ~0; + const uint max_import = sizeof(kf->flags)/sizeof(*kf->flags); + + uint i; + for ( i = 0; i < n; i++, pt++ ) + { + kmp_enpt_entry_t * item = AppendList(kmp->dlist+sect); + DASSERT(item); + bef4n(item->position,pt->position,4); + be16n(item->prop,pt->prop,2); + + if ( i < max_import ) + { + flags_or |= *(u32*)&item->scale; + flags_and &= *(u32*)&item->scale; + } + } + + + //--- import flags? + + const bool import = n > 1 + && !( flags_or & KMP_FE_ALWAYS_0 ) + && ( flags_and & KMP_FE_ALWAYS_1 ) == KMP_FE_ALWAYS_1; + if (import) + { + kf->import = kf->export = 1; + typeof(*kf->flags) *flags = kf->flags; + + const List_t *ptlist = kmp->dlist + sect; + kmp_enpt_entry_t * item = (kmp_enpt_entry_t*)ptlist->list; + + const uint max = ptlist->used < max_import ? ptlist->used : max_import; + for ( i = 0; i < max; i++, item++ ) + { + u32 *ptr = (u32*)&item->scale; + *flags++ = ( *ptr & KMP_FE_FLAGS ) >> KMP_FE_FLAGS_SHIFT; + *ptr &= ~KMP_FE_MASK; + } + } + + PRINT("(%s): or=%08x>%x, and=%08x>%x => %d (EXPORT)\n", + kmp_section_name[sect].name1, + flags_or, flags_or & KMP_FE_ALWAYS_0, + flags_and, flags_and & KMP_FE_ALWAYS_1, + import ); +} + + +/////////////////////////////////////////////////////////////////////////////// + +static int sort_ph ( const kmp_enph_entry_t * a, const kmp_enph_entry_t * b ) +{ + int stat = a->pt_start - b->pt_start; + if (!stat) + stat = a->pt_len - b->pt_len; + return stat; +} + +//----------------------------------------------------------------------------- + +static void scan_kmp_ph +( + kmp_t * kmp, // KMP data structure + const void * data, // data to scan + uint data_size, // size of 'data' + uint sect_ph, // PH section index + kmp_ph_t * ph // related PH info +) +{ + DASSERT(kmp); + DASSERT(data); + DASSERT(ph); + DASSERT( sect_ph == KMP_CKPH || sect_ph == KMP_ENPH || sect_ph == KMP_ITPH ); + + uint n; + const kmp_enph_entry_t *phe + = get_kmp_pointer(&n,0,0,kmp,data,data_size,sect_ph); + uint i; + for ( i = 0; i < n; i++, phe++ ) + { + kmp_enph_entry_t * item = AppendList(kmp->dlist+sect_ph); + DASSERT(item); + memcpy(item,phe,sizeof(*item)); + if ( i < KMP_MAX_GROUP ) + { + kmp_gopt2_t *go2 = ph->gopt + i; + go2->setting[0] = item->setting[0]; + go2->setting[1] = item->setting[1]; + } + } + + //--- sort groups + + kmp_enph_entry_t *list = (kmp_enph_entry_t*)(kmp->dlist[sect_ph].list); + qsort( list, kmp->dlist[sect_ph].used, sizeof(*list), (qsort_func)sort_ph ); + + //--- assign group names + + PRINT("scan_kmp_ph(%s), ac:%x,%x, battle=%d\n", + SECT_NAME(sect_ph), ph->ac_mode, ph->ac_flags, kmp->battle_mode); + RenameGroupKMP(kmp,sect_ph,ph->gname, ph->ac_mode == KMP_AC_DISPATCH ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void scan_kmp_pt2 +( + kmp_t * kmp, // KMP data structure + const void * data, // data to scan + uint data_size, // size of 'data' + uint sect // KMP section index +) +{ + DASSERT(kmp); + DASSERT( sect < KMP_N_SECT ); + + uint n; + const kmp_jgpt_entry_t *pt + = get_kmp_pointer(&n,0,0,kmp,data,data_size,sect); + FillIL(kmp->index+sect,n); + + u32 flags_or = 0; + u32 flags_and = ~0; + const uint max_import = sizeof(kmp->jgpt_flag.flags)/sizeof(*kmp->jgpt_flag.flags); + + uint i; + for ( i = 0; i < n; i++, pt++ ) + { + kmp_jgpt_entry_t * item = AppendList(kmp->dlist+sect); + DASSERT(item); + bef4n(item->position,pt->position,6); + be16n((u16*)&item->id,(u16*)&pt->id,2); + + if ( i < max_import ) + { + flags_or |= *(u32*)item->rotation; + flags_and &= *(u32*)item->rotation; + } + } + + const bool import = sect == KMP_JGPT + && !( flags_or & KMP_FE_ALWAYS_0 ) + && ( flags_and & KMP_FE_ALWAYS_1 ) == KMP_FE_ALWAYS_1; + + if (import) + { + kmp->jgpt_flag.export = kmp->jgpt_flag.import = 1; + + const List_t *ptlist = kmp->dlist + sect; + kmp_jgpt_entry_t * item = (kmp_jgpt_entry_t*)ptlist->list; + + const uint max = ptlist->used < max_import ? ptlist->used : max_import; + for ( i = 0; i < max; i++, item++ ) + { + u32 *ptr = (u32*)item->rotation; + kmp->jgpt_flag.flags[i] = ( *ptr & KMP_FE_FLAGS ) >> KMP_FE_FLAGS_SHIFT; + *ptr &= ~KMP_FE_MASK; + } + } + + PRINT("(%s): or=%08x>%x, and=%08x>%x => %d => %d %d (EXPORT)\n", + kmp_section_name[sect].name1, + flags_or, flags_or & KMP_FE_ALWAYS_0, + flags_and, flags_and & KMP_FE_ALWAYS_1, + import, kmp->jgpt_flag.import, kmp->jgpt_flag.export ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(kmp); + if (init_kmp) + InitializeKMP(kmp); + + PRINT0("KMP size:%x\n",data_size); + + const valid_t valid + = IsValidKMP(data,data_size,data_size, kmp->nowarn_format ? 0 : kmp->fname ); + if ( valid >= VALID_ERROR ) + { + if (!kmp->nowarn_format) + ERROR0(ERR_INVALID_DATA, + "Invalid KMP file [%s]: %s\n" + "Add option --kmp=force or --force to ignore some validity checks.", + valid_text[valid], kmp->fname ? kmp->fname : "?" ); + return ERR_INVALID_DATA; + } + + KMP_ACTION_LOG(false,"ScanRawKMP() %s\n",kmp->fname); + kmp->fform = FF_KMP; + kmp->kmp_version = be32( data + 0x0c ); + kmp->max_scanned = 0; + + + //--- test for unknown sections + + test_unknown_sections(data,data_size,kmp->fname,&kmp->is_shrinked); + + + //--- scan KTPT entries + + uint n; + const kmp_ktpt_entry_t * ktpt + = get_kmp_pointer(&n,0,0,kmp,data,data_size,KMP_KTPT); + for ( ; n > 0; n--, ktpt++ ) + { + kmp_ktpt_entry_t * entry = AppendList(kmp->dlist+KMP_KTPT); + DASSERT(entry); + bef4n(entry->position,ktpt->position,6); + be16n((u16*)&entry->player_index,(u16*)&ktpt->player_index,2); + } + + + //--- scan ENPT + ENPH entries + + scan_kmp_pt(kmp,data,data_size,KMP_ENPT); + scan_kmp_ph(kmp,data,data_size,KMP_ENPH,&kmp->enph); + + + //--- scan ITPT + ITPH entries + + scan_kmp_pt(kmp,data,data_size,KMP_ITPT); + scan_kmp_ph(kmp,data,data_size,KMP_ITPH,&kmp->itph); + + + //--- scan CKPT entries + + const kmp_ckpt_entry_t * ckpt + = get_kmp_pointer(&n,0,0,kmp,data,data_size,KMP_CKPT); + for ( ; n > 0; n--, ckpt++ ) + { + kmp_ckpt_entry_t * entry = AppendList(kmp->dlist+KMP_CKPT); + DASSERT(entry); + memcpy(entry,ckpt,sizeof(*entry)); + bef4n(entry->left,ckpt->left,4); + } + + + //--- scan CKPH entries + + scan_kmp_ph(kmp,data,data_size,KMP_CKPH,&kmp->ckph); + + + //--- scan GOBJ entries + + const kmp_gobj_entry_t * gobj + = get_kmp_pointer(&n,0,0,kmp,data,data_size,KMP_GOBJ); + for ( ; n > 0; n--, gobj++ ) + { + kmp_gobj_entry_t * entry = AppendList(kmp->dlist+KMP_GOBJ); + DASSERT(entry); + be16n((u16*)&entry->obj_id,(u16*)&gobj->obj_id,2); + bef4n(entry->position,gobj->position,9); + be16n(&entry->route_id,&gobj->route_id,10); + } + + + //--- scan POTI entries + + uint n_pt; + const kmp_poti_group_t * pgroup + = get_kmp_pointer(&n,&n_pt,0,kmp,data,data_size,KMP_POTI); + while ( n_pt > 0 && n-- > 0 ) + { + kmp_poti_group_t * group = AppendList(kmp->dlist+KMP_POTI); + DASSERT(group); + group->smooth = pgroup->smooth; + group->back = pgroup->back; + group->n_point = be16((u16*)&pgroup->n_point); + if ( group->n_point > n_pt ) + group->n_point = n_pt; + n_pt -= group->n_point; + + const kmp_poti_point_t * point = (kmp_poti_point_t*)(pgroup+1); + uint count; + for ( count = group->n_point; count > 0; count--, point++ ) + { + kmp_poti_point_t * entry = AppendList(&kmp->poti_point); + DASSERT(entry); + bef4n(entry->position,point->position,3); + be16n(&entry->speed,&point->speed,2); + } + pgroup = (kmp_poti_group_t*)point; + } + if ( kmp->max_scanned < (u8*)pgroup ) + kmp->max_scanned = (u8*)pgroup; + + + //--- scan AREA entries + + const kmp_area_entry_t * area + = get_kmp_pointer(&n,0,0,kmp,data,data_size,KMP_AREA); + for ( ; n > 0; n--, area++ ) + { + kmp_area_entry_t * entry = AppendList(kmp->dlist+KMP_AREA); + DASSERT(entry); + memcpy(entry,area,sizeof(*entry)); + bef4n(entry->position,area->position,9); + be16n(entry->setting,area->setting,4); + } + + + //--- scan CAME entries + + const kmp_came_entry_t * came + = get_kmp_pointer(&n,0,0,kmp,data,data_size,KMP_CAME); + for ( ; n > 0; n--, came++ ) + { + kmp_came_entry_t * entry = AppendList(kmp->dlist+KMP_CAME); + DASSERT(entry); + memcpy(entry,came,sizeof(*entry)); + be16n(&entry->came_speed,&came->came_speed,4); + bef4n(entry->position,came->position,15); + } + + + //--- scan JGPT + CNPT + MSPT entries + + scan_kmp_pt2(kmp,data,data_size,KMP_JGPT); + scan_kmp_pt2(kmp,data,data_size,KMP_CNPT); + scan_kmp_pt2(kmp,data,data_size,KMP_MSPT); + + + //--- scan STGI entries + + const kmp_stgi_entry_t * stgi + = get_kmp_pointer(&n,0,0,kmp,data,data_size,KMP_STGI); + if ( n > 0 ) + kmp->stgi = stgi; + for ( ; n > 0; n--, stgi++ ) + { + kmp_stgi_entry_t * entry = AppendList(kmp->dlist+KMP_STGI); + DASSERT(entry); + memcpy(entry,stgi,sizeof(*entry)); + entry->flare_color = be32(&stgi->flare_color); + entry->speed_mod = be16(&stgi->speed_mod); + } + + + //--- scan WIM0 data + + const u8 *wim0_data = get_kmp_pointer(&n,0,0,kmp,data,data_size,KMP_WIM0); + if ( wim0_data && n ) + { + PRINT(">>> WIM0: %p %d\n",wim0_data,n); + HEXDUMP16(9,0,wim0_data,n<0x40?n:0x40); + const u8 *wim0_end = wim0_data + n; + if ( kmp->max_scanned < wim0_end ) + kmp->max_scanned = wim0_end; + + if ( opt_wim0 >= 0 ) + { + kmp->wim0_import_size = sizeof(kmp_list_head_t) + n; + kmp->wim0_import_bz2 = ntohl(*(u32*)wim0_data) != 0; + kmp->wim0_export = 1; + kmp_wim0_t w0; + InitializeWim0(&w0); + w0.bz2data = (u8*)wim0_data; + w0.bz2size = n; + if ( DecompressWim0(&w0) == ERR_OK && w0.used ) + { + FREE(kmp->wim0_data); + kmp->wim0_data = (kmp_wim0_sect_t*)w0.data; + kmp->wim0_size = w0.used; + w0.data = 0; + + HEXDUMP16( 11, 0, kmp->wim0_data, + kmp->wim0_size < 0x80 ? kmp->wim0_size : 0x80 ); + ScanWim0(kmp); + } + w0.bz2data = 0; + ResetWim0(&w0); + } + } + + + //--- info string + + if (kmp->max_scanned) + { + PRINT("SCANNED: %zx/%x\n", kmp->max_scanned - (u8*)data, data_size ); + uint info_off = kmp->max_scanned - (u8*)data; + if ( info_off < data_size ) + { + ccp src = (ccp)kmp->max_scanned; + const int info_len = strlen(src); + if ( info_off + info_len < data_size ) + { + FreeString(kmp->info); + kmp->info = MALLOC(info_len+1); + char *dest = kmp->info; + for(;;) + { + const uchar ch = *src++; + if (!ch) + break; + *dest++ = ch == '\r' || ch == '\n' + ? ' ' + : ch == '"' + ? '\'' + : ch; + } + *dest = 0; + DASSERT( dest == kmp->info + info_len ); + PRINT("INFO FOUND: |%s|\n",kmp->info); + } + } + } + + + //--- setup battle mode and fix settings + + kmp->battle_mode = CheckBattleModeKMP(kmp); + if ( kmp->battle_mode == ARENA_DISPATCH ) + { + kmp->enph.ac_mode = KMP_AC_DISPATCH; + kmp->enph.ac_flags = 0; + RenameGroupKMP(kmp,KMP_ENPH,kmp->enph.gname,true); + } + + //--- term + + kmp->max_scanned = 0; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + const void * data, // data to scan + uint data_size, // size of 'data' + CheckMode_t mode // not NULL: call CheckKMP(mode) +) +{ + DASSERT(kmp); + PRINT("ScanKMP(mode=%x) size=%u\n",mode,data_size); + //HEXDUMP16(0,0,data,16); + + enumError err; +// [[analyse-magic]] + switch (GetByMagicFF(data,data_size,data_size)) + { + case FF_KMP: + kmp->fform = FF_KMP; + err = ScanRawKMP(kmp,init_kmp,data,data_size); + break; + + case FF_KMP_TXT: + kmp->fform = FF_KMP_TXT; + err = ScanTextKMP(kmp,init_kmp,data,data_size); + break; + + default: + if (init_kmp) + InitializeKMP(kmp); + return ERROR0(ERR_INVALID_DATA, + "No KMP file: %s\n", kmp->fname ? kmp->fname : "?"); + } + + PRINT("BATTLE-MODE: %d [opt=%d,isa=%d]\n", + kmp->battle_mode, opt_battle_mode, IsArenaKMP(kmp) ); + + if ( disable_patch_on_load <= 0 ) + PatchKMP(kmp); + + if ( err <= ERR_WARNING && mode ) + CheckKMP(kmp,mode); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckKMP(mode) +) +{ + DASSERT(kmp); + DASSERT(raw); + if (init_kmp) + InitializeKMP(kmp); + else + ResetKMP(kmp); + + kmp->fname = raw->fname; + raw->fname = 0; + + return raw->is_0 ? ERR_OK : ScanKMP(kmp,false,raw->data,raw->data_size,mode); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckKMP(mode) +) +{ + DASSERT(kmp); + DASSERT(fname); + if (init_kmp) + InitializeKMP(kmp); + else + ResetKMP(kmp); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + kmp->fname = raw.fname; + raw.fname = 0; + err = ScanKMP(kmp,false,raw.data,raw.data_size,mode); + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ShrinkKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ShrinkRawKMP +( + // returns true (=success), if dest is created and data is shrinked + + u8 **res_dest, // not NULL: store result here on success + uint *res_dest_size, // not NULL: store result here on success + bool free_old_dest, // true: FREE(*res_dest) on success + + const u8 *src_data, // NULL or source; if NULL: use *res_dest + uint src_size, // size of source + bool log // true: KMP_ACTION_LOG() +) +{ + if (!src_data) + { + src_data = *res_dest; + src_size = *res_dest_size; + } + + if ( !src_data || !src_size ) + return false; + + + //--- calculate new size + + kmp_section_t info[KMP_N_SECT], *iptr = info; + const kmp_file_mkw_t *kfile = (kmp_file_mkw_t*)src_data; + const uint src_sect = ntohs(kfile->n_sect); + uint total_size = 0; + + uint sect; + for ( sect = 0; sect < src_sect; sect++ ) + { + if ( AnalyseSectionKMP(iptr,src_data,src_size,sect) + && iptr->size > sizeof(kmp_list_head_t) ) + { + total_size += iptr->size; + TRACE(">> sect=%2d, index=%2d, off=%6zx .. %6zx, size=%5x %5x\n", + iptr->sect, iptr->index, + (u8*)iptr->ptr - src_data, + (u8*)iptr->ptr - src_data + iptr->size, + iptr->size, total_size ); + iptr++; + } + } + + const uint n_sect = iptr - info; + const uint head_size = GetFileHeadSizeKMP(n_sect); + total_size += head_size; + + if ( !n_sect || total_size >= src_size ) + return false; + + if (log) + KMP_ACTION_LOG(false,"ShrinkKMP() n-sect=%u/%u, size=%u+%u\n", + n_sect, be16(&kfile->n_sect), + head_size, total_size-head_size ); + + u8 *data = MALLOC(total_size); + kmp_file_mkw_t *khead = (kmp_file_mkw_t*)data; + + //--- setup file header + + memcpy(khead->magic,KMP_MAGIC,sizeof(khead->magic)); + khead->file_size = htonl(total_size); + khead->n_sect = htons(n_sect); + khead->head_size = htons(head_size); + khead->version = kfile->version; + + u8 *dest = data + head_size; + uint i; + for ( i = 0, iptr = info; i < n_sect; i++, iptr++ ) + { + khead->sect_off[i] = ntohl(dest-data-head_size); + memcpy(dest,iptr->ptr,iptr->size); + dest += iptr->size; + } + ASSERT( dest == data + total_size ); + + + //--- results + + if (res_dest) + { + if ( free_old_dest && *res_dest ) + FREE(*res_dest); + *res_dest = data; + } + else + FREE(data); + + if (res_dest_size) + *res_dest_size = total_size; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ShrinkKMP +( + kmp_t * kmp // pointer to valid KMP +) +{ + DASSERT(kmp); + TRACE("ShrinkKMP()\n"); + if (!kmp->raw_data) + return ERR_OK; + + return ShrinkRawKMP( &kmp->raw_data, &kmp->raw_data_size, true, 0, 0, true ) + ? ERR_DIFFER : ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawKMP helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void * setup_raw_kmp +( + const kmp_t * kmp, // input: KMP + uint sect, // input: section number + kmp_list_head_t ** hd, // modify: header pointer + const List_t ** dlist // result: list pointer +) +{ + DASSERT(kmp); + DASSERT( sect < KMP_N_SECT ); + DASSERT(dlist); + DASSERT(hd); + DASSERT(kmp->raw_data); + DASSERT(kmp->raw_head_size); + + const List_t *dl = kmp->dlist + sect; + *dlist = dl; + const uint n = dl->used; + + kmp_list_head_t *h = *hd; + *hd = (kmp_list_head_t*)( (u8*)(h+1) + kmp_entry_size[sect] * n ); + kmp_file_mkw_t *kfile = (kmp_file_mkw_t*)kmp->raw_data; + write_be32( kfile->sect_off + sect, (u8*)h - kmp->raw_data - kmp->raw_head_size ); + + memcpy(h->magic,kmp_section_name[sect].name1,sizeof(h->magic)); + write_be16(&h->n_entry,n); + write_be16(&h->value,kmp->value[sect]); + + return h+1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void create_kmp_pt +( + const kmp_t * kmp, // input: KMP + uint sect, // input: section number + kmp_list_head_t ** hd // modify: header pointer +) +{ + DASSERT(kmp); + DASSERT(hd); + DASSERT( sect == KMP_ENPT || sect == KMP_ITPT ); + + //--- section setup + + const kmp_ph_t *ph; + const kmp_flag_t *kf; + if ( sect == KMP_ENPT ) + { + ph = &kmp->enph; + kf = &kmp->enpt_flag; + } + else + { + ph = &kmp->itph; + kf = &kmp->itpt_flag; + } + + //--- prepare flag export + + const uint max_export = sizeof(kf->flags) / sizeof(*kf->flags); + PRINT(">> EXPORT_FLAGS[%s] = %d [%d], max=%d\n", + kmp_section_name[sect].name1,kf->export,opt_export_flags,max_export); + + + //--- create binary data + + const List_t *dlist; + kmp_enpt_entry_t * pt = setup_raw_kmp(kmp,sect,hd,&dlist); + uint i; + for ( i = 0; i < dlist->used; i++, pt++ ) + { + DASSERT( (u8*)pt < (u8*)*hd ); + const kmp_enpt_entry_t * src = (kmp_enpt_entry_t*)dlist->list + i; + + write_bef4n(pt->position,src->position,4); + if (kf->export) + { + u8 *ptr = (u8*)&pt->scale; + write_bef4(ptr,RoundF3bytes(bef4(ptr))); + ptr[3] = ( i < max_export ? kf->flags[i] : kf->mask ) + << KMP_FE_FLAGS_SHIFT | KMP_FE_ALWAYS_1; + } + + write_be16(pt->prop+0,src->prop[0]&ph->mask[0]); + write_be16(pt->prop+1,src->prop[1]&ph->mask[1]); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void create_kmp_ph +( + const kmp_t * kmp, // input: KMP + uint sect, // input: section number + kmp_list_head_t ** hd, // modify: header pointer + kmp_ph_t * ph // related PH info +) +{ + DASSERT(kmp); + DASSERT(hd); + DASSERT( sect == KMP_CKPH || sect == KMP_ENPH || sect == KMP_ITPH ); + + const List_t *dlist; + kmp_enph_entry_t * phe = setup_raw_kmp(kmp,sect,hd,&dlist); + uint i; + for ( i = 0; i < dlist->used; i++, phe++ ) + { + DASSERT( (u8*)phe < (u8*)*hd ); + const kmp_enph_entry_t * src = (kmp_enph_entry_t*)dlist->list + i; + memcpy(phe,src,sizeof(*phe)); + if ( i < KMP_MAX_GROUP && ph ) + { + kmp_gopt2_t *go2 = ph->gopt + i; + phe->setting[0] = go2->setting[0]; + phe->setting[1] = go2->setting[1]; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void create_kmp_pt2 +( + const kmp_t * kmp, // input: KMP + uint sect, // input: section number + kmp_list_head_t ** hd // modify: header pointer +) +{ + DASSERT(kmp); + DASSERT(hd); + + const List_t *dlist; + kmp_jgpt_entry_t * pt = setup_raw_kmp(kmp,sect,hd,&dlist); + const bool flag_export = sect == KMP_JGPT && kmp->jgpt_flag.export; + const uint max_export = sizeof(kmp->jgpt_flag.flags) / sizeof(*kmp->jgpt_flag.flags); + uint i; + + for ( i = 0; i < dlist->used; i++, pt++ ) + { + DASSERT( (u8*)pt < (u8*)*hd ); + const kmp_jgpt_entry_t * src = (kmp_jgpt_entry_t*)dlist->list + i; + + write_bef4n(pt->position,src->position,6); + if ( flag_export ) + { + u8 *ptr = (u8*)&pt->rotation; + write_bef4(ptr,RoundF3bytes(bef4(ptr))); + ptr[3] = ( i < max_export ? kmp->jgpt_flag.flags[i] : kmp->jgpt_flag.mask ) + << KMP_FE_FLAGS_SHIFT | KMP_FE_ALWAYS_1; + } + + write_be16n((u16*)&pt->id,(u16*)&src->id,2); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawKMP +( + kmp_t * kmp // pointer to valid KMP +) +{ + DASSERT(kmp); + TRACE("CreateRawKMP()\n"); + + + //--- calculate data size + + const uint info_size = kmp->info ? ALIGN32(strlen(kmp->info)+1,4) : 0; + + const u8 *wim0_data = 0; + uint n_sect, head_size, wim0_data_size = 0, wim0_size; + noPRINT(">>>> wim0_export= %d %d\n",opt_wim0,kmp->wim0_export); + if ( kmp->wim0_export && CreateWim0(&wim0_data,&wim0_data_size,kmp) ) + { + n_sect = KMP_N_SECT + 1; + head_size = sizeof(kmp_file_wim0_t); + wim0_size = ALIGN32(wim0_data_size,4); + } + else + { + n_sect = KMP_N_SECT; + head_size = sizeof(kmp_file_mkw_t); + wim0_data_size = 0; + wim0_size = 0; + } + + uint data_size = head_size + + sizeof(kmp_list_head_t) * n_sect + + sizeof(kmp_poti_point_t) * kmp->poti_point.used + + info_size + + wim0_size; + + uint sect; + for ( sect = 0; sect < KMP_N_SECT; sect++ ) + data_size += kmp_entry_size[sect] * kmp->dlist[sect].used; + + PRINT("CREATE KMP: size = %#x = %u\n",data_size,data_size); + KMP_ACTION_LOG(false,"CreateRawKMP() size=%u\n",data_size); + + + //--- alloc data + + FREE(kmp->raw_data); + kmp_file_mkw_t * kfile = CALLOC(1,data_size); + kmp->raw_data = (u8*)kfile; + kmp->raw_data_size = data_size; + kmp->raw_head_size = head_size; + + + //--- setup file header + + memcpy(kfile->magic,KMP_MAGIC,sizeof(kfile->magic)); + write_be32(&kfile->file_size,data_size); + write_be16(&kfile->n_sect,n_sect); + write_be16(&kfile->head_size,head_size); + write_be32(&kfile->version,kmp->kmp_version); + + kmp_list_head_t * hd = (kmp_list_head_t*)(kmp->raw_data+head_size); + + if (info_size) + strcpy( (char*)kfile + data_size - info_size, kmp->info ); + + uint i; + const List_t *dlist; + + + //--- save KTPT entries + + kmp_ktpt_entry_t * ktpt = setup_raw_kmp(kmp,KMP_KTPT,&hd,&dlist); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + for ( i = 0; i < dlist->used; i++, ktpt++ ) + { + DASSERT( (u8*)ktpt < (u8*)hd ); + const kmp_ktpt_entry_t * src = (kmp_ktpt_entry_t*)dlist->list + i; + write_bef4n(ktpt->position,src->position,6); + write_be16n((u16*)&ktpt->player_index,(u16*)&src->player_index,2); + } + + + //--- save ENPT + ENPH entries + + create_kmp_pt(kmp,KMP_ENPT,&hd); + create_kmp_ph(kmp,KMP_ENPH,&hd,&kmp->enph); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + + + //--- save ITPT + ITPH entries + + create_kmp_pt(kmp,KMP_ITPT,&hd); + create_kmp_ph(kmp,KMP_ITPH,&hd,&kmp->itph); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + + + //--- save CKPT entries + + kmp_ckpt_entry_t * ckpt = setup_raw_kmp(kmp,KMP_CKPT,&hd,&dlist); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + for ( i = 0; i < dlist->used; i++, ckpt++ ) + { + DASSERT( (u8*)ckpt < (u8*)hd ); + const kmp_ckpt_entry_t * src = (kmp_ckpt_entry_t*)dlist->list + i; + memcpy(ckpt,src,sizeof(*ckpt)); + write_bef4n(ckpt->left,src->left,4); + } + + + //--- save CKPH entries + + create_kmp_ph(kmp,KMP_CKPH,&hd,&kmp->enph); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + + + //--- save GOBJ entries + + kmp_gobj_entry_t * gobj = setup_raw_kmp(kmp,KMP_GOBJ,&hd,&dlist); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + for ( i = 0; i < dlist->used; i++, gobj++ ) + { + DASSERT( (u8*)gobj < (u8*)hd ); + const kmp_gobj_entry_t * src = (kmp_gobj_entry_t*)dlist->list + i; + write_be16n((u16*)&gobj->obj_id,(u16*)&src->obj_id,2); + write_bef4n(gobj->position,src->position,9); + write_be16n(&gobj->route_id,&src->route_id,10); + } + + + //--- save POTI entries + + kmp_list_head_t * hd2 = hd; + u8 * poti = setup_raw_kmp(kmp,KMP_POTI,&hd,&dlist); + uint ng = dlist->used; + uint np = kmp->poti_point.used; + write_be16(&hd2->n_entry,ng); + write_be16(&hd2->value,np); + + hd = (kmp_list_head_t*)( (u8*)hd + sizeof(kmp_poti_point_t) * np ); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + + const kmp_poti_group_t * pg = (kmp_poti_group_t*)dlist->list; + const kmp_poti_point_t * pp = (kmp_poti_point_t*)kmp->poti_point.list; + + uint gi; + for ( gi = 0; gi < ng && np > 0; gi++, pg++ ) + { + DASSERT( (u8*)poti < (u8*)hd ); + write_be16((u16*)poti,pg->n_point); + poti[2] = pg->smooth; + poti[3] = pg->back; + + poti += sizeof(*pg); + + uint en = pg->n_point; + DASSERT( en <= np ); + for ( ; en > 0; en--, i++, pp++ ) + { + noPRINT("%p/%p %p/%p\n", + poti, hd, pp, dlist->list + dlist->used * dlist->size ); + + DASSERT( (u8*)poti < (u8*)hd ); + write_bef4n((float32*)poti,pp->position,3); + poti += sizeof(pp->position); + + DASSERT( (u8*)poti < (u8*)hd ); + write_be16n((u16*)poti,&pp->speed,2); + poti += 2*sizeof(pp->speed); + } + } + + + //--- save AREA entries + + kmp_area_entry_t * area = setup_raw_kmp(kmp,KMP_AREA,&hd,&dlist); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + for ( i = 0; i < dlist->used; i++, area++ ) + { + DASSERT( (u8*)area < (u8*)hd ); + const kmp_area_entry_t * src = (kmp_area_entry_t*)dlist->list + i; + memcpy(area,src,sizeof(*area)); + write_bef4n(area->position,src->position,9); + write_be16n(area->setting,src->setting,4); + } + + + //--- save CAME entries + + hd2 = hd; + kmp_came_entry_t * came = setup_raw_kmp(kmp,KMP_CAME,&hd,&dlist); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + + for ( i = 0; i < dlist->used; i++, came++ ) + { + DASSERT( (u8*)came < (u8*)hd ); + const kmp_came_entry_t * src = (kmp_came_entry_t*)dlist->list + i; + memcpy(came,src,sizeof(*came)); + write_be16n(&came->came_speed,&src->came_speed,4); + write_bef4n(came->position,src->position,15); + } + + + //--- save JGPT + CNPT + MSPT entries + + create_kmp_pt2(kmp,KMP_JGPT,&hd); + create_kmp_pt2(kmp,KMP_CNPT,&hd); + create_kmp_pt2(kmp,KMP_MSPT,&hd); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + + + //--- save STGI entries + + kmp_stgi_entry_t * stgi = setup_raw_kmp(kmp,KMP_STGI,&hd,&dlist); + DASSERT( (u8*)hd <= kmp->raw_data + data_size ); + for ( i = 0; i < dlist->used; i++, stgi++ ) + { + DASSERT( (u8*)stgi < (u8*)hd ); + const kmp_stgi_entry_t * src = (kmp_stgi_entry_t*)dlist->list + i; + memcpy(stgi,src,sizeof(*stgi)); + write_be32(&stgi->flare_color,src->flare_color); + write_be16(&stgi->speed_mod,src->speed_mod); + } + + + //--- save WIM0 data + +// [[wim0+]] + if ( kmp->wim0_export ) + { + kmp_list_head_t *h = hd; + hd = (kmp_list_head_t*)( (u8*)(h+1) + wim0_size ); + write_be32( kfile->sect_off + KMP_N_SECT, + (u8*)h - kmp->raw_data - kmp->raw_head_size ); + memcpy(h->magic,"WIM0",sizeof(h->magic)); + write_be32(&h->n_entry,wim0_size); + memcpy(h->entry,wim0_data,wim0_data_size); + } + + + //--- terminate + + DASSERT( (u8*)hd + info_size == kmp->raw_data + data_size ); + + if ( KMP_MODE & KMPMD_RM_EMPTY ) + ShrinkKMP(kmp); + + FREE((u8*)wim0_data); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawKMP +( + kmp_t * kmp, // pointer to valid KMP + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(kmp); + DASSERT(fname); + PRINT("SaveRawKMP(%s,%d)\n",fname,set_time); + KMP_ACTION_LOG(false,"SaveRawKMP() %s\n",fname); + + //--- create raw data + + enumError err = CreateRawKMP(kmp); + if (err) + return err; + DASSERT(kmp->raw_data); + DASSERT(kmp->raw_data_size); + DASSERT(kmp->raw_head_size); + + //--- write to file + + File_t F; + err = CreateFileOpt(&F,true,fname,testmode,fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&kmp->fatt,0); + + if ( fwrite(kmp->raw_data,1,kmp->raw_data_size,F.f) != kmp->raw_data_size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Sort GOBJ /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct sort_gobj_t +{ + int rank; // sort 1: rank of object + int angle; // sort 2: h-angle around (0,0) in degree*1000000 + int pos; // sort 3: old pos of object + kmp_gobj_entry_t data; // raw object data + +} sort_gobj_t; + +//----------------------------------------------------------------------------- + +static int sort_gobj ( const sort_gobj_t * a, const sort_gobj_t * b ) +{ + int stat = a->rank - b->rank; + if (!stat) + { + stat = a->angle - b->angle; + if (!stat) + stat = a->pos - b->pos; + } + return stat; +} + +//----------------------------------------------------------------------------- + +static int sort_gobj_tiny ( const sort_gobj_t * a, const sort_gobj_t * b ) +{ + int stat = memcmp( a->data.setting, + b->data.setting, + sizeof(kmp_gobj_entry_t) - offsetof(kmp_gobj_entry_t,setting) ); + + if (!stat) + { + stat = memcmp( a->data.rotation, + b->data.rotation, + sizeof(kmp_gobj_entry_t) - offsetof(kmp_gobj_entry_t,rotation) ); + if (!stat) + { + stat = a->rank - b->rank; + if (!stat) + stat = a->pos - b->pos; + } + } + return stat; +} + +//----------------------------------------------------------------------------- + +bool SortGOBJ +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // <=0: dont sort + // 1: group only + // 2: secondary sort by angle + // 3: sort for TINY +) +{ + DASSERT(kmp); + PRINT("SortGOBJ(%d)\n",sort_mode); + + List_t *dlist = kmp->dlist + KMP_GOBJ; + if ( sort_mode < 1 || dlist->used < 2 ) + return false; + + + //--- build a group rank table + + int rank[N_KMP_GOBJ]; + memset(rank,0,sizeof(rank)); + + kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)dlist->list; + int i, rank_count = 0; + for ( i = dlist->used - 1; i >= 0; i-- ) + { + const typeof(gobj->obj_id) obj_id = gobj[i].obj_id; + if ( obj_id < N_KMP_GOBJ ) + rank[obj_id] = --rank_count; + } + rank[0] = 0; + + + //--- create sort array and copy data + + sort_gobj_t *sg_beg = CALLOC(dlist->used,sizeof(*sg_beg)); + sort_gobj_t *sg_end = sg_beg + dlist->used; + sort_gobj_t *sg; + for ( sg = sg_beg; sg < sg_end; sg++, gobj++ ) + { + const typeof(gobj->obj_id) obj_id = gobj->obj_id; + sg->pos = sg - sg_beg; + sg->rank = obj_id < N_KMP_GOBJ ? rank[obj_id] : obj_id; + if ( sort_mode == KMP_SORT_ANGLE ) + { + sg->angle = atan2(gobj->position[0],gobj->position[2]) * (180000000/M_PI); + if ( sg->angle < 0) + sg->angle += 360000000; + } + memcpy(&sg->data,gobj,sizeof(sg->data)); + } + + //--- sort data + + if ( sort_mode == KMP_SORT_TINY ) + qsort( sg_beg, dlist->used, sizeof(*sg_beg), (qsort_func)sort_gobj_tiny ); + else + qsort( sg_beg, dlist->used, sizeof(*sg_beg), (qsort_func)sort_gobj ); + + + //--- store sorted data + + gobj = (kmp_gobj_entry_t*)dlist->list; + for ( sg = sg_beg; sg < sg_end; sg++, gobj++ ) + { + PRINT("SORT: %5d %10d %5d\n",sg->rank,sg->angle,sg->pos); + memcpy(gobj,&sg->data,sizeof(*gobj)); + } + + //--- terminate + + FREE(sg_beg); + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Startpoint Calculations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct kmp_start_pos_t +{ + double x; // relative x position, range -800.0 .. +800.0 + double z; // relative z position, <0.0 if unknown + double z_narrow; // narrow z position, <0.0 if unknown +} +kmp_start_pos_t; + +static const kmp_start_pos_t StartPosTab[] = +{ + { 0.0, 0.00, 0.00 }, // 1/1 + + { -400.0, 0.00, 0.00 }, // 1/2 + { 400.0, 461.88, 373.06 }, // 2/2 + + { -800.0, 0.00, 0.00 }, // 1/3 + { 0.0, 461.88, 373.06 }, // 2/3 + { 800.0, 923.75, 746.12 }, // 3/3 + + { -800.0, 0.00, 0.00 }, // 1/4 + { 400.0, 692.81, 559.56 }, // 2/4 + { -400.0, 1530.94, 1386.56 }, // 3/4 + { 800.0, 2223.75, 1946.12 }, // 4/4 + + { -800.0, 0.00, 0.00 }, // 1/5 + { 0.0, 461.88, 373.06 }, // 2/5 + { 800.0, 923.75, 746.12 }, // 3/5 + { -400.0, 1530.94, 1386.56 }, // 4/5 unsure + { 400.0, 1992.81, 1759.62 }, // 5/5 unsure + + { -800.0, 0.00, 0.00 }, // 1/6 + { -160.0, 369.50, 298.44 }, // 2/6 + { 480.0, 739.00, 596.88 }, // 3/6 + { -480.0, 1484.75, 1349.19 }, // 4/6 + { 160.0, 1854.25, 1647.62 }, // 5/6 + { 800.0, 2223.75, 1946.06 }, // 6/6 + + { -400.0, 0.00, 0.00 }, // 1/7 ??? narrow guessed (compelte series) + { 400.0, 461.88, 373.06 }, // 2/7 + { -800.0, 1069.06, 1013.50 }, // 3/7 unsure + { 0.0, 1530.94, 1386.56 }, // 4/7 unsure + { 800.0, 1992.81, 1759.62 }, // 5/7 unsure + { -400.0, 2900.00, 2650.00 }, // 6/7 + { 400.0, 3361.88, 3023.06 }, // 7/7 + + { -800.0, 0.00, 0.00 }, // 1/8 + { 0.0, 461.88, 373.06 }, // 2/8 + { 800.0, 923.75, 746.12 }, // 3/8 + { -400.0, 1530.94, 1386.56 }, // 4/8 unsure + { 400.0, 1992.81, 1759.62 }, // 5/8 unsure + { -800.0, 2900.00, 2650.00 }, // 6/8 unsure + { 0.0, 3361.88, 3023.06 }, // 7/8 + { 800.0, 3823.75, 3396.12 }, // 8/8 + + { -800.0, 0.00, 0.00 }, // 1/9 + { -160.0, 369.50, 298.44 }, // 2/9 + { 480.0, 739.00, 596.88 }, // 3/9 + { -480.0, 1484.75, 1349.19 }, // 4/9 + { 160.0, 1854.25, 1647.62 }, // 5/9 + { 800.0, 2223.75, 1946.07 }, // 6/9 + { -800.0, 2900.00, 2650.00 }, // 7/9 + { -160.0, 3269.50, 2948.44 }, // 8/9 + { 480.0, 3639.00, 3246.88 }, // 9/9 + + { -800.0, 0.00, 0.00 }, // 1/10 + { 0.0, 461.88, 373.06 }, // 2/10 + { 800.0, 923.75, 746.13 }, // 3/10 + { -400.0, 1530.94, 1386.56 }, // 4/10 + { 400.0, 1992.82, 1759.56 }, // 5/10 + { -800.0, 2900.00, 2650.00 }, // 6/10 + { 0.0, 3361.88, 3023.06 }, // 7/10 + { 800.0, 3823.75, 3396.12 }, // 8/10 + { -400.0, 4430.94, 4036.56 }, // 9/10 + { 400.0, 4892.82, 4409.56 }, // 10/10 + + { -800.0, 0.00, 0.00}, // 1/11 + { -160.0, 369.50, 298.44 }, // 2/11 + { 480.0, 739.00, 596.88 }, // 3/11 + { -480.0, 1484.75, 1349.19 }, // 4/11 + { 160.0, 1854.25, 1647.62 }, // 5/11 + { 800.0, 2223.75, 1946.07 }, // 6/11 + { -800.0, 2900.00, 2650.00 }, // 7/11 + { -160.0, 3269.50, 2948.44 }, // 8/11 + { 480.0, 3639.00, 3246.88 }, // 9/11 + { -480.0, 4384.75, 3999.21 }, // 10/11 + { 160.0, 4754.25, 4297.62 }, // 11/11 + + { -800.0, 0.00, 0.00}, // 1/12 + { -160.0, 369.50, 298.44 }, // 2/12 + { 480.0, 739.00, 596.88 }, // 3/12 + { -480.0, 1484.75, 1349.19 }, // 4/12 + { 160.0, 1854.25, 1647.62 }, // 5/12 + { 800.0, 2223.75, 1946.07 }, // 6/12 + { -800.0, 2900.00, 2650.00 }, // 7/12 + { -160.0, 3269.50, 2948.44 }, // 8/12 + { 480.0, 3639.00, 3246.88 }, // 9/12 + { -480.0, 4384.75, 3999.21 }, // 10/12 + { 160.0, 4754.25, 4297.62 }, // 11/12 + { 800.0, 5123.75, 4596.06 }, // 12/12 +}; + +//----------------------------------------------------------------------------- + +uint kmp_start_pos_mirror = false; +uint kmp_start_pos_narrow = false; +bool kmp_start_pos_auto = false; + +const double3 * GetStartPosKMP +( + // Return NULL on failure. + // Otherwise return a pointer to 'tab' or to an internal static buffer + // with N elements. This buffer is overwritten on next call. + + double3 *tab, // table with 'n' elements to store the result. + // if NULL: use internal static buffer + uint n, // number of players: 1..12 + bool mirror, // true: mirror the data (pole is right) + bool narrow, // true: use 'narrow' mode + + kmp_t *kmp, // not NULL: Use KTPT/#0 and STGI/#0 + uint automode // bit field: Use KMP to override parameters + // 1: return absolute positions, if KMP available + // 2: use pole position of KMP if available + // 4: use narrow mode of KMP if available +) +{ + //--- valid 'n'? + + if ( n < 1 || n > 12 ) + return 0; + + + //--- auto mode + + kmp_start_pos_auto = 0; + kmp_start_pos_mirror = mirror; + kmp_start_pos_narrow = narrow; + + if ( kmp && kmp->dlist[KMP_STGI].used ) + { + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp->dlist[KMP_STGI].list; + if ( automode & 2 ) + { + kmp_start_pos_auto |= 2; + kmp_start_pos_mirror = stgi->pole_pos; + mirror = kmp_start_pos_mirror == 1; + } + if ( automode & 4 ) + { + kmp_start_pos_auto |= 4; + kmp_start_pos_narrow = stgi->narrow_start; + narrow = kmp_start_pos_narrow == 1; + } + } + + + //--- assign table values + + static double3 itab[12]; + if (!tab) + tab = itab; + double3 *d; + const kmp_start_pos_t *sp = StartPosTab + n * ( n-1 ) / 2; + + uint i; + for ( i = 0, d = tab; i < n; i++, d++, sp++ ) + { + d->x = mirror ? -sp->x : sp->x; + d->y = 0.0; + d->z = narrow ? sp->z_narrow : sp->z; + if (IsNormalD(d->z)) + d->z = ldexp(trunc(ldexp(d->z,4)+0.5),-4); + } + + + //--- valid KMP/KTPT? + + if ( !(automode&1) || !kmp || !kmp->dlist[KMP_KTPT].used ) + return tab; + kmp_start_pos_auto |= 1; + + + //--- transformation step 1: y-rotate by 180 degree to get normalized vectors + + MatrixD_t matrix = {0}; + InitializeMatrixD(&matrix); + SetRotateMatrixD(&matrix,1,180.0,0.0,0); + TransformD3NMatrixD(&matrix,tab,n,sizeof(*tab)); + + + //--- transformation step 2: rotate and translate by start point settings + + const kmp_ktpt_entry_t * ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list; + SetRotateMatrixD(&matrix,0,ktpt->rotation[0],0.0,0); + SetRotateMatrixD(&matrix,1,ktpt->rotation[1],0.0,0); + SetRotateMatrixD(&matrix,2,ktpt->rotation[2],0.0,0); + double3 temp; + temp.x = ktpt->position[0]; + temp.y = ktpt->position[1]; + temp.z = ktpt->position[2]; + SetTranslateMatrixD(&matrix,&temp); + TransformD3NMatrixD(&matrix,tab,n,sizeof(*tab)); + + return tab; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transform KMP /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool TransformKMP +( + kmp_t * kmp // pointer to valid KMP +) +{ + if ( have_patch_count <= 0 || !transform_active || disable_transformation > 0 ) + return false; + + KMP_ACTION_LOG(true,"Transform KMP\n"); + PRINT("** TransformKMP() **\n"); + int n; + + //--- AREA + + n = kmp->dlist[KMP_AREA].used; + if (n) + { + kmp_area_entry_t * area = (kmp_area_entry_t*)kmp->dlist[KMP_AREA].list; + if ( KMP_TFORM & KMPTF_AREA_POS ) + TransformPosFloat3D(area->position,n,sizeof(*area)); + if ( KMP_TFORM & KMPTF_AREA_ROTATE ) + TransformRotFloat3D(area->rotation,n,sizeof(*area)); + if ( KMP_TFORM & KMPTF_AREA_SCALE ) + TransformScaleFloat3D(area->scale,n,sizeof(*area)); + } + + //--- CAME + + u8 poti_notrans[0x100] = {0}; + + n = kmp->dlist[KMP_CAME].used; + if ( n ) + { + kmp_came_entry_t * came = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list; + while ( n-- > 0 ) + { + if ( came->type == 6 ) + poti_notrans[came->route] = 1; + else if ( KMP_TFORM & KMPTF_CAME_POS && came->type != 0 && came->type != 3 ) + { + TransformPosFloat3D(came->position,1,sizeof(*came)); + TransformPosFloat3D(came->viewpt_begin,1,sizeof(*came)); + TransformPosFloat3D(came->viewpt_end,1,sizeof(*came)); + } + came++; + } + } + + //--- CKPT + + n = kmp->dlist[KMP_CKPT].used; + if ( n && KMP_TFORM & KMPTF_CKPT_POS ) + { + kmp_ckpt_entry_t * ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + TransformPosFloat2D(ckpt->left,n,sizeof(*ckpt)); + TransformPosFloat2D(ckpt->right,n,sizeof(*ckpt)); + } + + //--- CNPT + + n = kmp->dlist[KMP_CNPT].used; + if (n) + { + kmp_cnpt_entry_t * cnpt = (kmp_cnpt_entry_t*)kmp->dlist[KMP_CNPT].list; + if ( KMP_TFORM & KMPTF_CNPT_POS ) + TransformPosFloat3D(cnpt->position,n,sizeof(*cnpt)); + if ( KMP_TFORM & KMPTF_CNPT_ROTATE ) + TransformRotFloat3D(cnpt->rotation,n,sizeof(*cnpt)); + } + + //--- ENPT + + n = kmp->dlist[KMP_ENPT].used; + if (n) + { + kmp_enpt_entry_t * enpt = (kmp_enpt_entry_t*)kmp->dlist[KMP_ENPT].list; + if ( KMP_TFORM & KMPTF_ENPT_POS ) + TransformPosFloat3D(enpt->position,n,sizeof(*enpt)); + if ( KMP_TFORM & KMPTF_ENPT_SCALE ) + TransformScaleFloat1D(&enpt->scale,n,sizeof(*enpt)); + } + + //--- GOBJ + + n = kmp->dlist[KMP_GOBJ].used; + if (n) + { + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + if ( KMP_TFORM & KMPTF_GOBJ_POS ) + TransformPosFloat3D(gobj->position,n,sizeof(*gobj)); + if ( KMP_TFORM & KMPTF_GOBJ_ROTATE ) + TransformRotFloat3D(gobj->rotation,n,sizeof(*gobj)); + if ( KMP_TFORM & KMPTF_GOBJ_SCALE ) + TransformScaleFloat3D(gobj->scale,n,sizeof(*gobj)); + } + + //--- ITPT + + n = kmp->dlist[KMP_ITPT].used; + if (n) + { + kmp_itpt_entry_t * itpt = (kmp_itpt_entry_t*)kmp->dlist[KMP_ITPT].list; + if ( KMP_TFORM & KMPTF_ITPT_POS ) + TransformPosFloat3D(itpt->position,n,sizeof(*itpt)); + if ( KMP_TFORM & KMPTF_ITPT_SCALE ) + TransformScaleFloat1D(&itpt->scale,n,sizeof(*itpt)); + } + + //--- JGPT + + n = kmp->dlist[KMP_JGPT].used; + if (n) + { + kmp_jgpt_entry_t * jgpt = (kmp_jgpt_entry_t*)kmp->dlist[KMP_JGPT].list; + if ( KMP_TFORM & KMPTF_JGPT_POS ) + TransformPosFloat3D(jgpt->position,n,sizeof(*jgpt)); + if ( KMP_TFORM & KMPTF_JGPT_ROTATE ) + TransformRotFloat3D(jgpt->rotation,n,sizeof(*jgpt)); + } + + //--- KTPT + + n = kmp->dlist[KMP_KTPT].used; + if (n) + { + kmp_ktpt_entry_t * ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list; + if ( KMP_TFORM & KMPTF_KTPT_POS ) + TransformPosFloat3D(ktpt->position,n,sizeof(*ktpt)); + if ( KMP_TFORM & KMPTF_KTPT_ROTATE ) + TransformRotFloat3D(ktpt->rotation,n,sizeof(*ktpt)); + } + + //--- MSPT + + n = kmp->dlist[KMP_MSPT].used; + if (n) + { + kmp_mspt_entry_t * mspt = (kmp_mspt_entry_t*)kmp->dlist[KMP_MSPT].list; + if ( KMP_TFORM & KMPTF_MSPT_POS ) + TransformPosFloat3D(mspt->position,n,sizeof(*mspt)); + if ( KMP_TFORM & KMPTF_MSPT_ROTATE ) + TransformRotFloat3D(mspt->rotation,n,sizeof(*mspt)); + } + + //--- POTI + +#if 1 + if ( KMP_TFORM & KMPTF_POTI_POS ) + { + const uint ng = kmp->dlist[KMP_POTI].used; + const kmp_poti_group_t *pg = (kmp_poti_group_t*)kmp->dlist[KMP_POTI].list; +// const uint np = kmp->poti_point.used; + kmp_poti_point_t *pp = (kmp_poti_point_t*)kmp->poti_point.list; + + uint gi; + for ( gi = 0; gi < ng; gi++, pg++ ) + { + if ( gi >= sizeof(poti_notrans) || !poti_notrans[gi] ) + TransformPosFloat3D(pp->position,pg->n_point,sizeof(*pp)); + + pp += pg->n_point; + } + } +#else + n = kmp->poti_point.used; + if ( n && KMP_TFORM & KMPTF_POTI_POS ) + { + kmp_poti_point_t * pp = (kmp_poti_point_t*)kmp->poti_point.list; + TransformPosFloat3D(pp->position,n,sizeof(*pp)); + } +#endif + + //--- STGI + // nothing to do + + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Patch KMP helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 rm_kmp_gobj[N_KMP_GOBJ] = {0}; +static bool have_rm_kmp_gobj = false; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRmGobj ( ccp arg ) +{ + if (have_rm_kmp_gobj) + have_patch_count--, have_kmp_patch_count--; + have_rm_kmp_gobj = false; + memset(rm_kmp_gobj,0,sizeof(rm_kmp_gobj)); + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --rm-gobj",0); + si.predef = SetupVarsKMP(); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + sf->disable_comma++; + for(;;) + { + while ( *sf->ptr > 0 && *sf->ptr <= ' ' || *sf->ptr == ',' ) + sf->ptr++; + if (!*sf->ptr) + break; + + DEFINE_VAR(val); + if (ScanExprSI(&si,&val)) + goto abort; + int idx1 = GetIntV(&val); + int idx2 = idx1; + if ( NextCharSI(&si,false) == ':' ) + { + sf->ptr++; + if (ScanExprSI(&si,&val)) + goto abort; + idx2 = GetIntV(&val); + } + + if ( idx1 < 0 ) + idx1 = 0; + if ( idx2 > N_KMP_GOBJ - 1 ) + idx2 = N_KMP_GOBJ - 1; + + while ( idx1 <= idx2 ) + { + rm_kmp_gobj[idx1++] = 1; + have_rm_kmp_gobj = true; + } + } + + if (have_rm_kmp_gobj) + have_patch_count++, have_kmp_patch_count++; + + #if defined(DEBUG) && defined(TEST) + { + PRINT("## have_rm_kmp_gobj = %d [%d,%d]\n", + have_rm_kmp_gobj, have_patch_count, have_kmp_patch_count ); + uint i; + for ( i = 0; i < N_KMP_GOBJ; i++ ) + if (rm_kmp_gobj[i]) + PRINT("%6u,0x%02x := 0x%02x\n", i, i, rm_kmp_gobj[i] ); + } + #endif + + return 0; + + abort: + memset(rm_kmp_gobj,0,sizeof(rm_kmp_gobj)); + return 1; +}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptBattle ( ccp arg ) +{ + const int stat = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_FORCE,"Option --battle"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_battle_mode = stat; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptExportFlags ( ccp arg ) +{ + const int stat = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_FORCE,"Option --export-flags"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_export_flags = stat; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRouteOptions ( ccp arg ) +{ + const int stat = ScanKeywordOffAutoOn(arg,OFFON_ON,12,"option --route-options"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_route_options = stat; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptWim0 ( ccp arg ) +{ + const int stat = ScanKeywordOffAutoOn(arg,OFFON_ON,12,"option --wim0"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_wim0 = stat; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool FixPH +( + kmp_t * kmp, // KMP data structure + uint sect_ph, // KMP PH section index + uint sect_pt, // related PT section index + bool allow_dup // true: allow multiple links into same section +) +{ + DASSERT(kmp); + DASSERT( sect_pt < KMP_N_SECT ); + DASSERT( sect_ph < KMP_N_SECT ); + + //--- setup + + PRINT("FixPH(%s,%s,%d)\n", + kmp_section_name[sect_ph].name1, + kmp_section_name[sect_pt].name1, allow_dup ); + + List_t *ph_list = kmp->dlist + sect_ph; + List_t *pt_list = kmp->dlist + sect_pt; + const uint max_pt = pt_list->used; + + + //--- save in-data + + const uint saved_n = ph_list->used; + const uint saved_size = saved_n * ph_list->elem_size; + u8 *saved_data = MALLOC(saved_size); + memcpy(saved_data,ph_list->list,saved_size); + + + //--- first loop: find valid groups + + u8 linkref[0x100]; + memset(linkref,0xff,sizeof(linkref)); + + uint pi, idx = 0; + kmp_enph_entry_t *ph, *dest, *ph0 = (kmp_enph_entry_t*)ph_list->list; + for ( pi = 0, ph = dest = ph0; pi < ph_list->used; pi++, ph++ ) + { + uint end = ph->pt_start + ph->pt_len; + if ( end > max_pt || pi == ph_list->used-1 ) + end = max_pt; + if ( end > idx ) + { + dest->pt_start = idx; + dest->pt_len = end - idx; + idx = end; + memset(dest->prev,0xff,sizeof(dest->prev)); + memcpy(dest->next,ph->next,sizeof(dest->next)); + linkref[pi] = dest - ph0; + dest++; + } + } + ph_list->used = dest - ph0; + + + //--- second loop: analyse next links + + for ( pi = 0, ph = ph0; pi < ph_list->used; pi++, ph++ ) + { + u8 new_next[KMP_MAX_PH_LINK]; + memset(new_next,0xff,sizeof(new_next)); + + uint il; + for ( il = 0; il < KMP_MAX_PH_LINK; il++ ) + { + const u8 link = linkref[ph->next[il]&0xff]; + if ( link < ph_list->used ) + { + InsertLinkPH(new_next,link,0,0,allow_dup); + InsertLinkPH(ph0[link].prev,pi,0,0,false); + } + } + + if ( new_next[0] == 0xff ) + { + // link to self + InsertLinkPH(new_next,pi,0,0,allow_dup); + } + + memcpy(ph->next,new_next,sizeof(ph->next)); + } + + + //--- third loop: find empty 'prev' lists + + for ( pi = 0, ph = ph0; pi < ph_list->used; pi++, ph++ ) + { + if ( ph->prev[0] == 0xff ) + { + // link to self + InsertLinkPH(ph->prev,pi,0,0,false); + } + } + + + //--- calc dirty + + const bool dirty = saved_n != ph_list->used + || memcmp(saved_data,ph_list->list,saved_size); + FREE(saved_data); + + if (dirty) + KMP_ACTION_LOG(true,"Section %s fixed.\n",kmp_section_name[sect_ph].name1); + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +static kmp_gobj_entry_t * AppendGOBJ ( kmp_t * kmp, u16 obj_id ) +{ + kmp_gobj_entry_t *go = AppendList(kmp->dlist + KMP_GOBJ); + if (go) + { + go->obj_id = obj_id; + go->route_id = M1(go->route_id); + go->pflags = 0x3f; + go->scale[0] = + go->scale[1] = + go->scale[2] = 1.0; + } + return go; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Patch KMP /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool ReversePT +( + const kmp_t * kmp, // KMP data structure + uint sect // KMP PT section index +) +{ + DASSERT(kmp); + DASSERT( sect == KMP_ENPT || sect == KMP_ITPT ); + + const List_t *ptlist, *phlist; + if ( sect == KMP_ENPT ) + { + ptlist = kmp->dlist + KMP_ENPT; + phlist = kmp->dlist + KMP_ENPH; + } + else + { + DASSERT( sect == KMP_ITPT ); + ptlist = kmp->dlist + KMP_ITPT; + phlist = kmp->dlist + KMP_ITPH; + } + + //--- reverse points + + const uint np = ptlist->used; + if ( np < 2 ) + return false; + + kmp_enpt_entry_t *p1 = (kmp_enpt_entry_t*)ptlist->list; + kmp_enpt_entry_t *p2 = p1 + np - 1; + while ( p1 < p2 ) + { + kmp_enpt_entry_t temp = *p1; + *p1++ = *p2; + *p2-- = temp; + } + + //--- reverse groups + + const uint ng = phlist->used; + const uint ng1 = ng - 1; + kmp_enph_entry_t *g1 = (kmp_enph_entry_t*)phlist->list; + kmp_enph_entry_t *g2 = g1 + ng1; + while ( g1 < g2 ) + { + kmp_enph_entry_t temp = *g1; + *g1++ = *g2; + *g2-- = temp; + } + + //--- fix groups + + g1 = (kmp_enph_entry_t*)phlist->list; + g2 = g1 + ng; + for ( ; g1 < g2; g1++ ) + { + kmp_enph_entry_t temp = *g1; + + uint i; + for ( i = 0; i < KMP_MAX_PH_LINK; i++ ) + { + g1->prev[i] = temp.next[i] == 0xff ? 0xff : ng1 - temp.next[i]; + g1->next[i] = temp.prev[i] == 0xff ? 0xff : ng1 - temp.prev[i]; + } + + g1->pt_start = np - g1->pt_start - g1->pt_len; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ReversePOTI +( + const kmp_t * kmp // KMP data structure +) +{ + DASSERT(kmp); + + const uint ng = kmp->dlist[KMP_POTI].used; + kmp_poti_group_t *pg = (kmp_poti_group_t*)kmp->dlist[KMP_POTI].list; + const uint np = kmp->poti_point.used; + kmp_poti_point_t *pp = (kmp_poti_point_t*)kmp->poti_point.list; + + uint pi, gi; + for ( gi = pi = 0; gi < ng; gi++ ) + { + uint en = pg[gi].n_point; + if ( en > np - pi ) + en = np - pi; + + kmp_poti_point_t *p1 = pp + pi; + kmp_poti_point_t *p2 = p1 + en - 1; + PRINT("==> POTI %02u: %d..%d /%u\n",gi,(int)(p1-pp),(int)(p2-pp),np); + pi += en; + + while ( p1 < p2 ) + { + kmp_poti_point_t temp = *p1; + *p1++ = *p2; + *p2-- = temp; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ReverseCKPT +( + const kmp_t * kmp // KMP data structure +) +{ + DASSERT(kmp); + // [[2do]] ??? + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ReverseKTPT +( + const kmp_t * kmp // KMP data structure +) +{ + DASSERT(kmp); + // [[2do]] ??? + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ReverseJGPT +( + const kmp_t * kmp // KMP data structure +) +{ + DASSERT(kmp); + // [[2do]] ??? + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ReverseKMP +( + kmp_t * kmp // pointer to valid KMP +) +{ + DASSERT(kmp); + return ReversePT(kmp,KMP_ENPT) + | ReversePT(kmp,KMP_ITPT) + | ReversePOTI(kmp) + | ReverseCKPT(kmp) // [[2do]] + | ReverseKTPT(kmp) // [[2do]] + | ReverseJGPT(kmp); // [[2do]] +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP tiny activities /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum kmp_tiny_mode_t +{ + KTM_OFF, // do nothing + KTM_ROUND_TO_INT, // round to next int + + KTM_ROUND_POS, // standard rounding for positions + KTM_ROUND_ROT, // standard rounding for rotatins + KTM_ROUND_SCALE, // standard rounding for scalings + + KTM_ROUND_ROUTE, // for route positions + KTM_ROUND_CANNON, // for cannon rotation +} +kmp_tiny_mode_t; + +typedef struct kmp_tiny_t +{ + u16 sect; // KMP section index + u16 offset; // offset within KMP section + u16 n; // number of related floats, usually 1 or 3 + u16 mode; // one of kmp_tiny_mode_t +} +kmp_tiny_t; + + +static const kmp_tiny_t kmp_tiny_tab[] = +{ + // sections are steadily increasing!! + + { KMP_KTPT, offsetof(kmp_ktpt_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_KTPT, offsetof(kmp_ktpt_entry_t,rotation), 3, KTM_ROUND_ROT }, + + { KMP_ENPT, offsetof(kmp_enpt_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_ENPT, offsetof(kmp_enpt_entry_t,scale), 3, KTM_ROUND_SCALE }, + + { KMP_ITPT, offsetof(kmp_itpt_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_ITPT, offsetof(kmp_itpt_entry_t,scale), 3, KTM_ROUND_SCALE }, + + { KMP_CKPT, offsetof(kmp_ckpt_entry_t,left), 4, KTM_ROUND_POS }, + + { KMP_GOBJ, offsetof(kmp_gobj_entry_t,position), 3, KTM_ROUND_POS }, + { KMP_GOBJ, offsetof(kmp_gobj_entry_t,rotation), 3, KTM_ROUND_ROT }, + { KMP_GOBJ, offsetof(kmp_gobj_entry_t,scale), 3, KTM_ROUND_SCALE }, + + { KMP_POTI, offsetof(kmp_poti_point_t,position), 3, KTM_ROUND_ROUTE }, + + { KMP_AREA, offsetof(kmp_area_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_AREA, offsetof(kmp_area_entry_t,rotation), 3, KTM_ROUND_ROT }, + { KMP_AREA, offsetof(kmp_area_entry_t,scale), 3, KTM_OFF }, + + { KMP_CAME, offsetof(kmp_came_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_CAME, offsetof(kmp_came_entry_t,rotation), 3, KTM_ROUND_ROT }, + { KMP_CAME, offsetof(kmp_came_entry_t,zoom_begin), 2, KTM_ROUND_ROUTE }, + { KMP_CAME, offsetof(kmp_came_entry_t,viewpt_begin),6, KTM_ROUND_ROUTE }, + { KMP_CAME, offsetof(kmp_came_entry_t,time), 1, KTM_OFF }, + + { KMP_JGPT, offsetof(kmp_jgpt_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_JGPT, offsetof(kmp_jgpt_entry_t,rotation), 3, KTM_ROUND_ROT }, + + { KMP_CNPT, offsetof(kmp_cnpt_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_CNPT, offsetof(kmp_cnpt_entry_t,rotation), 3, KTM_ROUND_CANNON }, + + { KMP_MSPT, offsetof(kmp_mspt_entry_t,position), 3, KTM_ROUND_ROUTE }, + { KMP_MSPT, offsetof(kmp_mspt_entry_t,rotation), 3, KTM_ROUND_ROT }, + + { KMP_N_SECT,0,0,0} // sections are steadily increasing!! +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Patch KMP /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PatchKMP +( + kmp_t * kmp // pointer to valid KMP +) +{ + DASSERT(kmp); + bool dirty = ( KMP_MODE & KMPMD_NEW ) != 0; + if ( have_patch_count <= 0 || have_kmp_patch_count <= 0 && !transform_active ) + return dirty; + + KMP_ACTION_LOG(false,"PatchKMP()\n"); + PRINT("** PatchKMP() **\n"); + + dirty |= TransformKMP(kmp); + + + //--- remove global objects (--rm-gobj, --slot) + + if ( opt_slot & SLOTMD_RM_ICE ) + { + rm_kmp_gobj[GOBJ_ICE] = 1; + have_rm_kmp_gobj = true; + } + if ( opt_slot & SLOTMD_RM_SUNDS ) + { + rm_kmp_gobj[GOBJ_SUN_DS] = 1; + rm_kmp_gobj[GOBJ_PYLON01] = 1; + have_rm_kmp_gobj = true; + } + if ( opt_slot & SLOTMD_RM_SHYGUY ) + { + rm_kmp_gobj[GOBJ_HEYHO_SHIP] = 1; + rm_kmp_gobj[GOBJ_HEYHO_BALL] = 1; + have_rm_kmp_gobj = true; + } + + if (have_rm_kmp_gobj) + { + kmp_gobj_entry_t *glist = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + kmp_gobj_entry_t *src = glist; + kmp_gobj_entry_t *dest = glist; + kmp_gobj_entry_t *end = glist + kmp->dlist[KMP_GOBJ].used; + for ( ; src < end; src++ ) + { + if (!rm_kmp_gobj[src->obj_id]) + { + memcpy(dest,src,sizeof(*dest)); + dest++; + } + else + RemoveIL(kmp->index+KMP_GOBJ,dest-glist); + } + + if ( dest < end ) + { + dirty = true; + kmp->dlist[KMP_GOBJ].used = dest - glist; + } + } + + + //--- SLOTMD_ADD_SHYGUY + + if ( opt_slot & (SLOTMD_ADD_GICE|SLOTMD_ADD_SHYGUY) ) + { + const kmp_gobj_entry_t *glist = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + const kmp_gobj_entry_t *gobj, *gend = glist + kmp->dlist[KMP_GOBJ].used; + bool ice_count = 0, shuyguy_count = 0; + for ( gobj = glist; gobj < gend; gobj++ ) + { + if ( gobj->obj_id == GOBJ_ICE ) + ice_count++; + if ( gobj->obj_id == GOBJ_HEYHO_SHIP ) + shuyguy_count++; + } + + if ( !ice_count && opt_slot & SLOTMD_ADD_GICE ) + { + PATCH_ACTION_LOG("Add","KMP","object ice\n"); + AppendGOBJ(kmp,GOBJ_ICE); + dirty = true; + } + + if ( !shuyguy_count && opt_slot & SLOTMD_ADD_SHYGUY ) + { + PATCH_ACTION_LOG("Add","KMP","object HeyhoShipGBA + route\n"); + + const uint faraway = 150000; + const uint distance = 2000; + kmp_gobj_entry_t *go = AppendGOBJ(kmp,GOBJ_HEYHO_SHIP); + go->position[0] = go->position[2] = faraway; + go->position[1] = 1; + go->setting[0] = 20; + go->setting[1] = 100; + go->route_id = kmp->dlist[KMP_POTI].used; + + kmp_poti_group_t * pg = AppendList(kmp->dlist+KMP_POTI); + pg->n_point = 3; + + uint i; + for ( i = 0; i < 3; i++ ) + { + kmp_poti_point_t * pg = AppendList(&kmp->poti_point); + pg->position[0] = faraway + ( i>0 ? distance : 0 ); + pg->position[1] = 1; + pg->position[2] = faraway + ( i>1 ? distance : 0 ); + } + dirty = true; + } + } + + + //--- reverse track order: KMPMD_REVERSE + + if ( KMP_MODE & KMPMD_REVERSE ) + dirty |= ReverseKMP(kmp); + + + //--- KMPMD_POLE_LEFT, KMPMD_POLE_RIGHT, KMPMD_SP_WIDE, KMPMD_SP_NARROW, KMPMD_*_LAPS + + if ( KMP_MODE & KMPMD_M_STGI || speed_mod_active ) + { + int n = kmp->dlist[KMP_STGI].used; + kmp_stgi_entry_t * stgi = (kmp_stgi_entry_t*)kmp->dlist[KMP_STGI].list; + + const uint new_lap_count = ( KMP_MODE & KMPMD_M_LAPS ) >> KMPMD_SHIFT_LAPS; + const uint max_lap_count = KMP_MODE & KMPMD_MAX_LAPS ? new_lap_count : 0; + bool laps_modified = false; + ccp pole_modified = 0; + ccp narrow_modified = 0; + uint speed_modified = false; + u16 old_speed_mod = 0; + + uint i; + for ( i = 0; i < n; i++, stgi++ ) + { + if ( new_lap_count + && stgi->lap_count != new_lap_count + && stgi->lap_count >= max_lap_count ) + { + stgi->lap_count = new_lap_count; + laps_modified = true; + } + + if ( KMP_MODE & KMPMD_POLE_LEFT && stgi->pole_pos != 0 ) + { + stgi->pole_pos = 0; + pole_modified = "left"; + } + if ( KMP_MODE & KMPMD_POLE_RIGHT && stgi->pole_pos != 1 ) + { + stgi->pole_pos = 1; + pole_modified = "right"; + } + + if ( KMP_MODE & KMPMD_SP_WIDE && stgi->narrow_start != 0 ) + { + stgi->narrow_start = 0; + narrow_modified = "wide"; + } + if ( KMP_MODE & KMPMD_SP_NARROW && stgi->narrow_start != 1 ) + { + stgi->narrow_start = 1; + narrow_modified = "narrow"; + } + + if ( speed_mod_active && stgi->speed_mod != speed_mod_val ) + { + old_speed_mod = stgi->speed_mod; + stgi->speed_mod = speed_mod_val; + speed_modified = true; + } + } + + dirty |= laps_modified || pole_modified || narrow_modified || speed_modified; + + char lap_count_info[50] = {0}; + if (laps_modified) + snprintf(lap_count_info,sizeof(lap_count_info), + " Lap counter set to '%u'.",new_lap_count); + + if ( pole_modified && narrow_modified ) + KMP_ACTION_LOG(true, + "STGI:%s Pole position set to '%s' and narrow mode to '%s'.\n", + lap_count_info, pole_modified, narrow_modified ); + else if ( pole_modified ) + KMP_ACTION_LOG(true,"STGI:%s Pole position set to '%s'.\n", + lap_count_info, pole_modified ); + else if ( narrow_modified ) + KMP_ACTION_LOG(true,"STGI:%s Narrow mode set to '%s'.\n", + lap_count_info, narrow_modified); + else if (laps_modified) + KMP_ACTION_LOG(true,"STGI:%s\n",lap_count_info); + + if (speed_modified) + { + if ( n == 1 ) + { + if (speed_mod_val) + KMP_ACTION_LOG(true, + "STGI: Speed modifier set from %5.3f to %5.3f (%04x->%04x/hex).\n", + SpeedMod2float(old_speed_mod), speed_mod_factor, + old_speed_mod, speed_mod_val ); + else + KMP_ACTION_LOG(true, + "STGI: Speed modifier cleared (old: %5.3f = %04x/hex).\n", + SpeedMod2float(old_speed_mod), old_speed_mod ); + } + else + { + if (speed_mod_val) + KMP_ACTION_LOG(true,"STGI: Speed modifier set to %5.3f (%04x/hex).\n", + speed_mod_factor, speed_mod_val ); + else + KMP_ACTION_LOG(true,"STGI: Speed modifier cleard.\n"); + } + } + } + + + //--- second KTPT + + bool k2_active = ktpt2_active; + double3 k2_pos = ktpt2_pos; + + if ( k2_active && ktpt2_auto ) + { + kmp_finish_t kf; + CheckFinishLine(kmp,&kf); + k2_active = kf.valid && kf.enpt_rec >= 0 && kf.distance_rec >= ktpt2_min; + k2_pos.x = kf.pos_ktpt_rec.x; + k2_pos.y = kf.pos_ktpt_rec.y; + k2_pos.z = kf.pos_ktpt_rec.z; + } + + if (k2_active) + { + const int stat = InsertSecondKTPT(kmp,k2_pos); + if ( stat && kmp->dlist[KMP_KTPT].used > 1 ) + { + const kmp_ktpt_entry_t *ktpt + = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list + 1; + + KMP_ACTION_LOG(true, + "KTPT: Second KTPT %s: position %1.0f,%1.0f,%1.0f (%4.2f°)\n", + stat == 1 ? "inserted" : "replaced", + ktpt->position[0], ktpt->position[1], ktpt->position[2], + ktpt->rotation[1] ); + dirty = true; + } + } + + + //--- KMPMD_RM_SPCITEM + + if ( KMP_MODE & KMPMD_RM_SPCITEM ) + { + const uint n = kmp->dlist[KMP_GOBJ].used; + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint i, count = 0; + for ( i = 0; i < n; i++, gobj++ ) + { + switch (gobj->obj_id) + { + case 0x65: // itembox + case 0x76: // s_itembox + case 0xc9: // f_itembox + case 0xd4: // w_itembox + case 0xd5: // w_itemboxline + case 0xee: // sin_itembox + if (gobj->setting[1]) + { + gobj->setting[1] = 0; + count++; + } + break; + } + } + if (count) + { + dirty = true; + KMP_ACTION_LOG(true,"%u special item%s for players removed.\n", + count, count == 1 ? "" : "s" ); + } + } + + + //--- KMPMD_FIX_CKPH, KMPMD_FIX_ENPH, KMPMD_FIX_ITPH + + if ( KMP_MODE & KMPMD_FIX_CKPH ) + dirty |= FixPH(kmp,KMP_CKPH,KMP_CKPT,false); + if ( KMP_MODE & KMPMD_FIX_ENPH ) + dirty |= FixPH(kmp,KMP_ENPH,KMP_ENPT,true); + if ( KMP_MODE & KMPMD_FIX_ITPH ) + dirty |= FixPH(kmp,KMP_ITPH,KMP_ITPT,false); + + + //--- KMPMD_FIX_CKNEXT & KMPMD_FIX_CKJGPT + + if ( KMP_MODE & (KMPMD_FIX_CKNEXT|KMPMD_FIX_CKJGPT) ) + { + List_t *pt_list = kmp->dlist + KMP_CKPT; + kmp_ckpt_entry_t *pt0 = (kmp_ckpt_entry_t*)pt_list->list; + const uint max_pt = pt_list->used; + + if ( max_pt > 0 ) + { + if ( KMP_MODE & KMPMD_FIX_CKNEXT ) + { + // save data + const uint saved_size = max_pt * pt_list->elem_size; + u8 *saved_data = MALLOC(saved_size); + memcpy(saved_data,pt_list->list,saved_size); + + uint ip; + kmp_ckpt_entry_t *pt; + for ( ip = 0, pt = pt0; ip < max_pt; ip++, pt++ ) + { + pt->prev = ip - 1; + pt->next = ip + 1; + } + pt0[max_pt-1].next = M1(pt0->next); + + uint ih; + List_t *ph_list = kmp->dlist + KMP_CKPH; + kmp_enph_entry_t *ph = (kmp_enph_entry_t*)ph_list->list; + for ( ih = 0; ih < ph_list->used; ih++, ph++ ) + { + uint idx = ph->pt_start; + if ( idx < max_pt ) + pt0[idx].prev = M1(pt0->prev); + + idx += ph->pt_len - 1; + if ( idx < max_pt ) + pt0[idx].next = M1(pt0->next); + } + + const bool pt_dirty = memcmp(saved_data,pt_list->list,saved_size); + FREE(saved_data); + dirty |= pt_dirty; + if (pt_dirty) + KMP_ACTION_LOG(true,"'prev' and 'next' of section CKPT fixed.\n"); + } + + if ( KMP_MODE & KMPMD_FIX_CKJGPT ) + { + const uint n_jgpt = kmp->dlist[KMP_JGPT].used; + + u8 last_valid = 0; + uint ip, count = 0; + kmp_ckpt_entry_t *pt; + for ( ip = 0, pt = pt0; ip < max_pt; ip++, pt++ ) + { + if ( pt->respawn && pt->respawn >= n_jgpt ) + { + pt->respawn = last_valid; + count++; + } + else + last_valid = pt->respawn; + } + + if (count) + { + dirty = true; + KMP_ACTION_LOG(true, + "Section CKPT: %u invalid respawn link%s fixed.\n", + count, count==1 ? "" : "s" ); + } + } + } + } + + + //--- KMPMD_MASK_PFLAGS + + if ( KMP_MODE & KMPMD_MASK_PFLAGS ) + { + const uint n = kmp->dlist[KMP_GOBJ].used; + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint i, count_pflags = 0, count_padding = 0; + for ( i = 0; i < n; i++, gobj++ ) + { + if ( gobj->pflags > 0x3f ) + { + gobj->pflags &= 0x3f; + count_pflags++; + } + if ( gobj->ref_id ) + { + gobj->ref_id = 0; + count_padding++; + } + } + if (count_pflags) + { + dirty = true; + KMP_ACTION_LOG(true,"GOBJ: %u presence flag%s masked by value 0x3f.\n", + count_pflags, count_pflags == 1 ? "" : "s" ); + } + if (count_padding) + { + dirty = true; + KMP_ACTION_LOG(true,"GOBJ: Padding %u element%s set to 0.\n", + count_padding, count_padding == 1 ? "" : "s" ); + } + } + + + //--- KMPMD_RM_LECODE & KMPMD_PURGE_GOBJ + + if ( KMP_MODE & (KMPMD_RM_LECODE|KMPMD_PURGE_GOBJ) ) + { + const uint n = kmp->dlist[KMP_GOBJ].used; + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + kmp_gobj_entry_t * dest = gobj; + uint i, count_lecode = 0, count_invalid = 0; + for ( i = 0; i < n; i++, gobj++ ) + { + const u16 relevant_id = gobj->obj_id & GOBJ_M_OBJECT; + if ( KMP_MODE & KMPMD_RM_LECODE && relevant_id != gobj->obj_id ) + { + count_lecode++; + continue; + } + + if ( KMP_MODE & KMPMD_PURGE_GOBJ + && gobj->obj_id <= GOBJ_MIN_DEF + && ( relevant_id >= N_KMP_GOBJ || !ObjectInfo[relevant_id].name )) + { + count_invalid++; + continue; + } + + memcpy(dest,gobj,sizeof(*dest)); + dest++; + } + kmp->dlist[KMP_GOBJ].used = dest - (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + + if (count_lecode) + { + dirty = true; + KMP_ACTION_LOG(true,"GOBJ: %u LE-CODE object%s removed.\n", + count_lecode, count_lecode == 1 ? "" : "s" ); + } + if (count_invalid) + { + dirty = true; + KMP_ACTION_LOG(true,"GOBJ: %u invalid object%s removed.\n", + count_invalid, count_invalid == 1 ? "" : "s" ); + } + } + + + //--- KMPMD_TEST1 + + if ( KMP_MODE & KMPMD_TEST1 ) + { + const uint n = kmp->dlist[KMP_GOBJ].used; + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint i; + for ( i = 0; i < n; i++, gobj++ ) + gobj->ref_id = MyRandom(0x10000); + if (n) + { + dirty = true; + KMP_ACTION_LOG(true,"GOBJ: Padding of %u element%s set to random value.\n", + n, n == 1 ? "" : "s" ); + } + } + + //--- KMPMD_TEST2 + + if ( KMP_MODE & KMPMD_TEST2 ) + { + const uint n = kmp->dlist[KMP_GOBJ].used; + kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + uint i, count = 0; + for ( i = 0; i < n; i++, gobj++ ) + { + const u16 new_id = gobj->obj_id & GOBJ_M_OBJECT | GOBJ_M_LECODE; + if ( gobj->obj_id != new_id ) + { + gobj->obj_id = new_id; + count++; + } + } + if (count) + { + dirty = true; + KMP_ACTION_LOG(true,"GOBJ: %u element%s set to invalid id ≥0x1000.\n", + count, count == 1 ? "" : "s" ); + } + } + + + //--- repair xpflags + + RepairXPF(kmp); + + + //--- tiny + + #if defined(DEBUG) || defined(TEST) + { + // proof steadily increasing sections!! + const kmp_tiny_t *kt; + for ( kt = kmp_tiny_tab; kt->sect < KMP_N_SECT; kt++ ) + ASSERT_MSG( kt->sect <= kt[1].sect, + "kmp_tiny_tab[%zu] > kmp_tiny_tab[%zu] (%u>%u)\n", + kt-kmp_tiny_tab, kt-kmp_tiny_tab+1, + kt->sect, kt[1].sect ); + } + #endif + + const int tiny_mode = ( KMP_MODE & KMPMD_M_TINY ) >> KMPMD_S_TINY; + if ( tiny_mode > 0 ) + { + DASSERT( tiny_mode < N_TINY_MODES ); + const tiny_param_t *tp = TinyParam + tiny_mode; + + uint sect; + int round_val = 0; + const kmp_tiny_t *kt = kmp_tiny_tab; + for ( sect = 0; sect < KMP_N_SECT; sect++ ) + { + List_t *dlist = sect == KMP_POTI ? &kmp->poti_point : kmp->dlist+sect; + if (!dlist->used) + continue; + + while ( kt->sect < sect ) + kt++; + for ( ; kt->sect == sect; kt++ ) + { + PRINT("%2zu > %2u.%02u, n=%u, md=%u, elem=%3u*%2u\n", + kt-kmp_tiny_tab, kt->sect, kt->offset, kt->n, kt->mode, + dlist->used, dlist->elem_size ); + + float *fp = (float*)( dlist->list + kt->offset ); + int n_elem = dlist->used; + + switch(kt->mode) + { + case KTM_ROUND_TO_INT: + while ( n_elem-- > 0 ) + { + int i = kt->n; + while ( i-- > 0 ) + fp[i] = roundf(fp[i]); + + fp = (float*)( (u8*)fp + dlist->elem_size ); + } + break; + + + case KTM_ROUND_POS: + round_val = tp->pos_round; + goto round_std; + + case KTM_ROUND_ROT: + round_val = tp->rot_round; + goto round_std; + + case KTM_ROUND_SCALE: + round_val = tp->scale_round; + goto round_std; + + case KTM_ROUND_ROUTE: + round_val = tp->route_round; + goto round_std; + + case KTM_ROUND_CANNON: + round_val = tp->cannon_round; + + round_std: + if ( round_val < 99 ) + { + while ( n_elem-- > 0 ) + { + int i = kt->n; + while ( i-- > 0 ) + fp[i] = RoundF(fp[i],round_val); + fp = (float*)( (u8*)fp + dlist->elem_size ); + } + } + break; + + } + } + } + + SortGOBJ(kmp,KMP_SORT_TINY); + } + + + //--- term + + return dirty; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchRawDataKMP +( + void * data, // data to scan + uint data_size // size of 'data' +) +{ + if ( have_patch_count <= 0 || have_kmp_patch_count <= 0 && !transform_active ) + return false; + + KMP_ACTION_LOG(false,"PatchRawDataKMP()\n"); + PRINT("** PatchRawDataKMP() **\n"); + + kmp_t kmp; + bool stat = false; + enumError err = ScanRawKMP(&kmp,true,data,data_size); + if ( !err && PatchKMP(&kmp) ) + { + stat = !CreateRawKMP(&kmp) && kmp.raw_data_size <= data_size; + if (stat) + memcpy(data,kmp.raw_data,kmp.raw_data_size); + } + ResetKMP(&kmp); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// detect KMP types /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void GetRouteTypeInfoKMP +( + kmp_rtype_info_t *rti, // store result here (never NULL) + const struct kmp_t *kmp, // KMP data + uint sect // section to check (PT or PH) +) +{ + DASSERT(rti); + DASSERT(kmp); + memset(rti,0,sizeof(*rti)); + + switch(sect) + { + case KMP_ENPT: + case KMP_ENPH: + rti->sect_pt = KMP_ENPT; + rti->sect_ph = KMP_ENPH; + break; + + case KMP_ITPT: + case KMP_ITPH: + rti->sect_pt = KMP_ITPT; + rti->sect_ph = KMP_ITPH; + break; + + default: + ASSERT(0); + rti->sect_pt = rti->sect_ph = KMP_NO_SECT; + return; + } + + const uint n = kmp->dlist[rti->sect_ph].used; + const kmp_enph_entry_t *p = (kmp_enph_entry_t*)kmp->dlist[rti->sect_ph].list; + + rti->n_group = n; + rti->n_point = kmp->dlist[rti->sect_pt].used;; + + uint i; + for ( i = 0; i < n; i++, p++ ) + { + if ( IsDispatchPointKMP(p) ) rti->n_dispatch++; + else if ( p->pt_len > 1 ) rti->n_route2++; + else if ( p->pt_len < 1 ) rti->n_route0++; + else rti->n_route1++; + } + + PRINT("RouteType[%s/%s]: g=%u, pt=%u, routes[0,1,>1]=%u+%u+%u, dispatch=%u\n", + kmp_section_name[rti->sect_ph].name1, + kmp_section_name[rti->sect_pt].name1, + rti->n_group, rti->n_point, + rti->n_route0, rti->n_route1, rti->n_route2, rti->n_dispatch ); +} + +/////////////////////////////////////////////////////////////////////////////// + +IsArena_t IsArenaKMP ( const kmp_t * kmp ) +{ + DASSERT(kmp); + const int n_itpt = kmp->dlist[KMP_ITPT].used; + const int n_ktpt = kmp->dlist[KMP_KTPT].used; + const int n_mspt = kmp->dlist[KMP_MSPT].used; + const int arena_count = 3* !kmp->dlist[KMP_CKPT].used + + ( !n_itpt ? 3 : n_itpt < 10 ? 2 : n_itpt < 20 ? 1 : 0 ) + + ( n_ktpt < 8 ? 0 : n_ktpt > 12 ? 4 : n_ktpt - 8 ) + + ( n_mspt < 4 ? n_mspt : 4 ); + + noPRINT("IsArenaKMP() %d,%d,%d,%d => %d [battle=%d]\n", + kmp->dlist[KMP_CKPT].used, + kmp->dlist[KMP_ITPT].used, + kmp->dlist[KMP_JGPT].used, + kmp->dlist[KMP_MSPT].used, + arena_count, opt_battle_mode); + + if ( arena_count >= 4 ) + { + kmp_rtype_info_t rti; + GetRouteTypeInfoKMP(&rti,kmp,KMP_ENPH); + if ( rti.n_dispatch > 2 || rti.n_dispatch > 0 && arena_count >= 5 ) + return ARENA_DISPATCH; + } + + return arena_count >= 9 ? ARENA_FOUND + : arena_count > 6 ? ARENA_MAYBE : ARENA_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +IsArena_t CheckBattleModeKMP ( const kmp_t * kmp ) +{ + const IsArena_t is_arena = IsArenaKMP(kmp); + return opt_battle_mode <= OFFON_OFF + ? ARENA_NONE + : opt_battle_mode == OFFON_AUTO || is_arena >= ARENA_FOUND + ? is_arena + : ARENA_FOUND; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CheckFinishLine() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeFinishLine ( kmp_finish_t *kf ) +{ + DASSERT(kf); + memset(kf,0,sizeof(*kf)); + kf->ckpt = kf->ckpt_opt = kf->ktpt = kf->enpt_rec = M1(kf->ckpt); +} + +/////////////////////////////////////////////////////////////////////////////// + +double CalcCheckPointDistance +( + const kmp_ckpt_entry_t *ckpt, + double x, + double z +) +{ + DASSERT(ckpt); + + double3 rot; + rot.x = rot.z = 0; + rot.y = -CalcDirection2F(ckpt->left,ckpt->right) * ( 180 / M_PI ); + + double3 base; + base.x = x; + base.y = 0; + base.z = z; + + double3 pos; + pos.x = ckpt->left[0]; + pos.y = base.y; + pos.z = ckpt->left[1]; + + Rotate(&base,&rot,&pos,0,1); + return fabs( pos.x - base.x ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcFinishLine +( + const kmp_t *kmp, // not NULL: use it for recommendation + kmp_finish_t *kf, // valid data + const kmp_ckpt_entry_t *ckpt, // valid CKPT + const kmp_ktpt_entry_t *ktpt // valid KTPT +) +{ + DASSERT(kf); + DASSERT(ckpt); + DASSERT(ktpt); + + kf->enpt_rec = M1(kf->enpt_rec); + + kf->pos_ktpt.x = ktpt->position[0]; + kf->pos_ktpt.y = ktpt->position[1]; + kf->pos_ktpt.z = ktpt->position[2]; + + double3 pos[2]; + pos[0].x = pos[0].y = 0; + pos[0].z = 1.0; + + double3 rot; + rot.x = ktpt->rotation[0]; + rot.y = ktpt->rotation[1]; + rot.z = ktpt->rotation[2]; + + Rotate(0,&rot,pos,0,1); + double direction = atan2(pos[0].x,pos[0].z) * ( 180 / M_PI ); + kf->dir_ktpt = fmod( 3600 + direction, 360 ); + if ( fabsf(kf->dir_ktpt) < 1e-3 ) + kf->dir_ktpt = 0.0; + + direction = CalcDirection2F(ckpt->left,ckpt->right) * ( 180 / M_PI ); + direction = fmod( 3600 + direction, 360 ); + kf->dir_ckpt = fmod( direction + 90, 360 ); + if ( fabsf(kf->dir_ckpt) < 1e-3 ) + kf->dir_ckpt = 0.0; + + kf->dir_delta = fmod( kf->dir_ckpt - kf->dir_ktpt + 540, 360 ) - 180; + if ( fabsf(kf->dir_delta) < 1e-3 ) + kf->dir_delta = 0.0; + + kf->hint = fabsf(kf->dir_delta) > 1.0; + kf->warn = fabsf(kf->dir_delta) > 5.0; + + + //--- calculate distance + + double3 base; + base.x = ktpt->position[0]; + base.y = ktpt->position[1]; + base.z = ktpt->position[2]; + + pos[0].x = ckpt->left[0]; + pos[0].z = ckpt->left[1]; + pos[1].x = ckpt->right[0]; + pos[1].z = ckpt->right[1]; + pos[0].y =pos[1].y = base.y; + + rot.x = rot.z = 0; + rot.y = -direction; + + Rotate(&base,&rot,pos,sizeof(*pos),2); + double3 *zmin, *zmax; + if ( pos[0].z < pos[1].z ) + { + zmin = pos+0; + zmax = pos+1; + } + else + { + zmin = pos+1; + zmax = pos+0; + } + + if ( base.z < zmin->z ) + { + kf->distance = LengthD(base.v,zmin->v); + //pos[0].z = zmin->z; + pos[0] = *zmin; + } + else if ( base.z > zmax->z ) + { + kf->distance = LengthD(base.v,zmax->v); + //pos[0].z = zmax->z; + pos[0] = *zmax; + } + else + { + kf->distance = fabs( pos[0].x - base.x ); + pos[0].z = base.z; + } + rot.y = direction; + Rotate(&base,&rot,pos,0,1); + kf->pos_ckpt.x = pos[0].x; + kf->pos_ckpt.y = pos[0].y; + kf->pos_ckpt.z = pos[0].z; + + if ( kf->distance > 500.0 ) + { + kf->hint |= 2; + if ( kf->distance > 2000.0 ) + kf->warn |= 2; + } + + + //--- recommendation + + if (!kmp) + return; + + const uint ne = kmp->dlist[KMP_ENPT].used; + const kmp_enpt_entry_t *enpt + = (kmp_enpt_entry_t*)kmp->dlist[KMP_ENPT].list; + + double distance = 1e30; + kmp_ktpt_entry_t temp; + uint i; + + for ( i = 0; i < ne; i++, enpt++ ) + { + const double x1 = enpt->position[0] - ckpt->left[0]; + const double z1 = enpt->position[2] - ckpt->left[1]; + const double x2 = enpt->position[0] - ckpt->right[0]; + const double z2 = enpt->position[2] - ckpt->right[1]; + const double dist = sqrt( x1*x1 + z1*z1 ) + sqrt( x2*x2 + z2*z2 ); + + if ( dist < distance ) + { + distance = dist; + kf->enpt_rec = i; + memset(&temp,0,sizeof(temp)); + memcpy(temp.position,enpt->position,sizeof(temp.position)); + } + } + + PRINT0("ENPT: %d/%d | %1.0f | %1.0f %1.0f %1.0f\n", + kf->enpt_rec, ne, distance, + temp.position[0], temp.position[1], temp.position[2] ); + + if ( kf->enpt_rec >= 0 ) + { + kmp_finish_t kf2; + memset(&kf2,0,sizeof(kf2)); + CalcFinishLine(0,&kf2,ckpt,&temp); + kf->pos_ktpt_rec = kf2.pos_ckpt; + kf->dir_ktpt_rec = kf2.dir_ckpt; + + const double dx = kf->pos_ktpt.x - kf->pos_ktpt_rec.x; + const double dz = kf->pos_ktpt.z - kf->pos_ktpt_rec.z; + kf->distance_rec = sqrt ( dx*dx + dz*dz ); + + PRINT("REC: %1.0f %1.0f %1.0f | %1.2f° | dist %1.0f\n", + kf->pos_ktpt_rec.x, kf->pos_ktpt_rec.y, kf->pos_ktpt_rec.z, + kf->dir_ktpt_rec, kf->distance_rec ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CheckFinishLine ( const kmp_t *kmp, kmp_finish_t *kf ) +{ + DASSERT(kmp); + DASSERT(kf); + InitializeFinishLine(kf); + + + //--- count CKPT_0 and find last lap counter + + const uint nc = kmp->dlist[KMP_CKPT].used; + const kmp_ckpt_entry_t *ckpt0 + = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + + uint i; + for ( i = 0; i < nc; i++ ) + if ( !ckpt0[i].mode ) + { + kf->n_ckpt_0++; + kf->ckpt = i; + } + + + //--- count KMPT<0 + + const uint nk = kmp->dlist[KMP_KTPT].used; + const kmp_ktpt_entry_t *ktpt + = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list; + + for ( i = 0; i < nk; i++ ) + if ( ktpt[i].player_index < 0 && kf->n_ktpt_m++ < 2 ) + kf->ktpt = i; + + if ( !kf->n_ckpt_0 || !kf->n_ktpt_m ) + return false; + + + //--- calculate + + const kmp_ckpt_entry_t *ckpt = ckpt0 + kf->ckpt; + ktpt += kf->ktpt; + + CalcFinishLine(kmp,kf,ckpt,ktpt); + + + //--- find optimized value + + kmp_finish_t temp; + memcpy(&temp,kf,sizeof(temp)); + double distance = 1e9; + + for ( i = 0; i < nc; i++ ) + if ( !ckpt0[i].mode ) + { + CalcFinishLine(kmp,&temp,ckpt0+i,ktpt); + if ( temp.distance < distance ) + { + distance = temp.distance; + kf->ckpt_opt = i; + kf->pos_ktpt_opt = temp.pos_ckpt; + kf->dir_ktpt_opt = temp.dir_ckpt; + } + } + + + //--- return true + + return kf->valid = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp LogFinishLine ( kmp_finish_t *kf ) +{ + if (!kf) + return "-"; + + char buf[100]; + uint len = snprintf(buf,sizeof(buf), + "n=%d/%d, idx=%d/%d", + kf->n_ckpt_0, kf->n_ktpt_m, kf->ckpt, kf->ktpt ); + + if (kf->valid) + len += snprintf(buf+len,sizeof(buf)-len, + ", dir=%4.2f/%4.2f >%4.2f, dist=%5.3f", + kf->dir_ckpt, kf->dir_ktpt, kf->dir_delta, kf->distance ); + + if (kf->warn) + len += snprintf(buf+len,sizeof(buf)-len,", warn=%x",kf->warn); + if ( kf->hint & ~kf->warn ) + len += snprintf(buf+len,sizeof(buf)-len,", hint=%x",kf->hint); + + return CopyCircBuf0(buf,len); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int InsertSecondKTPT +( + // returns: 0=not possible, 1:inserted, 2:replaced + + kmp_t *kmp, // valid KMP + double3 pos // position to insert +) +{ + DASSERT(kmp); + + const uint nk = kmp->dlist[KMP_KTPT].used; + kmp_ktpt_entry_t *ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list; + + if ( !nk || ktpt->player_index >= 0 ) + return 0; + + ktpt++; + int stat = 2; + if ( nk < 2 || ktpt->player_index >= 0 ) + { + stat = 1; + ktpt = InsertList(kmp->dlist+KMP_KTPT,1); + } + DASSERT(ktpt); + + memset(ktpt,0,sizeof(*ktpt)); + ktpt->position[0] = pos.x; + ktpt->position[1] = pos.y > 0 ? pos.y : ktpt[-1].position[1]; + ktpt->position[2] = pos.z; + ktpt->player_index = -1; + + + kmp_finish_t kf; + CheckFinishLine(kmp,&kf); + if (kf.valid) + { + if ( kf.ckpt_opt >= 0 ) + { + ktpt->position[0] = kf.pos_ktpt_opt.x; + ktpt->position[1] = kf.pos_ktpt_opt.y; + ktpt->position[2] = kf.pos_ktpt_opt.z; + ktpt->rotation[1] = kf.dir_ktpt_opt; + } + else + ktpt->rotation[1] = kf.dir_ckpt; + } + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CheckUsedPosKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeUsedPosObj ( kmp_usedpos_obj_t *obj ) +{ + DASSERT(obj); + memset(obj,0,sizeof(*obj)); + + obj->min.x = obj->min.y = obj->min.z = FLT_MAX; + obj->max.x = obj->max.y = obj->max.z = -FLT_MAX; + obj->allowed.x = obj->allowed.y = obj->allowed.z = 131071; // 2^17-1 + obj->shift.x = obj->shift.y = obj->shift.z = 0.0; + obj->stretch.x = obj->stretch.y = obj->stretch.z = 1.0; + obj->rating[0] = obj->rating[1] = obj->rating[2] = WLEVEL_NA; + + obj->pos_factor_valid = false; + obj->pos_factor = obj->stretch; +} + +/////////////////////////////////////////////////////////////////////////////// + +void EvaluateUsedPosObj ( kmp_usedpos_obj_t *obj ) +{ + DASSERT(obj); + + obj->valid = true; + obj->useful = obj->pos_factor_valid = false; + obj->max_rating = WLEVEL_OK; + + int i; + for ( i = 0; i < 3; i++ ) + { + WarnLevel_t rating = WLEVEL_OK; + + float temp = fabsf(obj->min.v[i]); + float value = fabsf(obj->max.v[i]); + if ( value < temp ) + value = temp; + + const float fail = obj->allowed.v[i]; + const float warn = fail - 5000.0; + const float hint = fail - 10000.0; + + if ( rating < WLEVEL_FAIL && value > fail ) rating = WLEVEL_FAIL; + else if ( rating < WLEVEL_WARN && value > warn ) rating = WLEVEL_WARN; + else if ( rating < WLEVEL_HINT && value > hint ) rating = WLEVEL_HINT; + + if ( i == 1 ) + { + // special handling of min.y + const float ymin = round(obj->min.y); + if ( rating < WLEVEL_FAIL && ymin < 0.0 ) rating = WLEVEL_FAIL; + else if ( rating < WLEVEL_WARN && ymin < 1.0 ) rating = WLEVEL_WARN; + else if ( rating < WLEVEL_HINT && ymin < 5.0 ) rating = WLEVEL_HINT; + } + + obj->rating[i] = rating; + if ( obj->max_rating < rating ) + obj->max_rating = rating; + + + //-- pos factors + + const float factor = ( value + 25000 ) / fail; + if ( factor > 1.0 ) + { + obj->pos_factor.v[i] = factor; + if ( factor > 1.1 ) + obj->pos_factor_valid = true; + } + else + obj->pos_factor.v[i] = 1.0; + } + + obj->shift_active = obj->shift.x || obj->shift.y || obj->shift.z; + obj->stretch_active = obj->stretch.x > 1.0 + || obj->stretch.y > 1.0 + || obj->stretch.z > 1.0; + obj->useful = obj->pos_factor_valid || obj->stretch_active; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ModifyUsedPosObj + ( kmp_usedpos_obj_t *obj, kmp_usedpos_obj_t *src, float3 *shift, float3 *stretch ) +{ + DASSERT(obj); + + if (src) + *obj = *src; + + bool useful = false; + int i; + for ( i = 0; i < 3; i++ ) + { + if ( shift && shift->v[i] ) + { + useful = true; + obj->shift.v[i] += shift->v[i]; + obj->min.v[i] += shift->v[i]; + obj->max.v[i] += shift->v[i]; + } + + if ( stretch && stretch->v[i] != 1.0 ) + { + useful = true; + obj->stretch.v[i] *= stretch->v[i]; + obj->allowed.v[i] *= stretch->v[i]; + } + } + EvaluateUsedPosObj(obj); + obj->useful = useful; + + if (src) + { + src->max_rating = WLEVEL_OK; + for ( i = 0; i < 3; i++ ) + { + if ( src->rating[i] > WLEVEL_FIXED && src->rating[i] > obj->rating[i] ) + src->rating[i] = obj->rating[i] > WLEVEL_FIXED + ? obj->rating[i] : WLEVEL_FIXED; + if ( src->max_rating < src->rating[i] ) + src->max_rating = src->rating[i]; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetUsedPosObjSuggestion + ( const kmp_usedpos_obj_t *obj, bool force, ccp return_if_empty ) +{ + if ( obj && ( force || obj->max_rating >= WLEVEL_HINT )) + { + if ( obj->shift_active && obj->pos_factor_valid ) + return PrintCircBuf( + "--shift %1.0f,%1.0f,%1.0f & @ITEM-POS-FACTOR=v(%4.2f,%4.2f,%4.2f)", + obj->shift.x, obj->shift.y, obj->shift.z, + obj->pos_factor.x, obj->pos_factor.y, obj->pos_factor.z ); + + if (obj->shift_active) + return PrintCircBuf("--shift %1.0f,%1.0f,%1.0f", + obj->shift.x, obj->shift.y, obj->shift.z ); + + if (obj->pos_factor_valid) + return PrintCircBuf("@ITEM-POS-FACTOR=v(%4.2f,%4.2f,%4.2f)", + obj->pos_factor.x, obj->pos_factor.y, obj->pos_factor.z ); + } + return return_if_empty; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintUsedPosObj ( FILE *f, int indent, const kmp_usedpos_obj_t *obj ) +{ + DASSERT(f); + DASSERT(obj); + indent = NormalizeIndent(indent); + + fprintf(f,"%*s" "[%u%u%u%u] mm=%1.0f,%1.0f,%1.0fk/%1.0f,%1.0f,%1.0fk, " + "a=%1.0f,%1.0f,%1.0fk, ss=%1.0f,%1.0f,%1.0fk/%1.2f,%1.2f,%1.2f -> " + "R=%d,%d,%d, P=%d/%1.2f,%1.2f,%1.2f\n", + indent,"", + obj->valid, obj->useful, obj->shift_active, obj->stretch_active, + obj->min.x/1000, obj->min.y/1000, obj->min.z/1000, + obj->max.x/1000, obj->max.y/1000, obj->max.z/1000, + obj->allowed.x/1000, obj->allowed.y/1000, obj->allowed.z/1000, + obj->shift.x/1000, obj->shift.y/1000, obj->shift.z/1000, + obj->stretch.x, obj->stretch.y, obj->stretch.z, + obj->rating[0], obj->rating[1], obj->rating[2], + obj->pos_factor_valid, + obj->pos_factor.x, obj->pos_factor.y, obj->pos_factor.z ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeUsedPos ( kmp_usedpos_t *up ) +{ + DASSERT(up); + memset(up,0,sizeof(*up)); + + InitializeUsedPosObj(&up->orig); + up->orig_lex = up->center = up->center_lex = up->orig; + up->suggest = &up->orig; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void min_max_used ( kmp_usedpos_t * up, const float *pos ) +{ + DASSERT(up); + DASSERT(pos); + + float3 *min = &up->orig.min; + if ( min->x > pos[0] ) min->x = pos[0]; + if ( min->y > pos[1] ) min->y = pos[1]; + if ( min->z > pos[2] ) min->z = pos[2]; + + float3 *max = &up->orig.max; + if ( max->x < pos[0] ) max->x = pos[0]; + if ( max->y < pos[1] ) max->y = pos[1]; + if ( max->z < pos[2] ) max->z = pos[2]; +} + +//----------------------------------------------------------------------------- + +void CheckUsedPos ( const kmp_t *kmp, kmp_usedpos_t *up ) +{ + DASSERT(kmp); + DASSERT(up); + + InitializeUsedPos(up); + + //--- get min and max values + + float3 *min = &up->orig.min; + float3 *max = &up->orig.max; + + min->x = min->y = min->z = FLT_MAX; + max->x = max->y = max->z = -FLT_MAX; + + uint i, n = kmp->dlist[KMP_ENPT].used; + const kmp_enpt_entry_t * enpt = (kmp_enpt_entry_t*)kmp->dlist[KMP_ENPT].list; + for ( i = 0; i < n; i++, enpt++ ) + min_max_used(up,enpt->position); + + n = kmp->dlist[KMP_ITPT].used; + const kmp_itpt_entry_t * itpt = (kmp_itpt_entry_t*)kmp->dlist[KMP_ITPT].list; + for ( i = 0; i < n; i++, itpt++ ) + min_max_used(up,itpt->position); + + if ( min->x > max->x ) + { + min->x = min->y = min->z = NAN; + *max = *min; + up->orig_lex = up->center = up->center_lex = up->orig; + return; + } + + + //--- evaluate standard settings + + EvaluateUsedPosObj(&up->orig); + + + //--- evaluate with LEX:SET1 + + up->orig_lex = up->orig; + up->orig_lex.useful = false; + + PRINT0("lexinfo=%p [%d]\n",kmp->lexinfo, kmp->lexinfo ? kmp->lexinfo->set1_found : -1 ); + if ( kmp->lexinfo && kmp->lexinfo->set1_found ) + { + ModifyUsedPosObj(&up->orig_lex,&up->orig,0,&kmp->lexinfo->set1.item_factor); + if ( up->orig_lex.useful ) + up->suggest = &up->orig_lex; + } + + //--- evaluate centered + + kmp_usedpos_obj_t *obj = &up->center; + InitializeUsedPosObj(obj); + obj->min = up->orig.min; + obj->max = up->orig.max; + + float3 shift; + shift.x = shift.y = shift.z = 0; + + for ( i = 0; i < 3; i++ ) + { + if ( up->orig.rating[i] > WLEVEL_OK ) + { + bool force_shift = false; + float shift_value = ( obj->min.v[i] + obj->max.v[i] ) / -2; + if ( i == 1 && obj->min.v[i] < 10.0 ) + { + // special case for Y + const float limit = 100.0 - obj->min.v[i]; + if ( shift_value < limit ) + { + shift_value = limit; + force_shift = true; + } + } + + if ( force_shift || fabsf(shift_value) >= 1000.0 ) + { + obj->useful = true; + shift.v[i] = shift_value; + } + } + } + + up->center_lex = up->center; + if (obj->useful) + { + ModifyUsedPosObj(obj,0,&shift,0); + if ( obj->useful ) + up->suggest = obj; + + up->center_lex.useful = false; + if ( obj->useful && kmp->lexinfo && kmp->lexinfo->set1_found ) + { + ModifyUsedPosObj(&up->center_lex,&up->center,0,&kmp->lexinfo->set1.item_factor); + if ( up->center_lex.useful ) + up->suggest = &up->center_lex; + } + } + + + #if HAVE_WIIMM_EXT && 0 + PrintUsedPosObj(stderr,1,&up->orig); + PrintUsedPosObj(stderr,1,&up->orig_lex); + if (up->center.useful||1) + { + PrintUsedPosObj(stderr,1,&up->center); + PrintUsedPosObj(stderr,1,&up->center_lex); + } + PrintUsedPosObj(stderr,1,up->suggest); + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DetectSpecialKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// order is important, compare enum have_kmp_obj_t and ct.wiimm.de + +const kmp_obj_info_t have_kmp_info[HAVEKMP__N] = +{ + { 0, "woodbox-ht", "Woodbox with fall height" }, // HAVEKMP_WOODBOX_HT + { 0, "mushroom-car","penguin_m as mushroom car" }, // HAVEKMP_MUSHROOM_CAR + { 0, "penguin-pos", "Alternative start of penguin_m" }, // HAVEKMP_PENGUIN_POS + { 0, "2ktpt", "Second KTPT with index -1" }, // HAVEKMP_SECOND_KTPT + { 0, "xpf", "Extended presence flags (XPF)" }, // HAVEKMP_X_PFLAGS + { 0, "xcond", "XPF: Conditional object" }, // HAVEKMP_X_COND + { 0, "xobj", "XPF: Definition object" }, // HAVEKMP_X_DEFOBJ + { 0, "xrnd", "XPF: Random scenarios" }, // HAVEKMP_X_RANDOM + { 0, "eprop-speed", "Epropeller with speed setting" }, // HAVEKMP_EPROP_SPEED +}; + +/////////////////////////////////////////////////////////////////////////////// + +void DetectSpecialKMP +( + const kmp_t *kmp, // pointer to valid KMP + kmp_special_t res // store result here, never NULL +) +{ + DASSERT(kmp); + DASSERT(res); + memset(res,0,sizeof(kmp_special_t)); + + const kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + const kmp_gobj_entry_t *gend = gobj + kmp->dlist[KMP_GOBJ].used; + for ( ; gobj < gend; gobj++ ) + { + if ( gobj->pflags ) + { + const u16 relevant_id = GetRelevantObjectId(gobj->obj_id); + switch(relevant_id) + { + case 0x070: // woodbox + if ( gobj->setting[7] && res[HAVEKMP_WOODBOX_HT] < 0xff ) + res[HAVEKMP_WOODBOX_HT]++; + break; + + case 0x0d8: // penguin_m + if ( gobj->setting[7] && res[HAVEKMP_MUSHROOM_CAR] < 0xff ) + res[HAVEKMP_MUSHROOM_CAR]++; + else if ( gobj->setting[1] && res[HAVEKMP_PENGUIN_POS] < 0xff ) + res[HAVEKMP_PENGUIN_POS]++; + break; + + case 0x1a6: // Epropeller + if ( gobj->setting[7] && res[HAVEKMP_EPROP_SPEED] < 0xff ) + res[HAVEKMP_EPROP_SPEED]++; + break; + } + } + } + + kmp_finish_t kf; + CheckFinishLine(kmp,&kf); + if ( kf.n_ktpt_m > 1 ) + res[HAVEKMP_SECOND_KTPT]++; + + kmp_pflags_t pf; + CountPresenceFlags(kmp,&pf); + res[HAVEKMP_X_PFLAGS] = pf.n_xpflags; + res[HAVEKMP_X_COND] = pf.n_obj_ref + pf.n_cond_ref; + res[HAVEKMP_X_DEFOBJ] = pf.n_defobj; + res[HAVEKMP_X_RANDOM] = pf.n_cond_rnd; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp CreateSpecialInfoKMP + ( kmp_special_t special, bool add_value, ccp return_if_empty ) +{ + static char buf[500]; + char *dest = buf; + + if (add_value) + { + uint i, val = 0; + for ( i = 0; i < HAVEKMP__N; i++ ) + if ( special[i] ) + val |= 1 << i; + dest = snprintfE( dest, buf+sizeof(buf), "%u=" , val ); + } + + uint i; + ccp sep = ""; + for ( i = 0; i < HAVEKMP__N; i++ ) + if ( special[i] ) + { + dest = StringCat2E(dest,buf+sizeof(buf),sep,have_kmp_info[i].name); + sep = ","; + } + + return dest == buf ? return_if_empty : CopyCircBuf0(buf,dest-buf); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CkeckKMP() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[check_kmp_t]] + +typedef struct check_kmp_t +{ + //--- param + + const kmp_t * kmp; // pointer to valid KMP + CheckMode_t mode; // print mode + + //--- status + + int warn_count; // number of warnings + int hint_count; // number of hints + int info_count; // number of hints + bool head_printed; // true: header line printed + bool info_printed; // true: info line printed + int sect; // last section + int index; // last object index + IsArena_t is_arena; // arena detected? + ccp t_track; // track" or "arena" + + //--- misc data + + ColorSet_t col; // color setup + +} check_kmp_t; + +/////////////////////////////////////////////////////////////////////////////// + +ccp print_name ( const kmp_t * kmp, uint sect, uint index ) +{ + DASSERT(kmp); + DASSERT( sect < KMP_N_SECT ); + + // cast because remove const + IndexList_t *ill = (IndexList_t*)kmp->index + sect; + ccp name = index < ill->used ? GetNameIL(ill,index) : 0; + if ( !name || !name ) + return ""; + + const uint bufsize = 50; + char *buf = GetCircBuf(bufsize); + snprintf(buf,bufsize," '%.46s'",name); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +// predefine function to avoid a warning because of __attribute__ usage +static void print_check_error +( + check_kmp_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_INFO|CMOD_HINT|CMOD_WARNING + int sect, // section + int index, // object index + ccp obj_type, // NULL or object type + ccp format, // format of message + ... // arguments + +) __attribute__ ((__format__(__printf__,6,7))); + +static void print_check_error +( + check_kmp_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_INFO|CMOD_HINT|CMOD_WARNING + int sect, // section + int index, // object index + ccp obj_type, // NULL or object type + ccp format, // format of message + ... // arguments + +) +{ + DASSERT(chk); + DASSERT(chk->kmp); + + ccp col; // message color + ccp bol; // begin of line + switch (print_mode&~CMOD_LECODE) + { + case CMOD_WARNING: + if (!( chk->mode & CMOD_WARNING )) + return; + col = chk->col.warn; + bol = check_bowl; + chk->warn_count++; + break; + + case CMOD_HINT: + if (!( chk->mode & CMOD_HINT )) + return; + col = chk->col.hint; + bol = check_bohl; + chk->hint_count++; + break; + + case CMOD_INFO: + if (!( chk->mode & CMOD_INFO )) + return; + col = chk->col.info; + bol = check_boil; + chk->info_count++; + break; + + default: + col = 0; + bol = 0; // print head only + break; + } + + if (!chk->head_printed) + { + chk->head_printed = true; + if ( chk->mode & (CMOD_HEADER|CMOD_FORCE_HEADER) ) + fprintf(stdlog,"%s* CHECK %s:%s%s\n", + chk->col.heading, GetNameFF(chk->kmp->fform,0), + chk->kmp->fname, chk->col.reset ); + } + + if (!bol) + return; + + if (!chk->info_printed) + { + chk->info_printed = true; + fprintf(stdlog," %s> All indices are zero based!%s\n", + chk->col.info, chk->col.reset ); + } + + if ( sect != -99 && ( chk->sect != sect || chk->index != index )) + { + chk->sect = sect; + chk->index = index; + + if ( sect < 0 ) + fprintf(stdlog," %s> Statistics%s\n",chk->col.info,chk->col.reset); + else if ( sect == KMP_N_SECT ) + fprintf(stdlog," %s> All sections%s\n",chk->col.info,chk->col.reset); + else if ( index < 0 ) + fprintf(stdlog," %s> Section %s%s%s\n", + chk->col.info, kmp_section_name[sect].name1, + index == -2 ? ", header" : "", chk->col.reset ); + else + { + fprintf(stdlog," %s> Section %s, %s #%u of %u%s", + chk->col.info, + kmp_section_name[sect].name1, + kmp_section_name[sect].name2, + index, chk->kmp->dlist[sect].used, + print_name(chk->kmp,sect,index) ); + + if (obj_type) + fprintf(stdlog,", type '%s'%s\n",obj_type,chk->col.reset); + else + fprintf(stdlog,"%s\n",chk->col.reset); + } + } + + if (format) + { + fputs(col,stdlog); + if ( print_mode & CMOD_LECODE ) + { + ccp colon = strchr(bol,':'); + fprintf(stdlog,"%.*s/LE-CODE: ",(int)(colon?colon-bol:100),bol); + } + else + fputs(bol,stdlog); + fputs(chk->col.reset,stdlog); + + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_item_hint +( + check_kmp_t * chk, // valid data structure + uint n_total, // total number of special item boxes + uint n_unsure, // unsure number of special item boxes + ccp dest // "players" or "enemies" +) +{ + DASSERT(chk); + DASSERT(dest); + + if (n_total) + { + char buf[25]; + if (n_unsure) + snprintf(buf,sizeof(buf)," (%u unsure)",n_unsure); + else + *buf = 0; + + if ( n_total > 1 ) + print_check_error(chk,CMOD_HINT,KMP_GOBJ,-1,0, + "%u item boxes give special items to %s%s.\n", + n_total, dest, buf ); + else + print_check_error(chk,CMOD_HINT,KMP_GOBJ,-1,0, + "One item box gives a special item to %s%s.\n", + dest, buf ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void check_pt +( + check_kmp_t *chk, // valid data structure + int sect_pt, // PT section + kmp_path_t *ck_path // path data, already initialized + // data is calculated in first call +) +{ + DASSERT(chk); + DASSERT(chk->kmp); + DASSERT( sect_pt == KMP_ENPT || sect_pt == KMP_ITPT ); + DASSERT(ck_path); + + uint n = chk->kmp->dlist[sect_pt].used; + if ( n > KMP_MAX_POINT ) + n = KMP_MAX_POINT; + const kmp_enpt_entry_t *pt = (kmp_enpt_entry_t*)chk->kmp->dlist[sect_pt].list; + + if ( chk->is_arena >= ARENA_FOUND ) + { + if ( n && sect_pt == KMP_ITPT ) + print_check_error(chk,CMOD_HINT,sect_pt,-1,0, + "%u element%s found, but ITPT not needed for battle arenas.\n", + n, n==1 ? "" : "s" ); + } + else + { + if ( !n && ( sect_pt != KMP_ITPT || chk->is_arena == ARENA_NONE )) + print_check_error(chk,CMOD_WARNING,sect_pt,-1,0, + "No elements found, but needed for racing tracks.\n" ); + + u8 fail[KMP_MAX_POINT]; + memset(fail,0,sizeof(fail)); + + uint i, fail_count = 0; + const kmp_link_t *last = 0; + for ( i = 0; i < n; i++, pt++ ) + { + const kmp_link_t *link + = FindCKPT(chk->kmp,ck_path,pt->position[0],pt->position[2],last); + if (link) + last = link; + else + fail[i]++, fail_count++; + } + + if ( fail_count ) + { + uint i; + for ( i = 0; i < n; i++ ) + if (fail[i]) + { + const int start = i; + while ( ++i < n && fail[i] ) + ; + if ( --i == start ) + print_check_error(chk,CMOD_WARNING,sect_pt,-1,0, + "Point #%u is outside of all convex check quadrilaterals" + " (pos bug?).\n", + i ); + else + { + print_check_error(chk,CMOD_WARNING,sect_pt,-1,0, + "Points #%u..#%u are outside of all convex check quadrilaterals" + " (pos bug?).\n", + start, i ); + } + } + } + } + + if ( sect_pt == KMP_ENPT ? chk->kmp->enpt_flag.import : chk->kmp->itpt_flag.import ) + print_check_error(chk,CMOD_HINT,sect_pt,-1,0, + "Exported flags detected (extension of tool wkmpt).\n"); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void check_ph +( + check_kmp_t *chk, // valid data structure + int sect_ph, // PH section + int sect_pt, // PT section + kmp_path_t *ck_path // ckeck point path data, already initialized + // data is calculated in first call +) +{ + DASSERT(chk); + const kmp_t *kmp = chk->kmp; + DASSERT(kmp); + + const List_t *ph_list = kmp->dlist + sect_ph; + const kmp_enph_entry_t *ph = (kmp_enph_entry_t*)ph_list->list; + const uint n_ph = ph_list->used; + + const List_t *pt_list = kmp->dlist + sect_pt; + const uint n_pt = pt_list->used; + + if ( !n_ph && ( sect_ph == KMP_ENPT || chk->is_arena < ARENA_FOUND ) ) + { + print_check_error(chk,CMOD_WARNING,sect_ph,-1,0, + "Empty section => track will freeze.\n" ); + } + + uint i, next_idx = 0; + for ( i = 0; i < n_ph; i++, ph++ ) + { + if ( ph->pt_start >= n_pt ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Route group starts at point %u, but max possible index is %u.\n", + ph->pt_start, n_pt ); + else if ( ph->pt_start + ph->pt_len > n_pt ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Route group ends at point %u, but max possible index is %u.\n", + ph->pt_start + ph->pt_len, n_pt ); + else if ( ph->pt_start != next_idx ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Route group starts at point %u," + " but %u is the expected is start point.\n", + ph->pt_start, next_idx ); + next_idx = ph->pt_start + ph->pt_len; + + + //--- test 'prev' links + + uint link, count; + for ( link = count = 0; link < KMP_MAX_PH_LINK; link++ ) + { + if ( !IS_M1(ph->prev[link]) ) + { + if ( ph->prev[link] >= n_ph ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Invalid 'prev' link at index #%u:" + " Link points to group %u, but max allowed is %u.\n", + link, ph->prev[link], n_ph-1 ); + else + count++; + } + } + + if ( !count && chk->is_arena < ARENA_FOUND ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "No valid 'prev' link found. Define at least one link.\n" ); + + + //--- test 'next' links + + for ( link = count = 0; link < KMP_MAX_PH_LINK; link++ ) + { + if ( !IS_M1(ph->next[link]) ) + { + if ( ph->next[link] >= n_ph ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Invalid 'next' link at index #%u:" + " Link points to group %u, but max allowed is %u.\n", + link, ph->next[link], n_ph-1 ); + else + count++; + } + else if ( !link && sect_ph == KMP_ITPH ) + { + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Missing 'next' link at index #0." + " It's needed for the standard itemroute.\n" ); + } + } + + if (!count) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "No valid 'next' link found => track will freeze." + " Define at least one link.\n" ); + + if ( sect_ph == KMP_ITPH ) + { + if ( ph->next[0] == i ) + { + if ( ph->pt_len == 1 ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Self linked item route with exact 1 point:" + " This will force a heavy lag if a red shell arrive.\n" ); + else + print_check_error(chk,CMOD_HINT,sect_ph,i,0, + "Self linked item route.\n" ); + } + else if ( ph->pt_len == 1 ) + print_check_error(chk,CMOD_HINT,sect_ph,i,0, + "Item route with exact 1 point.\n" ); + } + } + + if ( sect_ph != KMP_CKPH ) + { + // enemy and item routes + check_pt(chk,sect_pt,ck_path); + return; + } + + + //--- only CKPH/CKPT + + ph = (kmp_enph_entry_t*)ph_list->list; + for ( i = 0; i < n_ph; i++, ph++ ) + { + uint idx = ph->pt_start; + uint end = idx + ph->pt_len; + if ( end > n_pt ) + end = n_pt; + + u8 done[0xff]; + memset(done,0,sizeof(done)); + + uint j; + const kmp_ckpt_entry_t *pt = ( (kmp_ckpt_entry_t*)pt_list->list ) + idx; + for ( j = idx; j < end; j++, pt++ ) + { + const int good_prev = j == idx ? -1 : j - 1; + const int good_next = j == end-1 ? -1 : j + 1; + const int have_prev = IS_M1(pt->prev) ? -1 : pt->prev; + const int have_next = IS_M1(pt->next) ? -1 : pt->next; + + if ( good_prev != have_prev ) + print_check_error(chk,CMOD_WARNING,sect_pt,j,0, + "Invalid 'prev' link [have %d, should be %d]\n", + have_prev, good_prev ); + + if ( good_next != have_next ) + print_check_error(chk,CMOD_WARNING,sect_pt,j,0, + "Invalid 'next' link [have %d, should be %d]\n", + have_next, good_next ); + + typeof(pt->mode) mode = pt->mode; + if ( !IS_M1(mode) && mode < sizeof(done)/sizeof(*done) ) + done[mode]++; + } + + for ( j = 0; j < sizeof(done)/sizeof(*done); j++ ) + if ( done[j] > 1 ) + print_check_error(chk,CMOD_WARNING,sect_ph,i,0, + "Check point mode %u used %u times in single group.\n", + j, done[j] ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void print_stat +( + check_kmp_t * chk, // valid data structure + int sect_ph, // -1 or PH section + int sect_pt, // (PT) section + bool print_null, // true: print if null pounts + ccp name_ph, // name of ph + ccp name_pt // name of pt +) +{ + DASSERT(chk); + DASSERT(chk->kmp); + DASSERT( sect_ph >= -1 && sect_ph < KMP_N_SECT ); + DASSERT( sect_pt >= 0 && sect_pt < KMP_N_SECT ); + DASSERT( sect_ph < 0 || name_ph ); + DASSERT(name_pt); + + if ( sect_ph >= 0 ) + { + const uint nh = chk->kmp->dlist[sect_ph].used; + const uint nt = sect_pt == KMP_POTI + ? chk->kmp->poti_point.used + : chk->kmp->dlist[sect_pt].used; + print_check_error(chk,CMOD_INFO,-1,-1,0, + "%3u %s%s for %u %s%s defined.\n", + nt, name_pt, nt==1 ? "" : "s", + nh, name_ph, nh==1 ? "" : "s" ); + } + else + { + const uint np = chk->kmp->dlist[sect_pt].used; + if ( np || print_null ) + print_check_error(chk,CMOD_INFO,-1,-1,0, + "%3u %s%s defined.\n", np, name_pt, np==1 ? "" : "s"); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// + +int CheckKMP +( + // returns number of errors + + const kmp_t * kmp, // pointer to valid KMP + CheckMode_t mode // print mode +) +{ + DASSERT(kmp); + PRINT("CheckKMP(%d)\n",mode); + + //--- setup + + check_kmp_t chk; + memset(&chk,0,sizeof(chk)); + SetupColorSet(&chk.col,stdlog); + chk.kmp = kmp; + chk.mode = mode; + chk.sect = -1; + chk.t_track = "track"; + + if ( mode & CMOD_FORCE_HEADER ) + print_check_error(&chk,0,0,0,0,0); + + + //--- var defs + + uint i, n; + char buf[50]; + u8 done[0x100]; + + const uint route_usage_size = 0xff; + u8 route_usage[route_usage_size]; + memset(route_usage,0,sizeof(route_usage)); + enum { RU_CAME = 1, RU_GOBJ = 2 }; + + u16 jgpt_usage[0x100]; + memset(jgpt_usage,0,sizeof(jgpt_usage)); + + const int ncame = kmp->dlist[KMP_CAME].used; + const int npoti = kmp->dlist[KMP_POTI].used; + const int njgpt = kmp->dlist[KMP_JGPT].used; + + + //--- is arena + + chk.is_arena = IsArenaKMP(kmp); + switch(chk.is_arena) + { + case ARENA_NONE: + print_check_error(&chk,CMOD_INFO,-99,-1,0, + "%sRacing track detected.%s\n", + chk.col.select, chk.col.reset ); + break; + + case ARENA_MAYBE: + print_check_error(&chk,CMOD_HINT,-99,-1,0, + "%sSome hints for a battle arena found.%s\n", + chk.col.select, chk.col.reset ); + break; + + case ARENA_FOUND: + chk.t_track = "arena"; + print_check_error(&chk,CMOD_HINT,-99,-1,0, + "%sBattle arena detected.%s\n", + chk.col.select, chk.col.reset ); + break; + + case ARENA_DISPATCH: + chk.t_track = "arena"; + print_check_error(&chk,CMOD_HINT,-99,-1,0, + "%sBattle arena with dispatch points detected.%s\n", + chk.col.select, chk.col.reset ); + break; + + case ARENA__N: + break; + } + + IsArena_t battle_mode = CheckBattleModeKMP(kmp); + if ( battle_mode != chk.is_arena ) + { + chk.is_arena = battle_mode; + + ccp info; + if ( battle_mode >= ARENA_FOUND ) + { + chk.t_track = "arena"; + info = battle_mode == ARENA_DISPATCH + ? "battle arena with dispatch mode" + : "battle arena"; + } + else + { + chk.t_track = "track"; + info = "racing track"; + } + + print_check_error(&chk,CMOD_HINT,-99,-1,0, + "%sOption --battle forces mode »%s«!%s\n", + chk.col.select, info, chk.col.reset ); + } + + + //--- statistics + + if ( mode & CMOD_INFO ) + { + print_stat(&chk, -1, KMP_AREA, true, 0, "area"); + print_stat(&chk, -1, KMP_CAME, true, 0, "camera"); + print_stat(&chk, KMP_ENPH, KMP_ENPT, true, "group", "enemy point"); + print_stat(&chk, KMP_ITPH, KMP_ITPT, true, "group", "item point"); + print_stat(&chk, KMP_CKPH, KMP_CKPT, true, "group", "check point"); + print_stat(&chk, KMP_POTI, KMP_POTI, true, "route", "route point"); + print_stat(&chk, -1, KMP_GOBJ, true, 0, "global object"); + print_stat(&chk, -1, KMP_JGPT, true, 0, "respawn position"); + print_stat(&chk, -1, KMP_KTPT, true, 0, "starting position"); + print_stat(&chk, -1, KMP_CNPT, false, 0, "cannon target"); + print_stat(&chk, -1, KMP_MSPT, false, 0, "end battle position"); + print_stat(&chk, -1, KMP_STGI, true, 0, "stage record"); + } + + + //--- check AREA entries + + n = kmp->dlist[KMP_AREA].used; + const kmp_area_entry_t * area = (kmp_area_entry_t*)kmp->dlist[KMP_AREA].list; + for ( i = 0; i < n; i++, area++ ) + { + if ( area->type != 0x00 ) // only CAME checked + continue; + + if ( area->dest_id == 0xff ) + print_check_error(&chk,CMOD_WARNING,KMP_AREA,i,"CAME", + "Missing camera link.\n"); + else if ( area->dest_id >= ncame ) + print_check_error(&chk,CMOD_WARNING,KMP_AREA,i,"CAME", + "Invalid link to not existing camera #%u (max=#%d).\n", + area->dest_id, ncame-1 ); + } + + + //--- check CAME entries + + i = kmp->value[KMP_CAME] >> 8; + if ( i >= ncame ) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,-2,0, + "Invalid link to not existing opening camera #%u (max=#%d).\n", + i, ncame-1 ); + memset(done,0,sizeof(done)); + while ( i < ncame && i < sizeof(done)/sizeof(*done) && !done[i]++ ) + { + const kmp_came_entry_t * came = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list + i; + if ( came->type != 4 && came->type != 5 ) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,i,"opening camera", + "Opening camera type should be 4 or 5, but not %u.\n", + came->type ); + i = came->next; + } + + i = kmp->value[KMP_CAME] & 0xff; + if ( i >= ncame ) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,-2,0, + "Invalid link to not existing selection camera #%u (max=#%d).\n", + i, ncame-1 ); + + const kmp_came_entry_t * came = (kmp_came_entry_t*)kmp->dlist[KMP_CAME].list; + uint came0count = 0; + for ( i = 0; i < ncame; i++, came++ ) + { + snprintf(buf,sizeof(buf),"%u",came->type); + + const typeof(came->route) route = came->route; + if (!IS_M1(route)) + { + if ( route >= npoti ) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,i,buf, + "Invalid link to not existing route #%u (max=#%d).\n", + route, npoti-1 ); + + if ( route < route_usage_size ) + route_usage[route] |= RU_CAME; + } + + switch (came->type) + { + case 0: + came0count++; + // fall through + #ifdef TEST // [[2do]] is this true? + case 1: + case 3: + #endif + if (!IS_M1(route)) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,i,buf, + "Link to route #%u not needed.\n",route); + break; + + #ifdef TEST // [[2do]] is this true? + case 2: + #endif + case 5: + case 6: + if (IS_M1(route)) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,i,buf, + "Missing route link.\n"); + break; + + #ifndef TEST // [[2do]] + case 1: + case 2: + case 3: + #endif + case 4: + // may have a route + break; + + default: + print_check_error(&chk,CMOD_WARNING,KMP_CAME,i,buf, + "Unknown camera type %u.\n",came->type); + } + + if ( came->next != 0xff && came->next >= ncame ) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,i,buf, + "Invalid next link to not existing index #%u (max=#%d).\n", + came->next, ncame-1 ); + } + + if (!came0count) + print_check_error(&chk,CMOD_HINT,KMP_CAME,-1,"Camera type 0", + "Missing a camera with type 0.\n" ); + else if ( came0count > 1 ) + print_check_error(&chk,CMOD_WARNING,KMP_CAME,-1,"Camera type 0", + "Only one camera with type 0 expected, but %u exist.\n", + came0count ); + + //--- check KTPT entries + + n = kmp->dlist[KMP_KTPT].used; + if (!n) + { + print_check_error(&chk,CMOD_WARNING,KMP_KTPT,-1,0, + "No start point defined => %s will freeze.\n", + chk.t_track ); + } + else if ( chk.is_arena >= ARENA_FOUND ) + { + u8 ktpt_usage[MKW_MAX_PLAYER]; + memset(ktpt_usage,0,sizeof(ktpt_usage)); + + const kmp_ktpt_entry_t *ktpt = (kmp_ktpt_entry_t*)kmp->dlist[KMP_KTPT].list; + uint i, fail = 0, m1 = 0; + for ( i = 0; i < n; i++, ktpt++ ) + { + const int idx = ktpt->player_index; + if ( idx >= 0 && idx < MKW_MAX_PLAYER ) + ktpt_usage[idx]++; + else if ( idx == -1 ) + m1++; + else + fail++; + } + + if (m1) + print_check_error(&chk,CMOD_HINT,KMP_KTPT,-1,0, + "Start point with player index -1 not used by battle arenas.\n"); + if (fail) + print_check_error(&chk,CMOD_WARNING,KMP_KTPT,-1,0, + "%u start position%s with wrong player index found.\n", + fail, fail==1 ? "" : "s" ); + + int n_missing = 0, n_multi = 0; + for ( i = 0; i < MKW_MAX_PLAYER; i++ ) + if (!ktpt_usage[i]) + n_missing++; + else if ( ktpt_usage[i] > 1 ) + n_multi++; + + DASSERT( sizeof(buf) > 3*MKW_MAX_PLAYER ); + if ( n_missing ) + { + char *dest = buf, sep = ' '; + for ( i = 0; i < MKW_MAX_PLAYER; i++ ) + if (!ktpt_usage[i]) + { + dest = snprintfE(dest,buf+sizeof(buf),"%c%u",sep,i); + sep = ','; + } + print_check_error(&chk,CMOD_WARNING,KMP_KTPT,-1,0, + "%u start position%s missed: player %s%s.\n", + n_missing, n_missing==1 ? "" : "s", + n_missing==1 ? "index" : "indices", buf ); + } + + if ( n_multi ) + { + char *dest = buf, sep = ' '; + for ( i = 0; i < MKW_MAX_PLAYER; i++ ) + if (ktpt_usage[i]>1) + { + dest = snprintfE(dest,buf+sizeof(buf),"%c%u",sep,i); + sep = ','; + } + print_check_error(&chk,CMOD_HINT,KMP_KTPT,-1,0, + "%u start position%s defined twice or more: player %s%s.\n", + n_multi, n_multi==1 ? "" : "s", + n_multi==1 ? "index" : "indices", buf ); + } + } + else if ( chk.is_arena == ARENA_NONE ) + { + kmp_finish_t kf; + CheckFinishLine(kmp,&kf); + + if ( kf.n_ktpt_m > 2 ) + print_check_error(&chk,CMOD_HINT,KMP_KTPT,-1,0, + "%u racing start points defined," + " but only the first 2 are used by racing tracks.\n", + kf.n_ktpt_m ); + + if ( kf.hint & 1 ) + print_check_error(&chk,CMOD_HINT|CMOD_LECODE,KMP_KTPT,-1,0, + "Direction of KTPT #%u and CKPT #%u differ by %3.1f°:" + " KTPT=%3.1f, CKPT=%3.1f°\n", + kf.ktpt, kf.ckpt, kf.dir_delta, kf.dir_ktpt, kf.dir_ckpt ); + else if ( kf.n_ktpt_m > 1 ) + print_check_error(&chk,CMOD_HINT,KMP_KTPT,-1,0, + "Second racing start point found: Distance to finish line:" + " %1.0f (%4.2f°)\n", + kf.distance, kf.dir_delta ); + + if ( kf.warn & 2 ) + print_check_error(&chk,CMOD_WARNING|CMOD_LECODE,KMP_KTPT,-1,0, + "Distance KTPT #%u and CKPT #%u too large: %3.1f\n", + kf.ktpt, kf.ckpt, kf.distance ); + else if ( kf.hint & 2 ) + print_check_error(&chk,CMOD_HINT|CMOD_LECODE,KMP_KTPT,-1,0, + "Distance KTPT #%u and CKPT #%u larger than usual: %3.1f\n", + kf.ktpt, kf.ckpt, kf.distance ); + + const uint max = kf.n_ktpt_m > 1 ? 2 : 1; + if ( n > max ) + print_check_error(&chk,CMOD_HINT,KMP_KTPT,-1,0, + "%u start points defined, but only %s used by racing tracks.\n", + n, max > 1 ? "first and second are" : "the first is" ); + } + + + //--- check *PH entries + + kmp_path_t ck_path; + InitializeKmpPath(&ck_path); + + check_ph(&chk,KMP_ENPH,KMP_ENPT,&ck_path); + check_ph(&chk,KMP_ITPH,KMP_ITPT,&ck_path); + check_ph(&chk,KMP_CKPH,KMP_CKPT,&ck_path); + + ResetKmpPath(&ck_path); + + + //--- check CKPT entries + + uint mode0count = 0, max_mode = 0; + memset(done,0,sizeof(done)); + + n = kmp->dlist[KMP_CKPT].used; + const kmp_ckpt_entry_t * ckpt = (kmp_ckpt_entry_t*)kmp->dlist[KMP_CKPT].list; + + if ( !n && chk.is_arena == ARENA_NONE ) + print_check_error(&chk,CMOD_WARNING,KMP_CKPT,-1,0, + "No elements found, but needed for racing tracks.\n" ); + + for ( i = 0; i < n; i++, ckpt++ ) + { + if ( ckpt->respawn < sizeof(jgpt_usage)/sizeof(*jgpt_usage) ) + jgpt_usage[ckpt->respawn]++; + + typeof(ckpt->mode) mode = ckpt->mode; + if ( !IS_M1(mode) && mode < sizeof(done)/sizeof(*done) ) + { + done[mode] = 1; + if ( max_mode < mode ) + max_mode = mode; + if (!mode) + mode0count++; + } + + uint link; + for ( link = 0; link < KMP_MAX_PH_LINK; link++ ) + { + int next = GetNextPointKMP(kmp,KMP_CKPT,i,link,true,-1,0,0,0,0); + if ( next >= 0 ) + { + noPRINT("CKPT: %3d.%d -> %3d\n",i,link,next); + double min_dist; + int stat = CheckConvexCKPT(kmp,i,next,&min_dist); + + if ( min_dist < KMP_DIST_WARN ) + { + print_check_error(&chk,CMOD_WARNING,KMP_CKPT,-1,0, + "Check points #%u and #%u are to near together (%5.3f)" + " (pos bug?).\n", + i, next, min_dist ); + continue; + } + + if ( min_dist < KMP_DIST_HINT ) + { + print_check_error(&chk,CMOD_HINT,KMP_CKPT,-1,0, + "Check points #%u and #%u are near together (%3.1f).\n", + i, next, min_dist ); + } + + if ( stat > 0 ) + { + print_check_error(&chk,CMOD_WARNING,KMP_CKPT,-1,0, + "Check points #%u and #%u do not form a convex quadrilateral" + " (pos bug?).\n", + i, next ); + } + } + } + } + + if ( !mode0count && chk.is_arena < ARENA_FOUND ) + print_check_error(&chk,CMOD_WARNING,KMP_CKPT,-1,0, + "No check point with mode 0 (lap counter).\n"); + else if ( mode0count > 1 && !kmp->multi_ckpt_mode_0_ok ) + print_check_error(&chk,CMOD_HINT,KMP_CKPT,-1,0, + "%u check points with mode 0 (lap counter).\n", + mode0count ); + + for ( i = 1; i <= max_mode; i++ ) + if (!done[i]) + { + const int start = i; + while ( ++i <= max_mode && !done[i] ) + ; + if ( --i == start ) + print_check_error(&chk,CMOD_HINT,KMP_CKPT,-1,0, + "Missing mandatory check point with mode %u.\n", + i ); + else + print_check_error(&chk,CMOD_HINT,KMP_CKPT,-1,0, + "Missing mandatory check points with modes %u..%u.\n", + start, i ); + } + + for ( i = njgpt; i < sizeof(jgpt_usage)/sizeof(*jgpt_usage); i++ ) + if (jgpt_usage[i]) + print_check_error(&chk,CMOD_WARNING,KMP_CKPT,-1,0, + "%u invalid link%s to not existing respawn point #%u (max=#%d).\n", + jgpt_usage[i], jgpt_usage[i]>1 ? "s" : "", i, njgpt-1 ); + + + //--- check JGPT entries + + if ( chk.is_arena < ARENA_FOUND ) + { + n = njgpt; + if ( n > sizeof(jgpt_usage)/sizeof(*jgpt_usage) ) + n = sizeof(jgpt_usage)/sizeof(*jgpt_usage); + const kmp_jgpt_entry_t * jgpt = (kmp_jgpt_entry_t*)kmp->dlist[KMP_CKPT].list; + for ( i = 0; i < n; i++, jgpt++ ) + if (!jgpt_usage[i]) + { + const int start = i; + while ( ++i < n && !jgpt_usage[i] ) + ; + if ( --i == start ) + print_check_error(&chk,CMOD_HINT,KMP_JGPT,-1,0, + "Respawn point #%u of %u%s not used/linked.\n", + i, n, print_name(kmp,KMP_JGPT,i) ); + else + { + ccp t1 = print_name(kmp,KMP_JGPT,start); + ccp t2 = print_name(kmp,KMP_JGPT,i); + if ( *t1 && *t2 ) + { + t1++; + t2++; + print_check_error(&chk,CMOD_HINT,KMP_JGPT,-1,0, + "Respawn points #%u..#%u of %u (%s..%s) not used/linked.\n", + start, i, n, t1, t2 ); + } + else + print_check_error(&chk,CMOD_HINT,KMP_JGPT,-1,0, + "Respawn points #%u..#%u of %u not used/linked.\n", + start, i, n ); + } + } + } + + if (kmp->jgpt_flag.import) + print_check_error(&chk,CMOD_HINT,KMP_JGPT,-1,0, + "Exported flags detected (extension of tool wkmpt).\n"); + + + //--- check GOBJ entries & count COIN usage + + uint n_obj_0 = 0; + uint n_pitem = 0, n_pitem_unsure = 0; + uint n_eitem = 0, n_eitem_unsure = 0; + uint n_coin_start = 0, n_coin_respawn = 0, n_coin_invalid = 0; + uint n_coin_type[4] = {0}; + + n = kmp->dlist[KMP_GOBJ].used; + if ( n >= 200 ) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "%u global object%s defined.\n", + n, n==1 ? "" : "s" ); + + const kmp_gobj_entry_t * gobj = (kmp_gobj_entry_t*)kmp->dlist[KMP_GOBJ].list; + for ( i = 0; i < n; i++, gobj++ ) + { + uint id = gobj->obj_id; + if (!IsDefinitionObjectId(id)) + { + id &= GOBJ_M_OBJECT; + if ( id >= N_KMP_GOBJ || !ObjectInfo[id].name ) + { + if (!id) + n_obj_0++; + else + print_check_error(&chk,CMOD_WARNING,KMP_GOBJ,i,0, + "Unknown object with ID 0x%x.\n",id); + continue; + } + + if ( id == GOBJ_COIN ) + { + const u16 set2 = gobj->setting[1]; + if ( set2 == M1(set2) ) + n_coin_respawn++; + else if ( set2 < KMP_COIN_IS_INVALID ) + n_coin_start++; + else + n_coin_invalid++; + + const u16 set3 = gobj->setting[2]; + n_coin_type[ set3 < 3 ? set3 : 3 ]++; + + } + + const ObjectInfo_t *oi = ObjectInfo + id; + const typeof(gobj->route_id) route = gobj->route_id; + if (IS_M1(route)) + { + if ( oi->flags & OBF_ALWAYS_ROUTE ) + print_check_error(&chk,CMOD_WARNING,KMP_GOBJ,i,oi->name, + "Missing route link.\n"); + } + else + { + if ( route >= npoti ) + print_check_error(&chk,CMOD_WARNING,KMP_GOBJ,i,oi->name, + "Invalid link to not existing route #%u (max=#%d).\n", + route, npoti-1 ); + + if ( oi->flags & OBF_NO_ROUTE ) + print_check_error(&chk,CMOD_WARNING,KMP_GOBJ,i,oi->name, + "Link to route #%u not needed.\n",route); + + if ( route < route_usage_size ) + route_usage[route] |= RU_GOBJ; + } + + Itembox_t ibox; + if (AnalyseItembox(&ibox,gobj)) + { + if ( ibox.item_player ) + { + n_pitem++; + if ( ibox.unsure_player ) + n_pitem_unsure++; + } + + if ( ibox.item_enemy ) + { + n_eitem++; + if ( ibox.unsure_enemy ) + n_eitem_unsure++; + } + } + } // endif !IsDefinitionObjectId() + } + + if ( n_obj_0 > 0 ) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "%u object%s with ID 0 found.\n", + n_obj_0, n_obj_0 > 1 ? "s" : "" ); + + print_item_hint(&chk,n_pitem,n_pitem_unsure,"players"); + print_item_hint(&chk,n_eitem,n_eitem_unsure,"enemies"); + + + kmp_special_t special = {0}; + DetectSpecialKMP(kmp,special); + for ( i = 0; i < HAVEKMP__N; i++ ) + if (special[i]) + { + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "%u special object%s: %s.\n", + special[i], special[i] == 1 ? "" : "s", have_kmp_info[i].info ); +// i += have_kmp_info[i].skip; + } + + //--- COIN warnings + + const uint n_coin = n_coin_start + n_coin_respawn + n_coin_invalid; + if ( chk.is_arena == ARENA_NONE && n_coin ) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "%u coin%s found, but coins ignored by racing tracks.\n", + n_coin, n_coin==1 ? "" : "s" ); + + if ( chk.is_arena >= ARENA_FOUND ) + { + if (!n_coin) + print_check_error(&chk,CMOD_WARNING,KMP_GOBJ,-1,0, + "No coins found, but needed for battle arenas.\n" ); + else + { + if (!n_coin_start) + print_check_error(&chk,CMOD_WARNING,KMP_GOBJ,-1,0, + "No start places for coins found, but needed for battle arenas.\n" ); + else if ( n_coin_start < 20 ) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "Only %u starting place%s for coins (<20).\n", + n_coin_start, n_coin_start==1 ? "" : "s" ); + + if (!n_coin_respawn) + print_check_error(&chk,CMOD_WARNING,KMP_GOBJ,-1,0, + "No respawn places for coins found, but needed for battle arenas.\n" ); + else if ( n_coin_respawn < 20 ) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "Only %u respawn place%s for coins (<20).\n", + n_coin_respawn, n_coin_respawn==1 ? "" : "s" ); + } + } + + if (n_coin_invalid) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "%u coin%s with invalid index found.\n", + n_coin_invalid, n_coin_invalid==1 ? "" : "s" ); + + if ( n_coin_start || n_coin_respawn ) + print_check_error(&chk,CMOD_INFO,KMP_GOBJ,-1,0, + "Statistics about valid coin places:" + " %u start place%s + %u respawn place%s.\n", + n_coin_start, n_coin_start==1 ? "" : "s", + n_coin_respawn, n_coin_respawn==1 ? "" : "s" ); + + if (n_coin_type[3]) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "%u coin%s with invalid respawn types invalid found.\n", + n_coin_type[3], n_coin_type[3]==1 ? "" : "s" ); + + if ( n_coin_type[0] || n_coin_type[1] || n_coin_type[2] ) + print_check_error(&chk,CMOD_INFO,KMP_GOBJ,-1,0, + "Statistics about valid coin respawn types (0,1,2): %d + %d + %d.\n", + n_coin_type[0], n_coin_type[1], n_coin_type[2] ); + + + //--- check presence flags + + kmp_pflags_t pf; + CountPresenceFlags(kmp,&pf); + for ( i = 1; i < sizeof(pf.n_mode)/sizeof(*pf.n_mode); i++ ) + if (pf.n_mode[i]) + print_check_error(&chk,CMOD_HINT,KMP_GOBJ,-1,0, + "Extended presence flags: %u object%s with mode %u.\n", + pf.n_mode[i], pf.n_mode[i] == 1 ? "" : "s", i ); + + + //--- check POTI entries + + uint ng = kmp->dlist[KMP_POTI].used; + const kmp_poti_group_t * pg = (kmp_poti_group_t*)kmp->dlist[KMP_POTI].list; + uint np = kmp->poti_point.used; + const kmp_poti_point_t * pp = (kmp_poti_point_t*)kmp->poti_point.list; + + n = ng < route_usage_size ? ng : route_usage_size; + uint gi; + for ( gi = 0; gi < n; gi++ ) + { + if (!route_usage[gi]) + { + const int start = gi; + while ( ++gi < n && !route_usage[gi] ) + ; + if ( --gi == start ) + print_check_error(&chk,CMOD_HINT,KMP_POTI,-1,0, + "Route #%u of %u%s not used/linked.\n", + gi, ng, print_name(kmp,KMP_POTI,gi) ); + else + { + ccp t1 = print_name(kmp,KMP_POTI,start); + ccp t2 = print_name(kmp,KMP_POTI,gi); + if ( *t1 && *t2 ) + { + t1++; + t2++; + print_check_error(&chk,CMOD_HINT,KMP_POTI,-1,0, + "Routes #%u..#%u of %u (%s..%s) not used/linked.\n", + start, gi, ng, t1, t2 ); + } + else + print_check_error(&chk,CMOD_HINT,KMP_POTI,-1,0, + "Routes #%u..#%u of %u not used/linked.\n", + start, gi, ng ); + } + } + else if ( route_usage[gi] == (RU_CAME|RU_GOBJ) ) + print_check_error(&chk,CMOD_WARNING,KMP_POTI,-1,0, + "Route #%u of %u is linked by CAME and GOBJ (may force a freeze).\n", + gi,ng); + } + + for ( gi = i = 0; gi < ng && i < np; gi++, pg++ ) + { + uint en = pg->n_point; + DASSERT( en <= np-i ); + + const bool used_by_came = gi < route_usage_size && route_usage[gi] & RU_CAME; + uint count = 0; + uint ei; + for ( ei = 0; ei < en; ei++, i++, pp++ ) + if ( ei < en-1 && !pp->speed && used_by_came ) + count++; + if (count) + print_check_error(&chk,CMOD_HINT,KMP_POTI,gi,0, + "CAME route: %u route point%s of %u with zero speed.\n", + count, count == 1 ? "" : "s", en ); + } + + + //--- check MSPT entries + + n = kmp->dlist[KMP_MSPT].used; + + if ( chk.is_arena >= ARENA_FOUND ) + { + if (!n) + print_check_error(&chk,CMOD_WARNING,KMP_MSPT,-1,0, + "No element defined, but mandatory for battle arenas.\n"); + else if ( n != 4 ) + print_check_error(&chk, + n < 4 ? CMOD_WARNING : CMOD_HINT, KMP_MSPT,-1,0, + "%u element%s defined, but 4 needed for battle arenas.\n", + n, n==1 ? "" : "s" ); + } + else if ( chk.is_arena == ARENA_NONE && n ) + print_check_error(&chk,CMOD_HINT,KMP_MSPT,-1,0, + "%u element%s defined, but not used by racing tracks.\n", + n, n==1 ? "" : "s" ); + + + //--- check STGI entries + + n = kmp->dlist[KMP_STGI].used; + if (!n) + print_check_error(&chk,CMOD_WARNING,KMP_STGI,-1,0, + "No element defined.\n"); + else if (n>1) + print_check_error(&chk,CMOD_WARNING,KMP_STGI,-1,0, + "%u elements defined.\n", n ); + else + { + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp->dlist[KMP_STGI].list; + + if ( stgi->lap_count != 3 ) + print_check_error(&chk,CMOD_HINT,KMP_STGI,0,"STGI", + "Lap counter changed to %u.\n",stgi->lap_count); + else if ( mode & CMOD_INFO ) + print_check_error(&chk,CMOD_INFO,KMP_STGI,0,"STGI", + "Lap counter is to %u.\n",stgi->lap_count); + + if ( stgi->pole_pos > 1 ) + print_check_error(&chk,CMOD_WARNING,KMP_STGI,0,"STGI", + "Undefined value for pole positions: %u\n",stgi->pole_pos); + else if ( mode & CMOD_INFO ) + print_check_error(&chk,CMOD_INFO,KMP_STGI,0,"STGI", + "Pole position is '%s' (%u).\n", + stgi->pole_pos ? "right" : "left", stgi->pole_pos ); + + if ( stgi->narrow_start > 1 ) + print_check_error(&chk,CMOD_WARNING,KMP_STGI,0,"STGI", + "Undefined value for narrow mode: %u\n",stgi->narrow_start); + else if ( mode & CMOD_INFO ) + print_check_error(&chk,CMOD_INFO,KMP_STGI,0,"STGI", + "Start mode is '%s' (%u).\n", + stgi->narrow_start ? "narrow" : "wide", stgi->narrow_start ); + + if ( stgi->speed_mod ) + print_check_error(&chk,CMOD_HINT,KMP_STGI,0,"STGI", + "Speed modifier is set to %5.3f (%04x/hex).\n", + SpeedMod2float(stgi->speed_mod), stgi->speed_mod); + else if ( mode & CMOD_INFO ) + print_check_error(&chk,CMOD_INFO,KMP_STGI,0,"STGI", + "No speed modifier set (0000/hex).\n" ); + } + + + //--- check used positions + + kmp_usedpos_t up; + CheckUsedPos(kmp,&up); + + bool lex_hint = false; + for ( i = 0; i < 3; i++ ) + { + if ( up.orig.rating[i] < WLEVEL_HINT ) + continue; + + lex_hint = true; + ccp range_info = i == 1 ? "1..131071" : "±131071"; + + if ( up.orig.rating[i] >= WLEVEL_ERROR ) + print_check_error(&chk,CMOD_WARNING, KMP_N_SECT,-1,0, + "At least 1 %c coordinate of ENPT/ITPT" + " is outside online limit of %s. Used range: %1.0f..%1.0f\n", + 'X'+i, range_info, up.orig.min.v[i], up.orig.max.v[i] ); + else if ( up.orig.rating[i] >= WLEVEL_WARN ) + print_check_error(&chk,CMOD_WARNING, KMP_N_SECT,-1,0, + "At least 1 %c coordinate of ENPT/ITPT" + " is near online limit of %s. Used range: %1.0f..%1.0f\n", + 'X'+i, range_info, up.orig.min.v[i], up.orig.max.v[i] ); + else if ( up.orig.rating[i] >= WLEVEL_HINT ) + print_check_error(&chk,CMOD_HINT, KMP_N_SECT,-1,0, + "At least 1 %c coordinate of ENPT/ITPT" + " is near online limit of %s. Used range: %1.0f..%1.0f\n", + 'X'+i, range_info, up.orig.min.v[i], up.orig.max.v[i] ); + } + + if (lex_hint) + { + const kmp_usedpos_obj_t *obj = up.suggest; + if ( obj->shift_active && obj->pos_factor_valid ) + { + fprintf(stdlog,"%s%s%s" + "Shift the whole track by --shift %1.0f,%1.0f,%1.0f" + " and setup LEX/SET1 with @ITEM-POS-FACTOR=v(%4.2f,%4.2f,%4.2f)\n", + chk.col.info, check_bosugl, chk.col.reset, + obj->shift.x, obj->shift.y, obj->shift.z, + obj->pos_factor.x, obj->pos_factor.y, obj->pos_factor.z ); + if ( obj != &up.orig && up.orig.pos_factor_valid ) + fprintf(stdlog,"%s%s%s" + "... or setup LEX/SET1 only with" + " @ITEM-POS-FACTOR=v(%4.2f,%4.2f,%4.2f)\n", + chk.col.info, check_bosugl, chk.col.reset, + up.orig.pos_factor.x, up.orig.pos_factor.y, up.orig.pos_factor.z ); + } + else if (obj->shift_active) + fprintf(stdlog,"%s%s%s" + "Shift the whole track by --shift %1.0f,%1.0f,%1.0f\n", + chk.col.info, check_bosugl, chk.col.reset, + obj->shift.x, obj->shift.y, obj->shift.z ); + else if (obj->pos_factor_valid) + fprintf(stdlog,"%s%s%s" + "Setup LEX/SET1 with @ITEM-POS-FACTOR=v(%4.2f,%4.2f,%4.2f)\n", + chk.col.info, check_bosugl, chk.col.reset, + obj->pos_factor.x, obj->pos_factor.y, obj->pos_factor.z ); + } + + + //--- check existence of WIM0 + + if (kmp->wim0_import_size) + { + char *dest = buf, *end = buf+sizeof(buf) - 2, *sep = " ("; + const kmp_wim0_sect_t *sect = FindSectionWim0(0,kmp,W0ID_VERSION); + if (sect) + { + dest = snprintfE(dest,end,"%sv%s",sep,(ccp)sect->data); + sep = ","; + } + if (kmp->wim0_import_bz2) + dest = snprintfE(dest,end,"%sbzip2",sep); + + if ( dest > buf ) + { + *dest++ = ')'; + *dest = 0; + } + else + *buf = 0; + + print_check_error(&chk,CMOD_HINT,KMP_WIM0,-1,0, + "KMP section WIM0 with %u bytes total%s detected.\n", + kmp->wim0_import_size, buf ); + } + + + //--- terminate + + if ( mode & CMOD_GLOBAL_STAT ) + { + global_warn_count += chk.warn_count; + global_hint_count += chk.hint_count; + global_info_count += chk.info_count; + } + + if ( mode & (CMOD_FOOTER|CMOD_FORCE_FOOTER) ) + { + static ccp hint + = " => see https://szs.wiimm.de/cmd/wkmpt/check#desc for more info."; + + ccp bind; + char hbuf[60], ibuf[60]; + if (chk.info_count) + { + snprintf(ibuf,sizeof(ibuf)," and %s%u info%s%s", + chk.col.info, + chk.info_count, chk.info_count>1 ? "s" : "", + chk.col.heading ); + bind = ","; + } + else + { + *ibuf = 0; + bind = " and"; + } + + if (chk.hint_count) + snprintf(hbuf,sizeof(hbuf),"%s %s%u hint%s%s", + bind, chk.col.hint, + chk.hint_count, chk.hint_count>1 ? "s" : "", + chk.col.heading ); + else + *hbuf = 0; + + if (chk.warn_count) + fprintf(stdlog, + " %s=> %s%u warning%s%s%s%s for %s:%s\n%s%s%s\n\n", + chk.col.heading, chk.col.warn, + chk.warn_count, chk.warn_count == 1 ? "" : "s", + chk.col.heading, + hbuf, ibuf, GetNameFF(kmp->fform,0), kmp->fname, + chk.col.heading, hint, chk.col.reset ); + else if ( mode & CMOD_FORCE_FOOTER || chk.hint_count ) + fprintf(stdlog," %s=> No warnings%s%s for %s:%s\n%s%s%s\n\n", + chk.col.heading, hbuf, ibuf, GetNameFF(kmp->fform,0), kmp->fname, + chk.col.heading, hint, chk.col.reset ); + } + + return chk.warn_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command stgi /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_stgi() +{ + stdlog = stderr; + raw_data_t raw; + InitializeRawData(&raw); + + if (print_header) + printf("\n start pos speed laps LC : file\n%.*s\n", + 79, Minus300 ); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,"/course.kmp",opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + if ( opt_ignore && raw.fform != FF_KMP && raw.fform != FF_KMP_TXT ) + continue; + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,CMOD_SILENT); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if ( max_err < err ) + max_err = err; + continue; + } + + uint lap_count = 0; + const uint n_ckpt = kmp.dlist[KMP_CKPT].used; + if (n_ckpt) + { + const kmp_ckpt_entry_t *ckpt = (kmp_ckpt_entry_t*)kmp.dlist[KMP_CKPT].list; + uint i; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + if (!ckpt->mode) + lap_count++; + } + + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.dlist[KMP_STGI].list; + const uint n_stgi = kmp.dlist[KMP_STGI].used; + if (!n_stgi) + printf("%s no STGI found -%s", colout->warn, colout->reset ); + else if ( opt_ignore > 1 + && lap_count == 1 + && stgi->pole_pos < 2 + && stgi->narrow_start < 2 + && !stgi->speed_mod + && stgi->lap_count == 3 + ) + { + ResetKMP(&kmp); + continue; + } + else + { + if ( stgi->pole_pos == 0 ) + fputs(" left ",stdout); + else if ( stgi->pole_pos == 1 ) + fputs(" right",stdout); + else + printf("%s%5u%s ", colout->warn, stgi->pole_pos, colout->reset); + + if ( stgi->narrow_start == 0 ) + fputs(" wide ",stdout); + else if ( stgi->narrow_start == 1 ) + fputs(" narrow",stdout); + else + printf("%s%5u%s ", colout->warn, stgi->narrow_start, colout->reset ); + + if (stgi->speed_mod) + { + float speed = SpeedMod2float(stgi->speed_mod); + printf("%s %4.2f%s", + speed == 1.0 ? "" : speed <= 1.0 ? colout->info : colout->hint, + speed, colout->reset ); + } + else + fputs(" -- ",stdout); + + printf(" %s%3u%s", + stgi->lap_count == 3 ? "" + : stgi->lap_count < 3 ? colout->info : colout->hint, + stgi->lap_count, colout->reset ); + } + printf(" %s%3u%s : %s\n", + lap_count == 1 ? "" : colout->hint, + lap_count, colout->reset, + param->arg ); + ResetKMP(&kmp); + } + if (print_header) + printf( "%.*s\n\n", 79, Minus300 ); + + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command ktpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_ktpt() +{ + stdlog = stderr; + putchar('\n'); + + char sep[30]; + StringCat3S(sep,sizeof(sep),colout->heading,"│",colout->reset); + + raw_data_t raw; + InitializeRawData(&raw); + + kmp_t kmp; + InitializeKMP(&kmp); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,"/course.kmp",opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + if ( opt_ignore && raw.fform != FF_KMP && raw.fform != FF_KMP_TXT ) + continue; + + ResetKMP(&kmp); + err = ScanRawDataKMP(&kmp,false,&raw,CMOD_SILENT); + if ( err > ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + kmp_finish_t kf; + CheckFinishLine(&kmp,&kf); + if (!kf.valid) + { + if (opt_ignore<1) + { + if (kf.ckpt<0) + ERROR0(ERR_WARNING, + "No relevant lap counter (CKPT) found: %s\n",param->arg); + else if (kf.ktpt<0) + ERROR0(ERR_WARNING, + "No relevant start points (KTPT) found: %s\n",param->arg); + } + continue; + } + + printf( "%s> Path and file name: %s%s\n" + "Number of start points (KTPT): %3u, relevant #%u\n" + "Number of lap counters (CKPT): %3u, relevant #%u\n" + "Relevant start point (pos/dir):%13.3f %11.3f %11.3f /%8.2f°\n" + ,colout->caption + ,param->arg + ,colout->reset + ,kf.n_ktpt_m, kf.ktpt + ,kf.n_ckpt_0, kf.ckpt + ,kf.pos_ktpt.x, kf.pos_ktpt.z, kf.pos_ktpt.z, kf.dir_ktpt + ); + + if ( kf.ckpt_opt >= 0 && ( kf.ckpt_opt != kf.ckpt || kf.hint ) ) + printf( + "%sOptimized start point (pos/dir):%12.3f %11.3f %11.3f /%8.2f°%s\n" + ,colout->hint + ,kf.pos_ktpt_opt.x, kf.pos_ktpt_opt.z, kf.pos_ktpt_opt.z + ,kf.dir_ktpt_opt + ,colout->reset + ); + + printf( "Relevant check point (pos/dir): %12.3f %23.3f /%8.2f°\n" + "Distance between both points: %s%14.3f%s%25s/%s%8.2f°%s\n" + ,kf.pos_ckpt.x, kf.pos_ckpt.z, kf.dir_ckpt + ,kf.hint & 2 ? colout->warn : "", kf.distance ,colout->reset ,"" + ,kf.hint & 1 ? colout->warn : "", kf.dir_delta ,colout->reset + ); + + if ( kf.enpt_rec >= 0 ) + printf("\n" + "%sRecommendation based on nearest ENPT #%d:%s\n" + " Recommended KTPT record: " + "1 %11.3f %11.3f %11.3f 0 %6.2f 0 -1 0\n" + " Recommended patch option: --ktpt2=%1.0f,%1.0f\n" + ,colout->hint,kf.enpt_rec,colout->reset + ,kf.pos_ktpt_rec.x, kf.pos_ktpt_rec.y, kf.pos_ktpt_rec.z + ,kf.dir_ktpt_rec + ,kf.pos_ktpt_rec.x, kf.pos_ktpt_rec.z + ); + + if ( long_count > 0 ) + { + const int fw1 = 3 * 31; + const int fw2 = 3 * 24; + const int fw3 = 3 * 16; + const int fw4 = 3 * 16; + + printf( + "\n" + "%sCompare all relevant KTPT with all lap counters:%s\n" + "%s┌%.*s┬%.*s┬%.*s┬%.*s┐\n" + "%s│ Start Position (KTPT) │ Lap Counter (CKPT) " + "│ KTPT−CKPT │ Parameter for │\n" + "%s│ idx x-pos y-pos z-pos ° │ idx x-pos z-pos ° " + "│ distance ° │ option --ktpt2 │%s\n", + colout->heading, colout->reset, + colout->heading, + fw1, ThinLine300_3, fw2, ThinLine300_3, + fw3, ThinLine300_3, fw4, ThinLine300_3, + colout->heading, colout->heading, colout->reset ); + + const uint nc = kmp.dlist[KMP_CKPT].used; + const uint nk = kmp.dlist[KMP_KTPT].used; + const kmp_ktpt_entry_t *ktpt + = (kmp_ktpt_entry_t*)kmp.dlist[KMP_KTPT].list; + + uint ik; + for ( ik = 0; ik < nk; ik++, ktpt++ ) + { + if ( ktpt->player_index >= 0 ) + continue; + + const kmp_ckpt_entry_t *ckpt + = (kmp_ckpt_entry_t*)kmp.dlist[KMP_CKPT].list; + + if ( !ik || kf.n_ckpt_0 > 1 ) + printf("%s├%.*s┼%.*s┼%.*s┼%.*s┤%s\n", + colout->heading, + fw1, ThinLine300_3, fw2, ThinLine300_3, + fw3, ThinLine300_3, fw4, ThinLine300_3, + colout->reset ); + + uint ic; + for ( ic = 0; ic < nc; ic++, ckpt++ ) + { + if (ckpt->mode) + continue; + + CalcFinishLine(&kmp,&kf,ckpt,ktpt); + + char opt_buf[20], *opt = "-"; + if ( kf.enpt_rec >= 0 ) + { + snprintf(opt_buf,sizeof(opt_buf),"%1.0f,%1.0f", + kf.pos_ktpt_rec.x, kf.pos_ktpt_rec.z ); + opt = opt_buf; + } + + printf( + "%s%3u %7.0f %6.0f %7.0f %3.0f " + "%s%3u %7.0f %7.0f %3.0f %s %s%6.0f %s%7.2f %s%s %-15s%s\n", + sep, ik, + kf.pos_ktpt.x, kf.pos_ktpt.y, kf.pos_ktpt.z, kf.dir_ktpt, + sep, ic, + kf.pos_ckpt.x, kf.pos_ckpt.z, kf.dir_ckpt, + sep, + kf.warn & 2 ? colout->warn + : kf.hint & 2 ? colout->hint : "", + kf.distance, + kf.hint & 1 ? colout->warn + : kf.hint & 1 ? colout->hint : colout->reset, + kf.dir_delta, + sep, + ik == kf.ktpt && ic == kf.ckpt ? colout->hint : "", + opt, sep ); + } + } + + printf("%s└%.*s┴%.*s┴%.*s┴%.*s┘%s\n", + colout->heading, + fw1, ThinLine300_3, fw2, ThinLine300_3, + fw3, ThinLine300_3, fw4, ThinLine300_3, + colout->reset ); + } + putchar('\n'); + } + + ResetKMP(&kmp); + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-kmp.h b/project/src/lib-kmp.h new file mode 100644 index 0000000..c3b583c --- /dev/null +++ b/project/src/lib-kmp.h @@ -0,0 +1,2846 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// all about BMG files: +// http://wiki.tockdom.de/index.php?title=BMG + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_KMP_H +#define SZS_LIB_KMP_H 1 + +#include "lib-std.h" +#include "lib-object.h" +#include "lib-lecode.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define KMP_MAGIC "RKMD" +#define KMP_MAGIC_NUM 0x524B4D44 + +#define KMP_TEXT_MAGIC "#KMP" +#define KMP_TEXT_MAGIC_NUM 0x234b4d50 + +#define KMP_MAX_PH_LINK 6 +#define KMP_MAX_GROUP 255 +#define KMP_MAX_POINT 255 +#define KMP_MAX_NAME_LEN 20 + +#define KMP_DIST_HINT 10.0 +#define KMP_DIST_WARN 1.0 + +#define GOBJ_ELINE_CONTROL 0x014 +#define GOBJ_ICE 0x017 +#define GOBJ_ITEMBOX 0x065 +#define GOBJ_SUN_DS 0x072 +#define GOBJ_COIN 0x073 +#define GOBJ_HEYHO_SHIP 0x0ce +#define GOBJ_HEYHO_BALL 0x0ea +#define GOBJ_OBAKE_BLOCK 0x13c +#define GOBJ_PYLON01 0x144 + +#define KMP_COIN_IS_INVALID 50 + +//----------------------------------------------------------------------------- +// +// XPF ID map: +// +// 0000-03ff : Real objects +// 1000-13ff : Disabled objects +// +// 1000-1fff : reserved for predifined conditions +// 1000-102d : hard coded conditions +// 1100-18ff : >FREE (8 maps of 8 bits) +// 1900-1903 : developer conditions +// 1a00-1dff : >FREE (4 maps of 8 bits) +// 1e00-1e7f : engine classes +// 1f00-1fff : random scenarios +// +// 2000-3fff : Definition objects type BITS +// 4000-5fff : Definition objects type OR +// 6000-7fff : Definition objects type AND +// +// 8000-dfff : >FREE (6 maps of 12 bits) +// +// e000-e3ff : test conditions (debug version only) +// +// f000-ffff : >FREE (1 map of 12 bits) +// +//----------------------------------------------------------------------------- + +// KMP/GOBJ/obj_id +#define GOBJ_M_OBJECT 0x03ff +#define GOBJ_M_FLAGS ( 0xffff & ~GOBJ_M_OBJECT ) + +#define GOBJ_BIT_LECODE 12 // maybe enabled by lecode +#define GOBJ_BIT_CONDITION 12 // indicator bit for predefinied condition + +#define GOBJ_M_LECODE (1< POTI + /*0x04*/ u16 came_speed; // unsure! + /*0x06*/ u16 zoom_speed; // zoom speed in units per 100/60 sec + /*0x08*/ u16 viewpt_speed; // view point speed in units per 100/60 sec + /*0x0a*/ u16 unknown_0x0a; + /*0x0c*/ float32 position[3]; // camera position + /*0x18*/ float32 rotation[3]; // rotation? (always 0) + /*0x24*/ float32 zoom_begin; // zoom value begin + /*0x28*/ float32 zoom_end; // zoom value end + /*0x2c*/ float32 viewpt_begin[3];// view point begin + /*0x38*/ float32 viewpt_end[3]; // view point end + /*0x44*/ float32 time; // active time of the camera + /*0x48*/ +} +__attribute__ ((packed)) kmp_came_entry_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_jgpt_entry_t]] + +typedef struct kmp_jgpt_entry_t +{ + // Jugem points: Respawn positions + + /*0x00*/ float32 position[3]; + /*0x0c*/ float32 rotation[3]; + /*0x18*/ s16 id; + /*0x1a*/ u16 effect; + /*0x1c*/ +} +__attribute__ ((packed)) kmp_jgpt_entry_t; + +// [[kmp_cnpt_entry_t]] Cannon Points: Cannon target positions +typedef kmp_jgpt_entry_t kmp_cnpt_entry_t; + +// [[kmp_mspt_entry_t]] +// After battles and competitions have ended the players are placed on this point(s) +typedef kmp_jgpt_entry_t kmp_mspt_entry_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_stgi_entry_t]] + +typedef struct kmp_stgi_entry_t +{ + // Stage Information + + /*0x00*/ u8 lap_count; // XML suggest but ignored + /*0x01*/ u8 pole_pos; // 0:left, 1:right + /*0x02*/ u8 narrow_start; // 0:normal, 1:closer together + /*0x03*/ u8 enable_lens_flare; // 0:off, 1:enabled + /*0x04*/ u32 flare_color; // 0xe6e6e6 or 0xffffff + /*0x08*/ u8 flare_alpha; // alpha channel (transparency) + /*0x09*/ u8 unknown_09; // always 0 + /*0x0a*/ u16 speed_mod; // nintendo: always 0 + // custom: MSB of speed modifier + /*0x0c*/ +} +__attribute__ ((packed)) kmp_stgi_entry_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Wim0 support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_wim0_sect_id]] + +typedef enum kmp_wim0_sect_id +{ + W0ID_END = 0, + W0ID_VERSION = 'V' << 8 | 'e', +// W0ID_AUTO_CONNECT = 'A' << 8 | 'C', +} +kmp_wim0_sect_id; + +extern const KeywordTab_t Wim0SectionName[]; + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_wim0_format_t]] + +typedef enum kmp_wim0_format_t +{ + W0F_UNKNOWN = 0, + W0F_BINARY, + W0F_TEXT, + W0F__N +} +kmp_wim0_format_t; + +extern ccp Wim0FormatName[]; + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_wim0_t]] + +typedef struct kmp_wim0_t +{ + u8 *data; // uncompressed data, alloced + uint used; // number of used bytes + uint size; // number of alloced bytes + + u8 *bz2data; // NULL or bzip2 data, alloced + uint bz2size; // size of bz2data +} +__attribute__ ((packed)) kmp_wim0_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_wim0_sect_t]] + +typedef struct kmp_wim0_sect_t +{ + u16 id; // one of kmp_wim0_sect_id + u16 words; // number of words (2=bytes) of data + u16 data[]; // data, always ALIGN(2) +} +__attribute__ ((packed)) kmp_wim0_sect_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_wim0_info_t]] + +typedef struct kmp_wim0_info_t +{ + kmp_wim0_sect_id sect_id; // one of kmp_wim0_sect_id + uint index; // 0..N: Index of section + uint offset; // Offset relative to first WIM0 section + char name[8]; // either key->name1 or "0x####" + kmp_wim0_format_t format; // format of data, fallback to W0F_UNKNOWN + + const + kmp_wim0_sect_t *sect; // pointer to section + const u8 *data; // pointer to section data + uint size; // size of section data + +} +__attribute__ ((packed)) kmp_wim0_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeWim0 ( kmp_wim0_t *w0 ); +void ResetWim0 ( kmp_wim0_t *w0 ); +void * NeedWim0 ( kmp_wim0_t *w0, uint size, uint align ); +enumError CompressWim0 ( kmp_wim0_t *w0 ); +enumError DecompressWim0 ( kmp_wim0_t *w0 ); + +kmp_wim0_sect_t * AppendSectionWim0 +( + kmp_wim0_t *w0, // data structure + kmp_wim0_sect_id sect_id, // section id + cvp data, // pointer to data + uint size // size of data in bytes +); + +bool GetFirstSectionWim0 +( + kmp_wim0_info_t *info, // store result here (never NULL) + const struct kmp_t *kmp // pointer to valid KMP +); + +bool GetNextSectionWim0 +( + kmp_wim0_info_t *info, // update result (never NULL) + const struct kmp_t *kmp // pointer to valid KMP +); + +const kmp_wim0_sect_t * FindSectionWim0 +( + // return NULL or a pointer to found kmp_wim0_sect_t + + kmp_wim0_info_t *info, // not NULL: store extended result here + const struct kmp_t *kmp, // pointer to valid KMP + kmp_wim0_sect_id sect_id // section id to search +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_section_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_section_t]] + +typedef struct kmp_section_t +{ + int sect; // -1: invalid, -2:unknown, else: kmp_entry_t + uint index; // index of analyse section + kmp_list_head_t *ptr; // pointer to section + uint size; // size of section +} +kmp_section_t; + +//----------------------------------------------------------------------------- + +bool AnalyseSectionKMP +( + // return true if known section found + + kmp_section_t *ks, // store result here + const void *data, // KMP raw data to scan + uint size, // size of 'data' + uint index // index of section to analyse +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// route types /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_rtype_mode]] + +typedef enum kmp_rtype_mode +{ + KMP_RT_AUTO, // route type detected automatically + KMP_RT_ROUTE, // is real route + KMP_RT_DISPATCH, // is dispatch point + +} +__attribute__ ((packed)) kmp_rtype_mode; + +extern const KeywordTab_t kmp_rtype_name[]; + +//----------------------------------------------------------------------------- +// [[kmp_rtype_info_t]] + +typedef struct kmp_rtype_info_t +{ + // section type + uint sect_ph; // PH section index + uint sect_pt; // PT section index + + // basic stats + uint n_group; // number of route groups + uint n_point; // number of route points + + // analysis + uint n_route0; // number of routes with 0 points (ERROR) + uint n_route1; // number of real routes with only one points + uint n_route2; // number of real routes with >1 points + uint n_dispatch; // number of dispatch points (=not real route) +} +kmp_rtype_info_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_group_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_group_name_t]] + +typedef char kmp_group_name_t[KMP_MAX_NAME_LEN+1]; + +//----------------------------------------------------------------------------- +// [[kmp_gopt_t]] + +typedef struct kmp_gopt_t +{ + u8 rclass; // route class (for incomming connections) + u8 lclass; // link class (for outgoing connections) + u8 min_links; // minimal links to auto-add + u8 max_links; // maxmal links to auto-add + double distance; // if >0: max distance for N>min +} +kmp_gopt_t; + +//----------------------------------------------------------------------------- +// [[kmp_gopt2_t]] + +typedef struct kmp_gopt2_t +{ + kmp_rtype_mode rtype_user; // route type set by user + kmp_rtype_mode rtype_active; // active route type + kmp_oneway_mode ac_oneway; // oneway mode for auto-connect + u8 setting[2]; // values of bytes at offeset 0x0e and 0x0f + + kmp_gopt_t prev; // values for first point (prev link) + kmp_gopt_t next; // values for last point (next link) +} +kmp_gopt2_t; + +//----------------------------------------------------------------------------- +// [[kmp_group_elem_t]] + +typedef struct kmp_group_elem_t +{ + kmp_group_name_t name; // name of eleemns + kmp_oneway_mode oneway; // oneway mode + +} kmp_group_elem_t; + +//----------------------------------------------------------------------------- +// [[kmp_group_t]] + +typedef struct kmp_group_t +{ + int first_index; // index of first point + int last_index; // index of last point + bool no_rotate; // true: do not rotate links + + kmp_group_name_t name; // name of group + kmp_group_elem_t prev[KMP_MAX_PH_LINK]; // prev-link names and status + kmp_group_elem_t next[KMP_MAX_PH_LINK]; // next-link names and status + +} kmp_group_t; + +//----------------------------------------------------------------------------- +// [[kmp_group_list_t]] + +typedef struct kmp_group_list_t +{ + struct kmp_t *kmp; // related kmp, never NULL + struct kmp_ph_t *ph; // related ph info, never NULL + + uint used; // used groups + kmp_group_t group[KMP_MAX_GROUP]; // list with groups + char selector; // '!' selector + bool have_options; // >0 at least one option detected + Line_t line; // '$LINE' settings + +} kmp_group_list_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_rtobj_t, kmp_rtobj_list_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_rtobj_t]] + +typedef struct kmp_rtobj_t +{ + u16 obj; // object to use + float3 shift; // add to position + float3 scale; // store as 'scale' factor + float3 rotate; // add to rotation + +} kmp_rtobj_t; + +//----------------------------------------------------------------------------- +// [[kmp_rtobj_list_t]] + +typedef struct kmp_rtobj_list_t +{ + kmp_rtobj_t rtobj; // object to use + u16 index; // index to POTI group + +} kmp_rtobj_list_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_mode_t]] + +typedef enum kmp_mode_t +{ + //--- general options + + KMPMD_FORCE = 0x00000001, // like --force: accept little repairable issues + KMPMD_M_GENERAL = KMPMD_FORCE, + + + //--- KMP patching options + + KMPMD_NEW = 0x00000010, // create a new KMP file even if nothing changed + KMPMD_RM_SPCITEM = 0x00000020, // remove special items + KMPMD_REVERSE = 0x00000040, // reverse the track order + KMPMD_MAX_LAPS = 0x00000080, // set LAPS only if larger + + KMPMD_1_LAP = 0x00000100, // set lap count to #1 + KMPMD_2_LAPS = 0x00000200, // set lap count to #2 + KMPMD_3_LAPS = 0x00000300, // set lap count to #3 + KMPMD_4_LAPS = 0x00000400, // set lap count to #4 + KMPMD_5_LAPS = 0x00000500, // set lap count to #5 + KMPMD_6_LAPS = 0x00000600, // set lap count to #6 + KMPMD_7_LAPS = 0x00000700, // set lap count to #7 + KMPMD_8_LAPS = 0x00000800, // set lap count to #8 + KMPMD_9_LAPS = 0x00000900, // set lap count to #9 + KMPMD_M_LAPS = 0x00000f00, + KMPMD_SHIFT_LAPS = 8, + + KMPMD_POLE_LEFT = 0x00001000, // set pole position to left side + KMPMD_POLE_RIGHT = 0x00002000, // set pole position to right side + KMPMD_SP_WIDE = 0x00004000, // set start mode 'wide' + KMPMD_SP_NARROW = 0x00008000, // set start mode 'narrow' + KMPMD_M_POLE = KMPMD_POLE_LEFT + | KMPMD_POLE_RIGHT, + KMPMD_M_STARTPOS = KMPMD_SP_WIDE + | KMPMD_SP_NARROW, + KMPMD_M_STGI = KMPMD_M_POLE + | KMPMD_M_STARTPOS + | KMPMD_M_LAPS, + + KMPMD_FIX_CKPH = 0x00010000, // automatic calculations of section CKPH + KMPMD_FIX_ENPH = 0x00020000, // automatic calculations of section ENPH + KMPMD_FIX_ITPH = 0x00040000, // automatic calculations of section ITPH + KMPMD_FIX_CKNEXT = 0x00080000, // CKPT: auto calc of 'prev' and 'next' + KMPMD_FIX_CKJGPT = 0x00100000, // CKPT: fix invalid JGPT indices + KMPMD_M_FIX_PH = KMPMD_FIX_CKPH + | KMPMD_FIX_ENPH + | KMPMD_FIX_ITPH, + KMPMD_M_FIX_CK = KMPMD_FIX_CKPH + | KMPMD_FIX_CKNEXT + | KMPMD_FIX_CKJGPT, + KMPMD_M_FIX_ALL = KMPMD_M_FIX_PH + | KMPMD_M_FIX_CK, + + KMPMD_MASK_PFLAGS = 0x00200000, // mask presence flags by 0x3f & clear padding + KMPMD_RM_LECODE = 0x00400000, // remove LE-CODE objects of section GOBJ + KMPMD_PURGE_GOBJ = 0x00800000, // remove invalid objects of section GOBJ except def-obj + KMPMD_FULL_DEFOBJ = 0x01000000, // Display definitions objects as full object + KMPMD_M_XPF = KMPMD_MASK_PFLAGS + | KMPMD_RM_LECODE + | KMPMD_PURGE_GOBJ + | KMPMD_FULL_DEFOBJ, + + //--- dump options + + KMPMD_DUMP_CLASS = 0x02000000, // dump class list at end of section if not empty + KMPMD_DUMP_ONEWAY = 0x04000000, // dump oneway list at end of section if not empty + KMPMD_M_DUMP_ALL = KMPMD_DUMP_CLASS + | KMPMD_DUMP_ONEWAY, + + + //--- hidden options + + KMPMD_LOG = 0x08000000, // enable kmp trace log to stdout + KMPMD_SILENT = 0x10000000, // disable logging of patching operations + KMPMD_INPLACE = 0x20000000, // force inplace patching + KMPMD_M_HIDDEN = KMPMD_LOG + | KMPMD_SILENT + | KMPMD_INPLACE, + + KMPMD_F_HIDE = 0x40000000, // help flag to hide + + + //--- tiny modes + + KMPMD_S_TINY = 32, + KMPMD_TINY_0 = 0ull << KMPMD_S_TINY, // TINY levels + KMPMD_TINY_1 = 1ull << KMPMD_S_TINY, + KMPMD_TINY_2 = 2ull << KMPMD_S_TINY, + KMPMD_TINY_3 = 3ull << KMPMD_S_TINY, + KMPMD_TINY_4 = 4ull << KMPMD_S_TINY, + KMPMD_TINY_5 = 5ull << KMPMD_S_TINY, + KMPMD_TINY_6 = 6ull << KMPMD_S_TINY, + KMPMD_TINY_7 = 7ull << KMPMD_S_TINY, + KMPMD_M_TINY = KMPMD_TINY_7, + + KMPMD_RM_EMPTY = KMPMD_TINY_1 << 3, // skip empty KMP sections + KMPMD_MINIMIZE = KMPMD_RM_EMPTY << 1, // minimize KMP by reordering + + //--- tests + + KMPMD_TEST1 = KMPMD_MINIMIZE << 1, // reserved for testing + KMPMD_TEST2 = KMPMD_TEST1 << 1, // reserved for testing + KMPMD_M_TEST = KMPMD_TEST1 + | KMPMD_TEST2, + + //--- summaries + + KMPMD_M_DEFAULT = 0, // default settings + + KMPMD_M_PATCH = KMPMD_NEW // patching flags + | KMPMD_RM_SPCITEM + | KMPMD_REVERSE + | KMPMD_MAX_LAPS + | KMPMD_M_STGI + | KMPMD_M_FIX_ALL + | KMPMD_M_XPF + | KMPMD_M_TINY + | KMPMD_RM_EMPTY + | KMPMD_MINIMIZE + | KMPMD_M_TEST, + + KMPMD_M_ALL = KMPMD_M_GENERAL // all relevant bits + | KMPMD_M_PATCH + | KMPMD_M_DUMP_ALL + | KMPMD_M_HIDDEN, + + KMPMD_M_PRINT = KMPMD_M_ALL // all relevant bits for obj output + & ~KMPMD_M_HIDDEN, +} +kmp_mode_t; + +//----------------------------------------------------------------------------- +// [[kmp_diff_mode_t]] + +typedef enum kmp_diff_mode_t +{ + KDMD_NONE = 0, + + KDMD_KTPT = 1 << KMP_KTPT, + KDMD_ENPT = 1 << KMP_ENPT, + KDMD_ENPH = 1 << KMP_ENPH, + KDMD_ITPT = 1 << KMP_ITPT, + KDMD_ITPH = 1 << KMP_ITPH, + KDMD_CKPT = 1 << KMP_CKPT, + KDMD_CKPH = 1 << KMP_CKPH, + KDMD_GOBJ = 1 << KMP_GOBJ, + KDMD_POTI = 1 << KMP_POTI, + KDMD_AREA = 1 << KMP_AREA, + KDMD_CAME = 1 << KMP_CAME, + KDMD_JGPT = 1 << KMP_JGPT, + KDMD_CNPT = 1 << KMP_CNPT, + KDMD_MSPT = 1 << KMP_MSPT, + KDMD_STGI = 1 << KMP_STGI, + KDMD_M_SECTION = (1<0: enabled + kmp_class_mode default_class; // default class + kmp_ac_mode ac_mode; // one of KMP_AC_* + kmp_ac_mode ac_flags; // bitfield of KMP_ACF_* + u16 mask[2]; // (ENPH+ITPH only) + + // oneway support + uint oneway_used; // number of used elements + uint oneway_size; // number of alloced elements + u16 *oneway_list; // list of elements (from<<8|to) + + // lists + ParamField_t class_name; // list with class names + kmp_gopt2_t gopt[KMP_MAX_GROUP]; // group options (ENPH+ITPH only) + + // [[obsolete]] -> replaced by 'kmp_t::index' + kmp_group_name_t gname[KMP_MAX_GROUP]; // group names of check points +} +kmp_ph_t; + +//----------------------------------------------------------------------------- + +void InitializePH ( kmp_ph_t *ph, uint sect_ph, uint sect_pt, uint ac_default ); +void ResetPH ( kmp_ph_t *ph ); + +void InsertOnewayPH ( kmp_ph_t *ph, u8 from, u8 to ); +bool FindOnewayPH ( const kmp_ph_t *ph, u8 from, u8 to ); +void SortOnewayPH ( const kmp_ph_t *ph ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct kmp_flag_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_flag_t]] // special settings for ENPT, ITPH and JGPT + +typedef struct kmp_flag_t +{ + u8 import; // >0: flags imported + u8 export; // >0: flag export enabled + + u8 mask; // any of KPFL_M_* + u8 flags[KMP_MAX_POINT]; // kmp_pt_flag_t +} +kmp_flag_t; + +//----------------------------------------------------------------------------- + +void InitializeKMPFlags ( kmp_flag_t * kf, u8 mask ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_t]] + +typedef struct kmp_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // FF_KMP | FF_KMP_TXT + struct lex_info_t *lexinfo; // NULL or valid LEX info + + //--- error control: if >0: suppress warnings for ... + + int nowarn_format; // .. invalid file formats + + //--- data + + List_t dlist[KMP_N_SECT]; // data lists + List_t poti_point; // POTI points list + + int revision; // tool revision + u32 kmp_version; // unknown value if file header + u16 value[KMP_N_SECT+1]; // 'value' of section header + IsArena_t battle_mode; // one of ARENA_* + IndexList_t index[KMP_N_SECT]; // named indices + + char * info; // string behind last section + const u8 * max_scanned; // raw scanning: max scanned area + + //--- wim0 support + + kmp_wim0_sect_t *wim0_data; // NULL or (decompressed) data + uint wim0_size; // size of wim0_data in bytes + uint wim0_import_size; // 0 or total size of found WIM0 section + u8 wim0_import_bz2; // >0: found WIM0 section was bz2'ipped. + u8 wim0_export; // >0: create section WIM0 on export + + //--- support for PH sections + + kmp_ph_t ckph; + kmp_ph_t enph; + kmp_ph_t itph; + + //--- flags support + + kmp_flag_t enpt_flag; + kmp_flag_t itpt_flag; + kmp_flag_t jgpt_flag; + + //--- snap variables: if not VAR_UNSET + // => move each JGPT to next ENPT and and snap value + //--- priority: 'snap2' before 'snap' before 'hsnap' + + Var_t jgpt_snap2; // not VAR_UNSET: snap2 JGPT + Var_t cnpt_snap2; // not VAR_UNSET: snap2 CNPT + Var_t mspt_snap2; // not VAR_UNSET: snap2 MSPT + Var_t jgpt_snap; // not VAR_UNSET: snap JGPT + Var_t cnpt_snap; // not VAR_UNSET: snap CNPT + Var_t mspt_snap; // not VAR_UNSET: snap MSPT + Var_t jgpt_hsnap; // not VAR_UNSET: hsnap JGPT + Var_t cnpt_hsnap; // not VAR_UNSET: hsnap CNPT + Var_t mspt_hsnap; // not VAR_UNSET: hsnap MSPT + + //--- route objects (insert objects into KMP to show routes) + + kmp_rtobj_t rtobj; // predefined route object + kmp_rtobj_t rtobj_enpt; // >0: for each ENPT 1 object + kmp_rtobj_t rtobj_itpt; // >0: for each ITPT 1 object + kmp_rtobj_t rtobj_jgpt; // >0: for each JGPT 1 object + kmp_rtobj_t rtobj_cnpt; // >0: for each CNPT 1 object + kmp_rtobj_t rtobj_mspt; // >0: for each MSPT 1 object + kmp_rtobj_list_t rtobj_poti[50]; // objects for POTI routes + uint n_rtobj_poti; // number of valid elements + // in 'rtobj_poti[]' + //--- objects on check points + + int ckpt_object_mode; // view mode + int ckpt_object1; // object 1 + int ckpt_object2; // object 2 + Var_t ckpt_object1_scale; // scale of object 1 + Var_t ckpt_object2_scale; // scale of object 2 + float ckpt_object1_base; // y base of object 1 + float ckpt_object2_base; // y base of object 2 + + //--- special settings + + u8 ckpt_auto_respawn[KMP_MAX_POINT]; // >0: assign nearest JGPT + + int jgpt_auto_define; // >0 and valid section: auto define JGPT + float jgpt_auto_distance; // >0.0: define only if no other pt in distance + Var_t jgpt_auto_adjust; // add this vector to auto defined point + int jgpt_auto_remove; // >0: remove unused: 1=auto defined, 2=all + + //--- misc + + bool is_pass2; // true: KMP compiler runs pass2 + bool is_shrinked; // true: source is a shrinked KMP + bool multi_ckpt_mode_0_ok; // true: disable warnings about mode 0 + bool enable_fall_kcl; // true: enable 'fall_kcl' support + struct kcl_t *fall_kcl; // NULL or pointer to own fall-down kcl + + struct kmp_linfo_t *linfo; // NULL or link infos for 'linfo_sect' + kmp_entry_t linfo_sect; // section of 'linfo' + + const kmp_stgi_entry_t *stgi; // pointer to raw STGI section. + // only valid after ScanRawKMP() + // as long as source is valid too + + //--- raw data, created by CreateRawKMP() + + u8 * raw_data; // NULL or data + uint raw_data_size; // size of 'raw_data' + uint raw_head_size; // size of file header of 'raw_data' + +} kmp_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeKMP ( kmp_t * kmp ); +void ResetKMP ( kmp_t * kmp ); +void ClearSectionsKMP ( kmp_t * kmp ); +const VarMap_t * SetupVarsKMP(); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanTextKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + const void * data, // data to scan + uint data_size, // size of 'data' + CheckMode_t mode // not NULL: call CheckKMP(mode) +); + +//----------------------------------------------------------------------------- + +enumError ScanRawDataKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckKMP(mode) +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadKMP +( + kmp_t * kmp, // KMP data structure + bool init_kmp, // true: initialize 'kmp' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckKMP(mode) +); + +//----------------------------------------------------------------------------- + +enumError CreateRawKMP +( + kmp_t * kmp // pointer to valid KMP +); + +//----------------------------------------------------------------------------- + +enumError SaveRawKMP +( + kmp_t * kmp, // pointer to valid KMP + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextKMP +( + const kmp_t * kmp, // pointer to valid KMP + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +/////////////////////////////////////////////////////////////////////////////// + +bool SortGOBJ +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // <=0: dont sort + // 1: group only + // 2: secondary sort by angle +); + +//----------------------------------------------------------------------------- + +bool TransformKMP +( + kmp_t * kmp // pointer to valid KMP +); + +//----------------------------------------------------------------------------- + +bool PatchKMP +( + kmp_t * kmp // pointer to valid KMP +); + +//----------------------------------------------------------------------------- + +bool PatchRawDataKMP +( + void * data, // data to scan + uint data_size // size of 'data' +); + +/////////////////////////////////////////////////////////////////////////////// + +int GetNextPointKMP +( + const kmp_t * kmp, // KMP data structure + uint sect, // KMP PT section index + int pt_index, // index of point, <0: add N + int sub_index, // index into 'next' + // -1: use 'pt_index+1' in a group + // find first valid at group end + // !-1 && KMP_CKPT: try to use 'next' link + bool unique, // return 'not_found_val' on duplicates + int not_found_val, // return this value, if next point not found + int *ret_pt_index, // not NULL: store fixed 'pt_index' here + int *ret_sub_index, // not NULL: store used sub index here + bool *ret_new_group, // not NULL: store true, if new group + const kmp_enph_entry_t **ret_ph // not NULL: store found ph-record +); + +/////////////////////////////////////////////////////////////////////////////// + +const kmp_enph_entry_t * FindPH +( + const kmp_t * kmp, // KMP data structure + uint sect, // KMP PT or PH section index + int pt_index, // index of point, <0: add N + int * res_ph_index // not NULL: store ph_index or -1 here +); + +/////////////////////////////////////////////////////////////////////////////// + +int CheckConvexCKPT +( + // returns + // -1: illegal index + // 0: convex + // 1: concave + + const kmp_t *kmp, // KMP data structure + uint ckpt1, // index of first check point (robust) + uint ckpt2, // index of second check point (robust) + double *min_distance // not NULL: store minimum distance here +); + +/////////////////////////////////////////////////////////////////////////////// + +void RenameGroupKMP +( + kmp_t * kmp, // KMP data structure + uint sect_ph, // ph section index + kmp_group_name_t * gname, // pointer to group name array + bool enable_dp // enable dispatch point support +); + +/////////////////////////////////////////////////////////////////////////////// + +void InsertLinkPH +( + u8 * vector, // insert into vector + u8 link, // link to insert + ScanInfo_t * si, // not NULL: print error message + ccp name, // NULL or name of line for error message + bool allow_dup // true: allow multiple links into same section +); + +/////////////////////////////////////////////////////////////////////////////// + +#define MAX_CLASS_NAMES 255 + +//----------------------------------------------------------------------------- + +void SetupClassNamePH +( + kmp_ph_t * ph // valid PH data structure +); + +//----------------------------------------------------------------------------- + +int InsertClassNamePH +( + // return 1..254 if found or set, 255 if list is full, -1 on error + + kmp_ph_t * ph, // valid PH data structure + ccp cname, // NULL or name of class + bool no_warn // TRUE: suppress warning about full list +); + +//----------------------------------------------------------------------------- + +int FindClassNamePH +( + // return -1, if not found, 1..254 else + + kmp_ph_t * ph, // valid PH data structure + ccp cname // NULL or name of class +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP itembox support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct Itembox_t +{ + ObjectFlags_t + obj_flags; // >0: ObjectFlags_t of analyzed itembox + u16 obj_id; // >0: ID of analyzed itembox + u16 item_player; // mode of player item + u16 item_enemy; // mode of enemy item + bool unsure_player; // true: not sure, if 'item_player' is correct + bool unsure_enemy; // true: not sure, if 'item_enemy' is correct +} +Itembox_t; + +// initialze 'ibox' and returns 'ibox->obj_id' +u16 AnalyseItembox ( Itembox_t *ibox, const kmp_gobj_entry_t *op ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP draw support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[draw_mode_t]] + +typedef enum draw_mode_t +{ + DMD_NONE = 0, + + DMD_KTPT = 1 << KMP_KTPT, + DMD_ENPT = 1 << KMP_ENPT, + DMD_ITPT = 1 << KMP_ITPT, + DMD_CKPT = 1 << KMP_CKPT, + //DMD_GOBJ = 1 << KMP_GOBJ, + DMD_POTI = 1 << KMP_POTI, + DMD_AREA = 1 << KMP_AREA, + //DMD_CAME = 1 << KMP_CAME, + DMD_JGPT = 1 << KMP_JGPT, + DMD_CNPT = 1 << KMP_CNPT, + //DMD_MSPT = 1 << KMP_MSPT, + + DMD_SHIFT_PFLAGS = KMP_N_SECT, // shift right to get presence flags + DMD_1OFFLINE = 1 << KMP_N_SECT, + DMD_2OFFLINE = DMD_1OFFLINE << 1, + DMD_3OFFLINE = DMD_2OFFLINE << 1, + DMD_1ONLINE = DMD_3OFFLINE << 1, + DMD_2ONLINE = DMD_1ONLINE << 1, + DMD_3ONLINE = DMD_2ONLINE << 1, + + DMD_CJGPT = DMD_3ONLINE << 1, + DMD_ITEMBOX = DMD_CJGPT << 1, + DMD_COIN = DMD_ITEMBOX << 1, + DMD_ROADOBJ = DMD_COIN << 1, + DMD_SOLIDOBJ = DMD_ROADOBJ << 1, + DMD_DECORATION = DMD_SOLIDOBJ << 1, + DMD_DISPATCH = DMD_DECORATION << 1, + DMD_BLACK = DMD_DISPATCH << 1, + DMD_WHITE = DMD_BLACK << 1, + DMD_KCL = DMD_WHITE << 1, + + DMD_DETAILED = DMD_KCL * 2ull, + DMD_WARNINGS = DMD_DETAILED * 2ull, + + DMD_M_OFFLINE = DMD_1OFFLINE // offline presence flags + | DMD_2OFFLINE + | DMD_3OFFLINE, + + DMD_M_ONLINE = DMD_1ONLINE // online presence flags + | DMD_2ONLINE + | DMD_3ONLINE, + + DMD_M_PFLAGS = DMD_M_OFFLINE // all presence flags + | DMD_M_ONLINE, + + DMD_M_FLAGS = DMD_DETAILED // flags + | DMD_WARNINGS, + + DMD_M_RESPAWN = DMD_CKPT // all about respawning + | DMD_JGPT + | DMD_CJGPT + | DMD_KTPT + | DMD_KCL, + + DMD_M_PTS = DMD_JGPT // single points + | DMD_KTPT, + + DMD_M_GOBJ = DMD_ITEMBOX // global objects + | DMD_COIN + | DMD_ROADOBJ + | DMD_SOLIDOBJ + | DMD_DECORATION, + + DMD_M_ADD = DMD_M_PTS // modes that add faces + | DMD_M_GOBJ + | DMD_ENPT + | DMD_ITPT + | DMD_CKPT + | DMD_POTI + | DMD_AREA + | DMD_CNPT + | DMD_CJGPT + | DMD_DISPATCH + | DMD_BLACK + | DMD_WHITE, + + DMD_M_ALL = DMD_M_ADD // all possible modes + | DMD_KCL + | DMD_M_PFLAGS + | DMD_M_FLAGS, + + DMD_M_RESET = DMD_M_ALL // reset these bits + & ~DMD_M_FLAGS, + + DMD_M_NEED_REF = DMD_CNPT, // needs a reference KCL + + DMD_M_DEFAULT = DMD_M_ALL // default setting + & ~DMD_M_PFLAGS + +} draw_mode_t; + +/////////////////////////////////////////////////////////////////////////////// + +extern draw_mode_t KMP_DRAW; + +int ScanOptDraw ( ccp arg ); +ccp GetDrawMode(); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[DrawRouteMode_t]] + +typedef enum DrawRouteMode_t +{ + // order by priority, lowest priority first + + DRTM_OFF, // draw none + DRTM_POTI, // draw standard route + //DRTM_CAME, // draw came route + DRTM_GOBJ, // draw route for GOBJ objects + DRTM_ITEMBOX, // draw route for standard itembox + DRTM_ITEMBOX_ENEMY, // draw route for itembox with special item for enemies + DRTM_ITEMBOX_PLAYER, // draw route for itembox with special item for players +} +__attribute__ ((packed)) DrawRouteMode_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[DrawKCL_t]] + +#define MAX_ROUTE 0x100 + +typedef struct DrawKCL_t +{ + struct kcl_t *draw; // valid pointer to draw-KCL, maybe same as 'kcl' + + const struct kcl_t *kcl; // NULL or pointer to related source KCL + const struct kmp_t *kmp; // NULL or pointer to related source KMP + + DrawRouteMode_t route[MAX_ROUTE]; // List of routes to draw + bool warnings; // true: draw warnings in red + bool detailed; // true: print more detailed + bool as_road; // true: print as road (KCL flag 0x0000) +} +DrawKCL_t; + +//----------------------------------------------------------------------------- + +static inline void InitializeDrawKCL ( DrawKCL_t *dk ) + { DASSERT(dk); memset(dk,0,sizeof(*dk)); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP draw functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AddAreas2KCL +( + DrawKCL_t * dk // pointer to initalized object +); + +//----------------------------------------------------------------------------- + +void AddCheckPoints2KCL +( + DrawKCL_t * dk, // pointer to initalized object + bool draw_jgpt // true: draw connections to JGPT +); + +//----------------------------------------------------------------------------- + +void AddArrow2KCL +( + DrawKCL_t * dk, // pointer to initalized object + uint sect, // KMP section + uint arrow_color, // color of arrow + uint area_color, // color of area behind arrow + double area_len, // >0.0: width of area behind arrow + double area_wd // >0.0: length of area behind arrow +); + +//----------------------------------------------------------------------------- + +void AddRoutes2KCL +( + DrawKCL_t * dk, // pointer to initalized object + uint sect_pt, // KMP section + uint color_std // standart color index +); + +//----------------------------------------------------------------------------- + +void AddPotiRoutes2KCL +( + DrawKCL_t * dk // pointer to initalized object +); + +//----------------------------------------------------------------------------- + +void AddCannons2KCL +( + DrawKCL_t * dk // pointer to initalized object +); + +//----------------------------------------------------------------------------- + +void AddObject2KCL +( + DrawKCL_t * dk, // pointer to initalized object + const + kmp_gobj_entry_t * op // object to draw +); + +//----------------------------------------------------------------------------- + +void AddObjectsById2KCL +( + DrawKCL_t * dk, // pointer to initalized object + u16 obj_id // object id to draw +); + +//----------------------------------------------------------------------------- + +void AddObjectsByFlag2KCL +( + DrawKCL_t * dk, // pointer to initalized object + u32 flag_mask, // mask the object flag with this ... + u32 flag_result // ... and draw it, if the result is this. +); + +//----------------------------------------------------------------------------- + +void AddZeroGround2KCL +( + DrawKCL_t * dk, // pointer to initalized object + uint color // color of ground +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// pos file support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[pos_param_t]] [[pos_file_t]] + +#define MAX_POS_FILE_INDEX 50 +#define MAX_POS_PARAM 15 +#define MAX_POS_FILTER 3 + +typedef struct pos_param_t +{ + //--- columns, all column indeces are 1 based + + uint idx_x; // >0: column index of x-pos + uint idx_y; // >0: column index of y-pos + uint idx_z; // >0: column index of z-pos + uint idx_d; // >0: column index of directiion (degree) + + //--- color + + uint autocolor; // >0: column for auto color, ignore color + uint color; // kcl_flag_* + int transp; // 0,1,2 for transparency selection, -1:disabled + + //--- filter + + uint idx_f[MAX_POS_FILTER]; // column index of filter A + char filter[MAX_POS_FILTER][20]; // text of filter A +} +pos_param_t; + +typedef struct pos_file_t +{ + struct pos_file_t *next; // link to next entry + ccp fname; // filename (alloced). NULL if invalid + + uint n_param; // number of used 'param' elements + pos_param_t param[MAX_POS_PARAM]; +} +pos_file_t; + +//----------------------------------------------------------------------------- + +extern pos_param_t default_pos_param; +extern pos_file_t current_pos_param; +extern pos_file_t *first_pos_file; + +//----------------------------------------------------------------------------- + +int ScanOptPosFile ( ccp arg ); +int ScanOptPosMode ( ccp arg ); + +void DrawPosFiles( DrawKCL_t *draw ); +enumError DrawPosFile ( DrawKCL_t *draw, pos_file_t *pf ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// link info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define KMP_MAX_LINFO 0x100 + +//----------------------------------------------------------------------------- +// [[kmp_linfo_g_mode]] + +typedef enum kmp_linfo_g_mode +{ + LINFOG_DISPATCH = 0x01, // group is dispatch point +} +__attribute__ ((packed)) kmp_linfo_g_mode; + +//----------------------------------------------------------------------------- +// [[kmp_linfo_mode]] + +typedef enum kmp_linfo_mode +{ + LINFO_D_PREV = 0x01, // $1 links directly to $2 by PREV + LINFO_D_NEXT = 0x02, // $1 links directly to $2 by NEXT + LINFO_D_BY_PREV = 0x04, // $1 is directly linked from $2 by PREV + LINFO_D_BY_NEXT = 0x08, // $1 is directly linked from $2 by NEXT + + LINFO_G_PREV = 0x10, // $1 links via group to $2 by PREV + LINFO_G_NEXT = 0x20, // $1 links via group to $2 by NEXT + LINFO_G_BY_PREV = 0x40, // $1 is linked via group from $2 by PREV + LINFO_G_BY_NEXT = 0x80, // $1 is linked via group from $2 by NEXT + + LINFO_PREV = LINFO_D_PREV | LINFO_G_PREV, + LINFO_NEXT = LINFO_D_NEXT | LINFO_G_NEXT, + LINFO_BY_PREV = LINFO_D_BY_PREV | LINFO_G_BY_PREV, + LINFO_BY_NEXT = LINFO_D_BY_NEXT | LINFO_G_BY_NEXT, + + LINFO_M_D = LINFO_D_PREV | LINFO_D_NEXT | LINFO_D_BY_PREV | LINFO_D_BY_NEXT, + LINFO_M_G = LINFO_G_PREV | LINFO_G_NEXT | LINFO_G_BY_PREV | LINFO_G_BY_NEXT, +} +__attribute__ ((packed)) kmp_linfo_mode; + +//----------------------------------------------------------------------------- +// [[kmp_linfo_t]] + +typedef struct kmp_linfo_t +{ + uint sect_pt; + uint sect_ph; + + uint used; + + u8 n_point[KMP_MAX_LINFO]; + kmp_linfo_g_mode gmode[KMP_MAX_LINFO]; + kmp_linfo_mode summary[KMP_MAX_LINFO]; + kmp_linfo_mode list[KMP_MAX_LINFO][KMP_MAX_LINFO]; + +} +kmp_linfo_t; + +//----------------------------------------------------------------------------- + +void ResetRouteLinksKMP ( const kmp_t *kmp ); +kmp_linfo_t * SetupRouteLinksKMP ( const kmp_t *ckmp, uint sect, bool force ); + +uint AnalyseRouteKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect ); +uint AnalyseGroupLinksKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_ph ); +uint AnalyseRouteLinksKMP ( kmp_linfo_t *li, const kmp_t *kmp, uint sect_pt ); +uint AnalyseRouteLinksCKPT ( kmp_linfo_t *li, const kmp_t *kmp ); + +// print_group: -1=never, 0:auto, 1:always +ccp PrintLinkInfoMode ( kmp_linfo_mode mode, int print_group, bool adjust ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// detect KMP types /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static inline bool IsDispatchPointKMP ( const kmp_enph_entry_t *p ) +{ +// return p->pt_len == 1 && p->prev[0] == 0xff && p->next[0] != 0xff; + return p->pt_len == 1 && p->prev[0] == 0xff; +} + +//----------------------------------------------------------------------------- + +void GetRouteTypeInfoKMP +( + kmp_rtype_info_t *rti, // store result here (never NULL) + const struct kmp_t *kmp, // KMP data + uint sect // section to check (PT or PH) +); + +/////////////////////////////////////////////////////////////////////////////// + +IsArena_t IsArenaKMP ( const struct kmp_t * kmp ); +IsArena_t CheckBattleModeKMP ( const struct kmp_t * kmp ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_finish_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_finish_t]] + +typedef struct kmp_finish_t +{ + bool valid; // true: record is valid => + // finish_ktpt >= 0 && finish_ckpt >= 0 + u8 hint; // bit field: 1=direction, 2=distance + u8 warn; // bit field: 1=direction, 2=distance + + u8 n_ckpt_0; // number of check points + u8 n_ktpt_m; // number of KTPT with index <0 + + s16 ckpt; // -1 or index of relevant CKPT + s16 ckpt_opt; // -1 or index of CKPT used for optimized KTPT + s16 ktpt; // -1 or index of relevant KTPT + s16 enpt_rec; // -1 or index of ENPT used for recommended KTPT + + float3 pos_ckpt; // neareast point to ktpt + float3 pos_ktpt; // position + float3 pos_ktpt_opt; // optimized position + float3 pos_ktpt_rec; // recommended position + + float dir_ckpt; // direction of CKPT, (0..360( + float dir_ktpt; // direction of KTPT, (0..360( + float dir_ktpt_opt; // optimized direction of KTPT, (0..360( + float dir_ktpt_rec; // recommended direction of KTPT, (0..360( + float dir_delta; // abs(dir_ckpt-dir_ckpt), (0..180) + float distance; // 2D distance of KTPT and finish line + float distance_rec; // 2D distance of 'pos_ktpt' and 'pos_ktpt_rec' +} +kmp_finish_t; + +//----------------------------------------------------------------------------- + +void InitializeFinishLine ( kmp_finish_t *kf ); + +double CalcCheckPointDistance +( + const kmp_ckpt_entry_t *ckpt, + double x, + double z +); + +void CalcFinishLine +( + const kmp_t *kmp, // not NULL: use it for recommendation + kmp_finish_t *kf, // valid data + const kmp_ckpt_entry_t *ckpt, // valid CKPT + const kmp_ktpt_entry_t *ktpt // valid KTPT +); + +bool CheckFinishLine ( const kmp_t *kmp, kmp_finish_t *kf ); +ccp LogFinishLine ( kmp_finish_t *kf ); + + +int InsertSecondKTPT +( + // returns: 0=not possible, 1:inserted, 2:replaced + + kmp_t *kmp, // valid KMP + double3 pos // position to insert +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_usedpos_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_usedpos_obj_t]] + +typedef struct kmp_usedpos_obj_t +{ + //--- input + + float3 min; // minimal and maximal coordinates ... + float3 max; // ... used by ENPT and ITPT. + float3 allowed; // allowed values, init by 131071 == 2^17-1 + + //--- info (not used for calculations, set by ModifyUsedPosObj()) + + float3 shift; // active shift value + float3 stretch; // active stretch factor + + //--- results + + bool valid; // true: record is valid + bool useful; // true: record has useful data + bool shift_active; // true: shift is active + bool stretch_active; // true: stretch is active + + WarnLevel_t max_rating; // max 'rating' + WarnLevel_t rating[3]; // rating for each axis of min_used & max_used + bool pos_factor_valid; // TRUE: 'pos_factor' is valid + float3 pos_factor; // recommended factors for LEX/SET1/ITEM-POS-FACTOR +} +kmp_usedpos_obj_t; + +//----------------------------------------------------------------------------- + +void InitializeUsedPosObj ( kmp_usedpos_obj_t *obj ); +void EvaluateUsedPosObj ( kmp_usedpos_obj_t *obj ); +void ModifyUsedPosObj + ( kmp_usedpos_obj_t *obj, kmp_usedpos_obj_t *src, float3 *shift, float3 *stretch ); +ccp GetUsedPosObjSuggestion + ( const kmp_usedpos_obj_t *obj, bool force, ccp return_if_empty ); +void PrintUsedPosObj ( FILE *f, int indent, const kmp_usedpos_obj_t *obj ); + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_usedpos_t]] + +typedef struct kmp_usedpos_t +{ + kmp_usedpos_obj_t orig; // original values + kmp_usedpos_obj_t orig_lex; // impact LEX/SET1/ITEM-POS-FACTOR + kmp_usedpos_obj_t center; // after shifting to center + kmp_usedpos_obj_t center_lex; // + impact LEX/SET1/ITEM-POS-FACTOR + + kmp_usedpos_obj_t *suggest; // one of above, never NULL +} +kmp_usedpos_t; + +//----------------------------------------------------------------------------- + +void InitializeUsedPos ( kmp_usedpos_t *up ); +void CheckUsedPos ( const kmp_t *kmp, kmp_usedpos_t *up ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// kmp_pflags_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_pflags_t]] + +typedef struct kmp_pflags_t +{ + u32 n_gobj; // number of GOBJ elements + u32 n_standard; // number of standard elements + u32 n_xpflags; // number of GOBJ elements with mode>0 + u32 n_cat_std; // number of used 'std_count' categories + u32 n_cat_mode; // number of used 'mode_count' categories + + u32 n_defobj; // number of total definition-objects + u32 n_defobj_bits; // number of definition-objects type BITS + u32 n_defobj_or; // number of definition-objects type OR + u32 n_defobj_and; // number of definition-objects type AND + u32 n_obj_ref; // number of valid object references + u32 n_cond_ref; // number of valid condition references + u32 n_cond_rnd; // number of valid randpm condition references + + u32 n_mode[16]; // number of elements with mode N>0 + u8 n_std[64]; // counters for standard bits (pflags&0x3f) +} +kmp_pflags_t; + +//----------------------------------------------------------------------------- + +uint CountPresenceFlags ( const kmp_t *kmp, kmp_pflags_t *pf ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// support for extension of pflags /////////////// +/////////////////////////////////////////////////////////////////////////////// +// INFO + +//-------------------------------------------------------------------------- +// bit mask info +//-------------------------------------------------------------------------- +// 0 0x0001 Offline race with 1 human player. +// 1 0x0002 Offline race with 2 human players. +// 2 0x0004 Offline race with 3 human players. +// 3 0x0008 Offline race with 4 human players. +//-------------------------------------------------------------------------- +// 4 0x0010 Online race with 1-6 players total and 1 human at Wii. +// 5 0x0020 Online race with 1-6 players total and 2 humans at Wii. +// 6 0x0040 Online race with 7-9 players total and 1 human at Wii. +// 7 0x0080 Online race with 7-9 players total and 2 humans at Wii. +// 8 0x0100 Online race with 10-12 players total and 1 human at Wii. +// 9 0x0200 Online race with 10-12 players total and 2 humans at Wii. +// 10 0x0400 Online race with 13-18 players total and 1 human at Wii. +// 11 0x0800 Online race with 13-18 players total and 2 humans at Wii. +// 12 0x1000 Online race with 19 or more players total and 1 human at Wii. +// 13 0x2000 Online race with 19 or more players total and 2 humans at Wii. +//-------------------------------------------------------------------------- +// 14 0x4000 RESERVED1. In Versus (setting #3) it is used for Time Trial. +// 15 0x8000 RESERVED2. +//-------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////// +// enum [[condition_ref_t]] + +typedef enum condition_ref_t // extended IF +{ + COR_NEVER = GOBJ_MIN_CONDITION, + COR_LECODE, + COR_SINGLE, + COR_MULTI, + COR_OFFLINE, + COR_ONLINE, + + COR_LE6, // 0x1006 + COR_GE7, + COR_LE9, + COR_GE10, + COR_LE12, + COR_GE13, + COR_LE18, + COR_GE19, + + COR_7_9, // 0x100e + COR_NOT_7_9, + COR_7_12, + COR_NOT_7_12, + COR_7_18, + COR_NOT_7_18, + COR_10_12, + COR_NOT_10_12, + COR_10_18, + COR_NOT_10_18, + COR_13_18, + COR_NOT_13_18, + + COR_TIMETRIAL, // 0x101a + COR_NO_TIMETRIAL, + COR_BATTLE, + COR_RACE, + + COR_BALLOON, // 0x101e + COR_NO_BALLOON, + COR_COIN, + COR_NO_COIN, + COR_VERSUS, + COR_NO_VERSUS, + COR_ITEMRAIN, + COR_NO_ITEMRAIN, + COR_SETTING5, + COR_NO_SETTING5, + COR_SETTING6, + COR_NO_SETTING6, + COR_SETTING7, + COR_NO_SETTING7, + COR_SETTING8, + COR_NO_SETTING8, + + COR_P2, + COR_P134, + COR_P3, + COR_P124, + COR_P4, + COR_P123, + COR_P12, + COR_P34, + COR_P13, + COR_P24, + COR_P14, + COR_P23, + + COR__N, // 0x102e + + + //--- developer values + + CORD_DEBUG = GOBJ_DEV_CONDITION, + CORD_RELEASE, + CORD_TESTVAL, + CORD_NO_TESTVAL, + + CORD__N // 0x1904 +} +condition_ref_t; + +//----------------------------------------------------------------------------- +// [[gobj_cond_ref_t]] + +typedef struct gobj_cond_ref_t +{ + u16 ref_id; + char name[14]; + char alias[6]; + u8 option; // Prefix: 0:no "", 2:"IF$", 4:"IFDEV$" + // &1: new group + char info[20]; + + u16 cond_mask; // mask for consitions + u8 set_mask; // mask to select setting + u8 op; // operator: + // 0: cond_mask || set_mask + // 1: cond_mask && set_mask + // *: disabled +} +gobj_cond_ref_t; + +//----------------------------------------------------------------------------- + +extern const gobj_cond_ref_t cond_ref_tab[COR__N+1]; +const gobj_cond_ref_t * FindConditionRef ( u16 ref_id ); + +/////////////////////////////////////////////////////////////////////////////// +// [[gobj_cond_mask_t]] + +typedef struct gobj_cond_mask_t +{ + u16 bit_mask; // mask to select bit of 'setting' + u8 game_mask; // mask to select 'setting' + u8 engine_mask; // mask to select am engine + u8 random_mask; // mask to select a random scenario +} +gobj_cond_mask_t; + +// if cref==NULL, then it is searched if needed. +bool IsConditionRefEnabled + ( const gobj_cond_mask_t *cm, u16 ref_id, const gobj_cond_ref_t *cref ); + +/////////////////////////////////////////////////////////////////////////////// +// [[cond_ref_info_t]] + +typedef struct cond_ref_info_t +{ + bool is_condref; // true: is valid ref_id for condition-ref + const gobj_cond_ref_t + *condref; // not NULL: definition found + ccp cmd_suffix; // either "DISABLE || "ENABLE", NULL if !is_condref + u16 xor; // 0|1, if 1: ref_id xor'd to get 'cond' + char cond[80]; // either "IF$.." or "0x1234" or "0" (if !is_condref) + char info[44]; // info for user +} +cond_ref_info_t; + +// returns 'cri->s_condref' +bool GetConditionRefInfo + ( cond_ref_info_t *cri, u16 ref_id, u16 obj_id, bool create_cond ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: support for extension of pflags /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u16 GetActiveObjectId ( const kmp_gobj_entry_t *g ); + +static inline u16 GetRelevantObjectId ( u16 obj_id ) + { return obj_id < GOBJ_MIN_DEF ? obj_id & GOBJ_M_OBJECT : obj_id; } + +static inline u16 GetPFlagsMode ( u16 pflags ) + { return ( pflags & GOBJ_M_PF_MODE ) >> GOBJ_S_PF_MODE; } + +//--------------- + +static inline bool IsConditionRefId ( u16 ref_id ) + { return ref_id >= GOBJ_MIN_CONDITION + && ref_id <= GOBJ_MAX_CONDITION; } + +static inline bool IsConditionRefEngineId ( u16 ref_id ) + { return ref_id >= GOBJ_MIN_COND_ENGINE + && ref_id <= GOBJ_MAX_COND_ENGINE; } + +static inline bool IsConditionRefRandomId ( u16 ref_id ) + { return ref_id >= GOBJ_MIN_COND_RND + && ref_id <= GOBJ_MAX_COND_RND; } + +static inline bool IsConditionRefRelevantRandomId ( u16 ref_id ) + { return ref_id > GOBJ_MIN_COND_RND + && ref_id < GOBJ_MAX_COND_RND; } + +static inline bool IsConditionRefTestId ( u16 ref_id ) + { return ref_id >= GOBJ_MIN_COND_TEST + && ref_id <= GOBJ_MAX_COND_TEST; } + +//--------------- + +static inline bool IsDefinitionObjectIdBITS ( u16 obj_id ) + { return obj_id >= GOBJ_MIN_DEF_BITS + && obj_id <= GOBJ_MAX_DEF_BITS; } + +static inline bool IsDefinitionObjectIdOR ( u16 obj_id ) + { return obj_id >= GOBJ_MIN_DEF_OR + && obj_id <= GOBJ_MAX_DEF_OR; } + +static inline bool IsDefinitionObjectIdAND ( u16 obj_id ) + { return obj_id >= GOBJ_MIN_DEF_AND + && obj_id <= GOBJ_MAX_DEF_AND; } + +static inline bool IsDefinitionObjectId ( u16 obj_id ) +{ + return obj_id >= GOBJ_MIN_DEF_BITS && obj_id <= GOBJ_MAX_DEF_BITS + || obj_id >= GOBJ_MIN_DEF_OR && obj_id <= GOBJ_MAX_DEF_OR + || obj_id >= GOBJ_MIN_DEF_AND && obj_id <= GOBJ_MAX_DEF_AND; +} + +#if 0 // not used yet + +static inline bool IsDefinitionObjectRefBITS ( u16 obj_id ) + { return obj_id >= GOBJ_MIN_DEF_BITS + && obj_id <= (GOBJ_MAX_DEF_BITS|GOBJ_DEF_F_NOT); } + +static inline bool IsDefinitionObjectRefOR ( u16 obj_id ) + { return obj_id >= GOBJ_MIN_DEF_OR + && obj_id <= (GOBJ_MAX_DEF_OR|GOBJ_DEF_F_NOT); } + +static inline bool IsDefinitionObjectRefAND ( u16 obj_id ) + { return obj_id >= GOBJ_MIN_DEF_AND + && obj_id <= (GOBJ_MAX_DEF_AND|GOBJ_DEF_F_NOT); } + +static inline bool IsDefinitionObjectRef ( u16 obj_id ) + { return obj_id >= GOBJ_MIN_DEF && obj_id <= GOBJ_MAX_DEF;} + +#endif + + +static inline bool HaveDefinitionReference ( const kmp_gobj_entry_t *g ) + { return g + && IsDefinitionObjectId(g->ref_id) + && ( g->pflags & GOBJ_M_PF_MODE ) == GOBJ_PF_MODE1; } + +/////////////////////////////////////////////////////////////////////////////// +// [[opt_gamemode_t]] + +typedef enum opt_gamemode_t +{ + //--- main modes + + GMD_STD = 0x00000001, // standard modes + GMD_BALLOON = 0x00000002, // balloon battle + GMD_COIN = 0x00000004, // coin runners + GMD_VERSUS = 0x00000008, // versus races + GMD_ITEMRAIN = 0x00000010, // item rain + GMD__RESERVED = 0x000001e0, // reserved for settings 5-8 + + GMD_MD_BATTLE = 0x00000006, + GMD_MD_RACE = 0x00000018, + GMD_MD_DEFAULT = 0x0000011f, + GMD_MD_ALL = 0x0000011f, + + + //--- filters + + GMD_NO_MODES = 0x00001000, // suppress modes (only auto mode) + GMD_OFFLINE = 0x00002000, // offline modes + GMD_ONLINE = 0x00004000, // online modes + GMD_FI_DEFAULT = 0x00006000, + GMD_FI_ALL = 0x00007000, + + + //--- options + + GMD_AUTO = 0x00010000, // add generic case + GMD_STD_FIRST = 0x00020000, // create std settings first + GMD_ENGINE = 0x00040000, // append engine selection + GMD_RANDOM = 0x00080000, // append random selection + GMD_NO_TT = 0x00100000, // exclude time trial + GMD_FORCE_TT = 0x00200000, // force time trial + GMD_FULL = 0x00400000, // don't eliminate duplicates from list + GMD_NSORT = 0x00800000, // sort scenarios by name + GMD_ISORT = 0x01000000, // sort scenarios by index + GMD_DEBUG = 0x02000000, // enable debug mode + GMD_DUMP = 0x04000000, // enable dump mode + GMD_OP_ALL = 0x07ff0000, + + //--- flags + + GMD_F_HIDE = 0x40000000, // help flag to hide + + GMD_M_SORT = GMD_NSORT + | GMD_ISORT, + + GMD_M_OPTIONS = GMD_ENGINE // start flags if scanning options + | GMD_RANDOM, + + GMD_M_DEFAULT = GMD_MD_DEFAULT + | GMD_FI_DEFAULT + | GMD_ENGINE + | GMD_RANDOM, + + GMD_M_ALL = GMD_MD_ALL + | GMD_FI_ALL + | GMD_OP_ALL +} +opt_gamemode_t; + +extern opt_gamemode_t opt_gamemodes; +int ScanOptGamemodes ( ccp arg ); + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_ana_defobj_t]] + +typedef struct kmp_ana_defobj_t +{ + bool is_defobj; // true: is a valid id for definition-objects + bool is_pure_defobj; // true: is_defobj && other parms=default + bool is_logical; // true: is OR or AND condition + + ccp cmd_name; // suffix for "$DEFOBJ-" or "IF$" + ccp par_info; // Info about parameters + int index_beg; // first index of settings to print + int index_end; // first index of settings to skip +} +kmp_ana_defobj_t; + +void AnalyseDefObject ( kmp_ana_defobj_t *ad, const kmp_gobj_entry_t *gobj ); + +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_ana_ref_t]] + +typedef struct kmp_ana_ref_t +{ + u16 mode; // mode of presence flags (pflags) + bool mode_valid; // true: valid mode + + bool condref_valid; // true: valid reference_id for condition-ref + bool condeng_valid; // true: valid reference_id for engine condition-ref + bool condrnd_valid; // true: valid reference_id for random condition-ref + bool condtest_valid; // true: valid reference_id for test values + bool objref_valid; // true: valid reference_id for def-object + + bool is_condref; // true: mode_valid && condref_valid + bool is_condeng; // true: mode_valid && condeng_valid + bool is_condrnd; // true: mode_valid && condrnd_valid + bool is_condtest; // true: mode_valid && condtest_valid + bool is_objref; // true: mode_valid && objref_valid + bool is_valid; // true: is_condref || is_objref +} +kmp_ana_ref_t; + +void AnalyseRefObject ( kmp_ana_ref_t *ar, const kmp_gobj_entry_t *gobj ); +kmp_gobj_entry_t * FindRefObject ( const kmp_t *kmp, const kmp_gobj_entry_t *gobj ); +kmp_gobj_entry_t * FindRefObjectByID ( const kmp_t *kmp, u16 ref_id ); + +/////////////////////////////////////////////////////////////////////////////// + +bool NormalizeDefObj ( kmp_gobj_entry_t *gobj ); +uint CountDefObjUsage ( const kmp_t *kmp, u16 obj_id ); +ccp GetDefObjConditionName ( u16 cond ); + +bool FixDefObj +( + kmp_t * kmp, // pointer to valid KMP + int fix_mode // >0: clear unused parameters +); + +bool SortDefObj +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // sort if KMP_DOB_BEGIN || KMP_DOB_END +); + +/////////////////////////////////////////////////////////////////////////////// + +bool FixReference +( + kmp_t * kmp, // pointer to valid KMP + int fix_mode // >0: fix if valid mode + // >1: fix for all modes +); + +bool SortReference +( + kmp_t * kmp, // pointer to valid KMP + int sort_mode // sort if KMP_DOB_BEFORE || KMP_DOB_BEHIND +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Convert conditions to text /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[gobj_condition_t]] + +typedef struct gobj_condition_t +{ + u16 mask; // Any of 0001..ffff. + u8 setting; // 1 bit (bits 0-7) for each setting (#1..#8) + s8 bitnum; // 0..15 for a single bit, -1 for a bit combination + s8 min_offline; // number of min online players (-1:not defined) + s8 max_offline; // number of max online players (-1:not defined) + s8 min_online; // number of min online players (-1:not defined) + s8 max_online; // number of max online players (-1:not defined) + ccp info; // related info. NULL: list terminator +} +gobj_condition_t; + +extern const gobj_condition_t gobj_condition[]; + +//----------------------------------------------------------------------------- +// [[gobj_condition_set_t]] + +#define GOBJ_COND_SET_MAX 10 + +typedef struct gobj_condition_set_t +{ + u16 mask; // evaluated mask + u8 setting_mask; // setting mask + uint n; // number of used elements of 'cond[]' without term + gobj_condition_t cond[GOBJ_COND_SET_MAX+1]; + // 'n' elements + terminator +} +gobj_condition_set_t; + +//----------------------------------------------------------------------------- + +// terminated by info==0 +extern const gobj_condition_t gobj_condition[]; + +// if setting==-1: evaluate for all settings +uint GetGobjConditions ( gobj_condition_set_t *gcs, int setting, u16 mask ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Analyse GOBJ elements /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_gobj_type_t]] + +typedef enum kmp_gobj_type_t +{ + KGTY_NULL, // object-id is null + KGTY_INVALID, // from object range, but invalid + KGTY_OBJECT, // valid object + + KGTY_DEFOBJ_BITS, // definition-objects type BITS + KGTY_DEFOBJ_OR, // definition-objects type OR + KGTY_DEFOBJ_AND, // definition-objects type AND + + KGTY__N +} +__attribute__ ((packed)) kmp_gobj_type_t; + +//----------------------------------------------------------------------------- +// [[kmp_gobj_status_t]] + +typedef enum kmp_gobj_status_t +{ + KGST_USED = 0x0001, // object used + KGST_ENABLED = 0x0002, // object enabled by default + KGST_CONDREF = 0x0004, // object uses a condition-reference + KGST_DEFOBJ = 0x0008, // object uses a definition-object + KGST_REF_VALID = 0x0010, // condition-reference/definition-object valid + KGST_ENGINE = 0x0020, // uses a engine condition + KGST_RANDOM = 0x0040, // uses a random condition + KGST_DISABLED_BY = 0x0080, // always disabled by condition + + KGST_M_DERIVE = KGST_ENGINE|KGST_RANDOM, +} +__attribute__ ((packed)) kmp_gobj_status_t; + +//----------------------------------------------------------------------------- +// [[kmp_gobj_info_t]] + +typedef struct kmp_gobj_info_t +{ + u16 obj_id; // cleared object id + u16 ref_id; // cleared reference id + s16 defobj; // >=0: index of related definition-object + u16 ref_count; // number of references + kmp_gobj_status_t status; // object status + kmp_gobj_type_t type; // object type +} +kmp_gobj_info_t; + +//----------------------------------------------------------------------------- +// [[kmp_ana_gobj_t]] + +typedef struct kmp_ana_gobj_t +{ + //--- parameters + + const kmp_t *kmp; // valid kmp + const kmp_gobj_entry_t + *gobj; // pointer to GOBJ list + uint n_gobj; // number of elements in 'gobj' + + //--- analysis + + kmp_gobj_info_t *info; // alloced array of 'n_gobj' elements + + uint n_object; // number of objects := n_disabled+n_enabled + uint n_disabled; // number of initial disabled elements + uint n_enabled; // number of initial enabled elements + uint n_engine; // number of elements using a engine condition + uint n_random; // number of elements using a random condition + + uint n_defobj; // total number of definition-objects + uint n_defobj_bits; // number of definition-objects type BITS + uint n_defobj_or; // number of definition-objects type OR + uint n_defobj_and; // number of definition-objects type AND + uint n_defobj_used; // number of used definition-objects + + + //--- misc + + uint dur_usec; // duration in micro seconds +} +kmp_ana_gobj_t; + +//----------------------------------------------------------------------------- + +void ResetAnaGobj ( kmp_ana_gobj_t *ag ); +void AnalyseGobj ( kmp_ana_gobj_t *ag, const kmp_t *kmp ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: Analyse PFlag scenarios /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[kmp_ana_pflag_res_t]] + +typedef struct kmp_ana_pflag_res_t +{ + char name[16]; // name + u16 version; // version id + u8 n_std; // >0: result used by standard + u8 n_ext; // >0: result used by extension + lex_test_t lex_test; // template for LEX section TEST + ccp status; // alloced status, size=n_gobj+1 +} +kmp_ana_pflag_res_t; + +//----------------------------------------------------------------------------- +// [[kmp_ana_pflag_t]] + +typedef struct kmp_ana_pflag_t +{ + //--- parameters + + kmp_ana_gobj_t ag; + opt_gamemode_t opt_gamemode; // game mode to analyze + bool auto_sep; // true: automatic separators enabled + + //--- analysis + + uint status_len; // number of chars in res.status + uint n_res; // number of valid results + uint n_version; // number of different results + uint n_res_std; // number of different results for std-code + uint n_res_ext; // number of different results for extension + + kmp_ana_pflag_res_t *res_list; // alloced array with results + uint res_size; // number of alloced elements for 'res_list' + + //--- misc + + uint dur_usec; // duration in micro seconds +} +kmp_ana_pflag_t; + +//----------------------------------------------------------------------------- + +void ResetPFlagScenarios ( kmp_ana_pflag_t *ap ); +void AnalysePFlagScenarios + ( kmp_ana_pflag_t *ap, const kmp_t *kmp, opt_gamemode_t gmode ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// XPF: repair /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRepairXPF ( ccp arg ); +enumError RepairXPF ( kmp_t *kmp ); + +/////////////////////////////////////////////////////////////////////////////// +// [[xpf_mode1_t]] + +typedef enum xpf_mode1_t +{ + XMODE1_NONE, // MODE!=1 + XMODE1_NULL, // MODE==1, but ref_id=0 + XMODE1_INVALID, // MODE==1, but invalid ref_id + XMODE1_CONDREF, // MODE==1 && conditiopn reference + XMODE1_OBJREF, // MODE==1 && object reference + XMODE1__N + +} +xpf_mode1_t; + +xpf_mode1_t CheckMode1GOBJ ( const kmp_gobj_entry_t *g ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// KMP misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern uint kmp_start_pos_mirror; // last used setting +extern uint kmp_start_pos_narrow; // last used setting +extern bool kmp_start_pos_auto; // last call used 'automode'; + +const double3 * GetStartPosKMP +( + // Return NULL on failure. + // Otherwise return a pointer to 'tab' or to an internal static buffer + // with N elements. This buffer is overwritten on next call. + + double3 *tab, // table with 'n' elements to store the result. + // if NULL: use internal static buffer + uint n, // number of players: 1..12 + bool mirror, // true: mirror the data (pole is right) + bool narrow, // true: use 'narrow' mode + + kmp_t *kmp, // not NULL: Use KTPT/#0 and STGI/#0 + uint automode // bit field: Use KMP to override parameters + // 1: return absolute positions, if KMP available + // 2: use pole position of KMP if available + // 4: use narrow mode of KMP if available +); + +/////////////////////////////////////////////////////////////////////////////// + +void DetectSpecialKMP +( + const kmp_t *kmp, // pointer to valid KMP + kmp_special_t res // store result here, never NULL +); + +ccp CreateSpecialInfoKMP + ( kmp_special_t special, bool add_value, ccp return_if_empty ); + +/////////////////////////////////////////////////////////////////////////////// + +int CheckKMP +( + // returns number of errors + + const kmp_t *kmp, // pointer to valid KMP + CheckMode_t mode // print mode +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError DiffKMP +( + kmp_t * kmp1, // first kmp to compare + kmp_t * kmp2, // second kmp tp compare + uint verbose // 0:quiet, 1:summary only, 2:full report +); + +/////////////////////////////////////////////////////////////////////////////// + +extern int have_kmp_patch_count; + +int ScanOptRmGobj ( ccp arg ); +int ScanOptBattle ( ccp arg ); +int ScanOptExportFlags ( ccp arg ); +int ScanOptRouteOptions ( ccp arg ); +int ScanOptWim0 ( ccp arg ); + +enumError cmd_stgi(); +enumError cmd_ktpt(); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_KMP_H diff --git a/project/src/lib-lecode.c b/project/src/lib-lecode.c new file mode 100644 index 0000000..5bf9667 --- /dev/null +++ b/project/src/lib-lecode.c @@ -0,0 +1,4574 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "dclib-xdump.h" +#include "lib-lecode.h" +#include "lib-std.h" +#include "lib-mkw.h" +#include "lib-szs.h" +#include "lpar.inc" +#include "lex.inc" + +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp opt_le_define = 0; +ccp opt_lpar = 0; +ccp opt_track_dest = 0; +ParamField_t opt_track_source = {0}; + +ccp opt_le_alias = 0; +bool opt_engine_valid = false; +u8 opt_engine[3] = {0}; +OffOn_t opt_200cc = OFFON_AUTO; +OffOn_t opt_perfmon = OFFON_AUTO; +OffOn_t opt_custom_tt = OFFON_AUTO; +OffOn_t opt_xpflags = OFFON_AUTO; + +bool opt_complete = false; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX action log /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool LEX_ACTION_LOG ( bool is_patch, const char * format, ... ) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[LEX]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + +// if ( verbose > 2 || KMP_MODE & KMPMD_LOG || is_patch && ~KMP_MODE & KMPMD_SILENT ) + if ( verbose > 2 || is_patch ) + { + fflush(stdout); + fprintf(stdlog," %s>[LEX]%s ", + colset->heading, + is_patch ? colset->highlight : colset->info ); + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint GetNextRacingTrackLE ( uint tid ) +{ + if ( ++tid == 0xff ) + tid++; + + if ( tid > MKW_N_TRACKS && tid < LE_FIRST_LOWER_CT_SLOT ) + tid = LE_FIRST_LOWER_CT_SLOT; + return tid; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTrackSource ( ccp arg, int mode ) +{ + if ( arg && *arg ) + { + if (!opt_track_source.field) + InitializeParamField(&opt_track_source); + AppendParamField(&opt_track_source,arg,false,mode,0); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOpt200cc ( ccp arg ) +{ + const int stat + = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --200cc"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_200cc = stat; + PRINT("200CC=%d\n",opt_200cc); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPerfMon ( ccp arg ) +{ + const int stat + = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --perfmon"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_perfmon = stat; + PRINT("PERFMON=%d\n",opt_perfmon); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCustomTT ( ccp arg ) +{ + const int stat + = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --custom-tt"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_custom_tt = stat; + PRINT("CUSTOM-TT=%d\n",opt_custom_tt); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptXPFlags ( ccp arg ) +{ + const int stat + = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_ON,"Option --xpflags"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_xpflags = stat; + PRINT("XPFLAGS=%d\n",opt_xpflags); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptAlias ( ccp arg ) +{ + if ( arg && *arg ) + { + if (opt_le_alias) + { + const uint len1 = strlen(opt_le_alias); + const uint len2 = strlen(arg); + char *list = MALLOC(len1+len2+2); + memcpy(list,opt_le_alias,len1); + list[len1] = ','; + memcpy(list+len1+1,arg,len2+1); + FreeString(opt_le_alias); + opt_le_alias = list; + } + else + opt_le_alias = STRDUP(arg); + } + PRINT0("OPT ALIAS: %s|\n",opt_le_alias); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptEngine ( ccp arg ) +{ + if ( !arg || !*arg ) + { + opt_engine_valid = false; + return 0; + } + + const uint MAX = sizeof(opt_engine)/sizeof(*opt_engine); + double val[MAX], sum = 0.0; + ccp src = arg; + uint n = 0; + + for(;;) + { + if ( n > MAX ) + break; + char *end; + double d = strtod(src,&end); + if ( !isnormal(d) || d <= 0.0 ) + d = 0.0; + val[n++] = d; + sum += d; + + src = end; + while (isspace((int)*src)) + src++; + if ( *src != ',' ) + break; + src++; + } + + if ( n != 3 ) + return ERROR0(ERR_SYNTAX, + "Invalid argument for option --engine, »a,b,c« expected: %s",arg); + + if (sum > 0.0 ) + { + sum = 100/sum; + uint total = 100; + for ( n = 0; n < MAX-1; n++ ) + { + uint num = double2int( val[n] * sum ); + if ( num > total ) + num = total; + opt_engine[n] = num; + total -= num; + } + opt_engine[MAX-1] += total; + } + else + memset(opt_engine,0,sizeof(opt_engine)); + opt_engine_valid = true; + + PRINT("ENGINE: %g,%g,%g => %u + %u + %u = %u [200cc=%d])\n", + val[0], val[1], val[2], + opt_engine[0], opt_engine[1], opt_engine[2], + opt_engine[0] + opt_engine[1] + opt_engine[2], + opt_200cc ); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsLECODE() +{ + static VarMap_t vm = {0}; + if (!vm.used) + DefineMkwVars(&vm); + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LPAR support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +lpar_mode_t CalcCurrentLparMode ( const le_lpar_t * lp, bool use_limit_param ) +{ + DASSERT(lp); + + if ( use_limit_param && (uint)lp->limit_mode < LPM_AUTOMATIC ) + { + const lpar_mode_t temp = CalcCurrentLparMode(lp,false); + return temp > lp->limit_mode ? temp : lp->limit_mode; + } + + if ( lp->enable_perfmon > 1 ) + return LPM_EXPERIMENTAL; + + return lp->enable_perfmon || !lp->enable_xpflags + ? LPM_TESTING : LPM_PRODUCTIVE; +} + +//----------------------------------------------------------------------------- + +ccp GetLparModeName ( lpar_mode_t lpmode, bool export ) +{ + if (export) + return PrintCircBuf("%u # %s",lpmode,GetLparModeName(lpmode,false)); + + switch (lpmode) + { + case LPM_PRODUCTIVE: return "LE$PRODUCTIVE"; + case LPM_TESTING: return "LE$TESTING"; + case LPM_EXPERIMENTAL: return "LE$EXPERIMENTAL"; + default: return "LE$AUTOMATIC"; + } +} + +//----------------------------------------------------------------------------- + +bool LimitToLparMode ( le_lpar_t * lp, lpar_mode_t lpmode ) +{ + DASSERT(lp); + + bool modified = false; + + if ( lpmode == LPM_PRODUCTIVE ) + { + if ( lp->enable_perfmon > 0 ) + { lp->enable_perfmon = 0; modified = true; } + if (!lp->enable_xpflags) + { lp->enable_xpflags = 1; modified = true; } + } + else if ( lpmode == LPM_TESTING ) + { + if ( lp->enable_perfmon > 1 ) + { lp->enable_perfmon = 1; modified = true; } + } + + return modified; +} + +//----------------------------------------------------------------------------- + +void ClearLparChat ( le_lpar_t * lp ) +{ + DASSERT(lp); + memset(lp->chat_mode_1,0,sizeof(lp->chat_mode_1)); + memset(lp->chat_mode_1,0,sizeof(lp->chat_mode_2)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define RACE0 (CHATMD_N_RACE_1-1) + +const u16 chat_mode_legacy[BMG_N_CHAT] = +{ + 0,0,0,0, 0,0,0,0, // 1 .. 8 + 0,0,0,0, 0,0,0,0, // 9 .. 16 + 0,0,0,0, 0,0,0,0, // 17 .. 24 + 0,0,0,0, 0,0,0,0, // 25 .. 32 + 0,0,0,0, 0,0,0,0, // 33 .. 40 + 0,0,0,0, 0,0,0,0, // 41 .. 48 + 0,0,0,0, 0,0,0,0, // 49 .. 56 + 0,0,0,0, 0,0,0,0, // 57 .. 64 + + CHATMD_ANY_VEHICLE, 0,0,0, // 65 .. 68 + CHATMD_KARTS_ONLY, // 69 + CHATMD_BIKES_ONLY, // 70 + CHATMD_TRACK_BY_HOST, // 71 + CHATMD_ANY_TRACK, // 72 + + RACE0 + 1, // 73 + RACE0 + 2, // 74 + RACE0 + 3, // 75 + RACE0 + 4, // 76 + RACE0 + 5, // 77 + RACE0 + 6, // 78 + RACE0 + 8, // 79 + RACE0 + 10, // 80 + + 0,0,0,0, 0,0,0,0, // 81 .. 88 + 0,0,0,0, 0,0,0,0, // 89 .. 96 +}; + +//----------------------------------------------------------------------------- + +const u16 chat_mode_mkwfun[BMG_N_CHAT] = +{ + 0,0,0,0, 0,0,0,0, // 1 .. 8 + 0,0,0,0, 0,0,0,0, // 9 .. 16 + 0,0,0,0, 0,0,0,0, // 17 .. 24 + 0,0,0,0, 0,0,0,0, // 25 .. 32 + 0,0,0,0, 0,0,0,0, // 33 .. 40 + 0,0,0,0, 0,0,0,0, // 41 .. 48 + 0,0,0,0, 0,0,0,0, // 49 .. 56 + 0,0,0,0, // 57 .. 60 + + CHATMD_RESET_ENGINE, // 61 + CHATMD_USE_ENGINE_1, // 62 + CHATMD_USE_ENGINE_2, // 63 + CHATMD_USE_ENGINE_3, // 64 + + CHATMD_ANY_VEHICLE, // 65 + CHATMD_KARTS_ONLY, // 66 + CHATMD_BIKES_ONLY, // 67 + 0, // 68 + + CHATMD_ANY_TRACK, // 69 + CHATMD_TRACK_BY_HOST, // 70 + 0, // 71 + 0, // 72 + + RACE0 + 1, // 73 + RACE0 + 2, // 74 + RACE0 + 3, // 75 + RACE0 + 4, // 76 + RACE0 + 5, // 77 + RACE0 + 6, // 78 + RACE0 + 8, // 79 + RACE0 + 10, // 80 + + 0,0,0,0, 0,0,0,0, // 81 .. 88 + 0,0,0,0, 0,0,0,0, // 89 .. 96 +}; + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetChatModeName ( u16 mode ) +{ + char buf[100]; + + switch (mode) + { + case CHATMD_OFF: return "CHAT$OFF"; + + case CHATMD_RESET: return "CHAT$RESET"; + + case CHATMD_ANY_TRACK: return "CHAT$ANY_TRACK"; + case CHATMD_TRACK_BY_HOST: return "CHAT$TRACK_BY_HOST"; + + case CHATMD_BLOCK_CLEAR: return "CHAT$BLOCK_CLEAR"; + case CHATMD_BLOCK_DISABLE: return "CHAT$BLOCK_DISABLE"; + case CHATMD_BLOCK_ENABLE: return "CHAT$BLOCK_ENABLE"; + + case CHATMD_ANY_VEHICLE: return "CHAT$ANY_VEHICLE"; + case CHATMD_KARTS_ONLY: return "CHAT$KARTS_ONLY"; + case CHATMD_BIKES_ONLY: return "CHAT$BIKES_ONLY"; + + case CHATMD_RESET_ENGINE: return "CHAT$RESET_ENGINE"; + case CHATMD_USE_ENGINE_1: return "CHAT$USE_ENGINE_1"; + case CHATMD_USE_ENGINE_2: return "CHAT$USE_ENGINE_2"; + case CHATMD_USE_ENGINE_3: return "CHAT$USE_ENGINE_3"; + } + + + //--- vehicles + + if ( mode >= CHATMD_VEHICLE_BEG && mode <= CHATMD_VEHICLE_END ) + { + mode -= CHATMD_VEHICLE_BEG; + + struct mode_t { u8 mode; char name[9]; }; + static const struct mode_t tab[] = + { +// { CHATVEH_ANY, "ANY" }, + + { CHATVEH_ANY_SIZE, "ANY_SIZE" }, + { CHATVEH_SMALL, "SMALL" }, + { CHATVEH_MEDIUM, "MEDIUM" }, + { CHATVEH_LARGE, "LARGE" }, + + { CHATVEH_ANY_TYPE, "ANY_TYPE" }, + { CHATVEH_KART, "KART" }, + { CHATVEH_BIKE, "BIKE" }, + { CHATVEH_OUT_BIKE, "OUT_BIKE" }, + { CHATVEH_IN_BIKE, "IN_BIKE" }, + + { 0, "" }, + }; + + char *dest = buf; + dest[1] = 0; + const struct mode_t *ptr; + for ( ptr = tab; ptr->mode && mode; ptr++ ) + if ( (ptr->mode & mode) == ptr->mode ) + { + mode &= ~ptr->mode; + dest = StringCat2E(dest,buf+sizeof(buf),",VEH$",ptr->name); + } + return PrintCircBuf("chat$vehicles(%s)",buf+1); + } + + + //--- n races + + if ( mode >= CHATMD_N_RACE_1 && mode <= CHATMD_N_RACE_MAX ) + return PrintCircBuf("chat$n_races(%u)",mode-(CHATMD_N_RACE_1-1)); + + + //--- fallback + + return PrintCircBuf("#%x",mode); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeLPAR ( le_lpar_t *lpar, bool load_lpar ) +{ + DASSERT(lpar); + memset(lpar,0,sizeof(*lpar)); + + lpar->limit_mode = LPM__DEFAULT; + + lpar->engine[0] = 10; + lpar->engine[1] = 60; + lpar->engine[2] = 30; + lpar->enable_xpflags= 1; + lpar->block_track = 0; + + if ( load_lpar && opt_lpar ) + LoadLPAR(lpar,false,0,false); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void NormalizeLPAR ( le_lpar_t * lp ) +{ + DASSERT(lp); + + int sum = lp->engine[0] + lp->engine[1] + lp->engine[2]; + if ( sum != 0 && sum != 100 ) + { + double factor = 100.0/sum; + lp->engine[0] = lp->engine[0] * factor; + lp->engine[1] = lp->engine[1] * factor; + sum = lp->engine[0] + lp->engine[1]; + if ( sum > 100 ) + lp->engine[1] -= sum-100; + lp->engine[2] = 100 - lp->engine[0] - lp->engine[1]; + } + + lp->enable_200cc = lp->enable_200cc > 0; + lp->enable_perfmon = lp->enable_perfmon < 2 ? lp->enable_perfmon : 2; + lp->enable_custom_tt = lp->enable_custom_tt > 0; + lp->enable_xpflags = lp->enable_xpflags > 0; + lp->block_track = lp->block_track < LE_MAX_BLOCK_TRACK + ? lp->block_track : LE_MAX_BLOCK_TRACK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void CopyLPAR2Data ( le_analyse_t * ana ) +{ + DASSERT(ana); + + NormalizeLPAR(&ana->lpar); + const le_lpar_t *lp = &ana->lpar; + le_binpar_v1_t *h = ana->param_v1; + + if ( offsetof(le_binpar_v1_t,engine) < ana->param_size ) + memcpy(h->engine,lp->engine,sizeof(h->engine)); + + if ( offsetof(le_binpar_v1_t,enable_200cc) < ana->param_size ) + h->enable_200cc = lp->enable_200cc; + + if ( offsetof(le_binpar_v1_t,enable_perfmon) < ana->param_size ) + h->enable_perfmon = lp->enable_perfmon; + + if ( offsetof(le_binpar_v1_t,enable_custom_tt) < ana->param_size ) + h->enable_custom_tt = lp->enable_custom_tt; + + if ( offsetof(le_binpar_v1_t,enable_xpflags) < ana->param_size ) + h->enable_xpflags = lp->enable_xpflags; + + if ( offsetof(le_binpar_v1_t,block_track) < ana->param_size ) + h->block_track = lp->block_track; + + if ( offsetof(le_binpar_v1_t,chat_mode_1) + sizeof(h->chat_mode_1) <= ana->param_size ) + write_be16n(h->chat_mode_1,lp->chat_mode_1,BMG_N_CHAT); + + if ( offsetof(le_binpar_v1_t,chat_mode_2) + sizeof(h->chat_mode_2) <= ana->param_size ) + write_be16n(h->chat_mode_2,lp->chat_mode_2,BMG_N_CHAT); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextLPAR +( + const le_lpar_t *lpar, // LE-CODE parameters + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(lpar); + DASSERT(fname); + PRINT("SaveTextLEX(%s,%d)\n",fname,set_time); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,fname); + if ( err > ERR_WARNING || !F.f ) + return err; + + + //--- print header + syntax info + + if ( print_header && !brief_count && !export_count ) + fprintf(F.f,text_lpar_head_cr); + else + fprintf(F.f,"%s\r\n",LE_LPAR_MAGIC); + + + //--- print section + + err = WriteSectionLPAR(F.f,lpar); + + + //--- print footer + + fputs(section_end,F.f); + ResetFile(&F,set_time); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp PrintChatMode ( u16 msg, u16 mode1, u16 mode2, int numeric ) +{ + msg++; + if ( mode1 && mode2 && mode1 != mode2 ) + { + if (numeric) + return PrintCircBuf("M%02u = %4u, %4u # %s, %s", + msg, mode1, mode2, + GetChatModeName(mode1), GetChatModeName(mode2) ); + return PrintCircBuf("M%02u = %s, %s", + msg, GetChatModeName(mode1), GetChatModeName(mode2) ); + } + else if ( mode1 || mode2 ) + { + if (!mode1) + mode1 = mode2; + if (numeric) + return PrintCircBuf("M%02u = %4u # %s", + msg, mode1, GetChatModeName(mode1) ); + return PrintCircBuf("M%02u = %s", msg, GetChatModeName(mode1) ); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError WriteSectionLPAR +( + FILE *f, // output files + const le_lpar_t *lpar // LE-CODE parameters +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(f); + DASSERT(lpar); + + //--- general parameters + + fprintf(f,"\r\n#\f\r\n%.79s\r\n\r\n[LECODE-PARAMETERS]\r\n",Hash200); + + const bool verbose = print_header && !brief_count && !export_count; + if (verbose) + fprintf(f,text_lpar_sect_param_cr + ,GetLparModeName(CalcCurrentLparMode(lpar,true),export_count>0) + ,lpar->engine[0],lpar->engine[1],lpar->engine[2] + ,lpar->enable_200cc + ,lpar->enable_perfmon + ,lpar->enable_custom_tt + ,lpar->enable_xpflags + ,LE_MAX_BLOCK_TRACK + ,lpar->block_track + ); + else + fprintf(f, + "\r\n" + "LIMIT-MODE = %s\r\n\r\n" + "ENGINE = %u,%u,%u\r\n" + "ENABLE-200CC = %u\r\n" + "PERF-MONITOR = %u\r\n" + "CUSTOM-TT = %u\r\n" + "XPFLAGS = %u\r\n" + "BLOCK-TRACK = %u\r\n" + ,GetLparModeName(CalcCurrentLparMode(lpar,true),export_count>0) + ,lpar->engine[0],lpar->engine[1],lpar->engine[2] + ,lpar->enable_200cc + ,lpar->enable_perfmon + ,lpar->enable_custom_tt + ,lpar->enable_xpflags + ,lpar->block_track + ); + + + //--- chat modes + + fprintf(f,"\r\n#\f\r\n%.79s\r\n\r\n[CHAT-MESSAGE-MODES]\r\n",Hash200); + + if (verbose) + { + fputs(text_lpar_sect_chat_cr,f); + + int i; + for ( i = 0; i < BMG_N_CHAT; i++ ) + if ( lpar->chat_mode_1[i] || lpar->chat_mode_2[i] ) + break; + if ( i == BMG_N_CHAT ) + fputs(text_lpar_sect_chat_example_cr,f); + } + + int i, last_group = -1; + for ( i = 0; i < BMG_N_CHAT; i++ ) + { + const u16 mode1 = lpar->chat_mode_1[i]; + const u16 mode2 = lpar->chat_mode_2[i]; + if ( mode1 || mode2 ) + { + if ( last_group != i/4 ) + { + last_group = i/4; + fputs("\r\n",f); + } + + fprintf(f,"%s\n",PrintChatMode(i,mode1,mode2,export_count)); + if (export_count) + fprintf(f,"M%02u = %4u # %s\n",i+1,mode1,GetChatModeName(mode1)); + else + fprintf(f,"M%02u = %s\n",i+1,GetChatModeName(mode1)); + } + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// analyse /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetLEAnalyse ( le_analyse_t *ana ) +{ + if (ana) + { + ResetLEAnalyseUsage(ana); + memset(ana,0,sizeof(*ana)); + InitializeLPAR(&ana->lpar,false); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetLEAnalyseUsage ( le_analyse_t *ana ) +{ + if (ana) + { + if (ana->usage) + { + FREE(ana->usage); + ana->usage = 0; + } + ana->usage_size = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError AnalyseLEBinary +( + le_analyse_t *ana, // NULL or destination of analysis + const void *data, // data pointer + uint data_size // data size +) +{ + //--- setup analyse + + le_analyse_t ana0 = {0}; + if (!ana) + { + ResetLEAnalyse(&ana0); + ana = &ana0; + } + memset(ana,0,sizeof(*ana)); + + if ( !data + || data_size < sizeof(le_binary_head_t) + || memcmp(data,LE_BINARY_MAGIC,4) ) + { + return ERR_INVALID_DATA; + } + + le_binary_head_t *head = (le_binary_head_t*)data; + const uint head_size = ntohl(head->size); + if ( head_size > data_size ) + return ERR_INVALID_DATA; + + ana->valid = LE_HEAD_FOUND; + ana->version = head->phase; + ana->data = data; + ana->size = data_size; + ana->head = head; + ana->head_vers = ntohl(head->version); + ana->head_size = head_size; + ana->end_of_data = (u8*)data + head_size; + + uint off_param = 0; + switch(ana->head_vers) + { + case 4: + if ( ana->size > sizeof(le_binary_head_v4_t) ) + { + ana->valid |= LE_HEAD_VALID | LE_HEAD_KNOWN; + off_param = ntohl(ana->head_v4->off_param); + } + break; + + default: + // at least v4 + if ( ana->head_vers > 4 && ana->size > sizeof(le_binary_head_t) ) + { + ana->valid |= LE_HEAD_VALID; + off_param = ntohl(head->off_param); + } + break; + } + + + //--- analyse parameters (prepare) + + typedef struct ptr_ana_t + { + int off_offset; + int off_ptr; + } + ptr_ana_t; + + #undef DEF_TAB + #define DEF_TAB(s,o,p) { offsetof(s,o), offsetof(le_analyse_t,p) }, + + const u8 *ptr_list[8]; // for section-size analysis + uint n_ptr = 0; // number of used pointers + + + //--- analyse parameters + + if ( off_param + && !(off_param&3) + && ana->size >= off_param + sizeof(le_binary_param_t) + && !memcmp(data+off_param,LE_PARAM_MAGIC,4) ) + { + le_binary_param_t *param = (le_binary_param_t*)(data+off_param); + const uint param_size = ntohl(param->size); + if ( off_param + param_size <= data_size + && off_param + param_size <= ana->head_size ) + { + ana->valid |= LE_PARAM_FOUND; + ana->param = param; + ana->param_offset = off_param; + ana->param_vers = ntohl(param->version); + ana->param_size = param_size; + + const uint max_off = ana->head_size - off_param; + + switch(ana->param_vers) + { + case 1: + if ( param_size >= sizeof(le_binpar_v1_35_t) ) + { + static const ptr_ana_t tab[] = + { + DEF_TAB( le_binpar_v1_35_t, off_eod, end_of_data ) + DEF_TAB( le_binpar_v1_35_t, off_cup_par, cup_par ) + DEF_TAB( le_binpar_v1_35_t, off_cup_track, cup_track ) + DEF_TAB( le_binpar_v1_35_t, off_cup_arena, cup_arena ) + DEF_TAB( le_binpar_v1_35_t, off_course_par, course_par ) + DEF_TAB( le_binpar_v1_35_t, off_property, property ) + DEF_TAB( le_binpar_v1_35_t, off_music, music ) + DEF_TAB( le_binpar_v1_35_t, off_flags, flags ) + {-1,-1} + }; + + ana->valid |= LE_PARAM_VALID | LE_PARAM_KNOWN; + le_binpar_v1_35_t *p = (le_binpar_v1_35_t*)(data+off_param); + + const ptr_ana_t *tptr; + u32 min = max_off, max = 0; + for ( tptr = tab; tptr->off_offset >= 0; tptr++ ) + { + const u32 offset = be32((u8*)p+tptr->off_offset); + if ( offset && offset < max_off ) + { + if ( min > offset ) min = offset; + if ( max < offset ) max = offset; + u8 *ptr = (u8*)data + off_param + offset; + *(u8**)((u8*)ana+tptr->off_ptr) = ptr; + DASSERT( n_ptr < sizeof(ptr_list)/sizeof(*ptr_list) ); + ptr_list[n_ptr++] = ptr; + } + } + + ana->beg_of_data = (u8*)p + min; + ana->end_of_data = (u8*)p + max; + + memcpy(ana->lpar.engine,p->engine,sizeof(ana->lpar.engine)); + ana->lpar.enable_200cc = p->enable_200cc; + ana->lpar.enable_perfmon = p->enable_perfmon; + } + + if ( param_size >= sizeof(le_binpar_v1_37_t) ) + { + le_binpar_v1_37_t *p = (le_binpar_v1_37_t*)(data+off_param); + ana->lpar.enable_custom_tt = p->enable_custom_tt; + ana->lpar.enable_xpflags = p->enable_xpflags; + } + + if ( param_size >= sizeof(le_binpar_v1_f8_t) ) + { + le_binpar_v1_f8_t *p = (le_binpar_v1_f8_t*)(data+off_param); + ana->lpar.block_track = p->block_track; + be16n(ana->lpar.chat_mode_1,p->chat_mode,BMG_N_CHAT); + } + else + memcpy(ana->lpar.chat_mode_1,chat_mode_legacy,sizeof(ana->lpar.chat_mode_1)); + break; + + if ( param_size >= sizeof(le_binpar_v1_1b8_t) ) + { + le_binpar_v1_1b8_t *p = (le_binpar_v1_1b8_t*)(data+off_param); + be16n(ana->lpar.chat_mode_2,p->chat_mode_2,BMG_N_CHAT); + } + break; + + default: + // at least v1 + if ( ana->param_vers > 1 && param_size >= sizeof(le_binary_param_t) ) + { + ana->valid |= LE_PARAM_VALID; + } + break; + } + } + + ana->n_cup_track = ana->cup_par ? ntohl(ana->cup_par->n_racing_cups) : 0; + ana->n_cup_arena = ana->cup_par ? ntohl(ana->cup_par->n_battle_cups) : 0; + ana->n_slot = ana->course_par ? ntohl(ana->course_par->n_slot) : 0; + } + + + if (ana->end_of_data) + { + const int size = ana->data + ana->size - ana->end_of_data; + if ( size >= 0 ) + { + ana->bin_data = ana->end_of_data; + ana->bin_size = size; + } + } + + + //--- analyse n+max + + typedef struct ana_n_max_t + { + int off_ptr; + int off_n; + int off_max; + uint elem_size; + } + ana_n_max_t; + + #undef DEF_TAB + #define DEF_TAB(p,n,m,s) { offsetof(le_analyse_t,p), \ + offsetof(le_analyse_t,n), \ + offsetof(le_analyse_t,m), \ + sizeof(*ana->p)*s }, + + static const ana_n_max_t n_max_tab[] = + { + DEF_TAB( cup_track, n_cup_track, max_cup_track, 4 ) + DEF_TAB( cup_arena, n_cup_arena, max_cup_arena, 5 ) + DEF_TAB( property, n_slot, max_property, 1 ) + DEF_TAB( music, n_slot, max_music, 1 ) + DEF_TAB( flags, n_slot, max_flags, 1 ) + {-1,-1,-1} + }; + + #undef DEF_TAB + + const ana_n_max_t *pnm; + + // initialize max + for ( pnm = n_max_tab; pnm->off_ptr >= 0; pnm++ ) + *(uint*)((u8*)ana+pnm->off_max) = ~0; + + // calculate max + for ( pnm = n_max_tab; pnm->off_ptr >= 0; pnm++ ) + { + const u8 *ptr = *(u8**)((u8*)ana+pnm->off_ptr); + const u8 *end = data + ana->head_size; + uint i; + for ( i = 0; i < n_ptr; i++ ) + if ( ptr_list[i] > ptr && ptr_list[i] < end ) + end = ptr_list[i]; + + const uint max = (end-ptr) / pnm->elem_size; + if ( *(uint*)((u8*)ana+pnm->off_max) > max ) + *(uint*)((u8*)ana+pnm->off_max) = max; + } + + CalculateStatsLE(ana); + + + //--- return + + return ana->valid & LE_PARAM_VALID ? ERR_OK : + ana->valid & LE_HEAD_VALID ? ERR_MISSING_PARAM + : ERR_INVALID_DATA; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalculateStatsLE ( le_analyse_t *ana ) +{ + ResetLEAnalyseUsage(ana); + + ana->used_rslots = 0; + ana->max_rslots = 0; + ana->used_bslots = 0; + ana->max_bslots = 0; + + ana->max_slot = ana->max_property < ana->max_music + ? ana->max_property : ana->max_music; + if ( ana->max_slot > ana->max_flags ) + ana->max_slot = ana->max_flags; + if ( ana->max_slot < LE_FIRST_LOWER_CT_SLOT ) + return; + + + //-- count racing slots + + uint used = 0, total = 0, slot; + for ( slot = 0; slot < MKW_N_TRACKS; slot++ ) + { + total++; + if (IsLESlotUsed(ana,slot)) + used++; + } + + for ( slot = LE_FIRST_LOWER_CT_SLOT; slot < ana->n_slot; slot++ ) + { + total++; + if (IsLESlotUsed(ana,slot)) + used++; + } + ana->used_rslots = used; + ana->max_rslots = total; + + + //-- count battle slots + + used = total = 0; + for ( slot = MKW_ARENA_BEG; slot < MKW_ARENA_END; slot++ ) + { + total++; + if (IsLESlotUsed(ana,slot)) + used++; + } + ana->used_bslots = used; + ana->max_bslots = total; +} + +/////////////////////////////////////////////////////////////////////////////// + +const le_usage_t * GetLEUsage ( const le_analyse_t *ana0, bool force_recalc ) +{ + le_analyse_t *ana = (le_analyse_t*)ana0; + if ( force_recalc && ana->usage ) + ResetLEAnalyseUsage(ana); + + if ( ana->usage || ana->max_slot < LE_FIRST_LOWER_CT_SLOT ) + return ana->usage; + + ana->usage_size = ana->n_slot > 0x100 ? ana->n_slot : 0x100; + le_usage_t *usage = CALLOC(ana->usage_size,sizeof(*usage)); + ana->usage = usage; + memset(usage,LEU_S_UNUSED,ana->usage_size); + + + //-- special slots + + memset(usage+0x36,LEU_S_SPECIAL,5); + usage[0x42] = LEU_S_SPECIAL; + usage[0x43] = LEU_S_NETWORK|LEU_F_ONLINE; + + + //-- random slots + + memset( usage + LE_FIRST_RANDOM_SLOT, + LEU_S_WIIMM|LEU_F_ONLINE, + LE_LAST_RANDOM_SLOT - LE_FIRST_RANDOM_SLOT+1 ); + + + //-- check racing slots + + uint slot; + for ( slot = 0; slot < MKW_N_TRACKS; slot++ ) + usage[slot] = IsLESlotUsed(ana,slot) + ? LEU_S_TRACK|LEU_F_ONLINE : LEU_F_ONLINE; + + for ( slot = LE_FIRST_LOWER_CT_SLOT; slot < ana->usage_size; slot++ ) + { + if (IsLESlotUsed(ana,slot)) + { + const le_flags_t flags = ana->flags ? ana->flags[slot] : 0; + usage[slot] = flags & LETF_ALIAS ? LEU_S_ALIAS + : flags & LETF_RND_HEAD ? LEU_S_TRACK_RND + : flags & LETF_RND_GROUP ? LEU_S_TRACK_HIDE + : LEU_S_TRACK; + } + + if ( ana->version > 1 || slot < 0xff + || slot >= LE_FIRST_UPPER_CT_SLOT && slot <= LE_LAST_UPPER_CT_SLOT ) + usage[slot] |= LEU_F_ONLINE; + } + + + //-- check battle slots + + for ( slot = MKW_ARENA_BEG; slot < MKW_ARENA_END; slot++ ) + { + if (IsLESlotUsed(ana,slot)) + { + const le_flags_t flags = ana->flags ? ana->flags[slot] : 0; + usage[slot] = flags & LETF_ALIAS ? LEU_S_ALIAS + : flags & LETF_RND_HEAD ? LEU_S_ARENA_RND + : flags & LETF_RND_GROUP ? LEU_S_ARENA_HIDE + : LEU_S_ARENA; + } + } + + + //--- override Network.random + + if ( ana->version == 1 ) + usage[0xff] = LEU_S_NETWORK|LEU_F_ONLINE; + + return usage; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const char usage_ch[] = "-AraTRtsn*W>----"; + +char GetLEUsageChar ( le_usage_t usage ) +{ + return usage_ch[usage & LEU_S_MASK]; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetLEUsageCharCol ( le_usage_t usage, const ColorSet_t *colset, ccp * prev_col ) +{ + const char ch = usage_ch[usage & LEU_S_MASK]; + + if ( colset && colset->colorize ) + { + ccp col; + switch ( usage & LEU_S_MASK ) + { + case LEU_S_UNUSED: col = colset->reset; break; + + case LEU_S_ARENA: col = colset->b_cyan; break; + case LEU_S_ARENA_RND: col = colset->b_yellow; break; + case LEU_S_ARENA_HIDE: col = colset->b_green; break; + case LEU_S_TRACK: col = colset->b_cyan_blue; break; + case LEU_S_TRACK_RND: col = colset->b_yellow; break; + case LEU_S_TRACK_HIDE: col = colset->green_cyan; break; + + case LEU_S_SPECIAL: col = colset->reset; break; + case LEU_S_NETWORK: col = colset->b_orange; break; + case LEU_S_RANDOM: col = colset->b_yellow; break; + case LEU_S_WIIMM: col = colset->b_orange; break; + case LEU_S_ALIAS: col = colset->b_red; break; + default: col = colset->b_magenta; break; + } + + if ( !prev_col || *prev_col != col ) + { + if (prev_col) + *prev_col = col; + const int clen = strlen(col); + char *buf = GetCircBuf(clen+2); + memcpy(buf,col,clen); + buf[clen] = ch; + buf[clen+1] = 0; + return buf; + } + } + + return CopyCircBuf0(&ch,1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum +{ + CHECK_OFF, // don't check + CHECK_TRACK, // check for racing tracks + CHECK_ARENA // check for battle arenas +} +slot_check_t; + +//----------------------------------------------------------------------------- + +ccp GetLEValid ( le_valid_t valid ) +{ + char *buf = GetCircBuf(7); + DASSERT(buf); + + buf[0] = valid & LE_HEAD_FOUND ? 'F' : '-'; + buf[1] = valid & LE_HEAD_VALID ? 'V' : '-'; + buf[2] = valid & LE_HEAD_KNOWN ? 'K' : '-'; + buf[3] = valid & LE_PARAM_FOUND ? 'f' : '-'; + buf[4] = valid & LE_PARAM_VALID ? 'v' : '-'; + buf[5] = valid & LE_PARAM_KNOWN ? 'k' : '-'; + buf[6] = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +static ccp GetSlotInfo +( + const le_analyse_t *ana, // valid structure + int tid, // track id + ColorSet_t *col, // NULL or color set for warnings + slot_check_t check, // check modus + uint *warnings // not NULL: increment on warnings +) +{ + if ( tid < ana->n_slot ) + { + uint warn = 0; + + //-- property slot + + ccp prop1 = "", prop0 = ""; + const int prop = ana->property ? ana->property[tid] : -1; + ccp propx = GetMkwTrackName3(prop); + if ( check == CHECK_TRACK && ( IsMkwArena(tid) || IsMkwArena(prop) ) + || check == CHECK_ARENA && ( !IsMkwArena(tid) || !IsMkwArena(prop) ) + ) + { + warn |= 2; + if (col) + { + prop1 = col->bad; + prop0 = col->reset; + } + } + else if ( IsMkwOriginal(tid) && tid != prop ) + { + warn |= 1; + if (col) + { + prop1 = col->hint; + prop0 = col->reset; + } + } + + + //-- music slot + + ccp music1 = "", music0 = ""; + const int music = ana->music ? ana->music[tid] : -1; + ccp musicx = GetMkwMusicName3(music); + if ( tid < MKW_ORIGINAL_END || tid >= LE_FIRST_LOWER_CT_SLOT ) + { + if ( music < MKW_MUSIC_MIN_ID || music > MKW_MUSIC_MAX_ID ) + { + warn |= 2; + if (col) + { + music1 = col->bad; + music0 = col->reset; + } + } + else if (!(music&1)) + { + warn |= 1; + if (col) + { + music1 = col->hint; + music0 = col->reset; + } + } + } + + + //-- flags slot + + ccp flags; + char flags_buf[8]; + bool is_alias = false; + if (ana->flags) + { + const le_flags_t fl = ana->flags[tid]; + if ( fl & ~LETF__ALL ) + snprintf(flags_buf,sizeof(flags_buf),"?%03x",fl); + else + { + if ( fl & LETF_ALIAS ) + is_alias = true; + + snprintf(flags_buf,sizeof(flags_buf),"%c%c%c%c", + fl & LETF_NEW ? 'N' : '-', + fl & LETF_RND_HEAD ? 'H' : '-', + fl & LETF_RND_GROUP ? 'G' : '-', + fl & LETF_ALIAS ? 'A' : '-' ); + } + flags = flags_buf; + } + else + flags = "-?-"; + + static const char warn_list[] = " %4x %s,%s] ", + col->differ, TrackByAliasLE(prop,music), + col->reset, flags ) + : snprintf(buf,sizeof(buf),"[%s%s%s,%s%s%s,%s]%c", + prop1, propx, prop0, + music1, musicx, music0, + flags, warn_list[warn] ); + if ( len > 0 ) + return CopyCircBuf0(buf,len); + } + + if (col) + { + static char fail[40] = {0}; + if (!*fail) + snprintf(fail,sizeof(fail),"[%s---,---,----%s]!",col->bad,col->reset); + return fail; + } + return "[---,---,---]!"; +} + +//----------------------------------------------------------------------------- + +static le_cup_track_t * DumpLECup +( + FILE *f, // print file + const le_analyse_t *ana, // valid structure + const le_cup_track_t *cp, // pointer to cup + uint n_track, // number of tracks in cup + ColorSet_t *col, // NULL or color set for warnings + slot_check_t check, // check modus + uint *warnings, // not NULL: store numebr of tracks warnings + u16 *done // not NULL: mark slot as done +) +{ + if (warnings) + *warnings = 0; + + uint tr; + for ( tr = 0; tr < n_track; tr++ ) + { + const uint tid = htonl(*cp++); + fprintf(f," %3x%s",tid,GetSlotInfo(ana,tid,col,check,warnings)); + + if ( done && tid < ana->n_slot ) + done[tid]++; + } + return (le_cup_track_t*)cp; +} + +//----------------------------------------------------------------------------- + +static void DumpLETracks + ( FILE *f, uint indent, ColorSet_t *col, const le_analyse_t *ana ) +{ + DASSERT(f); + DASSERT(indent>=2); + DASSERT(col); + DASSERT(ana); + + u16 *done = CALLOC(sizeof(*done),ana->n_slot); + DASSERT(done); + if ( ana->version == 1 ) + { + uint max = ana->n_slot < LE_FIRST_UPPER_CT_SLOT + ? ana->n_slot : LE_FIRST_UPPER_CT_SLOT; + uint i; + for ( i = 0xff; i < max; i++ ) + done[i]++; + } + + static const char id_info[] = "id[prop,music,new+head+group+alias]"; + + + //--- battle cups + + if ( ana->n_cup_track && ana->cup_track ) + { + fprintf(f,"\n%*s%s" "%u cup%s with battle tracks (%s):%s\n", + indent-2,"", col->heading, + ana->n_cup_arena, ana->n_cup_arena == 1 ? "" : "s", + id_info, col->reset ); + + uint cup, hidden = 0; + const le_cup_track_t *cp = ana->cup_arena; + for ( cup = 0; cup < ana->n_cup_arena; cup++ ) + { + if ( cp[0] || cp[1] || cp[2] || cp[3] || cp[4] ) + { + fprintf(f,"%*s" "Cup %2u:",indent,"",cup); + uint warnings; + DumpLECup(f,ana,cp,5,col,CHECK_ARENA,&warnings,done); + if (warnings) + fputs(" <<<\n",f); + else + fputc('\n',f); + } + else + hidden++; + cp += 5; + } + if (hidden) + fprintf(f,"%*s > %u unused battle cup%s not listed.\n", + indent,"", hidden, hidden==1 ? "" : "s" ); + } + + + //--- racing cups + + if ( ana->n_cup_track && ana->cup_track ) + { + fprintf(f,"\n%*s%s" "%u cup%s with racing tracks (%s):%s\n", + indent-2,"", col->heading, + ana->n_cup_track, ana->n_cup_track == 1 ? "" : "s", + id_info, col->reset ); + + uint cup, hidden = 0; + const le_cup_track_t *cp = ana->cup_track; + for ( cup = 0; cup < ana->n_cup_track; cup++ ) + { + if ( cp[0] || cp[1] || cp[2] || cp[3] ) + { + fprintf(f,"%*s" "Cup %2u:",indent,"",cup); + uint warnings; + DumpLECup(f,ana,cp,4,col,CHECK_TRACK,&warnings,done); + if (warnings) + fputs(" <<<\n",f); + else + fputc('\n',f); + } + else + hidden++; + cp += 4; + } + if (hidden) + fprintf(f,"%*s > %u unused racing cup%s not listed.\n", + indent,"", hidden, hidden==1 ? "" : "s" ); + } + + + //--- count multi/group/unused slots + + #if 0 && defined(TEST) // for tests only + done[15] = 99; + done[16] = 0; + done[17] = 99; + done[18] = 0; + #endif + + uint i, max_used_slot = 0, n_multi = 0, n_group = 0, n_unused = 0; + for ( i = 0; i < ana->n_slot; i++ ) + if (IsRacingTrackLE(i)) + { + if (done[i]) + { + max_used_slot = i; + if ( done[i] > 1 ) + n_multi++; + } + else if ( !done[i] ) + { + if ( ana->flags && ana->flags[i] & LETF_RND_GROUP ) + n_group++; + else + n_unused++; + } + } + PRINT0("m=%d, g=%d, u=%d /%d\n",n_multi,n_group,n_unused,max_used_slot); + + + //--- multi used tracks + + if (n_multi) + { + fprintf(f,"\n%*s%s" "%u multiple used track slot%s (%s):%s\n", + indent-2,"", col->heading, + n_multi, n_multi == 1 ? "" : "s", + id_info, col->reset ); + + for ( i = 0; i < ana->n_slot; i++ ) + if ( done[i] > 1 && IsRacingTrackLE(i) ) + fprintf(f,"%*sSlot %4d/dec : %3x%s : %3ux\n", + indent,"", i, i, GetSlotInfo(ana,i,col,CHECK_OFF,0), done[i]); + } + + + //--- random group tracks + + if (n_group) + { + fprintf(f,"\n%*s%s" "%u track slot%s reserved for random groups (%s):%s\n", + indent-2,"", col->heading, + n_group, n_group == 1 ? "" : "s", + id_info, col->reset ); + + for ( i = 0; i < ana->n_slot; i++ ) + if ( !done[i] && IsRacingTrackLE(i) && ana->flags[i] & LETF_RND_GROUP ) + { + done[i]++; + fprintf(f,"%*sSlot %4d/dec : %3x%s\n", + indent,"", i, i, GetSlotInfo(ana,i,col,CHECK_OFF,0)); + } + } + + + //--- unused used track slots + + if ( n_unused) + { + fprintf(f,"\n%*s%s" "%u unused track slot%s (%s):%s\n", + indent-2,"", col->heading, + n_unused, n_unused == 1 ? "" : "s", + id_info, col->reset ); + for ( i = 0; i <= max_used_slot; i++ ) + { + if ( !done[i] && IsRacingTrackLE(i) ) + fprintf(f,"%*sSlot %4d/dec : %3x%s\n", + indent,"", i, i, GetSlotInfo(ana,i,col,CHECK_OFF,0) ); + } + + + uint first_unused_slot = max_used_slot + 1; + if (!IsRacingTrackLE(first_unused_slot)) + first_unused_slot = LE_FIRST_LOWER_CT_SLOT; + const int unused_slots = ana->n_slot - first_unused_slot; + if ( unused_slots > 0 ) + fprintf(f,"%*s > %u slot%s beginning with slot %u (%x/hex) are not used.\n", + indent,"", + unused_slots, unused_slots == 1 ? "" : "s", + first_unused_slot, first_unused_slot ); + } + + + //--- term + + FREE(done); +} + +//----------------------------------------------------------------------------- + +static void DumpLEUsageMap + ( FILE *f, uint indent, ColorSet_t *col, const le_analyse_t *ana ) +{ + DASSERT(f); + DASSERT(indent>=2); + DASSERT(col); + DASSERT(ana); + + const le_usage_t *usage = GetLEUsage(ana,false); + if (!usage) + return; + + fprintf(f,"\n%*s%s" "Slot usage map for %u slots%s\n" + "%*s" "%s%s=Arena, %s%s=Random Arena, %s%s=Hidden Arena, " + "%s%s=Wiimm Cup (random), %s%s=Alias,%s\n" + "%*s" "%s%s=Track, %s%s=Random Track, %s%s=Hidden Track, " + "%s%s=Network, %s%s=Special, %s%s=unused.%s\n", + indent-2,"", col->heading, ana->n_slot, col->reset, + indent,"", + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ARENA, col,0 ), col->info, + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ARENA_RND, col,0 ), col->info, + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ARENA_HIDE, col,0 ), col->info, + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_WIIMM, col,0 ), col->info, + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_ALIAS, col,0 ), col->info, + col->reset, + indent,"", + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_TRACK, col,0 ), col->info, + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_TRACK_RND, col,0 ), col->info, + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_TRACK_HIDE, col,0 ), col->info, + GetLEUsageCharCol( LEU_F_ONLINE | LEU_S_NETWORK, col,0 ), col->info, + GetLEUsageCharCol( LEU_S_SPECIAL, col,0 ), col->info, + GetLEUsageCharCol( LEU_S_UNUSED, col,0 ), col->info, + col->reset ); + + uint slot; + ccp prev_col = 0; + for ( slot = 0; slot < ana->usage_size; slot++ ) + { + if (!(slot&63)) + { + fprintf(f,"%s\n%*s%3x: ",col->reset,indent,"",slot); + prev_col = 0; + } + else if (!(slot&7)) + fputc(' ',f); + fputs(GetLEUsageCharCol(*usage++,col,&prev_col),f); + } + fprintf(f,"%s\n\n",col->reset); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpLEAnalyse ( FILE *f, uint indent, const le_analyse_t *ana ) +{ + DASSERT(f); + DASSERT(ana); + indent = NormalizeIndent(indent); + + ColorSet_t col; + SetupColorSet(&col,f); + + fprintf(f,"%*s%sLE-CODE binary, valid=%s, file size: %x/hex = %u%s\n", + indent,"", + col.caption, GetLEValid(ana->valid), ana->size, ana->size, + col.reset ); + indent += 4; + + #if 0 // TEST only! + { + static int done = 0; + if (!done++) + { + int i; + for ( i = -1; i < MKW_MUSIC_MAX_ID-MKW_MUSIC_MIN_ID + 4; i++ ) + { + uint m = i < 0 ? i : i + MKW_MUSIC_MIN_ID - 2; + printf(" %3d = 0x%02x : %-8s / %3d = 0x%02x : %s\n", + i, i&0xff, GetMkwTrackName3(i), m, m&0xff, GetMkwMusicName3(m) ); + } + + #if 0 + for ( i = 0; i <= MKW_MUSIC_MAX_ID - MKW_MUSIC_MIN_ID; i+=2 ) + { + ccp name = GetMkwTrackName3(music_info[i/2].track); + printf("\"%s\", \"%c%s\", \n",name,tolower(*name),name+1); + } + #endif + } + } + #endif + + + //--- file header + + if ( ana->valid & LE_HEAD_VALID ) + { + const le_binary_head_t *h = ana->head; + DASSERT(h); + fprintf(f, + "\n%*s%s" "File header:%s\n" + "%*s" "Magic: %.4s\n" + "%*s" "Version: %u\n" + "%*s" "Build number: %u\n" + "%*s" "Base address: %8x/hex\n" + "%*s" "Entry point: %8x/hex\n" + "%*s" "Total size: %8x/hex = %u\n" + "%*s" "Offset param: %8x/hex\n" + "%*s" "Region code: %c (%s)\n" + "%*s" "Debug flag: %c (%s)\n" + "%*s" "LE version/phase: %u\n" + ,indent-2,"", col.heading, col.reset + ,indent,"", h->magic + ,indent,"", ntohl(h->version) + ,indent,"", ntohl(h->build_number) + ,indent,"", ntohl(h->base_address) + ,indent,"", ntohl(h->entry_point) + ,indent,"", ntohl(h->size), ntohl(h->size) + ,indent,"", ntohl(h->off_param) + ,indent,"", h->region + ,h->region == 'P' ? "PAL" + :h->region == 'E' ? "USA" + :h->region == 'J' ? "Japan" + :h->region == 'K' ? "Korea" + : "?" + ,indent,"", h->debug + ,h->debug == 'R' ? "Release" + :h->debug == 'D' ? "Debug" + : "?" + ,indent,"", h->phase + ); + } + + switch (ana->head_vers) + { + case 4: + { + const le_binary_head_v4_t *h = ana->head_v4; + DASSERT(h); + fprintf(f, + "%*s" "Timestamp: %s\n" + ,indent,"", h->timestamp + ); + } + break; + + default: + fprintf(f,"%*s" ">>> other parameters unknown!\n",indent,""); + break; + } + + + //--- parameters + + if ( ana->valid & LE_PARAM_VALID ) + { + const le_binary_param_t *h = ana->param; + DASSERT(h); + fprintf(f, + "\n%*s%s" "Parameters:%s\n" + "%*s" "Magic: %.4s\n" + "%*s" "Version: %u\n" + "%*s" "Param size: %x/hex = %u\n" + ,indent-2,"", col.heading, col.reset + ,indent,"", h->magic + ,indent,"", ntohl(h->version) + ,indent,"", ntohl(h->size), ntohl(h->size) + ); + } + + switch (ana->param_vers) + { + case 1: + { + const le_binpar_v1_t *h = ana->param_v1; + DASSERT(h); + fprintf(f, + "%*s" "Engine chances: " + "%u + %u + %u = %u (1%s0cc,mirror)\n" + "%*s" "200cc: %u (%sabled)\n" + "%*s" "Perform. monitor: %u (%s)\n" + ,indent,"" + ,h->engine[0], h->engine[1], h->engine[2] + ,h->engine[0] + h->engine[1] + h->engine[2] + ,h->enable_200cc ? "50cc,20" : "00cc,15" + ,indent,"", h->enable_200cc, h->enable_200cc ? "en" :"dis" + ,indent,"", h->enable_perfmon, + h->enable_perfmon > 1 ? "Wii+Dolphin" + : h->enable_perfmon ? "Wii only" :"disabled" + ); + + if ( ana->param_size >= sizeof(le_binpar_v1_37_t) ) + fprintf(f, + "%*s" "Custom Timetrial: %u (%sabled)\n" + "%*s" "Extended P-Flags: %u (%sabled)\n" + "%*s" "Block tracks for: %u races\n" + ,indent,"", h->enable_custom_tt, h->enable_custom_tt ? "en" :"dis" + ,indent,"", h->enable_xpflags, h->enable_xpflags ? "en" :"dis" + ,indent,"", h->block_track + ); + + #if defined(DEBUG) && defined(TEST) && 0 + const uint po = ana->param_offset; + fprintf(f, + "%*s" "Offset cup params: %5x/hex => %5x/hex\n" + "%*s" "Offset cup tracks: %5x/hex => %5x/hex\n" + "%*s" "Offset cup arenas: %5x/hex => %5x/hex\n" + "%*s" "Offset course par: %5x/hex => %5x/hex\n" + "%*s" "Offset properties: %5x/hex => %5x/hex\n" + "%*s" "Offset music list: %5x/hex => %5x/hex\n" + ,indent,"", ntohl(h->off_cup_par), ntohl(h->off_cup_par) + po + ,indent,"", ntohl(h->off_cup_track), ntohl(h->off_cup_track) + po + ,indent,"", ntohl(h->off_cup_arena), ntohl(h->off_cup_arena) + po + ,indent,"", ntohl(h->off_course_par), ntohl(h->off_course_par) + po + ,indent,"", ntohl(h->off_property), ntohl(h->off_property) + po + ,indent,"", ntohl(h->off_music), ntohl(h->off_music) + po + ); + #endif + + + const bool hard_coded = ana->param_size < sizeof(le_binpar_v1_f8_t); + const u16 *chat_mode_1 = hard_coded ? chat_mode_legacy : ana->lpar.chat_mode_1; + const u16 *chat_mode_2 = hard_coded ? chat_mode_legacy : ana->lpar.chat_mode_2; + + uint i, n_special = 0; + for ( i = 0; i < BMG_N_CHAT; i++ ) + if ( chat_mode_1[i] || chat_mode_2[i] ) + n_special++; + + if (n_special) + { + fprintf(f,"\n%*s%s" "%u special chat message%s%s:%s\n", + indent-2,"", col.heading, + n_special, n_special == 1 ? "" : "s", + hard_coded ? " (hard coded)" : "", + col.reset ); + + for ( i = 0; i < BMG_N_CHAT; i++ ) + { + ccp info = PrintChatMode(i,chat_mode_1[i],chat_mode_2[i],0); + if (info) + fprintf(f,"%*s%s\n", indent,"", info ); + } + } + } + break; + + default: + fprintf(f,"%*s" ">>> other parameters unknown!\n",indent,""); + break; + } + + + //--- section offsets + + char fail[50]; + snprintf(fail,sizeof(fail)," %s FAIL! %s",col.fail2,col.reset); + + fprintf(f,"\n%*s%s" "Sections (offset,n/max):%s\n", + indent-2,"", col.heading, col.reset ); + + if ( ana->cup_par ) + fprintf(f, + "%*s" "Cup parameters: %5x/hex\n", + indent,"", PTR_DISTANCE_INT(ana->cup_par,ana->data) ); + + if ( ana->cup_track ) + fprintf(f, + "%*s" "Racing track cups: %5x/hex, %4u / %4u%s\n", + indent,"", PTR_DISTANCE_INT(ana->cup_track,ana->data), + ana->n_cup_track, ana->max_cup_track, + ana->n_cup_track > ana->max_cup_track ? fail : "" ); + + if ( ana->cup_arena ) + fprintf(f, + "%*s" "Battle arena cups: %5x/hex, %4u / %4u%s\n", + indent,"", PTR_DISTANCE_INT(ana->cup_arena,ana->data), + ana->n_cup_arena, ana->max_cup_arena, + ana->n_cup_arena > ana->max_cup_arena ? fail : "" ); + + if ( ana->course_par ) + fprintf(f, + "%*s" "Course parameters: %5x/hex\n", + indent,"", PTR_DISTANCE_INT(ana->course_par,ana->data) ); + + if ( ana->property ) + fprintf(f, + "%*s" "Property list: %5x/hex, %4u / %4u%s\n", + indent,"", PTR_DISTANCE_INT(ana->property,ana->data), + ana->n_slot, ana->max_property, + ana->n_slot > ana->max_property ? fail : "" ); + + if ( ana->music ) + fprintf(f, + "%*s" "Music list: %5x/hex, %4u / %4u%s\n", + indent,"", PTR_DISTANCE_INT(ana->music,ana->data), + ana->n_slot, ana->max_music, + ana->n_slot > ana->max_music ? fail : "" ); + + if ( ana->flags ) + fprintf(f, + "%*s" "Flags list: %5x/hex, %4u / %4u%s\n", + indent,"", PTR_DISTANCE_INT(ana->flags,ana->data), + ana->n_slot, ana->max_flags, + ana->n_slot > ana->max_flags ? fail : "" ); + + if ( ana->end_of_data ) + fprintf(f, + "%*s" "End of data: %5x/hex\n", + indent,"", PTR_DISTANCE_INT(ana->end_of_data,ana->data) ); + + fprintf(f, + "%*s" "End of file: %5x/hex\n", + indent,"", ana->size ); + + + //--- settings + + if ( ana->cup_par || ana->course_par ) + { + fprintf(f,"\n%*s%s" "Cup and course parameters (n/max):%s\n", + indent-2,"", col.heading, col.reset ); + + if ( ana->cup_par ) + fprintf(f, + "%*s" "Cup magic: %s\n" + "%*s" "Racing track cups: %4u / %4u%s\n" + "%*s" "Battle arena cups: %4u / %4u%s\n" + ,indent,"", ana->cup_par->magic + ,indent,"", ana->n_cup_track, ana->max_cup_track, + ana->n_cup_track > ana->max_cup_track ? fail : "" + ,indent,"", ana->n_cup_arena, ana->max_cup_arena, + ana->n_cup_arena > ana->max_cup_arena ? fail : "" + ); + + if ( ana->course_par ) + fprintf(f, + "%*s" "Course magic: %s\n" + "%*s" "Total track slots: %4u / %4u%s\n" + "%*s" "Used racing slots: %4u / %4u\n" + "%*s" "Used battle slots: %4u / %4u\n" + ,indent,"", ana->course_par->magic + ,indent,"", ana->n_slot, ana->max_slot, + ana->n_slot > ana->max_slot ? fail : "" + ,indent,"", ana->used_rslots, ana->max_rslots + ,indent,"", ana->used_bslots, ana->max_bslots + ); + } + + if ( long_count > 0 ) + DumpLETracks(f,indent,&col,ana); + if ( verbose > 0 ) + DumpLEUsageMap(f,indent,&col,ana); + + fputc('\n',f); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// copy track files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CopyTrackFile ( uint tid, ccp name ) +{ + if ( opt_track_dest + && IsDirectory(opt_track_dest,false) + && opt_track_source.used ) + { + char src[PATH_MAX]; + char dest[PATH_MAX]; + + ParamFieldItem_t *ptr = opt_track_source.field, *end; + for ( end = ptr + opt_track_source.used; ptr < end; ptr++ ) + { + PathCatBufPPE(src,sizeof(src),ptr->key,name,".szs"); + + struct stat st; + if ( !stat(src,&st) && S_ISREG(st.st_mode) ) + { + snprintf(dest,sizeof(dest),"%s/%03x.szs",opt_track_dest,tid); + if (TransferFile(src,dest,ptr->num,0666)) + { + PathCatBufPPE(src,sizeof(src),ptr->key,name,"_d.szs"); + if ( !stat(src,&st) && S_ISREG(st.st_mode) ) + { + snprintf(dest,sizeof(dest),"%s/%03x_d.szs",opt_track_dest,tid); + TransferFile(src,dest,ptr->num,0666); + } + } + break; + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LECODE file /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool le_file_done = false; +static ctcode_t *le_file = 0; + +/////////////////////////////////////////////////////////////////////////////// + +const ctcode_t * LoadLEFile ( uint le_phase ) +{ + const ct_mode_t ct_mode = le_phase == 2 ? CTM_LECODE2 : CTM_LECODE1; + if ( le_file && le_file->ctb.ct_mode != ct_mode ) + { + // rescan nessessary + ResetCTCODE(le_file); + FREE(le_file); + le_file = 0; + le_file_done = false; + } + + if ( !le_file_done && opt_le_define && *opt_le_define ) + { + PRINT("le=%d, ctm=%d[%s]\n", + le_phase, ct_mode, GetCtModeNameBMG(ct_mode,true) ); + if ( logging > 1 ) + fprintf(stdlog,"Load LE definition for phase %d (%s)\n", + le_phase, GetCtModeNameBMG(ct_mode,true) ); + + le_file_done = true; + le_file = MALLOC(sizeof(*le_file)); + InitializeCTCODE(le_file,ct_mode); + + raw_data_t raw; + if ( LoadRawData(&raw,true,opt_le_define,0,false,0) + || ScanRawDataCTCODE(le_file,CTM_NO_INIT,&raw,0) ) + { + ResetCTCODE(le_file); + FREE(le_file); + le_file = 0; + } + ResetRawData(&raw); + } + return le_file; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ApplyLEFile ( le_analyse_t * ana ) +{ + DASSERT(ana); + const ctcode_t *ctcode = LoadLEFile(ana->version); + return ctcode ? ApplyCTCODE(ana,ctcode) : ERR_ERROR; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ApplyCTCODE() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ApplyCTCODE ( le_analyse_t * ana, const ctcode_t * ctcode ) +{ + DASSERT(ana); + DASSERT(ctcode); + + //--- setup + + ana->n_cup_track = 0; + ana->n_cup_arena = 0; + ana->n_slot = 0; + + le_property_t *prop = ana->property; + le_music_t *music = ana->music; + le_flags_t *flags = ana->flags; + + if ( prop && music && flags ) + { + const uint max = ana->max_slot < LE_FIRST_LOWER_CT_SLOT + ? ana->max_slot : LE_FIRST_LOWER_CT_SLOT; + uint tid; + for ( tid = 0; tid < max; tid++ ) + { + *prop++ = tid; + *music++ = GetMkwMusicSlot(tid); + *flags++ = 0; + } + //HexDump16(stdout,5,0,ana->property,max); + //HexDump16(stdout,6,0,ana->music,max); + //HexDump16(stdout,7,0,ana->flags,max); + } + + + //--- copy racing cups + + const ctcode_cup1_data_t *cup = ctcode->cup_racing; + le_cup_track_t *cup_dest = ana->cup_track; + if ( cup && cup_dest ) + { + uint cup_idx, max = ctcode->n_racing_cups; + if ( max > ana->max_cup_track ) + max = ana->max_cup_track ; + + for ( cup_idx = 0; cup_idx < max; cup_idx++, cup++ ) + { + *cup_dest++ = cup->track_id[0]; + *cup_dest++ = cup->track_id[1]; + *cup_dest++ = cup->track_id[2]; + *cup_dest++ = cup->track_id[3]; + ana->n_cup_track++; + } + } + + + //--- copy battle cups + + cup = ctcode->cup_battle; + cup_dest = ana->cup_arena; + if ( cup && cup_dest ) + { + uint cup_idx, max = ctcode->n_battle_cups; + if ( max > ana->max_cup_arena ) + max = ana->max_cup_arena ; + + for ( cup_idx = 0; cup_idx < max; cup_idx++, cup++ ) + { + *cup_dest++ = cup->track_id[0]; + *cup_dest++ = cup->track_id[1]; + *cup_dest++ = cup->track_id[2]; + *cup_dest++ = cup->track_id[3]; + *cup_dest++ = cup->track_id[4]; + ana->n_cup_arena++; + } + } + + + //--- copy track properties + + const ctcode_crs1_data_t *td = ctcode->crs->data; + prop = ana->property; + music = ana->music; + flags = ana->flags; + + if ( td && prop && music && flags ) + { + uint tidx, max = ctcode->n_tracks; + if ( max > ana->max_slot ) + max = ana->max_slot ; + + for ( tidx = 0; tidx < max; tidx++, td++ ) + { + *prop++ = ntohl(td->property_id); + *music++ = ntohl(td->music_id); + *flags++ = ctcode->le_flags[tidx]; + ana->n_slot++; + } + } + + switch (ana->param_vers) + { + case 1: + ana->cup_par->n_racing_cups = htonl(ana->n_cup_track); + ana->cup_par->n_battle_cups = htonl(ana->n_cup_arena); + ana->course_par->n_slot = htonl(ana->n_slot); + break; + } + + + //--- copy/move/link track files + + char buf[500]; + const bmg_item_t * bi = ctcode->track_file.item; + const bmg_item_t * bi_end = bi + ctcode->track_file.item_used; + for ( ; bi < bi_end; bi++ ) + { + PrintString16BMG( buf, sizeof(buf), bi->text, bi->len, + BMG_UTF8_MAX, 0, true ); + CopyTrackFile(bi->mid - ctcode->ctb.track_name1.beg,buf); + } + + CalculateStatsLE(ana); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DefineAliasLE ( le_analyse_t * ana, uint slot, uint alias ) +{ + if ( !ana->property || !ana->music || !ana->flags ) + return false; + + DASSERT(ana); + if ( slot >= ana->n_slot ) + { + printf("ALIAS: Invalid slot: %x = %x\n",slot,alias); + return false; + } + + for(;;) + { + if ( alias >= ana->n_slot + || alias >= MKW_ARENA_BEG && alias < LE_FIRST_LOWER_CT_SLOT ) + { + printf("ALIAS: Invalid alias: %x = %x\n",slot,alias); + return false; + } + + const le_flags_t fl = ana->flags[alias]; + if ( !(fl & LETF_ALIAS) ) + { + ana->flags[slot] |= LETF_ALIAS; + ana->property[slot] = alias >> 8; + ana->music[slot] = alias & 0xff; + return true; + } + + alias = TrackByAliasLE(ana->property[alias],ana->music[alias]); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PatchAliasLE ( le_analyse_t * ana, ccp list ) +{ + DASSERT(ana); + + if (!list) + return 0; + + uint count = 0; + + ccp ptr = (char*)list; + while (*ptr) + { + while ( isspace((int)*ptr) || *ptr == ',' ) + ptr++; + + char *end; + const uint slot = str2ul(ptr,&end,16); + if ( end > ptr ) + { + ptr = end; + while ( isspace((int)*ptr) ) + ptr++; + + if ( *ptr == '=' ) + { + const uint alias = str2ul(ptr+1,&end,16); + if ( end > ptr ) + { + ptr = end; + count += DefineAliasLE(ana,slot,alias); + } + } + } + while ( *ptr && *ptr != ',' ) + ptr++; + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PatchLECODE ( le_analyse_t * ana ) +{ + DASSERT(ana); + PRINT("PatchLECODE() opt_lpar=%s)\n",opt_lpar); + + if (opt_lpar) + LoadLPAR(&ana->lpar,false,0,false); + + switch (ana->param_vers) + { + case 1: + { + if (opt_le_alias) + PatchAliasLE(ana,opt_le_alias); + + le_lpar_t *lp = &ana->lpar; + if (opt_engine_valid) + memcpy(lp->engine,opt_engine,sizeof(lp->engine)); + if ( opt_200cc != OFFON_AUTO ) + lp->enable_200cc = opt_200cc >= OFFON_ON; + if ( opt_perfmon != OFFON_AUTO ) + lp->enable_perfmon = opt_perfmon >= OFFON_FORCE ? 2 + : opt_perfmon >= OFFON_ON; + if ( opt_custom_tt != OFFON_AUTO ) + lp->enable_custom_tt = opt_custom_tt >= OFFON_ON; + if ( opt_xpflags != OFFON_AUTO ) + lp->enable_xpflags = opt_xpflags >= OFFON_ON; + } + break; + } + + CopyLPAR2Data(ana); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextLPAR() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadLPAR +( + le_lpar_t *lpar, // LE-CODE parameters + bool init_lpar, // true: initialize 'lpar' first + ccp fname, // filename of source, if NULL: use opt_lpar + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(lpar); + if (init_lpar) + InitializeLPAR(lpar,false); + + bool use_lpar = fname == 0; + if (use_lpar) + { + if ( !opt_lpar || !*opt_lpar ) + return ERR_OK; + fname = opt_lpar; + } + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if ( !err && raw.fform == FF_LPAR ) + err = ScanTextLPAR(lpar,false,fname,raw.data,raw.data_size); + + if ( err && use_lpar ) + opt_lpar = 0; + + ResetRawData(&raw); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextLPAR +( + le_lpar_t *lpar, // LE-CODE parameters + bool init_lpar, // true: initialize 'lpar' first + ccp fname, // NULL or filename for error messages + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + PRINT("ScanTextLPAR(init=%d,size=%u)\n",init_lpar,data_size); + DASSERT(lpar); + if (init_lpar) + InitializeLPAR(lpar,false); + + enum { C_PARAM, C_CHAT, C_END }; + static const KeywordTab_t section_name[] = + { + { C_PARAM, "LECODE-PARAMETERS", 0, 0 }, + { C_CHAT, "CHAT-MESSAGE-MODES", 0, 0 }, + { C_END, "END", 0, 0 }, + {0,0,0,0} + }; + + ScanInfo_t si; + InitializeSI(&si,data,data_size,fname,0); + si.predef = SetupVarsLEX(); + + bool is_pass2 = false; + enumError max_err = ERR_OK; + + for(;;) + { + PRINT("----- SCAN KMP SECTIONS, PASS%u ...\n",is_pass2+1); + + max_err = ERR_OK; + si.no_warn = !is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar,"$PASS",is_pass2+1); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,0); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + PRINT("--> pass=%u: #%04u: %s\n",is_pass2+1,si.cur_file->line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case C_PARAM: + err = ScanTextLPAR_PARAM(lpar,&si,is_pass2); + break; + + case C_CHAT: + err = ScanTextLPAR_CHAT(lpar,&si,is_pass2); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (is_pass2) + break; + is_pass2 = true; + + RestartSI(&si); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextKMP) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextLPAR_PARAM +( + le_lpar_t *lpar, // LE-CODE parameters + ScanInfo_t *si, // valid data + bool is_pass2 // false: pass1; true: pass2 +) +{ + DASSERT(lpar); + DASSERT(si); + PRINT(">> ScanTextLPAR_PARAM(pass=%u)\n",is_pass2+1); + + + //--- setup data + + int limit_mode = -1; + const ScanParam_t ptab[] = + { + { "LIMIT-MODE", SPM_INT, &limit_mode }, + { "ENGINE", SPM_U8, lpar->engine, 3,3 }, + { "ENABLE-200CC", SPM_U8, &lpar->enable_200cc }, + { "PERF-MONITOR", SPM_U8, &lpar->enable_perfmon }, + { "CUSTOM-TT", SPM_S8, &lpar->enable_custom_tt }, + { "XPFLAGS", SPM_U8, &lpar->enable_xpflags }, + { "BLOCK-TRACK", SPM_U8, &lpar->block_track }, + {0} + }; + + + //--- main loop + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + ScanParamSI(si,ptab); + } + CheckLevelSI(si); + + if ( limit_mode >= 0 ) + LimitToLparMode(lpar,limit_mode); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextLPAR_CHAT +( + le_lpar_t *lpar, // LE-CODE parameters + ScanInfo_t * si, // valid data + bool is_pass2 // false: pass1; true: pass2 +) +{ + PRINT(">> ScanTextLPAR_CHAT(pass=%u)\n",is_pass2+1); + // [[2do]] ??? + + + //--- setup data + + bool reset = true, legacy = false, mkwfun = false; + const ScanParam_t ptab[] = + { + { "RESET", SPM_BOOL, &reset }, + { "LEGACY", SPM_BOOL, &legacy }, + { "MKW-FUN", SPM_BOOL, &mkwfun }, + {0} + }; + + + //--- main loop + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + if (reset) + { + reset = false; + ClearLparChat(lpar); + } + + if ( legacy || mkwfun ) + { + const u16 *chat_mode = legacy ? chat_mode_legacy : chat_mode_mkwfun; + uint i; + for ( i = 0; i < BMG_N_CHAT; i++ ) + if (!lpar->chat_mode_1[i]) + lpar->chat_mode_1[i] = chat_mode[i]; + legacy = mkwfun = false; + } + continue; + } + + if (reset) + { + reset = false; + ClearLparChat(lpar); + } + + char name[20]; + ScanNameSI(si,name,sizeof(name),true,true,0); + if ( name[0] != 'M' ) + { + wrong_name:; + ScanFile_t *sf = si->cur_file; + sf->ptr = sf->prev_ptr; + WarnIgnoreSI(si,"Message name (M01..M96) expected: %s\n",name); + continue; + } + + char *end; + long num = strtol(name+1,&end,10); + if ( *end || num < 1 || num > BMG_N_CHAT ) + goto wrong_name; + + CheckWarnSI(si,'=',ERR_OK); + + DEFINE_VAR(var); + if (ScanExprSI(si,&var)) + continue; + lpar->chat_mode_1[num-1] = GetIntV(&var); + + if ( NextCharSI(si,false) == ',' ) + { + si->cur_file->ptr++; + if (!ScanExprSI(si,&var)) + lpar->chat_mode_2[num-1] = GetIntV(&var); + } + + FreeV(&var); + CheckEolSI(si); + } + CheckLevelSI(si); + + if (reset) + ClearLparChat(lpar); + + //HEXDUMP16(0,0,fb->buf,fb->ptr-fb->buf); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lecode extension LEX /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanLexFile + ( cvp data, uint data_size, lex_stream_func func, + cvp user_ptr, int user_int ) +{ + if ( !data || data_size < sizeof(lex_header_t) ) + return ERR_INVALID_DATA; + + const lex_header_t *hd = (lex_header_t*)data; + if (memcmp(hd->magic,LEX_BIN_MAGIC,4)) + return ERR_INVALID_DATA; + + if ( ntohs(hd->major_version) != LEX_MAJOR_VERSION ) + return ERR_INVALID_VERSION; + + const uint size = ntohl(hd->size); + const uint off = ntohl(hd->element_off); + if ( (size&3) || size > data_size || (off&3) || off + 4 > size ) + return ERR_INVALID_DATA; + + return ScanLexElements((u8*)data+off,size-off,func,user_ptr,user_int); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanLexElements + ( cvp data, uint data_size, lex_stream_func func, + cvp user_ptr, int user_int ) +{ + if ( !data || !data_size || (data_size&3) ) + return ERR_INVALID_DATA; + + enumError max_err = ERR_OK; + + uint off = 0; + while ( off < data_size ) + { + const lex_element_t *s = (lex_element_t*)( (u8*)data + off ); + + const u32 magic = ntohl(s->magic); + if (!magic) + break; + + const uint size = ntohl(s->size); + off += sizeof(lex_element_t) + size; + if ( (size&3) || off > data_size ) + return ERR_INVALID_DATA; + + PRINT("LEX SECTION: %.4s, size:%x\n",PrintID(&s->magic,4,0),size); + if (func) + { + const int err = func(magic,s->data,size,user_ptr,user_int); + if (err<0) + return -err; + if ( max_err < err ) + max_err = err; + } + } + + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +lex_element_t * FindLexElement ( lex_header_t *head, uint data_size, u32 magic ) +{ + if ( !head || data_size <= sizeof(lex_header_t) || (data_size&3) ) + return 0; + + uint file_size = ntohl(head->size); + if ( file_size > data_size ) + return 0; + file_size -= sizeof(lex_element_t); + + uint offset = ntohl(head->element_off); + if ( offset > file_size ) + return 0; + + magic = htonl(magic); + for(;;) + { + lex_element_t *elem = (lex_element_t*)((u8*)head + offset); + offset += 8 + ntohl(elem->size); + if ( offset > file_size ) + return 0; + if ( elem->magic == magic ) + return elem; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// fix lex items /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef void (*FixElementFunc) ( lex_t * lex, lex_item_t * item ); + +/////////////////////////////////////////////////////////////////////////////// + +static void FixLEXElement_SET1 ( lex_set1_t *set1 ) +{ + DASSERT(set1); + + uint i; + for ( i = 0; i < 3; i++ ) + { + float f = bef4(set1->item_factor.v+i); + if ( !isnormal(f) || f < 1.0 ) + write_bef4(set1->item_factor.v+i,1.0); + } +} + +//----------------------------------------------------------------------------- + +static void FixLEXItem_SET1 +( + lex_t * lex, // LEX data structure + lex_item_t * item // item to fix +) +{ + DASSERT(lex); + DASSERT(item); + DASSERT( ntohl(item->elem.size) >= sizeof(lex_set1_t) ); + + lex_set1_t *set1 = (lex_set1_t*)item->elem.data; + FixLEXElement_SET1(set1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lex_t helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static lex_item_t * FindElementLEX +( + const lex_t * lex, // LEX data structure + u32 magic // magic +) +{ + magic = htonl(magic); + + uint i; + lex_item_t **ss; + for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ ) + if ( (*ss)->elem.magic == magic ) + return *ss; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint GetLexSortOrder ( u32 magic ) +{ + switch(magic) + { + case LEXS_SET1: return 1; + case LEXS_TEST: return 9; + default: return 2; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void UpdateHaveLex ( lex_t *lex ) +{ + DASSERT(lex); + uint have_lex = 0; + + uint i; + lex_item_t **ss; + for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ ) + { + const u32 magic = ntohl((*ss)->elem.magic); + const uint size = ntohl((*ss)->elem.size); + uint j; + for ( j = 0; j < HAVELEX__N; j++ ) + if ( magic == have_lex_info[j].magic && size >= have_lex_info[j].min_size ) + { + have_lex |= 1 << j; + break; + } + } + + lex->have_lex = have_lex; +} + +/////////////////////////////////////////////////////////////////////////////// + +static lex_item_t * AppendElementLEX +( + lex_t * lex, // LEX data structure + u32 magic, // magic + const void * data, // data of section + uint size, // size of 'data' + bool overwrite // false: abort if exists and return 0 + // true: overwrite +) +{ + DASSERT(lex); + DASSERT( data || !size ); + + + //--- add have_lex + + uint i; + for ( i = 0; i < HAVELEX__N; i++ ) + if ( magic == have_lex_info[i].magic && size >= have_lex_info[i].min_size ) + { + lex->have_lex |= 1 << i; + break; + } + + + //--- check if already exists + + lex_item_t **ss; + const u32 be_magic = htonl(magic); + for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ ) + if ( (*ss)->elem.magic == be_magic ) + { + if (!overwrite) + return 0; + break; + } + + + //--- grow item list if necessary + + if ( i == lex->item_size ) + { + lex->item_size = lex->item_size + 30; + lex->item = REALLOC( lex->item, lex->item_size * sizeof(*lex->item) ); + // 'ss' becomes invalid! + ss = lex->item + i; + } + + if ( i < lex->item_used ) + FREE(*ss); + else + lex->item_used++; + + + //--- fix item size + + uint size4 = ALIGN32(size,4); + FixElementFunc fix_func = 0; + + switch(magic) + { + case LEXS_SET1: + fix_func = FixLEXItem_SET1; + if ( size4 < sizeof(lex_set1_t) ) + size4 = ALIGN32(sizeof(lex_set1_t),4); + break; + + case LEXS_TEST: + if ( size4 < sizeof(lex_test_t) ) + size4 = ALIGN32(sizeof(lex_test_t),4); + break; + } + + + //--- setup item + + lex_item_t *s = MALLOC(sizeof(lex_item_t)+size4); + *ss = s; + s->sort_order = GetLexSortOrder(magic); + s->insert_order = i; + s->elem.magic = be_magic; + s->elem.size = htonl(size4); + memcpy(s->elem.data,data,size); + if ( size < size4 ) + memset(s->elem.data+size,0,size4-size); + + if (fix_func) + fix_func(lex,s); + + return s; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool RemoveElementLEX +( + lex_t *lex, // LEX data structure + const lex_item_t *li // element to remove (robust) +) +{ + if ( !li || !li->elem.magic ) + return 0; + + uint i; + lex_item_t **ss; + for ( i = 0, ss = lex->item; i < lex->item_used; i++, ss++ ) + if ( *ss == li ) + { + memmove(ss,ss+1,lex->item_used-i); + lex->item_used--; + FREE((lex_item_t*)li); + UpdateHaveLex(lex); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +static uint RemoveElementByMagicLEX +( + lex_t *lex, // LEX data structure + u32 magic // magic +) +{ + if (!magic) + return 0; + + uint count = 0; + for(;;) + { + lex_item_t *li = FindElementLEX(lex,magic); + if (!li) + return count; + count += RemoveElementLEX(lex,li); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_lex_item ( const lex_item_t ** pa, const lex_item_t ** pb ) +{ + const lex_item_t *a = *pa; + const lex_item_t *b = *pb; + noPRINT(">> SORT: %u.%u : %u.%u\n", + a->sort_order, a->insert_order, + b->sort_order, b->insert_order ); + + int res = (int)a->sort_order - (int)b->sort_order; + return res ? res : (int)a->insert_order - (int)b->insert_order; +} + +//----------------------------------------------------------------------------- + +static void SortItemsLEX +( + lex_t * lex // LEX data structure +) +{ + if ( lex->item_used > 1 ) + qsort( lex->item, lex->item_used, sizeof(*lex->item), + (qsort_func)cmp_lex_item ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// have lex support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// order is important, compare have_lex_t and ct.wiimm.de + +const have_lex_info_t have_lex_info[HAVELEX__N] = +{ + { LEXS_SET1, "set1", 1 }, + { LEXS_CANN, "cannon", 0 }, + { LEXS_TEST, "test", 1 }, + { LEXS_HIPT, "hidepos", sizeof(lex_hipt_rule_t) }, + //--- add new sections here (order is important) +}; + +/////////////////////////////////////////////////////////////////////////////// + +ccp CreateSectionInfoLEX + ( have_lex_t special, bool add_value, ccp return_if_empty ) +{ + static char buf[500]; + char *dest = buf; + + if (add_value) + dest = snprintfE( dest, buf+sizeof(buf), "%u=" , special ); + + uint i, mask; + ccp sep = ""; + for ( i = 0, mask = 1; i < HAVELEX__N; i++, mask <<= 1 ) + if ( special & mask ) + { + dest = StringCat2E(dest,buf+sizeof(buf),sep,have_lex_info[i].name); + sep = ","; + } + + return dest == buf ? return_if_empty : CopyCircBuf0(buf,dest-buf); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeLEX ( lex_t * lex ) +{ + DASSERT(lex); + memset(lex,0,sizeof(*lex)); + + lex->fname = EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetLEX ( lex_t * lex ) +{ + if (lex) + { + FreeString(lex->fname); + if (lex->raw_data) + FREE(lex->raw_data); + + if (lex->item) + { + uint i; + for ( i = 0; i < lex->item_used; i++ ) + if (lex->item[i]) + FREE(lex->item[i]); + FREE(lex->item); + } + InitializeLEX(lex); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateLEX ( lex_t * lex, bool add_missing, bool add_test ) +{ + DASSERT(lex); + + if ( add_missing && lex->develop ) + { + } + + if (add_missing) + { + //--- SET1 + AppendSet1LEX(lex,false); + + //--- CANN + AppendCannLEX(lex,false); + + //--- HIPT + AppendHiptLEX(lex,false); + } + + if (add_test) //--- TEST + AppendTestLEX(lex,false); + + SortItemsLEX(lex); +} + +//----------------------------------------------------------------------------- + +lex_item_t * AppendSet1LEX ( lex_t * lex, bool overwrite ) +{ + lex_set1_t set1; + memset(&set1,0,sizeof(set1)); + // AppendElementLE() do call FixLEXItem_SET1() + return AppendElementLEX(lex,LEXS_SET1,&set1,sizeof(set1),false); +} + +//----------------------------------------------------------------------------- + +lex_item_t * AppendCannLEX ( lex_t * lex, bool overwrite ) +{ + return AppendElementLEX( lex, LEXS_CANN, CannonDataLEX, + sizeof(CannonDataLEX), overwrite); +} + +//----------------------------------------------------------------------------- + +lex_item_t * AppendHiptLEX ( lex_t * lex, bool overwrite ) +{ + return AppendElementLEX( lex, LEXS_HIPT, 0, 0, overwrite); +} + +//----------------------------------------------------------------------------- + +lex_item_t * AppendTestLEX ( lex_t * lex, bool overwrite ) +{ + return AppendElementLEX( lex, LEXS_TEST, TestDataLEX, + sizeof(TestDataLEX), overwrite); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpElementsLEX ( FILE *f, lex_t *lex, bool hexdump ) +{ + DASSERT(f); + DASSERT(lex); + + fprintf(f,"LEX SECTIONS: %u/%u\n",lex->item_used,lex->item_size); + + uint i; + for ( i = 0; i < lex->item_used; i++ ) + { + lex_item_t *s = lex->item[i]; + const uint size = ntohl(s->elem.size); + fprintf(f," %-4.4s %08x, size: %5u = 0x%04x\n", + PrintID(&s->elem.magic,4,0), s->elem.magic, size, size ); + if ( size && hexdump ) + HexDump16(f,8,0,s->elem.data,size); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lex parser functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isLEX +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + uint result = 0; + if ( si && si->lex ) + { + result = 1; + + if (si->lex->fname) + { + ccp fname = strrchr(si->lex->fname,'/'); + if (fname) + fname++; + else + fname = si->lex->fname; + + if ( !strcasecmp(fname,"course.lex") + || !strcasecmp(fname,"course.txt") + || !strcasecmp(fname,"course.txt.lex") ) + { + result = 2; + } + } + } + + res->i = result; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const struct FuncTable_t lex_func_tab[] = +{ + { 0, 0, "ISLEX", F_isLEX, 0, // replace only function call + "int", "isLEX()", 0 }, // but not info + + {0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup lex parser /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsLEX() +{ + static VarMap_t vm = {0}; + if (!vm.used) + { + DefineMkwVars(&vm); + DefineParserFuncTab(lex_func_tab,FF_LEX); + DefineParserVars(&vm); + } + + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawLEX() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpLEXFunc + ( u32 magic, const u8 *data, uint size, cvp user_ptr, int user_int ) +{ + u32 m = htonl(magic); + printf(" %s 0x%08x, %5u = 0x%04x bytes\n", + PrintID(&m,4,0), magic, size, size ); + if ( user_int < 0 || user_int >= 0x10 ) + { + if ( user_int > 0 && size > user_int ) + size = user_int; + HexDump16(stdout,5,0,data,size); + putchar('\n'); + } + return ERR_OK; +} + +//----------------------------------------------------------------------------- + +enumError ScanLEXFunc + ( u32 magic, const u8 *data, uint size, cvp user_ptr, int user_int ) +{ + #if HAVE_PRINT + u32 m = htonl(magic); + PRINT(">>>>> %-4.4s %08x : size: %u = 0x%x\n", + PrintID(&m,4,0), magic, size, size ); + #endif + + lex_t *lex = (lex_t*)user_ptr; + DASSERT(lex); + + switch (magic) + { + case LEXS_TERMINATE: + case LEXS_IGNORE: + // ignore + break; + + default: + AppendElementLEX(lex,magic,data,size,true); + #if HAVE_PRINT0 + if (logging>1) + DumpElementsLEX(stdout,lex,logging>2); + #endif + } + return ERR_OK; +} + +//----------------------------------------------------------------------------- + +enumError ScanRawLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + const void * data, // data to scan + uint data_size, // size of 'data' + uint dump_mode // 0: add element to lex->item + // 1: dump scanned elements + // >=0x10: add hexdump of max # bytes +) +{ + DASSERT(lex); + if (init_lex) + InitializeLEX(lex); + + if ( IsValidLEX(data,data_size,data_size,lex->fname) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid LEX file: %s\n" + "Add option --lex=force or --force to ignore some validity checks.", + lex->fname ? lex->fname : "?" ); + } + + lex->fform = FF_LEX; + enumError err = ScanLexFile( data, data_size, + dump_mode ? DumpLEXFunc : ScanLEXFunc, lex, dump_mode ); + if (err) + ERROR0(err,"Scanning of LEX file failed: %s\n",lex->fname); + else + UpdateLEX(lex,opt_complete,false); + + if (logging>0) + DumpElementsLEX(stdout,lex,logging>2); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextLEX() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[lex_section_id]] + +typedef enum lex_section_id +{ + LID_UNKNOWN, + LID_IGNORE, + LID_SETUP, + LID_HEXDUMP, + + LID_SETTINGS1, + LID_CANNON, + LID_HIDE_PT, + LID_TEST, +} +lex_section_id; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t lex_section_name[] = +{ + //------------------------------------------------------------------------ + // ID NAME INFO unused + //------------------------------------------------------------------------ + + { LID_IGNORE, "END", "--", 0 }, + { LID_SETUP, "SETUP", "Setup", 0 }, + { LID_HEXDUMP, "HEXDUMP", "Hexdump of unknown section", 0 }, + + { LID_SETTINGS1, "SET1", "Basic settings", 0 }, + { LID_CANNON, "CANN", "Cannon settings", 0 }, + { LID_HIDE_PT, "HIPT", "Hide position tracker", 0 }, + { LID_TEST, "TEST", "Test case", 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t lex_offline_online[] = +{ + { LEX_OO_AUTO, "AUTO", "0", 0 }, + { LEX_OO_OFFLINE, "OFFLINE", "1", 0 }, + { LEX_OO_OFFLINE, "NEVER", 0, 0 }, + { LEX_OO_ONLINE, "ONLINE", "2", 0 }, + { LEX_OO_ONLINE, "ALWAYS", 0, 0 }, + { -9, "OFF", "-1", -1 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t lex_game_mode[] = +{ + { LEX_GMODE_AUTO, "AUTO", "0", -1 }, + { LEX_GMODE_STD, "STANDARD", "1", KMP_GMODE_STD }, + { LEX_GMODE_STD, "STD", 0, KMP_GMODE_STD }, + { LEX_GMODE_BALLOON, "BALLOON", "2", KMP_GMODE_BALLOON }, + { LEX_GMODE_COIN, "COIN", "3", KMP_GMODE_COIN }, + { LEX_GMODE_VERSUS, "VERSUS", "4", KMP_GMODE_VERSUS }, + { LEX_GMODE_ITEMRAIN, "ITEMRAIN", "5", KMP_GMODE_ITEMRAIN }, + { -1, "OFF", "-1", -1 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t lex_engine[] = +{ + { LEX_ENGINE_AUTO, "AUTO", "0", -1 }, + { LEX_ENGINE_BATTLE, "BATTLE", 0, ENGINE_BATTLE }, + { LEX_ENGINE_50, "50CC", 0, ENGINE_50 }, + { LEX_ENGINE_100, "100CC", 0, ENGINE_100 }, + { LEX_ENGINE_150, "150CC", 0, ENGINE_150 }, + { LEX_ENGINE_200, "200CC", 0, ENGINE_200 }, + { LEX_ENGINE_150M, "150M", "150MIRROR", ENGINE_150M }, + { LEX_ENGINE_200M, "200M", "200MIRROR", ENGINE_200M }, + { -1, "OFF", "-1", -1 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanLEXElementHexdump +( + lex_t * lex, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(lex); + DASSERT(si); + TRACE("ScanLEXElementHexdump()\n"); + + + //--- setup data + + u32 magic = LEXS_IGNORE; + ScanParam_t ptab[] = + { + { "MAGIC", SPM_U32, &magic, }, + {0} + }; + + char fb_buf[1000]; + FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf)); + + + //--- main loop + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + ScanParamSI(si,ptab); + else + ScanHexlineSI(si,fb,true); + } + CheckLevelSI(si); + + if ( magic != LEXS_IGNORE || magic != LEXS_TERMINATE ) + AppendElementLEX(lex,magic,fb->buf,fb->ptr-fb->buf,true); + + ResetFastBuf(fb); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanLEXElement_SET1 +( + lex_t * lex, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(lex); + DASSERT(si); + TRACE("ScanLEXElement_SET1()\n"); + + + //--- setup data + + lex_set1_t set1; + memset(&set1,0,sizeof(set1)); + + const ScanParam_t ptab[] = + { + { "ITEM-POS-FACTOR", SPM_FLOAT3_BE, &set1.item_factor }, + {0} + }; + + char fb_buf[1000]; + FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf)); + AppendFastBuf(fb,&set1,sizeof(set1)); + + + //--- main loop + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + ScanParamSI(si,ptab); + else + ScanHexlineSI(si,fb,true); + } + CheckLevelSI(si); + + memcpy(fb->buf,&set1,sizeof(set1)); + HEXDUMP16(0,0,fb->buf,fb->ptr-fb->buf); + AppendElementLEX(lex,LEXS_SET1,fb->buf,fb->ptr-fb->buf,true); + + ResetFastBuf(fb); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanLEXElement_CANN +( + lex_t * lex, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(lex); + DASSERT(si); + TRACE("ScanLEXElement_CANN()\n"); + + + //--- setup data + + ScanParam_t ptab[] = + { + {0} + }; + + char fb_buf[1000]; + FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf)); + AppendFastBuf(fb,EmptyString,4); + + + //--- main loop + + uint n_cannon = 0; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + enumError err; + ScanIndexSI(si,&err,n_cannon,0,2); + if (err) + break; + + float f[4]; + ScanFloatSI(si,f,4); + + uint i; + u32 *fp = (u32*)f; + for ( i = 0; i < 4; i++, fp++ ) + *fp = htonl(*fp); + AppendFastBuf(fb,f,sizeof(f)); + n_cannon++; + CheckEolSI(si); + } + + if ( n_cannon > 0 ) + { + write_be32(fb->buf,n_cannon); + //HexDump16(stdout,0,0,fb->buf,fb->ptr-fb->buf); + AppendElementLEX(lex,LEXS_CANN,fb->buf,fb->ptr-fb->buf,true); + } + + ResetFastBuf(fb); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanLEXElement_HIPT +( + lex_t * lex, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(lex); + DASSERT(si); + TRACE("ScanLEXElement_HIPT()\n"); + + + //--- setup data + + ScanParam_t ptab[] = + { + {0} + }; + + char fb_buf[1000]; + FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf)); + + + //--- main loop + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + u8 num[sizeof(lex_hipt_rule_t)]; + ScanU8SI(si,num,sizeof(num),false); + AppendFastBuf(fb,num,sizeof(num)); + CheckEolSI(si); + } + + AppendElementLEX(lex,LEXS_HIPT,fb->buf,fb->ptr-fb->buf,true); + ResetFastBuf(fb); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanLEXElement_TEST +( + lex_t * lex, // KMP data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(lex); + DASSERT(si); + TRACE("ScanLEXElement_TEST()\n"); + + + //--- setup data + + lex_test_t test; + memcpy(&test,TestDataLEX,sizeof(test)); + + const ScanParam_t ptab[] = + { + { "OFFLINE-ONLINE", SPM_U8, &test.offline_online }, + { "N-OFFLINE", SPM_U8, &test.n_offline }, + { "N-ONLINE", SPM_U8, &test.n_online }, + { "COND-BIT", SPM_S8, &test.cond_bit }, + { "GAME-MODE", SPM_U8, &test.game_mode }, + { "ENGINE", SPM_U8, &test.engine }, + { "RANDOM", SPM_U8, &test.random }, + {0} + }; + + char fb_buf[1000]; + FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf)); + AppendFastBuf(fb,&test,sizeof(test)); + + + //--- main loop + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + ScanParamSI(si,ptab); + else + ScanHexlineSI(si,fb,true); + } + CheckLevelSI(si); + + memcpy(fb->buf,&test,sizeof(test)); + HEXDUMP16(0,0,fb->buf,fb->ptr-fb->buf); + AppendElementLEX(lex,LEXS_TEST,fb->buf,fb->ptr-fb->buf,true); + + ResetFastBuf(fb); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + PRINT("ScanTextLEX(init=%d)\n",init_lex); + + DASSERT(lex); + if (init_lex) + InitializeLEX(lex); + + ScanInfo_t si; + InitializeSI(&si,data,data_size,lex->fname,lex->revision); + si.predef = SetupVarsLEX(); + + enumError max_err = ERR_OK; + lex->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN LEX SECTIONS, PASS%u ...\n",lex->is_pass2+1); + + max_err = ERR_OK; + si.lex = lex; + si.no_warn = !lex->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", lex->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,lex->revision); + + si.cur_file->ptr++; + char sect_name[20]; + ScanNameSI(&si,sect_name,sizeof(sect_name),true,true,0); + PRINT0("--> pass=%u: #%04u: %s\n",lex->is_pass2+1,si.cur_file->line,sect_name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,sect_name,lex_section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + PRINT0("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case LID_IGNORE: + // ignore it without warning + break; + + case LID_SETUP: + // nothing to do at the moment + break; + + case LID_HEXDUMP: + err = ScanLEXElementHexdump(lex,&si); + break; + + case LID_SETTINGS1: + err = ScanLEXElement_SET1(lex,&si); + break; + + case LID_CANNON: + err = ScanLEXElement_CANN(lex,&si); + break; + + case LID_HIDE_PT: + err = ScanLEXElement_HIPT(lex,&si); + break; + + case LID_TEST: + err = ScanLEXElement_TEST(lex,&si); + break; + + default: + err = ERROR0(ERR_WARNING,"Unknown section (ignored): %s\n",sect_name); + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (lex->is_pass2) + break; + + lex->is_pass2 = true; + RestartSI(&si); + } + + ResetSI(&si); + + UpdateLEX(lex,opt_complete,false); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(lex); + PRINT("ScanLEX() size=%u\n",data_size); + HEXDUMP16(0,0,data,16); + + enumError err; +// [[analyse-magic]] + switch (GetByMagicFF(data,data_size,data_size)) + { + case FF_LEX: + lex->fform = FF_LEX; + err = ScanRawLEX(lex,init_lex,data,data_size,0); + break; + + case FF_LEX_TXT: + lex->fform = FF_LEX_TXT; + err = ScanTextLEX(lex,init_lex,data,data_size); + break; + + default: + if (init_lex) + InitializeLEX(lex); + return ERROR0(ERR_INVALID_DATA, + "No LEX file: %s\n", lex->fname ? lex->fname : "?"); + } + + PatchLEX(lex); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + struct raw_data_t * raw // valid raw data +) +{ + DASSERT(lex); + DASSERT(raw); + if (init_lex) + InitializeLEX(lex); + else + ResetLEX(lex); + + lex->fname = raw->fname; + raw->fname = 0; + + return raw->is_0 ? ERR_OK : ScanLEX(lex,false,raw->data,raw->data_size); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(lex); + DASSERT(fname); + if (init_lex) + InitializeLEX(lex); + else + ResetLEX(lex); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + lex->fname = raw.fname; + raw.fname = 0; + err = ScanLEX(lex,false,raw.data,raw.data_size); + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawLEX() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawLEX +( + lex_t * lex // pointer to valid LEX +) +{ + DASSERT(lex); + if (lex->raw_data) + FREE(lex->raw_data); + + uint total_size = sizeof(lex_header_t) + sizeof(lex_element_t); // final term! + + uint i; + for ( i = 0; i < lex->item_used; i++ ) + { + lex_item_t *s = lex->item[i]; + total_size += sizeof(lex_element_t) + ntohl(s->elem.size); + } + + lex->raw_data = MALLOC(total_size); + lex->raw_data_size = total_size; + + lex_header_t *hd = (lex_header_t*)lex->raw_data; + memcpy(hd->magic,LEX_BIN_MAGIC,sizeof(hd->magic)); + hd->major_version = htons(LEX_MAJOR_VERSION); + hd->minor_version = htons(LEX_MINOR_VERSION); + hd->size = htonl(total_size); + hd->element_off = htonl(sizeof(lex_header_t)); + + u8 *dest = lex->raw_data + sizeof(lex_header_t); + for ( i = 0; i < lex->item_used; i++ ) + { + lex_item_t *s = lex->item[i]; + const uint size = sizeof(lex_element_t) + ntohl(s->elem.size); + ASSERT ( dest + size < lex->raw_data + lex->raw_data_size ); + memcpy(dest,&s->elem,size); + dest += size; + } + ASSERT ( dest + sizeof(lex_element_t) == lex->raw_data + lex->raw_data_size ); + memset(dest,0,sizeof(lex_element_t)); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawLEX() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawLEX +( + lex_t * lex, // pointer to valid LEX + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(lex); + DASSERT(fname); + PRINT("SaveRawLEX(%s,%d)\n",fname,set_time); + + //--- create raw data + + enumError err = CreateRawLEX(lex); + if (err) + return err; + DASSERT(lex->raw_data); + DASSERT(lex->raw_data_size); + + + //--- write to file + + File_t F; + err = CreateFileOpt(&F,true,fname,testmode,lex->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&lex->fatt,0); + + if ( fwrite(lex->raw_data,1,lex->raw_data_size,F.f) != lex->raw_data_size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextLEX() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextLEX_SET1 +( + FILE * f, // file to write + const lex_t * lex, // pointer to valid LEX + lex_item_t * item // item to save +) +{ + DASSERT(f); + DASSERT(lex); + DASSERT(item); + + const uint size = ntohl(item->elem.size); + noPRINT("SaveTextLEX_SET1(), size=%u\n",size); + DASSERT( size >= sizeof(lex_set1_t) ); + if ( size < sizeof(lex_set1_t)) + return ERR_OK; + + const u8 *data = item->elem.data; + const lex_set1_t *set1 = (lex_set1_t*)data; + + fprintf(f,text_lex_elem_set1_cr + ,item->sort_order + ,bef4(&set1->item_factor.x) + ,bef4(&set1->item_factor.y) + ,bef4(&set1->item_factor.z) + ); + + if ( size > sizeof(lex_set1_t) ) + { + fputs("\n# Hex dump for unknown settings:\n",f); + HexDump(f,0,sizeof(lex_set1_t),5,16, + item->elem.data + sizeof(lex_set1_t), + size - sizeof(lex_set1_t) ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextLEX_CANN +( + FILE * f, // file to write + const lex_t * lex, // pointer to valid LEX + lex_item_t * item // item to save +) +{ + DASSERT(f); + DASSERT(lex); + DASSERT(item); + + fprintf(f,text_lex_elem_cann_cr,item->sort_order); + if ( item->elem.size < 4 + 16 ) + return ERR_OK; + + const u8 *data = item->elem.data; + uint n = be32(data); + uint max = ( ntohl(item->elem.size) - 4 ) / 16; + if ( n > max ) + n = max; + + uint i, off = 4; + for ( i = 0; i < n; i++, off += 16 ) + fprintf(f,"%4u %10.3f %10.3f %10.3f %10.3f\r\n", + i, bef4(data+off), bef4(data+off+4), + bef4(data+off+8), bef4(data+off+12) ); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextLEX_HIPT +( + FILE * f, // file to write + const lex_t * lex, // pointer to valid LEX + lex_item_t * item // item to save +) +{ + DASSERT(f); + DASSERT(lex); + DASSERT(item); + + fprintf(f,text_lex_elem_hipt_cr,item->sort_order); + if ( item->elem.size < 4 + 16 ) + return ERR_OK; + + const lex_hipt_rule_t *rule = (lex_hipt_rule_t*)item->elem.data; + int n = ntohl(item->elem.size) / sizeof(*rule) ; + + int i, last_lap = 1000; + for ( i = 0; i < n; i++, rule++ ) + { + if ( last_lap != rule->lap ) + { + last_lap = rule->lap; + fputs("\r\n",f); + } + + fprintf(f,"%5u %4d %4u %4u %4u\r\n", + rule->cond, rule->lap, rule->from, rule->to, rule->mode ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextLEX_TEST +( + FILE * f, // file to write + const lex_t * lex, // pointer to valid LEX + lex_item_t * item // item to save +) +{ + DASSERT(f); + DASSERT(lex); + DASSERT(item); + + const uint size = ntohl(item->elem.size); + PRINT("SaveTextLEX_TEST(), size=%u\n",size); + DASSERT( size >= sizeof(lex_test_t) ); + if ( size < sizeof(lex_test_t)) + return ERR_OK; + + const u8 *data = item->elem.data; + const lex_test_t *test = (lex_test_t*)data; + + const KeywordTab_t *off_on; + for ( off_on = lex_offline_online; off_on->name1; off_on++ ) + if ( off_on->id == test->offline_online && off_on->opt >= 0 ) + break; + + const KeywordTab_t *gmode; + for ( gmode = lex_game_mode; gmode->name1; gmode++ ) + if ( gmode->id == test->game_mode && gmode->opt >= 0 ) + break; + + const KeywordTab_t *engine; + for ( engine = lex_engine; engine->name1; engine++ ) + if ( engine->id == test->engine && engine->opt >= 0 ) + break; + + fprintf(f,text_lex_elem_test_cr + ,item->sort_order + ,off_on->name1 ? off_on->name1 : "AUTO" + ,test->n_offline + ,test->n_online + ,test->cond_bit + ,gmode->name1 ? gmode->name1 : "AUTO" + ,engine->name1 ? engine->name1 : "AUTO" + ,test->random + ); + + if ( size > sizeof(lex_test_t) ) + { + fputs("\n# Hex dump for unknown settings:\n",f); + HexDump(f,0,sizeof(lex_test_t),5,16, + item->elem.data + sizeof(lex_test_t), + size - sizeof(lex_test_t) ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextLEX +( + const lex_t * lex, // pointer to valid LEX + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(lex); + DASSERT(fname); + PRINT("SaveTextLEX(%s,%d)\n",fname,set_time); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,lex->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&lex->fatt,0); + + + //--- print header + syntax info + + if ( print_header && !brief_count && !export_count ) + fprintf(F.f,text_lex_head_cr); + else + fprintf(F.f, + "%s\r\n" + "# LEX syntax & semantics: https://szs.wiimm.de/info/lex-syntax.html\r\n" + ,LEX_TEXT_MAGIC); + + + //--- print info section & section counter alphabetically + + if ( brief_count || export_count ) + { + fprintf(F.f, + "%s[SETUP]\r\n\r\n" + "TOOL = %s\r\n" + "SYSTEM = %s\r\n" + "VERSION = %s\r\n" + "REVISION = %u\r\n" + "DATE = %s\r\n" + "\r\n", + section_sep, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + } + else + { + fprintf(F.f, text_lex_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + + uint i; + for ( i = 0; i < lex->item_used; i++ ) + { + lex_item_t *s = lex->item[i]; + const uint size = ntohl(s->elem.size); + fprintf(F.f,"# %-4.4s %08x, 0x%04x bytes\r\n", + PrintID(&s->elem.magic,4,0), s->elem.magic, size ); + } + } + + enumError max_err = ERR_OK; + + uint i; + for ( i = 0; i < lex->item_used; i++ ) + { + lex_item_t *s = lex->item[i]; + const uint size = ntohl(s->elem.size); + const u32 magic = ntohl(s->elem.magic); + enumError err = ERR_OK; + switch(magic) + { + case LEXS_SET1: + err = SaveTextLEX_SET1(F.f,lex,s); + break; + + case LEXS_CANN: + err = SaveTextLEX_CANN(F.f,lex,s); + break; + + case LEXS_HIPT: + err = SaveTextLEX_HIPT(F.f,lex,s); + break; + + case LEXS_TEST: + err = SaveTextLEX_TEST(F.f,lex,s); + break; + + default: + hexdump_eol = "\r\n"; + fprintf(F.f,text_lex_hexdump_cr, + s->sort_order, magic, PrintID(&s->elem.magic,4,0) ); + HexDump(F.f,0,0,5,16,s->elem.data,size); + break; + } + + if ( max_err < err ) + max_err = err; + } + + fputs(section_end,F.f); + ResetFile(&F,set_time); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool opt_lex_purge = false; +bool force_lex_test = false; +bool opt_lt_clear = false; +int opt_lt_online = -9; +int opt_lt_n_offline = -9; +int opt_lt_n_online = -9; +int opt_lt_cond_bit = -9; +int opt_lt_game_mode = -9; +int opt_lt_engine = -9; +int opt_lt_random = -9; + +//----------------------------------------------------------------------------- + +int ScanOptLtOnline ( ccp arg ) +{ + if ( !arg || !*arg ) + opt_lt_online = -9; + else + { + int abbrev; + const KeywordTab_t *key = ScanKeyword(&abbrev,arg,lex_offline_online); + if (!key) + { + PrintKeywordError(lex_offline_online,arg,abbrev,0, + "keyword for option --lt-online"); + return 1; + } + opt_lt_online = key->id; + } + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptLtNPlayers ( ccp arg ) +{ + if ( !arg || !*arg ) + opt_lt_n_offline = opt_lt_n_online = -9; + else + { + ccp comma = strchr(arg,','); + if (comma) + { + opt_lt_n_offline = str2l(arg,0,10); + opt_lt_n_online = str2l(comma+1,0,10); + } + else + { + int n = str2l(arg,0,10); + if ( n > 4 ) + { + opt_lt_n_offline = -9; + opt_lt_n_online = n; + } + else + { + opt_lt_n_offline = n; + opt_lt_n_online = -9; + } + } + + if ( opt_lt_n_offline < 0 ) + opt_lt_n_offline = -9; + else if ( opt_lt_n_offline > 4 ) + opt_lt_n_offline = 4; + + if ( opt_lt_n_online < 0 ) + opt_lt_n_online = -9; + else if ( opt_lt_n_online > 99 ) + opt_lt_n_online = 99; + } + + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptLtCondBit ( ccp arg ) +{ + if ( !arg || !*arg ) + opt_lt_cond_bit = -9; + else + { + int n = str2l(arg,0,10); + opt_lt_cond_bit = n < -1 || n >= 16 ? -9 : n; + } + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptLtGameMode ( ccp arg ) +{ + if ( !arg || !*arg ) + opt_lt_game_mode = -1; + else + { + int abbrev; + const KeywordTab_t *key = ScanKeyword(&abbrev,arg,lex_game_mode); + if (!key) + { + PrintKeywordError(lex_game_mode,arg,abbrev,0, + "keyword for option --lt-game-mode"); + return 1; + } + opt_lt_game_mode = key->id; + } + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptLtEngine ( ccp arg ) +{ + if ( !arg || !*arg ) + opt_lt_engine = -1; + else + { + int abbrev; + const KeywordTab_t *key = ScanKeyword(&abbrev,arg,lex_engine); + if (!key) + { + PrintKeywordError(lex_engine,arg,abbrev,0, + "keyword for option --lt-engine"); + return 1; + } + opt_lt_engine = key->id; + } + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptLtRandom ( ccp arg ) +{ + if ( !arg || !*arg ) + opt_lt_random = -1; + else + { + int n = str2l(arg,0,10); + opt_lt_random = n < 0 || n > 6 ? -9 : n; + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lex_info_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupLexInfo ( lex_info_t *info, const lex_t *lex ) +{ + DASSERT(info); + InitializeLexInfo(info); + + if (lex) + { + info->lex_found = true; + lex_item_t *item; + + item = FindElementLEX(lex,LEXS_SET1); + if (item) + { + info->set1_found = true; + memcpy(&info->set1,item->elem.data,sizeof(info->set1)); + bef4n(info->set1.item_factor.v,info->set1.item_factor.v,3); + } + + item = FindElementLEX(lex,LEXS_TEST); + if (item) + { + info->test_found = true; + memcpy(&info->test,item->elem.data,sizeof(info->test)); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SetupLexInfoByData ( lex_info_t *info, cvp data, uint data_size ) +{ + lex_t lex; + const enumError err = ScanLEX(&lex,true,data,data_size); + if (!err) + SetupLexInfo(info,&lex); + else + InitializeLexInfo(info); + ResetLEX(&lex); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX patching /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// return TRUE if any option set +bool HavePatchTestLEX() +{ + return force_lex_test + || opt_lt_clear + || opt_lt_online >= 0 + || opt_lt_n_offline >= 0 + || opt_lt_n_online >= 0 + || opt_lt_game_mode >= 0 + || opt_lt_engine >= 0 + || opt_lt_random >= 0; +} + + +//----------------------------------------------------------------------------- + +// return TRUE if any option set and not default +bool HaveActivePatchTestLEX() +{ + return force_lex_test + || opt_lt_online > 0 + || opt_lt_n_offline > 0 + || opt_lt_n_online > 0 + || opt_lt_game_mode > LEX_GMODE_AUTO + || opt_lt_engine > LEX_ENGINE_AUTO + || opt_lt_random >= 0; +} + +//----------------------------------------------------------------------------- + +// return TRUE if modified / set optional 'empty' to true, if test section is empty +bool PatchTestLEX ( lex_test_t *lt, bool *empty ) +{ + DASSERT(lt); + PRINT0("--lt-*: clear=%d, online=%d, n=%d,%d, gm=%d, en=%d, rnd=%d\n", + opt_lt_clear, opt_lt_online, opt_lt_n_offline, opt_lt_n_online, + opt_lt_game_mode, opt_lt_engine, opt_lt_random ); + + lex_test_t temp; + memcpy( &temp, opt_lt_clear ? (lex_test_t*)&TestDataLEX : lt, sizeof(temp) ); + + if ( opt_lt_online >= LEX_OO_AUTO && opt_lt_online <= LEX_OO_ONLINE ) + temp.offline_online = opt_lt_online; + + if ( opt_lt_n_offline >= 0 ) + temp.n_offline = opt_lt_n_offline > 4 ? 4 : opt_lt_n_offline; + + if ( opt_lt_n_online >= 0 ) + temp.n_online = opt_lt_n_online > 99 ? 99 : opt_lt_n_online; + + if ( opt_lt_cond_bit >= -1 && opt_lt_cond_bit < 16 ) + temp.cond_bit = opt_lt_cond_bit; + + if ( opt_lt_game_mode >= 0 ) + temp.game_mode = opt_lt_game_mode; + + if ( opt_lt_engine >= 0 ) + temp.engine = opt_lt_engine; + + if ( opt_lt_random >= 0 ) + temp.random = opt_lt_random; + + if (empty) + *empty = !memcmp(&temp,&TestDataLEX,sizeof(temp)); + if (memcmp(lt,&temp,sizeof(*lt))) + { + memcpy(lt,&temp,sizeof(*lt)); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool PatchLEX ( lex_t * lex ) +{ + DASSERT(lex); + + bool modified = false; + if (HavePatchTestLEX()) + { + lex_item_t *li = FindElementLEX(lex,LEXS_TEST); + const bool had_test = li != 0; + if (!had_test) + { + if (!HaveActivePatchTestLEX()) + return 0; + li = AppendTestLEX(lex,false); + } + DASSERT(li); + lex_test_t orig; + memcpy(&orig,li->elem.data,sizeof(orig)); + + bool empty; + modified = PatchTestLEX((lex_test_t*)li->elem.data,&empty); + PRINT("PATCH+ LEX/TEST: had=%d, empty=%d, modified=%d => have_lex:%x\n", + had_test, empty, modified, lex->have_lex ); + if ( empty && !force_lex_test ) + { + if ( RemoveElementLEX(lex,li) && had_test ) + modified = true; + } + else if (!had_test) + modified = true; + + PRINT("PATCH- LEX/TEST: had=%d, empty=%d, modified=%d => have_lex:%x\n", + had_test, empty, modified, lex->have_lex ); + + if (modified) + lex->modified = true; + } + + if (opt_lex_purge) + modified |= PurgeLEX(lex); + + return modified; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX purging /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool PurgeLEX ( lex_t * lex ) +{ + DASSERT(lex); + + bool modified = false; + + uint i = 0; + while ( i < lex->item_used ) + { + lex_item_t **ss = lex->item+i; + const uint size = ntohl((*ss)->elem.size); + const u8 *data = (*ss)->elem.data; + + bool remove = false; + switch(ntohl((*ss)->elem.magic)) + { + case LEXS_SET1: + { + lex_set1_t set1; + memset(&set1,0,sizeof(set1)); + FixLEXElement_SET1(&set1); + remove = !memcmp(data,&set1,sizeof(set1)); + } + break; + + case LEXS_CANN: + remove = size <= sizeof(CannonDataLEX) + && !memcmp(data,&CannonDataLEX,size); + break; + + case LEXS_HIPT: + remove = size < sizeof(lex_hipt_rule_t); + break; + + case LEXS_TEST: + remove = !memcmp(data,TestDataLEX,sizeof(TestDataLEX)); + break; + + default: + remove = true; + break; + } + + if (remove) + modified |= RemoveElementLEX(lex,*ss); + else + i++; + } + + if (modified) + UpdateHaveLex(lex); + + return modified; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-lecode.h b/project/src/lib-lecode.h new file mode 100644 index 0000000..79788c9 --- /dev/null +++ b/project/src/lib-lecode.h @@ -0,0 +1,896 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_LECODE_H +#define SZS_LIB_LECODE_H 1 + +#include "lib-std.h" +#include "lib-mkw.h" +#include "lib-ctcode.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define LE_BINARY_MAGIC "LECT" +#define LE_BINARY_MAGIC_NUM 0x4c454354 + +#define LE_PARAM_MAGIC "LPAR" +#define LE_PARAM_MAGIC_NUM 0x4c504152 + +#define LE_LPAR_MAGIC "#LE-LPAR" +#define LE_LPAR_MAGIC_NUM 0x234c452d4c504152ull + +#define LE_CUP_MAGIC "CUP2" +#define LE_COURSE_MAGIC "CRS2" + +#define LE_RESERVED_SLOTS (LE_FIRST_LOWER_CT_SLOT - 32) +#define LE_MAX_BLOCK_TRACK 50 + +//----------------------------------------------------------------------------- +// [[le_valid_t]] + +typedef enum le_valid_t // bit field +{ + LE_HEAD_FOUND = 0x01, + LE_HEAD_VALID = 0x02, + LE_HEAD_KNOWN = 0x04, + + LE_PARAM_FOUND = 0x10, + LE_PARAM_VALID = 0x20, + LE_PARAM_KNOWN = 0x40, +} +le_valid_t; + +//----------------------------------------------------------------------------- +// [[le_usage_t]] + +typedef enum le_usage_t +{ + LEU_S_UNUSED = 0, // - slot not used + LEU_S_ARENA = 1, // A arena slot + LEU_S_ARENA_RND = 2, // r arena slot + LEU_S_ARENA_HIDE = 3, // a arena slot + LEU_S_TRACK = 4, // T track slot + LEU_S_TRACK_RND = 5, // R track slot + LEU_S_TRACK_HIDE = 6, // t track slot + LEU_S_SPECIAL = 7, // s special slot + LEU_S_NETWORK = 8, // n network slot + LEU_S_RANDOM = 9, // * network slot + LEU_S_WIIMM = 10, // W random slot + LEU_S_ALIAS = 11, // > is alias + LEU_S_MASK = 15, + + LEU_F_ONLINE = 0x10, // Flag: used online +} +__attribute__ ((packed)) le_usage_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// le_lpar_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[le_lpar_t]] + +typedef struct le_lpar_t +{ + lpar_mode_t limit_mode; // limit of other params + + u8 engine[3]; // 100cc, 150cc, mirror (sum always 100) + u8 enable_200cc; // TRUE: 200C enabled => 150cc, 200cc, mirror + u8 enable_perfmon; // TRUE: performance monitor enabled + u8 enable_custom_tt; // TRUE: time trial for cusotm tracks enabled + u8 enable_xpflags; // TRUE: extended presence flags enabled + u8 block_track; // block used tracks for 0..20 races + + u16 chat_mode_1[BMG_N_CHAT]; // first set of modes for each chat message + u16 chat_mode_2[BMG_N_CHAT]; // second set of modes for each chat message +} +le_lpar_t; + +/////////////////////////////////////////////////////////////////////////////// + +lpar_mode_t CalcCurrentLparMode ( const le_lpar_t * lp, bool use_limit_param ); +ccp GetLparModeName ( lpar_mode_t lpmode, bool export ); +bool LimitToLparMode ( le_lpar_t * lp, lpar_mode_t lpmode ); +void ClearLparChat ( le_lpar_t * lp ); + +//----------------------------------------------------------------------------- + +ccp GetChatModeName ( u16 mode ); +extern const u16 chat_mode_legacy[BMG_N_CHAT]; +extern const u16 chat_mode_mkwfun[BMG_N_CHAT]; + +//----------------------------------------------------------------------------- + +void InitializeLPAR ( le_lpar_t *lpar, bool load_lpar ); + +enumError LoadLPAR +( + le_lpar_t *lpar, // LE-CODE parameters + bool init_lpar, // true: initialize 'lpar' first + ccp fname, // filename of source, if NULL: use opt_lpar + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +enumError ScanTextLPAR +( + le_lpar_t *lpar, // LE-CODE parameters + bool init_lpar, // true: initialize 'lpar' first + ccp fname, // NULL or filename for error messages + const void * data, // data to scan + uint data_size // size of 'data' +); + +enumError ScanTextLPAR_PARAM +( + le_lpar_t *lpar, // LE-CODE parameters + ScanInfo_t *si, // valid data + bool is_pass2 // false: pass1; true: pass2 +); + +enumError ScanTextLPAR_CHAT +( + le_lpar_t *lpar, // LE-CODE parameters + ScanInfo_t *si, // valid data + bool is_pass2 // false: pass1; true: pass2 +); + +//----------------------------------------------------------------------------- + +enumError SaveTextLPAR +( + const le_lpar_t *lpar, // LE-CODE parameters + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + + +enumError WriteSectionLPAR +( + FILE *f, // output files + const le_lpar_t *lpar // LE-CODE parameters +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// binary headers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[le_binary_head_t]] + +typedef struct le_binary_head_t +{ + /*00*/ char magic[4]; // LE_BINARY_MAGIC + /*04*/ u32 version; // always 4 for v4 + /*08*/ u32 build_number; // current code version + /*0c*/ u32 base_address; // memory address of binary (destination) + /*10*/ u32 entry_point; // memory address (prolog function) + /*14*/ u32 size; // size of complete file + /*18*/ u32 off_param; // offset of param section + /*1c*/ char region; // one of: P, E, J, K + /*1d*/ char debug; // one of: D (debug) or R (release) + /*1e*/ u8 phase; // >0: PHASE + /*1f*/ char unknown_1f; +} +__attribute__ ((packed)) le_binary_head_t; + +//----------------------------------------------------------------------------- +// [[le_binary_head_v4_t]] + +typedef struct le_binary_head_v4_t +{ + /*00*/ char magic[4]; // LE_BINARY_MAGIC + /*04*/ u32 version; // always 4 for v4 + /*08*/ u32 build_number; // current code version + /*0c*/ u32 base_address; // memory address of binary (destination) + /*10*/ u32 entry_point; // memory address (prolog function) + /*14*/ u32 size; // size of complete file + /*18*/ u32 off_param; // offset of param section + /*1c*/ char region; // one of: P, E, J, K + /*1d*/ char debug; // one of: D (debug) or R (release) + /*1e*/ u8 phase; // >0: PHASE + /*1f*/ char unknown_1f; + /*20*/ char timestamp[]; // timestamp of build in ASCII +} +__attribute__ ((packed)) le_binary_head_v4_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[le_binary_param_t]] + +typedef struct le_binary_param_t +{ + /*00*/ char magic[8]; // LE_PARAM_MAGIC + /*08*/ u32 version; // always 1 for v1 + /*0c*/ u32 size; // size (and minor version) + /*10*/ u32 off_eod; // offset of end-of-data + /*14*/ +} +__attribute__ ((packed)) le_binary_param_t; + +//----------------------------------------------------------------------------- +// [[le_binpar_v1_35_t]] + +typedef struct le_binpar_v1_35_t +{ + /*00*/ char magic[8]; // LE_PARAM_MAGIC + /*08*/ u32 version; // always 1 for v1 + /*0c*/ u32 size; // size (and minor version) + /*10*/ u32 off_eod; // offset of end-of-data + + /*14*/ u32 off_cup_par; // offset to cup param + /*18*/ u32 off_cup_track; // offset of cup-track list + /*1c*/ u32 off_cup_arena; // offset of cup-arena list + /*20*/ u32 off_course_par; // offset of course param + /*24*/ u32 off_property; // offset of property list + /*28*/ u32 off_music; // offset of music list + /*2c*/ u32 off_flags; // offset of music list + + /*30*/ u8 engine[3]; // 100cc, 150cc, mirror (sum always 100) + /*33*/ u8 enable_200cc; // TRUE: 200C enabled => 150cc, 200cc, mirror + /*34*/ u8 enable_perfmon; // TRUE: performance monitor enabled + /*35*/ +} +__attribute__ ((packed)) le_binpar_v1_35_t; + +//----------------------------------------------------------------------------- +// [[le_binpar_v1_37_t]] + +typedef struct le_binpar_v1_37_t +{ + /*00*/ char magic[8]; // LE_PARAM_MAGIC + /*08*/ u32 version; // always 1 for v1 + /*0c*/ u32 size; // size (and minor version) + /*10*/ u32 off_eod; // offset of end-of-data + + /*14*/ u32 off_cup_par; // offset to cup param + /*18*/ u32 off_cup_track; // offset of cup-track list + /*1c*/ u32 off_cup_arena; // offset of cup-arena list + /*20*/ u32 off_course_par; // offset of course param + /*24*/ u32 off_property; // offset of property list + /*28*/ u32 off_music; // offset of music list + /*2c*/ u32 off_flags; // offset of music list + + /*30*/ u8 engine[3]; // 100cc, 150cc, mirror (sum always 100) + /*33*/ u8 enable_200cc; // TRUE: 200C enabled => 150cc, 200cc, mirror + /*34*/ u8 enable_perfmon; // TRUE: performance monitor enabled + /*35*/ u8 enable_custom_tt; // TRUE: time trial for cusotm tracks enabled + /*36*/ u8 enable_xpflags; // TRUE: extended presence flags enabled + /*37*/ +} +__attribute__ ((packed)) le_binpar_v1_37_t; + +//----------------------------------------------------------------------------- +// [[le_binpar_v1_f8_t]] + +typedef struct le_binpar_v1_f8_t +{ + /*00*/ char magic[8]; // LE_PARAM_MAGIC + /*08*/ u32 version; // always 1 for v1 + /*0c*/ u32 size; // size (and minor version) + /*10*/ u32 off_eod; // offset of end-of-data + + /*14*/ u32 off_cup_par; // offset to cup param + /*18*/ u32 off_cup_track; // offset of cup-track list + /*1c*/ u32 off_cup_arena; // offset of cup-arena list + /*20*/ u32 off_course_par; // offset of course param + /*24*/ u32 off_property; // offset of property list + /*28*/ u32 off_music; // offset of music list + /*2c*/ u32 off_flags; // offset of music list + + /*30*/ u8 engine[3]; // 100cc, 150cc, mirror (sum always 100) + /*33*/ u8 enable_200cc; // TRUE: 200C enabled => 150cc, 200cc, mirror + /*34*/ u8 enable_perfmon; // TRUE: performance monitor enabled + /*35*/ u8 enable_custom_tt; // TRUE: time trial for cusotm tracks enabled + /*36*/ u8 enable_xpflags; // TRUE: extended presence flags enabled + /*37*/ u8 block_track; // block used track for 0.. tracks + /*38*/ u16 chat_mode[BMG_N_CHAT]; // mode for each chat message + /*f8*/ +} +__attribute__ ((packed)) le_binpar_v1_f8_t; + +//----------------------------------------------------------------------------- +// [[le_binpar_v1_1b8_t]] + +typedef struct le_binpar_v1_1b8_t +{ + /*000*/ char magic[8]; // LE_PARAM_MAGIC + /*008*/ u32 version; // always 1 for v1 + /*00c*/ u32 size; // size (and minor version) + /*010*/ u32 off_eod; // offset of end-of-data + + /*014*/ u32 off_cup_par; // offset to cup param + /*018*/ u32 off_cup_track; // offset of cup-track list + /*01c*/ u32 off_cup_arena; // offset of cup-arena list + /*020*/ u32 off_course_par; // offset of course param + /*024*/ u32 off_property; // offset of property list + /*028*/ u32 off_music; // offset of music list + /*02c*/ u32 off_flags; // offset of music list + + /*030*/ u8 engine[3]; // 100cc, 150cc, mirror (sum always 100) + /*033*/ u8 enable_200cc; // TRUE: 200C enabled => 150cc, 200cc, mirror + /*034*/ u8 enable_perfmon; // TRUE: performance monitor enabled + /*035*/ u8 enable_custom_tt;// TRUE: time trial for cusotm tracks enabled + /*036*/ u8 enable_xpflags; // TRUE: extended presence flags enabled + /*037*/ u8 block_track; // block used track for 0.. tracks + /*038*/ u16 chat_mode_1[BMG_N_CHAT]; // mode for each chat message + /*0f8*/ u16 chat_mode_2[BMG_N_CHAT]; // mode for each chat message + /*1b8*/ +} +__attribute__ ((packed)) le_binpar_v1_1b8_t; + +//----------------------------------------------------------------------------- +// [[le_binpar_v1_t]] + +typedef struct le_binpar_v1_1b8_t le_binpar_v1_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// binary sections /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[le_cup_par_t]] + +typedef struct le_cup_par_t +{ + /*00*/ char magic[4]; // LE_CUP_MAGIC + /*04*/ u32 n_racing_cups; // input: max cups, output: used cups + /*08*/ u32 n_battle_cups; // input: max cups, output: used cups + /*0c*/ +} +__attribute__ ((packed)) le_cup_par_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[le_course_par_t]] + +typedef struct le_course_par_t +{ + /*00*/ char magic[4]; // LE_COURSE_MAGIC + /*04*/ int n_slot; // input: max slots, output: used slots + /*08*/ +} +__attribute__ ((packed)) le_course_par_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// le_analyse_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[le_analyse_t]] + +typedef struct le_analyse_t +{ + //--- header data + + le_valid_t valid; // see LE_VALID_* above + uint version; // version = phase + + const u8 *data; // pointer to raw data + u32 size; // data size + + uint head_vers; // not NULL: version number of header + uint head_size; // not NULL: size told by header + + uint param_offset; // not NULL: offset of param + uint param_vers; // not NULL: version number of param + uint param_size; // not NULL: size told by param + + + //--- working data + + uint n_cup_track; // number of cup tracks + uint max_cup_track; // max possible cup tracks + uint n_cup_arena; // number of cup arenas + uint max_cup_arena; // max possible cup arenas + + uint n_slot; // number of slots + uint max_slot; // max possible slots + uint max_property; // max possible properties + uint max_music; // max possible music slots + uint max_flags; // max possible flags + + uint used_rslots; // used racing slots + uint max_rslots; // max racing slots + uint used_bslots; // used battle slots + uint max_bslots; // max battle slots + + le_lpar_t lpar; // LPAR parameters + + le_usage_t *usage; // NULL or alloced list[n_slot], setup by GetLEUsage() + uint usage_size; // number of alloed 'usage' elements + + + //--- binary data + + u8 *bin_data; // pointer to begin of binary data + uint bin_size; // size of binary data + + + //--- pointers + + le_cup_par_t *cup_par; // NULL or cup parameters + le_course_par_t *course_par; // NULL or course parameters + le_cup_track_t *cup_track; // NULL or list of cup tracks + le_cup_track_t *cup_arena; // NULL or list of cup arenas + le_property_t *property; // NULL or list of properties + le_music_t *music; // NULL or list of music slots + le_flags_t *flags; // NULL or list of flags + + u8 *beg_of_data; // NULL or begin of data + u8 *end_of_data; // NULL or end of data + + union + { + le_binary_head_t * head; + le_binary_head_v4_t * head_v4; + }; + + union + { + le_binary_param_t * param; + le_binpar_v1_t * param_v1; + }; +} +le_analyse_t; + +//----------------------------------------------------------------------------- + +void ResetLEAnalyse ( le_analyse_t *ana ); +void ResetLEAnalyseUsage ( le_analyse_t *ana ); + +enumError AnalyseLEBinary +( + le_analyse_t *ana, // NULL or destination of analysis + const void *data, // data pointer + uint data_size // data size +); + +void CalculateStatsLE ( le_analyse_t *ana ); +const le_usage_t * GetLEUsage ( const le_analyse_t *ana0, bool force_recalc ); +char GetLEUsageChar ( le_usage_t usage ); +ccp GetLEUsageCharCol ( le_usage_t usage, const ColorSet_t *colset, ccp * prev_col ); + +ccp GetLEValid ( le_valid_t valid ); +void CalculateStatsLE ( le_analyse_t *ana ); +void DumpLEAnalyse ( FILE *f, uint indent, const le_analyse_t *ana ); + +static inline bool IsLESlotUsed ( le_analyse_t *ana, uint slot ) + { return slot < ana->n_slot && ana->music[slot]; } + +/////////////////////////////////////////////////////////////////////////////// + +const ctcode_t * LoadLEFile ( uint le_phase ); + +enumError ApplyLEFile ( le_analyse_t * ana ); +enumError ApplyCTCODE ( le_analyse_t * ana, const ctcode_t * ctcode ); +void PatchLECODE ( le_analyse_t * ana ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lecode extension LEX /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define LEX_MAJOR_VERSION 1 +#define LEX_MINOR_VERSION 0 + +#define LEX_BIN_MAGIC "LE-X" +#define LEX_BIN_MAGIC_NUM 0x4c452d58 +#define LEX_TEXT_MAGIC "#LEX" +#define LEX_TEXT_MAGIC_NUM 0x234c4558 + +/////////////////////////////////////////////////////////////////////////////// +// [[have_lex_t]] +// order is important, compare have_lex_info[] and ct.wiimm.de + +typedef enum have_lex_t +{ + HAVELEX_SET1, + HAVELEX_CANN, + HAVELEX_TEST, + HAVELEX_HIPT, + //--- add new elements here (order is important) + HAVELEX__N +} +have_lex_t; + +//----------------------------------------------------------------------------- +// [[lex_stream_id]] + +typedef enum lex_stream_id +{ + LEXS_TERMINATE = 0, // terminate stream + LEXS_IGNORE = 0x2d2d2d2d, // "----" ignore and remove + + LEXS_SET1 = 0x53455431, // "SET1" primary settings + LEXS_CANN = 0x43414e4e, // "CANN" cannon settings + LEXS_HIPT = 0x48495054, // "HIPT" hide position tracker + LEXS_TEST = 0x54455354, // "TEST" settings for tests +} +lex_stream_id; + +//----------------------------------------------------------------------------- +// [[have_lex_info_t]] + +typedef struct have_lex_info_t +{ + lex_stream_id magic; // section magic + ccp name; // info name + uint min_size; // minimal size for 'is available' +} +have_lex_info_t; + +extern const have_lex_info_t have_lex_info[HAVELEX__N]; + +//----------------------------------------------------------------------------- +// [[lex_header_t]] + +typedef struct lex_header_t +{ + char magic[4]; // always LEX_BIN_MAGIC + u16 major_version; // usually LEX_MAJOR_VERSION + u16 minor_version; // usually LEX_MINOR_VERSION + u32 size; // size of this file (header+streams) + u32 element_off; // offset of first lex_element_t, 32-bit aligned +} +__attribute__ ((packed)) lex_header_t; + +//----------------------------------------------------------------------------- +// [[lex_element_t]] + +typedef struct lex_element_t +{ + u32 magic; // identification of section + u32 size; // size of 'data' (this header excluded) + u8 data[]; // section data, 32-bit aligned +} +__attribute__ ((packed)) lex_element_t; + +//----------------------------------------------------------------------------- +// [[lex_set1_t]] + +typedef struct lex_set1_t +{ + float3 item_factor; // factor for item positions, always >=1.0 +} +__attribute__ ((packed,aligned(4))) lex_set1_t; + +//----------------------------------------------------------------------------- +// [[lex_hipt_rule_t]] + +typedef struct lex_hipt_rule_t +{ + u8 cond; // condition bits: 1:offline, 2:online + s8 lap; // compare lap index with this. 99=all laps + u8 from; // compare CHKT >= from + u8 to; // && CHKT <= to + u8 mode; // result: 0=hide, 1:show +} +__attribute__ ((packed,)) lex_hipt_rule_t; + +//----------------------------------------------------------------------------- +// [[lex_test_t]] + +typedef struct lex_test_t +{ + u8 offline_online; // 1: assume offline / 2: assume online + u8 n_offline; // >0: assume # offline humans at Wii + u8 n_online; // >0: assume # players total if playing online + s8 cond_bit; // >=0: use bit # for CONDITIONS + u8 game_mode; // one of LEX_GMODE_* + u8 random; // 1..6: force random scenario + u8 engine; // >=0: force engine := kmp_engine_t+1 + u8 padding; +} +__attribute__ ((packed,aligned(4))) lex_test_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef enumError (*lex_stream_func) + ( u32 magic, const u8 *data, uint size, cvp user_ptr, int user_int ); + +enumError ScanLexFile + ( cvp data, uint data_size, lex_stream_func func, cvp user_ptr, int user_int ); +enumError ScanLexElements + ( cvp data, uint data_size, lex_stream_func func, cvp user_ptr, int user_int ); + +lex_element_t * FindLexElement ( lex_header_t *head, uint data_size, u32 magic ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lex_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[lex_item_t]] + +typedef struct lex_item_t +{ + uint sort_order; // sort priority, lesser comes first + uint insert_order; // second sort priority + + // always last element because of various size!! + lex_element_t elem; // raw element data, big endian +} +lex_item_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[lex_t]] + +typedef struct lex_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // FF_LEX | FF_LEX_TXT + bool develop; // true: enable developer mode + bool modified; // true: modified after loading + + + //--- sections + + uint item_used; // number of used items + uint item_size; // number of alloced items + lex_item_t **item; // NULL or item list (alloced) + uint have_lex; // status vector about found sections + + + //--- parser helpers + + int revision; // tool revision + bool is_pass2; // true: LEX compiler runs pass2 + + + //--- raw data, created by CreateRawLEX() + + u8 * raw_data; // NULL or data + uint raw_data_size; // size of 'raw_data' +} +lex_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeLEX ( lex_t * lex ); +void ResetLEX ( lex_t * lex ); +void UpdateLEX ( lex_t * lex, bool add_missing, bool add_test ); + +lex_item_t * AppendSet1LEX ( lex_t * lex, bool overwrite ); +lex_item_t * AppendCannLEX ( lex_t * lex, bool overwrite ); +lex_item_t * AppendHiptLEX ( lex_t * lex, bool overwrite ); +lex_item_t * AppendTestLEX ( lex_t * lex, bool overwrite ); + +const VarMap_t * SetupVarsLEX(); + +void DumpElementsLEX ( FILE *f, lex_t *lex, bool hexdump ); + +ccp CreateSectionInfoLEX + ( have_lex_t special, bool add_value, ccp return_if_empty ); + +static inline bool IsLexFF ( file_format_t fform ) + { return fform == FF_LEX || fform == FF_LEX_TXT; } + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + const void * data, // data to scan + uint data_size, // size of 'data' + uint dump_mode // 0: add element to lex->sect + // 1: dump scanned elements + // >=0x10: add hexdump of max # bytes +); + +//----------------------------------------------------------------------------- + +enumError ScanTextLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanRawDataLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + struct raw_data_t * raw // valid raw data +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadLEX +( + lex_t * lex, // LEX data structure + bool init_lex, // true: initialize 'lex' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +//----------------------------------------------------------------------------- + +enumError CreateRawLEX +( + lex_t * lex // pointer to valid LEX +); + +//----------------------------------------------------------------------------- + +enumError SaveRawLEX +( + lex_t * lex, // pointer to valid LEX + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextLEX +( + const lex_t * lex, // pointer to valid LEX + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lex_info_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[lex_info_t]] + +typedef struct lex_info_t +{ + bool lex_found; // true: LEX file found + + bool set1_found; // true: section SET1 found + lex_set1_t set1; // data of section SET1 + + bool test_found; // true: section TEST found + lex_set1_t test; // data of section TEST +} +lex_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeLexInfo ( lex_info_t *info ) + { DASSERT(info); memset(info,0,sizeof(*info)); } + +static inline void ResetLexInfo ( lex_info_t *info ) + { if (info) memset(info,0,sizeof(*info)); } + +void SetupLexInfo ( lex_info_t *info, const lex_t *lex ); +enumError SetupLexInfoByData ( lex_info_t *info, cvp data, uint data_size ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LEX patching /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern bool opt_lex_purge; +extern bool force_lex_test; +extern bool opt_lt_clear; +extern int opt_lt_online; +extern int opt_lt_n_offline; +extern int opt_lt_n_online; +extern int opt_lt_itemrain; + +int ScanOptLtOnline ( ccp arg ); +int ScanOptLtNPlayers ( ccp arg ); +int ScanOptLtCondBit ( ccp arg ); +int ScanOptLtGameMode ( ccp arg ); +int ScanOptLtEngine ( ccp arg ); +int ScanOptLtRandom ( ccp arg ); + +// return TRUE if any option set +bool HavePatchTestLEX(); +bool HaveActivePatchTestLEX(); +static inline bool HavePatchLEX() + { return opt_lex_purge || HavePatchTestLEX(); } +static inline bool HaveActivePatchLEX() + { return opt_lex_purge || HaveActivePatchTestLEX(); } + +// return TRUE if modified / set optional 'empty' to true, if test section is empty +bool PatchTestLEX ( lex_test_t *lt, bool *empty ); +bool PatchLEX ( lex_t *lex ); +bool PurgeLEX ( lex_t *lex ); + +struct szs_norm_t; +void AddTestLEX ( struct szs_file_t *szs, struct szs_norm_t *norm ); + +bool LEX_ACTION_LOG ( bool is_patch, const char * format, ... ) + __attribute__ ((__format__(__printf__,2,3))); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsLECODE(); + +static inline bool IsRacingTrackLE ( uint tid ) + { return tid < MKW_N_TRACKS + || tid >= LE_FIRST_LOWER_CT_SLOT && tid != 0xff; } + +uint GetNextRacingTrackLE ( uint tid ); + +/////////////////////////////////////////////////////////////////////////////// + +extern ccp opt_le_define; +extern ccp opt_lpar; +extern ccp opt_track_dest; +extern ParamField_t opt_track_source; + +extern ccp opt_le_alias; +extern bool opt_engine_valid; +extern u8 opt_engine[3]; +extern OffOn_t opt_200cc; +extern OffOn_t opt_perfmon; +extern OffOn_t opt_custom_tt; +extern OffOn_t opt_xpflags; + +extern bool opt_complete; + +int ScanOptTrackSource ( ccp arg, int mode ); +int ScanOptAlias ( ccp arg ); +int ScanOptEngine ( ccp arg ); +int ScanOpt200cc ( ccp arg ); +int ScanOptPerfMon ( ccp arg ); +int ScanOptCustomTT( ccp arg ); +int ScanOptXPFlags ( ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_LECODE_H + diff --git a/project/src/lib-mdl.c b/project/src/lib-mdl.c new file mode 100644 index 0000000..5e623bf --- /dev/null +++ b/project/src/lib-mdl.c @@ -0,0 +1,4556 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "lib-brres.h" +#include "lib-mdl.h" +#include "lib-bzip2.h" + +#include "mdl.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL action log /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool MDL_ACTION_LOG ( bool is_patch, const char * format, ... ) + __attribute__ ((__format__(__printf__,2,3))); + +static bool MDL_ACTION_LOG ( bool is_patch, const char * format, ... ) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[MDL]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + + if ( verbose > 2 || MDL_MODE & MDLMD_LOG || is_patch && ~MDL_MODE & MDLMD_SILENT ) + { + fflush(stdout); + fprintf(stdlog," %s>[MDL]%s ",colset->heading,colset->info); + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// mdl_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +mdl_mode_t MDL_MODE = MDLMD_M_DEFAULT; +int have_mdl_patch_count = 0; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_mdl_tab[] = +{ + { 0, "CLEAR", "RESET", MDLMD_M_ALL | MDLMD_F_HIDE }, + { MDLMD_M_DEFAULT, "DEFAULT", 0, MDLMD_M_ALL | MDLMD_F_HIDE }, + + { MDLMD_LOG, "LOG", 0, 0 }, + { MDLMD_SILENT, "SILENT", 0, 0 }, + + { MDLMD_M_NODES, "ALL-NODES", "ALLNODES", MDLMD_M_NODES }, + { MDLMD_PARENT_NODE, "PARENT-NODE", "PARENTNODES", MDLMD_M_NODES }, + { MDLMD_CHILD_NODES, "CHILD-NODES", "CHILDNODES", MDLMD_M_NODES }, + + { MDLMD_VECTOR, "VECTOR", 0, 0 }, + { MDLMD_MATRIX, "MATRIX", 0, 0 }, + { MDLMD_VERTEX, "VERTEX", "VERTICES", 0 }, + + { MDLMD_TEST0, "TEST0", "T0", MDLMD_M_TEST | MDLMD_F_HIDE }, + { MDLMD_TEST1, "TEST1", "T1", MDLMD_M_TEST }, + { MDLMD_TEST2, "TEST2", "T2", MDLMD_M_TEST }, + { MDLMD_TEST3, "TEST3", "T3", MDLMD_M_TEST }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +void LoadParametersMDL +( + ccp log_prefix // not NULL: + // print log message with prefix +) +{ + static bool done = false; + if (done) + return; + done = true; + + SetMdlMode(MDL_MODE); + + + //--- logging + + if ( !MDL_ACTION_LOG(false,"Global MDL Modes: %s\n",GetMdlMode()) + && log_prefix + && MDL_MODE != MDLMD_M_DEFAULT + && !(MDL_MODE & MDLMD_SILENT) ) + { + fprintf(stdlog,"%sglobal mdl modes: %s [%x]\n",log_prefix,GetMdlMode(),MDL_MODE); + fflush(stdlog); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +mdl_mode_t GetRelevantMdlMode ( mdl_mode_t mode ) +{ + mode = mode & MDLMD_M_ALL; + + if ( !( mode & (MDLMD_VECTOR|MDLMD_MATRIX) )) + mode &= ~MDLMD_M_NODES; + else if ( !( mode & MDLMD_M_NODES )) + mode |= MDLMD_M_NODES; + + if ( PATCH_FILE_MODE & PFILE_F_LOG_ALL ) + mode |= MDLMD_LOG; + + return mode; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetMdlMode ( uint new_mode ) +{ + //-------------------------------------- + DASSERT( 0x40 == sizeof(mdl_head_t) ); + DASSERT( 0xd0 == sizeof(mdl_sect1_t) ); + DASSERT( 0x38 == sizeof(mdl_sect2_t) ); + DASSERT( 0x418 == sizeof(mdl_sect8_t) ); + DASSERT( 0x34 == sizeof(mdl_sect8_layer_t) ); + DASSERT( 0x68 == sizeof(mdl_sect10_t) ); + //-------------------------------------- + + noPRINT("SET-MDL/IN: PC=%d, MPC=%d\n", + have_patch_count, have_mdl_patch_count ); + + if (have_mdl_patch_count) + have_patch_count--, have_mdl_patch_count--; + + if ( mdl_switched_to_vertex + && ( new_mode & (MDLMD_VECTOR|MDLMD_VERTEX) ) == MDLMD_VECTOR ) + { + new_mode = new_mode & ~MDLMD_VECTOR | MDLMD_VERTEX; + PRINT("MDL: VECTOR diabled, VERTEX enabled\n"); + if ( mdl_switched_to_vertex == 1 ) + mdl_switched_to_vertex++; + } + + MDL_MODE = GetRelevantMdlMode(new_mode); + + if ( MDL_MODE & MDLMD_M_PATCH ) + have_patch_count++, have_mdl_patch_count++; + + noPRINT("SET-MDL/OUT: PC=%d, MPC=%d\n", + have_patch_count, have_mdl_patch_count ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptMdl ( ccp arg ) +{ + if (!arg) + return 0; + + static bool done = false; + if (!done) + { + done = true; + if ( *arg != '+' && *arg != '-' ) + MDL_MODE = 0; + } + + s64 stat = ScanKeywordList(arg,opt_mdl_tab,0,true,0,MDL_MODE, + "Option --mdl",ERR_SYNTAX); + if ( stat != -1 ) + { + SetMdlMode(stat); + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintMdlMode ( char *buf, uint bufsize, mdl_mode_t mode ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + char *dest = buf; + char *end = buf + bufsize - 1; + + mode = GetRelevantMdlMode(mode) | MDLMD_F_HIDE; + mdl_mode_t mode1 = mode; + + const KeywordTab_t *ct; + for ( ct = opt_mdl_tab; ct->name1 && dest < end; ct++ ) + { + if ( ct->opt & MDLMD_F_HIDE ) + continue; + + if ( ct->opt ? (mode & ct->opt) == ct->id : mode & ct->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,ct->name1); + mode &= ~(ct->id|ct->opt); + } + } + + if ( mode1 == (MDLMD_M_DEFAULT|MDLMD_F_HIDE) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + return dest-buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetMdlMode() +{ + static char buf[100] = {0}; + if (!*buf) + PrintMdlMode(buf,sizeof(buf),MDL_MODE); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupMDL() +{ + #if 0 + if ( opt_tiny > 0 ) + { + const mdl_mode_t tiny + = ( (mdl_mode_t)opt_tiny << MDLMD_S_TINY ) & MDLMD_M_TINY; + if ( tiny > ( MDL_MODE & MDLMD_M_TINY )) + MDL_MODE = MDL_MODE & ~MDLMD_M_TINY | tiny; + } + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// general helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void * GetVectorMDL +( + float3 *dest, + const void *src, + uint format, + uint r_shift +) +{ + DASSERT(dest); + DASSERT(src); + + switch (format) + { + case 0: // u8 + dest->x = ldexpf(((u8*)src)[0],-r_shift); + dest->y = ldexpf(((u8*)src)[1],-r_shift); + dest->z = ldexpf(((u8*)src)[2],-r_shift); + return (u8*)src + 3; + + case 1: // s8 + dest->x = ldexpf(((s8*)src)[0],-r_shift); + dest->y = ldexpf(((s8*)src)[1],-r_shift); + dest->z = ldexpf(((s8*)src)[2],-r_shift); + return (s8*)src + 3; + + case 2: // u16 + dest->x = ldexpf(be16((u16*)src+0),-r_shift); + dest->y = ldexpf(be16((u16*)src+1),-r_shift); + dest->z = ldexpf(be16((u16*)src+2),-r_shift); + return (u16*)src + 3; + + case 3: // s16 + dest->x = ldexpf((s16)be16((u16*)src+0),-r_shift); + dest->y = ldexpf((s16)be16((u16*)src+1),-r_shift); + dest->z = ldexpf((s16)be16((u16*)src+2),-r_shift); + return (s16*)src + 3; + + case 4: // float + bef4n(dest->v,src,3); + return (float*)src + 3; + + default: + dest->x = dest->y = dest->z = 0.0; + return (void*)src; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static inline int float_to_mdl_int +( + float num, + uint r_shift, + int min, + int max +) +{ + //const int res = truncf( ldexpf(num,r_shift) + 0.5 ); + const int res = ldexpf(num,r_shift); + return res < min ? min : res > max ? max : res; +} + +//----------------------------------------------------------------------------- + +static void * SetVectorMDL +( + const float3 *src, + void *dest, + uint format, + uint r_shift +) +{ + DASSERT(dest); + DASSERT(src); + + switch (format) + { + case 0: // u8 + ((u8*)dest)[0] = float_to_mdl_int(src->x,r_shift,0,0xff); + ((u8*)dest)[1] = float_to_mdl_int(src->y,r_shift,0,0xff); + ((u8*)dest)[2] = float_to_mdl_int(src->z,r_shift,0,0xff); + return (u8*)dest + 3; + + case 1: // s8 + ((s8*)dest)[0] = float_to_mdl_int(src->x,r_shift,-0x80,0x7f); + ((s8*)dest)[1] = float_to_mdl_int(src->y,r_shift,-0x80,0x7f); + ((s8*)dest)[2] = float_to_mdl_int(src->z,r_shift,-0x80,0x7f); + return (s8*)dest + 3; + + case 2: // u16 + write_be16((u16*)dest+0,float_to_mdl_int(src->x,r_shift,0,0xffff)); + write_be16((u16*)dest+1,float_to_mdl_int(src->y,r_shift,0,0xffff)); + write_be16((u16*)dest+2,float_to_mdl_int(src->z,r_shift,0,0xffff)); + return (u16*)dest + 3; + + case 3: // s16 + write_be16((s16*)dest+0,float_to_mdl_int(src->x,r_shift,-0x8000,0x7fff)); + write_be16((s16*)dest+1,float_to_mdl_int(src->y,r_shift,-0x8000,0x7fff)); + write_be16((s16*)dest+2,float_to_mdl_int(src->z,r_shift,-0x8000,0x7fff)); + return (s16*)dest + 3; + + case 4: // float + write_bef4n(dest,src->v,3); + return (float*)dest + 3; + + default: + return (void*)dest; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const MemItem_t * GetMemItemMDLs1 +( + const mdl_t *mdl, // pointer to valid MDL + const MemItem_t *eptr, // pointer to section data + s32 offset // offset to neigbour section 1 +) +{ + DASSERT(mdl); + DASSERT(eptr); + + if (!offset) + return 0; + + const MemItem_t *ebase = GetMemListElem(&mdl->elem,0,0); + const MemItem_t *ptr, *eend = ebase + mdl->elem.used; + + #if USE_NEW_CONTAINER_MDL + if (InContainerP(&mdl->container,eptr->data)) + #else + if (InDataContainer(mdl->old_container,eptr->data)) + #endif + { + const u8 * search = eptr->data + offset; + for ( ptr = ebase; ptr < eend; ptr++ ) + if ( ptr->idx1 == 1 && ptr->data == search ) + { + noPRINT("MDLs1 found by address: %u + %x = %u\n", + eptr->idx2, offset, ptr->idx2 ); + return ptr; + } + } + + int idx = offset / (int)sizeof(mdl_sect1_t); + if ( idx * (int)sizeof(mdl_sect1_t) == offset ) + { + idx += eptr->idx2; + for ( ptr = ebase; ptr < eend; ptr++ ) + if ( ptr->idx1 == 1 && ptr->idx2 == idx ) + { + noPRINT("MDLs1 found by index: %u + %x = %u\n", + eptr->idx2, offset, ptr->idx2 ); + return ptr; + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetNameMDLs1 +( + const mdl_t *mdl, // pointer to valid MDL + const MemItem_t *eptr, // pointer to section data + s32 offset, // offset to neigbour section 1 + ccp if_not_found // return this, if not found +) +{ + DASSERT(mdl); + DASSERT(eptr); + + eptr = GetMemItemMDLs1(mdl,eptr,offset); + return eptr ? eptr->name : if_not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +static MemItem_t * FindSectionbyTypeMDL +( + const mdl_t *mdl, // pointer to valid MDL + uint idx // index of section to find +) +{ + DASSERT(mdl); + + MemItem_t *ptr = GetMemListElem(&mdl->elem,0,0); + MemItem_t *end = ptr + mdl->elem.used; + + for ( ; ptr < end; ptr++ ) + if ( ptr->idx1 == idx ) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static MemItem_t * FindSectionbyPtrMDL +( + const mdl_t *mdl, // valid MDL + const u8 *sptr, // pointer to search + int sect // >=0: search section type only +) +{ + MemItem_t *ptr = GetMemListElem(&mdl->elem,0,0); + MemItem_t *end = ptr + mdl->elem.used; + + for ( ; ptr < end; ptr++ ) + if ( ptr->data == sptr ) + return sect < 0 || sect == ptr->idx1 ? ptr : 0; + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL0 string iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void scan_sections +( + struct StringIteratorMDL_t *sit, // string iterator data + MemItem_t *mi // memory item of section +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + DASSERT(mi); + + switch(mi->idx1) + { + //case 1: + // offsets: 0x5c..: parent, child1, next, prev, part2 + + //case 8: + // offsets: 0x28=shader, 0x30=layer, 0x34=part2, + // 0x38=display_8_9, 0x3c=display_10_11, + + //case 10: + // offsets: 0x20=vertex_decl, 0x2c=vertex_data, 0x64=bone + + case 11: + { + MemItem_t *m8_base = GetMemListElem(&sit->mdl->elem,0,0); + MemItem_t *m8_ptr, *m8_end = m8_base + sit->mdl->elem.used; + + uint n = ( mi->size - 4 ) / 8; + if ( n > 0 ) + { + uint max = be32(mi->data); + if ( n > max ) + n = max; + u32 *sptr = GetStrIdxMIL(mi,2*n); + u32 *src = (u32*)mi->data + 1; + for ( ; n-- > 0; sptr += 2, src += 2 ) + { + sptr[0] = sptr[1] = 1; // default to "-" + const u8* ptr1 = mi->data + be32(src); + for ( m8_ptr = m8_base; m8_ptr < m8_end; m8_ptr++ ) + if ( m8_ptr->idx1 == 8 && m8_ptr->data == ptr1 ) + { + const u32 *m8_sptr = GetStrIdxListMIL(m8_ptr); + sptr[0] = m8_sptr[0]; + + const u8* ptr2 = mi->data + be32(src+1); + if ( ptr2 < m8_ptr->data + m8_ptr->size ) + { + const u8 *layer0 = m8_ptr->data + be32(m8_ptr->data+0x30); + const uint layer_index = ( ptr2 - layer0 ) / 0x34; + //printf("%p %p => 0x%06zx => %d\n",ptr2,layer0,ptr2-layer0,layer_index); + if ( layer0 + 0x34 * layer_index == ptr2 ) + sptr[1] = m8_sptr[layer_index+1]; + } + break; + } + } + } + } + break; + } +} + +//----------------------------------------------------------------------------- + +void scan_strings +( + struct StringIteratorMDL_t *sit, // string iterator data + u32 *sptr, // pointer to string index + u8 *base, // base of item + u32 *data // pointer to string reference +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + DASSERT(sit->sp); + DASSERT(sptr); + DASSERT(base); + DASSERT(data); + + #if USE_NEW_CONTAINER_MDL + if (InContainerP(&sit->mdl->container,data)) + #else + if (InDataContainer(sit->mdl->old_container,data)) + #endif + { + ccp str = (ccp)base + ntohl(*data); + #if USE_NEW_CONTAINER_MDL + if (InContainerP(&sit->mdl->container,str)) + #else + if (InDataContainer(sit->mdl->old_container,str)) + #endif + { + noPRINT("%p %p+%06x = %p |%s|\n",base,data,ntohl(*data),str,str); + *sptr = InsertStringPool(sit->sp,str,false,str); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void string_collect +( + struct StringIteratorMDL_t *sit, // string iterator data + u32 *sptr, // pointer to string index + u8 *base, // base of item + u32 *data // pointer to string reference +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + DASSERT(sit->sp); + DASSERT(base); + DASSERT(data); + + const uint idx = be32(data); + if ( idx < sit->mdl->spool.n2s_size ) + { + ccp str = sit->mdl->spool.n2s_list[idx]; + if (str) + InsertStringPool(sit->sp,str,false,0); + } +} + +//----------------------------------------------------------------------------- + +void string_export +( + struct StringIteratorMDL_t *sit, // string iterator data + u32 *sptr, // pointer to string index + u8 *base, // base of item + u32 *data // pointer to string reference +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + DASSERT(sit->sp); + DASSERT(sptr); + DASSERT(base); + DASSERT(data); + + u32 res = 0; + const uint idx = *sptr; + if ( idx < sit->mdl->spool.n2s_size ) + { + ccp str = sit->mdl->spool.n2s_list[idx]; + if (str) + res = FindStringPool(sit->sp,str,0) + sit->mdl->raw_data - base; + noPRINT("-> %d/%d : %zx %x %s\n", + idx, sit->sp->n2s_size, res + base - sit->mdl->raw_data, res, str ); + } + write_be32(data,res); +} + +//----------------------------------------------------------------------------- + +void offset_export +( + struct StringIteratorMDL_t *sit, // string iterator data + u8 *base, // base of item + u32 *data // pointer to string reference +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + DASSERT(sit->sp); + DASSERT(base); + DASSERT(data); + + write_be32(data,sit->mdl->raw_data-base); +} + +//----------------------------------------------------------------------------- + +void string_copy +( + struct StringIteratorMDL_t *sit, // string iterator data + u32 *sptr, // pointer to string index + u8 *base, // base of item + u32 *data // pointer to string reference +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + DASSERT(sit->sp); + DASSERT(sptr); + DASSERT(base); + DASSERT(data); + + u32 res = 0; + const uint idx = *sptr; + if ( idx < sit->mdl->spool.n2s_size ) + { + ccp str = sit->mdl->spool.n2s_list[idx]; + if (str) + res = InsertStringPool(sit->sp,str,false,0); + } + *sptr = res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void IterateStringsMDL +( + StringIteratorMDL_t *sit, // string iterator data + MemItem_t *mi, // pointer to memlist + int mi_count // number of elements of 'ml_ptr' +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + DASSERT(sit->sp); + DASSERT(mi); + + // => GetStrIdxMIL() must be called for every access, because data may be moved! + + for ( ; mi_count > 0; mi_count--, mi++ ) + { + u8 *data = (u8*)mi->data; + if (!data) + continue; + + noPRINT("--- grp=%d, idx=%d, size=%u\n",mi->idx1,mi->idx2,mi->size); + + if (sit->sect_func) + sit->sect_func(sit,mi); + + if ( sit->off_func && mi->idx1 >= 1 && mi->idx1 <= 10 ) + sit->off_func(sit,data,(u32*)(data+0x04)); + + switch(mi->idx1) // group + { + case 1: + if (sit->str_func) + sit->str_func(sit,GetStrIdxMIL(mi,1),data,(u32*)(data+0x08)); + break; + + case 2: + case 3: + case 4: + case 5: + if (sit->str_func) + sit->str_func(sit,GetStrIdxMIL(mi,1),data,(u32*)(data+0x0c)); + break; + + case 8: + if (sit->str_func) + { + const uint n_layer = be32(data+0x2c); + sit->str_func(sit,GetStrIdxMIL(mi,n_layer+1),data,(u32*)(data+0x08)); + noPRINT("[%d] %p %x\n",count,data,be32(data+0x08)); + u32 layer_off = be32(data+0x30); + uint i; + for ( i = 0; i < n_layer; i++, layer_off += 0x34 ) + { + u8 * d = data + layer_off; + sit->str_func(sit,GetStrIdxMIL(mi,0)+i+1,d,(u32*)d); + } + } + break; + + case 10: + if (sit->str_func) + sit->str_func(sit,GetStrIdxMIL(mi,1),data,(u32*)(data+0x38)); + break; + } + } +} + +//----------------------------------------------------------------------------- + +void IterateAllStringsMDL +( + StringIteratorMDL_t *sit // string iterator data +) +{ + DASSERT(sit); + DASSERT(sit->mdl); + + MemItem_t *mi = GetMemListElem(&sit->mdl->elem,0,0); + IterateStringsMDL(sit,mi,sit->mdl->elem.used); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL0 iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int mdl_iter_func +( + brsub_cut_t *bcut, // pointer to data structure + int grp, // index of group, <0: grp_entry_t + int entry // index of entry, -1: no entry +) +{ + DASSERT(bcut); + szs_iterator_t *it = bcut->it; + DASSERT(it); + DASSERT(it->func_it); + + it->brsub_version = bcut->version; + it->group = grp; + it->entry = entry; + + noPRINT("MDL( %2d, %2d, %s )\n",grp,entry,it->path); + + switch (grp) + { + case GROUP_IDX_SUB_HEADER: + { + const mdl_head_t *head = (mdl_head_t*)( bcut->data + it->off ); + it->size = sizeof(*head); + it->entry = 0; + //int stat = IterateFilesFuncBRSUB(bcut,grp,entry,name); + int stat = it->func_it(it,false); + it->index++; + + //--- MDL bone table + + u32 off = be32(&head->bone_offset); + if ( off && !stat ) + { + u8 * d = (u8*)head + off; + it->off = d - bcut->data; + it->size = be32(d) * 4 + 4; + it->entry = 1; + snprintf(it->path,sizeof(it->path),"%s.bone-table.bin",bcut->id); + stat = it->func_it(it,false); + it->index++; + } + return stat; + } + + case 0: + { + mdl_sect0_t ms0; + InitializeMDLs0(&ms0,bcut->data+it->off,bcut->size-it->off); + it->size = ms0.data_size; + } + break; + + case 11: + if (entry>=0) + it->size = it->endian->rd32(bcut->data+it->off) * 8 + 4; + break; + } + + const int stat = it->func_it(it,false); + it->index++; + return stat; +} + +//----------------------------------------------------------------------------- + +static ccp mdl_names[] = +{ + ".draw-list", // 0 + ".bones", // 1 + ".verticies", // 2 + ".normals", // 3 + ".colors", // 4 + ".t-coord", // 5 + "", // 6 + "", // 7 + ".materials", // 8 + ".mat-nodes", // 9 + ".objects", // 10 + ".t-links", // 11 + 0 +}; + +static ccp GetSectNameMDL ( uint index ) +{ + ccp res = index < sizeof(mdl_names)/sizeof(*mdl_names) + ? mdl_names[index] + : 0; + if ( !res || !*res ) + { + const int bufsize = 12; + char *buf = GetCircBuf(bufsize); + snprintf(buf,bufsize,".%u",index); + res = buf; + } + return res; +} + +//----------------------------------------------------------------------------- + +int IterateFilesMDL +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + return CutFilesBRSUB( it, mdl_iter_func, 0, + mdl_names, sizeof(mdl_names) / sizeof(*mdl_names) -1 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL0 base interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMDL ( mdl_t * mdl ) +{ + DASSERT(mdl); + memset(mdl,0,sizeof(*mdl)); + + mdl->fname = EmptyString; + InitializeMIL(&mdl->elem); + InitializeStringPool(&mdl->spool); + + LoadParametersMDL( verbose > 0 ? " - " : 0 ); + + // [[2do]] [[mdl]] ??? +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMDL ( mdl_t * mdl ) +{ + DASSERT(mdl); + FREE(mdl->bone_tab); + FreeString(mdl->fname); + FreeString(mdl->name); + ResetMIL(&mdl->elem); + ResetStringPool(&mdl->spool); + #if USE_NEW_CONTAINER_MDL + ResetContainer(&mdl->container); + #else + FreeContainer(mdl->old_container); + #endif + InitializeMDL(mdl); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStringMDL +( + mdl_t * mdl, // MDL data structure + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid // result if invalid +) +{ + DASSERT(mdl); + DASSERT(base); + + if (offset) + { + ccp string = (ccp)base + offset; + #if USE_NEW_CONTAINER_MDL + if (InContainerP(&mdl->container,string)) + #else + if (InDataContainer(mdl->old_container,string)) + #endif + { + //HEXDUMP16(0,0,string,32); + return string; + } + } + + return if_invalid; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetStringDataMDL +( + mdl_t *mdl, // MDL data structure + const void *data, // pointer to data + int group, // group index + ccp if_invalid // result if invalid +) +{ + DASSERT(mdl); + DASSERT(data); + + #if USE_NEW_CONTAINER_MDL + if (IsValidContainer(&mdl->container)) + #else + if (mdl->old_container) + #endif + { + switch (group) + { + case 1: + { + const mdl_sect1_t *ms = (mdl_sect1_t*)data; + const u32 name_off = ntohl(ms->name_off); + return GetStringMDL(mdl,data,name_off,if_invalid); + } + + case 2: + { + const mdl_sect2_t *ms = (mdl_sect2_t*)data; + const u32 name_off = ntohl(ms->name_off); + return GetStringMDL(mdl,data,name_off,if_invalid); + } + + case 8: + { + const mdl_sect8_t *ms = (mdl_sect8_t*)data; + const u32 name_off = ntohl(ms->name_off); + return GetStringMDL(mdl,data,name_off,if_invalid); + } + + case 10: + { + const mdl_sect10_t *ms = (mdl_sect10_t*)data; + const u32 name_off = ntohl(ms->name_off); + return GetStringMDL(mdl,data,name_off,if_invalid); + } + + // [[newsect]] + } + } + + return if_invalid; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStringItMDL +( + struct + szs_iterator_t *it, // iterator struct with all infos + bool *res_alloced // result => true: name is alloced +) +{ + DASSERT(it); + DASSERT(res_alloced); + DASSERT(it->szs); + mdl_t *mdl = it->param; + DASSERT(mdl); + + *res_alloced = false; + + + //--- first try 'it->name' + + #if USE_NEW_CONTAINER_MDL + if (InContainerP(&mdl->container,it->name)) + #else + if (InDataContainer(mdl->old_container,it->name)) + #endif + { + noPRINT("GetStringItMDL() LINK NAME => %s\n",it->name); + return it->name; + } + + + //--- analyse well known name offsets + + const u8 *data = it->szs->data + it->off; + ccp res = GetStringDataMDL(mdl,data,it->group,0); + + + //--- should never happen: make a copy of it->name + + if ( !res && it->name ) + { + noPRINT("!!!!!!!!!! GetStringItMDL() => |%.30s|\n",it->name); + res = STRDUP(it->name); + *res_alloced = true; + } + + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup mdl parser /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsMDL() +{ + static VarMap_t vm = {0}; + if (!vm.used) + { + #if 0 + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(&vm,ip->name,ip->val); + #endif + + //--- setup basic parser variables + + DefineParserVars(&vm); + } + + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Scan MDL sections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanSetupMDL +( + mdl_t * mdl, // MDL data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(mdl); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + ScanParam_t ptab[] = + { + { "MDL-NAME", SPM_STRING, &mdl->name }, + { "MDL-VERSION", SPM_NONE, 0 }, + { "N-SECTIONS", SPM_NONE, 0 }, + {0} + }; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + // ignore all others + GotoEolSI(si); + } + CheckLevelSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanHexMDL +( + mdl_t * mdl, // MDL data structure + mdl_section_id sect, // MDL section + ScanInfo_t * si // valid data +) +{ + DASSERT(mdl); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + char *name = 0; + uint sort_index = 0; + ScanParam_t ptab[] = + { + { "NAME", SPM_STRING, &name }, + { "SORT-INDEX", SPM_UINT, &sort_index }, + {0} + }; + + char fb_buf[1000]; + FastBuf_t *fb = InitializeFastBuf(fb_buf,sizeof(fb_buf)); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + if ( ch == '$' ) + { + GotoEolSI(si); + continue; + } + + ScanHexlineSI(si,fb,true); + } + CheckLevelSI(si); + + printf("NAME: %s\nSORT: %u\n",name,sort_index); + HexDump16(stdout,0,0,fb->buf,fb->ptr-fb->buf); + + FreeString(name); + ResetFastBuf(fb); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t mdl_section_name[] = +{ + { MDL_IGNORE, "END", "--", 0 }, + { MDL_SETUP, "SETUP", "Setup", 0 }, + { MDL_SETUP, "BONE-TABLE", "Bone Table", 0 }, + + { MDL_SECTION_0, "SECTION-0", "Section 0", 0 }, + { MDL_SECTION_1, "SECTION-1", "Section 1", 0 }, + { MDL_SECTION_2, "SECTION-2", "Section 2", 0 }, + { MDL_SECTION_3, "SECTION-3", "Section 3", 0 }, + { MDL_SECTION_4, "SECTION-4", "Section 4", 0 }, + { MDL_SECTION_5, "SECTION-5", "Section 5", 0 }, + { MDL_SECTION_6, "SECTION-6", "Section 6", 0 }, + { MDL_SECTION_7, "SECTION-7", "Section 7", 0 }, + { MDL_SECTION_8, "SECTION-8", "Section 8", 0 }, + { MDL_SECTION_9, "SECTION-9", "Section 9", 0 }, + { MDL_SECTION_10, "SECTION-10", "Section 10", 0 }, + { MDL_SECTION_11, "SECTION-11", "Section 11", 0 }, + + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + PRINT("ScanTextMDL(init=%d)\n",init_mdl); + + DASSERT(mdl); + if (init_mdl) + InitializeMDL(mdl); + + mdl->version = 11; + mdl->n_sect = 14; + + ScanInfo_t si; + InitializeSI(&si,data,data_size,mdl->fname,mdl->revision); + si.predef = SetupVarsMDL(); + + enumError max_err = ERR_OK; + mdl->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN MDL SECTIONS, PASS%u ...\n",mdl->is_pass2+1); + + max_err = ERR_OK; + si.mdl = mdl; + si.no_warn = !mdl->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", mdl->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,mdl->revision); + + si.cur_file->ptr++; + char sect_name[20]; + ScanNameSI(&si,sect_name,sizeof(sect_name),true,true,0); + PRINT0("--> pass=%u: #%04u: %s\n",mdl->is_pass2+1,si.cur_file->line,sect_name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,sect_name,mdl_section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + PRINT0("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case MDL_IGNORE: + // ignore it without warning + break; + + case MDL_SETUP: + ScanSetupMDL(mdl,&si); + break; + + case MDL_BONE_TABLE: + case MDL_SECTION_0: + case MDL_SECTION_1: + case MDL_SECTION_2: + case MDL_SECTION_3: + case MDL_SECTION_4: + case MDL_SECTION_5: + case MDL_SECTION_6: + case MDL_SECTION_7: + case MDL_SECTION_8: + case MDL_SECTION_9: + case MDL_SECTION_10: + case MDL_SECTION_11: + ScanHexMDL(mdl,cmd->id,&si); + break; + + default: + err = ERROR0(ERR_WARNING,"Unknown section (ignored): %s\n",sect_name); + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (mdl->is_pass2) + break; + + mdl->is_pass2 = true; + RestartSI(&si); + } + + ResetSI(&si); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int scan_raw_mdl_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + mdl_t *mdl = it->param; + DASSERT(mdl); + + noPRINT("--> %3d,%3d term=%u size=%4u : %s\n", + it->group, it->entry, term, it->size, it->path ); + if ( term || it->size <= 0 ) + return 0; + + const u8 *data = it->szs->data + it->off; + if ( it->group == GROUP_IDX_SUB_HEADER ) + { + if ( it->entry == 0 && it->size >= sizeof(mdl_head_t) ) + { + const mdl_head_t *hsrc = (mdl_head_t*)data; + mdl_head_t *hdest = &mdl->head; + be32n(&hdest->head_len,&hsrc->head_len,10); + bef4n(hdest->min,hsrc->min,6); + } + else if ( it->entry == 1 && it->size >= 4 ) + { + const int max = (it->size-4) / sizeof(*mdl->bone_tab); + uint n = be32(data); + PRINT("BONE TABLE: N=%u,%u\n",n,max); + if ( n > max ) + n = max; + if ( n > 0 ) + { + mdl->bone_n = n; + FREE(mdl->bone_tab); + const u32 *src = (u32*)(data+4); + u32 *dest = mdl->bone_tab = MALLOC( n * sizeof(*mdl->bone_tab) ); + while ( n-- > 0 ) + *dest++ = be32(src++); + DASSERT( (u8*)src <= data + it->size ); + } + } + } + else if ( it->entry >= 0 ) + { + MemItem_t * item = AppendMIL(&mdl->elem); + DASSERT(item); + item->idx1 = it->group; + item->idx2 = it->entry; + item->size = it->size; + #if USE_NEW_CONTAINER_MDL + if (InContainerP(&mdl->container,data)) + #else + if (InDataContainer(mdl->old_container,data)) + #endif + item->data = (u8*)data; + else + { + item->data = MEMDUP(data,item->size); + item->data_alloced = true; + } + + item->name = GetStringItMDL(it,&item->name_alloced); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerMDL_t * cdata // NULL or container-data +) +{ + DASSERT(mdl); + + #ifdef TEST + SaveFile("_mdl-1.tmp",0,FM_OVERWRITE,data,data_size,0); + #endif + + #if USE_NEW_CONTAINER_MDL // --------------------------------------- + + if (init_mdl) + InitializeMDL(mdl); + + CatchContainerData(&mdl->container,0,cdata); + AssignContainer(&mdl->container,0,data,data_size,CPM_COPY); + #ifdef TEST + DumpInfoContainer(stdlog,collog,2,"MDL: ",&mdl->container,0); + #endif + + if (InContainerP(&mdl->container,data)) + mdl->mdl_offset = (u8*)data - mdl->container.cdata->data; + + #else // !USE_NEW_CONTAINER_MDL // --------------------------------- + + if (init_mdl) + InitializeMDL(mdl); + else + FreeContainer(mdl->old_container); + + mdl->old_container = DupContainer(cdata,data,data_size,true,&data); + PRINT("CONTAINER: %p,0x%x -> %p,0x%x [%d]\n", + data, data_size, + mdl->old_container->data, mdl->old_container->size, + mdl->old_container->ref_count ); + + if (InDataContainer(cdata,data)) + mdl->mdl_offset = (u8*)data - cdata->data; + + #endif // !USE_NEW_CONTAINER_MDL // -------------------------------- + + const endian_func_t * endian = &be_func; + if ( IsValidMDL(data,data_size,data_size,0,mdl->fname,endian) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid MDL file: %s\n", mdl->fname ? mdl->fname : "?"); + } + + + //--- scan MDL base infos + + mdl->version = endian->rd32(data+8); + mdl->n_sect = GetSectionNumBRSUB(data,data_size,endian); + + + //--- get MDL name + + const uint off = offsetof(brsub_header_t,grp_offset) + mdl->n_sect * 4; + const uint name_off = be32(data+off); + ccp name = GetStringBRSUB(data,data+data_size,off,data,name_off,0,0); + if (name) + mdl->name = STRDUP(name); + + + //--- scan MDL header & sections + + cut_iter_func[FF_MDL] = IterateFilesMDL; // enable MDL support +// [[fname+]] + IterateFilesData(data,data_size,scan_raw_mdl_func,mdl,FF_MDL,mdl->fname); + SortMIL(&mdl->elem,false); + + + //--- string handling + + ResetStringPool(&mdl->spool); + InsertStringPool(&mdl->spool,"-",false,0); + + StringIteratorMDL_t sim; + memset(&sim,0,sizeof(sim)); + sim.mdl = mdl; + sim.sp = &mdl->spool; + sim.sect_func = scan_sections; + sim.str_func = scan_strings; + IterateAllStringsMDL(&sim); + + #ifdef TEST0 + { + uint i; + for ( i = 0; i < mdl->spool.n2s_size; i++ ) + { + ccp str = mdl->spool.n2s_list[i]; + if (str) + PRINT("SP %2u: %s\n",i,str); + } + } + #endif + + #ifdef TEST + SaveFile("_mdl-2.tmp",0,FM_OVERWRITE,data,data_size,0); + #endif + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// iterate MDL sub files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct iter_helper_mdl_t +{ + struct raw_data_t * raw; // valid raw data + CheckMode_t check_mode; // not NULL: call CheckMDL(mode) + iterate_mdl_func func; // function to call + void *param; // user defined parameter + enumError max_err; // max error code + +} iter_helper_mdl_t; + +//----------------------------------------------------------------------------- + +static int iter_raw_data_mdl +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if ( term || it->is_dir ) + return 0; + + DASSERT(it->szs); + const u8 *data = it->szs->data + it->off; +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,it->size,it->size); + if ( fform != FF_MDL && fform != FF_MDL_TXT ) + return 0; + + DASSERT(it->param); + iter_helper_mdl_t *iparam = it->param; + DASSERT(iparam->raw); + DASSERT(iparam->func); + + mdl_t mdl; + InitializeMDL(&mdl); + mdl.fname = STRDUP3(iparam->raw->fname,"/",it->path); + + #if USE_NEW_CONTAINER_MDL + enumError err = ScanMDL(&mdl,false,data,it->size, + LinkContainerRawData(iparam->raw),iparam->check_mode); + #else + enumError err = ScanMDL(&mdl,false,data,it->size, + ContainerRawData(iparam->raw),iparam->check_mode); + #endif + if ( err < ERR_WARNING ) + err = iparam->func(&mdl,iparam->param); + if ( iparam->max_err < err ) + iparam->max_err = err; + ResetMDL(&mdl); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError IterateRawDataMDL +( + struct raw_data_t * raw, // valid raw data + CheckMode_t check_mode, // not NULL: call CheckMDL(mode) + iterate_mdl_func func, // call this function for each found MDL + void * param // a user defined parameter +) +{ + DASSERT(raw); + DASSERT(func); + + if ( raw->fform == FF_MDL + || raw->fform == FF_MDL_TXT + || !IsArchiveFF(raw->fform) ) + { + mdl_t mdl; + enumError err = ScanRawDataMDL(&mdl,true,raw,check_mode); + if ( err < ERR_WARNING ) + err = func(&mdl,param); + ResetMDL(&mdl); + return err; + } + + iter_helper_mdl_t iparam; + iparam.raw = raw; + iparam.check_mode = check_mode; + iparam.func = func; + iparam.param = param; + iparam.max_err = ERR_OK; + + szs_file_t szs; +// [[fname+]] + AssignSZS(&szs,true,raw->data,raw->data_size,false,raw->fform,raw->fname); + IterateFilesSZS(&szs,iter_raw_data_mdl,&iparam,false,-1,-1,SORT_NONE); + ResetSZS(&szs); + return iparam.max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL0 scan + load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerMDL_t * cdata, // NULL or container-data + CheckMode_t mode // not NULL: call CheckMDL(mode) +) +{ + DASSERT(mdl); + PRINT("ScanMDL(mode=%x) size=%u\n",mode,data_size); + + enumError err; +// [[analyse-magic]] + switch (GetByMagicFF(data,data_size,data_size)) + { + case FF_MDL: + mdl->fform = FF_MDL; + if ( disable_patch_on_load > 0 || !HavePatchMDL() ) + err = ScanRawMDL(mdl,init_mdl,data,data_size,cdata); + else + { + //dc = 0; + u8 *temp = MEMDUP(data,data_size); + PatchRawDataMDL(temp,data_size,0,mdl->fname); + err = ScanRawMDL(mdl,init_mdl,temp,data_size,0); + FREE(temp); + } + break; + + case FF_MDL_TXT: + #if USE_NEW_CONTAINER_MDL + FreeContainerData(cdata); + #else + FreeContainer(cdata); + #endif + mdl->fform = FF_MDL_TXT; + err = ScanTextMDL(mdl,init_mdl,data,data_size); + break; + + default: + #if USE_NEW_CONTAINER_MDL + FreeContainerData(cdata); + #else + FreeContainer(cdata); + #endif + if (init_mdl) + InitializeMDL(mdl); + return ERROR0(ERR_INVALID_DATA, + "No MDL file: %s\n", mdl->fname ? mdl->fname : "?"); + } + + if ( err <= ERR_WARNING && mode ) + CheckMDL(mdl,mode); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckMDL(mode) +) +{ + DASSERT(mdl); + DASSERT(raw); + if (init_mdl) + InitializeMDL(mdl); + else + ResetMDL(mdl); + + mdl->fname = raw->fname; + raw->fname = 0; + #if USE_NEW_CONTAINER_MDL + return ScanMDL(mdl,false,raw->data,raw->data_size,LinkContainerRawData(raw),mode); + #else + return ScanMDL(mdl,false,raw->data,raw->data_size,ContainerRawData(raw),mode); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckMDL(mode) +) +{ + DASSERT(mdl); + DASSERT(fname); + if (init_mdl) + InitializeMDL(mdl); + else + ResetMDL(mdl); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,false); + if (!err) + { + mdl->fname = raw.fname; + raw.fname = 0; + #if USE_NEW_CONTAINER_MDL + err = ScanMDL(mdl,false,raw.data,raw.data_size,LinkContainerRawData(&raw),mode); + #else + err = ScanMDL(mdl,false,raw.data,raw.data_size,ContainerRawData(&raw),mode); + #endif + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawMDL +( + mdl_t * mdl // pointer to valid MDL +) +{ + DASSERT(mdl); + TRACE("CreateRawMDL()\n"); + + const uint MAX_SECT = 20; // max supported sections + + + //--- setup string pool + + string_pool_t sp; + InitializeStringPool(&sp); + + char fname_buf[200]; + ccp mdl_name = 0; + if (mdl->name) + { + mdl_name = mdl->name; + InsertStringPool(&sp,mdl_name,false,0); + } + else if (mdl->fname) + { + mdl_name = strrchr(mdl->fname,'/'); + mdl_name = mdl_name ? mdl_name+1 : mdl->fname; + uint cut = 0; + const int len = strlen(mdl_name); + if ( len > 4 && ( !strcasecmp(mdl_name+len-4,".mdl") + || !strcasecmp(mdl_name+len-4,".bin") )) + cut = len-4; + else if ( len > 5 && !strcasecmp(mdl_name+len-5,".mdl0") ) + cut = len-5; + + if (cut) + { + StringCopySM(fname_buf,sizeof(fname_buf),mdl_name,cut); + mdl_name = fname_buf; + } + InsertStringPool(&sp,mdl_name,false,0); + } + + + //--- version and sections + + uint sect_count[MAX_SECT]; + memset(sect_count,0,sizeof(sect_count)); + + int version = mdl->version; + int n_sect = GetKnownSectionNumBRSUB(FF_MDL,version); + if ( n_sect <= 0 ) + { + version = 11; + n_sect = GetKnownSectionNumBRSUB(FF_MDL,version); + ASSERT(n_sect>0); + } + const int max_sect = n_sect < MAX_SECT ? n_sect : MAX_SECT; + + + //--- calculate data size (section data) + + SortMIL(&mdl->elem,true); + MemItem_t *mi_base = GetMemListElem(&mdl->elem,0,0); + MemItem_t *mi_ptr, *mi_prev, *mi_end = mi_base + mdl->elem.used; + + uint data_size = 0; + for ( mi_ptr = mi_base, mi_prev = 0; mi_ptr < mi_end; mi_ptr++ ) + { + u8 *data = (u8*)mi_ptr->data; + if ( !data || mi_ptr->idx1 >= max_sect ) + continue; + + if ( !mi_prev || mi_ptr->size != mi_prev->size + || memcmp(mi_ptr->data,mi_prev->data,mi_ptr->size) ) + { + data_size += mi_ptr->size; + } + sect_count[mi_ptr->idx1]++; + + InsertStringPool(&sp,mi_ptr->name,false,0); + mi_prev = mi_ptr; + } + + + //--- calculate header size + + const uint head_offset = 0x14 + 4*n_sect; + const uint bone_offset = head_offset + sizeof(mdl_head_t); + const uint header_size = bone_offset + + 4 + mdl->bone_n * sizeof(*mdl->bone_tab); + + uint sect, brsub_n = 0, brsub_size = 0; + for ( sect = 0; sect < max_sect; sect++ ) + { + const uint n = sect_count[sect]; + if (n) + { + brsub_n += 1 + n; // root + N + brsub_size += 0x18 + n*0x10; + } + } + + PRINT("NS=%d, header_size=0x%x, brsub_n/size=%u,0x%x\n", + n_sect, header_size, brsub_n, brsub_size ); + + + //--- create string pool + + const uint string_offset = header_size + brsub_size + data_size; + + StringIteratorMDL_t sim; + memset(&sim,0,sizeof(sim)); + sim.mdl = mdl; + sim.sp = &sp; + sim.str_func = string_collect; + IterateAllStringsMDL(&sim); + CalcStringPool(&sp,string_offset,&be_func); + const uint file_size = string_offset + sp.size; + + PRINT("CREATE MDL: size = %#x = %u\n",data_size,data_size); + MDL_ACTION_LOG(false,"CreateRawMDL() size=%u\n",data_size); + + + //--- alloc data + + FREE(mdl->raw_data); + u8 * mdata = CALLOC(1,file_size); + mdl->raw_data = mdata; + mdl->raw_data_size = string_offset; + mdl->raw_file_size = file_size; + + + //--- setup header + string pool + + memcpy(mdata,MDL_MAGIC,4); + write_be32(mdata+0x04,string_offset); + write_be32(mdata+0x08,version); + if (mdl_name) + write_be32(mdata+0x10+n_sect*4,FindStringPool(&sp,mdl_name,0)); + memcpy(mdata+string_offset,sp.data,sp.size); + write_be32n((u32*)(mdata+head_offset),(u32*)&mdl->head,16); + + + //--- copy bones + + u32 *ptr = (u32*)(mdata + bone_offset); + write_be32(ptr++,mdl->bone_n); + uint i; + for ( i = 0; i < mdl->bone_n; i++ ) + write_be32(ptr++,mdl->bone_tab[i]); + + + //--- prepare entries + + uint sect_info_idx[MAX_SECT]; + memset(sect_info_idx,0,sizeof(sect_info_idx)); + + brres_info_t *info = CALLOC(brsub_n,sizeof(*info)); + brres_info_t *info_ptr = info; + + u32 offset = header_size; + for ( sect = 0; sect < max_sect; sect++ ) + { + const uint n = sect_count[sect]; + if (n) + { + info_ptr->id = 0xffff; + info_ptr++; + sect_info_idx[sect] = info_ptr - info; + info_ptr += n; + } + } + //HEXDUMP16(0,0,sect_info_idx,sizeof(sect_info_idx)); + + + //--- copy section data + + memset(&sim,0,sizeof(sim)); + sim.mdl = mdl; + sim.sp = &sp; + sim.off_func = offset_export; + sim.str_func = string_export; + + offset = header_size + brsub_size; + for ( mi_ptr = mi_base, mi_prev = 0; mi_ptr < mi_end; mi_ptr++ ) + { + noPRINT("SECT-%d.%d, off=0x%x, size=0x%x\n", + mi_ptr->idx1, mi_ptr->idx2, offset, mi_ptr->size ); + u8 *data = (u8*)mi_ptr->data; + if ( !data || mi_ptr->idx1 >= max_sect ) + continue; + + if ( mi_prev && mi_ptr->size == mi_prev->size + && !memcmp(mi_ptr->data,mi_prev->data,mi_ptr->size) ) + { + offset -= mi_prev->size; + } + + brres_info_t *info_ptr = info + sect_info_idx[mi_ptr->idx1]++; + info_ptr->data_off = offset; + info_ptr->name = mi_ptr->name ? mi_ptr->name : ""; + info_ptr->nlen = strlen(info_ptr->name); + info_ptr->name_off = FindStringPool(&sp,info_ptr->name,0); + + mi_ptr->data = mdata+offset; // temporary + memcpy(mi_ptr->data,data,mi_ptr->size); + IterateStringsMDL(&sim,mi_ptr,1); + mi_ptr->data = data; + + offset += mi_ptr->size; + DASSERT( offset <= string_offset ); + mi_prev = mi_ptr; + } + DASSERT( offset == string_offset ); + //HEXDUMP16(0,0,sect_info_idx,sizeof(sect_info_idx)); + + #ifdef TEST0 + { + uint i; + for ( i = 0; i < brsub_n; i++ ) + { + brres_info_t *bi = info + i; + printf("\t%04x %04x %04x : %06x %06x : [%2d] |%s|\n", + bi->id, bi->left_idx, bi->right_idx, + bi->name_off, bi->data_off, + bi->nlen, bi->name ); + } + } + #endif + + + //--- finish brsub header + + offset = header_size; + for ( info_ptr = info, sect = 0; sect < max_sect; sect++ ) + { + uint n = sect_count[sect]; + if (n) + { + u32 base_offset = offset; + write_be32(mdata+0x10+4*sect,offset); + write_be32(mdata+offset,(n+1)*0x10+8); + offset += 4; + write_be32(mdata+offset,n); + offset += 4; + + uint i; + for ( i = 1; i <= n; i++ ) // without root + CalcEntryBRRES(info_ptr,i); + + for ( i = 0; i <= n; i++ ) // with root + { + brres_entry_t *entry_ptr = (brres_entry_t*)(mdata+offset); + write_be16( &entry_ptr->id, info_ptr->id ); + write_be16( &entry_ptr->left_idx, info_ptr->left_idx ); + write_be16( &entry_ptr->right_idx, info_ptr->right_idx ); + if (info_ptr->data_off) + write_be32( &entry_ptr->data_off, info_ptr->data_off - base_offset ); + if (info_ptr->name_off) + write_be32( &entry_ptr->name_off, info_ptr->name_off - base_offset ); + offset += 0x10; + info_ptr++; + } + } + } + + + //--- terminate + + ResetStringPool(&sp); + FREE(info); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawMDL +( + mdl_t * mdl, // pointer to valid MDL + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(mdl); + DASSERT(fname); + PRINT("SaveRawMDL(%s,%d)\n",fname,set_time); + MDL_ACTION_LOG(false,"SaveRawMDL() %s\n",fname); + + + //--- create raw data + + enumError err = CreateRawMDL(mdl); + if (err) + return err; + DASSERT(mdl->raw_data); + DASSERT(mdl->raw_file_size); + + + //--- write to file + + File_t F; + err = CreateFileOpt(&F,true,fname,testmode,mdl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&mdl->fatt,0); + + if ( fwrite(mdl->raw_data,1,mdl->raw_file_size,F.f) != mdl->raw_file_size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextMDLhex +( + FILE * f, // file to print to + const mdl_t * mdl, // pointer to valid MDL + const MemItem_t * eptr // pointer to section data +) +{ + DASSERT(f); + DASSERT(mdl); + DASSERT(eptr); + + fputs(text_mdl_hexdump_cr,f); + hexdump_eol = "\r\n"; + HexDump(f,0,0,5,16,eptr->data,eptr->size); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextMDLs0 +( + FILE * f, // file to print to + const mdl_t * mdl, // pointer to valid MDL + const MemItem_t * eptr // pointer to section data +) +{ + DASSERT(f); + DASSERT(mdl); + DASSERT(eptr); + + if (!brief_count) + fprintf(f,text_mdl_s0_cr); + fprintf(f,text_mdl_s0_tabhead_cr); + + mdl_sect0_t ms0; + InitializeMDLs0(&ms0,eptr->data,eptr->size); + while (NextMDLs0(&ms0)) + { + ccp typestr = GetTypeMDLs0(*ms0.cur); + if (ms0.sub_size) + { + DASSERT(ms0.sub_n>0); + DASSERT(ms0.sub_ptr); + DASSERT(ms0.sub_ptr > ms0.cur); + PrintNumF(f,ms0.cur,ms0.sub_ptr-ms0.cur,typestr); + + typestr = strchr(typestr,'|'); + if (typestr) + typestr++; + + int i; + for ( i = 0; i < ms0.sub_n; i++ ) + { + fputs("\r\n\t> ",f); + PrintNumF(f,ms0.sub_ptr + i*ms0.sub_size,ms0.sub_size,typestr); + } + } + else + PrintNumF(f,ms0.cur,ms0.cur_size,typestr); + fputs("\r\n",f); + } + + if (ms0.fail) + { + fputs("$RAW",f); + PrintNumF(f,ms0.fail,ms0.cur_size,0); + fputs("\r\n",f); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextMDLs1 +( + FILE * f, // file to print to + const mdl_t * mdl, // pointer to valid MDL + const MemItem_t * eptr // pointer to section data +) +{ + DASSERT(f); + DASSERT(mdl); + DASSERT(eptr); + + mdl_sect1_t ms; + ntoh_MDLs1(&ms,(mdl_sect1_t*)eptr->data); + if ( eptr->size != sizeof(ms) || ms.length != sizeof(ms) ) + return SaveTextMDLhex(f,mdl,eptr); + + fprintf(f, + "length = %#11x\r\n" + "mdl0-offset = %#11x\r\n" +// "name-offset = %#11x\r\n" + "index = %#11x\r\n" + "id = %#11x\r\n" + "flags = %#11x\r\n" + "padding = %#11x %#11x\r\n" + "\r\n" + "parent = %#11x \"%s\"\r\n" + "child = %#11x \"%s\"\r\n" + "next = %#11x \"%s\"\r\n" + "prev = %#11x \"%s\"\r\n" + "part2 = %#11x \"%s\"\r\n" + "\r\n" + + ,ms.length + ,ms.mdl0_off +// ,ms.name_off + ,ms.index + ,ms.id + ,ms.flags + ,ms.padding[0] ,ms.padding[1] + + ,ms.parent_off ,GetNameMDLs1(mdl,eptr,ms.parent_off,"") + ,ms.child_off ,GetNameMDLs1(mdl,eptr,ms.child_off,"") + ,ms.next_off ,GetNameMDLs1(mdl,eptr,ms.next_off,"") + ,ms.prev_off ,GetNameMDLs1(mdl,eptr,ms.prev_off,"") + ,ms.part2_off ,GetNameMDLs1(mdl,eptr,ms.part2_off,"") + ); + + PrintMatrixMDLs1(f,0,"\r\n",&ms,~0); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextMDLs2 +( + FILE * f, // file to print to + const mdl_t * mdl, // pointer to valid MDL + const MemItem_t * eptr // pointer to section data +) +{ + DASSERT(f); + DASSERT(mdl); + DASSERT(eptr); + + mdl_sect2_t ms; + ntoh_MDLs2(&ms,(mdl_sect2_t*)eptr->data); + // if ( eptr->size != sizeof(ms) || ms.length != sizeof(ms) ) + if ( ms.length < sizeof(ms) ) + return SaveTextMDLhex(f,mdl,eptr); + + //--- print base data + + fprintf(f, + "length = %#11x\r\n" + "mdl0-offset = %#11x\r\n" + "data-offset = %#11x\r\n" +// "name-offset = %#11x\r\n" + "index = %#11x\r\n" + "has-bounds = %11u\r\n" + "format = %11u\r\n" + "r_shift = %11u\r\n" + "stride = %11u\r\n" + "n_vertex = %#11x = %6u\r\n" + "minimum = %11.3f %11.3f %11.3f\r\n" + "maximum = %11.3f %11.3f %11.3f\r\n" + "\r\n" + ,ms.length + ,ms.mdl0_off + ,ms.data_off +// ,ms.name_off + ,ms.index + ,ms.has_bounds + ,ms.format + ,ms.r_shift + ,ms.stride + ,ms.n_vertex ,ms.n_vertex + ,ms.minimum.x ,ms.minimum.y ,ms.minimum.z + ,ms.maximum.x ,ms.maximum.y ,ms.maximum.z + ); + + + //--- print vertex table + + const void *data = eptr->data + ms.data_off; + uint n_vertex = ms.n_vertex; + noPRINT("N-VERTEX=%u, FORMAT=%u, R-SHIFT=%u\n",n_vertex,ms.format,ms.r_shift); + + while ( n_vertex-- > 0 ) + { + float3 temp; + data = GetVectorMDL(&temp,data,ms.format,ms.r_shift); + fprintf(f,"v %11.3f %11.3f %11.3f\r\n", temp.x, temp.y, temp.z ); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SaveTextMDLs10 +( + FILE * f, // file to print to + const mdl_t * mdl, // pointer to valid MDL + const MemItem_t * eptr // pointer to section data +) +{ + DASSERT(f); + DASSERT(mdl); + DASSERT(eptr); + + mdl_sect10_t ms; + ntoh_MDLs10(&ms,(mdl_sect10_t*)eptr->data); + if ( eptr->size < ms.length || ms.length < sizeof(ms) ) + return SaveTextMDLhex(f,mdl,eptr); + + //--- print base data + + fprintf(f, + "length = %#10x\r\n" + "mdl0_off = %#10x\r\n" + "bone_idx = %#10x\r\n" + "cp_vtx = %#10x\r\n" + "cp_tex = %#10x\r\n" + "xf_nor_spec = %#10x\r\n" + "v_decl_size = %#10x\r\n" + "flags = %#10x\r\n" + "v_decl_offset = %#10x\r\n" + "v_data_size1 = %#10x\r\n" + "v_data_size2 = %#10x\r\n" + "v_data_offset = %#10x\r\n" + "xf_flags = %#10x\r\n" + "unknown_0x34 = %#10x\r\n" + "name_off = %#10x\r\n" + "idx = %#10x = %6u\r\n" + "n_vertex = %#10x = %6u\r\n" + "n_face = %#10x = %6u\r\n" + "v_group = %#10x = %6u\r\n" + "n_group = %#10x = %6u\r\n" + "col_groups = %#10x %#06x\r\n" + "tex_groups = %#10x %#06x %#06x %#06x %#06x %#06x %#06x %#06x\r\n" + "unknown_0x60 = %#10x\r\n" + "bone_offset = %#10x\r\n" + ,ms.length + ,ms.mdl0_off + ,ms.bone_idx + ,ms.cp_vtx + ,ms.cp_tex + ,ms.xf_nor_spec + ,ms.v_decl_size + ,ms.flags + ,ms.v_decl_offset + ,ms.v_data_size1 + ,ms.v_data_size2 + ,ms.v_data_offset + ,ms.xf_flags + ,ms.unknown_0x34 + ,ms.name_off + ,ms.idx ,ms.idx + ,ms.n_vertex ,ms.n_vertex + ,ms.n_face ,ms.n_face + ,ms.v_group ,ms.v_group + ,ms.n_group ,ms.n_group + ,ms.col_group[0] ,ms.col_group[1] + ,ms.tex_group[0] ,ms.tex_group[1] ,ms.tex_group[2] ,ms.tex_group[3] + ,ms.tex_group[4] ,ms.tex_group[5] ,ms.tex_group[6] ,ms.tex_group[7] + ,ms.unknown_0x60 + ,ms.bone_offset + ); + + + //--- additonally data + + u32 bone_off = ms.bone_offset; + u32 v_decl_offset = ms.v_decl_offset + 0x20; + u32 v_data_offset = ms.v_data_offset + 0x24; + + + //--- bones + + u32 end = eptr->size; + if ( v_decl_offset > bone_off && end > v_decl_offset ) + end = v_decl_offset; + if ( v_data_offset > bone_off && end > v_data_offset ) + end = v_data_offset; + + if ( bone_off < end ) + { + fprintf(f,"\r\n# bones\r\n\r\n"); + hexdump_prefix = " $HEX "; + hexdump_eol = "\r\n"; + HexDump(f,0,bone_off,5,16,eptr->data+bone_off,end-bone_off); + } + + + //--- vertex declaration + + end = eptr->size; + if ( bone_off > v_decl_offset && end > bone_off ) + end = bone_off; + if ( v_data_offset > v_decl_offset && end > v_data_offset ) + end = v_data_offset; + + if ( v_decl_offset < end ) + { + fprintf(f,"\r\n# vertex declaration\r\n\r\n"); + hexdump_prefix = " $HEX "; + hexdump_eol = "\r\n"; + HexDump(f,0,v_decl_offset,5,16,eptr->data+v_decl_offset,end-v_decl_offset); + } + + + //--- vertex data + + end = eptr->size; + if ( bone_off > v_data_offset && end > bone_off ) + end = bone_off; + if ( v_decl_offset > v_data_offset && end > v_decl_offset ) + end = v_decl_offset; + + if ( v_data_offset < end ) + { + #ifdef TEST0 + fprintf(f,"\r\n# vertex data\r\n"); + mdl_sect2_t ms2; + const u8 *s2data = 0; + const MemItem_t *s2 = FindSectionbyTypeMDL(mdl,2); + if (s2) + { + ntoh_MDLs2(&ms2,(mdl_sect2_t*)s2->data); + s2data = s2->data + ms2.data_off; + } + + u8 *ptr = eptr->data + v_data_offset; + u8 *endptr = eptr->data + end; + + uint i_face = 0; + while ( ptr+3 < endptr && *ptr ) + { + uint max = (endptr-ptr-3)/4; + uint n = be16(ptr+1); + if ( n > max ) + n = max; + fprintf(f,"\r\n # face %u, mode = %#04x, n = %u\r\n",i_face++,*ptr,n); + ptr += 3; + + if (s2data) + { + for ( ; n > 0; n--, ptr += 4 ) + { + float3 temp; + uint vidx = be16(ptr); + if ( vidx < ms2.n_vertex ) + { + GetVectorMDL(&temp, s2data + vidx * ms2.stride, + ms2.format, ms2.r_shift ); + } + else + temp.x = temp.y = temp.z = NAN; + + fprintf(f,"\t%11.3f %11.3f %11.3f %#06x\r\n", + temp.x, temp.y, temp.z, be16(ptr+2) ); + } + } + else + { + for ( ; n > 0; n--, ptr += 4 ) + fprintf(f,"\t%5u %#06x\r\n",be16(ptr),be16(ptr+2)); + } + } + fprintf(f,"\r\n# vertex data as hexdump\r\n\r\n"); + #else + fprintf(f,"\r\n# vertex data\r\n\r\n"); + #endif + + hexdump_prefix = " $HEX "; + hexdump_eol = "\r\n"; + HexDump(f,0,v_data_offset,5,16,eptr->data+v_data_offset,end-v_data_offset); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextMDL +( + const mdl_t * mdl, // pointer to valid MDL + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(mdl); + DASSERT(fname); + PRINT("SaveTextMDL(%s,%d)\n",fname,set_time); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,mdl->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&mdl->fatt,0); + + + //--- print file header + syntax info + + if ( print_header && !brief_count ) + fprintf(F.f,text_mdl_file_head_cr); + else + fprintf(F.f,"%s\r\n",MDL_TEXT_MAGIC); + + + //--- print info section + + if (brief_count) + { + fprintf(F.f, + "%s[SETUP]\r\n\r\n" + "TOOL = %s\r\n" + "SYSTEM = %s\r\n" + "VERSION = %s\r\n" + "REVISION = %u\r\n" + "DATE = %s\r\n" + "\r\n" + "MDL-NAME = \"%s\"\r\n" + "MDL-VERSION = %2u\r\n" + "N-SECTIONS = %2u\r\n" + "\r\n", + section_sep, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, + mdl->name ? mdl->name : "", + mdl->version, mdl->n_sect ); + } + else + { + fprintf(F.f, text_mdl_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, + mdl->name ? mdl->name : "", + mdl->version, mdl->n_sect ); + } + + #if 0 // [[2do]] [[mdl]] ??? + if (print_param) + { + if (!brief_count) + fwrite(text_mdl_param1_cr,1,sizeof(text_mdl_param1_cr)-1,F.f); + else + fprintf(F.f,"#%.78s\r\n# Parameter support\r\n",Minus300); + + fwrite(text_mdl_param2_cr,1,sizeof(text_mdl_param2_cr)-1,F.f); + if (!brief_count) + fwrite(text_mdl_param3_cr,1,sizeof(text_mdl_param3_cr)-1,F.f); + } + #endif + + + //--- write MDL header + + if (brief_count) + fprintf(F.f, + "%s[HEADER]\r\n" + "@REVISION = %u\r\n" + "\r\n", + section_sep, + REVISION_NUM ); + else + fprintf(F.f,text_mdl_header_cr,REVISION_NUM); + + const mdl_head_t *h = &mdl->head; + fprintf(F.f, + "HEAD-LEN = %#10x\r\n" + "OFFSET = %#10x\r\n" + "UNKNOWN-08 = %#10x\r\n" + "UNKNOWN-0c = %#10x\r\n" + "N-VERTEX = %10u\r\n" + "N-FACE = %10u\r\n" + "UNKNOWN-18 = %#10x\r\n" + "N-BONE = %10u\r\n" + "UNKNOWN-20 = %#10x\r\n" + "UNKNOWN-08 = %#10x\r\n" + "BONE-OFFSET = %#10x\r\n" + "MIN = %11.3f %11.3f %11.3f\r\n" + "MAX = %11.3f %11.3f %11.3f\r\n" + "\r\n", + h->head_len, + h->offset, + h->unknown_0x08, + h->unknown_0x0c, + h->n_vertex, + h->n_face, + h->unknown_0x18, + h->unknown_0x0c, + h->n_bone, + h->unknown_0x0c, + h->bone_offset, + h->min[0], h->min[1], h->min[2], + h->max[0], h->max[1], h->max[2] ); + + + //--- write bone table + +// [[2do]] +// if (brief_count) + fprintf(F.f, + "%s[BONE-TABLE]\r\n" + "@REVISION = %u\r\n" + "\r\n", + section_sep, + REVISION_NUM ); +// else +// fprintf(F.f,text_mdl_header_cr,REVISION_NUM); + + fprintf(F.f,"# N = %2u\r\n",mdl->bone_n); + int i; + for ( i = 0; i < mdl->bone_n; i++ ) + fprintf(F.f,"%8d\r\n",mdl->bone_tab[i]); + + + //--- write MDL sections + + const MemItem_t *eptr = GetMemListElem(&mdl->elem,0,0); + const MemItem_t *eend = eptr + mdl->elem.used; + for ( ; err < ERR_WARNING && eptr < eend; eptr++ ) + { + fprintf(F.f, + "%s[SECTION-%u]\r\n" + "# MDL%s.%02u", + section_sep, eptr->idx1, + GetSectNameMDL(eptr->idx1), eptr->idx2 + ); + + #if USE_NEW_CONTAINER_MDL + if (InContainerP(&mdl->container,eptr->data)) + { + u8* base = mdl->container.cdata->data; + // dont use "%zx" because of __APPLE__ + if ( mdl->mdl_offset ) + fprintf(F.f,", MDL file offset 0x%x, BRRES file offset 0x%x", + (int)( eptr->data - base - mdl->mdl_offset ), + (int)( eptr->data - base )); + else + fprintf(F.f,", MDL file offset 0x%x", + (int)( eptr->data - base )); + } + #else + if (InDataContainer(mdl->old_container,eptr->data)) + { + // dont use "%zx" because of __APPLE__ + if ( mdl->mdl_offset ) + fprintf(F.f,", MDL file offset 0x%x, BRRES file offset 0x%x", + (int)( eptr->data - mdl->old_container->data - mdl->mdl_offset ), + (int)( eptr->data - mdl->old_container->data )); + else + fprintf(F.f,", MDL file offset 0x%x", + (int)( eptr->data - mdl->old_container->data )); + } + #endif + + fprintf(F.f, + "\r\n" + "@REVISION = %5u\r\n" + "@ENTRY-INDEX = %5u\r\n" + "@SORT-INDEX = %5u\r\n" + "@NAME = \"%s\"\r\n" + "\r\n", + REVISION_NUM, + eptr->idx2, + eptr->sort_idx, + eptr->name ); + + switch(eptr->idx1) // [[newsect]] + { +// case 0: err = SaveTextMDLs0 (F.f,mdl,eptr); break; + case 1: err = SaveTextMDLs1 (F.f,mdl,eptr); break; + case 2: err = SaveTextMDLs2 (F.f,mdl,eptr); break; +// case 10: err = SaveTextMDLs10(F.f,mdl,eptr); break; + default: err = SaveTextMDLhex(F.f,mdl,eptr); break; + } + } + + + //--- terminate + + fputs(section_end,F.f); + ResetFile(&F,set_time); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CkeckMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int CheckMDL +( + // returns number of errors + + const mdl_t * mdl, // pointer to valid MDL + CheckMode_t mode // print mode +) +{ + // [[2do]] [[mdl]] ??? + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 0 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool InitializeMDLs0 ( mdl_sect0_t *ms, const void * data, uint data_size ) +{ + DASSERT(ms); + DASSERT(data); + + memset(ms,0,sizeof(*ms)); + ms->data_beg = data; + ms->data_end = ms->data_beg + data_size; + ms->ok = true; + + while (NextMDLs0(ms)) + { + ms->n++; + ms->data_size += ms->cur_size; + } + ClearMDLs0(ms); + + return ms->ok; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ClearMDLs0 ( mdl_sect0_t *ms ) +{ + DASSERT(ms); + ms->cur = ms->fail = 0; + ms->cur_size = 0; + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CheckMDLs0 ( mdl_sect0_t *ms ) +{ + DASSERT(ms); + ms->fail = 0; + ms->sub_size = 0; + if (!ms->cur) + return 0; + + if ( ms->cur < ms->data_beg || ms->cur >= ms->data_end ) + { + ms->fail = ms->cur; + ms->cur = 0; + ms->cur_size = 0; + return ms->ok = false; + } + + switch (*ms->cur) + { + case 1: // END (code) + ms->cur_size = 1; + break; + + case 2: // bone mapping (code,u16,u16) + case 5: // indexing (code,u16,u16) + ms->cur_size = 5; + break; + + case 3: // weighting (code,u16,N=u8,6*N) + ms->sub_size = 6; + ms->sub_ptr = ms->cur + 4; + ms->sub_n = ms->cur[3]; + ms->cur_size = 4 + 6 * ms->sub_n; + break; + + case 4: // draw polygon (code,u16,u16,u16,u8) + ms->cur_size = 8; + break; + + default: + ms->cur_size = 0; // == FAIL + break; + } + + if ( !ms->cur_size || ms->cur + ms->cur_size > ms->data_end ) + { + ms->fail = ms->cur; + ms->cur_size = ms->data_end - ms->cur; + ms->cur = 0; + return ms->ok = false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FirstMDLs0 ( mdl_sect0_t *ms ) +{ + DASSERT(ms); + ms->cur = ms->data_beg; + return CheckMDLs0(ms); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NextMDLs0 ( mdl_sect0_t *ms ) +{ + if (!ms->cur) + return FirstMDLs0(ms); + + if ( *ms->cur == 1 ) + return ClearMDLs0(ms); + + ms->cur += ms->cur_size; + return CheckMDLs0(ms); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp MDLs0_types[] = +{ + 0, + "u1", // 1 + "u1u2u2", // 2 + "u1u2u1|u2f4", // 3 + "u1u2u2u2x1", // 4 + "u1u2u2" // 5 +}; + +//----------------------------------------------------------------------------- + +ccp GetTypeMDLs0 ( uint index ) +{ + return index < sizeof(MDLs0_types)/sizeof(*MDLs0_types) + ? MDLs0_types[index] + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 1 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetMDLs1 ( mdl_sect1_t *ms, bool network_order ) +{ + DASSERT(ms); + memset(ms,0,sizeof(*ms)); + + ms->length = sizeof(*ms); + ms->flags = MINIMAP_DEFAULT_FLAGS; + ms->scale.x = ms->scale.y = ms->scale.z = 1.0; + + if (network_order) + hton_MDLs1(ms,ms); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearMDLs1 ( mdl_sect1_t *ms, bool network_order ) +{ + DASSERT(ms); + + if (network_order) + ntoh_MDLs1(ms,ms); + + memset(&ms->scale,0,sizeof(float3)*5); + memset(ms->trans_matrix.v,0,sizeof(ms->trans_matrix)); + memset(ms->inv_matrix.v,0,sizeof(ms->inv_matrix)); + ms->scale.x = ms->scale.y = ms->scale.z = 1.0; + + ms->length = sizeof(*ms); + ms->flags = MINIMAP_DEFAULT_FLAGS; + + if (network_order) + hton_MDLs1(ms,ms); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_MDLs1 ( mdl_sect1_t *dest, const mdl_sect1_t *src ) +{ + DASSERT(dest); + + if (src) + { + // float and u32 are equal for endian swapping! + be32n( (u32*)dest, (u32*)src, sizeof(*dest)/sizeof(u32) ); + } + else + memset(dest,0,sizeof(*dest)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void hton_MDLs1 ( mdl_sect1_t *dest, const mdl_sect1_t *src ) +{ + DASSERT(dest); + DASSERT(src); + + #if 1 + // float and u32 are equal for endian swapping! + write_be32n( (u32*)dest, (u32*)src, sizeof(*dest)/sizeof(u32) ); + #else + write_be32n( &dest->length, &src->length, 6 ); + write_bef4n( &dest->scale, &src->scal, 5*3 ); + write_be32n( &dest->parent_off, &src->parent_off, 5 ); + write_bef4n( dest->trans_matrix, src->trans_matrix, 2*4*3 ); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcMatrixMDLs1 ( mdl_sect1_t *ms ) +{ + DASSERT(ms); + + float3 rotate_rad; + Mult3f(rotate_rad,ms->rotate,M_PI/180.0); + + SetupMatrixF34( &ms->trans_matrix, + &ms->scale, + &rotate_rad, + &ms->translate ); + + CalcInverseMatrixF34( &ms->inv_matrix, + &ms->trans_matrix ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ImportMatrixMDLs1 // [[matrix]] [[test]] new function +( + mdl_sect1_t *ms, // valid and initialized data + bool network_order, // true: data is in network byte order + MatrixD_t *mat, // valid data, import from + uint import_mode // bit field: + // 1: import scale, rotate and translate + // 2: import transformation matrix + // 4: import inverse matrix +) +{ + DASSERT(ms); + DASSERT(mat); + + if (network_order) + ntoh_MDLs1(ms,ms); + + + //--- copy vectors + + if ( import_mode & 1 ) + { + CalcNormMatrixD(mat); + + ms->scale.x = mat->norm_scale.x; + ms->scale.y = mat->norm_scale.y; + ms->scale.z = mat->norm_scale.z; + + ms->rotate.x = mat->norm_rotate_deg.x; + ms->rotate.y = mat->norm_rotate_deg.y; + ms->rotate.z = mat->norm_rotate_deg.z; + + ms->translate.x = mat->norm_translate.x; + ms->translate.y = mat->norm_translate.y; + ms->translate.z = mat->norm_translate.z; + } + + + //--- copy matrices + + if ( import_mode & 2 ) + { + CalcTransMatrixD(mat,true); + CopyD34toF34(&ms->trans_matrix,&mat->trans_matrix); + } + + if ( import_mode & 4 ) + { + CalcMatrixD(mat,true); + CopyD34toF34(&ms->inv_matrix,&mat->inv_matrix); + } + + + //--- terminate + + if (network_order) + hton_MDLs1(ms,ms); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ExportMatrixMDLs1 // [[matrix]] [[test]] new function +( + mdl_sect1_t *ms, // valid and initialized data + bool network_order, // true: data is in network byte order + MatrixD_t *mat, // valid data, export to here + uint export_mode // bit field: + // 1: export scale, rotate and translate + // 2: export trans matrix and declare it valid + // 4: export inverse matrix and declare it valid +) +{ + DASSERT(ms); + DASSERT(mat); + + if (network_order) + ntoh_MDLs1(ms,ms); + + + //--- copy vectors + + InitializeMatrixD(mat); + + if ( export_mode & 1 ) + { + mat->scale.x = ms->scale.x; + mat->scale.y = ms->scale.y; + mat->scale.z = ms->scale.z; + + mat->rotate_deg.x = ms->rotate.x; + mat->rotate_deg.y = ms->rotate.y; + mat->rotate_deg.z = ms->rotate.z; + + mat->translate.x = ms->translate.x; + mat->translate.y = ms->translate.y; + mat->translate.z = ms->translate.z; + } + + CalcNormMatrixD(mat); + + + //--- copy matrices + + if ( export_mode & 2 ) + { + CopyF34toD34(&mat->trans_matrix,&ms->trans_matrix); + mat->tmatrix_valid = true; + } + + if ( export_mode & 4 ) + { + CopyF34toD34(&mat->inv_matrix,&ms->inv_matrix); + mat->imatrix_valid = true; + } + + + //--- terminate + + if (network_order) + hton_MDLs1(ms,ms); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool HaveStandardVectorsMDLs1 ( mdl_sect1_t *ms, bool network_order ) +{ + DASSERT(ms); + + mdl_sect1_t ms_local; + if (network_order) + { + ntoh_MDLs1(&ms_local,ms); + ms = &ms_local; + } + + return fabs(ms->scale.x-1.0) < 1e-6 + && fabs(ms->scale.y-1.0) < 1e-6 + && fabs(ms->scale.z-1.0) < 1e-6 + && fabs(ms->rotate.y) < 1e-6 + && fabs(ms->rotate.y) < 1e-6 + && fabs(ms->rotate.z) < 1e-6 + && fabs(ms->translate.x) < 1e-6 + && fabs(ms->translate.y) < 1e-6 + && fabs(ms->translate.z) < 1e-6; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineMatrixMDLs1 +( + // modify the vectors and calculate the matrices + + mdl_sect1_t * ms1, // pointer to valid MDLs1 + bool network_order, // true: data is in network byte order + uint reset, // 0: do nothing + // 1: reset only scale+rot+trans vectors + // 2: reset all + double * scale, // not NULL: multiply 3D scale to record + double * rotate, // not NULL: add 3D rotation to record + double * translate // not NULL: add 3D translation to record +) +{ + DASSERT(ms1); + if ( reset > 1 ) + ResetMDLs1(ms1,false); + else + { + if (network_order) + ntoh_MDLs1(ms1,ms1); + if (reset) + ClearMDLs1(ms1,false); + } + + if (scale) + { + ms1->scale.x *= scale[0]; + ms1->scale.y *= scale[1]; + ms1->scale.z *= scale[2]; + } + + if (rotate) + { + ms1->rotate.x += rotate[0]; + ms1->rotate.y += rotate[1]; + ms1->rotate.z += rotate[2]; + } + + if (translate) + { + ms1->translate.x += translate[0]; + ms1->translate.y += translate[1]; + ms1->translate.z += translate[2]; + } + + CalcMatrixMDLs1(ms1); + if (network_order) + hton_MDLs1(ms1,ms1); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintMatrixMDLs1 +( + FILE * f, // file to print to + uint indent, // indention + ccp eol, // not NULL: print as 'end of line' + const mdl_sect1_t * ms1, // pointer to valid MDLs1 + uint mode // bitfield: + // 1: print scale+rot+trans vectors + // 2: minimum + maximum + // 4: print matrix + // 8: print inverse matrix +) +{ + indent = NormalizeIndent(indent); + if (!eol) + eol = "\n"; + + if ( mode & 1 ) + { + fprintf(f, + "%*sscale = %11.3f %11.3f %11.3f%s" + "%*srotation = %11.3f %11.3f %11.3f%s" + "%*stranslation = %11.3f %11.3f %11.3f%s", + + indent,"", + ms1->scale.x, + ms1->scale.y, + ms1->scale.z, + eol, + + indent,"", + ms1->rotate.x, + ms1->rotate.y, + ms1->rotate.z, + eol, + + indent,"", + ms1->translate.x, + ms1->translate.y, + ms1->translate.z, + eol ); + } + + if ( mode & 2 ) + { + fprintf(f, + "%*sminimum = %11.3f %11.3f %11.3f%s" + "%*smaximum = %11.3f %11.3f %11.3f%s", + + indent,"", + ms1->minimum.x, + ms1->minimum.y, + ms1->minimum.z, + eol, + + indent,"", + ms1->maximum.x, + ms1->maximum.y, + ms1->maximum.z, + eol ); + } + + if ( mode & 3 ) + fputs(eol,f); + + if ( mode & 4 ) + { + fprintf(f, + "%*strans-matrix-x = %11.3f %11.3f %11.3f %11.3f%s" + "%*strans-matrix-y = %11.3f %11.3f %11.3f %11.3f%s" + "%*strans-matrix-z = %11.3f %11.3f %11.3f %11.3f%s" + "%s", + + indent,"", + ms1->trans_matrix.m[0][0], + ms1->trans_matrix.m[0][1], + ms1->trans_matrix.m[0][2], + ms1->trans_matrix.m[0][3], + eol, + + indent,"", + ms1->trans_matrix.m[1][0], + ms1->trans_matrix.m[1][1], + ms1->trans_matrix.m[1][2], + ms1->trans_matrix.m[1][3], + eol, + + indent,"", + ms1->trans_matrix.m[2][0], + ms1->trans_matrix.m[2][1], + ms1->trans_matrix.m[2][2], + ms1->trans_matrix.m[2][3], + eol, + eol ); + } + + if ( mode & 8 ) + { + fprintf(f, + "%*sinv-matrix-x = %11.3f %11.3f %11.3f %11.3f%s" + "%*sinv-matrix-y = %11.3f %11.3f %11.3f %11.3f%s" + "%*sinv-matrix-z = %11.3f %11.3f %11.3f %11.3f%s" + "%s", + + indent,"", + ms1->inv_matrix.m[0][0], + ms1->inv_matrix.m[0][1], + ms1->inv_matrix.m[0][2], + ms1->inv_matrix.m[0][3], + eol, + + indent,"", + ms1->inv_matrix.m[1][0], + ms1->inv_matrix.m[1][1], + ms1->inv_matrix.m[1][2], + ms1->inv_matrix.m[1][3], + eol, + + indent,"", + ms1->inv_matrix.m[2][0], + ms1->inv_matrix.m[2][1], + ms1->inv_matrix.m[2][2], + ms1->inv_matrix.m[2][3], + eol, + eol ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 2 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetMDLs2 ( mdl_sect2_t *ms, bool network_order ) +{ + DASSERT(ms); + memset(ms,0,sizeof(*ms)); + + ms->length = sizeof(*ms); + if (network_order) + hton_MDLs2(ms,ms); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_MDLs2 ( mdl_sect2_t *dest, const mdl_sect2_t *src ) +{ + DASSERT(dest); + DASSERT(src); + + be32n ( &dest->length, &src->length, 7 ); + memcpy( &dest->r_shift, &src->r_shift, 2 ); + be16n ( &dest->n_vertex, &src->n_vertex, 1 ); + bef4n ( dest->minimum.v, src->minimum.v, 6 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void hton_MDLs2 ( mdl_sect2_t *dest, const mdl_sect2_t *src ) +{ + DASSERT(dest); + DASSERT(src); + + write_be32n( &dest->length, &src->length, 7 ); + memcpy ( &dest->r_shift, &src->r_shift, 2 ); + write_be16n( &dest->n_vertex, &src->n_vertex, 1 ); + write_bef4n( dest->minimum.v, src->minimum.v, 6 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 10 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetMDLs10 ( mdl_sect10_t *ms, bool network_order ) +{ + DASSERT(ms); + memset(ms,0,sizeof(*ms)); + + ms->length = sizeof(*ms); + ms->cp_vtx = 0x4400; + ms->cp_tex = 2; + ms->flags = 0x80; + ms->xf_flags = 0x2a00; + ms->unknown_0x60 = M1(ms->unknown_0x60); + + if (network_order) + hton_MDLs10(ms,ms); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ntoh_MDLs10 ( mdl_sect10_t *dest, const mdl_sect10_t *src ) +{ + DASSERT(dest); + DASSERT(src); + + be32n( &dest->length, &src->length, 18 ); + be16n( &dest->v_group, &src->v_group, 12 ); + be32n( &dest->unknown_0x60, &src->unknown_0x60, 2 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void hton_MDLs10 ( mdl_sect10_t *dest, const mdl_sect10_t *src ) +{ + DASSERT(dest); + DASSERT(src); + + write_be32n( &dest->length, &src->length, 18 ); + write_be16n( &dest->v_group, &src->v_group, 12 ); + write_be32n( &dest->unknown_0x60, &src->unknown_0x60, 2 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL minimap /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define MINIMAP_MAX_VISIBLE 49e3 + +/////////////////////////////////////////////////////////////////////////////// + +static int find_minimap_mdl +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + if ( term || it->entry < 0 ) + return 0; + + if ( it->group == 1 ) + { + noPRINT("MDL-s1: %d.%d '%s'\n",it->group,it->entry,it->name); + mdl_sect1_t *ms = (mdl_sect1_t*)( it->szs->data + it->off ); + mdl_minimap_t *mmap = it->param; + + if (!mmap->root) + { + noPRINT("root FOUND! %p %u %u\n",it->param,it->group,it->entry); + mmap->root = ms; + } + + if ( it->name ) + { + if ( be32(&ms->length) == sizeof(*ms) ) + { + DASSERT(mmap); + if (!strcmp(it->name,"posLD")) + { + noPRINT("posLD FOUND! %p %u %u\n",it->param,it->group,it->entry); + mmap->posLD = ms; + } + else if (!strcmp(it->name,"posRU")) + { + noPRINT("posRU FOUND! %p %u %u\n",it->param,it->group,it->entry); + mmap->posRU = ms; + } + } + } + } + else if ( it->group == 2 ) + { + mdl_sect2_t *ms = (mdl_sect2_t*)( it->szs->data + it->off ); + //HEXDUMP16(0,0,ms,sizeof(*ms)); + const void *data = (u8*)ms + be32(&ms->data_off); + const uint format = be32(&ms->format); + const uint r_shift = ms->r_shift; + uint n_vertex = be16(&ms->n_vertex); + noPRINT("N-VERTEX=%u, FORMAT=%u, R-SHIFT=%u\n",n_vertex,format,r_shift); + + mdl_minimap_t *mmap = it->param; + DASSERT(mmap); + if ( n_vertex && !mmap->vertex ) + mmap->vertex = ms; + + float3 min = mmap->min; + float3 max = mmap->max; + + while ( n_vertex-- > 0 ) + { + float3 temp; + data = GetVectorMDL(&temp,data,format,r_shift); + noPRINT("GET= %11.3f %11.3f %11.3f [%d,%d,%d->%d]\n", + temp.x, temp.y, temp.z, + IsNormalF(temp.x), IsNormalF(temp.y), IsNormalF(temp.z), + IsNormalF3(temp.v) ); + + if ( IsNormalF3(temp.v) + && fabsf(temp.x) < 1e7 + && fabsf(temp.y) < 1e7 + && fabsf(temp.z) < 1e7 + ) + { + if ( min.x > temp.x ) min.x = temp.x; + if ( min.y > temp.y ) min.y = temp.y; + if ( min.z > temp.z ) min.z = temp.z; + if ( max.x < temp.x ) max.x = temp.x; + if ( max.y < temp.y ) max.y = temp.y; + if ( max.z < temp.z ) max.z = temp.z; + } + } + + PRINT("MIN= %11.3f %11.3f %11.3f\n",min.x,min.y,min.z); + PRINT("MAX= %11.3f %11.3f %11.3f\n",max.x,max.y,max.z); + mmap->min = min; + mmap->max = max; + + const float x_add = ( max.x - min.x ) * 0.02 + 100; + const float z_add = ( max.z - min.z ) * 0.02 + 100; + + mmap->rec_trans_ld.x = min.x - x_add; + mmap->rec_trans_ld.y = 0.0; + mmap->rec_trans_ld.z = max.z + z_add; + + mmap->rec_trans_ru.x = max.x + x_add; + mmap->rec_trans_ru.y = 0.0; + mmap->rec_trans_ru.z = min.z - z_add; + + if ( max.y > MINIMAP_MAX_VISIBLE ) + mmap->rec_trans_ld.y = mmap->rec_trans_ru.y + = 1.34 * ( max.y - MINIMAP_MAX_VISIBLE ); + + mmap->rec_trans_valid + = IsNormalF(mmap->rec_trans_ld.x) && fabsf(mmap->rec_trans_ld.x) < 1e6 + && IsNormalF(mmap->rec_trans_ld.y) && fabsf(mmap->rec_trans_ld.y) < 1e6 + && IsNormalF(mmap->rec_trans_ld.z) && fabsf(mmap->rec_trans_ld.z) < 1e6 + && IsNormalF(mmap->rec_trans_ru.x) && fabsf(mmap->rec_trans_ru.x) < 1e6 + && IsNormalF(mmap->rec_trans_ru.z) && fabsf(mmap->rec_trans_ru.z) < 1e6; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int find_minimap_brres +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + u8 *data = it->szs->data + it->off; + + if ( !term + && ( it->fform == FF_MDL + || it->fform == FF_UNKNOWN && !memcmp(data,MDL_MAGIC,4) ) + && !strcmp(it->name,"map") + ) + { + PRINT("MDL FOUND: %x |%s|%s|\n",it->fform,it->path,it->name); + szs_file_t szs; +// [[fname+]] + InitializeSubSZS(&szs,it->szs,it->off,it->size,FF_MDL,it->path,false); + int stat = IterateFilesSZS ( &szs, find_minimap_mdl, it->param, + true, 0, 1, SORT_NONE ); + ResetSZS(&szs); + return stat; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int find_minimap_szs +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + u8 *data = it->szs->data + it->off; + + if ( !term + && ( it->fform == FF_BRRES + || it->fform == FF_UNKNOWN && !memcmp(data,BRRES_MAGIC,4) ) + && !strcmp(it->name,"map_model.brres") + ) + { + PRINT("MAP MODEL FOUND: %x |%s|%s|\n",it->fform,it->path,it->name); + szs_file_t szs; +// [[fname+]] + InitializeSubSZS(&szs,it->szs,it->off,it->size,FF_BRRES,it->path,false); + int stat = IterateFilesSZS ( &szs, find_minimap_brres, it->param, + true, 0, -1, SORT_NONE ); + ResetSZS(&szs); + return stat; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FindMinimapData +( + mdl_minimap_t *mmap, // minmap data to fill + struct szs_file_t *szs // valid szs data +) +{ + DASSERT(mmap); + DASSERT(szs); + noPRINT("FindMinimapData(mmap=%p)\n",mmap); + + memset(mmap,0,sizeof(*mmap)); + mmap->min.x = mmap->min.y = mmap->min.z = +INFINITY; + mmap->max.x = mmap->max.y = mmap->max.z = -INFINITY; + + if ( szs->fform_arch == FF_MDL ) + IterateFilesSZS ( szs, find_minimap_mdl, mmap, true, 0, 1, SORT_NONE ); + else if ( szs->fform_arch == FF_BRRES ) + IterateFilesSZS ( szs, find_minimap_brres, mmap, true, 0, -1, SORT_NONE ); + else if (IsArchiveFF(szs->fform_arch)) + IterateFilesSZS ( szs, find_minimap_szs, mmap, true, 0, -1, SORT_NONE ); + + PRINT_IF(mmap->posLD," posLD found, in=%d\n", + (u8*)mmap->posLD >= szs->data && (u8*)mmap->posLD < szs->data + szs->size ); + PRINT_IF(mmap->posRU," posLD found, in=%d\n", + (u8*)mmap->posRU >= szs->data && (u8*)mmap->posRU < szs->data + szs->size ); + + return mmap->posLD && mmap->posRU; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AutoAdjustMinimap +( + // returns: + // 0: nothing done + // 1: minimap changed + // 3: minimap changed & vertex list flattened + + struct szs_file_t *szs // valid SZS data +) +{ + DASSERT(szs); + uint ret_status = 0; + + mdl_minimap_t mmap; + if ( FindMinimapData(&mmap,szs) && mmap.rec_trans_valid ) + { + PRINT("AutoAdjustMinimap()\n"); + DASSERT(mmap.posLD); + DASSERT(mmap.posRU); + ret_status = 1; + + if ( mmap.vertex && ( mmap.min.y < 0.0 || mmap.max.y > MINIMAP_MAX_VISIBLE ) ) + { + PATCH_ACTION_LOG("Patch","MINIMAP","(vertex list flattened)\n"); + ret_status |= 2; + mmap.rec_trans_ld.y = mmap.rec_trans_ru.y = 0; + // vertices are flattened only, if vectors or matrices changed. + } + else + PATCH_ACTION_LOG("Patch","MINIMAP",0); + + mdl_sect1_t ld; + ntoh_MDLs1(&ld,mmap.posLD); + ClearMDLs1(&ld,false); + ld.translate = mmap.rec_trans_ld; + CalcMatrixMDLs1(&ld); + + mdl_sect1_t ru; + ntoh_MDLs1(&ru,mmap.posRU); + ClearMDLs1(&ru,false); + ru.translate = mmap.rec_trans_ru; + CalcMatrixMDLs1(&ru); + + hton_MDLs1(&ld,&ld); + hton_MDLs1(&ru,&ru); + if ( memcmp(mmap.posLD,&ld,sizeof(ld)) + || memcmp(mmap.posRU,&ru,sizeof(ru)) ) + { + memcpy(mmap.posLD,&ld,sizeof(ld)); + memcpy(mmap.posRU,&ru,sizeof(ru)); + + if (ret_status&2) + { + void *data = (u8*)mmap.vertex + be32(&mmap.vertex->data_off); + const uint format = be32(&mmap.vertex->format); + const uint r_shift = mmap.vertex->r_shift; + uint n_vertex = be16(&mmap.vertex->n_vertex); + noPRINT("N-VERTEX=%u, FORMAT=%u, R-SHIFT=%u\n",n_vertex,r_shift); + + while ( n_vertex-- > 0 ) + { + float3 temp; + GetVectorMDL(&temp,data,format,r_shift); + temp.y = 20000.0; + data = SetVectorMDL(&temp,data,format,r_shift); + } + } + + return ret_status; + } + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL moonview data /////////////// +/////////////////////////////////////////////////////////////////////////////// +// CMD: ./work/scripts/etc/bin-to-bz_c.sh ext.tmp/*.bin +/////////////////////////////////////////////////////////////////////////////// + +const u8 S42_MDL_BZ2[] = +{ + 0x00,0x00,0x4b,0x00, // 19200 bytes decompressed + // 0x0: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x0d,0x14, 0x24,0x06,0x00,0x0c, + 0x3b,0xff,0xff,0xff, 0xff,0xff,0xf7,0xcf, 0x7e,0x7f,0xcd,0xf5, 0xff,0x76,0xc7,0xbf, + 0xef,0xde,0xf4,0x4c, 0x54,0x66,0x48,0x44, 0x40,0x46,0x44,0xd2, 0xc1,0x05,0x50,0xff, + 0xce,0x58,0x49,0xd0, 0x04,0x97,0x80,0x34, 0x00,0x1a,0x29,0xaa, 0x0c,0xa2,0x50,0x06, + 0x98,0x80,0x3d,0x40, 0x00,0x00,0x01,0xa0, 0x01,0xa0,0x00,0x00, 0xc8,0x00,0x01,0xa0, + 0x0f,0x50,0x34,0x00, 0x00,0xd0,0x68,0x03, 0xd4,0x1a,0x50,0x53, 0xd4,0xcf,0x4a,0x66, + 0xa7,0xa4,0x1e,0x14, 0x00,0x00,0xd0,0x00, 0x68,0x68,0x00,0xd0, 0x00,0x00,0x00,0x00, + 0x03,0x40,0x00,0x00, 0x0d,0x00,0x00,0x81, 0x93,0x04,0x64,0xc0, 0x8d,0x31,0x0d,0x30, + 0x8c,0x43,0x4c,0x00, 0x46,0x04,0x60,0x20, 0xc9,0xa6,0x43,0x46, 0x43,0x40,0x62,0x60, + 0x26,0x21,0x80,0x98, 0x8d,0x34,0xc4,0xc0, 0x98,0x10,0x32,0x60, 0x8c,0x98,0x11,0xa6, + 0x21,0xa6,0x11,0x88, 0x69,0x80,0x08,0xc0, 0x8c,0x04,0x19,0x34, 0xc8,0x68,0xc8,0x68, + 0x0c,0x4c,0x04,0xc4, 0x30,0x13,0x11,0xa6, 0x98,0x98,0x13,0x00, 0x89,0x48,0x49,0xa6, + 0x26,0x86,0x8a,0x66, 0xa6,0x98,0xd4,0x69, 0x80,0x46,0x83,0x4f, 0x50,0x18,0x8c,0x8d, + 0x07,0xa8,0xc4,0xc8, 0x68,0x69,0xe8,0x86, 0x8f,0x50,0x64,0xc8, 0xd3,0x26,0x99,0x19, + 0x1a,0x1a,0x64,0x66, 0xa1,0xea,0x64,0xc1, 0x32,0xb3,0x91,0x32, 0xeb,0xae,0xba,0xcb, + 0x26,0xa3,0x06,0x6e, 0x70,0x23,0x8e,0x0e, 0x45,0xc6,0x33,0xc0, 0x0a,0xf1,0xaf,0xf0, + // 0x100: + 0x04,0xe1,0x59,0xc9, 0xd9,0xe3,0x19,0x3c, 0x25,0xc0,0x05,0xc5, 0xd0,0x73,0xac,0xb9, + 0x72,0xd5,0xcc,0x70, 0xc5,0x85,0xf0,0x44, 0xc5,0x06,0x76,0x33, 0xb1,0x70,0x71,0x3e, + 0x2c,0x8d,0x10,0x76, 0x97,0xfa,0x77,0x41, 0xd9,0x83,0x9a,0x0f, 0x43,0x1c,0x1d,0x4e, + 0x15,0x14,0xca,0x70, 0x50,0x89,0x22,0x5b, 0x04,0x49,0x01,0xfc, 0x54,0xfc,0xed,0x54, + 0x00,0x0c,0x47,0x99, 0x83,0x99,0xd2,0xe4, 0x6a,0x37,0xfe,0x69, 0x6a,0xe7,0xe5,0xcf, + 0xbc,0x0e,0x2e,0x65, 0xff,0x44,0x18,0x0f, 0x70,0x1d,0x88,0x36, 0x94,0xe7,0xe4,0x83, + 0x89,0x86,0xa9,0x98, 0x45,0x78,0x78,0x68, 0x54,0x86,0xeb,0xac, 0x9d,0xe9,0x59,0xd1, + 0x34,0xfb,0xaa,0x95, 0x00,0xe2,0x03,0xf0, 0x50,0x13,0x61,0x1b, 0xca,0x67,0xd0,0x1d, + 0xb6,0x35,0x10,0x78, 0xb1,0x1d,0x66,0xfe, 0x80,0xf0,0x63,0xbd, 0x8a,0xa3,0xce,0x80, + 0xd3,0x7d,0x4f,0xe6, 0x00,0x03,0x78,0x1a, 0xe8,0x8e,0x74,0xcf, 0x80,0xde,0xe2,0xeb, + 0x33,0x2b,0x06,0xed, 0x94,0x92,0x11,0x18, 0x40,0x72,0xfd,0xc0, 0x7f,0xc0,0x74,0x41, + 0x0c,0xc0,0x1f,0xf4, 0x1b,0x40,0xe1,0x45, 0x24,0x8f,0x72,0x80, 0xfb,0x40,0xc0,0x00, + 0x0d,0xb0,0x30,0x04, 0xec,0xc4,0x77,0x71, 0x1c,0x90,0x70,0x81, 0xa7,0xe9,0x04,0x10, + 0xcd,0x94,0x88,0x28, 0xd0,0x06,0x40,0x18, 0x76,0xa2,0x35,0x44, 0x0f,0x9c,0x19,0x97, + 0x55,0x0d,0xed,0x28, 0x69,0xa9,0x4c,0xfa, 0x76,0x01,0xa6,0x4b, 0xc0,0x26,0xed,0x29, + 0x42,0xca,0x80,0x00, 0x2a,0x10,0x54,0x73, 0x4f,0x03,0x08,0xd2, 0xa9,0x9d,0x1b,0x5c, + // 0x200: + 0x38,0x86,0xf5,0x61, 0xbb,0xe6,0xa8,0x07, 0xba,0x24,0x03,0x04, 0x07,0xea,0x13,0x24, + 0x10,0xa0,0x21,0x01, 0x80,0xc0,0x60,0x21, 0xfb,0x83,0x14,0x47, 0x1a,0x09,0x96,0x08, + 0x90,0x44,0x4b,0x91, 0x12,0xfc,0x51,0xc8, 0x8a,0xe0,0x8a,0x38, 0x20,0x09,0x91,0x11, + 0xe1,0x40,0xc8,0x88, 0x0b,0x4c,0x8a,0x20, 0x28,0x78,0x90,0x57, 0x55,0x06,0x31,0x91, + 0x5c,0x80,0x60,0x85, 0x22,0x2a,0xc8,0x23, 0x08,0x20,0xa4,0x82, 0x26,0x14,0x01,0xee, + 0x83,0xa5,0x52,0x98, 0xd8,0x94,0x51,0xd2, 0x83,0x8a,0xa5,0x24, 0xce,0x06,0x29,0x40, + 0x18,0x59,0x6d,0x0a, 0x49,0x08,0xb0,0x18, 0x8a,0x90,0x4b,0x54, 0x88,0x50,0x25,0x01, + 0x90,0x4c,0x68,0xe0, 0x06,0x08,0xdf,0x3b, 0x20,0x85,0x10,0x06, 0xc2,0x0a,0x5a,0x80, + 0xfb,0x20,0xf2,0x39, 0x55,0x00,0x5f,0x06, 0x22,0xf4,0xd4,0x80, 0xea,0x81,0x98,0x84, + 0x06,0x61,0xb4,0x51, 0x39,0x70,0x5b,0xc4, 0x4e,0xa5,0x77,0x9a, 0x91,0x06,0xd4,0x51, + 0xaa,0xaa,0x32,0xaa, 0x15,0x44,0x00,0x66, 0x9a,0xd5,0x40,0xd7, 0x30,0xb9,0x20,0xd0, + 0x1d,0x40,0x37,0x81, 0xa0,0x20,0x62,0x5b, 0xb7,0x5d,0x68,0xb8, 0x01,0xe1,0x50,0xae, + 0xcd,0x86,0xd7,0x81, 0x60,0x77,0x49,0xcc, 0xbc,0x0d,0xb2,0xa3, 0x6f,0x38,0x20,0xc8, + 0x0d,0x59,0x15,0x1c, 0x9c,0x6d,0x00,0x42, 0xa0,0xb2,0xca,0x6c, 0x83,0xdb,0xdc,0xd0, + 0xdc,0x71,0x3f,0x2b, 0xd5,0xe6,0x95,0xd9, 0x64,0x92,0x56,0x5b, 0x46,0x12,0x17,0x29, + 0x42,0x28,0xde,0xaa, 0xed,0x5b,0x98,0xd5, 0xa3,0x5e,0x40,0x9c, 0x70,0x01,0xb0,0x00, + // 0x300: + 0x60,0x00,0xe5,0x80, 0x0d,0x01,0xb4,0x08, 0x9e,0x28,0x38,0xa0, 0xed,0xc1,0xb7,0x65, + 0x01,0xa8,0x06,0x86, 0x7d,0xee,0x05,0xbc, 0x2d,0x97,0x07,0x2f, 0xa1,0xdb,0xeb,0xdd, + 0x0d,0x7e,0x93,0xca, 0xab,0x93,0x10,0xd2, 0x57,0x6e,0xad,0xfd, 0xf8,0x3c,0xa5,0x97, + 0x66,0x89,0x84,0x1c, 0x01,0x92,0xd7,0xac, 0x34,0x3c,0x1c,0x90, 0x04,0xca,0x64,0x08, + 0xf0,0x09,0x48,0xf4, 0x91,0x00,0xba,0x0f, 0x07,0xc3,0x53,0xee, 0xb7,0xa3,0x66,0xb4, + 0x32,0xee,0x62,0x50, 0xa4,0x74,0x9c,0x7a, 0x6b,0xba,0x72,0xaa, 0x83,0x43,0xad,0x67, + 0x2c,0x1b,0xa0,0xf5, 0x20,0xed,0x08,0x1b, 0x80,0x73,0xc1,0xb6, 0x74,0xb9,0x53,0xaa, + 0xa6,0x93,0xf8,0x2f, 0xeb,0x81,0xb8,0x0f, 0x7e,0x0d,0x01,0xc0, 0x0f,0x00,0x06,0xb0, + 0x60,0x30,0x10,0x0d, 0x78,0x9c,0x79,0xce, 0x07,0x2b,0x29,0xd0, 0xbf,0x28,0x66,0xec, + 0x7d,0x8a,0x80,0xca, 0xbb,0x1e,0xb6,0x3b, 0x87,0xa9,0xd0,0x6f, 0x6a,0xb4,0x36,0xd7, + 0x00,0x4c,0xa5,0x80, 0x64,0x23,0x69,0xdb, 0x11,0xf0,0xf0,0x60, 0xe6,0xe1,0x50,0x10, + 0x40,0xb9,0xe6,0x9c, 0xc1,0x07,0xca,0x10, 0x1e,0x2f,0x91,0x24, 0x5b,0x05,0xbc,0xac, + 0x98,0x28,0x04,0xc9, 0xa6,0xf2,0xfe,0xad, 0x81,0x8d,0x4c,0x7e, 0xae,0x56,0xdc,0xc7, + 0x30,0xf2,0xb7,0xcf, 0x7d,0x93,0x14,0xa6, 0xe2,0x9f,0xb7,0x08, 0x11,0x24,0x05,0x10, + 0xfa,0x01,0xc7,0x52, 0xd5,0x40,0x03,0xcd, 0x07,0x3c,0x1f,0x1c, 0x1e,0x30,0x3c,0x60, + 0x6f,0x03,0xdb,0x07, 0x86,0x62,0x75,0xfb, 0x1d,0x1f,0xfb,0xb3, 0xda,0x63,0xb9,0xa4, + // 0x400: + 0xba,0xca,0x00,0x63, 0xf0,0x9d,0xfd,0xb2, 0x55,0x0e,0xf5,0x93, 0x3d,0xb7,0xf9,0xf4, + 0x01,0x97,0xc8,0xaf, 0x6d,0xc0,0xfe,0xd7, 0x3d,0xc3,0x48,0x37, 0xa5,0xd2,0x01,0x02, + 0x43,0xfb,0xae,0xd1, 0x85,0x79,0xe8,0x7d, 0x7d,0xca,0xb5,0x9c, 0xdd,0x9e,0xb7,0x26, + 0xce,0xec,0x3e,0x6d, 0x3e,0x2e,0x5e,0xa6, 0xca,0xf7,0x7b,0xce, 0xde,0xaf,0xf5,0xf3, + 0xc1,0xc2,0x80,0x86, 0xd6,0x02,0x14,0x02, 0x80,0x00,0x05,0x08, 0x00,0x92,0xc1,0x92, + 0xce,0x97,0xdd,0x6c, 0xbc,0xee,0xbb,0xa7, 0x1e,0x0c,0x78,0xec, 0x12,0xcc,0x02,0xe5, + 0x2a,0x80,0x54,0x00, 0xa6,0xe0,0x07,0x56, 0xb5,0xeb,0xe8,0xd0, 0x1a,0xd0,0x60,0x30, + 0x00,0x6e,0x90,0x0a, 0x03,0x10,0x2e,0xc0, 0x6c,0x05,0x78,0xc0, 0xc0,0x1a,0x56,0xa6, + 0xca,0x80,0x3a,0xbf, 0x4c,0x1b,0x01,0xff, 0xc5,0xdc,0x91,0x4e, 0x14,0x24,0x03,0x45, + 0x09,0x01,0x80, + // 0x493 = 1171 Bytes +}; + +/////////////////////////////////////////////////////////////////////////////// + +mdl_t slot42_mdl = {0}; + +#undef def42 +#define def42(n,i) { S42M_##n, i, 0 }, + +Slot42MaterialInfo_t Slot42MaterialInfo[S42M__N+1] = +{ + def42( GOAL_MERG, "Goal_Merg" ) + def42( IWA, "Iwa" ) + def42( IWA_ALFA, "Iwa_alfa" ) + def42( NUKI_RYOUMEN, "Nuki_Ryoumen" ) + def42( WALLMERG00, "WallMerg00" ) + def42( MOON_KABE0000, "moon_kabe0000" ) + def42( MOON_ROAD00, "moon_road00" ) + def42( ROAD, "road" ) + def42( ROAD01, "road01" ) + def42( ROAD02, "road02" ) + def42( ROAD03, "road03" ) + def42( SIBA00, "siba00" ) + {0,"",0} +}; + +#undef def42 + +/////////////////////////////////////////////////////////////////////////////// + +const Slot42MaterialInfo_t * SetupSlot42Materials() +{ + if ( slot42_mdl.fform != FF_MDL ) + { + InitializeMDL(&slot42_mdl); + slot42_mdl.fname = STRDUP("course.mdl"); + slot42_mdl.fform = FF_MDL; + + u8 *data; + uint size; + if (!DecodeBZIP2(&data,&size,0,S42_MDL_BZ2,sizeof(S42_MDL_BZ2))) + { + #if USE_NEW_CONTAINER_MDL + Container_t *c = CreateContainer(0,0,data,size,CPM_MOVE); + ScanRawMDL(&slot42_mdl,false,data,size,LinkContainerData(c)); + #else + DataContainer_t *dc = NewContainer(0,data,size,CPM_MOVE); + ScanRawMDL(&slot42_mdl,false,data,size,dc); + #endif + + MemItem_t *ptr = GetMemListElem(&slot42_mdl.elem,0,0); + MemItem_t *end = ptr + slot42_mdl.elem.used; + for ( ; ptr < end; ptr++ ) + { + Slot42MaterialInfo_t *sptr; + for ( sptr = Slot42MaterialInfo; sptr->val; sptr++ ) + if (!strcmp(sptr->name,ptr->name)) + { + sptr->memitem = ptr; + break; + } + } + } + //HEXDUMP16(0,0,Slot42MaterialInfo,sizeof(Slot42MaterialInfo)); + } + return Slot42MaterialInfo; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL moonview detection /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int ana_slot42_mdl +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + if ( term || it->entry < 0 ) + return 0; + + Slot42MaterialStat_t *stat42 = it->param; + DASSERT(stat42); + + char buf[2000]; + + if ( it->group == 8 && it->name ) + { + const Slot42MaterialInfo_t *ptr; + for ( ptr = SetupSlot42Materials(); ptr->val; ptr++ ) + if (!strcasecmp(ptr->name,it->name)) + { + stat42->found |= ptr->val; + const MemItem_t *mi = ptr->memitem; + if (mi) + { + DASSERT( mi->size <= sizeof(buf) ); + if ( mi->size != it->size || mi->size > sizeof(buf) ) + stat42->modified |= ptr->val; + else + { + // fix name offsets before comparing + + memcpy(buf,mi->data,mi->size); + + u8 *sdata = it->szs->data + it->off; + memcpy(buf+0x04,sdata+0x04,8); // offset + name + memcpy(buf+0x18,sdata+0x18,4); // culling + + const endian_func_t * endian = it->endian; + DASSERT(endian); + + const uint n_layer = endian->rd32(buf+0x2c); + u32 layer_off = endian->rd32(buf+0x30); + uint i; + for ( i = 0; i < n_layer; i++, layer_off += 0x34 ) + memcpy(buf+layer_off,sdata+layer_off,4); // name + HEXDIFF16(0,0,sdata,mi->size,buf,mi->size); + + if ( memcmp(buf,sdata,mi->size) ) + stat42->modified |= ptr->val; + } + } + + stat42->all_found = stat42->found == S42M__ALL; + stat42->content_ok = !stat42->modified; + stat42->ok = stat42->all_found && stat42->content_ok; + + //-- extract + + File_t F; + if (PrepareExtract(&F,-1,"%s.bin",ptr->name)) + { + printf("EXTRACT: %s\n",F.fname); + fwrite(it->szs->data+it->off,it->size,1,F.f); + } + CloseFile(&F,false); + break; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int ana_slot42_brres +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + u8 *data = it->szs->data + it->off; + + if ( !term + && ( it->fform == FF_MDL + || it->fform == FF_UNKNOWN && !memcmp(data,MDL_MAGIC,4) ) + && !strcasecmp(it->name,"course") + ) + { + PRINT("MOONVIEW MDL FOUND: %x |%s|%s|\n",it->fform,it->path,it->name); + szs_file_t szs; +// [[fname+]] + InitializeSubSZS(&szs,it->szs,it->off,it->size,FF_MDL,it->path,false); + int stat = IterateFilesSZS ( &szs, ana_slot42_mdl, it->param, + true, 0, 1, SORT_NONE ); + + Slot42MaterialStat_t *stat42 = it->param; + DASSERT(stat42); + if (stat42->ok) + { + File_t F; + if (PrepareExtract(&F,-1,"course-42.mdl")) + { + printf("EXTRACT: %s\n",F.fname); + + mdl_t mdl; + #if USE_NEW_CONTAINER_MDL + if (!ScanRawMDL(&mdl,true,szs.data,szs.size,LinkContainerSZS(&szs))) + #else + if (!ScanRawMDL(&mdl,true,szs.data,szs.size,ContainerSZS(&szs))) + #endif + { + mdl.bone_n = 0; + MemItem_t *ptr = GetMemListElem(&mdl.elem,0,0); + MemItem_t *end = ptr + mdl.elem.used; + for ( ; ptr < end; ptr++ ) + { + bool delete = true; + if ( ptr->idx1 == 8 ) + { + const Slot42MaterialInfo_t *sptr; + for ( sptr = Slot42MaterialInfo; sptr->val; sptr++ ) + if (!strcmp(sptr->name,ptr->name)) + { + delete = false; + break; + } + } + if (delete) + { + if (ptr->data_alloced) + FREE(ptr->data); + ptr->data = 0; + ptr->size = 0; + } + } + + CreateRawMDL(&mdl); + fwrite(mdl.raw_data,mdl.raw_file_size,1,F.f); + } + } + CloseFile(&F,false); + } + + ResetSZS(&szs); + return stat; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int ana_slot42_szs +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + u8 *data = it->szs->data + it->off; + + if ( !term + && ( it->fform == FF_BRRES + || it->fform == FF_UNKNOWN && !memcmp(data,BRRES_MAGIC,4) ) + && ( !strcmp(it->name,"course_model.brres") + || !strcmp(it->name,"course_d_model.brres") ) + ) + { + PRINT("COURSE MODEL FOUND: %x |%s|%s|\n",it->fform,it->path,it->name); + szs_file_t szs; +// [[fname+]] + InitializeSubSZS(&szs,it->szs,it->off,it->size,FF_BRRES,it->path,false); + int stat = IterateFilesSZS ( &szs, ana_slot42_brres, it->param, + true, 0, -1, SORT_NONE ); + ResetSZS(&szs); + return stat; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +Slot42MaterialStat_t GetSlot42SupportSZS ( struct szs_file_t *szs ) +{ + DASSERT(szs); + + Slot42MaterialStat_t stat42; + memset(&stat42,0,sizeof(stat42)); + + if ( !szs->data ) + return stat42; + + PRINT("CHECK MOONVIEW [%zu] %s\n",szs->size,GetNameFF(szs->fform_arch,0)); + + if ( szs->fform_arch == FF_MDL ) + IterateFilesSZS ( szs, ana_slot42_mdl, &stat42, true, 0, 1, SORT_NONE ); + else if ( szs->fform_arch == FF_BRRES ) + IterateFilesSZS ( szs, ana_slot42_brres, &stat42, true, 0, -1, SORT_NONE ); + else if (IsArchiveFF(szs->fform_arch)) + IterateFilesSZS ( szs, ana_slot42_szs, &stat42, true, 0, -1, SORT_NONE ); + + PRINT("MOONVIEW [%zu] %s: found=%x, mod=%x -> all=%d, content=%d -> ok=%d\n", + szs->size, GetNameFF(szs->fform_arch,0), + stat42.found, stat42.modified, + stat42.all_found, stat42.content_ok, stat42.ok ); + + return stat42; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[obsolete]] 2016-06 +// RenameForSlot42MDL() Don't work! A modified track runs at slot 1.1, +// but not at 4.2. I assume, that the layers must be correct. + +ccp RenameForSlot42MDL +( + void *rawdata, // pointer to data, modified + uint size, // size of 'rawdata' + ContainerMDL_t * cdata // NULL or container-data +) +{ + mdl_t mdl; + enumError err = ScanMDL(&mdl,true,rawdata,size,cdata,0); + ccp errmsg = 0; + if (err) + { + errmsg = "Invalid MDL"; + goto abort; + } + + { + u8 slot42ok[S42M__N]; + memset(slot42ok,0,sizeof(slot42ok)); + uint n_ok = 0; + + const uint MAX_SECT8 = 1000; + const MemItem_t *sect8[MAX_SECT8]; + uint sect8_count = 0; + + const MemItem_t *mi = GetMemListElem(&mdl.elem,0,0); + const MemItem_t *mi_end = mi + mdl.elem.used; + for ( ; mi < mi_end && sect8_count < MAX_SECT8; mi++ ) + { + if ( mi->idx1 != 8 || !mi->data ) + continue; + + sect8[sect8_count] = mi; + + const Slot42MaterialInfo_t *sptr; + for ( sptr = Slot42MaterialInfo; sptr->val; sptr++ ) + if (!strcmp(sptr->name,mi->name)) + { + slot42ok[sptr-Slot42MaterialInfo] = 1; + sect8[sect8_count] = 0; + n_ok++; + break; + } + + sect8_count++; + } + PRINT("N(slot42)=%d, N(mat)=%d\n",S42M__N,sect8_count); + HEXDUMP16(5,0,slot42ok,sizeof(slot42ok)); + + if ( sect8_count < S42M__N ) + { + errmsg = "Too less materials"; + goto abort; + } + if ( n_ok == S42M__N ) + goto abort; // all done + + ccp rename[S42M__N]; + memset(rename,0,sizeof(rename)); + uint sect, n_rename = 0; + + for ( sect = 0; sect < S42M__N; sect++ ) + { + if (slot42ok[sect]) + continue; + const Slot42MaterialInfo_t *sptr = Slot42MaterialInfo + sect; + const uint mlen = strlen(sptr->name); + + int found = -1; + uint flen = 0x40000000, s8idx; + for ( s8idx = 0; s8idx < sect8_count; s8idx++ ) + { + const MemItem_t *mi = sect8[s8idx]; + if (!mi) + continue; + + const uint slen = strlen(mi->name); + if ( mlen <= slen && slen < flen ) + { + found = s8idx; + flen = slen; + } + } + if ( found < 0 ) + { + errmsg = "Material names to short"; + goto abort; + } + + + rename[sect] = sect8[found]->name; + sect8[found] = 0; + n_rename++; + } + + #ifdef TEST + //if ( logging > 1 && stdlog ) + { + fprintf(stdlog, + "\tRename %u MDL material%s:\n", + n_rename, n_rename == 1 ? "" : "s" ); + for ( sect = 0; sect < S42M__N; sect++ ) + if (rename[sect]) + { + ParamFieldItem_t *par = FindParamField(&mdl.spool.pf,rename[sect]); + ccp pdata = par ? par->data : 0; + fprintf(stdlog,"\t %-13s <- %-30s %p %s\n", + Slot42MaterialInfo[sect].name, rename[sect], + pdata, pdata ); + } + } + #endif + + #if USE_NEW_CONTAINER_MDL + DASSERT(mdl.container.cdata); + DASSERT(mdl.container.cdata->data); + const u8 *copy_start = mdl.container.cdata->data + size; + #else + DASSERT(mdl.old_container); + DASSERT(mdl.old_container->data); + const u8 *copy_start = mdl.old_container->data + size; + #endif + for ( sect = 0; sect < S42M__N; sect++ ) + if (rename[sect]) + { + ParamFieldItem_t *par = FindParamField(&mdl.spool.pf,rename[sect]); + if ( !par || !par->data ) + { + errmsg = "Internal Error"; + goto abort; + } + DASSERT(!strcmp(rename[sect],par->data)); + u8 *dest = par->data - 4; + if ( copy_start > dest ) + copy_start = dest; + const uint rlen = strlen(rename[sect]); + const uint mlen = strlen(Slot42MaterialInfo[sect].name); + DASSERT( mlen <= rlen ); + write_be32(par->data-4,mlen); + memset(par->data,0,rlen); + memcpy(par->data,Slot42MaterialInfo[sect].name,mlen); + } + + #if USE_NEW_CONTAINER_MDL + const int offset = copy_start - mdl.container.cdata->data; + if ( offset > 0 ) + memcpy( rawdata+offset, mdl.container.cdata->data+offset, size-offset ); + #else + const int offset = copy_start - mdl.old_container->data; + if ( offset > 0 ) + memcpy( rawdata+offset, mdl.old_container->data+offset, size-offset ); + #endif + } + + abort: + ResetMDL(&mdl); + return errmsg; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrintStringsMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_sections_mdl +( + struct StringIteratorMDL_t *sit, // string iterator data + MemItem_t *mi // memory item of section +) +{ + DASSERT(sit); + DASSERT(sit->f); + DASSERT(mi); + + fprintf( sit->f, "%*sSection %2d.%02d: %s\n", + sit->indent,"", mi->idx1, mi->idx2, mi->name ); + + const u32 *sptr = GetStrIdxListMIL(mi); + uint i, n = GetStrIdxCountMIL(mi); + for ( i = 0; i < n; i++ ) + { + const uint idx = sptr[i]; + if ( idx && idx < sit->sp->n2s_size ) + { + ccp str = sit->sp->n2s_list[idx]; + if (!str) + str = "-invalid-"; + fprintf( sit->f, "%*s %s\n", sit->indent, "", str ); + } + } +} + +//----------------------------------------------------------------------------- + +enumError PrintStringsMDL +( + FILE * f, // output file + int indent, // indention + mdl_t * mdl // valid MDL +) +{ + DASSERT(f); + DASSERT(mdl); + + StringIteratorMDL_t sim; + memset(&sim,0,sizeof(sim)); + sim.mdl = mdl; + sim.sp = &mdl->spool; + sim.sect_func = print_sections_mdl; + //sim.str_func = print_strings_mdl; + sim.f = f; + sim.indent = NormalizeIndent(indent)+2; + + fprintf(f,"%*sStrings of %s\n",sim.indent-2,"",mdl->fname); + IterateAllStringsMDL(&sim); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AppendMDL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError AppendMDL +( + mdl_t * mdl, // working MDL (valid) + const mdl_t * src_mdl // pointer to source MDL +) +{ + DASSERT(mdl); + DASSERT(src_mdl); + + //--- find max sort_index + + int total_max_sort = 0; + MemItem_t *base = GetMemListElem(&mdl->elem,0,0); + MemItem_t *ptr, *end = base + mdl->elem.used; + for ( ptr = base; ptr < end; ptr++ ) + if ( total_max_sort < ptr->sort_idx ) + total_max_sort = ptr->sort_idx; + + PRINT("TOTAL-MAX-SORT: %d\n",total_max_sort); + + StringIteratorMDL_t sim; + memset(&sim,0,sizeof(sim)); + sim.mdl = mdl; + sim.sp = &mdl->spool; + sim.str_func = string_copy; + + const MemItem_t *mi = GetMemListElem(&src_mdl->elem,0,0); + const MemItem_t *mi_end = mi + src_mdl->elem.used; + for ( ; mi < mi_end; mi++ ) + { + //--- start with index corrections + + int sort_idx = -1, found = 0; + for ( ptr = base; ptr < end; ptr++ ) + { + if ( ptr->idx1 == mi->idx1 && sort_idx < ptr->sort_idx ) + sort_idx = ptr->sort_idx; + if (!strcmp(ptr->name,mi->name)) + { + found++; + break; + } + } + if (found) + continue; + + //if ( sort_idx < 0 ) + sort_idx = total_max_sort; + for ( ptr = base; ptr < end; ptr++ ) + if ( ptr->sort_idx > sort_idx ) + ptr->sort_idx += 10; + total_max_sort += 10; + + + //--- insert element + + MemItem_t * item = AppendMIL(&mdl->elem); + item->idx1 = mi->idx1; + item->idx2 = mi->idx2; + item->sort_idx = sort_idx + 10; + item->name = STRDUP(mi->name); + item->data = MEMDUP(mi->data,mi->size); + item->size = mi->size; + item->name_alloced = item->data_alloced = true; + + IterateStringsMDL(&sim,item,1); + } + + return ERR_WARNING; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Patch MDL /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct mdl_patch_t +{ + patch_file_t brres_type; // type of related BRRES file + u32 patch_status; // mdl patch status + + bool root_s1_valid; // true: 'root_s1' is valid copy + bool is_transformed; // true: is transformed by vectors + mdl_sect1_t root_s1; // root section-1 element + MatrixD_t matrix; // calculated matrix, if 'is_transformed' + MatrixD_t *mat; // Matrix for transforming. Is a pointer to + // either 'matrix' or 'opt_transform' (=init) +} mdl_patch_t; + + +/////////////////////////////////////////////////////////////////////////////// + +float3 TransformByMatrix +( + float3 *val, + float m[3][4] +) +{ + DASSERT(val); + DASSERT(m); + + float3 res; + res.x = m[0][0] * val->x + m[0][1] * val->y + m[0][2] * val->z + m[0][3]; + res.y = m[1][0] * val->x + m[1][1] * val->y + m[1][2] * val->z + m[1][3]; + res.z = m[2][0] * val->x + m[2][1] * val->y + m[2][2] * val->z + m[2][3]; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool HavePatchMDL() +{ + LoadParametersMDL( verbose > 0 ? " - " : 0 ); + if ( have_patch_count <= 0 || have_mdl_patch_count <= 0 ) + return false; + + return MDL_MODE & MDLMD_MATRIX || transform_active; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int patch_mdl +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + mdl_patch_t *mdlpat = it->param; + DASSERT(mdlpat); + + if ( term || it->entry < 0 ) + return 0; + + if ( it->group == 1 ) + { + mdl_sect1_t *ms = (mdl_sect1_t*)( it->szs->data + it->off ); + if ( be32(&ms->length) == sizeof(*ms) ) + { + if ( !mdlpat->root_s1_valid && !ms->parent_off ) + { + mdlpat->root_s1_valid = true; + ntoh_MDLs1(&mdlpat->root_s1,ms); + mdlpat->is_transformed + = !( mdlpat->brres_type & PFILE_MINIMAP ) + && !HaveStandardVectorsMDLs1(&mdlpat->root_s1,false); + + if (mdlpat->is_transformed) + { + MatrixD_t temp; + ExportMatrixMDLs1( &mdlpat->root_s1, false, &temp, 1 ); + MultiplyMatrixD( &mdlpat->matrix, &temp, &opt_transform, 0 ); + MultiplyMatrixD( &mdlpat->matrix, 0, &temp, 2 ); + mdlpat->mat = &mdlpat->matrix; + mdlpat->patch_status |= 2; + } + } + + if ( mdl_switched_to_vertex == 2 ) + { + mdl_switched_to_vertex++; + ERROR0(ERR_WARNING, + "Because of the complex transformation matrix, it is" + " impossible to modify the scale and rotate vectors" + " of the MDL bones. So the VECTOR transformation is" + " disabled and the VERTEX transformation enabled" + " to support full transformation." ); + } + + if ( MDL_MODE & ( ms->parent_off ? MDLMD_CHILD_NODES : MDLMD_PARENT_NODE )) + { + PRINT(" - PATCH MDLs1 '%s'\n", it->name ); + mdl_sect1_t ms1; + ntoh_MDLs1(&ms1,ms); + + if ( MDL_MODE & MDLMD_VECTOR && opt_transform.norm_valid ) + { + if (opt_transform.scale_enabled) + { + ms1.scale.x *= opt_transform.norm_scale.x; + ms1.scale.y *= opt_transform.norm_scale.y; + ms1.scale.z *= opt_transform.norm_scale.z; + mdlpat->patch_status |= 1; + } + + if (opt_transform.rotate_enabled) + { + ms1.rotate.x += opt_transform.norm_rotate_deg.x; + ms1.rotate.y += opt_transform.norm_rotate_deg.y; + ms1.rotate.z += opt_transform.norm_rotate_deg.z; + mdlpat->patch_status |= 1; + } + + if (opt_transform.translate_enabled) + { + ms1.translate.x += opt_transform.norm_translate.x; + ms1.translate.y += opt_transform.norm_translate.y; + ms1.translate.z += opt_transform.norm_translate.z; + mdlpat->patch_status |= 1; + } + } + + if ( MDL_MODE & MDLMD_MATRIX ) + { + CalcMatrixMDLs1(&ms1); + mdlpat->patch_status |= 1; + } + + hton_MDLs1(ms,&ms1); + + #ifdef TEST0 + PrintMatrixMDLs1(stdout,0,0,&ms1,13); + #endif + } + } + } + else if ( it->group == 2 && MDL_MODE & MDLMD_VERTEX ) + { + mdl_sect2_t *ms = (mdl_sect2_t*)( it->szs->data + it->off ); + void *data = (u8*)ms + be32(&ms->data_off); + const uint format = be32(&ms->format); + const uint r_shift = ms->r_shift; + uint n_vertex = be16(&ms->n_vertex); + noPRINT("N-VERTEX=%u, FORMAT=%u, R-SHIFT=%u\n",n_vertex,r_shift); + + if ( n_vertex > 0 ) + { + // [[2do]] optimization?: load N, transform N, store N + + float3 min, max; + min.x = min.y = min.z = +INFINITY; + max.x = max.y = max.z = -INFINITY; + + DASSERT(mdlpat->mat); + while ( n_vertex-- > 0 ) + { + float3 temp; + GetVectorMDL(&temp,data,format,r_shift); + temp = TransformF3MatrixD(mdlpat->mat,&temp); + if (tform_script_enabled) + TformScriptCallF(3,&temp,1,0); + data = SetVectorMDL(&temp,data,format,r_shift); + + if (IsNormalF3(temp.v)) + { + if ( min.x > temp.x ) min.x = temp.x; + if ( min.y > temp.y ) min.y = temp.y; + if ( min.z > temp.z ) min.z = temp.z; + if ( max.x < temp.x ) max.x = temp.x; + if ( max.y < temp.y ) max.y = temp.y; + if ( max.z < temp.z ) max.z = temp.z; + } + } + write_bef4n(ms->minimum.v,min.v,3); + write_bef4n(ms->maximum.v,max.v,3); + mdlpat->patch_status |= 1; + } + } + #ifdef TEST0 + else if ( it->group == 8 ) + { + mdl_sect8_t *ms = (mdl_sect8_t*)( it->szs->data + it->off ); + const uint n_layer = ntohl(ms->n_layer); + PRINT("### MDL SECTION 8, brres-type=%x, size=0x%x, n-lay=%u\n", + it->szs->brres_type, be32(&ms->length), n_layer ); + + uint layer; + mdl_sect8_layer_t *msl = (mdl_sect8_layer_t*)( (u8*)ms + ntohl(ms->layer_off) ); + for ( layer = 0; layer < n_layer; layer++, msl++ ) + { + PRINT("\tLAYER #%u, off=%zx, min-filter=%x\n", + layer, (u8*)msl - (u8*)ms, ntohl(msl->min_filter) ); + } + } + #endif + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PatchRawDataMDL +( + // returns + // 0: MDL not patched + // 1: MDL patched + // 3: MDL patched & vector transformation recognized + + void * data_ptr, // data to scan + uint data_size, // size of 'data' + patch_file_t brres_type, // type of related BRRES file + ccp fname // file name for error messages +) +{ + if (!HavePatchMDL()) + return false; + + MDL_ACTION_LOG(false,"PatchRawDataMDL(): %s\n",GetMdlMode()); + PRINT("PatchRawDataMDL(,,%x,): %s\n",brres_type,GetMdlMode()); + + mdl_patch_t mdlpat; + memset(&mdlpat,0,sizeof(mdlpat)); + mdlpat.brres_type = brres_type; + mdlpat.mat = &opt_transform; + + szs_file_t szs; +// [[fname+]] + AssignSZS(&szs,true,data_ptr,data_size,false,FF_MDL,fname); + szs.fname = fname; + IterateFilesSZS(&szs,patch_mdl,&mdlpat,true,0,1,SORT_NONE); + szs.fname = 0; + ResetSZS(&szs); + + return mdlpat.patch_status & 1 ? mdlpat.patch_status : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-mdl.h b/project/src/lib-mdl.h new file mode 100644 index 0000000..35316d9 --- /dev/null +++ b/project/src/lib-mdl.h @@ -0,0 +1,824 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_MDL_H +#define SZS_LIB_MDL_H 1 + +#include "lib-std.h" +#include "lib-szs.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_section_id]] + +typedef enum mdl_section_id +{ + MDL_SECTION_0, + MDL_SECTION_1, + MDL_SECTION_2, + MDL_SECTION_3, + MDL_SECTION_4, + MDL_SECTION_5, + MDL_SECTION_6, + MDL_SECTION_7, + MDL_SECTION_8, + MDL_SECTION_9, + MDL_SECTION_10, + MDL_SECTION_11, + + MDL_SETUP, + MDL_BONE_TABLE, + MDL_IGNORE, +} +mdl_section_id; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mdl_head_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_head_t]] + +typedef struct mdl_head_t +{ + /* 0x00 */ u32 head_len; // Header Length (0x40) + /* 0x04 */ u32 offset; // File Header Offset + /* 0x08 */ u32 unknown_0x08; // Unknown + /* 0x0c */ u32 unknown_0x0c; // Unknown + /* 0x10 */ u32 n_vertex; // Vertex Count + /* 0x14 */ u32 n_face; // Face Count + /* 0x18 */ u32 unknown_0x18; // Unknown + /* 0x1c */ u32 n_bone; // Bone Count + /* 0x20 */ u32 unknown_0x20; // Unknown (0x01000000) + /* 0x24 */ u32 bone_offset; // Bone Table Offset + /* 0x28 */ float32 min[3]; // Minimum (float3) + /* 0x34 */ float32 max[3]; // Maximum (float3) + /* 0x40 */ // ---END--- + +} +__attribute__ ((packed)) mdl_head_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// mdl_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum mdl_mode_t +{ + //--- MDL patching objects + + MDLMD_PARENT_NODE = 0x0001, // patch MDL parent node + MDLMD_CHILD_NODES = 0x0002, // patch all MDL child nodes + MDLMD_M_NODES = MDLMD_PARENT_NODE + | MDLMD_CHILD_NODES, + + MDLMD_M_OBJECT = MDLMD_M_NODES, + + + //--- MDL patching options + + MDLMD_VECTOR = 0x0010, // patch the base vectors + MDLMD_MATRIX = 0x0020, // calc transf-matrix and it's inverse + MDLMD_VERTEX = 0x0040, // patch vertex list + + MDLMD_M_PATCH = MDLMD_VECTOR + | MDLMD_MATRIX + | MDLMD_VERTEX, + + + //--- hidden options + + MDLMD_LOG = 0x00100000, // enable mdl trace log to stdout + MDLMD_SILENT = 0x00200000, // enable mdl trace log to stdout + MDLMD_M_HIDDEN = MDLMD_LOG + | MDLMD_SILENT, + + //--- test modes + + MDLMD_TEST0 = 0x00000000, + MDLMD_TEST1 = 0x10000000, + MDLMD_TEST2 = 0x20000000, + MDLMD_TEST3 = 0x30000000, + MDLMD_M_TEST = MDLMD_TEST3, // all bits! + + + //--- summaries + + MDLMD_F_HIDE = 0x40000000, // help flag to hide modes in PrintMdlMode() + + MDLMD_M_DEFAULT = MDLMD_VERTEX, // default settings + + MDLMD_M_ALL = MDLMD_M_OBJECT // all relevant bits + | MDLMD_M_PATCH + | MDLMD_M_HIDDEN + | MDLMD_M_TEST, + + MDLMD_M_PRINT = MDLMD_M_ALL // all relevant bits for obj output + & ~MDLMD_M_HIDDEN, + +} mdl_mode_t; + +//----------------------------------------------------------------------------- + +extern mdl_mode_t MDL_MODE; +extern int have_mdl_patch_count; + +void LoadParametersMDL +( + ccp log_prefix // not NULL: + // print log message with prefix +); + +void SetMdlMode ( uint new_mode ); +int ScanOptMdl ( ccp arg ); +mdl_mode_t GetRelevantMdlMode ( mdl_mode_t mode ); +uint PrintMdlMode( char *buf, uint bufsize, mdl_mode_t mode ); +ccp GetMdlMode(); +void SetupMDL(); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mdl_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_t]] + +struct mdl_sect1_t; + +typedef struct mdl_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // FF_MDL | FF_MDL_TXT + + //--- data + + uint version; // the mdl version of the source + uint n_sect; // number of sections + ccp name; // name of mdl, alloced + mdl_head_t head; // copy of mdl header, host endian + + u32 *bone_tab; // data of bone table + uint bone_n; // number of elements in 'bone_tab' + + #if USE_NEW_CONTAINER_MDL + Container_t container; // data container + #else + DataContainer_t *old_container; // old data container support, init with NULL + #endif + u32 mdl_offset; // offset of MDL in 'old_container' + + //--- list of elements + string pool + + List_t elem; // list of all elements (MemItem_t) + string_pool_t spool; // strings for 'elem' + + //--- parser helpers + + int revision; // tool revision + bool is_pass2; // true: LEX compiler runs pass2 + + //--- raw data, created by CreateRawMDL() + + u8 * raw_data; // NULL or data + uint raw_data_size; // data size without string pool + uint raw_file_size; // size of 'raw_data' + +} mdl_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMDL ( mdl_t * mdl ); +void ResetMDL ( mdl_t * mdl ); +const VarMap_t * SetupVarsMDL(); + +//----------------------------------------------------------------------------- + +ccp GetStringMDL +( + mdl_t * mdl, // MDL data structure + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid // result if invalid +); + +//----------------------------------------------------------------------------- + +ccp GetStringExMDL +( + mdl_t * mdl, // MDL data structure + const void * base, // base for offset + u32 sect_offset, // offset of other section + u32 str_offset, // offset of string offset in other section + ccp if_invalid // result if invalid +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerMDL_t * cdata // NULL or container-data +); + +//----------------------------------------------------------------------------- + +enumError ScanTextMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerMDL_t * cdata, // NULL or container-data + CheckMode_t mode // not NULL: call CheckMDL(mode) +); + +//----------------------------------------------------------------------------- + +struct raw_data_t; + +enumError ScanRawDataMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckMDL(mode) +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadMDL +( + mdl_t * mdl, // MDL data structure + bool init_mdl, // true: initialize 'mdl' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckMDL(mode) +); + +//----------------------------------------------------------------------------- + +enumError SaveRawMDL +( + mdl_t * mdl, // pointer to valid MDL + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextMDL +( + const mdl_t * mdl, // pointer to valid MDL + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintStringsMDL +( + FILE * f, // output file + int indent, // indention + mdl_t * mdl // valid MDL +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError AppendMDL +( + mdl_t * mdl, // working MDL (valid) + const mdl_t * src_mdl // pointer to source MDL +); + +/////////////////////////////////////////////////////////////////////////////// + +bool HavePatchMDL(); + +uint PatchRawDataMDL +( + // returns + // 0: MDL not patched + // 1: MDL patched + // 3: MDL patched & vector transformation recognized + + void * data_ptr, // data to scan + uint data_size, // size of 'data' + patch_file_t brres_type, // type of related BRRES file + ccp fname // file name for error messages +); + +float3 TransformByMatrix +( + float3 *val, + float m[3][4] +); + +/////////////////////////////////////////////////////////////////////////////// + +int CheckMDL +( + // returns number of errors + + const mdl_t * mdl, // pointer to valid MDL + CheckMode_t mode // print mode +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enumError (*iterate_mdl_func) +( + mdl_t * mdl, // MDL data structure + void * param // a user defined parameter +); + +//----------------------------------------------------------------------------- + +enumError IterateRawDataMDL +( + struct raw_data_t * raw, // valid raw data + CheckMode_t check_mode, // not NULL: call CheckMDL(mode) + iterate_mdl_func func, // call this function for each found MDL + void * param // a user defined parameter +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 0 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_sect0_t]] + +typedef struct mdl_sect0_t +{ + const u8 *data_beg; // begin of data + const u8 *data_end; // end of data + uint data_size; // calculated data size + uint n; // number of elements including 'END' + bool ok; // data ok + + const u8 *cur; // NULL or pointer of current entry + uint cur_size; // size of current entry + + uint sub_size; // size of each subelement + uint sub_n; // if sub_size>0: number of sub elements. + const u8 *sub_ptr; // if sub_size>0: pointer to first sub element + + const u8 *fail; // NULL or pointer to failed entry + +} mdl_sect0_t; + +//----------------------------------------------------------------------------- + +bool InitializeMDLs0 ( mdl_sect0_t *ms, const void * data, uint data_size ); +bool ClearMDLs0 ( mdl_sect0_t *ms ); +bool CheckMDLs0 ( mdl_sect0_t *ms ); +bool FirstMDLs0 ( mdl_sect0_t *ms ); +bool NextMDLs0 ( mdl_sect0_t *ms ); + +ccp GetTypeMDLs0 ( uint index ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 1 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_sect1_t]] + +typedef struct mdl_sect1_t +{ + /* 0x00 */ u32 length; // length of struct, usually 0xd0 + /* 0x04 */ u32 mdl0_off; + /* 0x08 */ u32 name_off; + /* 0x0c */ u32 index; + /* 0x10 */ u32 id; + /* 0x14 */ u32 flags; // 0x31f typical + /* 0x18 */ u32 padding[2]; + + /* 0x20 */ float3 scale; // scaling + /* 0x2c */ float3 rotate; // euler rotation in degree + /* 0x38 */ float3 translate; // translate == shift + /* 0x44 */ float3 minimum; + /* 0x50 */ float3 maximum; + + /* 0x5c */ u32 parent_off; // parent offset + /* 0x60 */ u32 child_off; // first child offset + /* 0x64 */ u32 next_off; // next sibling offset + /* 0x68 */ u32 prev_off; // previous sibling offset + /* 0x6c */ u32 part2_off; // part 2 offset + + /* 0x70 */ float34 trans_matrix; // transformation matrix + /* 0xa0 */ float34 inv_matrix; // inverse transformation matrix + /* 0xd0 */ +} +__attribute__ ((packed)) mdl_sect1_t; + +//----------------------------------------------------------------------------- + +void ResetMDLs1 ( mdl_sect1_t *ms, bool network_order ); +void ClearMDLs1 ( mdl_sect1_t *ms, bool network_order ); +void ntoh_MDLs1 ( mdl_sect1_t *dest, const mdl_sect1_t *src ); +void hton_MDLs1 ( mdl_sect1_t *dest, const mdl_sect1_t *src ); +void CalcMatrixMDLs1 ( mdl_sect1_t *ms ); + +void ImportMatrixMDLs1 +( + mdl_sect1_t *ms, // valid and initialized data + bool network_order, // true: data is in network byte order + MatrixD_t *mat, // valid data, import from + uint import_mode // bit field: + // 1: import scale, rotate and translate + // 2: import transformation matrix + // 4: import inverse matrix +); + +void ExportMatrixMDLs1 +( + mdl_sect1_t *ms, // valid and initialized data + bool network_order, // true: data is in network byte order + MatrixD_t *mat, // valid data, export to here + uint export_mode // bit field: + // 1: export scale, rotate and translate + // 2: export trans matrix and declare it valid + // 4: export inverse matrix and declare it valid +); + +bool HaveStandardVectorsMDLs1 ( mdl_sect1_t *ms, bool network_order ); + +void DefineMatrixMDLs1 +( + // modify the vectors and calculate the matrices + + mdl_sect1_t * ms1, // pointer to valid MDLs1 + bool network_order, // true: data is in network byte order + uint reset, // 0: do nothing + // 1: reset only scale+rot+trans vectors + // 2: reset all + double * scale, // not NULL: multiply 3D scale to record + double * rotate, // not NULL: add 3D rotation to record + double * translate // not NULL: add 3D translation to record +); + +void PrintMatrixMDLs1 +( + FILE * f, // file to print to + uint indent, // indention + ccp eol, // not NULL: print as 'end of line' + const mdl_sect1_t * ms1, // pointer to valid MDLs1 + uint mode // bitfield: + // 1: print scale+rot+trans vectors + // 2: minimum + maximum + // 4: print matrix + // 8: print inverse matrix +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 2 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_sect2_t]] + +typedef struct mdl_sect2_t +{ + /* 0x00 */ u32 length; // length of section (includes vertex list) + /* 0x04 */ u32 mdl0_off; + /* 0x08 */ u32 data_off; + /* 0x0c */ u32 name_off; + /* 0x10 */ u32 index; + /* 0x14 */ u32 has_bounds; + /* 0x18 */ u32 format; + + /* 0x1c */ u8 r_shift; + /* 0x1d */ u8 stride; + /* 0x1e */ u16 n_vertex; + + /* 0x20 */ float3 minimum; + /* 0x2c */ float3 maximum; + + /* 0x38 */ +} +__attribute__ ((packed)) mdl_sect2_t; + +//----------------------------------------------------------------------------- + +void ResetMDLs2 ( mdl_sect2_t *ms, bool network_order ); +void ntoh_MDLs2 ( mdl_sect2_t *dest, const mdl_sect2_t *src ); +void hton_MDLs2 ( mdl_sect2_t *dest, const mdl_sect2_t *src ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 8 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_sect8_t]] +// see http://wiki.tockdom.com/wiki/MDL0_%28File_Format%29#Section_8_-_Materials + +typedef struct mdl_sect8_t +{ + /* 0x00 */ u32 length; // length of section (includes vertex list) + /* 0x04 */ u32 mdl0_off; + /* 0x08 */ u32 name_off; + /* 0x0c */ u32 index; + /* 0x10 */ u32 xlu_material; + /* 0x14 */ u8 tex_gens; + /* 0x15 */ u8 light_channels; + /* 0x16 */ u8 shader_stages; + /* 0x17 */ u8 indirect_textures; + /* 0x18 */ u32 cull_mode; + /* 0x1c */ u8 alpha_func; + /* 0x1d */ u8 lightset; + /* 0x1e */ u8 fogset; + /* 0x1f */ u8 unknown_1f; // always 0 + /* 0x20 */ u32 unknown_20; // always 0 + /* 0x24 */ u32 unknown_24; // always ~0 + /* 0x28 */ u32 shader_off; + /* 0x2c */ u32 n_layer; + /* 0x30 */ u32 layer_off; + /* 0x34 */ u32 part2_off; + /* 0x38 */ u32 disp_list_off_8_9; + /* 0x3c */ u32 disp_list_off_10_11; + /* 0x40 */ u32 unknown_40; // always 0 + + /* 0x44 */ u8 any_data_44[0x418-0x44]; + /* 0x418 */ +} +__attribute__ ((packed)) mdl_sect8_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_sect8_layer_t]] + +typedef struct mdl_sect8_layer_t +{ + /* 0x00 */ u32 name_off; + /* 0x04 */ u8 any_data_04[0x20-0x04]; + /* 0x20 */ u32 min_filter; + /* 0x24 */ u8 any_data_24[0x34-0x24]; + /* 0x34 */ +} +__attribute__ ((packed)) mdl_sect8_layer_t; + +/////////////////////////////////////////////////////////////////////////////// + +enum Slot42Material +{ + S42M_GOAL_MERG = 0x0001, + S42M_IWA = 0x0002, + S42M_IWA_ALFA = 0x0004, + S42M_NUKI_RYOUMEN = 0x0008, + S42M_WALLMERG00 = 0x0010, + S42M_MOON_KABE0000 = 0x0020, + S42M_MOON_ROAD00 = 0x0040, + S42M_ROAD = 0x0080, + S42M_ROAD01 = 0x0100, + S42M_ROAD02 = 0x0200, + S42M_ROAD03 = 0x0400, + S42M_SIBA00 = 0x0800, + + S42M__ALL = 0x0fff, + S42M__N = 12 +}; + +//----------------------------------------------------------------------------- +// [[Slot42MaterialInfo_t]] + +typedef struct Slot42MaterialInfo_t +{ + u16 val; // Slot42Material, bit field, 1 bit set + const char name[14]; // name of material + const MemItem_t *memitem; // pointer to related meminfo of 'slot42_mdl' +} +Slot42MaterialInfo_t; + +extern mdl_t slot42_mdl; +extern Slot42MaterialInfo_t Slot42MaterialInfo[S42M__N+1]; // {0,"",0} terminated +const Slot42MaterialInfo_t * SetupSlot42Materials(); + +//----------------------------------------------------------------------------- +// [[Slot42MaterialStat_t]] + +typedef struct Slot42MaterialStat_t +{ + enum Slot42Material found; // bit field: materials found + enum Slot42Material modified; // for 'found' content modified + + // summary + bool all_found; // true: all matrials found + bool content_ok; // true: no content modified + bool ok; // true: all ok >= all_found && content_ok + +} Slot42MaterialStat_t; + +Slot42MaterialStat_t GetSlot42SupportSZS ( struct szs_file_t *szs ); + +ccp RenameForSlot42MDL +( + void *rawdata, // pointer to data, modified + uint size, // size of data + ContainerMDL_t * cdata // NULL or container-data +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL section 10 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mdl_sect10_t]] + +typedef struct mdl_sect10_t +{ + /* 0x00 */ u32 length; // length of struct + /* 0x04 */ u32 mdl0_off; // mdl0 offset + /* 0x08 */ u32 bone_idx; // bone index (for single bone ref, + // -1 for table usage) + /* 0x0c */ u32 cp_vtx; // cp_vtx (0x4400 typical) + /* 0x10 */ u32 cp_tex; // cp_tex (0x2 typical) + /* 0x14 */ u32 xf_nor_spec; // xf_nor_spec (0x11 typical) + /* 0x18 */ u32 v_decl_size; // vertex declaration size (0xe0) + /* 0x1c */ u32 flags; // unknown flags (0x80 typical) + /* 0x20 */ u32 v_decl_offset; // vertex declaration offset (0x68 typical) + /* 0x24 */ u32 v_data_size1; // vertex data size + /* 0x28 */ u32 v_data_size2; // vertex data size + /* 0x2c */ u32 v_data_offset; // vertex data offset (0x13c typical) + /* 0x30 */ u32 xf_flags; // xf_flags (0x2a00 typical) + /* 0x34 */ u32 unknown_0x34; // unknown (0x0 typical) + /* 0x38 */ u32 name_off; // name offset + /* 0x3c */ u32 idx; // index + /* 0x40 */ u32 n_vertex; // vertex count + /* 0x44 */ u32 n_face; // face count + /* 0x48 */ u16 v_group; // vertex group index + /* 0x4a */ u16 n_group; // normal group index + /* 0x4c */ u16 col_group[2]; // 2 color group indicies + /* 0x50 */ u16 tex_group[8]; // 8 texture coordinate group indicies + /* 0x60 */ u32 unknown_0x60; // unknown (-1 typical) (not seen in version 9 mdl0's) + /* 0x64 */ u32 bone_offset; // bone table offset + /* 0x68 */ + +} mdl_sect10_t; + +//----------------------------------------------------------------------------- + +void ResetMDLs10 ( mdl_sect10_t *ms, bool network_order ); +void ntoh_MDLs10 ( mdl_sect10_t *dest, const mdl_sect10_t *src ); +void hton_MDLs10 ( mdl_sect10_t *dest, const mdl_sect10_t *src ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL minimap /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct mdl_minimap_t +{ + mdl_sect1_t *root; // root section + mdl_sect1_t *posLD; // section with name "posLD" + mdl_sect1_t *posRU; // section with name "posRU" + mdl_sect2_t *vertex; // vertex section + + float3 min; // minimum vertex of section 2 + float3 max; // maximum vertex of section 2 + + bool rec_trans_valid; // true: recomendation is valid + float3 rec_trans_ld; // recommended translation for posLD + float3 rec_trans_ru; // recommended translation for posRU + +} mdl_minimap_t; + +//----------------------------------------------------------------------------- + +bool FindMinimapData +( + mdl_minimap_t *mmap, // minmap data to fill + struct szs_file_t *szs // valid szs data +); + +//----------------------------------------------------------------------------- + +uint AutoAdjustMinimap +( + // returns: + // 0: nothing done + // 1: minimap changed + // 3: minimap changed & vertex list flattened + + struct szs_file_t *szs // valid SZS data +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MDL0 string iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct StringIteratorMDL_t; + +typedef void (*SectionFuncMDL) +( + struct StringIteratorMDL_t *sit, // string iterator data + MemItem_t *mi // memory item of section +); + +//----------------------------------------------------------------------------- + +typedef void (*OffsetFuncMDL) +( + struct StringIteratorMDL_t *sit, // string iterator data + u8 *base, // base of item + u32 *data // pointer to string reference +); + +//----------------------------------------------------------------------------- + +typedef void (*StringFuncMDL) +( + struct StringIteratorMDL_t *sit, // string iterator data + u32 *sptr, // pointer to string index + u8 *base, // base of item + u32 *data // pointer to string reference +); + +//----------------------------------------------------------------------------- + +typedef struct StringIteratorMDL_t +{ + const mdl_t *mdl; // pointer to related MDL + string_pool_t *sp; // string pool to manipulate + + FILE *f; // NULL or output file + int indent; // indention of output + + SectionFuncMDL sect_func; // not NULL: call it for each section + OffsetFuncMDL off_func; // not NULL: call it for each begin-mdl-offset + StringFuncMDL str_func; // not NULL: call it for each string +} +StringIteratorMDL_t; + +/////////////////////////////////////////////////////////////////////////////// + +void IterateStringsMDL +( + StringIteratorMDL_t *sit, // string iterator data + MemItem_t *mi, // pointer to memlist + int mi_count // number of elements of 'ml_ptr' +); + +//----------------------------------------------------------------------------- + +void IterateAllStringsMDL +( + StringIteratorMDL_t *sit // string iterator data +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_MDL_H diff --git a/project/src/lib-minigame.c b/project/src/lib-minigame.c new file mode 100755 index 0000000..b97e086 --- /dev/null +++ b/project/src/lib-minigame.c @@ -0,0 +1,923 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include + +#include "dclib-xdump.h" +#include "lib-szs.h" +#include "lib-common.h" +#include "lib-numeric.h" +#include "lib-bzip2.h" +#include "dclib-numeric.h" + +#include "minigame.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int opt_kmg_arena = -1; +int opt_kmg_limit = -1; + +//----------------------------------------------------------------------------- + +enum +{ + HEAD_PARAM = 0, + BALLOON_PARAM = 1, + COIN_PARAM = 2, +}; + +//----------------------------------------------------------------------------- + +static ccp minigame_param_title[] = +{ + "Parameters of file header (16 bit)", + "Parameters for balloon battle (16 bit, offsets 0x1c8..0x1da)", + "Parameters for coin runners (16 bit, offsets 0x704..0x716)", +}; + +//----------------------------------------------------------------------------- + +static const KeywordTab_t minigame_param_list[] = +{ + { 0x008, "OFFSET-008", 0, HEAD_PARAM }, + { 0x00a, "OFFSET-00A", 0, HEAD_PARAM }, + + { 0x1c8, "OFFSET-1C8", "POINTS-IF-HIT-OTHER", BALLOON_PARAM }, + { 0x1ca, "OFFSET-1CA", "POINTS-IF-DEFEAT", BALLOON_PARAM }, + { 0x1cc, "OFFSET-1CC", "POINTS-IF-BALLOONS-LOST", BALLOON_PARAM }, + // [[obsolete]], typo "BALLON" fixed in 2020-01 + { 0x1cc, "POINTS-IF-BALLONS-LOST", 0, BALLOON_PARAM }, + { 0x1ce, "OFFSET-1CE", "POINTS-IF-HIT", BALLOON_PARAM }, + { 0x1d0, "OFFSET-1D0", "POINTS-HITTING-TEAMMATE", BALLOON_PARAM }, + { 0x1d2, "OFFSET-1D2", "POINTS-IF-HIT-BY-TEAMMATE", BALLOON_PARAM }, + { 0x1d4, "OFFSET-1D4", 0, BALLOON_PARAM }, + { 0x1d6, "OFFSET-1D6", 0, BALLOON_PARAM }, + { 0x1d8, "OFFSET-1D8", 0, BALLOON_PARAM }, + { 0x1da, "OFFSET-1DA", 0, BALLOON_PARAM }, + + { 0x704, "OFFSET-704", 0, COIN_PARAM }, + { 0x706, "OFFSET-706", 0, COIN_PARAM }, + { 0x708, "OFFSET-708", 0, COIN_PARAM }, + { 0x70a, "OFFSET-70A", 0, COIN_PARAM }, + { 0x70c, "OFFSET-70C", 0, COIN_PARAM }, + { 0x70e, "OFFSET-70E", 0, COIN_PARAM }, + { 0x710, "OFFSET-710", 0, COIN_PARAM }, + { 0x712, "OFFSET-712", "MIN-LOST-FALLDOWN", COIN_PARAM }, + { 0x714, "OFFSET-714", "MIN-LOST-IF-HIT", COIN_PARAM }, + { 0x716, "OFFSET-716", "LOST-PERCENTAGE", COIN_PARAM }, + + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +static const KeywordTab_t minigame_table_list[] = +{ + { 0x010, "OFFSET-010", "BALLOON-DURATION", BALLOON_PARAM }, + // [[obsolete]], typo "BALLON" fixed in 2020-01 + { 0x010, "BALLON-DURATION", 0, BALLOON_PARAM }, + { 0x0ec, "OFFSET-0EC", 0, BALLOON_PARAM }, + + { 0x1dc, "OFFSET-1DC", "COIN-DURATION", COIN_PARAM }, + { 0x2b8, "OFFSET-2B8", 0, COIN_PARAM }, + { 0x394, "OFFSET-394", "START-COINS", COIN_PARAM }, + { 0x470, "OFFSET-470", "MAX-COINS", COIN_PARAM }, + { 0x54c, "OFFSET-54C", 0, COIN_PARAM }, + { 0x628, "OFFSET-628", 0, COIN_PARAM }, + + { 0,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MINIGAME setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMINIGAME ( minigame_t * minigame, file_format_t fform ) +{ + DASSERT(minigame); + memset(minigame,0,sizeof(*minigame)); + minigame->fname = EmptyString; + minigame->fform_source = fform; + SetupBZ2MgrMINIGAME(minigame,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMINIGAME ( minigame_t * minigame ) +{ + DASSERT(minigame); + const typeof(minigame->fform_source) fform = minigame->fform_source; + FreeString(minigame->fname); + InitializeMINIGAME(minigame,fform); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GetOriginalMINIGAME ( minigame_kmg_t *dest ) +{ + DASSERT(dest); + BZ2Manager_t *bz = GetCommonBZ2Manager(FF_KMG); + DASSERT(bz); + DASSERT( bz->size == sizeof(*dest) ); + if ( bz && bz->size == sizeof(*dest) ) + { + memcpy(dest,bz->data,sizeof(*dest)); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void limit_duration ( be16_t *dest ) +{ + DASSERT(dest); + + const u16 limith = opt_kmg_limit; + const u16 limitn = htons(limith); + + u16 *end; + for ( end = dest + 10*11; dest < end; dest++ ) + if ( ntohs(*dest) > limith ) + *dest = limitn; +} + +//----------------------------------------------------------------------------- + +static void dup_slot ( be16_t dest[10][11] ) +{ + const be16_t *src = dest[opt_kmg_arena]; + uint slot; + for ( slot = 0; slot < 10; slot++ ) + if ( slot != opt_kmg_arena ) + memcpy(dest[slot],src,sizeof(dest[slot])); +} + +//----------------------------------------------------------------------------- + +static void PatchMINIGAME ( minigame_kmg_t *dest ) +{ + DASSERT(dest); + + if ( opt_kmg_limit > 0 ) + { + limit_duration(dest->dur_balloon[0]); + limit_duration(dest->dur_coin[0]); + } + + if ( opt_kmg_arena >= 0 && opt_kmg_arena < 10 ) + { + dup_slot(dest->dur_balloon); + dup_slot(dest->off_0ec); + + dup_slot(dest->dur_coin); + dup_slot(dest->off_2b8); + dup_slot(dest->start_coins); + dup_slot(dest->max_coins); + dup_slot(dest->off_5c4); + dup_slot(dest->off_628); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupBZ2MgrMINIGAME ( minigame_t * minigame, bool reload_bin ) +{ + DASSERT(minigame); + if ( reload_bin || memcmp(minigame->data.head.magic,KMG_MAGIC,4) ) + { + GetOriginalMINIGAME(&minigame->data); + PatchMINIGAME(&minigame->data); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawMINIGAME() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(minigame); + if (init_minigame) + InitializeMINIGAME(minigame,FF_KMG); + + if ( !data || data_size != sizeof(minigame->data) ) + { + SetupBZ2MgrMINIGAME(minigame,true); + return ERROR0(ERR_INVALID_DATA, + "Invalid file size (%u but not %zu bytes: %s\n", + data_size, sizeof(minigame->data), minigame->fname ); + } + memcpy(&minigame->data,data,sizeof(minigame->data)); + PatchMINIGAME(&minigame->data); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextMINIGAME() helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + MINIGAME_IGNORE, + MINIGAME_SETUP, + MINIGAME_PARAMETERS, +}; + +//----------------------------------------------------------------------------- + +static const KeywordTab_t minigame_section_name[] = +{ + { MINIGAME_IGNORE, "END", 0, 0 }, + { MINIGAME_SETUP, "SETUP", 0, 0 }, + { MINIGAME_PARAMETERS, "PARAMETERS", 0, 0 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextSETUP +( + minigame_t * minigame, // MINIGAME data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(minigame); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + ScanU32SI(si,(u32*)&minigame->revision,1,0); + si->init_revision = si->cur_file->revision = minigame->revision; + DefineIntVar(&si->gvar,"REVISION$SETUP",minigame->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",minigame->revision); + } + else + GotoEolSI(si); + CheckEolSI(si); + } + + CheckLevelSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextPARAMETERS +( + minigame_t * minigame, // MINIGAME data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(minigame); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + si->cur_file->ptr++; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + PRINT0("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + u32 rev; + ScanU32SI(si,&rev,1,0); + si->cur_file->revision = rev; + DefineIntVar(&si->gvar,"REVISION$ACTIVE",rev); + } + else + { + int abbrev_count; + const KeywordTab_t *par = ScanKeyword(&abbrev_count,name,minigame_param_list); + if ( !par || abbrev_count ) + { + // WARNING + GotoEolSI(si); + continue; + } + + const uint offset = par->id; + u16 *dest = (u16*)( (u8*)&minigame->data + offset ); + ScanBE16SI(si,dest,1,0); + } + CheckEolSI(si); + } + + CheckLevelSI(si); + return ERR_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextTABLE +( + minigame_t * minigame, // MINIGAME data structure + ScanInfo_t * si, // valid data + const KeywordTab_t * cmd // valid section pointer +) +{ + DASSERT(minigame); + DASSERT(si); + DASSERT(cmd); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,0); + continue; + } + + ScanFile_t *sf = si->cur_file; + + long slot; + enumError err = ScanUValueSI(si,&slot,0); + if (err) + break; + if ( slot >= 10 ) + { + ccp eol = FindNextLineFeedSI(si,true); + if ( si->no_warn <= 0 ) + ERROR0(ERR_WARNING, + "Slot must be between 0 and 9: [%s @%u]: %.*s\n", + sf->name, sf->line, (int)(eol-sf->ptr), sf->ptr ); + sf->ptr = eol; + continue; + } + + u16 *dest = (u16*)( (u8*)&minigame->data + cmd->id ) + slot * 11; + uint idx; + for ( idx = 0; idx < 11; idx++ ) + { + ch = NextCharSI(si,false); + if (!ch) + break; + + long val; + err = ScanUValueSI(si,&val,0); + if (err) + break; + *dest++ = htons(val); + } + CheckEolSI(si); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextMINIGAME() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + const void * data, // data to scan + uint data_size, // size of 'data' + file_format_t fform // file format +) +{ + DASSERT(minigame); + PRINT("ScanTextMINIGAME(init=%d)\n",init_minigame); + + if (init_minigame) + InitializeMINIGAME(minigame,FF_KMG_TXT); + else + { + minigame->fform_source = FF_KMG_TXT; + SetupBZ2MgrMINIGAME(minigame,true); + } + + ScanInfo_t si; + InitializeSI(&si,data,data_size,minigame->fname,minigame->revision); + si.predef = SetupVarsCOMMON(); + + enumError max_err = ERR_OK; + minigame->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN MINIGAME SECTIONS, PASS%u ...\n",minigame->is_pass2+1); + + max_err = ERR_OK; + si.no_warn = !minigame->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", minigame->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,minigame->revision); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + noPRINT("--> pass=%u: #%04u: %s\n",minigame->is_pass2+1,si.cur_file->line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,minigame_section_name); + if ( !cmd || abbrev_count ) + { + cmd = ScanKeyword(&abbrev_count,name,minigame_table_list); + if ( cmd && !abbrev_count ) + { + while ( cmd > minigame_table_list && cmd[-1].id == cmd->id ) + cmd--; + NextLineSI(&si,false,false); + enumError err = ScanTextTABLE(minigame,&si,cmd); + if ( max_err < err ) + max_err = err; + } + continue; + } + + NextLineSI(&si,false,false); + noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case MINIGAME_SETUP: + err = ScanTextSETUP(minigame,&si); + break; + + case MINIGAME_PARAMETERS: + err = ScanTextPARAMETERS(minigame,&si); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (minigame->is_pass2) + break; + + minigame->is_pass2 = true; + + RestartSI(&si); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextMINIGAME) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MINIGAME: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(minigame); + + enumError err; +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch (fform) + { + case FF_KMG: + err = ScanRawMINIGAME(minigame,init_minigame,data,data_size); + break; + + case FF_KMG_TXT: + err = ScanTextMINIGAME(minigame,init_minigame,data,data_size,fform); + break; + + default: + if (init_minigame) + InitializeMINIGAME(minigame,0); + return ERROR0(ERR_INVALID_DATA, + "No MINIGAME file: %s\n", minigame->fname ? minigame->fname : "?"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadMINIGAME +( + minigame_t * minigame, // MINIGAME data structure + bool init_minigame, // true: initialize 'minigame' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(minigame); + DASSERT(fname); + if (init_minigame) + InitializeMINIGAME(minigame,0); + else + ResetMINIGAME(minigame); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + minigame->fname = raw.fname; + raw.fname = 0; + err = ScanMINIGAME(minigame,false,raw.data,raw.data_size); + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawMINIGAME() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawMINIGAME +( + minigame_t * minigame, // pointer to valid MINIGAME + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(minigame); + DASSERT(fname); + + PRINT("SaveRawMINIGAME(%s,%d)\n",fname,set_time); + + //--- write to file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&minigame->fatt,0); + + const uint size = sizeof(minigame->data); + if ( fwrite(&minigame->data,1,size,F.f) != size ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextMINIGAME() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextMINIGAME +( + const minigame_t * minigame, // pointer to valid MINIGAME + ccp fname, // filename of destination + bool set_time, // true: set time stamps +// [[minimize]] + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >9: minimize output +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(minigame); + DASSERT(fname); + PRINT("SaveTextMINIGAME(%s,%d)\n",fname,set_time); + + BZ2Manager_t *bz = GetCommonBZ2Manager(FF_KMG); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,minigame->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&minigame->fatt,0); + + + //--- print header + syntax info + + const bool explain = print_header && !brief_count && !export_count; + if (explain) + fputs(text_minigame_info_cr,F.f); + else + fputs(text_minigame_head_cr,F.f); + + const bool tiny = brief_count > 1 || export_count; + if (!tiny) + fprintf(F.f,text_minigame_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + + + //--- print parameters + + bool sect_head_done = false; + int last_mode = -1; + uint active_offset = ~0; + + const KeywordTab_t *par; + for ( par = minigame_param_list; par->name1; par++ ) + { + const uint offset = par->id; + if ( active_offset == offset ) // skip entries for same parameter + continue; + active_offset = offset; + + const s16 val = be16((u8*)&minigame->data+offset); + const s16 orig = be16(bz->data+offset); + if ( !minimize || val != orig ) + { + if (!sect_head_done) + { + sect_head_done = true; + fputs( brief_count < 2 ? section_sep : "\r\n", F.f ); + fprintf(F.f,text_minigame_param_cr,REVISION_NUM); + } + + if ( last_mode != par->opt ) + { + last_mode = par->opt; + fprintf(F.f,"\r\n# %s\r\n",minigame_param_title[last_mode]); + } + ccp name = par->name2 ? par->name2 : par->name1; + const int tablen = (39-strlen(name))/8; + fprintf(F.f, "%s%.*s=%6d # orig:%6d%s\r\n", + name, tablen > 1 ? tablen : 1, Tabs20, + val, orig, val == orig || minimize ? "" : " DIFFER!" ); + } + } + + + //--- print tables + + const uint slot_size = 11*2; + const uint table_size = 10*slot_size; + + for ( par = minigame_table_list; par->name1; par++ ) + { + const uint offset = par->id; + if ( active_offset == offset ) // skip entries for same section + continue; + active_offset = offset; + + const u8 *val = (u8*)&minigame->data + offset; + const u8 *orig = bz->data + offset; + if ( !minimize || memcmp(val,orig,table_size) ) + { + fputs( brief_count < 2 ? section_sep : "\r\n", F.f ); + fprintf(F.f, text_minigame_table_cr, + par->name2 ? par->name2 : par->name1, + offset, REVISION_NUM ); + // [[2do]] ??? table dependent info + fputs(text_minigame_table_head_cr,F.f); + + uint slot; + for ( slot = 0; slot < 10; slot++ ) + { + const int differ = memcmp(val,orig,slot_size); + if ( !minimize || differ ) + { + fprintf(F.f,"%5u ",slot); + uint pl; + for ( pl = 0; pl < 11; pl++, val += 2 ) + fprintf(F.f," %4d",be16(val)); + fprintf(F.f," # A%02u %s\r\n", + arena_info[slot].def_slot, arena_info[slot].name_en ); + + if (differ) + { + fputs("#ORIG:",F.f); + val -= slot_size; + for ( pl = 0; pl < 11; pl++, val += 2, orig += 2 ) + if ( be16(val) == be16(orig) ) + fputs(" .",F.f); + else + fprintf(F.f," %4d",be16(orig)); + fputs("\r\n",F.f); + if (minimize) + fputs("\r\n",F.f); + } + else + orig += slot_size; + } + else + { + val += slot_size; + orig += slot_size; + } + } + } + } + + + //--- terminate + + fputs( brief_count < 2 ? section_end :"\r\n", F.f ); + ResetFile(&F,set_time); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKmgLimit ( ccp arg ) +{ + char *end; + const long num = str2l(arg,&end,10); + if ( end == arg || *end ) + { + ERROR0(ERR_SYNTAX,"Invalid argumant for option --kmg-limit: '%s'\n",arg); + return 1; + } + + opt_kmg_limit = num < 0 ? 0 : num > 0xffff ? 0xffff : num; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptKmgCopy ( ccp arg ) +{ + static const KeywordTab_t keytab[] = + { + { 0, "0", "A12", 0 }, + { 1, "1", "A11", 0 }, + { 2, "2", "A14", 0 }, + { 3, "3", "A13", 0 }, + { 4, "4", "A15", 0 }, + { 5, "5", "A24", 0 }, + { 6, "6", "A25", 0 }, + { 7, "7", "A21", 0 }, + { 8, "8", "A22", 0 }, + { 9, "9", "A23", 0 }, + { -1, "-1", "OFF", 0 }, + {0,0,0,0} + }; + + if ( !arg || !*arg ) + { + opt_kmg_arena = -1; + return 0; + } + + ccp p1 = strrchr(arg,'['); + if (p1) + { + ccp p2 = strchr(++p1,']'); + if (p2) + { + while ( p1 < p2 ) + { + ccp comma = strchr(p1,','); + if ( !comma || comma > p2 ) + comma = p2; + uint len = comma - p1; + char name[10]; + if ( ( *p1 == 'a' || *p1 == 'A' ) && len < sizeof(name) ) + { + memcpy(name,p1,len); + name[len] = 0; + const KeywordTab_t *key = ScanKeyword(0,name,keytab); + if (key) + { + opt_kmg_arena = key->id; + return 0; + } + } + p1 = comma+1; + } + return 0; + } + } + + int abbrev; + const KeywordTab_t *key = ScanKeyword(&abbrev,arg,keytab); + if (!key) + { + PrintKeywordError(keytab,arg,abbrev,0, + "keyword for option --kmg-arena"); + return 1; + } + opt_kmg_arena = key->id; + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-numeric.c b/project/src/lib-numeric.c new file mode 100644 index 0000000..bfe5ecb --- /dev/null +++ b/project/src/lib-numeric.c @@ -0,0 +1,2050 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-std.h" +#include "lib-szs.h" +#include "lib-brres.h" +#include "lib-breff.h" +#include "lib-xbmg.h" +#include "lib-image.h" +#include "dclib-utf8.h" +#include "ui.h" +#include "db-mkw.h" + +#include +#include +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan size options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptAlignU8 ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_align_u8, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "align-u8", // ccp opt_name + 0x00001, // u64 min + 0x10000, // u64 max + 0, // u32 multiple + 1, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +//----------------------------------------------------------------------------- + +int ScanOptAlignPACK ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_align_pack, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "align-pack", // ccp opt_name + 0x00001, // u64 min + 0x10000, // u64 max + 0, // u32 multiple + 1, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +//----------------------------------------------------------------------------- + +int ScanOptAlignBRRES ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_align_brres, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "align-brres", // ccp opt_name + 0x00001, // u64 min + 0x10000, // u64 max + 0, // u32 multiple + 1, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +//----------------------------------------------------------------------------- + +int ScanOptAlignBREFF ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_align_breff, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "align-breff", // ccp opt_name + 0x00001, // u64 min + 0x10000, // u64 max + 0, // u32 multiple + 1, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +//----------------------------------------------------------------------------- + +int ScanOptAlignBREFT ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_align_breft, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "align-breft", // ccp opt_name + 0x00001, // u64 min + 0x10000, // u64 max + 0, // u32 multiple + 1, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +//----------------------------------------------------------------------------- + +int ScanOptAlign ( ccp arg ) +{ + const int stat = ScanSizeOptU32( + &opt_align, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "align", // ccp opt_name + 0x00001, // u64 min + 0x10000, // u64 max + 0, // u32 multiple + 1, // u32 pow2 + true // bool print_err + ) != ERR_OK; + + if (!stat) + opt_align_u8 = opt_align_pack = + opt_align_brres = opt_align_breff = opt_align_breft = opt_align; + return stat; +} + +//----------------------------------------------------------------------------- + +int ScanOptMaxFileSize ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_max_file_size, // u32 * num + arg, // ccp source + MiB, // default_factor1 + 0, // int force_base + "max-file-size", // ccp opt_name + MiB, // u64 min + GiB, // u64 max + 0, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +//----------------------------------------------------------------------------- + +int ScanOptEpsilon ( ccp arg ) +{ + // [[2do]] ??? + + if ( !arg || !*arg ) + { + epsilon_pos = DEF_EPSILON_POS; + epsilon_rot = DEF_EPSILON_ROT; + epsilon_scale = DEF_EPSILON_SCALE; + epsilon_time = DEF_EPSILON_TIME; + } + else + { + double epsilon = strtod(arg,0); + if ( epsilon < 0.0 ) + epsilon = 0.0; + + epsilon_pos = + epsilon_rot = + epsilon_scale = + epsilon_time = epsilon; + } + + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptNMipmaps ( ccp arg ) +{ + static const KeywordTab_t tab[] = + { + { 0, "OFF", "AUTO", 0 }, + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (cmd) + { + opt_n_images = cmd->id; + return 0; + } + + uint num = 0; + const int stat = ScanSizeOptU32( + &num, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "n-mipmaps", // ccp opt_name + 0, // u64 min + MAX_MIPMAPS, // u64 max + 0, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ) != ERR_OK; + + if (!stat) + opt_n_images = num + 1; + return stat; +} + +//----------------------------------------------------------------------------- + +int ScanOptMaxMipmaps ( ccp arg ) +{ + uint num = 0; + const int stat = ScanSizeOptU32( + &num, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "max-mipmaps", // ccp opt_name + 0, // u64 min + MAX_MIPMAPS, // u64 max + 0, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ) != ERR_OK; + + if (!stat) + opt_max_images = num + 1; + return stat; +} + +//----------------------------------------------------------------------------- + +int ScanOptMipmapSize ( ccp arg ) +{ + uint num = 0; + const int stat = ScanSizeOptU32( + &num, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "mipmap-size", // ccp opt_name + 1, // u64 min + 1024, // u64 max + 0, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ) != ERR_OK; + + if (!stat) + opt_min_mipmap_size = num > 1 ? num : 1; + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan num/range /////////////// +/////////////////////////////////////////////////////////////////////////////// + +double strtod_comma ( const char *nptr, char **endptr ) +{ + DASSERT(nptr); + + char *end; + double d = strtod(nptr,&end); + if ( end && *end == ',' ) + { + *(char*)end = '.'; + char *end2; + d = strtod(nptr,&end2); + *(char*)end = ','; + end = end2; + } + if (endptr) + *endptr = end; + return d; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNumU32 +( + ccp arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..1) + u32 * p_num, // return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +) +{ + ASSERT(arg); + ASSERT(p_num); + TRACE("ScanNumU32(%s)\n",arg); + + while ( *arg > 0 && *arg <= ' ' ) + arg++; + + char * end; + u32 num = strtoul(arg,&end, arg[1] >= '0' && arg[1] <= '9' ? 10 : 0 ); + u32 stat = end > arg; + if (stat) + { + if ( num < min ) + num = min; + else if ( num > max ) + num = max; + + while ( *end > 0 && *end <= ' ' ) + end++; + } + else + num = 0; + + if (p_stat) + *p_stat = stat; + *p_num = num; + + TRACE("END ScanNumU32() stat=%u, n=%u ->%s\n",stat,num,arg); + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanRangeU32 +( + ccp arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..2) + u32 * p_n1, // first return value (valid pointer) + u32 * p_n2, // second return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +) +{ + ASSERT(arg); + ASSERT(p_n1); + ASSERT(p_n2); + TRACE("ScanRangeU32(%s)\n",arg); + + int stat = 0; + u32 n1 = ~(u32)0, n2 = 0; + + while ( *arg > 0 && *arg <= ' ' ) + arg++; + + if ( *arg == '-' ) + n1 = min; + else + { + char * end; + u32 num = strtoul(arg,&end,0); + if ( arg == end ) + goto abort; + + stat = 1; + arg = end; + n1 = num; + + while ( *arg > 0 && *arg <= ' ' ) + arg++; + } + + if ( *arg != '-' && *arg != ':' ) + { + stat = 1; + n2 = n1; + goto abort; + } + arg++; + + while ( *arg > 0 && *arg <= ' ' ) + arg++; + + char * end; + n2 = strtoul(arg,&end,0); + if ( end == arg ) + n2 = max; + stat = 2; + arg = end; + + abort: + + if ( stat > 0 ) + { + if ( n1 < min ) + n1 = min; + if ( n2 > max ) + n2 = max; + } + + if ( !stat || n1 > n2 ) + { + stat = 0; + n1 = ~(u32)0; + n2 = 0; + } + + if (p_stat) + *p_stat = stat; + *p_n1 = n1; + *p_n2 = n2; + + while ( *arg > 0 && *arg <= ' ' ) + arg++; + + TRACE("END ScanRangeU32() stat=%u, n=%u..%u ->%s\n",stat,n1,n2,arg); + return (char*)arg; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +unsigned long int strtoul16 +( + const u16 * ptr16, + u16 ** end16, + int base +) +{ + DASSERT(ptr16); + + char buf[100]; + char * dest = buf; + const u16 * p16 = ptr16; + while ( dest < buf + sizeof(buf)-1 ) + { + int ch = be16(p16++); + if ( ch >= '0' && ch <= '9' + || ch >= 'a' && ch <= 'f' + || ch >= 'A' && ch <= 'F' + || ch == 'x' + || ch == 'X' + || isspace(ch) + ) + { + *dest++ = ch; + } + else + break; + } + DASSERT( dest < buf + sizeof(buf) ); + *dest = 0; + + char *end; + unsigned long num = strtoul(buf,&end,base); + if (end16) + *end16 = (u16*)ptr16 + (end-buf); + return num; +} + +/////////////////////////////////////////////////////////////////////////////// + +u16 * skip_ctrl16 ( const u16 * ptr ) +{ + DASSERT(ptr); + for(;;) + { + const u16 ch = be16(ptr); + if ( !ch || ch > ' ' ) + return (u16*)ptr; + ptr++; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +u16 * ScanNum16U32 +( + const u16 * arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..1) + u32 * p_num, // return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +) +{ + ASSERT(arg); + ASSERT(p_num); + TRACE("ScanNum16U32()\n"); + + arg = skip_ctrl16(arg); + + u16 * end; + u32 num = strtoul16(arg,&end, arg[1] >= '0' && arg[1] <= '9' ? 10 : 0 ); + u32 stat = end > arg; + if (stat) + { + if ( num < min ) + num = min; + else if ( num > max ) + num = max; + + end = skip_ctrl16(end); + } + else + num = 0; + + if (p_stat) + *p_stat = stat; + *p_num = num; + + TRACE("END ScanNum16U32() stat=%u, n=%u\n",stat,num); + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +u16 * ScanRange16U32 +( + const u16 * arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..2) + u32 * p_n1, // first return value (valid pointer) + u32 * p_n2, // second return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +) +{ + ASSERT(arg); + ASSERT(p_n1); + ASSERT(p_n2); + TRACE("ScanRangeU32()\n"); + + int stat = 0; + + u32 n1 = ~(u32)0, n2 = 0; + arg = skip_ctrl16(arg); + + if ( be16(arg) == '-' ) + n1 = min; + else + { + u16 * end; + u32 num = strtoul16(arg,&end,0); + if ( arg == end ) + goto abort; + + stat = 1; + arg = skip_ctrl16(end); + n1 = num; + } + + if ( be16(arg) != '-' ) + { + stat = 1; + n2 = n1; + goto abort; + } + + arg = skip_ctrl16(arg+1); + u16 * end; + n2 = strtoul16(arg,&end,0); + if ( end == arg ) + n2 = max; + stat = 2; + arg = end; + + abort: + + if ( stat > 0 ) + { + if ( n1 < min ) + n1 = min; + if ( n2 > max ) + n2 = max; + } + + if ( !stat || n1 > n2 ) + { + stat = 0; + n1 = ~(u32)0; + n2 = 0; + } + + if (p_stat) + *p_stat = stat; + *p_n1 = n1; + *p_n2 = n2; + + arg = skip_ctrl16(arg); + + TRACE("END ScanRange16U32() stat=%u, n=%u..%u\n",stat,n1,n2); + return (u16*)arg; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// vector calculations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +double LengthF +( + const float *a, // A vector (x,y,z) + const float *b // B vector (x,y,z) +) +{ + DASSERT(a); + DASSERT(b); + + const double d0 = a[0] - b[0]; + const double d1 = a[1] - b[1]; + const double d2 = a[2] - b[2]; + + return sqrt ( d0*d0 + d1*d1 + d2*d2 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +double Length2F +( + const float * a, // A vector (x,y,z) + const float * b // B vector (x,y,z) +) +{ + DASSERT(a); + DASSERT(b); + + const double d0 = a[0] - b[0]; + const double d1 = a[1] - b[1]; + const double d2 = a[2] - b[2]; + + return d0*d0 + d1*d1 + d2*d2; +} + +/////////////////////////////////////////////////////////////////////////////// + +double LengthD +( + const double * a, // A vector (x,y,z) + const double * b // B vector (x,y,z) +) +{ + DASSERT(a); + DASSERT(b); + + const double d0 = a[0] - b[0]; + const double d1 = a[1] - b[1]; + const double d2 = a[2] - b[2]; + + return sqrt ( d0*d0 + d1*d1 + d2*d2 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +double Length2D +( + const double * a, // A vector (x,y,z) + const double * b // B vector (x,y,z) +) +{ + DASSERT(a); + DASSERT(b); + + const double d0 = a[0] - b[0]; + const double d1 = a[1] - b[1]; + const double d2 = a[2] - b[2]; + + return d0*d0 + d1*d1 + d2*d2; +} + +/////////////////////////////////////////////////////////////////////////////// + +double AngleVector +( + const float * prev, // vector (x,y,z) of previous point + const float * point, // vector (x,y,z) of current point + const float * next, // vector (x,y,z) of next point + bool return_degree // true: return in normed degree; false: radiants +) +{ + DASSERT(prev); + DASSERT(point); + DASSERT(next); + + noTRACE("ANGLE: %11.3f %11.3f | %11.3f %11.3f | %11.3f %11.3f\n", + prev[0], prev[2], point[0], point[2], next[0], next[2] ); + + const double ax = point[0] - prev[0]; + const double az = point[2] - prev[2]; + if ( abs(ax) + abs(az) < 1e-6 ) + return 0.0; + + const double bx = next[0] - point[0]; + const double bz = next[2] - point[2]; + if ( abs(bx) + abs(bz) < 1e-6 ) + return 0.0; + + if (!return_degree) + return atan2(bx,bz) - atan2(ax,az); + + const double deg = ( atan2(bx,bz) - atan2(ax,az) ) * ( 180 / M_PI ); + return deg > 180.0 ? deg - 360.0 : deg <= -180.0 ? deg + 360.0 : deg; +} + +/////////////////////////////////////////////////////////////////////////////// + +void WidthAndDirection +( + double * r_width, // if not NULL: return width here + double * r_direction, // if not NULL: return direction here + bool return_degree, // true: return in normed degree; false: radiants + const float * a, // A point (x,y) + const float * b // B point (x,y) +) +{ + const double dx = b[0] - a[0]; + const double dy = b[1] - a[1]; + const double width = sqrt ( dx*dx + dy*dy ); + if (r_width) + *r_width = width; + + if (r_direction) + { + if ( width < 1e-6 ) + *r_direction = 0.0; + else if (return_degree) + { + double deg = atan2(dx,dy) * ( 180 / M_PI ) + 90; + *r_direction = deg > 180.0 ? deg - 360.0 : deg; + } + else + *r_direction = atan2(dx,dy); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +double CalcDirection2F +( + const float * a, // A point (x,y) + const float * b // B point (x,y) +) +{ + const double dx = b[0] - a[0]; + const double dy = b[1] - a[1]; + return fabs(dx) + fabs(dy) < 1e-6 + ? 0.0 + : atan2(dx,dy); +} + +/////////////////////////////////////////////////////////////////////////////// + +float3 CalcDirection3F +( + const float * a, // A point (x,y,z) + const float * b // B point (x,y,z) +) +{ + DASSERT(a); + DASSERT(b); + + const double dx = b[0] - a[0]; + const double dy = b[1] - a[1]; + const double dz = b[2] - a[2]; + const double hl = sqrt ( dx*dx + dz*dz ); + + float3 res; + res.x = fabs(dy) + fabs(hl) < 1e-6 ? 0.0 : atan2(dy,hl) * ( -180.0 / M_PI ); + res.y = fabs(dx) + fabs(dz) < 1e-6 ? 0.0 : atan2(dx,dz) * ( 180.0 / M_PI ); + res.z = 0.0; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 CalcDirection3D +( + const double3 * a, // A point (x,y,z) + const double3 * b // B point (x,y,z) +) +{ + DASSERT(a); + DASSERT(b); + + const double dx = b->x - a->x; + const double dy = b->y - a->y; + const double dz = b->z - a->z; + const double hl = sqrt ( dx*dx + dz*dz ); + + double3 res; + res.x = fabs(dy) + fabs(hl) < 1e-6 ? 0.0 : atan2(dy,hl) * ( -180.0 / M_PI ); + res.y = fabs(dx) + fabs(dz) < 1e-6 ? 0.0 : atan2(dx,dz) * ( 180.0 / M_PI ); + res.z = 0.0; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void Scale +( + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *scale, // NULL or scaling vector + double3 *pt, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +) +{ + DASSERT( pt || !n_pt ); + + if (scale) + { + if (origin) + { + for ( ; n_pt-- > 0; pt = (double3*)((u8*)pt + pt_delta) ) + { + pt->x = ( pt->x - origin->x ) * scale->x + origin->x; + pt->y = ( pt->y - origin->y ) * scale->y + origin->y; + pt->z = ( pt->z - origin->z ) * scale->z + origin->z; + } + } + else + { + for ( ; n_pt-- > 0; pt = (double3*)((u8*)pt + pt_delta) ) + { + pt->x *= scale->x; + pt->y *= scale->y; + pt->z *= scale->z; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Rotate +( + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *rot, // rotation vector + double3 *pt1, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +) +{ + DASSERT(pt1); + + if (!rot) + return; + + bool have_rot[3]; + have_rot[0] = fabs(rot->x) > 1e-6; + have_rot[1] = fabs(rot->y) > 1e-6; + have_rot[2] = fabs(rot->z) > 1e-6; + if ( !have_rot[0] && !have_rot[1] && !have_rot[2] ) + return; + + uint p; + for ( p = 0; p < 3; p++ ) + { + if (!have_rot[p]) + continue; + + const double rad_rot = rot->v[p] * (M_PI/180.0); + const uint a = (p+2) % 3; + const uint b = (p+1) % 3; + + if (origin) + { + uint i; + double3 *pt = pt1; + for ( i = n_pt; i-- > 0; pt = (double3*)((u8*)pt+pt_delta) ) + { + const double da = pt->v[a] - origin->v[a]; + const double db = pt->v[b] - origin->v[b]; + const double rad = atan2(da,db) + rad_rot; + const double len = sqrt(da*da+db*db); + + pt->v[a] = sin(rad) * len + origin->v[a]; + pt->v[b] = cos(rad) * len + origin->v[b]; + } + } + else + { + uint i; + double3 *pt = pt1; + for ( i = n_pt; i-- > 0; pt = (double3*)((u8*)pt+pt_delta) ) + { + const double da = pt->v[a]; + const double db = pt->v[b]; + const double rad = atan2(da,db) + rad_rot; + const double len = sqrt(da*da+db*db); + + pt->v[a] = sin(rad) * len; + pt->v[b] = cos(rad) * len; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void Translate +( + const double3 *trans, // NULL or translation vector + double3 *pt, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +) +{ + DASSERT( pt || !n_pt ); + + if (trans) + for ( ; n_pt-- > 0; pt = (double3*)((u8*)pt + pt_delta) ) + { + pt->x += trans->x; + pt->y += trans->y; + pt->z += trans->z; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AxisRotate +( + const double3 *a1, // first axis point + const double3 *a2, // second axis point + double degree, // degree of rotation + double3 *pt1, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +) +{ + DASSERT(a1); + DASSERT(a2); + DASSERT( pt1 || !n_pt ); + + + //--- some precalculations + + double3 n12; + Sub3(n12,*a2,*a1); + Unit3(n12); + + const double rad = degree * (M_PI/180.0); + const double fsin = sin(rad); + const double fcos = cos(rad); + + uint i; + double3 *pt = pt1; + for ( i = n_pt; i-- > 0; pt = (double3*)((u8*)pt+pt_delta) ) + { + double3 d13, n13, nx, base; + Sub3(d13,*pt,*a1); + CrossProd3(nx,n12,d13); + CrossProd3(n13,nx,n12); + Sub3(base,*pt,n13); + + pt->x = fcos * n13.x + fsin * nx.x + base.x; + pt->y = fcos * n13.y + fsin * nx.y + base.y; + pt->z = fcos * n13.z + fsin * nx.z + base.z; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +double Degree // output: -180.00 .. +180.00 +( + double rad // radians +) +{ + double deg = fmod( rad * (180.0/M_PI) + 180.0, 360.0 ); + return deg < 0.0 ? deg + 180.0 : deg - 180.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +float NormDegreeF // output: -180.00 .. +180.00 +( + float deg +) +{ + deg = fmod( deg + 180.0, 360.0 ); + return deg < 0.0 ? deg + 180.0 : deg - 180.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void MinMax3 +( + double3 *min, // valid pointer to initialized min value + double3 *max, // valid pointer to initialized max value + const double3 *val, // valid pointer to source list + int n_val // if >0: number of elements in 'val' +) +{ + DASSERT(min); + DASSERT(max); + DASSERT(val); + + while ( n_val-- > 0 ) + { + if ( min->x > val->x ) min->x = val->x; + if ( min->y > val->y ) min->y = val->y; + if ( min->z > val->z ) min->z = val->z; + + if ( max->x < val->x ) max->x = val->x; + if ( max->y < val->y ) max->y = val->y; + if ( max->z < val->z ) max->z = val->z; + + val++; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UnitZero3 ( double3 *dest, const double3 *src ) +{ + DASSERT(dest); + DASSERT(src); + + double len = Length3(*src); + double min = len * 1e-5; + int dirty = 0; + + if ( fabs(src->x) < min ) + { + dest->x = 0.0; + dirty = true; + } + else + dest->x = src->x; + + if ( fabs(src->y) < min ) + { + dest->y = 0.0; + dirty = true; + } + else + dest->y = src->y; + + if ( fabs(src->z) < min ) + { + dest->z = 0.0; + dirty = true; + } + else + dest->z = src->z; + + if (dirty) + len = Length3(*dest); + + if (len) + { + dest->x /= len; + dest->y /= len; + dest->z /= len; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +double CalcNormals +( + // return length of p1..p2 + + double3 *res_norm, // return 3 normals here + const double3 *p1, // first point + const double3 *p2, // second point + const double3 *dir, // NULL or helper point for normal[1] + double r // radius == length of norm[0]..norm[2] +) +{ + DASSERT(res_norm); + DASSERT(p1); + DASSERT(p2); + + bool vertical = false; + + Sub3(res_norm[0],*p2,*p1); + double len0 = Length3(res_norm[0]); + if ( len0 < 1e-9 ) + { + len0 = 0.0; + res_norm[0].x = 0.0; + res_norm[0].y = 0.0; + res_norm[0].z = 1.0; + } + else + { + res_norm[0].x /= len0; + res_norm[0].y /= len0; + res_norm[0].z /= len0; + vertical = fabs(res_norm[0].y) > 0.9; + } + + double3 temp; + if (!dir) + { + dir = &temp; + memcpy(&temp,p1,sizeof(temp)); + if (vertical) + temp.z += 1000; + else + temp.y += 1000; + } + + double3 dist; + Sub3(dist,*dir,*p1); + CrossProd3(res_norm[2],res_norm[0],dist); + double len1 = Length3(res_norm[2]); + if ( len1 < 1e-9 ) + { + if (vertical) + { + temp.x = temp.y = 0.0; + temp.z = 1000.0; + } + else + { + temp.x = temp.z = 0.0; + temp.y = 1000.0; + } + CrossProd3(res_norm[2],res_norm[0],temp); + len1 = Length3(res_norm[2]); + } + len1 = r / len1; + res_norm[2].x *= len1; + res_norm[2].y *= len1; + res_norm[2].z *= len1; + + CrossProd3(res_norm[1],res_norm[2],res_norm[0]); + res_norm[0].x *= r; + res_norm[0].y *= r; + res_norm[0].z *= r; + + return len0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OverlapCubeTri() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool OverlapCubeTri +( + double3 *cube_mid, // middle of the cube + double cube_width, // width of the cube + double3 *pt1, // first point of triangle + double3 *pt2, // second point of triangle + double3 *pt3 // third point of triangle +) +{ + ERROR0(ERR_NOT_IMPLEMENTED,0); + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// triangles /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CalcTriMetricsByPoints +( + tri_metrics_t *met, // result + const double3 *p1, // NULL or first point + const double3 *p2, // NULL or second point + const double3 *p3 // NULL or third point +) +{ + DASSERT(met); + memset(met,0,sizeof(*met)); + if (p1) Assign3(met->p1,*p1); + if (p2) Assign3(met->p2,*p2); + if (p3) Assign3(met->p3,*p3); + CalcTriMetrics(met); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcTriMetrics ( tri_metrics_t *met ) +{ + DASSERT(met); + + double3 x, y, z; + Sub3(x,met->p2,met->p1); + Sub3(y,met->p3,met->p2); + Sub3(z,met->p1,met->p3); + + // calc side lengths + const double a2 = LengthSqare3(x); met->l1 = sqrt(a2); + const double b2 = LengthSqare3(y); met->l2 = sqrt(b2); + const double c2 = LengthSqare3(z); met->l3 = sqrt(c2); + + // calc area ( 0.0625 = 1/16 ) + met->area = sqrt(( 2*(a2*b2+a2*c2+b2*c2) - a2*a2 - b2*b2 - c2*c2 ) * 0.0625 ); + + // calc heights + const double area2 = 2*met->area; + met->h1 = area2/met->l1; + met->h2 = area2/met->l2; + met->h3 = area2/met->l3; + MinMax3p(met->min_ht,met->max_ht,met->h1,met->h2,met->h3); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// octahedron_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CreateRegularOctahedron +( + octahedron_t *oct, // valid pointer, initialize + double radius, // radius of octahedron + const double3 *rot, // not NULL: rotate octahedron + const double3 *shift // not NULL: shift octahedron +) +{ + DASSERT(oct); + + double3 radius3; + radius3.x = radius3.y = radius3.z = radius; + CreateOctahedron(oct,&radius3,rot,shift); +} + +//----------------------------------------------------------------------------- + +void CreateRegularOctahedronF +( + octahedron_t *oct, // valid pointer, initialize + double radius, // radius of octahedron + const float *rot, // not NULL: 3D vector, rotate octahedron + const float *shift // not NULL: 3D vector, shift octahedron +) +{ + DASSERT(oct); + + double3 radius3; + radius3.x = radius3.y = radius3.z = radius; + + double3 *rot3 = 0, rot3_data; + if (rot) + { + rot3_data.x = *rot++; + rot3_data.y = *rot++; + rot3_data.z = *rot; + rot3 = &rot3_data; + } + + double3 *shift3 = 0, shift3_data; + if (shift) + { + shift3_data.x = *shift++; + shift3_data.y = *shift++; + shift3_data.z = *shift; + shift3 = &shift3_data; + } + + CreateOctahedron(oct,&radius3,rot3,shift3); +} + +//----------------------------------------------------------------------------- + +void CreateOctahedron +( + octahedron_t *oct, // valid pointer, initialize + const double3 *radius, // radius of octahedron + const double3 *rot, // not NULL: rotate octahedron + const double3 *shift // not NULL: shift octahedron +) +{ + DASSERT(oct); + DASSERT(radius); + + oct->x1.x = -radius->x; + oct->x1.y = 0.0; + oct->x1.z = 0.0; + + oct->x2.x = +radius->x; + oct->x2.y = 0.0; + oct->x2.z = 0.0; + + oct->y1.x = 0.0; + oct->y1.y = -radius->y; + oct->y1.z = 0.0; + + oct->y2.x = 0.0; + oct->y2.y = +radius->y; + oct->y2.z = 0.0; + + oct->z1.x = 0.0; + oct->z1.y = 0.0; + oct->z1.z = -radius->z; + + oct->z2.x = 0.0; + oct->z2.y = 0.0; + oct->z2.z = +radius->z; + + if (rot) + RotateOctahedron(oct,0,rot); + if (shift) + ShiftOctahedron(oct,shift); +} + +//----------------------------------------------------------------------------- + +void ScaleOctahedron +( + octahedron_t *oct, // valid octahedron data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *scale // not NULL: scale octahedron +) +{ + DASSERT(oct); + if (scale) + { + if (origin) + { + uint p; + for ( p = 0; p < 6; p++ ) + { + oct->p[p].x = ( oct->p[p].x - origin->x ) * scale->x + origin->x; + oct->p[p].y = ( oct->p[p].y - origin->y ) * scale->y + origin->y; + oct->p[p].z = ( oct->p[p].z - origin->z ) * scale->z + origin->z; + } + } + else + { + uint p; + for ( p = 0; p < 6; p++ ) + { + oct->p[p].x *= scale->x; + oct->p[p].y *= scale->y; + oct->p[p].z *= scale->z; + } + } + } +} + +//----------------------------------------------------------------------------- + +void RotateOctahedron +( + octahedron_t *oct, // valid octahedron data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *rot // not NULL: rotate octahedron +) +{ + if (rot) + Rotate(origin,rot,oct->p,sizeof(*oct->p),6); +} + +//----------------------------------------------------------------------------- + +void ShiftOctahedron +( + octahedron_t *oct, // valid octahedron data + const double3 *shift // not NULL: shift octahedron +) +{ + DASSERT(oct); + if (shift) + { + uint p; + for ( p = 0; p < 6; p++ ) + { + oct->p[p].x += shift->x; + oct->p[p].y += shift->y; + oct->p[p].z += shift->z; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cuboid_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CreateCube +( + cuboid_t *cub, // valid pointer, initialize + double width, // width of cube + const double3 *rot, // not NULL: rotate cube + const double3 *shift // not NULL: shift cube +) +{ + DASSERT(cub); + + double3 size; + size.x = size.y = size.z = width; + CreateCuboid(cub,&size,rot,shift); +} + +//----------------------------------------------------------------------------- + +void CreateCubeF +( + cuboid_t *cub, // valid pointer, initialize + double width, // width of cube + const float *rot, // not NULL: 3D vector, rotate cube + const float *shift // not NULL: 3D vector, shift cube +) +{ + DASSERT(cub); + + double3 size; + size.x = size.y = size.z = width; + + double3 *rot3 = 0, rot3_data; + if (rot) + { + rot3_data.x = *rot++; + rot3_data.y = *rot++; + rot3_data.z = *rot; + rot3 = &rot3_data; + } + + double3 *shift3 = 0, shift3_data; + if (shift) + { + shift3_data.x = *shift++; + shift3_data.y = *shift++; + shift3_data.z = *shift; + shift3 = &shift3_data; + } + + CreateCuboid(cub,&size,rot3,shift3); +} + +//----------------------------------------------------------------------------- + +void CreateCuboid +( + cuboid_t *cub, // valid pointer, initialize + const double3 *size, // base size of cuboid + const double3 *rot, // not NULL: rotate cuboid + const double3 *shift // not NULL: shift cuboid +) +{ + DASSERT(cub); + DASSERT(size); + + double3 s; + s.x = size->x / 2; + s.y = size->y / 2; + s.z = size->z / 2; + + uint p; + for ( p = 0; p < 8; p++ ) + { + cub->p[p].x = p & 1 ? +s.x : -s.x; + cub->p[p].y = p & 2 ? +s.y : -s.y; + cub->p[p].z = p & 4 ? +s.z : -s.z; + } + + if (rot) + RotateCuboid(cub,0,rot); + if (shift) + ShiftCuboid(cub,shift); +} + +//----------------------------------------------------------------------------- + +void CreateCuboidF +( + cuboid_t *cub, // valid pointer, initialize + const float *size, // base size of cuboid + const float *rot, // not NULL: rotate cuboid + const float *shift // not NULL: shift cuboid +) +{ + DASSERT(cub); + DASSERT(size); + + double3 size3; + size3.x = *size++; + size3.y = *size++; + size3.z = *size; + + double3 *rot3 = 0, rot3_data; + if (rot) + { + rot3_data.x = *rot++; + rot3_data.y = *rot++; + rot3_data.z = *rot; + rot3 = &rot3_data; + } + + double3 *shift3 = 0, shift3_data; + if (shift) + { + shift3_data.x = *shift++; + shift3_data.y = *shift++; + shift3_data.z = *shift; + shift3 = &shift3_data; + } + + CreateCuboid(cub,&size3,rot3,shift3); +} + +//----------------------------------------------------------------------------- + +void ScaleCuboid +( + cuboid_t *cub, // valid cuboid data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *scale // not NULL: scale cuboid +) +{ + DASSERT(cub); + if (scale) + { + if (origin) + { + uint p; + for ( p = 0; p < 8; p++ ) + { + cub->p[p].x = ( cub->p[p].x - origin->x ) * scale->x + origin->x; + cub->p[p].y = ( cub->p[p].y - origin->y ) * scale->y + origin->y; + cub->p[p].z = ( cub->p[p].z - origin->z ) * scale->z + origin->z; + } + } + else + { + uint p; + for ( p = 0; p < 8; p++ ) + { + cub->p[p].x *= scale->x; + cub->p[p].y *= scale->y; + cub->p[p].z *= scale->z; + } + } + } +} + +//----------------------------------------------------------------------------- + +void RotateCuboid +( + cuboid_t *cub, // valid cuboid data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *rot // not NULL: rotate cuboid +) +{ + if (rot) + Rotate(origin,rot,cub->p,sizeof(*cub->p),8); +} + +//----------------------------------------------------------------------------- + +void ShiftCuboid +( + cuboid_t *cub, // valid cuboid data + const double3 *shift // not NULL: shift cuboid +) +{ + DASSERT(cub); + if (shift) + { + uint p; + for ( p = 0; p < 8; p++ ) + { + cub->p[p].x += shift->x; + cub->p[p].y += shift->y; + cub->p[p].z += shift->z; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// index lists /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeIL ( IndexList_t * il, ccp prefix ) +{ + DASSERT(il); + + memset(il,0,sizeof(*il)); + il->prefix = prefix; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetIL ( IndexList_t * il ) +{ + DASSERT(il); + DASSERT( il->used <= il->size ); + DASSERT( !il->list == !il->size ); + + uint i; + for ( i = 0; i < il->used; i++ ) + FreeString(il->list[i]); + FREE(il->list); + + InitializeIL(il,il->prefix); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FillIL +( + IndexList_t * il, // valid index list + uint min_fill // fill minimum # elements +) +{ + DASSERT(il); + DASSERT( il->used <= il->size ); + DASSERT( !il->list == !il->size ); + + if (il->prefix) + { + if ( min_fill > il->used ) + DefineIL(il,min_fill-1,0,0); + + uint i; + for ( i = 0; i < il->used; i++ ) + if (!il->list[i]) + { + if ( il->next_index < i ) + il->next_index = i; + char buf[VARNAME_SIZE+1]; + snprintf(buf,sizeof(buf),"%s%u",il->prefix,il->next_index++); + il->list[i] = STRDUP(buf); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineIL +( + IndexList_t * il, // valid index list + uint index, // index of object + ccp name, // name of object + bool move_name // true: 'name' is alloced -> move it +) +{ + DASSERT(il); + DASSERT( il->used <= il->size ); + DASSERT( !il->list == !il->size ); + + if ( index >= il->size ) + { + il->size = 3*il->size/2 + 10; + if ( il->size < index + 10 ) + il->size = index + 10; + il->list = REALLOC(il->list,il->size*sizeof(*il->list)); + } + DASSERT( il->list ); + DASSERT( index < il->size ); + + uint used = il->used; + while ( used <= index ) + il->list[used++] = 0; + il->used = used; + DASSERT( index < il->used ); + + FreeString(il->list[index]); + il->list[index] = move_name || !name ? name : STRDUP(name); + noPRINT("DEFINE-IL() %s = %u\n", name, index ); + + if ( name && il->prefix ) + { + ccp prefix = il->prefix; + while ( *prefix && toupper((int)*prefix) == toupper((int)*name) ) + prefix++, name++; + + if ( !*prefix && *name ) + { + char *end; + uint next = strtoul(name,&end,10) + 1; + if ( !*end && il->next_index < next ) + il->next_index = next; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveIL +( + IndexList_t * il, // valid index list + uint index // index of object +) +{ + DASSERT(il); + DASSERT( il->used <= il->size ); + DASSERT( !il->list == !il->size ); + if ( index >= il->used ) + return false; + + ccp *ptr = il->list + index; + FreeString(*ptr); + il->used--; + if ( index < il->used ) + memmove(ptr,ptr+1,sizeof(*ptr)*(il->used-index)); + il->list[il->used] = 0; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetNameIL +( + IndexList_t * il, // valid index list + uint index // index of object +) +{ + DASSERT(il); + DASSERT( il->used <= il->size ); + DASSERT( !il->list == !il->size ); + + if ( index >= il->used ) + return "-1"; + DASSERT( index < il->used ); + + ccp name = il->list[index]; + if (!name) + { + FillIL(il,0); + name = il->list[index]; + } + DASSERT(name); + return name; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintNameIL +( + FILE * f, // output files + IndexList_t * il, // valid index list + uint index, // index of object + uint align, // align end of name to character pos + uint fw, // field width of name + uint indent // indention of continuation line +) +{ + DASSERT(il); + DASSERT( il->used <= il->size ); + DASSERT( !il->list == !il->size ); + + ccp name = GetNameIL(il,index); + DASSERT(name); + const uint nlen = strlen(name); + + if ( nlen > fw ) + { + // print with continuation line + if ( align < fw ) + fprintf(f,"%s\r\n%*s%*s%*s", name, indent,"", align,">", fw-align,">" ); + else + fprintf(f,"%s\r\n%*s%*s", name, indent,"", fw,">" ); + } + else if ( nlen > align ) + { + // use extra space of fw + fprintf(f,"%-*s", fw, name ); + } + else if ( align < fw ) + { + // aligned output + fprintf(f,"%*s%*s", align, name, fw-align, "" ); + } + else + { + // aligned output + fprintf(f,"%*s", fw, name ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color conversions /////////////// +/////////////////////////////////////////////////////////////////////////////// + + +int ScanOptCmprDefault ( ccp arg ) +{ + DASSERT( opt_cmpr_def[4] == 0xff ); + DASSERT( opt_cmpr_def[5] == 0xff ); + DASSERT( opt_cmpr_def[6] == 0xff ); + DASSERT( opt_cmpr_def[7] == 0xff ); + + static u8 reset[8] = { 0x00,0x00, 0x00,0x20, 0xff,0xff,0xff,0xff }; + memcpy(opt_cmpr_def,reset,sizeof(opt_cmpr_def)); + opt_cmpr_valid = false; + + if ( !arg || !strcmp(arg,"-") ) + return 0; + + char *end; + u32 col1 = str2ul(arg,&end,16); + if ( end == arg ) + return 0; + + u32 col2 = col1; + arg = end; + while (isspace((int)*arg)) + arg++; + if ( *arg == ',' ) + { + arg++; + col2 = str2ul(arg,&end,16); + if ( end == arg ) + col2 = col1; + } + + u16 c1 = RGB_to_RGB565(col1); + u16 c2 = RGB_to_RGB565(col2); + if ( c1 > c2 ) + { + const u16 temp = c1; + c1 = c2; + c2 = temp; + } + else if ( c1 == c2 ) + { + c1 &= ~0x0020; // modify least significant bit of green + c2 |= 0x0020; + } + + write_be16(opt_cmpr_def,c1); + write_be16(opt_cmpr_def+2,c2); + opt_cmpr_valid = true; + + #if 0 + PRINT("%08x,%08x -> %04x,%04x\n",col1,col2,c1,c2); + HEXDUMP16(0,0,opt_cmpr_def,8); + #endif + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color conversion tables /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const u8 cc38[8] = // convert 3-bit color to 8-bit color +{ + 0x00,0x24,0x49,0x6d, 0x92,0xb6,0xdb,0xff +}; + +//----------------------------------------------------------------------------- + +const u8 cc48[16] = // convert 4-bit color to 8-bit color +{ + 0x00,0x11,0x22,0x33, 0x44,0x55,0x66,0x77, 0x88,0x99,0xaa,0xbb, 0xcc,0xdd,0xee,0xff +}; + +//----------------------------------------------------------------------------- + +const u8 cc58[32] = // convert 5-bit color to 8-bit color +{ + 0x00,0x08,0x10,0x19, 0x21,0x29,0x31,0x3a, 0x42,0x4a,0x52,0x5a, 0x63,0x6b,0x73,0x7b, + 0x84,0x8c,0x94,0x9c, 0xa5,0xad,0xb5,0xbd, 0xc5,0xce,0xd6,0xde, 0xe6,0xef,0xf7,0xff +}; + +//----------------------------------------------------------------------------- + +const u8 cc68[64] = // convert 6-bit color to 8-bit color +{ + 0x00,0x04,0x08,0x0c, 0x10,0x14,0x18,0x1c, 0x20,0x24,0x28,0x2d, 0x31,0x35,0x39,0x3d, + 0x41,0x45,0x49,0x4d, 0x51,0x55,0x59,0x5d, 0x61,0x65,0x69,0x6d, 0x71,0x75,0x79,0x7d, + 0x82,0x86,0x8a,0x8e, 0x92,0x96,0x9a,0x9e, 0xa2,0xa6,0xaa,0xae, 0xb2,0xb6,0xba,0xbe, + 0xc2,0xc6,0xca,0xce, 0xd2,0xd7,0xdb,0xdf, 0xe3,0xe7,0xeb,0xef, 0xf3,0xf7,0xfb,0xff +}; + +/////////////////////////////////////////////////////////////////////////////// + +const u8 cc83[256] = // convert 8-bit color to 3-bit color +{ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x02, 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, + 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, + 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, 0x03,0x03,0x03,0x03, + 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, + 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, + 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, + 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, + 0x04,0x04,0x04,0x04, 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, + 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, + 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, 0x05,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, + 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, + 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, 0x06,0x07,0x07,0x07, + 0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07 +}; + +//----------------------------------------------------------------------------- + +const u8 cc84[256] = // convert 8-bit color to 4-bit color +{ + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x02,0x02, 0x02,0x02,0x02,0x02, + 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, 0x02,0x02,0x02,0x03, 0x03,0x03,0x03,0x03, + 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, 0x04,0x04,0x04,0x04, + 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, 0x04,0x04,0x04,0x04, 0x04,0x05,0x05,0x05, + 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, 0x05,0x05,0x05,0x05, 0x05,0x05,0x06,0x06, + 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, 0x06,0x06,0x06,0x07, + 0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07, 0x07,0x07,0x07,0x07, + 0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08, + 0x08,0x09,0x09,0x09, 0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x09, + 0x09,0x09,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a, + 0x0a,0x0a,0x0a,0x0b, 0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0b, + 0x0b,0x0b,0x0b,0x0b, 0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0c, 0x0c,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e, + 0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0f, 0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f +}; + +//----------------------------------------------------------------------------- + +const u8 cc85[256] = // convert 8-bit color to 5-bit color +{ + 0x00,0x00,0x00,0x00, 0x00,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x02,0x02,0x02, + 0x02,0x02,0x02,0x02, 0x02,0x03,0x03,0x03, 0x03,0x03,0x03,0x03, 0x03,0x04,0x04,0x04, + 0x04,0x04,0x04,0x04, 0x04,0x04,0x05,0x05, 0x05,0x05,0x05,0x05, 0x05,0x05,0x06,0x06, + 0x06,0x06,0x06,0x06, 0x06,0x06,0x07,0x07, 0x07,0x07,0x07,0x07, 0x07,0x07,0x08,0x08, + 0x08,0x08,0x08,0x08, 0x08,0x08,0x09,0x09, 0x09,0x09,0x09,0x09, 0x09,0x09,0x09,0x0a, + 0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0b, 0x0b,0x0b,0x0b,0x0b, 0x0b,0x0b,0x0b,0x0c, + 0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0c,0x0d, 0x0d,0x0d,0x0d,0x0d, 0x0d,0x0d,0x0d,0x0d, + 0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e, 0x0f,0x0f,0x0f,0x0f, 0x0f,0x0f,0x0f,0x0f, + 0x10,0x10,0x10,0x10, 0x10,0x10,0x10,0x10, 0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11, + 0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x12, 0x12,0x13,0x13,0x13, 0x13,0x13,0x13,0x13, + 0x13,0x14,0x14,0x14, 0x14,0x14,0x14,0x14, 0x14,0x15,0x15,0x15, 0x15,0x15,0x15,0x15, + 0x15,0x16,0x16,0x16, 0x16,0x16,0x16,0x16, 0x16,0x16,0x17,0x17, 0x17,0x17,0x17,0x17, + 0x17,0x17,0x18,0x18, 0x18,0x18,0x18,0x18, 0x18,0x18,0x19,0x19, 0x19,0x19,0x19,0x19, + 0x19,0x19,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1b,0x1b, 0x1b,0x1b,0x1b,0x1b, + 0x1b,0x1b,0x1b,0x1c, 0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1d, 0x1d,0x1d,0x1d,0x1d, + 0x1d,0x1d,0x1d,0x1e, 0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1f, 0x1f,0x1f,0x1f,0x1f +}; + +//----------------------------------------------------------------------------- + +const u8 cc85s1[256] = // convert 8-bit color to 5-bit color, shifted by 1 bit +{ + 0x00,0x00,0x00,0x00, 0x00,0x02,0x02,0x02, 0x02,0x02,0x02,0x02, 0x02,0x04,0x04,0x04, + 0x04,0x04,0x04,0x04, 0x04,0x06,0x06,0x06, 0x06,0x06,0x06,0x06, 0x06,0x08,0x08,0x08, + 0x08,0x08,0x08,0x08, 0x08,0x08,0x0a,0x0a, 0x0a,0x0a,0x0a,0x0a, 0x0a,0x0a,0x0c,0x0c, + 0x0c,0x0c,0x0c,0x0c, 0x0c,0x0c,0x0e,0x0e, 0x0e,0x0e,0x0e,0x0e, 0x0e,0x0e,0x10,0x10, + 0x10,0x10,0x10,0x10, 0x10,0x10,0x12,0x12, 0x12,0x12,0x12,0x12, 0x12,0x12,0x12,0x14, + 0x14,0x14,0x14,0x14, 0x14,0x14,0x14,0x16, 0x16,0x16,0x16,0x16, 0x16,0x16,0x16,0x18, + 0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x1a, 0x1a,0x1a,0x1a,0x1a, 0x1a,0x1a,0x1a,0x1a, + 0x1c,0x1c,0x1c,0x1c, 0x1c,0x1c,0x1c,0x1c, 0x1e,0x1e,0x1e,0x1e, 0x1e,0x1e,0x1e,0x1e, + 0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20, 0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22, + 0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24, 0x24,0x26,0x26,0x26, 0x26,0x26,0x26,0x26, + 0x26,0x28,0x28,0x28, 0x28,0x28,0x28,0x28, 0x28,0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,0x2a, + 0x2a,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2c,0x2c, 0x2c,0x2c,0x2e,0x2e, 0x2e,0x2e,0x2e,0x2e, + 0x2e,0x2e,0x30,0x30, 0x30,0x30,0x30,0x30, 0x30,0x30,0x32,0x32, 0x32,0x32,0x32,0x32, + 0x32,0x32,0x34,0x34, 0x34,0x34,0x34,0x34, 0x34,0x34,0x36,0x36, 0x36,0x36,0x36,0x36, + 0x36,0x36,0x36,0x38, 0x38,0x38,0x38,0x38, 0x38,0x38,0x38,0x3a, 0x3a,0x3a,0x3a,0x3a, + 0x3a,0x3a,0x3a,0x3c, 0x3c,0x3c,0x3c,0x3c, 0x3c,0x3c,0x3c,0x3e, 0x3e,0x3e,0x3e,0x3e +}; + +//----------------------------------------------------------------------------- + +const u8 cc86[256] = // convert 8-bit color to 6-bit color +{ + 0x00,0x00,0x00,0x01, 0x01,0x01,0x01,0x02, 0x02,0x02,0x02,0x03, 0x03,0x03,0x03,0x04, + 0x04,0x04,0x04,0x05, 0x05,0x05,0x05,0x06, 0x06,0x06,0x06,0x07, 0x07,0x07,0x07,0x08, + 0x08,0x08,0x08,0x09, 0x09,0x09,0x09,0x0a, 0x0a,0x0a,0x0a,0x0b, 0x0b,0x0b,0x0b,0x0c, + 0x0c,0x0c,0x0c,0x0d, 0x0d,0x0d,0x0d,0x0e, 0x0e,0x0e,0x0e,0x0f, 0x0f,0x0f,0x0f,0x10, + 0x10,0x10,0x10,0x11, 0x11,0x11,0x11,0x12, 0x12,0x12,0x12,0x13, 0x13,0x13,0x13,0x14, + 0x14,0x14,0x14,0x15, 0x15,0x15,0x15,0x15, 0x16,0x16,0x16,0x16, 0x17,0x17,0x17,0x17, + 0x18,0x18,0x18,0x18, 0x19,0x19,0x19,0x19, 0x1a,0x1a,0x1a,0x1a, 0x1b,0x1b,0x1b,0x1b, + 0x1c,0x1c,0x1c,0x1c, 0x1d,0x1d,0x1d,0x1d, 0x1e,0x1e,0x1e,0x1e, 0x1f,0x1f,0x1f,0x1f, + 0x20,0x20,0x20,0x20, 0x21,0x21,0x21,0x21, 0x22,0x22,0x22,0x22, 0x23,0x23,0x23,0x23, + 0x24,0x24,0x24,0x24, 0x25,0x25,0x25,0x25, 0x26,0x26,0x26,0x26, 0x27,0x27,0x27,0x27, + 0x28,0x28,0x28,0x28, 0x29,0x29,0x29,0x29, 0x2a,0x2a,0x2a,0x2a, 0x2a,0x2b,0x2b,0x2b, + 0x2b,0x2c,0x2c,0x2c, 0x2c,0x2d,0x2d,0x2d, 0x2d,0x2e,0x2e,0x2e, 0x2e,0x2f,0x2f,0x2f, + 0x2f,0x30,0x30,0x30, 0x30,0x31,0x31,0x31, 0x31,0x32,0x32,0x32, 0x32,0x33,0x33,0x33, + 0x33,0x34,0x34,0x34, 0x34,0x35,0x35,0x35, 0x35,0x36,0x36,0x36, 0x36,0x37,0x37,0x37, + 0x37,0x38,0x38,0x38, 0x38,0x39,0x39,0x39, 0x39,0x3a,0x3a,0x3a, 0x3a,0x3b,0x3b,0x3b, + 0x3b,0x3c,0x3c,0x3c, 0x3c,0x3d,0x3d,0x3d, 0x3d,0x3e,0x3e,0x3e, 0x3e,0x3f,0x3f,0x3f +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetIntModeName ( IntMode_t mode ) +{ + switch(mode) + { + case IMD_UNSET: return "-"; + + case IMD_BE0: return "BE"; + case IMD_BE1: return "BE1"; + case IMD_BE2: return "BE2"; + case IMD_BE3: return "BE3"; + case IMD_BE4: return "BE4"; + case IMD_BE5: return "BE5"; + case IMD_BE6: return "BE6"; + case IMD_BE7: return "BE7"; + case IMD_BE8: return "BE8"; + + case IMD_LE0: return "LE"; + case IMD_LE1: return "LE1"; + case IMD_LE2: return "LE2"; + case IMD_LE3: return "LE3"; + case IMD_LE4: return "LE4"; + case IMD_LE5: return "LE5"; + case IMD_LE6: return "LE6"; + case IMD_LE7: return "LE7"; + case IMD_LE8: return "LE8"; + + default: return "?"; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-numeric.h b/project/src/lib-numeric.h new file mode 100644 index 0000000..7fc84df --- /dev/null +++ b/project/src/lib-numeric.h @@ -0,0 +1,2337 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_NUMERIC_H +#define SZS_LIB_NUMERIC_H 1 + +#define _GNU_SOURCE 1 + +#include "dclib-numeric.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// defines /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define EXPAND8(a) ( (a) + 7 & ~(u32)7 ) + +/////////////////////////////////////////////////////////////////////////////// + +#define MAX_LOOP_DEPTH 10 // max loop depth +#define MAX_FUNCTION_DEPTH 10 // max macro depth +#define MAX_SOURCE_DEPTH 50 // max open source files/macros + +/////////////////////////////////////////////////////////////////////////////// + +struct Var_t; +struct FuncParam_t; +struct ScanMacro_t; +struct ScanFile_t; +struct ScanInfo_t; + +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + RTL_N_NONE = 0, + RTL_N_HIDE, + RTL_N_SHOW, + RTL_N_SWAP, + + RTL_N_M_MODE = 0x0ff, // mask for modes above + + //--- flags + + RTL_N_F_HEX = 0x0100, // use hex codes for standard track files + RTL_N_F_WII = 0x0200, // Prefix 'Wii' on Wii tracks + + RTL_N_F_RED1 = 0x001000, // color console with 'red1' (alias 'red') + RTL_N_F_RED2 = 0x002000, // color console with 'red2' + RTL_N_F_RED3 = 0x004000, // color console with 'red3' + RTL_N_F_RED4 = 0x008000, // color console with 'red4' + RTL_N_F_YELLOW = 0x010000, // color console with 'yellow' + RTL_N_F_GREEN = 0x020000, // color console with 'green' + RTL_N_F_BLUE1 = 0x040000, // color console with 'blue1' (alias 'blue') + RTL_N_F_BLUE2 = 0x080000, // color console with 'blue2' + RTL_N_F_WHITE = 0x100000, // color console with 'white' + RTL_N_F_CLEAR = 0x200000, // color console with 'clear' +}; + +/////////////////////////////////////////////////////////////////////////////// +// slots + +#define SLOT_WAIT 0x43 // online: no track selected yet +#define SLOT_RANDOM 0xff // online: random track selected + +#define LE_SLOT_RND_ALL 0x3e +#define LE_SLOT_RND_ORIGINAL 0x3f +#define LE_SLOT_RND_CUSTOM 0x40 +#define LE_SLOT_RND_NEW 0x41 + +//----------------------------------------------------------------------------- +// bits for [[le_flags_t]] + +#define LETF_NEW 0x01 // new track +#define LETF_RND_HEAD 0x02 // first track of random group +#define LETF_RND_GROUP 0x04 // member of random group, but not first + // members are not added to cups +#define LETF_ALIAS 0x08 // use TrackByAliasLE(prop,music) + // as alias for another track + +#define LETF__ALL 0x0f + +//----------------------------------------------------------------------------- +// [[lpar_mode_t]] + +typedef enum lpar_mode_t +{ + LPM_PRODUCTIVE, + LPM_TESTING, + LPM_EXPERIMENTAL, + LPM_FORCE_AUTOMATIC, + LPM_AUTOMATIC, + + LPM__DEFAULT = LPM_AUTOMATIC +} +lpar_mode_t; + +//----------------------------------------------------------------------------- +// [[kmp_gmode_t]] settings for KMP GAME_MODE + +typedef enum kmp_gmode_t +{ + KMP_GMODE_BALLOON, // balloon battle + KMP_GMODE_COIN, // coin runners + KMP_GMODE_VERSUS, // versus + KMP_GMODE_ITEMRAIN, // itemrain + KMP_GMODE__N, + + KMP_GMODE_AUTO = 14, // special value: don't simulate a mode behaviour + KMP_GMODE_STD = 15, // special value: simulate standard behaviour +} +kmp_gmode_t; + +//----------------------------------------------------------------------------- +// [[kmp_engine_t]] + +typedef enum kmp_engine_t +{ + ENGINE_BATTLE, + ENGINE_50, + ENGINE_100, + ENGINE_150, + ENGINE_200, + ENGINE_150M, + ENGINE_200M, + ENGINE__N, + + ENGM_BATTLE = 1 << ENGINE_BATTLE, + ENGM_50 = 1 << ENGINE_50, + ENGM_100 = 1 << ENGINE_100, + ENGM_150 = 1 << ENGINE_150, + ENGM_200 = 1 << ENGINE_200, + ENGM_150M = 1 << ENGINE_150M, + ENGM_200M = 1 << ENGINE_200M, + + ENGM__ALL = ( 1 << ENGINE__N ) - 1, +} +kmp_engine_t; + +//----------------------------------------------------------------------------- +// [[lex_oomode_t]] settings for LEX/TEST @OFFLINE_ONLINE + +typedef enum lex_oomode_t +{ + LEX_OO_AUTO, // automatic select (default) + LEX_OO_OFFLINE, // force offline + LEX_OO_ONLINE, // force online +} +lex_oomode_t; + +//----------------------------------------------------------------------------- +// [[lex_gmode_t]] settings for LEX/TEST @GAME_MODE + +typedef enum lex_gmode_t +{ + LEX_GMODE_AUTO, // automatic select (default) + LEX_GMODE_STD, // standard node (no LE-CODE executor) + LEX_GMODE__BASE, // base index for KMP_GMODE_* values + + // force settings ... + LEX_GMODE_BALLOON = LEX_GMODE__BASE + KMP_GMODE_BALLOON, + LEX_GMODE_COIN = LEX_GMODE__BASE + KMP_GMODE_COIN, + LEX_GMODE_VERSUS = LEX_GMODE__BASE + KMP_GMODE_VERSUS, + LEX_GMODE_ITEMRAIN = LEX_GMODE__BASE + KMP_GMODE_ITEMRAIN, +} +lex_gmode_t; + +//----------------------------------------------------------------------------- +// [[lex_engine_t]] settings for LEX/TEST @ENGINE + +typedef enum lex_engine_t +{ + LEX_ENGINE_AUTO, // automatic select (default) + LEX_ENGINE__BASE, // base index for KMP_ENGINE_* values + + // force settings ... + LEX_ENGINE_BATTLE = LEX_ENGINE__BASE + ENGINE_BATTLE, + LEX_ENGINE_50 = LEX_ENGINE__BASE + ENGINE_50, + LEX_ENGINE_100 = LEX_ENGINE__BASE + ENGINE_100, + LEX_ENGINE_150 = LEX_ENGINE__BASE + ENGINE_150, + LEX_ENGINE_200 = LEX_ENGINE__BASE + ENGINE_200, + LEX_ENGINE_150M = LEX_ENGINE__BASE + ENGINE_150M, + LEX_ENGINE_200M = LEX_ENGINE__BASE + ENGINE_200M, +} +lex_engine_t; + +//----------------------------------------------------------------------------- +// [[chat_vehicle_t]] bits for vehicle settings for CHAT-MODE + +typedef enum chat_vehicle_t +{ + CHATVEH_SMALL = 0x01, + CHATVEH_MEDIUM = 0x02, + CHATVEH_LARGE = 0x04, + CHATVEH_ANY_SIZE = CHATVEH_SMALL | CHATVEH_MEDIUM | CHATVEH_LARGE, + + CHATVEH_KART = 0x08, + CHATVEH_OUT_BIKE = 0x10, + CHATVEH_IN_BIKE = 0x20, + CHATVEH_BIKE = CHATVEH_OUT_BIKE | CHATVEH_IN_BIKE, + CHATVEH_ANY_TYPE = CHATVEH_KART | CHATVEH_BIKE, + + CHATVEH_ANY = CHATVEH_ANY_SIZE | CHATVEH_ANY_TYPE, +} +chat_vehicle_t; + +//----------------------------------------------------------------------------- +// [[chat_mode_t]] settings for CHAT-MODE + +typedef enum chat_mode_t +{ + //-- 0x0000 - 0x01ff : simple values + + CHATMD_OFF, + + CHATMD_ANY_TRACK, + CHATMD_TRACK_BY_HOST, + + CHATMD_ANY_VEHICLE, + CHATMD_KARTS_ONLY, + CHATMD_BIKES_ONLY, + + CHATMD_RESET_ENGINE, + CHATMD_USE_ENGINE_1, + CHATMD_USE_ENGINE_2, + CHATMD_USE_ENGINE_3, + + CHATMD_RESET, + + CHATMD_BLOCK_CLEAR, + CHATMD_BLOCK_DISABLE, + CHATMD_BLOCK_ENABLE, + + CHATMD__N, + + + //-- 0x0200 - 0x03ff (-0x05ff): special range for n_races + + CHATMD_MAX_RACE = 512, + CHATMD_N_RACE_1 = 0x0200, + CHATMD_N_RACE_MAX = CHATMD_N_RACE_1 + CHATMD_MAX_RACE-1, + + + //-- 0x0600 - 0x063f (-0x6ff): special range for vehicles + + CHATMD_VEHICLE_BEG = 0x0600, + CHATMD_VEHICLE_END = CHATMD_VEHICLE_BEG + CHATVEH_ANY, +} +__attribute__ ((packed)) chat_mode_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptAlignU8 ( ccp arg ); +int ScanOptAlignPACK ( ccp arg ); +int ScanOptAlignBRRES ( ccp arg ); +int ScanOptAlignBREFF ( ccp arg ); +int ScanOptAlignBREFT ( ccp arg ); +int ScanOptAlign ( ccp arg ); +int ScanOptMaxFileSize ( ccp arg ); +int ScanOptEpsilon ( ccp arg ); +int ScanOptNMipmaps ( ccp arg ); +int ScanOptMaxMipmaps ( ccp arg ); +int ScanOptMipmapSize ( ccp arg ); +int ScanOptPtDir ( ccp arg ); +int ScanOptRecurse ( ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan num/range /////////////// +/////////////////////////////////////////////////////////////////////////////// + +double strtod_comma ( const char *nptr, char **endptr ); + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNumU32 +( + ccp arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..1) + u32 * p_num, // return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +); + +char * ScanRangeU32 +( + ccp arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..2) + u32 * p_n1, // first return value (valid pointer) + u32 * p_n2, // second return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +); + +unsigned long int strtoul16 +( + const u16 * ptr16, + u16 ** end16, + int base +); + +u16 * skip_ctrl16 ( const u16 * ptr ); + +u16 * ScanNum16U32 +( + const u16 * arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..1) + u32 * p_num, // return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +); + +u16 * ScanRange16U32 +( + const u16 * arg, // text to scan (valid pointer) + u32 * p_stat, // NULL or return status: + // number of scanned numbers (0..2) + u32 * p_n1, // first return value (valid pointer) + u32 * p_n2, // second return value (valid pointer) + u32 min, // min allowed value (fix num) + u32 max // max allowed value (fix num) +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// vector calculations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +double LengthF +( + const float *a, // A vector (x,y,z) + const float *b // B vector (x,y,z) +); + +double Length2F +( + const float *a, // A vector (x,y,z) + const float *b // B vector (x,y,z) +); + +double LengthD +( + const double *a, // A vector (x,y,z) + const double *b // B vector (x,y,z) +); + +double Length2D +( + const double *a, // A vector (x,y,z) + const double *b // B vector (x,y,z) +); + +double AngleVector +( + const float * prev, // vector (x,y,z) of previous point + const float * point, // vector (x,y,z) of current point + const float * next, // vector (x,y,z) of next point + bool return_degree // true: return in normed degree; false: radiants +); + +void WidthAndDirection +( + double * r_width, // if not NULL: return width here + double * r_direction, // if not NULL: return direction here + bool return_degree, // true: return in normed degree; false: radiants + const float * a, // A point (x,y) + const float * b // B point (x,y) +); + +double CalcDirection2F +( + const float * a, // A point (x,y) + const float * b // B point (x,y) +); + +float3 CalcDirection3F +( + const float * a, // A point (x,y,z) + const float * b // B point (x,y,z) +); + +double3 CalcDirection3D +( + const double3 * a, // A point (x,y,z) + const double3 * b // B point (x,y,z) +); + +void Scale +( + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *scale, // NULL or scaling vector + double3 *pt, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +); + +void Rotate +( + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *rot, // rotation vector + double3 *pt, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +); + +void Translate +( + const double3 *trans, // NULL or translation vector + double3 *pt, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +); + +void AxisRotate +( + const double3 *a1, // first axis point + const double3 *a2, // second axis point + double degree, // degree of rotation + double3 *pt, // pointer to first point to rotate + uint pt_delta, // delta in bytes to next point + uint n_pt // number of points to rotate +); + +double Degree // output: -180.00 .. +180.00 +( + double rad // radians +); + +float NormDegreeF // output: -180.00 .. +180.00 +( + float deg +); + +void MinMax3 +( + double3 *min, // valid pointer to initialized min value + double3 *max, // valid pointer to initialized max value + const double3 *val, // valid pointer to source list + int n_val // if >0: number of elements in 'val' +); + +void UnitZero3 ( double3 *dest, const double3 *src ); + +double CalcNormals +( + // return length of p1..p2 + + double3 *res_norm, // return 3 normals here + const double3 *p1, // first point + const double3 *p2, // second point + const double3 *dir, // NULL or helper point for normal[1] + double r // radius == length of norm[0]..norm[2] +); + +bool OverlapCubeTri +( + double3 *cube_mid, // middle of the cube + double cube_width, // width of the cube + double3 *pt1, // first point of triangle + double3 *pt2, // second point of triangle + double3 *pt3 // third point of triangle +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// triangles /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[tri_metrics_t]] + +typedef struct tri_metrics_t +{ + double3 p1, p2, p3; // base points + double l1, l2, l3; // side lengths + double area; // areas of triangle + double h1, h2, h3; // heights + double min_ht; // minimal height + double max_ht; // maximal height +} +tri_metrics_t; + +//----------------------------------------------------------------------------- + +void CalcTriMetricsByPoints +( + tri_metrics_t *met, // result + const double3 *p1, // first point + const double3 *p2, // second point + const double3 *p3 // third point +); + +void CalcTriMetrics ( tri_metrics_t *met ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// octahedron_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[octahedron_t]] + +typedef struct octahedron_t +{ + union + { + double3 p[6]; + struct + { + double3 x1; // left point + double3 x2; // right point + double3 y1; // bottom point + double3 y2; // top point + double3 z1; // far point + double3 z2; // near point + }; + }; + +} octahedron_t; + +//----------------------------------------------------------------------------- + +void CreateRegularOctahedron +( + octahedron_t *oct, // valid pointer, initialize + double radius, // radius of octahedron + const double3 *rot, // not NULL: rotate octahedron + const double3 *shift // not NULL: shift octahedron +); + +//----------------------------------------------------------------------------- + +void CreateRegularOctahedronF +( + octahedron_t *oct, // valid pointer, initialize + double radius, // radius of octahedron + const float *rot, // not NULL: 3D vector, rotate octahedron + const float *shift // not NULL: 3D vector, shift octahedron +); + +//----------------------------------------------------------------------------- + +void CreateOctahedron +( + octahedron_t *oct, // valid pointer, initialize + const double3 *radius, // radius of octahedron + const double3 *rot, // not NULL: rotate octahedron + const double3 *shift // not NULL: shift octahedron +); + +//----------------------------------------------------------------------------- + +void ScaleOctahedron +( + octahedron_t *oct, // valid octahedron data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *scale // not NULL: scale octahedron +); + +//----------------------------------------------------------------------------- + +void RotateOctahedron +( + octahedron_t *oct, // valid octahedron data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *rot // not NULL: rotate octahedron +); + +//----------------------------------------------------------------------------- + +void ShiftOctahedron +( + octahedron_t *oct, // valid octahedron data + const double3 *shift // not NULL: shift octahedron +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cuboid_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[cuboid_t]] + +typedef struct cuboid_t +{ + union + { + double3 p[8]; + struct + { + double3 p000; + double3 p001; + double3 p010; + double3 p011; + double3 p100; + double3 p101; + double3 p110; + double3 p111; + }; + }; + +} cuboid_t; + +//----------------------------------------------------------------------------- + +void CreateCube +( + cuboid_t *cub, // valid pointer, initialize + double width, // width of cube + const double3 *rot, // not NULL: rotate cube + const double3 *shift // not NULL: shift cube +); + +//----------------------------------------------------------------------------- + +void CreateCubeF +( + cuboid_t *cub, // valid pointer, initialize + double width, // width of cube + const float *rot, // not NULL: 3D vector, rotate cube + const float *shift // not NULL: 3D vector, shift cube +); + +//----------------------------------------------------------------------------- + +void CreateCuboid +( + cuboid_t *cub, // valid pointer, initialize + const double3 *size, // base size of cuboid + const double3 *rot, // not NULL: rotate cuboid + const double3 *shift // not NULL: shift cuboid +); + +//----------------------------------------------------------------------------- + +void CreateCuboidF +( + cuboid_t *cub, // valid pointer, initialize + const float *size, // base size of cuboid + const float *rot, // not NULL: rotate cuboid + const float *shift // not NULL: shift cuboid +); + +//----------------------------------------------------------------------------- + +void ScaleCuboid +( + cuboid_t *cub, // valid cuboid data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *scale // not NULL: scale cuboid +); + +//----------------------------------------------------------------------------- + +void RotateCuboid +( + cuboid_t *cub, // valid cuboid data + const double3 *origin, // origin of rotation, NULL = v(0,0,0) + const double3 *rot // not NULL: rotate cuboid +); + +//----------------------------------------------------------------------------- + +void ShiftCuboid +( + cuboid_t *cub, // valid cuboid data + const double3 *shift // not NULL: shift cuboid +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// index lists /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[IndexList_t]] + +typedef struct IndexList_t +{ + ccp * list; // list of names + uint used; // used elements of 'list' + uint size; // alloced elements of 'list' + + ccp prefix; // prefix for automatic named variables + uint next_index; // next index for autmatic named variables + +} IndexList_t; + +//----------------------------------------------------------------------------- + +void InitializeIL ( IndexList_t * il, ccp prefix ); +void ResetIL ( IndexList_t * il ); + +void FillIL +( + IndexList_t * il, // valid index list + uint min_fill // fill minimum # elements +); + +void DefineIL +( + IndexList_t * il, // valid index list + uint index, // index of object + ccp name, // name of object + bool move_name // true: 'name' is alloced -> move it +); + +bool RemoveIL +( + IndexList_t * il, // valid index list + uint index // index of object +); + +ccp GetNameIL +( + IndexList_t * il, // valid index list + uint index // index of object +); + +void PrintNameIL +( + FILE * f, // output files + IndexList_t * il, // valid index list + uint index, // index of object + uint align, // align end of name to character pos + uint fw, // field width of name + uint indent // indention of continuation line +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define MAX_FUNC_PARAM 1000 + +typedef enumError (*call_func_t) +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const struct FuncParam_t + * fpar // pointer to user parameter +); + +//----------------------------------------------------------------------------- +// [[FuncParam_t]] + +typedef struct FuncParam_t +{ + bool need_name; // true: needs a name as first param + s16 min_param; // minimum parameter + s16 max_param; // maximum parameter + u16 fform_id; // function class, based on file_format_t + union + { + s32 user_id; // user defined id + struct ScanMacro_t + * macro; // NULL or related macro to call + }; + call_func_t func; // function to call + +} FuncParam_t; + +extern FuncParam_t FuncDollar; + +//----------------------------------------------------------------------------- +// [[FuncTable_t]] + +typedef struct FuncTable_t +{ + //--- function def + s32 min_param; // Minimum number of parameters, + // <0: first param is a var name + s32 max_param; // Maximum number of parameters + ccp name; // Name of function / If NULL: info only + call_func_t func; // Function to call / If NULL: end of list + int user_id; // User defined id + + //--- function info + + ccp result; // Function result info + ccp syntax; // Syntax of function // If NULL: no info + ccp info; // Description + +} FuncTable_t; + +//----------------------------------------------------------------------------- + +void DefineDefaultParserFunc(); + +void DefineParserFuncTab +( + const FuncTable_t *ftab, // pointer to a definittion table + // terminated with member 'func == 0' + u16 fform_id // function class, based on file_format_t +); + +FuncParam_t * DefineParserFunc +( + ccp name, // name of function + s16 min_param, // minimum parameter + s16 max_param, // maximum parameter + u16 fform_id, // function class, based on file_format_t + call_func_t func, // function to call + int user_id // user defined id +); + +const FuncParam_t * GetParserFunc +( + ccp name, // name to search + struct ScanInfo_t *si, // not NULL: print warning if not found + FuncParam_t *temp_par // not NULL: search user defined functions, + // and use this to create the function result +); + +/////////////////////////////////////////////////////////////////////////////// +// [[FuncInfo_t]] + +typedef struct FuncInfo_t +{ + ccp result; // function result info + ccp info; // description + +} FuncInfo_t; + +struct search_filter_t; + +//----------------------------------------------------------------------------- + +FuncInfo_t * DefineParserFuncInfo +( + ccp result, // function result info + ccp syntax, // syntax of function + ccp info // description +); + +void DumpParserFuncInfo +( + FILE * f, // destination file + uint indent, // indention + bool print_header, // true: print table header + int long_count, // >0: print description too + struct search_filter_t * filter // NULL or filter data +); + +//----------------------------------------------------------------------------- + +struct search_filter_t * SetupParserFuncFilter(); +void ParserFuncFilter ( struct search_filter_t *filter_data, ccp arg, int mode ); + +void ExportTrackInfo ( FILE * f ); +void ExportParserFuncMakedoc ( FILE * f, ccp index ); +enumError ExportHelper ( ccp func_mode ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// variables & maps /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[VarMode_t]] [[VarModeHelpers_t]] + +#define VARNAME_SIZE 100 +#define STACK_SIZE 100 + +typedef enum VarMode_t +{ + VAR_UNSET, + VAR_INT, + VAR_DOUBLE, + VAR_VECTOR, + VAR_STRING, +} +__attribute__ ((packed)) VarMode_t; + +typedef enum VarModeHelpers_t +{ + VAR_X = VAR_VECTOR+1, + VAR_Y, + VAR_Z +} +__attribute__ ((packed)) VarModeHelpers_t; + +//----------------------------------------------------------------------------- + +static inline bool isScalarVM ( VarMode_t mode ) + { return mode <= VAR_DOUBLE; } + +static inline bool isNumericVM ( VarMode_t mode ) + { return mode >= VAR_INT || mode <= VAR_VECTOR; } + +/////////////////////////////////////////////////////////////////////////////// +// [[Var_t]] + +typedef struct Var_t +{ + ccp name; // name of variable, alloced + VarMode_t mode; // mode of variable + IntMode_t int_mode; // integer mode for special purpose + + union + { + s64 i; // int64 value + double d; // double value + double v[3]; // double vector + struct + { + double x; // double components + double y; + double z; + }; + double3 d3; // double3 value for references + struct + { + uint str_len; // used length of 'str' (support of NULL bytes) + uint str_size; // alloced size of 'str', NULL term is not counting! + char *str; // NULL or string (alloced) + }; + + // for internal usage + FuncParam_t func_param; + FuncInfo_t func_info; + struct ScanMacro_t * macro; + }; + +} Var_t; + +extern Var_t var_unset; + +//----------------------------------------------------------------------------- + +static void inline InitializeV ( Var_t * var ) +{ + DASSERT(var); + memset(var,0,sizeof(*var)); +} + +#define DEFINE_VAR(v) Var_t v = {0}; + +//----------------------------------------------------------------------------- + +static void inline ResetV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode == VAR_STRING ) + FREE(var->str); + var->mode = VAR_UNSET; + var->int_mode = IMD_UNSET; +} + +//----------------------------------------------------------------------------- + +static void inline FreeV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode == VAR_STRING ) + { + FREE(var->str); + var->mode = VAR_UNSET; + var->int_mode = IMD_UNSET; + } +} + +//----------------------------------------------------------------------------- + +static void inline MarkFreeV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode == VAR_STRING ) + var->mode = VAR_UNSET; +} + +//----------------------------------------------------------------------------- + +// move data only (exclude name); src can be NULL or ==dest +void MoveDataV ( Var_t *dest, Var_t * src ); +void MoveDataInitV ( Var_t *dest, Var_t * src ); + +// move complete including name); src can be NULL or ==dest +void MoveAllV ( Var_t *dest, Var_t * src ); +void MoveAllInitV ( Var_t *dest, Var_t * src ); + +/////////////////////////////////////////////////////////////////////////////// +// [[VarMap_t]] + +typedef struct VarMap_t +{ + Var_t * list; // pointer to the item list + uint used; // number of used elements of 'list' + uint size; // number of allocated elements of 'list' + +} VarMap_t; + +//----------------------------------------------------------------------------- + +void DefineParserVars ( VarMap_t * vm ); +void DefineMkwVars ( VarMap_t * vm ); +const VarMap_t * SetupParserVars(); + +enumError DumpSymbols ( const VarMap_t * vm ); +enumError ListParserFunctions(); +enumError ParserCalc ( const VarMap_t * predef ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan text /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[TextCommand_t]] + +typedef enum TextCommand_t +{ + TCMD_NONE = 0, // definitiv NULL + + TCMD_ECHO, + TCMD_WARN, + + TCMD_RESET_PRIVATE, + TCMD_RESET_LOCAL, + TCMD_RESET_GLOBAL, + + TCMD_DUMP_VAR, + TCMD_DUMP_PRIVATE, + TCMD_DUMP_LOCAL, + TCMD_DUMP_GLOBAL, + TCMD_DUMP_CONST, + TCMD_DUMP_PREDEF, + + TCMD_REVISION, + TCMD_SZS_MODIFIER, + TCMD_SETUP_RANDOM, + + TCMD_PDEF, //+++ 'TCMD_LDEF* - TCMD_PDEF*' is constant for all '*' + TCMD_PDEF_I, // + TCMD_PDEF_F, // + TCMD_PDEF_S, // + TCMD_PDEF_V, // + TCMD_PDEF_X, // //+++ _X _Y and _Z are well sorted + TCMD_PDEF_Y, // // + TCMD_PDEF_Z, // //--- + TCMD_PDEF_ENUM, // + TCMD_PDEF_SHIFT, //--- + + TCMD_LDEF, //+++ 'TCMD_PDEF* - TCMD_LDEF*' is constant for all '*' + TCMD_LDEF_I, // + TCMD_LDEF_F, // + TCMD_LDEF_S, // + TCMD_LDEF_V, // + TCMD_LDEF_X, // //+++ _X _Y and _Z are well sorted + TCMD_LDEF_Y, // // + TCMD_LDEF_Z, // //--- + TCMD_LDEF_ENUM, // + TCMD_LDEF_SHIFT, //--- + + TCMD_GDEF, //+++ 'TCMD_GDEF* - TCMD_LDEF*' is constant for all '*' + TCMD_GDEF_I, // + TCMD_GDEF_F, // + TCMD_GDEF_S, // + TCMD_GDEF_V, // + TCMD_GDEF_X, // //+++ _X _Y and _Z are well sorted + TCMD_GDEF_Y, // // + TCMD_GDEF_Z, // //--- + TCMD_GDEF_ENUM, // + TCMD_GDEF_SHIFT, //--- + + TCMD_IF, //+++ first IF related command + TCMD_ELIF, // + TCMD_ELSE, // + TCMD_ENDIF, //--- last IF related command + + TCMD_DOIF, + + TCMD_LOOP, //+++ LOOP commands, same order as TCMD_END* & loop_name[] + TCMD_REPEAT, // + TCMD_FOR, // + TCMD_FOREACH, // + TCMD_WHILE, //--- + + TCMD_ENDLOOP, //+++ END-LOOP commands, same order as TCMD_LOOP... + TCMD_ENDREPEAT, // & loop_name[] + TCMD_ENDFOR, // + TCMD_ENDFOREACH, // + TCMD_ENDWHILE, //--- + + TCMD_BREAK, // terminate a loop + TCMD_CONTINUE, // continue a loop + + TCMD_EXEC, + TCMD_INCLUDE, + TCMD_RETURN, + TCMD_EXIT, + TCMD_ASSERT, + + TCMD_MACRO, //+++ same order as TCMD_END* + TCMD_FUNCTION, //--- + TCMD_ENDMACRO, //+++ same order as TCMD_* without END + TCMD_ENDFUNCTION, //--- + + TCMD_CALL, + TCMD_PARAMETERS, + + TCMD__N + +} TextCommand_t; + +//----------------------------------------------------------------------------- +// [[ScanLoop_t]] + +typedef struct ScanLoop_t +{ + int tcmd_end; // closing TCMD_* + ccp ptr; // file position of loop start + uint line; // line number on loop begin + int if_level; // number of active IF..ENDIF on loop begin + + int count_val; // internal loop counter 0.. + int end_val; // end value for @REPEAT + @FOR + int step_val; // step_val for @FOR + ccp varname; // variable name for @FOR + @FOREACH, alloced + ccp expr_ptr; // expression pointer + uint expr_line; // expression line + +} ScanLoop_t; + +//----------------------------------------------------------------------------- +// [[ScanMacro_t]] + +typedef struct ScanMacro_t +{ + ccp data; // begin of text + uint data_size; // size of data + bool data_alloced; // true if 'data' alloced + uint line0; // line number of first line + ccp src_name; // name of source file, alloced + bool is_function; // true: defined as function + Var_t * map; // pointer to map entry + +} ScanMacro_t; + +//----------------------------------------------------------------------------- +// [[ScanFile_t]] + +typedef struct ScanFile_t // includes and macros +{ + //--- scanning infos + + struct ScanInfo_t * si; // base scan info + ScanMacro_t * sm; // not NULL: pointer to macro + + ccp data; // begin of text + bool data_alloced; // true if 'data' alloced + ccp ptr; // current text position + ccp end; // end of text + ccp prev_ptr; // 'ptr' before scanning an object + uint line; // current line number + uint line_err; // number of line errors + ccp name; // name or file for warnings + bool name_alloced; // true if 'name' alloced + int if_level; // number of active IF..ENDIF + struct ScanFile_t * next; // next file for RETURN + + //--- loop control + + int loop_level; // number of active loops + ScanLoop_t loop[MAX_LOOP_DEPTH]; // loop data + + //--- some options + + int revision; // current revision number + int szs_modifier; // >0: szs_modifier mode enabled + int disable_comma; // >0: disable comma in floats + int expr_depth; // level of expression depth + + //--- variables + + VarMap_t pvar; // private variables + const ScanMacro_t * active_macro; // active macro or function + + //--- parameter stack + + Var_t param[STACK_SIZE]; // parameter stack + int param_used; // number of used parameters + +} ScanFile_t; + +extern ScanFile_t empty_scan_file; + +//----------------------------------------------------------------------------- +// [[ScanInfo_t]] + +typedef struct ScanInfo_t +{ + //--- init data + + ccp init_data; // begin of text + uint init_data_size; // end of text + ccp init_name; // name or file for warnings + int init_revision; // revision number at start + + //--- current file + + uint n_files; // number of open files and macros + ScanFile_t * cur_file; // pointer to current file, never NULL + VarMap_t macro; // macro map + + //--- scanning infos + + uint total_err; // total number of errors + int no_warn; // >0: suppress warnings + int no_calc; // >0: suppress calculations and function calls + int fast_scan; // >0: ignore @commands + int force_hex; // >0: force hex for non prefixed integers + int point_is_null; // >0: a single point is read as Null + int float_div; // >0: floating point division + #if HAVE_PRINT + int debug; // >0: print debugging infos + #endif + + //--- variables + + VarMap_t * param; // NULL or parameters, not cleared + VarMap_t lvar; // local variables + VarMap_t gvar; // global variables + const VarMap_t * predef; // NULL or predefined variables + Var_t last_result; // last macro/function result + int func_status; // second status of some parser functions + + //--- user info + + struct kcl_t * kcl; // for KCL scanning + struct kmp_t * kmp; // for KMP scanning + struct lex_t * lex; // for LEX scanning + struct mdl_t * mdl; // for MDL scanning + +} ScanInfo_t; + +//----------------------------------------------------------------------------- + +typedef enum ScanParamMode +{ + SPM_NONE, // ignore result + SPM_INC, // result has type int: increment value + + SPM_BOOL, // result has type bool + + SPM_U8, // result has type u8 + SPM_S8, // result has type s8 + SPM_U16, // result has type u16 + SPM_S16, // result has type s16 + SPM_U32, // result has type u32 + SPM_S32, // result has type s32 + + SPM_U16_BE, // result has type u16, stored as big endian + SPM_S16_BE, // result has type s16, stored as big endian + SPM_U32_BE, // result has type u32, stored as big endian + SPM_S32_BE, // result has type s32, stored as big endian + + SPM_INT, // result has type int + SPM_UINT, // result has type uint + SPM_FLOAT, // result has type float + SPM_FLOAT_BE, // result has type float and is stored as big endian + SPM_DOUBLE, // result has type double + + SPM_DOUBLE_X, // result has type double, use X on vector + SPM_DOUBLE_Y, // result has type double, use Y on vector + SPM_DOUBLE_Z, // result has type double, use Z on vector + + SPM_FLOAT3, // result has type float3 + SPM_FLOAT3_BE, // result has type float3 and is stored as big endian + + SPM_STRING, // result has type char* + SPM_VAR, // result has type Var_t + +} ScanParamMode; + +//----------------------------------------------------------------------------- +// [[ScanParam_t]] + +typedef struct ScanParam_t +{ + ccp name; // name of the parameter + ScanParamMode mode; // type of result + void *result; // pointer to result + uint min; // >1: scan at least 'min' values + uint max; // >min: scan maxixmal 'max' values + int *n_result; // not NULL: store number of read values + +} ScanParam_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// conversion helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool GetBoolV ( const Var_t * var ); +int GetIntV ( const Var_t * var ); +double GetDoubleV ( const Var_t * var ); +double GetXDoubleV ( const Var_t * var ); +double GetYDoubleV ( const Var_t * var ); +double GetZDoubleV ( const Var_t * var ); +double3 GetVectorV ( const Var_t * var ); +float3 GetVectorFV ( const Var_t * var ); +Var_t * GetStringV ( const Var_t * var, Var_t * temp ); // 'var' can be NULL + +Var_t * ToNoneV ( Var_t * var ); +Var_t * ToBoolV ( Var_t * var ); +Var_t * ToIntV ( Var_t * var ); +Var_t * ToDoubleV ( Var_t * var ); +Var_t * ToXDoubleV ( Var_t * var ); +Var_t * ToYDoubleV ( Var_t * var ); +Var_t * ToZDoubleV ( Var_t * var ); +Var_t * ToVectorV ( Var_t * var ); +Var_t * ToVectorXV ( Var_t * var ); +Var_t * ToVectorYV ( Var_t * var ); +Var_t * ToVectorZV ( Var_t * var ); +Var_t * ToStringV ( Var_t * var ); +Var_t * ToVarMode ( Var_t * var, TextCommand_t mode ); + +void AssignIntV ( Var_t * var, int val ); +void AssignDoubleV ( Var_t * var, double val ); +void AssignXDoubleV ( Var_t * var, double val ); +void AssignYDoubleV ( Var_t * var, double val ); +void AssignZDoubleV ( Var_t * var, double val ); +void AssignVectorV ( Var_t * var, const double3 * val ); +void AssignVectorV3 ( Var_t * var, double x, double y, double z ); +void AssignVar ( Var_t * dest, const Var_t * src ); +void AssignVarMode ( Var_t * dest, const Var_t * src, TextCommand_t mode ); + +// return the length of the string after assigning +uint AssignStringVV ( Var_t * var, const Var_t * src ); // 'src' can be NULL +uint AppendStringVV ( Var_t * var, const Var_t * src ); // 'src' can be NULL +uint AssignStringVS ( Var_t * var, ccp str, int len ); +uint AssignStringVS2 ( Var_t * var, ccp str1, int len1, ccp str2, int len2 ); +uint AppendStringVS ( Var_t * var, ccp str, int len ); +uint AppendStringVC ( Var_t * var, char ch, int repeat ); + +// scan STRINGS, but copy 'src' on other modes +enumError ScanValueV ( Var_t * dest, const Var_t * src, ccp name ); +enumError ScanExprV ( Var_t * dest, const Var_t * src, ccp name ); + +//----------------------------------------------------------------------------- + +static void inline SetModeStringV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode != VAR_STRING ) + { + var->str_len = var->str_size = 0; + var->str = 0; + var->mode = VAR_STRING; + var->int_mode = IMD_UNSET; + } +} + +//----------------------------------------------------------------------------- + +enumError ResultFloat +( + Var_t * dest, // valid destination var + const float * val, // pointer to N floats + uint n_val // number of floats in 'n' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// variables & maps: interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeVarMap +( + VarMap_t * vm // data structure to initialize +); + +//----------------------------------------------------------------------------- + +void ResetVarMap +( + VarMap_t * vm // data structure to reset +); + +//----------------------------------------------------------------------------- + +void ClearVarMap +( + VarMap_t * vm // data structure to resewt +); + +//----------------------------------------------------------------------------- + +void CopyVarMap +( + VarMap_t * dest, // destination var map + bool init_dest, // true: initialialize 'dest' first + const VarMap_t * src // source var map +); + +//----------------------------------------------------------------------------- + +void MoveVarMap +( + VarMap_t * dest, // destination var map + bool init_dest, // true: initialialize 'dest' first + VarMap_t * src // source var map -> will be resetted +); + +//----------------------------------------------------------------------------- + +int FindVarMapIndex +( + const VarMap_t * vm, // valid variable map + ccp varname, // variable name + int not_found_value // return value if not found +); + +//----------------------------------------------------------------------------- + +bool RemoveVarMap +( + // returns 'true' if var deleted + + VarMap_t * vm, // valid variable map + ccp varname // variable name +); + +//----------------------------------------------------------------------------- + +Var_t * InsertVarMap +( + VarMap_t * vm, // valid variable map + ccp varname, // varname to insert + bool move_varname, // true: move 'varname', false: strdup() + ScanInfo_t * si, // not null: print "already defined" message + bool * found // not null: store found status +); + +//----------------------------------------------------------------------------- + +bool DefineIntVar +( + VarMap_t * vm, // valid variable map + ccp varname, // name of variable + int value // value to assign +); + +//----------------------------------------------------------------------------- + +bool DefineDoubleVar +( + VarMap_t * vm, // valid variable map + ccp varname, // name of variable + double value // value to assign +); + +//----------------------------------------------------------------------------- + +const Var_t * FindConst +( + ccp varname // variable name +); + +//----------------------------------------------------------------------------- + +const Var_t * FindVarMap +( + const VarMap_t * vm, // NULL or variable map + ccp varname, // variable name + ScanInfo_t * si // not null: print "not found" message +); + +//----------------------------------------------------------------------------- + +void PrintV +( + FILE * f, // destination file + const Var_t * v, // variable to print + int str_mode // 0:-, 1:limit+escape, 2:limit+escape+quote +); + +//----------------------------------------------------------------------------- + +void DumpVarMap +( + FILE * f, // destination file + uint indent, // indention + const VarMap_t * vm, // valid variable map + bool print_header // print table header +); + +//----------------------------------------------------------------------------- + +extern VarMap_t const_map; + +#define MINIMAP_DEFAULT_FLAGS 0x31f + +extern u32 set_flags; +extern bool have_set_scale; +extern bool have_set_rot; +extern Var_t set_scale, set_rot; + +extern uint have_set_value; // x=bit-0, y=bit-1, z=bit-2 +extern double3 set_value_min, set_value_max; + +//----------------------------------------------------------------------------- +// transformation options, values and functions + +#define N_TRANSFORM 10 + +extern uint force_transform; // bit 0: force tform even if not needed + // bit 1: ignore it + +extern MatrixD_t opt_transform; // current transform values +extern MatrixD_t transform_list[N_TRANSFORM]; // list with stored tforms +extern uint n_transform_list; // number of elements in list +extern bool transform_active; // true: transformation active +extern int disable_transformation; // >0: transformation is temporary disabled + +extern double yposition; // defined y position +extern bool yposition_valid; // 'yposition' is valid + +extern bool ascale_valid; // true: --ascale defined +extern double ascale; // scaling factor +extern double3 ascale_dir; // scaling direction (vector) + +extern bool arot_valid; // true: --arot defined +extern double arot_deg; // angle of arot in degree +extern double3 arot1, arot2; // points to define rotation axis + +extern uint mdl_switched_to_vertex; // 0: default + // 1: switch to 'VERTEX' needed + // 2: MDL_MODE switched to 'VERTEX' + // 3: '2' and message printed + +//------------------------------ + +void ResetTransformation(); // reset all transformation steps +int NextTransformation( bool for_close ); // enter next transformation step +void CloseTransformation(); // close transformation input +void DumpTransformationOpt(); // dump transformation options + +double3 TransformD3( uint mode, double3 *val ); // transformate a singe vector +// mode: +// 0: use opt_transform and default +// 1: single steps through 'list', use base vectors +// 2: single steps through 'list', use norm vectors +// 3: single steps through 'list', use matrix + +int ScanOptShift ( ccp arg ); // --shift +int ScanOptTranslate ( ccp arg ); // --translate +int ScanOptScale ( ccp arg ); // --scale +int ScanOptRotate ( ccp arg ); // --rot +int ScanOptXRotate ( uint xyz, ccp arg ); // --xrot --yrot --zrot +int ScanOptYPos ( ccp arg ); // --ypos +int ScanOptXSS ( uint xyz, ccp arg ); // --xss --yss --zss +int ScanOptAScale ( ccp arg ); // --ascale +int ScanOptARotate ( ccp arg ); // --arot + +//----------------------------------------------------------------------------- + +extern bool tform_script_enabled; + +int ScanOptTformScript ( ccp arg ); // --tfor-script +bool ScanTformBegin(); +bool TformScriptEnd(); + +enumError TformScriptCallF +( + uint dimension, // '2' for a 2D and '3' for a 3D vector + float3 *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +); + +enumError TformScriptCallD +( + uint dimension, // '2' for a 2D and '3' for a 3D vector + double3 *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +); + +//----------------------------------------------------------------------------- + +void SetupConst(); // inititalize constants +int ScanOptConst ( ccp arg ); // --const +int ScanOptSetFlags ( ccp arg ); // --set-flags +int ScanOptSetScale ( ccp arg ); // --set-scale +int ScanOptSetRot ( ccp arg ); // --set-rot +int ScanOptSet ( uint xyz, ccp arg ); // --set + +//----------------------------------------------------------------------------- + +void TransformPosFloat2D +( + float *v, // pointer to list of 2D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +); + +static inline void TransformPosFloat3D +( + float *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +) +{ + TransformF3NMatrixD(&opt_transform,(float3*)v,n,off); + if (tform_script_enabled) + TformScriptCallF(3,(float3*)v,n,off); +} + +void TransformPosDouble2D +( + double *v, // pointer to list of 2D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +); + +static inline void TransformPosDouble3D +( + double *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +) +{ + TransformD3NMatrixD(&opt_transform,(double3*)v,n,off); + if (tform_script_enabled) + TformScriptCallD(3,(double3*)v,n,off); +} + +//----------------------------------------------------------------------------- + +void TransformScaleFloat1D +( + float *v, // pointer to list of 3D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +); + +void TransformScaleFloat3D +( + float *v, // pointer to list of 3D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +); + +void TransformScaleDouble3D +( + double *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +); + +//----------------------------------------------------------------------------- + +void TransformRotFloat3D +( + float *v, // pointer to list of 3D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +); + +void TransformRotDouble3D +( + double *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan text: Interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ScanFile_t * AddSF +( + ScanInfo_t * si, // base scan info + ccp data, // data to scan + uint data_size, // size of data + ccp name, // NULL or name for warnings + int revision, // revision number + VarMap_t * varmap // NULL or new varmap (content moved) +); + +//----------------------------------------------------------------------------- + +bool DropSF +( + ScanInfo_t * si // data structure +); + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSI +( + ScanInfo_t * si, // data structure to setup + ccp data, // data to scan + uint data_size, // size of data + ccp name, // NULL or name for warnings + int revision // revision number +); + +//----------------------------------------------------------------------------- + +void ResetSI +( + ScanInfo_t * si // data structure to setup +); + +//----------------------------------------------------------------------------- + +void ResetLocalVarsSI +( + ScanInfo_t * si, // data structure to setup + int revision // revision number +); + +//----------------------------------------------------------------------------- + +void RestartSI +( + ScanInfo_t * si // data structure to setup +); + +//----------------------------------------------------------------------------- + +char NextCharSI +( + ScanInfo_t * si, // valid data + bool skip_lines // true: skip empty lines +); + +//----------------------------------------------------------------------------- + +char SkipCharSI +( + // return next non skipped character + + ScanInfo_t * si, // valid data + char ch // character to skip +); + +//----------------------------------------------------------------------------- + +char NextLineSI +( + ScanInfo_t * si, // valid data + bool skip_cont, // true: skip continuation lines + bool allow_tcmd // true: scan and execute text commands +); + +//----------------------------------------------------------------------------- + +enumError CheckLevelSI +( + ScanInfo_t * si // valid data +); + +//----------------------------------------------------------------------------- + +void GotoEolSI +( + ScanInfo_t * si // valid data +); + +//----------------------------------------------------------------------------- + +enumError CheckEolSI +( + ScanInfo_t * si // valid data +); + +//----------------------------------------------------------------------------- + +enumError CheckWarnSI +( + ScanInfo_t * si, // valid data + char ch, // char to test + enumError err // previous error level +); + +//----------------------------------------------------------------------------- + +enumError WarnIgnoreSI +( + ScanInfo_t * si, // valid data + ccp format, // NULL or format of text before 'line ignored' + ... // arguments for 'format' +) +__attribute__ ((__format__(__printf__,2,3))); + +//----------------------------------------------------------------------------- +// find first EOT|LF|CR|NUL + +ccp FindEndOfLineSI +( + ScanInfo_t * si, // valid data + ccp ptr // start search here +); + +//----------------------------------------------------------------------------- + +ccp FindLineFeedSI +( + ScanInfo_t * si, // valid data + ccp ptr, // start search here + bool mark_error // true: increment error counter +); + +//----------------------------------------------------------------------------- + +ccp FindNextLineFeedSI +( + ScanInfo_t * si, // valid data + bool mark_error // true: increment error counter +); + +/////////////////////////////////////////////////////////////////////////////// + +uint ScanNameSI +( + // return length of name + + ScanInfo_t * si, // valid data + char * name_buf, // destination buffer + uint buf_size, // size of 'name_buf' + bool allow_minus, // true: allow '-' in names + bool to_upper, // true: convert letters to upper case + int * xyz // not null: store vector index + // -1: no index, 0=x, 1=y, 2=z +); + +//----------------------------------------------------------------------------- + +enumError ConcatStringsSI +( + ScanInfo_t * si, // valid data + char ** dest_str, // store string here, use FREE + MALLOC + uint * len // not NULL: store length of return string here +); + +//----------------------------------------------------------------------------- + +enumError ScanStringSI +( + ScanInfo_t * si, // valid data + Var_t * var // store result here (already initialized) +); + +//----------------------------------------------------------------------------- + +extern char last_index_name[VARNAME_SIZE+1]; + +int ScanIndexSI +( + ScanInfo_t * si, // valid data + enumError * err, // return value: error code + int index, // index to define if name + IndexList_t * il, // NULL or index list + int insert_mode // 0: don't insert + // 1: insert into global namespace + // 2: '1' + print "already defined" error +); + +//----------------------------------------------------------------------------- + +void SetIndexName ( ccp name ); + +//----------------------------------------------------------------------------- + +const Var_t * FindVarSI +( + ScanInfo_t * si, // not NULL: use var maps + ccp varname, // variable name + bool print_warning // true && 'si': print 'not found' warning +); + +//----------------------------------------------------------------------------- + +Var_t * OverwriteVarSI +( + // returns always not NULL + + ScanInfo_t * si, // not NULL: use var maps for search + VarMap_t * vm_insert, // valid variable map, insert search failed + ccp varname, // varname to insert + int * xyz, // not NULL: enable .X/Y/Z support + // input: <0: not a X/Y/Z + // 0..2: is a X/Y/Z + // >2: unknown + // output: store corrected value + bool * found // not null: store found status (false=NEW) +); + +//----------------------------------------------------------------------------- + +bool DefineIndexInt +( + // returns true, if inserted + + ScanInfo_t * si, // valid data + ccp suffix, // name suffix + int value // value to assign +); + +//----------------------------------------------------------------------------- + +bool DefineIndexDouble +( + // returns true, if inserted + + ScanInfo_t * si, // valid data + ccp suffix, // name suffix + double value // value to assign +); + +//----------------------------------------------------------------------------- + +enumError ScanExprSI +( + ScanInfo_t * si, // valid data + Var_t * var // result: store value here +); + +//----------------------------------------------------------------------------- + +enumError ScanVectorExprSI +( + // scans: v | a,b | x,y,z + + ScanInfo_t * si, // valid data + Var_t * var, // result: store value here + double ab_default, // used for 'a,b' format + uint xyz // for ab default: 0:yz, 1:xz, 2:xy +); + +enumError ScanVectorExpr +( + // scans: v | x,z | x,y,z + + ccp arg, // string to scan + ccp name, // name for error messages + Var_t * var // result: store value here +); + +//----------------------------------------------------------------------------- + +enumError ScanMinMaxExprSI +( + // scans: val (min=-val,max=+val) | val1,val2 + + ScanInfo_t *si, // valid data + Var_t *min, // result: store min vector here + Var_t *max // result: store max vector here +); + +//----------------------------------------------------------------------------- + +enumError ScanValueSI +( + ScanInfo_t * si, // valid data + Var_t * var // result: store value here +); + +//----------------------------------------------------------------------------- + +enumError ScanUValueSI +( + ScanInfo_t * si, // valid data + long * num, // return value + int force_hex // >0: force hex for non prefixed integers +); + +//----------------------------------------------------------------------------- + +enumError ScanDValueSI +( + ScanInfo_t * si, // valid data + double * num // return value +); + +//----------------------------------------------------------------------------- + +enumError ScanBitfieldSI +( + ScanInfo_t * si, // valid data + Var_t * var // result: store value here +); + +//----------------------------------------------------------------------------- + +enumError ScanU8SI +( + ScanInfo_t * si, // valid data + u8 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +); + +//----------------------------------------------------------------------------- + +enumError ScanU16SI +( + ScanInfo_t * si, // valid data + u16 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +); + +//----------------------------------------------------------------------------- + +enumError ScanU32SI +( + ScanInfo_t * si, // valid data + u32 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +); + +//----------------------------------------------------------------------------- + +enumError ScanBE16SI +( + ScanInfo_t * si, // valid data + u16 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +); + +//----------------------------------------------------------------------------- + +enumError ScanBE32SI +( + ScanInfo_t * si, // valid data + u32 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +); + +//----------------------------------------------------------------------------- + +enumError ScanBE32SI_swap +( + ScanInfo_t * si, // valid data + u32 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +); + +//----------------------------------------------------------------------------- + +enumError ScanFloatSI +( + ScanInfo_t * si, // valid data + float * dest, // destination list + int n // number of elements in 'dest' +); + +//----------------------------------------------------------------------------- + +enumError ScanFloatV2SI +( + ScanInfo_t * si, // valid data + float * dest, // destination list + int n // number of 2d vectors in 'dest' +); + +//----------------------------------------------------------------------------- + +enumError ScanFloatV3SI +( + ScanInfo_t * si, // valid data + float * dest, // destination list + int n // number of 3d vectors in 'dest' +); + +//----------------------------------------------------------------------------- + +enumError ScanDoubleSI +( + ScanInfo_t * si, // valid data + double * dest, // destination list + int n // number of elements in 'dest' +); + +//----------------------------------------------------------------------------- + +enumError ScanDoubleV3SI +( + ScanInfo_t * si, // valid data + double * dest, // destination list + int n // number of elements in 'dest' +); + +//----------------------------------------------------------------------------- + +enumError ScanAssignIntSI +( + ScanInfo_t * si, // valid data + int * value // result: scanned value +); + +//----------------------------------------------------------------------------- + +enumError ScanAssignDoubleSI +( + ScanInfo_t * si, // valid data + double * value // result: scanned value +); + +//----------------------------------------------------------------------------- + +enumError ScanParamSI +( + ScanInfo_t * si, // valid data + const ScanParam_t * sp // NULL or scan parameter + // list ends with sp->name==NULL +); + +//----------------------------------------------------------------------------- + +enumError ScanHexlineSI +( + ScanInfo_t * si, // valid data + FastBuf_t * dest, // valid, append scanned data here + bool skip_addr // true: line may start with address field +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TextCommand_t GetTextCommand +( + ccp name // name to scan, upper case is assumed +); + +//----------------------------------------------------------------------------- + +enumError ScanTextCommand +( + ScanInfo_t * si // valid data +); + +//----------------------------------------------------------------------------- + +enumError ExecTextCommand +( + ScanInfo_t * si, // valid data + TextCommand_t tcmd // command to execute +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct Line_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[Line_t]] + +#define MAX_LINE_HELPER 2 +#define MAX_LINE_DIMEN 3 + +enum LineMode { LM_LINE, LM_BORDER, LM_BEZIER }; + +typedef struct Line_t +{ + uint n; // number of line points + uint n_pt; // number of helper points: 0..2 + double pt[MAX_LINE_HELPER][MAX_LINE_DIMEN]; // helper points + int line_mode; // user defined mode + +} Line_t; + +//----------------------------------------------------------------------------- + +void InitializeLine +( + Line_t * lin // valid line data +); + +enumError ScanLine +( + Line_t * lin, // valid line data + ScanInfo_t * si, // scanning source + uint n_dim, // number of elements in each vector (<=3) + int mode // user defined mode, stored in 'lin->mode' +); + +void CalcLine +( + Line_t * lin, // valid line data + float * dest, // destination vector + const float * p1, // source vector P1 + const float * p2, // source vector P2 + uint n_dim, // number of elements in each vector, n>3 possible + uint step // value between 0 and lin->n inclcusive +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// matrix support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_matrix(); + +bool PrintTestMatrix( bool print_sep ); + +bool TestMatrix +( + // returns false on error + + double3 *val, // NULL or value to test + uint print_mode, // 0: silent + // 1: print vectors on error + // 2: print vectors always + bool print_matrix // true && if vector shall print: + // print matrix before, but only the first time +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// operators /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[OpId_t]] created by work/scripts/create-db/optab.gen + +typedef enum OpId_t +{ + OPI_NONE = 0, + + OPI_ADD, + OPI_AND, + OPI_DIV, + OPI_EOR, + OPI_EQ, + OPI_EQEQ, + OPI_GE, + OPI_GT, + OPI_JOIN, + OPI_LAND, + OPI_LE, + OPI_LEOR, + OPI_LOR, + OPI_LSHIFT, + OPI_LT, + OPI_MOD, + OPI_MULT, + OPI_NEQ, + OPI_NEQNEQ, + OPI_OR, + OPI_POW, + OPI_RSHIFT, + OPI_SUB, + + OPI__N // = 24 + +} OpId_t; + +//----------------------------------------------------------------------------- + +void CalcExpr +( + Var_t * dst, // first source and destination + const Var_t * src, // second source + OpId_t op_id, // operator id + ScanInfo_t * si // not NULL: Use for warnings +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color conversions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const u8 cc38 [ 8]; // convert 3-bit color to 8-bit color +extern const u8 cc48 [ 16]; // convert 4-bit color to 8-bit color +extern const u8 cc58 [ 32]; // convert 5-bit color to 8-bit color +extern const u8 cc68 [ 64]; // convert 6-bit color to 8-bit color + +extern const u8 cc83 [256]; // convert 8-bit color to 3-bit color +extern const u8 cc84 [256]; // convert 8-bit color to 4-bit color +extern const u8 cc85 [256]; // convert 8-bit color to 5-bit color +extern const u8 cc85s1[256]; // convert 8-bit color to 5-bit color, shifted by 1 bit +extern const u8 cc86 [256]; // convert 8-bit color to 6-bit color + +//----------------------------------------------------------------------------- + +static inline u32 RGB565_to_RGB ( u16 rgb565 ) +{ + return cc58[ rgb565 >> 11 ] << 16 // red + | cc68[ rgb565 >> 5 & 0x3f ] << 8 // green + | cc58[ rgb565 & 0x1f ]; // blue +} + +static inline u16 RGB_to_RGB565 ( u32 rgb ) +{ + return cc85[ rgb >> 16 & 0xff ] << 11 + | cc86[ rgb >> 8 & 0xff ] << 5 + | cc85[ rgb & 0xff ]; +} + +int ScanOptCmprDefault ( ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_NUMERIC_H + diff --git a/project/src/lib-object.c b/project/src/lib-object.c new file mode 100644 index 0000000..bc84479 --- /dev/null +++ b/project/src/lib-object.c @@ -0,0 +1,336 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-object.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// find helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint Mark +( + const s16 *start, // start of index field + u8 *status, // status vector + uint size, // size of status vector (for assertions) + u8 mask // =0: add +1 / >0: OR mask +) +{ + DASSERT(start); + DASSERT(status); + + const s16 *ptr = start; + if (mask) + while ( *ptr >= 0 ) + { + DASSERT( *ptr < size ); + noPRINT("MARK[%u] |= %02x\n",*ptr,mask); + status[*ptr++] |= mask; + } + else + while ( *ptr >= 0 ) + { + DASSERT( *ptr < size ); + if (!++status[*ptr++]) + status[ptr[-1]]--; + } + + return ptr - start; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Find DB files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int FindDbFile +( + // returns the index of the file or -1 if not found + + ccp file // file to find, preeceding './' possible +) +{ + DASSERT(file); + if ( file[0] == '.' && file[1] == '/' ) + file += 2; + + int beg = 0; + int end = N_DB_FILE_FILE - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + int stat = strcmp(file,DbFileFILE[idx].file); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + return idx; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbFileByGroup +( + // returns the number of found files + + UsedFileFILE_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + uint group, // index of group (robust) + u8 mask // =0: add +1 / >0: OR mask +) +{ + noPRINT("FindDbFileByGroup(,%d,%u) size=%zu\n", + init_status, group, sizeof(*status) ); + + DASSERT(status); + if (init_status) + memset(status,0,sizeof(*status)); + + if ( group < N_DB_FILE_GROUP ) + { + DASSERT( DbFileGROUP[group].ref >= 0 ); + return Mark( DbFileRefGROUP + DbFileGROUP[group].ref, + status->d, N_DB_FILE_FILE, mask ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbFileByGroups +( + // returns the number of found files + + UsedFileFILE_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + const UsedFileGROUP_t *group // NULL or list of used groups +) +{ + noPRINT("FindDbFileByGroups(,%d,)\n",init_status); + + DASSERT(status); + if (init_status) + memset(status,0,sizeof(*status)); + + int count = 0; + if (group) + { + int gi; + for ( gi = 0; gi < N_DB_FILE_GROUP; gi++ ) + if (group->d[gi]) + { + noPRINT("group[%u] = %u -> %d -> %d, %d, %d\n",gi, + group->d[gi],DbFileGROUP[gi].ref, + DbFileRefGROUP[DbFileGROUP[gi].ref+0], + DbFileRefGROUP[DbFileGROUP[gi].ref+1], + DbFileRefGROUP[DbFileGROUP[gi].ref+2]); + + count += Mark( DbFileRefGROUP + DbFileGROUP[gi].ref, + status->d, N_DB_FILE_FILE, group->d[gi] ); + } + } + + #if HAVE_PRINT0 + { + int fi; + for ( fi = 0; fi < N_DB_FILE_FILE; fi++ ) + if (status->d[fi]) + printf(" -> FILE: %4u %02x %p\n", + fi, status->d[fi], DbFileFILE[fi].file ); + } + #endif + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbFileByObject +( + // returns the number of found files + + UsedFileFILE_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + uint object, // index of object (robust) + u8 mask // =0: add +1 / >0: OR mask +) +{ + noPRINT("FindDbFileByObject(,%d,%u) size=%zu\n", + init_status, object, sizeof(*status) ); + + DASSERT(status); + if (init_status) + memset(status,0,sizeof(*status)); + + UsedFileGROUP_t used_group; + FindDbGroupByObject(&used_group,true,object,mask); + return FindDbFileByGroups(status,init_status,&used_group); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Find DB groups /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbGroupByObject +( + // returns the number of found groups + + UsedFileGROUP_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + uint object, // index of object (robust) + u8 mask // =0: add +1 / >0: OR mask +) +{ + noPRINT("FindDbGroupByObjects(,%d,)\n",init_status); + + DASSERT(status); + if (init_status) + memset(status,0,sizeof(*status)); + + int count = 0; + if ( object < N_KMP_GOBJ ) + { + const ObjectInfo_t *oi = ObjectInfo + object; + if ( oi->fname ) + { + int i; + for ( i = 0; i < sizeof(oi->group_idx)/sizeof(*oi->group_idx); i++ ) + { + uint fid = oi->group_idx[i]; + if ( fid < N_DB_FILE_GROUP ) + { + if (mask) + status->d[fid] |= mask; + else if (!++status->d[fid]) + status->d[fid]--; + count++; + } + } + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbGroupByObjects +( + // returns the number of found groups + + UsedFileGROUP_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + const UsedObject_t *object // NULL or list of used objects +) +{ + noPRINT("FindDbGroupByObjects(,%d,)\n",init_status); + + DASSERT(status); + if (init_status) + memset(status,0,sizeof(*status)); + + int count = 0; + if (object) + { + int obj; + const ObjectInfo_t *oi = ObjectInfo; + for ( obj = 0; obj < N_KMP_GOBJ; obj++, oi++ ) + if ( object->d[obj] && oi->fname ) + { + int i; + for ( i = 0; i < sizeof(oi->group_idx)/sizeof(*oi->group_idx); i++ ) + { + uint fid = oi->group_idx[i]; + if ( fid < N_DB_FILE_GROUP ) + { + status->d[fid] |= object->d[obj]; + count++; + } + } + } + } + + #if HAVE_PRINT0 + { + int gi; + for ( gi = 0; gi < N_DB_FILE_GROUP; gi++ ) + if (status->d[gi]) + printf(" -> GROUP: %4u %02x %s\n", + gi, status->d[gi], DbFileGROUP[gi].name ); + } + #endif + + return count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// for external modules /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void DefineObjectNameVars ( VarMap_t *vm ) +{ + DASSERT(vm); + + char varname[50], *varname_end = varname + sizeof(varname)-1; + const ObjectInfo_t *oi, *oi_end = ObjectInfo + N_KMP_GOBJ; + int index = 0; + for ( oi = ObjectInfo; oi < oi_end; oi++, index++ ) + if (oi->name) + { + char * vn = varname; + *vn++ = 'O'; + *vn++ = '$'; + ccp src = oi->name; + while ( *src && vn < varname_end ) + *vn++ = toupper((int)*src++); + *vn = 0; + + DefineIntVar(vm,varname,index); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-object.h b/project/src/lib-object.h new file mode 100644 index 0000000..cf86ba1 --- /dev/null +++ b/project/src/lib-object.h @@ -0,0 +1,186 @@ + + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_OBJECT_H +#define SZS_LIB_OBJECT_H 1 + +#include "lib-std.h" +#include "db-file.h" +#include "db-object.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// status vectors /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[UsedObject_t]] [[UsedFile_t]] [[UsedFileSZS_t]] +// [[UsedFileSHA1_t]] [[UsedFileFILE_t]] [[UsedFileGROUP_t]] + +typedef struct UsedObject_t { u8 d[N_KMP_GOBJ]; } UsedObject_t; +typedef struct UsedFile_t { u8 d[N_DB_FILE]; } UsedFile_t; +typedef struct UsedFileSZS_t { u8 d[N_DB_FILE_SZS]; } UsedFileSZS_t; +typedef struct UsedFileSHA1_t { u8 d[N_DB_FILE_SHA1]; } UsedFileSHA1_t; +typedef struct UsedFileFILE_t { u8 d[N_DB_FILE_FILE]; } UsedFileFILE_t; +typedef struct UsedFileGROUP_t { u8 d[N_DB_FILE_GROUP];} UsedFileGROUP_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Find DB files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int FindDbFile +( + // returns the index of the file or -1 if not found + + ccp file // file to find, preeceding './' possible +); + +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbFileByGroup +( + // returns the number of found files + + UsedFileFILE_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + uint group, // index of group (robust) + u8 mask // =0: add +1 / >0: OR mask +); + +//----------------------------------------------------------------------------- + +uint FindDbFileByGroups +( + // returns the number of found files + + UsedFileFILE_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + const UsedFileGROUP_t *group // NULL or list of used groups +); + +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbFileByObject +( + // returns the number of found files + + UsedFileFILE_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + uint object, // index of object (robust) + u8 mask // =0: add +1 / >0: OR mask +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Find DB groups /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint FindDbGroupByObject +( + // returns the number of found groups + + UsedFileGROUP_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + uint object, // index of object (robust) + u8 mask // =0: add +1 / >0: OR mask +); + +//----------------------------------------------------------------------------- + +uint FindDbGroupByObjects +( + // returns the number of found groups + + UsedFileGROUP_t *status, // modify a byte for each found file + bool init_status, // clear 'status' first + const UsedObject_t *object // NULL or list of used objects +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// have_kmp_obj_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[have_kmp_obj_t]] +// order is important, compare have_kmp_info[] and ct.wiimm.de + +typedef enum have_kmp_obj_t +{ + HAVEKMP_WOODBOX_HT, // 0x070:woodbox setting #8>0 => fall height + HAVEKMP_MUSHROOM_CAR, // 0x0d8:penguin_m setting #8>0 => mushroom car + HAVEKMP_PENGUIN_POS, // 0x0d8:penguin_m setting #2>0 => start pos + HAVEKMP_SECOND_KTPT, // Second KTPT with index -1 + HAVEKMP_X_PFLAGS, // Extended presence flags (XPF) + HAVEKMP_X_COND, // XPF: Conditional object + HAVEKMP_X_DEFOBJ, // XPF: Definition object + HAVEKMP_X_RANDOM, // XPF: Random scenarios + HAVEKMP_EPROP_SPEED, // 0x1a6:Epropeller setting #8>0 => speed + HAVEKMP__N +} +__attribute__ ((packed)) have_kmp_obj_t; + +//----------------------------------------------------------------------------- +// [[kmp_special_t]] + +typedef u8 kmp_special_t[HAVEKMP__N]; // >0: number of special KMP objects found + +//----------------------------------------------------------------------------- +// [[kmp_obj_info_t]] + +typedef struct kmp_obj_info_t +{ + u32 skip; // >0: if found: skip next # messages + ccp name; // short name + ccp info; // info for check +} +kmp_obj_info_t; + +extern const kmp_obj_info_t have_kmp_info[HAVEKMP__N]; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void DefineObjectNameVars ( VarMap_t *vm ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_OBJECT_H + diff --git a/project/src/lib-objflow.c b/project/src/lib-objflow.c new file mode 100644 index 0000000..ddecb37 --- /dev/null +++ b/project/src/lib-objflow.c @@ -0,0 +1,641 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include + +#include "lib-szs.h" +#include "lib-common.h" +#include "lib-numeric.h" +#include "lib-bzip2.h" + +#include "objflow.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OBJFLOW setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeOBJFLOW ( objflow_t * objflow ) +{ + DASSERT(objflow); + memset(objflow,0,sizeof(*objflow)); + objflow->fname = EmptyString; + + InitializeObjMgr(&objflow->obj_mgr,FF_OBJFLOW); + BZ2Manager_t *bz_orig = GetCommonBZ2Manager(FF_OBJFLOW); + DASSERT(bz_orig); + const uint copy_size = bz_orig->size < sizeof(objflow->bin) + ? bz_orig->size : sizeof(objflow->bin); + memcpy( objflow->bin, bz_orig->data, copy_size ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetOBJFLOW ( objflow_t * objflow ) +{ + DASSERT(objflow); + ResetObjMgr(&objflow->obj_mgr); + FreeString(objflow->fname); + InitializeOBJFLOW(objflow); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(objflow); + if (init_objflow) + InitializeOBJFLOW(objflow); + + #if 0 // [[todo]] [[?]] ??? + if ( IsValidOBJFLOW(data,data_size,data_size,objflow->fname) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid OBJFLOW file: %s\n" + "Add option --objflow=force or --force to ignore some validity checks.", + objflow->fname ? objflow->fname : "?" ); + } + #endif + + const uint copy_size = data_size < sizeof(objflow->bin) + ? data_size : sizeof(objflow->bin); + memcpy(objflow->bin,data,copy_size); + objflow->fform = FF_OBJFLOW; + +// [[obj-type]] + #if USE_OBJECT_MANAGER + VerifyBinObjMgr(objflow->bin,sizeof(objflow->bin),"ObjFlow/raw"); + #endif + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextOBJFLOW() helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + OBJFLOW_IGNORE, + OBJFLOW_SETUP, + OBJFLOW_OBJECTS, +}; + +//----------------------------------------------------------------------------- + +static const KeywordTab_t objflow_section_name[] = +{ + { OBJFLOW_IGNORE, "END", 0, 0 }, + { OBJFLOW_SETUP, "SETUP", 0, 0 }, + { OBJFLOW_OBJECTS, "OBJECTS", 0, 0 }, + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTextSETUP +( + objflow_t * objflow, // OBJFLOW data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(objflow); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + ScanU32SI(si,(u32*)&objflow->revision,1,0); + si->init_revision = si->cur_file->revision = objflow->revision; + DefineIntVar(&si->gvar,"REVISION$SETUP",objflow->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",objflow->revision); + } +// [[obj-type]] + #if USE_OBJECT_MANAGER > 1 + else if (!SetupObjMgrBySI(&objflow->obj_mgr,si,name)) + GotoEolSI(si); + #else + else + GotoEolSI(si); + #endif + CheckEolSI(si); + } + + CheckLevelSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static objflow_bin_t * FindObjectOBJFLOW ( cvp data, uint oid ) +{ + oid = htons(oid); + const uint N = be16(data); + objflow_bin_t *bin = (objflow_bin_t*)((u8*)data+2); + uint i; + for ( i = 0; i < N; i++, bin++ ) + if ( bin->id == oid ) + return bin; + return 0; +} + +//----------------------------------------------------------------------------- + +static enumError ScanTextOBJECTS +( + objflow_t * objflow, // OBJFLOW data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(objflow); + DASSERT(si); + TRACE("ScanTextSETUP()\n"); + + objflow_bin_t *obj = 0; + si->point_is_null++; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,0); + continue; + } + + char name[50]; + const uint namelen = ScanNameSI(si,name,sizeof(name),true,true,0); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + noPRINT("### %s = %.4s...\n",name,sf->ptr); + + if (!namelen) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing name for option: [%s @%u]: %.*s\n", + sf->name, sf->line, (int)(eol-sf->ptr), sf->ptr ); + } + continue; + } + + if (!strcmp(name,"OBJECT")) + { + long oid; + ScanUValueSI(si,&oid,0); + obj = FindObjectOBJFLOW(objflow->bin,oid); + PRINT0("OID: %lx -> %p\n",oid,obj); + if (!obj) + GotoEolSI(si); + else + { + DEFINE_VAR(var); + if (SkipCharSI(si,',')) + { + ScanStringSI(si,&var); + memset(obj->name,0,sizeof(obj->name)); + StringCopyS(obj->name,sizeof(obj->name),var.str); + } + + if (SkipCharSI(si,',')) + { + ScanStringSI(si,&var); + memset(obj->resources,0,sizeof(obj->resources)); + StringCopyS(obj->resources,sizeof(obj->resources),var.str); + } + FreeV(&var); + } + } + else if (!strcmp(name,"PARAM")) + { + if (!obj) + { + sf->line_err++; + si->total_err++; + if ( si->no_warn <= 0 ) + ERROR0(ERR_WARNING, + "No object selected [%s @%u]: %s\n", + sf->name, sf->line, name ); + GotoEolSI(si); + } + else + { + uint i; + const uint N = sizeof(obj->param) / sizeof(*obj->param); + for ( i = 0; i < N; i++ ) + { + const char ch = NextCharSI(si,false); + if (!ch) + break; + if ( ch == '=' ) + { + si->cur_file->ptr++; + SkipCharSI(si,','); + } + else + { + long num; + ScanUValueSI(si,&num,0); + obj->param[i] = htons(num); + } + } + } + } + else + { + sf->line_err++; + si->total_err++; + if ( si->no_warn <= 0 ) + ERROR0(ERR_WARNING, + "Unknown keyword [%s @%u]: %s\n", + sf->name, sf->line, name ); + GotoEolSI(si); + } + CheckEolSI(si); + } + + si->point_is_null--; + CheckLevelSI(si); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + PRINT("ScanTextOBJFLOW(init=%d)\n",init_objflow); + + DASSERT(objflow); + if (init_objflow) + InitializeOBJFLOW(objflow); + + ScanInfo_t si; + InitializeSI(&si,data,data_size,objflow->fname,objflow->revision); + si.predef = SetupVarsCOMMON(); + + enumError max_err = ERR_OK; + objflow->is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN OBJFLOW SECTIONS, PASS%u ...\n",objflow->is_pass2+1); + + max_err = ERR_OK; + si.no_warn = !objflow->is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", objflow->is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,objflow->revision); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + noPRINT("--> pass=%u: #%04u: %s\n",objflow->is_pass2+1,si.cur_file->line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,objflow_section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + noPRINT("--> %-6s #%-4u |%.3s|\n",cmd->name1,si.cur_file->line,si.cur_file->ptr); + + enumError err = ERR_OK; + switch (cmd->id) + { + case OBJFLOW_SETUP: + err = ScanTextSETUP(objflow,&si); + break; + + case OBJFLOW_OBJECTS: + err = ScanTextOBJECTS(objflow,&si); + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (objflow->is_pass2) + break; + + objflow->is_pass2 = true; + + RestartSI(&si); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextOBJFLOW) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OBJFLOW: scan and load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + DASSERT(objflow); + + enumError err; +// [[analyse-magic]] +// [[obj-type]] + switch (GetByMagicFF(data,data_size,data_size)) + { + case FF_OBJFLOW: + objflow->fform = FF_OBJFLOW; + err = ScanRawOBJFLOW(objflow,init_objflow,data,data_size); + break; + + case FF_OBJFLOW_TXT: + objflow->fform = FF_OBJFLOW_TXT; + err = ScanTextOBJFLOW(objflow,init_objflow,data,data_size); + break; + + default: + if (init_objflow) + InitializeOBJFLOW(objflow); + return ERROR0(ERR_INVALID_DATA, + "No OBJFLOW file: %s\n", objflow->fname ? objflow->fname : "?"); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadOBJFLOW +( + objflow_t * objflow, // OBJFLOW data structure + bool init_objflow, // true: initialize 'objflow' first + ccp fname, // valid pointer to filenname + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(objflow); + DASSERT(fname); + if (init_objflow) + InitializeOBJFLOW(objflow); + else + ResetOBJFLOW(objflow); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,0); + if (!err) + { + objflow->fname = raw.fname; + raw.fname = 0; + err = ScanOBJFLOW(objflow,false,raw.data,raw.data_size); + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawOBJFLOW +( + objflow_t * objflow, // pointer to valid OBJFLOW + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(objflow); + DASSERT(fname); + + PRINT("SaveRawOBJFLOW(%s,%d)\n",fname,set_time); + + + //--- write to file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&objflow->fatt,0); + + if ( fwrite(objflow->bin,1,sizeof(objflow->bin),F.f) != sizeof(objflow->bin) ) + FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextOBJFLOW() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextOBJFLOW +( + const objflow_t * objflow, // pointer to valid OBJFLOW + ccp fname, // filename of destination + bool set_time, // true: set time stamps +// [[minimize]] + int minimize // usually a copy of global 'minimize_level': + // >0: write only modified sections + // >9: minimize output +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(objflow); + DASSERT(fname); + PRINT("SaveTextOBJFLOW(%s,%d)\n",fname,set_time); + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,objflow->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&objflow->fatt,0); + + + //--- print header + syntax info + + const bool explain = print_header && !brief_count && !export_count; + if (explain) + fputs(text_objflow_info_cr,F.f); + else + fputs(text_objflow_head_cr,F.f); + + const bool tiny = brief_count > 1 || export_count; + if (!tiny) + fprintf(F.f,text_objflow_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE ); + + #if USE_OBJECT_MANAGER>1 + WriteObjMgrToSetupSection( F.f, &objflow->obj_mgr, + long_count>0, tiny, explain ); + #endif + + + //--- iterate records + + BZ2Manager_t *bz_orig = GetCommonBZ2Manager(FF_OBJFLOW); + DASSERT(bz_orig); + uint n_rec = be16(objflow->bin); + if ( n_rec > be16(bz_orig->data) ) + n_rec = be16(bz_orig->data); + + uint i; + const objflow_bin_t *bin = (objflow_bin_t*)(objflow->bin + 2); + const objflow_bin_t *ref = (objflow_bin_t*)(bz_orig->data + 2); + + bool sect_head_done = false; + for ( i = 0; i < n_rec; i++, bin++, ref++ ) + { + const int differ = memcmp( bin, ref, sizeof(*bin) ); + if ( differ || !minimize ) + { + if (!sect_head_done) + { + sect_head_done = true; + fputs( brief_count < 2 ? section_sep : "\r\n", F.f ); + fprintf(F.f,text_objflow_objects_cr,REVISION_NUM); + } + + fprintf(F.f,"\r\nOBJECT 0x%03x, \"%s\", \"%s\"\r\n", + ntohs(bin->id), bin->name, bin->resources ); + + if (memcmp(bin,ref,offsetof(objflow_bin_t,param))) + fprintf(F.f,"#ORIG: 0x%03x \"%s\" \"%s\"\r\n", + ntohs(ref->id), ref->name, ref->resources ); + + fputs("PARAM ",F.f); + PrintParamGEOHIT(F.f,6,bin->param,0,9); + + if (memcmp(bin->param,ref->param,sizeof(bin->param))) + { + fputs("\r\n#ORIG:",F.f); + PrintParamGEOHIT(F.f,6,ref->param,bin->param,9); + } + fputs("\r\n",F.f); + } + } + + //--- terminate + + fputs( brief_count < 2 ? section_end :"\r\n", F.f ); + ResetFile(&F,set_time); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-pack.c b/project/src/lib-pack.c new file mode 100644 index 0000000..5961839 --- /dev/null +++ b/project/src/lib-pack.c @@ -0,0 +1,255 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-pack.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IterateFilesPACK() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesPACK +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size <= sizeof(pack_header_t) ) + return -1; + + //--- analyze file header + + const pack_header_t * pack = (pack_header_t*)szs->data; + const uint size = ntohl(pack->file_size); + const uint n_files = ntohl(pack->n_files); + const uint offset_metric = ntohl(pack->offset_metric); + + if + ( ntohl(pack->magic) != PACK_MAGIC_NUM + || size < szs->size + || offset_metric < 0x20 + || offset_metric + n_files * sizeof(pack_metric_t) >= size + ) + return -1; + + + //--- cut file header + + if (it->cut_files) + { + it->index = 0; + it->fst_item = 0; + it->is_dir = 0; + + it->off = 0; + it->size = sizeof(pack_header_t); + StringCopyS(it->path,sizeof(it->path),".PACK.header"); + it->func_it(it,false); + + it->off = sizeof(pack_header_t); + it->size = offset_metric; + StringCopyS(it->path,sizeof(it->path),".PACK.filenames"); + it->func_it(it,false); + + it->off = offset_metric; + it->size = offset_metric + n_files * sizeof(pack_metric_t); + StringCopyS(it->path,sizeof(it->path),".PACK.metric"); + it->func_it(it,false); + } + + + //--- sub files + + const pack_metric_t *metric = (pack_metric_t*)(szs->data+offset_metric); + ccp name = pack->name_pool; + + uint idx; + for ( idx = 0; idx < n_files; idx++, metric++ ) + { + while ( *name == '/' ) // skip leading slashes (security) + name++; + StringCopyS(it->path,sizeof(it->path),name); + name += strlen(name) + 1; + + it->index = idx+1; + it->fst_item = 0; + it->is_dir = 0; + + it->off = ntohl(metric->offset); + if ( it->off >= size ) + { + it->off = size; + it->size = 0; + } + else + { + it->size = ntohl(metric->size); + if ( it->size > size - it->off ) + it->size = size - it->off; + } + + it->func_it(it,false); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreatePACK +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 total_fsize, // total file size + SetupParam_t * setup_param // NULL or setup parameters +) +{ + DASSERT(szs); + PRINT("CreatePACK() ndir=%d, totfsize=%x, align=%x,%x\n", + szs->subfile.used, total_fsize, opt_align_breff, opt_align_pack ); + + //--- setup + + enumError max_err = ERR_OK; + const endian_func_t * endian = &be_func; + szs->endian = endian; + + szs->fform_arch = FF_PACK; + szs->ff_attrib = GetAttribFF(szs->fform_arch); + + SortSubFilesSZS(szs,SORT_AUTO); + + if ( logging > 1 ) + { + printf("\nsorted file list:\n"); + int i; + for ( i = 0; i < szs->subfile.used; i++ ) + { + szs_subfile_t * f = szs->subfile.list + i; + printf("%3d.: %u %6x %6x %s\n", i, f->is_dir, f->offset, f->size, f->path ); + } + } + + //--- calc segment sizes and alloc data + + u32 spool_size = 0, n_files = 0; + + int i; + szs_subfile_t * fptr; + for ( i = 0, fptr = szs->subfile.list; i < szs->subfile.used; i++, fptr++ ) + { + if (!fptr->is_dir) + { + n_files++; + spool_size += strlen(fptr->path) + 1; + } + } + + spool_size = ALIGN32(spool_size,PACK_METRIC_ALIGN); + const u32 metric_offset = sizeof(pack_header_t) + spool_size; + const u32 metric_size + = ALIGN32( n_files * sizeof(pack_metric_t), + opt_align_pack > PACK_METRIC_ALIGN ? opt_align_pack : PACK_METRIC_ALIGN ); + + total_fsize = ALIGN32(total_fsize,opt_align_pack); + u32 file_offset = ALIGN32(metric_offset+metric_size,opt_align_pack); + + szs->size = file_offset + total_fsize; + szs->data = CALLOC(1,szs->size); + + noPRINT(">>> SIZE=%zu=%#zx, N=%u,%u\n", + szs->size, szs->size, n_files, szs->subfile.used ); + + + //--- setup header + + pack_header_t *ph = (pack_header_t*)szs->data; + ph->magic = htonl(PACK_MAGIC_NUM); + ph->file_size = htonl(szs->size); + ph->n_files = htonl(n_files); + ph->offset_metric = htonl(metric_offset); + + + //--- copy files + + pack_metric_t *metric = (pack_metric_t*)(szs->data+metric_offset); + char *name = ph->name_pool; + char *name_end = (char*)metric; + + for ( i = 0, fptr = szs->subfile.list; i < szs->subfile.used; i++, fptr++ ) + { + MARK_USED(name,name_end); + if (!fptr->is_dir) + { + name = StringCopyE(name,name_end,fptr->path); + name++; + + metric->offset = htonl(file_offset); + metric->size = htonl(fptr->size); + metric++; + + if (fptr->data) + memcpy(szs->data + file_offset, fptr->data, fptr->size ); + else + { + PRINT("LOAD: %x+%x %s\n",file_offset,fptr->size,fptr->path); + enumError err = LoadFILE( source_dir, fptr->path, 0, + szs->data + file_offset, fptr->size, 0, &szs->fatt, true ); + if ( max_err < err ) + max_err = err; + } + file_offset += ALIGN32(fptr->size,opt_align_pack); + } + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-pack.h b/project/src/lib-pack.h new file mode 100644 index 0000000..ab343ec --- /dev/null +++ b/project/src/lib-pack.h @@ -0,0 +1,104 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_PACK_H +#define SZS_LIB_PACK_H 1 + +#include "lib-szs.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PACK support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define PACK_MAGIC "PACK" +#define PACK_MAGIC_NUM 0x5041434b + +#define PACK_METRIC_ALIGN 0x10 +#define PACK_SUBFILE_ALIGN 0x20 + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct pack_header_t +{ + u32 magic; // := PACK_MAGIC_NUM + u32 file_size; // total size of pack file + u32 n_files; // total number of subfiles + u32 offset_metric; // offset to (offset,size) table + char name_pool[0]; // pool with names +} +__attribute__ ((packed)) pack_header_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct pack_metric_t +{ + u32 offset; // offset of subfile + u32 size; // size of subfile +} +__attribute__ ((packed)) pack_metric_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PACK interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesPACK +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreatePACK +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 total_size, // total file size + SetupParam_t * setup_param // NULL or setup parameters +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_PACK_H diff --git a/project/src/lib-parser-func.c b/project/src/lib-parser-func.c new file mode 100644 index 0000000..5747f25 --- /dev/null +++ b/project/src/lib-parser-func.c @@ -0,0 +1,5597 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-std.h" +#include "lib-mkw.h" +#include "lib-kmp.h" +#include "db-mkw.h" +#include "ui.h" +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static Var_t NullVector = {0}; +static double3 NullVectorD; + +static VarMap_t parser_func = {0,0,0}; +static VarMap_t parser_func_info = {0,0,0}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// management functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void DefineParserVars ( VarMap_t * vm ) +{ + SetupConst(); + NullVectorD.x = NullVectorD.y = NullVectorD.z = 0.0; + + + //--- setup '$NONE' + + InsertVarMap(vm,"$NONE",false,0,0); + + + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + { "INT$MIN", INT_MIN }, + { "INT$MAX", INT_MAX }, + + { "TYPE$UNDEF", -1 }, + { "TYPE$UNSET", VAR_UNSET }, + { "TYPE$INT", VAR_INT }, + { "TYPE$FLOAT", VAR_DOUBLE }, + { "TYPE$VECTOR", VAR_VECTOR }, + { "TYPE$STR", VAR_STRING }, + { "TYPE$X", VAR_X }, + { "TYPE$Y", VAR_Y }, + { "TYPE$Z", VAR_Z }, + + { "REVISION$TOOL", REVISION_NUM }, + { "REVISION$SETUP", REVISION_NUM }, + { "REVISION$ACTIVE", REVISION_NUM }, + + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(vm,ip->name,ip->val); + + + //--- setup float variables + + struct dbltab_t { ccp name; double val; }; + static const struct dbltab_t dbltab[] = + { + { "M$E", 2.7182818284590452354 }, // e + { "M$LOG2E", 1.4426950408889634074 }, // log_2(e) + { "M$LOG10E", 0.43429448190325182765 }, // log_10(e) + { "M$LN2", 0.69314718055994530942 }, // log_e(2) + { "M$LN10", 2.30258509299404568402 }, // log_e(10) + { "M$PI", 3.14159265358979323846 }, // pi + { "M$PI_2", 1.57079632679489661923 }, // pi/2 + { "M$PI_4", 0.78539816339744830962 }, // pi/4 + { "M$C1_PI", 0.31830988618379067154 }, // 1/pi + { "M$2_PI", 0.63661977236758134308 }, // 2/pi + { "M$2_SQRTPI", 1.12837916709551257390 }, // 2/sqrt(pi) + { "M$SQRT2", 1.41421356237309504880 }, // sqrt(2) + { "M$1_SQRT1", 0.70710678118654752440 }, // 1/sqrt(2) + + { "INF", INFINITY }, + { "M$INF", INFINITY }, + + {0,0} + }; + + const struct dbltab_t * dp; + for ( dp = dbltab; dp->name; dp++ ) + DefineDoubleVar(vm,dp->name,dp->val); +} + +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupParserVars() +{ + static VarMap_t vm = {0}; + if (!vm.used) + DefineParserVars(&vm); + return &vm; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineMkwVars ( VarMap_t * vm ) +{ + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + { "N$NONE", RTL_N_NONE }, // Mode of Nintendo-Tracks + { "N$HIDE", RTL_N_HIDE }, + { "N$SHOW", RTL_N_SHOW }, + { "N$SWAP", RTL_N_SWAP }, + + { "N$F_HEX", RTL_N_F_HEX }, + { "N$F_WII", RTL_N_F_WII }, + + { "N$F_RED1", RTL_N_F_RED1 }, + { "N$F_RED", RTL_N_F_RED1 }, + { "N$F_RED2", RTL_N_F_RED2 }, + { "N$F_RED3", RTL_N_F_RED3 }, + { "N$F_RED4", RTL_N_F_RED4 }, + { "N$F_YELLOW", RTL_N_F_YELLOW }, + { "N$F_GREEN", RTL_N_F_GREEN }, + { "N$F_BLUE1", RTL_N_F_BLUE1 }, + { "N$F_BLUE", RTL_N_F_BLUE1 }, + { "N$F_BLUE2", RTL_N_F_BLUE2 }, + { "N$F_WHITE", RTL_N_F_WHITE }, + { "N$F_CLEAR", RTL_N_F_CLEAR }, + + { "SLOT$WAIT", SLOT_WAIT }, + { "SLOT$RANDOM", SLOT_RANDOM }, + { "SLOT$RND_ALL", LE_SLOT_RND_ALL }, + { "SLOT$RND_ORIG", LE_SLOT_RND_ORIGINAL }, + { "SLOT$RND_CUSTOM", LE_SLOT_RND_CUSTOM }, + { "SLOT$RND_NEW", LE_SLOT_RND_NEW }, + + { "LE$F_NEW", LETF_NEW }, + { "LE$F_HEAD", LETF_RND_HEAD }, + { "LE$F_GROUP", LETF_RND_GROUP }, + + { "LE$PRODUCTIVE", LPM_PRODUCTIVE }, + { "LE$TESTING", LPM_TESTING }, + { "LE$EXPERIMENTAL", LPM_EXPERIMENTAL }, + { "LE$AUTOMATIC", LPM_AUTOMATIC }, + + { "LEX$CB_AUTO", -1 }, + { "LEX$CB_TIMETRIAL", 14 }, + { "LEX$CB_RESERVED1", 14 }, + { "LEX$CB_RESERVED2", 15 }, + + { "LEX$OO_AUTO", LEX_OO_AUTO }, + { "LEX$OO_OFFLINE", LEX_OO_OFFLINE }, + { "LEX$OO_ONLINE", LEX_OO_ONLINE }, + + { "LEX$GM_AUTO", LEX_GMODE_AUTO }, + { "LEX$GM_STANDARD", LEX_GMODE_STD }, + { "LEX$GM_STD", LEX_GMODE_STD }, + { "LEX$GM_BALLOON", LEX_GMODE_BALLOON }, + { "LEX$GM_COIN", LEX_GMODE_COIN }, + { "LEX$GM_VERSUS", LEX_GMODE_VERSUS }, + { "LEX$GM_ITEMRAIN", LEX_GMODE_ITEMRAIN }, + + { "LEX$EN_AUTO", LEX_ENGINE_AUTO }, + { "LEX$EN_BATTLE", LEX_ENGINE_BATTLE }, + { "LEX$EN_50CC", LEX_ENGINE_50 }, + { "LEX$EN_100CC", LEX_ENGINE_100 }, + { "LEX$EN_150CC", LEX_ENGINE_150 }, + { "LEX$EN_200CC", LEX_ENGINE_200 }, + { "LEX$EN_150M", LEX_ENGINE_150M }, + { "LEX$EN_200M", LEX_ENGINE_200M }, + + { "VEH$SMALL", CHATVEH_SMALL }, + { "VEH$MEDIUM", CHATVEH_MEDIUM }, + { "VEH$LARGE", CHATVEH_LARGE }, + { "VEH$ANY_SIZE", CHATVEH_ANY_SIZE }, + { "VEH$KART", CHATVEH_KART }, + { "VEH$OUT_BIKE", CHATVEH_OUT_BIKE }, + { "VEH$IN_BIKE", CHATVEH_IN_BIKE }, + { "VEH$BIKE", CHATVEH_BIKE }, + { "VEH$ANY_TYPE", CHATVEH_ANY_TYPE }, + { "VEH$ANY", CHATVEH_ANY }, + + { "CHAT$OFF", CHATMD_OFF }, + { "CHAT$ANY_TRACK", CHATMD_ANY_TRACK }, + { "CHAT$TRACK_BY_HOST", CHATMD_TRACK_BY_HOST }, + { "CHAT$BLOCK_CLEAR", CHATMD_BLOCK_CLEAR }, + { "CHAT$BLOCK_DISABLE", CHATMD_BLOCK_DISABLE }, + { "CHAT$BLOCK_ENABLE", CHATMD_BLOCK_ENABLE }, + { "CHAT$ANY_VEHICLE", CHATMD_ANY_VEHICLE }, + { "CHAT$KARTS_ONLY", CHATMD_KARTS_ONLY }, + { "CHAT$BIKES_ONLY", CHATMD_BIKES_ONLY }, + { "CHAT$RESET_ENGINE", CHATMD_RESET_ENGINE }, + { "CHAT$USE_ENGINE_1", CHATMD_USE_ENGINE_1 }, + { "CHAT$USE_ENGINE_2", CHATMD_USE_ENGINE_2 }, + { "CHAT$USE_ENGINE_3", CHATMD_USE_ENGINE_3 }, + { "CHAT$RESET", CHATMD_RESET }, + + + + { "LC", 8 }, // LC has no own music slot + + { "T0", 0 }, // unknown track or arena + { "T00", 0 }, + { "A0", 0 }, + { "A00", 0 }, + + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(vm,ip->name,ip->val); + + + //--- setup basic parser variables + + DefineParserVars(vm); + + + //--- setup music and property ids + + uint tid; + char name[20]; + + + //--- tracks + + for ( tid = 0; tid < MKW_N_TRACKS; tid++ ) + { + const TrackInfo_t *info = track_info + tid; + snprintf(name,sizeof(name),"T%02u",info->def_slot); + DefineIntVar(vm,name,tid); + } + + + //--- arenas + + for ( tid = 0; tid < MKW_N_ARENAS; tid++ ) + { + const uint aid = tid + MKW_N_TRACKS; + const TrackInfo_t *info = arena_info + tid; + snprintf(name,sizeof(name),"A%02u",info->def_slot); + DefineIntVar(vm,name,aid); + } + + + //--- music id, tracks after arenas because of duplicate names + + const MusicInfo_t *mi; + for ( mi = music_info + MKW_N_MUSIC-1; mi >= music_info; mi-- ) + { + const uint id = mi->track < 0 ? mi->id : mi->track; + + ccp src = mi->name; + char *dest = name; + while ( ( *dest++ = toupper((int)*src++) ) != 0 ) + ; + DefineIntVar(vm,name,id); + if ( mi->flags & 2 ) + { + DefineIntVar(vm,name+1,id); + if ( mi->flags & 4 ) + { + name[1] = 'R'; + DefineIntVar(vm,name,id); + DefineIntVar(vm,name+1,id); + } + } + else if ( mi->flags & 4 ) + { + name[0] = 'R'; + DefineIntVar(vm,name,id); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: management /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_dollar +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = n_param; + res->mode = VAR_INT; + return ERR_OK; +} + +FuncParam_t FuncDollar; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_echo +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param>=0); + + uint i; + for ( i = 0; i < n_param; i++ ) + { + fprintf(stdlog," P.%-3u = ",i+1); + PrintV(stdlog,param+i,0); + fputc('\n',stdlog); + } + + AssignVar(res,param); + return ERR_OK; +} + +FuncParam_t FuncDollar; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_line +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->cur_file ? si->cur_file->line : 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_sourceLevel +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si ? si->n_files : 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_ifLevel +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->cur_file ? si->cur_file->if_level : 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_loopLevel +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->cur_file ? si->cur_file->loop_level : 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_loopCount +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + int loop_level = -1; + if (si) + { + DASSERT(si->cur_file); + if ( n_param > 0 ) + { + loop_level = GetIntV(param); + if ( loop_level < 0 ) + loop_level += MAX_LOOP_DEPTH; + } + loop_level = si->cur_file->loop_level - 1; + } + + res->i = loop_level >= 0 && loop_level < MAX_LOOP_DEPTH + ? si->cur_file->loop[loop_level].count_val + : -1; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_status +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + if (si) + { + res->mode = VAR_INT; + res->i = si->func_status; + } + else + res->mode = VAR_UNSET; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: source identification /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isKCL +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->kcl; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isKMP +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->kmp; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isLEX +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->lex; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isMDL +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = si && si->mdl; + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: macro support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_call_macro +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + //--- some tests + + if (!si) + { + res->mode = VAR_UNSET; + return ERR_OK; + } + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + static int depth = 0; + if ( depth > MAX_FUNCTION_DEPTH ) + { + si->total_err++; + ERROR0(ERR_WARNING, + "To many nested function calls (max=%u) [%s @%u]\n", + MAX_FUNCTION_DEPTH, sf->name, sf->line ); + res->mode = VAR_UNSET; + return ERR_WARNING; + } + depth++; + + //--- use macro body + + ScanMacro_t *macro = fpar->macro; + DASSERT(macro); + + const uint saved_n_files = si->n_files; + si->n_files = 0; + si->cur_file = &empty_scan_file; + ScanFile_t *newsf + = AddSF(si,macro->data,macro->data_size,macro->src_name,sf->revision,0); + DASSERT(newsf); + newsf->line = macro->line0; + VarMap_t *pvar = &newsf->pvar; + + //--- setup parameters + + uint i; + for ( i = 1; i <= n_param; i++ ) + { + char pname[20]; + snprintf(pname,sizeof(pname),"$%u",i); + Var_t *pv = InsertVarMap(pvar,pname,false,0,0); + DASSERT(pv); + AssignVar(pv,param++); + } + Var_t *var = InsertVarMap(pvar,"$N",false,0,0); + DASSERT(var); + AssignIntV(var,n_param); + + //--- scan source + + while (NextLineSI(si,false,true)) + ; + + //--- close source + + while (DropSF(si)) + ; + si->cur_file = sf; + si->n_files = saved_n_files; + + //--- get result and terminate + + AssignVar(res,&si->last_result); + depth--; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_result +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + if (si) + AssignVar(res,&si->last_result); + else + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_param +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + + const int idx = GetIntV(param); + if ( idx > 0 && si ) + { + DASSERT(si->cur_file); + const Var_t *var = FindVarMap(&si->cur_file->pvar,"$N",0); + if (var) + { + const int n = GetIntV(var); + if ( idx <= n ) + { + char varname[20]; + snprintf(varname,sizeof(varname),"$%u",idx); + var = FindVarMap(&si->cur_file->pvar,varname,0); + if (var) + { + AssignVar(res,var); + return ERR_OK; + } + } + } + } + + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isMacro +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + res->mode = VAR_INT; + res->i = 0; + if (si) + { + const Var_t *vp = FindVarMap(&si->macro,param->name,0); + if (vp) + { + ScanMacro_t *macro = vp->macro; + DASSERT(macro); + res->i = macro->is_function ? 2 : 1; + } + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isFunction +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + res->mode = VAR_INT; + res->i = FindVarMap(&parser_func,param->name,0) != 0; + if (si) + { + const Var_t *vp = FindVarMap(&si->macro,param->name,0); + if (vp) + { + ScanMacro_t *macro = vp->macro; + DASSERT(macro); + if (macro->is_function) + res->i |= 2; + } + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: variable status /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const Var_t * find_var ( ScanInfo_t * si, char * name, int *xyz ) +{ + DASSERT(name); + DASSERT(xyz); + + const Var_t *var = FindVarSI(si,name,false); + if (var) + { + *xyz = -1; + return var; + } + + const uint nlen = strlen(name); + if ( nlen > 2 + && name[nlen-2] == '.' + && name[nlen-1] >= 'X' + && name[nlen-1] <= 'Z' ) + { + name[nlen-2] = 0; + var = FindVarSI(si,name,false); + if ( var && var->mode == VAR_VECTOR ) + { + *xyz = name[nlen-1] - 'X'; + return var; + } + } + + *xyz = -1; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isDef +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + res->i = !var ? 0 : xyz < 0 ? 2 : 1; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isInt +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + res->i = !var || var->mode != VAR_INT ? 0 : var->i > 0 ? 2 : 1; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isFloat +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + res->i = !var || var->mode != VAR_DOUBLE ? 0 : var->d > 0.0 ? 2 : 1; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isVector +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + res->i = !var || var->mode != VAR_VECTOR ? 0 : var->d > 0.0 ? 2 : 1; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isScalar +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + if (!var) + res->i = 0; + else if ( var->mode == VAR_INT ) + res->i = var->i > 0 ? 2 : 1; + else if ( var->mode == VAR_DOUBLE ) + res->i = var->d > 0.0 ? 2 : 1; + else + res->i = 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isNumeric +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + res->i = var && var->mode != VAR_UNSET; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_isStr +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + res->i = !var || var->mode != VAR_STRING ? 0 : var->str_len > 0 ? 2 : 1; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_type +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + res->i = !var ? -1 : xyz >= 0 ? VAR_X+xyz : var->mode; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_var +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int xyz; + const Var_t *var = find_var(si,(char*)param->name,&xyz); + AssignVar( res, var && var->mode != VAR_UNSET ? var : param+1 ); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: type conversions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_int +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + const int i = GetIntV(param); + res->i = i; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_float +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + const double d = GetDoubleV(param); + res->d = d; + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_scalar +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + Var_t temp; + if ( param->mode == VAR_STRING ) + { + InitializeV(&temp); + ScanValueV(&temp,param,"scalar()"); + param = &temp; + } + + switch (param->mode) + { + case VAR_DOUBLE: + case VAR_VECTOR: + { + const double d = GetDoubleV(param); + res->d = d; + res->mode = VAR_DOUBLE; + } + break; + + case VAR_INT: + { + const int i = GetIntV(param); + res->i = i; + res->mode = VAR_INT; + } + break; + + default: + res->i = 0; + res->mode = VAR_INT; + break; + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_str +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + AssignStringVV(res,param); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_scanval +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + return ScanValueV(res,param,"scanVal()"); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_scanexpr +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + return ScanExprV(res,param,"scanExpr()"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: endian support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_be +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param==2); + + int i = GetIntV(param); + switch (GetIntV(param+1)) + { + case 1: res->int_mode = IMD_BE1; i &= 0xff; break; + case 2: res->int_mode = IMD_BE2; i &= 0xffff; break; + case 3: res->int_mode = IMD_BE3; i &= 0xffffff; break; + case 4: res->int_mode = IMD_BE4; i &= 0xffffffff; break; + case 5: res->int_mode = IMD_BE5; i &= 0xffffffffff; break; + case 6: res->int_mode = IMD_BE6; i &= 0xffffffffffff; break; + case 7: res->int_mode = IMD_BE7; i &= 0xffffffffffffff; break; + case 8: res->int_mode = IMD_BE8; break; + default: res->int_mode = IMD_BE0; break; + } + res->i = i; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_le +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param==2); + + int i = GetIntV(param); + switch (GetIntV(param+1)) + { + case 1: res->int_mode = IMD_LE1; i &= 0xff; break; + case 2: res->int_mode = IMD_LE2; i &= 0xffff; break; + case 3: res->int_mode = IMD_LE3; i &= 0xffffff; break; + case 4: res->int_mode = IMD_LE4; i &= 0xffffffff; break; + case 5: res->int_mode = IMD_LE5; i &= 0xffffffffff; break; + case 6: res->int_mode = IMD_LE6; i &= 0xffffffffffff; break; + case 7: res->int_mode = IMD_LE7; i &= 0xffffffffffffff; break; + case 8: res->int_mode = IMD_LE8; break; + default: res->int_mode = IMD_LE0; break; + } + res->i = i; + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: access functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_select +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int sel = GetIntV(param) + 1; + if ( sel < 1 ) + sel = 1; + else if ( sel >= n_param ) + sel = n_param-1; + + AssignVar(res,param+sel); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: access functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int getpos_beg ( int pos, int len ) +{ + if ( pos < 0 ) + { + pos += len; + return pos < 0 ? 0 : pos; + } + return pos < len ? pos : len; +} + +//----------------------------------------------------------------------------- + +static enumError extract_beg +( + struct Var_t * res, // store result here + struct Var_t * str, // string param + int p1, // start position + int p2 // length to extract +) +{ + ToStringV(str); + DASSERT( str->mode == VAR_STRING ) ; + p1 = getpos_beg(p1,str->str_len); + p2 = getpos_beg(p2,str->str_len); + const int len = p2 - p1; + AssignStringVS( res, str->str + p1, len<0 ? 0 : len ); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_left +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + return extract_beg(res,param,0,GetIntV(param+1)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_right +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + return extract_beg(res,param,-GetIntV(param+1),INT_MAX); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_mid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + int p1 = GetIntV(param+1); + int p2 = n_param > 2 && param[2].mode != VAR_UNSET + ? GetIntV(param+2) : param->str_len; + if ( p2 >= 0 ) + p2 += p1; + return extract_beg(res,param,p1,p2); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_extract +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + return extract_beg(res,param,GetIntV(param+1),GetIntV(param+2)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_remove +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + DASSERT( param->mode == VAR_STRING ) ; + + const int p1 = getpos_beg( GetIntV(param+1), param->str_len ); + const int p2 = getpos_beg( GetIntV(param+2), param->str_len ); + if ( p1 < p2 ) + AssignStringVS2(res,param->str,p1,param->str+p2,param->str_len-p2); + else + AssignStringVV(res,param); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: print() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct print_par_t +{ + int index; // index of next parameter: 1..n-1 + int n; // number of parameters + Var_t *list; // list of parameters + + // stats + int max_index; // index of max used parmeter + int select; // counter: index was selected +} +print_par_t; + +/////////////////////////////////////////////////////////////////////////////// + +static Var_t * get_print_par ( print_par_t *pp ) +{ + DASSERT(pp); + + if ( pp->max_index < pp->index ) + pp->max_index = pp->index; + + if ( pp->index >= pp->n ) + { + static Var_t temp = {0}; + ResetV(&temp); + return &temp; + } + + return pp->list + pp->index++; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp get_h_format ( double d ) +{ + const double a = fabs(d); + return !a || a < 1e10 && a >= 0.1 ? "%5.3f" : "%9.7e"; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_print +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT( param->mode == VAR_STRING ) ; + + if ( n_param < 1 || param->mode != VAR_STRING ) + { + AssignStringVS(res,0,0); + return ERR_WARNING; + } + + + //--- prepare vars + + DEFINE_VAR(collect); + collect.mode = VAR_STRING; + + const uint max_fw = 100; + + char fbuf[50]; // buffer for created format string + char buf[5000]; // working buffer + char *bufend = buf + sizeof(buf) - 3*max_fw - 20; + char *dest = buf; + + ccp format = param->str ? param->str : ""; + ccp end = format + param->str_len; + + print_par_t pp; + memset(&pp,0,sizeof(pp)); + pp.index = 1; + pp.n = n_param; + pp.list = param; + + bool warnings = si && !si->no_warn; + + + //--- main loop + + while ( format < end ) + { + noPRINT("->|%.*s|\n",(int)(end-format),format); + + if ( dest >= bufend ) + { + // buffer allmost full + AppendStringVS(&collect,buf,dest-buf); + dest = buf; + } + + if ( *format != '%' ) + { + *dest++ = *format++; + continue; + } + + format++; + if ( format < end && *format == '%' ) + { + *dest++ = *format++; + continue; + } + + + //--- now we have a print instruction + //--- scan parameter index + + if ( *format >= '1' && *format <= '9' ) + { + int index = 0; + ccp f = format; + while ( *f >= '0' && *f <= '9' ) + index = index * 10 + *f++ - '0'; + if ( *f == '$' ) + { + if ( index > 0 && index < n_param ) + { + pp.index = index; + pp.select++; + } + else if (warnings) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ERROR0(ERR_WARNING, + "Parameter %d not defined, available 1..%d [%s @%u]: %.*s\n", + index, pp.n-1, sf->name, sf->line, + (int)(f-format+2), format-1 ); + } + format = f+1; + } + } + + + //----- scan flags + + ccp flags_begin = format; + int alternative = 0; + char padding = ' '; + bool left_adjust = false; + char sign = 0; + bool right_sign = false; + bool grouping = false; + bool quote = false; + + for(;;) + { + switch(*format++) + { + case '#': // alternative format + alternative = 1; + break; + + case '0': // zero padding + padding = '0'; + break; + + case '-': // left adjusted + left_adjust = true; + break; + + case ' ': // blank before a positive number + if (!sign) + sign = ' '; + break; + + case '+': // '+' before a positive number (ignore blank above) + sign = '+'; + break; + + case '>': // (extension) + right_sign = true; + break; + + case '\'': // (extension) + grouping = true; + break; + + case '"': // (extension) + quote = true; + break; + + default: + goto end_scan_flags; + } + } + end_scan_flags:; + ccp flags_end = --format; + + + //----- scan field width + + int fw = 0; + if ( *format == '*' ) + { + format++; + const Var_t *v = get_print_par(&pp); + fw = GetIntV(v); + if ( fw < 0 ) + { + left_adjust = true; + fw = -fw; + } + } + else if ( *format >= '0' && *format <= '9' ) + { + while ( *format >= '0' && *format <= '9' ) + fw = fw * 10 + *format++ - '0'; + } + if ( fw > max_fw ) + fw = max_fw; + + + //----- scan precision + + int prec = -1; + if ( *format == '.' ) + { + format++; + prec = 0; + + if ( *format == '*' ) + { + format++; + const Var_t *v = get_print_par(&pp); + prec = GetIntV(v); + } + else + { + while ( *format >= '0' && *format <= '9' ) + prec = prec * 10 + *format++ - '0'; + } + if ( prec > 50 ) + prec = 50; + } + + + //-- scan variants + + int mode_must_match = 0; + bool force_vector = false; + bool enclose_vector = false; + for(;;) + { + switch(*format++) + { + case 'n': + if (!mode_must_match) + mode_must_match = 1; + break; + + case 'N': + mode_must_match = 2; + break; + + case 'v': + enclose_vector = true; + break; + + case 'V': + force_vector = true; + break; + + case 'l': + case 'z': + // ignored + break; + + default: + goto end_scan_variants; + } + } + end_scan_variants: + format--; + + + //-- scan conversion character + + Var_t *v = get_print_par(&pp); + char convert = *format++; + const bool human = convert == 'h'; + if ( human ) + { + switch(v->mode) + { + case VAR_UNSET: convert = '-'; break; + case VAR_INT: convert = 'd'; break; + case VAR_DOUBLE: + case VAR_VECTOR: break; // 'h' is ok + case VAR_STRING: convert = 's'; break; + } + } + else if ( convert == 'y' ) + { + switch(v->mode) + { + case VAR_UNSET: convert = 'n'; break; + case VAR_INT: convert = 'd'; break; + case VAR_DOUBLE: + case VAR_VECTOR: convert = 'a'; enclose_vector = true; break; + case VAR_STRING: convert = 's'; quote = true; break; + } + } + + noPRINT("FORMAT |%.*s| '%c' alt=%d, grp=%d, pad=%c, left=%d, sign%s=%c," + " fw=%d.%d, vec=%d,%d, idx=%d/%d/%d\n", + (int)(format-flags_begin), flags_begin, convert, + alternative, grouping, padding, left_adjust, + right_sign ? "/r" : "", sign ? sign : '0', + fw, prec, force_vector, enclose_vector, pp.index, pp.max_index, pp.n ); + + switch(convert) + { + //-------------------------------------------------- + + case 'b': + case 'o': + case 'x': + case 'X': + case 'd': + case 'i': + case 'u': + if ( !mode_must_match || v->mode == VAR_INT ) + { + if ( prec < 0 ) + prec = 1; + + int shift = 0; + uint mask = 0; + ccp hex = LoDigits; + int gstep = 4; + char gchar = ':'; + ccp alt = "0x"; + bool is_signed = false; + uint num = GetIntV(v); + + switch(convert) + { + case 'b': // binary (extension!) + shift = 1; + mask = 0x1; + gstep = 4; + gchar = ':'; + if (alternative) + { + alternative = 2; + alt = "0b"; + } + break; + + case 'o': // octal + shift = 3; + mask = 0x7; + gstep = 4; + gchar = ':'; + if ( alternative && num ) + { + alternative = 1; + alt = "0"; + } + else + alternative = 0; + break; + + case 'X': // hexadezimal with A-F + hex = HiDigits; + alt = "0X"; + // fall through + + case 'x': // hexadezimal with a-f + shift = 4; + mask = 0xf; + gstep = 4; + gchar = ':'; + if (alternative) + alternative = 2; + break; + + case 'd': + case 'i': + is_signed = true; + // fall through + case 'u': + gstep = 3; + gchar = ' '; + break; + } + + int gcount = grouping ? gstep : INT_MAX; + char *nend = buf + sizeof(buf) - 1; + char *nptr = nend; + + if (mask) + { + while ( prec-- > 0 || num ) + { + if (!--gcount) + { + gcount = gstep; + *--nptr = gchar; + } + *--nptr = hex[ num & mask ]; + num >>= shift; + DASSERT( nptr >= bufend ); + } + } + else // !mask + { + // decimal + alternative = 0; + if (is_signed) + { + if ( (int)num < 0 ) + { + sign = '-'; + num = -num; + } + if ( right_sign ) + { + *--nptr = sign ? sign : ' '; + sign = 0; + } + if (sign) + { + alternative = 1; + alt = &sign; + } + } + else + sign = 0; + + while ( prec-- > 0 || num ) + { + if (!--gcount) + { + gcount = gstep; + *--nptr = gchar; + } + lldiv_t ld = lldiv(num,10); + *--nptr = '0'+ld.rem; + num = ld.quot; + DASSERT( nptr >= bufend ); + } + + } // !mask + + int fill = fw - (nend-nptr) - alternative; + if ( grouping && padding == '0' ) + { + while ( fill-- > 0 ) + { + if ( !--gcount ) + { + gcount = gstep; + *--nptr = gchar; + if ( --fill < 0 ) + break; + } + *--nptr = '0'; + } + } + else if ( !left_adjust && padding == '0' ) + while ( fill-- > 0 ) + *--nptr = '0'; + + if (alternative) + { + PRINT("ALT[%u]: %2x %2x\n",alternative,alt[0],alt[1]); + alt += alternative; + while ( alternative-- > 0 ) + *--nptr = *--alt; + } + + if (!left_adjust) + while ( fill-- > 0 ) + *dest++ = padding; + + const uint len = nend - nptr; + memcpy(dest,nptr,len); + dest += len; + + if (left_adjust) + while ( fill-- > 0 ) + *dest++ = padding; + } + break; + + //-------------------------------------------------- + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + if ( !mode_must_match + || mode_must_match == 1 && ( v->mode == VAR_DOUBLE || v->mode == VAR_VECTOR ) + || ( force_vector ? v->mode == VAR_VECTOR : v->mode == VAR_DOUBLE ) + ) + { + // ***** 'double' wrapper to sprintf() ***** + + char *fptr = fbuf; + *fptr++ = '%'; + uint flags_len = flags_end - flags_begin; + if ( flags_len > sizeof(fbuf)-25 ) + flags_len = sizeof(fbuf)-25; + memcpy(fptr,flags_begin,flags_len); + fptr += flags_len; + + if ( fw < 0 ) + fw = 0; + + if ( prec < 0 ) + fptr += sprintf(fptr,"%u",fw); + else + fptr += sprintf(fptr,"%u.%u",fw,prec); + *fptr++ = convert; + *fptr = 0; + + if ( force_vector || v->mode == VAR_VECTOR ) + { + ToVectorV(v); + if (enclose_vector) + { + *dest++ = 'v'; + *dest++ = '('; + } + + dest = snprintfE(dest,buf+sizeof(buf),fbuf,v->x); *dest++ = ','; + dest = snprintfE(dest,buf+sizeof(buf),fbuf,v->y); *dest++ = ','; + dest = snprintfE(dest,buf+sizeof(buf),fbuf,v->z); + + if (enclose_vector) + *dest++ = ')'; + } + else + { + noPRINT("DOUBLE [%s] %g\n",fbuf,GetDoubleV(v)); + dest = snprintfE(dest,buf+sizeof(buf),fbuf,GetDoubleV(v)); + } + } + break; + + //-------------------------------------------------- + + case 'h': + { + char *start = buf + sizeof(buf) - 100; + char *ptr = start; + if ( force_vector || v->mode == VAR_VECTOR ) + { + ToVectorV(v); + if (enclose_vector) + { + *ptr++ = 'v'; + *ptr++ = '('; + } + + ptr = snprintfE(ptr,buf+sizeof(buf),get_h_format(v->x),v->x); + *ptr++ = ','; + ptr = snprintfE(ptr,buf+sizeof(buf),get_h_format(v->y),v->y); + *ptr++ = ','; + ptr = snprintfE(ptr,buf+sizeof(buf),get_h_format(v->y),v->z); + if (enclose_vector) + *ptr++ = ')'; + } + else + { + noPRINT("DOUBLE [%s] %g\n",fbuf,GetDoubleV(v)); + const double d = GetDoubleV(v); + ptr = snprintfE(ptr,buf+sizeof(buf),get_h_format(d),d); + } + + const int len = ptr - start; + int fill = fw - len; + if (!left_adjust) + while ( fill-- > 0 ) + *dest++ = ' '; + memmove(dest,start,len); + dest += len; + while ( fill-- > 0 ) + *dest++ = ' '; + } + break; + + + //-------------------------------------------------- + + case 's': + if ( !mode_must_match || v->mode == VAR_STRING ) + { + // write direct to output + AppendStringVS(&collect,buf,dest-buf); + dest = buf; + + ToStringV(v); + ccp str = v->str; + uint len = v->str_len; + + if ( quote || alternative ) + { + + PrintEscapedString(buf+1,sizeof(buf)-5, + v->str, v->str_len, CHMD__ALL, '"', &len ); + if (quote) + { + str = buf; + *buf = '"'; + } + else + str = buf+1; + } + + if ( prec > 0 ) + { + if (quote) + { + prec -= 2; + if ( prec < 1 ) + prec = 1; + } + if ( len > prec ) + len = prec; + } + + if ( len < fw && !left_adjust ) + AppendStringVC(&collect,' ',fw-len); + AppendStringVS(&collect,str,len); + if (quote) + AppendStringVC(&collect,'"',1); + if ( len < fw && left_adjust ) + AppendStringVC(&collect,' ',fw-len); + } + break; + + //-------------------------------------------------- + + case 'n': + dest = StringCopyE(dest,buf+sizeof(buf),"$NONE"); + break; + + case 'q': + warnings = false; + break; + + case '-': + // nothing to do + break; + + default: + // [[???]] error message + break; + } + } + + if (warnings) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + noPRINT("idx=%d, max=%d, n=%d, select=%d\n", + pp.index, pp.max_index, pp.n, pp.select ); + + if ( pp.max_index >= pp.n ) + ERROR0(ERR_WARNING, + "Access to non existing argument [%s @%u]: %s\n", + sf->name, sf->line, param->str ); + else if ( pp.max_index < pp.n-1 && !pp.select ) + ERROR0(ERR_WARNING, + "Only %u of %u arguments used [%s @%u]: %s\n", + pp.max_index, pp.n-1, sf->name, sf->line, param->str ); + } + + if ( dest > buf ) + AppendStringVS(&collect,buf,dest-buf); + MoveDataV(res,&collect); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: timer /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_sec +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = GetTimerMSec()/1000; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_mSec +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = GetTimerMSec(); + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_uSec +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + res->i = GetTimerUSec(); + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: basic vector /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_v +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + noPRINT("FUNC v(%p,%u,%p)\n",param,n_param,si); + + switch(n_param) + { + case 0: + res->x = res->y = res->z = 0.0; + break; + + case 1: + if ( param->mode == VAR_VECTOR ) + AssignVar(res,param); + else + { + res->x = GetDoubleV(param); + res->y = res->z = 0.0; + } + break; + + case 2: + res->x = GetXDoubleV(param); + res->y = 0.0; + res->z = GetZDoubleV(param+1); + break; + + default: + res->x = GetXDoubleV(param); + res->y = GetYDoubleV(param+1); + res->z = GetZDoubleV(param+2); + break; + } + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_v3 +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + res->x = res->y = res->z = GetDoubleV(param); + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_vx +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + res->x = GetXDoubleV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + res->y = param[1].y; + res->z = param[1].z; + } + else + { + res->y = 0.0; + res->z = 0.0; + } + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_vy +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + res->y = GetYDoubleV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + res->x = param[1].x; + res->z = param[1].z; + } + else + { + res->x = 0.0; + res->z = 0.0; + } + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_vz +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + res->z = GetZDoubleV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + res->x = param[1].x; + res->y = param[1].y; + } + else + { + res->x = 0.0; + res->y = 0.0; + } + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_vd +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + const double len = GetDoubleV(param); + const double angle = GetDoubleV(param+1) * (M_PI/180.0); + res->x = sin(angle) * len; + res->z = cos(angle) * len; + res->y = n_param > 2 ? GetYDoubleV(param+2) : 0.0; + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_x +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT(fpar); + DASSERT( fpar->user_id >= 0 && fpar->user_id <= 2 ); + + const Var_t *end_param; + for ( end_param = param + n_param; param < end_param; param++ ) + if ( param->mode == VAR_VECTOR ) + { + res->d = param->v[fpar->user_id]; + goto exit; + } + res->d = GetDoubleV(param-1); + + exit: + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: 2D vector /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_sideOfLine +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + ToVectorV(param); + ToVectorV(param+1); + ToVectorV(param+2); + const double dstat = SideOfLineXZ(param[0],param[1],param[2]); + res->i = dstat < 0.0 ? -1 : dstat > 0.0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_ptInConvexPolygon +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + uint count = 0; + + int n_pol = (int)n_param - 1; + if ( n_pol >= 3 ) + { + const uint max_pol = 100; + if ( n_pol > max_pol ) + n_pol = max_pol; + + double pol[max_pol*2], pts[2]; + + ToVectorV(param); + pts[0] = param->x; + pts[1] = param->z; + + double *p = pol; + uint i; + for ( i = n_pol; i > 0; i-- ) + { + ToVectorV(++param); + *p++ = param->x; + *p++ = param->z; + } + + int dir; + count = PointsInConvexPolygonD(pts,1,pol,n_pol,false,&dir); + si->func_status = -dir; + } + + res->i = count; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_ptsInConvexTri +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + uint count = 0; + + int n_pts = (int)n_param - 3; + if ( n_pts > 0 ) + { + const uint max_pts = 100; + if ( n_pts > max_pts ) + n_pts = max_pts; + + + double tri[3*2], pts[max_pts*2], *p; + uint i; + + for ( i = 3, p = tri; i > 0; i--, param++ ) + { + ToVectorV(param); + *p++ = param->x; + *p++ = param->z; + } + + for ( i = n_pts, p = pts; i > 0; i--, param++ ) + { + ToVectorV(param); + *p++ = param->x; + *p++ = param->z; + } + + int dir; + count = PointsInConvexPolygonD(pts,n_pts,tri,3,false,&dir); + si->func_status = -dir; + } + + res->i = count; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_ptsInConvexQuad +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=5); + + uint count = 0; + + int n_pts = (int)n_param - 3; + if ( n_pts > 0 ) + { + const uint max_pts = 100; + if ( n_pts > max_pts ) + n_pts = max_pts; + + + double quad[3*2], pts[max_pts*2], *p; + uint i; + + for ( i = 4, p = quad; i > 0; i--, param++ ) + { + ToVectorV(param); + *p++ = param->x; + *p++ = param->z; + } + + for ( i = n_pts, p = pts; i > 0; i--, param++ ) + { + ToVectorV(param); + *p++ = param->x; + *p++ = param->z; + } + + int dir; + count = PointsInConvexPolygonD(pts,n_pts,quad,4,false,&dir); + si->func_status = -dir; + } + + res->i = count; + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: 3D vector /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_dot +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + ToVectorV(param); + ToVectorV(param+1); + res->d = DotProd3(param[0],param[1]); + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_cross +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + ToVectorV(param); + ToVectorV(param+1); + CrossProd3x(*res,param[0],param[1]); + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_unit +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + double dx, dy, dz; + ToVectorV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + dx = param[1].x - param[0].x; + dy = param[1].y - param[0].y; + dz = param[1].z - param[0].z; + } + else + { + dx = param->x; + dy = param->y; + dz = param->z; + } + + const double len = sqrt ( dx*dx + dy*dy + dz*dz ); + if ( len < 1e-9 ) + res->x = res->y = res->z = 0.0; + else + { + res->x = dx / len; + res->y = dy / len; + res->z = dz / len; + } + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static double3 calced_normals[3]; + +//----------------------------------------------------------------------------- + +static enumError F_calcNormals +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + ToVectorV(param); + ToVectorV(param+1); + double3 *helper = 0; + if ( n_param > 2 && param[2].mode != VAR_UNSET ) + { + ToVectorV(param+2); + helper = ¶m[2].d3; + } + double r = n_param > 3 && param[3].mode != VAR_UNSET ? GetDoubleV(param+3) : 1.0; + res->d = CalcNormals(calced_normals,¶m[0].d3,¶m[1].d3,helper,r); + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +//----------------------------------------------------------------------------- + +static enumError F_getNormal +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + const uint idx = GetIntV(param); + if ( idx < 3 ) + { + res->x = calced_normals[idx].x; + res->y = calced_normals[idx].y; + res->z = calced_normals[idx].z; + res->mode = VAR_VECTOR; + } + else + res->mode = VAR_UNSET; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_len +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if ( param->mode == VAR_STRING ) + { + AssignIntV(res,param->str_len); + return ERR_OK; + } + + ToVectorV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + const double dx = param[1].x - param[0].x; + const double dy = param[1].y - param[0].y; + const double dz = param[1].z - param[0].z; + res->d = dx*dx + dy*dy + dz*dz; + } + else + res->d = param->x * param->x + + param->y * param->y + + param->z * param->z; + + if (!fpar->user_id) + res->d = sqrt(res->d); + + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_hLen +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + ToVectorV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + const double dx = param[1].x - param[0].x; + const double dz = param[1].z - param[0].z; + res->d = dx*dx + dz*dz; + } + else + res->d = param->x * param->x + param->z * param->z; + + if (!fpar->user_id) + res->d = sqrt(res->d); + + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_pos +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + if ( param[0].mode == VAR_VECTOR ) + { + ToVectorV(param+1); + ToVectorV(param+2); + res->x = param[1].x + param[0].x * ( param[2].x - param[1].x ); + res->y = param[1].y + param[0].y * ( param[2].y - param[1].y ); + res->z = param[1].z + param[0].z * ( param[2].z - param[1].z ); + res->mode = VAR_VECTOR; + } + else + { + double pos = GetDoubleV(param); + + if ( param[1].mode == VAR_VECTOR || param[2].mode == VAR_VECTOR ) + { + ToVectorV(param+1); + ToVectorV(param+2); + res->x = param[1].x + pos * ( param[2].x - param[1].x ); + res->y = param[1].y + pos * ( param[2].y - param[1].y ); + res->z = param[1].z + pos * ( param[2].z - param[1].z ); + res->mode = VAR_VECTOR; + } + else + { + double v1 = GetDoubleV(param+1); + res->d = v1 + GetDoubleV(param) * ( GetDoubleV(param+2) - v1 ); + res->mode = VAR_DOUBLE; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_bezier +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + //--- determine type of valculation, 1=float, 3=vector + + uint i, n_dim = 1; + for ( i = 0; i < n_param; i++ ) + if ( param[i].mode == VAR_VECTOR ) + { + n_dim = 3; + break; + } + + //--- convert parameters + + if ( n_dim == 1 ) + { + for ( i = 0; i < n_param; i++ ) + ToDoubleV(param+i); + ToDoubleV(res); + } + else + { + for ( i = 0; i < n_param; i++ ) + ToVectorV(param+i); + + ToVectorV(res); + } + + if ( n_param >= 5 ) + { + // Cubic bezier + // B(t) = (1-t)^3*P1 + 3*(1-t)^2*t*Pa + 3*(1-t)*t^2*Pb + t^3*P2 + + double *r = res->v; + double *t = param[0].v; + double *p1 = param[1].v; + double *pa = param[2].v; + double *pb = param[3].v; + double *p2 = param[4].v; + + int dim; + for ( dim = 0; dim < n_dim; dim++, t++ ) + { + const double t2 = 1.0 - *t; + *r++ = t2 * t2 * t2 * *p1++ + + 3* *t * t2 * t2 * *pa++ + + 3* *t * *t * t2 * *pb++ + + *t * *t * *t * *p2++; + } + } + else + { + // Quadratic bezier + // B(t) = (1-t)^2*P1 + 2*(1-t)*t*Pa + t^2*P2 + + double *r = res->v; + double *t = param[0].v; + double *p1 = param[1].v; + double *pa = param[2].v; + double *p2 = param[3].v; + + int dim; + for ( dim = 0; dim < n_dim; dim++, t++ ) + { + const double t2 = 1.0 - *t; + *r++ = t2 * t2 * *p1++ + + 2* *t * t2 * *pa++ + + *t * *t * *p2++; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_xRot // == xRot, yRot, zRot +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + DASSERT(fpar); + DASSERT( fpar->user_id >= 0 && fpar->user_id <= 2 ); + + uint r, a, b; + switch (fpar->user_id) + { + case 0: r = 0; a = 2; b = 1; ToVectorYV(param); break; + case 1: r = 1; a = 0; b = 2; ToVectorZV(param); break; + default: r = 2; a = 1; b = 0; ToVectorXV(param); break; + } + + const Var_t *origin = n_param > 2 ? ToVectorV(param+2) : &NullVector; + + const double da = param->v[a] - origin->v[a]; + const double db = param->v[b] - origin->v[b]; + const double rad = atan2(da,db) + GetDoubleV(param+1) * (M_PI/180.0); + const double len = sqrt(da*da+db*db); + + res->v[r] = param->v[r]; + res->v[a] = sin(rad) * len + origin->v[a]; + res->v[b] = cos(rad) * len + origin->v[b]; + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_rot +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + ToVectorV(param+0); + ToVectorV(param+1); + if (n_param > 2) + { + ToVectorV(param+2); + Rotate(¶m[2].d3,¶m[1].d3,¶m[0].d3,0,1); + } + else + Rotate(0,¶m[1].d3,¶m[0].d3,0,1); + + AssignVar(res,param); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_axisRot +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=4); + + ToVectorV(param+0); + ToVectorV(param+2); + ToVectorV(param+3); + AxisRotate(¶m[2].d3,¶m[3].d3,GetDoubleV(param+1),¶m[0].d3,0,1); + AssignVar(res,param); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_aDir +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT(fpar); + DASSERT( fpar->user_id >= 0 && fpar->user_id <= 2 ); + + uint a, b; + switch (fpar->user_id) + { + case 0: a = 2; b = 1; break; + case 1: a = 0; b = 2; break; + default: a = 1; b = 0; break; + } + + ToVectorV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + res->d = atan2( param[1].v[a] - param[0].v[a], + param[1].v[b] - param[0].v[b] ) * (180/M_PI); + } + else + res->d = atan2(param->v[a],param->v[b]) * (180/M_PI); + + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_dir +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + ToVectorV(param); + if ( n_param > 1 ) + { + ToVectorV(param+1); + res->d3 = CalcDirection3D(¶m->d3,¶m[1].d3); + } + else + res->d3 = CalcDirection3D(&NullVectorD,¶m->d3); + + res->mode = VAR_VECTOR; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_ptsInCuboid +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + ToVectorV(param+0); + ToVectorV(param+1); + + int count = 0; + struct Var_t *ptr = param + 2; + struct Var_t *end = param + n_param; + + if (!fpar->user_id) + { + // strict! + for ( ; ptr < end; ptr++ ) + { + ToVectorV(ptr); + if ( ptr->x >= param[0].x && ptr->x <= param[1].x + && ptr->y >= param[0].y && ptr->y <= param[1].y + && ptr->z >= param[0].z && ptr->z <= param[1].z + ) + { + count++; + } + } + } + else + { + // sloppy + for ( ; ptr < end; ptr++ ) + { + ToVectorV(ptr); + if ( ( param[0].x < param[1].x + ? ptr->x >= param[0].x && ptr->x <= param[1].x + : ptr->x >= param[1].x && ptr->x <= param[0].x ) + && ( param[0].y < param[1].y + ? ptr->y >= param[0].y && ptr->y <= param[1].y + : ptr->y >= param[1].y && ptr->y <= param[0].y ) + && ( param[0].z < param[1].z + ? ptr->z >= param[0].z && ptr->z <= param[1].z + : ptr->z >= param[1].z && ptr->z <= param[0].z ) + ) + { + count++; + } + } + } + + res->i = count; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_OverlapCubeTri +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=5); + + res->i = OverlapCubeTri( + &ToVectorV(param+0)->d3, + GetDoubleV(param+1), + &ToVectorV(param+2)->d3, + &ToVectorV(param+3)->d3, + &ToVectorV(param+4)->d3 ); + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: simple mathematical /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_abs +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + res->i = abs(param->i); + break; + + case VAR_DOUBLE: + res->d = fabs(param->d); + break; + + case VAR_VECTOR: + res->x = fabs(param->x); + res->y = fabs(param->y); + res->z = fabs(param->z); + break; + + case VAR_STRING: + DASSERT( res->mode == VAR_UNSET ); + break; + } + res->mode = param->mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_sign +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + res->i = param->i < 0 ? -1 : param->i > 0; + break; + + case VAR_DOUBLE: + res->d = param->d < 0.0 ? -1.0 : param->d > 0.0 ? 1.0 : 0.0; + break; + + case VAR_VECTOR: + res->x = param->x < 0.0 ? -1.0 : param->x > 0.0 ? 1.0 : 0.0; + res->y = param->y < 0.0 ? -1.0 : param->y > 0.0 ? 1.0 : 0.0; + res->z = param->z < 0.0 ? -1.0 : param->z > 0.0 ? 1.0 : 0.0; + break; + + case VAR_STRING: + DASSERT( res->mode == VAR_UNSET ); + break; + } + res->mode = param->mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_min +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT( VAR_VECTOR > VAR_DOUBLE ); + DASSERT( VAR_DOUBLE > VAR_INT ); + DASSERT( VAR_INT > VAR_UNSET ); + + uint i; + VarMode_t max_mode = VAR_UNSET, ref_idx = 0; + for ( i = 0; i < n_param; i++ ) + if ( max_mode < param[i].mode && isNumericVM(param[i].mode) ) + { + max_mode = param[i].mode; + ref_idx = i; + } + DASSERT( param[ref_idx].mode == max_mode ); + PRINT("MODE=%d, IDX=%d\n",max_mode,ref_idx); + + switch (max_mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + { + int min = param[ref_idx].i; + for (; n_param > 0; n_param--, param++ ) + if ( param->mode == VAR_INT && min > param->i ) + min = param->i; + res->i = min; + break; + } + + case VAR_DOUBLE: + { + double min = param[ref_idx].d; + for (; n_param > 0; n_param--, param++ ) + if ( param->mode != VAR_UNSET ) + { + const double temp = GetDoubleV(param); + if ( min > temp ) + min = temp; + } + res->d = min; + break; + } + + case VAR_VECTOR: + { + double xmin = param[ref_idx].x; + double ymin = param[ref_idx].y; + double zmin = param[ref_idx].z; + for (; n_param > 0; n_param--, param++ ) + switch(param->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + case VAR_DOUBLE: + { + const double temp = GetDoubleV(param); + if ( xmin > temp ) xmin = temp; + if ( ymin > temp ) ymin = temp; + if ( zmin > temp ) zmin = temp; + } + break; + + case VAR_VECTOR: + if ( xmin > param->x ) xmin = param->x; + if ( ymin > param->y ) ymin = param->y; + if ( zmin > param->z ) zmin = param->z; + break; + + case VAR_STRING: // ignore! + break; + } + res->x = xmin; + res->y = ymin; + res->z = zmin; + break; + } + + case VAR_STRING: // ignore! + break; + } + res->mode = max_mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_max +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + DASSERT( VAR_VECTOR > VAR_DOUBLE ); + DASSERT( VAR_DOUBLE > VAR_INT ); + DASSERT( VAR_INT > VAR_UNSET ); + + uint i; + VarMode_t max_mode = VAR_UNSET, ref_idx = 0; + for ( i = 0; i < n_param; i++ ) + if ( max_mode < param[i].mode && isNumericVM(param[i].mode) ) + { + max_mode = param[i].mode; + ref_idx = i; + } + DASSERT( param[ref_idx].mode == max_mode ); + PRINT("MODE=%d, IDX=%d\n",max_mode,ref_idx); + + switch (max_mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + { + int max = param[ref_idx].i; + for (; n_param > 0; n_param--, param++ ) + if ( param->mode == VAR_INT && max < param->i ) + max = param->i; + res->i = max; + break; + } + + case VAR_DOUBLE: + { + double max = param[ref_idx].d; + for (; n_param > 0; n_param--, param++ ) + if ( param->mode != VAR_UNSET ) + { + const double temp = GetDoubleV(param); + if ( max < temp ) + max = temp; + } + res->d = max; + break; + } + + case VAR_VECTOR: + { + double xmax = param[ref_idx].x; + double ymax = param[ref_idx].y; + double zmax = param[ref_idx].z; + for (; n_param > 0; n_param--, param++ ) + switch(param->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + case VAR_DOUBLE: + { + const double temp = GetDoubleV(param); + if ( xmax < temp ) xmax = temp; + if ( ymax < temp ) ymax = temp; + if ( zmax < temp ) zmax = temp; + } + break; + + case VAR_VECTOR: + if ( xmax < param->x ) xmax = param->x; + if ( ymax < param->y ) ymax = param->y; + if ( zmax < param->z ) zmax = param->z; + break; + + case VAR_STRING: // ignore! + break; + } + res->x = xmax; + res->y = ymax; + res->z = zmax; + break; + } + + case VAR_STRING: // ignore! + break; + } + res->mode = max_mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_minMax +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=3); + + switch(param->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + { + int i = param->i; + if ( isNumericVM(param[1].mode) ) + { + const int min = GetIntV(param+1); + if ( i < min ) + i = min; + } + if ( isNumericVM(param[2].mode) ) + { + const int max = GetIntV(param+2); + if ( i > max ) + i = max; + } + res->i = i; + break; + } + + case VAR_DOUBLE: + { + double d = param->d; + if ( isNumericVM(param[1].mode) ) + { + const double min = GetDoubleV(param+1); + if ( d < min ) + d = min; + } + if ( isNumericVM(param[2].mode) ) + { + const double max = GetDoubleV(param+2); + if ( d > max ) + d = max; + } + res->d = d; + break; + } + + case VAR_VECTOR: + { + double x = param->x; + double y = param->y; + double z = param->z; + if ( isNumericVM(param[1].mode) ) + { + const Var_t *min = ToVectorV(param+1); + if ( x < min->x ) x = min->x; + if ( y < min->y ) y = min->y; + if ( z < min->z ) z = min->z; + } + if ( isNumericVM(param[2].mode) ) + { + const Var_t *max = ToVectorV(param+2); + if ( x > max->x ) x = max->x; + if ( y > max->y ) y = max->y; + if ( z > max->z ) z = max->z; + } + res->x = x; + res->y = y; + res->z = z; + break; + } + + case VAR_STRING: // ignore! + break; + } + + res->mode = param->mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_mean +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + uint count = 0, vcount = 0; + double sum = 0.0; + double xsum = 0.0; + double ysum = 0.0; + double zsum = 0.0; + for ( ; n_param > 0; n_param--, param++ ) + { + switch(param->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + case VAR_DOUBLE: + { + count++; + sum += GetDoubleV(param); + } + break; + + case VAR_VECTOR: + vcount++; + xsum += param->x; + ysum += param->y; + zsum += param->z; + break; + + case VAR_STRING: // ignore! + break; + } + } + + if (vcount) + { + count += vcount; + res->x = ( sum + xsum ) / count; + res->y = ( sum + ysum ) / count; + res->z = ( sum + zsum ) / count; + res->mode = VAR_VECTOR; + } + else if (count) + { + res->d = sum / count; + res->mode = VAR_DOUBLE; + } + else + res->mode = VAR_UNSET; + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_trunc +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + case VAR_INT: + case VAR_STRING: + break; + + case VAR_DOUBLE: + res->d = trunc(param->d); + break; + + case VAR_VECTOR: + res->x = trunc(param->x); + res->y = trunc(param->y); + res->z = trunc(param->z); + break; + } + res->mode = param->mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_floor +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + case VAR_INT: + case VAR_STRING: + break; + + case VAR_DOUBLE: + res->d = floor(param->d); + break; + + case VAR_VECTOR: + res->x = floor(param->x); + res->y = floor(param->y); + res->z = floor(param->z); + break; + } + res->mode = param->mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_ceil +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + case VAR_INT: + case VAR_STRING: + break; + + case VAR_DOUBLE: + res->d = ceil(param->d); + break; + + case VAR_VECTOR: + res->x = ceil(param->x); + res->y = ceil(param->y); + res->z = ceil(param->z); + break; + } + res->mode = param->mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_round +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + case VAR_INT: + case VAR_STRING: + break; + + case VAR_DOUBLE: + res->d = round(param->d); + break; + + case VAR_VECTOR: + res->x = round(param->x); + res->y = round(param->y); + res->z = round(param->z); + break; + } + res->mode = param->mode; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_sqrt +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + res->mode = VAR_UNSET; + break; + + case VAR_INT: + res->d = sqrt(abs(param->i)); + res->mode = VAR_DOUBLE; + break; + + case VAR_DOUBLE: + res->d = sqrt(fabs(param->d)); + res->mode = VAR_DOUBLE; + break; + + case VAR_VECTOR: + res->x = sqrt(fabs(param->x)); + res->y = sqrt(fabs(param->y)); + res->z = sqrt(fabs(param->z)); + res->mode = VAR_VECTOR; + break; + + case VAR_STRING: // ignore! + break; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_log +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + bool have_p2 = n_param >= 2 && param[1].mode != VAR_UNSET; + double base = have_p2 ? log(GetDoubleV(param+1)) : 0.0; + + switch(param->mode) + { + case VAR_UNSET: + res->mode = VAR_UNSET; + break; + + case VAR_INT: + res->d = log(param->i); + if (have_p2) + res->d /= base; + res->mode = VAR_DOUBLE; + break; + + case VAR_DOUBLE: + res->d = log(param->d); + if (have_p2) + res->d /= base; + res->mode = VAR_DOUBLE; + break; + + case VAR_VECTOR: + res->x = log(param->x); + res->y = log(param->y); + res->z = log(param->z); + if (have_p2) + { + res->x /= base; + res->y /= base; + res->z /= base; + } + res->mode = VAR_VECTOR; + break; + + case VAR_STRING: // ignore! + break; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_exp +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + switch(param->mode) + { + case VAR_UNSET: + res->mode = VAR_UNSET; + break; + + case VAR_INT: + res->d = exp(param->i); + res->mode = VAR_DOUBLE; + break; + + case VAR_DOUBLE: + res->d = exp(param->d); + res->mode = VAR_DOUBLE; + break; + + case VAR_VECTOR: + res->x = exp(param->x); + res->y = exp(param->y); + res->z = exp(param->z); + res->mode = VAR_VECTOR; + break; + + case VAR_STRING: // ignore! + break; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_pow +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + double d = GetDoubleV(param+1); + switch(param->mode) + { + case VAR_UNSET: + res->mode = VAR_UNSET; + break; + + case VAR_INT: + res->d = pow(param->i,d); + res->mode = VAR_DOUBLE; + break; + + case VAR_DOUBLE: + res->d = pow(param->d,d); + res->mode = VAR_DOUBLE; + break; + + case VAR_VECTOR: + res->x = pow(param->x,d); + res->y = pow(param->y,d); + res->z = pow(param->z,d); + res->mode = VAR_VECTOR; + break; + + case VAR_STRING: // ignore! + break; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_random +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + switch( n_param ? param->mode : VAR_UNSET ) + { + case VAR_INT: + res->i = MyRandom(param->i); + res->mode = VAR_INT; + break; + + case VAR_DOUBLE: + res->d = MyRandom(0) * param->d / (double)RAND_MAX; + res->mode = VAR_DOUBLE; + break; + + case VAR_VECTOR: + res->x = MyRandom(0) * param->x / (double)RAND_MAX; + res->y = MyRandom(0) * param->y / (double)RAND_MAX; + res->z = MyRandom(0) * param->z / (double)RAND_MAX; + res->mode = VAR_VECTOR; + break; + + default: + res->d = MyRandom(0) / (double)RAND_MAX; + res->mode = VAR_DOUBLE; + break; + + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// parser functions: sin(), cos(), tan() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_sin +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if ( param->mode == VAR_VECTOR ) + { + res->x = sin( param->x * (M_PI/180.0) ); + res->y = sin( param->y * (M_PI/180.0) ); + res->z = sin( param->z * (M_PI/180.0) ); + res->mode = VAR_VECTOR; + } + else + { + res->d = sin( GetDoubleV(param) * (M_PI/180.0) ); + res->mode = VAR_DOUBLE; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_cos +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if ( param->mode == VAR_VECTOR ) + { + res->x = cos( param->x * (M_PI/180.0) ); + res->y = cos( param->y * (M_PI/180.0) ); + res->z = cos( param->z * (M_PI/180.0) ); + res->mode = VAR_VECTOR; + } + else + { + res->d = cos( GetDoubleV(param) * (M_PI/180.0) ); + res->mode = VAR_DOUBLE; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_tan +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if ( param->mode == VAR_VECTOR ) + { + res->x = tan( param->x * (M_PI/180.0) ); + res->y = tan( param->y * (M_PI/180.0) ); + res->z = tan( param->z * (M_PI/180.0) ); + res->mode = VAR_VECTOR; + } + else + { + res->d = tan( GetDoubleV(param) * (M_PI/180.0) ); + res->mode = VAR_DOUBLE; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_asin +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if ( param->mode == VAR_VECTOR ) + { + res->x = asin(param->x) * (180.0/M_PI); + res->y = asin(param->y) * (180.0/M_PI); + res->z = asin(param->z) * (180.0/M_PI); + res->mode = VAR_VECTOR; + } + else + { + res->d = asin(GetDoubleV(param)) * (180.0/M_PI); + res->mode = VAR_DOUBLE; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_acos +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if ( param->mode == VAR_VECTOR ) + { + res->x = acos(param->x) * (180.0/M_PI); + res->y = acos(param->y) * (180.0/M_PI); + res->z = acos(param->z) * (180.0/M_PI); + res->mode = VAR_VECTOR; + } + else + { + res->d = acos(GetDoubleV(param)) * (180.0/M_PI); + res->mode = VAR_DOUBLE; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_atan +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=1); + + if ( param->mode == VAR_VECTOR ) + { + res->x = atan(param->x) * (180.0/M_PI); + res->y = atan(param->y) * (180.0/M_PI); + res->z = atan(param->z) * (180.0/M_PI); + res->mode = VAR_VECTOR; + } + else + { + res->d = atan(GetDoubleV(param)) * (180.0/M_PI); + res->mode = VAR_DOUBLE; + } + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_atan2 +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param>=2); + + res->d = atan2(GetXDoubleV(param),GetZDoubleV(param+1)) * (180.0/M_PI); + res->mode = VAR_DOUBLE; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_if_engine +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + uint mask = 0; + + uint i; + for ( i = 0; i < n_param; i++ ) + { + int val = GetIntV(param+i); + if ( val >= 0 ) + mask |= val; + else + mask &= ~-val; + } + res->i = GOBJ_MIN_COND_ENGINE + ( mask & ENGM__ALL ); + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_if_random +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + + uint mask = 0; + + uint i; + for ( i = 0; i < n_param; i++ ) + { + int val = GetIntV(param+i); + if ( val == 0x7fffffff ) + mask = GOBJ_MASK_COND_RND; + else if ( val >= 1 && val <= GOBJ_MAX_RANDOM ) + mask |= 1 << val-1; + else if ( val >= -GOBJ_MAX_RANDOM && val <= -1 ) + mask &= ~(1 << -1-val); + } + res->i = GOBJ_MIN_COND_RND + mask; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_if_test +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param==2); + DASSERT(fpar); + + res->i = GOBJ_MIN_COND_TEST + | ( GetIntV(param+0) & 0x0f ) << 6 + | ( GetIntV(param+1) & 0x1f ) << 1; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_chat_n_races +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(n_param==1); + DASSERT(fpar); + + const int val = GetIntV(param+0); + res->i = val >= 1 && val <= CHATMD_MAX_RACE ? val + (CHATMD_N_RACE_1-1) : 0; + res->mode = VAR_INT; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError F_chat_vehicles +( + struct Var_t * res, // store result here + struct Var_t * param, // parameters, modified + uint n_param, // number of parameters + struct ScanInfo_t * si, // not NULL: use for error messages + const FuncParam_t * fpar // pointer to user parameter +) +{ + DASSERT(res); + DASSERT(param); + DASSERT(fpar); + + uint i, vehicles = 0; + for ( i = 0; i < n_param; i++ ) + { + int val = GetIntV(param+i); + if ( val < 0 ) + { + val = -val; + if ( val & CHATVEH_ANY_SIZE ) + val ^= CHATVEH_ANY_SIZE; + if ( val & CHATVEH_ANY_TYPE ) + val ^= CHATVEH_ANY_TYPE; + } + + if ( val <= CHATVEH_ANY || val >= CHATMD_VEHICLE_BEG && val <= CHATMD_VEHICLE_END ) + vehicles |= val & CHATVEH_ANY; + } + + res->i = vehicles + CHATMD_VEHICLE_BEG; + res->mode = VAR_INT; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// function table /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const struct FuncTable_t func_tab[] = +{ + //----- management + + { 0, MAX_FUNC_PARAM, "$", F_dollar, 0, + "int", "$(...)", + "This debug function returns the number of parameters." }, + + { 1, MAX_FUNC_PARAM, "ECHO", F_echo, 0, + "*", "echo(p1,...)", + "This debug function prints all parameters, each in a separate line," + " and returns the first parameter." }, + + { 0, 0, "LINE", F_line, 0, + "int", "line()", + "This debug function returns the current line number." }, + + { 0, 0, "SOURCELEVEL", F_sourceLevel, 0, + "int", "sourceLevel()", + "This debug function returns the current source level" + " (number of open files and macros)." }, + + { 0, 0, "IFLEVEL", F_ifLevel, 0, + "int", "ifLevel()", + "This debug function returns the current IF..ENDIF level" + " (number of active '@if's)." }, + + { 0, 0, "LOOPLEVEL", F_loopLevel, 0, + "int", "loopLevel()", + "This debug function returns the current loop level" + " (number of active loops)." }, + + { 0, 1, "LOOPCOUNT", F_loopCount, 0, + "int", "loopCount([level])", + "This debug function returns the loop counter of the specified loop level." + " If 'level' is omitted, the loop count of the current loop is returned." }, + + { 0, 0, "STATUS", F_status, 0, + "int", "status()", + "Some functions support a return value and an additionally status." + " This function will return the last set status." }, + + //----- source identfication + + { 0, 0, "ISKCL", F_isKCL, 0, + "int", "isKCL()", + "Returns 2, if the source is a KCL or OBJ" + " named 'course.kcl' or 'course.txt' or 'course.txt.kcl'," + " 1 if the source is any other KCL or OBJ, and 0 else." }, + + { 0, 0, "ISKMP", F_isKMP, 0, + "int", "isKMP()", + "Returns 2, if the source is a KMP (binary or text)" + " named 'course.kmp' or 'course.txt' or 'course.txt.kmp'," + " 1 if the source is any other KMP (binary or text), and 0 else." }, + + { 0, 0, "ISLEX", F_isLEX, 0, + "int", "isLEX()", + "Returns 2, if the source is a LEX (binary or text)" + " named 'course.lex' or 'course.txt' or 'course.txt.lex'," + " 1 if the source is any other LEX (binary or text), and 0 else." }, + + { 0, 0, "ISMDL", F_isMDL, 0, + "int", "isMDL()", + "Returns 1, if the source is a MDL (binary or text)." + " Otherwise it returns 0." }, + + + //----- macro support + + { 0, 0, "RESULT", F_result, 0, + "*", "result()", + "Returns the last include, macro or function result." + " The value is usually '$NONE', unless the command '@RETURN' is used." }, + + { 1, 1, "PARAM", F_param, 0, + "*", "param(index)", + "Returns the value of the macro or function parameter" + " with the entered 1-based index." + " Therefor the private variable '$N' and one of '$1', '$2', ... are read." }, + + { -1, 1, "ISMACRO", F_isMacro, 0, + "int", "isMacro(name)", + "Returns 2, if 'name' is defined as user function," + " or 1, if 'name' is defined as simple macro." + " Otherwise it's not a macro or user function and 0 is returned." }, + + { -1, 1, "ISFUNCTION", F_isFunction, 0, + "int", "isFunction(name)", + "Returns 1, if 'name' is defined as system function," + " or 2, if 'name' is defined as user function," + " or 3, if 'name' is defined as system and as user function." + " Otherwise it's not a function and 0 is returned." }, + + + //----- variable status + + { -1, 1, "ISDEF", F_isDef, 0, + "int", "isDef(name)", + "Returns 2, if variable 'name' is defined," + " or 1, if a variable with the base name is defined as vector," + " or 0, if it is not defined." }, + + { -1, 1, "ISINT", F_isInt, 0, + "int", "isInt(name)", + "Returns 2, if variable 'name' is defined as an integer with a value >0," + " or 1, if it is an integer with a value <=0." + " Otherwise it returns 0." }, + + { -1, 1, "ISFLOAT", F_isFloat, 0, + "int", "isFloat(name)", + "Returns 2, if variable 'name' is defined as float with a value >0," + " or 1, if it is a float with a value <=0." + " Otherwise it returns 0." }, + + { -1, 1, "ISVECTOR", F_isVector, 0, + "int", "isVector(name)", + "Returns 1, if variable 'name' is defined as vector." + " Otherwise it returns 0." }, + + { -1, 1, "ISSCALAR", F_isScalar, 0, + "int", "isScalar(name)", + "Returns 2, if variable 'name' is an integer or float with a value >0," + " or 1, if it is an integer or float with a value <=0." + " Otherwise it returns 0." }, + + { -1, 1, "ISNUMERIC", F_isNumeric, 0, + "int", "isNumeric(name)", + "Returns 1, if variable 'name' is an integer, float or vector." + " Otherwise it returns 0." }, + + { -1, 1, "ISSTR", F_isStr, 0, + "int", "isStr(name)", + "Returns 2, if variable 'name' is defined as string with 1 or more characters," + " or 1, if it is a string without characters." + " Otherwise it returns 0." }, + + { -1, 1, "TYPE", F_type, 0, + "int", "type(name)", + "Returns the type of the variable 'name'" + " (TYPE$UNSET, TYPE$INT, TYPE$FLOAT, TYPE$VECTOR, TYPE$STR, TYPE$X, TYPE$Y, TYPE$Z)" + " or value 'TYPE$UNDEF', if it is not defined." + " It is guaranteed, that 'TYPE$UNDEF < TYPE$UNSET < all_others'." }, + + { -2, 2, "VAR", F_var, 0, + "*", "var(name,val)", + "If variable 'name' is defined and valid, return its value." + " Otherwise return 'val'." }, + + + //----- type conversions + + { 1, 1, "INT", F_int, 0, + "int", "int(val)", + "Converts 'val' to an integer value. Strings are scanned for an integer." }, + + { 1, 1, "FLOAT", F_float, 0, + "float", "float(val)", + "Converts 'val' to a float value. Strings are scanned for a float." }, + + { 1, 1, "SCALAR", F_scalar, 0, + "scalar", "scalar(val)", + "If 'val' is a string, it is scanned for a number and converted first. " + "If 'val' is a float or a vector, a float value is returned." + " Otherwise an integer value is returned." }, + + { 1, 1, "STR", F_str, 0, + "str", "str(val)", + "Converts 'val' to a string value." }, + + { 1, 1, "SCANVAL", F_scanval, 0, + "*", "scanVal(val)", + "If 'val' is a string, scan it for a value." + " Otherwise return 'val'." + }, + + { 1, 1, "SCANEXPR", F_scanexpr, 0, + "*", "scanExpr(val)", + "If 'val' is a string, scan it for an expression." + " Otherwise return 'val'." + }, + + + //---- endian support + + { 2, 2, "BE", F_be, 0, + "int", "be(val,n)", + "Convert 'val' to integer, limit it to 'n' bytes (1-8)" + " and mark it for big endian usage." }, + + { 2, 2, "LE", F_le, 0, + "int", "le(val,n)", + "Convert 'val' to integer, limit it to 'n' bytes (1-8)" + " and mark it for little endian usage." }, + + + //----- access functions + + { 2, MAX_FUNC_PARAM, "SELECT", F_select, 0, + "*", "select(sel,p0,p1,...,pN)", + "First convert 'sel' to an integer." + " If 'sel' is less or equal 0, 'p0' is returned." + " If 'sel' is greater or equal N, 'pN' is returned." + " Otherwise 'sel' is between 0 and N and 'p(sel)' is returned." }, + + + //----- basic string functions + + { 2, 2, "LEFT", F_left, 0, + "str", "left(str,len)", + "Extract the left LEN characters of string STR." + " If LEN<0, then extract all but not the last -LEN characters." }, + + { 2, 2, "RIGHT", F_right, 0, + "str", "right(str,len)", + "Extract the right LEN characters of string STR." + " If LEN<0, then extract all but not the first -LEN characters." }, + + { 2, 3, "MID", F_mid, 0, + "str", "mid(str,pos[,len])", + "Extract LEN characters of string STR beginning at postition POS." + " If POS<0, then a position relative to the end of STR is used." + " If LEN<0, then extract all but not the last -LEN characters." + " If LEN is not set, then extract all characters until end of string." }, + + { 3, 3, "EXTRACT", F_extract, 0, + "str", "extract(str,pos1,pos2)", + "Extract all characters of string STR" + " including position POS1 and excluding position POS2." + " Positions <0 are relative to the end of STR and adjusted." + " If POS1≥POS2, an empty string is returned." }, + + { 3, 3, "REMOVE", F_remove, 0, + "str", "remove(str,pos1,pos2)", + "Create a copy of string STR and remove all characters" + " including position POS1 and excluding position POS2." + " Positions <0 are relative to the end of STR and adjusted." + " If POS1≥POS2, nothing is removed and the complete STR is returned." }, + + { 1, MAX_FUNC_PARAM, "PRINT", F_print, 0, + "str", "print(format,...)", + "Create a string like sprintf() of other programming languages." + " See https://szs.wiimm.de/doc/print for details." }, + + + //----- timer + + { 0, 0, "SEC", F_sec, 0, + "int", "sec()", + "Returns the number of seconds since an unspecific timer start." + " Use differences between 2 calls to get the elapsed time." + " The 3 functions sec(), mSec() and uSec() use the same time base." }, + + { 0, 0, "MSEC", F_mSec, 0, + "int", "mSec()", + "Returns the number of milliseconds since an unspecific timer start." + " Use differences between 2 calls to get the elapsed time." + " The 3 functions sec(), mSec() and uSec() use the same time base." }, + + { 0, 0, "USEC", F_uSec, 0, + "int", "uSec()", + "Returns the number of microseconds since an unspecific timer start." + " Use differences between 2 calls to get the elapsed time." + " This timer has an overflow at about 35 minutes." + " The 3 functions sec(), mSec() and uSec() use the same time base." }, + + + //----- basic vector functions + + { 0, 3, "V", F_v, 0, + "vector", "v([val])", + "Defines a vector." + " If 'val' is a vector, it is copied." + " Otherwise 'val' is used for the x coordinate and y and z are set to 0.0." + " If 'val' is not set, 0.0 is used." + "\n " + " This function is used if an internal conversion to a vector is needed." }, + { 0, 0, 0, F_v, 0, + "vector", "v(x[,y],z)", + "Defines a vector by using the entered coordinates." + " If 'y' is not set, use 0.0 instead." + " If 'x', 'y' or 'z' is a vector itself, the corespondent coordinate is used." }, + + { 1, 1, "V3", F_v3, 0, + "vector", "v3(val)", + "Create a vector with all 3 components equal to 'val'" }, + + { 1, 2, "VX", F_vx, 0, + "vector", "vx(x[,v])", + "Read the vector 'v' and replace the x component by 'x'." + " If 'x' is a vector, its x component is used." + " If 'v' is not set, use v(0,0,0) instead." }, + + { 1, 2, "VY", F_vy, 0, + "vector", "vy(y[,v])", + "Read the vector 'v' and replace the y component by 'y'." + " If 'y' is a vector, its y component is used." + " If 'v' is not set, use v(0,0,0) instead." }, + + { 1, 2, "VZ", F_vz, 0, + "vector", "vz(z[,v])", + "Read the vector 'v' and replace the z component by 'z'." + " If 'z' is a vector, its z component is used." + " If 'v' is not set, use v(0,0,0) instead." }, + + { 2, 3, "VD", F_vd, 0, + "vector", "vd(len,deg[,y])", + "Creates a vector of length 'len' and horizontal angle 'deg' (degree)." + " Value 'y' (if not set: 0.0) is used for the height." }, + + { 1, MAX_FUNC_PARAM, "X", F_x, 0, + "float", "x(val_1,...,val_n)", + "Scan all parameters (at least 1 must exist) and find the first vector." + " If found, return its x component." + " If no vector exists, return the last value 'val_n'." + "\n " + " Confirming the rules above, 'x(val)' means:" + " If 'val' is a vector, return its x component." + " Otherwise convert 'val' to a float and return it." }, + + { 1, MAX_FUNC_PARAM, "Y", F_x, 1, + "float", "y(val_1,...,val_n)", + "Scan all parameters (at least 1 must exist) and find the first vector." + " If found, return its y component." + " If no vector exists, return the last value 'val_n'." + "\n " + " Confirming the rules above, 'y(val)' means:" + " If 'val' is a vector, return its y component." + " Otherwise convert 'val' to a float and return it." }, + + { 1, MAX_FUNC_PARAM, "Z", F_x, 2, + "float", "z(val_1,...,val_n)", + "Scan all parameters (at least 1 must exist) and find the first vector." + " If found, return its z component." + " If no vector exists, return the last value 'val_n'." + "\n " + " Confirming the rules above, 'z(val)' means:" + " If 'val' is a vector, return its z component." + " Otherwise convert 'val' to a float and return it." }, + + + //----- 2D vector functions + + { 3, 3, "SIDEOFLINE", F_sideOfLine, 0, + "int", "sideOfLine(a,b,pt)", + "All parameters are converted into vectors," + " but only the x and z coordinates are used." + " Vectors 'a' and 'b' define a line (from a to b)." + " The function returns -1, if the point 'pt' is on the left side of the line," + " or +1, if the point is on the right side," + " or 0 if the point is on the line." }, + + { 4, 101, "PTINCONVEXPOLYGON", F_ptInConvexPolygon, 0, + "int", "ptInConvexPolygon(pt,p1,..,pN)", + "All parameters are converted into vectors," + " but only the x and z coordinates are used." + " 'p1..pN' are up to 100 vertices of a convex polygon." + " The function returns 1, if the point 'pt' is inside" + " the polygon (including the lines), and 0 otherwise." + "\n " + " Function status() will return the direction of the polygon:" + " -1 for counterclockwise, +1 for clockwise and 0 for unknown." }, + + { 4, 103, "PTSINCONVEXTRI", F_ptsInConvexTri, 0, + "int", "ptsInConvexTri(t1,t2,t3,pt1,...,ptN)", + "All parameters are converted into vectors," + " but only the x and z coordinates are used." + " 't1..t3' define a convex triangle," + " and 'pt1..ptN' is a list of up to 100 points." + " The functions returns the number of points, that are inside the triangle." + " Points on the line are counted as inside too." + "\n " + " Function status() will return the direction of the triangle:" + " -1 for counterclockwise, +1 for clockwise and 0 for unknown." }, + + { 5, 104, "PTSINCONVEXQUAD", F_ptsInConvexQuad, 0, + "int", "ptsInConvexQuad(q1,q2,q3,q4,pt1,...,ptN)", + "All parameters are converted into vectors," + " but only the x and z coordinates are used." + " 'q1..q4' define a convex quadrilateral," + " and 'pt1..ptN' is a list of up to 100 points." + " The functions returns the number of points, that are inside the quadrilateral." + " Points on the line are counted as inside too." + "\n " + " Function status() will return the direction of the quadrilateral:" + " -1 for counterclockwise, +1 for clockwise and 0 for unknown." }, + + + //----- 3D vector functions + + { 2, 2, "DOT", F_dot, 0, + "float", "dot(v1,v2)", + "Returns the dot product of the 2 vectors 'v1' and 'v2'." }, + + { 2, 2, "CROSS", F_cross, 0, + "vector", "cross(v1,v2)", + "Returns the cross product of the 2 vectors 'v1' and 'v2'." }, + + { 1, 2, "UNIT", F_unit, 0, + "vector", "unit(v1[,v2])", + "Returns the unit vector of the vector 'v1'." + " If 'v2' is set, then the unit vector for the difference 'v2-v1' is returned." }, + + { 2, 4, "CALCNORMALS", F_calcNormals, 0, + "float", "calcNormals(p1,p2[,helper][,r]])", + "This function calculates 3 right-angled normals (index 0..2) " + " for the vector 'p1..p2', 'normal[0]' is the direction of this vector." + " Is 'helper' is set, it is used to determine the direction of normal[1]." + " All 3 normals are multiplied by the factor 'r'." + " The function returns the distance between 'p1' and 'p2'." + " To get the normals, use function getNormal()." }, + + { 1, 1, "GETNORMAL", F_getNormal, 0, + "vector", "getNormal(index)", + "This function returns the normal 'index' (0..2) of the last call" + " of function calcNormals()." + " If 'index' is invalid, $NONE is returned." }, + + { 1, 2, "LEN", F_len, 0, + "int/flt", "len(v1[,v2])", + "If 'v1' is a string, then 'v2' is ignored and the length of the string" + " is returned as integer." + " Otherwise the length of the vector 'v1' is returned as float." + " If 'v2' is set, the distance of both points is returned." }, + + { 1, 2, "LEN2", F_len, 2, + "float", "len2(v1[,v2])", + "Returns the square of the length of the vector 'v1'." + " If 'v2' is set, the square distance of both points is returned." }, + + { 1, 2, "HLEN", F_hLen, 0, + "float", "hLen(v1[,v2])", + "Returns the horizontal length of the vector 'v1' (ignoring the y component)." + " If 'v2' is set, the horizontal distance of both points is returned." }, + + { 1, 2, "HLEN2", F_hLen, 2, + "float", "hLen2(v1[,v2])", + "Returns the square of the horizontal length of the vector 'v1'" + " (ignoring the y component)." + " If 'v2' is set, the horizontal square distance of both points is returned." }, + + { 3, 3, "POS", F_pos, 0, + "flt|vec", "pos(pos,p1,p2)", + "This function returns the relative position 'pos' on the axis 'p1' to 'p2'" + " by calculating 'p1+pos*(p2-p1)'." + " If one of the 3 arguments is a vector, the result is also a vector." }, + + { 4, 5, "BEZIER", F_bezier, 0, + "flt|vec", "bezier(pos,v1,va[,vb],v2)", + "Calculate the position 'pos' on a bezier curve going from 'v1' to 'v2'." + " 'va' and 'vb' are helper points." + " If 'vb' is not set, a quadratic bezier curve is calculated." + " Otherwise 'va' and 'vb' are set and a cubic bezier curve is calculated." + " Position 0.0 returns 'v1' and position 1.0 'v2'." + " All position values between 0.0 and 1.0 return a point of the calculated" + " bezier curve between points 'v1' and 'v2'." + " If 'pos' is a vector, different positions for each coordinate are used." + "\n " + " If any parameter is a vector, all parameters are converted to vectors and" + " the result is a vector. Otherwise all parameters are converted to" + " floats and the result is a float." }, + + { 2, 3, "XROT", F_xRot, 0, + "vector", "xRot(pt,deg[,origin])", + "The point 'pt' is rotated around the x-axis of 'origin' by 'deg' degree." + " If 'pt' is a scalar, 'vy(pt)' is used." + " If 'origin' is not set, v(0,0,0) is used." }, + + { 2, 3, "YROT", F_xRot, 1, + "vector", "yRot(pt,deg[,origin])", + "The point 'pt' is rotated around the y-axis of 'origin' by 'deg' degree." + " If 'pt' is a scalar, 'vz(pt)' is used." + " If 'origin' is not set, v(0,0,0) is used." + " This is the classical horizontal rotation." }, + + { 2, 3, "ZROT", F_xRot, 2, + "vector", "zRot(pt,deg[,origin])", + "The point 'pt' is rotated around the z-axis of 'origin' by 'deg' degree." + " If 'pt' is a scalar, 'vx(pt)' is used." + " If 'origin' is not set, v(0,0,0) is used." }, + + { 2, 3, "HROT", F_xRot, 1, + "vector", "hRot(pt,deg[,origin])", + "'hRot()' is the old name for 'yRot()'." + " The point 'pt' is horizontal rotated around 'origin' by 'deg' degree." + " If 'pt' is a scalar, 'vz(pt)' is used." + " If 'origin' is not set, v(0,0,0) is used." }, + + { 2, 3, "ROT", F_rot, 0, + "vector", "rot(pt,deg_vector[,origin])", + "The point 'pt' is rotated around 'origin'." + " The rotation is done for the x-, y- and z-axis in this order." + " All parameters are converted to vectors before the operation." + " If 'origin' is not set, v(0,0,0) is used." }, + + { 4, 4, "AXISROT", F_axisRot, 0, + "vector", "axisRot(pt,deg,axis1,axis2)", + "The point 'pt' is rotated around the axis 'axis1->axis2'" + " by 'deg' degree, which is a scalar." + " The other 3 parameters are converted to vectors before operation." }, + + { 1, 2, "XDIR", F_aDir, 0, + "float", "xDir([v1,]v2)", + "The functions returns the direction in degree of point 'v2'" + " relative to the x-axis of point 'v1'." + " If 'v1' is not set, v(0,0,0) is used." }, + + { 1, 2, "YDIR", F_aDir, 1, + "float", "yDir([v1,]v2)", + "The functions returns the direction in degree of point 'v2'" + " relative to the y-axis of point 'v1'." + " If 'v1' is not set, v(0,0,0) is used." }, + + { 1, 2, "ZDIR", F_aDir, 2, + "float", "zDir([v1,]v2)", + "The functions returns the direction in degree of point 'v2'" + " relative to the z-axis of point 'v1'." + " If 'v1' is not set, v(0,0,0) is used." }, + + { 1, 2, "HDIR", F_aDir, 1, + "float", "hDir([v1,]v2)", + "'hDir()' is the old name for 'yDir()'." + " The horizontal direction in degree from position 'v1' to 'v2' is calculated." + " If 'v1' is not set, v(0,0,0) is used." }, + + { 1, 2, "DIR", F_dir, 0, + "float", "dir([v1,]v2)", + "The 3D direction in degree from position 'v1' to 'v2' is calculated." + " If 'v1' is not set, v(0,0,0) is used." + " The Z coordiante of the result is always 0.0." }, + + { 3, MAX_FUNC_PARAM, "PTSINCUBOID", F_ptsInCuboid, 0, + "int", "ptsInCuboid(cube_min,cube_max,pt1,...)", + "All parameters are converted to vectors." + " 'cube_*' describe 2 diagonal corners of a rectangular cuboid," + " assuming that 'cube_min<=cube_max' is true for each coordinate." + " The functions returns the number of points ('pt1', 'pt2', ...)" + " that are inside of the cube including the border." }, + + { 3, MAX_FUNC_PARAM, "PTINCUBOID", F_ptsInCuboid, 0, // altenative name + 0,0,0 }, + + { 3, MAX_FUNC_PARAM, "PTSINCUBOIDS", F_ptsInCuboid, 1, + "int", "ptsInCuboidS(cube1,cube2,pt1,...)", + "This is the sloppy version of ptInCuboid():" + " All parameters are converted to vectors." + " 'cube*' describe any 2 diagonal corners of a rectangular cuboid." + " The functions returns the number of points ('pt1', 'pt2', ...)" + " that are inside of the cube including the border." }, + + { 3, MAX_FUNC_PARAM, "PTINCUBOIDS", F_ptsInCuboid, 1, // altenative name + 0,0,0 }, + + #ifdef TEST // [[2do]] + { 5, 5, "OVERLAPCUBETRI", F_OverlapCubeTri, 1, + "int", "OverlapCubeTri(cube_mid,cube_width,pt1,pt2,pt3)", + "'cube_mid' is the middle point of a cube" + " and 'cube_width' the length of its edges." + " 'pt1'..'pt3' are the 3 points of the triangle." + " The functions returns 1, if the cube and the triangle" + " are overlapped. Otherwise 0 is returned." }, + #endif + + + //----- simple mathematical functions + + { 1, 1, "ABS", F_abs, 0, + "*", "abs(val)", + "Returns the absolute value of 'val'." + " If 'val' is a vector, the absolute value of each component is calculated." + " The return type is identical to the type of 'val'." }, + + { 1, 1, "SIGN", F_sign, 0, + "*", "sign(val)", + "Returns the sign of 'val': -1 if 'val<0'; 0, if 'val==0'; +1 if 'val>0'." + " If 'val' is a vector, the sign of each component is calculated." + " The return type is identical to the type of 'val'." }, + + { 1, MAX_FUNC_PARAM, "MIN", F_min, 0, + "*", "min(p1,...)", + "Returns the minimum value of all parameters." + " If at least one parameter is a vector, the result is a vector too" + " and the minimum value of each component is calculated." }, + + { 1, MAX_FUNC_PARAM, "MAX", F_max, 0, + "*", "max(p1,...)", + "Returns the maximum value of all parameters." + " If at least one parameter is a vector, the result is a vector too" + " and the maximum value of each component is calculated." }, + + { 3, 3, "MINMAX", F_minMax, 0, + "*", "minMax(val,minval,maxval)", + "Returns 'max(min(val,minval),maxval)':" + " Limit the value 'val' by 'minval' and 'maxval'." + " The return type is identical to the type of 'val'." }, + + { 1, MAX_FUNC_PARAM, "MEAN", F_mean, 0, + "*", "mean(p1,...)", + "Returns the arithmetic mean of all parameters." + " If at least one parameter is a vector, the result is a vector too" + " and the maximum value of each component is calculated." + " The result type is UNSET, FLOAT or VECTOR, but never INT." }, + + { 1, 1, "TRUNC", F_trunc, 0, + "*", "trunc(val)", + "Returns the nearest integer not larger in absolute value than 'val'" + " (rounding towards zero)." + " If 'val' is a vector, the calculation is done for each component." + " The return type is identical to the type of 'val'." }, + + { 1, 1, "FLOOR", F_floor, 0, + "*", "floor(val)", + "Returns the largest integral value that is not greater than 'val'." + " If 'val' is a vector, the calculation is done for each component." + " The return type is identical to the type of 'val'." }, + + { 1, 1, "CEIL", F_ceil, 0, + "*", "ceil(val)", + "Returns the smallest integral value that is not less than 'val'." + " If 'val' is a vector, the calculation is done for each component." + " The return type is identical to the type of 'val'." }, + + { 1, 1, "ROUND", F_round, 0, + "*", "round(val)", + "Returns the rounded integer value of 'val'." + " If 'val' is a vector, the calculation is done for each component." + " The return type is identical to the type of 'val'." }, + + { 1, 1, "SQRT", F_sqrt, 0, + "flt|vec", "sqrt(val)", + "Returns the nonnegative square root of 'abs(val)'." + " If 'val' is a vector, the square root of each component is calculated." }, + + { 1, 2, "LOG", F_log, 0, + "flt|vec", "log(val[,base])", + "If base is not set or invalid, the function returns the natural logarithm of 'val'." + " Otherwise base is converted to a float" + " and logarithm with the entered base is returned (=log(val)/log(base))." + " If 'val' is a vector, the calculation is done for each component." }, + + { 1, 1, "EXP", F_exp, 0, + "flt|vec", "exp(val)", + "Returns the value of e (the base of natural logarithms) raised to the power of 'val'." + " If 'val' is a vector, the calculation is done for each component." }, + + { 2, 2, "POW", F_pow, 0, + "flt|vec", "pow(a,b)", + "Returns the value of 'a' raised to the power of 'b'." + " If 'a' is a vector, each component is raised by 'b'." + " The operator ** does the same, but have integer support." }, + + { 0, 1, "RANDOM", F_random, 0, + "*", "random([max])", + "Returns a random number between 0 and 'max', but never equal 'max'." + " The return type is identical to the type of 'max'." + " If 'max' is not set, use float 1.0 instead." + " If 'max' is a vector, a vector with three random floats will be returned." }, + + + //----- sin(), cos(), tan(), ... + + { 1, 1, "SIN", F_sin, 0, + "flt|vec", "sin(val)", + "Returns the sine of 'val', where 'val' is given in degree." + " If 'val' is a vector, the sine of each component is calculated." } , + + { 1, 1, "COS", F_cos, 0, + "flt|vec", "cos(val)", + "Returns the cosine of 'val', where 'val' is given in degree." + " If 'val' is a vector, the cosine of each component is calculated." } , + + { 1, 1, "TAN", F_tan, 0, + "flt|vec", "tan(val)", + "Returns the tangent of 'val', where 'val' is given in degree." + " If 'val' is a vector, the tangent of each component is calculated." } , + + { 1, 1, "ASIN", F_asin, 0, + "flt|vec", "asin(val)", + "Returns the arc sine of 'val' in degree." + " If 'val' is a vector, the arc sine of each component is calculated." } , + + { 1, 1, "ACOS", F_acos, 0, + "flt|vec", "acos(val)", + "Returns the arc cosine of 'val' in degree." + " If 'val' is a vector, the arc cosine of each component is calculated." } , + + { 1, 1, "ATAN", F_atan, 0, + "flt|vec", "atan(val)", + "Returns the arc tangent of 'val' in degree." + " If 'val' is a vector, the arc tangent of each component is calculated." } , + + { 2, 2, "ATAN2", F_atan2, 0, + "float", "atan2(x,z)", + "Returns the arc tangent of 'z/x' in degree, using the signs of the two" + " arguments to determine the quadrant of the result." + " Both arguments are converted to a float before operation." + " If 'x' is a vector, its x component is used." + " If 'z' is a vector, its z component is used." }, + + + //----- LE-CODE/LEX support functions + + { 0, MAX_FUNC_PARAM, "IF$ENGINE", F_if_engine, 0, + "int", "if$engine(...)", + "Create a condition for KMP/GOBJ references." + " The parameters are used to select engine types." + " Available values: EN$BATTLE, EN$50, EN$100, EN$150 and EN$200." + " Use EN$150M, EN$200M for mirror modes." + " Additional short cuts: EN$150X for EN$150,EN$150M," + " EN$200X for EN$200,EN$200M, and EN$MIRROR for EN$150M,EN$200M." + " A negative index deselects an engine mode." + " '*' is a short cut for all modes." }, + + { 0, MAX_FUNC_PARAM, "IF$RANDOM", F_if_random, 0, + "int", "if$random(...)", + "Create a condition for KMP/GOBJ references." + " The parameters are used to select random scenarios between 1 and 8." + " A negative value deselects a scenario." + " '*' is a short cut for the parameter list '1,2,3,4,5,6,7,8'." }, + + #if HAVE_WIIMM_EXT + { 2, 2, "IF$TEST", F_if_test, 0, + "int", "if$test(index,bitnum)", + "[Hidden for the public]" + " Create a test condition for KMP/GOBJ references." + " Index is between 0 and 15 and bitnum between 0 and 31." }, + #else + { 2, 2, "IF$TEST", F_if_test, 0, 0,0,0 }, + #endif + + { 1, 1, "CHAT$N_RACES", F_chat_n_races, 0, + "int", "chat$n_races(n)", + "Create a chat modus for LE-CODE setup." + " N is a values between 1 and 512 and defines the number of races." + " The parameters are used to select random scenarios between 1 and 8." }, + + { 0, MAX_FUNC_PARAM, "CHAT$VEHICLES", F_chat_vehicles, 0, + "int", "chat$vehicles(...)", + "Create a chat modus for a vehicle group." + " 0 to N values are expected as function parameters." + " Each parameter is either from the" + " group VEH$SMALL, VEH$MEDIUM, VEH$LARGE and VEH$ANY_SIZE" + " for size selections and/or from the group" + " VEH$KART, VEH$OUT_BIKE, VEH$IN_BIKE, VEH$BIKE, VEH$ANY_TYPE" + " for type selections." + " Negative values are an alias for 'all of group except'." + " VEH$ANY is a short cut for VEH$ANY_SIZE,VEH$ANY_TYPE." }, + + //----- list terminator + + {0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// function access /////////////// +/////////////////////////////////////////////////////////////////////////////// + +FuncParam_t * DefineParserFunc +( + ccp name, // name of function + s16 min_param, // minimum parameter + s16 max_param, // maximum parameter + u16 fform_id, // function class, based on file_format_t + call_func_t func, // function to call + int user_id // user defined id +) +{ + DASSERT(name); + DASSERT( min_param <= max_param ); + DASSERT(func); + + if (!parser_func.used) + DefineDefaultParserFunc(); + + Var_t *vp = InsertVarMap(&parser_func,name,false,0,0); + DASSERT(vp); + vp->func_param.need_name = min_param < 0; + if (vp->func_param.need_name) + min_param = -min_param; + vp->func_param.min_param = min_param; + vp->func_param.max_param = max_param; + vp->func_param.func = func; + vp->func_param.fform_id = fform_id; + vp->func_param.user_id = user_id; + return &vp->func_param; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineParserFuncTab +( + const FuncTable_t *ftab, // pointer to a definittion table + // terminated with member 'func == 0' + u16 fform_id // function class, based on file_format_t +) +{ + DASSERT(ftab); + for ( ; ftab->func; ftab++ ) + { + if (ftab->name) + DefineParserFunc( ftab->name, ftab->min_param, ftab->max_param, + fform_id, ftab->func, ftab->user_id ); + + if (ftab->syntax) + DefineParserFuncInfo( ftab->result, ftab->syntax, ftab->info ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void DefineDefaultParserFunc() +{ + static bool done = 0; + if (!done) + { + done = true; + + NullVector.name = 0; + NullVector.mode = VAR_VECTOR; + NullVector.x = 0.0; + NullVector.y = 0.0; + NullVector.z = 0.0; + + DefineParserFuncTab(func_tab,0); + + memset(&FuncDollar,0,sizeof(FuncDollar)); + FuncDollar.max_param = MAX_FUNC_PARAM; + FuncDollar.func = F_dollar; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +const FuncParam_t * GetParserFunc +( + ccp name, // name to search + struct ScanInfo_t *si, // not NULL: print warning if not found + FuncParam_t *temp_par // not NULL: search user defined functions, + // and use this to create the function result +) +{ + DASSERT(name); + + if (!parser_func.used) + DefineDefaultParserFunc(); + + if ( si && temp_par ) + { + const Var_t *vp = FindVarMap(&si->macro,name,0); + if (vp) + { + ScanMacro_t *macro = vp->macro; + DASSERT(macro); + if (macro->is_function) + { + memset(temp_par,0,sizeof(*temp_par)); + temp_par->max_param = MAX_FUNC_PARAM; + temp_par->macro = macro; + temp_par->func = F_call_macro; + return temp_par; + } + } + } + + const Var_t *vp = FindVarMap(&parser_func,name,0); + if (vp) + return &vp->func_param; + + if ( si && si->no_warn <= 0 ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Function '%s' not defined [%s @%u]: %.*s\n", + name, sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + + return &FuncDollar; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// function info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +FuncInfo_t * DefineParserFuncInfo +( + ccp result, // function result info + ccp syntax, // syntax of function + ccp info // description +) +{ + DASSERT(syntax); + + Var_t *vp = InsertVarMap(&parser_func_info,syntax,false,0,0); + DASSERT(vp); + if (result) + vp->func_info.result = result; + if (info) + vp->func_info.info = info; + return &vp->func_info; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpParserFuncInfo +( + FILE * f, // destination file + uint indent, // indention + bool print_header, // true: print table header + int be_verbose, // >0: print description too + struct search_filter_t * filter // NULL or filter data +) +{ + DASSERT(f); + DefineDefaultParserFunc(); + + if ( indent > 40 ) + indent = 40; + const uint twidth = GetTermWidth(80,indent+40)-1; + + uint fw_result = 4; + uint fw_syntax = 6; + + uint i; + for ( i = 0; i < parser_func_info.used; i++ ) + if ( !filter || filter[i].mode ) + { + const Var_t *vp = parser_func_info.list + i; + + uint len = strlen(vp->name); + if ( fw_syntax < len ) + fw_syntax = len; + + len = strlen(vp->func_info.result); + if ( fw_result < len ) + fw_result = len; + } + + if ( be_verbose > 0 ) + { + if ( fw_syntax > 15 ) + fw_syntax = 15; + + if (print_header) + fprintf(f,"%*s%-*s %-*s Description\n%*s%.*s\n", + indent, "", + fw_result, "Type", + fw_syntax, "Syntax", + indent, "", + twidth, Minus300 ); + + const uint fw_head = indent + fw_result + fw_syntax + 2; + + for ( i = 0; i < parser_func_info.used; i++ ) + if ( !filter || filter[i].mode ) + { + const Var_t *vp = parser_func_info.list + i; + + uint len = fprintf(f,"%*s%-*s %s", + indent, "", + fw_result, vp->func_info.result, + vp->name ); + if ( len >= fw_head ) + { + fputc('\n',f); + len = 0; + } + PutLines(stdout,fw_head,twidth,len,0,vp->func_info.info,0); + } + } + else + { + const uint fw_head = indent + fw_result + fw_syntax + 4; + if (print_header) + fprintf(f,"%*s %-*s Syntax\n%*s%.*s\n", + indent, "", + fw_result, "Type", + indent, "", + fw_head, Minus300 ); + + for ( i = 0; i < parser_func_info.used; i++ ) + if ( !filter || filter[i].mode ) + { + const Var_t *vp = parser_func_info.list + i; + fprintf(f,"%*s %-*s %s\n", + indent, "", + fw_result, vp->func_info.result, + vp->name ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +search_filter_t * SetupParserFuncFilter() +{ + return AllocSearchFilter(parser_func_info.used); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParserFuncFilter ( search_filter_t *filter, ccp arg, int mode ) +{ + DASSERT(filter); + + if ( !arg || !*arg ) + return; + + char needle[200]; + CreateNeedleWCMP(needle,sizeof(needle),arg); + + uint i; + for ( i = 0; i < parser_func_info.used; i++ ) + { + if (!filter[i].mode) + { + const Var_t *vp = parser_func_info.list + i; + filter[i].mode = strcasestr(vp->name,arg) != 0; + if (!filter[i].mode) + { + if (!filter[i].string) + { + ccp list[] = { vp->name, mode ? vp->func_info.info : 0, 0 }; + char haystack[5000]; + uint len = CreateHaystackListWCMP(haystack,sizeof(haystack),list); + filter[i].string = MEMDUP(haystack,len+1); + } + filter[i].mode = FindWCMP(filter[i].string,needle,3,0,0); + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpSymbols ( const VarMap_t * vm ) +{ + if (print_header) + putchar('\n'); + DumpVarMap(stdout,0,vm,print_header); + if (print_header) + putchar('\n'); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ListParserFunctions() +{ + DefineDefaultParserFunc(); + + search_filter_t *filter = 0; + if (first_param) + { + filter = SetupParserFuncFilter(); + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + ParserFuncFilter(filter,param->arg,long_count); + } + + if (print_header) + putchar('\n'); + DumpParserFuncInfo(stdout,0,print_header,!brief_count,filter); + if (print_header) + putchar('\n'); + + FreeSearchFilter(filter); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ParserCalc ( const VarMap_t * predef ) +{ + int np = 1; + ParamList_t *param; + + for ( param = first_param; param; param = param->next, np++ ) + { + char name[20]; + snprintf(name,sizeof(name),"Param #%u",np); + + ScanInfo_t si; + InitializeSI(&si,param->arg,strlen(param->arg),name,0); + si.predef = predef; + //si.point_is_null++; + + DEFINE_VAR(val); + ScanExprSI(&si,&val); + PrintV(stdout,&val,1); + putchar('\n'); + CheckEolSI(&si); + ResetSI(&si); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ExportParserFuncMakedoc ( FILE * f, ccp index ) +{ + DASSERT(f); + DASSERT(index); + + fprintf(f,"\n#\f\n%.79s\n%.15s%20s%-29s%.15s\n%.79s\n\n", + Hash200, Hash200, "", "Functions", Hash200, Hash200 ); + + fprintf(f,"#gdef $func['%s'] = @map\n\n",index); + + const Var_t * func_end = parser_func.list + parser_func.used; + const Var_t * info_end = parser_func_info.list + parser_func_info.used; + const Var_t * func; + + for ( func = parser_func.list; func < func_end; func++ ) + { + fprintf(f, + "#pdef d = @map\n" + "#pdef d['name'] = '%s'\n" + "#pdef d['class'] = '%s'\n" + "#pdef d['p-name'] = %d\n" + "#pdef d['p-min'] = %d\n" + "#pdef d['p-max'] = %d\n" + "#pdef d['info'] = @map\n" + ,func->name + ,func->func_param.fform_id ? GetNameFF(0,func->func_param.fform_id) : "" + ,func->func_param.need_name + ,func->func_param.min_param + ,func->func_param.max_param + ); + + const Var_t * info; + for ( info = parser_func_info.list; info < info_end; info++ ) + { + char name[100]; + StringCopyS(name,sizeof(name),info->name); + char *end = strchr(name,'('); + if (end) + *end = 0; + end = strchr(info->name,' '); + if (end) + *end = 0; + if (!strcasecmp(func->name,name)) + { + fprintf(f, + "#pdef d['info']['%s']['result'] = '%s'\n" + "#pdef d['info']['%s']['info'] = \\\n" + ,info->name ,info->func_info.result + ,info->name + ); + DumpText(f,0,0,info->func_info.info,-1,true,"\n"); + } + } + + fprintf(f,"#gdef $func['%s']['%s'] = move(d)\n\n",index,func->name); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ExportTrackInfo ( FILE * f ) +{ + fputs("$MKW_TRACK_INFO = array\n(\n",f); + + uint i; + for ( i = 0; i < MKW_N_TRACKS + MKW_N_ARENAS; i++ ) + { + const bool is_arena = i >= MKW_N_TRACKS; + const TrackInfo_t *ti = is_arena + ? arena_info + i - MKW_N_TRACKS + : track_info + i; + + fprintf(f," %u => (object)array(\n" + " 'slot' => %3u,\n" + " 'index' => %3u,\n" + " 'music' => %3u,\n" + " 'track' => '%c%2u',\n" + " 'abbrev'=> '%s',\n" + " 'de' => \"%s\",\n" + " 'en' => \"%s\",\n" + " 'fname' => \"%s\",\n" + " ),\n\n" + ,i + ,i + ,ti->def_index + ,ti->music_id + ,is_arena ? 'A' : 'T', ti->def_slot + ,ti->abbrev + ,ti->name_de + ,ti->name_en + ,ti->track_fname + ); + } + + fputs(");\n\n",f); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ExportHelper ( ccp func_mode ) +{ + SetPatchFileModeReadonly(); + + stdlog = stderr; + export_mode_t exmode = ScanExportMode(); + if (!exmode) + return ERR_SYNTAX; + + switch( exmode & EXPORT_CMD_MASK ) + { + case EXPORT_PHP: + if ( exmode & EXPORT_F_TRACKS ) + ExportTrackInfo(stdout); + break; + + case EXPORT_MAKEDOC: + if ( exmode & EXPORT_F_FUNCTIONS ) + ExportParserFuncMakedoc(stdout,func_mode); + if ( exmode & EXPORT_F_FILEATTRIB ) + ExportFileAttribMakedoc(stdout); + break; + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-parser.c b/project/src/lib-parser.c new file mode 100644 index 0000000..094a1ee --- /dev/null +++ b/project/src/lib-parser.c @@ -0,0 +1,7617 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-std.h" +#include +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// conversion helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +Var_t var_unset = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +bool GetBoolV ( const Var_t * var ) +{ + if (var) + switch(var->mode) + { + case VAR_UNSET: + return false; + + case VAR_INT: + return var->i != 0; + + case VAR_DOUBLE: + return var->d != 0.0; + + case VAR_VECTOR: + return var->x || var->y || var->z; + + case VAR_STRING: + return var->str_len > 0; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// + +int GetIntV ( const Var_t * var ) +{ + if (var) + switch(var->mode) + { + case VAR_UNSET: + return 0; + + case VAR_INT: + return var->i; + + case VAR_DOUBLE: + case VAR_VECTOR: + { + const double d = floor ( var->d + 0.5 ); + return d < INT_MIN ? INT_MIN : d > INT_MAX ? INT_MAX : d; + } + + case VAR_STRING: + return str2l(var->str,0,10); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +double GetDoubleV ( const Var_t * var ) +{ + if (var) + switch(var->mode) + { + case VAR_UNSET: + return 0.0; + + case VAR_INT: + return var->i; + + case VAR_DOUBLE: + case VAR_VECTOR: + return var->d; + + case VAR_STRING: + return var->str_len ? strtod_comma(var->str,0) : 0.0; + } + return 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +double GetXDoubleV ( const Var_t * var ) +{ + if (var) + switch(var->mode) + { + case VAR_UNSET: + return 0.0; + + case VAR_INT: + return var->i; + + case VAR_DOUBLE: + return var->d; + + case VAR_VECTOR: + return var->x; + + case VAR_STRING: + return var->str_len ? strtod_comma(var->str,0) : 0.0; + } + return 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +double GetYDoubleV ( const Var_t * var ) +{ + if (var) + switch(var->mode) + { + case VAR_UNSET: + return 0.0; + + case VAR_INT: + return var->i; + + case VAR_DOUBLE: + return var->d; + + case VAR_VECTOR: + return var->y; + + case VAR_STRING: + return var->str_len ? strtod_comma(var->str,0) : 0.0; + } + return 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +double GetZDoubleV ( const Var_t * var ) +{ + if (var) + switch(var->mode) + { + case VAR_UNSET: + return 0.0; + + case VAR_INT: + return var->i; + + case VAR_DOUBLE: + return var->d; + + case VAR_VECTOR: + return var->z; + + case VAR_STRING: + return var->str_len ? strtod_comma(var->str,0) : 0.0; + } + return 0.0; +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 GetVectorV ( const Var_t * var ) +{ + double3 d; + if (!var) + d.x = d.y = d.z = 0.0; + else + switch(var->mode) + { + case VAR_UNSET: + d.x = d.y = d.z = 0.0; + break; + + case VAR_INT: + d.x = d.y = d.z = var->i; + break; + + case VAR_DOUBLE: + d.x = d.y = d.z = var->d; + break; + + case VAR_VECTOR: + return var->d3; + + case VAR_STRING: + { + DEFINE_VAR(temp); + ScanVectorExpr(var->str,var->name,&temp); + DASSERT( temp.mode == VAR_VECTOR ); + return temp.d3; + } + } + return d; +} + +/////////////////////////////////////////////////////////////////////////////// + +float3 GetVectorFV ( const Var_t * var ) +{ + float3 f; + if (!var) + f.x = f.y = f.z = 0.0; + else + switch(var->mode) + { + case VAR_UNSET: + f.x = f.y = f.z = 0.0; + break; + + case VAR_INT: + f.x = f.y = f.z = var->i; + break; + + case VAR_DOUBLE: + f.x = f.y = f.z = var->d; + break; + + case VAR_VECTOR: + f.x = var->x; + f.y = var->y; + f.z = var->z; + break; + + case VAR_STRING: + { + DEFINE_VAR(temp); + ScanVectorExpr(var->str,var->name,&temp); + DASSERT( temp.mode == VAR_VECTOR ); + f.x = temp.x; + f.y = temp.y; + f.z = temp.z; + } + } + return f; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// move content; src can be NULL or ==dest + +void MoveDataV ( Var_t *dest, Var_t * src ) +{ + DASSERT(dest); + FreeV(dest); + + if (!src) + dest->mode = VAR_UNSET; + else if ( src != dest ) + { + memcpy( &dest->mode, &src->mode, sizeof(*dest)-offsetof(Var_t,mode) ); + src->mode = VAR_UNSET; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveDataInitV ( Var_t *dest, Var_t * src ) +{ + DASSERT(dest); + + if (!src) + dest->mode = VAR_UNSET; + else if ( src != dest ) + { + memcpy( &dest->mode, &src->mode, sizeof(*dest)-offsetof(Var_t,mode) ); + src->mode = VAR_UNSET; + } +} + +/////////////////////////////////////////////////////////////////////////////// +void MoveAllV ( Var_t *dest, Var_t * src ) +{ + DASSERT(dest); + FreeV(dest); + + if (!src) + dest->mode = VAR_UNSET; + else if ( src != dest ) + { + memcpy(dest,src,sizeof(*dest)); + src->name = 0; + src->mode = VAR_UNSET; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveAllInitV ( Var_t *dest, Var_t * src ) +{ + DASSERT(dest); + + if (!src) + dest->mode = VAR_UNSET; + else if ( src != dest ) + { + memcpy(dest,src,sizeof(*dest)); + src->name = 0; + src->mode = VAR_UNSET; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// assign strings + +uint AssignStringVV ( Var_t * var, const Var_t * src ) +{ + DASSERT(var); + + DEFINE_VAR(temp); + Var_t *res = GetStringV(src,&temp); + DASSERT( res && res->mode == VAR_STRING ); + AssignStringVS(var,res->str,res->str_len); + FreeV(&temp); + return var->str_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AppendStringVV ( Var_t * var, const Var_t * src ) +{ + DASSERT(var); + + DEFINE_VAR(temp); + Var_t *res = GetStringV(src,&temp); + DASSERT( res && res->mode == VAR_STRING ); + AppendStringVS(var,res->str,res->str_len); + FreeV(&temp); + return var->str_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AssignStringVS ( Var_t * var, ccp str, int len ) +{ + DASSERT(var); + + SetModeStringV(var); + var->str_len = 0; + + if (str) + { + if ( len < 0 ) + len = strlen(str); + if ( len > 0 ) + { + char *old = 0; + if ( len > var->str_size ) + { + old = var->str; + var->str_size = len; + var->str = MALLOC(var->str_size+1); + } + DASSERT( len <= var->str_size); + memcpy(var->str,str,len); + var->str[len] = 0; + var->str_len = len; + if (old) + FREE(old); + } + } + return var->str_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AssignStringVS2 ( Var_t * var, ccp str1, int len1, ccp str2, int len2 ) +{ + DASSERT(var); + + if ( !str1 || !len1 ) return AssignStringVS(var,str2,len2); + if ( !str2 || !len2 ) return AssignStringVS(var,str1,len1); + + SetModeStringV(var); + var->str_len = 0; + + if ( len1 < 0 ) len1 = strlen(str1); + if ( len2 < 0 ) len2 = strlen(str2); + const int total = len1 + len2; + + if ( total > 0 ) + { + char *old = 0; + if ( total > var->str_size ) + { + old = var->str; + var->str_size = total; + var->str = MALLOC(var->str_size+1); + } + DASSERT( total <= var->str_size); + memcpy(var->str,str1,len1); + memcpy(var->str+len1,str2,len2); + var->str[total] = 0; + var->str_len = total; + if (old) + FREE(old); + } + + return var->str_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AppendStringVS ( Var_t * var, ccp str, int len ) +{ + DASSERT(var); + + if ( var->mode != VAR_STRING ) + return AssignStringVS(var,str,len); + + if (str) + { + if ( len < 0 ) + len = strlen(str); + if ( len > 0 ) + { + char *old = 0; + const uint total = var->str_len + len; + if ( total > var->str_size ) + { + old = var->str; + var->str_size = total + 20; + var->str = MALLOC(var->str_size+1); + memcpy(var->str,old,var->str_len); + } + DASSERT( total <= var->str_size); + memcpy(var->str+var->str_len,str,len); + var->str_len = total; + var->str[total] = 0; + if (old) + FREE(old); + } + } + return var->str_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AppendStringVC ( Var_t * var, char ch, int repeat ) +{ + DASSERT(var); + + SetModeStringV(var); + if ( repeat > 0 ) + { + const uint total = var->str_len + repeat; + if ( total > var->str_size ) + { + var->str_size = total + 20; + var->str = REALLOC(var->str,var->str_size+1); + } + DASSERT( total <= var->str_size); + char *dest = var->str+var->str_len; + while ( repeat-- > 0 ) + *dest++ = ch; + *dest = 0; + var->str_len = total; + DASSERT( dest - var->str == var->str_len ); + } + return var->str_len; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * GetStringV ( const Var_t * var, Var_t * temp ) +{ + DASSERT(temp); + + if (!var) + { + SetModeStringV(temp); + temp->str_len = 0; + return temp; + } + + if ( var->mode == VAR_STRING ) + return (Var_t*)var; + + int len = 0; + char buf[100]; + + switch (var->mode) + { + case VAR_INT: + len = snprintf(buf,sizeof(buf),"%lld",var->i); + break; + + case VAR_DOUBLE: + len = snprintf(buf,sizeof(buf),"%5.3f",var->d); + break; + + case VAR_VECTOR: + len = snprintf(buf,sizeof(buf),"%5.3f,%5.3f,%5.3f", + var->x, var->y, var->z ); + break; + + case VAR_STRING: // VAR_STRING will not happen + ASSERT(0); + case VAR_UNSET: + break; + } + + AssignStringVS(temp,buf,len); + return temp; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToNoneV ( Var_t * var ) +{ + DASSERT(var); + FreeV(var); + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToBoolV ( Var_t * var ) +{ + DASSERT(var); + const bool i = GetBoolV(var); + FreeV(var); + var->i = i; + var->mode = VAR_INT; + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToIntV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode != VAR_INT ) + { + const int i = GetIntV(var); + FreeV(var); + var->i = i; + var->mode = VAR_INT; + } + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToDoubleV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode != VAR_DOUBLE ) + { + const double d = GetDoubleV(var); + FreeV(var); + var->d = d; + var->mode = VAR_DOUBLE; + } + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToXDoubleV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode != VAR_DOUBLE ) + { + const double d = GetXDoubleV(var); + FreeV(var); + var->d = d; + var->mode = VAR_DOUBLE; + } + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToYDoubleV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode != VAR_DOUBLE ) + { + const double d = GetYDoubleV(var); + FreeV(var); + var->d = d; + var->mode = VAR_DOUBLE; + } + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToZDoubleV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode != VAR_DOUBLE ) + { + const double d = GetZDoubleV(var); + FreeV(var); + var->d = d; + var->mode = VAR_DOUBLE; + } + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToVectorV ( Var_t * var ) +{ + switch(var->mode) + { + case VAR_UNSET: + var->x = var->y = var->z = 0.0; + break; + + case VAR_INT: + var->x = var->y = var->z = (double)var->i; + break; + + case VAR_DOUBLE: + DASSERT( &var->x == &var->d ); + var->y = var->z = var->x; + break; + + case VAR_VECTOR: + return var; + + case VAR_STRING: + { + DEFINE_VAR(temp); + ScanVectorExpr(var->str,var->name,&temp); + DASSERT( temp.mode == VAR_VECTOR ); + var->x = temp.x; + var->y = temp.y; + var->z = temp.z; + } + break; + } + var->mode = VAR_VECTOR; + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToVectorXV ( Var_t * var ) +{ + switch(var->mode) + { + case VAR_UNSET: + var->x = var->y = var->z = 0.0; + break; + + case VAR_INT: + var->x = (double)var->i; + var->y = var->z = 0.0; + break; + + case VAR_DOUBLE: + DASSERT( &var->x == &var->d ); + var->y = var->z = 0.0; + break; + + case VAR_VECTOR: + return var; + + case VAR_STRING: + { + const double d = var->str_len ? strtod_comma(var->str,0) : 0.0; + FreeV(var); + var->x = d; + var->y = var->z = 0.0; + } + break; + } + var->mode = VAR_VECTOR; + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToVectorYV ( Var_t * var ) +{ + switch(var->mode) + { + case VAR_UNSET: + var->x = var->y = var->z = 0.0; + break; + + case VAR_INT: + var->y = (double)var->i; + var->x = var->z = 0.0; + break; + + case VAR_DOUBLE: + var->y = var->d; + var->x = var->z = 0.0; + break; + + case VAR_VECTOR: + return var; + + case VAR_STRING: + { + const double d = var->str_len ? strtod_comma(var->str,0) : 0.0; + FreeV(var); + var->y = d; + var->x = var->z = 0.0; + } + break; + } + var->mode = VAR_VECTOR; + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToVectorZV ( Var_t * var ) +{ + switch(var->mode) + { + case VAR_UNSET: + var->x = var->y = var->z = 0.0; + break; + + case VAR_INT: + var->z = (double)var->i; + var->x = var->y = 0.0; + break; + + case VAR_DOUBLE: + var->z = var->d; + var->x = var->y = 0.0; + break; + + case VAR_VECTOR: + return var; + + case VAR_STRING: + { + const double d = var->str_len ? strtod_comma(var->str,0) : 0.0; + FreeV(var); + var->z = d; + var->x = var->y = 0.0; + } + break; + } + var->mode = VAR_VECTOR; + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToStringV ( Var_t * var ) +{ + DASSERT(var); + if ( var->mode != VAR_STRING ) + GetStringV(var,var); + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * ToVarMode ( Var_t * var, TextCommand_t mode ) +{ + DASSERT(var); + noPRINT("ToVarMode(%u)\n",mode); + + switch (mode) + { + case TCMD_PDEF_I: + case TCMD_LDEF_I: + case TCMD_GDEF_I: + return ToIntV(var); + + case TCMD_PDEF_F: + case TCMD_LDEF_F: + case TCMD_GDEF_F: + return ToDoubleV(var); + + case TCMD_PDEF_S: + case TCMD_LDEF_S: + case TCMD_GDEF_S: + return ToStringV(var); + + case TCMD_PDEF_V: + case TCMD_LDEF_V: + case TCMD_GDEF_V: + return ToVectorV(var); + + case TCMD_PDEF_X: + case TCMD_LDEF_X: + case TCMD_GDEF_X: + return ToXDoubleV(var); + + case TCMD_PDEF_Y: + case TCMD_LDEF_Y: + case TCMD_GDEF_Y: + return ToYDoubleV(var); + + case TCMD_PDEF_Z: + case TCMD_LDEF_Z: + case TCMD_GDEF_Z: + return ToZDoubleV(var); + + default: + return var; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AssignIntV ( Var_t * var, int val ) +{ + DASSERT(var); + FreeV(var); + var->i = val; + var->mode = VAR_INT; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignDoubleV ( Var_t * var, double val ) +{ + DASSERT(var); + FreeV(var); + var->d = val; + var->mode = VAR_DOUBLE; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignXDoubleV ( Var_t * var, double val ) +{ + DASSERT(var); + FreeV(var); + if ( var->mode != VAR_VECTOR ) + { + var->mode = VAR_VECTOR; + var->y = var->z = 0.0; + } + var->x = val; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignYDoubleV ( Var_t * var, double val ) +{ + DASSERT(var); + FreeV(var); + if ( var->mode != VAR_VECTOR ) + { + var->mode = VAR_VECTOR; + var->x = var->z = 0.0; + } + var->y = val; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignZDoubleV ( Var_t * var, double val ) +{ + DASSERT(var); + FreeV(var); + if ( var->mode != VAR_VECTOR ) + { + var->mode = VAR_VECTOR; + var->x = var->y = 0.0; + } + var->z = val; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignVectorV ( Var_t * var, const double3 * val ) +{ + DASSERT(var); + DASSERT(val); + FreeV(var); + memcpy(&var->d3,val,sizeof(var->d3)); + var->mode = VAR_VECTOR; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignVectorV3 ( Var_t * var, double x, double y, double z ) +{ + DASSERT(var); + FreeV(var); + var->x = x; + var->y = y; + var->z = z; + var->mode = VAR_VECTOR; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignVar +( + Var_t * dest, // valid destination var + const Var_t * src // NULL or valid source var +) +{ + // assertions needed for memcpy() below + DASSERT( (u8*)&dest->name == (u8*)dest ); + DASSERT( (u8*)&dest->name + sizeof(dest->name) == (u8*)&dest->mode ); + + if (src) + { + if ( src->mode == VAR_STRING ) + AssignStringVV(dest,src); + else + memcpy(&dest->mode,&src->mode,sizeof(*dest)-offsetof(Var_t,mode)); + } + else + FreeV(dest); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignVarMode ( Var_t * dest, const Var_t * src, TextCommand_t mode ) +{ + DASSERT(dest); + noPRINT("AssignVarMode(%u) name=%s src_mode=%d\n", + mode, dest->name, src ? src->mode : -1 ); + + // assertions needed for memcpy() below + DASSERT( (u8*)&dest->name == (u8*)dest ); + DASSERT( (u8*)&dest->name + sizeof(dest->name) == (u8*)&dest->mode ); + + switch (mode) + { + case TCMD_PDEF_I: + case TCMD_LDEF_I: + case TCMD_GDEF_I: + dest->i = GetIntV(src); + dest->mode = VAR_INT; + break; + + case TCMD_PDEF_F: + case TCMD_LDEF_F: + case TCMD_GDEF_F: + dest->d = GetDoubleV(src); + dest->mode = VAR_DOUBLE; + break; + + case TCMD_PDEF_S: + case TCMD_LDEF_S: + case TCMD_GDEF_S: + AssignStringVV(dest,src); + break; + + case TCMD_PDEF_V: + case TCMD_LDEF_V: + case TCMD_GDEF_V: + dest->d3 = GetVectorV(src); + dest->mode = VAR_VECTOR; + break; + + case TCMD_PDEF_X: + case TCMD_LDEF_X: + case TCMD_GDEF_X: + if ( dest->mode == VAR_VECTOR ) + dest->x = GetXDoubleV(src); + else + { + dest->x = GetXDoubleV(src); + dest->y = dest->z = 0.0; + dest->mode = VAR_VECTOR; + } + break; + + case TCMD_PDEF_Y: + case TCMD_LDEF_Y: + case TCMD_GDEF_Y: + if ( dest->mode == VAR_VECTOR ) + dest->y = GetYDoubleV(src); + else + { + dest->y = GetYDoubleV(src); + dest->x = dest->z = 0.0; + dest->mode = VAR_VECTOR; + } + break; + + case TCMD_PDEF_Z: + case TCMD_LDEF_Z: + case TCMD_GDEF_Z: + if ( dest->mode == VAR_VECTOR ) + dest->z = GetZDoubleV(src); + else + { + dest->z = GetYDoubleV(src); + dest->x = dest->y = 0.0; + dest->mode = VAR_VECTOR; + } + break; + + default: + if (src) + memcpy( &dest->mode, &src->mode, sizeof(*dest)-sizeof(dest->name) ); + else + { + dest->mode = VAR_UNSET; + dest = 0; + } + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanValueV ( Var_t * dest, const Var_t * src, ccp name ) +{ + Var_t temp; + if ( src && src->mode == VAR_STRING ) + { + if (!name) + name = src->name; + + ScanInfo_t si; + InitializeSI(&si,src->str,src->str_len,name,0); + if (!name) + si.no_warn++; + + InitializeV(&temp); + enumError err = ScanValueSI(&si,&temp); + if (!err) + CheckEolSI(&si); + ResetSI(&si); + src = &temp; + } + + FreeV(dest); + if (src) + memcpy(&dest->mode,&src->mode,sizeof(*dest)-offsetof(Var_t,mode)); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanExprV ( Var_t * dest, const Var_t * src, ccp name ) +{ + Var_t temp; + if ( src && src->mode == VAR_STRING ) + { + if (!name) + name = src->name; + + ScanInfo_t si; + InitializeSI(&si,src->str,src->str_len,name,0); + if (!name) + si.no_warn++; + + InitializeV(&temp); + enumError err = ScanExprSI(&si,&temp); + if (!err) + CheckEolSI(&si); + ResetSI(&si); + src = &temp; + } + + FreeV(dest); + if (src) + memcpy(&dest->mode,&src->mode,sizeof(*dest)-offsetof(Var_t,mode)); + return ERR_OK; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ResultFloat +( + Var_t * dest, // valid destination var + const float * val, // pointer to N floats + uint n_val // number of floats in 'n' +) +{ + FreeV(dest); + switch (n_val) + { + case 0: + dest->i = 0; + dest->mode = VAR_UNSET; + break; + + case 1: + dest->d = *val; + dest->mode = VAR_DOUBLE; + break; + + case 2: + dest->x = val[0]; + dest->y = 0.0; + dest->z = val[1]; + dest->mode = VAR_VECTOR; + break; + + default: + dest->x = val[0]; + dest->y = val[1]; + dest->z = val[2]; + dest->mode = VAR_VECTOR; + break; + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// variables & maps: interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool IsNum ( char ch, int hex_mode ) +{ + return ch >= '0' && ch <= '9' + || ch == '-' + || ch == '+' + || ch == '.' + || hex_mode > 0 && ( ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F' ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeVarMap +( + VarMap_t * vm // data structure to initialize +) +{ + DASSERT(vm); + memset(vm,0,sizeof(*vm)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetVarMap +( + VarMap_t * vm // data structure to resewt +) +{ + DASSERT(vm); + ClearVarMap(vm); + FREE(vm->list); + memset(vm,0,sizeof(*vm)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearVarMap +( + VarMap_t * vm // data structure to resewt +) +{ + DASSERT(vm); + if (vm->used) + { + uint count = vm->used; + Var_t * v = vm->list; + while ( count-- > 0 ) + { + FreeV(v); + FreeString(v->name); + v++; + } + vm->used = 0; + // not needed: memset(vm->list,0,sizeof(*vm->list)*vm->size); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CopyVarMap +( + VarMap_t * dest, // destination var map + bool init_dest, // true: initialialize 'dest' first + const VarMap_t * src // source var map +) +{ + DASSERT(dest); + DASSERT(src); + + if (init_dest) + InitializeVarMap(dest); + + if (src->used) + { + Var_t * vsrc = src->list; + // assertions needed for memcpy() below + DASSERT( (u8*)&vsrc->name == (u8*)vsrc ); + DASSERT( (u8*)&vsrc->name + sizeof(vsrc->name) == (u8*)&vsrc->mode ); + + uint count; + for ( count = src->used; count > 0; count--, vsrc++ ) + { + Var_t * vdest = InsertVarMap( dest, vsrc->name, false, 0, 0 ); + DASSERT(vdest); + if ( vsrc->mode == VAR_STRING ) + AssignStringVV(vdest,vsrc); + else + memcpy(&vdest->mode,&vsrc->mode,sizeof(*vdest)-offsetof(Var_t,mode)); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveVarMap +( + VarMap_t * dest, // destination var map + bool init_dest, // true: initialialize 'dest' first + VarMap_t * src // source var map -> will be resetted +) +{ + DASSERT(dest); + DASSERT(src); + + if (!init_dest) + ResetVarMap(dest); + memcpy(dest,src,sizeof(*dest)); + InitializeVarMap(src); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint FindVarMapHelper ( const VarMap_t * vm, bool * p_found, ccp name ) +{ + ASSERT(vm); + + int beg = 0; + if ( vm && name ) + { + int end = vm->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + int stat = strcmp(name,vm->list[idx].name); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + { + TRACE("FindVarMapHelper(%s) FOUND=%d/%d/%d\n", + name, idx, vm->used, vm->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + TRACE("FindVarMapHelper(%s) failed=%d/%d/%d\n", + name, beg, vm->used, vm->size ); + + if (p_found) + *p_found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindVarMapIndex +( + const VarMap_t * vm, // valid variable map + ccp varname, // variable name + int not_found_value // return value if not found +) +{ + bool found; + const int idx = FindVarMapHelper(vm,&found,varname); + return found ? idx : not_found_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveVarMap +( + // returns 'true' if var deleted + + VarMap_t * vm, // valid variable map + ccp varname // variable name +) +{ + bool found; + uint idx = FindVarMapHelper(vm,&found,varname); + if (found) + { + vm->used--; + ASSERT( idx <= vm->used ); + Var_t *dest = vm->list + idx; + FreeV(dest); + FREE((char*)dest->name); + memmove(dest,dest+1,(vm->used-idx)*sizeof(*dest)); + } + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * InsertVarMap +( + VarMap_t * vm, // valid variable map + ccp varname, // varname to insert + bool move_varname, // true: move 'varname', false: strdup() + ScanInfo_t * si, // not null: print "already defined" message + bool * found // not null: store found status +) +{ + DASSERT(vm); + DASSERT( vm->used <= vm->size ); + + if (!varname) + { + if (found) + *found = false; + return 0; + } + + bool my_found; + const int idx = FindVarMapHelper(vm,&my_found,varname); + if (found) + *found = my_found; + + Var_t * var; + if (my_found) + { + var = vm->list + idx; + if (move_varname) + FreeString(varname); + + if ( si && si->no_warn <= 0 ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Variable '%s' already defined [%s @%u]: %.*s\n", + varname, sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + } + else + { + if ( vm->used == vm->size ) + { + vm->size = 3*vm->size/2 + 100; + vm->list = REALLOC(vm->list,vm->size*sizeof(*vm->list)); + } + + DASSERT( idx <= vm->used ); + var = vm->list + idx; + memmove(var+1,var,(vm->used-idx)*sizeof(*var)); + vm->used++; + + var->name = move_varname ? varname : STRDUP(varname); + var->mode = VAR_UNSET; + } + + return var; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DefineIntVar +( + VarMap_t * vm, // valid variable map + ccp varname, // name of variable + int value // value to assign +) +{ + DASSERT(vm); + DASSERT(varname); + + bool found; + Var_t * var = InsertVarMap( vm, varname, false, 0, &found ); + DASSERT(var); + FreeV(var); + var->mode = VAR_INT; + var->i = value; + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DefineDoubleVar +( + VarMap_t * vm, // valid variable map + ccp varname, // name of variable + double value // value to assign +) +{ + DASSERT(vm); + DASSERT(varname); + + bool found; + Var_t * var = InsertVarMap( vm, varname, false, 0, &found ); + DASSERT(var); + FreeV(var); + var->mode = VAR_DOUBLE; + var->d = value; + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintV +( + FILE * f, // destination file + const Var_t * v, // variable to print + int str_mode // 0:-, 1:limit+escape, 2:limit+escape+quote +) +{ + DASSERT(f); + DASSERT(v); + + const uint max_str = 100; + char strbuf[5*max_str+100]; + + switch(v->mode) + { + case VAR_UNSET: + fputs("$NONE",f); + break; + + case VAR_INT: + fprintf(f,"%19lld = %#16llx", v->i, v->i ); + if (v->int_mode) + fprintf(f," [%s]",GetIntModeName(v->int_mode)); + break; + + case VAR_DOUBLE: + if ( fabs(v->d) < 1e10 ) + fprintf(f,"%15.3f = %14.7e", v->d, v->d ); + else + fprintf(f," %14.7e", v->d ); + break; + + case VAR_VECTOR: + if ( fabs(v->x) < 1e10 ) + fprintf(f,"v( %14.3f,", v->x ); + else + fprintf(f,"v( %14.7e,", v->x ); + + if ( fabs(v->y) < 1e10 ) + fprintf(f," %14.3f,",v->y); + else + fprintf(f," %14.7e,",v->y); + + if ( fabs(v->z) < 1e10 ) + fprintf(f," %14.3f )",v->z); + else + fprintf(f," %14.7e )",v->z); + break; + + case VAR_STRING: + if (!str_mode) + fwrite(v->str,v->str_len,1,f); + else + { + ccp quote = str_mode > 1 ? "\"" : ""; + PrintEscapedString( strbuf, sizeof(strbuf), + v->str, v->str_len < max_str ? v->str_len : max_str, + CHMD__ALL, *quote, 0 ); + if ( v->str_len > max_str ) + fprintf(f,"[%u/%u] %s%s%s...",max_str,v->str_len,quote,strbuf,quote); + else + fprintf(f,"[%u] %s%s%s",v->str_len,quote,strbuf,quote); + } + break; + + default: + fputs("?",f); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpVarMap +( + FILE * f, // destination file + uint indent, // indention + const VarMap_t * vm, // valid variable map + bool print_header // print table header +) +{ + DASSERT(f); + DASSERT(indent<100); + DASSERT(vm); + + if (!vm->used) + return; + + uint maxlen = 4; + Var_t *v, *vend = vm->list + vm->used; + for ( v = vm->list; v < vend; v++ ) + { + const uint len = strlen(v->name); + if ( maxlen < len ) + maxlen = len; + } + + char last_name1 = 0; + if (print_header) + { + fprintf(f,"%*s%-*s = value = value\n" + "%*s%.*s\n", + indent,"", maxlen, "name", + indent,"", maxlen+36, Minus300 ); + last_name1 = vm->list->name[0]; + } + + for ( v = vm->list; v < vend; v++ ) + { + if ( print_header && last_name1 != v->name[0] ) + { + last_name1 = v->name[0]; + fputc('\n',f); + } + + fprintf(f,"%*s%-*s = ", indent,"", maxlen, v->name ); + PrintV(f,v,2); + fputc('\n',f); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SI var access /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const Var_t * FindConst +( + ccp varname // variable name +) +{ + bool found; + const int idx = FindVarMapHelper(&const_map,&found,varname); + return found ? const_map.list + idx : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const Var_t * FindVarMap +( + const VarMap_t * vm, // NULL or variable map + ccp varname, // variable name + ScanInfo_t * si // not null: print "not found" message +) +{ + if (vm) + { + bool found; + const int idx = FindVarMapHelper(vm,&found,varname); + if (found) + return vm->list + idx; + } + + if ( si && si->no_warn <= 0 ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Variable '%s' not found [%s @%u]: %.*s\n", + varname, sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const Var_t * FindVarSI +( + ScanInfo_t * si, // not NULL: use var maps + ccp varname, // variable name + bool print_warning // true && 'si': print 'not found' warning +) +{ + if (si) + { + bool found; + DASSERT(si->cur_file); + int idx = FindVarMapHelper(&si->cur_file->pvar,&found,varname); + if (found) + return si->cur_file->pvar.list + idx; + + if (si->param) + { + idx = FindVarMapHelper(si->param,&found,varname); + if (found) + return si->param->list + idx; + } + + idx = FindVarMapHelper(&si->lvar,&found,varname); + if (found) + return si->lvar.list + idx; + + idx = FindVarMapHelper(&si->gvar,&found,varname); + if (found) + return si->gvar.list + idx; + + if (si->predef) + { + idx = FindVarMapHelper(si->predef,&found,varname); + if (found) + return si->predef->list + idx; + } + } + + const Var_t *res = FindConst(varname); + if (res) + return res; + + if (print_warning) + FindVarMap(0,varname,si); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +Var_t * OverwriteVarSI +( + // returns always not NULL + + ScanInfo_t * si, // not NULL: use var maps for search + VarMap_t * vm_insert, // valid variable map, insert search failed + ccp varname, // varname to insert + int * xyz, // not NULL: enable .X/Y/Z support + // input: <0: not a X/Y/Z + // 0..2: is a X/Y/Z + // >2: unknown + // output: store corrected value + bool * found // not null: store found status (false=NEW) +) +{ + DASSERT(vm_insert); + DASSERT( vm_insert->used <= vm_insert->size ); + noPRINT("OverwriteVarSI(xyz=%d) %s\n",xyz?*xyz:-999,varname); + + Var_t *var = (Var_t*)FindVarSI(si,varname,false); + if (var) + { + if (xyz) + *xyz = -1; + if (found) + *found = true; + return var; + } + + if ( xyz && *xyz >= 0 ) + { + uint namelen = strlen(varname); + if ( *xyz > 2 ) + { + *xyz = namelen > 2 + && varname[namelen-2] == '.' + && varname[namelen-1] >= 'X' + && varname[namelen-1] <= 'Z' + ? varname[namelen-1] - 'X' : -1; + } + + if ( *xyz >= 0 && namelen > 2 && namelen <= VARNAME_SIZE ) + { + char basename[VARNAME_SIZE+1]; + memcpy(basename,varname,namelen-2); + basename[namelen-2] = 0; + var = (Var_t*)FindVarSI(si,basename,0); + if ( var && var->mode == VAR_VECTOR ) + { + if (found) + *found = true; + return var; + } + *xyz = -1; + } + } + + return InsertVarMap(vm_insert,varname,false,0,0); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +VarMap_t const_map = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +void SetupConst() +{ + if (!const_map.used) + { + InsertVarMap(&const_map,"$NONE",false,0,0); + InsertVarMap(&const_map,"$NULL",false,0,0); + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + { "$N", 0 }, + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(&const_map,ip->name,ip->val); + + struct dbltab_t { ccp name; double val; }; + static const struct dbltab_t dbltab[] = + { + { "INF", INFINITY }, + { "NAN", NAN }, + {0,0} + }; + + const struct dbltab_t * dp; + for ( dp = dbltab; dp->name; dp++ ) + DefineDoubleVar(&const_map,dp->name,dp->val); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptConst ( ccp arg ) +{ + SetupConst(); + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --const",0); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + sf->disable_comma++; + for(;;) + { + while ( *sf->ptr > 0 && *sf->ptr <= ' ' || *sf->ptr == ',' ) + sf->ptr++; + if (!*sf->ptr) + break; + + char varname[VARNAME_SIZE+1]; + const uint namelen = ScanNameSI(&si,varname,sizeof(varname),false,true,0); + if (!namelen) + { + ERROR0(ERR_SYNTAX,"Option --const: Missing name: %s\n",sf->prev_ptr); + goto abort; + } + + if ( NextCharSI(&si,false) != '=' ) + { + ERROR0(ERR_SYNTAX, + "Option --const: Missing '=' behind name: %s\n", + sf->prev_ptr); + goto abort; + } + sf->ptr++; + + Var_t * vdest = InsertVarMap( &const_map, varname, false, 0, 0 ); + DASSERT(vdest); + if (ScanExprSI(&si,vdest)) + goto abort; + } + + ResetSI(&si); + return 0; + + abort: + ResetSI(&si); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan text /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ScanFile_t empty_scan_file = {0}; // used as last file in ScanInfo_t + +/////////////////////////////////////////////////////////////////////////////// + +static void InitializeSM +( + ScanMacro_t * sm // data structure to setup +) +{ + DASSERT(sm); + memset(sm,0,sizeof(*sm)); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ResetSM +( + ScanMacro_t * sm // data structure to reset +) +{ + DASSERT(sm); + if (sm->data_alloced) + FREE((void*)sm->data); + FreeString(sm->src_name); + memset(sm,0,sizeof(*sm)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void InitializeSF +( + ScanFile_t * sf, // data structure to setup + ScanInfo_t * si, // NULL or base scan info + ccp data, // data to scan + uint data_size, // size of data + ccp name, // NULL or name for warnings + int revision, // revision number + VarMap_t * varmap // NULL or new varmap (moved) +) +{ + DASSERT(sf); + memset(sf,0,sizeof(*sf)); + + sf->data = data; + sf->ptr = data + GetTextBOMLen(data,data_size); + sf->prev_ptr = sf->ptr; + sf->end = data + data_size; + sf->line = 1; + sf->line_err = 0; + sf->name = name ? name : "?"; + sf->revision = revision; + + if (si) + { + sf->next = si->cur_file; + si->cur_file = sf; + si->n_files++; + #if HAVE_PRINT + si->debug = 0; + #endif + } + + if (varmap) + MoveVarMap(&sf->pvar,true,varmap); + else + { + InitializeVarMap(&sf->pvar); + Var_t *var = InsertVarMap(&sf->pvar,"$N",false,0,0); + DASSERT(var); + AssignIntV(var,0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ScanFile_t * AddSF +( + ScanInfo_t * si, // base scan info + ccp data, // data to scan + uint data_size, // size of data + ccp name, // NULL or name for warnings + int revision, // revision number + VarMap_t * varmap // NULL or new varmap (moved) +) +{ + DASSERT(si); + noPRINT("AddSF(rev=%u)\n",revision); + + if ( si->n_files > MAX_SOURCE_DEPTH ) + { + si->total_err++; + ScanFile_t * sf = si->cur_file; + DASSERT(sf); + ERROR0(ERR_WARNING, + "To many open source files and/or macros (max=%u) [%s @%u]\n", + MAX_SOURCE_DEPTH, sf->name, sf->line ); + return 0; + } + + ScanFile_t * sf = MALLOC(sizeof(*sf)); + InitializeSF(sf,si,data,data_size,name,revision,varmap); + noPRINT("AddSF() %p,%p, rev=%u\n",sf,sf->next,sf->revision); + return sf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ResetSF +( + ScanFile_t * sf // data structure to reset +) +{ + DASSERT(sf); + + int i; + for ( i = 0; i < MAX_LOOP_DEPTH; i++ ) + FreeString(sf->loop[i].varname); + + if (sf->data_alloced) + FREE((void*)sf->data); + + if (sf->name_alloced) + FreeString(sf->name); + + ResetVarMap(&sf->pvar); + memset(sf,0,sizeof(*sf)); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DropSF +( + ScanInfo_t * si // data structure +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + + if ( !sf || sf == &empty_scan_file ) + return false; + + si->n_files--; + si->cur_file = sf->next; + ResetSF(sf); + FREE(sf); + DASSERT(si->cur_file); + noPRINT("DROPSF() -> |%.20s|\n",si->cur_file->ptr); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSI +( + ScanInfo_t * si, // data structure to setup + ccp data, // data to scan + uint data_size, // size of data + ccp name, // NULL or name for warnings + int revision // revision number +) +{ + DASSERT(si); + DASSERT(data); + memset(si,0,sizeof(*si)); + + si->init_data = data; + si->init_data_size = data_size; + si->init_name = name; + si->init_revision = revision; + + InitializeSF(&empty_scan_file,0,0,0,EmptyString,0,0); + si->cur_file = &empty_scan_file; + empty_scan_file.revision = REVISION_NUM; + AddSF(si,data,data_size,name,revision,0); + + InitializeVarMap(&si->macro); + InitializeVarMap(&si->lvar); + InitializeVarMap(&si->gvar); + + DefineIntVar(&si->gvar,"REVISION$SETUP",revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",revision); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetHelperSI +( + ScanInfo_t * si // data structure to reset +) +{ + DASSERT(si); + + //--- close all files + + while (DropSF(si)) + ; + + //--- drop all macros + + if (si->macro.used) + { + uint count = si->macro.used; + Var_t * v = si->macro.list; + while ( count-- > 0 ) + { + ScanMacro_t *sm = v->macro; + if (sm) + { + v->macro = 0; + ResetSM(sm); + FREE(sm); + } + v++; + } + } + ResetVarMap(&si->macro); + + //--- clear variables + + ResetVarMap(&si->lvar); + si->cur_file = &empty_scan_file; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSI +( + ScanInfo_t * si // data structure to reset +) +{ + DASSERT(si); + + ResetHelperSI(si); + ResetVarMap(&si->gvar); + + memset(si,0,sizeof(*si)); + si->cur_file = &empty_scan_file; +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestartSI +( + ScanInfo_t * si // data structure to setup +) +{ + ResetHelperSI(si); + AddSF(si,si->init_data,si->init_data_size,si->init_name,si->init_revision,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetLocalVarsSI +( + ScanInfo_t * si, // data structure to setup + int revision // revision number +) +{ + DASSERT(si); + ResetVarMap(&si->lvar); + + ScanFile_t *sf ; + for ( sf = si->cur_file; sf && sf != &empty_scan_file; sf = sf->next ) + sf->revision = revision; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool TestContLineSI +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + ccp ptr = sf->ptr; + ccp end = sf->end; + uint lines = 0; + + uchar ch = 0; + for(;;) + { + if ( ptr == end ) + { + ch = 0; + break; + } + + ch = *ptr; + if ( ch == '#' ) + { + while ( ptr < end && *ptr != '\n' ) + ptr++; + continue; + } + + if ( ch == '\n' ) + lines++; + + if ( ch > ' ' ) + break; + ptr++; + } + + if ( ch != '>' ) + return false; + + sf->ptr = ptr; + if (lines) + { + sf->line += lines; + sf->line_err = 0; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +char NextCharSI +( + ScanInfo_t * si, // valid data + bool skip_lines // true: skip empty lines +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + ccp ptr = sf->ptr; + ccp end = sf->end; + + for(;;) + { + if ( ptr == end ) + { + sf->ptr = ptr; + if (!skip_lines) + { + CheckLevelSI(si); + return 0; + } + + if (!NextLineSI(si,false,true)) + return 0; + + sf = si->cur_file; + DASSERT(sf); + ptr = sf->ptr; + end = sf->end; + noPRINT("CHAR [%zu] |%.20s|\n",end-ptr,ptr); + continue; + } + + const uchar ch = *ptr++; + if ( ch == '#' ) + { + while ( ptr < end && *ptr != '\n' ) + ptr++; + continue; + } + + if ( ch == '@' && skip_lines ) + { + sf->ptr = ptr; + const TextCommand_t tcmd = ScanTextCommand(si); + if (tcmd) + { + ExecTextCommand(si,tcmd); + sf = si->cur_file; + DASSERT(sf); + ptr = sf->ptr; + end = sf->end; + continue; + } + } + + if ( ch > ' ' ) + { + sf->ptr = ptr - 1; + return ch; + } + + if ( ch == '\n' ) + { + sf->ptr = sf->prev_ptr = ptr - 1; + if (TestContLineSI(si)) + ptr = sf->ptr+1; + else + return skip_lines ? NextLineSI(si,true,true) : 0; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +char SkipCharSI +( + // return next non skipped character + + ScanInfo_t * si, // valid data + char ch // character to skip +) +{ + DASSERT(si); + const char next_ch = NextCharSI(si,false); + if ( next_ch == ch ) + { + DASSERT(si->cur_file); + si->cur_file->ptr++; + return NextCharSI(si,false); + } + return next_ch; +} + +/////////////////////////////////////////////////////////////////////////////// + +char NextLineSI +( + ScanInfo_t * si, // valid data + bool skip_cont, // true: skip continuation lines + bool allow_tcmd // true: scan and execute text commands +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + char cont_ch = skip_cont ? '>' : '#'; + ccp ptr = sf->ptr; + ccp end = sf->end; + + for(;;) + { + //--- find beginning of next line first (skip current line) + + while ( ptr < end ) + if ( *ptr++ == '\n' ) + { + sf->line++; + sf->line_err = 0; + break; + } + + //--- now find next relevant line + + for(;;) + { + if ( ptr == end ) + { + sf->ptr = sf->prev_ptr = ptr; + CheckLevelSI(si); + + if ( sf != &empty_scan_file ) + si->last_result.mode = VAR_UNSET; + if ( !allow_tcmd || !DropSF(si) ) + return 0; + + sf = si->cur_file; + DASSERT(sf); + ptr = sf->ptr; + end = sf->end; + cont_ch = '#'; + continue; + } + + // skip blanks and controls at beginning of line + + while ( ptr < end && (uchar)*ptr <= ' ' ) + { + if ( *ptr == '\n' ) + { + sf->line++; + sf->line_err = 0; + } + ptr++; + } + + if ( ptr == end ) + continue; + + if ( *ptr == '@' && allow_tcmd ) + { + sf->ptr = ptr; + const TextCommand_t tcmd = ScanTextCommand(si); + if (tcmd) + { + ExecTextCommand(si,tcmd); + sf = si->cur_file; + DASSERT(sf); + ptr = sf->ptr; + end = sf->end; + continue; + } + } + + if ( *ptr != '#' && *ptr != cont_ch ) + { + // neither comment nor continuation line + sf->ptr = sf->prev_ptr = ptr; + return *ptr; + } + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CheckLevelSI +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT(sf->if_level >= 0); + + enumError stat = ERR_OK; + + if ( sf->if_level > 0 ) + { + if ( si->no_warn <= 0 ) + { + si->total_err++; + ERROR0(ERR_WARNING, + "End of text/section within @IF..@ENDIF level #%u [%s @%u]\n", + sf->if_level, sf->name, sf->line ); + } + sf->if_level = 0; + stat = ERR_WARNING; + } + + if ( sf->loop_level > 0 ) + { + if ( si->no_warn <= 0 ) + { + si->total_err++; + ERROR0(ERR_WARNING, + "End of text/section within loop level #%u [%s @%u]\n", + sf->loop_level, sf->name, sf->line ); + } + sf->loop_level = 0; + stat = ERR_WARNING; + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +void GotoEolSI +( + ScanInfo_t * si // valid data +) +{ + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + sf->prev_ptr = sf->ptr; + while (NextCharSI(si,false)) + { + ccp ptr = sf->ptr; + while ( ptr < sf->end && *ptr != '\n' ) + ptr++; + sf->ptr = ptr; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CheckEolSI +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + sf->prev_ptr = sf->ptr; + if (!NextCharSI(si,false)) + return ERR_OK; + + if ( si->no_warn <= 0 ) + { + si->total_err++; + if (!sf->line_err) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "End of line expected [%s @%u]: %.*s\n", + sf->name, sf->line, (int)(eol - sf->ptr), sf->ptr ); + } + } + GotoEolSI(si); + return ERR_WARNING; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CheckWarnSI +( + ScanInfo_t * si, // valid data + char ch, // char to test + enumError err // previous error level +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + if ( NextCharSI(si,false) == ch ) + sf->ptr++; + else + { + if ( err < ERR_WARNING ) + err = ERR_WARNING; + + si->total_err++; + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Character '%c' expected [%s @%u]: %.*s\n", + ch, sf->name, sf->line, (int)(eol - sf->ptr), sf->ptr ); + } + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError WarnIgnoreSI +( + ScanInfo_t * si, // valid data + ccp format, // NULL or format of text before 'line ignored' + ... // arguments for 'format' +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + si->total_err++; + if ( si->no_warn <= 0 ) + { + char buf[1000]; + *buf = 0; + + if (format) + { + va_list arg; + va_start(arg,format); + vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + } + + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "%sLine ignored [%s @%u]:\n%.*s\n", + buf, sf->name, sf->line, (int)(eol - sf->ptr), sf->ptr ); + } + + GotoEolSI(si); + return ERR_WARNING; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp FindEndOfLineSI +( + ScanInfo_t * si, // valid data + ccp ptr // start search here +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + while ( ptr < sf->end && *ptr != '\n' && *ptr != '\r' && *ptr ) + ptr++; + + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp FindLineFeedSI +( + ScanInfo_t * si, // valid data + ccp ptr, // start search here + bool mark_error // true: increment error counter +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + if (mark_error) + { + sf->line_err++; + si->total_err++; + } + + while ( ptr < sf->end && *ptr != '\n' ) + ptr++; + + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp FindNextLineFeedSI +( + ScanInfo_t * si, // valid data + bool mark_error // true: increment error counter +) +{ + DASSERT(si); + DASSERT(si->cur_file); + return FindLineFeedSI(si,si->cur_file->ptr,mark_error); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint ScanNameSI +( + // return length of name + + ScanInfo_t * si, // valid data + char * name_buf, // destination buffer + uint buf_size, // size of 'name_buf' + bool allow_minus, // true: allow '-' in names + bool to_upper, // true: convert letters to upper case + int * xyz // not null: store vector index + // -1: no index, 0=x, 1=y, 2=z +) +{ + DASSERT(si); + DASSERT(name_buf); + DASSERT(buf_size>1); + NextCharSI(si,false); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + sf->prev_ptr = sf->ptr; + char *dest = name_buf, *end = name_buf + buf_size - 1; + + for(;;) + { + ccp src = sf->ptr; + while ( src < sf->end ) + { + char ch = *src; + if ( ch >= 'A' && ch <= 'Z' + || ch >= '0' && ch <= '9' + || ch == '_' + || ch == '.' + || ch == '$' + || ch == '-' && allow_minus + ) + { + // nothing to do + } + else if ( ch >= 'a' && ch <= 'z' ) + { + if (to_upper) + ch += 'A' - 'a'; + } + else + break; + + src++; + if ( dest < end ) + *dest++ = ch; + } + sf->ptr = src; + + if ( dest == name_buf || NextCharSI(si,false) != '[' ) + break; + + sf->ptr++; + DEFINE_VAR(val); + CheckWarnSI(si,']',ScanExprSI(si,&val)); + char buf[20]; + snprintf(buf,sizeof(buf),"[%d]",GetIntV(&val)); + dest = StringCopyE(dest,end,buf); + } + memset(dest,0,name_buf+buf_size-dest); + + if (xyz) + { + *xyz = dest > name_buf+2 && dest[-2] == '.' && dest[-1] >= 'X' && dest[-1] <= 'Z' + ? dest[-1] - 'X' : -1; + noPRINT("NAME=%s XYZ=%d\n",name_buf,*xyz); + } + + return dest - name_buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ConcatStringsSI +( + ScanInfo_t * si, // valid data + char ** dest_str, // store string here, use FREE + MALLOC + uint * ret_len // not NULL: store length of return string here +) +{ + DASSERT(si); + + if (dest_str) + { + FreeString(*dest_str); + *dest_str = 0; + } + if (ret_len) + *ret_len = 0; + +// [[var-string-]] support of dynamic buffer + char buf[5000]; + char *buf_end = buf + sizeof(buf) - 1; + char *dest = buf, *dest_end = buf_end - 50; + + enumError err = ERR_OK; + while ( dest < dest_end ) + { + char ch = NextCharSI(si,false); + if ( !ch || ch == ',' || ch == ';' ) + break; + + if ( ch == '"' || ch == '\'' ) + { + DEFINE_VAR(temp); + err = ScanStringSI(si,&temp); + dest = StringCopyEM(dest,dest_end,temp.str,temp.str_len); + continue; + } + +// [[var-string-]] optimize? + DEFINE_VAR(var); + err = ScanExprSI(si,&var); + if (err) + break; + + switch (var.mode) + { + case VAR_INT: + dest += snprintf(dest,buf_end-dest,"%lld",var.i); + break; + + case VAR_DOUBLE: + dest += snprintf(dest,buf_end-dest,"%5.3f",var.d); + break; + + case VAR_VECTOR: + dest += snprintf(dest,buf_end-dest,"%5.3f,%5.3f,%5.3f", + var.x, var.y, var.z ); + break; + + case VAR_STRING: // [[var-string+]] NULL support missed! + dest = StringCopyE(dest,buf_end,var.str); + break; + + case VAR_UNSET: + break; + } + } + + const uint len = dest - buf; + if (ret_len) + *ret_len = len; + if (dest_str) + { + *dest_str = MEMDUP(buf,len); + noPRINT("STRING: |%s|\n",*dest_str); + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanStringSI +( + ScanInfo_t * si, // valid data + Var_t * var // store result here (already initialized) +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT(var); + + DEFINE_VAR(temp); + temp.mode = VAR_STRING; + + enumError err = ERR_OK; + for(;;) + { + const char sep = NextCharSI(si,false); + if ( sep != '\'' && sep != '"' ) + break; + + ccp ptr = ++sf->ptr; + ccp eol = FindEndOfLineSI(si,ptr); + ccp start = ptr; + + while ( ptr < eol && *ptr != sep ) + { + const char ch = *ptr++; + if ( ch == '\\' && ptr < eol ) + ptr++; + } + + char buf[5000]; + const uint len + = ScanEscapedString( buf, sizeof(buf), start, ptr-start, true, 0, 0 ); + AppendStringVS(&temp,buf,len); + + if ( *ptr == sep ) + sf->ptr = ptr + 1; + else + { + sf->ptr = ptr; + err = ERROR0(ERR_WARNING, + "Unterminated string [%s @%u]\n", sf->name, sf->line ); + } + } + + MoveDataV(var,&temp); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char last_index_name[VARNAME_SIZE+1] = {0}; + +//----------------------------------------------------------------------------- + +int ScanIndexSI +( + ScanInfo_t * si, // valid data + enumError * err, // return value: error code + int index, // index to define if name + IndexList_t * il, // NULL or index list + int insert_mode // 0: don't insert + // 1: insert into global namespace + // 2: '1' + print "already defined" error +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + enumError local_err; + if (!err) + err = &local_err; + + char ch = NextCharSI(si,false); + if (IsNum(ch,0)) + { + u32 num; + *err = ScanU32SI(si,&num,1,0); + if (il) + DefineIL(il,index,0,0); + return num; + } + + *err = ERR_OK; + + const uint name_len + = ScanNameSI(si,last_index_name,sizeof(last_index_name),false,false,0); + if (!name_len) + { + *err = ERR_WARNING; + if (il) + DefineIL(il,index,0,0); + } + else if ( name_len > 1 || *last_index_name != '_' ) + { + if (il) + { + DefineIL(il,index,last_index_name,false); + noPRINT("DEFINE: %3u = %s\n",index,last_index_name); + } + + char *ptr = last_index_name; + while ( *ptr = toupper((int)*ptr) ) + ptr++; + + if ( insert_mode > 0 ) + { + bool found; + Var_t * var = InsertVarMap( &si->gvar, last_index_name, false, 0, &found ); + + if (found) + { + if ( insert_mode > 1 + && si->no_warn <= 0 + && ( var->mode != VAR_INT || var->i != index) ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Variable '%s' already defined [%s @%u]: %.*s\n", + last_index_name, sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + //*err = ERR_WARNING; + } + else + { + var->mode = VAR_INT; + var->i = index; + } + } + } + else + { + *last_index_name = 0; + if (il) + DefineIL(il,index,0,0); + } + + return index; +} + +//----------------------------------------------------------------------------- + +void SetIndexName ( ccp name ) +{ + if (!name) + name = ""; + char *dest = last_index_name; + char *end = dest + sizeof(last_index_name) -1 ; + while ( dest < end && *name ) + *dest++ = toupper((int)*name++); + *dest = 0; +} + +//----------------------------------------------------------------------------- + +bool DefineIndexInt +( + // returns true, if inserted + + ScanInfo_t * si, // valid data + ccp suffix, // name suffix + int value // value to assign +) +{ + if ( *last_index_name ) + { + char name[2*VARNAME_SIZE]; + snprintf(name,sizeof(name),"%s.%s",last_index_name,suffix); + + bool found; + Var_t * var = InsertVarMap( &si->gvar, name, false, 0, &found ); + noPRINT("DEFINE %s = %d\n",name,value); + var->mode = VAR_INT; + var->i = value; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool DefineIndexDouble +( + // returns true, if inserted + + ScanInfo_t * si, // valid data + ccp suffix, // name suffix + double value // value to assign +) +{ + if ( *last_index_name ) + { + char name[2*VARNAME_SIZE]; + snprintf(name,sizeof(name),"%s.%s",last_index_name,suffix); + + bool found; + Var_t * var = InsertVarMap( &si->gvar, name, false, 0, &found ); + noPRINT("DEFINE %s = %g\n",name,value); + var->mode = VAR_DOUBLE; + var->d = value; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanU8SI +( + ScanInfo_t * si, // valid data + u8 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + long num; + if (!ScanUValueSI(si,&num,force_hex)) + *dest++ = num; + else + { + while ( n-- >= 0 ) + *dest++ = 0; + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanU16SI +( + ScanInfo_t * si, // valid data + u16 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + long num; + if (!ScanUValueSI(si,&num,force_hex)) + *dest++ = num; + else + { + while ( n-- >= 0 ) + *dest++ = 0; + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanU32SI +( + ScanInfo_t * si, // valid data + u32 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + long num; + if (!ScanUValueSI(si,&num,force_hex)) + *dest++ = num; + else + { + while ( n-- >= 0 ) + *dest++ = 0; + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanBE16SI +( + ScanInfo_t * si, // valid data + u16 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + long num; + if (!ScanUValueSI(si,&num,force_hex)) + write_be16(dest++,num); + else + { + while ( n-- >= 0 ) + write_be16(dest++,0); + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanBE32SI +( + ScanInfo_t * si, // valid data + u32 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + long num; + if (!ScanUValueSI(si,&num,force_hex)) + write_be32(dest++,num); + else + { + while ( n-- >= 0 ) + write_be32(dest++,0); + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanBE32SI_swap +( + ScanInfo_t * si, // valid data + u32 * dest, // destination list + int n, // number of elements in 'dest' + int force_hex // >0: force hex for non prefixed integers +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + long num; + if (!ScanUValueSI(si,&num,force_hex)) + { + u16 temp[2]; + write_be32(temp,num); + ((u16*)dest)[0] = be16(temp); + ((u16*)dest)[1] = be16(temp+1); + dest++; + } + else + { + while ( n-- >= 0 ) + write_be32(dest++,0); + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanFloatSI +( + ScanInfo_t * si, // valid data + float * dest, // destination list + int n // number of elements in 'dest' +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + double num; + if (!ScanDValueSI(si,&num)) + *dest++ = num; + else + { + while ( n-- >= 0 ) + *dest++ = 0; + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanFloatV2SI +( + ScanInfo_t * si, // valid data + float * dest, // destination list + int n // number of 2d vectors in 'dest' +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + float *end = dest + 2*n; + + enumError err = ERR_OK; + while ( n-- > 0 ) + { + DEFINE_VAR(var); + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetXDoubleV(&var); + SkipCharSI(si,','); + + if ( var.mode == VAR_VECTOR ) + *dest++ = GetZDoubleV(&var); + else + { + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetZDoubleV(&var); + SkipCharSI(si,','); + } + } + + while ( dest < end ) + *dest++ = 0.0; + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanFloatV3SI +( + ScanInfo_t * si, // valid data + float * dest, // destination list + int n // number of 3d vectors in 'dest' +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + float *end = dest + 3*n; + + enumError err = ERR_OK; + while ( n-- > 0 ) + { + DEFINE_VAR(var); + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetXDoubleV(&var); + SkipCharSI(si,','); + + if ( var.mode == VAR_VECTOR ) + { + *dest++ = GetYDoubleV(&var); + *dest++ = GetZDoubleV(&var); + } + else + { + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetYDoubleV(&var); + SkipCharSI(si,','); + + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetZDoubleV(&var); + SkipCharSI(si,','); + } + } + + while ( dest < end ) + *dest++ = 0.0; + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanDoubleSI +( + ScanInfo_t * si, // valid data + double * dest, // destination list + int n // number of elements in 'dest' +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + while ( n-- > 0 ) + { + double num; + if (!ScanDValueSI(si,&num)) + *dest++ = num; + else + { + while ( n-- >= 0 ) + *dest++ = 0; + return ERR_WARNING; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanDoubleV3SI +( + ScanInfo_t * si, // valid data + double * dest, // destination list + int n // number of 3d vectors in 'dest' +) +{ + DASSERT(si); + DASSERT(dest); + DASSERT(n>0); + + double *end = dest + 3*n; + + enumError err = ERR_OK; + while ( n-- > 0 ) + { + DEFINE_VAR(var); + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetXDoubleV(&var); + SkipCharSI(si,','); + + if ( var.mode == VAR_VECTOR ) + { + *dest++ = GetYDoubleV(&var); + *dest++ = GetZDoubleV(&var); + } + else + { + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetYDoubleV(&var); + SkipCharSI(si,','); + + err = ScanValueSI(si,&var); + if (err) + break; + *dest++ = GetZDoubleV(&var); + SkipCharSI(si,','); + } + } + + while ( dest < end ) + *dest++ = 0.0; + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SetVarSI +( + ScanInfo_t * si, // valid data + VarMap_t * vm, // valid varmap + TextCommand_t mode // TCMD_DEF* +) +{ + DASSERT(si); + DASSERT(vm); + + enum EnumMode { EM_OFF, EM_ENUM, EM_SHIFT } enum_mode; + switch (mode) + { + case TCMD_PDEF_ENUM: enum_mode = EM_ENUM; mode = TCMD_PDEF; break; + case TCMD_PDEF_SHIFT: enum_mode = EM_SHIFT; mode = TCMD_PDEF; break; + case TCMD_LDEF_ENUM: enum_mode = EM_ENUM; mode = TCMD_LDEF; break; + case TCMD_LDEF_SHIFT: enum_mode = EM_SHIFT; mode = TCMD_LDEF; break; + case TCMD_GDEF_ENUM: enum_mode = EM_ENUM; mode = TCMD_GDEF; break; + case TCMD_GDEF_SHIFT: enum_mode = EM_SHIFT; mode = TCMD_GDEF; break; + default: enum_mode = EM_OFF; break; + } + + DEFINE_VAR(next_val); + AssignIntV( &next_val, enum_mode == EM_SHIFT ); + + restart:; + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + int xyz; + char name[VARNAME_SIZE+1]; + if (!ScanNameSI(si,name,sizeof(name),false,true,&xyz)) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing name [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + return ERR_WARNING; + } + if ( mode != TCMD_PDEF && mode != TCMD_LDEF && mode != TCMD_GDEF ) + xyz = -2; + + char ch = NextCharSI(si,false); + bool have_param = ch && ch != ','; + bool overwrite = have_param || enum_mode != EM_OFF; + if ( ch == '?' ) + { + sf->ptr++; + overwrite = false; + } + + DEFINE_VAR(val); + if (have_param) + { + if ( NextCharSI(si,false) != '=' ) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing '=' [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + return ERR_WARNING; + } + sf->ptr++; + + enumError err = ScanExprSI(si,&val); + if (err) + return err; + } + else if ( enum_mode != EM_OFF ) + { + AssignVar(&val,&next_val); + have_param = true; + noPRINT_IF( val.mode == VAR_INT, "NEXT: %s = %d\n",name,GetIntV(&val)); + noPRINT_IF( val.mode == VAR_DOUBLE, "NEXT: %s ~ %g\n",name,GetDoubleV(&val)); + } + else + val.mode = VAR_UNSET; + + switch (enum_mode) + { + case EM_ENUM: + if ( val.mode >= VAR_DOUBLE ) + AssignDoubleV(&next_val,GetDoubleV(&val)+1); + else + AssignIntV(&next_val,GetIntV(&val)+1); + break; + + case EM_SHIFT: + if ( val.mode >= VAR_DOUBLE ) + AssignDoubleV(&next_val, GetDoubleV(&val) * 2 ); + else + AssignIntV(&next_val, GetIntV(&val) << 1 ); + break; + + default: + break; + } + + bool found; + Var_t *var = OverwriteVarSI( 0, vm, name, &xyz, &found ); + + DASSERT(var); + if ( overwrite || !found ) + { + if ( xyz >= 0 ) + { + DASSERT( xyz <= 2 ); + DASSERT( var->mode == VAR_VECTOR ); + var->v[xyz] = GetDoubleV(&val); + } + else + AssignVarMode(var,&val,mode); + } + else if (!have_param) + ToVarMode(var,mode); + + if ( NextCharSI(si,false) == ',' ) + { + sf->ptr++; + goto restart; + } + + return CheckEolSI(si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanAssignIntSI +( + ScanInfo_t * si, // valid data + int * value // result: scanned value +) +{ + DASSERT(si); + DASSERT(si->cur_file); + DASSERT(value); + + if ( NextCharSI(si,false) == '=' ) + si->cur_file->ptr++; + + DEFINE_VAR(val); + enumError err = ScanExprSI(si,&val); + *value = GetIntV(&val); + return err ? err : CheckEolSI(si); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanAssignDoubleSI +( + ScanInfo_t * si, // valid data + double * value // result: scanned value +) +{ + DASSERT(si); + DASSERT(si->cur_file); + DASSERT(value); + + if ( NextCharSI(si,false) == '=' ) + si->cur_file->ptr++; + + DEFINE_VAR(val); + enumError err = ScanExprSI(si,&val); + *value = GetDoubleV(&val); + return err ? err : CheckEolSI(si); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanParamSI +( + ScanInfo_t * si, // valid data + const ScanParam_t * sp // NULL or scan parameter + // list ends with sp->name==NULL +) +{ + DASSERT(si); + + static const ScanParam_t ptab[] = {{0}}; + if (!sp) + sp = ptab; + + #if HAVE_PRINT0 + const ScanParam_t *sp0 = sp; + #endif + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + char ch = NextCharSI(si,false); + if ( ch == '@' ) + { + sf->ptr++; + ch = NextCharSI(si,false); + } + + char name[VARNAME_SIZE+1]; + if (!ScanNameSI(si,name,sizeof(name),true,true,0)) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing name [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + return ERR_WARNING; + } + + for ( ; sp->name; sp++ ) + if (!strcmp(name,sp->name)) + break; + + if (!sp->name) + { + if ( si->no_warn <= 0 ) + { + #if HAVE_PRINT0 + PRINT(">>> SEARCH: |%s|\n",name); + for ( ; sp0->name; sp0++ ) + PRINT(">>> HAVE NAME: |%s| => %d\n",sp0->name,!strcmp(name,sp0->name)); + #endif + + sf->line_err++; + si->total_err++; + ERROR0(ERR_WARNING, + "Unknown key [%s @%u]: %s\n", + sf->name, sf->line, name ); + } + + GotoEolSI(si); + return ERR_WARNING; + } + + if ( sp->mode == SPM_INC ) + { + // special case; increment value + if (sp->result) + ((int*)sp->result)[0]++; + if (sp->n_result) + *sp->n_result = 0; + + CheckEolSI(si); + return ERR_OK; + } + + if ( NextCharSI(si,false) == '=' ) + sf->ptr++; + + if ( sp->mode == SPM_STRING ) // special case: STRING + return ConcatStringsSI(si,(char**)sp->result,0); + + sf->disable_comma++; + + enumError max_err = ERR_OK; + const uint min = sp->min > 0 ? sp->min : 1; + const uint max = sp->max > min ? sp->max : min; + uint i, n_result = 0, skip = 0; + for ( i = 0; i < max; i++ ) + { + if ( i && NextCharSI(si,false) == ',' ) + sf->ptr++; + + if ( i >= min && !NextCharSI(si,false) ) + skip++; + + DEFINE_VAR(val); + if (skip) + val.mode = VAR_UNSET; + else + { + enumError err = ScanExprSI(si,&val); + if ( err > ERR_WARNING ) + skip++; + else + n_result++; + if ( max_err < err ) + max_err = err; + } + + if (sp->result) + switch(sp->mode) + { + case SPM_NONE: + case SPM_INC: + break; + + case SPM_BOOL: + ((bool*)sp->result)[i] = GetBoolV(&val); + break; + + case SPM_U8: + case SPM_S8: + ((u8*)sp->result)[i] = GetIntV(&val); + break; + + case SPM_U16: + case SPM_S16: + ((u16*)sp->result)[i] = GetIntV(&val); + break; + + case SPM_U32: + case SPM_S32: + ((u32*)sp->result)[i] = GetIntV(&val); + break; + + case SPM_U16_BE: + case SPM_S16_BE: + ((u16*)sp->result)[i] = htons(GetIntV(&val)); + break; + + case SPM_U32_BE: + case SPM_S32_BE: + ((u32*)sp->result)[i] = htonl(GetIntV(&val)); + break; + + case SPM_INT: + case SPM_UINT: + ((uint*)sp->result)[i] = GetIntV(&val); + break; + + case SPM_FLOAT: + ((float*)sp->result)[i] = GetDoubleV(&val); + break; + + case SPM_FLOAT_BE: + write_bef4( ((float*)sp->result)+i, GetDoubleV(&val) ); + break; + + case SPM_DOUBLE: + ((double*)sp->result)[i] = GetDoubleV(&val); + break; + + case SPM_DOUBLE_X: + ((double*)sp->result)[i] = GetXDoubleV(&val); + break; + + case SPM_DOUBLE_Y: + ((double*)sp->result)[i] = GetYDoubleV(&val); + break; + + case SPM_DOUBLE_Z: + ((double*)sp->result)[i] = GetZDoubleV(&val); + break; + + case SPM_FLOAT3: + ((float3*)sp->result)[i] = GetVectorFV(&val); + break; + + case SPM_FLOAT3_BE: + { + float3 src = GetVectorFV(&val); + float3 *dest = ((float3*)sp->result)+i; + write_bef4(&dest->x,src.x); + write_bef4(&dest->y,src.y); + write_bef4(&dest->z,src.z); + } + break; + + case SPM_VAR: + AssignVar(((Var_t*)sp->result)+i,&val); + break; + + case SPM_STRING: + ASSERT(0); + break; + } + } + if (sp->n_result) + *sp->n_result = n_result; + + sf->disable_comma--; + CheckEolSI(si); + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanHexlineSI +( + ScanInfo_t * si, // valid data + FastBuf_t * dest, // valid, append scanned data here + bool skip_addr // true: line may start with address field +) +{ + DASSERT(si); + DASSERT(dest); + + NextCharSI(si,false); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + //--- skip address field + + if (skip_addr) + { + ccp ptr = sf->ptr; + while (isalnum((int)*ptr)) + ptr++; + if ( ptr > sf->ptr && *ptr == ':' ) + sf->ptr = ptr + 1; + } + + Var_t val; + InitializeV(&val); + + enumError err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,false); + if (!ch) + break; + + if ( ch == ':' || ch == '/' || ch == '#' ) + { + GotoEolSI(si); + break; + } + + if ( ch == '(' ) + { + sf->ptr++; + err = ScanExprSI(si,&val); + err = CheckWarnSI(si,')',err); + if (err) + break; + + switch (val.mode) + { + case VAR_UNSET: + // nothing to do + break; + + case VAR_INT: + AppendInt64FastBuf( dest, val.i, val.int_mode ? val.int_mode : IMD_BE4 ); + break; + + case VAR_DOUBLE: + { + float f = val.d; + AppendBE32FastBuf(dest,*(u32*)&f); + } + break; + + case VAR_VECTOR: + { + float f = val.x; + AppendBE32FastBuf(dest,*(u32*)&f); + f = val.y; + AppendBE32FastBuf(dest,*(u32*)&f); + f = val.z; + AppendBE32FastBuf(dest,*(u32*)&f); + } + break; + + case VAR_STRING: + AppendFastBuf(dest,val.str,val.str_len); + break; + } + } + else + { + u8 data; + err = ScanU8SI(si,&data,1,true); + if (err) + break; + + AppendCharFastBuf(dest,data); + } + } + + CheckEolSI(si); + ResetV(&val); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// text commands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_SkipToENDIF +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + DASSERT(si->cur_file); + + const int start_level = si->cur_file->if_level; + for(;;) + { + const char ch = NextLineSI(si,true,false); // don't use 'sf' + if (!ch) + return ERR_WARNING; + if ( ch != '@' ) + continue; + + const TextCommand_t tcmd = ScanTextCommand(si); + if ( tcmd == TCMD_IF ) + si->cur_file->if_level++; + else if ( tcmd == TCMD_ENDIF && --si->cur_file->if_level < start_level ) + return ERR_OK; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_IF +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + DASSERT(si->cur_file); + TRACE("### IF ###\n"); + si->cur_file->if_level++; + + DEFINE_VAR(cond); + enumError err = ScanExprSI(si,&cond); + if (err) + return err; + + err = CheckEolSI(si); + if (err) + return err; + + while (!GetBoolV(&cond)) + { + const char ch = NextLineSI(si,true,false); + DASSERT(si->cur_file); + if (!ch) + return ERR_WARNING; + if ( ch != '@' ) + continue; + + const TextCommand_t tcmd = ScanTextCommand(si); + switch (tcmd) + { + case TCMD_IF: + si->cur_file->if_level++; + SI_SkipToENDIF(si); + break; + + case TCMD_ELIF: + err = ScanExprSI(si,&cond); + if (err) + return err; + + err = CheckEolSI(si); + if (err) + return err; + break; + + case TCMD_ELSE: + return CheckEolSI(si); + + case TCMD_ENDIF: + si->cur_file->if_level--; + return CheckEolSI(si); + + default: + break; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ELSE +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + DASSERT(si->cur_file); + TRACE("### ELSE/ELIF ###\n"); + + if ( si->cur_file->if_level <= 0 ) + { + if ( si->no_warn <= 0 ) + { + si->total_err++; + ERROR0(ERR_WARNING, + "Illegal '@ELSE' or '@ELIF' -> ignored [%s @%u]\n", + si->cur_file->name, si->cur_file->line ); + } + return ERR_WARNING; + } + + return SI_SkipToENDIF(si); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ENDIF +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + TRACE("### ENDIF ###\n"); + + CheckEolSI(si); + if ( sf->if_level <= 0 ) + { + if ( si->no_warn <= 0 ) + { + si->total_err++; + ERROR0(ERR_WARNING, + "Illegal '@ENDIF' -> ignored [%s @%u]\n", + sf->name, sf->line ); + } + return ERR_WARNING; + } + + sf->if_level--; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_DOIF +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("### DOIF ###\n"); + + enumError err = CheckWarnSI(si,'(',ERR_OK); + if (!err) + { + DEFINE_VAR(cond); + err = ScanExprSI(si,&cond); + err = CheckWarnSI(si,')',err); + if ( err || !GetBoolV(&cond) ) + GotoEolSI(si); + } + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// loop helpers + +static ccp loop_name[] = // same order as TCMD_LOOP... & TCMD_ENDLOOP... +{ + "LOOP", + "REPEAT", + "FOR", + "FOREACH", + "WHILE" +}; + +//----------------------------------------------------------------------------- + +static ccp GetLoopName ( TextCommand_t tcmd_end ) +{ + return tcmd_end >= TCMD_LOOP && tcmd_end <= TCMD_WHILE + ? loop_name[tcmd_end-TCMD_LOOP] + : tcmd_end >= TCMD_ENDLOOP && tcmd_end <= TCMD_ENDWHILE + ? loop_name[tcmd_end-TCMD_ENDLOOP] + : "?"; +} + +//----------------------------------------------------------------------------- + +static ccp GetLoopNameL ( const ScanLoop_t * loop ) +{ + return loop ? GetLoopName(loop->tcmd_end) : "-"; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ScanLoop_t * SI_CheckLoop +( + ScanInfo_t * si, // valid data + TextCommand_t tcmd_end, // loop termiantion command + enumError * return_stat // store return status (never NULL) +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT( sf->loop_level >= 0 && sf->loop_level <= MAX_LOOP_DEPTH ); + DASSERT( tcmd_end >= TCMD_ENDLOOP && tcmd_end <= TCMD_ENDWHILE ); + DASSERT(return_stat); + + noPRINT("##%s## SI_CheckLoop() line=%u, loop_level=%d\n", + GetLoopName(tcmd_end), sf->line, sf->loop_level ); + + if ( sf->loop_level < MAX_LOOP_DEPTH ) + { + ScanLoop_t * loop = sf->loop + sf->loop_level++; + + loop->tcmd_end = tcmd_end; + loop->ptr = sf->ptr; + loop->line = sf->line; + loop->if_level = sf->if_level; + + loop->count_val = 0; + loop->end_val = 1; + loop->step_val = 1; + + FreeString(loop->varname); + loop->varname = 0; + loop->expr_ptr = 0; + loop->expr_line = 0; + + *return_stat = ERR_OK; + return loop; + } + + if ( si->no_warn <= 0 ) + { + si->total_err++; + ERROR0(ERR_WARNING, + "To many nested loops (max=%u) [%s @%u]\n", + MAX_LOOP_DEPTH, sf->name, sf->line ); + } + + *return_stat = ERR_WARNING; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ScanLoop_t * SI_CheckEndLoop +( + ScanInfo_t * si, // valid data + TextCommand_t tcmd_end, // loop termiantion command + enumError * return_stat // store return status (never NULL) +) +{ + DASSERT( si ); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT( sf->loop_level >= 0 && sf->loop_level <= MAX_LOOP_DEPTH ); + DASSERT( !tcmd_end || tcmd_end >= TCMD_ENDLOOP && tcmd_end <= TCMD_ENDWHILE ); + DASSERT( return_stat ); + noPRINT("##%s## SI_CheckEndLoop() line=%u, loop_level=%d\n", + GetLoopName(tcmd_end), sf->line, sf->loop_level ); + + enumError err = CheckEolSI(si); + const int loop_level = sf->loop_level; + DASSERT( loop_level >= 0 && loop_level <= MAX_LOOP_DEPTH ); + if (loop_level) + { + ScanLoop_t * loop = sf->loop + loop_level - 1; + if ( !tcmd_end || loop->tcmd_end == tcmd_end ) + { + loop->count_val += loop->step_val; + *return_stat = err; + return loop; + } + } + + if ( si->no_warn <= 0 && tcmd_end ) + { + si->total_err++; + if (loop_level) + { + ScanLoop_t * loop = sf->loop + loop_level - 1; + ERROR0(ERR_WARNING, + "End of loop command '@END%s' does not match current '%s' loop [%s @%u]\n", + GetLoopName(tcmd_end), GetLoopName(loop->tcmd_end), + sf->name, sf->line ); + } + else + { + ERROR0(ERR_WARNING, + "End of loop command 'END%s' not allowed outside a loop [%s @%u]\n", + GetLoopName(tcmd_end), sf->name, sf->line ); + } + } + + *return_stat = ERR_WARNING; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void SI_RestartLoop +( + ScanInfo_t * si, // valid data + ScanLoop_t * loop // valid loop pointer +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT(loop); + DASSERT( loop == sf->loop + sf->loop_level - 1 ); + noPRINT("##%s## SI_RestartLoop()\n", GetLoopName(loop->tcmd_end) ); + + sf->ptr = loop->ptr; + sf->line = loop->line; + sf->if_level = loop->if_level; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SI_ExitLoop +( + ScanInfo_t * si, // valid data + ScanLoop_t * loop // valid loop pointer +) +{ + // exit loop at end-loop-command + + DASSERT(si); + noPRINT("##%s## SI_ExitLoop()\n", GetLoopNameL(loop) ); + + if (loop) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT( loop == sf->loop + sf->loop_level - 1 ); + + sf->if_level = loop->if_level; + if ( --sf->loop_level > 0 ) + { + loop--; + if ( loop >= sf->loop && loop->varname ) + { + Var_t *var = InsertVarMap(&si->lvar,loop->varname,false,0,0); + var->mode = VAR_INT; + var->i = loop->count_val; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SI_TerminateLoop +( + ScanInfo_t * si, // valid data + ScanLoop_t * loop // valid loop pointer +) +{ + DASSERT(si); + DASSERT(si->cur_file); + DASSERT(loop); + DASSERT( loop == si->cur_file->loop + si->cur_file->loop_level - 1 ); + noPRINT("##%s## SI_TerminateLoop()\n", GetLoopName(loop->tcmd_end) ); + + const TextCommand_t tcmd_beg = TCMD_LOOP - TCMD_ENDLOOP + loop->tcmd_end; + int level = 1; + for(;;) + { + const char ch = NextLineSI(si,true,false); + DASSERT(si->cur_file); + if (!ch) + return; + if ( ch != '@' ) + continue; + + const TextCommand_t tcmd = ScanTextCommand(si); + if ( tcmd == tcmd_beg ) + level++; + else if ( tcmd == loop->tcmd_end && !--level ) + break; + } + + CheckEolSI(si); + SI_ExitLoop(si,loop); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// loop commands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_LOOP +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("##LOOP(BEGIN)##\n"); + + enumError err; + ScanLoop_t * loop = SI_CheckLoop(si,TCMD_ENDLOOP,&err); + if (loop) + { + if (err) + GotoEolSI(si); + else + CheckEolSI(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + loop->ptr = sf->ptr; + loop->line = sf->line; + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ENDLOOP +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("##LOOP(END)##\n"); + + enumError err; + ScanLoop_t * loop = SI_CheckEndLoop(si,TCMD_ENDLOOP,&err); + if (loop) + SI_RestartLoop(si,loop); + else + SI_ExitLoop(si,loop); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_REPEAT +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("##REPEAT(BEGIN)##\n"); + + enumError err; + ScanLoop_t * loop = SI_CheckLoop(si,TCMD_ENDREPEAT,&err); + if (loop) + { + DEFINE_VAR(val); + err = ScanExprSI(si,&val); + loop->end_val = GetIntV(&val); + noPRINT("-> count=%d/%d +%d\n",loop->count_val,loop->end_val,loop->step_val); + + if (err) + GotoEolSI(si); + else + CheckEolSI(si); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + loop->ptr = sf->ptr; + loop->line = sf->line; + + if ( loop->count_val >= loop->end_val ) + SI_TerminateLoop(si,loop); + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ENDREPEAT +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("##REPEAT(END)##\n"); + + enumError err; + ScanLoop_t * loop = SI_CheckEndLoop(si,TCMD_ENDREPEAT,&err); + if ( loop && loop->count_val < loop->end_val ) + SI_RestartLoop(si,loop); + else + SI_ExitLoop(si,loop); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError scan_for_varname +( + ScanInfo_t * si, // valid data + ScanLoop_t * loop // valid loop pointer +) +{ + DASSERT(si); + DASSERT(loop); + + char name[VARNAME_SIZE+1]; + if (!ScanNameSI(si,name,sizeof(name),false,true,0)) + { + if ( si->no_warn <= 0 ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing name [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + return ERR_WARNING; + } + + loop->varname = STRDUP(name); + return CheckWarnSI(si,'=',0); +} + +//----------------------------------------------------------------------------- + +static enumError SI_FOR +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("##FOR(BEGIN)##\n"); + + enumError err; + ScanLoop_t * loop = SI_CheckLoop(si,TCMD_ENDFOR,&err); + if (loop) + { + err = scan_for_varname(si,loop); + if (err) + goto abort; + + DEFINE_VAR(val); + err = ScanExprSI(si,&val); + if (err) + goto abort; + loop->count_val = GetIntV(&val); + + err = CheckWarnSI(si,';',err); + if (err) + goto abort; + err = ScanExprSI(si,&val); + if (err) + goto abort; + loop->end_val = GetIntV(&val); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + if ( NextCharSI(si,false) == ';' ) + { + sf->ptr++; + err = ScanExprSI(si,&val); + if (err) + goto abort; + loop->step_val = GetIntV(&val); + } + + if (err) + GotoEolSI(si); + else + CheckEolSI(si); + loop->ptr = sf->ptr; + loop->line = sf->line; + + noPRINT("-> FOR %s = %d -> %d, step = %d\n", + loop->varname, loop->count_val, loop->end_val, loop->step_val ); + + Var_t *var = InsertVarMap(&sf->pvar,loop->varname,false,0,0); + var->mode = VAR_INT; + var->i = loop->count_val; + + if ( loop->step_val > 0 && loop->count_val > loop->end_val + || loop->step_val < 0 && loop->count_val < loop->end_val ) + goto abort; + } + return err; + + abort: + GotoEolSI(si); + if (loop) + SI_TerminateLoop(si,loop); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ENDFOR +( + ScanInfo_t * si, // valid data + ScanLoop_t * loop // not NULL: SI_CheckEndLoop() already done + // + call TermLoop() instead of ExitLoop() +) +{ + DASSERT(si); + noPRINT("##FOR(END)##\n"); + + enumError err = ERR_OK; + bool use_term = loop != 0; + if (!loop) + loop = SI_CheckEndLoop(si,TCMD_ENDFOR,&err); + + if (loop) + { + if ( loop->step_val > 0 && loop->count_val > loop->end_val + || loop->step_val < 0 && loop->count_val < loop->end_val ) + { + if (use_term) + SI_TerminateLoop(si,loop); + else + SI_ExitLoop(si,loop); + } + else + { + DASSERT(si->cur_file); + Var_t *var = InsertVarMap(&si->cur_file->pvar,loop->varname,false,0,0); + var->mode = VAR_INT; + var->i = loop->count_val; + SI_RestartLoop(si,loop); + } + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_FOREACH +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("##WHILE(BEGIN)##\n"); + + enumError err; + ScanLoop_t * loop = SI_CheckLoop(si,TCMD_ENDFOREACH,&err); + if (loop) + { + err = scan_for_varname(si,loop); + if (err) + { + GotoEolSI(si); + SI_TerminateLoop(si,loop); + return err; + } + + if (!NextCharSI(si,false)) + SI_TerminateLoop(si,loop); + else + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + Var_t *var = InsertVarMap(&sf->pvar,loop->varname,false,0,0); + err = ScanExprSI(si,var); + + loop->expr_ptr = sf->ptr; + loop->expr_line = sf->line; + + GotoEolSI(si); + loop->ptr = sf->ptr; + loop->line = sf->line; + } + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ENDFOREACH +( + ScanInfo_t * si, // valid data + ScanLoop_t * loop // not NULL: SI_CheckEndLoop() already done + // + call TermLoop() instead of ExitLoop() +) +{ + DASSERT(si); + noPRINT("##WHILE(END)##\n"); + + enumError err = ERR_OK; + bool use_term = loop != 0; + if (!loop) + loop = SI_CheckEndLoop(si,TCMD_ENDFOREACH,&err); + + if (loop) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT(loop->expr_ptr); + ccp saved_ptr = sf->ptr; + uint saved_line = sf->line; + sf->ptr = loop->expr_ptr; + sf->line = loop->expr_line; + + if ( NextCharSI(si,false) == ';' ) + sf->ptr++; + + if (NextCharSI(si,false)) + { + Var_t *var = InsertVarMap(&sf->pvar,loop->varname,false,0,0); + ccp saved_ptr = sf->ptr; + err = ScanExprSI(si,var); + if ( sf->ptr > saved_ptr ) + { + loop->expr_ptr = sf->ptr; + loop->expr_line = sf->line; + SI_RestartLoop(si,loop); + return err; + } + } + + sf->ptr = saved_ptr; + sf->line = saved_line; + } + + if (use_term) + SI_TerminateLoop(si,loop); + else + SI_ExitLoop(si,loop); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_WHILE +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("##WHILE(BEGIN)##\n"); + + enumError err; + ScanLoop_t * loop = SI_CheckLoop(si,TCMD_ENDWHILE,&err); + if (loop) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + loop->expr_ptr = sf->ptr; + loop->expr_line = sf->line; + + DEFINE_VAR(val); + err = ScanExprSI(si,&val); + + if (err) + GotoEolSI(si); + else + CheckEolSI(si); + loop->ptr = sf->ptr; + loop->line = sf->line; + + if (!GetBoolV(&val)) + SI_TerminateLoop(si,loop); + } + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ENDWHILE +( + ScanInfo_t * si, // valid data + ScanLoop_t * loop // not NULL: SI_CheckEndLoop() already done + // + call TermLoop() instead of ExitLoop() +) +{ + DASSERT(si); + noPRINT("##WHILE(END)##\n"); + + enumError err = ERR_OK; + bool use_term = loop != 0; + if (!loop) + loop = SI_CheckEndLoop(si,TCMD_ENDWHILE,&err); + + if (loop) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT(loop->expr_ptr); + ccp saved_ptr = sf->ptr; + uint saved_line = sf->line; + sf->ptr = loop->expr_ptr; + sf->line = loop->expr_line; + + DEFINE_VAR(val); + err = ScanExprSI(si,&val); + if ( !err && GetBoolV(&val) ) + { + SI_RestartLoop(si,loop); + return ERR_OK; + } + + sf->ptr = saved_ptr; + sf->line = saved_line; + } + + if (use_term) + SI_TerminateLoop(si,loop); + else + SI_ExitLoop(si,loop); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_BreakLoop +( + ScanInfo_t * si, // valid data + bool is_continue // true: is @CONTINUE +) +{ + DASSERT(si); + + ccp tcmd_name = is_continue ? "CONTINUE" : "BREAK"; + noPRINT("##%s##\n",tcmd_name); + + enumError err = ERR_OK; + char ch = NextCharSI(si,false); + int level = 1; + if ( ch && ch != '@' ) + { + DEFINE_VAR(val); + err = ScanExprSI(si,&val); + level = GetIntV(&val); + if ( level < 1 ) + level = 1; + ch = NextCharSI(si,false); + } + + bool cond = true; + if ( ch == '@' ) + { + const TextCommand_t tcmd = ScanTextCommand(si); + if ( tcmd != TCMD_IF ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ERROR0(ERR_WARNING, + "Commandline '@%s ... @IF ...' expected [%s @%u]\n", + tcmd_name, sf->name, sf->line ); + } + else + { + DEFINE_VAR(val); + err = ScanExprSI(si,&val); + if (!err) + cond = GetBoolV(&val); + } + } + CheckEolSI(si); + noPRINT(""##%s##\n" -> cond = %d, level = %d\n",cond,level); + if (!cond) + return err; + + while ( level-- > 0 ) + { + ScanLoop_t * loop = SI_CheckEndLoop(si,0,&err); + if (!loop) + break; + + if ( !is_continue || level > 0 ) + SI_TerminateLoop(si,loop); + else + { + // CONTINUE + switch(loop->tcmd_end) + { + case TCMD_ENDLOOP: + SI_RestartLoop(si,loop); + break; + + case TCMD_ENDREPEAT: + if ( loop->count_val < loop->end_val ) + SI_RestartLoop(si,loop); + else + SI_TerminateLoop(si,loop); + break; + + case TCMD_ENDFOR: + SI_ENDFOR(si,loop); + break; + + case TCMD_ENDFOREACH: + SI_ENDFOREACH(si,loop); + break; + + case TCMD_ENDWHILE: + SI_ENDWHILE(si,loop); + break; + } + } + } + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// exec, include and macros + +static enumError SI_EXEC +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + char *str = 0; + uint len = 0; + enumError err = ConcatStringsSI(si,&str,&len); + + if (!err) + { + sf = AddSF(si,str,len,"@EXEC",si->init_revision,0); + if (sf) + { + sf->data_alloced = true; + NextCharSI(si,true); + } + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_INCLUDE +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + const bool have_quote = NextCharSI(si,false) == '"'; + sf->prev_ptr = sf->ptr; + + ccp start, ptr; + if (have_quote) + { + start = ptr = sf->ptr + 1; + while ( ptr < sf->end && *ptr && *ptr != '"' ) + ptr++; + + if ( *ptr == '"' ) + sf->ptr = ptr + 1; + else + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing '\"' [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + sf->ptr = ptr; + } + } + else + { + start = ptr = sf->ptr; + while ( ptr < sf->end && (u8)*ptr > ' ' ) + ptr++; + sf->ptr = ptr; + } + CheckEolSI(si); + + char path[PATH_MAX]; + ccp slash = strrchr(sf->name,'/'); + uint len1 = slash ? slash+1 - sf->name : 0; + if ( len1 > sizeof(path) - 1 ) + len1 = sizeof(path) - 1; + memcpy(path,sf->name,len1); + + uint len2 = ptr - start; + if ( len2 > sizeof(path) - len1 - 1 ) + len2 = sizeof(path) - len1 - 1; + memcpy(path+len1,start,len2); + path[len1+len2] = 0; + + PRINT("SI_INCLUDE(%s) quote=%d\n",path,have_quote); + + u8 *data; + uint size; + ccp fname; + enumError err = OpenReadFILE(path,0,false,&data,&size,&fname,0); + if (err) + { + FREE(data); + FreeString(fname); + } + else + { + sf = AddSF(si,(ccp)data,size,fname,si->init_revision,0); + if (sf) + { + sf->data_alloced = sf->name_alloced = true; + noPRINT(" -> %.30s\n",sf->ptr); + NextCharSI(si,true); + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_RETURN +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("SI_RETURN()\n"); + + si->last_result.mode = VAR_UNSET; + enumError err = ERR_OK; + if (NextCharSI(si,false)) + { + err = ScanExprSI(si,&si->last_result); + CheckEolSI(si); + } + + DropSF(si); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_EXIT +( + ScanInfo_t * si, // valid data + bool is_assert // true: @ASSERT +) +{ + DASSERT(si); + PRINT("SI_RETURN()\n"); + + enumError err = ERR_OK; + if (is_assert) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ccp prev = sf->ptr; + + DEFINE_VAR(cond); + ScanExprSI(si,&cond); + if (GetBoolV(&cond)) + { + CheckEolSI(si); + return ERR_OK; + } + + //ccp eol = FindNextLineFeedSI(si,true); + err = ERROR0(ERR_ERROR, + "Assertion failed [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(sf->ptr - prev), prev ); + GotoEolSI(si); + } + + CheckEolSI(si); + while (DropSF(si)) + ; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_MACRO +( + ScanInfo_t * si, // valid data + TextCommand_t tcmd_beg // command to execute +) +{ + DASSERT(si); + + char mname[VARNAME_SIZE+1]; + bool valid = ScanNameSI(si,mname,sizeof(mname),false,true,0) > 0; + CheckEolSI(si); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ccp macro_begin = sf->ptr; + ccp macro_end = macro_begin; + uint macro_line = sf->line; + + //--- skip macro body + + const TextCommand_t tcmd_end = TCMD_ENDMACRO - TCMD_MACRO + tcmd_beg; + int level = 1; + for(;;) + { + const char ch = NextLineSI(si,true,false); + DASSERT(si->cur_file); + if (!ch) + { + ERROR0(ERR_WARNING, + "End of source reached, but %s '%s' not terminated [%s @%u]\n", + tcmd_beg == TCMD_FUNCTION ? "function" : "macro", + mname, sf->name, macro_line ); + macro_end = sf->ptr; + valid = false; + break; + } + if ( ch != '@' ) + continue; + + macro_end = sf->ptr; + const TextCommand_t tcmd = ScanTextCommand(si); + if ( tcmd == tcmd_beg ) + level++; + else if ( tcmd == tcmd_end && !--level ) + break; + } + + if (valid) + { + bool found; + Var_t * mvar = InsertVarMap(&si->macro,mname,false,0,&found); + DASSERT(mvar); + ScanMacro_t *macro; + if (found) + { + macro = mvar->macro; + DASSERT(macro); + ResetSM(macro); + } + else + { + //DASSERT(!mvar->macro); + macro = MALLOC(sizeof(*macro)); + DASSERT(macro); + InitializeSM(macro); + mvar->macro = macro; + } + + DASSERT( sf == si->cur_file ); + DASSERT( sf->ptr > macro_begin ); + const uint macro_size = macro_end - macro_begin; + macro->data = MEMDUP(macro_begin,macro_size); + DASSERT(macro->data); + macro->data_size = macro_size; + macro->data_alloced = true; + macro->line0 = macro_line; + macro->map = mvar; + macro->src_name = STRDUP(sf->name); + macro->is_function = tcmd_beg == TCMD_FUNCTION; + + PRINT("DEFINE %s '%s' : line=%u, size=%u\n", + macro->is_function ? "FUNCTION" : "MACRO", mname, + macro_line, macro_size ); + } + + return valid ? ERR_OK : ERR_WARNING; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_ENDMACRO +( + ScanInfo_t * si, // valid data + TextCommand_t tcmd // command to execute +) +{ + DASSERT(si); + PRINT("SI_ENDMACRO(%u) func=%d\n",tcmd,tcmd==TCMD_ENDFUNCTION); + CheckEolSI(si); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + if (!sf->active_macro) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "No active %s, END%s command ignored [%s @%u]: %.*s\n", + tcmd==TCMD_ENDFUNCTION ? "function" : "macro", + tcmd==TCMD_ENDFUNCTION ? "FUNCTION" : "MACRO", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + return ERR_WARNING; + } + + si->last_result.mode = VAR_UNSET; + DropSF(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const ScanMacro_t *ScanMacroNameSI +( + ScanInfo_t * si, // valid data + char * mname, // pointer to name buffer + uint mname_size // size of 'mname' +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + DASSERT(mname); + DASSERT(mname_size>1); + + //--- scan macro name + + if (!ScanNameSI(si,mname,mname_size,false,true,0)) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Missing macro name [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + GotoEolSI(si); + return 0; + } + + + //--- find macro name + + const Var_t * mvar = FindVarMap(&si->macro,mname,0); + if (!mvar) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Macro '%s' not found [%s @%u]: %.*s\n", + mname, sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + GotoEolSI(si); + return 0; + } + + DASSERT(mvar->macro); + return mvar->macro; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_CALL +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("SI_CALL()\n"); + + //--- scan macro name + + char mname[VARNAME_SIZE+1]; + const ScanMacro_t *macro = ScanMacroNameSI(si,mname,sizeof(mname)); + if (!macro) + return ERR_WARNING; + + + //--- evaluate parameters + + uint n_param = 0; + enumError err = ERR_OK; + VarMap_t pvar; + InitializeVarMap(&pvar); + + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + if ( NextCharSI(si,false) == '(' ) + { + sf->ptr++; + if ( NextCharSI(si,false) != ')' ) + { + sf->disable_comma++; + for(;;) + { + char pname[20]; + snprintf(pname,sizeof(pname),"$%u",++n_param); + Var_t *param = InsertVarMap(&pvar,pname,false,0,0); + DASSERT(param); + enumError err1 = ScanExprSI(si,param); + if ( err < err1 ) + err = err1; + if ( NextCharSI(si,false) != ',' ) + break; + sf->ptr++; + } + sf->disable_comma--; + } + err = CheckWarnSI(si,')',err); + } + Var_t *var = InsertVarMap(&pvar,"$N",false,0,0); + DASSERT(var); + AssignIntV(var,n_param); + CheckEolSI(si); + + + //--- call macro + + sf = AddSF(si,macro->data,macro->data_size,macro->src_name,sf->revision,&pvar); + if (sf) + { + DASSERT(!pvar.used); + DASSERT(!pvar.list); + sf->active_macro = macro; + sf->line = macro->line0; + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SI_PARAMETERS +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + noPRINT("SI_PARAMETERS()\n"); + + enumError err = ERR_OK; + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + const Var_t *var = FindVarMap(&sf->pvar,"$N",0); + const int n = var && var->mode != VAR_UNSET ? GetIntV(var) : 0; + + int i; + for ( i = 1;; i++ ) + { + while ( NextCharSI(si,false) == ',' ) + sf->ptr++; + + if (!NextCharSI(si,false)) + break; + + char varname[VARNAME_SIZE+1]; + int xyz; + const uint namelen + = ScanNameSI(si,varname,sizeof(varname),false,true,&xyz); + if (!namelen) + { + ccp eol = FindNextLineFeedSI(si,true); + err = ERROR0(ERR_WARNING, + "Missing name [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + GotoEolSI(si); + break; + } + + Var_t *dest = OverwriteVarSI(0,&sf->pvar,varname,&xyz,0); + DASSERT(dest); + dest->mode = VAR_UNSET; + noPRINT("DEST#%u/%u: %p |%s|\n",i,n,dest,varname); + + if ( i <= n ) + { + char pname[20]; + snprintf(pname,sizeof(pname),"$%u",i); + const Var_t *src = FindVarMap(&sf->pvar,pname,0); + noPRINT("SRC#%u/%u: %p |%s|\n",i,n,src,pname); + if (src) + AssignVar(dest,src); + } + } + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +TextCommand_t GetTextCommand +( + ccp name // name to scan, upper case is assumed +) +{ + static FormatField_t cmdtab = {0}; + if (!cmdtab.used) + { + struct tcmdtab_t { ccp name; int id; }; + static const struct tcmdtab_t def_tab[] = + { + //--- messages + + { "ECHO", TCMD_ECHO }, + { "WARN", TCMD_WARN }, + + //--- reset var maps + + { "RESET-PRIVATE", TCMD_RESET_PRIVATE }, + { "RESET-LOCAL", TCMD_RESET_LOCAL }, + { "RESET-GLOBAL", TCMD_RESET_GLOBAL }, + + //--- dump var maps + + { "DUMP-VAR", TCMD_DUMP_VAR }, + { "DUMP-PRIVATE", TCMD_DUMP_PRIVATE }, + { "DUMP-LOCAL", TCMD_DUMP_LOCAL }, + { "DUMP-GLOBAL", TCMD_DUMP_GLOBAL }, + { "DUMP-CONST", TCMD_DUMP_CONST }, + { "DUMP-PREDEF", TCMD_DUMP_PREDEF }, + + //--- settings + + { "REVISION", TCMD_REVISION }, + { "SZS-MODIFIER", TCMD_SZS_MODIFIER }, + { "SMOD", TCMD_SZS_MODIFIER }, + { "SETUP-RANDOM", TCMD_SETUP_RANDOM }, + + //--- define variables + + { "DEF", TCMD_LDEF }, + { "DEF.I", TCMD_LDEF_I }, + { "DEF.F", TCMD_LDEF_F }, + { "DEF.S", TCMD_LDEF_S }, + { "DEF.V", TCMD_LDEF_V }, + { "DEF.X", TCMD_LDEF_X }, + { "DEF.Y", TCMD_LDEF_Y }, + { "DEF.Z", TCMD_LDEF_Z }, + { "DEF.ENUM", TCMD_LDEF_ENUM }, + { "DEF.SHIFT", TCMD_LDEF_SHIFT }, + + { "PDEF", TCMD_PDEF }, + { "PDEF.I", TCMD_PDEF_I }, + { "PDEF.F", TCMD_PDEF_F }, + { "PDEF.S", TCMD_PDEF_S }, + { "PDEF.V", TCMD_PDEF_V }, + { "PDEF.X", TCMD_PDEF_X }, + { "PDEF.Y", TCMD_PDEF_Y }, + { "PDEF.Z", TCMD_PDEF_Z }, + { "PDEF.ENUM", TCMD_PDEF_ENUM }, + { "PDEF.SHIFT", TCMD_PDEF_SHIFT }, + + { "LDEF", TCMD_LDEF }, + { "LDEF.I", TCMD_LDEF_I }, + { "LDEF.F", TCMD_LDEF_F }, + { "LDEF.S", TCMD_LDEF_S }, + { "LDEF.V", TCMD_LDEF_V }, + { "LDEF.X", TCMD_LDEF_X }, + { "LDEF.Y", TCMD_LDEF_Y }, + { "LDEF.Z", TCMD_LDEF_Z }, + { "LDEF.ENUM", TCMD_LDEF_ENUM }, + { "LDEF.SHIFT", TCMD_LDEF_SHIFT }, + + { "GDEF", TCMD_GDEF }, + { "GDEF.I", TCMD_GDEF_I }, + { "GDEF.F", TCMD_GDEF_F }, + { "GDEF.S", TCMD_GDEF_S }, + { "GDEF.V", TCMD_GDEF_V }, + { "GDEF.X", TCMD_GDEF_X }, + { "GDEF.Y", TCMD_GDEF_Y }, + { "GDEF.Z", TCMD_GDEF_Z }, + { "GDEF.ENUM", TCMD_GDEF_ENUM }, + { "GDEF.SHIFT", TCMD_GDEF_SHIFT }, + + //--- define variables (old names) + + { "NUM", TCMD_LDEF }, + { "GNUM", TCMD_GDEF }, + { "INT", TCMD_LDEF_I }, + { "GINT", TCMD_GDEF_I }, + { "FLOAT", TCMD_LDEF_F }, + { "GFLOAT", TCMD_GDEF_F }, + + //--- if then else + + { "IF", TCMD_IF }, + { "ELIF", TCMD_ELIF }, + { "ELSEIF", TCMD_ELIF }, + { "ELSE", TCMD_ELSE }, + { "ENDIF", TCMD_ENDIF }, + { "FI", TCMD_ENDIF }, + + { "DOIF", TCMD_DOIF }, + + //--- loops + + { "LOOP", TCMD_LOOP }, + { "REPEAT", TCMD_REPEAT }, + { "FOR", TCMD_FOR }, + { "FOREACH", TCMD_FOREACH }, + { "WHILE", TCMD_WHILE }, + + { "ENDLOOP", TCMD_ENDLOOP }, + { "ENDREPEAT", TCMD_ENDREPEAT }, + { "ENDFOR", TCMD_ENDFOR }, + { "ENDFOREACH", TCMD_ENDFOREACH }, + { "ENDEACH", TCMD_ENDFOREACH }, + { "ENDWHILE", TCMD_ENDWHILE }, + + { "BREAK", TCMD_BREAK }, + { "CONTINUE", TCMD_CONTINUE }, + + //--- include & return + + { "EXEC", TCMD_EXEC }, + { "INCLUDE", TCMD_INCLUDE }, + { "RETURN", TCMD_RETURN }, + { "EXIT", TCMD_EXIT }, + { "ASSERT", TCMD_ASSERT }, + + //--- macros and functions + + { "MACRO", TCMD_MACRO }, + { "ENDMACRO", TCMD_ENDMACRO }, + { "FUNCTION", TCMD_FUNCTION }, + { "ENDFUNCTION", TCMD_ENDFUNCTION }, + + { "CALL", TCMD_CALL }, + { "PARAMETERS", TCMD_PARAMETERS }, + { "PARAM", TCMD_PARAMETERS }, + + {0,0} + }; + + const struct tcmdtab_t * cp; + for ( cp = def_tab; cp->name; cp++ ) + InsertFormatField(&cmdtab,cp->name,0,0,0)->num = cp->id; + } + + FormatFieldItem_t * item = FindFormatField(&cmdtab,name); + return item ? item->num : TCMD_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextCommand +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + if ( si->fast_scan <= 0 ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + ScanFile_t saved_sf; + memcpy(&saved_sf,sf,sizeof(saved_sf)); + if ( NextCharSI(si,false) == '@' ) + sf->ptr++; + + char name[VARNAME_SIZE+1]; + if (ScanNameSI(si,name,sizeof(name),true,true,0)) + { + const TextCommand_t tcmd = GetTextCommand(name); + //if ( tcmd && ( sf->no_if <= 0 || tcmd < TCMD_IF || tcmd > TCMD_ENDIF )) + if (tcmd) + { + noPRINT("CMD FOUND @ line %u: %u %s |%.10s|\n", + sf->line, tcmd, name, sf->ptr ); + return tcmd; + } + } + + switch(NextCharSI(si,false)) + { + case ':': + sf->ptr++; + return TCMD_CALL; + + case '>': + sf->ptr++; + return TCMD_EXEC; + } + + memcpy(sf,&saved_sf,sizeof(*sf)); + } + return TCMD_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ExecTextCommand +( + ScanInfo_t * si, // valid data + TextCommand_t tcmd // command to execute +) +{ + DASSERT(si); + DASSERT(si->cur_file); + TRACE("ExecTextCommand(%u)\n",tcmd); + + switch (tcmd) + { + case TCMD_ECHO: + { + char * str = 0; + enumError err = ConcatStringsSI(si,&str,0); + if ( si->fast_scan || si->no_warn ) + err = ERR_OK; + else if ( str && !opt_no_echo ) + fprintf(stdlog,"%s\n",str); + FreeString(str); + return err; + } + + case TCMD_WARN: + { + char * str = 0; + enumError err = ConcatStringsSI(si,&str,0); + if ( si->fast_scan || si->no_warn ) + err = ERR_OK; + else if ( !err && str ) + err = ERROR0(ERR_WARNING,"%s",str); + FreeString(str); + return err; + } + + case TCMD_RESET_PRIVATE: + ResetVarMap(&si->cur_file->pvar); + return ERR_OK; + + case TCMD_RESET_LOCAL: + ResetVarMap(&si->lvar); + return ERR_OK; + + case TCMD_RESET_GLOBAL: + ResetVarMap(&si->gvar); + return ERR_OK; + + case TCMD_DUMP_VAR: + case TCMD_DUMP_PRIVATE: + case TCMD_DUMP_LOCAL: + case TCMD_DUMP_GLOBAL: + case TCMD_DUMP_CONST: + case TCMD_DUMP_PREDEF: + { + if (NextCharSI(si,false)) + { + DEFINE_VAR(var); + if ( ScanExprSI(si,&var) || !GetBoolV(&var) ) + return ERR_OK; + } + else if (si->no_warn) + return ERR_OK; + + if (si->fast_scan) + return ERR_OK; + + if ( tcmd == TCMD_DUMP_VAR || tcmd == TCMD_DUMP_PRIVATE ) + { + fprintf(stdlog,"VAR DUMP/PRIVATE:\n"); + DumpVarMap(stdlog,3,&si->cur_file->pvar,false); + } + if ( tcmd == TCMD_DUMP_VAR || tcmd == TCMD_DUMP_LOCAL ) + { + fprintf(stdlog,"VAR DUMP/LOCAL:\n"); + DumpVarMap(stdlog,3,&si->lvar,false); + } + if ( tcmd == TCMD_DUMP_VAR || tcmd == TCMD_DUMP_GLOBAL ) + { + fprintf(stdlog,"VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdlog,3,&si->gvar,false); + } + if ( tcmd == TCMD_DUMP_VAR || tcmd == TCMD_DUMP_CONST ) + { + fprintf(stdlog,"VAR DUMP/CONST:\n"); + DumpVarMap(stdlog,3,&const_map,false); + } + if ( tcmd == TCMD_DUMP_PREDEF ) + { + fprintf(stdlog,"VAR DUMP/PREDEF:\n"); + if (si->predef) + DumpVarMap(stdlog,3,si->predef,false); + } + return ERR_OK; + } + + + case TCMD_REVISION: + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + enumError err = ScanAssignIntSI(si,&sf->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",sf->revision); + return err; + } + + case TCMD_SZS_MODIFIER: + return ScanAssignIntSI(si,&si->cur_file->szs_modifier); + + case TCMD_SETUP_RANDOM: + { + int num; + enumError err = ScanAssignIntSI(si,&num); + MySeed(num); + return err; + } + + case TCMD_PDEF: + case TCMD_PDEF_I: + case TCMD_PDEF_F: + case TCMD_PDEF_S: + case TCMD_PDEF_V: + case TCMD_PDEF_X: + case TCMD_PDEF_Y: + case TCMD_PDEF_Z: + case TCMD_PDEF_ENUM: + case TCMD_PDEF_SHIFT: return SetVarSI(si,&si->cur_file->pvar,tcmd); + + case TCMD_LDEF: + case TCMD_LDEF_I: + case TCMD_LDEF_F: + case TCMD_LDEF_S: + case TCMD_LDEF_V: + case TCMD_LDEF_X: + case TCMD_LDEF_Y: + case TCMD_LDEF_Z: + case TCMD_LDEF_ENUM: + case TCMD_LDEF_SHIFT: return SetVarSI(si,&si->lvar,tcmd); + + case TCMD_GDEF: + case TCMD_GDEF_I: + case TCMD_GDEF_F: + case TCMD_GDEF_S: + case TCMD_GDEF_V: + case TCMD_GDEF_X: + case TCMD_GDEF_Y: + case TCMD_GDEF_Z: + case TCMD_GDEF_ENUM: + case TCMD_GDEF_SHIFT: return SetVarSI(si,&si->gvar,tcmd); + + case TCMD_IF: return SI_IF(si); + case TCMD_ELIF: + case TCMD_ELSE: return SI_ELSE(si); + case TCMD_ENDIF: return SI_ENDIF(si); + + case TCMD_DOIF: return SI_DOIF(si); + + case TCMD_LOOP: return SI_LOOP(si); + case TCMD_REPEAT: return SI_REPEAT(si); + case TCMD_FOR: return SI_FOR(si); + case TCMD_FOREACH: return SI_FOREACH(si); + case TCMD_WHILE: return SI_WHILE(si); + + case TCMD_ENDLOOP: return SI_ENDLOOP(si); + case TCMD_ENDREPEAT: return SI_ENDREPEAT(si); + case TCMD_ENDFOR: return SI_ENDFOR(si,0); + case TCMD_ENDFOREACH: return SI_ENDFOREACH(si,0); + case TCMD_ENDWHILE: return SI_ENDWHILE(si,0); + + case TCMD_BREAK: return SI_BreakLoop(si,false); + case TCMD_CONTINUE: return SI_BreakLoop(si,true); + + case TCMD_EXEC: return SI_EXEC(si); + case TCMD_INCLUDE: return SI_INCLUDE(si); + case TCMD_RETURN: return SI_RETURN(si); + case TCMD_EXIT: return SI_EXIT(si,false); + case TCMD_ASSERT: return SI_EXIT(si,true); + + case TCMD_MACRO: + case TCMD_FUNCTION: return SI_MACRO(si,tcmd); + case TCMD_ENDMACRO: + case TCMD_ENDFUNCTION: return SI_ENDMACRO(si,tcmd); + + case TCMD_CALL: return SI_CALL(si); + case TCMD_PARAMETERS: return SI_PARAMETERS(si); + + default: + DASSERT(0); + return ERR_WARNING; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// expressions: definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[OpCounter_t]] created by work/scripts/create-db/optab.gen + +typedef enum OpCounter_t +{ + OP_MAX_LEN = 3, + OP_CHARMAP_SIZE = 128, + OP_CHARIDX_SIZE = 15, + OP_TAB_SIZE = 29 + +} OpCounter_t; + +//----------------------------------------------------------------------------- +// [[OpPrio_t]] created by work/scripts/create-db/optab.gen + +typedef enum OpPrio_t +{ + OPP_NONE, // 3* + OPP_JOIN, // 1* + OPP_LOR, // 1* + OPP_LEOR, // 2* + OPP_LAND, // 1* + OPP_OR, // 1* + OPP_EOR, // 2* + OPP_AND, // 1* + OPP_EQ, // 5* + OPP_CMP, // 4* + OPP_SHIFT, // 2* + OPP_ADD, // 2* + OPP_MULT, // 3* + OPP_POW, // 1* + + OPP__N // = 14 + +} OpPrio_t; + +//----------------------------------------------------------------------------- +// [[OpMode_t]] created by work/scripts/create-db/optab.gen + +typedef enum OpMode_t +{ + OPM_0, // 27* + OPM_AND, // 1* + OPM_OR, // 1* + +} OpMode_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[Operator_t]] // created by work/scripts/create-db/optab.gen + +typedef struct Operator_t +{ + OpId_t id; + OpPrio_t prio; + OpMode_t mode; + int op_len; + const char op_name[OP_MAX_LEN+1]; + const u8 sub_index[OP_CHARIDX_SIZE]; + +} Operator_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[op_tab]] // created by work/scripts/create-db/optab.gen + +static const Operator_t op_tab[OP_TAB_SIZE] = // OP_TAB_SIZE := 29 +{ + { OPI_NONE, OPP_NONE, OPM_0, 0,"", {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14} }, + + { OPI_NONE, OPP_NONE, OPM_0, 1,"!", {0,0,0,0,0,0,0,0,0,15,0,0,0,0,0} }, + { OPI_MOD, OPP_MULT, OPM_0, 1,"%", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_AND, OPP_AND, OPM_0, 1,"&", {0,0,0,16,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_MULT, OPP_MULT, OPM_0, 1,"*", {0,0,0,0,17,0,0,0,0,0,0,0,0,0,0} }, + { OPI_ADD, OPP_ADD, OPM_0, 1,"+", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_SUB, OPP_ADD, OPM_0, 1,"-", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_DIV, OPP_MULT, OPM_0, 1,"/", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_LT, OPP_CMP, OPM_0, 1,"<", {0,0,0,0,0,0,0,0,18,19,20,0,0,0,0} }, + { OPI_NONE, OPP_NONE, OPM_0, 1,"=", {0,0,0,0,0,0,0,0,0,21,0,0,0,0,0} }, + { OPI_GT, OPP_CMP, OPM_0, 1,">", {0,0,0,0,0,0,0,0,0,22,23,0,0,0,0} }, + { OPI_JOIN, OPP_JOIN, OPM_0, 1,"@", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_EOR, OPP_EOR, OPM_0, 1,"^", {0,0,0,0,0,0,0,0,0,0,0,0,24,0,0} }, + { OPI_OR, OPP_OR, OPM_0, 1,"|", {0,0,0,0,0,0,0,0,0,0,0,0,0,25,0} }, + { OPI_EOR, OPP_EOR, OPM_0, 1,"~", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,26} }, + + { OPI_NEQ, OPP_EQ, OPM_0, 2,"!=", {0,0,0,0,0,0,0,0,0,27,0,0,0,0,0} }, + { OPI_LAND, OPP_LAND, OPM_AND, 2,"&&", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_POW, OPP_POW, OPM_0, 2,"**", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_LSHIFT, OPP_SHIFT, OPM_0, 2,"<<", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_LE, OPP_CMP, OPM_0, 2,"<=", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_NEQ, OPP_EQ, OPM_0, 2,"<>", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_EQ, OPP_EQ, OPM_0, 2,"==", {0,0,0,0,0,0,0,0,0,28,0,0,0,0,0} }, + { OPI_GE, OPP_CMP, OPM_0, 2,">=", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_RSHIFT, OPP_SHIFT, OPM_0, 2,">>", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_LEOR, OPP_LEOR, OPM_0, 2,"^^", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_LOR, OPP_LOR, OPM_OR, 2,"||", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_LEOR, OPP_LEOR, OPM_0, 2,"~~", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + + { OPI_NEQNEQ, OPP_EQ, OPM_0, 3,"!==", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, + { OPI_EQEQ, OPP_EQ, OPM_0, 3,"===", {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }, +}; + +/////////////////////////////////////////////////////////////////////////////// +// created by work/scripts/create-db/optab.gen + +static const u8 op_charmap[OP_CHARMAP_SIZE] = // OP_CHARMAP_SIZE := 128 +{ + 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, 1, 0, 0, 0, 2, 3, 0, 0, 0, 4, 5, 0, 6, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10, 0, + 11, 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, 12, 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, 13, 0, 14, 0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// expressions: helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const Operator_t * FindOperator +( + ScanInfo_t * si // valid data +) +{ + DASSERT(si); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + const Operator_t *op = op_tab, *found = op_tab; + + NextCharSI(si,false); + ccp ptr = sf->prev_ptr = sf->ptr; + while ( ptr < sf->end && (uchar)*ptr < OP_CHARMAP_SIZE ) + { + const uint ch_idx = op_charmap[(uchar)*ptr]; + DASSERT( ch_idx < OP_CHARIDX_SIZE ); + const uint op_idx = op->sub_index[ch_idx]; + if (!op_idx) + break; + ptr++; + DASSERT( op_idx < OP_TAB_SIZE ); + op = op_tab + op_idx; + if (op->id) + { + found = op; + sf->ptr = ptr; + } + // else: a temporary step + } + noPRINT("FindOperator(%.*s) %zu\n", + (int)(sf->ptr - sf->prev_ptr), sf->prev_ptr, found - op_tab ); + return found; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void ShiftLeftV ( Var_t *val, int shift ) +{ + #undef MAXBIT + #define MAXBIT ((int)(CHAR_BIT*sizeof(val->i))) + + DASSERT(val); + switch(val->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + if ( shift >= MAXBIT ) val->i = 0; + else if ( shift >= 0 ) val->i <<= shift; + else if ( shift > -MAXBIT ) val->i >>= -shift; + else if ( val->i < 0 ) val->i = -1; + else val->i = 0; + break; + + case VAR_DOUBLE: + val->d = ldexp(val->d,shift); + break; + + case VAR_VECTOR: + val->x = ldexp(val->x,shift); + val->y = ldexp(val->y,shift); + val->z = ldexp(val->z,shift); + break; + + case VAR_STRING: + // ignored! + break; + } + + #undef MAXBIT +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcExpr +( + Var_t * dest, // first source and destination + const Var_t * src2, // second source + OpId_t op_id, // operator id + ScanInfo_t * si // not NULL: Use for warnings +) +{ + DASSERT(dest); + DASSERT(src2); + DASSERT( op_id > OPI_NONE && op_id < OPI__N ); + + noPRINT("%%%%%% CalcExpr() %p,%d,%p %s %p,%d,%p\n", + dest,dest->mode,dest->str, op_tab[op_id].op_name, + src2,src2->mode,src2->str ); + + dest->int_mode = IMD_UNSET; + + + //--- if one unset: assign only! + + if ( src2->mode == VAR_UNSET ) + return; + + if ( dest->mode == VAR_UNSET ) + { + AssignVar(dest,src2); + return; + } + + //--- OPI_JOIN is the only force-string operator + + DEFINE_VAR(temp); + + if ( op_id == OPI_JOIN ) + { + op_join:; + AssignStringVV(&temp,dest); + AppendStringVV(&temp,src2); + MoveDataV(dest,&temp); + return; + } + + + //--- string operations + + if ( dest->mode == VAR_STRING || src2->mode == VAR_STRING ) + { + if ( dest->mode == VAR_STRING && src2->mode == VAR_STRING ) + { + str_cmp:; + int res = 0; + switch (op_id) + { + case OPI_LT: // SxS + res = CmpMem( MemByS(dest->str,dest->str_len), + MemByS(src2->str,src2->str_len)) < 0; + break; + + case OPI_LE: // SxS + res = CmpMem( MemByS(dest->str,dest->str_len), + MemByS(src2->str,src2->str_len)) <= 0; + break; + + case OPI_GT: // SxS + res = CmpMem( MemByS(dest->str,dest->str_len), + MemByS(src2->str,src2->str_len)) > 0; + break; + + case OPI_GE: // SxS + res = CmpMem( MemByS(dest->str,dest->str_len), + MemByS(src2->str,src2->str_len)) >= 0; + break; + + case OPI_EQ: // SxS + case OPI_EQEQ: // SxS + res = dest->str_len == src2->str_len + && !memcmp(dest->str,src2->str,dest->str_len); + break; + + case OPI_NEQ: // SxS + case OPI_NEQNEQ: // SxS + res = dest->str_len != src2->str_len + || memcmp(dest->str,src2->str,dest->str_len); + break; + + case OPI_ADD: // SxS + goto op_join; + + case OPI_LAND: // SxS + res = dest->str_len > 0 && src2->str_len > 0; + break; + + case OPI_LOR: // SxS + res = dest->str_len > 0 || src2->str_len > 0; + break; + + case OPI_LEOR: // SxS + res = dest->str_len > 0 != src2->str_len > 0; + break; + + case OPI_POW: // SxS + case OPI_MULT: // SxS + case OPI_DIV: // SxS + case OPI_MOD: // SxS + case OPI_SUB: // SxS + case OPI_LSHIFT: // SxS + case OPI_RSHIFT: // SxS + case OPI_AND: // SxS + case OPI_OR: // SxS + case OPI_EOR: // SxS + ResetV(dest); + return; + + case OPI_JOIN: // SxS + case OPI_NONE: // SxS + case OPI__N: // SxS + DASSERT(0); // never executed + ResetV(dest); + return; + } + + FreeV(dest); + dest->mode = VAR_INT; + dest->i = res; + return; + } + else // one param is not string + { + DASSERT( ( dest->mode == VAR_STRING ) != ( src2->mode == VAR_STRING ) ); + switch (op_id) + { + case OPI_LAND: // S + case OPI_LOR: // S + case OPI_LEOR: // S + if ( dest->mode == VAR_STRING ) + ToBoolV(dest); + else if ( src2->mode == VAR_STRING ) + { + temp.mode = VAR_INT; + temp.i = src2->str_len > 0; + src2 = &temp; + } + // continue like numeric values + break; + + case OPI_EQEQ: // S + case OPI_NEQNEQ: // S + FreeV(dest); + dest->mode = VAR_INT; + dest->i = op_id == OPI_NEQNEQ; + return; + + case OPI_EQ: // S + case OPI_NEQ: // S + case OPI_LT: // S + case OPI_LE: // S + case OPI_GT: // S + case OPI_GE: // S + if ( dest->mode == VAR_STRING ) + { + AssignStringVV(&temp,src2); + src2 = &temp; + } + else + ToStringV(dest); + goto str_cmp; + + default: + ResetV(dest); + return; + } + } + } + + DASSERT( dest->mode != VAR_STRING ); + DASSERT( src2->mode != VAR_STRING ); + + + if ( dest->mode == VAR_VECTOR ) + { + if ( src2->mode == VAR_VECTOR ) + { + // both vectors + switch (op_id) + { + case OPI_POW: // VxV + dest->x = pow(dest->x,src2->x); + dest->y = pow(dest->y,src2->y); + dest->z = pow(dest->z,src2->z); + return; + + case OPI_MULT: // VxV + dest->x *= src2->x; + dest->y *= src2->y; + dest->z *= src2->z; + return; + + case OPI_DIV: // VxV + dest->x /= src2->x; + dest->y /= src2->y; + dest->z /= src2->z; + return; + + case OPI_MOD: // VxV + dest->x = fmod(dest->x,src2->x); + dest->y = fmod(dest->y,src2->y); + dest->z = fmod(dest->z,src2->z); + return; + + case OPI_ADD: // VxV + dest->x += src2->x; + dest->y += src2->y; + dest->z += src2->z; + return; + + case OPI_SUB: // VxV + dest->x -= src2->x; + dest->y -= src2->y; + dest->z -= src2->z; + return; + + case OPI_LSHIFT: // VxV + dest->x = ldexp(dest->x,(int)src2->x); + dest->y = ldexp(dest->y,(int)src2->y); + dest->z = ldexp(dest->z,(int)src2->z); + return; + + case OPI_RSHIFT: // VxV + dest->x = ldexp(dest->x,(int)-src2->x); + dest->y = ldexp(dest->y,(int)-src2->y); + dest->z = ldexp(dest->z,(int)-src2->z); + return; + + case OPI_LT: // VxV + if ( dest->x != src2->x ) + dest->i = dest->x < src2->x; + else if ( dest->y != src2->y ) + dest->i = dest->y < src2->y; + else if ( dest->z != src2->z ) + dest->i = dest->z < src2->z; + else + dest->i = 0; + dest->mode = VAR_INT; + return; + + case OPI_LE: // VxV + if ( dest->x != src2->x ) + dest->i = dest->x <= src2->x; + else if ( dest->y != src2->y ) + dest->i = dest->y <= src2->y; + else if ( dest->z != src2->z ) + dest->i = dest->z <= src2->z; + else + dest->i = 1; + dest->mode = VAR_INT; + return; + + case OPI_GT: // VxV + if ( dest->x != src2->x ) + dest->i = dest->x > src2->x; + else if ( dest->y != src2->y ) + dest->i = dest->y > src2->y; + else if ( dest->z != src2->z ) + dest->i = dest->z > src2->z; + else + dest->i = 0; + dest->mode = VAR_INT; + return; + + case OPI_GE: // VxV + if ( dest->x != src2->x ) + dest->i = dest->x >= src2->x; + else if ( dest->y != src2->y ) + dest->i = dest->y >= src2->y; + else if ( dest->z != src2->z ) + dest->i = dest->z >= src2->z; + else + dest->i = 1; + dest->mode = VAR_INT; + return; + + case OPI_EQ: // VxV + case OPI_EQEQ: // VxV + dest->i = dest->x == src2->x && dest->y == src2->y && dest->z == src2->z; + dest->mode = VAR_INT; + return; + + case OPI_NEQ: // VxV + case OPI_NEQNEQ: // VxV + dest->i = dest->x != src2->x || dest->y != src2->y || dest->z == src2->z; + dest->mode = VAR_INT; + return; + + case OPI_AND: // VxV + case OPI_OR: // VxV + case OPI_EOR: // VxV + dest->mode = VAR_UNSET; + return; + + case OPI_LAND: // VxV + dest->i = ( dest->x || dest->y || dest->z ) + && ( src2->x || src2->y || src2->z ); + dest->mode = VAR_INT; + return; + + case OPI_LOR: // VxV + dest->i = ( dest->x || dest->y || dest->z ) + || ( src2->x || src2->y || src2->z ); + dest->mode = VAR_INT; + return; + + case OPI_LEOR: // VxV + dest->i = ( dest->x || dest->y || dest->z ) + != ( src2->x || src2->y || src2->z ); + dest->mode = VAR_INT; + return; + + case OPI_JOIN: // VxV + case OPI_NONE: // VxV + case OPI__N: // VxV + DASSERT(0); + return; + } + } + else + { + // only dest is vector + double d = GetDoubleV(src2); + switch (op_id) + { + case OPI_POW: // VxD + dest->x = pow(dest->x,d); + dest->y = pow(dest->y,d); + dest->z = pow(dest->z,d); + return; + + case OPI_MULT: // VxD + dest->x *= d; + dest->y *= d; + dest->z *= d; + return; + + case OPI_DIV: // VxD + dest->x /= d; + dest->y /= d; + dest->z /= d; + return; + + case OPI_MOD: // VxD + dest->x = fmod(dest->x,d); + dest->y = fmod(dest->y,d); + dest->z = fmod(dest->z,d); + return; + + case OPI_ADD: // VxD + dest->x += d; + dest->y += d; + dest->z += d; + return; + + case OPI_SUB: // VxD + dest->x -= d; + dest->y -= d; + dest->z -= d; + return; + + case OPI_LSHIFT: // VxD + { + int shift = (int)floor(d+0.5); + dest->x = ldexp(dest->x,shift); + dest->y = ldexp(dest->y,shift); + dest->z = ldexp(dest->z,shift); + } + return; + + case OPI_RSHIFT: // VxD + { + int shift = -(int)floor(d+0.5); + dest->x = ldexp(dest->x,shift); + dest->y = ldexp(dest->y,shift); + dest->z = ldexp(dest->z,shift); + } + return; + + case OPI_LT: // VxD + dest->i = dest->x < d; + dest->mode = VAR_INT; + return; + + case OPI_LE: // VxD + dest->i = dest->x <= d; + dest->mode = VAR_INT; + return; + + case OPI_GT: // VxD + dest->i = dest->x > d; + dest->mode = VAR_INT; + return; + + case OPI_GE: // VxD + dest->i = dest->x >= d; + dest->mode = VAR_INT; + return; + + case OPI_EQ: // VxD + dest->i = dest->x == d; + dest->mode = VAR_INT; + return; + + case OPI_EQEQ: // VxD + dest->i = 0; + dest->mode = VAR_INT; + return; + + case OPI_NEQ: // VxD + dest->i = dest->x != d; + dest->mode = VAR_INT; + return; + + case OPI_NEQNEQ: // VxD + dest->i = 1; + dest->mode = VAR_INT; + return; + + case OPI_AND: // VxD + case OPI_OR: // VxD + case OPI_EOR: // VxD + dest->mode = VAR_UNSET; + return; + + case OPI_LAND: // VxD + dest->i = ( dest->x || dest->y || dest->z ) && d; + dest->mode = VAR_INT; + return; + + case OPI_LOR: // VxD + dest->i = ( dest->x || dest->y || dest->z ) || d; + dest->mode = VAR_INT; + return; + + case OPI_LEOR: // VxD + dest->i = ( dest->x || dest->y || dest->z ) == !d; + dest->mode = VAR_INT; + return; + + case OPI_JOIN: // VxD + case OPI_NONE: // VxD + case OPI__N: // VxD + DASSERT(0); + return; + } + } + } + else if ( src2->mode == VAR_VECTOR ) + { + // only src2 is vector + double d = GetDoubleV(dest); + MARK_USED(d); + switch (op_id) + { + case OPI_POW: // DxV + dest->x = pow(d,src2->x); + dest->y = pow(d,src2->y); + dest->z = pow(d,src2->z); + dest->mode = VAR_VECTOR; + return; + + case OPI_MULT: // DxV + dest->x = d * src2->x; + dest->y = d * src2->y; + dest->z = d * src2->z; + dest->mode = VAR_VECTOR; + return; + + case OPI_DIV: // DxV + dest->x = d / src2->x; + dest->y = d / src2->y; + dest->z = d / src2->z; + dest->mode = VAR_VECTOR; + return; + + case OPI_MOD: // DxV + dest->x = fmod(d,src2->x); + dest->y = fmod(d,src2->y); + dest->z = fmod(d,src2->z); + dest->mode = VAR_VECTOR; + return; + + case OPI_ADD: // DxV + dest->x = d + src2->x; + dest->y = d + src2->y; + dest->z = d + src2->z; + dest->mode = VAR_VECTOR; + return; + + case OPI_SUB: // DxV + dest->x = d - src2->x; + dest->y = d - src2->y; + dest->z = d - src2->z; + dest->mode = VAR_VECTOR; + return; + + case OPI_LSHIFT: // DxV + dest->x = ldexp(d,(int)src2->x); + dest->y = ldexp(d,(int)src2->y); + dest->z = ldexp(d,(int)src2->z); + return; + + case OPI_RSHIFT: // DxV + dest->x = ldexp(d,(int)-src2->x); + dest->y = ldexp(d,(int)-src2->y); + dest->z = ldexp(d,(int)-src2->z); + return; + + case OPI_LT: // DxV + dest->i = dest->x < d; + dest->mode = VAR_INT; + return; + + case OPI_LE: // DxV + dest->i = dest->x <= d; + dest->mode = VAR_INT; + return; + + case OPI_GT: // DxV + dest->i = dest->x > d; + dest->mode = VAR_INT; + return; + + case OPI_GE: // DxV + dest->i = dest->x >= d; + dest->mode = VAR_INT; + return; + + case OPI_EQ: // DxV + dest->i = dest->x == d; + dest->mode = VAR_INT; + return; + + case OPI_EQEQ: // DxV + dest->i = 0; + dest->mode = VAR_INT; + return; + + case OPI_NEQ: // DxV + dest->i = dest->x != d; + dest->mode = VAR_INT; + return; + + case OPI_NEQNEQ: // DxV + dest->i = 1; + dest->mode = VAR_INT; + return; + + case OPI_AND: // DxV + case OPI_OR: // DxV + case OPI_EOR: // DxV + dest->mode = VAR_UNSET; + return; + + case OPI_LAND: // DxV + dest->i = ( dest->x || dest->y || dest->z ) && d; + dest->mode = VAR_INT; + return; + + case OPI_LOR: // DxV + dest->i = ( dest->x || dest->y || dest->z ) || d; + dest->mode = VAR_INT; + return; + + case OPI_LEOR: // DxV + dest->i = ( dest->x || dest->y || dest->z ) == !d; + dest->mode = VAR_INT; + return; + + case OPI_JOIN: // DxV + case OPI_NONE: // DxV + case OPI__N: // DxV + DASSERT(0); + return; + } + } + else if ( dest->mode == VAR_DOUBLE || src2->mode == VAR_DOUBLE ) + { + switch (op_id) + { + case OPI_POW: // DxD | DxI | IxD + dest->d = pow( dest->mode == VAR_DOUBLE ? dest->d : dest->i, + src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + break; + + case OPI_MULT: // DxD | DxI | IxD + dest->d = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + * ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + break; + + case OPI_DIV: // DxD | DxI | IxD + dest->d = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + / ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + break; + + case OPI_MOD: // DxD | DxI | IxD + dest->d = fmod( dest->mode == VAR_DOUBLE ? dest->d : dest->i, + src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + break; + + case OPI_ADD: // DxD | DxI | IxD + dest->d = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + + ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + break; + + case OPI_SUB: // DxD | DxI | IxD + dest->d = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + - ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + break; + + case OPI_LSHIFT: // DxD | DxI | IxD + ShiftLeftV(dest, GetIntV(src2)); + break; + + case OPI_RSHIFT: // DxD | DxI | IxD + ShiftLeftV(dest,-GetIntV(src2)); + break; + + case OPI_LT: // DxD | DxI | IxD + dest->i = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + < ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + dest->mode = VAR_INT; + return; + + case OPI_LE: // DxD | DxI | IxD + dest->i = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + <= ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + dest->mode = VAR_INT; + return; + + case OPI_GT: // DxD | DxI | IxD + dest->i = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + > ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + dest->mode = VAR_INT; + return; + + case OPI_GE: // DxD | DxI | IxD + dest->i = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + >= ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + dest->mode = VAR_INT; + return; + + case OPI_EQ: // DxD | DxI | IxD + dest->i = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + == ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + dest->mode = VAR_INT; + return; + + case OPI_NEQ: // DxD | DxI | IxD + dest->i = ( dest->mode == VAR_DOUBLE ? dest->d : dest->i ) + != ( src2->mode == VAR_DOUBLE ? src2->d : src2->i ); + dest->mode = VAR_INT; + return; + + case OPI_EQEQ: // DxD | DxI | IxD + dest->i = dest->mode == src2->mode && dest->d == src2->d; + dest->mode = VAR_INT; + return; + + case OPI_NEQNEQ: // DxD | DxI | IxD + dest->i = dest->mode != src2->mode || dest->d != src2->d; + dest->mode = VAR_INT; + return; + + case OPI_AND: // DxD | DxI | IxD + dest->i = GetIntV(dest) & GetIntV(src2); + dest->mode = VAR_INT; + return; + + case OPI_OR: // DxD | DxI | IxD + dest->i = GetIntV(dest) | GetIntV(src2); + dest->mode = VAR_INT; + return; + + case OPI_EOR: // DxD | DxI | IxD + dest->i = GetIntV(dest) ^ GetIntV(src2); + dest->mode = VAR_INT; + return; + + case OPI_LAND: // DxD | DxI | IxD + dest->i = GetBoolV(dest) && GetBoolV(src2); + dest->mode = VAR_INT; + return; + + case OPI_LOR: // DxD | DxI | IxD + dest->i = GetBoolV(dest) || GetBoolV(src2); + dest->mode = VAR_INT; + return; + + case OPI_LEOR: // DxD | DxI | IxD + dest->i = GetBoolV(dest) != GetBoolV(src2); + dest->mode = VAR_INT; + return; + + case OPI_JOIN: // DxD | DxI | IxD + case OPI_NONE: // DxD | DxI | IxD + case OPI__N: // DxD | DxI | IxD + DASSERT(0); + break; + } + dest->mode = VAR_DOUBLE; + } + else + { + DASSERT( dest->mode == VAR_INT ); + DASSERT( src2->mode == VAR_INT ); + + switch (op_id) + { + case OPI_POW: // IxI + if ( src2->i >= 0 ) + { + // try integer calculation first, double is fall back on overflow + int n = src2->i, res = 1, err = 0; + while ( n-- > 0 ) + { + int new_res = res * dest->i; + if ( abs(new_res) <= abs(res) ) + { + err++; + break; + } + res = new_res; + } + if (!err) + { + dest->i = res; + break; + } + } + dest->d = pow(dest->i,src2->i); + dest->mode = VAR_DOUBLE; + break; + + case OPI_DIV: // IxI + if (src2->i) + { + if ( si->float_div > 0 ) + { + dest->d = (double)dest->i / (double)src2->i; + dest->mode = VAR_DOUBLE; + } + else + dest->i /= src2->i; + } + else if ( si && si->no_warn <= 0 ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + sf->line_err++; + si->total_err++; + ERROR0(ERR_WARNING,"Division by 0 [%s @%u]\n",sf->name,sf->line); + } + break; + + case OPI_MOD: // IxI + if (src2->i) + dest->i %= src2->i; + else if ( si && si->no_warn <= 0 ) + { + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + sf->line_err++; + si->total_err++; + ERROR0(ERR_WARNING,"Modulo by 0 [%s @%u]\n",sf->name,sf->line); + } + break; + + // IxI + case OPI_MULT: dest->i *= src2->i; break; + case OPI_ADD: dest->i += src2->i; break; + case OPI_SUB: dest->i -= src2->i; break; + + case OPI_LSHIFT: ShiftLeftV(dest, src2->i); break; + case OPI_RSHIFT: ShiftLeftV(dest,-src2->i); break; + + case OPI_LT: dest->i = dest->i < src2->i; break; + case OPI_LE: dest->i = dest->i <= src2->i; break; + case OPI_GT: dest->i = dest->i > src2->i; break; + case OPI_GE: dest->i = dest->i >= src2->i; break; + case OPI_EQ: + case OPI_EQEQ: dest->i = dest->i == src2->i; break; + case OPI_NEQ: + case OPI_NEQNEQ: dest->i = dest->i != src2->i; break; + + case OPI_AND: dest->i &= src2->i; break; + case OPI_OR: dest->i |= src2->i; break; + case OPI_EOR: dest->i ^= src2->i; break; + break; + + case OPI_LAND: dest->i = dest->i && src2->i; break; + case OPI_LOR: dest->i = dest->i || src2->i; break; + case OPI_LEOR: dest->i = !dest->i != !src2->i; break; + + case OPI_JOIN: // IxI + case OPI_NONE: // IxI + case OPI__N: // IxI + DASSERT(0); + break; + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// expressions: functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanExprSI +( + ScanInfo_t * si, // valid data + Var_t * var // result: store value here +) +{ + DASSERT(si); + DASSERT(var); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + sf->expr_depth++; + noPRINT("ENTER ScanExprSI(), depth=%d, nowarn=%d, nocalc=%d\n", + sf->expr_depth++, si->no_warn, si->no_calc ); + + struct PARMS + { + const Operator_t * op; + Var_t val; + int no_calc; + + } parm[OPP__N]; + const Operator_t *cur_op = parm->op = op_tab; + struct PARMS *p = parm; + p->val.mode = VAR_UNSET; + + //enum { M_VALUE, M_ALT, M_ALT1, M_ALT2 } mode = M_VALUE; + + enumError maxerr = ERR_OK; + do + { + p++; + DASSERT( p < parm + OPP__N ); + p->op = cur_op; + p->no_calc = si->no_calc; + p->val.mode = VAR_UNSET; + p->val.int_mode = IMD_UNSET; + enumError err = ScanValueSI(si,&p->val); + if ( maxerr < err ) + maxerr = err; + cur_op = FindOperator(si); + + while ( p > parm+1 && cur_op->prio <= p->op->prio ) + { + // calc operations with higher and same priority + + PRINT("%s: %g %s %g [%d,%d,%d] nocalc=%d,%d mode=%dx%d imode=%dx%d\n", + p->no_calc <= 0 ? "CALC" : "SKIP", + GetDoubleV(&p[-1].val), + p->op->op_name, + GetDoubleV(&p->val), + p->op->id, p->op->prio, p->op->mode, + p[-1].no_calc, p->no_calc, + p[-1].val.mode, p->val.mode, + p[-1].val.int_mode, p->val.int_mode ); + if ( p->no_calc <= 0 ) + CalcExpr(&p[-1].val,&p->val,p->op->id,si); + FreeV(&p->val); + p--; + si->no_calc = p->no_calc; + } + + #if HAVE_PRINT + if ( cur_op->id == OPI_LAND || cur_op->id == OPI_LOR ) + PRINT(">>OP=%u '%g %s ...'\n", + cur_op->id, GetDoubleV(&p->val), cur_op->op_name ); + #endif + + if ( cur_op->id == OPI_LAND && !GetBoolV(&p->val) ) + { + PRINT("NO-CALC++ [%d->%d] (AND)\n",si->no_calc,si->no_calc+1); + FreeV(&p->val); + p->val.mode = VAR_INT; + p->val.i = 0; + si->no_calc++; + } + else if ( cur_op->id == OPI_LOR && GetBoolV(&p->val) ) + { + PRINT("NO-CALC++ [%d->%d] (OR)\n",si->no_calc,si->no_calc+1); + FreeV(&p->val); + p->val.mode = VAR_INT; + p->val.i = 1; + si->no_calc++; + } + + } while ( cur_op->prio > OPP_NONE ); + + if ( NextCharSI(si,false) == '?' ) + { + sf->ptr++; + noPRINT("NO-WARN = %d, \n",si->no_warn); + if (GetBoolV(&p->val)) + { + enumError err = ScanExprSI(si,var); + if ( maxerr < err ) + maxerr = err; + + err = CheckWarnSI(si,':',ERR_OK); + if (!err) + { + noPRINT("NO-CALC++ [%d->%d] (1?:)\n",si->no_calc,si->no_calc+1); + si->no_calc++; + ScanExprSI(si,&p->val); + noPRINT("NO-CALC-- [%d->%d] (1?:)\n",si->no_calc,si->no_calc-1); + si->no_calc--; + } + else if ( maxerr < err ) + maxerr = err; + } + else + { + noPRINT("NO-CALC++ [%d->%d] (0?:)\n",si->no_calc,si->no_calc+1); + si->no_calc++; + ScanExprSI(si,var); + noPRINT("NO-CALC-- [%d->%d] (0?:)\n",si->no_calc,si->no_calc-1); + si->no_calc--; + + enumError err = CheckWarnSI(si,':',ERR_OK); + if (!err) + err = ScanExprSI(si,var); + if ( maxerr < err ) + maxerr = err; + } + } + else + AssignVar(var,&p->val); + + noPRINT("LEAVE ScanExprSI(), depth=%d, nowarn=%d, nocalc=%d\n", + sf->expr_depth++, si->no_warn, si->no_calc ); + sf->expr_depth--; + return maxerr; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanVectorExprSI +( + // scans: v | a,b | x,y,z + + ScanInfo_t * si, // valid data + Var_t * var, // result: store value here + double ab_default, // used for 'a,b' format + uint xyz // for ab default: 0:yz, 1:xz, 2:xy +) +{ + DASSERT(si); + DASSERT(var); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + sf->disable_comma++; + enumError err = ScanExprSI(si,var); + ToVectorV(var); + + if ( NextCharSI(si,false) == ',' ) + sf->ptr++; + if (*sf->ptr) + { + DEFINE_VAR(temp); + err = ScanExprSI(si,&temp); + if (!err) + { + if ( NextCharSI(si,false) == ',' ) + sf->ptr++; + if (*sf->ptr) + { + var->y = GetYDoubleV(&temp); + err = ScanExprSI(si,&temp); + var->z = GetZDoubleV(&temp); + } + else if ( xyz == 0 ) + { + var->y = var->x; + var->x = ab_default; + var->z = GetZDoubleV(&temp); + } + else if ( xyz == 2 ) + { + var->y = GetYDoubleV(&temp); + var->z = ab_default; + } + else // xyz == 1 + { + var->y = ab_default; + var->z = GetZDoubleV(&temp); + } + } + } + + sf->disable_comma--; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanVectorExpr +( + // scans: v | x,z | x,y,z + + ccp arg, // string to scan + ccp name, // NULL or name for error messages + Var_t * var // result: store value here +) +{ + DASSERT(var); + + if (!arg) + arg = ""; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),name,0); + if (!name) + si.no_warn++; + const enumError err = ScanVectorExprSI(&si,var,0.0,1); + CheckEolSI(&si); + ResetSI(&si); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanMinMaxExprSI +( + // scans: val (min=-val,max=+val) | val1,val2 + + ScanInfo_t *si, // valid data + Var_t *min, // result: store min vector here + Var_t *max // result: store max vector here +) +{ + DASSERT(si); + DASSERT(min); + DASSERT(max); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + sf->disable_comma++; + enumError err = ScanExprSI(si,min); + ToVectorV(min); + + if ( err || NextCharSI(si,false) != ',' ) + { + max->x = fabs(min->x); min->x = -max->x; + max->y = fabs(min->y); min->y = -max->y; + max->z = fabs(min->z); min->z = -max->z; + max->mode = VAR_VECTOR; + } + else + { + sf->ptr++; + err = ScanExprSI(si,max); + ToVectorV(max); + + if ( min->x > max->x ) { double d = min->x; min->x = max->x; max->x = d; } + if ( min->y > max->y ) { double d = min->y; min->y = max->y; max->y = d; } + if ( min->z > max->z ) { double d = min->z; min->z = max->z; max->z = d; } + } + + sf->disable_comma--; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanValueSI +( + ScanInfo_t * si, // valid data + Var_t * var // result: store value here +) +{ + DASSERT(si); + DASSERT(var); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + FreeV(var); + var->mode = VAR_UNSET; + var->int_mode = IMD_UNSET; + var->i = 0; + + enumError err = ERR_OK; + int sign = 0, neg = 0, invert = 0; + char ch = NextCharSI(si,false); + + + //--- arithmetic negation -> delayed + + for(;;) + { + if ( ch == '+' ) + ; + else if ( ch == '-' ) + sign++; + else + break; + sf->ptr++; + ch = NextCharSI(si,false); + } + + + //--- logical negation + + for(;;) + { + if ( ch == '+' ) + ; + else if ( ch == '!' ) + neg++; + else + break; + sf->ptr++; + ch = NextCharSI(si,false); + } + + if (neg) + { + err = ScanValueSI(si,var); + switch (var->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + var->i = var->i != 0; + break; + + case VAR_DOUBLE: + var->i = var->d != 0; + break; + + case VAR_VECTOR: + var->i = var->x || var->y || var->z; + break; + + case VAR_STRING: + { + const int i = var->str_len > 0; + FREE(var->str); + var->i = i; + } + break; + } + if (neg&1) + var->i = !var->i; + var->mode = VAR_INT; + goto value_ok; + } + + + //--- invertion + + for(;;) + { + if ( ch == '+' ) + ; + else if ( ch == '~' || ch == '^' ) + invert++; + else + break; + sf->ptr++; + ch = NextCharSI(si,false); + } + + if (invert) + { + err = ScanValueSI(si,var); + switch (var->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + if (invert&1) + var->i = ~var->i; + break; + + case VAR_DOUBLE: + var->i = ~(int)floor(var->d+0.5); + var->mode = VAR_INT; + break; + + case VAR_VECTOR: + var->mode = VAR_UNSET; + break; + + case VAR_STRING: + ResetV(var); + break; + } + goto value_ok; + } + + + //--- analye operand + + switch(ch) + { + case '*': + sf->ptr++; + var->mode = VAR_INT; + var->i = INT_MAX; + goto value_ok; + + case '!': + case '~': + case '^': + err = ScanValueSI(si,var); + goto value_ok; + + case '"': + case '\'': + err = ScanStringSI(si,var); + goto value_ok; + + case '(': + sf->ptr++; + err = CheckWarnSI(si,')',ScanExprSI(si,var)); + goto value_ok; + + case '<': + sf->ptr++; + err = CheckWarnSI(si,'>',ScanBitfieldSI(si,var)); + goto value_ok; + } + + if ( si->force_hex > 0 && IsNum(ch,1) ) + { + char *end; + var->i = strtoul( sf->ptr, &end, 16 ); + var->mode = VAR_INT; + if ( end == sf->ptr ) + { + ccp eol = FindNextLineFeedSI(si,true); + err = ERROR0(ERR_WARNING, + "Can't scan a hex number [%s @%u]: %.*s\n", + sf->name, sf->line, (int)(eol - sf->ptr), sf->ptr ); + } + sf->ptr = end; + } + else if (IsNum(ch,0)) + { + ccp ptr = sf->ptr; + if ( ptr[0] != '0' || ptr[1] != 'x' ) + { + // int or float + + if ( sf->disable_comma <= 0 ) + { + // is this a floating point with a comma? + + ccp src = ptr; + char buf[60], *dest = buf; + char *dest_end = buf + sizeof(buf)/2; + while ( *src >= '0' && *src <= '9' && dest < dest_end ) + *dest++ = *src++; + if ( *src == ',' && src[1] >= '0' && src[1] <= '9' ) + { + src++; + *dest++ = '.'; + dest_end = buf + sizeof(buf); + while ( *src >= '0' && *src <= '9' && dest < dest_end ) + *dest++ = *src++; + if ( dest < dest_end ) + { + *dest = 0; + var->mode = VAR_DOUBLE; + var->d = strtod(buf,0); + sf->ptr = src; + goto value_ok; + } + } + } + + char * end; + double d = strtod(ptr,&end); + + for ( ; ptr < end; ptr++ ) + if ( *ptr < '0' || *ptr > '9' ) + { + // number is float + var->mode = VAR_DOUBLE; + var->d = d; + sf->ptr = end; + goto value_ok; + } + } + + // number is integer + + const uint base = sf->ptr[1] < '0' || sf->ptr[1] > '9' ? 0 : 10; + ccp start = sf->ptr; + var->i = strtoul( sf->ptr, (char**)&sf->ptr, base ); + var->mode = VAR_INT; + if ( sf->ptr == start && *start == '.' && si->point_is_null > 0 ) + sf->ptr++; + } + else + { + char varname[VARNAME_SIZE+1]; + int xyz; + const uint name_len + = ScanNameSI(si,varname,sizeof(varname),false,true,&xyz); + if (!name_len) + { + if ( si->no_warn <= 0 ) + { + ccp eol = FindNextLineFeedSI(si,true); + if ( sf->line_err < 2 ) + { + si->total_err++; + ERROR0(ERR_WARNING, + "Neither name nor number [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)( eol - sf->prev_ptr ), sf->prev_ptr ); + } + } + return ERR_WARNING; + } + + // outside an expression no spaces are allowed between name and '(' + // because of ambiguous meaning with space separated parameter lists + // like: name1 (expression2) + if ( *sf->ptr == '(' || sf->expr_depth && NextCharSI(si,false) == '(' ) + { + //--- we have a function call + + const uint saved_line = sf->line; + const ccp saved_ptr = sf->prev_ptr; + + FuncParam_t temppar; + const FuncParam_t *fpar = si->no_calc > 0 + ? &FuncDollar + : GetParserFunc(varname,si,&temppar); + DASSERT(fpar); + noPRINT("FUNC %s(%u..%u)\n",varname,fpar->min_param,fpar->max_param); + + sf->disable_comma++; + sf->ptr++; + int n_param = 0; + if ( NextCharSI(si,false) != ')' ) + { + while ( sf->param_used < STACK_SIZE ) + { + Var_t * dest = sf->param + sf->param_used++; + if ( !n_param++ && fpar->need_name ) + { + char vname[VARNAME_SIZE+1]; + ScanNameSI(si,vname,sizeof(vname),false,true,0); + dest->name = vname; + } + else + { + enumError err1 = ScanExprSI(si,dest); + if ( err < err1 ) + err = err1; + } + if ( NextCharSI(si,false) != ',' ) + break; + sf->ptr++; + } + } + + if ( si->no_warn <= 0 ) + { + if ( sf->param_used >= STACK_SIZE ) + { + ccp eol = FindNextLineFeedSI(si,true); + ERROR0(ERR_WARNING, + "Stack overflow [%s @%u]: %.*s\n", + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + else if ( n_param < fpar->min_param || n_param > fpar->max_param ) + { + ccp eol = FindLineFeedSI(si,saved_ptr,true); + ccp attrib = n_param < fpar->min_param ? "few" : "many"; + if ( fpar->min_param == fpar->max_param ) + ERROR0(ERR_WARNING, + "To %s parameters for %s(), have %u, need %u [%s @%u]: %.*s\n", + attrib, varname, n_param, fpar->min_param, + sf->name, saved_line, + (int)(eol - saved_ptr), saved_ptr ); + else + ERROR0(ERR_WARNING, + "To %s parameters for %s(), have %u, need %u..%u [%s @%u]: %.*s\n", + attrib, varname, n_param, fpar->min_param, fpar->max_param, + sf->name, sf->line, + (int)(eol - sf->prev_ptr), sf->prev_ptr ); + } + } + + err = CheckWarnSI(si,')',err); + sf->disable_comma--; + + if ( n_param < fpar->min_param ) + fpar = &FuncDollar; + + ResetV(var); + sf->param_used -= n_param; + enumError err1 = fpar->func(var,sf->param+sf->param_used,n_param,si,fpar); + if ( err < err1 ) + err = err1; + goto value_ok; + } + + if (si->no_calc) + { + // don't search the variable + FreeV(var); + goto value_ok; + } + + if ( xyz >= 0 ) + { + DASSERT( xyz >= 0 && xyz <= 2 ); + const Var_t *svar = FindVarSI(si,varname,false); + if (svar) + { + AssignVar(var,svar); + goto value_ok; + } + + varname[name_len-2] = 0; + svar = FindVarSI(si,varname,false); + if ( svar && svar->mode == VAR_VECTOR ) + { + var->d = svar->v[xyz]; + var->mode = VAR_DOUBLE; + goto value_ok; + } + varname[name_len-2] = '.'; + } + + const Var_t *svar = FindVarSI(si,varname,true); + AssignVar(var,svar); + } + + value_ok: + + if (sign&1) + { + switch (var->mode) + { + case VAR_UNSET: + break; + + case VAR_INT: + var->i = -var->i; + break; + + case VAR_DOUBLE: + var->d = -var->d; + break; + + case VAR_VECTOR: + var->x = -var->x; + var->y = -var->y; + var->z = -var->z; + break; + + case VAR_STRING: + ResetV(var); + break; + } + } + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanUValueSI +( + ScanInfo_t * si, // valid data + long * num, // return value + int force_hex // >0: force hex for non prefixed integers +) +{ + DASSERT(si); + DASSERT(num); + + const int old_force_hex = si->force_hex; + si->force_hex = force_hex; + + DEFINE_VAR(var); + enumError err = ScanValueSI(si,&var); + si->force_hex = old_force_hex; + SkipCharSI(si,','); + + switch(var.mode) + { + case VAR_UNSET: + *num = 0; + return err; + + case VAR_INT: + *num = var.i; + return err; + + case VAR_DOUBLE: + case VAR_VECTOR: + *num = floor( var.d + 0.5 ); + return err; + + case VAR_STRING: + *num = str2l(var.str,0,10); + break; + } + + // only needed to avoid compiler warnings + *num = 0; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanDValueSI +( + ScanInfo_t * si, // valid data + double * num // return value +) +{ + DASSERT(si); + DASSERT(num); + + DEFINE_VAR(var); + enumError err = ScanValueSI(si,&var); + SkipCharSI(si,','); + + switch(var.mode) + { + case VAR_UNSET: + *num = 0.0; + return err; + + case VAR_INT: + *num = var.i; + return err; + + case VAR_DOUBLE: + case VAR_VECTOR: + *num = var.d; + return err; + + case VAR_STRING: + *num = var.str_len ? strtod(var.str,0) : 0.0; + break; + } + + // only needed to avoid compiler warnings + *num = 0.0; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanBitfieldSI +( + ScanInfo_t * si, // valid data + Var_t * var // result: store value here +) +{ + DASSERT(si); + DASSERT(var); + ScanFile_t *sf = si->cur_file; + DASSERT(sf); + + uint val = 0; + sf->disable_comma++; + + DEFINE_VAR(temp); + enumError err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,false); + if ( !ch || ch == '>' ) + break; + + err = ScanValueSI(si,&temp); + if (err) + break; + + int n1 = GetIntV(&temp), n2 = n1; + if ( NextCharSI(si,false) == ':' ) + { + sf->ptr++; + err = ScanValueSI(si,&temp); + if (err) + break; + + n2 = GetIntV(&temp); + } + + if ( n1 < 0 ) + n1 = 0; + if ( n2 > CHAR_BIT * sizeof(uint) - 1 ) + n2 = CHAR_BIT * sizeof(uint) - 1; + PRINT("BITFIELD: %u..%u => %x\n",n1,n2,val); + while ( n1 <= n2 ) + val |= 1u << n1++; + + if ( NextCharSI(si,false) != ',' ) + break; + sf->ptr++; + } + + FreeV(&temp); + sf->disable_comma--; + var->mode = VAR_INT; + var->i = val; + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct Line_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeLine ( Line_t * lin ) +{ + DASSERT(lin); + memset(lin,0,sizeof(*lin)); + lin->n = 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanLine +( + Line_t * lin, // valid line data + ScanInfo_t * si, // scanning source + uint n_dim, // number of elements in each vector (<=3) + int mode // user defined mode, stored in 'lin->mode' +) +{ + DASSERT(lin); + DASSERT(si); + DASSERT( n_dim > 0 ); + DASSERT( n_dim <= MAX_LINE_DIMEN ); + + InitializeLine(lin); + lin->line_mode = mode; + if (!NextCharSI(si,false)) + return ERR_OK; + + long n; + enumError err = ScanUValueSI(si,&n,false); + if (err) + return err; + lin->n = ( n < 0 ? 0 : n ) + 1; + + DEFINE_VAR(var); + while ( lin->n_pt < MAX_LINE_HELPER && NextCharSI(si,false) ) + { + err = ScanValueSI(si,&var); + if (err) + goto err; + + double *dest = lin->pt[lin->n_pt]; + *dest++ = GetXDoubleV(&var); + SkipCharSI(si,','); + + if ( var.mode == VAR_VECTOR ) + { + if ( n_dim > 1 ) + { + if ( n_dim > 2 ) + *dest++ = GetYDoubleV(&var); + *dest++ = GetZDoubleV(&var); + } + } + else if ( n_dim > 1 ) + { + if ( n_dim > 2 ) + { + err = ScanValueSI(si,&var); + if (err) + goto err; + *dest++ = GetYDoubleV(&var); + SkipCharSI(si,','); + } + + err = ScanValueSI(si,&var); + SkipCharSI(si,','); + if (err) + goto err; + *dest++ = GetZDoubleV(&var); + } + lin->n_pt++; + } + FreeV(&var); + return CheckEolSI(si); + + err: + FreeV(&var); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcLine +( + Line_t * lin, // valid line data + float * dest, // destination vector + const float * p1, // source vector P1 + const float * p2, // source vector P2 + uint n_dim, // number of elements in each vector, n>3 possible + uint step // value between 0 and lin->n inclusive +) +{ + DASSERT(lin); + DASSERT(dest); + + if ( lin->n <= 0 ) + lin->n = 0; + + if ( step == lin->n ) + { + // a little optimization for the most common case + // Important: This case do not access 'p1' + + DASSERT(p2); + memcpy(dest,p2,sizeof(*dest)*n_dim); + return; + } + + if (!step) + { + // a little optimization for the most common case + // Important: This case do not access 'p2' + + DASSERT(p1); + memcpy(dest,p1,sizeof(*dest)*n_dim); + return; + } + + DASSERT(p1); + DASSERT(p2); + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + uint dim = 0; + + if ( lin->n_pt == 2 ) + { + // Cubic bezier + // B(t) = (1-t)^3*P1 + 3*(1-t)^2*t*Pa + 3*(1-t)*t^2*Pb + t^3*P2 + + const double t1 = (double)step / (double)lin->n; + const double t2 = 1.0 - t1; + double *pa = lin->pt[0]; + double *pb = lin->pt[1]; + + uint max_dim = n_dim < MAX_LINE_DIMEN ? n_dim : MAX_LINE_DIMEN; + for ( ; dim < max_dim; dim++ ) + { + *dest++ = t2 * t2 * t2 * *p1++ + + 3* t1 * t2 * t2 * *pa++ + + 3* t1 * t1 * t2 * *pb++ + + t1 * t1 * t1 * *p2++; + } + } + else if ( lin->n_pt == 1 ) + { + // Quadratic bezier + // B(t) = (1-t)^2*P1 + 2*(1-t)*t*Pa + t^2*P2 + + const double t1 = (double)step / (double)lin->n; + const double t2 = 1.0 - t1; + double *pa = lin->pt[0]; + + uint max_dim = n_dim < MAX_LINE_DIMEN ? n_dim : MAX_LINE_DIMEN; + for ( ; dim < max_dim; dim++ ) + { + *dest++ = t2 * t2 * *p1++ + + 2* t1 * t2 * *pa++ + + t1 * t1 * *p2++; + } + } + + // all other points are calculated linear: B(t) = (1-t)*P1 + t*P2 + + while ( dim++ < n_dim ) + { + *dest++ = step * ( *p2 - *p1 ) / lin->n + *p1; + p1++; + p2++; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-pat.c b/project/src/lib-pat.c new file mode 100644 index 0000000..131d0bc --- /dev/null +++ b/project/src/lib-pat.c @@ -0,0 +1,2166 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-brres.h" +#include "lib-pat.h" + +#include "pat.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PAT action log /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool PAT_ACTION_LOG ( const char * format, ...) + __attribute__ ((__format__(__printf__,1,2))); + +static bool PAT_ACTION_LOG ( const char * format, ...) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[PAT]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + + if ( verbose > 2 || PAT_MODE & PATMD_LOG ) + { + fflush(stdout); + fprintf(stdlog," %s>[PAT]%s ",colset->heading,colset->info); + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum pat_entry_t +{ + PAT_SECT_SETUP, + PAT_SECT_SIMPLE, + PAT_SECT_HEADER, + PAT_SECT_S0_BASE, + PAT_SECT_S0_LIST, + PAT_SECT_STRINGS, + + PAT_N_SECT // number of PAT sections +}; + +const KeywordTab_t pat_section_name[] = +{ + { PAT_SECT_SETUP, "SETUP", 0, 0 }, + { PAT_SECT_SIMPLE, "SIMPLE", 0, 0 }, + { PAT_SECT_HEADER, "HEADER", 0, 0 }, + { PAT_SECT_S0_BASE, "S0_BASE", 0, 0 }, + { PAT_SECT_S0_LIST, "S0_LIST", 0, 0 }, + { PAT_SECT_STRINGS, "STRINGS", 0, 0 }, + { 0,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// pat_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +pat_mode_t PAT_MODE = PATMD_M_DEFAULT; +int have_pat_patch_count = 0; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_pat_tab[] = +{ + { 0, "CLEAR", "RESET", PATMD_M_ALL | PATMD_F_HIDE }, + { PATMD_M_DEFAULT, "DEFAULT", 0, PATMD_M_ALL | PATMD_F_HIDE }, + + { PATMD_LOG, "LOG", 0, 0 }, + { PATMD_SILENT, "SILENT", 0, 0 }, + + { 0, "AUTO", 0, PATMD_M_BOTH | PATMD_F_HIDE }, + { PATMD_M_BOTH, "BOTH", 0, PATMD_M_BOTH }, + { PATMD_SIMPLE, "SIMPLE", 0, PATMD_M_BOTH }, + { PATMD_COMPLEX, "COMPLEX", 0, PATMD_M_BOTH }, + + { PATMD_TEST0, "TEST0", "T0", PATMD_M_TEST | PATMD_F_HIDE }, + { PATMD_TEST1, "TEST1", "T1", PATMD_M_TEST }, + { PATMD_TEST2, "TEST2", "T2", PATMD_M_TEST }, + { PATMD_TEST3, "TEST3", "T3", PATMD_M_TEST }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +void LoadParametersPAT +( + ccp log_prefix // not NULL: + // print log message with prefix +) +{ + static bool done = false; + if (done) + return; + done = true; + + SetPatMode(PAT_MODE); + + + //--- logging + + if ( !PAT_ACTION_LOG("Global PAT Modes: %s\n",GetPatMode()) + && log_prefix + && PAT_MODE != PATMD_M_DEFAULT + && !(PAT_MODE & PATMD_SILENT) ) + { + fprintf(stdlog,"%sglobal pat modes: %s [%x]\n",log_prefix,GetPatMode(),PAT_MODE); + fflush(stdlog); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +pat_mode_t GetRelevantPatMode ( pat_mode_t mode ) +{ + mode = mode & PATMD_M_ALL; + + if ( PATCH_FILE_MODE & PFILE_F_LOG_ALL ) + mode |= PATMD_LOG; + + return mode; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetPatMode ( uint new_mode ) +{ + noPRINT("SET-PAT/IN: PC=%d, MPC=%d\n", + have_patch_count, have_pat_patch_count ); + +// if (have_pat_patch_count) +// have_patch_count--, have_pat_patch_count--; + + PAT_MODE = GetRelevantPatMode(new_mode); + +// if ( PAT_MODE & PATMD_M_PATCH ) +// have_patch_count++, have_pat_patch_count++; + + noPRINT("SET-PAT/OUT: PC=%d, MPC=%d\n", + have_patch_count, have_pat_patch_count ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPat ( ccp arg ) +{ + if (!arg) + return 0; + + static bool done = false; + if (!done) + { + done = true; + if ( *arg != '+' && *arg != '-' ) + PAT_MODE = 0; + } + + s64 stat = ScanKeywordList(arg,opt_pat_tab,0,true,0,PAT_MODE, + "Option --pat",ERR_SYNTAX); + if ( stat != -1 ) + { + SetPatMode(stat); + return 0; + } + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintPatMode ( char *buf, uint bufsize, pat_mode_t mode ) +{ + DASSERT(buf); + DASSERT(bufsize>10); + char *dest = buf; + char *end = buf + bufsize - 1; + + mode = GetRelevantPatMode(mode) | PATMD_F_HIDE; + pat_mode_t mode1 = mode; + + const KeywordTab_t *ct; + for ( ct = opt_pat_tab; ct->name1 && dest < end; ct++ ) + { + if ( ct->opt & PATMD_F_HIDE ) + continue; + + if ( ct->opt ? (mode & ct->opt) == ct->id : mode & ct->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,ct->name1); + mode &= ~(ct->id|ct->opt); + } + } + + if ( mode1 == (PATMD_M_DEFAULT|PATMD_F_HIDE) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + return dest-buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetPatMode() +{ + static char buf[100] = {0}; + if (!*buf) + PrintPatMode(buf,sizeof(buf),PAT_MODE); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PAT0 iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int pat_iter_func +( + brsub_cut_t *bcut, // pointer to data structure + int grp, // index of group, <0: grp_entry_t + int entry // index of entry, -1: no entry +) +{ + DASSERT(bcut); + szs_iterator_t *it = bcut->it; + DASSERT(it); + DASSERT(it->func_it); + + it->group = grp; + it->entry = entry; + + noPRINT("PAT( %2d, %2d, %s )\n",grp,entry,it->path); + + switch (grp) + { + case GROUP_IDX_SUB_HEADER: + { + const pat_head_t *head = (pat_head_t*)( bcut->data + it->off ); + it->size = sizeof(*head); + it->entry = 0; + const int stat = it->func_it(it,false); + it->index++; + return stat; + } + } + + const int stat = it->func_it(it,false); + it->index++; + return stat; +} + +//----------------------------------------------------------------------------- + +static ccp pat_names[] = +{ + ".base", // 0 + ".name", // 1 + "", // 2 + ".attrib", // 3 + 0 +}; + +static ccp GetSectNamePAT ( uint index ) +{ + ccp res = index < sizeof(pat_names)/sizeof(*pat_names) + ? pat_names[index] + : 0; + if ( !res || !*res ) + { + const int bufsize = 12; + char *buf = GetCircBuf(bufsize); + snprintf(buf,bufsize,".%u",index); + res = buf; + } + return res; +} + +//----------------------------------------------------------------------------- + +int IterateFilesPAT +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + return CutFilesBRSUB( it, pat_iter_func, 0, + pat_names, sizeof(pat_names) / sizeof(*pat_names) -1 ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PAT0 base interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializePAT ( pat_t * pat ) +{ + DASSERT(pat); + memset(pat,0,sizeof(*pat)); + + pat->version = 4; + pat->n_sect = 6; + pat->revision = REVISION_NUM; + + pat->fname = EmptyString; + InitializeParamField(&pat->s1_str); + + LoadParametersPAT( verbose > 0 ? " - " : 0 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ClearElementsPAT ( pat_t * pat ) +{ + DASSERT(pat); + + uint i; + pat_element_t *pe = pat->pelem; + for ( i = 0; i < pat->n_pelem; i++, pe++ ) + { + FREE(pe->selem); + FreeString(pe->bname); + FreeString(pe->sname); + memset(pe,0,sizeof(*pe)); + } + pat->n_pelem = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ClearPAT ( pat_t * pat ) +{ + DASSERT(pat); + + ClearElementsPAT(pat); + if ( pat->outmode != PATMD_SIMPLE ) + ResetParamField(&pat->s1_str); + + FREE(pat->raw_data); + pat->raw_data = 0; + pat->raw_data_size = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPAT ( pat_t * pat ) +{ + DASSERT(pat); + + ClearPAT(pat); + ResetParamField(&pat->s1_str); + FreeString(pat->fname); + FreeString(pat->name); + #if USE_NEW_CONTAINER_PAT + ResetContainer(&pat->container); + #else + FreeContainer(pat->old_container); + #endif + InitializePAT(pat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool IsSimplePAT ( pat_t *pat ) +{ + DASSERT(pat); + const pat_element_t *pe = pat->pelem; + + if + ( + pat->n_pelem != 1 + + || pat->head.unknown_00 + || pat->head.unknown_02 + || pat->head.unknown_0a + || pat->head.unknown_0c + + || pat->base.unknown_04 + || pat->base.unknown_08 != htons(0xffff) + || pat->base.unknown_0a + || pat->base.n_unknown != htons(1) + || pat->base.unknown_0e + || pat->base.unknown_10 + || pat->base.unknown_12 + || pat->base.unknown_14 + || pat->base.unknown_16 + + || pe->belem.unknown_02 + || pe->belem.unknown_04 + || pe->belem.unknown_06 != htons(1) + + || pe->sref.unknown_04 + || pe->sref.type != htons(5) + + || pe->shead.unknown_02 + + || !pe->bname + || !pe->sname + || strcmp(pe->bname,pe->sname) + ) + return false; + + uint i; + ParamFieldItem_t *it = pat->s1_str.field; + for ( i = 0; i < pat->s1_str.used; i++, it++ ) + if (it->num) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetNamePAT ( pat_t *pat ) +{ + DASSERT(pat); + if (!pat->name) + { + if (pat->fname) + { + ccp begin = strrchr(pat->fname,'/'); + begin = begin ? begin + 1 : pat->fname; + ccp end = strchr(begin,'.'); + const uint len = end ? end-begin : strlen(begin); + pat->name = MEMDUP(begin,len); + } + else + pat->name = STRDUP("pat"); + } + return pat->name; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ParamFieldItem_t * AddS1String ( pat_t *pat, ccp string, u32 num ) +{ + DASSERT(pat); + DASSERT(string); + + ParamFieldItem_t *it = AppendParamField(&pat->s1_str,string,false,num,0); + DASSERT(it); + return it; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStringPAT +( + pat_t * pat, // PAT data structure + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid // result if invalid +) +{ + DASSERT(pat); + DASSERT(base); + + if (offset) + { + ccp string = (ccp)base + offset; + #if USE_NEW_CONTAINER_PAT + if (InContainerP(&pat->container,string)) + #else + if (InDataContainer(pat->old_container,string)) + #endif + return string; + } + + return if_invalid; +} + + +/////////////////////////////////////////////////////////////////////////////// + +static float CalcFactorSHEAD +( + pat_s0_selem_t *se, // element list, big endian + uint n_se // number of elements in 'se' +) +{ + if ( !se || n_se < 2 ) + return 1.0; + + double min, max; + min = max = bef4(&se->time); + while ( --n_se > 0 ) + { + se++; + const double time = bef4(&se->time); + if ( min > time ) min = time; + if ( max < time ) max = time; + } + + // special rounding to get little smaller values like Nintendo + const float delta = max - min; + return delta < 1e-6 ? 1.0 : 0.9999999/delta; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct slist_param_t +{ + float factor; // factor for list header + u16 unknown_02; // value for list header padding +} +slist_param_t; + +//----------------------------------------------------------------------------- + +typedef struct slist_list_t +{ + float factor; // factor for list header + u16 unknown_02; // value for list header padding + + pat_s0_selem_t *selem; // list with string elements + uint selem_used; // number of used elements of 'selem' + uint selem_size; // number of alloced elements of 'selem' +} +slist_list_t; + +//----------------------------------------------------------------------------- + +static void Close_SList +( + slist_param_t *param, // parameter, never NULL + slist_list_t *list // NULL or list element +) +{ + DASSERT(param); + + if (list) + { + list->unknown_02 = param->unknown_02; + list->factor = IsNormalF(param->factor) + ? param->factor + : CalcFactorSHEAD(list->selem,list->selem_used); + } + + //--- reset param + + param->factor = NAN; + param->unknown_02 = 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRawPAT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRawPAT +( + pat_t * pat // pointer to valid PAT +) +{ + DASSERT(pat); + TRACE("CreateRawPAT()\n"); + + //--- setup string pool + + string_pool_t sp; + InitializeStringPool(&sp); + InsertStringPool(&sp,GetNamePAT(pat),false,0); + + uint i; + for ( i = 0; i < pat->s1_str.used; i++ ) + InsertStringPool(&sp,pat->s1_str.field[i].key,false,0); + + + //--- calculate sizes + + const uint s0_offset = PAT_HEAD_OFFSET + sizeof(pat_head_t); + uint s0_size = sizeof(pat_s0_bhead_t) + + pat->n_pelem + * ( sizeof(pat_s0_belem_t) + + sizeof(pat_s0_sref_t) + + sizeof(pat_s0_shead_t) ); + + pat_element_t *pe = pat->pelem; + for ( i = 0; i < pat->n_pelem; i++, pe++ ) + { + s0_size += pe->selem_used * sizeof(pat_s0_selem_t); + InsertStringPool(&sp,pe->bname,false,0); + InsertStringPool(&sp,pe->sname,false,0); + } + + const uint s1_size = sizeof(u32) * pat->s1_str.used; + const uint pat_size = s0_offset + s0_size + 2 * s1_size; + + CalcStringPool(&sp,pat_size,&be_func); + uint total_size = pat_size + sp.size; + + PRINT("\e[31;1m SIZE: s0=%x, s1=%x, pat=%x, tot=%x\e[0m\n", + s0_size, s1_size, pat_size, total_size ); + + + //--- setup data + + FREE(pat->raw_data); + pat->raw_data = CALLOC(1,total_size); + pat->raw_data_size = total_size; + + memcpy( pat->raw_data + pat_size, sp.data, sp.size ); + + + //--- setup brres sub header + + brsub_header_t *brh = (brsub_header_t*)pat->raw_data; + memcpy(brh->magic,PAT_MAGIC,sizeof(brh->magic)); + + brh->size = htonl(pat_size); + brh->version = htonl(pat->version); + brh->grp_offset[0] = htonl(s0_offset); + + const u32 s1_offset = s0_offset + s0_size; + brh->grp_offset[1] = htonl(s1_offset); + + const u32 s3_offset = s1_offset + s1_size; + brh->grp_offset[2] = brh->grp_offset[3] = htonl(s3_offset); + + brh->grp_offset[4] = htonl(s3_offset+s1_size); + brh->grp_offset[6] = htonl(FindStringPool(&sp,GetNamePAT(pat),0)); + + + //--- setup header data + + pat_head_t *ph = &pat->head; + ph->n_sect0 = htons(pat->n_pelem); + ph->n_sect1 = htons(pat->s1_str.used); + memcpy( pat->raw_data + PAT_HEAD_OFFSET, ph, sizeof(*ph) ); + + + //--- setup section 0 + + pat_s0_bhead_t *bh = &pat->base; + const uint base_size = sizeof(pat_s0_bhead_t) + pat->n_pelem * sizeof(pat_s0_belem_t); + bh->size = htonl(base_size); + bh->n_elem = htons(pat->n_pelem); + + pat_s0_bhead_t *dest_bh = (pat_s0_bhead_t*)(pat->raw_data + s0_offset); + memcpy( dest_bh, bh, sizeof(*bh) ); + + + //--- setup section 0: string lists + + u32 sref_offset = s0_offset + base_size; + u32 shead_offset = sref_offset + pat->n_pelem * sizeof(pat_s0_sref_t); + + for ( i = 0; i < pat->n_pelem; i++ ) + { + const int idx = pat->shead_order[i]; + pat_element_t *pe = pat->pelem + idx; + pat_s0_shead_t *sh = &pe->shead; + sh->n_elem = htons(pe->selem_used); + + //--- copy data + + memcpy( pat->raw_data + shead_offset, sh, sizeof(*sh) ); + pe->sref.offset_strlist + = htonl(shead_offset - sref_offset - sizeof(pat_s0_sref_t) * idx); + shead_offset += sizeof(pat_s0_shead_t); + + const uint copy_size = sizeof(pat_s0_selem_t) * pe->selem_used; + memcpy( pat->raw_data + shead_offset, pe->selem, copy_size ); + shead_offset += copy_size; + } + + + //--- setup section 0: string reference + + for ( i = 0; i < pat->n_pelem; i++ ) + { + const int idx = pat->sref_order[i]; + pat_element_t *pe = pat->pelem + idx; + pat_s0_sref_t *sr = &pe->sref; + sr->offset_name = htonl(FindStringPool(&sp,pe->sname,0)-sref_offset); + + memcpy( pat->raw_data + sref_offset, sr, sizeof(*sr) ); + pe->belem.offset_strref = htonl(sref_offset - s0_offset); + sref_offset += sizeof(pat_s0_sref_t); + } + + + //--- setup section 0: base elements + + for ( i = 0, pe = pat->pelem; i < pat->n_pelem; i++, pe++ ) + { + pat_s0_belem_t *be = &pe->belem; + be->offset_name = htonl(FindStringPool(&sp,pe->bname,0)-s0_offset); + + memcpy( dest_bh->elem+i, be, sizeof(*be) ); + } + + + //--- setup section 1+3 + + ParamFieldItem_t *it = pat->s1_str.field; + for ( i = 0; i < pat->s1_str.used; i++, it++ ) + { + u32 offset = FindStringPool(&sp,it->key,0); + if (offset) + offset -= s1_offset; + write_be32( pat->raw_data + s1_offset + 4*i, offset ); + write_be32( pat->raw_data + s3_offset + 4*i, it->num ); + } + + + //--- terminate + + ResetStringPool(&sp); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// setup pat parser /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const VarMap_t * SetupVarsPAT() +{ + static VarMap_t vm = {0}; + if (!vm.used) + { + #if 0 + //--- setup integer variables + + struct inttab_t { ccp name; int val; }; + static const struct inttab_t inttab[] = + { + {0,0} + }; + + const struct inttab_t * ip; + for ( ip = inttab; ip->name; ip++ ) + DefineIntVar(&vm,ip->name,ip->val); + #endif + + //--- setup basic parser variables + + DefineParserVars(&vm); + } + + return &vm; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextPAT: helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanText_SETUP +( + pat_t * pat, // PAT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(pat); + DASSERT(si); + TRACE("ScanText_SETUP()\n"); + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + char name[50]; + if ( !ScanNameSI(si,name,sizeof(name),true,true,0) + || NextCharSI(si,true) != '=' ) + { + CheckEolSI(si); + continue; + } + DASSERT(si->cur_file); + si->cur_file->ptr++; + noPRINT("### %s = %.4s...\n",name,si->cur_file->ptr); + + if (!strcmp(name,"REVISION")) + { + ScanU32SI(si,(u32*)&pat->revision,1,0); + si->init_revision = si->cur_file->revision = pat->revision; + DefineIntVar(&si->gvar,"REVISION$SETUP",pat->revision); + DefineIntVar(&si->gvar,"REVISION$ACTIVE",pat->revision); + } + else if (!strcmp(name,"NAME")) + ConcatStringsSI(si,(char**)&pat->name,0); + else + GotoEolSI(si); + CheckEolSI(si); + } + CheckLevelSI(si); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanText_SIMPLE +( + pat_t * pat, // PAT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(pat); + DASSERT(si); + TRACE("ScanText_SIMPLE()\n"); + + memset(&pat->head,0,sizeof(pat->head)); + memset(&pat->base,0,sizeof(pat->base)); + pat->base.unknown_08 = htons(0xffff); + pat->base.n_unknown = 1; + + ClearElementsPAT(pat); + pat_element_t *pe = pat->pelem; + pe->belem.unknown_06 = htons(1); + pe->sref.type = htons(5); + pat->n_pelem = 1; + + slist_param_t parm; + Close_SList(&parm,0); + slist_list_t elem; + memset(&elem,0,sizeof(elem)); + double last_time = 0.0; + + int enabled = 1; + ScanParam_t ptab[] = + { + { "ENABLED", SPM_INT, &enabled }, + { "BRRES-NAME", SPM_STRING, &pat->name }, + { "PAT-NAME", SPM_STRING, &pe->bname }, + { "N-FRAMES", SPM_U16, &pat->head.n_frames }, + { "CYCLIC", SPM_U16, &pat->head.cyclic }, + { "UNKNOWN-00", SPM_U16, &pe->belem.unknown_00 }, + { "FACTOR", SPM_FLOAT, &parm.factor }, + { "PADDING", SPM_U16, &parm.unknown_02 }, + {0} + }; + + enumError err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + if (!enabled) + { + ClearPAT(pat); + pat->outmode = PATMD_COMPLEX; + return err; + } + continue; + } + pat->outmode = PATMD_SIMPLE; + + double time; + char *str = 0; + if ( ScanDoubleSI(si,&time,1) || ConcatStringsSI(si,&str,0) ) + continue; + + ParamFieldItem_t *it + = FindInsertParamField( &pat->s1_str, str, true, 0 ); + const uint sidx = it ? it - pat->s1_str.field : 0; + + if ( elem.selem_used == elem.selem_size ) + { + elem.selem_size += elem.selem_size + 20; + elem.selem = REALLOC(elem.selem,elem.selem_size*sizeof(*elem.selem)); + } + DASSERT(elem.selem); + DASSERT(elem.selem_used < elem.selem_size); + pat_s0_selem_t *se = elem.selem + elem.selem_used++; + last_time += time; + write_bef4(&se->time,last_time); + se->index = htons(sidx); + se->unknown_06 = htons(0); + + CheckEolSI(si); + } + Close_SList(&parm,&elem); + + pe->sname = STRDUP(pe->bname); + be16n((u16*)&pat->head,(u16*)&pat->head,sizeof(pat->head)/2); + be16n((u16*)&pat->base,(u16*)&pat->base,sizeof(pat->base)/2); + be16n((u16*)&pe->belem.unknown_00,(u16*)&pe->belem.unknown_00,1); + pe->shead.unknown_02 = htons(elem.unknown_02); + write_bef4(&pe->shead.factor,elem.factor); + pe->selem = elem.selem; + pe->selem_used = elem.selem_used; + pe->selem_size = elem.selem_size; + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanText_HEADER +( + pat_t * pat, // PAT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(pat); + DASSERT(si); + TRACE("ScanText_HEADER()\n"); + + memset(&pat->head,0,sizeof(pat->head)); + + ScanParam_t ptab[] = + { + { "NAME", SPM_STRING, &pat->name }, + { "UNKNOWN-00", SPM_U16, &pat->head.unknown_00 }, + { "UNKNOWN-02", SPM_U16, &pat->head.unknown_02 }, + { "N-FRAMES", SPM_U16, &pat->head.n_frames }, + // { "N-SECT0", SPM_U16, &pat->head.n_sect0 }, + // { "N-SECT1", SPM_U16, &pat->head.n_sect1 }, + { "UNKNOWN-0A", SPM_U16, &pat->head.unknown_0a }, + { "UNKNOWN-0C", SPM_U16, &pat->head.unknown_0c }, + { "CYCLIC", SPM_U16, &pat->head.cyclic }, + {0} + }; + + enumError err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + WarnIgnoreSI(si,0); + } + + be16n((u16*)&pat->head,(u16*)&pat->head,sizeof(pat->head)/2); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanText_S0_BASE +( + pat_t * pat, // PAT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(pat); + DASSERT(si); + TRACE("ScanText_S0_BASE()\n"); + + memset(&pat->base,0,sizeof(pat->base)); + pat->base.unknown_08 = 0xffff; + + ScanParam_t ptab[] = + { + // { "SIZE", SPM_U32, &pat->base.size }, + { "UNKNOWN-04", SPM_U16, &pat->base.unknown_04 }, + // { "N-ELEM", SPM_U16, &pat->base.n_elem }, + { "UNKNOWN-08", SPM_U16, &pat->base.unknown_08 }, + { "UNKNOWN-0A", SPM_U16, &pat->base.unknown_0a }, + { "N-UNKNOWN", SPM_U16, &pat->base.n_unknown }, + { "UNKNOWN-0E", SPM_U16, &pat->base.unknown_0e }, + { "UNKNOWN-10", SPM_U16, &pat->base.unknown_10 }, + { "UNKNOWN-12", SPM_U16, &pat->base.unknown_12 }, + { "UNKNOWN-14", SPM_U16, &pat->base.unknown_14 }, + { "UNKNOWN-16", SPM_U16, &pat->base.unknown_16 }, + {0} + }; + + ClearElementsPAT(pat); + uint n_elem = 0; + uint n_sref = 0; + u8 elem_to_sref[PAT_MAX_ELEM]; + u8 sref_to_shead[PAT_MAX_ELEM]; + pat_s0_sref_t sref_list[PAT_MAX_ELEM]; + memset(sref_list,0,sizeof(sref_list)); + char *sname[PAT_MAX_ELEM] = {0}; + + enumError err = ERR_OK; + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + if ( ch == '$' ) + { + char name[PAT_MAX_NAME_LEN+1]; + ScanNameSI(si,name,sizeof(name),true,true,0); + + if ( !strcmp(name,"$BASE") ) + { + if ( n_elem == PAT_MAX_ELEM ) + { + WarnIgnoreSI(si, + "Maximum number of base elements (%u) already defined.\n", + PAT_MAX_ELEM ); + continue; + } + + enumError err; + ScanIndexSI(si,&err,n_elem,0,2); + if (err) + continue; + + pat_element_t *pe = pat->pelem + n_elem; + pat_s0_belem_t *be = &pe->belem; + memset(be,0,sizeof(*be)); + ScanU16SI(si,(u16*)be,4,0); + be16n((u16*)be,(u16*)be,4); + + ScanU8SI(si,elem_to_sref+n_elem,1,0); + ConcatStringsSI(si,(char**)&pe->bname,0); + + n_elem++; + CheckEolSI(si); + continue; + } + + if ( !strcmp(name,"$SREF") ) + { + if ( n_sref == n_elem ) + { + WarnIgnoreSI(si, + "You can't define more string references as base elements (%u).\n", + n_elem ); + continue; + } + + enumError err; + ScanIndexSI(si,&err,n_sref,0,2); + if (err) + continue; + + pat_s0_sref_t *sref = sref_list + n_sref; + ScanU16SI(si,&sref->unknown_04,2,0); + be16n(&sref->unknown_04,&sref->unknown_04,2); + + ScanU8SI(si,sref_to_shead+n_sref,1,0); + ConcatStringsSI(si,sname+n_sref,0); + + n_sref++; + CheckEolSI(si); + continue; + } + + si->cur_file->ptr = si->cur_file->prev_ptr; + } + + WarnIgnoreSI(si,0); + } + + be16n((u16*)&pat->base,(u16*)&pat->base,sizeof(pat->base)/2); + pat->n_pelem = n_elem; + + //--- copy 'sref' and 'sname' + + if (!si->no_warn) + { + uint i; + pat_element_t *pe = pat->pelem; + for ( i = 0; i < n_elem; i++, pe++ ) + { + const uint s_idx = elem_to_sref[i]; + noPRINT(">>> %u -> %u\n",i,s_idx); + if ( s_idx >= n_sref ) + { + ERROR0(ERR_WARNING, + "No 'string reference' for base element %u (sref=%u).\n[%s @%u]\n", + i, s_idx, si->cur_file->name, si->cur_file->line ); + continue; + } + + memcpy( &pe->sref, sref_list+s_idx, sizeof(pe->sref) ); + if (sname[s_idx]) + pe->sname = STRDUP(sname[s_idx]); + + pat->sref_order[s_idx] = i; + pat->elem_to_shead[i] = sref_to_shead[s_idx]; + } + } + + uint i; + for ( i = 0; i < n_sref; i++ ) + FreeString(sname[i]); + + //PRINT("\e[33;1m elem_to_shead:\e[0m\n"); + //HEXDUMP16(15,0,pat->elem_to_shead,sizeof(pat->elem_to_shead)); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanText_S0_LIST +( + pat_t * pat, // PAT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(pat); + DASSERT(si); + TRACE("ScanText_S0_LIST()\n"); + + slist_param_t parm; + Close_SList(&parm,0); + + ScanParam_t ptab[] = + { + { "FACTOR", SPM_FLOAT, &parm.factor }, + { "PADDING", SPM_U16, &parm.unknown_02 }, + {0} + }; + + slist_list_t elist[PAT_MAX_ELEM], *elem = 0; + memset(elist,0,sizeof(elist)); + uint n_list = 0; + bool no_elem_warned = false; + + double last_time = 0.0; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + if ( ch == '$' ) + { + char name[PAT_MAX_NAME_LEN+1]; + ScanNameSI(si,name,sizeof(name),true,true,0); + if (!strcmp(name,"$LIST")) + { + Close_SList(&parm,elem); + + if ( n_list == PAT_MAX_ELEM ) + { + WarnIgnoreSI(si, + "Maximum number of string list (%u) already defined.\n", + PAT_MAX_ELEM ); + elem = 0; + continue; + } + + enumError err; + ScanIndexSI(si,&err,n_list,0,2); + if (err) + continue; + + elem = elist + n_list++; + last_time = 0.0; + CheckEolSI(si); + continue; + } + + WarnIgnoreSI(si,0); + continue; + } + + if (!elem) + { + if (!no_elem_warned) + { + no_elem_warned = true; + WarnIgnoreSI(si,"Define a list with '$LIST ' first.\n"); + } + else + GotoEolSI(si); + continue; + } + + double time; + u16 param[2]; + if ( ScanDoubleSI(si,&time,1) || ScanU16SI(si,param,2,0) ) + continue; + + if ( elem->selem_used == elem->selem_size ) + { + elem->selem_size += elem->selem_size + 20; + elem->selem = REALLOC(elem->selem,elem->selem_size*sizeof(*elem->selem)); + } + DASSERT(elem->selem); + DASSERT(elem->selem_used < elem->selem_size); + pat_s0_selem_t *se = elem->selem + elem->selem_used++; + last_time += time; + write_bef4(&se->time,last_time); + se->index = htons(param[0]); + se->unknown_06 = htons(param[1]); + + CheckEolSI(si); + } + Close_SList(&parm,elem); + + if (!si->no_warn) + { + uint i; + pat_element_t *pe = pat->pelem; + for ( i = 0; i < pat->n_pelem; i++, pe++ ) + { + const uint h_idx = pat->elem_to_shead[i]; + noPRINT(">>> %u -> %u\n",i,h_idx); + if ( h_idx >= n_list ) + { + ERROR0(ERR_WARNING, + "No 'string list' for base element %u (index=%u).\n[%s @%u]\n", + i, h_idx, si->cur_file->name, si->cur_file->line ); + continue; + } + pat->shead_order[h_idx] = i; + + slist_list_t *elem = elist + h_idx; + FREE(pe->selem); + const uint copy_size = elem->selem_used * sizeof(*elem->selem); + pe->selem = MALLOC(copy_size); + memcpy(pe->selem,elem->selem,copy_size); + pe->selem_used = pe->selem_size = elem->selem_used; + + write_bef4(&pe->shead.factor, elem->factor ); + pe->shead.unknown_02 = htons(elem->unknown_02); + } + } + + uint i; + for ( i = 0; i < n_list; i++ ) + FREE(elist[i].selem); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanText_STRINGS +( + pat_t * pat, // PAT data structure + ScanInfo_t * si // valid data +) +{ + DASSERT(pat); + DASSERT(si); + TRACE("ScanText_STRINGS()\n"); + + ResetParamField(&pat->s1_str); + + ScanParam_t ptab[] = + { + {0} + }; + + for(;;) + { + char ch = NextCharSI(si,true); + if ( !ch || ch == '[' ) + break; + + if ( ch == '@' ) + { + ScanParamSI(si,ptab); + continue; + } + + enumError err; + ScanIndexSI(si,&err,pat->s1_str.used,0,2); + if (err) + continue; + + uint attrib = 0; + if (ScanU32SI(si,&attrib,1,0)) + continue; + + char *str = 0; + if (ConcatStringsSI(si,&str,0)) + continue; + + AppendParamField(&pat->s1_str,str,true,attrib,0); + CheckEolSI(si); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextPAT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanTextPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + const void * data, // data to scan + uint data_size // size of 'data' +) +{ + PRINT("ScanTextPAT(init=%d)\n",init_pat); + + DASSERT(pat); + if (init_pat) + InitializePAT(pat); + + ScanInfo_t si; + InitializeSI(&si,data,data_size,pat->fname,pat->revision); + si.predef = SetupVarsPAT(); + + enumError max_err = ERR_OK; + bool is_pass2 = false; + + for(;;) + { + PRINT("----- SCAN PAT SECTIONS, PASS%u ...\n",is_pass2); + + ClearPAT(pat); + + max_err = ERR_OK; + si.no_warn = !is_pass2; + si.total_err = 0; + DefineIntVar(&si.gvar, "$PASS", is_pass2+1 ); + + for(;;) + { + char ch = NextCharSI(&si,true); + if (!ch) + break; + + if ( ch != '[' ) + { + NextLineSI(&si,true,false); + continue; + } + ResetLocalVarsSI(&si,pat->revision); + + si.cur_file->ptr++; + char name[20]; + ScanNameSI(&si,name,sizeof(name),true,true,0); + noPRINT("--> #%04u: %s\n",si.line,name); + + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,name,pat_section_name); + if ( !cmd || abbrev_count ) + continue; + NextLineSI(&si,false,false); + + noPRINT("\e[44;37;1m pass %u [%s] line %u \e[0m\n", + is_pass2+1, cmd->name1, si.cur_file->line ); + + if ( cmd->id < 0 || cmd->id >= PAT_N_SECT ) + continue; + + enumError err = ERR_OK; + switch (cmd->id) + { + case PAT_SECT_SETUP: + err = ScanText_SETUP(pat,&si); + break; + + case PAT_SECT_SIMPLE: + if ( pat->outmode != PATMD_COMPLEX ) + err = ScanText_SIMPLE(pat,&si); + break; + + case PAT_SECT_HEADER: + if ( pat->outmode != PATMD_SIMPLE ) + { + pat->outmode = PATMD_COMPLEX; + err = ScanText_HEADER(pat,&si); + } + break; + + case PAT_SECT_S0_BASE: + if ( pat->outmode != PATMD_SIMPLE ) + { + pat->outmode = PATMD_COMPLEX; + err = ScanText_S0_BASE(pat,&si); + } + break; + + case PAT_SECT_S0_LIST: + if ( pat->outmode != PATMD_SIMPLE ) + { + pat->outmode = PATMD_COMPLEX; + err = ScanText_S0_LIST(pat,&si); + } + break; + + case PAT_SECT_STRINGS: + if ( pat->outmode != PATMD_SIMPLE ) + { + pat->outmode = PATMD_COMPLEX; + err = ScanText_STRINGS(pat,&si); + } + break; + + default: + // ignore all other section without any warnings + break; + } + + if ( max_err < err ) + max_err = err; + } + + if (is_pass2) + break; + is_pass2 = true; + RestartSI(&si); + } + + #if HAVE_PRINT0 + printf("VAR DUMP/GLOBAL:\n"); + DumpVarMap(stdout,3,&si.gvar,false); + #endif + + CheckLevelSI(&si); + if ( max_err < ERR_WARNING && si.total_err ) + max_err = ERR_WARNING; + PRINT("ERR(ScanTextPAT) = %u (errcount=%u)\n", max_err, si.total_err ); + ResetSI(&si); + + CreateRawPAT(pat); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawPAT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerPAT_t * cdata // NULL or container-data +) +{ + DASSERT(pat); + + #if USE_NEW_CONTAINER_PAT // --------------------------------------- + + if (init_pat) + InitializePAT(pat); + + CatchContainerData(&pat->container,0,cdata); + AssignContainer(&pat->container,0,data,data_size,CPM_COPY); + #ifdef TEST + DumpInfoContainer(stdlog,collog,2,"PAT: ",&pat->container,0); + #endif + + #else // !USE_NEW_CONTAINER_PAT // --------------------------------- + + if (init_pat) + InitializePAT(pat); + else + FreeContainer(pat->old_container); + pat->old_container = cdata ? cdata : NewContainer(0,data,data_size,CPM_COPY); + + #endif // !USE_NEW_CONTAINER_PAT // -------------------------------- + + const endian_func_t * endian = &be_func; + pat_analyse_t ana; + if ( IsValidPAT(data,data_size,data_size,0,pat->fname,endian,&ana) >= VALID_ERROR ) + { + return ERROR0(ERR_INVALID_DATA, + "Invalid PAT file: %s\n", pat->fname ? pat->fname : "?"); + } + + + //--- scan PAT base infos + + pat->version = endian->rd32(data+8); + pat->n_sect = GetSectionNumBRSUB(data,data_size,endian); + + u32 offset = pat->n_sect * 4 + sizeof(brsub_header_t); + ccp name = GetStringPAT(pat, data, be32(data+offset), 0 ); + if (name) + pat->name = STRDUP(name); + PRINT("PAT/NAME: %s\n",pat->name); + + + //--- copy header and section 0 + + memcpy( &pat->head, ana.head, sizeof(pat->head) ); + memcpy( &pat->base, ana.s0_base, sizeof(pat->base) ); + + memcpy( &pat->sref_order, ana.s0_sref_order, sizeof(pat->sref_order) ); + memcpy( &pat->shead_order, ana.s0_shead_order, sizeof(pat->shead_order) ); + + uint i; + pat->n_pelem = ana.n_sect0 < PAT_MAX_ELEM ? ana.n_sect0 : PAT_MAX_ELEM; + pat_element_t *pe = pat->pelem; + for ( i = 0; i < pat->n_pelem; i++, pe++ ) + { + memcpy( &pe->belem, ana.s0_base->elem+i, sizeof(pe->belem) ); + memcpy( &pe->sref, ana.s0_sref[i], sizeof(pe->sref) ); + memcpy( &pe->shead, ana.s0_shead[i], sizeof(pe->shead) ); + + pe->bname = STRDUP(GetStringPAT( pat, ana.s0_base, + be32(&ana.s0_base->elem[i].offset_name), "?" )); + pe->sname = STRDUP(GetStringPAT( pat, ana.s0_sref[i], + be32(&ana.s0_sref[i]->offset_name), "?" )); + + const uint n = ana.n_s0_list[i]; + if (n) + { + pe->selem_used = pe->selem_size = n; + pe->selem = REALLOC(pe->selem,n*sizeof(*pe->selem)); + memcpy( pe->selem, ana.s0_shead[i]->elem, n*sizeof(*pe->selem) ); + } + } + + + //--- analyse sections 1 and 3 + + if (ana.s1_list) + { + uint i; + for ( i = 0; i < ana.n_sect1; i++ ) + { + ccp str = GetStringPAT(pat,ana.s1_list,be32(ana.s1_list+i),"?"); + AddS1String( pat, str, ana.s3_list ? be32(ana.s3_list+i) : 0 ); + } + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PAT0 scan + load /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerPAT_t * cdata, // NULL or container-data + CheckMode_t mode // not NULL: call CheckPAT(mode) +) +{ + DASSERT(pat); + PRINT("ScanPAT(mode=%x) size=%u\n",mode,data_size); + + enumError err; +// [[analyse-magic]] + switch (GetByMagicFF(data,data_size,data_size)) + { + case FF_PAT: + pat->fform = FF_PAT; + err = ScanRawPAT(pat,init_pat,data,data_size,cdata); + break; + + case FF_PAT_TXT: + #if USE_NEW_CONTAINER_PAT + FreeContainerData(cdata); + #else + FreeContainer(cdata); + #endif + pat->fform = FF_PAT_TXT; + err = ScanTextPAT(pat,init_pat,data,data_size); + break; + + default: + #if USE_NEW_CONTAINER_PAT + FreeContainerData(cdata); + #else + FreeContainer(cdata); + #endif + if (init_pat) + InitializePAT(pat); + return ERROR0(ERR_INVALID_DATA, + "No PAT file: %s\n", pat->fname ? pat->fname : "?"); + } + + if ( err <= ERR_WARNING && mode ) + CheckPAT(pat,mode); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckPAT(mode) +) +{ + DASSERT(pat); + DASSERT(raw); + if (init_pat) + InitializePAT(pat); + else + ResetPAT(pat); + + pat->fname = raw->fname; + raw->fname = 0; + #if USE_NEW_CONTAINER_PAT + return ScanPAT( pat, false, raw->data, raw->data_size, + LinkContainerRawData(raw), mode ); + #else + return ScanPAT(pat,false,raw->data,raw->data_size,ContainerRawData(raw),mode); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckPAT(mode) +) +{ + DASSERT(pat); + DASSERT(fname); + if (init_pat) + InitializePAT(pat); + else + ResetPAT(pat); + + //--- load and scan data + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,fname,0,ignore_no_file,false); + if (!err) + { + pat->fname = raw.fname; + raw.fname = 0; + #if USE_NEW_CONTAINER_PAT + err = ScanPAT( pat, false, raw.data, raw.data_size, + LinkContainerRawData(&raw), mode ); + #else + err = ScanPAT(pat,false,raw.data,raw.data_size,ContainerRawData(&raw),mode); + #endif + } + + ResetRawData(&raw); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawPAT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawPAT +( + pat_t * pat, // pointer to valid PAT + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(pat); + DASSERT(fname); + PRINT("SaveRawPAT(%s,%d)\n",fname,set_time); + + //--- create raw data + + enumError err = CreateRawPAT(pat); + if (err) + return err; + DASSERT(pat->raw_data); + DASSERT(pat->raw_data_size); + + //--- write to file + + File_t F; + err = CreateFileOpt(&F,true,fname,testmode,pat->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&pat->fatt,0); + + if ( fwrite(pat->raw_data,1,pat->raw_data_size,F.f) != pat->raw_data_size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED,"Write failed: %s\n",fname); + return ResetFile(&F,set_time); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextPAT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveSimplePAT +( + pat_t * pat, // pointer to valid PAT + FILE *f, // open FILE + pat_mode_t outmode // output mode +) +{ + if (brief_count) + fprintf(f, + "%s[SIMPLE]\r\n" + "@REVISION = %u\r\n", + section_sep, + REVISION_NUM ); + else + fprintf(f,text_pat_simple_cr,REVISION_NUM); + + if ( outmode & PATMD_COMPLEX ) + { + const bool is_simple = IsSimplePAT(pat); + fprintf(f, + "\r\n" + "# 0: Ignore this section and enable COMPLEX support%s.\r\n" + "# 1: Enable this section and disable all others%s.\r\n" + "@ENABLED = %u\r\n", + is_simple ? "" + : " (default because of complex PAT)", + is_simple ? " (default for this simple PAT)" + : " (data will be lost if enabled)", + is_simple ); + } + + const pat_element_t *pe = pat->pelem; + fprintf(f, + "\r\n" + "@BRRES-NAME = \"%s\"\r\n" + "@PAT-NAME = \"%s\"\r\n" + "@N-FRAMES = %5u\r\n" + "@CYCLIC = %5u\r\n" + "@UNKNOWN-00 = %5u\r\n" + ,GetNamePAT(pat) + ,pe->bname + ,ntohs(pat->head.n_frames) + ,ntohs(pat->head.cyclic) + ,ntohs(pe->belem.unknown_00) + ); + + float factor = bef4(&pe->shead.factor); + if (!IsSameF(factor,CalcFactorSHEAD(pe->selem,pe->selem_used),2)) + fprintf(f, + "@FACTOR = %8.6f # if defined, automatic calculation is disabled\r\n", + factor ); + + uint l, fw = 4; + const pat_s0_selem_t *se; + for ( l = 0, se = pe->selem; l < pe->selem_used; l++, se++ ) + { + uint sidx = ntohs(se->index); + if ( sidx < pat->s1_str.used ) + { + const uint slen = strlen(pat->s1_str.field[sidx].key); + if ( fw < slen ) + fw = slen; + } + } + fw += 3; + + fprintf(f, + "\r\n" + " #-----------------%.*s#-----------\r\n" + " # delta string %*s# abs_time\r\n" + " #-----------------%.*s#-----------\r\n", + fw - 4, Minus300, + fw - 4, "", + fw - 4, Minus300 ); + + float prev = 0.0; + for ( l = 0, se = pe->selem; l < pe->selem_used; l++, se++ ) + { + float time = bef4(&se->time); + uint sidx = ntohs(se->index); + ccp str = sidx < pat->s1_str.used ? pat->s1_str.field[sidx].key : "?"; + fprintf(f,"%13.4f \"%s\" %*s# %9.4f\r\n", + time - prev, + str, + (int)(fw - strlen(str)), "", + time ); + prev = time; + } + + fprintf(f, + " #-----------------%.*s#-----------\r\n", + fw - 4, Minus300 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextPAT +( + pat_t * pat, // pointer to valid PAT + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + // use DOS/Windows line format -> unix can handle it ;) + + DASSERT(pat); + DASSERT(fname); + + pat_mode_t outmode = PAT_MODE & PATMD_M_BOTH; + if (!outmode) + outmode = IsSimplePAT(pat) ? PATMD_SIMPLE : PATMD_COMPLEX; + char outmode_info[20]; + PrintPatMode(outmode_info,sizeof(outmode_info),outmode); + + PRINT("SaveTextPAT(%s,%d) outmode=%x[%s]\n", + fname, set_time, outmode, outmode_info ); + + ccp calc_on_scan = " # calculated on scanning"; + + + //--- open file + + File_t F; + enumError err = CreateFileOpt(&F,true,fname,testmode,pat->fname); + if ( err > ERR_WARNING || !F.f ) + return err; + SetFileAttrib(&F.fatt,&pat->fatt,0); + + + //--- print file header + syntax info + + if ( print_header && !brief_count ) + fprintf(F.f,text_pat_file_head_cr); + else + fprintf(F.f,"%s\r\n",PAT_TEXT_MAGIC); + + + //--- print info section + + if (brief_count) + { + fprintf(F.f, + "%s[SETUP]\r\n\r\n" + "TOOL = %s\r\n" + "SYSTEM = %s\r\n" + "VERSION = %s\r\n" + "REVISION = %u\r\n" + "DATE = %s\r\n" + "\r\n" + "PAT-VERSION = %2u\r\n" + "N-SECTIONS = %2u\r\n" + "OUTPUT-MODE = %s\r\n" + "\r\n", + section_sep, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, + pat->version, pat->n_sect, outmode_info ); + } + else + { + fprintf(F.f, text_pat_setup_cr, + tool_name, SYSTEM, VERSION, REVISION_NUM, DATE, + pat->version, pat->n_sect, outmode_info ); + } + + if ( outmode & PATMD_SIMPLE ) + { + SaveSimplePAT(pat,F.f,outmode); + if (!(outmode & PATMD_COMPLEX)) + { + fputs(section_end,F.f); + ResetFile(&F,set_time); + return err; + } + } + + + //--- write PAT header + + if (brief_count) + fprintf(F.f, + "%s[HEADER]\r\n" + "@REVISION = %u\r\n" + "\r\n", + section_sep, + REVISION_NUM ); + else + fprintf(F.f,text_pat_header_cr,REVISION_NUM); + + const pat_head_t *h = &pat->head; + fprintf(F.f, + "@NAME = \"%s\"\r\n" + "\r\n" + "@UNKNOWN-00 = %5u\r\n" + "@UNKNOWN-02 = %5u\r\n" + "@N-FRAMES = %5u\r\n" + "# N-SECT0 = %5u%s\r\n" + "# N-SECT1 = %5u%s\r\n" + "@UNKNOWN-0A = %5u\r\n" + "@UNKNOWN-0C = %5u\r\n" + "@CYCLIC = %5u\r\n" + "\r\n", + GetNamePAT(pat), + ntohs(h->unknown_00), + ntohs(h->unknown_02), + ntohs(h->n_frames), + ntohs(h->n_sect0), calc_on_scan, + ntohs(h->n_sect1), calc_on_scan, + ntohs(h->unknown_0a), + ntohs(h->unknown_0c), + ntohs(h->cyclic) ); + + + //--- write PAT S0 base + + if (brief_count) + fprintf(F.f, + "%s[S0_BASE]\r\n" + "@REVISION = %u\r\n" + "\r\n", + section_sep, + REVISION_NUM ); + else + fprintf(F.f,text_pat_s0_base_cr,REVISION_NUM); + + const pat_s0_bhead_t *bh = &pat->base; + fprintf(F.f, + "# SIZE = %#6x%s\r\n" + "@UNKNOWN-04 = %#6x\r\n" + "# N-ELEM = %6u%s\r\n" + "@UNKNOWN-08 = %#6x\r\n" + "@UNKNOWN-0A = %#6x\r\n" + "@N-UNKNOWN = %6u\r\n" + "@UNKNOWN-0E = %#6x\r\n" + "@UNKNOWN-10 = %#6x\r\n" + "@UNKNOWN-12 = %#6x\r\n" + "@UNKNOWN-14 = %#6x\r\n" + "@UNKNOWN-16 = %#6x\r\n" + "\r\n", + ntohl(bh->size), calc_on_scan, + ntohs(bh->unknown_04), + ntohs(bh->n_elem), calc_on_scan, + ntohs(bh->unknown_08), + ntohs(bh->unknown_0a), + ntohs(bh->n_unknown), + ntohs(bh->unknown_0e), + ntohs(bh->unknown_10), + ntohs(bh->unknown_12), + ntohs(bh->unknown_14), + ntohs(bh->unknown_16) ); + + fputs( + "\r\n" + "#-------------------+\r\n" + "# base element list |\r\n" + "#----------+--------+-------------------+------------------------------\r\n" + "# index | p00 p02 p04 p06 | sref name\r\n" + "#----------+----------------------------+------------------------------\r\n" + ,F.f ); + + uint i; + const pat_element_t *pe = pat->pelem; + for ( i = 0; i < pat->n_pelem; i++, pe++ ) + { + fprintf(F.f," $BASE B%-2u %#6x %#6x %#6x %#6x R%-3u \"%s\"\r\n", + i, + ntohs(pe->belem.unknown_00), + ntohs(pe->belem.unknown_02), + ntohs(pe->belem.unknown_04), + ntohs(pe->belem.unknown_06), + i, + pe->bname ); + } + + fputs( + "#----------+----------------------------+------------------------------\r\n" + "\r\n" + "\r\n" + "#-----------------------+\r\n" + "# string reference list |\r\n" + "#----------+------------+-+------------------------------\r\n" + "# index | p04 type | sref name\r\n" + "#----------+--------------+------------------------------\r\n" + ,F.f ); + + + for ( i = 0; i < pat->n_pelem; i++ ) + { + const int idx = pat->sref_order[i]; + const pat_element_t *pe = pat->pelem + idx; + + fprintf(F.f," $SREF R%-2u %#6x %#6x L%-3u \"%s\"\r\n", + idx, + ntohs(pe->sref.unknown_04), + ntohs(pe->sref.type), + idx, + pe->sname ); + } + + fputs( + "#----------+--------------+------------------------------\r\n" + ,F.f ); + + + //--- write PAT S0 list + + if (brief_count) + fprintf(F.f, + "%s[S0_LIST]\r\n" + "@REVISION = %u\r\n", + section_sep, + REVISION_NUM ); + else + fprintf(F.f,text_pat_s0_list_cr,REVISION_NUM); + + for ( i = 0; i < pat->n_pelem; i++ ) + { + const int idx = pat->shead_order[i]; + const pat_element_t *pe = pat->pelem + idx; + const double factor = bef4(&pe->shead.factor); + fprintf(F.f, + "\r\n" + " $LIST L%-2u # N=%u, factor=1/%6.4f\r\n" + " %c@FACTOR = %11.6f # if defined, automatic calculation is disabled\r\n" + " @PADDING = %#11x # padding value at offset 2 of list header\r\n" + "\r\n" + " #-----------------------#-----------\r\n" + " # delta string p06 # abs_time\r\n" + " #-----------------------#-----------\r\n", + idx, + ntohs(pe->shead.n_elem), + 1.0/factor, + IsSameF(factor,CalcFactorSHEAD(pe->selem,pe->selem_used),1) ? '#' : ' ', + factor, ntohs(pe->shead.unknown_02) ); + + float prev = 0.0; + uint l; + const pat_s0_selem_t *se = pe->selem; + for ( l = 0; l < pe->selem_used; l++, se++ ) + { + float tiome = bef4(&se->time); + fprintf(F.f,"%13.4f S%-3u %#6x # %9.4f\r\n", + tiome - prev, + ntohs(se->index), + ntohs(se->unknown_06), + tiome ); + prev = tiome; + } + + fputs(" #-----------------------#-----------\r\n",F.f); + } + + + //--- strings + + if (brief_count) + fprintf(F.f, + "%s[STRINGS]\r\n" + "@REVISION = %u\r\n" + "\r\n", + section_sep, + REVISION_NUM ); + else + fprintf(F.f,text_pat_strings_cr,REVISION_NUM); + + fputs( + "#-------------------------------------------\r\n" + "# var attribute name of TEX pattern\r\n" + "#-------------------------------------------\r\n" + ,F.f ); + ParamFieldItem_t *it = pat->s1_str.field; + for ( i = 0; i < pat->s1_str.used; i++, it++ ) + fprintf(F.f," S%-2u %#10x \"%s\"\r\n", i, it->num, it->key ); + + + //--- terminate + + fputs(section_end,F.f); + ResetFile(&F,set_time); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CkeckPAT() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int CheckPAT +( + // returns number of errors + + const pat_t * pat, // pointer to valid PAT + CheckMode_t mode // print mode +) +{ + // [[2do]] [[pat]] ??? + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-pat.h b/project/src/lib-pat.h new file mode 100644 index 0000000..319c777 --- /dev/null +++ b/project/src/lib-pat.h @@ -0,0 +1,441 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_PAT_H +#define SZS_LIB_PAT_H 1 + +#include "lib-std.h" + +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define PAT_MAX_NAME_LEN 20 +#define PAT_MAX_ELEM 20 + +#define PAT_HEAD_OFFSET 0x2c + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// pat_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum pat_mode_t +{ + //--- PAT text output + + PATMD_SIMPLE = 0x0001, // force simple output + PATMD_COMPLEX = 0x0002, // force complex output + PATMD_M_BOTH = PATMD_SIMPLE + | PATMD_COMPLEX, + + //--- hidden options + + PATMD_LOG = 0x00100000, // enable pat trace log to stdout + PATMD_SILENT = 0x00200000, // enable pat trace log to stdout + PATMD_M_HIDDEN = PATMD_LOG + | PATMD_SILENT, + + //--- test modes + + PATMD_TEST0 = 0x00000000, + PATMD_TEST1 = 0x10000000, + PATMD_TEST2 = 0x20000000, + PATMD_TEST3 = 0x30000000, + PATMD_M_TEST = PATMD_TEST3, // all bits! + + + //--- summaries + + PATMD_F_HIDE = 0x40000000, // help flag to hide modes in PrintPatMode() + + PATMD_M_DEFAULT = 0, // default settings + + PATMD_M_ALL = PATMD_M_BOTH // all relevant bits + | PATMD_M_HIDDEN + | PATMD_M_TEST, + + PATMD_M_PRINT = PATMD_M_ALL // all relevant bits for obj output + & ~PATMD_M_HIDDEN, + +} pat_mode_t; + +//----------------------------------------------------------------------------- + +extern pat_mode_t PAT_MODE; +//extern int have_pat_patch_count; + +void LoadParametersPAT +( + ccp log_prefix // not NULL: + // print log message with prefix +); + +void SetPatMode ( uint new_mode ); +int ScanOptPat ( ccp arg ); +pat_mode_t GetRelevantPatMode ( pat_mode_t mode ); +uint PrintPatMode ( char *buf, uint bufsize, pat_mode_t mode ); +ccp GetPatMode(); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct pat_head_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[pat_head_t]] + +typedef struct pat_head_t +{ + /* 0x00 */ u16 unknown_00; // unknown, always 0 in MKW + /* 0x02 */ u16 unknown_02; // unknown, always 0 in MKW + /* 0x04 */ u16 n_frames; // number of frames (2..179) + /* 0x06 */ u16 n_sect0; // number of entries in section 0 (1..2) + /* 0x08 */ u16 n_sect1; // number of strings in section 1 & 3 (2..12) + /* 0x0a */ u16 unknown_0a; // unknown, always 0 in MKW + /* 0x0c */ u16 unknown_0c; // unknown, always 0 in MKW + /* 0x0e */ u16 cyclic; // 0:disabled, 1: enabled (0..1) + /* 0x10 */ // ---END--- + +} +__attribute__ ((packed)) pat_head_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PAT section 0 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[pat_s0_belem_t]] + +typedef struct pat_s0_belem_t +{ + /* 0x00 */ u16 unknown_00; // unknown, 0x19 .. 0x96 + /* 0x02 */ u16 unknown_02; // unknown, always 0 + /* 0x04 */ u16 unknown_04; // unknown, 0..2, >0 only if n_sect0>0 + /* 0x06 */ u16 unknown_06; // unknown, 1..2, >1 only if n_sect0>0 + /* 0x08 */ u32 offset_name; // offset into name table + // relative to 'pat_s0_bhead_t' + /* 0x0c */ u32 offset_strref; // offset to string reference, + // relative to 'pat_s0_bhead_t' + /* 0x10 */ // ---END--- + +} +__attribute__ ((packed)) pat_s0_belem_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[pat_s0_bhead_t]] + +typedef struct pat_s0_bhead_t +{ + /* 0x00 */ u32 size; // total size of this object + /* 0x04 */ u16 unknown_04; // unknown + /* 0x06 */ u16 n_elem; // number of elements of 'elem' + /* 0x08 */ u16 unknown_08; // unknown, always 0xffff in MKW. + /* 0x0a */ u16 unknown_0a; // unknown, always 0 in MKW. + /* 0x0c */ u16 n_unknown; // mostly same as 'n_elem', sometimes smaller + /* 0x0e */ u16 unknown_0e; // unknown, always 0 in MKW. + /* 0x10 */ u16 unknown_10; // unknown, always 0 in MKW. + /* 0x12 */ u16 unknown_12; // unknown, always 0 in MKW. + /* 0x14 */ u16 unknown_14; // unknown, always 0 in MKW. + /* 0x16 */ u16 unknown_16; // unknown, always 0 in MKW. + /* 0x18 */ pat_s0_belem_t elem[0]; // 'n_elem' elements + +} +__attribute__ ((packed)) pat_s0_bhead_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[pat_s0_sref_t]] + +typedef struct pat_s0_sref_t +{ + /* 0x00 */ u32 offset_name; // offset into name table + // relative to this structure + /* 0x04 */ u16 unknown_04; // unknown, always 0, maybe MSB of 'type' + /* 0x06 */ u16 type; // type, always 5 in MKW + /* 0x08 */ u32 offset_strlist; // offset to string list, + // relative to this header + /* 0x0c */ // ---END--- +} +__attribute__ ((packed)) pat_s0_sref_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[pat_s0_selem_t]] + +typedef struct pat_s0_selem_t +{ + /* 0x00 */ float time; // all floats in ascending order + /* 0x04 */ u16 index; // string index + /* 0x06 */ u16 unknown_06; // unknown/padding + /* 0x08 */ // ---END--- +} +__attribute__ ((packed)) pat_s0_selem_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[pat_s0_shead_t]] + +typedef struct pat_s0_shead_t +{ + /* 0x00 */ u16 n_elem; // number of elements of 'elem' + /* 0x02 */ u16 unknown_02; // unknown/padding + /* 0x04 */ float factor; // := 1/MAX(pat_s0_selem_t.time) + /* 0x08 */ pat_s0_selem_t elem[0]; // 'n_elem' elements +} +__attribute__ ((packed)) pat_s0_shead_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct pat_analyse_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[pat_analyse_t]] : analysed PAT data, setup by IsValidPAT + +typedef struct pat_analyse_t +{ + //--- base data + + const u8 *data; // analysed data + uint data_size; // size of data + + //--- statistics + + valid_t valid; // validity status + bool data_complete; // true: data is complete and valid + + uint n_sect0; // number of base elements + uint n_sect1; // number of string ref elements + uint n_s0_list[PAT_MAX_ELEM]; + // number of string list elements + + //--- data structures + + pat_head_t *head; // pointer to header, never NULL if valid + pat_s0_bhead_t *s0_base; // pointer to string ref base, not NULL if valid + pat_s0_sref_t *s0_sref[PAT_MAX_ELEM]; + // pointer to string ref, not NULL if valid + pat_s0_shead_t *s0_shead[PAT_MAX_ELEM]; + // pointer to string list, not NULL if valid + + u8 s0_sref_order[PAT_MAX_ELEM]; // memory order of 's0_sref' elements + u8 s0_shead_order[PAT_MAX_ELEM]; // memory order of 's0_shead' elements + + u32 *s1_list; // NULL or pointer to section 1 list + u32 *s3_list; // NULL or pointer to section 3 list +} +pat_analyse_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct pat_element_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[pat_element_t]] + +typedef struct pat_element_t +{ + pat_s0_belem_t belem; // base element + pat_s0_sref_t sref; // string ref element + pat_s0_shead_t shead; // string list head + + pat_s0_selem_t *selem; // list with string elements + uint selem_used; // number of used elements of 'selem' + uint selem_size; // number of alloced elements of 'selem' + + ccp bname; // base name, alloced + ccp sname; // string ref name, alloced +} +pat_element_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct pat_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[pat_t]] + +typedef struct pat_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // FF_PAT | FF_PAT_TXT + + //--- data + + uint version; // the pat version of the source + uint n_sect; // number of sections + int revision; // tool revision + ccp name; // NULL or alloced name of the object + + #if USE_NEW_CONTAINER_PAT + Container_t container; // data container + #else + DataContainer_t *old_container; // old data container support, init with NULL + #endif + + + //--- PAT data, big endian + + pat_mode_t outmode; // output mode: + // 0 | PATMD_SIMPLE | PATMD_COMPLEX + + pat_head_t head; // copy of pat header + pat_s0_bhead_t base; // PAT base data + pat_element_t pelem[PAT_MAX_ELEM]; // elements + uint n_pelem; // number of used 'pelem' + + u8 sref_order[PAT_MAX_ELEM]; // memory order of 'pelem->sref' elements + u8 shead_order[PAT_MAX_ELEM]; // memory order of 'pelem->shead' elements + u8 elem_to_shead[PAT_MAX_ELEM]; // scanning: asigning indices + + + //--- string support + + ParamField_t s1_str; // strings of section 1 with attribs of s3 + // neither sorted nor unique + + //--- raw data, created by CreateRawPAT() + + u8 * raw_data; // NULL or data + uint raw_data_size; // size of 'raw_data' + +} pat_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializePAT ( pat_t * pat ); +void ResetPAT ( pat_t * pat ); +const VarMap_t * SetupVarsPAT(); + +//----------------------------------------------------------------------------- + +ccp GetStringPAT +( + pat_t * pat, // PAT data structure + const void * base, // base for offset + u32 offset, // offset of string + ccp if_invalid // result if invalid +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerPAT_t * cdata // NULL or container-data +); + +//----------------------------------------------------------------------------- + +enumError ScanTextPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + const void * data, // data to scan + uint data_size // size of 'data' +); + +//----------------------------------------------------------------------------- + +enumError ScanPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + const void * data, // data to scan + uint data_size, // size of 'data' + ContainerPAT_t * cdata, // NULL or container-data + CheckMode_t mode // not NULL: call CheckPAT(mode) +); + +//----------------------------------------------------------------------------- + +struct raw_data_t; + +enumError ScanRawDataPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + struct raw_data_t * raw, // valid raw data + CheckMode_t mode // not NULL: call CheckPAT(mode) +); + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadPAT +( + pat_t * pat, // PAT data structure + bool init_pat, // true: initialize 'pat' first + ccp fname, // valid pointer to filenname + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + CheckMode_t mode // not NULL: call CheckPAT(mode) +); + +//----------------------------------------------------------------------------- + +enumError SaveRawPAT +( + pat_t * pat, // pointer to valid PAT + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +enumError SaveTextPAT +( + pat_t * pat, // pointer to valid PAT + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +/////////////////////////////////////////////////////////////////////////////// + +int CheckPAT +( + // returns number of errors + + const pat_t * pat, // pointer to valid PAT + CheckMode_t mode // print mode +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_PAT_H diff --git a/project/src/lib-rarc.c b/project/src/lib-rarc.c new file mode 100644 index 0000000..a38b4b5 --- /dev/null +++ b/project/src/lib-rarc.c @@ -0,0 +1,271 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-rarc.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IterateFilesRARC() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rarc_info_t +{ + const u8 *data; // begin of file data + uint data_off; // offset of 'data' + + const rarc_header_t *rh; // RARC header + const rarc_node_t *node; // first RARC node + const rarc_entry_t *entry; // first RARC entry + ccp str_pool; // pointer to string pool + ccp str_pool_end; // pointer to string pool end + uint n_node; // total number of nodes + uint n_entry; // total number of entires + +} rarc_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int iterate_rarc_dir +( + szs_iterator_t *it, // iterator struct with all infos + rarc_info_t *ri, // valid rarc info + uint node_idx // node to print +) +{ + DASSERT(it); + DASSERT(it->szs); + DASSERT(ri); + DASSERT(ri->rh); + DASSERT(ri->node); + DASSERT(ri->entry); + + if ( node_idx >= ri->n_node ) + return 0; + + const rarc_node_t *node = ri->node + node_idx; + uint idx = be32(&node->entry_index); + if ( idx >= ri->n_entry ) + return 0; + uint n = be16(&node->n_entry); + if ( n > ri->n_entry - idx ) + n = ri->n_entry - idx; + + char *path = it->trail_path; + char *path_end = it->path + sizeof(it->path) - 4; + const rarc_entry_t *entry = ri->entry + idx; + + int stat; + for ( stat = 0; !stat && n > 0; idx++, n--, entry++ ) + { + it->index = idx; + + ccp fname = ri->str_pool + be16(&entry->name_off); + char *dest = StringCopyE( path, path_end, fname ); + if ( be16(&entry->id) != 0xffff ) + { + // is file + + it->is_dir = 0; + it->off = ri->data_off + be32(&entry->data_off); + it->size = be32(&entry->data_size); + stat = it->func_it(it,false); + } + else if ( !( *fname == '.' && ( !fname[1] || fname[1] == '.' && !fname[2] ))) + { + // is directory + + *dest++ = '/'; + *dest = 0; + it->is_dir = 1; + it->off = 0; + it->size = 0; + stat = it->func_it(it,false); + + char *save_trail_path = it->trail_path; + it->trail_path = dest; + iterate_rarc_dir(it,ri,be32(&entry->data_off)); + it->trail_path = save_trail_path; + } + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesRARC +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size <= sizeof(rarc_file_header_t) ) + return -1; + + //--- analyze file header + + const rarc_file_header_t *fh = (rarc_file_header_t*)szs->data; + const uint file_size = be32(&fh->file_size); + const uint hd_off = be32(&fh->header_off); + const uint hd_size = be32(&fh->header_size); + TRACE("RARC/FH: fsize=%x, header=%x+%x\n",file_size,hd_off,hd_size); + + const u8 *data_beg = szs->data + hd_off + hd_size; + const u8 *data_end = szs->data + szs->size; + + if ( be32(&fh->magic) != RARC_MAGIC_NUM + || file_size > szs->size + || hd_off >= szs->size + || hd_size >= szs->size + || hd_off + hd_size >= szs->size ) + return -1; + + + //--- analyze rarc header + + rarc_info_t ri; + memset(&ri,0,sizeof(ri)); + ri.data = data_beg; + ri.data_off = ri.data - szs->data; + + ri.rh = (rarc_header_t*)(szs->data+hd_off); + ri.node = (rarc_node_t*)( (u8*)ri.rh + be32(&ri.rh->root_off) ); + ri.entry = (rarc_entry_t*)( (u8*)ri.rh + be32(&ri.rh->entry_off) ); + ri.str_pool = (ccp)ri.rh + be32(&ri.rh->str_pool_off); + ri.str_pool_end = ri.str_pool + be32(&ri.rh->str_pool_size); + ri.n_node = be32(&ri.rh->n_node); + ri.n_entry = be32(&ri.rh->n_entry); + + if ( (u8*)ri.node < (u8*)ri.rh+sizeof(*ri.rh) + || (u8*)ri.node + sizeof(*ri.node) > data_end + || (u8*)ri.entry <= (u8*)ri.node + || (u8*)ri.entry + sizeof(*ri.node) * ri.n_entry > data_end ) + return -1; + + #if HAVE_PRINT0 + { + PRINT(">> %6zx header\n", (u8*)ri.rh - szs->data ); + PRINT(">> %6zx %u nodes\n", (u8*)ri.node - szs->data, ri.n_node ); + PRINT(">> %6zx %u entries\n", (u8*)ri.entry - szs->data, ri.n_entry ); + PRINT(">> %6zx string pool\n", (u8*)ri.str_pool - szs->data ); + PRINT(">> %6zx END string pool\n", (u8*)ri.str_pool_end - szs->data ); + PRINT(">> %6zx data\n", (u8*)data_beg - szs->data ); + PRINT(">> %6zx END data\n", (u8*)data_end - szs->data ); + PRINT(">> %6zx END file\n", szs->size ); + + int i; + + putchar('\n'); + const rarc_node_t *node = ri.node; + for ( i = 0; i < ri.n_node; i++, node++ ) + { + printf("N %3d: %.4s %04x : %4u %4u : %s\n", + i, + node->name, be16(&node->unknown_08), + be16(&node->n_entry), be32(&node->entry_index), + ri.str_pool + be16(&node->name_off) ); + } + + putchar('\n'); + const rarc_entry_t *entry = ri.entry; + for ( i = 0; i < ri.n_entry; i++, entry++ ) + { + printf("E %3d: %04x %04x %04x : %8x %6x : %08x : %s\n", + i, + be16(&entry->id), + be16(&entry->unknown_02), be16(&entry->unknown_04), + be32(&entry->data_off), be32(&entry->data_size), + be32(&entry->unknown_10), + ri.str_pool + be16(&entry->name_off) ); + } + } + #endif + + if (it->cut_files) + { + it->index = 0; + it->fst_item = 0; + it->is_dir = 0; + + it->off = 0; + it->size = sizeof(rarc_file_header_t); + StringCopyS(it->path,sizeof(it->path),".RARC.file-header"); + it->func_it(it,false); + + it->off = (u8*)ri.rh - szs->data; + it->size = sizeof(rarc_header_t); + StringCopyS(it->path,sizeof(it->path),".RARC.header"); + it->func_it(it,false); + + it->off = (u8*)ri.node - szs->data; + it->size = sizeof(rarc_node_t) * ri.n_node; + snprintf(it->path,sizeof(it->path),".RARC.%u_node%s", + ri.n_node, ri.n_node == 1 ? "" : "s" ); + it->func_it(it,false); + + it->off = (u8*)ri.entry - szs->data; + it->size = sizeof(rarc_entry_t) * ri.n_entry; + snprintf(it->path,sizeof(it->path),".RARC.%u_entr%s", + ri.n_entry, ri.n_entry == 1 ? "y" : "ies" ); + it->func_it(it,false); + + it->off = (u8*)ri.str_pool - szs->data; + it->size = ri.str_pool_end - ri.str_pool; + StringCopyS(it->path,sizeof(it->path),".RARC.string-pool"); + it->func_it(it,false); + } + + it->trail_path = it->path; + iterate_rarc_dir(it,&ri,0); + it->trail_path = 0; + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-rarc.h b/project/src/lib-rarc.h new file mode 100644 index 0000000..329bf66 --- /dev/null +++ b/project/src/lib-rarc.h @@ -0,0 +1,139 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// all about SZS, YAZ0 and U8 files: +// http://wiki.tockdom.de/index.php?title=SZS +// http://wiki.tockdom.de/index.php?title=YAZ0 +// http://wiki.tockdom.de/index.php?title=U8 + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_RARC_H +#define SZS_LIB_RARC_H 1 + +#include "lib-szs.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// RARC support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define RARC_MAGIC "RARC" +#define RARC_MAGIC_NUM 0x52415243 + +/////////////////////////////////////////////////////////////////////////////// +// http://hitmen.c02.at/files/yagcd/yagcd/chap15.html#sec15.3 + +typedef struct rarc_file_header_t +{ + /* 0x00 */ be32_t magic; // = RARC_MAGIC_NUM + /* 0x04 */ be32_t file_size; // size of file + /* 0x08 */ be32_t header_off; // offset of 'rarc_header_t' + /* 0x0c */ be32_t header_size; // size of header + + /* 0x10 */ be32_t unknown_10; // perhaps padding + /* 0x14 */ be32_t unknown_14; // " + /* 0x18 */ be32_t unknown_18; // " + /* 0x1c */ be32_t unknown_1c; // " +} +__attribute__ ((packed)) rarc_file_header_t; + +//----------------------------------------------------------------------------- + +typedef struct rarc_header_t +{ + /* 0x00 */ be32_t n_node; // number of nodes + /* 0x04 */ be32_t root_off; // offset of root + /* 0x08 */ be32_t n_entry; // total number of entires + /* 0x0c */ be32_t entry_off; // offset of file entries + /* 0x10 */ be32_t str_pool_size; // size of string pool + /* 0x14 */ be32_t str_pool_off; // offset of the string pool + + /* 0x18 */ be16_t unknown_18; // maybe == 'n_entry' + /* 0x1a */ be16_t unknown_1a; + /* 0x1c */ be32_t unknown_1c; +} +__attribute__ ((packed)) rarc_header_t; + +//----------------------------------------------------------------------------- + +typedef struct rarc_node_t +{ + /* 0x00 */ char name[4]; // uppercase of first 4 chars of filename + // 'ROOT' for very first node + /* 0x04 */ be32_t name_off; // offset into string pool + /* 0x08 */ be16_t unknown_08; + /* 0x0a */ be16_t n_entry; // number of entires + /* 0x0c */ be32_t entry_index; // index of first entry +} +__attribute__ ((packed)) rarc_node_t; + +//----------------------------------------------------------------------------- + +typedef struct rarc_entry_t +{ + /* 0x00 */ be16_t id; // file: index of file == entry_number + // dir: 0xffff + /* 0x02 */ be16_t unknown_02; // ? checksum over filename + /* 0x04 */ be16_t unknown_04; // ? file mode + // - 0200 : directories + // - 1100 : plain files + /* 0x06 */ be16_t name_off; // offset into string pool + /* 0x08 */ be32_t data_off; // file: offset of data, + // dir: index of node + /* 0x0c */ be32_t data_size; // file: size of data + /* 0x10 */ be32_t unknown_10; // ? always 0 +} +__attribute__ ((packed)) rarc_entry_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// RARC interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesRARC +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_RARC_H diff --git a/project/src/lib-rkc.c b/project/src/lib-rkc.c new file mode 100644 index 0000000..c7bbb51 --- /dev/null +++ b/project/src/lib-rkc.c @@ -0,0 +1,276 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-rkc.h" +#include "lib-szs.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// RKC action log /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool RKC_ACTION_LOG ( const char * format, ...) + __attribute__ ((__format__(__printf__,1,2))); + +static bool RKC_ACTION_LOG ( const char * format, ...) +{ + #if HAVE_PRINT + { + char buf[200]; + snprintf(buf,sizeof(buf),">>>[RKC]<<< %s",format); + va_list arg; + va_start(arg,format); + PRINT_ARG_FUNC(buf,arg); + va_end(arg); + } + #endif + + if ( verbose > 2 ) // || RKC_MODE & RKCMD_LOG + { + fflush(stdout); + fprintf(stdlog," %s>[RKC]%s ",colset->heading,colset->info); + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + fputs(colset->reset,stdlog); + fflush(stdlog); + return true; + } + + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IterateFilesRKC() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesRKC +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size < sizeof(rkct_t) ) + return -1; + + const rkct_t *rkct = (rkct_t*)szs->data; + if ( ntohl(rkct->magic) != RKCT_MAGIC_NUM ) + return -1; + + it->index = 0; + it->fst_item = 0; + it->is_dir = 0; + + if (it->cut_files) + { + it->no_recurse++; + it->off = 0; + it->size = sizeof(rkct_t); + StringCopyS(it->path,sizeof(it->path),".rkc.header"); + it->func_it(it,false); + it->no_recurse--; + } + + it->off = sizeof(rkct_t); + if ( szs->size < sizeof(rkct_t)+sizeof(rkco_t) ) + { + it->size = szs->size - it->off; + StringCopyS(it->path,sizeof(it->path),".rkc.trash"); + return it->func_it(it,false); + } + + it->size = sizeof(rkco_t); + StringCopyS(it->path,sizeof(it->path),RKCO_FILENAME); + it->func_it(it,false); + + const uint u8_off = ntohl(rkct->u8_off); + const uint u8_end = ntohl(rkct->size); + if ( szs->size < u8_off || szs->size < u8_end ) + { + it->size = szs->size - it->off; + StringCopyS(it->path,sizeof(it->path),".rkc.trash.bin"); + return it->func_it(it,false); + } + + if ( it->cut_files && sizeof(rkct_t)+sizeof(rkco_t) < u8_off ) + { + it->off = sizeof(rkct_t)+sizeof(rkco_t); + it->size = u8_off - it->off; + StringCopyS(it->path,sizeof(it->path),".rkc.pad-head.bin"); + it->func_it(it,false); + } + + it->off = u8_off; + it->size = u8_end - u8_off; + StringCopyS(it->path,sizeof(it->path),RKC_U8_FILENAME); + int stat = it->func_it(it,false); + if (stat) + return stat; + + if ( !it->no_recurse && it->recurse_level < it->recurse_max ) + { + szs_file_t szs2; +// [[fname+]] + InitializeSubSZS(&szs2,szs,u8_off,u8_end-u8_off,FF_UNKNOWN,it->path,true); + + szs_iterator_func it_func + = GetIteratorFunction(szs2.fform_arch,it->cut_files); + + if (it_func) + { + szs_iterator_t it2; + memcpy(&it2,it,sizeof(it2)); + it2.szs = &szs2; + it2.index = 0; + it2.client_int = 0; + it2.parent_it = it; + it2.recurse_level++; + + const int stat2 = it_func(&it2,false); + it2.name = 0; + *it2.path = 0; + if ( stat2 != -1 ) + it2.func_it(&it2,true); // ignore status + } + ResetSZS(&szs2); + } + + if ( szs->size > u8_end ) + { + it->off = u8_end; + it->size = szs->size - u8_end; + StringCopyS(it->path,sizeof(it->path),".rkc.pad-szs.bin"); + it->func_it(it,false); + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CreateRKC() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint get_file_size ( ccp source_dir, ccp fname, uint min, uint max ) +{ + s64 szs_size = GetFileSize(source_dir,fname,-1,0,false); + if ( szs_size < 0 ) + { + ERROR1(ERR_CANT_OPEN, + "Can't open file: %s/%s",source_dir,fname); + return 0; + } + + if ( szs_size < min || szs_size > max ) + { + ERROR0(ERR_INVALID_DATA, + "Wrong filesize (not %u..%u, but %llu): %s/%s", + min, max, szs_size, source_dir, fname ); + return 0; + } + + return szs_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateRKC +( + struct szs_file_t *szs, // valid szs + ccp source_dir // NULL or path to source dir +) +{ + PRINT("CreateRKC(%s) size=%zu, data=%p\n", + source_dir, szs->size, szs->data ); + + + //--- check files + + const uint rkco_size + = get_file_size( source_dir, RKCO_FILENAME, + sizeof(rkco_t), RKC_U8_OFFSET-sizeof(rkct_t) ); + const uint szs_size + = get_file_size( source_dir, RKC_U8_FILENAME, + sizeof(yaz0_header_t), RKC_U8_LIMIT ); + if ( !rkco_size || !szs_size ) + return ERR_INVALID_DATA; + + + //--- create szs + + szs->size = RKC_U8_OFFSET + szs_size; + szs->data = CALLOC(szs->size,1); + + ClearFileAttrib(&szs->fatt); + + enumError err; + err = LoadFile( source_dir, RKCO_FILENAME, 0, + szs->data + sizeof(rkct_t), rkco_size, 0, + &szs->fatt, true ); + if (err) + return err; + + err = LoadFile( source_dir, RKC_U8_FILENAME, 0, + szs->data + RKC_U8_OFFSET, szs_size, 0, + &szs->fatt, true ); + if (err) + return err; + + rkct_t *rkct = (rkct_t*)szs->data; + rkct->magic = htonl(RKCT_MAGIC_NUM); + rkct->size = htonl(szs->size); + rkct->u8_off = htonl(RKC_U8_OFFSET); + rkct->version = htonl(RKC_VERSION); + + szs->fform_arch = szs->fform_file = szs->fform_current = FF_RKC; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-rkc.h b/project/src/lib-rkc.h new file mode 100644 index 0000000..414141a --- /dev/null +++ b/project/src/lib-rkc.h @@ -0,0 +1,117 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_RKC_H +#define SZS_LIB_RKC_H 1 + +#include "lib-std.h" + +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define RKCT_MAGIC "RKCT" +#define RKCT_MAGIC_NUM 0x524b4354 + +#define RKCO_MAGIC "RKCO" +#define RKCO_MAGIC_NUM 0x524b434f +#define RKCO_FILENAME "rkco.bin" + +#define RKC_VERSION 0x640 +#define RKC_U8_OFFSET 0x50 +#define RKC_U8_LIMIT 0x5000 +#define RKC_U8_FILENAME "rkc.szs" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct rkct_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rkco_t +{ + /*00*/ u32 magic; // always RKCO_MAGIC_NUM + /*04*/ u8 xdata[0x40-4]; // misc data +} +__attribute__ ((packed)) rkco_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct rkct_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rkct_t +{ + /*00*/ u32 magic; // always RKCT_MAGIC_NUM + /*04*/ u32 size; // total RKC size + /*08*/ u32 u8_off; // Offset uf U8-archice (YAZ0 compressed) + // usually RKC_U8_OFFSET + /*0c*/ u32 version; // always 0x640 (RKC_VERSION) + /*ff*/ rkco_t rkco[0]; +} +__attribute__ ((packed)) rkct_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct szs_iterator_t; + +int IterateFilesRKC +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +//----------------------------------------------------------------------------- + +struct szs_file_t; + +enumError CreateRKC +( + struct szs_file_t *szs, // valid szs + ccp source_dir // NULL or path to source dir +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_RKC_H diff --git a/project/src/lib-rkg.c b/project/src/lib-rkg.c new file mode 100644 index 0000000..9d39c74 --- /dev/null +++ b/project/src/lib-rkg.c @@ -0,0 +1,153 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +//#include +//#include +//#include +//#include +//#include + +#include "lib-rkg.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanRawDataGHOST() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u32 ScanTimeRKG ( const u8 * data ) +{ + DASSERT(data); + const u32 min = data[0] >> 1; + const u32 sec = be16(data) >> 2 & 0x7f; + return ( be16(data+1) & 0x3ff ) + 1000 * sec + 60000 * min; +} + +//----------------------------------------------------------------------------- + +static uint CalcFrameTime ( const u8 ** pptr, uint n ) +{ + const u8 *ptr = *pptr; + uint frames = 0; + + while ( n-- > 0 ) + { + ptr++; // skip data field + frames += *ptr++; // add frame count + } + *pptr = ptr; + + // subtract 4 sec (0xfd frames) for countdown, then convert to msec + frames = frames < 0xf0 ? 0 : frames - 0xf0; + return frames * 100000 / 5994; +} + +//----------------------------------------------------------------------------- + +enumError ScanRawDataGHOST +( + rkg_info_t * ri, // RKG info + bool init_ri, // true: initialize 'ri' first + struct raw_data_t * raw // valid raw data +) +{ + DASSERT(ri); + DASSERT(raw); + + if (init_ri) + InitializeRKGInfo(ri); + else + ResetRKGInfo(ri); + + if ( raw->data_size < sizeof(rkg_head_t) ) + { + ri->error = "too small"; + return ERR_WARNING; + } + + rkg_head_t *rh = (rkg_head_t*)raw->data; + if ( memcmp(&rh->magic,RKG_MAGIC,4) ) + { + ri->error = "wrong magic"; + return ERR_WARNING; + } + + ri->score = ScanTimeRKG(rh->score); + ri->is_compressed = ( ntohs(rh->type) & 0x0800 ) != 0; + ri->data_size = ntohs(rh->data_size); + + const u8 * data = raw->data + sizeof(*rh); + uint alloced_size = 0; + if (ri->is_compressed) + { + uint alloced_size = ri->data_size; + u8 *dest = CALLOC(alloced_size,1); + size_t written, csize = be32(data); + + enumError err = DecompressYAZ( data+0x14, csize, + dest, alloced_size, &written, raw->fname, 1, false, 0 ); + if (err) + { + FREE(dest); + ri->error = "decompression failed"; + return false; + } + + data = dest; + ri->data_size = written; + if ( verbose > 1 ) + HexDump16(stdout,0,0,data,ri->data_size); + } + + ri->n_but = be16(data); + ri->n_dir = be16(data+2); + ri->n_trick = be16(data+4); + + const u8 *ptr = data+8; + ri->time_but = CalcFrameTime(&ptr,ri->n_but); + ri->time_dir = CalcFrameTime(&ptr,ri->n_dir); + ri->time_trick = CalcFrameTime(&ptr,ri->n_trick); + + if (alloced_size) + FREE((char*)data); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-rkg.h b/project/src/lib-rkg.h new file mode 100644 index 0000000..0f4de23 --- /dev/null +++ b/project/src/lib-rkg.h @@ -0,0 +1,120 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_RKD_H +#define SZS_LIB_RKD_H 1 + +//#include "lib-std.h" +#include "lib-szs.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define RKG_MAGIC "RKGD" +#define RKG_MAGIC_NUM 0x524b4744 + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct rkg_head_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[rkg_head_t]] +// http://wiki.tockdom.com/wiki/RKG + +typedef struct rkg_head_t +{ + /* 0x00 */ u32 magic; // RKG_MAGIC_NUM + /* 0x04 */ u8 score[3]; // 7:min 7:sec 10:milli + /* 0x07 */ u8 track_id; + /* 0x08 */ u32 id; // different IDs + /* 0x0c */ u16 type; + /* 0x0e */ u16 data_size; // decompressed data size + /* 0x10 */ u8 any_data[0x88-0x10]; + /* 0xxx */ + /* 0x88 */ +} +__attribute__ ((packed)) rkg_head_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct rkg_info_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[rkg_info_t]] + +typedef struct rkg_info_t +{ + bool valid; // true: data is valid + ccp error; // not NULL: error message + u32 score; // time in msec + + uint data_size; // input data size + bool is_compressed; // true: data is compressed + + uint n_but; + uint n_dir; + uint n_trick; + + uint time_but; + uint time_dir; + uint time_trick; +} +rkg_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeRKGInfo ( rkg_info_t *ri ) + { DASSERT(ri); memset(ri,0,sizeof(*ri)); } + +static inline void ResetRKGInfo ( rkg_info_t *ri ) + { DASSERT(ri); memset(ri,0,sizeof(*ri)); } + +enumError ScanRawDataGHOST +( + rkg_info_t * ri, // RKG info + bool init_ri, // true: initialize 'ri' first + struct raw_data_t * raw // valid raw data +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_MDL_H diff --git a/project/src/lib-staticr.c b/project/src/lib-staticr.c new file mode 100644 index 0000000..2125714 --- /dev/null +++ b/project/src/lib-staticr.c @@ -0,0 +1,6886 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-staticr.h" +#include "crypt.h" +#include "db-dol.h" +#include "db-mkw.h" +#include "lib-mkw.h" +#include "db-ctcode.h" + +#include "libbz2/bzlib.h" +#include "lib-bzip2.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data: tracks + arenas /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u32 pal_track_offset[] = +{ + 0x37fcd0, + // 0x380ee0, // original has an alternative order here + 0x384f58, + 0x397da8, + 0x398130, + 0x39d040, + 0 +}; + +static const u32 pal_arena_offset[] = +{ + 0x384fd8, + 0x3974f8, + 0x397780, + 0x39c8e0, + 0 +}; + +//----------------------------------------------------------------------------- + +static const u32 usa_track_offset[] = +{ + 0x37fbe0, + // 0x380b70, // original has an alternative order here + 0x3839e0, + 0x397450, + 0x3977d8, + 0x39b300, + 0 +}; + +static const u32 usa_arena_offset[] = +{ + 0x383a60, + 0x39d130, + 0x39d3b8, + 0x39e988, + 0 +}; + +//----------------------------------------------------------------------------- + +static const u32 jap_track_offset[] = +{ + 0x37f9a0, + // 0x380bb0, // original has an alternative order here + 0x384c28, + 0x397588, + 0x397910, + 0x39c820, + 0 +}; + +static const u32 jap_arena_offset[] = +{ + 0x384ca8, + 0x396cd8, + 0x396f60, + 0x39c0c0, + 0 +}; + +//----------------------------------------------------------------------------- + +static const u32 kor_track_offset[] = +{ + 0x3800b0, + // 0x3812c8, // original has an alternative order here + 0x385348, + 0x3981e8, + 0x398570, + 0x39d480, + 0 +}; + +static const u32 kor_arena_offset[] = +{ + 0x3853c8, + 0x397938, + 0x397bc0, + 0x39cd20, + 0 +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data: region /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum rpat_mode_t // bit field +{ + RPAT_FF = 1, // mask value by 0xff + RPAT_TEST = 2, // test mode, only patched on 'opt_x' +} +rpat_mode_t; + +typedef struct region_patch_t +{ + rpat_mode_t patch_mode; // bit field, see above + u32 offset[STR_ZBI_N]; // offsets for all 4 DVD types + u32 orig; // original value + u32 patch; // patch base value + ccp info; // info about impact +} +region_patch_t; + +static const region_patch_t vs_region_patch[] = +{ + {0,{0x143364,0x14325c,0x143050,0x14365c}, 0x80a30084,0x38a00000, "vs: locstring status" }, + {1,{0x149d54,0x149c4c,0x149a40,0x14a04c}, 0x80840084,0x38800000, "vs: check locstring, masked by 0xff" }, + {0,{0x148f2c,0x148e24,0x148c18,0x149224}, 0x80e50084,0x38e00000, "'vs_#' at regional matches." }, + {0,{0x149444,0x14933c,0x149130,0x14973c}, 0x80e50084,0x38e00000, "'vs_#' after friend connect" }, + {0,{0,0,0,0},0,0,0} +}; + +static const region_patch_t bt_region_patch[] = +{ + {0,{0x1433d0,0x1432c8,0x1430bc,0x1436c8}, 0x80c30084,0x38c00000, "bt: locstring status" }, + {1,{0x149da0,0x149c98,0x149a8c,0x14a098}, 0x80840084,0x38800000, "bt: check locstring, masked by 0xff" }, + {0,{0x148f80,0x148e78,0x148c6c,0x149278}, 0x80e50084,0x38e00000, "'bt_#' at regional matches." }, + {0,{0x149498,0x149390,0x149184,0x149790}, 0x80e50084,0x38e00000, "'bt_#' after friend connect" }, + {0,{0,0,0,0},0,0,0} +}; + +enum +{ + N_TAB_VS_REGION_PATCH = sizeof(vs_region_patch) / sizeof(*vs_region_patch) - 1, + N_VS_REGION_PATCH = N_TAB_VS_REGION_PATCH < MAX_REGION_PATCH + ? N_TAB_VS_REGION_PATCH : MAX_REGION_PATCH, + + N_TAB_BT_REGION_PATCH = sizeof(bt_region_patch) / sizeof(*bt_region_patch) - 1, + N_BT_REGION_PATCH = N_TAB_BT_REGION_PATCH < MAX_REGION_PATCH + ? N_TAB_BT_REGION_PATCH : MAX_REGION_PATCH, +}; + +//----------------------------------------------------------------------------- + +typedef struct region_info_t +{ + int * region; // vector with regions, -1=orig + const + region_patch_t * patch_tab; // patch table + uint max_patch; // N_(VS|BT)_REGION_PATCH + uint flag_known; // STR_S_(VS|BT)_REGION_KNOWN + uint flag_differ; // STR_S_(VS|BT)_REGION_KNOWN_DIFF + uint flag_unknown; // STR_S_(VS|BT)_REGION_UNKNOWN + ccp name; // "versus" or "battle" + + int *opt_region; // pointer to option + bool *opt_x; // pointer to x-mode + bool *opt_t; // pointer to t-mode +} +region_info_t; + +void SetupRegionInfo ( region_info_t *ri, staticr_t * str, bool is_battle ) +{ + DASSERT(ri); + + if (is_battle) + { + ri->region = str ? str->bt_region : 0; + ri->patch_tab = bt_region_patch; + ri->max_patch = N_BT_REGION_PATCH; + ri->flag_known = STR_S_BT_REGION_KNOWN; + ri->flag_differ = STR_S_BT_REGION_KNOWN_DIFF; + ri->flag_unknown= STR_S_BT_REGION_UNKNOWN; + ri->name = "battle"; + + ri->opt_region = &opt_str_bt_region; + ri->opt_x = &opt_str_xbt_region; + ri->opt_t = &opt_str_tbt_region; + } + else + { + ri->region = str ? str->vs_region : 0; + ri->patch_tab = vs_region_patch; + ri->max_patch = N_VS_REGION_PATCH; + ri->flag_known = STR_S_VS_REGION_KNOWN; + ri->flag_differ = STR_S_VS_REGION_KNOWN_DIFF; + ri->flag_unknown= STR_S_VS_REGION_UNKNOWN; + ri->name = "versus"; + + ri->opt_region = &opt_str_vs_region; + ri->opt_x = &opt_str_xvs_region; + ri->opt_t = &opt_str_tvs_region; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static char all_ranks_orig[] = "%s >= %d and %s <= %d"; +static char all_ranks_patch1[] = "(%s > %d or %s <= %d)"; +static char all_ranks_patch2[] = "(%s >= %d or %s < %d)"; +static int all_ranks_size = sizeof(all_ranks_orig); + +static const u32 pal_all_ranks_offset[] = +{ + 0x389f36, + 0x389f52, + 0 +}; + +static const u32 usa_all_ranks_offset[] = +{ + 0x389be6, + 0x389c02, + 0 +}; + +static const u32 jap_all_ranks_offset[] = +{ + 0x389716, + 0x389732, + 0 +}; + +static const u32 kor_all_ranks_offset[] = +{ + 0x38a34e, + 0x38a36a, + 0 +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data: vs/bat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u32 pal_vs_offset[] = +{ + 0x389f1b, + 0x389f24, + 0x389f9e, + 0x389fa7, + 0 +}; + +static const u32 usa_vs_offset[] = +{ + 0x389bcb, + 0x389bd4, + 0x389c4e, + 0x389c57, + 0 +}; + +static const u32 jap_vs_offset[] = +{ + 0x3896fb, + 0x389704, + 0x38977e, + 0x389787, + 0 +}; + +static const u32 kor_vs_offset[] = +{ + 0x38a333, + 0x38a33c, + 0x38a3b6, + 0x38a3bf, + 0 +}; + +//----------------------------------------------------------------------------- + +static const u32 pal_bt_offset[] = +{ + 0x389f2a, + 0x389f33, + 0x389fad, + 0x389fb6, + 0 +}; + +static const u32 usa_bt_offset[] = +{ + 0x389bda, + 0x389be3, + 0x389c5d, + 0x389c66, + 0 +}; + +static const u32 jap_bt_offset[] = +{ + 0x38970a, + 0x389713, + 0x38978d, + 0x389796, + 0 +}; + +static const u32 kor_bt_offset[] = +{ + 0x38a342, + 0x38a34b, + 0x38a3c5, + 0x38a3ce, + 0 +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data: cannons /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define CANNON_N 3 +#define CANNON_SIZE 16 +#define CANNON_N_PARAM 4 + +typedef struct cannon_param_t +{ + bool valid; // TRUE: params are valid + float param[CANNON_N_PARAM]; // parameters +} +cannon_param_t; + +static cannon_param_t cannon_param[CANNON_N] = {{0}}; + +//----------------------------------------------------------------------------- + +static u32 GetCannonOffset ( const staticr_t * str ) +{ + ASSERT( sizeof(CannonDataLEX)-4 == sizeof(str->cannon_data) ); + ASSERT( sizeof(CannonDataLEX)-4 == CANNON_SIZE * CANNON_N ); + + if ( str && str->is_staticr ) + { + switch (str->mode) + { + case STR_M_PAL: return 0x3a5a08; + case STR_M_USA: return 0x3a54c8; + case STR_M_JAP: return 0x3a51e8; + case STR_M_KOR: return 0x3a5e60; + default: return 0; + } + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data: /Scene/UI/MenuSingle /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u32 pal_menu_offset[] = +{ + 0x387b64, // /Scene/UI/MenuSingle + 0x387b9d, // /Scene/UI/MenuSingle + 0x387bea, // /Scene/UI/MenuSingle + 0x387c48, // /Scene/UI/MenuSingle + 0x387cd9, // /Scene/UI/MenuSingle + + 0x387b79, // /Scene/UI/MenuMulti + 0x387bb2, // /Scene/UI/MenuMulti + 0x387bd6, // /Scene/UI/MenuMulti + 0x387bff, // /Scene/UI/MenuMulti + + 0 +}; + +static const u32 usa_menu_offset[] = +{ + 0x38513c, // /Scene/UI/MenuSingle + 0x385175, // /Scene/UI/MenuSingle + 0x3851c2, // /Scene/UI/MenuSingle + 0x385220, // /Scene/UI/MenuSingle + 0x3852b1, // /Scene/UI/MenuSingle + + 0x385151, // /Scene/UI/MenuMulti + 0x38518a, // /Scene/UI/MenuMulti + 0x3851ae, // /Scene/UI/MenuMulti + 0x3851d7, // /Scene/UI/MenuMulti + + 0 +}; + +static const u32 jap_menu_offset[] = +{ + 0x387834, // /Scene/UI/MenuSingle + 0x38786d, // /Scene/UI/MenuSingle + 0x3878ba, // /Scene/UI/MenuSingle + 0x387918, // /Scene/UI/MenuSingle + 0x3879a9, // /Scene/UI/MenuSingle + + 0x387849, // /Scene/UI/MenuMulti + 0x387882, // /Scene/UI/MenuMulti + 0x3878a6, // /Scene/UI/MenuMulti + 0x3878cf, // /Scene/UI/MenuMulti + + 0 +}; + +static const u32 kor_menu_offset[] = +{ + 0x38846c, // /Scene/UI/MenuSingle + 0x3884a5, // /Scene/UI/MenuSingle + 0x3884f2, // /Scene/UI/MenuSingle + 0x388550, // /Scene/UI/MenuSingle + 0x3885e1, // /Scene/UI/MenuSingle + + 0x388481, // /Scene/UI/MenuMulti + 0x3884ba, // /Scene/UI/MenuMulti + 0x3884de, // /Scene/UI/MenuMulti + 0x388507, // /Scene/UI/MenuMulti + + 0 +}; + +static const int meno_patch_idx[] = +{ + 1, // Single: Global VS + Battle + 2, // Single: Room + 7, // Multi: Global VS + Battle + 8, // Multi: Room + -1 +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data: servers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const str_server_list_t ServerList[SERV__N+1] = +{ + { SERV_NONE, "NONE", 0, 0, "" }, + { SERV_NASW, "NASW", 90, 23, "naswii.nintendowifi.net" }, + { SERV_NASW_DEV, "NASW_DEV", 91, 27, "naswii.dev.nintendowifi.net" }, + { SERV_NASW_TEST, "NASW_TEST", 92, 28, "naswii.test.nintendowifi.net" }, + { SERV_MKW_RACE, "MKW_RACE", 93, 37, "mariokartwii.race.gs.nintendowifi.net" }, + { SERV_SAKE, "SAKE", 0, 37, "mariokartwii.sake.gs.nintendowifi.net" }, + { SERV_S_SAKE, "S_SAKE", 0, 27, "%s.sake.gs.nintendowifi.net" }, + { SERV_GS, "GS", 0, 19, "gs.nintendowifi.net" }, + {0,0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +const str_server_patch_t ServerPatch[217+1] = +{ + { STR_M_PAL,0, 2,SERV_NONE, 0x38aa32, 5, "" }, + { STR_M_PAL,0, 2,SERV_NONE, 0x38ab4c, 5, "" }, + { STR_M_PAL,2, 2,SERV_MKW_RACE, 0x38a38c, 75, "/raceservice/maindl_%s_%s.ashx" }, + { STR_M_PAL,2, 2,SERV_MKW_RACE, 0x38a458, 78, "/raceservice/messagedl_%s_%s.ashx" }, + { STR_M_PAL,0, 2,SERV_MKW_RACE, 0x38a8f0, 84, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 1,SERV_MKW_RACE, 0x38a945, 83, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 2,SERV_SAKE, 0x38a3d8, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687®ion=0" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38a9bc, 117, "/SakeFileServer/ghostupload.aspx?gameid=%d®ionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38aaef, 92, "/SakeFileServer/ghostdownload.aspx?gameid=1687®ion=0&p0=%d&c0=%d&t0=%d" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38ac9c, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38ae05, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38af33, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38b014, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38b109, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38b21e, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_PAL,0, 0,SERV_GS, 0x38b330, 58, "/RaceService/NintendoRacingService.asmx" }, + + { STR_M_PAL,9, 2,SERV_NONE, 0x2797f8, 8, "" }, // "https://" + { STR_M_PAL,9, 2,SERV_NONE, 0x279808, 8, "" }, // "https://" + { STR_M_PAL,9, 2,SERV_NONE, 0x279a78, 8, "" }, // "https://" + { STR_M_PAL,9, 2,SERV_NONE, 0x29d568, 8, "" }, // "https://" + { STR_M_PAL,9, 2,SERV_NONE, 0x29e388, 8, "" }, // "https://" + + { STR_M_PAL,5, 2,SERV_NASW, 0x276528, 34, "/ac" }, + { STR_M_PAL,1, 2,SERV_NASW, 0x2765a8, 34, "/pr" }, + { STR_M_PAL,5, 2,SERV_NASW_DEV, 0x27654c, 38, "/ac" }, + { STR_M_PAL,1, 2,SERV_NASW_DEV, 0x2765cc, 38, "/pr" }, + { STR_M_PAL,5, 2,SERV_NASW_TEST, 0x276500, 39, "/ac" }, + { STR_M_PAL,1, 2,SERV_NASW_TEST, 0x276580, 39, "/pr" }, + { STR_M_PAL,1, 2,SERV_S_SAKE, 0x275e58, 72, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_PAL,1, 2,SERV_S_SAKE, 0x275ea4, 62, "/SakeFileServer/upload.aspx" }, + { STR_M_PAL,1, 2,SERV_S_SAKE, 0x275ee4, 64, "/SakeFileServer/download.aspx" }, + { STR_M_PAL,1, 1,SERV_S_SAKE, 0x27a1a0, 71, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_PAL,1, 0,SERV_GS, 0x2718be, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x272635, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x272651, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x272672, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x272693, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x2726b5, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x2726d7, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x2726f7, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x272717, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x272736, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x272759, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x27277e, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x27279c, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x276e65, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x2775d5, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x277fed, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x27918e, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x279b82, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x279da8, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x279dc4, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x279de0, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x279f30, 19, "" }, + { STR_M_PAL,1, 0,SERV_GS, 0x279fb4, 19, "" }, + + { STR_M_USA,0, 2,SERV_NONE, 0x38a3ba, 5, "" }, + { STR_M_USA,0, 2,SERV_NONE, 0x38a4d4, 5, "" }, + { STR_M_USA,2, 2,SERV_MKW_RACE, 0x389d14, 75, "/raceservice/maindl_%s_%s.ashx" }, + { STR_M_USA,2, 2,SERV_MKW_RACE, 0x389de0, 78, "/raceservice/messagedl_%s_%s.ashx" }, + { STR_M_USA,0, 2,SERV_MKW_RACE, 0x38a278, 84, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 1,SERV_MKW_RACE, 0x38a2cd, 83, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 2,SERV_SAKE, 0x389d60, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687®ion=0" }, + { STR_M_USA,0, 0,SERV_GS, 0x38a344, 117, "/SakeFileServer/ghostupload.aspx?gameid=%d®ionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" }, + { STR_M_USA,0, 0,SERV_GS, 0x38a477, 92, "/SakeFileServer/ghostdownload.aspx?gameid=1687®ion=0&p0=%d&c0=%d&t0=%d" }, + { STR_M_USA,0, 0,SERV_GS, 0x38a604, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 0,SERV_GS, 0x38a76d, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 0,SERV_GS, 0x38a89b, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 0,SERV_GS, 0x38a97c, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 0,SERV_GS, 0x38aa71, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 0,SERV_GS, 0x38ab86, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_USA,0, 0,SERV_GS, 0x38ac98, 58, "/RaceService/NintendoRacingService.asmx" }, + + { STR_M_USA,9, 2,SERV_NONE, 0x275498, 8, "" }, // "https://" + { STR_M_USA,9, 2,SERV_NONE, 0x2754a8, 8, "" }, // "https://" + { STR_M_USA,9, 2,SERV_NONE, 0x275718, 8, "" }, // "https://" + { STR_M_USA,9, 2,SERV_NONE, 0x299208, 8, "" }, // "https://" + { STR_M_USA,9, 2,SERV_NONE, 0x29a028, 8, "" }, // "https://" + + { STR_M_USA,5, 2,SERV_NASW, 0x2721c8, 34, "/ac" }, + { STR_M_USA,1, 2,SERV_NASW, 0x272248, 34, "/pr" }, + { STR_M_USA,5, 2,SERV_NASW_DEV, 0x2721ec, 38, "/ac" }, + { STR_M_USA,1, 2,SERV_NASW_DEV, 0x27226c, 38, "/pr" }, + { STR_M_USA,5, 2,SERV_NASW_TEST, 0x2721a0, 39, "/ac" }, + { STR_M_USA,1, 2,SERV_NASW_TEST, 0x272220, 39, "/pr" }, + { STR_M_USA,1, 2,SERV_S_SAKE, 0x271af8, 72, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_USA,1, 2,SERV_S_SAKE, 0x271b44, 62, "/SakeFileServer/upload.aspx" }, + { STR_M_USA,1, 2,SERV_S_SAKE, 0x271b84, 64, "/SakeFileServer/download.aspx" }, + { STR_M_USA,1, 1,SERV_S_SAKE, 0x275e40, 71, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_USA,1, 0,SERV_GS, 0x26d55e, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e2d5, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e2f1, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e312, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e333, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e355, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e377, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e397, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e3b7, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e3d6, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e3f9, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e41e, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x26e43c, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x272b05, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x273275, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x273c8d, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x274e2e, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x275822, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x275a48, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x275a64, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x275a80, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x275bd0, 19, "" }, + { STR_M_USA,1, 0,SERV_GS, 0x275c54, 19, "" }, + + { STR_M_JAP,0, 2,SERV_NONE, 0x38a212, 5, "" }, + { STR_M_JAP,0, 2,SERV_NONE, 0x38a32c, 5, "" }, + { STR_M_JAP,2, 2,SERV_MKW_RACE, 0x389b6c, 75, "/raceservice/maindl_%s_%s.ashx" }, + { STR_M_JAP,2, 2,SERV_MKW_RACE, 0x389c38, 78, "/raceservice/messagedl_%s_%s.ashx" }, + { STR_M_JAP,0, 2,SERV_MKW_RACE, 0x38a0d0, 84, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 1,SERV_MKW_RACE, 0x38a125, 83, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 2,SERV_SAKE, 0x389bb8, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687®ion=0" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a19c, 117, "/SakeFileServer/ghostupload.aspx?gameid=%d®ionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a2cf, 92, "/SakeFileServer/ghostdownload.aspx?gameid=1687®ion=0&p0=%d&c0=%d&t0=%d" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a47c, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a5e5, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a713, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a7f4, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a8e9, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38a9fe, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_JAP,0, 0,SERV_GS, 0x38ab10, 58, "/RaceService/NintendoRacingService.asmx" }, + + { STR_M_JAP,9, 2,SERV_NONE, 0x279178, 8, "" }, // "https://" + { STR_M_JAP,9, 2,SERV_NONE, 0x279188, 8, "" }, // "https://" + { STR_M_JAP,9, 2,SERV_NONE, 0x2793f8, 8, "" }, // "https://" + { STR_M_JAP,9, 2,SERV_NONE, 0x29cee8, 8, "" }, // "https://" + { STR_M_JAP,9, 2,SERV_NONE, 0x29dd08, 8, "" }, // "https://" + + { STR_M_JAP,5, 2,SERV_NASW, 0x275ea8, 34, "/ac" }, + { STR_M_JAP,1, 2,SERV_NASW, 0x275f28, 34, "/pr" }, + { STR_M_JAP,5, 2,SERV_NASW_DEV, 0x275ecc, 38, "/ac" }, + { STR_M_JAP,1, 2,SERV_NASW_DEV, 0x275f4c, 38, "/pr" }, + { STR_M_JAP,5, 2,SERV_NASW_TEST, 0x275e80, 39, "/ac" }, + { STR_M_JAP,1, 2,SERV_NASW_TEST, 0x275f00, 39, "/pr" }, + { STR_M_JAP,1, 2,SERV_S_SAKE, 0x2757d8, 72, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_JAP,1, 2,SERV_S_SAKE, 0x275824, 62, "/SakeFileServer/upload.aspx" }, + { STR_M_JAP,1, 2,SERV_S_SAKE, 0x275864, 64, "/SakeFileServer/download.aspx" }, + { STR_M_JAP,1, 1,SERV_S_SAKE, 0x279b20, 71, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_JAP,1, 0,SERV_GS, 0x27123e, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x271fb5, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x271fd1, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x271ff2, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x272013, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x272035, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x272057, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x272077, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x272097, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x2720b6, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x2720d9, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x2720fe, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x27211c, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x2767e5, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x276f55, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x27796d, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x278b0e, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x279502, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x279728, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x279744, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x279760, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x2798b0, 19, "" }, + { STR_M_JAP,1, 0,SERV_GS, 0x279934, 19, "" }, + + { STR_M_KOR,2, 2,SERV_MKW_RACE, 0x38a7a4, 75, "/raceservice/maindl_%s_%s.ashx" }, + { STR_M_KOR,2, 2,SERV_MKW_RACE, 0x38a858, 78, "/raceservice/messagedl_%s_%s.ashx" }, + { STR_M_KOR,0, 2,SERV_MKW_RACE, 0x38ad10, 84, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 2,SERV_MKW_RACE, 0x38ad65, 89, "/RaceServiceStage/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 2,SERV_SAKE, 0x38a7f0, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687®ion=0" }, + { STR_M_KOR,0, 2,SERV_SAKE, 0x38adcb, 131, "/%s/ghostupload.aspx?gameid=%d®ionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" }, + { STR_M_KOR,0, 2,SERV_SAKE, 0x38af0b, 106, "/%s/ghostdownload.aspx?gameid=1687®ion=0&p0=%d&c0=%d&t0=%d" }, + { STR_M_KOR,0, 0,SERV_GS, 0x38b0dc, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 0,SERV_GS, 0x38b245, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 0,SERV_GS, 0x38b373, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 0,SERV_GS, 0x38b454, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 0,SERV_GS, 0x38b549, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 0,SERV_GS, 0x38b65e, 58, "/RaceService/NintendoRacingService.asmx" }, + { STR_M_KOR,0, 0,SERV_GS, 0x38b770, 58, "/RaceService/NintendoRacingService.asmx" }, + + { STR_M_KOR,9, 2,SERV_NONE, 0x2676a8, 8, "" }, // "https://" + { STR_M_KOR,9, 2,SERV_NONE, 0x2676b8, 8, "" }, // "https://" + { STR_M_KOR,9, 2,SERV_NONE, 0x267928, 8, "" }, // "https://" + { STR_M_KOR,9, 2,SERV_NONE, 0x28b570, 8, "" }, // "https://" + { STR_M_KOR,9, 2,SERV_NONE, 0x28c390, 8, "" }, // "https://" + + { STR_M_KOR,5, 2,SERV_NASW, 0x2643d8, 34, "/ac" }, + { STR_M_KOR,1, 2,SERV_NASW, 0x264458, 34, "/pr" }, + { STR_M_KOR,5, 2,SERV_NASW_DEV, 0x2643fc, 38, "/ac" }, + { STR_M_KOR,1, 2,SERV_NASW_DEV, 0x26447c, 38, "/pr" }, + { STR_M_KOR,5, 2,SERV_NASW_TEST, 0x2643b0, 39, "/ac" }, + { STR_M_KOR,1, 2,SERV_NASW_TEST, 0x264430, 39, "/pr" }, + { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263c38, 72, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263c84, 62, "/SakeFileServer/upload.aspx" }, + { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263cc4, 64, "/SakeFileServer/download.aspx" }, + { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263d08, 75, "/SakeStorageServerDev/StorageServer.asmx" }, + { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263d54, 65, "/SakeFileServerDev/upload.aspx" }, + { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263d98, 67, "/SakeFileServerDev/download.aspx" }, + { STR_M_KOR,1, 1,SERV_S_SAKE, 0x268050, 71, "/SakeStorageServer/StorageServer.asmx" }, + { STR_M_KOR,1, 0,SERV_GS, 0x25f69e, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x260415, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x260431, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x260452, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x260473, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x260495, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x2604b7, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x2604d7, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x2604f7, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x260516, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x260539, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x26055e, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x26057c, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x264d15, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x265485, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x265e9d, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x26703e, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x267a32, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x267c58, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x267c74, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x267c90, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x267de0, 19, "" }, + { STR_M_KOR,1, 0,SERV_GS, 0x267e64, 19, "" }, + + {0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// data access /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetStrModeName ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_UNKNOWN: return "---"; + case STR_M_PAL: return "PAL"; + case STR_M_USA: return "USA"; + case STR_M_JAP: return "JAP"; + case STR_M_KOR: return "KOR"; + case STR_M__N: return "???"; + } + return "???"; +} + +/////////////////////////////////////////////////////////////////////////////// + +char GetStrModeRegionChar ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_PAL: return 'P'; + case STR_M_USA: return 'E'; + case STR_M_JAP: return 'J'; + case STR_M_KOR: return 'K'; + default: return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static u32 GetIdAddress ( const staticr_t * str ) +{ + if (str) + { + switch (str->mode) + { + case STR_M_PAL: return 0x80276054; + case STR_M_USA: return 0x80271d14; + case STR_M_JAP: return 0x802759f4; + case STR_M_KOR: return 0x80263e34; + default: return 0; + } + } + return 0; +} + +//----------------------------------------------------------------------------- + +static char * GetIdPointer ( const staticr_t * str ) +{ + const u32 addr = GetIdAddress(str); + if (addr) + { + const dol_header_t *dol_head = (dol_header_t*)str->data; + const u32 off = GetDolOffsetByAddr(dol_head,addr,PATCHED_BY_SIZE,0); + if (off) + return (char*)str->data + off; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const u32 * GetTrackOffsetTab ( const staticr_t * str ) +{ + if (str) + { + switch (str->mode) + { + case STR_M_PAL: return pal_track_offset; + case STR_M_USA: return usa_track_offset; + case STR_M_JAP: return jap_track_offset; + case STR_M_KOR: return kor_track_offset; + default: return 0; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const u32 * GetArenaOffsetTab ( const staticr_t * str ) +{ + if (str) + { + switch (str->mode) + { + case STR_M_PAL: return pal_arena_offset; + case STR_M_USA: return usa_arena_offset; + case STR_M_JAP: return jap_arena_offset; + case STR_M_KOR: return kor_arena_offset; + default: return 0; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const u32 * GetAllRanksOffsetTab ( const staticr_t * str ) +{ + if (str) + { + switch (str->mode) + { + case STR_M_PAL: return pal_all_ranks_offset; + case STR_M_USA: return usa_all_ranks_offset; + case STR_M_JAP: return jap_all_ranks_offset; + case STR_M_KOR: return kor_all_ranks_offset; + default: return 0; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const u32 * GetVsBtOffsetTab ( const staticr_t * str, bool battle ) +{ + if (str) + { + if (battle) + { + switch (str->mode) + { + case STR_M_PAL: return pal_bt_offset; + case STR_M_USA: return usa_bt_offset; + case STR_M_JAP: return jap_bt_offset; + case STR_M_KOR: return kor_bt_offset; + default: return 0; + } + } + else + { + switch (str->mode) + { + case STR_M_PAL: return pal_vs_offset; + case STR_M_USA: return usa_vs_offset; + case STR_M_JAP: return jap_vs_offset; + case STR_M_KOR: return kor_vs_offset; + default: return 0; + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const u32 * GetMenuOffsetTab ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_PAL: return pal_menu_offset; + case STR_M_USA: return usa_menu_offset; + case STR_M_JAP: return jap_menu_offset; + case STR_M_KOR: return kor_menu_offset; + default: return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#define ORIG_VBI_VALUE 0x4e800020 + +static u32 GetVbiAddress ( const staticr_t * str ) +{ + DASSERT(str); + switch (str->mode) + { + case STR_M_PAL: return 0x801bab20; + case STR_M_USA: return 0x801baa80; + case STR_M_JAP: return 0x801baa40; + case STR_M_KOR: return 0x801bae7c; + default: return FindDolAddressOfVBI(str->data,str->data_size); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#define ORIG_SDK_VERS "001000\0" // one more '\0' is added by compiler + +static u32 GetSdkverAddress ( str_mode_t mode ) +{ + // return address of string '001000\0\0' + // string 'sdkver\0\0' follows with offset +8 + + switch (mode) + { + case STR_M_PAL: return 0x80384fd0; + case STR_M_USA: return 0x80380c50; + case STR_M_JAP: return 0x80384950; + case STR_M_KOR: return 0x80372fd0; + default: return 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// second host at nas login /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u32 GetSecondHostAddress ( str_mode_t mode, u32 *ret_orig ) +{ + // PAL: Offset 800ed868, original 0x480eb86d, neu 0x60000000 + // USA: Offset 800ed7c8, original 0x480eb86d, neu 0x60000000 + // JAP: Offset 800ed788, original 0x480eb86d, neu 0x60000000 + // KOR: Offset 800ed8e0, original 0x480ebb51, neu 0x60000000 + + if (ret_orig) + *ret_orig = mode == STR_M_KOR ? 0x480ebb51 : 0x480eb86d; + + switch (mode) + { + case STR_M_PAL: return 0x800ed868; + case STR_M_USA: return 0x800ed7c8; + case STR_M_JAP: return 0x800ed788; + case STR_M_KOR: return 0x800ed8e0; + default: return 0; + } +} + +//----------------------------------------------------------------------------- + +static u32 * GetSecondHostPointer ( const staticr_t * str, u32 *ret_orig ) +{ + const u32 addr = GetSecondHostAddress(str->mode,ret_orig); + if (addr) + { + const dol_header_t *dol_head = (dol_header_t*)str->data; + const u32 off = GetDolOffsetByAddr(dol_head,addr,4,0); + if (off) + return (u32*)(str->data + off); + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// wiimmfi codes /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 wiimmfi_code_pal_bz2[] = +{ + // 4 bytes decompressed len + 0x00,0x00,0x01,0x18, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xe0,0xa4, 0xf4,0x97,0x00,0x00, + 0x27,0x7f,0xfd,0xff, 0xd7,0xcd,0x42,0xe6, 0x56,0x00,0x5c,0x74, 0x65,0x84,0x00,0x60, + 0x42,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x30,0x09,0x00,0xc4, + 0x5c,0xb0,0x00,0xd9, 0x8c,0x70,0xd3,0x4c, 0x10,0xc8,0x69,0xa6, 0x46,0x4c,0x20,0x1a, + 0x68,0x03,0x09,0xa3, 0x4c,0x98,0x00,0x40, 0xd0,0x6a,0x69,0x46, 0xd4,0xd0,0xd1,0xa4, + 0xc0,0x00,0x32,0x00, 0x00,0x8c,0x0d,0x00, 0x34,0x32,0x4c,0xc9, 0xa1,0x04,0x4d,0x4d, + 0x06,0x80,0x00,0x06, 0x80,0x34,0x0d,0x01, 0xa1,0x90,0x34,0x0c, 0x80,0x03,0x12,0x46, + 0x06,0xbe,0x9a,0x92, 0xa6,0x1b,0x60,0xf3, 0x6f,0x35,0x10,0x01, 0xa1,0xd6,0xd4,0x64, + 0x10,0xcb,0x04,0x73, 0x81,0xc8,0x82,0x20, 0x03,0x02,0xca,0xcd, 0x3d,0x4c,0xe4,0xb8, + 0xac,0x91,0xdc,0xa1, 0x90,0xe1,0xa8,0x81, 0xa2,0x9c,0x05,0xc6, 0xa0,0xf2,0x82,0xaa, + 0x02,0x49,0x45,0x0c, 0x04,0x26,0x10,0x90, 0xdc,0xf7,0x57,0x43, 0x99,0x8c,0x29,0x26, + 0x3f,0x8e,0x05,0x82, 0x03,0xa9,0x1a,0xe8, 0xde,0x7b,0x29,0x22, 0xfc,0x74,0x21,0x91, + 0x50,0x43,0x29,0x9d, 0xf4,0x8c,0x69,0xff, 0xb1,0x09,0xa4,0x5f, 0xd0,0xfc,0x66,0x24, + 0xd5,0x53,0x6c,0x59, 0x0d,0xb6,0x07,0xe0, 0x1b,0x98,0xcf,0xa9, 0xf1,0xa0,0x4b,0x23, + 0x2e,0xbf,0xcd,0xce, 0x59,0x65,0xdf,0x8d, 0xcd,0xa4,0xac,0x46, 0xc0,0xac,0x91,0xab, + 0xf8,0x3b,0x2b,0x49, 0x4c,0x24,0x1a,0x22, 0x3f,0x99,0xed,0x48, 0x28,0x06,0xad,0x2d, + 0x19,0x7b,0x07,0x61, 0x05,0x5c,0xf6,0x43, 0x93,0x95,0x54,0x58, 0xc0,0xe1,0xfb,0xba, + 0xd7,0x72,0x1a,0x68, 0x3a,0x9e,0x81,0xde, 0xa2,0xee,0x07,0x9e, 0x5a,0x9b,0x3c,0x04, + 0x9f,0xf8,0xbb,0x92, 0x29,0xc2,0x84,0x87, 0x05,0x27,0xa4,0xb8, + // [300 Bytes] +}; + +static BZ2Manager_t wiimmfi_code_pal_mgr + = { wiimmfi_code_pal_bz2, sizeof(wiimmfi_code_pal_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static const u8 wiimmfi_code_usa_bz2[] = +{ + // 4 bytes decompressed len + 0x00,0x00,0x01,0x18, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x24,0x09, 0x21,0x7b,0x00,0x00, + 0x28,0x7f,0xfd,0xff, 0xd7,0xcd,0x42,0xe6, 0x56,0x00,0x5c,0x70, 0x65,0x84,0x44,0x60, + 0x42,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x30,0x09,0x00,0xc0, + 0x18,0xb0,0x00,0xd9, 0xb4,0x22,0x50,0x64, 0x68,0x30,0x98,0x40, 0x68,0x18,0x8c,0x02, + 0x00,0x68,0xc8,0x00, 0xc2,0x61,0x04,0x53, 0xd2,0x32,0x9a,0x32, 0x68,0x00,0x34,0x0c, + 0x80,0x1a,0x06,0x81, 0xa0,0x00,0x1a,0x68, 0xc4,0xf5,0x06,0xa6, 0xa7,0xa9,0xa8,0x00, + 0x06,0x80,0x00,0x03, 0x40,0x01,0xa1,0x90, 0x00,0x69,0xa0,0x0e, 0x65,0x49,0x81,0x7c, + 0xa8,0xd3,0x51,0x16, 0x82,0x77,0xf6,0x80, 0x90,0x34,0xb7,0xc7, 0x83,0x21,0x2c,0xb0, + 0x46,0x60,0xc8,0x84, 0x40,0x18,0x43,0x2b, 0xb4,0xf1,0xd9,0x96, 0x24,0x2e,0xa9,0xf7, + 0xa1,0x98,0xc6,0xc2, 0x0d,0x14,0xd0,0x5d, 0x2a,0xa3,0xac,0x1a, 0x89,0x0b,0x14,0x51, + 0x84,0x90,0xa0,0x0e, 0x12,0xdc,0xa7,0xf8, 0xc6,0x96,0xd8,0x51, 0x0a,0xbb,0x40,0xe8, + 0x80,0x5c,0x10,0x0f, 0x42,0x83,0x41,0xcc, 0x97,0x4a,0x6c,0x73, 0x0c,0x67,0x42,0xc0, + 0x28,0x34,0x5e,0xda, 0x0d,0xa6,0xff,0x18, 0xc9,0x15,0xa7,0x12, 0x0d,0x02,0x9a,0xca, + 0x2a,0x64,0x9f,0x40, 0x39,0x96,0x00,0xf7, 0x4d,0xdc,0x7a,0xfb, 0xa8,0x2a,0xd6,0xe8, + 0x09,0x44,0xe7,0xed, 0x3e,0x7c,0x91,0x47, 0x1d,0xe9,0x8c,0xb2, 0x58,0x06,0x31,0x84, + 0xa4,0x54,0x60,0x48, 0xea,0xa0,0x30,0x7c, 0x11,0x22,0xcb,0xbd, 0x2c,0x62,0x20,0xc4, + 0xe3,0x36,0xe8,0x45, 0x05,0xa1,0x80,0x43, 0xa4,0x35,0x99,0x55, 0x94,0x01,0x10,0x2b, + 0x8c,0x40,0xa4,0x42, 0x91,0x57,0xc0,0x87, 0x5c,0xe7,0x3a,0x38, 0x8d,0xef,0xf8,0xbb, + 0x92,0x29,0xc2,0x84, 0x81,0x20,0x49,0x0b, 0xd8, + // [297 Bytes] +}; + +static BZ2Manager_t wiimmfi_code_usa_mgr + = { wiimmfi_code_usa_bz2, sizeof(wiimmfi_code_usa_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static const u8 wiimmfi_code_jap_bz2[] = +{ + // 4 bytes decompressed len + 0x00,0x00,0x01,0x18, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x19,0x2d, 0x5f,0x73,0x00,0x00, + 0x27,0x7f,0xfd,0xff, 0xd7,0xcd,0x46,0xe6, 0x56,0x00,0x5c,0x70, 0x65,0x84,0x00,0x64, + 0x42,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x24,0x11,0x00,0xc0, + 0x18,0xb0,0x00,0xd9, 0x34,0x35,0x10,0x6a, 0x68,0x34,0xd0,0x34, 0x00,0x34,0x34,0x64, + 0x34,0xd0,0x00,0xd0, 0x01,0xa6,0x26,0x80, 0x22,0x9e,0x94,0x64, 0x19,0x0d,0x1a,0x32, + 0x06,0x8d,0x34,0x00, 0x34,0x1a,0x62,0x34, 0x00,0x01,0x8d,0x47, 0xa8,0x41,0x13,0x53, + 0x40,0x00,0xd0,0x01, 0xa0,0x0d,0x03,0x40, 0xf5,0x06,0x40,0xd0, 0x32,0x00,0x32,0xa8, + 0x60,0xea,0xb1,0xaa, 0x2e,0x61,0xb8,0x0f, 0xb8,0x2b,0x09,0x00, 0x1a,0x5e,0x73,0x83, + 0x40,0x86,0x92,0x47, 0x38,0x1c,0x88,0x23, 0x80,0x24,0x5a,0x61, 0xb7,0xea,0x9c,0x99, + 0x16,0x14,0x3b,0xb4, 0xb3,0x1c,0x36,0x10, 0x36,0x54,0x00,0xb9, 0xd6,0x1e,0x91,0x17, + 0x40,0x2c,0x52,0x03, 0x11,0x09,0x84,0x25, 0xb9,0x45,0xe6,0x50, 0xea,0x73,0x9c,0x53, + 0xb5,0x58,0x24,0xfc, 0x88,0x0c,0x14,0x0e, 0xe5,0x23,0x43,0x89, 0xcc,0xa2,0x66,0xd5, + 0x31,0x9d,0x16,0x84, 0x72,0xd3,0x79,0xc1, 0x99,0x2f,0x5b,0xe3, 0x42,0x66,0xf6,0xa0, + 0x34,0x95,0x25,0x24, 0x27,0x32,0x1f,0x20, 0x72,0x96,0x07,0x0d, 0x6e,0x20,0x48,0x8a, + 0x94,0x96,0x17,0x5f, 0xef,0x2f,0xac,0x40, 0x7b,0x2f,0xd0,0xf6, 0xa2,0x6c,0x00,0xb2, + 0x24,0xb8,0xf1,0xce, 0xad,0x39,0x20,0x88, 0x7c,0x44,0x90,0x55, 0xf6,0xa0,0xc8,0x39, + 0x85,0x10,0xd3,0xe4, 0x25,0x04,0xd2,0xc4, 0x21,0xcc,0xb6,0x11, 0xb4,0x70,0x10,0x23, + 0xeb,0x63,0xe9,0x51, 0x92,0x81,0x12,0x8b, 0x00,0xc6,0x35,0x73, 0x1b,0xf1,0xed,0x1a, + 0x0f,0xe2,0xee,0x48, 0xa7,0x0a,0x12,0x03, 0x25,0xab,0xee,0x60, + // [300 Bytes] +}; + +static BZ2Manager_t wiimmfi_code_jap_mgr + = { wiimmfi_code_jap_bz2, sizeof(wiimmfi_code_jap_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static const u8 wiimmfi_code_kor_bz2[] = +{ + // 4 bytes decompressed len + 0x00,0x00,0x01,0x18, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xc2,0x0b, 0xe6,0xb5,0x00,0x00, + 0x27,0x7f,0xfd,0xff, 0xd7,0xcd,0x42,0xe4, 0x76,0x00,0x5c,0x70, 0x61,0xd4,0x40,0x60, + 0x46,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x28,0x05,0x00,0xc0, + 0x18,0xb0,0x00,0xd9, 0xb5,0x11,0x28,0x34, 0x68,0x06,0x9a,0x69, 0xa6,0x4d,0x34,0xd1, + 0x91,0xa6,0x9a,0x1a, 0x18,0x20,0x0d,0x00, 0x68,0xda,0x43,0x6a, 0x0d,0x53,0xd2,0x99, + 0xa2,0x19,0x00,0xd0, 0x03,0x40,0x00,0x68, 0x00,0x00,0x00,0x34, 0x62,0x68,0x35,0x34, + 0x4d,0x46,0x80,0x00, 0x03,0x40,0x00,0x00, 0xd1,0xa0,0xd0,0x1a, 0x06,0x40,0x02,0xea, + 0x1c,0x46,0xbc,0x75, 0xa8,0x9c,0xd6,0x03, 0xad,0xfd,0x5f,0xa4, 0x0d,0x0e,0x35,0xc1, + 0x90,0x96,0x58,0x24, 0x20,0x20,0x88,0x44, 0x01,0x80,0xc9,0x5a, 0xd6,0xab,0xb4,0x16, + 0x13,0x5b,0x51,0x0d, 0xac,0xba,0x06,0xc2, 0x0c,0x94,0xd0,0x5c, 0xea,0x1b,0xa4,0x0a, + 0x69,0x12,0x0a,0x28, 0xc0,0x46,0x39,0x84, 0x76,0xa7,0x39,0x55, 0x2d,0xe6,0x38,0xf0, + 0x49,0x55,0x07,0x9f, 0x01,0x60,0x80,0xf1, 0xf6,0xf0,0xec,0x88, 0x16,0xae,0xa8,0xca, + 0xe7,0x3d,0x30,0xa9, 0x4c,0x6b,0x50,0xc4, 0x9b,0x7b,0x9f,0xed, 0x3b,0x3a,0x51,0x19, + 0x49,0x48,0x4a,0xad, 0x17,0xe5,0x60,0x0d, 0xab,0x70,0xe3,0xab, 0x67,0x91,0x81,0x42, + 0x51,0x19,0x42,0xc5, 0x18,0x03,0x12,0x73, 0x93,0x26,0x68,0x86, 0x3b,0x9f,0x8d,0xab, + 0x18,0x02,0xe5,0x09, 0x88,0xa5,0xfa,0x5e, 0xc9,0x60,0x90,0xf8, 0x44,0x65,0xa8,0xb5, + 0x18,0x93,0x42,0xb2, 0xe7,0x91,0xe8,0x3f, 0x14,0xa1,0xba,0xe1, 0x0e,0xae,0xb0,0xad, + 0x54,0x01,0x82,0x2a, 0x30,0xe4,0x04,0xd2, 0x34,0x16,0xc1,0x9e, 0x9a,0x42,0x12,0x63, + 0x53,0x87,0xfc,0x5d, 0xc9,0x14,0xe1,0x42, 0x43,0x08,0x2f,0x9a, 0xd4, + // [301 Bytes] +}; + +static BZ2Manager_t wiimmfi_code_kor_mgr + = { wiimmfi_code_kor_bz2, sizeof(wiimmfi_code_kor_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static BZ2Manager_t * GetWiimmfiCode ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_PAL: return &wiimmfi_code_pal_mgr; + case STR_M_USA: return &wiimmfi_code_usa_mgr; + case STR_M_JAP: return &wiimmfi_code_jap_mgr; + case STR_M_KOR: return &wiimmfi_code_kor_mgr; + default: return 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// lecode data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 lecode_loader_pal_bz2[] = +{ + // decompressed len: 0x200 = 512 bytes + 0x00,0x00,0x02,0x00, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xa1,0xb1, 0x2c,0x5f,0x00,0x00, + 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x30,0x60, 0xc7,0xc4,0x6f,0x2f, 0x6f,0xc4,0x84,0x7f, + 0xe7,0xfe,0x6e,0xdc, 0x04,0x44,0x0c,0x43, 0x60,0x00,0x15,0x54, 0x00,0x48,0x49,0x7a, + 0x40,0x00,0x01,0xb0, 0x01,0x59,0xb2,0x20, 0xd4,0xd4,0xd0,0xd1, 0x4d,0xa9,0xb5,0x34, + 0xf5,0x3d,0x40,0x7a, 0x80,0x68,0x1b,0x50, 0x64,0xf5,0x03,0x40, 0x3c,0xa1,0xa0,0x06, + 0x43,0xd4,0xc8,0xf5, 0x19,0x1e,0x88,0x7b, 0x54,0x22,0x9e,0x80, 0x98,0x4d,0x49,0xe9, + 0x3d,0x4f,0x24,0x01, 0xa0,0x00,0x0d,0x00, 0x1a,0x0d,0x00,0x00, 0xd3,0x4f,0x29,0x88, + 0x00,0x64,0x0d,0x4d, 0x48,0xd3,0x11,0x18, 0x09,0x9a,0x99,0x18, 0x9a,0x69,0x89,0x93, + 0x10,0xc9,0x84,0x32, 0x64,0x34,0x61,0x36, 0x8d,0x4c,0x68,0x8f, 0x51,0xa6,0x34,0x09, + 0x90,0x29,0xc9,0x9c, 0x3a,0x07,0xfa,0xe1, 0x92,0x40,0x83,0x36, 0x02,0x8a,0x62,0x3f, + 0xb9,0x73,0x96,0x54, 0x1d,0xea,0x43,0xbc, 0x8c,0xb1,0x75,0x00, 0xd4,0xe6,0x09,0x05, + 0xb5,0x08,0xc9,0x83, 0xc5,0x95,0xcb,0x97, 0xa4,0x10,0xfc,0x88, 0x6b,0x5c,0x06,0x08, + 0xa4,0x31,0x85,0xa7, 0x80,0x46,0xeb,0x37, 0x3a,0xa8,0x18,0x22, 0x01,0x17,0xf6,0x46, + 0x43,0x05,0xdc,0x21, 0x55,0x8e,0x6e,0xc5, 0x89,0xaa,0x1b,0x08, 0xa6,0x7c,0x55,0x37, + 0x85,0x02,0x55,0xaa, 0x2d,0x62,0xf7,0xf4, 0xde,0x0b,0x2e,0xbb, 0xf2,0xa3,0x29,0x42, + 0xe6,0x78,0x2d,0x2d, 0xeb,0x95,0xdb,0xb1, 0x49,0x30,0xb7,0x11, 0xd4,0xa1,0x0c,0x2b, + 0x2c,0x22,0x54,0xd8, 0x87,0xeb,0x40,0xe5, 0x18,0xca,0xfa,0x10, 0xe9,0x92,0x3a,0xee, + 0xc1,0x41,0x9c,0x0a, 0x45,0xe2,0x46,0xed, 0x58,0x1a,0x0a,0xf7, 0xe8,0x5c,0x42,0x4d, + 0xd6,0x77,0x31,0xf7, 0xac,0x48,0xc5,0x18, 0xe3,0x53,0x80,0x54, 0xd9,0x3d,0x52,0x40, + 0x8b,0x28,0xc4,0x73, 0x3f,0x3e,0x92,0xe1, 0x77,0xe7,0x0a,0x9a, 0xae,0x38,0xc3,0x64, + 0x75,0x14,0x14,0x14, 0x89,0x35,0x16,0x33, 0xc4,0x42,0x00,0x9d, 0xb6,0x0b,0x98,0x40, + 0xf6,0xcc,0xda,0xf4, 0x1f,0xd7,0x4c,0x76, 0x21,0x89,0x44,0x6a, 0x92,0x11,0x22,0x45, + 0x6b,0x4a,0x40,0x81, 0x27,0x8d,0xfa,0x48, 0x3e,0x21,0x02,0x47, 0x0a,0x8a,0x02,0xa2, + 0x95,0xcb,0xb2,0xe3, 0x01,0x17,0x3d,0xc5, 0x41,0xae,0x16,0xb7, 0x84,0xaf,0xa4,0x8b, + 0x62,0x06,0x18,0xd2, 0x12,0x28,0xe3,0x38, 0xa4,0x30,0x32,0x04, 0x4c,0x2a,0x4b,0x22, + 0xa4,0xe7,0x94,0x2d, 0x7c,0x63,0xf3,0x59, 0xcb,0xf7,0x6e,0xd0, 0x42,0x21,0x74,0x9b, + 0xeb,0x55,0x32,0xeb, 0xd8,0x97,0x3e,0xb7, 0xee,0x55,0xb1,0x58, 0x5d,0xb9,0x55,0xe9, + 0x47,0x11,0x18,0x7b, 0x34,0x02,0xb3,0xf9, 0xf9,0x06,0xfb,0x5b, 0x8e,0x30,0xd4,0xb2, + 0xd8,0x62,0xf8,0xae, 0x75,0x02,0x06,0x20, 0xf0,0x69,0x79,0x9e, 0x40,0x98,0x48,0xeb, + 0x63,0xa1,0x3b,0xd0, 0x1a,0xe8,0x64,0x67, 0xfe,0x51,0x09,0x4c, 0xe4,0xa7,0xbc,0xce, + 0xa1,0x94,0xad,0x87, 0xfe,0x2e,0xe4,0x8a, 0x70,0xa1,0x21,0x43, 0x62,0x58,0xbe, + // 0x1ef = 495 Bytes +}; + +static BZ2Manager_t lecode_loader_pal_mgr + = { lecode_loader_pal_bz2, sizeof(lecode_loader_pal_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static const u8 lecode_loader_usa_bz2[] = +{ + // decompressed len: 0x200 = 512 bytes + 0x00,0x00,0x02,0x00, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x9f,0x2b, 0x3c,0x9b,0x00,0x00, + 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x34,0x64, 0xc7,0xc0,0x6f,0x2f, 0x6f,0x8e,0x84,0x7f, + 0xe7,0xfe,0x6e,0xdc, 0x04,0x44,0x0c,0x43, 0x60,0x00,0x15,0x50, 0x40,0x48,0x49,0x7c, + 0x80,0x00,0x01,0xb0, 0x01,0x59,0x18,0xd8, 0x6a,0x68,0x20,0x9e, 0xa9,0xa7,0xa4,0x7a, + 0x23,0x40,0x00,0x07, 0x94,0x0d,0xa8,0x32, 0x01,0x84,0x00,0x1a, 0x1e,0x93,0xd2,0x69, + 0xa0,0x00,0x7e,0xa8, 0x45,0x4f,0xd3,0x53, 0x14,0xfd,0x15,0x3d, 0xa8,0x4d,0x3c,0x90, + 0x00,0x00,0x64,0xd3, 0x4d,0x00,0x34,0x00, 0x00,0x00,0x6d,0x13, 0x20,0x1a,0x7a,0x80, + 0x4a,0x9a,0x94,0xf4, 0x64,0x35,0x34,0x34, 0x64,0x18,0x8d,0x00, 0x62,0x06,0x81,0x84, + 0x68,0xc8,0x1a,0x32, 0x06,0x99,0x33,0x51, 0xa7,0xa9,0xa6,0x8c, 0x98,0x83,0x09,0x2b, + 0xc9,0x63,0x5c,0xfe, 0x70,0xc4,0x78,0x20, 0xbd,0xe2,0xad,0x5b, 0x0e,0xa6,0x38,0xb3, + 0x75,0xc5,0xa9,0x21, 0x3d,0x14,0xda,0x6d, 0x83,0xd5,0xaf,0x23, 0x10,0x3f,0x0d,0x4c, + 0xb1,0xd1,0x49,0xc4, 0x89,0x20,0x43,0xe3, 0x41,0x54,0xda,0x50, 0xcb,0xc2,0xc8,0xdf, + 0x00,0x22,0xa5,0x2b, 0x4a,0xb3,0x0a,0x19, 0x04,0x4b,0x97,0xfd, 0x97,0x2b,0x78,0x58, + 0x12,0xbb,0xd4,0x9a, 0xeb,0x24,0xd6,0x4e, 0x32,0x12,0xe1,0x3b, 0xe1,0x62,0xf8,0xf1, + 0xc6,0x57,0x0b,0x95, 0x52,0x23,0x0f,0x2e, 0xa7,0xb0,0x2d,0x64, 0x59,0x40,0x9a,0xf8, + 0x5b,0x72,0xf9,0x6c, 0xbd,0xbd,0x3f,0xd2, 0xa9,0xce,0xf5,0x3a, 0x6a,0x50,0xa8,0x93, + 0x5e,0x23,0x99,0xb1, 0xbe,0x27,0xa6,0x32, 0x1c,0x96,0xec,0x57, 0x8f,0x5a,0xe9,0xd9, + 0x90,0x90,0x11,0x74, 0x28,0x1b,0xb2,0x46, 0x9a,0x96,0x55,0x49, 0xee,0x39,0xd8,0x90, + 0xc3,0xdd,0x6d,0xcc, 0x99,0x24,0x3e,0xe0, 0xa1,0x12,0x36,0x89, 0xb1,0x5b,0x0c,0xc4, + 0x83,0x41,0x73,0x9c, 0xd0,0x6a,0x05,0x8a, 0x10,0x3e,0xfb,0x8f, 0x12,0x0e,0x72,0x51, + 0x21,0x6f,0x61,0x35, 0xb5,0x44,0x1f,0x09, 0x55,0x01,0x91,0x08, 0x24,0x9b,0x5a,0x72, + 0xf4,0xd5,0x05,0x79, 0x73,0x4d,0x0d,0x35, 0x84,0xd5,0x42,0x7a, 0x35,0x50,0x0f,0xad, + 0x30,0x52,0x35,0x27, 0x11,0x01,0x0b,0x84, 0xc8,0x04,0xc8,0x9c, 0xcf,0x37,0x3c,0x08, + 0x65,0xbb,0x89,0x4f, 0x4b,0x63,0x24,0x09, 0x49,0x5a,0x18,0xd5, 0x0c,0xc8,0x16,0x10, + 0x9a,0x46,0x51,0x21, 0xe0,0x6b,0xea,0x88, 0x4d,0x6d,0xb8,0xa4, 0xe9,0x06,0x36,0xd2, + 0x46,0x1b,0x0c,0x49, 0x7d,0x47,0x18,0x4c, 0x10,0x81,0x77,0xb1, 0xee,0xe5,0x45,0x5b, + 0xca,0x85,0x8b,0x3f, 0x7c,0x41,0xd6,0x5d, 0xf9,0x9b,0x13,0xb4, 0x9e,0x8a,0x3d,0x5a, + 0x05,0xdf,0xf4,0xf6, 0x13,0x9a,0xe2,0xc9, 0x67,0x37,0x17,0xef, 0xf4,0xbb,0xe6,0xf5, + 0xa4,0xb9,0x4c,0x41, 0x48,0x67,0x8f,0xdd, 0xc5,0x76,0x3f,0x45, 0xaf,0xe9,0x83,0x89, + 0x94,0xb5,0x4d,0xf2, 0x1d,0x19,0x5f,0x59, 0x61,0x8c,0x5b,0x19, 0xd6,0x69,0xad,0xba, + 0xc5,0x4e,0xff,0x17, 0x72,0x45,0x38,0x50, 0x90,0x9f,0x2b,0x3c, 0x9b, + // 0x1ed = 493 Bytes +}; + +static BZ2Manager_t lecode_loader_usa_mgr + = { lecode_loader_usa_bz2, sizeof(lecode_loader_usa_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static const u8 lecode_loader_jap_bz2[] = +{ + // decompressed len: 0x200 = 512 bytes + 0x00,0x00,0x02,0x00, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x5f,0x33, 0xd7,0x7b,0x00,0x00, + 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x30,0x60, 0xc7,0xc0,0x6f,0x2f, 0x7f,0xc4,0x84,0x7f, + 0xe7,0xfe,0x6e,0xdc, 0x44,0x44,0x0c,0x43, 0x60,0x00,0x15,0x50, 0x00,0x48,0x4d,0x6c, + 0x80,0x00,0x01,0xb0, 0x01,0x59,0x18,0xd8, 0x6a,0x04,0x34,0x4d, 0x4f,0x49,0xa7,0xa9, + 0xa7,0xa4,0x03,0xd4, 0x00,0x3f,0x54,0x0f, 0x28,0x00,0x6d,0x4c, 0x86,0x40,0xf4,0x8f, + 0x53,0x10,0xd0,0x00, 0x37,0xaa,0x11,0x53, 0xf4,0xd2,0x60,0x54, 0xf6,0xa1,0x3d,0x4f, + 0x6a,0x40,0x00,0x01, 0x90,0x01,0x90,0x00, 0x00,0x19,0x0c,0x98, 0x80,0x34,0x61,0x04, + 0xa9,0xa9,0x06,0x9a, 0x9b,0x48,0x1a,0x34, 0x68,0xc8,0x64,0x34, 0xc8,0xd1,0x91,0xa6, + 0x86,0x80,0x0d,0x34, 0xd0,0x64,0x7a,0x26, 0x9e,0xa1,0xb5,0x34, 0xd3,0x64,0x80,0xba, + 0x50,0x9e,0xb1,0xaa, 0x7f,0x38,0x3e,0x60, 0x41,0xfb,0xc5,0x9b, 0x37,0x20,0x53,0x71, + 0x66,0xda,0x16,0xaa, 0x87,0x0a,0x29,0xb4, 0x9e,0x18,0x8b,0x3c, 0x8b,0xb7,0x84,0xd4, + 0xcd,0x20,0x1d,0x6e, 0x24,0x99,0xc2,0x1f, 0x1a,0x4a,0xae,0xca, 0x88,0x4c,0x17,0x0f, + 0x4a,0x40,0x8a,0xa2, 0xad,0x36,0x97,0x51, 0x08,0x11,0x2f,0xb6, 0xf8,0xf2,0xef,0xb6, + 0x37,0x65,0x84,0x4a, 0xe1,0x51,0x51,0x10, 0x9d,0x09,0x2b,0x7d, 0xfd,0xd4,0x8d,0x34, + 0xc5,0x76,0x73,0x92, 0x46,0x41,0xe6,0xf6, 0x0f,0x86,0x45,0xb3, 0x87,0xef,0x83,0x8d, + 0x3b,0xb1,0x6d,0xee, 0x18,0x3d,0x64,0x31, 0xa7,0xd3,0xa6,0xc3, 0x02,0x44,0x12,0x89, + 0xeb,0x34,0x38,0x84, 0x08,0x94,0xa3,0x96, 0xe5,0x99,0x66,0x8e, 0x83,0x77,0xba,0x2d, + 0x98,0x0d,0x7e,0x76, 0x41,0x2c,0x52,0xb0, 0x96,0xae,0x82,0xd4, 0x2d,0x0a,0xd0,0x42, + 0xa5,0x76,0xf9,0x27, 0x32,0xd5,0xa3,0x70, 0x08,0xa3,0x3e,0xf1, 0x5a,0x15,0x61,0x21, + 0x4e,0x45,0x3d,0x03, 0x21,0x95,0x4e,0x11, 0x29,0xf7,0xd3,0x3c, 0xa7,0x31,0x85,0x04, + 0x64,0x9f,0x1d,0xad, 0xa6,0x21,0x08,0x49, 0xdd,0x06,0x7a,0x08, 0x40,0x4d,0xab,0x31, + 0x3b,0x48,0x8e,0xb4, 0xa8,0x96,0x66,0xad, 0x21,0x12,0x1f,0xa6, 0xcd,0x9a,0x66,0x07, + 0x29,0x0f,0x5c,0x07, 0xc8,0x30,0x3d,0xc2, 0xa2,0x01,0x51,0x13, 0x97,0x5d,0xc7,0x03, + 0x32,0xee,0x12,0x41, 0xe5,0x31,0x92,0x04, 0x94,0x93,0x31,0xa8, 0x19,0x71,0xa4,0x1e, + 0xb8,0x06,0x49,0x18, 0xe0,0xd8,0x51,0x50, 0x56,0x96,0xc5,0xaa, 0xe9,0x0b,0x1b,0xa5, + 0x0b,0xfd,0x59,0x01, 0x58,0xb5,0x64,0x25, 0x88,0x46,0x9c,0x1b, 0x3c,0x79,0xd5,0x6b, + 0x54,0x9d,0x6a,0xdd, 0x8c,0x32,0xb0,0xf7, 0xcf,0x04,0x87,0x4e, 0x44,0x51,0xec,0xc8, + 0x3f,0xc5,0xab,0xc4, 0x54,0x66,0x9c,0x5c, 0x86,0x22,0x3a,0xeb, 0x4c,0x43,0x4b,0xf5, + 0xc4,0x14,0x84,0x82, 0x24,0x1c,0xf8,0x25, 0xd1,0x72,0xf0,0x55, 0x5e,0xd4,0x44,0xaf, + 0xf4,0x1b,0x19,0x5f, 0x79,0x61,0x6b,0x75, 0x6b,0xaf,0xd6,0x4a, 0x29,0x71,0x6c,0xe0, + 0x7f,0x8b,0xb9,0x22, 0x9c,0x28,0x48,0x2f, 0x99,0xeb,0xbd,0x80, + // 0x1ec = 492 Bytes +}; + +static BZ2Manager_t lecode_loader_jap_mgr + = { lecode_loader_jap_bz2, sizeof(lecode_loader_jap_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static const u8 lecode_loader_kor_bz2[] = +{ + // decompressed len: 0x200 = 512 bytes + 0x00,0x00,0x02,0x00, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xaa,0xbf, 0x22,0x0b,0x00,0x00, + 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x30,0x60, 0xc7,0xc4,0x6f,0x2f, 0x6f,0x94,0x84,0x7f, + 0xe7,0xfe,0x6e,0xdc, 0x04,0x44,0x0c,0x43, 0x64,0x00,0x15,0x50, 0x00,0x48,0x49,0x6a, + 0x40,0x00,0x01,0xb0, 0x01,0x59,0xb2,0x36, 0x22,0x0a,0x7a,0x35, 0x43,0x1a,0x4d,0xa2, + 0x64,0x62,0x06,0x03, 0x28,0x61,0x1a,0x60, 0x06,0x90,0xd3,0x02, 0x18,0xd3,0x42,0x32, + 0x62,0x69,0xa3,0xf5, 0x08,0xa7,0xa0,0x98, 0x9a,0x68,0x29,0xe9, 0x92,0x00,0x68,0x00, + 0x00,0xc8,0x06,0x83, 0x40,0x01,0xa1,0xe8, 0x68,0x9e,0xa0,0x06, 0x80,0x4a,0x9a,0x90, + 0x9f,0xa9,0xa8,0xf4, 0x8c,0x80,0x60,0x8d, 0x0d,0x00,0x01,0x84, 0x69,0xa6,0x9a,0x06, + 0x9a,0x06,0x9a,0x34, 0xd3,0xd4,0x33,0x51, 0xbd,0x49,0xa6,0x86, 0x5a,0x6e,0xd6,0x68, + 0xd4,0x3f,0xde,0x1f, 0x36,0x20,0xfe,0xf1, 0x5e,0xbd,0x98,0x13, 0xde,0x69,0xdb,0x03, + 0x55,0x10,0xa2,0x8a, 0x75,0x36,0xc3,0x0d, 0xab,0xc8,0xb7,0x7f, 0x09,0x79,0xa4,0x03, + 0xae,0xda,0x49,0xd7, 0x10,0x7a,0x52,0xbb, 0x5c,0x51,0x05,0xb1, 0xd3,0xda,0x68,0x22, + 0xb7,0x95,0xa5,0x61, 0x6a,0x20,0x82,0x23, 0x3d,0xdf,0x1e,0x7e, 0x16,0x45,0x59,0x39, + 0xab,0xb8,0x26,0xe3, 0x36,0x29,0x4c,0x91, 0x55,0xbb,0x17,0xa4, 0x96,0x51,0x75,0x6e, + 0x57,0xad,0x4d,0xa0, 0x9d,0xfb,0xfb,0x98, 0xd3,0x14,0x28,0x44, 0x0b,0x43,0x1d,0x99, + 0xed,0xfa,0x3b,0xbe, 0xec,0x94,0x8e,0x98, 0x60,0xc6,0x0b,0x2d, 0x85,0xc8,0x5b,0xa9, + 0x85,0x80,0x7e,0xd8, 0x56,0xb4,0x35,0xf0, 0xcf,0x7b,0xa3,0xa8, 0xb7,0x01,0x00,0x2e, + 0x98,0x05,0x85,0x9f, 0x06,0xe7,0x4c,0x18, 0x1a,0x96,0x9e,0x74, 0x2e,0xbe,0x3d,0x07, + 0x88,0xd2,0x84,0xa4, 0x12,0xe6,0x16,0x80, 0xd3,0x06,0x21,0x08, 0x65,0x12,0xce,0x64, + 0x24,0x09,0x2d,0xc4, 0x12,0xd6,0x55,0x53, 0xa8,0x32,0x90,0x83, 0x48,0xa4,0x41,0xa9, + 0x2d,0x76,0x84,0x18, 0x0f,0xee,0x03,0x96, 0xc4,0x0e,0x55,0xdc, 0xa2,0x8a,0x8d,0xc7, + 0xe5,0x04,0x4a,0x22, 0xd4,0x80,0x89,0xb9, 0x15,0x4e,0xc1,0x01, 0x88,0x3c,0x69,0x39, + 0xa9,0x10,0x23,0x60, 0xa4,0xa0,0x29,0x29, 0x58,0xa9,0xd8,0x60, 0x23,0x90,0xb0,0xad, + 0x99,0xfd,0x45,0xad, 0xa1,0x32,0x92,0x45, 0xba,0xd8,0xc3,0x1a, 0x02,0x45,0x1c,0x60, + 0x29,0x0c,0x0e,0xbc, 0xdd,0x91,0x52,0x17, 0x43,0x4d,0xe6,0x94, 0x2e,0xa4,0x64,0xf3, + 0x70,0xe5,0xfa,0xa9, 0x74,0x21,0x10,0x91, 0x18,0x37,0x78,0x33, 0x6d,0x4d,0x2e,0x8c, + 0xf0,0x17,0xe4,0x2b, 0x1f,0x16,0xf8,0x63, 0x30,0xa2,0x8a,0x3d, 0x5c,0x07,0xf0,0xb9, + 0x98,0x71,0x39,0x37, 0x1f,0x45,0xb8,0x4c, 0x67,0x8b,0x78,0x89, 0xf3,0x50,0x41,0x48, + 0x48,0x3f,0x8c,0x55, 0xc8,0x59,0xfc,0x19, 0x9c,0x87,0x9a,0x27, 0xcc,0x16,0xc0,0x38, + 0x43,0x46,0xd8,0x60, 0x84,0x37,0x42,0x2f, 0x89,0xe2,0x7c,0x28, 0x33,0xdd,0xe2,0xee, + 0x48,0xa7,0x0a,0x12, 0x15,0x57,0xe4,0x41, 0x60, + // 0x1e9 = 489 Bytes +}; + +static BZ2Manager_t lecode_loader_kor_mgr + = { lecode_loader_kor_bz2, sizeof(lecode_loader_kor_bz2), 0, 0 }; + +//----------------------------------------------------------------------------- + +static BZ2Manager_t * GetLecodeLoader ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_PAL: return &lecode_loader_pal_mgr; + case STR_M_USA: return &lecode_loader_usa_mgr; + case STR_M_JAP: return &lecode_loader_jap_mgr; + case STR_M_KOR: return &lecode_loader_kor_mgr; + default: return 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// more data /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const u8 codehandleronly_bz2[] = +{ + // 4 bytes decompressed len + 0x00,0x00,0x0a,0xb8, + + // bzipped data, includes a GCH header of sizeof(gch_header_t) == 16 bytes + // 0x0: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x88,0x5d, 0x08,0x9e,0x00,0x02, + 0x4f,0xff,0xff,0xff, 0xfd,0xe5,0xf5,0xff, 0xff,0xff,0xff,0xff, 0xfd,0xef,0xf6,0x7e, + 0xfd,0xfc,0xf7,0xff, 0xff,0xff,0xf5,0x7b, 0xff,0xfe,0x7d,0x4e, 0x75,0x75,0x45,0x46, + 0xe4,0xe4,0xff,0xd0, 0x05,0x3e,0xdd,0x80, 0x1d,0xd5,0xb5,0xd7, 0x1d,0x57,0x6e,0xa3, + 0x6c,0x11,0x4c,0x22, 0x64,0x9e,0xa9,0xfa, 0xa7,0xe9,0x95,0x3d, 0xa8,0x8f,0x53,0xda, + 0x93,0x41,0xb4,0x9e, 0x48,0xd1,0xfa,0xa0, 0x06,0x9e,0x46,0xa6, 0x9a,0x34,0x01,0xa3, + 0xd4,0x03,0x34,0x47, 0x84,0x86,0x8d,0x3d, 0x41,0xea,0x34,0xf4, 0x83,0xd4,0xd1,0xa3, + 0xf5,0x40,0x7a,0x80, 0x00,0x00,0x3d,0x4c, 0x86,0x80,0x00,0x68, 0x7a,0x8f,0x49,0xe5, + 0x04,0x53,0xd2,0x0d, 0x20,0x35,0x30,0x9a, 0x9e,0xa1,0xea,0x79, 0xa9,0x3d,0xaa,0x7a, + 0x9e,0x9e,0x82,0x65, 0x34,0x7a,0x81,0xe4, 0x46,0xd3,0x51,0xea, 0x6d,0x4d,0x0d,0xa9, + 0xea,0x1e,0x9a,0x4d, 0x34,0x01,0xea,0x68, 0xf5,0x1a,0x00,0x0c, 0x8d,0x03,0x41,0xa0, + 0xd1,0xea,0x06,0x80, 0x00,0x00,0x00,0x0d, 0x34,0x00,0x1a,0x15, 0x4f,0x4d,0x4c,0xa7, + 0xa9,0xa7,0x91,0xa6, 0xa1,0x1b,0x4c,0x9a, 0x00,0x00,0x00,0x08, 0xc0,0x02,0x60,0x98, + 0x00,0x03,0x40,0x46, 0x00,0x09,0x82,0x60, 0x00,0x26,0x00,0x00, 0x4c,0x46,0x00,0x02, + 0x60,0x26,0x00,0x1a, 0x08,0x00,0xd3,0x40, 0xc8,0x34,0x00,0x34, 0x0d,0x00,0xd0,0x06, + 0x43,0x43,0x4c,0x40, 0x0d,0x03,0x13,0x40, 0xd0,0x19,0x00,0x00, 0x00,0xc8,0xd1,0x88, + // 0x100: + 0x0d,0x00,0x00,0x00, 0x06,0x86,0x46,0x8c, 0x98,0x08,0xc9,0x93, 0x4d,0x03,0x4d,0x21, + 0x04,0xd3,0x49,0x95, 0x3d,0xa6,0x46,0x40, 0x53,0x4f,0x26,0xa7, 0x8d,0x29,0xb4,0xca, + 0x1a,0x79,0xa5,0x01, 0xe9,0x34,0x7a,0x83, 0x69,0xa9,0x82,0x68, 0x06,0x81,0x90,0x00, + 0x66,0x51,0x91,0xea, 0x7a,0x46,0x8c,0x8d, 0x36,0x8c,0xa7,0xea, 0x4c,0xd4,0xc9,0x91, + 0xa0,0x0d,0x01,0xfa, 0xa0,0x00,0x19,0x1a, 0x3c,0xa6,0x77,0x37, 0x61,0x49,0xb5,0xfd, + 0x12,0xb5,0x55,0x35, 0x28,0x01,0x28,0x12, 0xd4,0xf8,0x84,0x4a, 0xca,0xdc,0x30,0x0d, + 0x2a,0x2f,0x0e,0x06, 0xc4,0xbe,0x15,0xb5, 0x0c,0xca,0x0e,0x1b, 0x4f,0x31,0x16,0xd9, + 0xf1,0xbe,0x3b,0xdb, 0xd4,0xa2,0x22,0xda, 0x45,0x51,0xf9,0x21, 0xe0,0x31,0x35,0x0b, + 0x66,0x00,0xc0,0xb4, 0x89,0x10,0xc2,0x4d, 0x66,0x0f,0x5f,0x4d, 0x7e,0xeb,0xc4,0x29, + 0xdc,0xc1,0x45,0xbb, 0x74,0x39,0xaf,0xc5, 0xe1,0xa4,0x41,0x10, 0x7c,0x26,0x16,0x41, + 0xee,0x61,0xb3,0x88, 0x06,0x60,0x4e,0x20, 0x79,0x42,0x4a,0x51, 0xc5,0xe1,0x76,0x23, + 0xab,0xd1,0x6a,0x73, 0xd6,0x2b,0x05,0x12, 0x59,0xe4,0xa2,0x17, 0x6b,0x86,0x01,0x22, + 0x61,0x1a,0xe1,0x8b, 0x53,0x78,0x18,0xfa, 0xb6,0x82,0xa9,0x00, 0x82,0x27,0x2f,0x26, + 0xa2,0x2c,0x60,0xc9, 0x7a,0xba,0xa8,0x51, 0x20,0x66,0x87,0x8b, 0x06,0x7b,0xf7,0x8e, + 0x68,0x32,0xe2,0x5e, 0x26,0x73,0xbd,0x96, 0xc4,0x21,0x56,0xf1, 0x64,0x28,0x79,0xce, + 0x7a,0x47,0xc2,0xb9, 0xaf,0xa7,0xc6,0xf4, 0x25,0x99,0x40,0xb9, 0x8a,0x60,0x55,0x1c, + // 0x200: + 0x5b,0x75,0x74,0x40, 0xc3,0x0c,0xc7,0x35, 0x1c,0xa4,0xa9,0x07, 0x0f,0x3d,0x82,0xb0, + 0x89,0x26,0x89,0x09, 0x3e,0xeb,0xc7,0x51, 0x2f,0xec,0x75,0xee, 0x3b,0xa3,0xd3,0xbb, + 0xed,0x38,0x62,0xaa, 0xaf,0xf4,0xa3,0xac, 0x23,0xf3,0x28,0x91, 0xc2,0xa1,0x1e,0x22, + 0xd9,0x9a,0xf6,0x26, 0xac,0xce,0xae,0x73, 0x20,0x30,0x58,0x2a, 0x34,0xd2,0x20,0x50, + 0x7d,0xb6,0xf6,0xd4, 0xde,0x4f,0x28,0x43, 0x16,0x90,0x5c,0xc6, 0x3b,0x86,0xc2,0x36, + 0xd0,0xf4,0x98,0xf7, 0x4c,0x02,0x2f,0x73, 0x6f,0x32,0x60,0x24, 0x83,0xbd,0x2b,0xae, + 0x19,0x10,0x24,0xa2, 0x88,0x8e,0x20,0x5d, 0x52,0xc1,0x10,0xb5, 0x00,0x78,0x8d,0x8d, + 0x1b,0x99,0xba,0xa9, 0x59,0xd0,0xd6,0x08, 0x70,0x08,0xf9,0x32, 0xd5,0x04,0x5f,0xc4, + 0x89,0x3f,0xcc,0xa9, 0x4f,0x81,0xf3,0x5f, 0xe1,0x8c,0xc6,0xe5, 0x90,0x13,0xde,0x9b, + 0x65,0x67,0xb7,0xdb, 0x10,0x41,0x28,0x49, 0x42,0x2c,0x67,0x88, 0x00,0xff,0x22,0xe4, + 0x46,0xde,0x17,0x0d, 0xc1,0x82,0xe2,0xc2, 0xa3,0xd7,0x82,0x21, 0xbe,0x1c,0x88,0x05, + 0x88,0xd4,0x28,0x46, 0x68,0x28,0x40,0x9c, 0x1a,0xdb,0x70,0x44, 0x21,0xaa,0x01,0x07, + 0xdd,0x6b,0x6b,0x5c, 0x1f,0x23,0xae,0x20, 0x0d,0x02,0x81,0xa4, 0x38,0xee,0xb2,0x19, + 0x3a,0xfd,0xcf,0x94, 0x03,0x4c,0x42,0x12, 0x63,0x7d,0x9b,0x81, 0xae,0x3a,0xfc,0xce, + 0x39,0x73,0xca,0xae, 0xd3,0xee,0xd5,0xf7, 0x15,0x71,0xc6,0x5c, 0xf1,0xfb,0x15,0x4b, + 0xcd,0xd9,0x5f,0x36, 0xdb,0x65,0x5d,0xce, 0x4e,0xb2,0x7d,0x94, 0x39,0x8c,0x17,0x25, + // 0x300: + 0x10,0x3d,0x68,0x74, 0x9a,0xc0,0xe6,0x39, 0x06,0x49,0xd9,0xdf, 0x2e,0x47,0x81,0xad, + 0xcb,0x6b,0x8f,0x1d, 0xfd,0xff,0x7e,0x75, 0x61,0x6c,0xac,0x75, 0x41,0x0b,0x83,0x38, + 0xe2,0x90,0x43,0x01, 0xb8,0x82,0x68,0x96, 0xc1,0x4a,0x46,0x01, 0xa0,0x58,0x67,0x85, + 0xbc,0x82,0xc5,0x84, 0x90,0xc8,0x38,0x12, 0xc5,0x4e,0x05,0xb5, 0xd0,0x59,0x3e,0x08, + 0xdd,0x52,0x00,0x2d, 0x65,0x17,0x03,0x35, 0xa2,0x58,0xf8,0x0a, 0x5a,0x75,0x86,0x2e, + 0x34,0x15,0x10,0xf2, 0x3c,0x30,0xfa,0x80, 0x1e,0x51,0xb9,0x1d, 0x3c,0x93,0xc2,0x77, + 0x99,0x49,0x15,0xc4, 0xe1,0x86,0x9b,0x9d, 0x4a,0xc9,0xdf,0x15, 0x66,0xd9,0xcd,0x64, + 0xd2,0x1a,0x14,0x62, 0x6c,0x85,0x7f,0x99, 0x87,0x77,0x9b,0xe3, 0xca,0xc5,0xa3,0xfd, + 0x67,0xa4,0x83,0xb6, 0x0e,0xe8,0xc3,0x5c, 0x75,0x63,0x5b,0xf7, 0x35,0x82,0x6e,0x13, + 0xac,0xaa,0x29,0x91, 0xbb,0x10,0xbd,0xd4, 0x43,0xa8,0xa2,0xde, 0x20,0x26,0xd3,0x38, + 0xb4,0x77,0x24,0x67, 0x45,0xab,0xba,0xde, 0x7e,0x29,0xbc,0x68, 0x1f,0xe6,0xeb,0xc5, + 0xd1,0x08,0x26,0x42, 0xbb,0x56,0xba,0xc0, 0x0f,0x3a,0x81,0xfb, 0x19,0xb8,0x2f,0x2e, + 0x54,0x33,0x68,0xdd, 0xbc,0x26,0x94,0xff, 0xaf,0xf3,0xe5,0x51, 0xac,0x79,0xf0,0x40, + 0xc0,0xe5,0x2b,0x30, 0xec,0x79,0xe1,0xc6, 0x36,0xf5,0x28,0xd3, 0xe6,0x7c,0x1e,0x7c, + 0xf7,0xc1,0x07,0x96, 0x34,0x85,0x7a,0xd2, 0x3d,0xbd,0x8b,0xdb, 0xe1,0x1e,0x82,0xce, + 0xff,0xd2,0x14,0x39, 0x29,0x23,0x80,0x02, 0x12,0xe0,0x34,0x04, 0x15,0x12,0xe8,0xe9, + // 0x400: + 0x98,0x86,0x3f,0x68, 0xb4,0x51,0x0f,0x74, 0x09,0xbf,0xa0,0x0c, 0x49,0x32,0xcb,0x86, + 0xfa,0xfb,0x9f,0xd3, 0x1d,0xe4,0xac,0x27, 0xa6,0xde,0xe2,0xd7, 0x46,0xd9,0xb3,0x97, + 0x0b,0x1f,0x09,0xb5, 0x1f,0x4a,0xfb,0xe1, 0x31,0x82,0x16,0xac, 0x61,0xf6,0x15,0xa0, + 0xe0,0xe3,0xa0,0x1c, 0x50,0xe1,0x01,0x7f, 0x10,0x9d,0x1e,0x35, 0x13,0x67,0x81,0x48, + 0x8d,0x24,0xf0,0x02, 0x79,0x02,0xba,0xa2, 0x30,0x8e,0x7d,0xe8, 0x7a,0x34,0xe6,0x8a, + 0x0c,0xd1,0x8c,0x85, 0x03,0x13,0x4f,0x0c, 0x98,0x60,0x40,0x45, 0x48,0x9d,0x3a,0x47, + 0x2a,0x95,0x6c,0xa9, 0xf6,0x19,0x2d,0x99, 0x3e,0xc3,0x83,0xd6, 0xd6,0x51,0x63,0xe5, + 0x96,0x22,0x2b,0xa2, 0xa3,0x41,0x61,0x35, 0x4b,0xc9,0x2b,0x06, 0xbc,0x55,0xc4,0xc9, + 0x23,0x6e,0x50,0xa7, 0xba,0x3f,0xcf,0x5b, 0xf3,0x10,0x5a,0xfc, 0x38,0x81,0xdd,0x6a, + 0xca,0x2c,0x66,0x69, 0x60,0xbc,0xa4,0x04, 0xcd,0x7e,0x6a,0xab, 0x1d,0xae,0x01,0x08, + 0xd5,0x25,0x2c,0xa1, 0xaa,0x58,0x45,0x39, 0x34,0xc6,0x86,0x62, 0xa4,0x7e,0x49,0xab, + 0x65,0x25,0xf4,0x76, 0xb3,0xa7,0xaf,0x99, 0x1e,0x7c,0x93,0x42, 0x24,0xa4,0x20,0x08, + 0x39,0x5c,0x36,0x82, 0x76,0x3e,0x22,0x9e, 0xe3,0xe6,0xe1,0x7a, 0x8b,0xac,0x1d,0xca, + 0x98,0x73,0x6b,0x3b, 0xfa,0x0a,0xbf,0x80, 0xdb,0xd4,0x21,0x45, 0xed,0x7b,0xca,0xb6, + 0x28,0xaa,0x20,0xe4, 0xe7,0x0e,0xab,0x0a, 0x94,0x1c,0x23,0x84, 0x32,0x6a,0x26,0x76, + 0x5e,0xe1,0x8b,0xca, 0x4b,0x66,0x85,0x37, 0x2d,0x89,0x96,0x48, 0x50,0xa1,0xc2,0x16, + // 0x500: + 0x84,0x1f,0xf4,0xef, 0x70,0x94,0x3b,0x6e, 0xd5,0x49,0x6e,0x97, 0x04,0xa6,0xff,0x2c, + 0x2c,0x69,0x0e,0x92, 0x65,0xdb,0x5b,0xeb, 0xf5,0x5a,0x55,0xd2, 0x88,0xf0,0x78,0x49, + 0x57,0x4b,0x1d,0x5a, 0x68,0x4a,0x0b,0x48, 0x24,0x7a,0x26,0x19, 0x6b,0x22,0xfc,0x9a, + 0xe6,0xb2,0xd0,0x03, 0x36,0x77,0x04,0xb1, 0x1a,0x57,0x24,0xb9, 0xd9,0x6f,0x2c,0x65, + 0xb4,0x64,0x50,0xbc, 0xbc,0x5f,0x0d,0x6d, 0xd1,0xc1,0x30,0x0d, 0xd6,0xbc,0xa5,0xcf, + 0x47,0xac,0xce,0xa4, 0xb6,0x6d,0xa5,0x73, 0xdd,0xd5,0x49,0x7f, 0xb3,0xe8,0x76,0x88, + 0xa3,0x9c,0x2b,0x1a, 0xf7,0x13,0xdc,0xfd, 0x3d,0x89,0x55,0x04, 0xfe,0xc1,0xaf,0x1f, + 0x0c,0x2b,0xc4,0x1b, 0x72,0x0d,0x11,0x77, 0x0e,0x7b,0x40,0xea, 0x6c,0x57,0xc2,0x9f, + 0x15,0x35,0x11,0x70, 0x0a,0x20,0xa1,0x73, 0x31,0x4d,0x43,0x02, 0x72,0xeb,0x58,0xdf, + 0xd2,0x9a,0x16,0x82, 0xc0,0xb6,0x47,0xa5, 0x07,0xf7,0xa5,0xcf, 0xa2,0xcd,0x8a,0x24, + 0xc5,0x9c,0x1c,0x71, 0xab,0x10,0x3e,0x83, 0x68,0x84,0x58,0xcf, 0x6e,0x26,0x17,0x2f, + 0x54,0x44,0x52,0x88, 0x06,0x31,0x24,0xaa, 0x03,0x4c,0x4d,0x30, 0x85,0xd4,0x95,0xc1, + 0x5c,0x46,0x9d,0xba, 0x20,0xf8,0x24,0x42, 0x57,0xe5,0x08,0xff, 0x37,0xe2,0x3e,0x0a, + 0xa4,0x26,0xfb,0x9b, 0x5a,0xca,0x94,0x5a, 0x47,0x0a,0xac,0x3d, 0xc4,0x35,0x11,0xc5, + 0x9c,0x63,0x7e,0x4b, 0xac,0x74,0xc3,0x0d, 0x13,0xc5,0x36,0x30, 0x88,0x37,0x64,0x4e, + 0x0b,0xb1,0x01,0x03, 0xde,0xc9,0x5d,0x44, 0xae,0xa6,0x5f,0xe4, 0xbc,0x9b,0x3a,0x5a, + // 0x600: + 0x5b,0x4a,0xd9,0x64, 0x14,0x4d,0x44,0x01, 0xeb,0xfb,0x89,0xf6, 0x0f,0xc0,0x42,0x14, + 0x05,0x02,0x12,0x10, 0x91,0x4c,0x8d,0x85, 0xcd,0xd5,0xc7,0xa6, 0xb7,0x69,0x83,0xa7, + 0x30,0x2c,0xdd,0xa8, 0x39,0x71,0xa3,0xc6, 0x29,0x86,0x43,0xc6, 0x05,0x83,0x57,0xf3, + 0x86,0x77,0x7a,0x2c, 0x50,0xb9,0xc0,0xad, 0x26,0xec,0x10,0x40, 0x0c,0x0c,0x04,0xc4, + 0xcb,0xb2,0xeb,0xb6, 0x1b,0x9b,0x50,0x16, 0x62,0x42,0x57,0x56, 0x35,0xd1,0x49,0x0b, + 0x14,0x16,0xb6,0x48, 0x40,0xe6,0xd4,0xe8, 0x51,0x91,0x89,0x38, 0xa5,0x2e,0x71,0x7a, + 0x60,0xd8,0xc5,0x7f, 0x3b,0xcb,0xbd,0x88, 0x38,0x10,0x08,0xbb, 0xd6,0x31,0x83,0x05, + 0xfd,0x6e,0x8d,0x63, 0xb6,0x25,0x04,0xf0, 0x4b,0x09,0x0b,0x89, 0x67,0x5e,0x18,0x91, + 0x63,0x4e,0x14,0x20, 0x0d,0x09,0x7a,0xf2, 0x32,0xfe,0xd5,0x6b, 0x5e,0xc1,0x73,0xc8, + 0xa6,0xc5,0xed,0xd4, 0x32,0xc4,0x9d,0xce, 0xfc,0x17,0x94,0x15, 0x45,0x46,0x5d,0x8a, + 0x7b,0x20,0xf6,0xce, 0x67,0x03,0xc9,0x80, 0x45,0x3d,0x33,0x6d, 0x21,0x4b,0x37,0xf3, + 0xf3,0xce,0x43,0x3a, 0xa5,0x4e,0x59,0x66, 0xa4,0x52,0x29,0x17, 0x4a,0x31,0x9e,0x86, + 0x02,0x7a,0x65,0x36, 0x43,0x81,0x04,0x0b, 0xf7,0x92,0x32,0xff, 0x1e,0xa6,0x1c,0x01, + 0x41,0x00,0x36,0xbc, 0xfd,0x20,0xd5,0x0d, 0x64,0x69,0x67,0x6c, 0x4c,0x9f,0x3d,0xc1, + 0xec,0x1c,0xd2,0x22, 0xe1,0x49,0x86,0xaa, 0xca,0xa6,0xcd,0xe1, 0xb3,0x31,0x46,0xb9, + 0x47,0xab,0x91,0x20, 0x6a,0x21,0xc6,0x1e, 0xa9,0x53,0x6a,0xe2, 0x41,0xa2,0xc9,0x44, + // 0x700: + 0x9c,0x9f,0x2d,0x06, 0xc1,0x73,0xc3,0xb8, 0xc4,0xb5,0x26,0x41, 0x97,0x06,0xa6,0xcb, + 0x1c,0x0b,0x48,0x29, 0xfe,0xf7,0x90,0x70, 0xa2,0x8b,0x01,0xb1, 0xe3,0x34,0x55,0x6a, + 0x6d,0x01,0x02,0x5f, 0x6a,0xa2,0x1e,0x95, 0x52,0xa9,0xce,0x2d, 0xfb,0xd5,0x86,0xc0, + 0xd2,0x05,0x03,0x8a, 0x34,0xfc,0x45,0x48, 0x20,0x80,0x4d,0x3e, 0x27,0x03,0xa9,0xf5, + 0x5d,0xd4,0x15,0x2f, 0x90,0x8f,0x8e,0x30, 0x6b,0x9a,0x39,0x3d, 0x05,0xcc,0x0d,0x48, + 0xd5,0x65,0x09,0x7e, 0x1b,0x03,0xb7,0xbd, 0x1a,0x03,0xa2,0xaa, 0x88,0x34,0x56,0xee, + 0xaa,0xb5,0x00,0xda, 0x91,0x79,0xf3,0xea, 0x8e,0xd5,0xce,0x0a, 0x50,0x7f,0xe2,0xee, + 0x48,0xa7,0x0a,0x12, 0x11,0x0b,0xa1,0x13, 0xc0, + // 0x779 = 1913 Bytes +}; + +static BZ2Manager_t codehandleronly_mgr + = { codehandleronly_bz2, sizeof(codehandleronly_bz2), 0, 0 }; + +/////////////////////////////////////////////////////////////////////////////// + +static const u8 codehandler_bz2[] = +{ + // 4 bytes decompressed len + 0x00,0x00,0x10,0xc8, + + // bzipped data, includes a GCH header of sizeof(gch_header_t) == 16 bytes + // 0x0: + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x1e,0xd9, 0x97,0x25,0x00,0x03, + 0xe9,0xff,0xff,0xff, 0xfd,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xfd,0xff,0xff,0x7e, + 0xfd,0xff,0xff,0xff, 0xff,0xff,0xf7,0x7f, 0xff,0xfe,0x7f,0x4e, 0x77,0x75,0xe5,0x66, + 0xe6,0xf4,0xff,0xe0, 0x08,0x9f,0x5c,0xbe, 0xf8,0x7b,0x4e,0xb4, 0xe9,0xdd,0xb1,0xdf, + 0x6b,0x6f,0xb6,0xed, 0xb7,0xcd,0xe9,0xec, 0x7b,0xef,0x5a,0x1d, 0x8f,0x70,0x35,0x32, + 0x21,0x1a,0x27,0xa4, 0xd4,0xf2,0x66,0xa6, 0x46,0x54,0xf4,0x9f, 0x88,0x93,0xfd,0x52, + 0x69,0xfa,0x9a,0x6a, 0x1e,0xa7,0xb5,0x35, 0x07,0xa9,0xe9,0x1a, 0x33,0xd5,0x34,0xf6, + 0xa6,0x89,0xb1,0x21, 0xa6,0xd4,0xf4,0x8f, 0x14,0x3d,0x43,0xf5, 0x1a,0x64,0xd3,0x51, + 0xfa,0x51,0xe9,0x1e, 0xa0,0xf5,0x1e,0x48, 0x68,0x32,0x68,0x7a, 0x11,0xb4,0x83,0x47, + 0xa8,0x3d,0x46,0x81, 0xe9,0x00,0x06,0x86, 0x8f,0x48,0x7e,0xa9, 0xa6,0x41,0x13,0x50, + 0x84,0xfc,0x91,0xa9, 0xb5,0x18,0x99,0x4f, 0x49,0xa0,0xf6,0xa1, 0xe8,0x8f,0x50,0x8f, + 0x4d,0x36,0xa8,0x34, 0xd3,0xd2,0x62,0x0d, 0x1b,0x51,0xea,0x68, 0x06,0x9b,0x53,0xd4, + 0x0d,0x00,0x0d,0x34, 0x0d,0x00,0x06,0x80, 0x1a,0x34,0x00,0x34, 0xd0,0x1a,0x00,0x00, + 0x00,0x00,0x00,0x34, 0x00,0x04,0x44,0xd4, 0xc4,0xc4,0xc8,0xf4, 0x8d,0x1a,0x53,0xf4, + 0x26,0x9a,0x9b,0x50, 0xf2,0x9e,0xa1,0xb4, 0xd4,0x06,0x8f,0x50, 0x07,0xa8,0x3d,0x40, + 0x06,0x80,0x1a,0x68, 0xf2,0x8c,0x80,0x01, 0xa0,0x1e,0xa0,0x0c, 0x80,0x00,0x34,0x1a, + // 0x100: + 0x07,0xa8,0x00,0x00, 0x00,0x06,0x80,0x00, 0x00,0x69,0xa1,0x14, 0xf2,0x35,0x32,0x20, + 0x26,0x86,0x9a,0x26, 0x13,0x4d,0x34,0x34, 0x03,0x4c,0x8d,0xa9, 0xa1,0x90,0x1a,0x7a, + 0x9a,0x03,0x43,0x40, 0x34,0x06,0x80,0xd0, 0x03,0x40,0x00,0x01, 0xa0,0x00,0x0d,0x00, + 0x64,0x34,0x69,0xa0, 0x69,0xa0,0x00,0x00, 0x00,0x0d,0x00,0x03, 0x53,0xd2,0x41,0x26, + 0x9e,0x52,0x3d,0x13, 0xd4,0x69,0xe5,0x1e, 0xa0,0x3d,0x11,0xa1, 0xea,0x3d,0x20,0x32, + 0x00,0x00,0xf4,0x86, 0x80,0x00,0x00,0x00, 0x0d,0x0c,0x83,0x20, 0x00,0x00,0x01,0xa0, + 0x1a,0x0d,0x00,0x06, 0x80,0x00,0x00,0x00, 0x00,0x00,0x25,0x35, 0x08,0x26,0xa3,0x4c, + 0x24,0x6d,0x4c,0x8f, 0x54,0xc9,0xb4,0x62, 0x9b,0x42,0x6f,0x2a, 0x60,0x4f,0x29,0x93, + 0x6a,0x7a,0x8d,0x1a, 0x7a,0x46,0x9a,0x07, 0xa8,0x34,0xd0,0x64, 0xd0,0x34,0x7a,0x8d, + 0x03,0x43,0x43,0x20, 0x06,0x99,0x00,0x34, 0x03,0x43,0x40,0x0d, 0x1a,0x69,0xa0,0x00, + 0x34,0x00,0x34,0x03, 0x46,0x87,0xa9,0xb4, 0xde,0xf4,0x75,0xe1, 0x8b,0xbc,0x72,0xbd, + 0x73,0x68,0x58,0x04, 0x99,0x8b,0x84,0x6b, 0x11,0xac,0xe9,0x36, 0x54,0xef,0xb6,0x1d, + 0xac,0x8c,0x79,0x0a, 0xbb,0x3f,0xcd,0xf1, 0xac,0xb1,0xd1,0xd3, 0xb6,0x54,0x4c,0x17, + 0x24,0xca,0xac,0x92, 0xf6,0xf8,0x49,0x67, 0x6d,0x9b,0x8c,0x8f, 0x66,0x2c,0x5a,0x6b, + 0xcd,0xf7,0x23,0x77, 0x97,0x67,0x7c,0xf2, 0x4c,0xad,0x5c,0x67, 0x42,0xe4,0x18,0xd6, + 0x12,0x38,0x2f,0x61, 0xef,0x30,0x61,0x6d, 0xdb,0x51,0x04,0x4c, 0x58,0xf9,0x8b,0x04, + // 0x200: + 0x4a,0x75,0x24,0xbd, 0x41,0x04,0x88,0x35, 0x87,0x5e,0x18,0x80, 0x83,0x13,0x81,0x03, + 0xe0,0xe3,0xdb,0x63, 0xdd,0xe9,0xac,0xa5, 0xee,0x0e,0x36,0xda, 0xe5,0xe2,0x04,0x60, + 0xcf,0x1e,0x9c,0x51, 0xd0,0xab,0xb4,0x5d, 0xc9,0x04,0x87,0x18, 0xca,0x34,0x22,0xa4, + 0xe0,0x6e,0x9b,0xa2, 0xd5,0x0f,0x59,0x22, 0x90,0x5c,0xf6,0x61, 0xcd,0x18,0x9c,0xb2, + 0xb0,0x0c,0x02,0x74, 0x6e,0xd2,0xb2,0x05, 0x8e,0xba,0x3b,0x32, 0x83,0x14,0x7d,0x14, + 0xa9,0x87,0x19,0x66, 0xf2,0x3e,0x27,0x97, 0xa2,0xab,0x72,0x00, 0x2e,0xbe,0x5f,0x56, + 0x04,0x20,0x05,0x2b, 0x70,0x12,0x21,0x28, 0xcd,0x5c,0xa5,0x4d, 0xad,0x98,0xf9,0x67, + 0x8f,0x16,0xbd,0xdb, 0xca,0x16,0x38,0xe9, 0x4b,0x82,0xa4,0x6d, 0xc8,0x0b,0x6e,0x1a, + 0x21,0x00,0x82,0x20, 0xa5,0xaa,0x8c,0x04, 0x72,0x40,0xd9,0x96, 0xf9,0xe3,0xe8,0x49, + 0xe2,0x80,0xf4,0xa2, 0x01,0xe0,0x3c,0xa6, 0x06,0xc5,0x21,0x52, 0xe1,0x2b,0x08,0xa2, + 0x39,0x77,0xad,0x23, 0x9c,0x28,0x38,0xb7, 0x64,0x74,0xec,0xd4, 0x24,0xde,0x03,0x4e, + 0x92,0xcd,0x53,0x6e, 0x9a,0x38,0xc6,0x67, 0x84,0x12,0xb3,0x6d, 0x79,0x2b,0xaf,0x5d, + 0x26,0xf9,0xa6,0x1b, 0xec,0xa4,0x9d,0x08, 0x02,0xd0,0x64,0x08, 0x20,0x66,0x4c,0xee, + 0xe0,0x29,0x17,0x22, 0x42,0xd8,0xdf,0xc6, 0xaa,0x54,0x96,0xd2, 0x90,0x4f,0x40,0x5d, + 0xbb,0x2c,0xa4,0xb4, 0x6b,0xd5,0x98,0x03, 0x42,0xc0,0x0b,0x5a, 0x60,0x4b,0xf3,0x4a, + 0x1c,0xa0,0x88,0x58, 0x02,0x82,0x35,0x28, 0x08,0xf3,0x02,0x28, 0xb2,0x06,0xc6,0x24, + // 0x300: + 0x53,0x28,0x49,0x10, 0x62,0x4a,0x67,0x54, 0x41,0x58,0x93,0x84, 0x53,0x73,0xec,0xa1, + 0xe4,0xe3,0x94,0x79, 0x46,0x16,0x18,0x7c, 0x48,0x31,0x45,0x69, 0x30,0x73,0x00,0x4c, + 0x1f,0x7a,0x68,0xf0, 0x1d,0x6a,0x80,0xd4, 0xde,0x60,0x62,0x0a, 0x2a,0x26,0x28,0x7d, + 0x4e,0xa0,0x88,0x58, 0x56,0xce,0xcb,0xa5, 0x8d,0xb4,0xeb,0x48, 0xb1,0xf8,0xe9,0xd2, + 0x06,0x3d,0x34,0xf6, 0x80,0x5b,0x88,0x49, 0x05,0xd4,0x43,0x2a, 0x8a,0x01,0x65,0xa2, + 0xa7,0x7f,0x29,0x28, 0x42,0xa5,0x1c,0x71, 0xeb,0xc9,0x96,0xa2, 0xbf,0x48,0xa0,0xa8, + 0x34,0x65,0x89,0x68, 0x66,0xb2,0xf1,0xc6, 0xa4,0x7b,0xd6,0x56, 0xf2,0xcd,0xb2,0xd3, + 0x53,0x9d,0x64,0xdd, 0xb4,0x26,0x17,0x68, 0x68,0xb7,0x64,0xb1, 0x5e,0xa6,0x29,0x2d, + 0xcf,0x70,0xd5,0x2a, 0x09,0x21,0x2f,0x25, 0xce,0x22,0xaa,0x4e, 0x75,0x3d,0x83,0x99, + 0xa9,0xd3,0xe1,0x9e, 0xc0,0x65,0x51,0x32, 0x66,0xc9,0x2e,0x0a, 0xfd,0xac,0x9e,0xcc, + 0xd3,0x90,0x56,0x3a, 0x2d,0x53,0xa5,0x23, 0x65,0x5e,0xf9,0xcd, 0x0f,0x03,0xb7,0xbd, + 0xe0,0x44,0x6b,0x17, 0x86,0x5a,0x01,0xc4, 0x50,0xa9,0x05,0x06, 0x6e,0xfc,0x1a,0x47, + 0x53,0xe7,0x98,0x42, 0x49,0xf0,0x87,0x6f, 0x0c,0x36,0x45,0x4e, 0x59,0xf5,0x2d,0x63, + 0x93,0xf7,0xb4,0xb6, 0xb1,0xc4,0xe3,0x6f, 0xcc,0xec,0x06,0x3e, 0x3d,0x0e,0x09,0x9a, + 0x83,0x78,0x48,0xba, 0x45,0xf2,0xcc,0xa9, 0xc9,0x86,0x3d,0xbb, 0x7a,0xcf,0xaf,0x55, + 0xcd,0xb2,0x74,0x8a, 0x02,0x09,0x51,0x25, 0x49,0x69,0x58,0x61, 0x4b,0x88,0x90,0xc2, + // 0x400: + 0xd1,0x44,0xc7,0x24, 0xab,0xf0,0xc2,0x0f, 0xd4,0x95,0x23,0xf6, 0x55,0x57,0xe6,0x32, + 0x16,0xa9,0x87,0xf7, 0x1a,0x59,0x32,0xc5, 0xf5,0x23,0x68,0x6e, 0x26,0x0b,0xa3,0x46, + 0xe6,0x0d,0x7c,0x30, 0xde,0x00,0x79,0xd6, 0xd9,0x81,0x13,0x16, 0xfa,0x08,0xe7,0xa8, + 0x82,0xd5,0xce,0xdc, 0xf2,0x95,0x66,0x53, 0x62,0xc9,0x18,0x9e, 0x86,0xb1,0x13,0x06, + 0x1a,0x02,0x08,0x89, 0x6c,0xef,0xb6,0x30, 0x9f,0xbe,0x3b,0x43, 0x20,0xd3,0x90,0xd7, + 0xe9,0x84,0x2c,0x30, 0xe0,0x4c,0x58,0x42, 0x80,0x91,0x69,0x72, 0x90,0x6c,0xf7,0x41, + 0x45,0x73,0x07,0xe9, 0x4c,0x1a,0x63,0x14, 0x82,0x88,0x8e,0x22, 0x94,0x94,0x62,0x50, + 0x59,0x2c,0x71,0x48, 0x6d,0x8d,0xda,0x7b, 0x19,0xcf,0x11,0x4b, 0x46,0x05,0x7d,0x96, + 0x4b,0x0a,0xad,0xa5, 0xad,0xc4,0xaf,0xd2, 0xd5,0xde,0x40,0xe0, 0x10,0xc5,0xaa,0x66, + 0x25,0x45,0xcb,0xb2, 0x9a,0x72,0x3d,0xe4, 0x2e,0xf5,0xd5,0xa7, 0x33,0x65,0x7b,0xb7, + 0x1d,0x52,0xa4,0x36, 0x51,0x0c,0xce,0xe2, 0xaf,0x75,0x0f,0xd8, 0xd8,0x5f,0xec,0xe1, + 0x1a,0xdc,0x2b,0x0e, 0xf3,0xad,0x16,0x0b, 0xab,0x1a,0xda,0xb0, 0xc4,0xda,0x62,0xc2, + 0x4b,0x22,0x62,0x08, 0x7a,0x47,0x92,0x94, 0x23,0x71,0x18,0xed, 0x09,0xc2,0x40,0x1c, + 0xb0,0x46,0x55,0x54, 0xf0,0xb8,0x5c,0x51, 0x02,0xed,0x97,0x4c, 0x60,0xbd,0x19,0x1b, + 0xb5,0x61,0x5a,0x40, 0xb2,0xb6,0x32,0xb5, 0xf6,0xcd,0x9c,0x30, 0xc8,0xd1,0xe0,0xb3, + 0x73,0x84,0x02,0xa6, 0xe1,0x5d,0x83,0x74, 0x88,0x04,0x93,0xa6, 0x98,0x63,0x19,0xca, + // 0x500: + 0x88,0x61,0xc7,0x0d, 0x8c,0x8c,0x01,0xe7, 0xcc,0x12,0xd8,0x4c, 0x56,0x9e,0xce,0xff, + 0x38,0x1d,0xe8,0x67, 0x0b,0x37,0xf8,0x10, 0x6d,0xeb,0xcd,0xff, 0x57,0x5a,0x6b,0x95, + 0x32,0x0c,0x8d,0xbe, 0xeb,0x2e,0xd9,0xe2, 0xf8,0xa7,0xc1,0xb8, 0x77,0xa7,0x15,0xa5, + 0x9d,0xc2,0xc2,0xfb, 0xec,0x69,0x15,0xdb, 0x0e,0xc4,0xf2,0x86, 0xc7,0x0a,0x0b,0xf5, + 0xf9,0x0e,0xe2,0xcf, 0xdc,0xf9,0x2f,0xd6, 0x5e,0x2e,0x53,0x0a, 0x83,0x95,0x86,0xd6, + 0x2d,0x95,0xb6,0x37, 0xcd,0xc7,0xf6,0xa2, 0x7c,0x7d,0x24,0x4d, 0x2b,0xfe,0x5d,0xf7, + 0x32,0x0e,0x9c,0x11, 0x02,0x90,0x71,0xc7, 0x74,0xfa,0x6d,0x94, 0x5d,0xe3,0xb5,0x6a, + 0xeb,0xad,0xda,0xfb, 0x52,0x73,0xc7,0x18, 0xc6,0x99,0x7b,0x86, 0x66,0x69,0x2d,0xed, + 0x6b,0x12,0xc2,0xf0, 0x81,0x0c,0x7b,0xf7, 0x49,0x15,0x31,0x8f, 0xd5,0x20,0x2a,0x33, + 0x37,0x36,0x76,0x37, 0x31,0x6d,0x03,0x1a, 0xc1,0xd3,0x1e,0x82, 0xf5,0xe9,0x6e,0x8e, + 0x9b,0xbd,0x21,0x48, 0xc4,0x0a,0xbb,0x8c, 0xc4,0x89,0x91,0x35, 0xdd,0x3f,0x36,0x92, + 0x30,0x46,0x48,0x5c, 0x04,0x9a,0xde,0xf0, 0x15,0x34,0xa7,0xcc, 0xeb,0xcc,0x13,0xfe, + 0x26,0x2e,0xd5,0x74, 0xf2,0xcd,0xd1,0x03, 0x32,0x93,0xac,0xeb, 0x15,0xb2,0x79,0x0c, + 0x2a,0x08,0x0d,0x34, 0x0d,0x59,0x59,0x9c, 0x68,0xe0,0xaa,0x36, 0x95,0xa3,0x9d,0x55, + 0x20,0x8f,0x12,0x3e, 0x10,0xe9,0x04,0xfc, 0x5a,0x0b,0x53,0xa9, 0x27,0x54,0x85,0x44, + 0x35,0x47,0x07,0xc1, 0xd9,0xe2,0x07,0x84, 0x24,0x7c,0x28,0x20, 0xc8,0x4e,0x98,0x7e, + // 0x600: + 0x33,0x4a,0x0e,0xc6, 0x54,0xef,0xeb,0xe7, 0xf3,0x6a,0x98,0x04, 0x0b,0x82,0x7d,0xcb, + 0x47,0x60,0x3e,0xb7, 0x78,0xed,0xc6,0x9c, 0xc6,0x04,0x5d,0x50, 0x79,0x60,0xd1,0xd4, + 0x58,0x57,0x42,0x53, 0x23,0x9e,0x3e,0xd8, 0x10,0xa8,0x07,0x28, 0x53,0xa6,0x84,0xf1, + 0x0f,0x74,0x10,0xef, 0x2a,0xfd,0x3c,0x71, 0xce,0x06,0x2e,0x82, 0x39,0x64,0x13,0x3a, + 0x46,0x0b,0x30,0xa6, 0x68,0xd8,0x58,0x05, 0x92,0xf3,0x4d,0xc7, 0x20,0xd6,0xd3,0xe7, + 0x7b,0x5c,0x00,0x73, 0x17,0x3f,0x01,0x0f, 0xcd,0xfc,0x5f,0xf9, 0x10,0xa8,0x21,0x1d, + 0x3b,0x4c,0x8b,0x0d, 0x4c,0x04,0x42,0x01, 0x29,0x27,0x60,0xa4, 0x09,0x47,0x98,0x4c, + 0xdf,0x62,0x90,0x0b, 0xd1,0xad,0x18,0x19, 0x67,0xba,0x51,0xea, 0x2c,0x63,0xbe,0x94, + 0xb5,0xdd,0xac,0x3e, 0x16,0xc3,0x0a,0x43, 0x79,0xce,0xf9,0x45, 0xef,0xff,0xd9,0xed, + 0xf1,0x3f,0xd9,0x3a, 0x42,0x82,0xdd,0xfc, 0xba,0x7a,0x2d,0x48, 0x96,0xdb,0xb5,0x3c, + 0x0a,0x4b,0xc8,0x4b, 0x3d,0x94,0x4a,0x01, 0x9c,0xd9,0xe9,0xdb, 0x2a,0xd4,0xc9,0x2d, + 0xb9,0x8c,0xc0,0xb5, 0x5c,0xba,0x60,0xb8, 0x90,0x92,0x3d,0xc9, 0x69,0x86,0x66,0x48, + 0x06,0x1d,0x48,0x47, 0x30,0xa7,0xa6,0x43, 0x62,0xb0,0xf2,0xaa, 0x30,0xe3,0x9f,0xa0, + 0xad,0x14,0x30,0xe1, 0x1e,0x85,0x34,0x57, 0x23,0x91,0x51,0x93, 0x9b,0x33,0x64,0x87, + 0x03,0xfd,0x42,0x45, 0x9f,0x9b,0x55,0xf5, 0x1d,0x08,0x57,0x0e, 0xdc,0x20,0x88,0x4d, + 0xc9,0xf5,0x49,0xac, 0x36,0x24,0x0c,0x39, 0xc5,0x77,0xb3,0x5a, 0x6f,0x30,0xc7,0x50, + // 0x700: + 0x59,0x25,0x0d,0x92, 0x91,0x6c,0x29,0x82, 0x3f,0x65,0xee,0xb7, 0xa4,0x37,0xc9,0x1a, + 0x7f,0x58,0xd6,0xfd, 0x2e,0xa8,0x90,0x64, 0x74,0xa1,0x66,0x0f, 0xbb,0xe8,0x28,0xa5, + 0x45,0x4c,0x8a,0x4f, 0xb5,0x87,0x05,0xc4, 0xfa,0x0c,0xf2,0x0e, 0xcd,0x01,0x98,0x08, + 0x45,0x87,0xb2,0x2a, 0xa5,0x0c,0xb4,0x74, 0xd9,0xe1,0x5f,0x59, 0x68,0xf6,0x6e,0xe6, + 0x9b,0x25,0xd6,0x4b, 0xd6,0x2a,0xc6,0x5e, 0x75,0x4c,0xb0,0xfc, 0x5b,0x04,0x10,0x07, + 0xde,0xc7,0x35,0x99, 0x3a,0x27,0x45,0x20, 0x54,0x11,0x5a,0x7d, 0xb3,0x33,0x51,0xa9, + 0xc8,0x52,0x5c,0x4c, 0x25,0xe6,0x17,0x96, 0x5c,0x43,0xe8,0xd3, 0x60,0x95,0x92,0x66, + 0xdd,0x29,0x62,0x92, 0xfa,0xaa,0xd8,0x58, 0xeb,0x4f,0x8f,0x74, 0xf7,0x50,0xe5,0xee, + 0xd9,0x55,0x18,0x94, 0x72,0x2b,0x7a,0x1e, 0x8a,0x17,0x24,0x85, 0x07,0x36,0x51,0xb5, + 0x2d,0x3a,0xa9,0x21, 0x54,0xaa,0xb7,0x94, 0xee,0xe1,0x95,0x35, 0x23,0x3c,0x94,0x16, + 0x45,0x88,0xf5,0xe2, 0x11,0xb7,0x5c,0x86, 0x97,0xf9,0x15,0x9b, 0xd8,0x39,0x31,0xf5, + 0x0f,0xd7,0x90,0xab, 0x66,0xc2,0x0c,0x15, 0xeb,0x86,0x30,0x43, 0xea,0x63,0x77,0xc6, + 0x93,0xb6,0x56,0x80, 0x17,0x12,0x2d,0xc6, 0xd0,0xd0,0xe2,0xc3, 0x42,0x68,0xcc,0x8b, + 0x60,0x20,0x35,0x95, 0x50,0x62,0x92,0x7e, 0x81,0xd9,0x68,0xf4, 0xad,0xbe,0xc3,0x38, + 0x63,0x1f,0x1a,0xfe, 0xff,0xc8,0x02,0x3e, 0x34,0x52,0x0c,0x99, 0x28,0xa8,0xe1,0x61, + 0x63,0x97,0xc6,0x90, 0x5c,0xcb,0x79,0x9b, 0x84,0x4e,0x9b,0xd7, 0x5f,0x40,0x4a,0xa1, + // 0x800: + 0x0c,0xe0,0x62,0x34, 0xa6,0x6a,0xd0,0x88, 0x04,0xd2,0x84,0x10, 0xca,0xfe,0x17,0xef, + 0x3d,0x2b,0xf7,0x13, 0x9d,0xe4,0x57,0xac, 0x84,0x78,0xb6,0xae, 0xbf,0xb4,0xf9,0x4f, + 0x83,0xea,0xb2,0xd1, 0x56,0x75,0x8e,0xd1, 0x72,0xc2,0x1e,0xa2, 0xc0,0x91,0xd9,0x6f, + 0xcf,0x68,0x51,0xa5, 0x9f,0x87,0x78,0x37, 0x32,0xe7,0x29,0x58, 0xa3,0x6b,0x7c,0x56, + 0x03,0xc4,0xc2,0x85, 0x12,0xdd,0x10,0x52, 0x39,0xa0,0x98,0x12, 0xa1,0xa7,0xd5,0xf1, + 0x90,0x92,0x1d,0x24, 0x3e,0x16,0x3d,0x56, 0x28,0x90,0x9d,0xbf, 0x5f,0x55,0x07,0x18, + 0x80,0xda,0x9c,0xd2, 0x43,0xab,0xfc,0x4a, 0x8c,0x3d,0xba,0xcf, 0xdf,0xdb,0x28,0x2b, + 0x3d,0x83,0xd4,0xcf, 0xd0,0xf3,0x64,0x71, 0x71,0x98,0xd6,0x8f, 0x13,0x35,0x6a,0xfc, + 0xed,0x7a,0x41,0xed, 0x0c,0x62,0xab,0x46, 0x9c,0xc7,0x29,0x53, 0xc6,0x67,0x5e,0xf8, + 0x69,0x79,0x3c,0xe0, 0xc2,0xec,0x0a,0x52, 0x51,0x80,0x8e,0x7c, 0x9d,0x11,0x02,0x1f, + 0x91,0x6b,0x9c,0x96, 0xb4,0xa1,0x2d,0x79, 0x6c,0x5d,0x6e,0x12, 0x97,0x97,0x0e,0xb2, + 0xfb,0xba,0x4a,0x14, 0x6d,0xe0,0xe6,0x30, 0xb2,0xa0,0x19,0xc8, 0x86,0xb5,0x18,0x19, + 0xb3,0x80,0xb0,0x50, 0x40,0x44,0x4b,0x93, 0xa3,0x70,0x4a,0x9e, 0xdb,0xa9,0xd0,0x38, + 0x17,0x10,0xf4,0x66, 0xbb,0xa5,0x26,0x8a, 0xf2,0xb8,0xe7,0x8b, 0x2b,0xc2,0x41,0xf0, + 0xcb,0x80,0x30,0x0e, 0xa5,0x90,0x42,0x28, 0x65,0x13,0xb2,0xa9, 0x01,0xd1,0xd9,0x34, + 0x00,0xc2,0xc1,0x92, 0xd5,0x14,0xdb,0x94, 0xaf,0xd9,0x4b,0x28, 0x54,0x11,0xf4,0xe6, + // 0x900: + 0xbe,0xb9,0xac,0x06, 0x67,0xb3,0xc5,0xcc, 0x54,0xf9,0xa9,0x2c, 0x13,0x46,0xde,0x12, + 0xe6,0xc9,0x8c,0xa1, 0xeb,0xb6,0x0d,0x4b, 0xe7,0xa8,0xb1,0x4f, 0x31,0x0c,0x34,0xd3, + 0x6a,0xd3,0x73,0x49, 0x3c,0xbb,0xc3,0x32, 0xb2,0x9d,0x29,0x3e, 0x70,0x96,0x56,0xd4, + 0xb9,0x2a,0xc8,0x83, 0x47,0x51,0x67,0xe0, 0x7e,0x84,0x02,0x68, 0xc9,0x7d,0xbc,0xc1, + 0x93,0x3a,0x27,0x0d, 0x8d,0x0a,0x03,0x9c, 0xb0,0xdc,0x75,0xf1, 0xbb,0x34,0x59,0x8e, + 0x2d,0x9a,0xa7,0xf2, 0xff,0xa3,0x9f,0x3d, 0xa2,0x41,0xc4,0x74, 0xc9,0x03,0x69,0x5a, + 0xad,0x3b,0x53,0x49, 0xf8,0x80,0xb2,0x3f, 0x21,0x2f,0xa5,0x69, 0xf3,0x56,0x49,0xa6, + 0xab,0xcb,0x58,0x80, 0x80,0x01,0x9c,0xcc, 0x8c,0xc2,0x40,0xce, 0x86,0x75,0x57,0xa4, + 0x31,0x11,0x43,0x21, 0x93,0x09,0x32,0x4c, 0x21,0x0c,0xc2,0x43, 0x55,0xb0,0x70,0x61, + 0xfe,0x3a,0x49,0x6c, 0x6a,0xcd,0x34,0xa0, 0x90,0x25,0x84,0xcb, 0x9a,0x3a,0x43,0x96, + 0x82,0xe7,0x66,0x20, 0x4e,0xd3,0x34,0x73, 0x78,0xfd,0xe6,0x27, 0x8e,0xb2,0x02,0xc4, + 0x28,0x4c,0x1b,0x37, 0x9a,0xdb,0x13,0x5f, 0xd4,0xce,0xa1,0x40, 0x3c,0xa5,0x21,0x0c, + 0xb7,0xbc,0x31,0x34, 0xcc,0x47,0x7a,0xe6, 0x30,0xa8,0xed,0x6c, 0xf0,0x25,0xd7,0xf7, + 0xbc,0xe9,0xae,0xa9, 0xd7,0x11,0x36,0x43, 0x30,0x19,0x12,0x83, 0x6e,0x2a,0xd9,0x90, + 0x22,0x8e,0x8d,0x21, 0xd4,0x22,0xb9,0xc8, 0x30,0x80,0x75,0x7a, 0x74,0x44,0x3c,0x6a, + 0xa7,0x9a,0x83,0xd8, 0xf8,0xb5,0x45,0x8e, 0x20,0x42,0x60,0x45, 0xc5,0x7a,0x29,0x48, + // 0xa00: + 0x97,0xe1,0x04,0x6e, 0xee,0x01,0x54,0x80, 0xc1,0x60,0xfd,0x13, 0xda,0x4e,0x6b,0xe1, + 0x5b,0x64,0x2d,0x89, 0xe2,0x04,0x4a,0x66, 0x96,0x73,0x4c,0xa9, 0x5e,0xf0,0x94,0x2f, + 0x66,0x99,0xb1,0xb4, 0xfa,0x0c,0x3a,0xf6, 0x51,0xe1,0x5b,0xb8, 0x69,0xc8,0xb9,0x16, + 0xd7,0x3f,0x26,0x6c, 0xb3,0x8d,0xc0,0xf7, 0x22,0x49,0x23,0x7d, 0xa5,0x42,0x01,0x16, + 0xa6,0xc0,0xbb,0x90, 0x32,0x62,0x88,0x6b, 0x8b,0xe2,0x5c,0x14, 0x52,0x8d,0xb4,0x05, + 0x77,0x0e,0xc6,0x3a, 0x75,0xdd,0xac,0x52, 0xb3,0xd5,0x2c,0x9c, 0x8e,0xc5,0x66,0x74, + 0xd4,0x28,0x54,0x2c, 0x71,0x14,0x6e,0x9b, 0x62,0x39,0x27,0x30, 0x58,0xbd,0x93,0xfc, + 0xc7,0xb2,0x70,0x40, 0x82,0x20,0xdf,0x82, 0x61,0xcc,0xe8,0xf3, 0xe6,0x8b,0x56,0x49, + 0x2d,0x16,0xa3,0x21, 0x02,0x7d,0x26,0xab, 0xf0,0xa4,0x72,0x57, 0x51,0xf6,0xfc,0x2e, + 0xa1,0x3e,0x86,0xaa, 0xa5,0xc8,0x40,0xc5, 0x0d,0xbb,0x57,0x31, 0x18,0x35,0xa5,0x46, + 0x56,0x0d,0x6b,0xa8, 0x91,0x1d,0xab,0x58, 0xee,0xc4,0x55,0x6a, 0x6d,0x9a,0x2f,0xa1, + 0xb0,0xcd,0x86,0xc0, 0xc6,0x8c,0xeb,0x86, 0x0a,0xcc,0x10,0x7f, 0x59,0xb1,0x32,0x38, + 0x86,0x22,0xc6,0x4a, 0x82,0xa9,0xe9,0x98, 0xcb,0xff,0x5c,0x90, 0x12,0x10,0x0a,0x60, + 0x9e,0x0b,0x1f,0xe7, 0x72,0x29,0x9c,0x5c, 0xa5,0xae,0xb0,0xa8, 0xe4,0x34,0x92,0xcb, + 0x01,0x0b,0x28,0xc9, 0x72,0xd6,0xbe,0x0b, 0x7c,0xcb,0x52,0x38, 0x0a,0x7a,0x58,0x15, + 0xd9,0x96,0x36,0x3e, 0x64,0xcc,0x80,0x6a, 0x27,0x0e,0x50,0x53, 0xa1,0xa1,0x40,0x32, + // 0xb00: + 0x79,0xfe,0x0e,0xc2, 0x0a,0x11,0x1d,0xcf, 0x60,0x8a,0x41,0x1a, 0x2c,0xec,0xc8,0x36, + 0xa4,0xad,0xab,0x10, 0x7c,0x04,0x6f,0x64, 0x67,0x01,0x8b,0xb3, 0xdd,0xb2,0xaf,0x16, + 0x04,0x6c,0x56,0xe5, 0x0b,0xc3,0x12,0x10, 0xec,0x0c,0xab,0x19, 0x70,0x09,0x73,0x52, + 0xba,0x81,0x2d,0x24, 0x88,0x8c,0x33,0x25, 0x3e,0xab,0x56,0x20, 0x12,0x0e,0xd9,0x08, + 0x5e,0x63,0x00,0xc4, 0x00,0xb0,0xf0,0x62, 0x72,0xa4,0xc3,0xa8, 0x52,0x90,0xc9,0x63, + 0xd0,0x35,0x1c,0x7a, 0x54,0x2d,0x0f,0x81, 0x06,0x32,0x23,0x6b, 0xe3,0x07,0xca,0xd5, + 0xec,0xac,0xad,0xad, 0x52,0xc2,0x8a,0x6b, 0x24,0xc5,0x8a,0xdd, 0x27,0x21,0x08,0x1e, + 0xe7,0x60,0x6d,0xfd, 0x2e,0x37,0x11,0x16, 0x8d,0x39,0x1b,0x6a, 0xf6,0x93,0x41,0x82, + 0x63,0x72,0x49,0x2f, 0xf5,0x13,0x6d,0xd1, 0x9e,0x59,0xb8,0x4e, 0x78,0xd4,0x50,0xe5, + 0x66,0x63,0x03,0xa1, 0x8a,0xac,0x43,0xa0, 0x91,0xd6,0x2b,0xb6, 0x1d,0x86,0xf7,0xbd, + 0x98,0xc3,0x73,0xaf, 0x21,0xf6,0xc2,0x0c, 0x1d,0x7a,0x0b,0xce, 0xeb,0x05,0x4b,0xae, + 0x51,0xc6,0x33,0xd0, 0xc7,0xfe,0x2e,0xe4, 0x8a,0x70,0xa1,0x20, 0x3d,0xb3,0x2e,0x4a, + // 0xbc0 = 3008 Bytes +}; + +static BZ2Manager_t codehandler_mgr + = { codehandler_bz2, sizeof(codehandler_bz2), 0, 0 }; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct staticr_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSTR ( staticr_t * str ) +{ + memset(str,0,sizeof(*str)); + memset(str->dol_flags,'-',sizeof(str->dol_flags)-1); + str->fname = EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSTR ( staticr_t * str ) +{ + if (str) + { + if (str->data_alloced) + FREE(str->data); + FreeString(str->fname); + + InitializeSTR(str); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool CheckSizeSTR ( staticr_t * str ) +{ + DASSERT(str); + + switch (str->data_size) + { + case 4903876: + str->mode = STR_M_PAL; + break; + + case 4902804: + str->mode = STR_M_USA; + break; + + case 4901564: + str->mode = STR_M_JAP; + break; + + case 4905468: + str->mode = STR_M_KOR; + break; + + default: + str->mode = STR_M_UNKNOWN; + str->str_status = STR_S_ANALYZED | STR_S_WRONG_SIZE; + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadSTR +( + staticr_t * str, // valid pointer + bool initialize, // true: initialize 'bmg' + ccp fname, // filename of source + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(str); + DASSERT(fname); + TRACE("LoadSTR(%d,%d) fbname=%s\n",initialize,ignore_no_file,fname); + + if (initialize) + InitializeSTR(str); + else + ResetSTR(str); + + str->data_alloced = true; + enumError err = OpenReadFILE( fname, 0, ignore_no_file, + &str->data, &str->data_size, &str->fname, &str->fatt ); + if (err) + return err; + +// [[analyse-magic]] + str->fform = GetByMagicFF(str->data,str->data_size,str->data_size); + switch(str->fform) + { + case FF_STATICR: + if (!CheckSizeSTR(str)) + break; + str->is_staticr = true; + return ERR_OK; + + case FF_DOL: + str->is_dol = true; + return ERR_OK; + + default: + break; + } + + if (!ignore_no_file) + ERROR0(ERR_WARNING,"Neither StaticR nor DOL file: %s\n",fname); + return ERR_WARNING; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ReplaceRegionChar ( staticr_t *str, char *buf, uint buf_size, ccp fname ) +{ + DASSERT(str); + DASSERT(buf); + DASSERT(buf_size>100); + + bool stat = false; + const char reg_char = GetStrModeRegionChar(str->mode); + + if (reg_char) + { + StringCopyS(buf,buf_size,fname); + char *p = strrchr(buf,'/'); + if (!p) + p = buf; + + for ( ; *p; p++ ) + if ( *p == '@' ) + { + *p = reg_char; + stat = true; + } + } + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dol_sect_info_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const dol_sect_info_t dol_info_pal[DOL_NN_SECTIONS] = +{ + { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0, + { 0x67,0xa0,0xfd,0x48,0x8f, 0x9b,0xe2,0xb8,0xa1,0x35, + 0xed,0x61,0x48,0xe7,0x41, 0x0b,0xb6,0x5b,0xdf,0x2f }}, + { 1, "T1", 0x2560, 0x800072c0, 0x23db20, 1,0,1, 0, + { 0x1c,0xbe,0x24,0xaf,0x98, 0x6a,0x6e,0x3e,0xbe,0xa1, + 0x5c,0xc9,0x66,0x49,0xce, 0xc7,0x7d,0x73,0x75,0x7b }}, + { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} }, + { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} }, + { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} }, + { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} }, + { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} }, + { 7, "D0", 0x240080, 0x80006460, 0x5c0, 1,0,1, 0, + { 0xc8,0xbf,0xf1,0x5d,0x1d, 0x5a,0x05,0x77,0xbc,0xec, + 0x42,0x05,0xe7,0x11,0x76, 0x64,0xa1,0x87,0xfd,0x3d }}, + { 8, "D1", 0x240640, 0x80006a20, 0x8a0, 1,0,1, 0, + { 0x6c,0xe1,0x67,0x91,0x92, 0x92,0x44,0x05,0x12,0x12, + 0x64,0xff,0x57,0x7f,0xae, 0x81,0xe5,0xa8,0xf8,0x7a }}, + { 9, "D2", 0x240ee0, 0x80244de0, 0xc0, 1,0,1, 0, + { 0xf6,0x7f,0x2b,0x81,0x56, 0x65,0x9d,0xac,0xd2,0x56, + 0xb0,0xcd,0x6c,0xca,0xfd, 0x72,0x39,0xe5,0x3d,0x2e }}, + { 10, "D3", 0x240fa0, 0x80244ea0, 0x20, 1,0,1, 0, + { 0xeb,0x28,0xbc,0x20,0x23, 0x0a,0xce,0xa4,0xc9,0xe0, + 0x6d,0x67,0xee,0x25,0x20, 0x1c,0xa1,0x55,0x39,0x7d }}, + { 11, "D4", 0x240fc0, 0x80244ec0, 0x136c0, 1,0,1, 0, + { 0x4f,0xd6,0xcf,0x0f,0x65, 0x76,0xc1,0x23,0x96,0x86, + 0xc8,0x37,0x1b,0x95,0x93, 0x9f,0xba,0x72,0xf6,0xc5 }}, + { 12, "D5", 0x254680, 0x80258580, 0x4bac0, 1,0,1, 0, + { 0x4a,0x9a,0x76,0x06,0x69, 0x35,0x0b,0xd2,0x54,0xd2, + 0xbf,0x22,0xac,0xd9,0xf9, 0xc8,0x13,0xcf,0x95,0xd8 }}, + { 13, "D6", 0x2a0140, 0x80384c00, 0x13c0, 1,0,1, 0, + { 0xaa,0x9a,0x25,0x46,0xb8, 0xd9,0x57,0x5e,0x22,0x6d, + 0x72,0x2f,0x71,0xff,0x86, 0x49,0xc6,0x12,0x2d,0xcf }}, + { 14, "D7", 0x2a1500, 0x80386fa0, 0x21a0, 1,0,1, 0, + { 0x43,0x0d,0x6f,0xd3,0xe0, 0x12,0x72,0x2f,0x44,0x5b, + 0x9e,0xdc,0x92,0x1b,0xe0, 0x62,0x8e,0x72,0xe8,0x07 }}, + { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} }, + { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} }, + { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} }, + { 18, "BSS", 0, 0x802a4080, 0xe50fc, 1,0,0, 0, {0} }, + { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const dol_sect_info_t dol_info_usa[DOL_NN_SECTIONS] = +{ + { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0, + { 0x5e,0x16,0x94,0x9f,0x50, 0x5f,0xc6,0xc3,0x6e,0xca, + 0x76,0x5b,0x8a,0x07,0x6e, 0x23,0x68,0x77,0x84,0x67 }}, + { 1, "T1", 0x2560, 0x800072c0, 0x23da80, 1,0,1, 0, + { 0x3a,0x6d,0x28,0xc3,0xdd, 0xc8,0x10,0xcc,0xf0,0xb2, + 0x7f,0xe7,0xd8,0x7d,0x12, 0x8f,0x62,0x72,0xd0,0xed }}, + { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} }, + { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} }, + { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} }, + { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} }, + { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} }, + { 7, "D0", 0x23ffe0, 0x80006460, 0x5c0, 1,0,1, 0, + { 0x47,0x6d,0x32,0x6c,0x21, 0xaa,0x65,0xf2,0x9e,0x69, + 0xf9,0xde,0xd2,0xaa,0xb6, 0xc2,0x76,0x94,0x19,0x09 }}, + { 8, "D1", 0x2405a0, 0x80006a20, 0x8a0, 1,0,1, 0, + { 0x73,0x5b,0x90,0x85,0x58, 0x13,0x3f,0x1e,0x56,0x67, + 0xeb,0x61,0x3a,0xa9,0xe9, 0x13,0xc4,0xd3,0xd5,0x6f }}, + { 9, "D2", 0x240e40, 0x80244d40, 0xc0, 1,0,1, 0, + { 0x4a,0x87,0x49,0xbd,0xa1, 0x78,0x9f,0x8c,0x9c,0x9e, + 0x25,0x6a,0x6b,0xda,0x19, 0x7f,0x7f,0xc7,0xf5,0x7f }}, + { 10, "D3", 0x240f00, 0x80244e00, 0x20, 1,0,1, 0, + { 0xe1,0x05,0x64,0x2d,0x86, 0x59,0xc9,0xac,0x76,0xde, + 0x8c,0x84,0xd0,0x67,0xc6, 0x9a,0x37,0x2f,0xd5,0xab }}, + { 11, "D4", 0x240f20, 0x80244e40, 0x13420, 1,0,1, 0, + { 0xbd,0x62,0x9a,0x5e,0x1b, 0xda,0xf5,0x46,0xe8,0x04, + 0xcb,0xa1,0xfa,0xca,0xac, 0x38,0xac,0x7a,0xe6,0xaf }}, + { 12, "D5", 0x254340, 0x80258260, 0x47aa0, 1,0,1, 0, + { 0x9b,0x08,0x11,0xf2,0xb5, 0xed,0x17,0x69,0x7e,0x4b, + 0xb3,0xef,0xb4,0x65,0x23, 0x78,0x21,0x24,0x27,0xc1 }}, + { 13, "D6", 0x29bde0, 0x80380880, 0x13c0, 1,0,1, 0, + { 0x98,0x55,0xd3,0xa2,0x15, 0x10,0xe6,0xd4,0x78,0xa8, + 0x40,0x51,0xe5,0x51,0x77, 0x04,0x10,0x18,0x29,0x63 }}, + { 14, "D7", 0x29d1a0, 0x80382c20, 0x21a0, 1,0,1, 0, + { 0x9b,0x2c,0x27,0x2b,0x6b, 0x43,0xff,0xce,0x11,0x05, + 0x92,0x29,0x42,0x26,0x6f, 0x9c,0x59,0xd6,0x55,0xe8 }}, + { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} }, + { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} }, + { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} }, + { 18, "BSS", 0, 0x8029fd00, 0xe50fc, 1,0,0, 0, {0} }, + { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const dol_sect_info_t dol_info_jap[DOL_NN_SECTIONS] = +{ + { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0, + { 0xa6,0x86,0x2e,0xbc,0x13, 0xdd,0x9e,0x7e,0xfa,0xa9, + 0x0d,0x49,0x19,0x5f,0x9b, 0xe6,0x30,0x19,0x01,0x4e }}, + { 1, "T1", 0x2560, 0x800072c0, 0x23da40, 1,0,1, 0, + { 0x93,0xb4,0x6b,0xbe,0xab, 0xf5,0x7d,0x98,0x93,0xf2, + 0x16,0x07,0x4f,0xc0,0x44, 0x82,0x01,0xd1,0x42,0x40 }}, + { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} }, + { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} }, + { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} }, + { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} }, + { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} }, + { 7, "D0", 0x23ffa0, 0x80006460, 0x5c0, 1,0,1, 0, + { 0x7f,0xcd,0xe8,0xc8,0x06, 0xfb,0x10,0xbc,0x81,0x9c, + 0xb8,0xbb,0xa0,0x72,0x6a, 0xd8,0x75,0xb2,0xb7,0x6a }}, + { 8, "D1", 0x240560, 0x80006a20, 0x8a0, 1,0,1, 0, + { 0x5a,0x2c,0x70,0x39,0x11, 0x58,0xa0,0x71,0xc0,0xb9, + 0x09,0xac,0x3e,0xde,0x55, 0x64,0xb0,0x70,0xb7,0x52 }}, + { 9, "D2", 0x240e00, 0x80244d00, 0xc0, 1,0,1, 0, + { 0xb4,0x63,0xae,0x32,0x03, 0x5d,0xcc,0xa7,0xb0,0x64, + 0xee,0x4a,0x1c,0x24,0xd3, 0x10,0x84,0xd9,0xf7,0xe1 }}, + { 10, "D3", 0x240ec0, 0x80244dc0, 0x20, 1,0,1, 0, + { 0x40,0x65,0xb7,0x71,0x65, 0x6e,0xbc,0x7f,0x04,0x1c, + 0xe3,0x44,0xb5,0x32,0x42, 0x0d,0x43,0x05,0x76,0xa8 }}, + { 11, "D4", 0x240ee0, 0x80244e00, 0x13120, 1,0,1, 0, + { 0xba,0x43,0xcc,0x17,0x8b, 0x01,0x44,0xc0,0x5b,0x38, + 0x59,0x9d,0xfc,0x2c,0x14, 0xcd,0xa6,0x9b,0xc1,0x8a }}, + { 12, "D5", 0x254000, 0x80257f20, 0x4bac0, 1,0,1, 0, + { 0x16,0x24,0x5d,0xad,0xb9, 0x7f,0xe9,0x5f,0x00,0x7f, + 0xef,0x6f,0xec,0xf6,0x24, 0xe5,0x35,0xc1,0x7c,0x38 }}, + { 13, "D6", 0x29fac0, 0x80384580, 0x13c0, 1,0,1, 0, + { 0xb7,0x6b,0x13,0x92,0x1c, 0xad,0x77,0x78,0x92,0xe8, + 0xff,0xe4,0x5d,0x45,0xe6, 0x1c,0x06,0xb8,0x4d,0xdb }}, + { 14, "D7", 0x2a0e80, 0x80386920, 0x21a0, 1,0,1, 0, + { 0x4d,0xc4,0xf5,0x81,0x3b, 0x8b,0x9e,0x5d,0xc3,0x36, + 0xfb,0x91,0xa9,0xc0,0x5b, 0xd7,0x59,0x11,0xee,0x25 }}, + { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} }, + { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} }, + { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} }, + { 18, "BSS", 0, 0x802a3a00, 0xe50fc, 1,0,0, 0, {0} }, + { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const dol_sect_info_t dol_info_kor[DOL_NN_SECTIONS] = +{ + { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0, + { 0x8e,0x71,0x21,0xdf,0x02, 0x9f,0xff,0x15,0x45,0x5b, + 0xc9,0x30,0xdd,0xda,0x1a, 0x84,0x35,0x41,0xe8,0xc3 }}, + { 1, "T1", 0x2560, 0x800072c0, 0x23dea0, 1,0,1, 0, + { 0xfc,0xf6,0x28,0x91,0x85, 0x47,0x11,0xc3,0x4e,0xac, + 0x2e,0x61,0x47,0x53,0x5e, 0xe1,0x7c,0x3c,0xb8,0xe6 }}, + { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} }, + { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} }, + { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} }, + { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} }, + { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} }, + { 7, "D0", 0x240400, 0x80006460, 0x5c0, 1,0,1, 0, + { 0x2d,0x62,0xc1,0xdb,0x2a, 0x82,0x64,0x0a,0xa7,0x61, + 0xdd,0x6d,0xfc,0x4a,0x4d, 0x59,0xb4,0x51,0x6c,0x27 }}, + { 8, "D1", 0x2409c0, 0x80006a20, 0x8a0, 1,0,1, 0, + { 0x8b,0x99,0x7f,0x81,0xdf, 0x8b,0x0e,0x1d,0x96,0x3e, + 0xbf,0xe7,0xa3,0xb2,0x0e, 0x92,0x7d,0xcc,0x5f,0x5b }}, + { 9, "D2", 0x241260, 0x80245160, 0xc0, 1,0,1, 0, + { 0x7f,0xcd,0xfb,0xdd,0x3e, 0x2b,0xf4,0xf2,0x35,0xd0, + 0x4a,0x57,0xcb,0x9a,0xb0, 0xa2,0x2a,0x86,0xf0,0xa5 }}, + { 10, "D3", 0x241320, 0x80245220, 0x20, 1,0,1, 0, + { 0xcd,0x27,0xcf,0xe9,0xce, 0x3f,0x28,0xbc,0x64,0xb3, + 0xb2,0x05,0x70,0x10,0xaf, 0x9a,0x5c,0xe6,0x04,0xf7 }}, + { 11, "D4", 0x241340, 0x80245240, 0x13100, 1,0,1, 0, + { 0x23,0x8c,0xc9,0x84,0xd1, 0x07,0x8a,0x1b,0xc4,0x56, + 0x1b,0x56,0x30,0xe0,0x1a, 0x4d,0xbc,0xa1,0x48,0x0c }}, + { 12, "D5", 0x254440, 0x80258340, 0x39d00, 1,0,1, 0, + { 0x4a,0x10,0xfb,0x98,0x57, 0xb0,0xa5,0x96,0xb7,0xf6, + 0x92,0x06,0xed,0xf2,0xdf, 0x04,0x5e,0x6d,0x63,0x55 }}, + { 13, "D6", 0x28e140, 0x80372c00, 0x13e0, 1,0,1, 0, + { 0xad,0x8f,0x5f,0x3d,0xf0, 0x9d,0xa5,0x26,0x9f,0x71, + 0xfb,0xeb,0x7b,0x68,0x99, 0x72,0x71,0xdc,0x65,0xcc }}, + { 14, "D7", 0x28f520, 0x80374fc0, 0x21a0, 1,0,1, 0, + { 0xc3,0x04,0xfc,0x7f,0xf8, 0x6f,0xf0,0x39,0xa9,0xe7, + 0xda,0x78,0xf3,0x1b,0xfa, 0x41,0x91,0x7d,0xf1,0x3c }}, + { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} }, + { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} }, + { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} }, + { 18, "BSS", 0, 0x80292080, 0xe511c, 1,0,0, 0, {0} }, + { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +const dol_sect_info_t * GetInfoDOL ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_PAL: return dol_info_pal; + case STR_M_USA: return dol_info_usa; + case STR_M_JAP: return dol_info_jap; + case STR_M_KOR: return dol_info_kor; + default: return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GetSectionDOL +( + staticr_t * str, // valid pointer + dol_sect_info_t * ret_info, // valid pointer: returned data + uint section, // 0 .. DOL_NN_SECTIONS + // (7*TEXT, 11*DATA, BSS, ENTRY ) + bool calc_hash // true: calculate SHA1 hash if data available + +) +{ + DASSERT(str); + DASSERT(ret_info); + + bool stat = GetDolSectionInfo( ret_info,( dol_header_t*)str->data, + str->data_size, section ); + if ( calc_hash && stat && ret_info->data_valid ) + { + ret_info->hash_valid = true; + SHA1( ret_info->data, ret_info->size, ret_info->hash ); + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +const DolSectionMap_t * FindSectionDOL ( uint size, u8 *hash ) +{ + if ( !size || !hash ) + return 0; + + uint beg = 0; + uint end = N_DOL_SECTION_MAP - 1; + + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + const DolSectionMap_t *dsm = DolSectionMap + idx; + if ( size < dsm->size ) + end = idx - 1 ; + else if ( size > dsm->size ) + beg = idx + 1; + else + { + const int stat = memcmp(hash,dsm->hash,sizeof(dsm->hash)); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + return dsm; + } + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ct-code dol extensions /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ct_dol_ext_t]] + +typedef struct ct_dol_ext_t +{ + u32 t2_addr; + BZ2Source_t t2_src; + + u32 d8_addr; + BZ2Source_t d8_src; +} +ct_dol_ext_t; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ct_dol_ext_t ct_dol_ext_pal = +{ + 0x802a5000, + { "rmcp/boot_code.bin;boot_code.bin", + ctcode_boot_code_pal_bz2, sizeof(ctcode_boot_code_pal_bz2), + FF_CT0_CODE }, + + 0x80002800, + { "rmcp/boot_data.bin;boot_data.bin", + ctcode_boot_data_pal_bz2, sizeof(ctcode_boot_data_pal_bz2), + FF_CT0_DATA }, +}; + +//----------------------------------------------------------------------------- + +ct_dol_ext_t ct_dol_ext_usa = +{ + 0x802a5000, + { "rmce/boot_code.bin;boot_code.bin", + ctcode_boot_code_usa_bz2, sizeof(ctcode_boot_code_usa_bz2), + FF_CT0_CODE }, + + 0x80002800, + { "rmce/boot_data.bin;boot_data.bin", + ctcode_boot_data_usa_bz2, sizeof(ctcode_boot_data_usa_bz2), + FF_CT0_DATA }, +}; + +//----------------------------------------------------------------------------- + +ct_dol_ext_t ct_dol_ext_jap = +{ + 0x802a5000, + { "rmcj/boot_code.bin;boot_code.bin", + ctcode_boot_code_jap_bz2, sizeof(ctcode_boot_code_jap_bz2), + FF_CT0_CODE }, + + 0x80002800, + { "rmcj/boot_data.bin;boot_data.bin", + ctcode_boot_data_jap_bz2, sizeof(ctcode_boot_data_jap_bz2), + FF_CT0_DATA }, +}; + +//----------------------------------------------------------------------------- + +ct_dol_ext_t ct_dol_ext_kor = +{ + 0x802a5000, + { "rmck/boot_code.bin;boot_code.bin", + 0, 0, // [[2do]] [[ct-korea]] + FF_CT0_CODE }, + + 0x80002800, + { "rmck/boot_data.bin;boot_data.bin", + 0, 0, // [[2do]] [[ct-korea]] + FF_CT0_DATA }, +}; + +//----------------------------------------------------------------------------- + +ct_dol_ext_t * GetCtExtDOL ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_PAL: return &ct_dol_ext_pal; + case STR_M_USA: return &ct_dol_ext_usa; + case STR_M_JAP: return &ct_dol_ext_jap; + case STR_M_KOR: return &ct_dol_ext_kor; + default: return 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchHTTPS() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchHTTPS_HTTP +( + staticr_t * str, // valid pointer + https_mode_t mode +) +{ + DASSERT(str); + char buf[200], domain_buf[100]; + + static const char nwn[] = "nintendowifi.net"; + static uint nwn_len = sizeof(nwn) - 1; + + const bool mode_sake = mode == HTTPS_SAKE0 || mode == HTTPS_SAKE1; + const bool repl_domain = mode == HTTPS_DOMAIN || mode_sake; + + if ( repl_domain && strlen(wifi_domain) > nwn_len ) + { + ERROR0(ERR_SEMANTIC, + "Domain must be smaller than %u characters if using --https=DOMAIN: %s", + nwn_len+1, wifi_domain ); + opt_https = mode = HTTPS_HTTP; // fall back + } + + uint count = 0; + const str_server_patch_t *pat; + const str_flags_t dol_flag = str->is_dol ? STR_F_IS_DOL : 0; + + for ( pat = ServerPatch; pat->mode; pat++ ) + { + // dirty code: pat->flags is either 0(srel), 1(mdol), 2(mdol+special) + if ( pat->mode != str->mode || (pat->flags & STR_F_IS_DOL) != dol_flag ) + continue; + if ( pat->flags & STR_F_IS_OPTIONAL && opt_wc24 ) + continue; + + const str_server_list_t *sl = ServerList + pat->serv_type; + memset(buf,0,sizeof(buf)); + + if ( pat->flags & STR_F_RESTORE_HTTPS ) + { + strncpy(buf,"https://",sizeof(buf)); + } + else if ( pat->flags & STR_F_IS_SPECIAL && wifi_domain_is_wiimmfi ) + { + switch (pat->serv_type) + { + case SERV_NASW: + snprintf(buf,sizeof(buf),"http://naswii.%s/ac",wifi_domain); + break; + + case SERV_NASW_DEV: + snprintf(buf,sizeof(buf),"https://main.nas.%s/p%c", + wifi_domain, + tolower((int)VersusPointsInfo[pat->mode].region_code) ); + break; + + case SERV_NASW_TEST: + snprintf(buf,sizeof(buf),"http://ca.nas.%s/ca",wifi_domain); + break; + + default: + ASSERT(0); + break; + } + PRINT("STR_F_IS_SPECIAL: %u %s\n",pat->serv_type,buf); + } + else if ( mode_sake && pat->serv_type == SERV_SAKE ) + { + // special case for SAKE servers + + if (!pat->is_http) + snprintf(buf,sizeof(buf),"gs.%s%s", nwn, pat->param ); + else + { + snprintf(buf,sizeof(buf),"%s://%s%s", + mode == HTTPS_SAKE0 && pat->is_http == 2 ? "https" : "http", + sl->domain, pat->param ); + } + } + else if ( pat->serv_type == SERV_NONE ) + { + DASSERT(pat->is_http==2); + char * dest = StringCopyS(buf,sizeof(buf), + mode == HTTPS_RESTORE ? "https" : "http" ); + if ( pat->url_len == 8 ) + StringCopyE(dest,buf+sizeof(buf),"://"); + } + else if (!pat->is_http) + { + snprintf(buf,sizeof(buf),"gs.%s%s", + repl_domain ? wifi_domain : nwn, pat->param); + } + else if ( repl_domain ) + { + snprintf(buf,sizeof(buf),"http://%.*s%s%s", + sl->domain_len - nwn_len, sl->domain, + wifi_domain, pat->param ); + } + else + { + ccp domain = sl->domain; + if ( mode == HTTPS_HTTP && !memcmp(domain,"naswii.",7) ) + { + snprintf(domain_buf,sizeof(domain_buf),"nas.%s",domain+7); + domain = domain_buf; + } + + snprintf(buf,sizeof(buf),"%s://%s%s", + mode == HTTPS_RESTORE && pat->is_http == 2 ? "https" : "http", + domain, pat->param ); + } + + char *data = (char*)str->data + pat->offset; + if (memcmp(data,buf,pat->url_len+1)) + { + //HexDump16(stderr,0,0,data,pat->url_len+1); + //HexDump16(stderr,0,0,buf,pat->url_len+1); + memcpy(data,buf,pat->url_len+1); + count++; + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchHTTPS +( + staticr_t * str // valid pointer +) +{ + switch (opt_https) + { + case HTTPS_RESTORE: + case HTTPS_HTTP: + case HTTPS_DOMAIN: + case HTTPS_SAKE0: + case HTTPS_SAKE1: + return PatchHTTPS_HTTP(str,opt_https); + + case HTTPS_NONE: + case HTTPS__N: + break; + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main.dol support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyzeDOL +( + staticr_t * str, // valid pointer + bool restore // true: restore patched data +) +{ + DASSERT(str); + str->dol_status = DOL_S_ANALYZED; + + if (PatchHTTPS_HTTP(str,HTTPS_RESTORE)) + str->dol_status |= DOL_S_HTTPS; + + static str_mode_t tab_mode[] + = { STR_M_PAL, STR_M_USA, STR_M_JAP, STR_M_KOR, STR_M_UNKNOWN }; + + static sha1_hash_t tab_sha1_mode[] = + { + // STR_M_PAL + { 0x12, 0xb0, 0x2a, 0xf1, 0x9e, 0x07, 0xd3, 0x6d, 0x8a, 0x38, + 0xc9, 0xa7, 0xfe, 0x89, 0x15, 0x55, 0x2f, 0x2e, 0x58, 0x5f }, + + // STR_M_USA + { 0x0a, 0xed, 0x61, 0x96, 0x28, 0x11, 0xa1, 0x64, 0xf5, 0x8c, + 0xd7, 0xed, 0xfc, 0x71, 0x6e, 0xeb, 0x05, 0xb8, 0x4d, 0xab }, + + // STR_M_JAP + { 0x69, 0xed, 0xa1, 0xa4, 0x05, 0xde, 0x0e, 0xe1, 0x29, 0x09, + 0x15, 0x4f, 0xd5, 0xdc, 0x2c, 0x33, 0x62, 0x9f, 0x6f, 0x37 }, + + // STR_M_KOR + { 0x86, 0x2a, 0x38, 0xf2, 0xd7, 0x56, 0xe5, 0x96, 0x2c, 0xa1, + 0x9f, 0xd0, 0x62, 0xd0, 0xbd, 0x55, 0x67, 0x10, 0xed, 0x7e }, + }; + + if ( str->data_size < 0x1100 ) + return; + + sha1_hash_t hash; + SHA1(str->data+0x100,0x1000,hash); + + uint i; + for ( i = 0; tab_mode[i] != STR_M_UNKNOWN; i++ ) + { + if (!memcmp(tab_sha1_mode+i,hash,sizeof(hash))) + { + str->is_main = true; + str->mode = tab_mode[i]; + str->dol_status |= DOL_S_KNOWN; + + SHA1(str->data,str->data_size,hash); + const DolSectionMap_t *dsm = FindSectionDOL(str->data_size,hash); + if ( dsm && dsm->mode == str->mode && dsm->dol_stat ) + { + str->dol_status |= dsm->dol_stat; + str->dol_info = dsm->info; + if ( str->dol_status & DOL_S_WIIMMFI ) + str->dol_status &= ~DOL_S_HTTPS; + else if ( str->dol_status & DOL_S_HTTPS ) + str->dol_status &= ~DOL_S_ORIG; + } + break; + } + } + str->dol_info_flags = ( str->dol_info_flags & ~DIF_MODE ) + | ( str->mode - STR_M_PAL & DIF_MODE ); + + + //-- check VBI + + const u32 vbi_addr = GetVbiAddress(str); + if (vbi_addr) + { + dol_header_t *dh = (dol_header_t*)str->data; + const u32 offset = GetDolOffsetByAddr(dh,vbi_addr,4,0); + if (offset) + { + const u32 cur_value = be32(str->data+offset); + if ( cur_value != ORIG_VBI_VALUE ) + { + if (restore) + write_be32(str->data+offset,ORIG_VBI_VALUE); + str->dol_status = str->dol_status & ~DOL_S_ORIG | DOL_S_VBI; + + if ( ( cur_value & 0xff000000 ) == 0x4b000000 ) + { + str->vbi_address = cur_value & 0x00ffffff; + if ( str->vbi_address & 0x00800000 ) + str->vbi_address |= 0xff000000; + str->vbi_address += vbi_addr; + } + } + } + } + + + //-- check patched_by VERSION + + char *id_ptr = GetIdPointer(str); + if (id_ptr) + { + char *id_end = id_ptr + PATCHED_BY_SIZE; + char *end = id_ptr; + while ( end < id_end && (uchar)*end >= ' ' ) + end++; + while ( end > id_ptr && ( end[-1] == '*' || end[-1] == ' ' ) ) + end--; + + memset(str->patched_by,0,sizeof(str->patched_by)); + if ( id_ptr < end ) + memcpy(str->patched_by,id_ptr,end-id_ptr); + if (restore) + memset(id_ptr,'*',PATCHED_BY_SIZE); + } + + + //-- check sdkver VERSION + + const u32 vers_addr = GetSdkverAddress(str->mode); + if (vbi_addr) + { + dol_header_t *dh = (dol_header_t*)str->data; + const u32 offset = GetDolOffsetByAddr(dh,vers_addr,8,0); + if ( offset && memcmp(str->data+offset,ORIG_SDK_VERS,8) ) + { + memcpy(str->sdk_version,str->data+offset,8); + str->dol_status = str->dol_status & ~DOL_S_ORIG | DOL_S_SDKVER; + if (restore) + memcpy(str->data+offset,ORIG_SDK_VERS,8); + } + } + + + //-- check second host + + u32 h2_orig, *h2 = GetSecondHostPointer(str,&h2_orig); + if (h2) + { + if ( ntohl(*h2) == ASM_NOP ) + { + str->dol_status = str->dol_status & ~DOL_S_ORIG | DOL_S_2HOST; + if (restore) + *h2 = htonl(h2_orig); + } + } + + + //-- finish + + if (!str->dol_info_flags_set) + { + str->dol_info_flags_set = true; + if ( str->dol_status & DOL_S_ORIG ) + str->dol_info_flags |= DIF_ORIG; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintStatusDOL +( + FILE * f, // destination file + int indent, // indention + staticr_t * str, // valid pointer + int long_mode // >0: print more track details if changed +) +{ + indent = NormalizeIndent(indent); + if (!(str->dol_status&DOL_S_ANALYZED)) + AnalyzeDOL(str,true); + + #ifdef TEST + if ( opt_new > 0 ) + { + u32 off1 = GetVbiAddress(str); + u32 off2 = FindDolAddressOfVBI(str->data,str->data_size); + printf("VBI: %08x %c= %08x : %s\n", + off1, off1 == off2 ? '=' : '!', + off2, str->fname ); + } + #endif + + switch (str->mode) + { + case STR_M_PAL: + fprintf(f,"%*sPAL version of main.dol found.\n", indent,"" ); + break; + + case STR_M_USA: + fprintf(f,"%*sNTSC/USA version of main.dol found.\n", indent,"" ); + break; + + case STR_M_JAP: + fprintf(f,"%*sNTSC/JAP version of main.dol found.\n", indent,"" ); + break; + + case STR_M_KOR: + fprintf(f,"%*sNTSC/KOREA version of main.dol found.\n", indent,"" ); + break; + + default: + fprintf(f,"%*sUnknown main.dol found.\n", indent,"" ); + return; + } + + if (str->dol_cleaned) + fprintf(f,"%*s- DOL cleaned before analysis.\n", indent,""); + + if ( str->patched_by[0] ) + fprintf(f,"%*s- Patched by: %s\n", indent,"", str->patched_by ); + + + if ( str->dol_status & DOL_S_HTTPS ) + { + fprintf(f,"%*s- Known 'https://' URLs modified.\n", indent,""); + } + else if (str->dol_info) + { + fprintf(f,"%*s- %s\n", indent,"",str->dol_info); + return; + } + + const dol_header_t *dh = (dol_header_t*)str->data; + if ( str->dol_status & DOL_S_VBI ) + { + if (str->vbi_address) + fprintf(f,"%*s- VBI hook to address 0x%08x%s found.\n", + indent,"", str->vbi_address, + GetDolSectionAddrInfo(dh,str->vbi_address,4) ); + else + fprintf(f,"%*s- VBI hook found.\n", indent,""); + } + + #ifdef HAVE_WIIMM_EXT + if ( str->dol_status & DOL_S_SDKVER ) + fprintf(f,"%*s- Value of 'sdkver' changed from '001000' to '%s'.\n", + indent,"", str->sdk_version ); + #endif + + if ( str->dol_status & DOL_S_2HOST ) + fprintf(f,"%*s- Suppress second 'Host:' header at login.\n", + indent,"" ); + + const dol_sect_info_t *orig = GetInfoDOL(str->mode); + if (orig) + { + uint sect; + for ( sect = 0; sect < DOL_NN_SECTIONS; sect++, orig++ ) + { + dol_sect_info_t info; + GetSectionDOL(str,&info,sect,true); + noPRINT("%-4s: %d,%d : %8x %8x %8x\n", + info.name, info.sect_valid, info.hash_valid, + info.off, info.addr, info.size ); + + if (info.sect_valid) + { + bool find_section = info.hash_valid; + + if (!orig->sect_valid) + fprintf(f,"%*s- Additional section %s.\n", indent,"", orig->name ); + else if ( orig->size != info.size ) + fprintf(f,"%*s- Size of section %s changed.\n", indent,"", orig->name ); + else if ( orig->hash_valid && memcmp(orig->hash,info.hash,sizeof(info.hash))) + fprintf(f,"%*s- Section %s modified.\n", indent,"", orig->name ); + else if ( orig->addr != info.addr ) + { + if ( sect == DOL_IDX_ENTRY ) + fprintf(f,"%*s- Entry point moved from %08x%s to %08x%s.\n", + indent,"", + orig->addr, GetDolSectionAddrInfo(dh,orig->addr,4), + info.addr, GetDolSectionAddrInfo(dh,info.addr,4) ); + else + fprintf(f,"%*s- Section %s moved.\n", indent,"", orig->name ); + } + else + find_section = false; + + if (find_section) + { + const DolSectionMap_t *dsm = FindSectionDOL(info.size,info.hash); + if (dsm) + printf("%*s- %s\n",indent+3,"",dsm->info); + else if (info.data) + { + bool found = false; + if ( info.addr == 0x80001800 && info.size > 0x800 ) + { + if ( be64(info.data+0x11c) == 0x3c6000d06063c0deull ) + { + found = true; + printf("%*s- Gecko Code Handler (Only)\n",indent+3,""); + } + else if ( be64(info.data+0x770) == 0x3c6000d06063c0deull ) + { + ccp attrib = "full"; + const u64 val = be64(info.data+0x56C); + if ( val == 0x92f8680090786810ull ) + attrib = "Slot A"; + else if ( val == 0x92f8681490786824ull ) + attrib = "Slot B"; + printf("%*s- Gecko Code Handler (%s).\n",indent+3,"",attrib); + found = true; + } + } + + if ( !found && info.addr == 0x802c0000 ) + printf("%*s- Maybe code loader by wstrt.\n",indent+3,""); + } + } + } + else if (orig->sect_valid) + fprintf(f,"%*s- Section %s removed.\n", indent,"", orig->name ); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// versus points /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint GetVersusPointsOffset ( str_mode_t mode ) +{ + switch (mode) + { + case STR_M_PAL: return 0x37fd50; + case STR_M_USA: return 0x37fc60; + case STR_M_JAP: return 0x37fa20; + case STR_M_KOR: return 0x380130; + default: return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[VersusPointsInfo]] + +const VersusPointsInfo_t VersusPointsInfo[STR_M__N] = +{ + { STR_M_UNKNOWN, '0', 0, 0 }, + { STR_M_PAL, 'P', 0x37fd50, 0x06890030 }, + { STR_M_USA, 'E', 0x37fc60, 0x0688bbc0 }, + { STR_M_JAP, 'J', 0x37fa20, 0x0688f680 }, + { STR_M_KOR, 'K', 0x380130, 0x0687e430 }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +str_mode_t opt_cheat_region = STR_M_UNKNOWN; + +int ScanOptCheatRegion ( ccp arg ) +{ + if ( !arg || !*arg ) + { + opt_cheat_region = STR_M_UNKNOWN; + return 0; + } + + static const KeywordTab_t tab[] = + { + { STR_M_PAL, "EUROPE", "PAL", 0 }, + { STR_M_PAL, "AUSTRALIA", "OCEANIA", 0 }, + { STR_M_USA, "AMERICA", "NTSC", 0 }, + { STR_M_JAP, "JAPAN", 0, 0 }, + { STR_M_KOR, "KOREA", 0, 0 }, + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (cmd) + { + opt_cheat_region = cmd->id; + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid cheat region (option --cheat): '%s'\n",arg); + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// analyze helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeTracks +( + staticr_t * str, // valid pointer + u8 * data, // data to analyze and modify + const u32 * offset_tab, // null terminated table with offsets + const u32 * ref_tab, // reference table + uint base_index, // base index of first track + uint n, // number of values in 'ref_tab' + int base_status // base status bit +) +{ + DASSERT(str); + DASSERT(data); + DASSERT(offset_tab); + DASSERT(ref_tab); + DASSERT(n>0); + DASSERT(n<=32); + + const uint cmp_size = n * sizeof(u32); + + uint i; + u32 be_ref[32]; + for ( i = 0; i < n; i++ ) + write_be32( be_ref + i, base_index + ref_tab[i] ); + TRACE_HEXDUMP16(0,0,be_ref,cmp_size); //putchar('\n'); + + uint ok_count = 0; // number of unmodified records + uint mod_count = 0; // number of modified records + + u8 *first = 0; + const u32 *off_ptr; + for ( off_ptr = offset_tab ; *off_ptr; off_ptr++ ) + { + DASSERT( *off_ptr < str->data_size ); + u8 * d = data + *off_ptr; + if (!memcmp(d,be_ref,cmp_size)) + { + ok_count++; + continue; + } + mod_count++; + + if (!first) + first = d; + else if (memcmp(first,d,cmp_size)) + str->str_status |= base_status << STR_S_SHIFT_ORDER_DIFF; + + uint fail_buf[32]; + memset(fail_buf,0,sizeof(fail_buf)); + for ( i = 0; i < n; i++ ) + { + const u32 idx = be32(d+4*i) - base_index; + if ( idx >= n ) + { + str->str_status |= base_status << STR_S_SHIFT_ORDER_DIFF; + break; + } + if ( fail_buf[idx] ) + { + str->str_status |= base_status << STR_S_SHIFT_ORDER_MULTI; + break; + } + fail_buf[idx]++; + } + if ( i == n ) + str->str_status |= base_status << STR_S_SHIFT_ORDER_ALT; + + TRACE_HEXDUMP16(0,*off_ptr,d,cmp_size); //putchar('\n'); + } + + if ( mod_count && ok_count ) + str->str_status |= base_status << STR_S_SHIFT_ORDER_DIFF; + + + //--- normalize + + for ( off_ptr = offset_tab ; *off_ptr; off_ptr++ ) + { + DASSERT( *off_ptr < str->data_size ); + u8 * d = data + *off_ptr; + memcpy(d,be_ref,cmp_size); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeRegion +( + staticr_t * str, // valid pointer + u8 * data // data to analyze and modify +) +{ + DASSERT(str); + DASSERT(data); + + bool is_battle; + for ( is_battle = false; is_battle <= true; is_battle++ ) + { + region_info_t ri; + SetupRegionInfo(&ri,str,is_battle); + + uint ok_stat = ri.flag_known; + + const uint offset_idx = str->mode - STR_ZBI_FIRST; + memset(ri.region,0,MAX_REGION_PATCH*sizeof(*ri.region)); + uint idx, patch_count = 0, unknown_count = 0; + + for ( idx = 0; idx < ri.max_patch; idx++ ) + { + ri.region[idx] = -1; + const region_patch_t *rp = ri.patch_tab + idx; + if (!rp->orig) + break; + + u8 *focus = data + rp->offset[offset_idx]; + u32 val = be32(focus); + if ( val != rp->orig ) + { + patch_count++; + if ( ( val & 0xffff0000 ) == rp->patch ) + { + ri.region[idx] = (u16)val; + if ( ri.region[idx] != ri.region[0] ) + ok_stat = ri.flag_differ; + } + else + unknown_count++; + write_be32(focus,rp->orig); + } + } + + if (unknown_count) + str->str_status |= ri.flag_unknown; + else if (patch_count) + str->str_status |= ok_stat; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeAllRanks +( + staticr_t * str, // valid pointer + u8 * data, // data to analyze and modify + const u32 * offset_tab // null terminated table with offsets +) +{ + DASSERT(str); + DASSERT(data); + DASSERT(offset_tab); + + uint i, patch_count = 0, known_count = 0; + for ( i = 0; offset_tab[i]; i++ ) + { + u8 *d = data + offset_tab[i]; + if (memcmp(d,all_ranks_orig,all_ranks_size)) + { + patch_count++; + if ( memcmp(d,all_ranks_patch1,all_ranks_size) + || memcmp(d,all_ranks_patch2,all_ranks_size)) + { + known_count++; + } + memcpy(d,all_ranks_orig,all_ranks_size); + } + } + + if ( i == known_count ) + { + str->str_status |= STR_S_ALL_RANKS_KNOWN; + } + else if ( patch_count ) + str->str_status |= STR_S_ALL_RANKS_UNKNOWN; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeVsBt +( + staticr_t * str, // valid pointer + u8 * data, // data to analyze and modify + const u32 * offset_tab, // null terminated table with offsets + ccp name, // 'vs' or 'bt' + uint flag // flag to set +) +{ + DASSERT(str); + DASSERT(data); + DASSERT(offset_tab); + + uint i; + for ( i = 0; offset_tab[i]; i++ ) + { + u8 *d = data + offset_tab[i]; + if (strncmp((ccp)d,name,3)) + { + str->str_status |= flag; + memcpy(d,name,2); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeVersusPoints +( + staticr_t * str, // valid pointer + u8 * data // data to analyze and modify +) +{ + DASSERT(str); + DASSERT(data); + + const u32 offset = GetVersusPointsOffset(str->mode); + if (offset) + { + data += offset; + if (memcmp(data,MkwPointsTab_NINTENDO,12*12)) + { + str->str_status |= STR_S_VERSUS_POINTS; + memcpy(data,MkwPointsTab_NINTENDO,12*12); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeCannon +( + staticr_t * str, // valid pointer + u8 * data // data to analyze and modify +) +{ + DASSERT(str); + DASSERT(data); + + const u32 off = GetCannonOffset(str); + if ( off && !(str->str_status&STR_S_CANNON) ) + { + memcpy(str->cannon_data,data+off,sizeof(str->cannon_data)); + if (memcmp(data+off,CannonDataLEX+4,sizeof(CannonDataLEX)-4)) + { + str->str_status |= STR_S_CANNON; + memcpy(data+off,CannonDataLEX+4,sizeof(CannonDataLEX)-4); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeMenO +( + staticr_t * str, // valid pointer + u8 * data // data to analyze and modify +) +{ + DASSERT(str); + DASSERT(data); + + const u32 *optr = GetMenuOffsetTab(str->mode); + if (optr) + { + uint count = 0, total = 0; + const int *idx; + for ( idx = meno_patch_idx; *idx >= 0; idx++ ) + { + total++; + u8 *d = data + optr[*idx] + 13; // 13 = 'O' offset + if ( *d == 'O' ) + { + *d = 'u'; + count++; + } + } + + if ( total == count ) + str->str_status |= STR_S_MENO; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// analyze staticr /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzePAL +( + staticr_t * str // valid pointer +) +{ + DASSERT(str); + + + //--- first hash test + + static const u8 good_hash[20] = + { + 0x88,0x7b,0xcc,0x07,0x67, 0x81,0xf5,0xb0,0x05,0xcc, + 0x31,0x7a,0x6e,0x3c,0xc8, 0xfd,0x5f,0x91,0x13,0x00 + }; + + sha1_hash_t hash; + SHA1(str->data,str->data_size,hash); + if (!memcmp(hash,good_hash,sizeof(hash))) + { + if (!( str->str_status & STR_S_HTTPS )) + str->str_status |= STR_S_ORIG; + #ifndef TEST + return; + #endif + } + + + //--- create a copy + + u8 * data = MALLOC(str->data_size); + memcpy(data,str->data,str->data_size); + + + //--- ctgp 4.4 tests + + static const u32 ctgp44[] = + { + // addr orig value ctgp value + //------------------------------- + 0x3d22b8, 0x00000db4, 0xff750030, + 0x3d22c8, 0x00000db4, 0xff750030, + 0x40df78, 0x00012660, 0xff7742e0, + 0x40df88, 0x00012660, 0xff7742e0, + 0,0,0 + }; + + const u32 * ptr; + uint orig_failed = 0, ctgp_failed = 0; + for ( ptr = ctgp44; *ptr; ptr += 3 ) + { + DASSERT( *ptr + 4 <= str->data_size ); + const u32 val = be32( str->data + *ptr ); + if ( val != ptr[1] ) orig_failed++; + if ( val != ptr[2] ) ctgp_failed++; + write_be32( data + *ptr, ptr[1] ); + } + + if (orig_failed) + { + str->str_status |= STR_S_CTGP44_LIKE; + if (!ctgp_failed) + str->str_status |= STR_S_CTGP44; + } + + + //--- analyse tracks and arenas + + AnalyzeTracks( str, data, pal_track_offset, track_pos_default, + MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER ); + AnalyzeTracks( str, data, pal_arena_offset, arena_pos_default, + MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER ); + + + //--- analyse misc + + AnalyzeRegion(str,data); + AnalyzeAllRanks(str,data,pal_all_ranks_offset); + AnalyzeVsBt(str,data,pal_vs_offset,"vs",STR_S_VS); + AnalyzeVsBt(str,data,pal_bt_offset,"bt",STR_S_BT); + AnalyzeVersusPoints(str,data); + AnalyzeCannon(str,data); + AnalyzeMenO(str,data); + + + //--- last hash test + + SHA1(data,str->data_size,hash); + if (memcmp(hash,good_hash,sizeof(hash))) + str->str_status |= STR_S_UNKNOWN_PATCH; + + + //--- clean + + FREE(data); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeUSA +( + staticr_t * str // valid pointer +) +{ + DASSERT(str); + + //--- first hash test + + static const u8 good_hash[20] = + { + 0x07,0x2b,0xa4,0x43,0x82, 0xfd,0xca,0x9b,0xf5,0xc0, + 0xec,0x9d,0x1e,0x9b,0x92, 0x2c,0xaf,0xd9,0x92,0x60 + }; + + sha1_hash_t hash; + SHA1(str->data,str->data_size,hash); + if (!memcmp(hash,good_hash,sizeof(hash))) + { + str->str_status |= STR_S_ORIG; + #ifndef TEST + return; + #endif + } + + + //--- create a copy + + u8 * data = MALLOC(str->data_size); + memcpy(data,str->data,str->data_size); + + + //--- ctgp 4.4 tests + + static const u32 ctgp44[] = + { + // addr orig value ctgp value + //------------------------------- + 0x3d1c38, 0x00000c14, 0xff7546e0, + 0x3d1c48, 0x00000c14, 0xff7546e0, + 0x40cda0, 0x00012098, 0xff778720, + 0x40cdb0, 0x00012098, 0xff778720, + 0,0,0 + }; + + const u32 * ptr; + uint orig_failed = 0, ctgp_failed = 0; + for ( ptr = ctgp44; *ptr; ptr += 3 ) + { + DASSERT( *ptr + 4 <= str->data_size ); + const u32 val = be32( str->data + *ptr ); + if ( val != ptr[1] ) orig_failed++; + if ( val != ptr[2] ) ctgp_failed++; + write_be32( data + *ptr, ptr[1] ); + } + + if (orig_failed) + { + str->str_status |= STR_S_CTGP44_LIKE; + if (!ctgp_failed) + str->str_status |= STR_S_CTGP44; + } + + + //--- anlyze tracks and arenas + + AnalyzeTracks( str, data, usa_track_offset, track_pos_default, + MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER ); + AnalyzeTracks( str, data, usa_arena_offset, arena_pos_default, + MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER ); + + + //--- analyse misc + + AnalyzeRegion(str,data); + AnalyzeAllRanks(str,data,usa_all_ranks_offset); + AnalyzeVsBt(str,data,usa_vs_offset,"vs",STR_S_VS); + AnalyzeVsBt(str,data,usa_bt_offset,"bt",STR_S_BT); + AnalyzeVersusPoints(str,data); + AnalyzeMenO(str,data); + + + //--- last hash test + + SHA1(data,str->data_size,hash); + if (memcmp(hash,good_hash,sizeof(hash))) + str->str_status |= STR_S_UNKNOWN_PATCH; + + + //--- clean + + FREE(data); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeJAP +( + staticr_t * str // valid pointer +) +{ + DASSERT(str); + + //--- first hash test + + static const u8 good_hash[20] = + { + 0x20,0xb4,0x16,0x1d,0x41, 0x5e,0x40,0x92,0xa6,0x29, + 0x9b,0xb2,0x85,0xfa,0xa6, 0x22,0x83,0x5b,0x2b,0x16 + }; + + sha1_hash_t hash; + SHA1(str->data,str->data_size,hash); + if (!memcmp(hash,good_hash,sizeof(hash))) + { + str->str_status |= STR_S_ORIG; + #ifndef TEST + return; + #endif + } + + + //--- create a copy + + u8 * data = MALLOC(str->data_size); + memcpy(data,str->data,str->data_size); + + + //--- anlyze tracks and arenas + + AnalyzeTracks( str, data, jap_track_offset, track_pos_default, + MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER ); + AnalyzeTracks( str, data, jap_arena_offset, arena_pos_default, + MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER ); + + + //--- analyse misc + + AnalyzeRegion(str,data); + AnalyzeAllRanks(str,data,jap_all_ranks_offset); + AnalyzeVsBt(str,data,jap_vs_offset,"vs",STR_S_VS); + AnalyzeVsBt(str,data,jap_bt_offset,"bt",STR_S_BT); + AnalyzeVersusPoints(str,data); + AnalyzeMenO(str,data); + + + //--- last hash test + + SHA1(data,str->data_size,hash); + if (memcmp(hash,good_hash,sizeof(hash))) + str->str_status |= STR_S_UNKNOWN_PATCH; + + + //--- clean + + FREE(data); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void AnalyzeKOR +( + staticr_t * str // valid pointer +) +{ + DASSERT(str); + + //--- first hash test + + static const u8 good_hash[20] = + { + 0xe0,0x00,0x62,0x50,0xb1, 0xe5,0xc1,0xbd,0xee,0x8b, + 0xdb,0x0e,0x78,0x9e,0xa2, 0x00,0xb3,0xa8,0x46,0xe4 + }; + + sha1_hash_t hash; + SHA1(str->data,str->data_size,hash); + if (!memcmp(hash,good_hash,sizeof(hash))) + { + str->str_status |= STR_S_ORIG; + #ifndef TEST + return; + #endif + } + + + //--- create a copy + + u8 * data = MALLOC(str->data_size); + memcpy(data,str->data,str->data_size); + + + //--- anlyze tracks and arenas + + AnalyzeTracks( str, data, kor_track_offset, track_pos_default, + MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER ); + AnalyzeTracks( str, data, kor_arena_offset, arena_pos_default, + MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER ); + + + //--- analyse misc + + AnalyzeRegion(str,data); + AnalyzeAllRanks(str,data,kor_all_ranks_offset); + AnalyzeVsBt(str,data,kor_vs_offset,"vs",STR_S_VS); + AnalyzeVsBt(str,data,kor_bt_offset,"bt",STR_S_BT); + AnalyzeVersusPoints(str,data); + AnalyzeMenO(str,data); + + + //--- last hash test + + SHA1(data,str->data_size,hash); + if (memcmp(hash,good_hash,sizeof(hash))) + str->str_status |= STR_S_UNKNOWN_PATCH; + + + //--- clean + + FREE(data); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AnalyzeSTR +( + staticr_t * str // valid pointer +) +{ + DASSERT(str); + + if (str->is_dol) + { + if (!(str->dol_status&DOL_S_ANALYZED)) + AnalyzeDOL(str,true); + return; + } + + if (!CheckSizeSTR(str)) + return; + + str->str_status = STR_S_ANALYZED; + if (PatchHTTPS_HTTP(str,HTTPS_RESTORE)) + str->str_status |= STR_S_HTTPS; + + switch (str->mode) + { + case STR_M_PAL: + AnalyzePAL(str); + break; + + case STR_M_USA: + AnalyzeUSA(str); + break; + + case STR_M_JAP: + AnalyzeJAP(str); + break; + + case STR_M_KOR: + AnalyzeKOR(str); + break; + + default: + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void PrintStatusRegion +( + FILE * f, // destination file + int indent, // indention + staticr_t * str, // valid pointer + bool is_battle, // true: is battle + int long_mode // >0: print more track details if changed +) +{ + DASSERT(f); + DASSERT(str); + + region_info_t ri; + SetupRegionInfo(&ri,str,is_battle); + + if ( long_mode > 1 + || long_mode > 0 && str->str_status & (ri.flag_unknown|ri.flag_differ) ) + { + fprintf(f,"%*s- %c%s region settings:\n", + indent,"", toupper((int)ri.name[0]), ri.name+1 ); + + const region_patch_t *rp; + for ( rp = ri.patch_tab; rp->orig; rp++ ) + { + const uint offset = rp->offset[str->mode - STR_ZBI_FIRST]; + const u8 *d = str->data + offset; + fprintf(f,"%*s%9x: %02x %02x %02x %02x : ", + indent,"", offset, d[0], d[1], d[2], d[3] ); + const u32 val = be32(d); + if ( val == rp->orig ) + fprintf(f,"%-23s : %s\n","not patched",rp->info); + else if ( (val & 0xffff0000) == rp->patch ) + fprintf(f,"region %4x/hex = %5u : %s\n", + val&0xffff, val&0xffff, rp->info ); + else + fprintf(f,"%-23s : %s\n","unknown patch",rp->info); + } + } + else if ( str->str_status & ri.flag_differ ) + { + fprintf(f,"%*s- Known %s region patch found, regions = ", indent,"", ri.name ); + ccp sep = ""; + uint i; + for ( i = 0; i < ri.max_patch; i++ ) + { + if ( ri.region[i] < 0 ) + fprintf(f,"%sunpatched",sep); + else + fprintf(f,"%s%#x=%u",sep,ri.region[i],ri.region[i]); + sep = ", "; + } + fputc('\n',f); + } + else if ( str->str_status & ri.flag_known ) + fprintf(f,"%*s- Known %s region patch found, region = 0x%02x = %u\n", + indent,"", ri.name, ri.region[0], ri.region[0] ); + else if ( str->str_status & ri.flag_unknown ) + fprintf(f,"%*s- Unknown %s region patch found.\n", indent,"", ri.name ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintStatusSTR +( + FILE * f, // destination file + int indent, // indention + staticr_t * str, // valid pointer + int long_mode // >0: print more track details if changed +) +{ + DASSERT(f); + DASSERT(str); + + if (str->is_dol) + { + PrintStatusDOL(f,indent,str,long_mode); + return; + } + + indent = NormalizeIndent(indent); + if (!(str->str_status&STR_S_ANALYZED)) + AnalyzeSTR(str); + + switch (str->mode) + { + case STR_M_PAL: + fprintf(f,"%*sPAL version of StaticR found.\n", indent,"" ); + break; + + case STR_M_USA: + fprintf(f,"%*sNTSC/USA version of StaticR found.\n", indent,"" ); + break; + + case STR_M_JAP: + fprintf(f,"%*sNTSC/JAP version of StaticR found.\n", indent,"" ); + break; + + case STR_M_KOR: + fprintf(f,"%*sNTSC/KOREA version of StaticR found.\n", indent,"" ); + break; + + default: + fprintf(f,"%*sNo StaticR found.\n", indent,"" ); + break; + } + + const str_status_t st = str->str_status; + + if ( st & STR_S_WRONG_SIZE ) + fprintf(f,"%*s- Wrong file size.\n", indent,"" ); + + if ( st & STR_S_ORIG ) + fprintf(f,"%*s- Original file.\n", indent,"" ); + + if ( st & STR_S_HTTPS ) + fprintf(f,"%*s- Known 'https://' URLs modified.\n", indent,"" ); + + if ( st & STR_S_TRACK_ORDER_ALT ) + fprintf(f,"%*s- Valid alternative track order detected.\n", indent,"" ); + if ( st & STR_S_TRACK_ORDER_MULTI ) + fprintf(f,"%*s- Multiple usage of singe track detected.\n", indent,"" ); + if ( st & STR_S_TRACK_ORDER_BAD ) + fprintf(f,"%*s- Bad track index detected.\n", indent,"" ); + if ( st & STR_S_TRACK_ORDER_DIFF ) + fprintf(f,"%*s- Different track orders detected.\n", indent,"" ); + + if ( long_mode > 1 || long_mode > 0 && st & STR_S_TRACK_ORDER__FAIL ) + { + fprintf(f,"%*s- Track assigning tables:\n", indent,"" ); + const u32 *tab = GetTrackOffsetTab(str); + DASSERT(tab); + for(;;) + { + u32 offset = *tab++; + if (!offset) + break; + fprintf(f,"%*s%9x:", indent,"", offset ); + const u32 * d = (u32*)( str->data + offset ); + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + { + if (!(i%4)) + fputc(' ',f); + fprintf(f,"%u%c", be32(d++), i == MKW_N_TRACKS-1 ? '\n' : ',' ); + } + } + } + + if ( st & STR_S_ARENA_ORDER_ALT ) + fprintf(f,"%*s- Valid alternative arena order detected.\n", indent,"" ); + if ( st & STR_S_ARENA_ORDER_MULTI ) + fprintf(f,"%*s- Multiple usage of singe arena detected.\n", indent,"" ); + if ( st & STR_S_ARENA_ORDER_BAD ) + fprintf(f,"%*s- Bad arena index detected.\n", indent,"" ); + if ( st & STR_S_ARENA_ORDER_DIFF ) + fprintf(f,"%*s- Different arena orders detected.\n", indent,"" ); + + if ( long_mode > 1 || long_mode > 0 && st & STR_S_ARENA_ORDER__FAIL ) + { + fprintf(f,"%*s- Arena assigning tables:\n", indent,"" ); + const u32 *tab = GetArenaOffsetTab(str); + DASSERT(tab); + for(;;) + { + u32 offset = *tab++; + if (!offset) + break; + fprintf(f,"%*s%9x:", indent,"", offset ); + const u32 * d = (u32*)( str->data + offset ); + uint i; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + { + if (!(i%5)) + fputc(' ',f); + fprintf(f,"%u%c", be32(d++), i == MKW_N_ARENAS-1 ? '\n' : ',' ); + } + } + } + + if ( st & STR_S_CTGP44 ) + fprintf(f,"%*s- CTGP 4.4 modification found.\n", indent,"" ); + else if ( st & STR_S_CTGP44_LIKE ) + fprintf(f,"%*s- CTGP 4.4 like modification found.\n", indent,"" ); + + PrintStatusRegion( f, indent, str, false, long_mode ); + PrintStatusRegion( f, indent, str, true, long_mode ); + + if ( st & STR_S_ALL_RANKS_KNOWN ) + fprintf(f,"%*s- Known 'all ranks' patch found.\n", + indent,"" ); + else if ( st & STR_S_ALL_RANKS_UNKNOWN ) + fprintf(f,"%*s- Unknown 'all ranks' patch found.\n", indent,"" ); + + if ( st & STR_S_VS ) + { + fprintf(f,"%*s- Versus identification ('vs') patched:",indent,""); + const u32 *ptr; + for ( ptr = GetVsBtOffsetTab(str,false); *ptr; ptr++ ) + fprintf(f," %.2s",str->data+*ptr); + fputc('\n',f); + } + + if ( st & STR_S_BT ) + { + fprintf(f,"%*s- Battle identification ('bt') patched:",indent,""); + const u32 *ptr; + for ( ptr = GetVsBtOffsetTab(str,true); *ptr; ptr++ ) + fprintf(f," %.2s",str->data+*ptr); + fputc('\n',f); + } + + if ( st & STR_S_VERSUS_POINTS ) + { + const u32 offset = GetVersusPointsOffset(str->mode); + fprintf(f,"%*s- Versus points modified to '%s'.\n", + indent,"", GetMkwPointInfo(str->data+offset)->info ); + } + + if ( st & STR_S_CANNON ) + { + fprintf(f,"%*s- Cannon parameters modified.\n", indent,"" ); + const u8 *orig = CannonDataLEX+4; + const u8 *data = str->cannon_data; + int idx; + for ( idx = 0; orig < CannonDataLEX + sizeof(CannonDataLEX); + idx++, orig += CANNON_SIZE, data += CANNON_SIZE ) + { + if (memcmp(orig,data,CANNON_SIZE)) + { + printf("%*s > Cannon %u orig: %12.4f %12.4f %12.4f %12.4f\n", + indent,"", + idx, bef4(orig), bef4(orig+4), bef4(orig+8), bef4(orig+12) ); + printf("%*s Cannon %u data: %12.4f %12.4f %12.4f %12.4f\n", + indent,"", + idx, bef4(data), bef4(data+4), bef4(data+8), bef4(data+12) ); + } + } + } + + if ( st & STR_S_MENO ) + fprintf(f,"%*s- Known 'MenO' patches found.\n", indent,"" ); + + if ( st & STR_S_UNKNOWN_PATCH ) + fprintf(f,"%*s- Unknown patches found.\n", indent,"" ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchSTR() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchTracks +( + staticr_t * str, // valid pointer + const u32 * offset_tab, // null terminated table with offsets + const u32 * track_tab, // table with track indexes + uint base_index, // base index of first track + uint n // number of values in 'ref_tab' +) +{ + DASSERT(str); + DASSERT(offset_tab); + DASSERT(track_tab); + DASSERT(n>0); + DASSERT(n<=32); + + uint count = 0; + const uint copy_size = n * sizeof(u32); + + uint i; + u32 be_data[32]; + for ( i = 0; i < n; i++ ) + write_be32( be_data + i, base_index + track_tab[i] ); + TRACE_HEXDUMP16(0,0,be_data,copy_size); + + for ( ; *offset_tab; offset_tab++ ) + { + DASSERT( *offset_tab < str->data_size ); + u8 * d = str->data + *offset_tab; + if (memcmp(d,be_data,copy_size)) + { + count++; + memcpy(d,be_data,copy_size); + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchVsBt +( + staticr_t *str, // valid pointer + bool is_bt, // false: VS, true: BT + enum VsBtMode mode, // mode + ccp patch // string to set +) +{ + DASSERT(str); + DASSERT(str); + + uint count = 0; + const u32 *tab = GetVsBtOffsetTab(str,is_bt); + switch (mode) + { + case VSBT_OFF: + break; + + case VSBT_PATCH: + while (*tab) + { + u8 *d = str->data + *tab++; + if ( d[0] != patch[0] || d[1] != patch[1] ) + { + *d++ = patch[0]; + *d = patch[1]; + count++; + } + } + break; + + case VSBT_TEST: + { + char c0 = is_bt ? 'b' : 'v'; + char c1 = '1'; + while (*tab) + { + u8 *d = str->data + *tab++; + if ( d[0] != c0 || d[1] != c1 ) + { + *d++ = c0; + *d = c1; + count++; + } + c1++; + } + } + break; + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +#if 0 // region specific patch functions not needed yet +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchPAL +( + // returns: number of patches + + staticr_t * str // valid pointer +) +{ + DASSERT(str); + DASSERT(str->data); + DASSERT(str->data_size); + DASSERT( str->mode == STR_M_PAL ); + + uint count = 0; + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchUSA +( + // returns: number of patches + + staticr_t * str // valid pointer +) +{ + DASSERT(str); + DASSERT(str->data); + DASSERT(str->data_size); + DASSERT( str->mode == STR_M_USA ); + + uint count = 0; + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchJAP +( + // returns: number of patches + + staticr_t * str // valid pointer +) +{ + DASSERT(str); + DASSERT(str->data); + DASSERT(str->data_size); + DASSERT( str->mode == STR_M_JAP ); + + uint count = 0; + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchKOR +( + // returns: number of patches + + staticr_t * str // valid pointer +) +{ + DASSERT(str); + DASSERT(str->data); + DASSERT(str->data_size); + DASSERT( str->mode == STR_M_KOR ); + + uint count = 0; + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif // region specific patch functions not needed yet +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchRegion +( + staticr_t * str, // valid pointer + bool is_battle // true: is battle +) +{ + DASSERT(str); + region_info_t ri; + SetupRegionInfo(&ri,str,is_battle); + + uint count = 0; + u8 temp[20]; + + DASSERT(ri.opt_region); + if ( *ri.opt_region == SREG_RESTORE ) + { + const region_patch_t *rp; + for ( rp = ri.patch_tab; rp->orig; rp++ ) + { + if ( rp->patch_mode & RPAT_TEST && !*ri.opt_x ) + continue; + + write_be32(temp,rp->orig); + u8 *d = str->data + rp->offset[str->mode - STR_ZBI_FIRST]; + if (memcmp(d,temp,4)) + { + memcpy(d,temp,4); + count++; + } + } + } + else if ( *ri.opt_region >= 0 ) + { + uint val = *ri.opt_region & 0xffff; + uint add = *ri.opt_t; + + const region_patch_t *rp; + for ( rp = ri.patch_tab; rp->orig; rp++ ) + { + if ( rp->patch_mode & RPAT_TEST && !*ri.opt_x ) + continue; + + write_be32( temp, rp->patch | ( rp->patch_mode & RPAT_FF ? val & 0xff : val ) ); + val += add; + u8 *d = str->data + rp->offset[str->mode - STR_ZBI_FIRST]; + if (memcmp(d,temp,4)) + { + memcpy(d,temp,4); + count++; + } + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PatchSTR +( + // returns: number of patches + + staticr_t * str // valid pointer +) +{ + DASSERT(str); + if (!str->data_size) + return 0; + DASSERT(str->data); + + if (str->is_dol) + return PatchDOL(str); + + if (!CheckSizeSTR(str)) + return 0; + + uint count = 0; + + + //--- patch tracks and arenas + + if (opt_tracks) + { + const u32 *tab = GetTrackOffsetTab(str); + if (tab) + count += PatchTracks( str, tab, track_pos, MKW_TRACK_BEG, MKW_N_TRACKS ); + } + + if (opt_arenas) + { + const u32 *tab = GetArenaOffsetTab(str); + if (tab) + count += PatchTracks( str, tab, arena_pos, MKW_ARENA_BEG, MKW_N_ARENAS ); + } + + + //--- patch region + + count += PatchRegion(str,false); + count += PatchRegion(str,true); + + + //--- patch all-ranks + + if ( opt_all_ranks ) + { + ccp p1, p2; + if ( opt_all_ranks < 0 ) + p1 = p2 = all_ranks_orig; + else + { + p1 = all_ranks_patch1; + p2 = all_ranks_patch2; + } + + const u32 *tab = GetAllRanksOffsetTab(str); + if (tab) + { + while (*tab) + { + u8 *d = str->data + *tab++; + if (memcmp(d,p1,all_ranks_size)) + { + memcpy(d,p1,all_ranks_size); + count++; + ccp temp = p1; + p1 = p2; + p2 = temp; + } + } + } + } + + + //--- patch vs & bt + + count += PatchVsBt(str,false,patch_vs_mode,patch_vs_str); + count += PatchVsBt(str,true, patch_bt_mode,patch_bt_str); + + + //--- patch versus points + + if (opt_points_used) + { + const u32 offset = GetVersusPointsOffset(str->mode); + if (offset) + { + u8 *data = str->data + offset; + if (memcmp(data,MkwPointsTab,12*12)) + { + count++; + memcpy(data,MkwPointsTab,12*12); + } + } + } + + + //--- patch cannon params + + u32 offset = GetCannonOffset(str); + if (offset) + { + u8 *data = str->data + offset; + uint i; + for ( i = 0; i < CANNON_N; i++ ) + { + const cannon_param_t *cp = cannon_param+i; + if (cp->valid) + { + uint j; + for ( j = 0; j < CANNON_N_PARAM; j++, data += sizeof(float) ) + write_bef4(data,cp->param[j]); + } + else + data += CANNON_SIZE; + } + } + + + //-- patch 'Menu*' -> 'MenO*' + + if ( opt_meno ) + { + const u32 *optr = GetMenuOffsetTab(str->mode); + if (optr) + { + const int *idx; + for ( idx = meno_patch_idx; *idx >= 0; idx++ ) + { + u8 *d = str->data + optr[*idx] + 13; // 13 = 'O' offset + if ( *d != 'O' ) + { + *d = 'O'; + count++; + } + } + } + } + + + //--- patch https + + count += PatchHTTPS(str); + + + #if 0 // region specific patch functions not needed yet + + //--- patch version specific + + str->str_status = STR_S_ANALYZED; + switch (str->mode) + { + case STR_M_PAL: + count += PatchPAL(str); + break; + + case STR_M_USA: + count += PatchUSA(str); + break; + + case STR_M_JAP: + count += PatchJAP(str); + break; + + case STR_M_KOR: + count += PatchKOR(str); + break; + + default: + break; + } + #endif + + return count + PatchByListWPF(str); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CleanDOL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool CleanDol ( staticr_t * str ) // return TRUE if dol data modified. +{ + AnalyzeDOL(str,true); + if ( str->dol_status & DOL_S_ORIG ) + { + str->dol_status = 0; + return 0; + } + + uint sect_list; + dol_header_t *dol_head = (dol_header_t*)str->data; + + uint new_size = RemoveDolSections(dol_head,0x7f83,§_list,logging>2?stdlog:0); + bool stat = new_size > 0; + noPRINT("SIZE: %x -> %x / sect=%x\n",str->data_size,new_size,sect_list); + if (stat) + str->data_size = new_size; + + + //--- fix entry point + + const dol_sect_info_t *orig = GetInfoDOL(str->mode); + DASSERT(orig); + if ( orig[DOL_IDX_ENTRY].addr != ntohl(dol_head->entry_addr) ) + { + stat = true; + dol_head->entry_addr = htonl(orig[DOL_IDX_ENTRY].addr); + } + + + //--- restore HTTPS links + + PatchHTTPS_HTTP(str,HTTPS_RESTORE); + + + //--- terminate + + str->dol_status = 0; + str->dol_cleaned = true; + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchDOL() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// GCH support + +#define GCT_ADDRESS 0x80001800u +#define GCT_MAX 0x80003000u +#define GCT_MM_BASE (GCT_ADDRESS-sizeof(gch_header_t)) + +static MemMap_t gct_data_mem = {0}; // memory map of GCT section +static GrowBuffer_t gecko_data = {0}; +static GrowBuffer_t wcode_job = {0}; +static GrowBuffer_t wcode_data = {0}; + + +static bool use_gct_loader = false; + +/////////////////////////////////////////////////////////////////////////////// +// patched by + +#define STD_PATCHED_BY WSTRT_SHORT " v" VERSION " r" REVISION +static char opt_patched_by[PATCHED_BY_SIZE+1] = STD_PATCHED_BY; + +patched_by_t patched_by_mode = PBY_AUTO; + +//----------------------------------------------------------------------------- + +int ScanOptPBMode ( ccp arg ) +{ + if ( !arg || !*arg ) + { + patched_by_mode = PBY_AUTO; + return 0; + } + + static const KeywordTab_t tab[] = + { + { PBY_OFF, "OFF", 0, 0 }, + { PBY_RESET, "RESET", 0, 0 }, + { PBY_WIIMMFI, "WIIMMFI", 0, 0 }, + { PBY_AUTO, "AUTO", "DEFAULT", 0 }, + { PBY_ALWAYS, "ALWAYS", 0, 0 }, + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (cmd) + { + patched_by_mode = cmd->id; + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid mode for option --pb-mode: '%s'\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +#if defined(TEST) || defined(DEBUG) //|| HAVE_WIIMM_EXT + static void TraceGCT ( ccp info ) + { + fprintf(stdlog,"%s#GCT: gecko=%u/%u, wc-job=%u/%u, wc-data=%u/%u%s%s%s\n", + collog->b_cyan, + gecko_data.used, gecko_data.size, + wcode_job.used, wcode_job.size, + wcode_data.used, wcode_data.size, + info ? " : " : "", + info ? info : "", + collog->reset ); + } +#else + static inline void TraceGCT ( ccp info ) {} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +static void InititializeGCT() +{ + static bool done = false; + if (!done) + { + done = true; + + InitializeMemMap(&gct_data_mem); + InitializeGrowBuffer(&gecko_data,MiB); + InitializeGrowBuffer(&wcode_job,MiB); + InitializeGrowBuffer(&wcode_data,MiB); + gecko_data.grow_size = 0x1000; + wcode_job.grow_size = 0x0100; + wcode_data.grow_size = 0x0400; + + if (logging>2) TraceGCT("Init"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ResetGCT() +{ + InititializeGCT(); + + ResetMemMap(&gct_data_mem); + ClearGrowBuffer(&gecko_data); + ClearGrowBuffer(&wcode_job); + ClearGrowBuffer(&wcode_data); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError AddSectionHelper2 +( + // return ERR_OK | ERR_WARNING of created with warnings | ERR_ERROR on abort + staticr_t *str, // valid pointer + const wch_segment_t *seg, // segement info, local endian, data+size ignored + const u8 *seg_data, // segement data + uint seg_size, // segement data + ccp fname // filename for messages +) +{ + //--- patching possible? + + const u32 patch_addr = GetVbiAddress(str); + if (!patch_addr) + return 0; + + + //--- check overlaps + + dol_header_t *dh = (dol_header_t*)str->data; + const uint overlap = CountOverlappedDolSections(dh,seg->addr,seg_size); + if (overlap) + { + if (!force_count) + { + ERROR0(ERR_WARNING, + "New section would overlap %u existing section%s -> ignore: %s\n", + overlap, overlap == 1 ? "" : "s", fname ); + return ERR_ERROR; + } + + ERROR0(ERR_WARNING, + "New section overlaps %u existing section%s: %s\n", + overlap, overlap == 1 ? "" : "s", fname ); + } + + + //--- find first unused section + + int dol_seg_type; + switch (seg->type) + { + case 't': dol_seg_type = 0; break; // Any segment, TEXT first + case 'T': dol_seg_type = 1; break; // Only TEXT segment + case 'D': dol_seg_type = 2; break; // Only DATA segment + default: dol_seg_type = 3; break; // Any segment, DATA first (alias 'd') + } + + dol_sect_info_t di; + if (!FindFirstFreeDolSection(&di,dh,dol_seg_type)) + { + ERROR0(ERR_WARNING, + "DOL: No free section available: %s\n", str->fname ); + return ERR_ERROR; + } + PRINT("NEW SECTION: %s\n",di.name); + + + //--- append seg_data to end of dol + + uint offset = str->data_size; + str->data_size += seg_size; + str->data = REALLOC(str->data,str->data_size); + dh = (dol_header_t*)str->data; + memcpy( str->data + offset, seg_data, seg_size ); + + + //--- create section + + PRINT(" - create section %s [%08x..%08x], f-off 0x%06x, size 0x%x\n", + di.name, seg->addr, seg->addr+seg_size, offset, seg_size ); + + if ( verbose > 0 ) + fprintf(stdlog, + "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x\n", + di.name, seg->addr, seg->addr+seg_size, offset, seg_size ); + + dh->sect_off [di.section] = htonl(offset); + dh->sect_addr[di.section] = htonl(seg->addr); + dh->sect_size[di.section] = htonl(seg_size); + + + //--- patch entry point + + u32 addr = seg->main_entry_old; + if (addr) + { + const u32 store = GetDolOffsetByAddr(dh,addr,sizeof(u32),0); + if (store) + memcpy(str->data+store,&dh->entry_addr,sizeof(u32)); + } + + if (seg->main_entry) + { + if ( verbose >= 1 ) + fprintf(stdlog, + "- Change entry point from %08x to %08x\n", + ntohl(dh->entry_addr), seg->main_entry ); + write_be32(&dh->entry_addr,seg->main_entry); + } + + + //--- patch VBI entry point + + if (seg->vbi_entry) + { + const u32 offset = GetDolOffsetByAddr(dh,patch_addr,sizeof(u32),0); + if (!offset) + { + ERROR0(ERR_WARNING, + "Can't patch DOL at address 0x%08x: %s\n", + patch_addr, str->fname ); + } + else + { + u32 addr = ntohl(seg->vbi_entry_old); + if (addr) + { + const u32 store = GetDolOffsetByAddr(dh,addr,sizeof(u32),0); + if (!store) + { + ERROR0(ERR_WARNING, + "Can't store old VBI at DOL address 0x%08x: %s\n", + seg->vbi_entry_old, str->fname ); + } + else + memcpy(str->data+store,str->data+offset,sizeof(u32)); + } + + const u32 code = ( seg->vbi_entry - patch_addr ) & 0xffffff | 0x4b000000; + PRINT("PATCH addr %08x off %08x from %08x to %08x\n", + patch_addr, offset, be32(str->data+offset), code ); + if ( verbose > 0 ) + fprintf(stdlog, + "- Patch address %08x (off %08x, VBI) from %08x to %08x\n", + patch_addr, offset, be32(str->data+offset), code ); + write_be32(str->data+offset,code); + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct gct_sect_t +{ + u16 off; // source offset + u16 size; // size of section + u16 idx; // initial section index + u16 dest; // dest address of section (add 0x8000000 for real address) +} +gct_sect_t; + +//----------------------------------------------------------------------------- + +static int sort_sect_list_by_size + ( const gct_sect_t * a, const gct_sect_t * b ) +{ + return (int)b->size - (int)a->size; +} + +//----------------------------------------------------------------------------- + +static int sort_sect_list_by_dest + ( const gct_sect_t * a, const gct_sect_t * b ) +{ + return (int)a->dest - (int)b->dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct cheat_code_loader_t +{ + u32 entry; // entry point => ASM: bl start + u32 dummy_04; // ... + + //--- data, see cheat-code-loader.sh + + u32 return_addr; // absolute return address + u32 var_version; // 'vars' version of current code loader + u32 space; // needed space, must not < source_len + + u32 patch_addr; // address of patch list, relative to 'vars' + u32 data_addr; // data address, relative to 'vars' + u32 data_end; // data-end address, relative to 'vars' + u32 gecko_off; // offset of first gecko code relative to data + + u32 verify_addr; // address to verify own codehandler + u32 verify_val; // use this to verify & to decode + u32 ch_patch_1; // abs address for first codehandler patch + u32 ch_patch_2; // abs address for second codehandler patch + u32 deny_user_gch; // 1: skip patching on !rSZS + u32 info_flags; // info flags for NAS (8 bits) +} +__attribute__ ((packed)) cheat_code_loader_t; + +#define CCL_VAR_OFFSET offsetof(cheat_code_loader_t,return_addr) + +/////////////////////////////////////////////////////////////////////////////// + +typedef enum { CHT_NONE, CHT_ONLY, CHT_FULL } code_handler_t; + +//----------------------------------------------------------------------------- + +static const u8 loader_asm_bz2[] = +{ + // decompressed len: 0x338 = 824 bytes + 0x00,0x00,0x03,0x38, + + // bzipped data + 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x9d,0xf6, 0x23,0xf1,0x00,0x01, + 0x20,0xff,0xff,0xfd, 0xde,0xc7,0xf6,0xc5, 0xef,0xf5,0xff,0x64, 0x5b,0xef,0xd0,0x6a, + 0xdc,0xc5,0xd7,0xfc, 0xde,0x79,0xe0,0x53, 0xd6,0x51,0x10,0x55, 0xc3,0x41,0x65,0x45, + 0x84,0x65,0x44,0xc0, 0x01,0xf5,0xc2,0xca, 0xd2,0x1a,0xa6,0x8c, 0xa6,0x90,0xf5,0x3d, + 0x27,0xa9,0x90,0x00, 0xd0,0x06,0x26,0x83, 0xd2,0x00,0x69,0x88, 0x1a,0x00,0x19,0x00, + 0x1a,0x1a,0x32,0x01, 0xa0,0xf4,0x41,0x90, 0x32,0x00,0xc8,0x11, 0x34,0x41,0x1a,0x4f, + 0xd5,0x3d,0x27,0x94, 0xf5,0x32,0x1e,0xa0, 0x19,0x34,0x19,0x00, 0xd0,0xf4,0x80,0x00, + 0x00,0x00,0x00,0x00, 0x00,0x00,0x0d,0x00, 0xd0,0x06,0xd4,0x41, 0x80,0x4c,0x46,0x09, + 0x93,0x08,0x30,0x98, 0x98,0x13,0x00,0x43, 0x08,0xc0,0x43,0x02, 0x60,0x4c,0x26,0x08, + 0xc0,0x00,0x04,0xd0, 0xc2,0x62,0x69,0x81, 0x03,0x4d,0x42,0x98, 0x50,0x09,0xb4,0x3d, + 0x40,0x69,0xa8,0xc9, 0x80,0x68,0x00,0x01, 0x32,0x34,0xd1,0xa6, 0x00,0x11,0x81,0xa0, + 0x32,0x23,0x00,0x00, 0x08,0x69,0x91,0xe9, 0x0c,0xa2,0x54,0xaf, 0x84,0x8f,0x0a,0x52, + 0x54,0xba,0xa7,0xd1, 0x59,0x0d,0x00,0x4b, 0x69,0xdf,0xb0,0xb0, 0xb3,0xd3,0x4a,0x1c, + 0xa2,0x85,0xc9,0x8f, 0xc7,0xb6,0x9e,0x7e, 0x12,0x2b,0x1d,0x13, 0x3f,0x9e,0x9e,0x4b, + 0xb0,0x31,0x30,0x83, 0x55,0x67,0x43,0xde, 0x80,0x53,0x40,0x4f, 0xcd,0x7b,0x06,0x1c, + 0x04,0xd7,0x3e,0x0c, 0xc5,0xd2,0x66,0x27, 0xf5,0x19,0x65,0xeb, 0x81,0x01,0x48,0xa9, + 0x51,0x42,0x9b,0xba, 0x56,0x19,0x92,0x45, 0x52,0x6e,0x22,0xe0, 0xb5,0x97,0x2c,0x23, + 0xda,0x75,0x1c,0x16, 0x2e,0xb1,0x88,0x8e, 0x91,0xa0,0xcf,0x5a, 0xc9,0x5e,0x99,0x27, + 0xe1,0x52,0x18,0x16, 0x42,0x60,0x17,0x49, 0x28,0xc2,0x8e,0x18, 0x12,0x10,0xda,0x46, + 0x01,0x76,0x36,0xd4, 0x07,0xe3,0x9d,0x42, 0x5c,0xc2,0xba,0x55, 0x7f,0xbd,0x14,0x02, + 0x70,0x29,0x37,0x17, 0xf6,0xda,0x50,0x5b, 0x05,0xa1,0xb9,0x06, 0xcd,0xb0,0xde,0x93, + 0x88,0x8b,0x0f,0x9d, 0x2a,0x4d,0xc8,0x6b, 0x7c,0xf1,0x01,0xf2, 0x14,0x6b,0x11,0x48, + 0x27,0x7d,0xb8,0xab, 0x43,0x15,0x5d,0x46, 0x21,0x1f,0x70,0x56, 0x77,0x4a,0x82,0xcc, + 0x8b,0xf1,0xc9,0xc0, 0x73,0xde,0x98,0x74, 0x4b,0x96,0x39,0xb2, 0x6b,0x99,0xee,0x23, + 0x0d,0xf0,0x69,0x16, 0x12,0x26,0xf7,0x2c, 0xe8,0x60,0x78,0xfa, 0x13,0x3b,0x56,0xc8, + 0xaa,0x69,0x82,0x3e, 0xc8,0x2b,0xee,0xad, 0xb9,0x65,0x8e,0x44, 0x98,0x27,0x77,0xcd, + 0xc5,0xcf,0x48,0x17, 0x93,0x04,0x2a,0x82, 0x96,0xa2,0x6d,0x64, 0x1c,0x03,0x7d,0x91, + 0xd1,0x4f,0x38,0x78, 0x7f,0x2a,0xfb,0x03, 0xe6,0x2f,0xe3,0x9f, 0xe5,0x0b,0x36,0x58, + 0x8c,0x70,0xba,0x9d, 0x3b,0xf3,0x4b,0xfe, 0xb9,0x53,0x6a,0x4c, 0xe8,0x6c,0xb9,0xfc, + 0x32,0xa3,0x13,0xc9, 0xed,0x29,0x10,0x4c, 0x48,0xe1,0x32,0x81, 0x0a,0x24,0x80,0x20, + 0x15,0x82,0x0d,0x89, 0x26,0xa1,0x29,0x66, 0x08,0x0a,0xd0,0x9e, 0xe0,0xce,0x50,0x7b, + 0xc5,0xfb,0x95,0x66, 0x42,0x65,0x84,0xda, 0xdf,0x37,0x52,0x53, 0x23,0x06,0x09,0x39, + 0xf6,0x1f,0x63,0xf1, 0x0b,0x13,0xd6,0x9c, 0xa2,0x63,0x2d,0xef, 0xa2,0xb2,0x8d,0xd2, + 0xa0,0xdd,0xe8,0x39, 0x9e,0xf5,0x71,0xa4, 0xd1,0xc5,0x74,0x11, 0x11,0xbb,0x42,0x20, + 0x53,0x92,0xff,0xc5, 0x36,0x67,0x4e,0xa4, 0x1b,0x55,0x95,0xef, 0x3a,0x30,0x12,0x85, + 0xea,0xe2,0x75,0xbf, 0x1a,0xb8,0xc8,0xa8, 0x12,0x2d,0x19,0x17, 0x91,0xdd,0xa7,0x23, + 0xbb,0xe7,0x18,0xb4, 0x61,0x78,0xa8,0xc4, 0x3b,0x84,0x5a,0x2d, 0x35,0x5f,0xac,0xd8, + 0x82,0xae,0x21,0x8e, 0x23,0x7d,0xf3,0x93, 0x2d,0xff,0x7b,0xbc, 0xbd,0x42,0x65,0xfc, + 0x1e,0xf9,0x2d,0xf9, 0x69,0x5a,0x12,0x08, 0x20,0x90,0x41,0x00, 0xc9,0x62,0xd2,0x88, + 0xc2,0x2a,0xa2,0x95, 0xc6,0x52,0xab,0x53, 0x31,0xb5,0xae,0x62, 0x36,0x07,0x94,0x8f, + 0x05,0x87,0x98,0x4a, 0x26,0x1a,0x0f,0x1f, 0xdb,0x2e,0x20,0xbb, 0x00,0x3c,0xb8,0x74, + 0x4c,0xb2,0x04,0x8f, 0x98,0xec,0x1d,0x43, 0x10,0x97,0x08,0x33, 0x05,0x0a,0xa0,0x48, + 0x23,0x04,0x50,0xc0, 0xfd,0x68,0x2e,0x24, 0x97,0xb1,0x4c,0xac, 0xb6,0x12,0xd4,0x00, + 0x65,0xdb,0x5d,0x74, 0x19,0x20,0x9e,0x58, 0xc2,0x10,0x12,0x00, 0x2b,0x52,0x00,0xea, + 0xa8,0xe0,0x54,0x84, 0xd0,0x7e,0x52,0x38, 0x08,0x2a,0x82,0x40, 0x59,0x3c,0xde,0xc5, + 0x08,0x1c,0x8f,0x4a, 0x79,0x27,0x98,0xbb, 0x92,0x29,0xc2,0x84, 0x84,0xef,0xb1,0x1f, + 0x88, + // 0x2e1 = 737 Bytes +}; + +static BZ2Manager_t loader_asm_mgr + = { loader_asm_bz2, sizeof(loader_asm_bz2), 0, 0 }; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SetupCodeLoader +( + // return ERR_OK | ERR_WARNING of created with warnings, + // ERR_ERROR on abort, ERR_NOTHING_TO_DO if nothing to do + + staticr_t *str, // valid pointer + const wch_segment_t *seg, // NULL or segement info, seg->data is ignored + u8 *code_data, // NULL or code data + uint code_size, // NULL or size of code data + uint cheat_offset, // offset first cheat code + code_handler_t ch_type, // code handler type + ccp fname // filename for messages +) +{ + ASSERT( code_size >= cheat_offset ); + const uint total_data_size = wcode_job.used + wcode_data.used + + code_size - cheat_offset; + + #if defined(TEST) || defined(DEBUG) // || HAVE_WIIMM_EXT + if (logging>2) + fprintf(stderr, + "SetupCodeLoader() %d+%d+%d = %d, c-off=0x%x, ch-type=%d, seg=%d => %s\n", + code_size, wcode_job.used, wcode_data.used, total_data_size, + cheat_offset, ch_type, seg!=0, + total_data_size ? "\e[41;37;1m CREATE WIIMMFI CODE \e[0m" : "quit" ); + #endif + + if (!total_data_size) + return ERR_NOTHING_TO_DO; + + + //--- data + + GrowBuffer_t data; + InitializeGrowBuffer(&data,MiB); + data.grow_size = 0x1000; + + + //--- code handler + + const u32 security_off = GCT_REG_OFFSET + 16; // gecko register 4 + const u32 security_val = 0xdc020463; + + enumError err = ERR_OK; + if ( code_size ) + { + ASSERT(code_data); + write_be32(code_data+security_off,security_val); + write_be32(code_data+security_off+8,security_val); + write_be64(code_data+cheat_offset,GCT_TERM_NUM); + err = AddSectionHelper2(str,seg,code_data,cheat_offset+8,fname); + if (err) + goto abort; + code_data += cheat_offset; + code_size -= cheat_offset; + write_be64(code_data,GCT_MAGIC8_NUM); + str->dol_flags[DPF_CHEAT] = 'l'; + } + + + //--- setup data buffer + + DecodeBZIP2Manager(&loader_asm_mgr); + InsertGrowBuffer(&data,loader_asm_mgr.data,loader_asm_mgr.size); + cheat_code_loader_t ccl; + memcpy(&ccl,data.ptr,sizeof(ccl)); + ccl.info_flags = htonl(str->dol_info_flags); + + uint code_offset = loader_asm_mgr.size - CCL_VAR_OFFSET; + uint deny_user_gch = opt_allow_user_gch < OFFON_AUTO; + if (wcode_job.used) + { + ccl.patch_addr = htonl(code_offset); + InsertGrowBuffer(&data,wcode_job.ptr,wcode_job.used); + InsertGrowBuffer(&data,EmptyString,4); + code_offset += wcode_job.used + 4; + deny_user_gch = opt_allow_user_gch <= OFFON_AUTO; + } + str->dol_flags[DPF_ALLOW_USER] = deny_user_gch ? 'd' : 'a'; + if ( deny_user_gch && code_size ) + { + str->dol_flags[DPF_ALLOW_USER] = 's'; + deny_user_gch = 2; + } + ccl.deny_user_gch = htonl(deny_user_gch); + ccl.data_addr = htonl(code_offset); + ccl.gecko_off = htonl( wcode_data.used ); + + if (wcode_data.used) + InsertGrowBuffer(&data,wcode_data.ptr,wcode_data.used); + + if (code_size) + { + InsertGrowBuffer(&data,code_data,code_size); + ccl.verify_addr = htonl(seg->addr+security_off); + } + ccl.data_end = htonl(data.used-CCL_VAR_OFFSET); + const u32 space = data.used - CCL_VAR_OFFSET - code_offset; + ccl.space = htonl( space > opt_gct_space ? space : opt_gct_space ); + ccl.verify_val = htonl(security_val); + + if ( ch_type == CHT_FULL ) + { + ccl.ch_patch_1 = htonl(seg->addr+0x04DC); + ccl.ch_patch_2 = htonl(seg->addr+0x0758); + } + else if ( ch_type == CHT_ONLY ) + ccl.ch_patch_1 = htonl(seg->addr+0x0104); + + memcpy(data.ptr,&ccl,sizeof(ccl)); + + + //--- create segment + + wch_segment_t dseg; + memset(&dseg,0,sizeof(dseg)); + dseg.type = 't'; + dseg.size = data.used; + dseg.addr = opt_gct_addr < 0x80000000 + ? FindFreeBssSpaceDOL((dol_header_t*)str->data,dseg.size,32,0) + : opt_gct_addr; + dseg.main_entry = dseg.addr; + dseg.main_entry_old = dseg.addr + offsetof(cheat_code_loader_t,return_addr); + + u32 *ptr = (u32*)( data.ptr + loader_asm_mgr.size ); + u32 *end = (u32*)( data.ptr + data.used ); + u32 key = security_val; + while ( ptr < end ) + { + const u32 orig = be32(ptr); + const u32 val = orig ^ key; + write_be32(ptr, val << 19 | val >> 13 ); + key += orig >> 5; + ptr++; + } + + err = AddSectionHelper2(str,&dseg,data.ptr,dseg.size,fname); + + //--- clean +abort: + ResetGrowBuffer(&data); + ClearGrowBuffer(&wcode_job); + ClearGrowBuffer(&wcode_data); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError AddSectionHelper +( + // return ERR_OK | ERR_WARNING of created with warnings | ERR_ERROR on abort + staticr_t *str, // valid pointer + const wch_segment_t *seg, // segement info, seg->data is ignored + const u8 *seg_data, // segement data + ccp fname // filename for messages +) +{ + //--- patching possible? + + const u32 patch_addr = GetVbiAddress(str); + if (!patch_addr) + return 0; + + + //--- check presence of 'codehandleronly' or 'codehandler' + //--- and optional replace 'codehandleronly' by 'codehandler' + + u8 *new_data = 0; + uint seg_size = seg->size; + + code_handler_t ch_type = CHT_NONE; + uint cheat_offset = 0; + + BZ2Manager_t *cho = &codehandleronly_mgr; + DecodeBZIP2Manager(cho); + if ( cho->size > sizeof(gch_header_t) ) + { + const u8 *cho_data = cho->data + sizeof(gch_header_t); + const uint cho_size = cho->size - sizeof(gch_header_t); + if ( seg_size >= cho_size && !memcmp(cho_data,seg_data,cho_size) ) + { + ch_type = CHT_ONLY; + cheat_offset = cho_size; + + if (opt_full_gch) + { + PRINT("\e[35;1m Only: %u >= ( %u -> %u -> %u )\e[0m\n", + seg_size, codehandleronly_mgr.src_size, + codehandleronly_mgr.size, cho_size ); + + BZ2Manager_t *chf = &codehandler_mgr; + DecodeBZIP2Manager(chf); + if ( chf->size > sizeof(gch_header_t) ) + { + if ( verbose > 0 ) + fprintf(stdlog, + "- Replace 'codehandleronly' by 'codehandler'\n"); + + const u8 *chf_data = chf->data + sizeof(gch_header_t); + const uint chf_size = chf->size - sizeof(gch_header_t); + const uint new_size = seg_size - cho_size + chf_size; + new_data = MALLOC(new_size); + memcpy( new_data, chf_data, chf_size ); + memcpy( new_data+chf_size, seg_data+cho_size, seg_size-cho_size ); + seg_data = new_data; + seg_size = new_size; + + ch_type = CHT_FULL; + cheat_offset = chf_size; + } + } + } + } + + if ( ch_type == CHT_NONE ) + { + BZ2Manager_t *chf = &codehandler_mgr; + DecodeBZIP2Manager(chf); + if ( chf->size > sizeof(gch_header_t) ) + { + const u8 *chf_data = chf->data + sizeof(gch_header_t); + const uint chf_size = chf->size - sizeof(gch_header_t); + if ( seg_size >= chf_size && !memcmp(chf_data,seg_data,chf_size) ) + { + ch_type = CHT_FULL; + cheat_offset = chf_size; + } + } + } + + + //--- find and remove separators + + enum { MAX_SECT = 200 }; + gct_sect_t sect_list[MAX_SECT+1], + *sect = sect_list, + *sect_end = sect_list + MAX_SECT; + memset(sect_list,0,sizeof(sect_list)); + + if ( cheat_offset > sizeof(gch_header_t) && opt_gct_scan_sep ) + { + if (!new_data) + { + // we need modifiable data! + new_data = MEMDUP(seg_data,seg_size); + seg_data = new_data; + } + + u8 *src = new_data + cheat_offset; + u8 *dest = src; + u8 *end = new_data + seg_size; + + while ( src + 8 <= end ) + { + PRINT(">> LINE %3zd => %3zd : %08x %08x\n", + ( src - new_data - cheat_offset ) / 8, + ( dest - new_data - cheat_offset ) / 8, + be32(src), be32(src+4) ); + + uint copy_lines = 0; + if ( be32(src) == 0xf0000000 && !be32(src+4) ) + { + const u32 off = dest - new_data; + sect->size = off - sect->off; + sect++; + sect->idx = sect - sect_list; + sect->off = off; + sect->size = 8; + *(u64*)dest = *(u64*)src; + src += 8; + dest += 8; + break; + } + else if ( be32(src) == GCT_SEP_NUM1 && !be16(src+4) ) + { + if ( opt_gct_sep ) + { + const u32 off = dest - new_data; + if ( sect->off != off && sect < sect_end ) + { + sect->size = off - sect->off; + sect++; + sect->idx = sect - sect_list; + sect->off = off; + sect->size = 0; + } + } + + PRINT("\t>> SEP#%zu found: %08x %08x\n", + sect-sect_list, be32(src),be32(src+4) ); + copy_lines = be16(src+6); + src += 8; + } + else if ( *src == 0xc0 || *src == 0xc2 || *src == 0xd0 || *src == 0xd2 ) + { + if ( opt_gct_asm_sep ) + { + const u32 off = dest - new_data; + if ( sect->off != off && sect < sect_end ) + { + sect->size = off - sect->off; + sect++; + sect->idx = sect - sect_list; + sect->off = off; + sect->size = 0; + } + } + + PRINT(">> ASM#%zu found: %08x %08x\n", + sect-sect_list, be32(src), be32(src+4) ); + copy_lines = be32(src+4) + 1; + } + else if ( ( *src == 0x66 || *src == 0x68 ) + && ( src[1] == 0x00 || src[1] == 0x10 || src[1] == 0x20 ) ) + { + PRINT(">> GOTO/GOSUB#%zu found: %08x %08x\n", + sect-sect_list, be32(src), be32(src+4) ); + s16 go = be16(src+2); + if ( go >= 0 ) + copy_lines = go+1; + } + else + { + *(u64*)dest = *(u64*)src; + src += 8; + dest += 8; + continue; + } + + if ( copy_lines > 0 ) + { + // use it this way to detect bad large values + const uint max = ( end - src ) / 8; + if ( copy_lines > max ) + copy_lines = max; + copy_lines *= 8; // now we have copy_bytes! + + memmove(dest,src,copy_lines); + src += copy_lines; + dest += copy_lines; + } + } + + //PRINT(">>> %zd %zd /%zd\n",dest-new_data,src-new_data,end-new_data); + if ( src >= end ) + seg_size = dest - new_data; + else if ( src > dest ) + memset(dest,0,src-dest); + } + const int n_sect = sect - sect_list; + + + //--- move GCT part + + str->dol_flags[DPF_CHEAT] = 'c'; + + enumError err = ERR_OK; + if ( opt_gct_move >= OFFON_ON && cheat_offset >= 0x100 ) + { + use_loader:; + if (!new_data) + { + // we need modifiable data! + new_data = MEMDUP(seg_data,seg_size); + seg_data = new_data; + } + + err = SetupCodeLoader(str,seg,new_data,seg_size,cheat_offset,ch_type,fname); + goto abort; + } + + + //--- split GCT part + + const u32 off_end = seg->addr + seg_size; + PRINT("N=%d, of=%08x\n",n_sect,off_end); + if ( n_sect > 2 + && seg->addr + cheat_offset < 0x80002ff0 + && off_end >= 0x80003000 && off_end < 0x80004000 ) + { + #ifdef TEST0 + { + uint i; + for ( i = 0, sect = sect_list; i <= n_sect; i++, sect++ ) + PRINT(" -> SECT %2u: %4x .. %4x, size %4x\n", + i, sect->off, sect->off + sect->size, sect->size ); + } + #endif + + bool sorted = false; + restart:; + u8 data[0x4000]; + memset(data,0,sizeof(data)); + + const u32 base_addr = seg->addr - 0x80000000; + memcpy(data+base_addr,seg_data,cheat_offset+8); + + struct data_t { u32 used; u32 size; u8 *data; } data_tab[] = + { + { cheat_offset+8, 0x2ff8 - base_addr, data + base_addr }, + { 0, 0x37f8 - 0x3200, data + 0x3200 }, + { 0, 0x3ff8 - 0x3880, data + 0x3880 }, + {0,0,0} + }; + + uint i; + struct data_t *dp; + bool fail = false; + + if ( opt_gct_list > 1 ) + { + fprintf(stdlog,"\nSummary of detected GCT sections:\n"); + for ( i = 1, sect = sect_list+1; i < n_sect; i++, sect++ ) + fprintf(stdlog," S%02u: %04x .. %04x, size %04x = %4u/dec\n", + sect->idx, sect->off, sect->off+sect->size, + sect->size, sect->size ); + } + + for ( i = 1, sect = sect_list+1; i < n_sect; i++, sect++ ) + { + bool done = false; + for ( dp = data_tab; dp->data; dp++ ) + if ( sect->size <= dp->size - dp->used ) + { + memcpy(dp->data+dp->used,seg_data+sect->off,sect->size); + sect->dest = dp->data + dp->used - data; + dp->used += sect->size; + done = true; + break; + } + if (!done) + { + sect->dest = M1(sect->dest); + ERROR0(ERR_WARNING, + "No space for cheat code section %u (size %x): %s\n", + i, sect->size, fname ); + fail = true; + } + } + + #ifdef TEST0 + { + uint i; + const struct data_t *dp; + for ( i = 0, dp = data_tab; dp->data; i++, dp++ ) + printf(" -> %2u: %04zx .. %04zx : %4x / %4x : %4x\n", + i, dp->data - data, dp->data - data + dp->used, + dp->used, dp->size, dp->size - dp->used ); + } + #endif + + if (fail) + { + if (!sorted) + { + qsort( sect_list+1, n_sect-1, sizeof(*sect_list), + (qsort_func)sort_sect_list_by_size ); + sorted = true; + goto restart; + } + + FREE(new_data); + ERROR0(ERR_ERROR, + "Not enough space for cheat codes: %s\n",fname); + return ERR_WARNING; + } + + if ( opt_gct_list > 0 ) + { + qsort( sect_list+1, n_sect-1, sizeof(*sect_list), + (qsort_func)sort_sect_list_by_dest ); + fprintf(stdlog,"\nSummary of GCT section distribution:\n"); + for ( i = 1, sect = sect_list+1; i < n_sect; i++, sect++ ) + { + fprintf(stdlog, + " S%02u: %04x .. %04x, size %04x = %4u/dec => ", + sect->idx, sect->off, sect->off+sect->size, + sect->size, sect->size ); + if (IS_M1(sect->dest)) + fprintf(stdlog,"FAILED!\n"); + else + fprintf(stdlog,"%04x .. %04x\n",sect->dest,sect->dest+sect->size); + } + fputc('\n',stdlog); + } + + struct data_t *prev = 0; + for ( dp = data_tab; dp->data; dp++ ) + if ( dp->used) + { + if (prev) + { + u8 *dest = prev->data + prev->used; + write_be16(dest,0x6620); + write_be16( dest+2, ( dp->data - dest - 8 ) / 8 ); + write_be32(dest+4,0); + prev->used += 8; + } + prev = dp; + } + if (prev) + { + u8 *dest = prev->data + prev->used; + write_be32(dest,0xf0000000ul); + write_be32(dest+4,0); + prev->used += 8; + } + + #ifdef TEST0 + { + uint i; + const struct data_t *dp; + for ( i = 0, dp = data_tab; dp->data; i++, dp++ ) + printf(" -> %2u: %04zx .. %04zx : %4x / %4x : %4x\n", + i, dp->data - data, dp->data - data + dp->used, + dp->used, dp->size, dp->size - dp->used ); + } + #endif + + err = AddSectionHelper2(str,seg,data_tab->data,data_tab->used,fname); + if ( err == ERR_OK ) + { + wch_segment_t dseg; + memset(&dseg,0,sizeof(dseg)); + dseg.type = 'd'; + if ( data_tab[1].used ) + { + dseg.addr = data_tab[1].data - data + 0x80000000; + const uint size = data_tab[2].used + ? data_tab[2].data + data_tab[2].used - data_tab[1].data + : data_tab[1].used; + err = AddSectionHelper2(str,&dseg,data_tab[1].data,size,fname); + } + else if ( data_tab[2].used ) + { + dseg.addr = data_tab[2].data - data + 0x80000000; + err = AddSectionHelper2(str,&dseg,data_tab[2].data,data_tab[2].used,fname); + } + } + return err; + } + + + //--- warn if overlapping 0x80003xxx + + if ( ch_type != CHT_NONE + && ( seg->addr < 0x80004000 && off_end > 0x80003000 )) + { + if ( opt_gct_move >= OFFON_AUTO && cheat_offset >= 0x100 ) + goto use_loader; + + FREE(new_data); + ERROR0(ERR_ERROR, + "Cheat codes must not overlay 0x80003000..0x80004000: %s\n", + fname); + return ERR_WARNING; + } + + + //--- standard: use 1 section + + err = AddSectionHelper2(str,seg,seg_data,seg_size,fname); + + abort: + FREE(new_data); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ReadCodeFile ( staticr_t *str, ccp fname, u8 **data, uint *size ) +{ + DASSERT(str); + DASSERT(fname); + DASSERT(data); + DASSERT(size); + + *data = 0; + *size = 0; + + if ( fname && fname[0] == '@' ) + { + BZ2Manager_t *wcm = 0; + if (!strcmp(fname,"@LECODE")) wcm = GetLecodeLoader(str->mode); +// else if (!strcmp(fname,"@XXX")) wcm = GetXXXX(str->mode); + + if (wcm) + { + DecodeBZIP2Manager(wcm); + *data = wcm->data; + *size = wcm->size; + return ERR_OK; + } + } + + enumError err = OpenReadFILE(fname,0,true,data,size,0,0); + if (err) + { + char path_buf[PATH_MAX]; + if (ReplaceRegionChar(str,path_buf,sizeof(path_buf),fname)) + err = OpenReadFILE(path_buf,0,true,data,size,0,0); + + if (err) + { + ERROR0(ERR_WARNING,"Can't load file: %s\n",fname); + return err; + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError AddWCode ( cvp data, uint size ) +{ + DASSERT(data); + DASSERT(!(size&7)); + InititializeGCT(); + + u8 *ptr = (u8*)data; + u8 *end = ptr + size; + u32 buf[5]; + + if ( be64(ptr) == GCT_MAGIC8_NUM ) + ptr += 8; + + while ( ptr < end ) + { + switch (*ptr) + { + case 0x04: + buf[0] = htonl(0x04); + buf[1] = htonl( be32(ptr) & 0xffffff | 0x80000000 ); + buf[2] = ((u32*)ptr)[1]; + InsertGrowBuffer(&wcode_job,buf,12); + use_gct_loader = true; + ptr += 8; + break; + + case 0xc2: + { + const uint len = 8 * be32(ptr+4); + buf[0] = htonl(0xc2); + buf[1] = htonl( be32(ptr) & 0xffffff | 0x80000000 ); + buf[2] = htonl( wcode_data.used ); + buf[3] = htonl( wcode_data.used + len - 4 ); + InsertGrowBuffer(&wcode_job,buf,16); + InsertGrowBuffer(&wcode_data,ptr+8,len); + use_gct_loader = true; + ptr += 8 + len; + } + break; + + case 0xf0: + if ( be32(ptr) != GCT_SEP_NUM1 ) + goto term; + ptr += 8; // ignore separators + break; + + default: + return ERROR0(ERR_WARNING,"WCODE %02x not supported => ABORT\n",*ptr); + } + } + term: + if (logging>2) TraceGCT("WCode"); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError AddCheatCode +( + // returns ERR_OK, ERR_NOTHING_TO_DO, ERR_WARNING or ERR_INTERNAL + + const u8 *code_data, // cheat code data + uint code_size, // cheat code size + bool check_dup, // TRUE: check for duplicates + ccp fname // file name for error messages +) +{ + DASSERT(code_data); + + //--- verify code_size and skip magic and terminator + + code_size &= ~7; // multiple of 8, cut additional bytes + if ( code_size >= 8 && be64(code_data) == GCT_MAGIC8_NUM ) + { + // skip magic + code_data += 8; + code_size -= 8; + } + + if ( code_size >= 8 && be64(code_data+code_size-8) == GCT_TERM_NUM ) + { + // ignore terminator + code_size -= 8; + } + + if (!code_size) + return ERR_NOTHING_TO_DO; + + + //--- first code -> add code handler + + InititializeGCT(); + + if (!gct_data_mem.used) + { + BZ2Manager_t *mgr = opt_full_gch + ? &codehandler_mgr : &codehandleronly_mgr; + enumError err = DecodeBZIP2Manager(mgr); + if (err) + return ERROR0(ERR_INTERNAL,0); + + gct_data_mem.begin = GCT_ADDRESS; + MemMapItem_t *mi; + mi = InsertMemMap(&gct_data_mem,GCT_ADDRESS,mgr->size-sizeof(gch_header_t)); + StringCopyS(mi->info,sizeof(mi->info),"Code Handler"); + mi = InsertMemMap(&gct_data_mem,GCT_MM_BASE+mgr->size,8); + StringCopyS(mi->info,sizeof(mi->info),"GCT magic"); + if (!use_gct_loader) + { + mi = InsertMemMap(&gct_data_mem,GCT_MAX,0); + StringCopyS(mi->info,sizeof(mi->info),"-- max possible --"); + } + + InsertGrowBuffer(&gecko_data,mgr->data,mgr->size); + u64 magic = hton64(GCT_MAGIC8_NUM); + InsertGrowBuffer(&gecko_data,&magic,sizeof(magic)); + if (logging>2) + TraceGCT("Setup"); + } + + + //--- check, if same code already inserted + + if ( check_dup && code_size <= gecko_data.used ) + { + const u8 *ptr = gecko_data.ptr; + const u8 *end = ptr + gecko_data.used - code_size; + + while ( ptr <= end ) + { + if (!memcmp(ptr,code_data,code_size)) + return ERR_NOTHING_TO_DO; + ptr += 8; + } + } + + + //--- add cheat code + + MemMapItem_t *mi + = InsertMemMap(&gct_data_mem,GCT_MM_BASE+gecko_data.used,code_size); + snprintf(mi->info,sizeof(mi->info), + "%4u line%s of GCT file", code_size/8, code_size == 8 ? "" : "s" ); + + if ( opt_gct_scan_sep && opt_gct_sep && be32(code_data) != GCT_SEP_NUM1 ) + { + u32 buf[2]; + buf[0] = htonl(GCT_SEP_NUM1); + buf[1] = htonl(code_size/8); + InsertGrowBuffer(&gecko_data,buf,8); + } + + InsertGrowBuffer(&gecko_data,code_data,code_size); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError AddWCodeFile ( staticr_t *str, ccp fname ) +{ + DASSERT(str); + if ( !fname || !*fname ) + return ERR_OK; + + u8 *data; + uint size; + enumError err = ReadCodeFile(str,fname,&data,&size); + if (err) + return err; + +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,size,size); + if ( fform == FF_GCT ) + { + err = opt_wcode == WCODE_GECKO + ? AddCheatCode(data,size,true,fname) + : AddWCode(data,size); + } + else + err = ERROR0(ERR_WARNING,"Not a GCT file: %s\n",fname); + + FREE(data); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +#if HAVE_XSRC + #include "xsrc/x-staticr.c" +#endif + +/////////////////////////////////////////////////////////////////////////////// + +static uint AddSectionGCH +( + staticr_t *str, // valid pointer + u8 *gh_data, // GCH data + uint gh_size, // GHC size + ccp fname, // filename for messages + bool get_min_addr // true: do nothing and get minimal section addr +) +{ + DASSERT(str); + DASSERT(str->data_size); + DASSERT(str->data); + DASSERT(str->is_dol); + DASSERT(gh_data||!gh_size); + + + //--- get param + + const u32 patch_addr = GetVbiAddress(str); + if (!patch_addr) + return 0; + + + //--- check for GCT + +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(gh_data,gh_size,gh_size); + if ( fform == FF_GCT ) + { + AddCheatCode(gh_data,gh_size,false,fname); + return 0; + } + + const gch_header_t *gh = (gch_header_t*)gh_data; + if (!IsValidGCH(gh,gh_size)) + { + #if HAVE_XSRC + return IsNotGCH(str,fname,gh_data,gh_size,get_min_addr); + #else + ERROR0(ERR_WARNING,"Not a valid GCH file: %s\n",fname); + return 0; + #endif + } + + gh_size -= sizeof(gch_header_t); // from now we only need the raw data size + + if (get_min_addr) + return ntohl(gh->addr); + + wch_segment_t seg; + memset(&seg,0,sizeof(seg)); + seg.type = 't'; + seg.addr = ntohl(gh->addr); + seg.size = gh_size; + seg.vbi_entry = ntohl(gh->vbi_entry); + + const enumError err = AddSectionHelper(str,&seg,gh->data,fname); + return err < ERR_ERROR; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint AddSectionDOL +( + staticr_t * str, // valid pointer + ccp fname, // filename to open + bool get_min_addr // true: do nothing and get minimal section addr +) +{ + DASSERT(str); + DASSERT(str->data_size); + DASSERT(str->data); + DASSERT(str->is_dol); + + if ( !fname || !*fname ) + return 0; + + + //--- get param + + const u32 patch_addr = GetVbiAddress(str); + if (!patch_addr) + return 0; + + + //--- load file + + u8 *gh_data = 0; + uint gh_size = 0; + enumError err = ReadCodeFile(str,fname,&gh_data,&gh_size); + if (err) + return err; + + const uint stat = AddSectionGCH(str,gh_data,gh_size,fname,get_min_addr); + FREE(gh_data); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint AddSectionsDOL +( + staticr_t * str, // valid pointer + bool get_min_addr // true: do nothing and get minimal section addr +) +{ + DASSERT(str); + DASSERT(str->data_size); + DASSERT(str->data); + DASSERT(str->is_dol); + + uint i, count = get_min_addr ? 0xffffffff : 0; + for ( i = 0; i < opt_sect_list.used; i++ ) + { + ccp fname = opt_sect_list.field[i]; + const uint stat = AddSectionDOL(str,fname,get_min_addr); + if (!get_min_addr) + count += stat; + else if ( stat && count > stat ) + count = stat; + } + + if ( gecko_data.used ) + { + if (logging>0) + fprintf(stdlog,">> GCT DATA/GROW, %u = 0x%x bytes\n", + gecko_data.used, gecko_data.used ); + + if (!get_min_addr) + { + // terminate section and add it to dol + MemMapItem_t *mi = InsertMemMap(&gct_data_mem,GCT_MM_BASE+gecko_data.used,8); + StringCopyS(mi->info,sizeof(mi->info),"GCT terminator"); + + u64 term = hton64(GCT_TERM_NUM); + InsertGrowBuffer(&gecko_data,&term,sizeof(term)); + count += AddSectionGCH(str,gecko_data.ptr,gecko_data.used,"GCT",false); + + if ( verbose > 1 || logging > 1 ) + { + fputs("\n Memory map of code handler and GCT files:\n",stdlog); + PrintMemMap(&gct_data_mem,stdlog,5,"part"); + fputc('\n',stdlog); + } + } + else if ( count > 0x80001800 ) + count = 0x80001800; + ResetGCT(); + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint CreateSectionDOL +( + staticr_t * str // valid pointer +) +{ + DASSERT(str); + DASSERT(str->data_size); + DASSERT(str->data); + DASSERT(str->is_dol); + + enum + { + OFF_SECT_ADDR = 0x10, + OFF_REL_ENTRY = 0x14, + OFF_ORIG_ENTRY = 0x18, + OFF_RDOL_ADDR = 0x1c, + OFF_END_PARAM = 0x20, + + RED_DOL_SIZE = 0xe4 - 0x48, // reduced DOL size + }; + + u32 dol_sect_offset = 0; // file offset of DOL section + u32 dol_sect_addr = 0; // address of DOL section + dol_header_t *dh = (dol_header_t*)str->data; + + + //--- find max dol address + + u32 max_addr = dh->bss_addr + dh->bss_size; + uint sect; + for ( sect = 0; sect < DOL_N_SECTIONS; sect++ ) + { + const u32 size = ntohl(dh->sect_size[sect]); + if (size) + { + const u32 off = ntohl(dh->sect_addr[sect]) + size; + if ( max_addr < off ) + max_addr = off; + } + } + max_addr = ALIGN32(max_addr,4); + + + //--- iterate new section list + + uint count; + for ( count = sect = 0; sect < n_section_list; sect++ ) + { + dol_sect_select_t *sl = section_list + sect; + sl->offset = 0; + if ( sl->find_mode < 0 ) + continue; + + dol_sect_info_t si; + if (!FindDolSectionBySelector(&si,dh,sl)) + { + ERROR0(ERR_WARNING,"DOL section '%s' not available: %s\n", + sl->name, str->fname ); + return 0; + } + + bool is_dol_info = !strcasecmp(sl->fname,".dol"); + if (is_dol_info) + { + sl->use_param = 0; + sl->size = RED_DOL_SIZE; + } + else if (!sl->data) + { + if (OpenReadFILE( sl->fname, 0, true, &sl->data, &sl->size, 0, 0 )) + { + ERROR0(ERR_WARNING,"Can't load file for '%s': %s\n", + sl->name, sl->fname ); + sl->find_mode = -1; + continue; + } + } + count++; + + uint offset = str->data_size; + sl->offset = offset; + str->data_size += sl->size; + str->data = REALLOC(str->data,str->data_size); + dh = (dol_header_t*)str->data; + u8 *dest = str->data + offset; + + u32 sect_addr = !sl->addr && sl->use_param + ? be32(sl->data+OFF_SECT_ADDR) : sl->addr; + + if (is_dol_info) + { + sl->data = (u8*)&dh->sect_addr; + if (!sect_addr) + sect_addr = 0x80004000 - RED_DOL_SIZE; + dol_sect_addr = sect_addr; + dol_sect_offset = offset; + } + + if (!sect_addr) + sect_addr = max_addr; + const u32 end_addr = ALIGN32( sect_addr + sl->size, 4 ); + if ( max_addr < end_addr ) + max_addr = end_addr; + + u32 new_entry = 0; + bool param_valid = false; + if ( sl->use_param && sl->size >= OFF_END_PARAM ) + { + new_entry = be32(sl->data+OFF_REL_ENTRY); + if (!new_entry) + { + param_valid = true; + } + else if ( !(new_entry&3) && new_entry < sl->size ) + { + param_valid = true; + new_entry += sect_addr; + } + else + new_entry = 0; + } + if (!param_valid) + sl->use_param = false; + + + //--- create section + + PRINT(" - create section %s[%d] [%08x..%08x], f-off 0x%06x, size 0x%x, par=0x%x\n", + si.name, si.section, sect_addr, sect_addr + sl->size, + offset, sl->size, new_entry ); + + if (verbose>0) + { + fprintf(stdlog, + "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x", + si.name, sect_addr, sect_addr + sl->size, offset, sl->size ); + if (new_entry) + fprintf(stdlog,", entry 0x%x\n",new_entry); + else if (param_valid) + fprintf(stdlog,", use param\n"); + else + fputc('\n',stdlog); + } + + dh->sect_off [si.section] = htonl(offset); + dh->sect_addr[si.section] = htonl(sect_addr); + dh->sect_size[si.section] = htonl(sl->size); + memcpy( dest, sl->data, sl->size ); + + //--- store original entry point + + memcpy(dest+OFF_ORIG_ENTRY,&dh->entry_addr,sizeof(u32)); + + //--- use relative entry point of new section + + if (new_entry) + dh->entry_addr = htonl(new_entry); + } + + + //--- final '.dol' section support + + if (dol_sect_offset) + { + //- refresh dol section + + PRINT("UPDATE RED-DOL\n"); + dol_header_t *dh = (dol_header_t*)str->data; + memcpy( str->data + dol_sect_offset, &dh->sect_addr, RED_DOL_SIZE ); + + //- update sections with 'use_param' + + for ( sect = 0; sect < n_section_list; sect++ ) + { + dol_sect_select_t *sl = section_list + sect; + PRINT("UPDATE sect %s [off=%x,param=%d] := %x\n", + sl->name, sl->offset+OFF_RDOL_ADDR, sl->use_param, dol_sect_addr ); + if ( sl->offset && sl->use_param ) + write_be32( str->data + sl->offset + OFF_RDOL_ADDR, dol_sect_addr ); + } + } + + return count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint PatchCTCODEDOL +( + staticr_t * str // valid pointer +) +{ + DASSERT(str); + DASSERT(str->data_size); + DASSERT(str->data); + DASSERT(str->is_dol); + + if (!opt_add_ctcode) + return 0; + + if (!(str->dol_status&DOL_S_ORIG)) + { + ERROR0(ERR_WARNING,"Can't add CT-CODE to modified DOL: %s\n",str->fname); + return 0; + } + + ct_dol_ext_t *ce = GetCtExtDOL(str->mode); + if (!ce) + { + ERROR0(ERR_WARNING,"Can't add CT-CODE: %s\n",str->fname); + return 0; + } + + BZ2Source_t *bs = &ce->t2_src; + SearchBZ2S(bs,ct_dir_list.field,ct_dir_list.used,SEA_LOG_OFF); + if ( !bs->data || !bs->size ) + { + missing_ctcode:; + ccp sem = strchr(bs->search,';'); + ERROR0(ERR_WARNING, + "Missing %.*s => Can't add CT-CODE: %s\n\n", + (int)( sem ? sem - bs->search : strlen(bs->search) ), + bs->search, str->fname ); + return 0; + } + + bs = &ce->d8_src; + SearchBZ2S(&ce->d8_src,ct_dir_list.field,ct_dir_list.used,SEA_LOG_OFF); + if ( !bs->data || !bs->size ) + goto missing_ctcode; + + const BZ2Source_t *t2 = &ce->t2_src; + const BZ2Source_t *d8 = &ce->d8_src; + + + //--- check data address + + bool move_d8 = opt_move_d8 || opt_sect_list.used; + u32 d8_addr = move_d8 ? 0x802a8000 : ce->d8_addr; + + dol_header_t *dh = (dol_header_t*)str->data; + if (CountOverlappedDolSections(dh,d8_addr,d8->size)) + { + move_d8 = true; + d8_addr = 0x802a8000; + if (CountOverlappedDolSections(dh,d8_addr,d8->size)) + { + ERROR0(ERR_WARNING,"No space for CTCODE/D8 section: %s\n",str->fname); + return 0; + } + } + + if (CountOverlappedDolSections(dh,ce->t2_addr,t2->size)) + { + ERROR0(ERR_WARNING,"No space for CTCODE/T2 section: %s\n",str->fname); + return 0; + } + + dol_sect_info_t t2info; + if (!FindFirstFreeDolSection(&t2info,dh,0)) + { + err2sect: + ERROR0(ERR_WARNING,"Need 2 unused sections for CTCODE: %s\n",str->fname); + return 0; + } + const int T_SECT = t2info.section; + dh->sect_size[T_SECT] = htonl(t2->size); // mark used for next search + + dol_sect_info_t d8info; + if (!FindFirstFreeDolSection(&d8info,dh,3)) + { + dh->sect_size[T_SECT] = htonl(0); + goto err2sect; + } + const int D_SECT = d8info.section; + + + //--- logging + + uint offset = str->data_size; + if ( verbose > 0 ) + { + fprintf(stdlog, + "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x\n", + t2info.name, ce->t2_addr, + ce->t2_addr + t2->size, offset, t2->size ); + fprintf(stdlog, + "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x\n", + d8info.name, ce->d8_addr, + ce->d8_addr + d8->size, offset + t2->size, d8->size ); + } + + + //--- assign data and add section + + str->data_size += t2->size + d8->size; + str->data = REALLOC(str->data,str->data_size); + dh = (dol_header_t*)str->data; + + PRINT("%s: off=%6x, addr=%8x, size=%6x\n",t2info.name,offset,ce->t2_addr,t2->size); + memcpy( str->data + offset, t2->data, t2->size ); + write_be32( str->data + offset + 0x14, d8_addr ); + dh->sect_off [T_SECT] = htonl(offset); + dh->sect_addr[T_SECT] = htonl(ce->t2_addr); + dh->sect_size[T_SECT] = htonl(t2->size); + + offset += t2->size; + PRINT("%s: off=%6x, addr=%8x, size=%6x\n",d8info.name,offset,ce->d8_addr,d8->size); + memcpy( str->data + offset, d8->data, d8->size ); + dh->sect_off [D_SECT] = htonl(offset); + dh->sect_addr[D_SECT] = htonl(d8_addr); + dh->sect_size[D_SECT] = htonl(d8->size); + + memcpy(&dh->entry_addr,t2->data+0x0c,4); + + ClearBZ2S(&ce->t2_src,1); + ClearBZ2S(&ce->d8_src,1); + str->dol_flags[DPF_CTCODE] = 'C'; + str->dol_info_flags |= DIF_CTCODE; + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PatchDOL +( + // returns: number of patches + + staticr_t * str // valid pointer +) +{ + DASSERT(str); + if (!str->data_size) + return 0; + DASSERT(str->data); + DASSERT(str->is_dol); + if (!str->is_dol) + return 0; + + uint clean_count = 0; + if ( opt_clean_dol && CleanDol(str) ) + clean_count++; + + if (!(str->dol_status&DOL_S_ANALYZED)) + AnalyzeDOL(str,false); + + InititializeGCT(); + if ( opt_wcode >= WCODE_ENABLED ) + { + if (wifi_domain_is_wiimmfi) + { + BZ2Manager_t *wcm = GetWiimmfiCode(str->mode); + if (wcm) + { + DecodeBZIP2Manager(wcm); + if ( opt_wcode == WCODE_GECKO ) + { + str->dol_flags[DPF_WCODE] = 'w'; + AddCheatCode(wcm->data,wcm->size,true,"WCODE"); + } + else + { + str->dol_flags[DPF_WCODE] = 'W'; + AddWCode(wcm->data,wcm->size); + } + } + } + + uint i; + for ( i = 0; i < opt_wcode_list.used; i++ ) + if (AddWCodeFile(str,opt_wcode_list.field[i])) + opt_wcode_list.field[i] = 0; + } + + uint patch_count = 0; + patch_count += PatchHTTPS(str); + patch_count += PatchCTCODEDOL(str); + patch_count += AddSectionsDOL(str,false); + patch_count += CreateSectionDOL(str); + patch_count += PatchByListWPF(str); + + if (use_gct_loader) + { + enumError err = SetupCodeLoader(str,0,0,0,0,CHT_NONE,0); + if ( err == ERR_OK || err == ERR_WARNING ) + patch_count++; + } + + + //-- sdkver by region + + if ( opt_str_vs_region >= 0 ) + { + const u32 vers_addr = GetSdkverAddress(str->mode); + if (vers_addr) + { + dol_header_t *dh = (dol_header_t*)str->data; + const u32 offset = GetDolOffsetByAddr(dh,vers_addr,8,0); + if (offset) + { + u8 old[8]; + memcpy(old,str->data+offset,sizeof(old)); + + char buf[20]; + snprintf(buf,sizeof(buf), "%05u", opt_str_vs_region & 0xffff); + memcpy(str->data+offset+1,buf,6); + if ( str->data[offset] == '0' ) + str->data[offset]++; + + if (memcmp(old,str->data+offset,sizeof(old))) + patch_count++; + } + } + } + + + //--- patch second host + + bool wiimmfi_patched = false; + if ( wifi_domain_is_wiimmfi || opt_wcode >= WCODE_ENABLED ) + { + u32 *h2 = GetSecondHostPointer(str,0); + noPRINT("h2=%p %x\n",h2,h2?ntohl(*h2):0); + if ( h2 && ntohl(*h2) != ASM_NOP ) + { + write_be32(h2,ASM_NOP); + patch_count++; + wiimmfi_patched = true; + } + } + + + //-- patched by + + patched_by_t pb_mode = patched_by_mode; + switch((int)pb_mode) + { + case PBY_WIIMMFI: + if (wiimmfi_patched) + pb_mode = PBY_ALWAYS; + break; + + case PBY_AUTO: + if (patch_count) + pb_mode = PBY_ALWAYS; + break; + } + + if ( pb_mode == PBY_RESET ) + { + char *dest = GetIdPointer(str); + if (dest) + memset(dest,'*',PATCHED_BY_SIZE); + } + else if ( pb_mode == PBY_ALWAYS ) + { + char *dest = GetIdPointer(str); + if (dest) + { + char *end = dest + PATCHED_BY_SIZE; + if (*str->dol_flags) + dest += snprintf( dest, PATCHED_BY_SIZE, "%.*s [%s]", + PATCHED_BY_SIZE - 3 - DPF__N, + opt_patched_by, str->dol_flags ); + else + dest = StringCopyE(dest,end,opt_patched_by); + while ( dest < end ) + *dest++ = ' '; + } + } + + return clean_count + patch_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// patch via WPF /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint PatchByListWPF +( + staticr_t *str // valid pointer +) +{ + DASSERT(str); + DASSERT(str->data_size); + DASSERT(str->data); + + uint i, count = 0; + for ( i = 0; i < opt_wpf_list.used; i++ ) + { + ccp fname = opt_wpf_list.field[i]; + if ( fname && *fname ) + { + const int stat = PatchByFileWPF(str,fname); + if ( stat < 0 ) + { + FreeString(opt_wpf_list.field[i]); + opt_wpf_list.field[i] = 0; + } + else if ( stat > 0 ) + count++; + } + } + + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PatchByFileWPF +( + staticr_t *str, // valid pointer + ccp fname +) +{ + DASSERT(str); + DASSERT(fname); + + + //--- load file + + u8 *data = 0; + uint size = 0; + if (OpenReadFILE(fname,0,true,&data,&size,0,0)) + { + ERROR0(ERR_WARNING,"Can't load WPF file: %s\n",fname); + return -1; + } + + + //--- analyse file + +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,size,size); + PRINT(">>> [%s,%u]: %s\n",GetNameFF(fform,0),size,fname); + bool done = false; + int stat = -1; + + if ( size > sizeof(wpf_head_t) && be32(data+4) <= size ) + { + wpf_t *wpf = (wpf_t*)(data+sizeof(wpf_head_t)); + if ( fform == FF_WPF ) + { + done = true; + stat = PatchByWPF( str, wpf, size-sizeof(wpf_head_t), fname ); + } + + #if HAVE_XSRC + if ( fform == FF_XPF ) + { + done = true; + stat = PatchByXPF( str, wpf, size-sizeof(wpf_head_t), fname ); + } + #endif + } + + if (!done) + ERROR0(ERR_WARNING,"Not a WPF file:: %s\n",fname); + FREE(data); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PatchByWPF +( + staticr_t *str, // valid pointer + const wpf_t *wpf, // wpf patch list + uint size, // size of data beginning at 'wpf' + ccp fname // filename for error messages +) +{ + DASSERT(str); + DASSERT(fname); + printf("PatchByWPF(): %s\n",fname); + + HexDump16(stdout,0,0,wpf, size < 32 ? size : 32); + return -1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool opt_clean_dol = false; // true: remove additional DOL sections +bool opt_add_ctcode = false; // true: ad CT-CODE to main.dol +int opt_all_ranks = 0; // -1:off, 0:don't patch, 1:on +bool opt_move_d8 = false; // true: move CT-CODE D8 section to another place +bool opt_full_gch = false; // true: use full- of only- 'codehandler' + +bool opt_gct_scan_sep = true; // true: scan 'F0000001 0000xxxx' separators +bool opt_gct_sep = false; // true: search cheat code separators +bool opt_gct_asm_sep = false; // true: use beginning of ASM blocks as separator +uint opt_gct_list = false; // >0: print a summary / >1: be verbose +int opt_gct_move = OFFON_AUTO; // move GCT to heap +u32 opt_gct_addr = 0x802c0000; // address for opt_gct_move +u32 opt_gct_space = 0; // minimal code space for opt_gct_move +int opt_allow_user_gch = OFFON_AUTO; // allow user define Gecko Code Handler + +StringField_t opt_sect_list = {0}; // files with section data of type GCT|GCH|WCH +StringField_t opt_wpf_list = {0}; // files with patches of type WPF|XPF +StringField_t opt_wcode_list = {0}; // GCT files for wcode jobs + +int opt_str_vs_region = SREG_NONE; // versus region patch mode +bool opt_str_xvs_region = false; // true: patch also test offsets +bool opt_str_tvs_region = false; // true: enable test mode with incremented regions + +int opt_str_bt_region = SREG_NONE; // battle region patch mode +bool opt_str_xbt_region = false; // true: patch also test offsets +bool opt_str_tbt_region = false; // true: enable test mode with incremented regions + +bool opt_meno = false; // true: patch 'Menu*' to 'MenO*' + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRegionHelper ( ccp arg, bool is_battle, ccp prefix ) +{ + region_info_t ri; + SetupRegionInfo(&ri,0,is_battle); + + DASSERT(ri.opt_region); + if ( !arg || !*arg ) + { + *ri.opt_region = SREG_NONE; + return 0; + } + + DASSERT(ri.opt_x); + *ri.opt_x = *arg == 'x' || *arg == 'X'; + if (*ri.opt_x) + arg++; + + DASSERT(ri.opt_t); + *ri.opt_t = ( *arg == 't' || *arg == 'T' ) && arg[1] >= '0' && arg[1] <= '9'; + if (*ri.opt_t) + arg++; + + if ( *arg >= '0' && *arg <= '9' ) + { + u32 num; + const enumError err = + ScanSizeOptU32( + &num, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "region", // ccp opt_name + 0, // u64 min + 0xffff, // u64 max + 1, // u32 multiple + 0, // u32 pow2 + false // bool print_err + ); + + if ( err == ERR_OK ) + { + *ri.opt_region = num; + return 0; + } + } + + static const KeywordTab_t tab[] = + { + { SREG_JAPAN, "JAPAN", 0, 0 }, + { SREG_AMERICA, "AMERICA", "USA", 0 }, + { SREG_EUROPE, "EUROPE", 0, 0 }, + + { SREG_AUSTRALIA, "AUSTRALIA", "OCEANIA", 0 }, + { SREG_KOREA, "KOREA", 0, 0 }, + { SREG_TAIWAN, "TAIWAN", 0, 0 }, + { SREG_CHINA, "CHINA", 0, 0 }, + + { SREG_NONE, "NONE", 0, 0 }, + { SREG_RESTORE, "RESTORE", "NINTENDO", 0 }, + { SREG_TEST, "TEST", 0, 9001 }, + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (cmd) + { + *ri.opt_region = cmd->id; + if ( *ri.opt_region == SREG_TEST ) + { + *ri.opt_region = cmd->opt; + *ri.opt_t = true; + } + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid `region' mode (option --%sregion): '%s'\n",prefix,arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptVersusRegion ( ccp arg ) +{ + return ScanOptRegionHelper(arg,false,"vs-"); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptBattleRegion ( ccp arg ) +{ + return ScanOptRegionHelper(arg,true,"bt-"); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRegion ( ccp arg ) +{ + const int stat = ScanOptRegionHelper(arg,false,""); + + opt_str_bt_region = opt_str_vs_region; + opt_str_xbt_region = opt_str_xvs_region; + opt_str_tbt_region = opt_str_tvs_region; + if (opt_str_tbt_region) + opt_str_bt_region += N_TAB_VS_REGION_PATCH; + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptAllRanks ( ccp arg ) +{ + if ( !arg || !*arg ) + { + opt_all_ranks = 1; + return 0; + } + + static const KeywordTab_t tab[] = + { + { -1, "RETORE", "NINTENDO", 0 }, + { -1, "RESET", 0, 0 }, // [obsolete]] in 2015 + { 0, "NONE", 0, 0 }, + { 1, "ON", 0, 0 }, + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (cmd) + { + opt_all_ranks = cmd->id; + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid `all-ranks' mode (option --all-ranks): '%s'\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t HttpsOptions[] = +{ + { HTTPS_NONE, "NONE", 0, 0 }, + { HTTPS_RESTORE, "RESTORE", "NINTENDO", 0 }, + { HTTPS_HTTP, "HTTP", 0, 0 }, + { HTTPS_DOMAIN, "DOMAIN", 0, 0 }, + { HTTPS_SAKE0, "SAKE0", "S0", 0 }, + { HTTPS_SAKE1, "SAKE1", "S1", 0 }, + { 0,0,0,0 } +}; + +https_mode_t opt_https; +bool opt_wc24 = false; +char mkw_domain[100] = "mariokartwii.gs.wiimmfi.de"; +ccp wifi_domain = mkw_domain + 16; +bool wifi_domain_is_wiimmfi = false; +wcode_t opt_wcode = WCODE_AUTO; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptHttps ( ccp arg ) +{ + const KeywordTab_t *cmd = ScanKeyword(0,arg,HttpsOptions); + if (cmd) + { + opt_https = cmd->id; + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid mode for option --https: '%s'\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetHttpsName ( https_mode_t mode, ccp return_if_failed ) +{ + const KeywordTab_t *cmd; + for ( cmd = HttpsOptions; cmd->name1; cmd++ ) + if ( cmd->id == mode ) + return cmd->name1; + return return_if_failed; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptDomain ( ccp https, ccp arg ) +{ + if (arg) + snprintf(mkw_domain,sizeof(mkw_domain),"mariokartwii.gs.%s",arg); + + int stat = https ? ScanOptHttps(https) : 0; + wifi_domain_is_wiimmfi = opt_https >= HTTPS_DOMAIN + && ( !strcmp(wifi_domain,"wiimmfi.de") + || !strcmp(wifi_domain,"test.wiimmfi.de") ); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t wcode_keytab[] = +{ + { WCODE_OFF, "OFF", "0", 0 }, + { WCODE_AUTO, "AUTO", "DEFAULT", 0 }, + { WCODE_GECKO, "GECKO", "CHEAT", 0 }, + { WCODE_ON, "ON", "1", 0 }, + { 0,0,0,0 } +}; + +int ScanOptWCode ( ccp arg ) +{ + if (!arg) + { + opt_wcode = WCODE_ON; + return 0; + } + + int status; + const KeywordTab_t *key = ScanKeyword(&status,arg,wcode_keytab); + if (key) + { + opt_wcode = key->id; + return 0; + } + + PrintKeywordError(wcode_keytab,arg,status,0,"option --wcode"); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPatchedBy ( ccp arg ) +{ + if (!*arg) + arg = ""; + + while ( *arg > 0 && *arg <= ' ' ) + arg++; + + if (!*arg) + StringCopyS(opt_patched_by,sizeof(opt_patched_by),STD_PATCHED_BY); + else if ( *arg == '+' ) + snprintf(opt_patched_by,sizeof(opt_patched_by), + "%s [%s]",STD_PATCHED_BY,arg+1); + else + StringCopyS(opt_patched_by,sizeof(opt_patched_by),arg); + + uchar *ptr; + for ( ptr = (uchar*)opt_patched_by; *ptr; ptr++ ) + if ( *ptr < ' ' || *ptr == 0xa0 ) + *ptr = ' '; + + noPRINT("< %s\n> %s\n",arg,opt_patched_by); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enum VsBtMode patch_vs_mode = VSBT_OFF; +enum VsBtMode patch_bt_mode = VSBT_OFF; +char patch_vs_str[2] = {0}; +char patch_bt_str[2] = {0}; + +int ScanOptVS ( bool is_bt, bool allow_2, ccp arg ) +{ + if (!arg) + return 0; + + ccp std_str; + char *str; + enum VsBtMode *mode; + if (is_bt) + { + std_str = "bt"; + str = patch_bt_str; + mode = &patch_bt_mode; + } + else + { + std_str = "vs"; + str = patch_vs_str; + mode = &patch_vs_mode; + } + + switch(strlen(arg)) + { + case 0: + *mode = VSBT_OFF; + return 0; + + case 1: + *mode = VSBT_PATCH; + str[0] = std_str[0]; + str[1] = arg[0]; + return 0; + + case 2: + if (allow_2) + { + *mode = VSBT_PATCH; + str[0] = arg[0]; + str[1] = arg[1]; + return 0; + } + break; + + case 4: + if (!strcasecmp(arg,"test")) + { + *mode = VSBT_TEST; + return 0; + } + break; + } + + if (allow_2) + ERROR0(ERR_SYNTAX,"Only 1 or 2 characters allowed for option --%s2: %s\n", + std_str, arg ); + else + ERROR0(ERR_SYNTAX,"Only 1 character allowed for option --%s: %s\n", + std_str, arg ); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCannon ( ccp arg ) +{ + if ( !arg || !*arg ) + { + memset(cannon_param,0,sizeof(cannon_param)); + return 0; + } + + char *end; + uint index = str2ul(arg,&end,10); + if ( end == arg ) + return ERROR0(ERR_SYNTAX,"Option --cannon: Invalid argument: %s",arg); + if ( index >= CANNON_N ) + return ERROR0(ERR_SYNTAX,"Option --cannon: Invalid index: %d",index); + + cannon_param_t *cp = cannon_param + index; + memset(cp,0,sizeof(*cp)); + const u8 * orig = CannonDataLEX+4 + index * CANNON_SIZE; + uint i; + for ( i = 0; i < CANNON_N_PARAM; i++, orig += sizeof(float) ) + cp->param[i] = bef4(orig); + + for ( i = 0; i < CANNON_N_PARAM; i++ ) + { + arg = end; + while ( *arg == ' ' || *arg == '\t' || *arg == ',' ) + arg++; + double d = strtod(arg,&end); + if ( end == arg ) + break; + cp->param[i] = d; + } + if ( i > 0 ) + cp->valid = true; + + #if HAVE_PRINT + { + uint i; + cannon_param_t *cp = cannon_param; + for ( i = 0; i < CANNON_N; i++, cp++ ) + PRINT(">CANNON[%u]: valid=%d, %10f %10f %10f %10f\n", + i, cp->valid, cp->param[0], cp->param[1], cp->param[2], cp->param[3] ); + } + #endif + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Option --create-sect /////////////// +/////////////////////////////////////////////////////////////////////////////// + +dol_sect_select_t section_list[DOL_N_SECTIONS]; +uint n_section_list = 0; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptGctMove ( ccp arg ) +{ + const int stat = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_FORCE,"Option --gct-move"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_gct_move = stat; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptAllowUserGch ( ccp arg ) +{ + const int stat + = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_FORCE,"Option --allow-user-gch"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_allow_user_gch = stat; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptGctAddr ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_gct_addr, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "gct-addr", // ccp opt_name + 0x00000000, // u64 min + 0x817fffff, // u64 max + 4, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptGctSpace ( ccp arg ) +{ + return ScanSizeOptU32( + &opt_gct_space, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "gct-space", // ccp opt_name + 0, // u64 min + 0x1000000, // u64 max + 4, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCreateSect ( ccp arg ) +{ + if ( !arg || !*arg ) + return 0; + + if ( n_section_list >= DOL_N_SECTIONS ) + { + ERROR0(ERR_SEMANTIC,"Section list for --create-sect is full.\n"); + return 1; + } + + + //--- check for 'P' + + ccp arg0 = arg; + bool use_param = *arg == 'P' || *arg == 'p'; + if (use_param) + { + arg++; + while ( *arg == ' ' || *arg == '\t' || *arg == ',' ) + arg++; + } + + + //--- check for 'T' 'D' 'T#' 'D#' + + int find_mode = 0, idx = -1; + if ( *arg == 'T' || *arg == 't' ) + { + find_mode = 1; + arg++; + if ( *arg >= '0' && *arg < '0' + DOL_N_TEXT_SECTIONS ) + idx = *arg++ - '0'; + } + else if ( *arg == 'D' || *arg == 'd' ) + { + find_mode = 2; + arg++; + if ( *arg >= '0' && *arg < '0' + DOL_N_DATA_SECTIONS ) + idx = *arg++ - '0' + DOL_N_TEXT_SECTIONS; + } + + if (find_mode) + { + ccp start = arg; + while ( *arg == ' ' || *arg == '\t' || *arg == ',' ) + arg++; + if ( arg == start && *arg != '=' ) + { + arg = arg0; + find_mode = 0; + idx = -1; + } + } + + //--- check for an addr + + char *end; + uint addr = str2ul(arg,&end,16); + if ( end > arg ) + { + arg = end; + if (addr) + addr |= 0x80000000; + } + else + addr = 0; + + if ( *arg != '=' ) + { + arg = arg0; + use_param = false; + find_mode = 0; + idx = -1; + addr = 0; + } + else + arg++; + + dol_sect_select_t *sl = section_list + n_section_list++; + memset(sl,0,sizeof(*sl)); + sl->find_mode = find_mode; + sl->sect_idx = idx; + sl->use_param = use_param; + sl->addr = addr; + sl->fname = arg; + + switch (sl->find_mode) + { + case 1: + sl->name[0] = 'T'; + sl->name[1] = sl->sect_idx < 0 ? '*' + : '0' + sl->sect_idx; + break; + + case 2: + sl->name[0] = 'D'; + sl->name[1] = sl->sect_idx < 0 ? '*' + : '0' + sl->sect_idx - DOL_N_TEXT_SECTIONS; + break; + + default: + sl->name[0] = '*'; + sl->name[1] = '*'; + break; + } + + PRINT("CREATE-SECTION[%s,%c]: %u,%2d %#10x : %s\n", + sl->name, sl->use_param ? 'W' : '-', + sl->find_mode, sl->sect_idx, sl->addr, sl->fname ); + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DumpSTR() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpSTR +( + FILE * f, // dump to this file + int indent, // indention + staticr_t * str, // valid pointer + bool use_c // true: print in C language fragments +) +{ + DASSERT(f); + DASSERT(str); + + if (str->is_dol) + return DumpDOL(f,indent,str,use_c); + + DASSERT(str->data); + DASSERT( str->data_size >= sizeof(rel_header_t) ); + + indent = NormalizeIndent(indent); + + + //----- dump header + + rel_header_t hd; + be32n( (u32*)&hd, (u32*)str->data, sizeof(rel_header_t)/sizeof(u32) ); + + ccp mod_name = "-"; + if ( hd.mod_name && hd.mod_name + hd.mod_name_len < str->data_size ) + mod_name = (ccp)str->data + hd.mod_name; + + + fprintf(f,"%*sId: %6u = 0x%x\n", indent,"", hd.id, hd.id ); + fprintf(f,"%*sN(sections): %6u\n", indent,"", hd.n_section ); + fprintf(f,"%*sModule Name: %6s\n", indent,"", mod_name ); + fprintf(f,"%*sReloc Offset: %6x\n", indent,"", hd.reloc_off ); + + fprintf(f,"\n%*sSection (0..%u):\n", indent,"", hd.n_section-1 ); + rel_sect_info_t *si = (rel_sect_info_t*)( str->data + hd.section_off ); + uint s; + for ( s = 0; s < hd.n_section; s++, si++ ) + { + u32 size = be32(&si->size); + if (size) + { + u32 offset = be32(&si->offset); + if (!offset) + fprintf(f,"%*s%4u. BSS %7x\n", indent,"", s, size ); + else + { + ccp type = offset & 1 ? "TEXT" : "DATA"; + offset &= ~1; + fprintf(f,"%*s%4u. %s %7x : %6x .. %6x\n", + indent,"", s, type, size, offset, offset + size - 1 ); + } + } + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DumpDOL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError DumpDOL_c +( + FILE * f, // dump to this file + staticr_t * str // valid pointer +) +{ + DASSERT(f); + DASSERT(str); + DASSERT(str->data); + DASSERT( str->data_size >= sizeof(dol_header_t) ); + + dol_header_t dol; + if ( str->data_size < sizeof(dol) ) + return ERR_WARNING; + ntoh_dol_header(&dol,(dol_header_t*)str->data); + + fputs("\nstatic const dol_sect_info_t dol_info_xxx[DOL_NN_SECTIONS] =\n{\n",f); + char name[8]; + + uint sect; + for ( sect = 0; sect < DOL_NN_SECTIONS; sect++ ) + { + dol_sect_info_t info; + GetSectionDOL(str,&info,sect,true); + snprintf(name,sizeof(name),"\"%s\",",info.name); + fprintf(f," { %2u, %-7s%#8x, %#10x, %#8x, %u,0,%u, 0,", + sect, name, info.off, info.addr, info.size, + info.sect_valid, info.hash_valid ); + + if (info.hash_valid) + { + u8 *ptr = info.hash; + uint line; + for ( line = 0; line < 2; line++ ) + { + ccp sep = line ? ",\n\t\t " : "\n\t\t{ "; + uint idx; + for ( idx = 0; idx < sizeof(info.hash)/2; idx++ ) + { + fprintf(f,"%s%s0x%02x",sep, idx==sizeof(info.hash)/4 ? " " : "", *ptr++ ); + sep = ","; + } + } + fputs(" }},\n",f); + } + else + fputs(" {0} },\n",f); + } + + fputs("};\n\n",f); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DumpDOL +( + FILE * f, // dump to this file + int indent, // indention + staticr_t * str, // valid pointer + bool use_c // true: print in C language fragments +) +{ + DASSERT(f); + DASSERT(str); + + if (use_c) + return DumpDOL_c(f,str); + + DASSERT(str->data); + DASSERT( str->data_size >= sizeof(dol_header_t) ); + + DumpDolHeader( f, indent, (dol_header_t*)str->data, str->data_size, + long_count > 1 ? 7 : long_count > 0 ? 3 : 1 ); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ExtractSTR() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ExtractSTR +( + staticr_t * str, // valid pointer + ccp dest_dir // valid destination directory +) +{ + DASSERT(str); + DASSERT(str->data); + DASSERT(dest_dir); + + if (str->is_dol) + return ExtractDOL(str,dest_dir); + + return ERROR0(ERR_WARNING, + "Extraction of StaticR.rel files is not implemented yet!\n"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ExtractDOL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ExtractDOL +( + staticr_t * str, // valid pointer + ccp dest_dir // valid destination directory +) +{ + DASSERT(str); + DASSERT(str->data); + DASSERT(str->is_dol); + DASSERT(dest_dir); + + CreatePath(dest_dir,true); + + //--- open log file + + char path_buf[PATH_MAX]; + ccp path = PathCatPP(path_buf,sizeof(path_buf),dest_dir,"dol-setup.txt"); + FILE *log = fopen(path,"wb"); + if (!log) + return ERROR0(ERR_CANT_CREATE, + "Can't create setup file: %s\n",path); + + static ccp sep = "#---------------------------------------------------------------------\n"; + fprintf(log,"\n%s#sect offset address size sha1 checksum\n%s",sep,sep); + + + //--- extract 'dol-header.bin' + + path = PathCatPP(path_buf,sizeof(path_buf),dest_dir,"dol-header.bin"); + FILE *f = fopen(path,"wb"); + if (!f) + { + fclose(log); + return ERROR0(ERR_CANT_CREATE, + "Can't create data file: %s\n",path); + } + fwrite(str->data,1,0x100,f); + fclose(f); + + + //--- extract sections + + uint sect; + for ( sect = 0; sect < DOL_NN_SECTIONS; sect++ ) + { + if ( sect == DOL_N_TEXT_SECTIONS || sect == DOL_N_SECTIONS ) + fputs(sep,log); + dol_sect_info_t info; + GetSectionDOL(str,&info,sect,true); + if (!info.sect_valid) + { + //fprintf(log,"%-4s= -\n",info.name); + continue; + } + + fprintf(log,"%-4s= %06x %08x %06x",info.name,info.off,info.addr,info.size); + if (info.data_valid) + { + if (info.hash_valid) + { + fputc(' ',log); + uint i; + for ( i = 0; i < sizeof(info.hash); i++ ) + fprintf(log,"%02x",info.hash[i]); + } + + path = PathCatPPE(path_buf,sizeof(path_buf),dest_dir,info.name,".bin"); + FILE *f = fopen(path,"wb"); + if (!f) + { + fclose(log); + return ERROR0(ERR_CANT_CREATE, + "Can't create data file: %s\n",path); + } + fwrite(info.data,1,info.size,f); + fclose(f); + } + fputc('\n',log); + } + fputs(sep,log); + + + //--- close log and return + + fprintf(log,"\n"); + fclose(log); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ctcode_data_tab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef DEF_DATA +#undef DEF_BZIP +//#undef DEF_PATCH + +#define DEF_DATA(a) { #a, 0, a, sizeof(a) }, +#define DEF_BZIP(a) { #a, 1, a##_bz2, sizeof(a##_bz2) }, +//#define DEF_PATCH(m,a,b) { #a, m, b, sizeof(b), a }, + +const data_tab_t maindol_data_tab[] = +{ + DEF_BZIP(ctcode_boot_code_pal) + DEF_BZIP(ctcode_boot_code_usa) + DEF_BZIP(ctcode_boot_code_jap) + + DEF_BZIP(ctcode_boot_data_pal) + DEF_BZIP(ctcode_boot_data_usa) + DEF_BZIP(ctcode_boot_data_jap) + + DEF_BZIP(codehandler) + DEF_BZIP(codehandleronly) + + {0,0,0} +}; + +#undef DEF_DATA +#undef DEF_BZIP +//#undef DEF_PATCH + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-staticr.h b/project/src/lib-staticr.h new file mode 100644 index 0000000..afa62f6 --- /dev/null +++ b/project/src/lib-staticr.h @@ -0,0 +1,745 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// all about BMG files: +// http://wiki.tockdom.de/index.php?title=BMG + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_STATICR_H +#define SZS_LIB_STATICR_H 1 + +#include "lib-std.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// rel file format /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rel_header_t +{ + /*00*/ u32 id; // Arbitrary identification number for game use. + /*04*/ u32 unknown_04; // padding? ignored by MKW + /*08*/ u32 unknown_08; // padding? ignored by MKW + /*0c*/ u32 n_section; // Number of sections in the file. + /*10*/ u32 section_off; // Offset to the start of the section table. + /*14*/ u32 mod_name; // NULL or Offset to ASCII string containing name of module. + /*18*/ u32 mod_name_len; // Length of 'mod_name' name in bytes. + /*1c*/ u32 version; // Version number of the REL file format. + /*20*/ u32 bss_size; // Size of the 'bss' section. + /*24*/ u32 reloc_off; // Offset to the relocation table. + /*28*/ u32 imp_off; // Offset to imp(?). + /*2c*/ u32 imp_size; // Size of imp(?). + /*30*/ u32 flags; // flags of some sort? ignored by MKW + /*34*/ u32 prolog; // ? + /*38*/ u32 epilog; // ? + /*3c*/ u32 unknown_3c; // unresolved: ? + /*40*/ u32 align_all; // Version e 2: Alignment constraint on all sections. + /*44*/ u32 align_bss; // Version e 2: Alignment constraint on all 'bss' sections. + /*48*/ u32 fix_size; // Version e 3: fixSize + +} +__attribute__ ((packed)) rel_header_t; + + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rel_sect_info_t +{ + u32 offset; // Location in file of the section information. + // The last bit determines if this section is + // executable or not. If offset is zero, + // the section is an uninitialized section. + u32 size; // size of of the section +} +__attribute__ ((packed)) rel_sect_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rel_imp_t +{ + u32 type; // Type of the relocation entries: + // 1 for internal, 0 for external. + u32 offset; // Offset from the beginning of the REL + // to the relocatino data. +} +__attribute__ ((packed)) rel_imp_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct rel_data_t +{ + u16 skip; // Number of bytes to skip before this relocation. + u8 type; // The relocation type => enum rel_type_t + u8 section; // The section of the symbol to locate. + u32 address; // Address of the symbol in the section. +} +__attribute__ ((packed)) rel_data_t; + +/////////////////////////////////////////////////////////////////////////////// + +typedef enum rel_type_t +{ + RELT_NONE = 0, // Do nothing. Skip this entry. + + RELT_ADDR32 = 1, // Write the 32 bit address of the symbol. + + RELT_ADDR24 = 2, // Write the 24 bit address of the symbol divided by + // four shifted up 2 bits to the 32 bit value + // (for relocating branch instructions). + // Fail if address won't fit. + + RELT_ADDR16 = 3, // Write the 16 bit address of the symbol. + // Fail if address more than 16 bits. + + RELT_ADDR16_LO = 4, // Write the low 16 bits of the address of the symbol. + + RELT_ADDR16_HI = 5, // Write the high 16 bits of the address of the symbol. + + RELT_ADDR16_HA = 6, // Write the high 16 bits of the address of the symbol + // plus 0x8000. + + RELT_ADDR14a = 7, // Write the 14 bits of the address of the symbol + RELT_ADDR14b, // divided by four shifted up 2 bits to the 32 + RELT_ADDR14c, // bit value (for relocating conditional branch + // instructions). Fail if address won't fit. + + + + RELT_REL24 = 10, // Write the 24 bit address of the symbol minus the + // address of the relocation divided by four shifted + // up 2 bits to the 32 bit value (for relocating relative + // branch instructions). Fail if address won't fit. + + RELT_REL14a = 11, // Write the 14 bit address of the symbol minus the + RELT_REL14b, // address of the relocation divided by four shifted + RELT_REL14c, // up 2 bits to the 32 bit value (for relocating + // conditional relative branch instructions). + // Fail if address won't fit. + + RELT__NONE = 200, // Do nothing. Skip this entry. + // Carry the address of the symbol to the next entry. + + RELT__SECT = 201, // Change which section relocations are being applied + // to. Set the offset into the section to 0. + + RELT__STOP = 202, // Stop parsing the relocation table. + +} +__attribute__ ((packed)) rel_type_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enums /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[str_mode_t]] + +typedef enum str_mode_t +{ + STR_M_UNKNOWN, + + STR_M_PAL, + STR_M_USA, + STR_M_JAP, + STR_M_KOR, + + STR_M__N, + + //--- zero based index support + STR_ZBI_FIRST = STR_M_PAL, + STR_ZBI_N = STR_M__N - STR_ZBI_FIRST, + +} +__attribute__ ((packed)) str_mode_t; + +ccp GetStrModeName ( str_mode_t ); + +//----------------------------------------------------------------------------- +// [[str_flags_t]] + +typedef enum str_flags_t +{ + STR_F_IS_DOL = 0x01, // 0: staticr.rel, 1: main.dol + STR_F_IS_OPTIONAL = 0x02, // optional, ignore at normal operation + STR_F_IS_SPECIAL = 0x04, // special, hard coded for wiimmfi.de + STR_F_RESTORE_HTTPS = 0x08, // restore "https://" +} +__attribute__ ((packed)) str_flags_t; + +//----------------------------------------------------------------------------- +// [[str_server_t]] + +typedef enum str_server_t +{ + SERV_NONE, // special case: only 'https' + + SERV_NASW, + SERV_NASW_DEV, + SERV_NASW_TEST, + SERV_MKW_RACE, + SERV_SAKE, + SERV_S_SAKE, + SERV_GS, + + SERV__N + +} str_server_t; + +//----------------------------------------------------------------------------- +// [[str_server_list_t]] + +typedef struct str_server_list_t +{ + str_server_t serv_type; // type of server + ccp name; // short identification name + uint port; // redirection port + uint domain_len; // length of domain + ccp domain; // full qualified domain +} +str_server_list_t; + +extern const str_server_list_t ServerList[SERV__N+1]; + +//----------------------------------------------------------------------------- +// [[str_server_patch_t]] + +typedef struct str_server_patch_t +{ + str_mode_t mode; // language mode + str_flags_t flags; // flags + u8 is_http; // 0:neither, 1:http, 2:https + str_server_t serv_type; // server type + u32 offset; // file offset of 'https://' + uint url_len; // length of complete URL + ccp param; // param incl. separator +} +str_server_patch_t; + +extern const str_server_patch_t ServerPatch[]; + +//----------------------------------------------------------------------------- +// [[str_status_t]] + +typedef enum str_status_t +{ + //--- shift values + + STR_S_SHIFT_ORDER_ALT = 0, // alternative order detected + STR_S_SHIFT_ORDER_MULTI = 1, // multiple used elements detected + STR_S_SHIFT_ORDER_BAD = 2, // bad alternatives detected + STR_S_SHIFT_ORDER_DIFF = 3, // different alternatives detected + + //--- concrete values + + STR_S_ANALYZED = 0x0000001, // data analyzed + STR_S_WRONG_SIZE = 0x0000002, // wrong file size + STR_S_ORIG = 0x0000004, // original file (checksum ok) + STR_S_UNKNOWN_PATCH = 0x0000008, // unknown patches found + + STR_S_TRACK_ORDER = 0x0000010, // base value for tracks + STR_S_TRACK_ORDER_ALT = STR_S_TRACK_ORDER << STR_S_SHIFT_ORDER_ALT, + STR_S_TRACK_ORDER_MULTI = STR_S_TRACK_ORDER << STR_S_SHIFT_ORDER_MULTI, + STR_S_TRACK_ORDER_BAD = STR_S_TRACK_ORDER << STR_S_SHIFT_ORDER_BAD, + STR_S_TRACK_ORDER_DIFF = STR_S_TRACK_ORDER << STR_S_SHIFT_ORDER_DIFF, + + STR_S_ARENA_ORDER = 0x0000100, // base value for arenas + STR_S_ARENA_ORDER_ALT = STR_S_ARENA_ORDER << STR_S_SHIFT_ORDER_ALT, + STR_S_ARENA_ORDER_MULTI = STR_S_ARENA_ORDER << STR_S_SHIFT_ORDER_MULTI, + STR_S_ARENA_ORDER_BAD = STR_S_ARENA_ORDER << STR_S_SHIFT_ORDER_BAD, + STR_S_ARENA_ORDER_DIFF = STR_S_ARENA_ORDER << STR_S_SHIFT_ORDER_DIFF, + + STR_S_CTGP44 = 0x00001000, // CTGP 4.4 modification found + STR_S_CTGP44_LIKE = 0x00002000, // CTGP 4.4 like modification found + + STR_S_VS_REGION_KNOWN = 0x00004000, // known versus region patch found + STR_S_VS_REGION_KNOWN_DIFF = 0x00008000, // " with different values + STR_S_VS_REGION_UNKNOWN = 0x00010000, // unknown versus region patch found + + STR_S_BT_REGION_KNOWN = 0x00020000, // known battle region patch found + STR_S_BT_REGION_KNOWN_DIFF = 0x00040000, // " with different values + STR_S_BT_REGION_UNKNOWN = 0x00080000, // unknown battle region patch found + + STR_S_ALL_RANKS_KNOWN = 0x00100000, // known expand-fc patch found + STR_S_ALL_RANKS_UNKNOWN = 0x00200000, // expand-fc patch found + + STR_S_VS = 0x00400000, // 'vs' fields modified + STR_S_BT = 0x00800000, // 'bt' fields modified + + STR_S_HTTPS = 0x01000000, // https strings patched + STR_S_VERSUS_POINTS = 0x02000000, // versus points table modified + STR_S_CANNON = 0x04000000, // at least 1 cannon patched + STR_S_MENO = 0x08000000, // 'Menu' -> 'MenO' patched + + + //--- combined values + + STR_S_TRACK_ORDER__FAIL = STR_S_TRACK_ORDER_MULTI + | STR_S_TRACK_ORDER_BAD + | STR_S_TRACK_ORDER_DIFF, + + STR_S_ARENA_ORDER__FAIL = STR_S_ARENA_ORDER_MULTI + | STR_S_ARENA_ORDER_BAD + | STR_S_ARENA_ORDER_DIFF, + +} str_status_t; + +//----------------------------------------------------------------------------- +// [[dol_status_t]] + +typedef enum dol_status_t +{ + DOL_S_ANALYZED = 0x0001, // data analyzed + DOL_S_KNOWN = 0x0002, // seems to be a known main.dol + DOL_S_ORIG = 0x0004, // original file (checksum ok) + DOL_S_CTCODE = 0x0010, // CT-CODE variant found (checksum ok) + DOL_S_WIIMMFI = 0x0020, // wiimmfi patch found (checksum ok) + + DOL_S_HTTPS = 0x0040, // https strings patched + DOL_S_VBI = 0x0080, // VBI patched + DOL_S_SDKVER = 0x0100, // sdk version patched + DOL_S_2HOST = 0x0200, // second host patched + +} dol_status_t; + +//----------------------------------------------------------------------------- +// [[dol_info_flags_t]] + +typedef enum dol_info_flags_t +{ + DIF_MODE = 0x03, // str_mode_t - 1 + DIF_ORIG = 0x04, // orig image found + DIF_CTCODE = 0x08, // CT-CODE patched +} +dol_info_flags_t; + +//----------------------------------------------------------------------------- +// [[dol_patch_flags_t]] + +typedef enum dol_patch_flags_t +{ + DPF_WCODE, + DPF_CHEAT, + DPF_ALLOW_USER, + DPF_CTCODE, + DPF__N, +} +dol_patch_flags_t; + +//----------------------------------------------------------------------------- + +#define MAX_REGION_PATCH 5 +#define PATCHED_BY_SIZE 42 + +typedef enum patched_by_t +{ + PBY_OFF, + PBY_RESET, + PBY_WIIMMFI, + PBY_AUTO, + PBY_ALWAYS, +} +patched_by_t; + +extern patched_by_t patched_by_mode; +int ScanOptPBMode ( ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct staticr_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[staticr_t]] + +typedef struct staticr_t +{ + //--- base info + + ccp fname; // filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // file format + + //--- file status + + bool is_staticr; // file is a 'StaticR.rel' + bool is_dol; // file is a '*.dol' + bool is_main; // file is a known 'main.dol' + str_mode_t mode; // data mode + str_status_t str_status; // data status (only StaticR) + dol_status_t dol_status; // data status (only DOL files) + ccp dol_info; // NULL or pointer to a dol info + u32 dol_info_flags; // info flags for NAS by WCODE + bool dol_info_flags_set; // true, if DOF_ORIG already set + char dol_flags[DPF__N+1]; // flags to patch patched_by + bool dol_cleaned; // TRUE if DOL was cleaned + + //--- analyze data + + int vs_region[MAX_REGION_PATCH]; // found vs regions, -1=orig + int bt_region[MAX_REGION_PATCH]; // found bt regions, -1=orig + u32 vbi_address; // >0: VBI entry address + char patched_by[PATCHED_BY_SIZE+1]; // patched by version + char sdk_version[9]; // patched value of 'sdkver' + u8 cannon_data[48]; // copied cannon data + + //--- raw data + + u8 * data; // raw data + uint data_size; // size of 'data' + bool data_alloced; // true: 'data' must be freed + +} staticr_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSTR ( staticr_t * str ); +void ResetSTR ( staticr_t * str ); + +enumError LoadSTR +( + staticr_t * str, // valid pointer + bool initialize, // true: initialize 'str' + ccp fname, // filename of source + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +void AnalyzeSTR +( + staticr_t * str // valid pointer +); + +void PrintStatusSTR +( + FILE * f, // destination file + int indent, // indention + staticr_t * str, // valid pointer + int long_mode // >0: print more track details if changed +); + +uint PatchSTR +( + // returns: number of patches + + staticr_t * str // valid pointer +); + +enumError DumpSTR +( + FILE * f, // dump to this file + int indent, // indention + staticr_t * str, // valid pointer + bool use_c // true: print in C language fragments +); + +enumError ExtractSTR +( + staticr_t * str, // valid pointer + ccp dest_dir // valid destination directory +); + +/////////////////////////////////////////////////////////////////////////////// + +const u32 * GetTrackOffsetTab ( const staticr_t * str ); +const u32 * GetArenaOffsetTab ( const staticr_t * str ); +const u32 * GetRegionOffsetTab ( const staticr_t * str ); + +/////////////////////////////////////////////////////////////////////////////// +// [[VersusPointsInfo_t]] + +typedef struct VersusPointsInfo_t +{ + u8 region_mode; // related mode, type str_mode_t + char region_code; // region code: E|P|J|K + u32 off_staticr; // offset witing StaticR.rel + u32 cheat_base; // offset for the cheat code +} +VersusPointsInfo_t; + +extern const VersusPointsInfo_t VersusPointsInfo[STR_M__N]; +extern str_mode_t opt_cheat_region; + +static inline const VersusPointsInfo_t * GetVersusPointsInfo ( str_mode_t mode ) + { return VersusPointsInfo + ( mode < STR_M__N ? mode : STR_M_UNKNOWN ); } + +uint GetVersusPointsOffset ( str_mode_t mode ); +int ScanOptCheatRegion ( ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// WPF: Wiimms Patch File /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define WPF_MAGIC_NUM 0x57504601 // "WPF\1" +#define XPF_MAGIC_NUM 0x58504601 // "XPF\1" + +/////////////////////////////////////////////////////////////////////////////// +// [[wpf_t]] + +typedef struct wpf_t +{ + // big endian! + + u8 selector; // object selector, bit field + // 0: end of list + // 0x01..0x08 staticr.rel of regions (PAL/USA/JAP/KOR) + // 0x10..0x80 main.dol of regions (PAL/USA/JAP/KOR) + + u8 patch_type; // type of record: + // 0: end of list + // 'P': patch + // 'E': condition, true if data is equal + // 'N': condition, true if data is not equal + + u8 clear_cond; // 0: do nothing + // 'C': clear previous condition + // 'E': else: invert condition + + u8 addr_type; // 0: end of list + // 'A': address + // 'O': file offset + + u32 addr; // address or file offset + u32 size; // size of data + u8 data[]; // data, aligned by ALIGN(size,4) +} +__attribute__ ((packed)) wpf_t; + +// Note: +// If a conditions fails, +// then all jobs are ignored until 'clear_cond' is "E" or "C". + +/////////////////////////////////////////////////////////////////////////////// +// [[wpf_head_t]] + +typedef struct wpf_head_t +{ + u32 magic; // WPF_MAGIC_NUM + u32 version; // patch version + u32 size; // total size of patch + wpf_t wpf[]; // wpf records +} +__attribute__ ((packed)) wpf_head_t; + +/////////////////////////////////////////////////////////////////////////////// + +uint PatchByListWPF +( + staticr_t *str // valid pointer +); + +int PatchByFileWPF +( + staticr_t *str, // valid pointer + ccp fname +); + +int PatchByWPF +( + staticr_t *str, // valid pointer + const wpf_t *wpf, // wpf patch list + uint size, // size of data beginning at 'wpf' + ccp fname // filename for error messages +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main.dol support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void AnalyzeMAIN +( + staticr_t * str // valid pointer +); + +void PrintStatusMAIN +( + FILE * f, // destination file + int indent, // indention + staticr_t * str, // valid pointer + int long_mode // >0: print more track details if changed +); + +uint PatchDOL +( + // returns: number of patches + + staticr_t * str // valid pointer +); + +enumError DumpDOL +( + FILE * f, // dump to this file + int indent, // indention + staticr_t * str, // valid pointer + bool use_c // true: print in C language fragments +); + +enumError ExtractDOL +( + staticr_t * str, // valid pointer + ccp dest_dir // valid destination directory +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum str_region_t +{ + //--- official regions + SREG_JAPAN, + SREG_AMERICA, + SREG_EUROPE, + + //--- not clear + SREG_AUSTRALIA, + SREG_KOREA, + SREG_TAIWAN, + SREG_CHINA, + + //--- special + SREG_NONE = -1, // don't change region + SREG_RESTORE = -2, // restore region to nintendos default + SREG_TEST = -3, // test with regions 9001, 9002 and 9003 + +} str_region_t; + + +extern bool opt_clean_dol; // true: remove additional DOL sections +extern bool opt_add_ctcode; // true: ad CT-CODE to main.dol +extern int opt_all_ranks; // -1:off, 0:don't patch, 1:on +extern bool opt_move_d8; // true: move CT-CODE D8 section to another place +extern bool opt_full_gch; // true: use full- of only- 'codehandler' + +extern bool opt_gct_scan_sep; // true: scan 'F0000001 0000xxxx' separators +extern bool opt_gct_sep; // true: search cheat code separators +extern bool opt_gct_asm_sep; // true: use beginning of ASM blocks as separator +extern uint opt_gct_list; // >0: print a summary / >1: be verbose +extern int opt_gct_move; // move GCT to heap, one of KeyTab_OFF_AUTO_ON +extern u32 opt_gct_addr; // address for opt_gct_move +extern u32 opt_gct_space; // minimal code space for opt_gct_move +extern int opt_allow_user_gch; // allow user define Gecko Code Handler + +extern StringField_t opt_sect_list; // files with section data of type GCT|GCH|WCH +extern StringField_t opt_wpf_list; // files with patches of type WPF|XPF +extern StringField_t opt_wcode_list; // GCT files for wcode jobs + +extern int opt_str_vs_region; // versus region patch mode +extern bool opt_str_xvs_region; // true: patch also test offsets +extern bool opt_str_tvs_region; // true: enable test mode with incremented regions + +extern int opt_str_bt_region; // battle region patch mode +extern bool opt_str_xbt_region; // true: patch also test offsets +extern bool opt_str_tbt_region; // true: enable test mode with incremented regions + +extern bool opt_meno; // true: patch 'Menu*' to 'MenO*' + +extern dol_sect_select_t section_list[]; +extern uint n_section_list; + +int ScanOptRegion ( ccp arg ); +int ScanOptVersusRegion ( ccp arg ); +int ScanOptBattleRegion ( ccp arg ); +int ScanOptAllRanks ( ccp arg ); +int ScanOptGctAddr ( ccp arg ); +int ScanOptGctSpace ( ccp arg ); +int ScanOptCreateSect ( ccp arg ); + +//----------------------------------------------------------------------------- + +typedef enum https_mode_t +{ + HTTPS_NONE, + HTTPS_RESTORE, + HTTPS_HTTP, + HTTPS_DOMAIN, + HTTPS_SAKE0, + HTTPS_SAKE1, + + HTTPS__N +} +https_mode_t; + +extern https_mode_t opt_https; +extern bool opt_wc24; +extern const KeywordTab_t HttpsOptions[]; + +int ScanOptHttps ( ccp arg ); +ccp GetHttpsName ( https_mode_t mode, ccp return_if_failed ); + +extern char mkw_domain[]; +extern ccp wifi_domain; +extern bool wifi_domain_is_wiimmfi; +int ScanOptDomain ( ccp https, ccp arg ); + +typedef enum wcode_t +{ + WCODE_OFF, // wcode support disabled + WCODE_AUTO, // auto mode (default), set to WCODE_OFF or WCODE_ON + WCODE_GECKO, // enabled, but insert cheat codes + WCODE_ON, // enabled as wcode job and data + + WCODE_ENABLED = WCODE_GECKO, // minimal enabled code +} +wcode_t; + +extern wcode_t opt_wcode; +extern const KeywordTab_t wcode_keytab[]; +int ScanOptWCode ( ccp arg ); + +int ScanOptPatchedBy ( ccp arg ); +int ScanOptCannon ( ccp arg ); +int ScanOptGctMove ( ccp arg ); +int ScanOptAllowUserGch ( ccp arg ); + +enum VsBtMode { VSBT_OFF, VSBT_PATCH, VSBT_TEST }; +extern enum VsBtMode patch_vs_mode; +extern enum VsBtMode patch_bt_mode; +extern char patch_vs_str[2]; +extern char patch_bt_str[2]; + +int ScanOptVS ( bool is_bt, bool allow_2, ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_STATICR_H diff --git a/project/src/lib-std.c b/project/src/lib-std.c new file mode 100644 index 0000000..fd1d201 --- /dev/null +++ b/project/src/lib-std.c @@ -0,0 +1,6604 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#ifndef WIN_SZS_LIB + #include +#endif +#include + +#include "lib-std.h" +#include "lib-szs.h" +#include "lib-rarc.h" +#include "lib-pack.h" +#include "lib-brres.h" +#include "lib-breff.h" +#include "lib-xbmg.h" +#include "lib-kcl.h" +#include "lib-kmp.h" +#include "lib-lecode.h" +#include "lib-mdl.h" +#include "lib-pat.h" +#include "lib-image.h" +#include "lib-object.h" +#include "lib-staticr.h" +#include "lib-ctcode.h" +#include "lib-common.h" +#include "lib-bzip2.h" +#include "dclib-utf8.h" +#include "dclib-ui.h" +#include "lib-mkw.h" +#include "db-mkw.h" +#include "logo.inc" + +#ifdef WIN_SZS_LIB + #include +#endif + +#if defined(TEST) && !defined(__APPLE__) && !defined(__CYGWIN__) + #include +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp tool_name = "?"; +ccp share_path = 0; +ccp search_path[6] = {0}; +ccp auto_add_path[6] = {0}; // same size as search path +volatile int SIGINT_level = 0; +volatile int verbose = 0; +volatile int logging = 0; +bool allow_all = false; +char escape_char = '%'; +bool use_utf8 = true; +bool use_de = false; +bool ctcode_enabled = false; +bool lecode_enabled = false; +uint opt_ct_mode = 0; // calculated by ctcode_enabled & lecode_enabled +int testmode = 0; +int force_count = 0; +uint opt_tiny = 0; +bool force_kmp = false; +OffOn_t opt_battle_mode = OFFON_AUTO; +OffOn_t opt_export_flags = OFFON_AUTO; +int opt_route_options = 0; +OffOn_t opt_wim0 = OFFON_AUTO; +ccp opt_source = 0; +StringField_t source_list = {0}; +ccp opt_dest = 0; +bool opt_mkdir = false; +bool opt_overwrite = false; +bool opt_number = false; +bool opt_remove_src = false; +bool opt_remove_dest = false; +bool opt_update = false; +bool opt_preserve = false; +ccp opt_extract = 0; +int opt_ignore = 0; +bool opt_ignore_setup = false; +bool opt_purge = false; +file_format_t opt_fform = FF_UNKNOWN; +file_format_t fform_compr = FF_YAZ0; +file_format_t fform_compr_force = 0; +file_format_t script_fform = FF_TXT; +ccp script_varname = "res"; +int script_array = 0; +int opt_pt_dir = 0; +bool opt_links = false; +bool opt_rm_aiparam = false; +u32 opt_align = 0; +u32 opt_align_u8 = U8_DEFAULT_ALIGN; +u32 opt_align_pack = PACK_SUBFILE_ALIGN; +u32 opt_align_brres = BRRES_DEFAULT_ALIGN; +u32 opt_align_breff = BREFF_DEFAULT_ALIGN; +u32 opt_align_breft = 0; +bool print_header = true; +bool print_param = true; +int print_sections = 0; +double opt_tri_area = 1.0; +double opt_tri_height = 1.0; +bool opt_kmp_flags = true; +bool opt_id = false; +bool opt_base64 = false; +bool opt_db64 = false; +EncodeMode_t opt_coding64 = ENCODE_OFF; +bool opt_verify = false; +ccp opt_cache = 0; +ccp opt_cname = 0; +bool opt_round = false; +int brief_count = 0; +int long_count = 0; +//int full_count = 0; +int pipe_count = 0; +int delta_count = 0; +int diff_count = 0; +int minimize_level = 0; +int export_count = 0; +int all_count = 0; +bool raw_mode = false; +SortMode_t opt_sort = SORT_NONE; +u32 opt_max_file_size = 100*MiB; +int opt_compr_mode = 0; +u32 opt_compr = 9; +bool opt_norm = false; +int need_norm = 0; // enabled if > 0 +bool opt_fast = false; +char *opt_basedir = 0; +int opt_recurse = -1; +int opt_ext = 0; +bool opt_decode = false; +bool opt_cut = false; +bool opt_cmpr_valid = false; +u8 opt_cmpr_def[8] = { 0x00,0x00, 0x00,0x20, 0xff,0xff,0xff,0xff }; +uint opt_n_images = 0; +uint opt_max_images = 5; +uint opt_min_mipmap_size = 8; // minimal mipmap size +int opt_mipmaps = 0; +bool opt_strip = false; +bool opt_pre_convert = false; +bool opt_encode_all = false; +bool opt_encode_img = false; +bool opt_no_encode = false; +bool opt_no_recurse = false; +bool opt_auto_add = false; +bool opt_no_echo = false; +bool opt_generic = false; +bool opt_raw = false; +bool opt_tracks = false; +bool opt_arenas = false; +bool opt_order_all = false; +ccp opt_order_by = 0; +ccp opt_write_tracks = 0; +bool opt_no_check = false; +CheckMode_t global_check_mode = CMOD_DEFAULT; +ccp opt_flag_file = 0; +int opt_xtridata = 0; +int have_patch_count = 0; // <=0: disable patching at all +int have_kmp_patch_count = 0; +int have_kcl_patch_count = 0; +ParamField_t patch_bmg_list = {0}; +FormatField_t patch_image_list = {0}; +StringField_t ct_dir_list = {0}; +ccp analyze_fname = 0; +FILE * analyze_file = 0; + +double epsilon_pos = DEF_EPSILON_POS; // 'p' Position +double epsilon_rot = DEF_EPSILON_ROT; // 'r' Rotation +double epsilon_scale = DEF_EPSILON_SCALE; // 's' Scale +double epsilon_time = DEF_EPSILON_TIME; // 't' Time + +char iobuf [0x400000]; // global io buffer +//const char zerobuf[0x100] = {0}; // global zero buffer + +const char indent_msg[] = "> > > > > > > > > > > > > > > > > > > > "; + +const char section_sep[] = + "\r\n" + "#\f\r\n" + "###############################################################################\r\n" + "\r\n"; + +const char section_end[] = + "\r\n" + "#\f\r\n" + "###############################################################################\r\n" + "\r\n" + "[END]\r\n" + "# This section is ignored.\r\n" + "\r\n"; + +// begin of warning+hint line, for checks +const char check_bowl[] = " + WARNING: "; +const char check_bohl[] = " - HINT: "; +const char check_bosl[] = " * SLOT: "; +const char check_boil[] = " * INFO: "; +const char check_bosugl[] = " * SUGGESTION: "; + +int global_warn_count = 0; +int global_hint_count = 0; +int global_info_count = 0; + +const ColorSet_t *colset = 0; +void (*print_title_func) ( FILE * f ) = 0; + +const char WarnLevelNameLo[WLEVEL__N][8] = +{ + "unknown", + "na", + "ok", + "fixed", + "hint", + "warn", + "error", + "fail", + "fatal", + "invalid", +}; + +const char WarnLevelNameUp[WLEVEL__N][8] = +{ + "UNKNOWN", + "NA", + "OK", + "FIXED", + "HINT", + "WARN", + "ERROR", + "FAIL", + "FATAL", + "INVALID", +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// tables /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// PAL: file offset: 3a5a08, 3 cannons, 4 floats each +// 3a5a08: 500.00000 0 6000.00000 -1.00000 +// 3a5a18: 500.00000 5000.00000 6000.00000 -1.00000 +// 3a5a28: 120.00000 2000.00000 1000.00000 45.00000 + +const u8 CannonDataLEX[4+3*16] = +{ + 0,0,0,3, // number of cannons + 0x43,0xfa,0x00,0x00, 0x00,0x00,0x00,0x00, 0x45,0xbb,0x80,0x00, 0xbf,0x80,0x00,0x00, + 0x43,0xfa,0x00,0x00, 0x45,0x9c,0x40,0x00, 0x45,0xbb,0x80,0x00, 0xbf,0x80,0x00,0x00, + 0x42,0xf0,0x00,0x00, 0x44,0xfa,0x00,0x00, 0x44,0x7a,0x00,0x00, 0x42,0x34,0x00,0x00, +}; + +//----------------------------------------------------------------------------- + +const u8 TestDataLEX[8*1] = +{ + 0, // offline_online + 0, // n_offline + 0, // n_online + ~0, // cond_bit + 0, // game_mode + 0, // random + 0,0 +}; + +//----------------------------------------------------------------------------- + +const char is_arena_name[ARENA__N][9] = +{ + "no", // ARENA_NONE + "maybe", // ARENA_MAYBE + "found", // ARENA_FOUND + "dispatch", // ARENA_DISPATCH +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupColors() +{ + //----- setup colors + + colorize_stdout = colorize_stdlog = opt_colorize; // reset it first + colorize_stdout = GetFileColorized(stdout); + colorize_stdlog = GetFileColorized(stdlog); + + colout = GetColorSet(colorize_stdout); + collog = GetColorSet(colorize_stdlog); + colset = collog; + SetupStdMsg(); + + PRINT("COLORIZE: out=%d[%zd], err=%d[%zd], log=%d[%zd], msg=%d[%zd]\n", + colorize_stdout, strlen(colout->reset), + colorize_stderr, strlen(colerr->reset), + colorize_stdlog, strlen(collog->reset), + colorize_stdmsg, strlen(colmsg->reset) ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupLib ( int argc, char ** argv, ccp p_progname ) +{ + if ( p_progname && *p_progname ) + progname = p_progname; + + SetupTimezone(true); + GetTimerMSec(); + SetupColors(); + + #if defined(TEST) && !defined(__APPLE__) && !defined(__CYGWIN__) + mtrace(); + #endif + + INIT_TRACE_ALLOC; + //MALLOC(1234); + + GetErrorNameHook = LibGetErrorName; + GetErrorTextHook = LibGetErrorText; + + #ifdef DEBUG + if (!TRACE_FILE) + { + char fname[100]; + snprintf(fname,sizeof(fname),"_trace-%s.tmp",p_progname); + TRACE_FILE = fopen(fname,"w"); + if (!TRACE_FILE) + fprintf(stderr,"open TRACE_FILE failed: %s\n",fname); + } + #endif + + #ifdef __BYTE_ORDER + TRACE("__BYTE_ORDER=%d\n",__BYTE_ORDER); + #endif + #ifdef LITTLE_ENDIAN + TRACE("LITTLE_ENDIAN=%d\n",LITTLE_ENDIAN); + #endif + #ifdef BIG_ENDIAN + TRACE("BIG_ENDIAN=%d\n",BIG_ENDIAN); + #endif + + // numeric types + + TRACE("- int\n"); + TRACE_SIZEOF(bool); + TRACE_SIZEOF(short); + TRACE_SIZEOF(int); + TRACE_SIZEOF(long); + TRACE_SIZEOF(long long); + TRACE_SIZEOF(size_t); + TRACE_SIZEOF(off_t); + + #ifndef WIN_SZS_LIB + TRACE_SIZEOF(int8_t); + TRACE_SIZEOF(int16_t); + TRACE_SIZEOF(int32_t); + TRACE_SIZEOF(int64_t); + TRACE_SIZEOF(uint8_t); + TRACE_SIZEOF(uint16_t); + TRACE_SIZEOF(uint32_t); + TRACE_SIZEOF(uint64_t); + #endif + + TRACE_SIZEOF(u8); + TRACE_SIZEOF(u16); + TRACE_SIZEOF(u32); + TRACE_SIZEOF(u64); + TRACE_SIZEOF(s8); + TRACE_SIZEOF(s16); + TRACE_SIZEOF(s32); + TRACE_SIZEOF(s64); + TRACE_SIZEOF(be16_t); + TRACE_SIZEOF(be32_t); + TRACE_SIZEOF(be64_t); + + TRACE_SIZEOF(float); + TRACE_SIZEOF(float32); + TRACE_SIZEOF(float3); + TRACE_SIZEOF(float3List_t); + + TRACE_SIZEOF(double); + TRACE_SIZEOF(double3); + TRACE_SIZEOF(double3List_t); + + TRACE_SIZEOF(float34); + TRACE_SIZEOF(double34); + TRACE_SIZEOF(MatrixD_t); + + TRACE("- arch\n"); + TRACE_SIZEOF(yaz0_header_t); + TRACE_SIZEOF(wbz_header_t); + TRACE_SIZEOF(szs_subfile_t); + TRACE_SIZEOF(szs_subfile_list_t); + TRACE_SIZEOF(szs_file_t); + TRACE_SIZEOF(szs_extract_t); + TRACE_SIZEOF(szs_iterator_t); + TRACE_SIZEOF(string_pool_t); + TRACE_SIZEOF(u8_header_t); + TRACE_SIZEOF(u8_node_t); + TRACE_SIZEOF(pack_header_t); + TRACE_SIZEOF(pack_metric_t); + TRACE_SIZEOF(rarc_file_header_t); + TRACE_SIZEOF(rarc_header_t); + TRACE_SIZEOF(rarc_node_t); + TRACE_SIZEOF(rarc_entry_t); + + TRACE("- sub file system\n"); + TRACE_SIZEOF(SubDirList_t); + TRACE_SIZEOF(SubDir_t); + TRACE_SIZEOF(SubFileList_t); + TRACE_SIZEOF(SubFile_t); + + TRACE("- brres\n"); + TRACE_SIZEOF(brres_t); + TRACE_SIZEOF(brres_header_t); + TRACE_SIZEOF(brres_root_t); + TRACE_SIZEOF(brres_group_t); + TRACE_SIZEOF(brres_entry_t); + TRACE_SIZEOF(brsub_header_t); + TRACE_SIZEOF(brsub_list_t); + TRACE_SIZEOF(brsub_cut_t); + TRACE_SIZEOF(grp_entry_t); + TRACE_SIZEOF(tex_info_t); + + TRACE("- breff\n"); + TRACE_SIZEOF(breff_item_data_t); + TRACE_SIZEOF(breff_item_list_t); + TRACE_SIZEOF(breff_item_name_t); + TRACE_SIZEOF(breff_root_t); + TRACE_SIZEOF(breff_root_head_t); + TRACE_SIZEOF(breft_image_t); + + TRACE("- bmg\n"); + TRACE_SIZEOF(bmg_header_t); + TRACE_SIZEOF(bmg_section_t); + TRACE_SIZEOF(bmg_inf_t); + TRACE_SIZEOF(bmg_inf_item_t); + TRACE_SIZEOF(bmg_dat_t); + TRACE_SIZEOF(bmg_mid_t); + TRACE_SIZEOF(bmg_flw_t); + TRACE_SIZEOF(bmg_fli_t); + TRACE_SIZEOF(bmg_t); + TRACE_SIZEOF(bmg_item_t); + + TRACE("- ctcode\n"); + TRACE_SIZEOF(ctcode_header_t); + TRACE_SIZEOF(ctcode_sect_info_t); + TRACE_SIZEOF(ctcode_cup1_head_t); + TRACE_SIZEOF(ctcode_cup1_data_t); + TRACE_SIZEOF(ctcode_crs1_head_t); + TRACE_SIZEOF(ctcode_crs1_data_t); + TRACE_SIZEOF(ctcode_t); + + TRACE("- kcl\n"); + TRACE_SIZEOF(kcl_mode_t); + TRACE_SIZEOF(kcl_t); + TRACE_SIZEOF(kcl_head_t); + TRACE_SIZEOF(kcl_triangle_t); + TRACE_SIZEOF(kcl_tridata_status_t); + TRACE_SIZEOF(kcl_tridata_flt_t); + TRACE_SIZEOF(kcl_tridata_dbl_t); + TRACE_SIZEOF(kcl_tridata0_t); + TRACE_SIZEOF(kcl_tridata1_t); + TRACE_SIZEOF(kcl_tridata_t); + TRACE_SIZEOF(kcl_tri_param_t); + TRACE_SIZEOF(kcl_cube_t); + TRACE_SIZEOF(kcl_tri_t); + + TRACE("- kmp\n"); + TRACE_SIZEOF(kmp_t); + TRACE_SIZEOF(kmp_ph_t); + TRACE_SIZEOF(kmp_flag_t); + TRACE_SIZEOF(kmp_file_mkw_t); + TRACE_SIZEOF(kmp_gopt_t); + TRACE_SIZEOF(kmp_gopt2_t); + TRACE_SIZEOF(kmp_group_t); + TRACE_SIZEOF(kmp_group_list_t); + TRACE_SIZEOF(kmp_group_name_t); + TRACE_SIZEOF(kmp_rtobj_t); + TRACE_SIZEOF(kmp_rtobj_list_t); + TRACE_SIZEOF(kmp_linfo_mode); + TRACE_SIZEOF(kmp_linfo_t); + + TRACE_SIZEOF(kmp_area_entry_t); + TRACE_SIZEOF(kmp_came_entry_t); + TRACE_SIZEOF(kmp_ckph_entry_t); + TRACE_SIZEOF(kmp_ckpt_entry_t); + TRACE_SIZEOF(kmp_cnpt_entry_t); + TRACE_SIZEOF(kmp_enph_entry_t); + TRACE_SIZEOF(kmp_enpt_entry_t); + TRACE_SIZEOF(kmp_gobj_entry_t); + TRACE_SIZEOF(kmp_itph_entry_t); + TRACE_SIZEOF(kmp_itpt_entry_t); + TRACE_SIZEOF(kmp_jgpt_entry_t); + TRACE_SIZEOF(kmp_ktpt_entry_t); + TRACE_SIZEOF(kmp_mspt_entry_t); + TRACE_SIZEOF(kmp_poti_group_t); + TRACE_SIZEOF(kmp_poti_point_t); + TRACE_SIZEOF(kmp_stgi_entry_t); + + TRACE_SIZEOF(pos_param_t); + TRACE_SIZEOF(pos_file_t); + + TRACE("- mdl\n"); + TRACE_SIZEOF(mdl_t); + TRACE_SIZEOF(mdl_head_t); + TRACE_SIZEOF(mdl_sect0_t); + TRACE_SIZEOF(mdl_sect1_t); + TRACE_SIZEOF(mdl_sect2_t); + TRACE_SIZEOF(mdl_sect8_t); + TRACE_SIZEOF(mdl_sect8_layer_t); + TRACE_SIZEOF(mdl_sect10_t); + TRACE_SIZEOF(mdl_minimap_t); + + TRACE("- pat\n"); + TRACE_SIZEOF(pat_t); + TRACE_SIZEOF(pat_element_t); + TRACE_SIZEOF(pat_analyse_t); + TRACE_SIZEOF(pat_head_t); + TRACE_SIZEOF(pat_s0_bhead_t); + TRACE_SIZEOF(pat_s0_belem_t); + TRACE_SIZEOF(pat_s0_sref_t); + TRACE_SIZEOF(pat_s0_shead_t); + TRACE_SIZEOF(pat_s0_selem_t); + + TRACE("- static rel\n"); + TRACE_SIZEOF(rel_header_t); + TRACE_SIZEOF(rel_sect_info_t); + TRACE_SIZEOF(rel_imp_t); + TRACE_SIZEOF(rel_data_t); + TRACE_SIZEOF(rel_type_t); + + TRACE("- tpl, bti\n"); + TRACE_SIZEOF(tpl_header_t); + TRACE_SIZEOF(tpl_imgtab_t); + TRACE_SIZEOF(tpl_pal_header_t); + TRACE_SIZEOF(tpl_img_header_t); + TRACE_SIZEOF(bti_header_t); + + TRACE("- common.szs\n"); + TRACE_SIZEOF(geohit_bin_t); + TRACE_SIZEOF(geohit_t); + TRACE_SIZEOF(itemslot_bin_t); + TRACE_SIZEOF(itemslot_t); + TRACE_SIZEOF(minigame_kmg_head_t); + TRACE_SIZEOF(minigame_kmg_t); + + TRACE("---\n"); + TRACE_SIZEOF(Color_t); + TRACE_SIZEOF(KeywordTab_t); + TRACE_SIZEOF(Container_t); + TRACE_SIZEOF(ContainerData_t); + TRACE_SIZEOF(DataContainer_t); + TRACE_SIZEOF(File_t); + TRACE_SIZEOF(FileAttrib_t); + TRACE_SIZEOF(FormatField_t); + TRACE_SIZEOF(FormatFieldItem_t); + TRACE_SIZEOF(FuncInfo_t); + TRACE_SIZEOF(FuncParam_t); + TRACE_SIZEOF(Image_t); + TRACE_SIZEOF(ImageGeometry_t); + TRACE_SIZEOF(iobuf); + TRACE_SIZEOF(List_t); + TRACE_SIZEOF(MemItem_t); + TRACE_SIZEOF(ParamList_t); + TRACE_SIZEOF(PatchImage_t); + TRACE_SIZEOF(ScanFile_t); + TRACE_SIZEOF(ScanInfo_t); + TRACE_SIZEOF(ScanLoop_t); + TRACE_SIZEOF(ScanMacro_t); + TRACE_SIZEOF(ScanParamMode); + TRACE_SIZEOF(ScanParam_t); + TRACE_SIZEOF(SetupDef_t); + TRACE_SIZEOF(SetupParam_t); + TRACE_SIZEOF(StringField_t); + TRACE_SIZEOF(SubstString_t); + TRACE_SIZEOF(TrackInfo_t); + TRACE_SIZEOF(VarMode_t); + TRACE_SIZEOF(IntMode_t); + TRACE_SIZEOF(Var_t); + TRACE_SIZEOF(VarMap_t); + + TRACE("- DbFile\n"); + TRACE_SIZEOF(DbFile_t); + TRACE_SIZEOF(DbFile); + TRACE_SIZEOF(UsedFile_t); + TRACE_SIZEOF(DbFileSZS_t); + TRACE_SIZEOF(DbFileSZS); + TRACE_SIZEOF(DbFileRefSZS); + TRACE_SIZEOF(UsedFileSZS_t); + TRACE_SIZEOF(DbFileSHA1_t); + TRACE_SIZEOF(DbFileSHA1); + TRACE_SIZEOF(DbFileRefSHA1); + TRACE_SIZEOF(UsedFileSHA1_t); + TRACE_SIZEOF(DbFileFILE_t); + TRACE_SIZEOF(DbFileFILE); + TRACE_SIZEOF(DbFileRefFILE); + TRACE_SIZEOF(UsedFileFILE_t); + TRACE_SIZEOF(DbFileGROUP_t); + TRACE_SIZEOF(DbFileGROUP); + TRACE_SIZEOF(DbFileRefGROUP); + TRACE_SIZEOF(UsedFileGROUP_t); + + TRACE("- DbObject\n"); + TRACE_SIZEOF(ObjectInfo); + TRACE_SIZEOF(UsedObject_t); + + // assertions + + TRACE("-\n"); + DASSERT( 1 == sizeof(u8) ); + DASSERT( 2 == sizeof(u16) ); + DASSERT( 4 == sizeof(u32) ); + DASSERT( 8 == sizeof(u64) ); + DASSERT( 1 == sizeof(s8) ); + DASSERT( 2 == sizeof(s16) ); + DASSERT( 4 == sizeof(s32) ); + DASSERT( 8 == sizeof(s64) ); + DASSERT( 4 == sizeof(float32) ); + DASSERT( 12 == sizeof(float3) ); + DASSERT( 24 == sizeof(double3) ); + + DASSERT( 1 == sizeof(bmg_encoding_t) ); + DASSERT( 32 == sizeof(bmg_header_t) ); + + DASSERT( 300 == strlen(Minus300) ); + DASSERT( 4 == sizeof(Color_t) ); + DASSERT( 0x40 == sizeof(ctcode_cup1_head_t) ); + DASSERT( 0x100 == sizeof(ctcode_cup1_data_t) ); + DASSERT( 0x40 == sizeof(ctcode_crs1_head_t) ); + DASSERT( 0x100 == sizeof(ctcode_crs1_data_t) ); + + TRACE("-\n"); + + //----- some assertion + + #if HAVE_ASSERT && !defined(WIN_SZS_LIB) // ??? [[mvc]] + { + double3 d3; + ASSERT( (double*)&d3 == d3.v ); + ASSERT( d3.v+0 == &d3.x ); + ASSERT( d3.v+1 == &d3.y ); + ASSERT( d3.v+2 == &d3.z ); + + DEFINE_VAR(var); + ASSERT( &var.d == var.v ); + ASSERT( &var.d == var.d3.v ); + ASSERT( var.v+0 == &var.x ); + ASSERT( var.v+1 == &var.y ); + ASSERT( var.v+2 == &var.z ); + + ASSERT( sizeof(FuncParam_t) <= sizeof(double3) ); + ASSERT( sizeof(FuncInfo_t) <= sizeof(double3) ); + + ASSERT( sizeof(mdl_sect1_t) == 0xd0 ); + } + #endif + + //----- initialize data structures + + InitializeParamField(&patch_bmg_list); + InitializeFormatField(&patch_image_list); + stdlog = stdout; + MySeedByTime(); + + + //----- setup progname + + tool_name = p_progname; + if ( argc > 0 && *argv && **argv ) + p_progname = *argv; + progname = strrchr(p_progname,'/'); + progname = progname ? progname+1 : p_progname; + //argv[0] = (char*)progname; + if ( !tool_name || !*tool_name ) + tool_name = progname; + + TRACE("##PROG## PROG-NAME=%s\n",progname); + + + //----- setup search_path + + char path[PATH_MAX]; + ccp *sp = search_path, *sp2; + DASSERT( sizeof(search_path)/sizeof(*search_path) > 5 ); + + #ifndef WIN_SZS_LIB + // determine program path + char proc_path[30]; + snprintf(proc_path,sizeof(proc_path),"/proc/%u/exe",getpid()); + TRACE("PROC-PATH: %s\n",proc_path); + + static char share[] = "/share/szs/"; + static char local_share[] = "/usr/local/share/szs/"; + #ifndef __CYGWIN__ + share_path = local_share; + #endif + + if (readlink(proc_path,path,sizeof(path))) + { + // program path found! + TRACE("PROG-PATH: %s\n",path); + + char * file_ptr = strrchr(path,'/'); + if ( file_ptr ) + { + // seems to be a real path -> terminate string behind '/' + *++file_ptr = 0; + + #ifdef TEST + // for development: append 'share' dir + StringCopyE(file_ptr,path+sizeof(path),"share/"); + *sp = STRDUP(path); + TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp); + sp++; + *file_ptr = 0; + #endif + + *sp = STRDUP(path); + TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp); + #ifdef __CYGWIN__ + share_path = *sp; + #endif + sp++; + + if ( file_ptr-5 >= path && !memcmp(file_ptr-4,"/bin/",5) ) + { + StringCopyS(file_ptr-5,sizeof(path),share); + *sp = STRDUP(path); + TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp); + sp++; + } + } + } + + // insert 'local_share' if not already done + + for ( sp2 = search_path; sp2 < sp && strcmp(*sp2,local_share); sp2++ ) + ; + if ( sp2 == sp ) + { + *sp = STRDUP(local_share); + TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp); + sp++; + } + #endif + + // insert CWD if not already done + getcwd(path,sizeof(path)-1); + strcat(path,"/"); + for ( sp2 = search_path; sp2 < sp && strcmp(*sp2,path); sp2++ ) + ; + if ( sp2 == sp ) + { + *sp = STRDUP("./"); + TRACE("SEARCH_PATH[%zd] = %s\n",sp-search_path,*sp); + sp++; + } + + *sp = 0; + ASSERT( sp - search_path < sizeof(search_path)/sizeof(*search_path) ); + + #ifdef TEST0 + CHECK_TRACE_ALLOC; + MEM_CHECK_SETUP(search_path[1]-0x20,0x40); + #endif + + SetupStandardSZS(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void NormalizeOptions +( + uint log_level // 0: no log + // 1: print PROGRAM_NAME + // 2: + print SHARE_PATH + SEARCH_PATH + // 3: + print AUTOADD_PATH +) +{ + SetupColors(); + + + //--- diff, delta and minimze + + minimize_level = delta_count < 9 ? delta_count : 9; + if ( diff_count > 0 ) + { + minimize_level = 10; + if ( delta_count < 2 ) + delta_count = 2; + if ( brief_count < 2 ) + brief_count = 2; + print_header = false; + } + + + //--- compatibility settings + + if ( compatible < COMPAT_1_46 ) + opt_kmp_flags = false; + + if ( verbose < 0 && PATCH_FILE_MODE & PFILE_F_DEFAULT ) + PATCH_FILE_MODE = PFILE_M_SILENT; + + global_check_mode = opt_no_check + ? 0 + : GetCheckMode(false,brief_count>0,verbose<0,long_count>0); + + + //--- CT-CODE & LE-CODE + + opt_ct_mode = CTM_NINTENDO; + if (lecode_enabled) + { + ctcode_enabled = true; + opt_ct_mode = CTM_LECODE2; + } + else if (ctcode_enabled) + opt_ct_mode = CTM_CTCODE; + PRINT0("OPT: %s\n",GetCtLeInfo()); + + + //--- loglevel + + if (force_count) + force_kmp = true; + + if ( log_level > 0 ) + { + if (print_title_func) + print_title_func(stdout); + + fprintf(stdlog,"PROGRAM_NAME = %s\n",progname); + if ( compatible != COMPAT_CURRENT ) + fprintf(stdlog,"COMPATIBILITY = %s\n",PrintOptCompatible()); + + if ( log_level > 2 ) + { + fprintf(stdlog,"SHARE_PATH = %s\n",share_path); + + ccp * sp; + for ( sp = search_path; *sp; sp++ ) + fprintf(stdlog,"SEARCH_PATH[%td] = %s\n",sp-search_path,*sp); + + if ( log_level > 1 ) + { + SetupAutoAdd(); + for ( sp = auto_add_path; *sp; sp++ ) + fprintf(stdlog,"AUTOADD_PATH[%td] = %s\n",sp-auto_add_path,*sp); + } + } + fprintf(stdlog,"\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetCtLeInfo() +{ + return PrintCircBuf("ct=%d, le=%d, mode=%d[%s]", + ctcode_enabled, lecode_enabled, opt_ct_mode, + GetCtModeNameBMG(opt_ct_mode,true) ); +} + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t * CheckCommandHelper + ( int argc, char ** argv, const KeywordTab_t * key_tab ) +{ + DASSERT(argv); + DASSERT(key_tab); + TRACE("CheckCommandHelper(%d,) optind=%d\n",argc,optind); + + if ( optind >= argc ) + { + ERROR0(ERR_SYNTAX,"Missing command.\n"); + return 0; + } + + int cmd_stat; + ccp arg = argv[optind]; + const KeywordTab_t * cmd = ScanKeyword(&cmd_stat,arg,key_tab); + + if (!cmd) + { + if ( cmd_stat < 2 && toupper((int)*arg) == 'C' ) + { + if ( arg[1] == '-' ) + { + arg += 2; + cmd = ScanKeyword(&cmd_stat,arg,key_tab); + } + else + { + int cmd_stat2; + cmd = ScanKeyword(&cmd_stat2,arg+1,key_tab); + } + } + + if (!cmd) + PrintKeywordError(key_tab,arg,cmd_stat,0,0); + else if ( opt_colorize < COLMD_ON ) + { + opt_colorize = COLMD_ON; + SetupColors(); + } + } + + return cmd; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// error messages /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp LibGetErrorName ( int stat, ccp ret_not_found ) +{ + switch(stat) + { + case ERR_CACHE_USED: return "SZS CACHE USED"; + case ERR_INVALID_IFORM: return "INVALID IMAGE FORMAT"; + case ERR_INVALID_FFORM: return "INVALID FILE FORMAT"; + case ERR_BZIP2: return "BZIP2 ERROR"; + case ERR_PNG: return "PNG ERROR"; + } + return ret_not_found; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp LibGetErrorText ( int stat, ccp ret_not_found ) +{ + switch(stat) + { + case ERR_CACHE_USED: return "SZS cache used"; + case ERR_INVALID_IFORM: return "Invalid image format"; + case ERR_INVALID_FFORM: return "Invalid file format"; + case ERR_BZIP2: return "BZIP2 error"; + case ERR_PNG: return "PNG error"; + } + return ret_not_found; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// compatibility /////////////// +/////////////////////////////////////////////////////////////////////////////// + +compatible_t compatible = COMPAT_CURRENT; + +const compatible_info_t compatible_info[COMPAT__N] = +{ + { COMPAT_0, 2385, "0.01" }, + { COMPAT_1_23, 4682, "1.23" }, + { COMPAT_1_39, 6123, "1.39" }, + { COMPAT_1_44, 6558, "1.44" }, + { COMPAT_1_46, 6614, "1.46" }, + { COMPAT_2_08, 7961, "2.08" }, + { COMPAT_CURRENT, REVISION_NUM, VERSION_NUM } +}; + +/////////////////////////////////////////////////////////////////////////////// + +const compatible_info_t * ScanCompatible ( ccp arg ) +{ + if ( !arg || !*arg ) + return compatible_info + COMPAT_CURRENT; + + if ( *arg == 'r' || *arg == 'R' ) + { + char *end; + const uint rev = strtoul(arg+1,&end,10); + if ( end == arg || *end ) + return 0; + + compatible_t c = COMPAT_CURRENT; + while ( c > COMPAT_0 && rev < compatible_info[c].revision ) + c--; + return compatible_info + c; + } + + if ( *arg == 'v' || *arg == 'V' ) + arg++; + + if ( *arg >= '0' && *arg <= '9' ) + { + compatible_t c = COMPAT_CURRENT; + while ( c > COMPAT_0 && strcmp(arg,compatible_info[c].version) < 0 ) + c--; + return compatible_info + c; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCompatible ( ccp arg ) +{ + const compatible_info_t *ci = ScanCompatible(arg); + if (!ci) + { + ERROR0(ERR_SYNTAX, + "Wrong argument for option --compatible: %s\n",arg); + return 1; + } + + compatible = ci->compatible; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintOptCompatible() +{ + uint buf_size = 20; + char *buf = GetCircBuf(buf_size); + + const compatible_info_t *ci = compatible_info + + ( (uint)compatible < COMPAT__N ? compatible : COMPAT_CURRENT ); + snprintf( buf, buf_size, "v%s, r%u", ci->version, ci->revision ); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// output_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +output_mode_t output_mode = {0}; + +void InitializeOutputMode ( output_mode_t * outmode ) +{ + DASSERT(outmode); + outmode->syntax = true; + outmode->mode = 1; + outmode->cross_ref = true; + outmode->hex = false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// [[obsolete+]] old_container /////////////// +/////////////////////////////////////////////////////////////////////////////// +// old [[container]] will be replaced by Container_t & ContainerData_t +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * UseSubContainer +( + DataContainer_t *dc, // old_container to reuse, if + // InDataContainer(dc,data) && ref:count == 1 + ContainerData_t *cdata_src // NULL or container data source +) +{ + if ( dc && dc->container.cdata == cdata_src ) + return dc; + + if (dc) + FreeContainer(dc); + + dc = MALLOC(sizeof(*dc)); + noPRINT("CONTAINER: NEW(%p), dc_alloced=%d\n",dc,dc_alloced); + DASSERT(dc); + memset(dc,0,sizeof(*dc)); + + if (cdata_src) + cdata_src->ref_count++; + CatchContainerData(&dc->container,false,cdata_src); + ContainerData_t *cdata = dc->container.cdata; + if (cdata) + { + dc->data = cdata->data; + dc->size = cdata->size; + } + dc->ref_count = 1; + + return dc; +} + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * NewContainer +( + DataContainer_t *dc, // old_container to reuse, + // if InDataContainer(dc,data) + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +) +{ + if ( InDataContainer(dc,data) && dc->ref_count == 1 ) + return dc; + + if (dc) + FreeContainer(dc); + + dc = MALLOC(sizeof(*dc)); + noPRINT("CONTAINER: NEW(%p), dc_alloced=%d\n",dc,dc_alloced); + DASSERT(dc); + memset(dc,0,sizeof(*dc)); + + CreateContainer(&dc->container,false,data,size,mode); + ContainerData_t *cdata = dc->container.cdata; + if (cdata) + { + dc->data = cdata->data; + dc->size = cdata->size; + } + dc->ref_count = 1; + return dc; +} + +/////////////////////////////////////////////////////////////////////////////// +// old [[container]] will be replaced by Container_t & ContainerData_t + +DataContainer_t * DupContainer +( + DataContainer_t *dup_dc, // old_container to reuse, if + // InDataContainer(dc,data) && ref:count == 1 + // => complete dc->data is copied + const void *data, // data to copy/move/link + uint size, // size of 'data' + bool include_behind, // on creation: include 'dc->data' + // behind 'data+size' if possible + const void **new_data // not NULL: store new data pointer here +) +{ + if (InDataContainer(dup_dc,data)) + { + if ( dup_dc->ref_count == 1 ) + { + if (new_data) + *new_data = data; + return dup_dc; + } + + if (include_behind) + { + const uint max = dup_dc->data + dup_dc->size - (u8*)data; + if ( size < max ) + size = max; + } + } + + DataContainer_t *dc = MALLOC(sizeof(*dc)); + noPRINT("CONTAINER: NEW(%p), dc_alloced=%d\n",dc,dc_alloced); + DASSERT(dc); + memset(dc,0,sizeof(*dc)); + + CreateContainer(&dc->container,false,data,size,CPM_COPY); + ContainerData_t *cdata = dc->container.cdata; + if (cdata) + { + dc->data = cdata->data; + dc->size = cdata->size; + } + dc->ref_count = 1; + + if (dup_dc) + FreeContainer(dup_dc); + + if (new_data) + *new_data = dc->data; + + return dc; +}; + +/////////////////////////////////////////////////////////////////////////////// +// old [[container]] will be replaced by Container_t & ContainerData_t + +DataContainer_t * AllocContainer +( + // increments the reference counter and returns 'dc' + + DataContainer_t *dc // NULL or valid old_container +) +{ + if (dc) + { + dc->ref_count++; + noPRINT("CONTAINER: ALLOC(%p), ref_count=%d\n",dc,dc->ref_count); + } + return dc; +} + +/////////////////////////////////////////////////////////////////////////////// +// old [[container]] will be replaced by Container_t & ContainerData_t + +DataContainer_t * FreeContainer +( + // decrements the reference counter and returns 'NULL' + // if reference counter == 0: free data and/or dc + + DataContainer_t *dc // NULL or valid old_container +) +{ + if (dc) + { + noPRINT("CONTAINER: FREE(%p) ref_count=%d\n", dc, dc->ref_count ); + DASSERT( dc->ref_count > 0 ); + + if (!--dc->ref_count) + { + ResetContainer(&dc->container); + + if (dc->data_alloced) + FREE((void*)dc->data); + + if (dc->dc_alloced) + FREE(dc); + else + { + dc->data = (void*)EmptyString; + dc->size = 0; + dc->data_alloced = false; + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// old [[container]] will be replaced by Container_t & ContainerData_t + +bool InDataContainer +( + // return true, if ptr is in old_container + + const DataContainer_t *dc, // NULL or valid old_container + const void *ptr // NULL or pointer to test +) +{ + return dc && ptr && (u8*)ptr >= dc->data && (u8*)ptr <= dc->data + dc->size; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int NintendoCmp ( ccp path1, ccp path2 ) +{ + noPRINT("NintendoCmp( | %s | %s | )\n",path1,path2); + + // try to sort in a nintendo like way + // 1.) ignoring case but sort carefully directories + // 2.) files before sub directories + + static uchar transform[0x100] = {0,0}; + if (!transform[1]) // ==> setup needed once! + { + // define some special characters + + uint index = 1; + transform[(u8)'/'] = index++; + transform[(u8)'.'] = index++; + + // define all characters until and excluding 'A' + + uint i; + for ( i = 1; i < 'A'; i++ ) + if (!transform[i]) + transform[i] = index++; + + // define letters + + for ( i = 'A'; i <= 'Z'; i++ ) + transform[i] = transform[i-'A'+'a'] = index++; + + // define all other + + for ( i = 1; i < sizeof(transform); i++ ) + if (!transform[i]) + transform[i] = index++; + + DASSERT( index <= sizeof(transform) ); + #if defined(TEST) && defined(DEBUG) + //HexDump16(stderr,0,0,transform,sizeof(transform)); + #endif + } + + + //--- eliminate equal characters + + while ( *path1 == *path2 ) + { + if (!*path1) + return 0; + path1++; + path2++; + } + + //--- start the case+path test + + const uchar * p1 = (const uchar *)path1; + const uchar * p2 = (const uchar *)path2; + while ( *p1 || *p2 ) + { + int ch1 = transform[*p1++]; + int ch2 = transform[*p2++]; + int stat = ch1 - ch2; + noPRINT("%02x,%02x,%c %02x,%02x,%c -> %2d\n", + p1[-1], ch1, ch1, p2[-1], ch2, ch2, stat ); + if (stat) + { + #ifdef WSZST // special case for Wiimms SZS Tools + // sort files before dirs + const bool indir1 = strchr((ccp)p1-1,'/') != 0; + const bool indir2 = strchr((ccp)p2-1,'/') != 0; + return indir1 != indir2 ? indir1 - indir2 : stat; + #else + return stat; + #endif + } + + if ( ch1 == 1 ) + break; + } + return *path1 - *path2; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintNumF +( + FILE *f, // output stream + const u8 *data, // valid pointer to data + uint size, // size of 'data' + ccp type_str // NULL or type string +) +{ + DASSERT(f); + DASSERT(data); + + const u8 *end = data + size; + while ( type_str && data < end ) + { + switch( type_str[0] << 8 | type_str[1] ) + { + case 'u' << 8 | '1': + fprintf(f," %4u",*data++); + type_str += 2; + break; + + case 'u' << 8 | '2': + fprintf(f," %4u",be16(data)); + data += 2; + type_str += 2; + break; + + case 'x' << 8 | '1': + fprintf(f," 0x%02x",*data++); + type_str += 2; + break; + + case 'x' << 8 | '2': + fprintf(f," 0x%02x",be16(data)); + data += 2; + type_str += 2; + break; + + case 'f' << 8 | '4': + fprintf(f," %6.3f",bef4(data)); + data += 4; + type_str += 2; + break; + + default: + type_str = 0; + break; + } + } + + while ( data < end ) + fprintf(f," 0x%02x",*data++); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// word compare functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint CreateHaystackListWCMP +( + char * buf, // valid pointer to output buffer + uint buf_size, // soue of 'buf' + ccp * source // NULL terminated list with source Strings +) +{ + DASSERT(buf); + DASSERT( buf_size > 10 ); + DASSERT(source); + + char *dest = buf; + char * dest_end = buf + buf_size - 4; + + uint src_count = 0; + while (*source) + { + ccp src = *source++; + if (src_count++) + *dest++ = '|'; + + *dest++ = ' '; + bool have_space = true; + while ( dest < dest_end ) + { + const uchar ch = *src++; + if ( ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' || ch == '$' ) + { + *dest++ = ch; + have_space = false; + } + else if ( ch >= 'A' && ch <= 'Z' ) + { + *dest++ = ch + ('a'-'A'); + have_space = false; + } + else if (!ch) + { + break; + } + else if (!have_space) + { + *dest++ = ' '; + have_space = true; + } + } + if (!have_space) + *dest++ = ' '; + } + + DASSERT( dest < buf + buf_size ); + *dest = 0; + return dest - buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CreateHaystackWCMP +( + char * buf, // valid pointer to output buffer + uint buf_size, // soue of 'buf' + ccp source // source String +) +{ + ccp list[2] = { source, 0 }; + return CreateHaystackListWCMP(buf,buf_size,list); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CreateNeedleWCMP +( + char * buf, // valid pointer to output buffer + uint buf_size, // soue of 'buf' + ccp source // NULL terminated list with source Strings +) +{ + DASSERT(buf); + DASSERT( buf_size > 10 ); + DASSERT(source); + + char *dest = buf; + char * dest_end = buf + buf_size - 4; + + if ( *source == '*' ) + source++; + else + *dest++ = ' '; + + bool have_space = true; + while ( dest < dest_end ) + { + const uchar ch = *source++; + if ( ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9' || ch == '$' ) + { + *dest++ = ch; + have_space = false; + } + else if ( ch >= 'A' && ch <= 'Z' ) + { + *dest++ = ch + ('a'-'A'); + have_space = false; + } + else if (!ch) + { + break; + } + else if (!have_space) + { + *dest++ = ' '; + have_space = true; + } + } + + DASSERT( dest < buf + buf_size ); + *dest = 0; + return dest - buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FindWCMP +( + ccp haystack, // search in this string + ccp needle, // try to find this string + uint flags, // bit field: + // bit-0 set = 1: 'haystack' already in WCMP format + // bit-1 set = 2: 'needle' already in WCMP format + char * tempbuf, // NULL or fast buffer for temporary WCMP conversions + uint tempbuf_size // size of 'buf' +) +{ + if ( !haystack || !*haystack ) + return false; + if ( !needle || !*needle ) + return true; + + const uint extra_size = 3; + + char *alloced_haystack = 0; + if (!(flags&1)) + { + ccp haystack_list[2] = { haystack, 0 }; + + uint len = strlen(haystack) + extra_size; + if ( tempbuf && len <= tempbuf_size ) + { + len = CreateHaystackListWCMP(tempbuf,tempbuf_size,haystack_list); + haystack = tempbuf; + + // adjust temp buf for next access + tempbuf += len+1; + tempbuf_size -= len+1; + } + else + { + alloced_haystack = MALLOC(len); + CreateHaystackListWCMP(alloced_haystack,len,haystack_list); + needle = alloced_haystack; + } + } + + char *alloced_needle = 0; + if (!(flags&2)) + { + uint len = strlen(needle) + extra_size; + if ( tempbuf && len <= tempbuf_size ) + { + len = CreateNeedleWCMP(tempbuf,tempbuf_size,needle); + needle = tempbuf; + + // adjust of temp buf not needed + //tempbuf += len+1; + //tempbuf_size -= len+1; + } + else + { + alloced_needle = MALLOC(len); + CreateNeedleWCMP(alloced_needle,len,needle); + needle = alloced_needle; + } + } + + const bool stat = strstr(haystack,needle) != 0; + + if (alloced_haystack) + FREE(alloced_haystack); + if (alloced_needle) + FREE(alloced_needle); + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +search_filter_t * AllocSearchFilter ( uint n ) +{ + search_filter_t *f = CALLOC(n+1,sizeof(search_filter_t)); + f->mode = n; + return f+1; +} + +void FreeSearchFilter ( search_filter_t *f ) +{ + if (f) + { + int i, n = f[-1].mode; + for ( i = 0; i < n; i++ ) + FreeString(f[i].string); + FREE(f-1); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string substitutions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanEscapeChar ( ccp arg ) +{ + if ( arg && strlen(arg) > 1 ) + { + ERROR0(ERR_SYNTAX,"Invalid character (option --esc): '%s'\n",arg); + return -1; + } + + escape_char = arg ? *arg : 0; + return (unsigned char)escape_char; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SubstString +( + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + SubstString_t * tab, // replacement table + ccp source, // source text + int * count, // not NULL: number of replacements + char escape_char // the escape character, usually '%' +) +{ + ASSERT(buf); + ASSERT(bufsize > 1); + ASSERT(tab); + TRACE("SubstString(%s)\n",source); + + char tempbuf[PATH_MAX]; + int conv_count = 0; + + char *dest = buf; + char *end = buf + bufsize + 1; + if (source) + while ( dest < end && *source ) + { + if ( *source != escape_char && *source != 1 ) + { + *dest++ = *source++; + continue; + } + if ( source[0] == source[1] ) + { + source++; + *dest++ = *source++; + continue; + } + ccp start = source++; + + bool check_prev = *source == '?'; + if (check_prev) + source++; + + u32 p1, p2, stat; + source = ScanRangeU32(source,&stat,&p1,&p2,0,~(u32)0); + if ( stat == 1 ) + p1 = 0; + else if ( stat < 1 ) + { + p1 = 0; + p2 = ~(u32)0; + } + + char ch = *source++; + int convert = 0; + if ( ch == 'u' || ch == 'U' ) + { + convert++; + ch = *source++; + } + else if ( ch == 'l' || ch == 'L' ) + { + convert--; + ch = *source++; + } + if (!ch) + break; + + size_t count = source - start; + + SubstString_t * ptr; + for ( ptr = tab; ptr->c1; ptr++ ) + if ( ch == ptr->c1 || ch == ptr->c2 ) + { + if (ptr->str) + { + const size_t slen = strlen(ptr->str); + if ( p1 > slen ) + p1 = slen; + if ( p2 > slen ) + p2 = slen; + count = p2 - p1; + start = ptr->str+p1; + conv_count++; + } + else + count = 0; + break; + } + + if (!ptr->c1) // invalid conversion + convert = 0; + + if ( count > sizeof(tempbuf)-1 ) + count = sizeof(tempbuf)-1; + TRACE("COPY '%.*s' conv=%d\n",(int)count,start,convert); + if ( convert > 0 ) + { + char * tp = tempbuf; + size_t copy_count = count; + while ( copy_count-- > 0 ) + *tp++ = toupper((int)*start++); + *tp = 0; + } + else if ( convert < 0 ) + { + char * tp = tempbuf; + size_t copy_count = count; + while ( copy_count-- > 0 ) + *tp++ = tolower((int)*start++); // cygwin needs the '(int)' + *tp = 0; + } + else + { + memcpy(tempbuf,start,count); + tempbuf[count] = 0; + } + char * new_dest + = NormalizeFileName(dest,end-dest,tempbuf,ptr->allow_slash,use_utf8); + if ( check_prev && *dest == '.' ) + { + int len = new_dest - dest; + if ( dest - buf > len && !memcmp(dest-len,dest,len) ) + new_dest = dest; + } + dest = new_dest; + } + + if (count) + *count = conv_count; + *dest = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +int SubstDest +( + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + ccp source, // source text + ccp dest, // destination path + ccp dest_fname, // if 'dest' has no file part + // then extract filename from here or source + ccp default_ext, // NULL or default extension (e.g. '.ext') + bool dest_is_dir // destination is a directory +) +{ + DASSERT(buf); + DASSERT(source); + + if ( !dest || !*dest ) + { + StringCopyS(buf,bufsize,source); + return 0; + } + + if ( source && !strcmp(source,"-") && ( !opt_dest || !strcmp(opt_dest,dest) )) + { + StringCopyS(buf,bufsize,"-"); + return 0; + } + + + //---- check source path + + char src_path[PATH_MAX]; + StringCopyS(src_path,sizeof(src_path),source); + + char * p = strrchr(src_path,'/'); + ccp dir, name, fname; + if (p) + { + dir = src_path; + *p++ = 0; + name = p; + fname = source + ( name - src_path ); + } + else + { + dir = "."; + name = src_path; + fname = source; + } + + p = strrchr(name,'.'); + ccp ext; + if (p) + { + *p = 0; + ext = source + ( p - src_path ); + } + else + ext = ""; + + SubstString_t subst_tab[] = + { + { 'q', 'Q', 1, source }, + { 'p', 'P', 1, dir }, + { 'f', 'F', 1, fname }, + { 'n', 'N', 1, name }, + { 'e', 'E', 1, ext }, + { 't', 'T', 1, default_ext ? default_ext : ext }, + {0,0,0,0} + }; + + #if HAVE_PRINT0 + { + const SubstString_t * ptr; + for ( ptr = subst_tab; ptr->c1; ptr++ ) + PRINT(" %c %c %u -> %s\n", + ptr->c1, ptr->c2, ptr->allow_slash, ptr->str ); + } + #endif + + char dest_path[PATH_MAX]; + if (dest_is_dir) + dest = PathCatPP(dest_path,sizeof(dest_path),dest,"/"); + else if (IsDirectory(dest,0)) + { + if (!dest_fname) + dest_fname = source; + ccp p = strrchr(dest_fname,'/'); + dest = PathCatPP(dest_path,sizeof(dest_path), dest, p ? p+1 : dest_fname ); + } + + int conv_count; + SubstString(buf,bufsize,subst_tab,dest,&conv_count,escape_char); + if ( opt_number && !opt_overwrite ) + NumberedFilename(buf,bufsize,buf,default_ext,0,true); + + return conv_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// FormatField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeFormatField ( FormatField_t * ff ) +{ + DASSERT(ff); + memset(ff,0,sizeof(*ff)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetFormatField ( FormatField_t * ff ) +{ + ASSERT(ff); + if ( ff && ff->used > 0 ) + { + ASSERT(ff->list); + FormatFieldItem_t *ptr = ff->list, *end; + for ( end = ptr + ff->used; ptr < end; ptr++ ) + FreeString(ptr->key); + FREE(ff->list); + } + InitializeFormatField(ff); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveFormatField ( FormatField_t * dest, FormatField_t * src ) +{ + DASSERT(src); + DASSERT(dest); + if ( src != dest ) + { + ResetFormatField(dest); + dest->list = src->list; + dest->used = src->used; + dest->size = src->size; + InitializeFormatField(src); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static uint FindFormatFieldHelper ( const FormatField_t * ff, bool * p_found, ccp key ) +{ + ASSERT(ff); + + int beg = 0; + if ( ff && key ) + { + int end = ff->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + int stat = strcmp(key,ff->list[idx].key); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + { + TRACE("FindFormatFieldHelper(%s) FOUND=%d/%d/%d\n", + key, idx, ff->used, ff->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + TRACE("FindFormatFieldHelper(%s) failed=%d/%d/%d\n", + key, beg, ff->used, ff->size ); + + if (p_found) + *p_found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindFormatFieldIndex ( const FormatField_t * ff, ccp key, int not_found_value ) +{ + bool found; + const int idx = FindFormatFieldHelper(ff,&found,key); + return found ? idx : not_found_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +FormatFieldItem_t * FindFormatField ( const FormatField_t * ff, ccp key ) +{ + bool found; + const int idx = FindFormatFieldHelper(ff,&found,key); + return found ? ff->list + idx : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +FormatFieldItem_t * FindFormatFieldNC ( const FormatField_t * ff, ccp key ) +{ + DASSERT(ff); + DASSERT(key); + + FormatFieldItem_t *ptr = ff->list, *end; + for ( end = ptr + ff->used; ptr < end; ptr++ ) + if (!strcasecmp(ptr->key,key)) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +FormatFieldItem_t * MatchFormatField ( const FormatField_t * ff, ccp key ) +{ + DASSERT(ff); + DASSERT(key); + + FormatFieldItem_t *ptr = ff->list, *end; + for ( end = ptr + ff->used; ptr < end; ptr++ ) + if (MatchPattern(ptr->key,key,'/')) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static FormatFieldItem_t * InsertFormatFieldHelper ( FormatField_t * ff, int idx ) +{ + DASSERT(ff); + DASSERT( ff->used <= ff->size ); + noPRINT("+FF: %u/%u/%u\n",idx,ff->used,ff->size); + if ( ff->used == ff->size ) + { + ff->size += 0x100; + ff->list = REALLOC(ff->list,ff->size*sizeof(*ff->list)); + } + DASSERT( idx <= ff->used ); + FormatFieldItem_t * dest = ff->list + idx; + memmove(dest+1,dest,(ff->used-idx)*sizeof(*dest)); + ff->used++; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveFormatField ( FormatField_t * ff, ccp key ) +{ + bool found; + uint idx = FindFormatFieldHelper(ff,&found,key); + if (found) + { + ff->used--; + ASSERT( idx <= ff->used ); + FormatFieldItem_t * dest = ff->list + idx; + FREE((char*)dest); + memmove(dest,dest+1,(ff->used-idx)*sizeof(*dest)); + } + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SetFormatFieldParam +( + FormatFieldItem_t * item, // valid attrib item + ccp key, // comma list of keys + ccp keyend // end of key +) +{ + DASSERT(item); + DASSERT(key); + DASSERT(keyend); + + bool first = true; + for(;;) + { + while ( key < keyend && ( *key >= 0 && *key <= ' ' || *key == ',' )) + key++; + char buf[100]; + char *bufptr = buf, *bufend = buf + sizeof(buf) - 1; + while ( key < keyend && *key > ' ' && *key != ',' ) + { + if ( bufptr < bufend ) + *bufptr++ = toupper((int)*key); + key++; + } + + if ( bufptr == buf ) + break; + *bufptr = 0; + noPRINT("TEST: |%s| %zu\n",buf,bufptr-buf); + + if ( *buf >= '0' && *buf <= '9' ) + { + char *end; + const uint num = strtoul(buf,&end, buf[0]=='0' && buf[1]=='x' ? 16 : 10 ); + if ( end > buf && !*end ) + { + item->num = num; + continue; + } + } + + if (first) + { + first = false; + const file_format_t fform = GetByNameFF(buf); + if ( fform != FF_UNKNOWN ) + { + item->fform = fform; + continue; + } + } + + int iform = ScanImageFormat(buf); + if ( iform >= 0 ) + { + item->iform = iform; + continue; + } + + int pform = ScanPaletteFormat(buf); + if ( pform >= 0 ) + { + item->pform = pform; + continue; + } + + if (ScanPatchImage(buf,&item->patch_mode)) + continue; + } + + noPRINT("SET FORMAT: %d %d %d = %s\n", + item->fform, item->iform, item->pform, item->key ); +} + +/////////////////////////////////////////////////////////////////////////////// + +FormatFieldItem_t * InsertFormatField +( + FormatField_t * ff, // valid attrib field + ccp key, // key to insert + bool scan_form, // true: scan 'key' for FORM= prefix + bool move_key, // true: move string, false: strdup() + bool * found // not null: store found status +) +{ + if (!key) + return 0; + + if (scan_form) + { + ccp eq = strchr(key,'='); + if (eq) + { + ccp slash = strchr(key,'/'); + if ( !slash || slash > eq ) + { + ccp new_key = eq + 1; + while ( *new_key > 0 && *new_key <= ' ' ) + new_key++; + noPRINT("INSERT: %s\n",new_key); + FormatFieldItem_t * item = InsertFormatField(ff,new_key,false,false,found); + SetFormatFieldParam(item,key,eq); + if (move_key) + FreeString(key); + return item; + } + } + } + + bool my_found; + const int idx = FindFormatFieldHelper(ff,&my_found,key); + if (found) + *found = my_found; + + FormatFieldItem_t * item; + if (my_found) + { + item = ff->list + idx; + if (move_key) + FreeString(key); + } + else + { + item = InsertFormatFieldHelper(ff,idx); + item->key = move_key ? key : STRDUP(key); + } + + item->fform = -1; + item->iform = -1; + item->pform = -1; + item->num = 0; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +FormatFieldItem_t * InsertFormatFieldFF +( + FormatField_t * ff, // valid attrib field + ccp key, // key to insert + file_format_t fform, // file format + bool scan_form, // true: scan 'key' for FORM= prefix + bool move_key, // true: move string, false: strdup() + bool * found // not null: store found status +) +{ + FormatFieldItem_t *item = InsertFormatField(ff,key,scan_form,move_key,found); + DASSERT(item); + item->fform = fform; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +FormatFieldItem_t * AppendFormatField +( + FormatField_t * ff, // valid attrib field + ccp key, // key to insert + bool scan_form, // true: scan 'key' for FORM= prefix + bool move_key // true: move string, false: strdup() +) +{ + if (!key) + return 0; + + if (scan_form) + { + ccp eq = strchr(key,'='); + if (eq) + { + ccp slash = strchr(key,'/'); + if ( !slash || slash > eq ) + { + ccp new_key = eq + 1; + while ( *new_key > 0 && *new_key <= ' ' ) + new_key++; + noPRINT("INSERT: %s\n",new_key); + FormatFieldItem_t * item = AppendFormatField(ff,new_key,false,false); + SetFormatFieldParam(item,key,eq); + if (move_key) + FreeString(key); + return item; + } + } + } + + DASSERT( ff->used <= ff->size ); + noPRINT(">FF: %u/%u\n",ff->used,ff->size); + if ( ff->used == ff->size ) + { + ff->size += 0x100; + ff->list = REALLOC(ff->list,ff->size*sizeof(*ff->list)); + } + TRACE("AppendFormatField(%s,%d) %d/%d\n",key,move_key,ff->used,ff->size); + + FormatFieldItem_t * item = ff->list + ff->used++; + item->key = move_key ? key : STRDUP(key); + item->fform = -1; + item->iform = -1; + item->pform = -1; + item->num = 0; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// +#if 0 // [[not-needed]] not needed yet 2011-06 +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadFormatField +( + FormatField_t * ff, // attrib field + bool init_ff, // true: initialize 'ff' first + bool sort, // true: sort 'ff' + ccp filename, // filename of source file + bool silent // true: don't print open/read errors +) +{ + ASSERT(ff); + ASSERT(filename); + ASSERT(*filename); + + TRACE("LoadFormatField(%p,%d,%d,%s,%d)\n",ff,init_att,sort,filename,silent); + + if (init_att) + InitializeFormatField(ff); + + FILE * f = fopen(filename,"rb"); + if (!f) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",filename); + return ERR_CANT_OPEN; + } + + while (fgets(iobuf,sizeof(iobuf)-1,f)) + { + char * ptr = iobuf; + while (*ptr) + ptr++; + if ( ptr > iobuf && ptr[-1] == '\n' ) + { + ptr--; + if ( ptr > iobuf && ptr[-1] == '\r' ) + ptr--; + } + + if ( ptr > iobuf ) + { + *ptr++ = 0; + const size_t len = ptr-iobuf; + ptr = MALLOC(len); + memcpy(ptr,iobuf,len); + if (sort) + InsertFormatField(ff,ptr,true); + else + AppendFormatField(ff,ptr,true); + } + } + + fclose(f); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveFormatField +( + FormatField_t * ff, // valid attrib field + ccp filename, // filename of dest file + bool rm_if_empty // true: rm dest file if 'ff' is empty +) +{ + ASSERT(ff); + ASSERT(filename); + ASSERT(*filename); + + TRACE("SaveFormatField(%p,%s,%d)\n",ff,filename,rm_if_empty); + + if ( !ff->used && rm_if_empty ) + { + unlink(filename); + return ERR_OK; + } + + FILE * f = fopen(filename,"wb"); + if (!f) + return ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",filename); + + enumError err = WriteFormatField(f,filename,ff,0,0,0,0); + fclose(f); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError WriteFormatField +( + FILE * f, // open destination file + ccp filename, // NULL or filename (needed on write error) + FormatField_t * ff, // valid format field + ccp line_prefix, // not NULL: insert prefix before each line + ccp key_prefix, // not NULL: insert prefix before each key + ccp eol, // end of line text (if NULL: use LF) + uint hex_fw // if >0: print num as hex with field width +) +{ + if (!key_prefix) + key_prefix = ""; + if (!eol) + eol = "\n"; + + FormatFieldItem_t *ptr = ff->list, *end; + for ( end = ptr + ff->used; ptr < end; ptr++ ) + { + if ( line_prefix && fprintf(f,"%s",line_prefix) < 0 ) + goto abort; + + int n = 0; + ccp sep = ""; + if ( ptr->fform >= 0 ) + { + const int stat = fprintf(f,"%s%s",sep,GetNameFF(0,ptr->fform)); + if ( stat < 0 ) + goto abort; + n += stat; + sep = ","; + } + + ccp iname = GetImageFormatName(ptr->iform,0); + if (iname) + { + const int stat = fprintf(f,"%s%s",sep,iname); + if ( stat < 0 ) + goto abort; + n += stat; + sep = ","; + } + + ccp pname = GetPaletteFormatName(ptr->pform,0); + if (pname) + { + const int stat = fprintf(f,"%s%s",sep,pname); + if ( stat < 0 ) + goto abort; + n += stat; + sep = ","; + } + + if (ptr->num) + { + const int stat = hex_fw + ? fprintf(f,"%s%#*x",sep,hex_fw,ptr->num) + : fprintf(f,"%s%u",sep,ptr->num); + if ( stat < 0 ) + goto abort; + n += stat; + //sep = ","; + } + + if ( fprintf(f,"%s= %s%s%s", n<8 ? "\t\t" : "\t", key_prefix,ptr->key,eol) < 0 ) + goto abort; + } + return ERR_OK; + + abort: + return ERROR1(ERR_WRITE_FAILED, + "Error while writing attrib field: %s\n", + filename ? filename : "?" ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string lists /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int AtFileHelper +( + ccp arg, + int (*func) ( ccp arg ) +) +{ + if ( !arg || !*arg || !func ) + return 0; + + TRACE("AtFileHelper(%s)\n",arg); + if ( *arg != '@' ) + return func(arg); + + FILE * f; + char buf[PATH_MAX]; + const bool use_stdin = arg[1] == '-' && !arg[2]; + + if (use_stdin) + f = stdin; + else + { + #ifdef __CYGWIN__ + char buf[PATH_MAX]; + NormalizeFilenameCygwin(buf,sizeof(buf),arg+1); + f = fopen(buf,"r"); + #else + f = fopen(arg+1,"r"); + #endif + if (!f) + return func(arg); + } + + ASSERT(f); + + u32 max_stat = 0; + while (fgets(buf,sizeof(buf)-1,f)) + { + char * ptr = buf; + while (*ptr) + ptr++; + if ( ptr > buf && ptr[-1] == '\n' ) + ptr--; + if ( ptr > buf && ptr[-1] == '\r' ) + ptr--; + *ptr = 0; + const u32 stat = func(buf); + if ( max_stat < stat ) + max_stat = stat; + } + fclose(f); + return max_stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint n_param = 0, id6_param_found = 0; +ParamList_t * first_param = 0; +ParamList_t ** append_param = &first_param; + +/////////////////////////////////////////////////////////////////////////////// + +static ParamList_t* GetPoolParam() +{ + static ParamList_t * pool = 0; + static int n_pool = 0; + + if (!n_pool) + { + const int alloc_count = 100; + pool = (ParamList_t*) CALLOC(alloc_count,sizeof(ParamList_t)); + n_pool = alloc_count; + } + + n_pool--; + return pool++; +} + +/////////////////////////////////////////////////////////////////////////////// + +int AddParam ( ccp arg ) +{ + if ( !arg || !*arg ) + return 0; + + TRACE("ARG#%02d: %s\n",n_param,arg); + + ParamList_t *param = GetPoolParam(); + param->arg = STRDUP(arg); + + while (*append_param) + append_param = &(*append_param)->next; + + noTRACE("INS: A=%p->%p P=%p &N=%p->%p\n", + append_param, *append_param, + param, ¶m->next, param->next ); + *append_param = param; + append_param = ¶m->next; + noTRACE(" => A=%p->%p\n", append_param, *append_param ); + n_param++; + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AtExpandParam ( ParamList_t ** p_param ) +{ + if ( !p_param || !*p_param ) + return; + + ParamList_t *param = *p_param; + if ( param->is_expanded || !param->arg || *param->arg != '@' ) + return; + + FILE * f; + char buf[PATH_MAX]; + const bool use_stdin = param->arg[1] == '-' && !param->arg[2]; + if (use_stdin) + f = stdin; + else + { + #ifdef __CYGWIN__ + NormalizeFilenameCygwin(buf,sizeof(buf),param->arg+1); + f = fopen(buf,"r"); + #else + f = fopen(param->arg+1,"r"); + #endif + if (!f) + return; + } + + ASSERT(f); + + u32 count = 0; + while (fgets(buf,sizeof(buf)-1,f)) + { + char * ptr = buf; + while (*ptr) + ptr++; + if ( ptr > buf && ptr[-1] == '\n' ) + ptr--; + if ( ptr > buf && ptr[-1] == '\r' ) + ptr--; + *ptr = 0; + + if (count++) + { + // insert a new item + ParamList_t * new_param = GetPoolParam(); + new_param->next = param->next; + param->next = new_param; + param = new_param; + n_param++; + } + param->arg = STRDUP(buf); + param->is_expanded = true; + } + fclose(f); + + if (!count) + { + *p_param = param->next; + n_param--; + } + + append_param = &first_param; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AtExpandAllParam ( ParamList_t ** p_param ) +{ + if (p_param) + for ( ; *p_param; p_param = &(*p_param)->next ) + AtExpandParam(p_param); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// tiny support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[TinyParam]] + +const tiny_param_t TinyParam[N_TINY_MODES] = +{ + // TINY 0 == OFF + { + 0, // kcl_round + 0, // kcl_mask + + 999, // pos_round + 999, // rot_round + 999, // scale_round + + 999, // route_round + 999, // cannon_round + }, + + // TINY 1 == LOSSLESS + { + 8, // kcl_round + 0xfffffff0, // kcl_mask + + 999, // pos_round + 999, // rot_round + 999, // scale_round + + 999, // route_round + 999, // cannon_round + }, + + // TINY 2 == RECREATE + { + 8, // kcl_round + 0xffffffc0, // kcl_mask + + 999, // pos_round + 999, // rot_round + 999, // scale_round + + 999, // route_round + 999, // cannon_round + }, + + // TINY 3 == MINIMAL + { + 7, // kcl_round + 0xffffff00, // kcl_mask + + 6, // pos_round + 3, // rot_round + 7, // scale_round + + 0, // route_round + 12, // cannon_round + }, + + // TINY 4 == GOOD + { + 6, // kcl_round + 0xfffffc00, // kcl_mask + + 4, // pos_round + 2, // rot_round + 6, // scale_round + + 0, // route_round + 11, // cannon_round + }, + + // TINY 5 == MEDIUM + { + 5, // kcl_round + 0xfffff000, // kcl_mask + + 2, // pos_round + 1, // rot_round + 5, // scale_round + + -1, // route_round + 10, // cannon_round + }, + + // TINY 6 == EXTREME + { + 4, // kcl_round + 0xffffc000, // kcl_mask + + 0, // pos_round + 0, // rot_round + 4, // scale_round + + -2, // route_round + 9, // cannon_round + }, + + // TINY 7 == DANGEROUS + { + 3, // kcl_round + 0xffff0000, // kcl_mask + + -2, // pos_round + -1, // rot_round + 3, // scale_round + + -3, // route_round + 8, // cannon_round + }, +}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTiny ( ccp arg ) +{ + static const KeywordTab_t tab[] = + { + { 0, "TINY-0", "OFF", 0 }, + { 1, "TINY-1", "LOSSLESS", 0 }, + { 2, "TINY-2", "RECREATE", 0 }, + { 3, "TINY-3", "MINIMAL", 0 }, + { 4, "TINY-4", "GOOD", 0 }, + { 5, "TINY-5", "MEDIUM", 0 }, + { 6, "TINY-6", "EXTREME", 0 }, + { 7, "TINY-7", "DANGEROUS", 0 }, + { 0,0,0,0 } + }; + + char *end; + const ulong num = str2ul(arg,&end,10); + if ( end > arg && !*end && num < N_TINY_MODES ) + opt_tiny = num; + else + { + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (!cmd) + { + ERROR0(ERR_SYNTAX,"Invalid --tiny mode: '%s'\n",arg); + return 1; + } + + opt_tiny = cmd->opt; + } + + if ( opt_tiny && !opt_compr_mode ) + { + opt_compr_mode = 11; + opt_compr = 23 + opt_tiny; + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// general list support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeList ( List_t * list, uint elem_size ) +{ + DASSERT(list); + DASSERT(elem_size>0); + memset(list,0,sizeof(*list)); + list->elem_size = elem_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetList ( List_t * list ) +{ + DASSERT(list); + FREE(list->list); + const uint elem_size = list->elem_size; + memset(list,0,sizeof(*list)); + list->elem_size = elem_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * InsertList ( List_t * list, int index ) +{ + DASSERT( list ); + DASSERT( list->elem_size > 0 ); + DASSERT( list->used <= list->size ); + DASSERT( !list->list == !list->size ); + + if ( list->used == list->size ) + { + list->size = 3*list->size/2 + 100; + list->list = REALLOC( list->list, list->size * list->elem_size ) ; + } + + DASSERT( list->list ); + DASSERT( list->used < list->size ); + + if ( index < 0 ) + { + index += list->used++; + if ( index < 0 ) + index = 0; + } + else if ( index > list->used ) + index = list->used; + DASSERT( index >= 0 && index <= list->used ); + u8 * ptr = list->list + index * list->elem_size; + if ( index < list->used ) + memmove( ptr+list->elem_size, ptr, list->elem_size * (list->used-index) ); + memset(ptr,0,list->elem_size); + + list->used++; + DASSERT( list->used <= list->size ); + + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * AppendList ( List_t * list ) +{ + DASSERT( list ); + DASSERT( list->elem_size > 0 ); + DASSERT( list->used <= list->size ); + DASSERT( !list->list == !list->size ); + + if ( list->used == list->size ) + { + list->size = 3*list->size/2 + 100; + list->list = REALLOC( list->list, list->size * list->elem_size ) ; + } + + DASSERT( list->list ); + DASSERT( list->used < list->size ); + u8 *ptr = list->list + list->used++ * list->elem_size; + memset(ptr,0,list->elem_size); + return ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * GrowList ( List_t * list, uint n ) +{ + DASSERT( list ); + DASSERT( list->elem_size > 0 ); + DASSERT( list->used <= list->size ); + DASSERT( !list->list == !list->size ); + + const uint new_used = list->used + n; + if ( new_used > list->size || !list->size ) + { + list->size = new_used > 10 ? new_used : 10; + list->list = REALLOC( list->list, list->size * list->elem_size ) ; + } + + DASSERT( list->list ); + DASSERT( new_used <= list->size ); + + void *result = list->list + list->used * list->elem_size; + list->used = new_used; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * GrowListSize ( List_t * list, uint n, uint add_size ) +{ + DASSERT( list ); + DASSERT( list->elem_size > 0 ); + DASSERT( list->used <= list->size ); + DASSERT( !list->list == !list->size ); + + const uint new_used = list->used + n; + if ( new_used > list->size || !list->size ) + { + noPRINT("GROW-LIST: %u -> %u (size=%u)\n", + list->size, new_used + add_size, (new_used + add_size)*list->elem_size ); + list->size = new_used + add_size; + list->list = REALLOC( list->list, list->size * list->elem_size ) ; + } + + DASSERT( list->list ); + DASSERT( new_used <= list->size ); + + void *result = list->list + list->used * list->elem_size; + list->used = new_used; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * GetListElem + ( const List_t * list, int index, const void * return_not_found ) +{ + DASSERT( list ); + DASSERT( list->elem_size > 0 ); + DASSERT( list->used <= list->size ); + DASSERT( !list->list == !list->size ); + + if ( index < 0 ) // allow acces to elements relative to end + index += list->used; + + return (uint)index < list->used + ? list->list + index * list->elem_size + : (void*)return_not_found; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMIL ( List_t * list ) +{ + DASSERT(list); + InitializeList(list,sizeof(MemItem_t)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMIL ( List_t * list ) +{ + DASSERT(list); + + MemItem_t *eptr = GetMemListElem(list,0,0); + MemItem_t *eend = eptr + list->used; + for ( ; eptr < eend; eptr++ ) + { + if (eptr->name_alloced) + FreeString(eptr->name); + if (eptr->data_alloced) + FREE(eptr->data); + FREE(eptr->str_idx); + } + ResetList(list); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int sort_mem_list_by_idx ( const MemItem_t * a, const MemItem_t * b ) +{ + int stat = a->idx1 - b->idx1; + if (!stat) + { + stat = a->idx2 - b->idx2; + if (!stat) + stat = a->sort_idx - b->sort_idx; + } + return stat; +} + +//----------------------------------------------------------------------------- + +static int sort_mem_list_by_sort ( const MemItem_t * a, const MemItem_t * b ) +{ + int stat = a->sort_idx - b->sort_idx; + if (!stat) + { + stat = a->idx1 - b->idx1; + if (!stat) + stat = a->idx2 - b->idx2; + } + return stat; +} + +//----------------------------------------------------------------------------- + +void SortMIL ( List_t * list, bool use_sort_idx ) +{ + DASSERT(list); + if ( list->used > 1 ) + { + noPRINT("qsort(%p,%d,%zd)\n", list->list, list->used, sizeof(MemItem_t) ); + qsort( list->list, list->used, sizeof(MemItem_t), + use_sort_idx + ? (qsort_func)sort_mem_list_by_sort + : (qsort_func)sort_mem_list_by_idx ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +MemItem_t * AppendMIL ( List_t * list ) +{ + MemItem_t * item = AppendList(list); // data is zeroed! + item->sort_idx = 10*list->used; + item->str_idx = 0; + item->str_idx_size = 0; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemItem_t * GrowMIL ( List_t * list, uint n ) +{ + return GrowList(list,n); +} + +/////////////////////////////////////////////////////////////////////////////// + +MemItem_t * GetMemListElem + ( const List_t * list, int index, const MemItem_t * return_not_found ) +{ + return GetListElem(list,index,return_not_found); +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 * GetStrIdxMIL ( MemItem_t * mi, uint n_need ) +{ + DASSERT(mi); + const uint BUF_MAX = sizeof(mi->str_idx_buf)/sizeof(*mi->str_idx_buf); + + if ( n_need <= BUF_MAX || n_need <= mi->str_idx_size ) + return mi->str_idx ? mi->str_idx : mi->str_idx_buf; + + typeof(mi->str_idx) new_list = CALLOC(n_need,sizeof(*new_list)); + if ( mi->str_idx ) + { + memcpy(new_list,mi->str_idx,mi->str_idx_size*sizeof(*new_list)); + FREE(mi->str_idx); + } + else + memcpy(new_list,mi->str_idx_buf,BUF_MAX*sizeof(*new_list)); + + mi->str_idx = new_list; + mi->str_idx_size = n_need; + return mi->str_idx; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// modes and options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCompr ( ccp arg ) +{ + static const KeywordTab_t alias[] = + { + { 0, "LIST", 0, -9 }, + { 1, "C-LIST", "CLIST", -9 }, + + { 0, "UNCOMPRESSED", 0, -1 }, + + { 0, "NOCHUNKS", 0, 1 }, + { 1, "FAST", 0, 1 }, + { 9, "BEST", 0, 1 }, + { 10, "ULTRA", 0, 1 }, + + { 9, "T2", "TRY2", 2 }, + { 9, "T3", "TRY3", 3 }, + { 9, "T4", "TRY4", 4 }, + { 9, "T5", "TRY5", 5 }, + + { 0,0,0,0 } + }; + + char *end; + const long num = str2l(arg,&end,10); + if ( end > arg && !*end ) + { + struct tab_t { s16 from, to, add, mode; }; + static const struct tab_t tab[] = + { + { -1, -1, 10, -1 }, + { 0, 10, 0, 1 }, + { 91, 93, 0, 9 }, + { 100, 150, -100, 11 }, + { -9,0,0,0} + }; + + static const struct tab_t *ptr; + for ( ptr = tab; ptr->from != -9; ptr++ ) + if ( num >= ptr->from && num <= ptr->to ) + { + opt_compr = num + ptr->add; + opt_compr_mode = ptr->mode; + return 0; + } + } + + const KeywordTab_t * cmd = ScanKeyword(0,arg,alias); + if (!cmd) + { + ERROR0(ERR_SYNTAX,"Invalid compression level (option --compr): '%s'\n",arg); + return 1; + } + + opt_compr_mode = cmd->opt; + if ( opt_compr_mode != -1 ) + opt_compr = cmd->id; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPtDir ( ccp arg ) +{ + if ( !arg || !*arg ) + { + opt_pt_dir = 1; + return 0; + } + + static const KeywordTab_t tab[] = + { + { -1, "REMOVE", "0", 0 }, + { 0, "AUTO", 0, 0 }, + { 1, "FORCE", "1", 0 }, + + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (cmd) + { + opt_pt_dir = cmd->id; + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid `point directory' mode (option --pdir): '%s'\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRecurse ( ccp arg ) +{ + if ( !arg || !*arg ) + { + opt_recurse = INT_MAX; + return 0; + } + + static const KeywordTab_t tab[] = + { + { 0, "NONE", 0, 0 }, + { INT_MAX, "UNLIMITED", "ALL", 0 }, + + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (cmd) + { + opt_recurse = cmd->id; + return 0; + } + + char * end; + ulong num = strtoul(arg,&end,10); + if ( end > arg && !*end && num <= INT_MAX ) + { + opt_recurse = num; + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid recurse level (option --recurse): '%s'\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +SlotMode_t opt_slot = SLOTMD_NONE; + +static const KeywordTab_t opt_slot_tab[] = +{ + { SLOTMD_NONE, "NONE", "-", 0 }, + + { SLOTMD_M_DAISY, "DAISY", "DESERT", 0 }, + { SLOTMD_M_DAISY, "3.1", "31", 0 }, + { SLOTMD_M_DAISY, "7.1", "71", 0 }, + + { SLOTMD_M_SHERBET, "SHERBET", "T61", 0 }, + { SLOTMD_M_SHERBET, "6.1", "61", 0 }, + + { SLOTMD_M_SHYGUY, "SHYGUY", "T62", 0 }, + { SLOTMD_M_SHYGUY, "6.2", "62", 0 }, + + { SLOTMD_M_STANDARD, "STANDARD", "STD", 0 }, + + { SLOTMD_M_MOST, "MOST", "ALL", 0 }, + + { SLOTMD_RM_ICE, "RM-ICE", "RMICE", 1 }, + { SLOTMD_RM_SUNDS, "RM-SUNDS", "RMSUNDS", 1 }, + { SLOTMD_RM_SHYGUY, "RM-SHYGUY", "RMSHYGUY", 1 }, + { SLOTMD_ADD_GICE, "ADD-GICE", "ADDGICE", 1 }, + { SLOTMD_ADD_BICE, "ADD-BICE", "ADDBICE", 1 }, + { SLOTMD_ADD_SHYGUY, "ADD-SHYGUY", "ADDSHYGUY", 1 }, + { SLOTMD_ICE_TO_WATER, "ICE2WATER", "ICETOWATER", 1 }, + { SLOTMD_WATER_TO_ICE, "WATER2ICE", "WATERTOICE", 1 }, + + //{ -1, "4.2", "42", 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptSlot ( ccp arg ) +{ + if ( !arg || !*arg ) + return 0; + + const KeywordTab_t *cmd = ScanKeyword(0,arg,opt_slot_tab); + if (!cmd) + { + const KeywordTab_t *c2 = ScanKeyword(0,arg,track_name_tab); + if ( c2 && c2->id != 10 ) // 10 == moonview highway + { + char buf[10]; + snprintf(buf,sizeof(buf),"%llu",c2->id); + cmd = ScanKeyword(0,buf,opt_slot_tab); + if (!cmd) + cmd = ScanKeyword(0,"STANDARD",opt_slot_tab); + } + } + + if ( cmd && cmd->id >= 0 ) + { + if ( opt_slot & SLOTMD_JOB_RM_SZS ) + need_norm--; + if ( opt_slot & SLOTMD_JOB_ADD_SZS ) + have_patch_count--; + if ( opt_slot & SLOTMD_JOB_KMP ) + have_patch_count--, have_kmp_patch_count--; + if ( opt_slot & SLOTMD_JOB_KCL ) + have_patch_count--, have_kcl_patch_count--; + + opt_slot = cmd->id; + + if ( opt_slot & SLOTMD_JOB_RM_SZS ) + need_norm++; + if ( opt_slot & SLOTMD_JOB_ADD_SZS ) + have_patch_count++; + if ( opt_slot & SLOTMD_JOB_KMP ) + have_patch_count++, have_kmp_patch_count++; + if ( opt_slot & SLOTMD_JOB_KCL ) + have_patch_count++, have_kcl_patch_count++; + + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid keyword for option --slot: '%s'\n",arg); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintSlotMode ( SlotMode_t mode ) +{ + const KeywordTab_t *cmd; + for ( cmd = opt_slot_tab; cmd->name1; cmd++ ) + if ( mode == (u32)cmd->id ) + return cmd->name1; + return "?"; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp NormalizeTrackName ( char *buf, uint bufsize, ccp arg ) +{ + DASSERT(buf ); + DASSERT(bufsize>10); + DASSERT(arg); + + // skip leading separators + while ( *arg > 1 && *arg <= ' ' || *arg == ',' || *arg == ':' || *arg == '=' ) + arg++; + + char *dest = buf, *dest_end = buf + bufsize - 2; + while ( dest < dest_end && *arg && *arg != ',' && *arg != ':' && *arg != '=' ) + { + int code = ScanUTF8AnsiChar(&arg); + if (isalnum(code)) + *dest++ = toupper(code); + else switch(code) + { + case '.': + *dest++ = '.'; + break; + + case 0xe4: // ä + case 0xc4: // Ä + *dest++ = 'A'; + *dest++ = 'E'; + break; + + case 0xf6: // ö + case 0xd6: // Ö + *dest++ = 'O'; + *dest++ = 'E'; + break; + + case 0xfc: // ü + case 0xdc: // Ü + *dest++ = 'U'; + *dest++ = 'E'; + break; + + case 0xdf: // ß + *dest++ = 'S'; + *dest++ = 'S'; + break; + } + } + + *dest = 0; + return arg; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int scan_track_arena +( + ccp arg, + const u32 * def_tab, + u32 * tab, + u32 * r_tab, + const TrackInfo_t * info, + uint n, + ccp opt_name, + int (*scan_func) ( ccp name ) +) +{ + DASSERT(def_tab); + DASSERT(tab); + DASSERT(r_tab); + DASSERT(info); + DASSERT(scan_func); + DASSERT( n > 0 ); + + if (!arg) + return 0; + + memcpy(tab,def_tab,n*sizeof(*tab)); + + uint track_index = 0; + bool swap_tracks = true; + + for(;;) + { + char name[100]; + arg = NormalizeTrackName(name,sizeof(name),arg); + if (!*name) + break; + + if (!strcmp(name,"0")) + { + uint i; + for ( i = 0; i < n; i++ ) + tab[i] = i; + continue; + } + + int index = scan_func(name); + if ( index < 0 ) + { + ERROR0(ERR_SYNTAX,"Option --%ss: Not a %s name: %s\n", + opt_name, opt_name, name); + return 1; + } + + if ( *arg == ':' || *arg == '=' ) + { + swap_tracks = *arg == '='; + PRINT("INDEX --%ss: %2u -> %2u\n", + opt_name, track_index, info[index].def_index ); + track_index = info[index].def_index; + + if ( *arg == ':' ) arg++; + if ( *arg == '=' ) arg++; + continue; + } + + if ( track_index >= n ) + { + ERROR0(ERR_SEMANTIC,"Option --%ss: Invald track index: %u (max=%u)\n", + opt_name, track_index, n-1 ); + return 1; + } + + u32 * tptr = tab; + if (swap_tracks) + while ( tptr < tab+n && *tptr != index ) + tptr++; + + if ( swap_tracks && tptr < tab+n ) + { + PRINT("SWAP --%s: %2u=%2u, %2zu=%2u\n", + opt_name, track_index, *tptr, + tptr-tab, tab[track_index] ); + *tptr = tab[track_index]; + } + tab[track_index] = index; + track_index++; + } + + uint i; + for ( i = 0; i < n; i++ ) + r_tab[tab[i]] = i; + //HEXDUMP16(0,0,r_tab,n*sizeof(*tab)); + + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptTracks ( ccp arg ) +{ + opt_tracks = true; + return scan_track_arena ( arg, track_pos_default, track_pos, track_pos_r, + track_info, MKW_N_TRACKS, "track", ScanTrack ); +} + +//----------------------------------------------------------------------------- + +int ScanOptArenas ( ccp arg ) +{ + opt_arenas = true; + return scan_track_arena ( arg, arena_pos_default, arena_pos, arena_pos_r, + arena_info, MKW_N_ARENAS, "arena", ScanArena ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpTrackList +( + FILE * f, // NULL(=stdout) or file + const u32 * tab, // NULL or list with track indices + int brief_mode // 0: full table + // 1: suppress header + // 2: file names only +) +{ + if (!f) + f = stdout; + if (!tab) + tab = track_pos; + + if ( brief_mode > 1 ) + { + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + { + const uint idx = *tab++; + const TrackInfo_t * info = idx < MKW_N_TRACKS ? track_info + idx : &InvalidTrack; + fprintf(f,"%s\n",info->track_fname); + } + return; + } + + if (brief_mode<1) + { + if (long_count) + fprintf(f, + "\n" + " new std file name sound standard fast sound track track\n" + " idx slot slot of track slot sound file name f.name abbr. name\n" + ); + else + fprintf(f, + "\n" + " new std file name standard fast sound track\n" + " idx slot slot of track sound file name f.name name\n" + ); + } + + const uint width = long_count ? 99 : 87; + + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + { + if ( brief_mode<1 &&!(i%4) ) + fprintf(f,"%.*s\n",width,Minus300); + + const uint idx = *tab++; + const TrackInfo_t * info = idx < MKW_N_TRACKS ? track_info + idx : &InvalidTrack; + fprintf(f,"%3u. %u.%u %u.%u %-*s ", + idx, + i/4+1, i%4+1, + info->def_slot/10, info->def_slot%10, + FW_TRACK_FNAME, info->track_fname ); + + if (long_count) + fprintf(f,"0x%02x ",info->music_id); + + fprintf(f,"%-*s %s ", + FW_TRACK_SOUND_N, info->sound_n_fname, + info->sound_f_fname + strlen(info->sound_f_fname) - 2 ); + + if (long_count) + fprintf(f,"%-5s ",info->abbrev); + + fprintf(f,"%s\n", use_de ? info->name_de : info->name_en ); + } + if (brief_mode<1) + fprintf(f,"%.*s\n\n",width,Minus300); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpArenaList +( + FILE * f, // NULL(=stdout) or file + const u32 * tab, // NULL or list with arena indices + int brief_mode // 0: full table + // 1: suppress header + // 2: file names only +) +{ + if (!f) + f = stdout; + if (!tab) + tab = arena_pos; + + if ( brief_mode > 1 ) + { + uint i; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + { + const uint idx = *tab++; + const TrackInfo_t * info = idx < MKW_N_ARENAS ? arena_info + idx : &InvalidTrack; + fprintf(f,"%s\n",info->track_fname); + } + return; + } + + if (brief_mode<1) + { + if (long_count) + fprintf(f, + "\n" + " new std file name sound standard + fast arena arena\n" + " idx slot slot of arena slot sound file name abbr. name\n" + ); + else + fprintf(f, + "\n" + " new std file name standard + fast arena\n" + " idx slot slot of arena sound file name name\n" + ); + } + + const uint width = long_count ? 88 : 76; + + uint i; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + { + if ( brief_mode<1 &&!(i%5) ) + fprintf(f,"%.*s\n",width,Minus300); + + const uint idx = *tab++; + const TrackInfo_t * info = idx < MKW_N_ARENAS ? arena_info + idx : &InvalidTrack; + fprintf(f,"%3u. %u.%u %u.%u %-*s ", + idx, + i/5+1, i%5+1, + info->def_slot/10, info->def_slot%10, + FW_ARENA_FNAME, info->track_fname ); + + if (long_count) + fprintf(f,"0x%02x ",info->music_id); + + fprintf(f,"%-*s %s ", + FW_ARENA_SOUND_N, info->sound_n_fname, + info->sound_f_fname + strlen(info->sound_f_fname) - 2 ); + + if (long_count) + fprintf(f,"%-5s ",info->abbrev); + + fprintf(f,"%s\n", use_de ? info->name_de : info->name_en ); + } + if (brief_mode<1) + fprintf(f,"%.*s\n\n",width,Minus300); +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpTrackFileList +( + FILE * f, // NULL(=stdout) or file + const u32 * track_tab, // NULL or list with track indices + const u32 * arena_tab // NULL or list with arena indices +) +{ + if (!f) + f = stdout; + if (!track_tab) + track_tab = track_pos; + if (!arena_tab) + arena_tab = arena_pos; + + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + { + const uint idx = *track_tab++; + const TrackInfo_t * info = idx < MKW_N_TRACKS ? track_info + idx : &InvalidTrack; + + fprintf(f,"T%u.%u %s %s %s\n", + i/4+1, i%4+1, + info->track_fname, info->sound_n_fname, info->sound_f_fname ); + } + + for ( i = 0; i < MKW_N_ARENAS; i++ ) + { + const uint idx = *arena_tab++; + const TrackInfo_t * info = idx < MKW_N_ARENAS ? arena_info + idx : &InvalidTrack; + + fprintf(f,"A%u.%u %s %s %s\n", + i/5+1, i%5+1, + info->track_fname, info->sound_n_fname, info->sound_f_fname ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sort mode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +SortMode_t GetSortMode +( + SortMode_t sort_mode, // wanted sort mode + file_format_t fform, // use it if 'sort_mode==SORT_AUTO' + SortMode_t default_mode // default if all other fails +) +{ + switch (sort_mode) + { + case SORT_NONE: + case SORT_INAME: + case SORT_OFFSET: + case SORT_U8: + case SORT_PACK: + case SORT_BRRES: + case SORT_BREFF: + return sort_mode; + + case SORT_AUTO: + switch(fform) + { + case FF_U8: + case FF_WU8: + case FF_RARC: + return SORT_U8; + + case FF_PACK: + return SORT_PACK; + + case FF_BRRES: + return SORT_BRRES; + + case FF_BREFF: + case FF_BREFT: + return SORT_BREFF; + + default: + return default_mode; + } + } + + return SORT_INAME; +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t opt_sort_tab[] = +{ + { SORT_NONE, "NONE", "0", 0 }, + { SORT_INAME, "NAME", "INAME", 0 }, + { SORT_OFFSET, "OFFSET", 0, 0 }, + + { SORT_U8, "U8", 0, 0 }, + { SORT_PACK, "PACK", 0, 0 }, + { SORT_BRRES, "BRRES", "BRES", 0 }, + { SORT_BREFF, "BREFF", "BREFT", 0 }, + + { SORT_AUTO, "AUTO", 0, 0 }, + + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +ccp GetSortName ( int id, ccp res_not_found ) +{ + return GetKewordNameById(opt_sort_tab,id,res_not_found); +} + +//----------------------------------------------------------------------------- + +int ScanOptSort ( ccp arg ) +{ + const KeywordTab_t * cmd = ScanKeyword(0,arg,opt_sort_tab); + if (cmd) + { + opt_sort = cmd->id; + return 0; + } + + ERROR0(ERR_SYNTAX,"Invalid sort mode (option --sort): '%s'\n",arg); + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command argtest /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_argtest ( int argc, char ** argv ) +{ + printf("ARGUMENT TEST: %u arguments:\n",argc); + + int idx; + for ( idx = 0; idx < argc; idx++ ) + printf("%4u.: |%s|\n",idx,argv[idx]); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command error /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_error() +{ + if (!n_param) + { + if ( print_sections ) + { + int i; + for ( i=0; iarg); + int i; + for ( i = 0; i < ERR__N; i++ ) + if (!strcmp(GetErrorName(i,""),buf)) + { + num = i; + break; + } + + if ( num == ERR__N ) + { + char * end; + num = strtoul(first_param->arg,&end,10); + stat = *end ? ERR_SYNTAX : num < 0 || num >= ERR__N ? ERR_SEMANTIC : ERR_OK; + } + } + + if (print_sections) + printf("\n[error]\ncode=%lu\nname=%s\ntext=%s\n", + num, GetErrorName(num,"?"), GetErrorText(num,"?")); + else if (long_count) + printf("%s\n",GetErrorText(num,"?")); + else if (brief_count) + printf("%lu\n",num); + else + printf("%s\n",GetErrorName(num,"?")); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command filetype /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_filetype() +{ + #ifdef __CYGWIN__ + { + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + NORMALIZE_FILENAME_PARAM(param); + } + #endif + + if (print_header) + { + uint max_len = 0; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + uint len = strlen(param->arg); + if ( max_len < len ) + max_len = len; + } + + if ( long_count > 1 ) + printf("\n" + "type decomp vers valid file name\n" + "%.*s\n", max_len + 25, Minus300 ); + else if ( long_count ) + printf("\n" + "type decomp vers file name\n" + "%.*s\n", max_len + 20, Minus300 ); + else + printf("\n" + "type file name\n" + "%.*s\n", max_len + 8, Minus300 ); + } + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + char buf1[CHECK_FILE_SIZE], buf2[CHECK_FILE_SIZE], bufstat[10]; + FileAttrib_t fatt; + enumError err = LoadFILE(param->arg,0,0,buf1,sizeof(buf1),1,&fatt,false); + if ( err <= ERR_WARNING || !opt_ignore ) + { + file_format_t fform1; + if (S_ISDIR(fatt.mode)) + { + err = ERR_OK; + fform1 = FF_DIRECTORY; + } + else +// [[analyse-magic]] + fform1 = GetByMagicFF(buf1,sizeof(buf1),fatt.size); + + ccp stat1 = err > ERR_WARNING ? "-" : GetNameFF(0,fform1); + ccp buf = buf1; + if (long_count) + { + size_t written = fatt.size; + file_format_t fform2 = fform1; + ccp stat2 = "-"; + int version = -1; + char suffix = 0; + bool load_full = false; + if (IsYazFF(fform1)) + { + if ( fform1 == FF_XYZ ) + DecodeXYZ((u8*)buf1,(u8*)buf1,sizeof(buf1)); + + fatt.size = be32(&((yaz0_header_t*)buf1)->uncompressed_size); + DecompressYAZ( buf1 + sizeof(yaz0_header_t), + sizeof(buf1) - sizeof(yaz0_header_t), + buf2, sizeof(buf2), + &written, param->arg, + GetYazVersionFF(fform1), true, 0 ); + DASSERT( written <= sizeof(buf2) ); +// [[analyse-magic]] + fform2 = GetByMagicFF(buf2,written,written); + version = GetVersionFF(fform2,buf2,written,&suffix); + stat2 = GetNameFF(0,fform2); + buf = buf2; + } + else if ( fform1 == FF_BZ ) + { + load_full = true; + wbz_header_t *wh = (wbz_header_t*)buf1; + const file_format_t ff_temp +// [[analyse-magic]] + = GetByMagicFF(wh->first_8,sizeof(wh->first_8),be32(wh->bz_data)); + stat2 = GetNameFF(0,ff_temp); + version = GetVersionFF(ff_temp,wh->first_8,sizeof(wh->first_8),&suffix); + if ( fatt.size >= 20 ) + { + char compr = buf1[19]; + if ( compr >= '1' && compr <= '9' ) + { + snprintf(bufstat,sizeof(bufstat),"%s.%c",stat1,compr); + stat1 = bufstat; + } + } + } + else if ( fform1 == FF_BZ2 ) + { + snprintf(bufstat,sizeof(bufstat),"BZ2.%u",IsBZIP2(buf1,sizeof(buf1))); + stat1 = bufstat; + + uint wr; + DecodeBZIP2part(buf2,sizeof(buf2),&wr,buf1,sizeof(buf1)); +// [[analyse-magic]] + fform2 = GetByMagicFF(buf2,wr,wr); + stat2 = GetNameFF(0,fform2); + } + else + version = GetVersionFF(fform1,(u8*)buf1,sizeof(buf1),&suffix); + + char vbuf[20] = " -"; + if ( version >= 0 ) + snprintf(vbuf,sizeof(vbuf),"%4u%c", version, suffix ? suffix : ' ' ); + + if ( long_count > 1 ) + { + valid_t valid = VALID_UNKNOWN_FF; + if (load_full) + { + szs_file_t szs; + InitializeSZS(&szs); + if (!LoadSZS(&szs,param->arg,true,true,true)) + valid = IsValidSZS(&szs,false); + ResetSZS(&szs); + } + else + valid = IsValid(buf,written,fatt.size,0,fform2,param->arg); + printf("%-6s %-6s %s %-5s %s\n", + stat1, stat2, vbuf, valid_text[valid], param->arg ); + } + else + printf("%-6s %-6s %s %s\n",stat1,stat2,vbuf,param->arg); + } + else + printf("%-6s %s\n",stat1,param->arg); + } + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command fileattrib /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t cmdtab_file_attrib[] = +{ + { FFT_VALID, "VALID", 0, 0 }, + { FFT_VALID, "INVALID", "DIRECTORY", 1 }, + + { FFT_COMPRESS, "COMPRESSED", 0, 0 }, + { FFT_ARCHIVE, "ARCHIVES", 0, 0 }, + { FFT_GRAPHIC, "GRAPHICS", "IMAGES", 0 }, + { FFT_TEXT, "TEXT", "TXT", 0 }, + { FFT_M_FTYPE, "ETC", 0, 1 }, + + { FFT_BRSUB, "BRSUB", "BR-SUB", 0 }, + { FFT_BRSUB, "BRESSUB", "BRES-SUB", 0 }, + { FFT_BRSUB, "BRRESSUB", "BRRES-SUB", 0 }, + { FFT_BRSUB2, "BRSUB2", "BR-SUB2", 0 }, + { FFT_BRSUB2, "BRESSUB2", "BRES-SUB2", 0 }, + { FFT_BRSUB2, "BRRESSUB2", "BRRES-SUB2", 0 }, + { FFT_EXTERNAL, "EXTERNALS", 0, 0 }, + { FFT_EXTERNAL, "INTERNALS", 0, 1 }, + + { FFT_CREATE, "CREATE", 0, 0 }, + { FFT_EXTRACT, "EXTRACT", 0, 0 }, + { FFT_CUT, "CUT", 0, 0 }, + { FFT_DECODE, "DECODE", 0, 0 }, + { FFT_ENCODE, "ENCODE", 0, 0 }, + { FFT_PARSER, "PARSER", 0, 0 }, + + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +enumError cmd_fileattrib() +{ + enumError stat = ERR_OK; + + u8 show[FF_N]; + if (n_param) + { + memset(show,0,sizeof(show)); + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + uint fform = GetByNameFF(param->arg); + if ( fform != FF_UNKNOWN && fform < FF_N ) + show[fform] = 1; + else + { + const KeywordTab_t * cmd + = ScanKeyword(0,param->arg,cmdtab_file_attrib); + if (!cmd) + stat = ERROR0(ERR_WARNING, + "Unknown keyword ignored: %s\n", param->arg); + else if (cmd->opt) + { + int i; + for ( i = 0; i < FF_N; i++ ) + if ( !( GetAttribFF(i) & cmd->id ) ) + show[i] = 1; + } + else + { + int i; + for ( i = 0; i < FF_N; i++ ) + if ( GetAttribFF(i) & cmd->id ) + show[i] = 1; + } + } + } + } + else + memset(show,1,sizeof(show)); + + if (print_header) + printf("\n" + "type classification possible operations%30s\n" + "%.80s\n", + "file extensions", Minus300 ); + + ccp last = ""; + for(;;) + { + ccp found = 0; + int i, found_idx = 0; + for ( i = 1; i < FF_N; i++ ) + { + if (show[i]) + { + ccp name = GetNameFF(0,i); + if ( strcmp(name,last) > 0 && ( !found || strcmp(name,found) < 0 ) ) + { + found = name; + found_idx = i; + } + } + } + + if (!found) + break; + + printf("%-7s",GetNameFF(0,found_idx)); + + const file_type_t *fft = GetFileTypeFF(found_idx); + ASSERT(fft); + ff_attrib_t fa = fft->attrib; + ccp info; + + switch ( fa & (FFT_VALID|FFT_M_FTYPE) ) + { + case 0: info = "directory"; break; + case FFT_VALID|FFT_COMPRESS: info = "compress"; break; + case FFT_VALID|FFT_ARCHIVE: info = "archive"; break; + case FFT_VALID|FFT_GRAPHIC: info = "graphic"; break; + case FFT_VALID|FFT_TEXT: info = "text"; break; + default: info = "misc"; break; + } + printf(" %-9s",info); + + switch ( fa & FFT_M_CLASS ) + { + case FFT_BRSUB|FFT_BRSUB2: info = "bres-sub2"; break; + case FFT_BRSUB: info = "bres-sub"; break; + case FFT_EXTERNAL: info = "external"; break; + default: info = "-"; break; + } + printf(" %-9s",info); + + printf(" %s %s %s %s %s", + fa & FFT_CREATE ? "create" : "- ", + fa & FFT_EXTRACT ? "extract" : "- ", + fa & FFT_CUT ? "cut" : "- ", + fa & FFT_DECODE ? "decode" + : fa & FFT_LINK ? "link " : "- ", + fa & FFT_PARSER ? "parser" + : fa & FFT_ENCODE ? "encode" + : fa & FFT_PATCH ? "patch " : "- " ); + + printf(" %-6s",fft->ext); + if ( fa & FFT_ARCHIVE && strcmp(fft->ext_compr,fft->ext) ) + printf(" %s",fft->ext_compr); + if ( strcmp(fft->ext_magic,fft->ext) ) + printf(" %s",fft->ext_magic); + putchar('\n'); + + last = found; + } + + if (print_header) + printf("%.80s\n\n",Minus300); + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +#undef DEF_ENTRY +#define DEF_ENTRY(a) { FFT_##a, #a }, + +void ExportFileAttribMakedoc ( FILE * f ) +{ + DASSERT(f); + + fprintf(f,"\n#\f\n%.79s\n%.15s%17s%-32s%.15s\n%.79s\n\n", + Hash200, Hash200, "", "File Attributes", Hash200, Hash200 ); + + struct info_t { int val; ccp name; }; + static const struct info_t info_tab[] = + { + DEF_ENTRY(VALID) + + DEF_ENTRY(COMPRESS) + DEF_ENTRY(ARCHIVE) + DEF_ENTRY(GRAPHIC) + DEF_ENTRY(TEXT) + DEF_ENTRY(M_FTYPE) + + DEF_ENTRY(BRSUB) + DEF_ENTRY(BRSUB2) + DEF_ENTRY(EXTERNAL) + DEF_ENTRY(M_CLASS) + + DEF_ENTRY(CREATE) + DEF_ENTRY(EXTRACT) + DEF_ENTRY(CUT) + DEF_ENTRY(DECODE) + DEF_ENTRY(ENCODE) + DEF_ENTRY(PARSER) + DEF_ENTRY(M_SUPPORT) + + DEF_ENTRY(M_ALL) + + {0,0} + }; + + + fprintf(f,"#!--- define file attribute flags\n\n"); + const struct info_t *info_ptr; + for ( info_ptr = info_tab; info_ptr->name; info_ptr++ ) + fprintf(f,"#gdef FILEATT_%-10s = %#10x\n",info_ptr->name,info_ptr->val); + + fprintf(f, "\n#!--- define file attribute map\n\n" + "#gdef fileatt$flag = @map\n\n"); + for ( info_ptr = info_tab; info_ptr->name; info_ptr++ ) + fprintf(f,"#gdef fileatt$flag['%s'] %*s %#10x\n", + info_ptr->name, + (int)( 10 - strlen(info_ptr->name) ), "=", + info_ptr->val ); + + + fprintf(f, "\n#!--- define file attributes\n\n" + "#gdef $fileatt = @map\n\n"); + + int fform; + for ( fform = 1; fform < FF_N; fform++ ) + { + ccp name = GetNameFF(0,fform); + fprintf(f, + "#pdef d = @map\n" + "#pdef d['idx'] = %2u\n" + "#pdef d['name'] = '%s'\n" + "#pdef d['ext'] = '%s'\n" + "#pdef d['c-ext'] = '%s'\n" + "#pdef d['attrib'] = %#x\n" + "#gdef $fileatt['%s'] = move(d)\n\n" + ,fform + ,name + ,GetExtFF(0,fform) + ,GetExtFF(fform_compr,fform) + ,GetAttribFF(fform) + ,name + ); + } +} + +#undef DEF_ENTRY + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command float /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_float() +{ + u16 w4[2], w8[4]; + memset(w4,0,sizeof(w4)); + memset(w8,0,sizeof(w8)); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + ccp arg = param->arg; + if (!arg) + continue; + while ( *arg && (uchar)*arg <= ' ' ) + arg++; + + if (!strcmp(arg,"+")) + { + write_be32(w4,be32(w4)+1); + write_be64(w8,be64(w8)+1); + } + else if (!strcmp(arg,"-")) + { + write_be32(w4,be32(w4)-1); + write_be64(w8,be64(w8)-1); + } + else + { + memset(w4,0,sizeof(w4)); + memset(w8,0,sizeof(w8)); + + uint skip; + if ( arg[0] == '0' && ( arg[1] == 'x' || arg[1] == 'X' )) + skip = 2; + else if ( arg[0] == 'x' || arg[0] == 'X' ) + skip = 1; + else + skip = 0; + + if (skip) + { + ccp src = arg + skip; + ScanHexString(w8,sizeof(w8),&src,0,true); + memcpy(w4,w8,sizeof(w4)); + } + else + { + double d = strtod(arg,0); + write_bef4(w4,d); + write_bef8(w8,d); + } + } + + printf(" %04x %04x = %12.9g : %04x %04x %04x %04x = %22.18g : |%s|\n", + be16(w4+0), be16(w4+1), bef4(w4), + be16(w8+0), be16(w8+1), be16(w8+2), be16(w8+3), bef8(w8), + arg ); + + if (opt_round) + { + u16 r4[2], r8[4]; + + write_bef4(r4,RoundF3bytes(bef4(w4))); + write_bef8(r8,RoundD7bytes(bef8(w8))); + printf(" %04x %04x = %12.9g : %04x %04x %04x %04x = %22.18g" + " : rounded to 3|7 bytes\n", + be16(r4+0), be16(r4+1), bef4(r4), + be16(r8+0), be16(r8+1), be16(r8+2), be16(r8+3), bef8(r8) ); + + write_bef4(r4,RoundF2bytes(bef4(w4))); + write_bef8(r8,RoundD6bytes(bef8(w8))); + printf(" %04x %04x = %12.9g : %04x %04x %04x %04x = %22.18g" + " : rounded to 2|6 bytes\n\n", + be16(r4+0), be16(r4+1), bef4(r4), + be16(r8+0), be16(r8+1), be16(r8+2), be16(r8+3), bef8(r8) ); + } + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command vr-calc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int get_vr_value ( ccp arg, int res_on_err ) +{ + if (!arg) + return res_on_err; + + while ( *arg == ' ' || *arg == '\t' ) + arg++; + + if (!*arg) + return res_on_err; + + bool neg = *arg == '-' || *arg == '/'; + if (neg) + arg++; + int val = str2ul(arg,0,10); + if ( val > 99999 ) + val = 99999; + + return neg ? -val : val; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_vr_calc() +{ + #if HAVE_PRINT + GetVrDiffByTab(0); // force logging here and now + #endif + + if (brief_count) + { + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + ccp arg = param->arg; + printf("%4u\n",GetVrDiffByTab(get_vr_value(arg,0))); + } + } + else + { + const uint seplen = 3*27; + if (print_header) + printf( + "\n" + "%s%.*s%s\n" + "%s vr vr-increment for%s\n" + "%s delta win-lose lose-win%s\n" + "%s%.*s%s\n", + colout->heading, seplen, ThinLine300_3, colout->reset, + colout->heading, colout->reset, + colout->heading, colout->reset, + colout->heading, seplen, ThinLine300_3, colout->reset ); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + ccp arg = param->arg; + const int delta = get_vr_value(arg,0); + printf("%7d %6u %9u\n", + delta, GetVrDiffByTab(delta), GetVrDiffByTab(-delta) ); + } + + if (print_header) + printf("%s%.*s%s\n\n", + colout->heading, seplen, ThinLine300_3, colout->reset ); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command vr-race /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_vr_race() +{ + #if HAVE_PRINT + GetVrDiffByTab(0); // force logging here and now + #endif + + enum { MAX = 30 }; + int n = 0, vr0[MAX], delta[MAX] = {0}, delta2[MAX][MAX] = {{0}}; + + //--- scan param + + ParamList_t *param; + for ( param = first_param; param && n < MAX; param = param->next ) + { + ccp arg = param->arg; + int repeat = 1; + ccp star = strchr(arg,'*'); + if (star) + { + repeat = str2l(arg,0,10); + arg = star+1; + } + + const int vr = get_vr_value(arg,-1); + if ( vr < 0 ) + continue; + + while ( repeat-- > 0 && n < MAX ) + { + vr0[n] = vr; + n++; + } + } + + if ( n < 2 ) + return ERROR0(ERR_SEMANTIC,"Need VR values of 2..%u players.",MAX); + + + //--- calculate delta + + int i, j; + for ( i = 0; i < n-1; i++ ) + { + for ( j = i+1; j < n; j++ ) + { + const int d = GetVrDiffByTab( vr0[i] - vr0[j] ); + delta[i] += d; + delta[j] -= d; + delta2[i][j] = d; + delta2[j][i] = -d; + } + } + + + //--- print table + + if (long_count) + { + const uint seplen = 3*(20+5*n); + printf( "\n" + "%s%.*s%s\n" + "%s vr : %*s= vr%s\n" + "%srk. start : %*s= final%s\n" + "%s%.*s%s\n", + colout->heading, seplen, ThinLine300_3, colout->reset, + colout->heading, -5*n, " vr", colout->reset, + colout->heading, -5*n, "delta...", colout->reset, + colout->heading, seplen, ThinLine300_3, colout->reset ); + + for ( i = 0; i < n; i++ ) + { + printf("%2d. %5d :", i+1, vr0[i] ); + ccp col = colout->reset; + for ( j = 0; j < n; j++ ) + { + if ( i == j ) + { + printf("%s •",colout->b_cyan); + col = colout->b_cyan; + } + else + { + const int d = delta2[i][j]; + ccp next_col = d < 0 ? colout->b_red + : d > 0 ? colout->b_green : colout->reset; + if ( col != next_col ) + { + col = next_col; + printf(" %s%4d",col,d); + } + else + printf(" %4d",d); + } + } + + + const int d = delta[i]; + ccp next_col = d < 0 ? colout->b_red : d > 0 ? colout->b_green : ""; + + printf(" %s= %s%5d%s\n", + col == colout->reset ? "" : colout->reset, + next_col, vr0[i] + delta[i], + *next_col ? colout->reset : "" ); + } + printf("%s%.*s%s\n\n", + colout->heading, seplen, ThinLine300_3, colout->reset ); + } + else + { + const uint seplen = 3*28; + printf( "\n" + "%s%.*s%s\n" + "%s vr vr vr%s\n" + "%s rank start delta final%s\n" + "%s%.*s%s\n", + colout->heading, seplen, ThinLine300_3, colout->reset, + colout->heading, colout->reset, + colout->heading, colout->reset, + colout->heading, seplen, ThinLine300_3, colout->reset ); + + for ( i = 0; i < n; i++ ) + printf("%4d. %6d %+6d = %5d\n", + i+1, vr0[i], delta[i], vr0[i]+delta[i] ); + + printf("%s%.*s%s\n\n", + colout->heading, seplen, ThinLine300_3, colout->reset ); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command rawdump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_rawdump ( const data_tab_t *data_tab ) +{ + CheckOptDest( first_param ? first_param->arg : "rawdump.tmp/", true ); + opt_mkdir = true; + + const data_tab_t *dt; + for ( dt = data_tab; dt->name; dt++ ) + { + char path_buf[PATH_MAX]; + ccp dest = PathCatPPE(path_buf,sizeof(path_buf),opt_dest,dt->name,".bin"); + + const u8 *data = dt->data; + uint size = dt->size; + if ( dt->mode & 1 ) + DecodeBZIP2((u8**)&data,&size,0,dt->data,dt->size); + + printf("SAVE %7u = %6x %x %s\n",size,size,dt->mode,dest); + SaveFILE(dest,0,true,data,size,0); + + u8 *bz_data = 0; + uint bz_size = 0, bz_compr = 0; + uint compr; + + for ( compr = 1; compr <= 9; compr++ ) + { + u8 *temp_data; + uint temp_size; + enumError err = EncodeBZIP2( &temp_data, &temp_size, true, + 0, data, size, compr ); + if ( !err && ( !bz_data || temp_size < bz_size ) ) + { + FREE(bz_data); + bz_data = temp_data; + bz_size = temp_size; + bz_compr = compr; + } + else + FREE(temp_data); + } + + if (bz_data) + { + dest = PathCatPPE(path_buf,sizeof(path_buf),opt_dest,dt->name,".bz-bin"); + printf("SAVE %7u = %6x %s [c%u,%u%%]\n", + bz_size, bz_size, dest, bz_compr, bz_size*100/size ); + SaveFILE(dest,0,true,bz_data,bz_size,0); + } + + if ( data != dt->data ) + FREE((u8*)data); + } + + return ERR_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////// export keys /////////////// +/////////////////////////////////////////////////////////////////////////////// + +export_mode_t ScanExportMode() // scan parameters +{ + static const KeywordTab_t tab[] = + { + { -1, "HELP", "?", 0 }, + +// { EXPORT_C, "C", 0, EXPORT_CMD_MASK }, + { EXPORT_PHP, "PHP", 0, EXPORT_CMD_MASK }, + { EXPORT_MAKEDOC, "MAKEDOC", 0, EXPORT_CMD_MASK }, + + { EXPORT_F_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { EXPORT_F_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { EXPORT_F_TRACKS, "TRACKS", "T", 0 }, + + { 0,0,0,0 } + }; + + export_mode_t exmode = 0; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + ccp arg = param->arg; + if (!*arg) + continue; + + const KeywordTab_t * cmd = ScanKeyword(0,arg,tab); + if (!cmd) + continue; + + if ( cmd->id == -1 ) + { + printf("\nList of kexwords:\n\n"); + const KeywordTab_t *ptr; + for ( ptr = tab; ptr->name1; ptr++ ) + { + if (ptr->name2) + printf(" %s = %s\n",ptr->name1,ptr->name2); + else + printf(" %s\n",ptr->name1); + } + putchar('\n'); + return 0; + } + + if (cmd->opt) + exmode &= ~cmd->opt; + exmode |= cmd->id; + } + + if (!( exmode & EXPORT_FLAG_MASK )) + exmode |= EXPORT_FLAG_MASK; + + if (!( exmode & EXPORT_CMD_MASK )) + { + ERROR0(ERR_SYNTAX,"Export: No ouput format entered!\n"); + exmode = 0; + } + + return exmode; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// repair magic /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[repair_mode_t]] + +// vars for --repair-magic +repair_mode_t repair_magic = REPMD_OFF; +repair_mode_t repair_magic_flags = 0; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRepairMagic ( ccp arg ) +{ + if (!arg) + { + repair_magic = REPMD_REPAIR; + repair_magic_flags = 0; + return 0; + } + + static const KeywordTab_t keytab[] = + { + { REPMD_OFF, "OFF", 0, REPMD_M_MODES }, + { REPMD_ANALYZE, "ANALYZE", 0, REPMD_M_MODES }, + { REPMD_REPAIR, "REPLACE", "ON", REPMD_M_MODES }, + { REPMD_F_FNAME_FIRST, "FNAME", 0, 0 }, + + {0,0,0,0} + }; + + const s64 stat = ScanKeywordList(arg,keytab,0,true,0,0, + "Option --repair-magic",ERR_SYNTAX); + if ( stat == -1 ) + return 1; + + repair_magic = stat & REPMD_M_MODES; + repair_magic_flags = stat & REPMD_M_FLAGS; + PRINT("%llx => repair_magic=%x,%x\n",stat,repair_magic,repair_magic_flags); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +repair_ff_t * ResetRepairMagic ( repair_ff_t * data, repair_ff_t * fallback ) +{ + static repair_ff_t falback2; + if (!data) + data = fallback ? fallback : &falback2; + + memset(data,0,sizeof(*data)); + data->mode = REPMD_OFF; + data->fform = FF_UNKNOWN; + + return data; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetRepairMagicInfo ( const repair_ff_t *rep ) +{ + if (!rep) + return NULL; + + char buf[80]; + int len = snprintfS(buf,sizeof(buf), + "smode=%u%s%s%s, fform=%s, ext=%s, magic=%s/%s", + rep->mode, + rep->flags & REPMD_F_FNAME_FIRST? "f" : "", + rep->flags & REPMD_R_FNAME ? "F" : "", + rep->flags & REPMD_R_FALLBACK ? "X" : "", + GetNameFF(rep->fform,0), rep->file_ext, + PrintID(rep->orig_magic,rep->magic_len,0), + PrintID(rep->magic,rep->magic_len,0) + ) + 1; + + char *res = GetCircBuf(len); + memcpy(res,buf,len); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +file_format_t AnalyseMagic +( + // returns rep->fform + + repair_ff_t *rep, // not NULL: result, will be initalized + const void *data, // pointer to data + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname, // filename to analyse if GetByMagicFF() fails + bool fname_first // analyse the filename first. +) +{ + //fprintf(stderr,">>> %s\n",fname); HexDump16(stderr,0,0,data,16); + + repair_ff_t local_rep; + rep = ResetRepairMagic(rep,&local_rep); + DASSERT(rep); + + memcpy( rep->magic, data, + data_size < sizeof(rep->magic) ? data_size : sizeof(rep->magic) ); + memcpy( rep->orig_magic, rep->magic, sizeof(rep->orig_magic) ); + + if (!be32(rep->magic)) + fname_first = true; + rep->flags = fname_first ? REPMD_F_FNAME_FIRST : 0; + + if (fname) + { + ccp point = strrchr(fname,'.'); + if (point) + { + ccp slash = strrchr(fname,'/'); + if ( !slash || point > slash && strlen(point) < sizeof(rep->file_ext) ) + StringLowerS(rep->file_ext,sizeof(rep->file_ext),point); + } + } + + repair_mode_t status = 0; + file_format_t ff_magic = GetByMagicFF(data,data_size,file_size); + if ( ff_magic == FF_UNKNOWN && ff_fallback != FF_UNKNOWN ) + { + ff_magic = ff_fallback; + status = REPMD_R_FALLBACK; + } + + rep->magic_len = FileTypeTab[ff_magic].magic_len; + if ( ff_magic != FF_UNKNOWN && !fname_first ) + { + rep->flags |= status; + return rep->fform = ff_magic; + } + + const file_type_t *ft = GetFileTypeByExt(rep->file_ext,true); + if (!ft) + ft = GetFileTypeBySubdir(fname,true); + rep->flags |= ft ? REPMD_R_FNAME : status; + if ( !ft || ft->fform == ff_magic ) + return rep->fform = ff_magic; + + //fprintf(stderr,">>> ff: %d > %d : %s\n",ff_magic,ft->fform,fname); + DASSERT(ft); + rep->fform = ft->fform; + const uint mlen = ft->magic_len < sizeof(rep->magic) + ? ft->magic_len : sizeof(rep->magic); + rep->magic_len = mlen; + memset(rep->magic,0,sizeof(rep->magic)); + memcpy(rep->magic,ft->magic,mlen); + rep->mode = REPMD_ANALYZE; + + PRINT("AnalyseMagic() %s\n",GetRepairMagicInfo(rep)); + return rep->fform; +} + +/////////////////////////////////////////////////////////////////////////////// + +file_format_t RepairMagic +( + // returns rep->fform + + repair_ff_t *rep, // not NULL: result, will be initalized + void *data, // pointer to data, magic will be replaced + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname, // filename to analyse if GetByMagicFF() fails + bool fname_first // analyse the filename first. +) +{ + repair_ff_t local_rep; + if (!rep) + rep = &local_rep; + AnalyseMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first); + + if ( rep->mode == REPMD_ANALYZE && rep->magic_len ) + { + if (logging>1) + fprintf(stderr,"REPAIR MAGIC: %s -> %s (%s) : %s\n", + PrintID(data,rep->magic_len,0), + PrintID(rep->magic,rep->magic_len,0), + GetNameFF(0,rep->fform), fname ); + + rep->mode = REPMD_REPAIR; + memcpy(data,rep->magic,rep->magic_len); + } + + PRINT("RepairMagic() %s\n",GetRepairMagicInfo(rep)); + return rep->fform; +} + +/////////////////////////////////////////////////////////////////////////////// + +file_format_t AnalyseMagicByOpt +( + // returns rep->fform + // mode & fname_first depends on --repair-magic, but is never REPMD_REPAIR + + repair_ff_t *rep, // not NULL: result, will be initalized + const void *data, // pointer to data + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname // filename to analyse if GetByMagicFF() fails +) +{ + repair_ff_t local_rep; + rep = ResetRepairMagic(rep,&local_rep); + DASSERT(rep); + + const bool fname_first = ( repair_magic_flags & REPMD_F_FNAME_FIRST ) != 0; + switch(repair_magic) + { + case REPMD_ANALYZE: + case REPMD_REPAIR: + AnalyseMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first); + break; + + default: + rep->fform = GetByMagicFF(data,data_size,file_size); + if ( rep->fform == FF_UNKNOWN && ff_fallback != FF_UNKNOWN ) + { + rep->fform = ff_fallback; + rep->flags |= REPMD_R_FALLBACK; + } + break; + } + return rep->fform; +} + +/////////////////////////////////////////////////////////////////////////////// + +file_format_t RepairMagicByOpt +( + // returns rep->fform + // mode & fname_first depends on --repair-magic + + repair_ff_t *rep, // not NULL: result, will be initalized + void *data, // pointer to data, magic will be replaced + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname // filename to analyse if GetByMagicFF() fails +) +{ + repair_ff_t local_rep; + rep = ResetRepairMagic(rep,&local_rep); + DASSERT(rep); + + const bool fname_first = ( repair_magic_flags & REPMD_F_FNAME_FIRST ) != 0; + switch(repair_magic) + { + case REPMD_ANALYZE: + AnalyseMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first); + break; + + case REPMD_REPAIR: + RepairMagic(rep,data,data_size,file_size,ff_fallback,fname,fname_first); + break; + + default: + rep->fform = GetByMagicFF(data,data_size,file_size); + if ( rep->fform == FF_UNKNOWN && ff_fallback != FF_UNKNOWN ) + { + rep->fform = ff_fallback; + rep->flags |= REPMD_R_FALLBACK; + } + break; + } + return rep->fform; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SHA1_to_ID ( sha1_id_t id, const sha1_hash_t hash ) +{ + //--- create a smaller hash value (20->5 bytes with 3 bytes padding) + + u8 buf[8]; + u8 * dest = buf; + const u8 * src = hash; + + uint i; + for ( i = 0; i < 5; i++ ) + { + *dest++ = src[0] ^ src[5] ^ src[10] ^ src[15]; + src++; + } + + //--- now create a printable and case insenitive ID + + static const u8 ctab[] = "0123456789abcdefghijklmnopqrstuv"; + u64 val = le64(buf); + dest = id; + for ( i = 0; i < 8; i++ ) + { + *dest++ = ctab[ val & 0x1f ]; + val >>= 5; + } + *dest = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool IsExtractEnabled() +{ + static bool enabled = false; + if ( opt_extract && *opt_extract && !enabled ) + { + if ( CreatePath(opt_extract,true) == ERR_OK ) + enabled = true; + } + return enabled; +} + +//----------------------------------------------------------------------------- + +bool PrepareExtract +( + // return FALSE on disabled or failure, otherwise return TRUE + + File_t *f, // file structure, will be initialized + int mode, // convert filename: <0=to lower, >0=to upper + ccp format, // format of filename + ... // arguments +) +{ + DASSERT(f); + DASSERT(format); + + if (!IsExtractEnabled()) + { + InitializeFile(f); + return 0; + } + + char fname[1000]; + va_list arg; + va_start(arg,format); + vsnprintf(fname,sizeof(fname),format,arg); + va_end(arg); + + if (mode) + { + char *ptr = fname; + if ( mode < 0 ) + while ( ( *ptr = tolower((int)*ptr) ) != 0 ) + ptr++; + else + while ( ( *ptr = toupper((int)*ptr) ) != 0 ) + ptr++; + } + + char pathbuf[PATH_MAX]; + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),opt_extract,fname); + CreateFile(f,true,path,FM_REMOVE); + return f->f != NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +CheckMode_t GetCheckMode +( + bool no_head, // true: suppress header + bool no_hints, // true: suppress hints + bool quiet, // true: be quiet if no hint|warning is printed + bool verbose // true: print some more hints +) +{ + CheckMode_t mode = CMOD_DEFAULT; + if (no_head) + mode &= ~(CMOD_HEADER|CMOD_FORCE_HEADER); + if (no_hints) + mode &= ~CMOD_HINT; + if (quiet) + mode &= ~(CMOD_FORCE_HEADER|CMOD_FORCE_FOOTER); + if (verbose) + mode |= CMOD_INFO|CMOD_VERBOSE; + return mode; +} + +/////////////////////////////////////////////////////////////////////////////// + +CheckMode_t GetMainCheckMode ( CheckMode_t mode, bool reset_counter ) +{ + if (reset_counter) + { + global_warn_count = 0; + global_hint_count = 0; + global_info_count = 0; + } + + return mode | CMOD_FORCE_HEADER; +} + +/////////////////////////////////////////////////////////////////////////////// + +CheckMode_t GetSubCheckMode ( CheckMode_t mode, bool reset_counter ) +{ + return mode & ~(CMOD_FOOTER|CMOD_FORCE_FOOTER) + | CMOD_FORCE_HEADER + | CMOD_GLOBAL_STAT; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetMkwMusicSlot ( uint tid ) +{ + if ( tid < MKW_N_TRACKS ) + return track_info[tid].music_id; + + if ( tid - MKW_N_TRACKS < MKW_N_ARENAS ) + return arena_info[tid-MKW_N_TRACKS].music_id; + + switch (tid) + { + case 0x36: return 0xc9; // Galaxy Stadium + default: return 0x75; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// apple only /////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifdef __APPLE__ +/////////////////////////////////////////////////////////////////////////////// + +void * memmem ( const void *l, size_t l_len, const void *s, size_t s_len ) +{ + register char *cur, *last; + const char *cl = (const char *)l; + const char *cs = (const char *)s; + + /* we need something to compare */ + if (l_len == 0 || s_len == 0) + return NULL; + + /* "s" must be smaller or equal to "l" */ + if (l_len < s_len) + return NULL; + + /* special case where s_len == 1 */ + if (s_len == 1) + return memchr(l, (int)*cs, l_len); + + /* the last position where its possible to find "s" in "l" */ + last = (char *)cl + l_len - s_len; + + for (cur = (char *)cl; cur <= last; cur++) + if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) + return cur; + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif // __APPLE__ +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ImageGeometry_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const ImageGeometry_t img_geo_I4 = +{ + IMG_I4, // iform + 0, 1, 0, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 4, 8, 8, 32, // bits_per_pixel, block: width,height,size + "I4" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_I8 = +{ + IMG_I8, // iform + 0, 1, 0, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 8, 8, 4, 32, // bits_per_pixel, block: width,height,size + "I8" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_IA4 = +{ + IMG_IA4, // iform + 0, 1, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 8, 8, 4, 32, // bits_per_pixel, block: width,height,size + "IA4" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_IA8 = +{ + IMG_IA8, // iform + 0, 1, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 16, 4, 4, 32, // bits_per_pixel, block: width,height,size + "IA8" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_RGB565 = +{ + IMG_RGB565, // iform + 0, 0, 0, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 16, 4, 4, 32, // bits_per_pixel, block: width,height,size + "RGB565" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_RGB5A3 = +{ + IMG_RGB5A3, // iform + 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 16, 4, 4, 32, // bits_per_pixel, block: width,height,size + "RGB5A3" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_RGBA32 = +{ + IMG_RGBA32, // iform + 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 32, 4, 4, 64, // bits_per_pixel, block: width,height,size + "RGBA32" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_C4 = +{ + IMG_C4, // iform + 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0x10, 1, 0, // max_palette, read_support, write_support + 4, 8, 8, 32, // bits_per_pixel, block: width,height,size + "C4" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_C8 = +{ + IMG_C8, // iform + 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0x100, 1, 0, // max_palette, read_support, write_support + 8, 8, 4, 32, // bits_per_pixel, block: width,height,size + "C8" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_C14X2 = +{ + IMG_C14X2, // iform + 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0x4000, 1, 0, // max_palette, read_support, write_support + 16, 4, 4, 32, // bits_per_pixel, block: width,height,size + "C14X2" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_CMPR = +{ + IMG_CMPR, // iform + 0, 0, 1, 1, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 4, 8, 8, 32, // bits_per_pixel, block: width,height,size + "CMPR" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_X_GRAY = +{ + IMG_X_GRAY, // iform + 1, 1, 1, 0, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 16, 1, 1, 2, // bits_per_pixel, block: width,height,size + "X-GRAY" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_X_RGB = +{ + IMG_X_RGB, // iform + 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks + 0, 1, 1, // max_palette, read_support, write_support + 32, 1, 1, 4, // bits_per_pixel, block: width,height,size + "X-RGB" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_X_PAL_4 = +{ + IMG_X_PAL4, // iform + 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks + 0x10, 1, 1, // max_palette, read_support, write_support + 16, 1, 1, 2, // bits_per_pixel, block: width,height,size + "X-PAL4" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_X_PAL_8 = +{ + IMG_X_PAL8, // iform + 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks + 0x100, 1, 1, // max_palette, read_support, write_support + 16, 1, 1, 2, // bits_per_pixel, block: width,height,size + "X-PAL8" // name +}; + +//----------------------------------------------------------------------------- + +static const ImageGeometry_t img_geo_X_PAL_14 = +{ + IMG_X_PAL14, // iform + 1, 0, 1, 0, // is_x, is_gray, has_alpha, has_blocks + 0x4000, 1, 1, // max_palette, read_support, write_support + 16, 1, 1, 2, // bits_per_pixel, block: width,height,size + "X-PAL14" // name +}; + +/////////////////////////////////////////////////////////////////////////////// + +const ImageGeometry_t * GetImageGeometry ( image_format_t iform ) +{ + switch (iform) + { + case IMG_I4: return &img_geo_I4; + case IMG_I8: return &img_geo_I8; + case IMG_IA4: return &img_geo_IA4; + case IMG_IA8: return &img_geo_IA8; + case IMG_RGB565: return &img_geo_RGB565; + case IMG_RGB5A3: return &img_geo_RGB5A3; + case IMG_RGBA32: return &img_geo_RGBA32; + case IMG_C4: return &img_geo_C4; + case IMG_C8: return &img_geo_C8; + case IMG_C14X2: return &img_geo_C14X2; + case IMG_CMPR: return &img_geo_CMPR; + case IMG_X_GRAY: return &img_geo_X_GRAY; + case IMG_X_RGB: return &img_geo_X_RGB; + case IMG_X_PAL4: return &img_geo_X_PAL_4; + case IMG_X_PAL8: return &img_geo_X_PAL_8; + case IMG_X_PAL14: return &img_geo_X_PAL_14; + case IMG_X_PAL: return &img_geo_X_PAL_14; + + case IMG_N_INTERN: + case IMG_INVALID: + case IMG_X_AUTO: + return 0; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintImageFormat +( + image_format_t iform, // image format + palette_format_t pform // palette format +) +{ + const ImageGeometry_t *geo = GetImageGeometry(iform); + if (geo) + { + if (!geo->max_palette) + return geo->name; + + ccp pal; + switch (pform) + { + case PAL_IA8: pal = "IA8"; break; + case PAL_RGB565: pal = "RGB565"; break; + case PAL_RGB5A3: pal = "RGB5A3"; break; + case PAL_X_RGB: pal = "X-RGB"; break; + default: pal = 0; + } + + const uint bufsize = 20; + char *buf = GetCircBuf(bufsize); + if (pal) + snprintf(buf,bufsize,"%s.%s",geo->name,pal); + else + snprintf(buf,bufsize,"%s.#%x",geo->name,pform); + return buf; + } + + const uint bufsize = 20; + char *buf = GetCircBuf(bufsize); + snprintf(buf,bufsize,"#%x",iform); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CalcImageSize +( + uint width, // width of image in pixel + uint height, // height of image in pixel + + uint bits_per_pixel, // number of bits per pixel + uint block_width, // width of a single block + uint block_height, // height of a single block + + uint * x_width, // not NULL: store extended width here + uint * x_height, // not NULL: store extended height here + uint * h_blocks, // not NULL: store number of horizontal blocks + uint * v_blocks // not NULL: store number of vertical blocks +) +{ + const uint hblocks = ( width + block_width - 1 ) / block_width; + if (h_blocks) + *h_blocks = hblocks; + + const uint xwidth = hblocks * block_width; + if (x_width) + *x_width = xwidth; + + const uint vblocks = ( height + block_height - 1 ) / block_height; + if (v_blocks) + *v_blocks = vblocks; + + const uint xheight = vblocks * block_height; + if (x_height) + *x_height = xheight; + + return xwidth * xheight * bits_per_pixel / 8; +} + +/////////////////////////////////////////////////////////////////////////////// + +const ImageGeometry_t * CalcImageGeometry +( + image_format_t iform, // image format + uint width, // width of image in pixel + uint height, // height of image in pixel + + uint * x_width, // not NULL: store extended width here + uint * x_height, // not NULL: store extended height here + uint * h_blocks, // not NULL: store number of horizontal blocks + uint * v_blocks, // not NULL: store number of vertical blocks + uint * img_size // not NULL: return image size +) +{ + const ImageGeometry_t *geo = GetImageGeometry(iform); + if (geo) + { + const uint size + = CalcImageSize( width, height, + geo->bits_per_pixel, geo->block_width, geo->block_height, + x_width, x_height, h_blocks, v_blocks ); + if (img_size) + *img_size = size; + return geo; + } + + if (x_width) *x_width = width; + if (x_height) *x_height = height; + if (h_blocks) *h_blocks = width; + if (v_blocks) *v_blocks = height; + if (img_size) *img_size = 0; + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image formats /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t cmdtab_image_format[] = +{ + { IMG_I4, "I4", 0, CF_IMAGE }, + { IMG_I8, "I8", 0, CF_IMAGE }, + { IMG_IA4, "IA4", 0, CF_IMAGE }, + { IMG_IA8, "IA8", 0, CF_IMAGE }, + { IMG_RGB565, "RGB565", 0, CF_IMAGE }, + { IMG_RGB5A3, "RGB5A3", 0, CF_IMAGE }, + { IMG_RGBA32, "RGBA32", "RGBA8", CF_IMAGE }, + { IMG_C4, "C4", "CI4", CF_IMAGE|CF_PALETTE }, + { IMG_C8, "C8", "CI8", CF_IMAGE|CF_PALETTE }, + { IMG_C14X2, "C14X2", "CI14X2", CF_IMAGE|CF_PALETTE }, + { IMG_CMPR, "CMPR", 0, CF_IMAGE }, + + { IMG_X_AUTO, "AUTO", 0, CF_AUTO }, + { IMG_X_GRAY, "X-GRAY", "GRAY", 0 }, + { IMG_X_RGB, "X-RGB", "RGB", 0 }, + + { IMG_X_PAL4, "X-PAL4", "PAL4", 0 }, + { IMG_X_PAL8, "X-PAL8", "PAL8", 0 }, + { IMG_X_PAL14, "X-PAL14", "PAL14", 0 }, + { IMG_X_PAL, "X-PAL", "PAL", CF_AUTO }, + { IMG_X_PAL, "X-PALETTE", "PALETTE", CF_AUTO }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +image_format_t ScanImageFormat ( ccp arg ) +{ + const KeywordTab_t * cmd = ScanKeyword(0,arg,cmdtab_image_format); + return cmd ? cmd->id : IMG_INVALID; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetImageFormatName ( image_format_t iform, ccp unknown_value ) +{ + if ( iform >= 0 ) + { + const KeywordTab_t * ct; + for ( ct = cmdtab_image_format; ct->name1; ct++ ) + if ( ct->id == iform ) + return ct->name1; + } + return unknown_value; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// palette formats /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t cmdtab_palette_format[] = +{ + { PAL_IA8, "P-IA8", "PIA8", 0 }, + { PAL_IA8, "GRAY", "IA8", 0 }, + + { PAL_RGB565, "P-RGB565", "PRGB565", 0 }, + { PAL_RGB565, "COLOR", "RGB565", 0 }, + + { PAL_RGB5A3, "P-RGB5A3", "PRGB5A3", 0 }, + { PAL_RGB5A3, "ALPHA", "RGB5A3", 0 }, + + { PAL_X_RGB, "P-RGB", "RGB", 0 }, + + { PAL_AUTO, "P-AUTO", "AUTO", 0 }, + + { 0,0,0,0 } +}; + +/////////////////////////////////////////////////////////////////////////////// + +palette_format_t ScanPaletteFormat ( ccp arg ) +{ + const KeywordTab_t * cmd = ScanKeyword(0,arg,cmdtab_palette_format); + return cmd ? cmd->id : PAL_INVALID; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetPaletteFormatName ( image_format_t iform, ccp unknown_value ) +{ + if ( iform >= 0 ) + { + const KeywordTab_t * ct; + for ( ct = cmdtab_palette_format; ct->name1; ct++ ) + if ( ct->id == iform ) + return ct->name1; + } + return unknown_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PaletteToImageFormat ( int pform, int return_if_invalid ) +{ + switch (pform) + { + case PAL_IA8: return IMG_IA8; + case PAL_RGB565: return IMG_RGB565; + case PAL_RGB5A3: return IMG_RGB5A3; + case PAL_X_RGB: return IMG_X_RGB; + } + return return_if_invalid; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetPaletteCountIF +( + image_format_t iform // image format +) +{ + switch (iform) + { + case IMG_C4: + case IMG_X_PAL4: + return 0x10; + + case IMG_C8: + case IMG_X_PAL8: + return 0x100; + + case IMG_C14X2: + case IMG_X_PAL14: + case IMG_X_PAL: + return 0x4000; + + default: + return 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan patch options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t cmdtab_patch_image[] = +{ + // order is important for back calculations + + { 0, "DEFAULT", "D", PIM_MASK }, + { PIM_CENTER, "CENTER", "C", PIM_M_V | PIM_M_H }, + + { PIM_VCENTER, "VCENTER", "V", PIM_M_V }, + { PIM_TOP, "TOP", "T", PIM_M_V }, + { PIM_BOTTOM, "BOTTOM", "B", PIM_M_V }, + { PIM_INS_TOP, "INS-TOP", "IT", PIM_M_V }, + { PIM_INS_BOTTOM, "INS-BOTTOM", "IB", PIM_M_V }, + + { PIM_HCENTER, "HCENTER", "H", PIM_M_H }, + { PIM_LEFT, "LEFT", "L", PIM_M_H }, + { PIM_RIGHT, "RIGHT", "R", PIM_M_H }, + { PIM_INS_LEFT, "INS-LEFT", "IL", PIM_M_H }, + { PIM_INS_RIGHT, "INS-RIGHT", "IR", PIM_M_H }, + + { PIM_MIX, "MIX", "MX", PIM_M_COPY }, + { PIM_FOREGROUND, "FOREGROUND", "FG", PIM_M_COPY }, + { PIM_BACKGROUND, "BACKGROUND", "BG", PIM_M_COPY }, + { PIM_COPY, "COPY", "CP", PIM_M_COPY }, + + { PIM_LEAVE, "LEAVE", "LV", PIM_M_SIZE }, + { PIM_SHRINK, "SHRINK", "SH", PIM_M_SIZE }, + { PIM_GROW, "GROW", "GR", PIM_M_SIZE }, + + {0,0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +bool ScanPatchImage ( ccp arg, PatchImage_t * mode ) +{ + const KeywordTab_t * cmd = ScanKeyword(0,arg,cmdtab_patch_image); + if (!cmd) + return false; + + if (mode) + *mode = *mode & ~cmd->opt | cmd->id; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPatchImage ( ccp arg ) +{ + ccp eq = strchr(arg,'='); + if (!eq) + { + ERROR0(ERR_SYNTAX,"Missing equal sign: %s\n",arg); + return 1; + } + + int stat = 0; + if ( eq > arg ) + { + char keyword[1000]; + StringCopyS(keyword,sizeof(keyword),arg); + if ( eq-arg < sizeof(keyword) ) + keyword[eq-arg] = 0; + + stat = ScanKeywordList(keyword,cmdtab_patch_image,0,true,0,0, + "Option --patch",ERR_SYNTAX); + if ( stat == -1 ) + return 1; + } + + PRINT("ADD PATCH: %02x %s\n",stat,eq+1); + FormatFieldItem_t * ffi = AppendFormatField(&patch_image_list,eq+1,false,false); + DASSERT(ffi); + ffi->patch_mode = stat; + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SZS file list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateTrackFiles +( + FileList_t mode, // bit field with requested files + IterateTrackFilesFunc + func, // call back function + void *param // user defined parameter +) +{ + int count = 0; + char idbuf[10], fbuf[50]; + + if ( mode & FLT_TRACK ) + { + int i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + { + const TrackInfo_t *ti = track_info + track_pos[i]; + if ( mode & FLT_STD ) + { + snprintf(idbuf,sizeof(idbuf),"T%u%u",i/4+1,i%4+1); + int stat = func(ti->track_fname,idbuf,FLT_TRACK|FLT_STD,ti,param); + if ( stat < 0 ) + return stat; + count += stat; + } + if ( mode & FLT_D ) + { + snprintf(idbuf,sizeof(idbuf),"T%u%ud",i/4+1,i%4+1); + StringCat2S(fbuf,sizeof(fbuf),ti->track_fname,"_d"); + int stat = func(fbuf,idbuf,FLT_TRACK|FLT_D,ti,param); + if ( stat < 0 ) + return stat; + count += stat; + } + } + } + + if ( mode & FLT_ARENA ) + { + int i; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + { + const TrackInfo_t *ti = arena_info + arena_pos[i]; + if ( mode & FLT_STD ) + { + snprintf(idbuf,sizeof(idbuf),"A%u%u",i/5+1,i%5+1); + int stat = func(ti->track_fname,idbuf,FLT_ARENA|FLT_STD,ti,param); + if ( stat < 0 ) + return stat; + count += stat; + } + if ( mode & FLT_D ) + { + snprintf(idbuf,sizeof(idbuf),"A%u%ud",i/5+1,i%5+1); + StringCat2S(fbuf,sizeof(fbuf),ti->track_fname,"_d"); + int stat = func(fbuf,idbuf,FLT_ARENA|FLT_D,ti,param); + if ( stat < 0 ) + return stat; + count += stat; + } + } + } + + if ( mode & (FLT_OTHER|FLT_OLD) ) + { + struct tab_t { bool have_d; uint mode; char id[4]; ccp fname; }; + static const struct tab_t tab[] = + { + { 1, FLT_OTHER, "WIN", "winningrun_demo" }, + { 1, FLT_OTHER, "LOS", "loser_demo" }, + { 1, FLT_OTHER, "END", "ending_demo" }, + { 1, FLT_OTHER, "MAR", "old_mario_gc_b" }, + { 1, FLT_OTHER, "RNG", "ring_mission" }, + + { 1, FLT_OLD, "DRW", "draw_demo" }, + { 0, FLT_OLD, "HAY", "old_mario_gc_hayasi" }, + { 0, FLT_OLD, "NAR", "old_mario_gc_narita" }, + { 0, FLT_OLD, "YAB", "old_mario_gc_yabuki" }, + + {0,0,"",0} + }; + + const struct tab_t *ptr; + for ( ptr = tab; ptr->fname; ptr++ ) + { + if (!(mode&ptr->mode)) + continue; + + if ( mode & FLT_STD ) + { + int stat = func(ptr->fname,ptr->id,ptr->mode|FLT_STD,0,param); + if ( stat < 0 ) + return stat; + count += stat; + } + if ( mode & FLT_D && ptr->have_d ) + { + snprintf(idbuf,sizeof(idbuf),"%sd",ptr->id); + StringCat2S(fbuf,sizeof(fbuf),ptr->fname,"_d"); + int stat = func(fbuf,idbuf,ptr->mode|FLT_D,0,param); + if ( stat < 0 ) + return stat; + count += stat; + } + } + } + + return count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IsValid() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp valid_text[VALID__N] = +{ + "ok+", // VALID_OK + "ok", // VALID_NO_TEST + "warn", // VALID_WARNING + "fail", // VALID_ERROR + "type", // VALID_WRONG_FF + "-", // VALID_UNKNOWN_FF +}; + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidSZS +( + struct szs_file_t *szs, // valid szs archive + bool warnings // true: print warnings +) +{ + DASSERT(szs); + return IsValid( szs->data, szs->size, szs->file_size, + szs, szs->fform_current, warnings ? szs->fname : 0 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValid +( + const void *data, // valid data to test + uint data_size, // size of 'data' + uint file_size, // not NULL: size of complete file + szs_file_t *szs, // not NULL: base archive + file_format_t fform, // known file format + // if FF_UNKNOWN: call GetByMagicFF() + ccp fname // not NULL: print warnings with file ref +) +{ + DASSERT( !szs || data_size <= szs->size ); + +// [[analyse-magic]] + file_format_t real_fform = GetByMagicFF(data,data_size,file_size); + if ( fform == FF_UNKNOWN ) + fform = real_fform; + else if ( fform == FF_DIRECTORY ) + return VALID_UNKNOWN_FF; + else if ( fform != real_fform ) + return VALID_WRONG_FF; + + switch (fform) + { + case FF_UNKNOWN: + return VALID_UNKNOWN_FF; + + case FF_BTI: + return IsValidBTI(data,data_size,file_size,fname); + + case FF_KCL: + return IsValidKCL(0,data,data_size,file_size,fname); + + case FF_KMP: + return IsValidKMP(data,data_size,file_size,fname); + + case FF_LEX: + return IsValidLEX(data,data_size,file_size,fname); + + case FF_MDL: + return IsValidMDL(data,data_size,file_size,szs,fname,0); + + case FF_PAT: + return IsValidPAT(data,data_size,file_size,szs,fname,0,0); + + case FF_STATICR: + return IsValidSTATICR(data,data_size,file_size,fname); + + case FF_TEX_CT: + return IsValidTEXCT(data,data_size,file_size,szs,fname); + + case FF_CT1_DATA: + return IsValidCTCODE(data,data_size,file_size,fname); + + default: + break; + } + + const ff_attrib_t fa = GetAttribFF(fform); + if ( fa & FFT_BRSUB ) + return IsValidBRSUB(data,data_size,file_size,szs,fform,true,fname,0); + + if ( fa & FFT_TEXT ) + return VALID_OK; + + return VALID_NO_TEST; +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidBRSUB +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + struct szs_file_t *szs, // not NULL: base archive + file_format_t fform, // known file format + // if FF_UNKNOWN: call GetByMagicFF() + bool check_fform, // true: confirm 'fform' by calling GetByMagicFF() + ccp fname, // not NULL: print warnings with file ref + const endian_func_t *endian // not NULL: use this endian functions +) +{ + DASSERT( !szs || szs->file_size >= szs->size ); + + if ( !data || data_size < sizeof(brsub_header_t) ) + return VALID_WRONG_FF; + + if (!endian) + endian = szs ? szs->endian : &be_func; + DASSERT(endian); + + if ( fform == FF_UNKNOWN || check_fform ) + { +// [[analyse-magic]] + file_format_t real_fform = GetByMagicFF(data,data_size,file_size); + if ( fform == FF_UNKNOWN ) + fform = real_fform; + else if ( fform != real_fform ) + return VALID_WRONG_FF; + } + + const brsub_header_t * bh = data; + const u32 size = endian->rd32(&bh->size); + + if ( data_size < sizeof(*bh) + //|| data_size < size + || file_size && file_size < size ) + { + PRINT("INVALID! data_size=%x, size=%x, version=%u\n", + data_size, size, endian->rd32(&bh->version) ); + return VALID_ERROR; + } + + if (szs) + { + if (!szs->data) + { + PRINT("INVALID BRSUB: no data\n"); + return VALID_ERROR; + } + + if ( (u8*)data + data_size > szs->data + szs->file_size ) + { + PRINT("INVALID BRSUB: invalid data size: %zx > %zx\n", + (u8*)data + data_size - szs->data, szs->file_size ); + return VALID_ERROR; + } + + const u32 brres_offset = endian->rd32(&bh->brres_offset); + if ( brres_offset != (u32)( szs->data - (u8*)data ) + && ( !szs->parent || brres_offset != (u32)( szs->parent->data - (u8*)data ) )) + { + PRINT("INVALID BRSUB: Invalid back offset: %zx != %x\n", + szs->data - (u8*)data, + endian->rd32(&bh->brres_offset) ); + #ifdef TEST // [[2do]] + ERROR0(ERR_WARNING, + "Invalid subfile back reference to BRRES archive file: Offset %x @%s\n", + (u32)( (u8*)data - szs->data ), szs->fname ); + #else + return VALID_ERROR; + #endif + } + } + else + { + const u32 off = endian->rd32(&bh->brres_offset); + if ( off && off < 0xc0000000 ) // 1GB limit + { + PRINT("INVALID BRSUB: limit %u\n",off); + return VALID_ERROR; + } + } + + noPRINT("BRSUB: %d.%d.%d.%d\n", + isalnum( (int)(bh->magic[0]) ), + isalnum( (int)(bh->magic[1]) ), + isalnum( (int)(bh->magic[2]) ), + isalnum( (int)(bh->magic[3]) ) ); + + return isalnum( (int)(bh->magic[0]) ) + && isalnum( (int)(bh->magic[1]) ) + && isalnum( (int)(bh->magic[2]) ) + && isalnum( (int)(bh->magic[3]) ) + ? VALID_OK : VALID_ERROR; +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidBTI +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +) +{ + //--- First make a plausibility checks and return VALID_WRONG_FF on fail. + //--- This phase is done because of missing a magic + + if ( !data || data_size < sizeof(bti_header_t) ) + return VALID_WRONG_FF; + + const bti_header_t *bti = (bti_header_t*)data; + u32 data_off = be32(&bti->data_off); + if ( !bti->n_image || data_off < sizeof(bti_header_t) || data_off & 3 ) + return VALID_WRONG_FF; + + const image_format_t iform = bti->iform; + const ImageGeometry_t *geo = GetImageGeometry(iform); + if ( !geo || geo->is_x ) + return VALID_WRONG_FF; + + const u32 pal_off = be32(&bti->pal_off); + const palette_format_t pform = be16(&bti->pform); + const uint n_pal = be16(&bti->n_pal); + if ( geo->max_palette > 0 ) + { + if ( pal_off < sizeof(bti_header_t) || pal_off & 3 ) + return VALID_WRONG_FF; + if (PaletteToImageFormat(pform,IMG_X_RGB) == IMG_X_RGB ) + return VALID_WRONG_FF; + const uint max_pal = GetPaletteCountIF(iform); + if ( !n_pal || n_pal > max_pal ) + return VALID_WRONG_FF; + } + else + { + if ( pform || n_pal || pal_off ) + return VALID_WRONG_FF; + } + + const uint width = be16(&bti->width); + const uint height = be16(&bti->height); + if ( !width || !height ) + return VALID_WRONG_FF; + + const uint image_size = geo->bits_per_pixel * width * height / 8; + if ( data_off + image_size > data_size ) + return VALID_WRONG_FF; + + + //--- Now we think it's a BTI => make content checks + + if (file_size) + { + // [[2do]] + // return VALID_ERROR; + // return VALID_OK; + } + + return VALID_NO_TEST; // [[2do]] or VALID_OK? +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidTEXCT +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + szs_file_t *szs, // not NULL: base archive + ccp fname // not NULL: print warnings with file ref +) +{ + const valid_t brsub_valid + = IsValidBRSUB(data,data_size,file_size,szs,FF_TEX,false,fname,0); + if ( brsub_valid >= VALID_ERROR ) + return brsub_valid; + + const brsub_header_t *bh = (brsub_header_t*)data; + if ( be32(&bh->version) != 3 ) + return VALID_WRONG_FF; + + const u32 img_off = be32(bh->grp_offset); + if ( img_off <= CT_CODE_TEX_OFFSET || file_size && img_off >= file_size ) + return VALID_WRONG_FF; + + return IsValidCTCODE( data + CT_CODE_TEX_OFFSET, + img_off - CT_CODE_TEX_OFFSET, + file_size ? file_size - CT_CODE_TEX_OFFSET : 0, + fname ); +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidCTCODE +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +) +{ + if ( data_size < sizeof(ctcode_header_t) ) + return VALID_WRONG_FF; + + const ctcode_header_t *ch = (ctcode_header_t*)data; + const uint n_sect = be32(&ch->n_sect); + const u32 min_data_off = sizeof(ctcode_header_t) + n_sect * sizeof(ctcode_sect_info_t); + if ( n_sect < 2 + || data_size < min_data_off + || be32(&ch->magic) != CT1_DATA_MAGIC_NUM + || memcmp(ch->sect_info[0].name,"CUP1",4) + || memcmp(ch->sect_info[1].name,"CRS1",4) + ) + { + return VALID_WRONG_FF; + } + + // ??? [[2do]] [[ctcode]] + return VALID_NO_TEST; +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidKCL +( + kcl_analyze_t * ka, // not NULL: init and store stats + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +) +{ + kcl_analyze_t local_ka; + if (!ka) + ka = &local_ka; + memset(ka,0,sizeof(*ka)); + + noPRINT("IsValidKCL(%u,%u,%s)\n",data_size,file_size,fname); + if ( !data || data_size < sizeof(kcl_head_t) ) + return ka->valid = VALID_WRONG_FF; + + ka->head_size = sizeof(kcl_head_t); + + + //--- setup memory map + + MemMap_t mm; + InitializeMemMap(&mm); + if (file_size) + { + ka->file_size = file_size; + MemMapItem_t *mi = InsertMemMap(&mm,file_size,0); + mi->index = N_KCL_SECT; + StringCopyS(mi->info,sizeof(mi->info),"End of file"); + } + + uint sect; + const kcl_head_t * kcl = data; + + for ( sect = 0; sect < N_KCL_SECT; sect++ ) + { + u32 offset = be32(kcl->sect_off+sect); + if ( sect == 2 ) + offset += 0x10; + + if ( offset == sizeof(kcl_head_t) - 4 ) + ka->head_size = offset; + + ka->off[sect] = offset; + if ( offset & 3 + || offset < sizeof(kcl_head_t) - 4 // unknown_0x38 is optional + || file_size && offset > file_size + ) + { + noPRINT("INVALID KCL: sect=%d, off=%x, headsize=%zx, filesize=%x\n", + sect, offset, sizeof(kcl_head_t), file_size ); + return ka->valid = VALID_ERROR; + } + + MemMapItem_t *mi = InsertMemMap(&mm,offset,0); + DASSERT(mi); + mi->index = sect; + snprintf(mi->info,sizeof(mi->info),"Section #%u",sect+1); + } + + + //--- calculate section sizes + + static const uint elem_size[N_KCL_SECT] + = { sizeof(float3), sizeof(float3), sizeof(kcl_triangle_t) }; + + uint i, err = 0, order = 0; + for ( i = 0; i < mm.used - 1; i++ ) + { + MemMapItem_t *p1 = mm.field[i]; + MemMapItem_t *p2 = mm.field[i+1]; + p1->size = p2->off - p1->off; + order = order * 10 + p1->index + 1; + + const uint esize = elem_size[p1->index]; + if (esize) + { + const uint n = p1->size / esize; + ka->n[p1->index] = n; + if ( p1->size != n * esize ) + { + p1->size = n * esize; + err++; + } + } + ka->size[p1->index] = p1->size; + } + ka->order_value = order; + ka->order_ok = order == 1234; + + + //--- logging + + if ( logging > 1 ) + { + fprintf(stdlog,"\nMemory map of KCL sections: %s\n", fname ? fname : "" ); + PrintMemMap(&mm,stdlog,3,"info"); + fputc('\n',stdlog); + } + + // [[2do]] [[kcl]] more tests? + + noPRINT_IF(err,"INVALID KCL: %s\n",GetValidInfoKCL(ka)); + return ka->valid = err ? VALID_ERROR : VALID_OK; +} + +//----------------------------------------------------------------------------- + +ccp GetValidInfoKCL ( kcl_analyze_t * ka ) +{ + if (!ka) + return 0; + + char order[20]; + if (!ka->order_ok) + snprintf(order,sizeof(order),"order=%u / ",ka->order_value); + else + *order = 0; + + char buf[150]; + uint len = snprintfS(buf,sizeof(buf), + "%ssect=%x+%x, %x+%x, %x+%x, %x+%x, %x / N=%d,%d,%d,%d", + order, + ka->off[0], ka->size[0], + ka->off[1], ka->size[1], + ka->off[2], ka->size[2], + ka->off[3], ka->size[3], + ka->file_size, + ka->n[0], ka->n[1], ka->n[2], ka->n[3] ) + 1; + + char *res = GetCircBuf(len); + memcpy(res,buf,len); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ScanHeadInfoKMP ( kmp_head_info_t *hi, cvp data, uint data_size ) +{ + DASSERT(hi); + DASSERT(data||!data_size); + + memset(hi,0,sizeof(*hi)); + hi->data = data; + hi->end = hi->data + data_size; + hi->data_size = data_size; + + const kmp_file_gen_t * kfile = data; + hi->file_size = ntohl(kfile->file_size); + hi->max_size = hi->file_size < data_size ? hi->file_size : data_size; + + hi->head_size = ntohs(kfile->head_size); + hi->max_off = data_size - hi->head_size - 4; + hi->n_sect = ntohs(kfile->n_sect); + + const uint max_sect = ( hi->head_size - 0x10 ) / 4; + if ( hi->n_sect > max_sect ) + hi->n_sect = 0; + hi->sect_off = (u32*) ((u8*)data + hi->head_size - 4*hi->n_sect); +} + +//----------------------------------------------------------------------------- + +valid_t IsValidKMP +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +) +{ + if ( !data || data_size < sizeof(kmp_file_gen_t) ) + return VALID_WRONG_FF; + + kmp_head_info_t hi; + ScanHeadInfoKMP(&hi,data,data_size); + if (memcmp(data,"RKMD",4)) + return VALID_WRONG_FF; + + PRINT("M=%.4s n-sect=%d, h-size=%u,%u,%zu f-size=%u,%u\n", + (ccp)data, hi.n_sect, + hi.head_size, GetFileHeadSizeKMP(hi.n_sect), sizeof(kmp_file_mkw_t), + hi.file_size, file_size ); + + if ( force_kmp && hi.file_size < file_size ) + hi.file_size = file_size; + if ( hi.head_size < GetFileHeadSizeKMP(hi.n_sect) || hi.head_size >= hi.file_size ) + return VALID_ERROR; + + valid_t stat = VALID_OK; + if ( file_size && hi.file_size > file_size ) + { + if (!force_kmp) + return VALID_ERROR; + + stat = VALID_WARNING; + if (fname) + ERROR0(ERR_WARNING, + "KMP header declares file size as %u bytes, but it has only %u bytes: %s", + hi.file_size, file_size, fname ); + hi.file_size = file_size; + + kmp_file_gen_t *kfile = (kmp_file_gen_t*)data; + write_be32(&kfile->file_size,hi.file_size); + } + + const uint max_off = hi.file_size - hi.head_size; + uint warn_count = 0, sect; + for ( sect = 0; sect < hi.n_sect; sect++ ) + { + const u32 off = ntohl(hi.sect_off[sect]); + noPRINT("OFF #%02u: %4x/%4x/%4x\n", sect, off, max_off, file_size ); + if ( off >= max_off || file_size && off > file_size ) + warn_count++; + } + + if ( warn_count > 0 && fname && *fname ) + ERROR0(ERR_WARNING,"KMP: %u segment%s behind end of file marker: %s\n", + warn_count, warn_count == 1 ? "" : "s", fname ); + + return warn_count ? VALID_WARNING : stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidLEX +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +) +{ + if ( !data || data_size < sizeof(lex_header_t) ) + return VALID_WRONG_FF; + + const lex_header_t * lex = data; + if ( memcmp(lex->magic,"LE-X",sizeof(lex->magic))) + return VALID_WRONG_FF; + + // [[todo]] [[lex]] ??? + return VALID_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidMDL +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + struct szs_file_t *szs, // not NULL: base archive + ccp fname, // not NULL: print warnings with file ref + const endian_func_t *endian // not NULL: use this endian functions +) +{ + valid_t brsub_valid + = IsValidBRSUB(data,data_size,file_size,szs,FF_MDL,true,fname,endian); + if ( brsub_valid >= VALID_ERROR ) + return brsub_valid; + + // [[2do]] [[mdl]] ??? + return brsub_valid; +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidPAT +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + struct szs_file_t *szs, // not NULL: base archive + ccp fname, // not NULL: print warnings with file ref + const endian_func_t *endian, // not NULL: use this endian functions + struct pat_analyse_t *ana // not NULL: store analysis data here +) +{ + pat_analyse_t ana0; + if (!ana) + ana = &ana0; + memset(ana,0,sizeof(*ana)); + ana->data = data; + ana->data_size = data_size; + + ana->valid = IsValidBRSUB(data,data_size,file_size,szs,FF_PAT,true,fname,endian); + if ( ana->valid >= VALID_ERROR ) + return ana->valid; + + + //--- start with PAT analysis + + //HEXDUMP16(0,0,data,data_size); + + if (!file_size) + file_size = szs ? szs->size : data_size; + + + //--- check header + + if ( file_size < PAT_HEAD_OFFSET + sizeof(pat_head_t) ) + return ana->valid = VALID_ERROR; + if ( data_size < PAT_HEAD_OFFSET + sizeof(pat_head_t) ) + return ana->valid = VALID_WARNING; + + ana->head = (pat_head_t*)(data + PAT_HEAD_OFFSET); + ana->n_sect0 = be16(&ana->head->n_sect0); + ana->n_sect1 = be16(&ana->head->n_sect1); + ana->data_complete = true; + + + //--- check existence of sections 1 and 3 + + const brsub_header_t *bh = data; + u32 off = be32(bh->grp_offset+1); + u32 end_off = off + 4*ana->n_sect1; + if (!off) + ana->data_complete = false; + else if ( end_off > file_size ) + { + ana->data_complete = false; + ana->valid = VALID_ERROR; + } + else if ( end_off <= data_size ) + ana->s1_list = (u32*)(ana->data + off); + + off = be32(bh->grp_offset+3); + end_off = off + 4*ana->n_sect1; + if (!off) + ana->data_complete = false; + else if ( end_off > file_size ) + { + ana->data_complete = false; + ana->valid = VALID_ERROR; + } + else if ( end_off <= data_size ) + ana->s3_list = (u32*)(ana->data + off); + + + //--- now analyse section 0 + + off = be32(bh->grp_offset+0); + if ( off + sizeof(pat_s0_bhead_t) > data_size ) + { + invalid: + ana->data_complete = false; + ana->valid = VALID_ERROR; + } + else + { + pat_s0_bhead_t *bhead = (pat_s0_bhead_t*)(ana->data + off); + u32 bhead_end = off + be32(&bhead->size); + if ( bhead_end > data_size ) + goto invalid; + ana->s0_base = bhead; + if ( be16(&bhead->n_elem) != ana->n_sect0 ) + { + PRINT("N-SECT: %d %d %d\n", + ana->n_sect0, be16(&bhead->n_elem), be16(&bhead->n_unknown) ); + ana->valid = VALID_WARNING; + } + + uint i; + for ( i = 0; i < ana->n_sect0; i++ ) + { + pat_s0_belem_t *belem = bhead->elem + i; + const u32 ref_off = off + be32(&belem->offset_strref); + if ( ref_off + sizeof(pat_s0_sref_t) > data_size ) + goto invalid; + + pat_s0_sref_t *sref = (pat_s0_sref_t*)( ana->data + ref_off ); + const u32 str_off = ref_off + be32(&sref->offset_strlist); + if ( str_off + 4 > data_size ) + goto invalid; + + pat_s0_shead_t *shead = (pat_s0_shead_t*)( ana->data + str_off ); + uint n_slist = be16(&shead->n_elem); + if ( str_off + 4 * (n_slist+1) > data_size ) + goto invalid; + + if ( i < PAT_MAX_ELEM ) + { + ana->n_s0_list[i] = n_slist; + ana->s0_sref[i] = sref; + ana->s0_shead[i] = shead; + } + } + } + + //--- 'sref' order + + const uint max = ana->n_sect0 < PAT_MAX_ELEM ? ana->n_sect0 : PAT_MAX_ELEM; + pat_s0_sref_t *last_sr = 0; + u8 *dest_sr = ana->s0_sref_order; + for(;;) + { + uint i; + pat_s0_sref_t *found_sr = 0, **sr = ana->s0_sref; + for ( i = 0; i < max; i++, sr++ ) + if ( *sr > last_sr && ( !found_sr || *sr < found_sr )) + found_sr = *sr; + if (!found_sr) + break; + + sr = ana->s0_sref; + for ( i = 0; i < max; i++, sr++ ) + if ( *sr == found_sr ) + *dest_sr++ = i; + last_sr = found_sr; + } + + + //--- 's0_shead' order + + pat_s0_shead_t *last_sh = 0; + u8 *dest_sh = ana->s0_shead_order; + for(;;) + { + uint i; + pat_s0_shead_t *found_sh = 0, **sh = ana->s0_shead; + for ( i = 0; i < max; i++, sh++ ) + if ( *sh > last_sh && ( !found_sh || *sh < found_sh )) + found_sh = *sh; + if (!found_sh) + break; + + sh = ana->s0_shead; + for ( i = 0; i < max; i++, sh++ ) + if ( *sh == found_sh ) + *dest_sh++ = i; + last_sh = found_sh; + } + + + //--- terminate + + PRINT("ORDER:\n"); + HEXDUMP16(10,0,ana->s0_sref_order,sizeof(ana->s0_sref_order)); + HEXDUMP16(10,0,ana->s0_shead_order,sizeof(ana->s0_shead_order)); + + DASSERT( PAT_MAX_ELEM >= 5 ); + PRINT("ANA(PAT): valid=%d,%d, N=%d,%d,[%d,%d,%d,%d,%d], " + " ptr=%zx,%zx,[%zx,%zx,%zx,%zx,%zx],[%zx,%zx,%zx,%zx,%zx],%zx,%zx\n", + ana->valid, ana->data_complete, + ana->n_sect0, ana->n_sect1, + + ana->n_s0_list[0], + ana->n_s0_list[1], + ana->n_s0_list[2], + ana->n_s0_list[3], + ana->n_s0_list[4], + + ana->head ? (u8*)ana->head - ana->data : 0, + ana->s0_base ? (u8*)ana->s0_base - ana->data : 0, + + ana->s0_sref[0] ? (u8*)ana->s0_sref[0] - ana->data : 0, + ana->s0_sref[1] ? (u8*)ana->s0_sref[1] - ana->data : 0, + ana->s0_sref[2] ? (u8*)ana->s0_sref[2] - ana->data : 0, + ana->s0_sref[3] ? (u8*)ana->s0_sref[3] - ana->data : 0, + ana->s0_sref[4] ? (u8*)ana->s0_sref[4] - ana->data : 0, + + ana->s0_shead[0]? (u8*)ana->s0_shead[0] - ana->data : 0, + ana->s0_shead[1]? (u8*)ana->s0_shead[1] - ana->data : 0, + ana->s0_shead[2]? (u8*)ana->s0_shead[2] - ana->data : 0, + ana->s0_shead[3]? (u8*)ana->s0_shead[3] - ana->data : 0, + ana->s0_shead[4]? (u8*)ana->s0_shead[4] - ana->data : 0, + + ana->s1_list ? (u8*)ana->s1_list - ana->data : 0, + ana->s3_list ? (u8*)ana->s3_list - ana->data : 0 ); + + return ana->valid; +} + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidSTATICR +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +) +{ + //--- First make a plausibility checks and return VALID_WRONG_FF on fail. + //--- This phase is done because of missing a magic + + if ( !data || data_size < 0x100 ) + return VALID_WRONG_FF; + + static const u8 identification[] = + { + /* 80 */ 0x00, 0x00, 0x78, 0xb0, 0x00, 0x00, 0x00, 0x00, + /* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* a0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* a8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* b0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* b8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* c0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* c8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* d0 */ 0x00, 0x00, 0x00, 0x00, 0x80, 0xa3, 0x00, 0x04, + /* d8 */ 0x80, 0x04, 0x00, 0x04, 0x7c, 0x65, 0x00, 0x50, + /* e0 */ 0x7c, 0x00, 0x28, 0x50, 0x7c, 0x60, 0x03, 0x78, + /* e8 */ 0x54, 0x03, 0x0f, 0xfe, 0x4e, 0x80, 0x00, 0x20, + /* f0 */ 0x94, 0x21, 0xff, 0xf0, 0x7c, 0x08, 0x02, 0xa6, + /* f8 */ 0x2c, 0x03, 0x00, 0x00, 0x90, 0x01, 0x00, 0x14, + }; + + if (memcmp(data+0x80,identification,sizeof(identification))) + return VALID_WRONG_FF; + + // ??? [[2do]] + return VALID_NO_TEST; // [[2do]] or VALID_OK? +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// xsrc/x-lib /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if HAVE_XSRC + #include "xsrc/x-std.c" +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-std.h b/project/src/lib-std.h new file mode 100644 index 0000000..28d1eaa --- /dev/null +++ b/project/src/lib-std.h @@ -0,0 +1,2234 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_LIB_STD_H +#define SZS_LIB_STD_H 1 + +#define _GNU_SOURCE 1 + +#include "version.h" +#include "dclib-types.h" + +#include +#include +#include +#include +#include +#include + +#ifndef WIN_SZS_LIB + #include + #include + #include +#endif + +#include "system.h" +#include "dclib-basics.h" +#include "dclib-file.h" +#include "dclib-debug.h" +#include "lib-dol.h" +#include "file-type.h" +#include "lib-numeric.h" + +#if HAVE_XSRC + #include "xsrc/x-std.h" +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// defines /////////////// +/////////////////////////////////////////////////////////////////////////////// +// also used by UI + +#define SZS_SETUP_FILE "wszst-setup.txt" +#define CHECK_FILE_SIZE 0x800 +#define OPT_PNG_TYPE_CLASS 0x7fef0bff + +#define DEF_EPSILON_POS 0.01 +#define DEF_EPSILON_ROT 0.01 +#define DEF_EPSILON_SCALE 0.01 +#define DEF_EPSILON_SIZE 0.01 +#define DEF_EPSILON_VEL 0.01 +#define DEF_EPSILON_TIME 0.01 + +#define M1(a) ( (typeof(a))~0 ) +#define IS_M1(a) ( (a) == (typeof(a))~0 ) + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupColors(); +void SetupLib ( int argc, char ** argv, ccp p_progname ); +void NormalizeOptions +( + uint log_level // 0: no log + // 1: print PROGRAM_NAME + // 2: + print SHARE_PATH + SEARCH_PATH + // 3: + print AUTOADD_PATH +); + +ccp GetCtLeInfo(); + +const KeywordTab_t * CheckCommandHelper + ( int argc, char ** argv, const KeywordTab_t * key_tab ); + +extern const char text_logo[]; +extern void (*print_title_func) ( FILE * f ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// error interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + ERR_CACHE_USED = ERU_SOURCE_FOUND, + ERR_ENCODING_WARN = ERR_SUBJOB_WARNING, + + ERR_INVALID_IFORM = ERU_ERROR1_01, + ERR_INVALID_FFORM = ERU_ERROR1_02, + + ERR_BZIP2 = ERU_ERROR2_01, + ERR_PNG = ERU_ERROR2_02, +}; + +//----------------------------------------------------------------------------- + +ccp LibGetErrorName ( int stat, ccp ret_not_found ); +ccp LibGetErrorText ( int stat, ccp ret_not_found ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// compatibility /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum compatible_t +{ + COMPAT_0, // legacy mode + COMPAT_1_23, // BMG colors => opt_bmg_colors = 0 + COMPAT_1_39, // BMG inline support => opt_bmg_inline_attrib = false + COMPAT_1_44, // new BMG escapes + attributes => opt_bmg_old_escapes = true + COMPAT_1_46, // KMP flags => opt_kmp_flags = false; + COMPAT_2_08, // BMG slots and sections => opt_bmg_use_* = false + COMPAT_CURRENT, // current version + + COMPAT__N // number of elements of 'compatible_info' +} +compatible_t; + +//----------------------------------------------------------------------------- + +typedef struct compatible_info_t +{ + compatible_t compatible; // selector + u32 revision; // revision number + char version[8]; // version string of format "1.23" of "1.23a" +} +compatible_info_t; + +//----------------------------------------------------------------------------- + +extern compatible_t compatible; // = COMPAT_CURRENT +extern const compatible_info_t compatible_info[COMPAT__N]; + +const compatible_info_t * ScanCompatible ( ccp arg ); +int ScanOptCompatible ( ccp arg ); +char * PrintOptCompatible(); // circ-buf + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// warn level /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[WarnLevel_t]] Same as CTW: WarnLevel + +typedef enum WarnLevel_t +{ + WLEVEL_UNKNOWN, // unknown + WLEVEL_NA, // not applicable + WLEVEL_OK, // ok + WLEVEL_FIXED, // fixed + WLEVEL_HINT, // hint + WLEVEL_WARN, // warning + WLEVEL_ERROR, // error + WLEVEL_FAIL, // failed + WLEVEL_FATAL, // fatal error + WLEVEL_INVALID, // invalid value + + WLEVEL__N +} +__attribute__ ((packed)) WarnLevel_t; + +extern const char WarnLevelNameLo[WLEVEL__N][8]; // lower case +extern const char WarnLevelNameUp[WLEVEL__N][8]; // upper case + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sub file system /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[SubFile_t]] + +typedef struct SubFile_t +{ + ccp fname; // filename + u8 *data; // pointer to data + uint size; // file size + uint type; // file type (user data) + + bool fname_alloced; // true: 'fname' is alloced + bool data_alloced; // true: 'data' is alloced +} +SubFile_t; + +//----------------------------------------------------------------------------- +// [[SubFileList_t]] + +typedef struct SubFileList_t +{ + SubFile_t **list; // file list + uint used; // used elements of 'list' + uint size; // allooced elements of 'list' +} +SubFileList_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[SubDirList_t]] + +struct SubDir_t; + +typedef struct SubDirList_t +{ + struct SubDir_t **list; // dir list + uint used; // used elements of 'list' + uint size; // allooced elements of 'list' +} +SubDirList_t; + +//----------------------------------------------------------------------------- +// use [[SubDir_t]] as top level of a file system + +typedef struct SubDir_t +{ + ccp dname; // directory name + bool dname_alloced; // true: 'dname' is alloced + SubDirList_t dir; // sub-directories + SubFileList_t file; // sub-files +} +SubDir_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeSubDirList ( SubDirList_t *s ) + { DASSERT(s); memset(s,0,sizeof(*s)); } +static inline void InitializeSubDir ( SubDir_t *s ) + { DASSERT(s); memset(s,0,sizeof(*s)); s->dname = EmptyString; } +static inline void InitializeSubFileList ( SubFileList_t *s ) + { DASSERT(s); memset(s,0,sizeof(*s)); } +static inline void InitializeSubFile ( SubFile_t *s ) + { DASSERT(s); memset(s,0,sizeof(*s)); } + +void ResetSubDirList ( SubDirList_t *sdl ); +void ResetSubDir ( SubDir_t *sd ); +void ResetSubFileList ( SubFileList_t *sfl ); +void ResetSubFile ( SubFile_t *sf ); + +void DumpSubDir ( FILE *f, int indent, const SubDir_t *sd ); +void DumpSubDirList ( FILE *f, int indent, const SubDirList_t *sdl ); +void DumpSubFileList ( FILE *f, int indent, const SubFileList_t *sfl ); + +/////////////////////////////////////////////////////////////////////////////// + +SubDir_t * FindSubDir ( const SubDir_t *sd, mem_t path ); +SubFile_t * FindSubFile ( const SubDir_t *sd, mem_t path ); + +SubDir_t * InsertSubDir ( SubDir_t *sd, mem_t path, bool *new_dir ); +SubFile_t * InsertSubFile ( SubDir_t *sd, mem_t path, bool *new_file ); + +bool RemoveSubDir ( SubDir_t *sd, mem_t path ); +bool RemoveSubFile ( SubDir_t *sd, mem_t path ); + +FILE * OpenSubFile ( const SubDir_t *sd, mem_t path ); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[SubFileFunc]] + +struct SubFileIterator_t; + +typedef int (*SubFileFunc) +( + struct + SubFileIterator_t *sfi, // iterator data + uint mode, // 0:file, 1:open dir, 2:close dir + SubDir_t *dir, // current directory + SubFile_t *file // NULL or current file +); + +/////////////////////////////////////////////////////////////////////////////// +// [[SubFileIterator_t]] + +typedef struct SubFileIterator_t +{ + SubFileFunc func; // function to call + int user_int; // user int + void *user_ptr; // user pointer + + uint depth; // dir depth + char *path_ptr; // pointer to NULL term of 'fullpath' + char path[2000]; // full path +} +SubFileIterator_t; + +/////////////////////////////////////////////////////////////////////////////// + +int IterateSubDir ( SubDir_t *sd, SubFileIterator_t *sfi ); +enumError SaveSubFiles ( SubDir_t *sd, ccp basepath ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// [[obsolete+]] old_container /////////////// +/////////////////////////////////////////////////////////////////////////////// +// old [[container]] will be replaced by Container_t & ContainerData_t +// [[DataContainer_t]] + +#ifdef TEST // [[container]] [[obsolete+]] + #define USE_NEW_CONTAINER_MDL 1 + #define USE_NEW_CONTAINER_PAT 1 + #define USE_NEW_CONTAINER_CTC 1 +#elif HAVE_WIIMM_EXT + #define USE_NEW_CONTAINER_MDL 1 + #define USE_NEW_CONTAINER_PAT 1 + #define USE_NEW_CONTAINER_CTC 1 +#else // activated in 2020-01 -> [[obsilete]] at mid 2020 + #define USE_NEW_CONTAINER_MDL 1 + #define USE_NEW_CONTAINER_PAT 1 + #define USE_NEW_CONTAINER_CTC 1 +#endif + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct DataContainer_t +{ + Container_t container; // low level container + + const u8 *data; // data + uint size; // size of 'data' + int ref_count; // reference counter + bool data_alloced; // true: free data + bool dc_alloced; // true: container itself is alloced + +} DataContainer_t; + +/////////////////////////////////////////////////////////////////////////////// + +#if USE_NEW_CONTAINER_MDL + typedef ContainerData_t ContainerMDL_t; +#else + typedef DataContainer_t ContainerMDL_t; +#endif + +#if USE_NEW_CONTAINER_PAT + typedef ContainerData_t ContainerPAT_t; +#else + typedef DataContainer_t ContainerPAT_t; +#endif + +#if USE_NEW_CONTAINER_CTC + typedef ContainerData_t ContainerCTC_t; +#else + typedef DataContainer_t ContainerCTC_t; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * UseSubContainer +( + DataContainer_t *dc, // old_container to reuse, if + // InDataContainer(dc,data) && ref:count == 1 + ContainerData_t *cdata_src // NULL or container data source +); + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * NewContainer +( + DataContainer_t *dc, // old_container to reuse, if + // InDataContainer(dc,data) && ref:count == 1 + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +); + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * DupContainer +( + DataContainer_t *dup_dc, // old_container to reuse, if + // InDataContainer(dc,data) && ref:count == 1 + // => complete dc->data is copied + const void *data, // data to copy/move/link + uint size, // size of 'data' + bool include_behind, // on creation: include 'dc->data' + // behind 'data+size' if possible + const void **new_data // not NULL: store new data pointer here +); + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * AllocContainer +( + // increments the reference counter and returns 'ct' + + DataContainer_t *dc // NULL or valid old_container +); + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * FreeContainer +( + // decrements the reference counter and returns 'NULL' + // if reference counter == 0: free data and/or ct + + DataContainer_t *dc // NULL or valid old_container +); + +/////////////////////////////////////////////////////////////////////////////// + +bool InDataContainer +( + // return true, if ptr is in old_container + + const DataContainer_t *dc, // NULL or valid old_container + const void *ptr // NULL or pointer to test +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int NintendoCmp ( ccp path1, ccp path2 ); + +void PrintNumF +( + FILE *f, // output stream + const u8 *data, // valid pointer to data + uint size, // size of 'data' + ccp type_str // NULL or type string +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// word compare functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint CreateHaystackListWCMP +( + char * buf, // valid pointer to output buffer + uint buf_size, // soue of 'buf' + ccp * source // NULL terminated list with source Strings +); + +uint CreateHaystackWCMP +( + char * buf, // valid pointer to output buffer + uint buf_size, // soue of 'buf' + ccp source // source String +); + +uint CreateNeedleWCMP +( + char * buf, // valid pointer to output buffer + uint buf_size, // soue of 'buf' + ccp source // NULL terminated list with source Strings +); + +bool FindWCMP +( + ccp haystack, // search in this string + ccp needle, // try to find this string + uint flags, // bit field: + // bit-0 set = 1: 'haystack' already in WCMP format + // bit-1 set = 2: 'needle' already in WCMP format + char * tempbuf, // NULL or fast buffer for temporary WCMP conversions + uint tempbuf_size // size of 'buf' +); + +/////////////////////////////////////////////////////////////////////////////// + +typedef struct search_filter_t +{ + ccp string; // alloced wcmp string + int mode; // user defined mode + +} search_filter_t; + +search_filter_t * AllocSearchFilter ( uint n ); +void FreeSearchFilter ( search_filter_t *f ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string substitutions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct SubstString_t +{ + char c1, c2; // up to 2 codes (lower+upper case) + bool allow_slash; // true: allow slash in replacement + ccp str; // pointer to string + +} SubstString_t; + +//----------------------------------------------------------------------------- + +int ScanEscapeChar ( ccp arg ); + +char * SubstString +( + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + SubstString_t * tab, // replacement table + ccp source, // source text + int * count, // not NULL: number of replacements + char escape_char // the escape character, usually '%' +); + +int SubstDest +( + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + ccp source, // source text + ccp dest, // destination path + ccp dest_fname, // if 'dest' has no file part + // then extract filename from here or source + ccp default_ext, // NULL or default extension (e.g. '.ext') + bool dest_is_dir // destination is a directory +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// file support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef CF_PALETTE // for windows + +typedef enum cmd_flags_t +{ + CF_IMAGE = 0x0001, // valid if scanning image type + CF_PALETTE = 0x0002, // img format has palette + CF_AUTO = 0x0004, // auto mode + +} cmd_flags_t; + +//----------------------------------------------------------------------------- + +#define PNG_MAGIC8_NUM 0x89504e470d0a1a0aull + +//----------------------------------------------------------------------------- + +ccp GetNameFF ( file_format_t ff1, file_format_t ff2 ); +ccp GetNameFFv ( file_format_t ff1, file_format_t ff2, int version ); +ccp GetExtFF ( file_format_t ff1, file_format_t ff2 ); +ccp GetBinExtFF ( file_format_t ff ); +ccp GetTextExtFF ( file_format_t ff ); +ccp GetMagicExtFF ( file_format_t ff1, file_format_t ff2 ); +ccp GetMagicStringFF ( file_format_t ff ); + +file_format_t GetBinFF ( file_format_t ff ); +file_format_t GetTextFF ( file_format_t ff ); +file_format_t GetByNameFF ( ccp name ); + +file_format_t GetByMagicFF +( + const void *data, // pointer to data + uint data_size, // size of data + uint file_size // NULL or total size of file +); + +const file_type_t * GetFileTypeByExt +( + ccp ext, // NULL or file extension with optional preceeding point + bool need_magic // filter only file types with magic +); + +const file_type_t * GetFileTypeBySubdir +( + ccp path, // NULL or file extension with optional preceeding point + bool need_magic // filter only file types with magic +); + +const file_type_t * GetFileTypeFF ( file_format_t ff ); + +ff_attrib_t GetAttribFF ( file_format_t ff ); +ff_attrib_t GetAttribByMagicFF +( + const void * data, // valid pointer to data + uint data_size, // size of 'data' + file_format_t *ff // not NULL: store file format here +); + +int GetVersionFF +( + // returns -1 for unknown, or the version number + + file_format_t fform, // valid firl format + const void *data, // data pointer + uint data_size, // size of available data + char *res_suffix // not NULL: return 1 char as suffix, or 0 +); + +int GetVersionByMagic +( + // returns -1 for unknown, or the version number + + const void *data, // data pointer + uint data_size, // size of available data + char *res_suffix // not NULL: return 1 char as suffix, or 0 +); + +bool IsBRSUB ( file_format_t ff ); +bool IsByMagicBRSUB ( const void * data, uint data_size ); + +bool IsArchiveFF ( file_format_t ff ); +bool IsTextFF ( file_format_t ff ); +bool IsGeoHitKartFF ( file_format_t ff ); +bool IsGeoHitObjFF ( file_format_t ff ); + +bool IsYazFF ( file_format_t ff ); +int GetYazVersionFF ( file_format_t ff ); +bool IsCompressedFF ( file_format_t ff ); +file_format_t GetYazFF ( file_format_t ff ); +file_format_t GetCompressedFF ( file_format_t ff ); + +void SetCompressionFF +( + file_format_t ff_arch, // if >0: set 'opt_fform' + file_format_t ff_compr // if >0: set 'fform_compr' + // and 'fform_compr_force' +); + +file_format_t IsImageFF +( + // returns the normalized FF for a valid image or NULL (=FF_UNKNOWN) + + file_format_t fform, // file format to check + bool allow_png // true: allow PNG +); + +file_format_t GetImageFF +( + file_format_t fform1, // if a valid image ff, use this one + file_format_t fform2, // if a valid image ff, use this one + ccp path, // not NULL and exists: use file format + file_format_t default_fform, // use this as default, if valid ff + bool allow_png, // true: allow PNG + file_format_t not_found // return this, if all other fails +); + +/////////////////////////////////////////////////////////////////////////////// + +#define BRSUB_MODE2(a,b) ( (a) << 24 | (b) << 5 ) +#define BRSUB_MODE3(a,b,c) ( (a) << 24 | (b) << 5 | (c) ) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError OpenFILE +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool ignore_no_file, // ignore if file doeas not exists + // and return warning ERR_NOT_EXISTS + bool ignore_limit // ignore opt_max_file_size +); + +enumError CreateFILE +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool testmode, // true: don't open + bool create_path, // create path if necessary + bool overwrite, // overwrite existing files without warning + bool remove_dest, // remove existing file before creating + bool update // don't create new files +); + +FileMode_t GetFileModeByOpt +( + int testmode, // test mode + ccp fname1, // NULL or dest filename + ccp fname2 // NULL or source filename +); + +enumError CreateFileOpt +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool testmode, // true: don't open + ccp srcfile // NULL or src file name + // if fname == srcfile: overwrite enabled +); + +enumError AppendFILE +( + File_t * f, // file structure + bool initialize, // true: initialize 'f' + ccp fname, // file to open + bool testmode, // true: don't open + bool create_path // create path if necessary +); + +/////////////////////////////////////////////////////////////////////////////// + +FILE * CreateAnalyzeFile(); +void CloseAnalyzeFile(); + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptChdir ( ccp arg ); + +char * SplitSubPath +( + char * buf, // destination buffer + size_t buf_size, // size of 'buf' + ccp path // source path, if NULL: use 'buf' +); + +enumError OpenReadFILE +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + bool silent, // suppress all error messages + u8 ** res_data, // store alloced data here + uint * res_size, // not NULL: store data size + ccp * res_fname, // not NULL: store alloced filename + FileAttrib_t * res_fatt // not NULL: store file attributes +); + +enumError LoadFILE +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + size_t skip, // skip num of bytes before reading + void * data, // destination buffer, size = 'size' + size_t size, // size to read + int silent, // 0: print all error messages + // 1: suppress file size warning + // 2: suppress all error messages + FileAttrib_t * fatt, // not NULL: store file attributes + bool fatt_max // true: store *max* values to 'fatt' +); + +enumError SaveFILE +( + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + bool overwrite, // true: force overwriting + const void * data, // data to write + uint data_size, // size of 'data' + FileAttrib_t * fatt // not NULL: set timestamps using this attribs +); + +uint IsolateFilename +( + // return length of found and copied string + + char * buf, // destination buffer + size_t bufsize, // size of 'buf' + ccp path, // path to analyze + file_format_t fform // not NULL: remove related file extension +); + +/////////////////////////////////////////////////////////////////////////////// + +bool SetupAutoAdd(); +bool DefineAutoAddPath ( ccp path ); +s64 FindAutoAdd ( ccp fname, ccp ext, char *buf, uint buf_size ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// repair magic /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[repair_mode_t]] + +typedef enum repair_mode_t +{ + REPMD_OFF, // repair is OFF + REPMD_ANALYZE, // repair is ON, but only for analysis + REPMD_REPAIR, // repair is ON & replace correct magic + + REPMD_F_FNAME_FIRST = 0x10, // flag: Use file extension first + + REPMD_R_FALLBACK = 0x20, // result: 'ff_fallback' used + REPMD_R_FNAME = 0x40, // result: file extension used + + REPMD_M_MODES = 0x07, // mask for modes + REPMD_M_FLAGS = 0x10, // mask for flags + REPMD_M_RESULTS = 0x60, // mask for flags +} +repair_mode_t; + +//----------------------------------------------------------------------------- + +extern repair_mode_t repair_magic; +extern repair_mode_t repair_magic_flags; + +int ScanOptRepairMagic ( ccp arg ); +static inline bool IsRepairMagic() { return repair_magic > REPMD_OFF; } + +/////////////////////////////////////////////////////////////////////////////// +// [[repair_ff_t]] + +typedef struct repair_ff_t +{ + repair_mode_t mode; // repair mode without flags + repair_mode_t flags; // result flags + file_format_t fform; // file format, original or repaired + char magic[8]; // magic, original or repaired + uint magic_len; // length of 'magic' + + char orig_magic[8]; // magig of original + char file_ext[8]; // relevant file extension +} +repair_ff_t; + +//----------------------------------------------------------------------------- + +repair_ff_t * ResetRepairMagic ( repair_ff_t * data, repair_ff_t * fallback ); +ccp GetRepairMagicInfo ( const repair_ff_t *rep ); + +//----------------------------------------------------------------------------- + +file_format_t AnalyseMagic +( + // returns rep->fform + + repair_ff_t *rep, // not NULL: result, will be initalized + const void *data, // pointer to data + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname, // filename to analyse if GetByMagicFF() fails + bool fname_first // analyse the filename first. +); + +//----------------------------------------------------------------------------- + +file_format_t RepairMagic +( + // returns rep->fform + + repair_ff_t *rep, // not NULL: result, will be initalized + void *data, // pointer to data, magic will be replaced + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname, // filename to analyse if GetByMagicFF() fails + bool fname_first // analyse the filename first. +); + +//----------------------------------------------------------------------------- + +file_format_t AnalyseMagicByOpt +( + // returns rep->fform + // mode & fname_first depends on --repair-magic, but is never REPMD_REPAIR + + repair_ff_t *rep, // not NULL: result, will be initalized + const void *data, // pointer to data + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname // filename to analyse if GetByMagicFF() fails +); + +//----------------------------------------------------------------------------- + +file_format_t RepairMagicByOpt +( + // returns rep->fform + // mode & fname_first depends on --repair-magic + + repair_ff_t *rep, // not NULL: result, will be initalized + void *data, // pointer to data, magic will be replaced + uint data_size, // size of data + uint file_size, // NULL or total size of file + file_format_t ff_fallback, // FF_UNKNOWN or fallback FF if GetByMagicFF() fails + ccp fname // filename to analyse if GetByMagicFF() fails +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// file class /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum patch_file_t +{ + PFILE_NO_FILENAME = 0x0001, // no filename or an empty filename specified + PFILE_UNKNOWN = 0x0002, // unknown (not supported) file type + + //--- the following values are always >PFILE_UNKNOWN + + PFILE_COURSE = 0x0010, // KMP file 'course.txt', unsure if KMP or KCL + PFILE_COURSE_KMP = 0x0020, // KMP file 'course.{kmp,kmp.txt,txt}' + PFILE_COURSE_KCL = 0x0040, // KCL file 'course.{kcl,kcl.txt,txt}' + PFILE_OTHER_KCL = 0x0080, // other KCL files + + PFILE_MODEL = 0x0100, // BRRES file 'course_[d_]model.brres' + PFILE_MINIMAP = 0x0200, // BRRES file 'map_model.brres' + PFILE_VRCORN = 0x0400, // BRRES file 'vrcorn_model.brres' + PFILE_OTHER_BRRES = 0x0800, // other BRRES files + + + //--- flags + + PFILE_F_DEFAULT = 0x1000, // flags for default settings + PFILE_F_LOG = 0x2000, // enable logging + PFILE_F_LOG_ALL = 0x4000, // enable logging -> inherit by KCP+KMP+MDL + PFILE_F_HIDE = 0x8000, // help flag to hide modes in PrintFileClassInfo() + + + //--- masks + + PFILE_M_NONE = PFILE_UNKNOWN + | PFILE_NO_FILENAME, + + PFILE_M_KCL = PFILE_COURSE_KCL + | PFILE_OTHER_KCL, + + PFILE_M_BRRES = PFILE_MODEL + | PFILE_MINIMAP + | PFILE_VRCORN + | PFILE_OTHER_BRRES, + + PFILE_M_TRACK = PFILE_COURSE_KMP // mask for 'TRACK' keyword + | PFILE_COURSE_KCL + | PFILE_MODEL + | PFILE_MINIMAP + | PFILE_VRCORN, + + PFILE_M_ALL = PFILE_M_NONE + | PFILE_COURSE + | PFILE_COURSE_KMP + | PFILE_M_KCL + | PFILE_M_BRRES + | PFILE_F_LOG + | PFILE_F_LOG_ALL, + // not PFILE_F_HIDE + + PFILE_M_SILENT = PFILE_M_TRACK, + + PFILE_M_DEFAULT = PFILE_M_SILENT + | PFILE_F_LOG, + +} patch_file_t; + +//----------------------------------------------------------------------------- + +extern patch_file_t PATCH_FILE_MODE; // = PFILE_M_DEFAULT|PFILE_F_DEFAULT +extern int disable_patch_on_load; // if >0: disable patching after loading + +int ScanOptPatchFile ( ccp arg ); +void SetPatchFileModeReadonly(); // switch to readonly default flags (silent), + // if not set by user + +uint PrintFileClassInfo ( char *buf, uint bufsize, patch_file_t mode ); +ccp GetFileClassInfo(); + +patch_file_t GetFileClass +( + file_format_t fform, // not FF_UNKNOWN: check file type + ccp fname // NULL or file name to check +); + +bool PatchFileClass +( + file_format_t fform, // not FF_UNKNOWN: check file type + ccp fname // NULL or file name to check +); + +extern int patch_action_log_disabled; +bool PATCH_ACTION_LOG ( ccp action, ccp object, ccp format, ... ) + __attribute__ ((__format__(__printf__,3,4))); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// image formats /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[image_format_t]] + +typedef enum image_format_t +{ + //--- nintendo formats + + IMG_I4 = 0x00, + IMG_I8 = 0x01, + IMG_IA4 = 0x02, + IMG_IA8 = 0x03, + IMG_RGB565 = 0x04, + IMG_RGB5A3 = 0x05, + IMG_RGBA32 = 0x06, + IMG_C4 = 0x08, + IMG_C8 = 0x09, + IMG_C14X2 = 0x0a, + IMG_CMPR = 0x0e, + + IMG_N_INTERN, // number intern formats + + + //--- non nintendo formats + + IMG_X_AUTO = 0x7c00, // auto format (one of below) + + IMG_X_GRAY, // 2 bytes: gray with alpha + IMG_X_RGB, // 4 bytes: RGB with alpha + + // palette formats: 2 bytes index into 4 bytes palette + IMG_X_PAL4, // 4 bit index (max 16 palette entries) + IMG_X_PAL8, // 8 bit index (max 256 palette entries) + IMG_X_PAL14, // 14 bit index (max 16386 palette entries) + IMG_X_PAL, // one of above + + + //--- valid X formats as range + + IMG_X__MIN = IMG_X_GRAY, + IMG_X__MAX = IMG_X_PAL, + + IMG_X_PAL__MIN = IMG_X_PAL4, + IMG_X_PAL__MAX = IMG_X_PAL, + + + //--- special values + + IMG_INVALID = -1, // no image + +} image_format_t; + +//----------------------------------------------------------------------------- + +extern const KeywordTab_t cmdtab_image_format[]; +int ScanImageFormat ( ccp arg ); +ccp GetImageFormatName ( image_format_t iform, ccp unknown_value ); + +#define MAX_MIPMAPS 20 + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// palette formats /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[palette_format_t]] + +typedef enum palette_format_t +{ + PAL_IA8 = 0x00, + PAL_RGB565 = 0x01, + PAL_RGB5A3 = 0x02, + + PAL_X_RGB = 0x7c01, + + PAL_N, // number of formats + PAL_AUTO = PAL_N, // auto format + PAL_DEFAULT = PAL_RGB5A3, // default value + PAL_INVALID = -1, // no image + +} palette_format_t; + +//----------------------------------------------------------------------------- + +extern const KeywordTab_t cmdtab_palette_format[]; +int ScanPaletteFormat ( ccp arg ); +ccp GetPaletteFormatName ( image_format_t iform, ccp unknown_value ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sort mode /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[SortMode_t]] + +typedef enum SortMode_t +{ + SORT_NONE, // do not sort + + SORT_INAME, // sort with strcasecmp() + SORT_OFFSET, // sort by offset and size + + SORT_U8, // sort like Nintendo sort U8 archives + SORT_PACK, // sort like Nintendo sort U8 archives (inexact) + SORT_BRRES, // sort like Nintendo sort BRRES archives + SORT_BREFF, // sort like Nintendo sort BREFF and BREFT archives + + SORT_AUTO // auto sorting, using 'fform_arch' + +} SortMode_t; + +//----------------------------------------------------------------------------- + +SortMode_t GetSortMode +( + SortMode_t sort_mode, // wanted sort mode + file_format_t fform, // use it if 'sort_mode==SORT_AUTO' + SortMode_t default_mode // default if all other fails +); + +//----------------------------------------------------------------------------- + +ccp GetSortName ( int id, ccp res_not_found ); +int ScanOptSort ( ccp arg ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// slot mode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum SlotMode_t +{ + //--- no flag set + + SLOTMD_NONE = 0, + + + //--- jobs + + SLOTMD_RM_ICE = 0x0001, // rm ice.brres from SZS and KMP + SLOTMD_RM_SUNDS = 0x0002, // rm 'sunDS' and 'pylon01' from SZS and KMP + SLOTMD_RM_SHYGUY = 0x0004, // rm 'HeyhoShipGBA'+'HeyhoBallGBA' from SZS and KMP + + SLOTMD_ADD_GICE = 0x0010, // add 'ice' to KMP/GOBJ + SLOTMD_ADD_BICE = 0x0020, // add 'ice.brres' to SZS + SLOTMD_ADD_SHYGUY = 0x0040, // add 'HeyhoShipGBA' to SZS and KMP + + SLOTMD_ICE_TO_WATER = 0x0100, // change KCL flag 0x70 to 0x30 + SLOTMD_WATER_TO_ICE = 0x0200, // change KCL flag 0x30 to 0x70 + + + //--- job classes + + SLOTMD_JOB_RM_SZS = SLOTMD_RM_ICE + | SLOTMD_RM_SUNDS + | SLOTMD_RM_SHYGUY, + + SLOTMD_JOB_ADD_SZS = SLOTMD_ADD_BICE + | SLOTMD_ADD_SHYGUY, + + SLOTMD_JOB_KCL = SLOTMD_ICE_TO_WATER + | SLOTMD_WATER_TO_ICE, + + SLOTMD_JOB_KMP = SLOTMD_RM_ICE + | SLOTMD_RM_SUNDS + | SLOTMD_RM_SHYGUY + | SLOTMD_ADD_GICE + | SLOTMD_ADD_SHYGUY, + + + //--- mode for --slot + + SLOTMD_M_DAISY = SLOTMD_RM_ICE + | SLOTMD_RM_SHYGUY + | SLOTMD_ICE_TO_WATER, + + SLOTMD_M_SHERBET = SLOTMD_RM_SUNDS + | SLOTMD_RM_SHYGUY + | SLOTMD_ADD_GICE + | SLOTMD_ADD_BICE + | SLOTMD_WATER_TO_ICE, + + SLOTMD_M_SHYGUY = SLOTMD_RM_ICE + | SLOTMD_RM_SUNDS + | SLOTMD_ADD_SHYGUY + | SLOTMD_ICE_TO_WATER, + + SLOTMD_M_STANDARD = SLOTMD_RM_ICE + | SLOTMD_RM_SUNDS + | SLOTMD_RM_SHYGUY + | SLOTMD_ICE_TO_WATER, + + SLOTMD_M_MOST = SLOTMD_RM_SUNDS + | SLOTMD_RM_SHYGUY + | SLOTMD_ADD_BICE + | SLOTMD_ICE_TO_WATER, +} +SlotMode_t; + +extern SlotMode_t opt_slot; +int ScanOptSlot ( ccp arg ); +ccp PrintSlotMode ( SlotMode_t mode ); + +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchImage_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[PatchImage_t]] + +typedef enum PatchImage_t +{ + PIM_VCENTER = 0x0000, + PIM_TOP = 0x0001, + PIM_BOTTOM = 0x0002, + PIM_INS_TOP = 0x0003, + PIM_INS_BOTTOM = 0x0004, + PIM_M_V = 0x0007, + + PIM_HCENTER = 0x0000, + PIM_LEFT = 0x0010, + PIM_RIGHT = 0x0020, + PIM_INS_LEFT = 0x0030, + PIM_INS_RIGHT = 0x0040, + PIM_M_H = 0x0070, + PIM_SHIFT_V_H = 4, // shift value from vertical to horizontal values + + PIM_CENTER = PIM_VCENTER | PIM_HCENTER, + + PIM_MIX = 0x0000, + PIM_FOREGROUND = 0x0100, + PIM_BACKGROUND = 0x0200, + PIM_COPY = 0x0300, + PIM_M_COPY = 0x0300, + + PIM_LEAVE = 0x0000, + PIM_SHRINK = 0x1000, + PIM_GROW = 0x2000, + PIM_M_SIZE = 0x3000, + + PIM_MASK = PIM_M_V | PIM_M_H | PIM_M_COPY | PIM_M_SIZE + +} PatchImage_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct FormatField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct FormatFieldItem_t +{ + ccp key; // string key of object + file_format_t fform; // -1 or file format + image_format_t iform; // -1 or image format + palette_format_t pform; // -1 or palette format + PatchImage_t patch_mode; // 0 or alignment + uint num; // free number, printed if >0 + +} FormatFieldItem_t; + +//----------------------------------------------------------------------------- + +typedef struct FormatField_t +{ + FormatFieldItem_t * list; // pointer to the item list + uint used; // number of used elements of 'list' + uint size; // number of allocated elements of 'list' + +} FormatField_t; + +//----------------------------------------------------------------------------- + +void InitializeFormatField ( FormatField_t * ff ); +void ResetFormatField ( FormatField_t * ff ); +void MoveFormatField ( FormatField_t * dest, FormatField_t * src ); + +int FindFormatFieldIndex ( const FormatField_t * ff, ccp key, int not_found_value ); +FormatFieldItem_t * FindFormatField ( const FormatField_t * ff, ccp key ); + +// return first matching element +FormatFieldItem_t * FindFormatFieldNC ( const FormatField_t * ff, ccp key ); +FormatFieldItem_t * MatchFormatField ( const FormatField_t * ff, ccp key ); + +bool RemoveFormatField ( FormatField_t * ff, ccp key ); // return: true if item deleted + +FormatFieldItem_t * InsertFormatField +( + FormatField_t * ff, // valid attrib field + ccp key, // key to insert + bool scan_form, // true: scan 'key' for FORM= prefix + bool move_key, // true: move string, false: strdup() + bool * found // not null: store found status +); + +FormatFieldItem_t * InsertFormatFieldFF +( + FormatField_t * ff, // valid attrib field + ccp key, // key to insert + file_format_t fform, // file format + bool scan_form, // true: scan 'key' for FORM= prefix + bool move_key, // true: move string, false: strdup() + bool * found // not null: store found status +); + +FormatFieldItem_t * AppendFormatField +( + FormatField_t * ff, // valid attrib field + ccp key, // key to insert + bool scan_form, // true: scan 'key' for FORM= prefix + bool move_key // true: move string, false: strdup() +); + +#if 0 // [[not-needed]] not needed yet 2011-06 +enumError LoadFormatField +( + FormatField_t * ff, // attrib field + bool init_ff, // true: initialize 'ff' first + bool sort, // true: sort 'ff' + ccp filename, // filename of source file + bool silent // true: don't print open/read errors +); +#endif + +enumError SaveFormatField +( + FormatField_t * ff, // valid attrib field + ccp filename, // filename of dest file + bool rm_if_empty // true: rm dest file if 'ff' is empty +); + +enumError WriteFormatField +( + FILE * f, // open destination file + ccp filename, // NULL or filename (needed on write error) + FormatField_t * ff, // valid format field + ccp line_prefix, // not NULL: insert prefix before each line + ccp key_prefix, // not NULL: insert prefix before each key + ccp eol, // end of line text (if NULL: use LF) + uint hex_fw // if >0: print num as hex with field width +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// tiny support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[tiny_param_t]] + +#define N_TINY_MODES 8 + +typedef struct tiny_param_t +{ + // HINT: Value 0: function is off! + + //--- KCL + int kcl_round; // positions: shift value for RoundF()/RoundD() + int kcl_mask; // normals: value for MaskF() and normals + // also condition for 'kcl_round' + + //--- position calculations, 999==Off + int pos_round; // positions: shift value for RoundF()/RoundD() + int rot_round; // rotations: shift value for RoundF()/RoundD() + int scale_round; // scales: shift value for RoundF()/RoundD() + + int route_round; // route positions: shift value for RoundF()/RoundD() + int cannon_round; // cannon direction: shift value for RoundF()/RoundD() +} +tiny_param_t; + +extern const tiny_param_t TinyParam[N_TINY_MODES]; + +//----------------------------------------------------------------------------- + +int ScanOptTiny ( ccp arg ); + +static inline float RoundF( float num, int shift ) + { return ldexpf(roundf(ldexpf(num,shift)),-shift); } + +static inline double RoundD( double num, int shift ) + { return ldexp(round(ldexp(num,shift)),-shift); } + +static inline float MaskF( float num, u32 mask ) + { union { float f; u32 u; } x; x.f = num; x.u &= mask; return x.f; } + +static inline float RoundMaskF( float num, int shift, u32 mask ) +{ + union { float f; u32 u; } x; + x.f = ldexpf(roundf(ldexpf(num,shift)),-shift); + x.u &= mask; + return x.f; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string lists /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct ParamList_t +{ + char * arg; + bool is_expanded; + int count; + struct ParamList_t * next; + +} ParamList_t; + +extern uint n_param; +extern ParamList_t * first_param; +extern ParamList_t ** append_param; + +/////////////////////////////////////////////////////////////////////////////// + +int AtFileHelper ( ccp arg, int (*func)(ccp arg) ); + +int AddParam ( ccp arg ); +void AtExpandParam ( ParamList_t ** param ); +void AtExpandAllParam ( ParamList_t ** first_param ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// general list support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[List_t]] + +typedef struct List_t +{ + u8 *list; // list of data + uint used; // used elements of 'list' + uint size; // alloced elements of 'list' + uint elem_size; // size of 1 element + +} List_t; + +void InitializeList ( List_t * list, uint elem_size ); +void ResetList ( List_t * list ); +void * InsertList ( List_t * list, int index ); +void * AppendList ( List_t * list ); +void * GrowList ( List_t * list, uint n ); +void * GrowListSize ( List_t * list, uint n, uint add_size ); +void * GetListElem + ( const List_t * list, int index, const void * return_not_found ); + +/////////////////////////////////////////////////////////////////////////////// +// [[MemItem_t]] + +#define NAME_IDX_SIZE 10 + +typedef struct MemItem_t +{ + int idx1; // first user index + int idx2; // second user index + int sort_idx; // a sorting index + ccp name; // NULL or pointer to name + u8 *data; // NULL or pointer to data + uint size; // size of 'data' + + //--- string index support + + u32 str_idx_buf[NAME_IDX_SIZE]; + // standard buffer for 'str_idx' + u32 *str_idx; // NULL or pointer to alloced string index list + uint str_idx_size; // number of alloced elements of 'str_idx' + + //--- flags + + bool name_alloced; // true: 'name' is alloced + bool data_alloced; // true: 'data' is alloced + +} MemItem_t; + +//----------------------------------------------------------------------------- + +void InitializeMIL ( List_t * list ); +void ResetMIL ( List_t * list ); +void SortMIL ( List_t * list, bool use_sort_idx ); + +MemItem_t * AppendMIL ( List_t * list ); +MemItem_t * GrowMIL ( List_t * list, uint n ); +MemItem_t * GetMemListElem + ( const List_t * list, int index, const MemItem_t * return_not_found ); + +u32 * GetStrIdxMIL ( MemItem_t * mi, uint n_need ); + +static inline const u32 * GetStrIdxListMIL ( const MemItem_t * mi ) +{ + DASSERT(mi); + return mi->str_idx ? mi->str_idx : mi->str_idx_buf; +} + +static inline uint GetStrIdxCountMIL ( const MemItem_t * mi ) +{ + DASSERT(mi); + return mi->str_idx_size ? mi->str_idx_size + : sizeof(mi->str_idx_buf)/sizeof(*mi->str_idx_buf); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cygwin support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __CYGWIN__ + + void NormalizeFilenameParam ( ParamList_t *param ); + #define NORMALIZE_FILENAME_PARAM(p) NormalizeFilenameParam(p) + +#else + + #define NORMALIZE_FILENAME_PARAM(p) + +#endif // __CYGWIN__ + +//----------------------------------------------------------------------------- + +void SetSource ( ccp source ); +void SetDest ( ccp dest, bool mkdir ); +void CheckOptDest ( ccp default_dest, bool default_mkdir ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// modes and options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptCompr ( ccp arg ); +int ScanOptTracks ( ccp arg ); +int ScanOptArenas ( ccp arg ); +ccp NormalizeTrackName ( char *buf, uint bufsize, ccp arg ); + +void DumpTrackList +( + FILE * f, // NULL(=stdout) or file + const u32 * tab, // NULL or list with track indices + int brief_mode // 0: full table + // 1: suppress header + // 2: file names only +); + +void DumpArenaList +( + FILE * f, // NULL(=stdout) or file + const u32 * tab, // NULL or list with arena indices + int brief_mode // 0: full table + // 1: suppress header + // 2: file names only +); + +void DumpTrackFileList +( + FILE * f, // NULL(=stdout) or file + const u32 * track_tab, // NULL or list with track indices + const u32 * arena_tab // NULL or list with arena indices +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan setup files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct SetupDef_t +{ + ccp name; // name of parameter, NULL=list terminator + u32 factor; // alignment factor; 0: text param + ccp param; // alloced text param + u64 value; // numeric value of param + +} SetupDef_t; + +//----------------------------------------------------------------------------- + +typedef struct ListDef_t +{ + ccp name; // section name + bool append; // true: append string instead of insert + StringField_t *sf; // valid pointer to string field + +} ListDef_t; + +//----------------------------------------------------------------------------- + +size_t ResetSetup +( + SetupDef_t * list // object list terminated with an element 'name=NULL' +); + +enumError ScanSetupFile +( + SetupDef_t * sdef, // object list terminated with an element 'name=NULL' + ListDef_t * ldef, // NULL or object list terminated + // with an element 'name=NULL' + ccp path1, // filename of text file, part 1 + ccp path2, // filename of text file, part 2 + SubDir_t * sdir, // not NULL: use it and ignore 'path1' + bool silent // true: suppress error message if file not found +); + +/////////////////////////////////////////////////////////////////////////////// +// [[SetupParam_t]] + +typedef struct SetupParam_t +{ + ccp object_name; // name of object, alloced + u32 object_id; // id of object + bool object_id_used; // 'object_id' set and valid + file_format_t fform_file; // file format + file_format_t fform_arch; // archive format + int version; // a version number + int compr_mode; // -1:no compr, 0:auto, 1:compr + int have_pt_dir; // -1:no, 0:unknown, 1:yes + u32 min_data_off; // minimum found data offset + u32 max_data_off; // maximum found data offset + u32 data_align; // data alignment + + StringField_t include_pattern;// include usually hidden files, pattern list + StringField_t include_name; // include usually hidden files, name list + StringField_t exclude_pattern;// exclude files, pattern list + StringField_t exclude_name; // exclude files, name list + FormatField_t encode_list; // encode before including + StringField_t create_list; // create sub file before including + FormatField_t order_list; // use this list as sorting reference + +} SetupParam_t; + +//----------------------------------------------------------------------------- + +void InitializeSetupParam ( SetupParam_t * sp ); +void ResetSetupParam ( SetupParam_t * sp ); + +//----------------------------------------------------------------------------- + +enumError ScanSetupParam +( + SetupParam_t * sp, // setup param to scan + bool use_options, // true: use options to override values + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + SubDir_t * sdir, // not NULL: use it and ignore 'path1' + bool silent // true: suppress printing of error messages +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// export keys /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum export_mode_t +{ + EXPORT_NONE, + EXPORT_C, + EXPORT_PHP, + EXPORT_MAKEDOC, + EXPORT_CMD_MASK = 0x00f, + + EXPORT_F_FUNCTIONS = 0x100, + EXPORT_F_FILEATTRIB = 0x200, + EXPORT_F_TRACKS = 0x400, + EXPORT_FLAG_MASK = 0x700 + +} export_mode_t; + +/////////////////////////////////////////////////////////////////////////////// + +export_mode_t ScanExportMode(); // scan parameters + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SZS file list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum FileList_t +{ + FLT_STD = 0x01, // non '_d' files + FLT_D = 0x02, // '_d' files + + FLT_TRACK = 0x10, // 32 track files + FLT_ARENA = 0x20, // 10 arena files + FLT_OTHER = 0x40, // 5 other files + FLT_OLD = 0x80, // 4 old files (old BRSUB formats) + + FLT_M_CLASS = FLT_STD | FLT_D, + FLT_M_TYPE = FLT_TRACK | FLT_ARENA | FLT_OTHER | FLT_OLD, + FLT_M_ALL = FLT_M_CLASS | FLT_M_TYPE, + +} FileList_t; + +//----------------------------------------------------------------------------- + +typedef struct TrackInfo_t TrackInfo_t; + +typedef int (*IterateTrackFilesFunc) +( + ccp fname, // name of current file without '.szs' extension + ccp id, // unique ID of current file + FileList_t mode, // mode of current file + const TrackInfo_t *ti, // NULL or related track info + void *param // user defined parameter +); + +//----------------------------------------------------------------------------- + +int IterateTrackFiles +( + FileList_t mode, // bit field with requested files + IterateTrackFilesFunc + func, // call back function + void *param // user defined parameter +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// file type validation /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[valid_t]] + +typedef enum valid_t +{ + VALID_OK, // validation success + VALID_NO_TEST, // no validation test available + VALID_WARNING, // warnings, repairable + VALID_ERROR, // error in data structures + VALID_WRONG_FF, // wrong file type + VALID_UNKNOWN_FF, // unknown file type + + VALID__N // number of status modes + +} valid_t; + +extern ccp valid_text[VALID__N]; + +/////////////////////////////////////////////////////////////////////////////// + +struct szs_file_t; + +valid_t IsValidSZS +( + struct szs_file_t *szs, // valid szs archive + bool warnings // true: print warnings +); + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValid +( + const void *data, // valid data to test + uint data_size, // size of 'data' + uint file_size, // not NULL: size of complete file + struct szs_file_t *szs, // not NULL: base archive + file_format_t fform, // known file format + // if FF_UNKNOWN: call GetByMagicFF() + ccp fname // not NULL: print warnings with file ref +); + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidBRSUB +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + struct szs_file_t *szs, // not NULL: base archive + file_format_t fform, // known file format + // if FF_UNKNOWN: call GetByMagicFF() + bool check_fform, // true: confirm 'fform' by calling GetByMagicFF() + ccp fname, // not NULL: print warnings with file ref + const endian_func_t *endian // not NULL: use this endian functions +); + +/////////////////////////////////////////////////////////////////////////////// + +valid_t IsValidBTI +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +); + +//----------------------------------------------------------------------------- + +struct kcl_analyze_t; + +valid_t IsValidKCL +( + struct + kcl_analyze_t * ka, // not NULL: init and store stats + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +); + +//----------------------------------------------------------------------------- + +valid_t IsValidKMP +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +); + +//----------------------------------------------------------------------------- + +valid_t IsValidLEX +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +); + +//----------------------------------------------------------------------------- + +valid_t IsValidMDL +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + struct szs_file_t *szs, // not NULL: base archive + ccp fname, // not NULL: print warnings with file ref + const endian_func_t *endian // not NULL: use this endian functions +); + +//----------------------------------------------------------------------------- + +struct pat_analyse_t; + +valid_t IsValidPAT +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + struct szs_file_t *szs, // not NULL: base archive + ccp fname, // not NULL: print warnings with file ref + const endian_func_t *endian, // not NULL: use this endian functions + struct pat_analyse_t *ana // not NULL: store analysis data here +); + +//----------------------------------------------------------------------------- + +valid_t IsValidSTATICR +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +); + +//----------------------------------------------------------------------------- + +valid_t IsValidTEXCT +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + struct szs_file_t *szs, // not NULL: base archive + ccp fname // not NULL: print warnings with file ref +); + +//----------------------------------------------------------------------------- + +valid_t IsValidCTCODE +( + const void * data, // data + uint data_size, // size of 'data' + uint file_size, // NULL or size of complete file + ccp fname // not NULL: print warnings with file ref +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PrintScript /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[PrintScript_t]] + +typedef struct PrintScript_t +{ + File_t *fo; // pointer output file + bool add_index; // true: add index to varname + uint index; // index for array operations + char sep[2]; // separator for JSON + char prefix[100]; // varname prefix +} +PrintScript_t; + +static inline void InitializePrintScript ( PrintScript_t *ps ) + { DASSERT(ps); memset(ps,0,sizeof(*ps)); } + +static inline void ResetPrintScript ( PrintScript_t *ps ) + { DASSERT(ps); memset(ps,0,sizeof(*ps)); } + +/////////////////////////////////////////////////////////////////////////////// + +void PrintScriptHeader ( PrintScript_t *ps ); +void PrintScriptFooter ( PrintScript_t *ps ); + +int PutScriptVars +( + PrintScript_t *ps, // valid control struct + uint mode, // bit field: 1=open var, 2:close var + ccp text // text with line of format NAME=VALUE +); + +int PrintScriptVars +( + PrintScript_t *ps, // valid control struct + uint mode, // bit field: 1=open var, 2:close var + ccp format, // format of message + ... // arguments +) +__attribute__ ((__format__(__printf__,3,4))); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[IsArena_t]] + +typedef enum IsArena_t +{ + ARENA_NONE = 0, // no arena found + ARENA_MAYBE, // maybe an arena (some hints detected) + ARENA_FOUND, // arena detected + ARENA_DISPATCH, // arena detected with ENPT dispatch points + ARENA__N +} +IsArena_t; + +extern const char is_arena_name[ARENA__N][9]; + +/////////////////////////////////////////////////////////////////////////////// +// [[data_tab_t]] + +typedef struct data_tab_t +{ + ccp name; // name of data + uint mode; // bit filed: + // 1: source is bzip'ed + // 2: base data is bzip'ed + + const void *data; // valid pointer to data + uint size; // size of 'data' + + const u32 *patch; // not NULL: use this patch to get real data +} +data_tab_t; + +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_argtest ( int argc, char ** argv ); +enumError cmd_error(); +enumError cmd_filetype(); +enumError cmd_fileattrib(); +enumError cmd_float(); +enumError cmd_vr_calc(); +enumError cmd_vr_race(); +enumError cmd_rawdump ( const data_tab_t *data_tab ); + +void ExportFileAttribMakedoc ( FILE * f ); + +extern uint opt_analyze_mode; +int ScanOptAnalyzeMode ( ccp arg ); + +struct szs_file_t; +void AnalyzeBRSUB ( struct szs_file_t * szs, u8 * data, uint data_size, ccp name ); +void AnalyzeBREFF ( struct szs_file_t * szs, u8 * data, uint data_size, ccp name ); +void AnalyzeTPL ( struct szs_file_t * szs, u8 * data, uint data_size, ccp name ); + +struct kmp_t; +void AnalyzeKMP ( const struct kmp_t * kmp, ccp base_fname ); + +struct kcl_t; +void AnalyzeKCL ( const struct kcl_t * kcl ); + +void SHA1_to_ID ( sha1_id_t id, const sha1_hash_t hash ); + +/////////////////////////////////////////////////////////////////////////////// + +bool IsExtractEnabled(); // return true if enabled and mkdir() successful + +bool PrepareExtract +( + // return FALSE on disabled or failure, otherwise return TRUE + + File_t *f, // file structure, will be initialized + int mode, // convert filename: <0=to lower, >0=to upper + ccp format, // format of filename + ... // arguments +) +__attribute__ ((__format__(__printf__,3,4))); + +/////////////////////////////////////////////////////////////////////////////// +// [[CheckMode_t]] + +typedef enum CheckMode_t +{ + CMOD_HEADER = 0x01, // print header line before first message + CMOD_FOOTER = 0x02, // print footer line (summary) behind last message + CMOD_FORCE_HEADER = 0x04, // print header line always + CMOD_FORCE_FOOTER = 0x08, // print footer line always + + CMOD_SILENT = 0, // print nothing + CMOD_WARNING = 0x10, // print warnings + CMOD_HINT = 0x20, // print hints + CMOD_SLOT = 0x40, // print slot infos + CMOD_INFO = 0x80, // simple info or statistic + CMOD_VERBOSE = 0x100, // be verbose + CMOD_LECODE = 0x200, // only relevant for LE-CODE + + CMOD_GLOBAL_STAT = 0x100, // store global statistics + + CMOD_PRINT = CMOD_WARNING | CMOD_HINT | CMOD_SLOT, + CMOD_DEFAULT = CMOD_HEADER | CMOD_FOOTER | CMOD_PRINT, + CMOD_FORCE = CMOD_FORCE_HEADER | CMOD_FORCE_FOOTER, + +} CheckMode_t; + +//----------------------------------------------------------------------------- + +CheckMode_t GetCheckMode +( + bool no_head, // true: suppress header + bool no_hints, // true: suppress hints + bool quiet, // true: be quiet if no hint|warning is printed + bool verbose // true: print some more hints +); + +CheckMode_t GetMainCheckMode ( CheckMode_t mode, bool reset_counter ); +CheckMode_t GetSubCheckMode ( CheckMode_t mode, bool reset_counter ); + +extern const char check_bowl[]; // begin of 'warning' line, for checks +extern const char check_bohl[]; // begin of 'hint' line, for checks +extern const char check_bosl[]; // begin of 'slot' line, for checks +extern const char check_boil[]; // begin of 'info' line, for checks +extern const char check_bosugl[]; // begin of 'suggestion' line, for checks + +extern int global_warn_count; +extern int global_hint_count; +extern int global_info_count; + +extern const ColorSet_t *colset; + +/////////////////////////////////////////////////////////////////////////////// + +extern char *TableDecode64BySetup; +extern const char *TableEncode64BySetup; + +int ScanOptCoding64 ( ccp arg ); +EncodeMode_t SetupCoding64 ( EncodeMode_t mode, EncodeMode_t fallback ); + +/////////////////////////////////////////////////////////////////////////////// + +uint GetMkwMusicSlot ( uint tid ); + +//----------------------------------------------------------------------------- + +static inline ccp PrintU8M1 ( u8 num ) +{ + return num == M1(num) ? "-1" : PrintCircBuf("%u",num); +} + +static inline ccp PrintU16M1 ( u16 num ) +{ + return num == M1(num) ? "-1" : PrintCircBuf("%u",num); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// output_mode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[output_mode_t]] + +typedef struct output_mode_t +{ + bool syntax; // true: print syntax infos + u8 mode; // 1:list, 2:ref, 3:full + bool cross_ref; // true: print cross reference infos + bool hex; // force hex out +} +output_mode_t; + +extern output_mode_t output_mode; +void InitializeOutputMode ( output_mode_t * outmode ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// apple only /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __APPLE__ + + void * memmem ( const void *l, size_t l_len, const void *s, size_t s_len ); + +#endif // __APPLE__ + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// tables /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const u8 CannonDataLEX[4+3*16]; +extern const u8 TestDataLEX[8*1]; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern ccp tool_name; +extern ccp progname; +extern ccp share_path; +extern ccp search_path[]; +extern ccp auto_add_path[6]; +extern volatile int SIGINT_level; +extern volatile int verbose; +extern volatile int logging; +extern bool allow_all; +extern char escape_char; +extern bool use_utf8; +extern bool use_de; +extern bool ctcode_enabled; +extern bool lecode_enabled; +extern uint opt_ct_mode; // calculated by ctcode_enabled & lecode_enabled +extern int testmode; +extern int force_count; +extern uint opt_tiny; +extern bool force_kmp; +extern OffOn_t opt_battle_mode; +extern OffOn_t opt_export_flags; +extern int opt_route_options; +extern OffOn_t opt_wim0; +extern ccp opt_source; +extern StringField_t source_list; +extern ccp opt_dest; +extern bool opt_mkdir; +extern bool opt_overwrite; +extern bool opt_number; +extern bool opt_remove_src; +extern bool opt_remove_dest; +extern bool opt_update; +extern bool opt_preserve; +extern ccp opt_extract; +extern int opt_ignore; +extern bool opt_ignore_setup; +extern bool opt_purge; +extern file_format_t opt_fform; +extern file_format_t fform_compr; +extern file_format_t fform_compr_force; +extern file_format_t script_fform; +extern ccp script_varname; +extern int script_array; +extern int opt_pt_dir; +extern bool opt_links; +extern bool opt_rm_aiparam; +extern u32 opt_align; +extern u32 opt_align_u8; +extern u32 opt_align_pack; +extern u32 opt_align_brres; +extern u32 opt_align_breff; +extern u32 opt_align_breft; +extern bool print_header; +extern bool print_param; +extern int print_sections; +extern double opt_tri_area; +extern double opt_tri_height; +extern bool opt_kmp_flags; +extern bool opt_id; +extern bool opt_base64; +extern bool opt_db64; +extern EncodeMode_t opt_coding64; +extern bool opt_verify; +extern ccp opt_cache; +extern ccp opt_cname; +extern bool opt_round; +extern int brief_count; +extern int long_count; +//extern int full_count; +extern int pipe_count; +extern int delta_count; +extern int diff_count; +extern int minimize_level; +extern int export_count; +extern int all_count; +extern bool raw_mode; +extern SortMode_t opt_sort; +extern u32 opt_max_file_size; +extern int opt_compr_mode; +extern u32 opt_compr; +extern bool opt_norm; +extern int need_norm; // enabled if > 0 +extern bool opt_fast; +extern char *opt_basedir; +extern int opt_recurse; +extern int opt_ext; +extern bool opt_decode; +extern bool opt_cut; +extern bool opt_cmpr_valid; +extern u8 opt_cmpr_def[8]; +extern uint opt_n_images; +extern uint opt_max_images; +extern uint opt_min_mipmap_size; // minimal mipmap size +extern int opt_mipmaps; +extern bool opt_strip; +extern bool opt_pre_convert; +extern bool opt_encode_all; +extern bool opt_encode_img; +extern bool opt_no_encode; +extern bool opt_no_recurse; +extern bool opt_auto_add; +extern bool opt_no_echo; +extern bool opt_generic; +extern bool opt_raw; +extern bool opt_tracks; +extern bool opt_arenas; +extern bool opt_order_all; +extern ccp opt_order_by; +extern ccp opt_write_tracks; +extern bool opt_no_check; +extern CheckMode_t global_check_mode; +extern ccp opt_flag_file; +extern int opt_xtridata; +extern int have_patch_count; // <=0: disable patching at all +extern ParamField_t patch_bmg_list; +extern FormatField_t patch_image_list; +extern StringField_t ct_dir_list; + +extern double epsilon_pos; // 'p' Position +extern double epsilon_rot; // 'r' Rotation +extern double epsilon_scale; // 's' Scale +extern double epsilon_time; // 't' Time + +extern ccp analyze_fname; +extern FILE * analyze_file; + +extern char iobuf [0x400000]; // global io buffer +//extern const char zerobuf[0x100]; // global zero buffer + +extern const char indent_msg[]; // N * "> " + NULL +extern const char section_sep[]; +extern const char section_end[]; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_STD_H diff --git a/project/src/lib-szs-create.c b/project/src/lib-szs-create.c new file mode 100644 index 0000000..8301400 --- /dev/null +++ b/project/src/lib-szs-create.c @@ -0,0 +1,4744 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +#include "lib-szs.h" +#include "lib-rarc.h" +#include "lib-pack.h" +#include "lib-brres.h" +#include "lib-breff.h" +#include "lib-ctcode.h" +#include "lib-xbmg.h" +#include "lib-kcl.h" +#include "lib-kmp.h" +#include "lib-lecode.h" +#include "lib-mdl.h" +#include "lib-pat.h" +#include "lib-rkc.h" +#include "lib-image.h" +#include "lib-object.h" +#include "lib-checksum.h" +#include "crypt.h" + +#include "setup.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alter file table /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct alter_file_t +{ + SlotMode_t rm_mode; + SlotMode_t add_mode; + ccp fname; +} +alter_file_t; + +static const alter_file_t AlterFileTab[] = +{ + //--------------------------------------------------------------------- + // mode for remove mode for add file name + //--------------------------------------------------------------------- + { SLOTMD_RM_ICE, SLOTMD_ADD_BICE, "ice.brres" }, + { SLOTMD_RM_SUNDS, 0, "sunDS.brres" }, + { SLOTMD_RM_SUNDS, 0, "pylon01.brres" }, + { SLOTMD_RM_SHYGUY, SLOTMD_ADD_SHYGUY, "HeyhoShipGBA.brres" }, + { SLOTMD_RM_SHYGUY, 0, "HeyhoBallGBA.brres" }, + {0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const alter_file_t * ShallRemoveFile ( ccp fname ) +{ + + if ( opt_slot & SLOTMD_JOB_RM_SZS && fname ) + { + const alter_file_t *alter; + for ( alter = AlterFileTab; alter->fname; alter++ ) + if ( opt_slot & alter->rm_mode && !strcasecmp(fname,alter->fname) ) + return alter; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool AddSlotFiles ( szs_file_t *szs, struct szs_norm_t * norm ) +{ + DASSERT(szs); + bool dirty = false; + if ( szs->fform_arch == FF_U8 || szs->fform_arch == FF_WU8 ) + { + const alter_file_t *alter; + for ( alter = AlterFileTab; alter->fname; alter++ ) + if ( opt_slot & alter->add_mode + && AddMissingFileSZS(szs,alter->fname,FF_BRRES,norm,-1) ) + { + PATCH_ACTION_LOG("Add","SZS","%s\n",alter->fname); + dirty = true; + } + } + return dirty; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan dir & create szs /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[scan_data_t]] + +typedef struct scan_data_t +{ + szs_file_t * szs; // valid pointer to szs data structure + SetupParam_t * setup_param; // valid pointer to setup parameters + char path[PATH_MAX]; // the current path + char * path_rel; // ptr to 'path': path relative to base directory + char * path_dir; // ptr to 'path': current directory + u32 n_directories; // total number of directories + u32 namepool_size_u8; // total size of name pool (for U8) + u32 depth; // current depth + u32 max_depth; // max allowed depth + u64 total_size; // total aligned size of all files + u32 align; // data alignmet + +} scan_data_t; + +/////////////////////////////////////////////////////////////////////////////// + +static bool fname_allowed ( scan_data_t *sd, ccp name, char * path_dir ) +{ + // ignore "." and ".." always + if ( *name == '.' && ( !name[1] || name[1] == '.' && !name[2] )) + return false; + + if ( opt_rm_aiparam && !strcasecmp(name,"aiparam") ) + { + PATCH_ACTION_LOG("Remove","SZS","%s\n","AIParam"); + sd->szs->aiparam_removed = true; + return false; + } + + if (ShallRemoveFile(name)) + { + PATCH_ACTION_LOG("Remove","SZS","%s\n",name); + return false; + } + + char *path_end = sd->path + sizeof(sd->path) - 1; + sd->path_dir = StringCopyE(path_dir,path_end,name); + const SetupParam_t * sp = sd->setup_param; + DASSERT(sp); + if ( !FindStringField(&sp->include_name,sd->path_rel) + && !MatchStringField(&sp->include_pattern,sd->path_rel) ) + { + // file is not in include list + if ( *name == '.' + || FindStringField(&sp->exclude_name,sd->path_rel) + || MatchStringField(&sp->exclude_pattern,sd->path_rel) ) + { + noPRINT("EXCLUDED: %s\n",sd->path_rel); + return false; + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u32 scan_data ( scan_data_t * sd ) +{ + ASSERT(sd); + ASSERT(sd->szs); + szs_file_t *szs = sd->szs; + const u16 dir_id = sd->n_directories++; + + char * path_end = sd->path + sizeof(sd->path) - 1; + DASSERT( sd->path <= sd->path_rel ); + DASSERT( sd->path_rel <= sd->path_dir ); + DASSERT( sd->path_dir <= path_end ); + + PRINT("scan_data(%.*s|%.*s|%s) depth=%d, links=%d\n", + (int)(sd->path_rel - sd->path), sd->path, + (int)(sd->path_dir - sd->path_rel), sd->path_rel, + sd->path_dir, sd->depth, szs->links ); + + // save current path_dir + char * path_dir = sd->path_dir; + + u32 count = 0; + DIR * dir = opendir(sd->path); + if (dir) + { + for(;;) + { + struct dirent * dent = readdir(dir); + if (!dent) + break; + + ccp name = dent->d_name; + #ifdef TEST // test it! + if (!fname_allowed(sd,name,path_dir)) + continue; + #else + // ignore "." and ".." always + if ( *name == '.' && ( !name[1] || name[1] == '.' && !name[2] )) + continue; + + if ( opt_rm_aiparam && !strcasecmp(name,"aiparam") ) + { + PATCH_ACTION_LOG("Remove","SZS","%s\n","AIParam"); + szs->aiparam_removed = true; + continue; + } + + if (ShallRemoveFile(name)) + { + PATCH_ACTION_LOG("Remove","SZS","%s\n",name); + continue; + } + + sd->path_dir = StringCopyE(path_dir,path_end,name); + const SetupParam_t * sp = sd->setup_param; + DASSERT(sp); + if ( !FindStringField(&sp->include_name,sd->path_rel) + && !MatchStringField(&sp->include_pattern,sd->path_rel) ) + { + // file is not in include list + if ( *name == '.' + || FindStringField(&sp->exclude_name,sd->path_rel) + || MatchStringField(&sp->exclude_pattern,sd->path_rel) ) + { + noPRINT("EXCLUDED: %s\n",sd->path_rel); + continue; + } + } + #endif + + struct stat st; + if (stat(sd->path,&st)) + continue; + + if ( S_ISDIR(st.st_mode) && sd->depth < sd->max_depth ) + { + count++; + szs_subfile_t * file = AppendSubfileSZS(szs,0,0); + DASSERT(file); + file->dir_id = dir_id; + const uint nlen = strlen(name); + sd->namepool_size_u8 += nlen + 1; + + file->is_dir = true; + *sd->path_dir++ = '/'; + *sd->path_dir = 0; + file->path = STRDUP(sd->path_rel); + noTRACE("DIR: %s\n",path_dir); + file->offset = sd->depth++; + + // pointer 'file' becomes invalid if realloc() called => store index + const int idx = file - szs->subfile.list; + const u32 sub_count = scan_data(sd); + szs->subfile.list[idx].size = sub_count; + count += sub_count; + sd->depth--; + } + else if (S_ISREG(st.st_mode)) + { + count++; + szs_subfile_t * file = AppendSubfileSZS(szs,0,0); + DASSERT(file); + file->dir_id = dir_id; + const uint nlen = strlen(name); + sd->namepool_size_u8 += nlen + 1; + + szs_subfile_t *lptr = szs->links + ? FindLinkSZS(szs,st.st_dev,st.st_ino,file) : 0; + if (lptr) + { + if (!lptr->link_index) + lptr->link_index = ++szs->subfile.link_count; + file->link_index = lptr->link_index; + PRINT("LINK[%d]: %s -> %s\n", + lptr->link_index, lptr->path, sd->path_rel ); + } + + file->path = STRDUP(sd->path_rel); + noTRACE("FILE: %s\n",path_dir); + file->is_dir = false; + file->size = st.st_size; + file->device = st.st_dev; + file->inode = st.st_ino; + if (!lptr) + { + MaxFileAttrib(&szs->fatt,0,&st); + sd->total_size += ALIGN32(file->size,sd->align); + } + } + // else ignore all other files + } + closedir(dir); + } + + // restore path_dir + sd->path_dir = path_dir; + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +static u32 scan_sdir ( scan_data_t *sd, SubDir_t *sdir ) +{ + ASSERT(sd); + ASSERT(sd->szs); + ASSERT(sdir); + const u16 dir_id = sd->n_directories++; + + char * path_dir = sd->path_dir; + u32 count = 0; + + uint i; + for ( i = 0; i < sdir->file.used; i++ ) + { + SubFile_t *sf = sdir->file.list[i]; + ccp name = sf->fname; + if (!fname_allowed(sd,name,path_dir)) + continue; + + count++; + szs_subfile_t * file = AppendSubfileSZS(sd->szs,0,0); + DASSERT(file); + file->dir_id = dir_id; + const uint nlen = strlen(name); + sd->namepool_size_u8 += nlen + 1; + + file->path = STRDUP(sd->path_rel); + noTRACE("FILE: %s\n",path_dir); + file->is_dir = false; + file->size = sf->size; + file->data = sf->data; + sd->total_size += ALIGN32(file->size,sd->align); + } + + for ( i = 0; i < sdir->dir.used; i++ ) + { + SubDir_t *sub = sdir->dir.list[i]; + ccp name = sub->dname; + if (!fname_allowed(sd,name,path_dir)) + continue; + + count++; + szs_subfile_t * file = AppendSubfileSZS(sd->szs,0,0); + DASSERT(file); + file->dir_id = dir_id; + const uint nlen = strlen(name); + sd->namepool_size_u8 += nlen + 1; + + file->is_dir = true; + *sd->path_dir++ = '/'; + *sd->path_dir = 0; + file->path = STRDUP(sd->path_rel); + noTRACE("DIR: %s\n",path_dir); + file->offset = sd->depth++; + + // pointer 'file' becomes invalid if realloc() called => store index + const int idx = file - sd->szs->subfile.list; + const u32 sub_count = scan_sdir(sd,sub); + sd->szs->subfile.list[idx].size = sub_count; + count += sub_count; + sd->depth--; + } + + // restore path_dir + sd->path_dir = path_dir; + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateSZS +( + szs_file_t *szs, // valid szs + ccp dest_fname, // NULL or planned destination file name + ccp source_dir, // path to base directory + SubDir_t *sdir, // not NULL: read from here and ignore source_dir + SetupParam_t *setup_param, // setup parameters + // if NULL: read SZS_SETUP_FILE + uint depth, // creation depth + uint log_depth, // print creation log if depthfform_file = szs->fform_arch = szs->fform_current = FF_DIRECTORY; + szs->ff_attrib = GetAttribFF(szs->fform_arch); + szs->ff_version = -1; + szs->fname = STRDUP(source_dir); + szs->allow_ext_data = true; + + //--- setup + + scan_data_t sd; + memset(&sd,0,sizeof(sd)); + sd.szs = szs; + sd.path_rel = StringCat2S(sd.path,sizeof(sd.path),source_dir,"/"); + if ( sd.path_rel > sd.path + 1 && sd.path_rel[-2] == '/' ) + sd.path_rel--; + *sd.path_rel = 0; + sd.depth = 1; + sd.path_dir = sd.path_rel; + + if (!sdir) + { + struct stat st; + if (stat(sd.path,&st)) + return ERROR1(ERR_CANT_OPEN,"Can't open directory: %s\n",sd.path); + if (!S_ISDIR(st.st_mode)) + return ERROR1(ERR_CANT_OPEN,"Not a directory: %s\n",sd.path); + } + + SetupParam_t local_setup_param; + if (!setup_param) + { + *sd.path_rel = 0; + InitializeSetupParam(&local_setup_param); + setup_param = &local_setup_param; + ScanSetupParam(setup_param,true,sd.path,SZS_SETUP_FILE,sdir,true); + } + sd.setup_param = setup_param; + szs->links = opt_links && GetAttribFF(sd.setup_param->fform_arch) & FFT_LINK; + + + //--- encode files + + uint encode_warnings = 0, encode_errors = 0; + if ( !sdir && ( !opt_no_encode || opt_encode_img )) + { + disable_patch_on_load++; + have_patch_count -= 1000000; + const FormatFieldItem_t *ptr = setup_param->encode_list.list; + const FormatFieldItem_t *end = ptr + setup_param->encode_list.used; + for ( ; ptr < end; ptr++ ) + { + ccp ext; + bool encoding_needed = opt_encode_all; + noPRINT("FF=%u %s\n",ptr->fform,GetNameFF(ptr->fform,0)); + switch(ptr->fform) + { + case FF_BMG: + case FF_BMG_TXT: + case FF_KMP: + case FF_KMP_TXT: + case FF_LEX: + case FF_LEX_TXT: + #if 0 // [[2do]] [[mdl]] + case FF_MDL: + case FF_MDL_TXT: + #endif + case FF_PAT: + case FF_PAT_TXT: + if (opt_no_encode) + { + *sd.path_rel = 0; + continue; + } + ext = ".txt"; + break; + + case FF_KCL: + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + if (opt_no_encode) + { + *sd.path_rel = 0; + continue; + } + ext = ".obj"; + break; + + case FF_TPL: + case FF_BTI: + case FF_TEX: + case FF_TEX_CT: + case FF_BREFT: + case FF_BREFT_IMG: + ext = ".png"; + encoding_needed = opt_encode_all || opt_encode_img; + break; + + default: + *sd.path_rel = 0; + PRINT("NO-ENCODE: %s/%s\n",sd.path,ptr->key); + continue; + } + + struct stat st_dest; + if (!encoding_needed) + { + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + encoding_needed = stat(sd.path,&st_dest) || !S_ISREG(st_dest.st_mode); + } + + StringCat2E( sd.path_rel, sd.path+sizeof(sd.path), ptr->key, ext ); + TRACE("FIND: %s\n",sd.path); + + struct stat st; + if ( stat(sd.path,&st) || !S_ISREG(st.st_mode) ) + { + NewFileExtS( sd.path, sizeof(sd.path), 0, ext ); + if ( stat(sd.path,&st) || !S_ISREG(st.st_mode) ) + continue; + } + InsertStringField(&setup_param->exclude_name,sd.path_rel,false); + if ( !encoding_needed && st.st_mtime <= st_dest.st_mtime ) + { + PRINT("NO-ENCODE [%d,%d]: %s\n", opt_encode_all, encoding_needed, sd.path ); + continue; + } + PRINT("ENCODE: %s\n",sd.path ); + + u8 * data = MALLOC(st.st_size+1); + if (!LoadFILE(sd.path,0,0,data,st.st_size,0,0,false)) + { + data[st.st_size] = 0; // this EOT marker helps scanning text files + + const file_format_t fform = GetByMagicFF(data,st.st_size,st.st_size); + if ( depth < log_depth ) + fprintf(stdlog,"%*s%sENCODE %s:%s\n", + 2*depth, "", + testmode ? "WOULD " : "", + GetNameFF(fform,0), + sd.path ); + + switch (fform) + { + case FF_BMG: + case FF_BMG_TXT: + { + bmg_t bmg; + enumError err = ScanBMG(&bmg,true,sd.path,data,st.st_size); + if (err) + { + if ( err > ERR_WARNING ) + { + encode_errors++; + break; + } + encode_warnings++; + } + + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + if (!testmode) + { + unlink(sd.path); + if (SaveRawXBMG(&bmg,sd.path,true)) + encode_errors++; + } + ResetBMG(&bmg); + } + break; + + case FF_KCL: + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + { + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = FF_KCL; + kcl.fname = STRDUP(sd.path); + enumError err = ScanKCL(&kcl,false,data,st.st_size,global_check_mode,true); + if (err) + { + if ( err > ERR_WARNING ) + { + encode_errors++; + break; + } + encode_warnings++; + } + + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + if (!testmode) + { + unlink(sd.path); + if (SaveRawKCL(&kcl,sd.path,true)) + encode_errors++; + } + ResetKCL(&kcl); + } + break; + + case FF_KMP: + case FF_KMP_TXT: + { + kmp_t kmp; + InitializeKMP(&kmp); + kmp.fname = STRDUP(sd.path); + enumError err = ScanKMP(&kmp,false,data,st.st_size,global_check_mode); + if (err) + { + if ( err > ERR_WARNING ) + { + encode_errors++; + break; + } + encode_warnings++; + } + + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + if (!testmode) + { + unlink(sd.path); + if (SaveRawKMP(&kmp,sd.path,true)) + encode_errors++; + } + ResetKMP(&kmp); + } + break; + + case FF_LEX: + case FF_LEX_TXT: + { + lex_t lex; + InitializeLEX(&lex); + lex.fname = STRDUP(sd.path); + enumError err = ScanLEX(&lex,false,data,st.st_size); + if (err) + { + if ( err > ERR_WARNING ) + { + encode_errors++; + break; + } + encode_warnings++; + } + + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + if (!testmode) + { + unlink(sd.path); + if (SaveRawLEX(&lex,sd.path,true)) + encode_errors++; + } + ResetLEX(&lex); + } + break; + + #if 0 // [[mdl]] + case FF_MDL: + case FF_MDL_TXT: + { + mdl_t mdl; + InitializeMDL(&mdl); + mdl.fname = STRDUP(sd.path); + // [[mdl]] container [[2do]] [[container]] + enumError err = ScanMDL(&mdl,false,data,st.st_size,0,global_check_mode); + if (err) + { + if ( err > ERR_WARNING ) + { + encode_errors++; + break; + } + encode_warnings++; + } + + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + if (!testmode) + { + unlink(sd.path); + if (SaveRawMDL(&mdl,sd.path,true)) + encode_errors++; + } + ResetMDL(&kmp); + } + break; + #endif + + case FF_PAT: + case FF_PAT_TXT: + { + pat_t pat; + enumError err = ScanPAT(&pat,true,data,st.st_size,0,global_check_mode); + if (err) + { + if ( err > ERR_WARNING ) + { + encode_errors++; + break; + } + encode_warnings++; + } + + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + if (!testmode) + { + unlink(sd.path); + if (SaveRawPAT(&pat,sd.path,true)) + encode_errors++; + } + ResetPAT(&pat); + } + break; + + case FF_PNG: + { + FREE(data); + data = 0; + Image_t img; + enumError err = LoadIMG( &img, true, sd.path, 0, + opt_mipmaps >= 0, false, opt_ignore ); + if (err) + { + if ( err > ERR_WARNING ) + { + encode_errors++; + break; + } + encode_warnings++; + } + + Transform3IMG(&img,ptr->fform,ptr->iform,ptr->pform,true); + TransformIMG(&img,-1); + Transform2InternIMG(&img); + + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), ptr->key ); + const file_format_t fform + = GetImageFF( img.tform_valid ? img.tform_fform : FF_INVALID, + ptr->fform, + sd.path, + img.info_fform, + false, + FF_TEX ); + img.info_n_image = ptr->num; + if (SaveIMG(&img,fform,0,sd.path,true)) + encode_errors++; + } + break; + + default: + ERROR0(ERR_INVALID_DATA,"Can't encode: %s\n",sd.path); + break; + } + } + FREE(data); + } + have_patch_count += 1000000; + disable_patch_on_load--; + } + + + //--- create sub archives + + if ( !opt_no_recurse && !sdir ) + { + disable_patch_on_load++; + depth++; + ccp *ptr = setup_param->create_list.field; + ccp *end = ptr + setup_param->create_list.used; + for ( ; ptr < end; ptr++ ) + { + StringCat2E( sd.path_rel, sd.path+sizeof(sd.path), *ptr, ".d" ); + struct stat st; + if ( stat(sd.path,&st) || !S_ISDIR(st.st_mode) ) + { + NewFileExtS( sd.path, sizeof(sd.path), 0, ".d" ); + if ( stat(sd.path,&st) || !S_ISDIR(st.st_mode) ) + continue; + } + InsertStringField(&setup_param->exclude_name,sd.path_rel,false); + PRINT("CREATE %s\n",sd.path); + + SetupParam_t setup_param2; + InitializeSetupParam(&setup_param2); + ScanSetupParam(&setup_param2,true,sd.path,SZS_SETUP_FILE,0,true); + setup_param2.compr_mode = -1; + + szs_file_t szs; + InitializeSZS(&szs); + if (!CreateSZS(&szs,0,sd.path,0,&setup_param2,depth,log_depth,mark_readonly)) + { + StringCopyE( sd.path_rel, sd.path+sizeof(sd.path), *ptr ); + struct stat st; + const time_t mtime = stat(sd.path,&st) ? 0 : st.st_mtime; +#if 0 + BINGO; + printf("mtime: %u - %u = %d [%s>%s>] size=%zu,%zu\n", + (uint)mtime, (uint)szs.fatt.mtime, (int)(mtime-szs.fatt.mtime), + GetNameFF(setup_param2.fform_file,setup_param2.fform_arch), + GetNameFF(szs.fform_file,szs.fform_arch), + szs.size, szs.csize ); +#endif + if ( mtime < szs.fatt.mtime ) + { + const bool is_compressed = IsCompressedFF(setup_param2.fform_file); + if (is_compressed) + CompressSZS(&szs,true); + + if ( depth < log_depth ) + fprintf(stdlog,"%*s%sCREATE %s:%s\n", + 2*depth, "", + testmode ? "WOULD " : "", + GetNameFF(szs.fform_file,szs.fform_arch), + sd.path ); + SaveSZS(&szs,sd.path,true,is_compressed); + } + } + ResetSZS(&szs); + } + disable_patch_on_load--; + } + + + //--- scan files + + // pointer 'file' and 'part->file' becomes invalid if realloc() called + // store the size first and then assign it (cross a sequence point) + + bool allow_add_files = false; + switch (setup_param->fform_arch) + { + case FF_BRRES: + sd.align = opt_align_brres; + sd.max_depth = 20; + break; + + case FF_BREFF: + case FF_BREFT: + sd.path_dir = StringCopyE(sd.path_rel,sd.path+sizeof(sd.path),"files/"); + sd.align = 4; // any value because calculation is done later + break; + + case FF_PACK: + sd.align = opt_align_pack; + sd.max_depth = 20; + break; + + case FF_RKC: + sd.align = 0x10; + sd.max_depth = 0; + break; + + //case FF_RARC: // [[2do]] [[arc]] + //case FF_U8: + //case FF_WU8: + default: + sd.align = opt_align_u8; + sd.max_depth = 20; + allow_add_files = true; + break; + } + + *sd.path_dir = 0; + const u32 n_sub_files = sdir ? scan_sdir(&sd,sdir) : scan_data(&sd); + ASSERT_MSG( n_sub_files == szs->subfile.used, + "%d+1 != %d [%s]\n", n_sub_files, + szs->subfile.used, szs->subfile.list->path ); + + + //--- add missing files + + if ( !sdir && opt_auto_add && allow_add_files ) + AddMissingFiles(szs,source_dir,&sd,0, verbose>=0 ? 2*(int)depth : -1 ); + + + //--- debugging + + if ( logging > 1 ) + { + int i; + for ( i = 0; i < szs->subfile.used; i++ ) + { + szs_subfile_t * f = szs->subfile.list + i; + printf("%3d.: %u %6x %6x %s\n", i, f->is_dir, f->offset, f->size, f->path ); + } + } + + + //--- create archive + + enumError err; + switch (setup_param->fform_arch) + { + case FF_BRRES: + szs->order_list = &setup_param->order_list; + szs->min_data_off = setup_param->min_data_off; + err = CreateBRRES( szs, source_dir, 0, sd.total_size ); + break; + + case FF_BREFF: + case FF_BREFT: + szs->order_list = &setup_param->order_list; + err = CreateBREFF( szs, source_dir, 0, sd.total_size, setup_param ); + break; + + case FF_PACK: + szs->order_list = &setup_param->order_list; + err = CreatePACK( szs, source_dir, 0, sd.total_size, setup_param ); + break; + + case FF_RKC: + err = CreateRKC(szs,source_dir); + setup_param->compr_mode = -1; + break; + + default: + err = CreateU8( szs, source_dir, 0, + sd.namepool_size_u8, sd.total_size, + setup_param->have_pt_dir > 0 ); + szs->allow_ext_data = true; + break; + } + szs->order_list = 0; + + + //--- transform & compress data + + szs->fform_file = szs->fform_arch; + PatchSZS(szs); + if ( szs->allow_ext_data ) + { + const bool clean_lex = opt_lex_purge || HavePatchTestLEX(); + NormalizeExSZS(szs,opt_rm_aiparam,clean_lex,false); + } + + if ( !err && setup_param->fform_arch == FF_WU8 ) + err = EncodeWU8(szs); + + if ( !err && setup_param->compr_mode >= 0 ) + { + szs->dest_fname = dest_fname; + CompressSZS(szs,true); + szs->dest_fname = 0; + } + + + //--- clean and end + + if ( setup_param == &local_setup_param ) + ResetSetupParam(&local_setup_param); + + return encode_errors && err < ERR_ENCODING + ? ERR_ENCODING + : encode_warnings && err < (enumError)ERR_ENCODING_WARN // [[dclib]] + ? ERR_ENCODING_WARN + : err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LoadCreateSZS() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadCreateSZS +( + szs_file_t * szs, // valid szs + ccp fname, // valid pointer to filenname + bool decompress, // decompress after loading + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + bool mark_readonly // true: the file will never written +) +{ + MEM_CHECK; + DASSERT(szs); + DASSERT(fname); + TRACE("LoadCreateSZS(%d,%d) fname=%s\n",decompress,ignore_no_file,fname); + + struct stat st; + if ( !stat(fname,&st) && S_ISDIR(st.st_mode) ) + { + SetupParam_t sp; + InitializeSetupParam(&sp); + ScanSetupParam(&sp,true,fname,SZS_SETUP_FILE,0,true); + sp.compr_mode = -1; + const enumError err = CreateSZS(szs,0,fname,0,&sp,0,0,mark_readonly); + ResetFileSZS(szs,true); + ResetSetupParam(&sp); + return err; + } + + enumError err = LoadSZS(szs,fname,decompress,ignore_no_file,mark_readonly); + PatchSZS(szs); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadObjFileListSZS +( + szs_file_t * szs, // valid szs + const void * kmp_data, // not NULL: source data + size_t data_size, // size of 'kmp_data' + ccp kmp_fname, // if !kmp_data: load kmp file + bool ignore_no_file, // param for LoadKMP()/ScanKMP() + CheckMode_t check_mode, // param for LoadKMP()/ScanKMP() + lex_info_t *lexinfo // NULL or valid LEX info +) +{ + DASSERT(szs); + if (szs->used_file) + return ERR_OK; + + PRINT("sizeof(UsedFileFILE_t)=%zu\n",sizeof(UsedFileFILE_t)); + szs->used_file = CALLOC(1,sizeof(*szs->used_file)); + szs->required_file = CALLOC(1,sizeof(*szs->required_file)); + InitializeParamField(szs->required_file); + + + //--- load KMP + + kmp_t kmp; + enumError err; + if (kmp_data) + { + InitializeKMP(&kmp); + kmp.fname = STRDUP2(szs->fname,"/course.kmp"); + kmp.lexinfo = lexinfo; + err = ScanKMP(&kmp,false,kmp_data,data_size,check_mode); + } + else + err = LoadKMP(&kmp,true,kmp_fname,ignore_no_file,check_mode); + if (err) + { + ResetKMP(&kmp); + return err; + } + + szs->n_cannon = kmp.dlist[KMP_CNPT].used; + szs->is_arena = IsArenaKMP(&kmp); + PRINT("LoadObjFileListSZS(): %d cannons found, is_arena=%d\n", + szs->n_cannon, szs->is_arena ); + + + //--- find used objects + + const uint PFLAG = 0x3f; + UsedObject_t pf_obj; // OR'ed presence flag + UsedObject_t used_obj; + memset(&used_obj,0,sizeof(used_obj)); + memset(&pf_obj,0,sizeof(pf_obj)); + PRINT("sizeof(UsedObject_t)=%zu\n",sizeof(used_obj)); + + const kmp_gobj_entry_t *gobj = (kmp_gobj_entry_t*)kmp.dlist[KMP_GOBJ].list; + const kmp_gobj_entry_t *gend = gobj + kmp.dlist[KMP_GOBJ].used; + for ( ; gobj < gend; gobj++ ) + { + const uint id = GetActiveObjectId(gobj); + if (id) + { + pf_obj.d[id] |= gobj->pflags & PFLAG; + used_obj.d[id] = 1; + } + + const ObjectInfo_t *oi = GetObjectInfo(id); + if ( oi && oi->flags & OBF_SPECIAL ) + { + const ObjSpec_t *os; + for ( os = ObjSpec; os->obj_id; os++ ) + if ( os->obj_id == id ) + switch (os->type) + { + case OSP_FILE_BY_SETTING: + { + const int submode = os->mode / 10; + const int setting = os->mode % 10; + const uint value = gobj->setting[setting]; + if ( submode == 1 && value == 1 ) + break; + + char buf[100]; + snprintf(buf,sizeof(buf),os->text,value); + InsertParamField(szs->required_file,buf,false,0,0); + break; + } + + default: + break; + } + } + } + + //DetectSpecialKMP(&kmp,szs->kmp_special); + + + //--- slot analysis: 31+71, 62 + + szs->slot_analyzed = true; + + szs->slot_31_71 = ( used_obj.d[GOBJ_SUN_DS] ? SLOT_31_71_SUNDS : 0 ) + | ( used_obj.d[GOBJ_PYLON01] ? SLOT_31_71_PYLON01 : 0 ); + + szs->slot_62 = used_obj.d[GOBJ_HEYHO_SHIP] != 0; + + + //--- slot analysis: 42 + + szs->slot_42 = ( used_obj.d[0xd0] ? SLOT_42_KART_TRUCK_U : 0 ) + | ( used_obj.d[0xd1] ? SLOT_42_CAR_BODY_U : 0 ) + | ( pf_obj.d[0xd0] == PFLAG ? SLOT_42_KART_TRUCK_PF : 0 ) + | ( pf_obj.d[0xd1] == PFLAG ? SLOT_42_CAR_BODY_PF : 0 ); + + const uint USED42 = SLOT_42_KART_TRUCK_U | SLOT_42_CAR_BODY_U; + if ( (szs->slot_42 & USED42) == USED42 ) + szs->slot_42 |= SLOT_42_ALL_U; + + const uint PF42 = SLOT_42_KART_TRUCK_PF | SLOT_42_CAR_BODY_PF; + if ( (szs->slot_42 & PF42) == PF42 ) + szs->slot_42 |= SLOT_42_ALL_PF; + //printf("--> SLOT_42 = %02x\n",szs->slot_42); + + + //--- find used files + + UsedFileGROUP_t used_group; + FindDbGroupByObjects(&used_group,true,&used_obj); + FindDbFileByGroups(szs->used_file,false,&used_group); + + ResetKMP(&kmp); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * IsFileRequiredSZS +( + // check DBF_SPECIAL && szs->required_file + szs_file_t * szs, // valid szs + const DbFileFILE_t * file // file to proof +) +{ + DASSERT(szs); + DASSERT(file); + + if (!szs->required_file) + return 0; + + ParamFieldItem_t *it = FindParamField(szs->required_file,file->file); + if (it) + it->num |= 1; + return it; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsFileOptionalSZS +( + // check DBF_OPTIONAL && DBF_SPECIAL && szs->required_file + szs_file_t * szs, // valid szs + const DbFileFILE_t * file // NULL or file to proof +) +{ + DASSERT(szs); + if ( !file || !(file->flags & DBF_OPTIONAL) ) + return false; + + return !IsFileRequiredSZS(szs,file); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void AddSpecialFile + ( szs_file_t *szs, ccp fname, const u8 *data, uint size ) +{ + if (!szs->special_file) + { + szs->special_file = CALLOC(1,sizeof(*szs->special_file)); + InitializeParamField(szs->special_file); + szs->special_file->free_data = true; + } + + char buf[200]; + const file_format_t fform = GetByMagicFF(data,size,size); + snprintf(buf,sizeof(buf),"%s %s",GetNameFF(fform,0),fname); + + ParamFieldItem_t *it + = InsertParamFieldEx(szs->special_file,buf,false,size,0); + if (!it->data) + { + sha1_hash_t *hash = MALLOC(sizeof(sha1_hash_t)); + SHA1(data,size,*hash); + it->data = hash; + } +} + +//----------------------------------------------------------------------------- + +static void CheckSpecialFile + ( szs_file_t *szs, const szs_subfile_t *file, ccp fname, ccp dir ) +{ + DASSERT(szs); + DASSERT(file); + DASSERT(dir); + + const int dlen = strlen(dir); + if ( strlen(fname) > dlen + && !strncasecmp(fname,dir,dlen) + && !strchr(fname+dlen,'/') ) + { + AddSpecialFile(szs,fname,szs->data+file->offset,file->size); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void FindSpecialFilesSZS +( + szs_file_t * szs, // valid szs + bool force // true: force new scanning +) +{ + DASSERT(szs); + + if ( !force && szs->special_done ) + return; + + ClearSpecialFilesSZS(szs); + CollectFilesSZS(szs,true,0,-1,SORT_NONE); + + szs_subfile_t *file, *file_end = szs->subfile.list + szs->subfile.used; + for ( file = szs->subfile.list; file < file_end; file++ ) + { + ccp fname = file->path; + if ( fname[0] == '.' && fname[1] == '/' ) + fname += 2; + + if ( !szs->course_kcl_data && !strcasecmp(fname,"course.kcl") ) + { + szs->course_kcl_data = szs->data + file->offset; + szs->course_kcl_size = file->size; + } + else if ( !szs->course_kmp_data && !strcasecmp(fname,"course.kmp") ) + { + szs->course_kmp_data = szs->data + file->offset; + szs->course_kmp_size = file->size; + } + else if ( !szs->course_lex_data && !strcasecmp(fname,"course.lex") ) + { + szs->course_lex_data = szs->data + file->offset; + szs->course_lex_size = file->size; + szs->szs_special[HAVESZS_COURSE_LEX] = true; + } + else if ( !szs->course_model_data && !strcasecmp(fname,"course_model.brres") ) + { + szs->course_model_data = szs->data + file->offset; + szs->course_model_size = file->size; + } + else if ( !szs->course_d_model_data && !strcasecmp(fname,"course_d_model.brres") ) + { + szs->course_d_model_data = szs->data + file->offset; + szs->course_d_model_size = file->size; + } + else if ( !szs->vrcorn_model_data && !strcasecmp(fname,"vrcorn_model.brres") ) + { + szs->vrcorn_model_data = szs->data + file->offset; + szs->vrcorn_model_size = file->size; + } + else if ( !szs->map_model_data && !strcasecmp(fname,"map_model.brres") ) + { + szs->map_model_data = szs->data + file->offset; + szs->map_model_size = file->size; + } + else if ( !szs->have_ice_brres && !strcasecmp(fname,"ice.brres") ) + { + szs->have_ice_brres = true; + } + + if ( !szs->moonview_mdl_stat + && ( !strcasecmp(fname,"course_model.brres") + || !strcasecmp(fname,"course_d_model.brres") )) + { + szs_file_t subszs; +// [[fname+]] + InitializeSubSZS(&subszs,szs,file->offset,file->size,FF_UNKNOWN,fname,false); + Slot42MaterialStat_t slot42 = GetSlot42SupportSZS(&subszs); + noPRINT("SLOT42: found=%03x, mod=%03x, all=%d, cok=%d, ok=%d\n", + slot42.found, slot42.modified, + slot42.all_found, slot42.content_ok, slot42.ok ); + szs->moonview_mdl_stat = slot42.ok ? 3 + : slot42.all_found ? 2 + : slot42.found ? 1 + : 0; + ResetSZS(&subszs); + } + else + { + uint i; + for ( i = 0; i < HAVESZS__N; i++ ) + if ( !szs->szs_special[i] && !strcasecmp(fname,have_szs_file[i]) ) + { + szs->szs_special[i] = true; + break; + } + } + + CheckSpecialFile(szs,file,fname,"common/"); + CheckSpecialFile(szs,file,fname,"itemslottable/"); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp CreateSpecialFileInfo + ( szs_file_t * szs, bool add_value, ccp return_if_empty ) +{ + DASSERT(szs); + + static char buf[500]; + char *dest = buf; + + if (add_value) + { + uint i, val = 0; + for ( i = 0; i < HAVESZS__N; i++ ) + if ( szs->szs_special[i] ) + val |= 1 << i; + dest = snprintfE( dest, buf+sizeof(buf), "%u=" , val ); + } + + uint i; + ccp sep = ""; + for ( i = 0; i < HAVESZS__N; i++ ) + if ( szs->szs_special[i] ) + { + dest = StringCat2E(dest,buf+sizeof(buf),sep,have_szs_name[i]); + sep = ","; + } + + return dest == buf ? return_if_empty : CopyCircBuf0(buf,dest-buf); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// NormalizeSZS() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int norm_collect_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if (term) + return 0; + + szs_norm_t * norm = it->param; + DASSERT(norm); + + uint depth = it->depth; + ccp path = it->path; + if ( path[0] == '.' && path[1] == '/' ) + { + path += 2; + depth--; + if (!*path) + { + norm->have_pt_dir = true; + return 0; + } + } + + if (!*path) + return 0; + + if ( norm->rm_aiparam && !strncasecmp(path,"aiparam",7) ) + { + PATCH_ACTION_LOG("Remove","SZS","%s\n","AIPARAM"); + it->szs->aiparam_removed = true; + return 0; + } + + if ( norm->clean_lex && !strncasecmp(path,"course.lex",10) ) + { + lex_t lex; + u8 *data = it->szs->data + it->off; + enumError err = ScanRawLEX(&lex,true,data,it->size,0); + if ( !err && PatchLEX(&lex) && CreateRawLEX(&lex) == ERR_OK ) + { + if (!lex.have_lex) + { + PATCH_ACTION_LOG("Remove","SZS","%s\n","course.lex"); + return 0; + } + + if ( lex.raw_data_size <= it->size + && memcmp(data,lex.raw_data,lex.raw_data_size) ) + { + PATCH_ACTION_LOG("Purge","SZS","%s\n","course.lex"); + memcpy(data,lex.raw_data,lex.raw_data_size); + it->size = lex.raw_data_size; + } + } + } + + if (ShallRemoveFile(path)) + { + PATCH_ACTION_LOG("Remove","SZS","%s\n",path); + return 0; + } + + noPRINT_IF(it->is_dir,"ADD[%u-%u=%u]: %s\n", + it->size, it->index, it->size-it->index, path ); + + szs_subfile_t * file = AppendSubfileSZS(it->szs,it,0); + DASSERT(file); + ccp ptr = file->path + strlen(file->path) - 1; + if (it->is_dir) + ptr--; + while ( ptr > file->path && *ptr != '/' ) + ptr--; + if ( *ptr == '/' ) + ptr++; + + norm->namepool_size += strlen(ptr); + noPRINT("ADD: %s[%zu+%u]\n",ptr,strlen(ptr),!it->is_dir); + if (it->is_dir) + { + file->offset = depth; + file->size = it->size - it->index - 1; + } + else + { + file->device = it->size; + file->inode = it->off; + + uint relevant_size = file->size; + if (it->szs->ext_data.used) + { + int idx; + for ( idx = 0; idx < it->szs->ext_data.used; idx++ ) + { + szs_subfile_t * f = it->szs->ext_data.list + idx; + if (!StrPathCmp(it->path,f->path)) + { + PRINT("########## %s %s / %u -> %u%s\n", + it->path, f->path, it->size, f->size, + f->removed ? " REMOVE!" : "" ); + file->ext = f; + relevant_size = f->size; + file->device = 0; + file->inode = 0; + + if ( f->removed) + { + norm->namepool_size -= strlen(ptr)+1; + relevant_size = 0; + } + } + } + } + + szs_subfile_t *lptr = opt_links + ? FindLinkSZS(it->szs,file->device,file->inode,file) : 0; + if (lptr) + { + if (!lptr->link_index) + lptr->link_index = ++it->szs->subfile.link_count; + file->link_index = lptr->link_index; + PRINT("NORM/LINK[%d]: %s -> %s\n", + lptr->link_index, lptr->path, file->path ); + } + else + norm->total_size += ALIGN32(relevant_size,opt_align_u8); + + norm->namepool_size++; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NormalizeSZS ( szs_file_t *szs ) +{ + const bool clean_lex = opt_lex_purge || HavePatchTestLEX(); + return NormalizeExSZS(szs,opt_rm_aiparam,clean_lex,opt_auto_add); +} + +//----------------------------------------------------------------------------- + +bool NormalizeExSZS + ( szs_file_t *szs, bool rm_aiparam, bool clean_lex, bool autoadd ) +{ + DASSERT(szs); + PRINT("*** NormalizeExSZS(,%d,%d,%d) ***\n",rm_aiparam,clean_lex,autoadd); + + // [[2do]] [[arc]] + if ( szs->fform_arch != FF_U8 && szs->fform_arch != FF_WU8 || !szs->size || !szs->data ) + return false; + + ResetFileSZS(szs,false); + + szs_norm_t norm; + memset(&norm,0,sizeof(norm)); + norm.rm_aiparam = rm_aiparam; + norm.clean_lex = clean_lex; + IterateFilesSZS(szs,norm_collect_func,&norm,false,0,-1,SORT_NONE); + + if (autoadd) + AddMissingFiles(szs,0,0,&norm,2); + AddSlotFiles(szs,&norm); + AddTestLEX(szs,&norm); + SortSubFilesSZS(szs,SORT_AUTO); + + uint old_size = szs->size; + u8 * old_data = szs->data; + bool old_alloced = szs->data_alloced; + szs->data = 0; + szs->data_alloced = false; + + CreateU8(szs,0,old_data,norm.namepool_size,norm.total_size,norm.have_pt_dir); + + bool dirty = old_size != szs->size || memcmp(old_data,szs->data,old_size); + if (old_alloced) + FREE(old_data); + return dirty; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// AddMissingFiles() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct add_missing_t +{ + szs_file_t * szs; // valid szs + scan_data_t * sd; // NULL or valid scan data + szs_norm_t * norm; // NULL or valid norm data + int log_indent; // >-1: print log with indention + bool print_err; // true: print errors about missing files + +} add_missing_t; + +//----------------------------------------------------------------------------- + +static int add_missing_file +( + ccp path, // calculated path + file_format_t fform, // FF_BRRES | FF_BREFF | FF_BREFT + add_missing_t *am // user defined parameter +) +{ + DASSERT(path); + DASSERT(fform); + DASSERT(am); + DASSERT(am->szs); + DASSERT( am->sd || am->norm ); + + noPRINT("--> %s:%s\n", GetNameFF(0,fform), path ); + + szs_subfile_list_t *subfile = &am->szs->subfile; + szs_subfile_t *sptr, *send = subfile->list + subfile->used; + for ( sptr = subfile->list; sptr < send; sptr++ ) + { + ccp spath = sptr->path; + if ( spath[0] == '.' && spath[1] == '/' ) + spath += 2; + if (!strcmp(path,spath)) + { + PRINT("AUTO-ADD/FOUND: %s\n",path); + return 0; + } + } + + const bool is_course_lex = !strcmp(path,"course.lex"); + + s64 size = 0; + char load_path[PATH_MAX]; + + if (is_course_lex) + { + StringCopyS(load_path,sizeof(load_path),path); + } + else + { + size = FindAutoAdd(path,0,load_path,sizeof(load_path)); + if ( size <= 0 ) + { + noPRINT("AUTO-ADD/MISSING: %s\n",path); + if (am->print_err) + ERROR0(ERR_WARNING, + "Missing file in auto-add archive: %s\n",path); + #ifdef TEST + if (verbose>=0) + ERROR0(ERR_WARNING,"Missing sub file '%s': %s\n", + path, am->szs->fname ); + #endif + return 0; + } + } + + if ( am->log_indent >= 0 ) + fprintf(stdlog,"%*s%sAUTO-ADD %s\n", + am->log_indent, "", + testmode ? "WOULD " : "", + load_path ); + + int stack[10], *stack_end = stack; + int dir_id = 0; + + char path_buf[ARCH_FILE_MAX]; + StringCopyS(path_buf,sizeof(path_buf),path); + char *ptr = path_buf; + bool pt_prefix = false; + + for(;;) + { + ccp start = ptr; + while ( *ptr && *ptr != '/' ) + ptr++; + if ( !*ptr || !ptr[1] ) + break; + + const char save_ch = *++ptr; + *ptr = 0; + bool found = false; + szs_subfile_t *sptr, *send = subfile->list + subfile->used; + for ( sptr = subfile->list; sptr < send; sptr++ ) + { + noPRINT(" -> %s | %s\n",path_buf,sptr->path); + ccp spath = sptr->path; + if ( spath[0] == '.' && spath[1] == '/' ) + spath += 2; + if (!strcmp(path_buf,spath)) + { + found = true; + if ( spath > sptr->path ) + pt_prefix = true; + break; + } + } + + if (found) + { + dir_id = am->sd ? sptr->dir_id : sptr - subfile->list; + PRINT("DIR-FOUND[%u]: %s\n",dir_id,sptr->path); + } + else + { + + if (am->sd) + { + int *stack_ptr; + for ( stack_ptr = stack; stack_ptr < stack_end; stack_ptr++ ) + subfile->list[*stack_ptr].size++; + + sptr = AppendSubfileSZS(am->szs,0,0); + } + else + { + int *stack_ptr; + for ( stack_ptr = stack; stack_ptr < stack_end; stack_ptr++ ) + { + subfile->list[*stack_ptr].size++; + if ( *stack_ptr > dir_id ) + (*stack_ptr)++; + } + + sptr = InsertSubfileSZS(am->szs,++dir_id,0,0); + } + DASSERT(sptr); + + sptr->dir_id = dir_id; + sptr->is_dir = true; + sptr->path = pt_prefix ? STRDUP2("./",path_buf) : STRDUP(path_buf); + PRINT("ADD-DIR[%d]: %s\n",dir_id,sptr->path); + + if (am->sd) + { + am->sd->namepool_size_u8 += ptr - start; + dir_id = am->sd->n_directories++; + } + else if (am->norm) + { + am->norm->namepool_size += ptr - start; + sptr->offset = stack_end - stack + 1; + } + } + + sptr->size++; + *stack_end++ = sptr - subfile->list; + *ptr = save_ch; + } + + lex_t lex; // only valid if 'is_course_lex' + if ( is_course_lex ) + { + InitializeLEX(&lex); + if ( PatchLEX(&lex) && CreateRawLEX(&lex) == ERR_OK ) + size = lex.raw_data_size; + } + + szs_subfile_t *file = 0; + if (am->sd) + { + file = AppendSubfileSZS(am->szs,0,0); + DASSERT(file); + ccp fname = strrchr(path,'/'); + am->sd->namepool_size_u8 += strlen(fname ? fname+1 : path) + 1; + + file->dir_id = dir_id; + file->is_dir = false; + file->path = STRDUP(path); + file->size = size; + am->sd->total_size += ALIGN32(file->size,am->sd->align); + + if (!is_course_lex) + file->load_path = STRDUP(load_path); + PRINT("ADD-FILE[%d]: %s\n",dir_id,path); + } + else if (am->norm) + { + file = InsertSubfileSZS(am->szs,dir_id+1,0,0); + + file->dir_id = dir_id; + file->is_dir = false; + file->path = pt_prefix ? STRDUP2("./",path) : STRDUP(path); + file->size = size; + + if (!is_course_lex) + file->load_path = STRDUP(load_path); + + ccp fname = strrchr(path,'/'); + am->norm->namepool_size += strlen(fname ? fname+1 : path) + 1; + am->norm->total_size += ALIGN32(file->size,opt_align_u8); + + PRINT("ADD-FILE[%d,%zd]: %s, %u bytes\n", + dir_id+1, file-am->szs->subfile.list, path, file->size ); + } + + if (is_course_lex) + { + if (file) + { + file->data_alloced = true; + file->data = lex.raw_data; + lex.raw_data = 0; + } + ResetLEX(&lex); + } + + return 1; +} + +//----------------------------------------------------------------------------- + +int AddMissingFileSZS +( + szs_file_t * szs, // valid szs + ccp fname, // filename to add + file_format_t fform, // valid file format + struct szs_norm_t *norm, // NULL or norm struct + int log_indent // >-1: print log with indention +) +{ + DASSERT(szs); + DASSERT(fname); + DASSERT(fform); + + add_missing_t am = {0}; + am.szs = szs; + am.norm = norm; + am.log_indent = log_indent; + am.print_err = true; + return add_missing_file(fname,fform,&am); +} + +//----------------------------------------------------------------------------- + +enumError AddMissingFiles +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to base directory + struct scan_data_t * sd, // NULL or valid scan data + struct szs_norm_t * norm, // NULL or valid norm data + int log_indent // >-1: print log with indention +) +{ + DASSERT(szs); + DASSERT( sd || norm ); + + if (!SetupAutoAdd()) + return ERR_OK; + + char path_buf[PATH_MAX]; + if (source_dir) + { + ccp path = PathCatPP(path_buf,sizeof(path_buf),source_dir,"course.kmp"); + LoadObjFileListSZS(szs,0,0,path,true,0,0); + } + else + { + szs_subfile_t *sptr, *send = szs->subfile.list + szs->subfile.used; + for ( sptr = szs->subfile.list; sptr < send; sptr++ ) + if ( !strcmp(sptr->path,"./course.kmp") || !strcmp(sptr->path,"course.kmp") ) + break; + if ( sptr >= send ) + return ERR_WARNING; + LoadObjFileListSZS(szs,szs->data+sptr->offset,sptr->size,0,true,0,0); + } + if (!szs->used_file) + return ERR_WARNING; + + add_missing_t am = {0}; + am.szs = szs; + am.sd = sd; + am.norm = norm; + am.log_indent = log_indent; + + int i, insert_count = 0; + for ( i = 0; i < N_DB_FILE_FILE; i++ ) + if (szs->used_file->d[i]) + { + const DbFileFILE_t *ptr = DbFileFILE + i; + if (!IsFileOptionalSZS(szs,ptr)) + insert_count += add_missing_file(ptr->file,ptr->fform,&am); + } + + #if HAVE_PRINT0 + { + szs_subfile_t *sptr, *send = szs->subfile.list + szs->subfile.used; + for ( sptr = szs->subfile.list; sptr < send; sptr++ ) + PRINT("-- %2d.%d %6u %s\n", + sptr->dir_id, sptr->is_dir, sptr->size, sptr->path ); + } + #endif + + return insert_count ? ERR_DIFFER : ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddTestLEX ( szs_file_t *szs, szs_norm_t *norm ) +{ + DASSERT(szs); + if (HaveActivePatchTestLEX()) + { + lex_test_t temp; + memcpy(&temp,&TestDataLEX,sizeof(temp)); + bool empty; + PatchTestLEX(&temp,&empty); + if ( !empty || force_lex_test ) + AddMissingFileSZS(szs,"course.lex",FF_LEX,norm,verbose<1?-1:0); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cut files /////////////// +/////////////////////////////////////////////////////////////////////////////// +// The 'cut files' iterators are placed in this file, because they cover +// all other file formats like CreateSZS(). +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesIMG +( + szs_iterator_t *it, // iterator struct with all infos + bool multi_img // false: mipmaps, true: multi images +) +{ + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + const u8 * data = szs->data; + DASSERT(data); + + int stat = 0; + + Image_t img; + if (!AssignIMG(&img,true,data,szs->size,0,true,it->endian,EmptyString)) + { + if (!img.mipmap) + multi_img = false; + + //--- first evaluate palette data + + int i; + Image_t *iptr; + u8 *last_pal = 0; + for ( i = 0, iptr = &img; iptr && !stat; i++, iptr = iptr->mipmap ) + { + if ( iptr->pal && iptr->pal != last_pal ) + { + it->index++; + it->off = iptr->pal - data; + it->size = iptr->pal_size; + if (multi_img) + snprintf(it->path,sizeof(it->path),"palette-%u.%u.%s", + i, iptr->n_pal, GetPaletteFormatName(iptr->pform,"unknown") ); + else + snprintf(it->path,sizeof(it->path),"palette.%u.%s", + iptr->n_pal, GetPaletteFormatName(iptr->pform,"unknown") ); + it->func_it(it,false); + } + } + + + //--- second: evaluate image data + + for ( i = 0, iptr = &img; iptr && !stat; i++, iptr = iptr->mipmap ) + { + it->index++; + it->off = iptr->data - data; + it->size = iptr->data_size; + ccp formname = GetImageFormatName(iptr->iform,"unknown"); + if (multi_img) + snprintf(it->path,sizeof(it->path),"image-%u.%ux%u.%s", + i, iptr->width, iptr->height, formname ); + else if (i) + snprintf(it->path,sizeof(it->path),"mipmap-%u.%ux%u.%s", + i, iptr->width, iptr->height, formname ); + else + snprintf(it->path,sizeof(it->path),"image.%ux%u.%s", + iptr->width, iptr->height, formname ); + stat = it->func_it(it,false); + } + } + ResetIMG(&img); + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesBREFT_IMG +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size < sizeof(breft_image_t) ) + return -1; + + it->no_recurse++; + + //const u8 * data = szs->data; + //const breft_image_t *bi = (breft_image_t*)data; + + it->index = 0; + it->off = 0; + it->size = sizeof(breft_image_t); + it->is_dir = 0; + StringCopyS(it->path,sizeof(it->path),".BREFT-IMG.header"); + it->func_it(it,false); + + return IterateFilesIMG(it,false); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesTEX +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + // [[2do]] ??? BRSUB support + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size <= sizeof(brsub_header_t) ) + return -1; + + const u8 * data = szs->data; + const u8 * data_end = data + szs->size; + const brsub_header_t *bh = (brsub_header_t*)data; + const uint n_grp = GetSectionNumBRSUB(data,szs->size,it->endian); + const tex_info_t *ti = (tex_info_t*)( bh->grp_offset + n_grp ); + + if ( (u8*)ti >= data_end || memcmp(bh->magic,TEX_MAGIC,sizeof(bh->magic)) ) + return -1; + + it->no_recurse++; + + it->index = 0; + it->off = 0; + it->size = (u8*)ti - data; + it->is_dir = 0; + PrintHeaderNameBRSUB(it->path,sizeof(it->path),bh,n_grp,it->endian); + it->func_it(it,false); + + it->index++; + it->off += it->size; + it->size = sizeof(*ti); + StringCopyS(it->path,sizeof(it->path),".TEX.header"); + it->func_it(it,false); + + if ( be32(&bh->version) == 3 ) + { + const u32 raw_off = 0x40; + const u32 img_off = be32(bh->grp_offset); + if ( img_off > raw_off && img_off <= szs->size ) + { + const bool have_ct_code = szs->fform_arch == FF_TEX_CT + && img_off > CT_CODE_TEX_OFFSET; + + const u32 raw_end = have_ct_code ? CT_CODE_TEX_OFFSET : img_off; + it->index++; + it->off = raw_off; + it->size = raw_end - raw_off; + StringCopyS(it->path,sizeof(it->path),"raw.bin"); + it->func_it(it,false); + + if (have_ct_code) + { + it->index++; + it->off = CT_CODE_TEX_OFFSET; + it->size = img_off - CT_CODE_TEX_OFFSET; + StringCopyS(it->path,sizeof(it->path),"raw.ctcode"); + it->func_it(it,false); + } + } + } + + return IterateFilesIMG(it,false); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesTPL +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size < sizeof(tpl_header_t) + sizeof(tpl_imgtab_t) ) + return -1; + + const tpl_header_t *head; + const tpl_imgtab_t *tab; + const u8 * data = szs->data; + if (!SetupPointerTPL(data,szs->size,0,&head,&tab,0,0,0,0,it->endian)) + return -1; + + it->no_recurse++; + + const uint n_img = it->endian->rd32(&head->n_image); + + it->index = 0; + it->off = (u8*)head - data; + it->size = sizeof(tpl_header_t); + it->is_dir = 0; + StringCopyS(it->path,sizeof(it->path),".TPL.header"); + it->func_it(it,false); + + it->index++; + it->off = (u8*)tab - data; + it->size = sizeof(tpl_imgtab_t) * n_img; + StringCopyS(it->path,sizeof(it->path),".TPL.image-table"); + it->func_it(it,false); + + uint i; + for ( i = 0; i < n_img; i++ ) + { + const tpl_pal_header_t *tpl_pal; + const tpl_img_header_t *tpl_img; + if (SetupPointerTPL(data,szs->size,i,0,0,&tpl_pal,&tpl_img,0,0,it->endian)) + { + if (tpl_pal) + { + it->index++; + it->off = (u8*)tpl_pal - data; + it->size = sizeof(tpl_pal_header_t); + if ( n_img > 0 ) + snprintf(it->path,sizeof(it->path),".palette-%u.header",i); + else + StringCopyS(it->path,sizeof(it->path),".palette.header"); + it->func_it(it,false); + } + + if (tpl_img) + { + it->index++; + it->off = (u8*)tpl_img - data; + it->size = sizeof(tpl_img_header_t); + if ( n_img > 0 ) + snprintf(it->path,sizeof(it->path),".image-%u.header",i); + else + StringCopyS(it->path,sizeof(it->path),".image.header"); + it->func_it(it,false); + } + } + } + + return IterateFilesIMG(it,true); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesBTI +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size < sizeof(bti_header_t) ) + return -1; + + it->index++; + it->off = 0; + it->size = sizeof(bti_header_t); + StringCopyS(it->path,sizeof(it->path),".BTI.header"); + it->func_it(it,false); + + return IterateFilesIMG(it,false); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesBMG +( + szs_iterator_t *it, // iterator struct with all infos + bool multi_img // false: mipmaps, true: multi images +) +{ + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + const u8 * data = szs->data; + const u8 * data_end = data + szs->size; + DASSERT(data); + + if ( !data || szs->size < sizeof(bmg_header_t) + sizeof(bmg_section_t) ) + return -1; + + const bmg_header_t * bh = (bmg_header_t*)data; + if (memcmp(bh->magic,BMG_MAGIC,sizeof(bh->magic))) + return -1; + + it->no_recurse++; + + it->index = 0; + it->off = 0; + it->size = sizeof(bmg_header_t); + it->is_dir = 0; + StringCopyS(it->path,sizeof(it->path),".BMG.header"); + int stat = it->func_it(it,false); + + const bmg_section_t *sect = (bmg_section_t*)(bh+1); + char id_buf[sizeof(sect->magic)+1]; + int idx; + for ( idx = 0; (u8*)sect < data_end && !stat; idx++ ) + { + const uint sect_size = it->endian->rd32(§->size); + if (!sect_size) + break; + + it->index++; + it->off = (u8*)sect - data; + it->size = sect_size; + snprintf(it->path,sizeof(it->path), "BMG.section-%u.%s", + idx, PrintID(sect->magic,sizeof(sect->magic),id_buf) ); + stat = it->func_it(it,false); + + sect = (bmg_section_t*)( (u8*)sect + sect_size ); + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesKCL +( + szs_iterator_t *it, // iterator struct with all infos + bool multi_img // false: mipmaps, true: multi images +) +{ + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + const u8 *data = szs->data; + DASSERT(data); + + kcl_analyze_t ka; + if ( IsValidKCL(&ka,data,szs->size,szs->size,"") >= VALID_ERROR ) + return -1; + + it->no_recurse++; + + const kcl_head_t *kclhead = (kcl_head_t*)data; + u32 sect_off[N_KCL_SECT+1]; + DASSERT( sizeof(sect_off) > sizeof(kclhead->sect_off) ); + + it->index = 0; + it->off = 0; + it->size = sizeof(kcl_head_t); + it->is_dir = 0; + StringCopyS(it->path,sizeof(it->path),".KCL.header.bin"); + int stat = it->func_it(it,false); + + uint i; + for ( i = 0; i < N_KCL_SECT; i++ ) + sect_off[i] = be32(kclhead->sect_off+i); + sect_off[2] += 0x10; + sect_off[N_KCL_SECT] = szs->size; + + static ccp name[N_KCL_SECT] = { "verticies", "normals", "triangles", "spartial-index" }; + static uint size[N_KCL_SECT] = { sizeof(double3), sizeof(double3), + sizeof(kcl_triangle_t), 0 }; + + for ( i = 0; i < N_KCL_SECT && !stat; i++ ) + { + it->off = sect_off[i]; + it->size = sect_off[i+1] - sect_off[i]; + if (size[i]) + snprintf(it->path,sizeof(it->path),"KCL.section-%u.%u_%s.bin", + i+1,it->size/size[i],name[i]); + else + snprintf(it->path,sizeof(it->path),"KCL.section-%u.%s.bin",i+1,name[i]); + stat = it->func_it(it,false); + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int IterateFilesKMP +( + szs_iterator_t *it, // iterator struct with all infos + bool multi_img // false: mipmaps, true: multi images +) +{ + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + const u8 * data = szs->data; + DASSERT(data); + + if ( IsValidKMP(data,szs->size,szs->size,"") >= VALID_ERROR ) + return -1; + + it->no_recurse++; + + kmp_head_info_t hi; + ScanHeadInfoKMP(&hi,data,szs->size); + + it->index = 0; + it->off = 0; + it->size = sizeof(hi.head_size); + it->is_dir = 0; + StringCopyS(it->path,sizeof(it->path),".KMP.header"); + it->func_it(it,false); + + int stat = 0; + uint sect, min_off = 0; + char id_buf[4+1]; + + for ( sect = 0; sect < hi.n_sect && !stat; sect++ ) + { + uint off = ntohl(hi.sect_off[sect]); + if ( off < min_off || off > hi.max_off || off&3 ) + continue; + + it->index++; + off += hi.head_size; + it->off = off; + it->size = 0; + + PrintID(data+off,4,id_buf); + int abbrev_count; + const KeywordTab_t *cmd = ScanKeyword(&abbrev_count,id_buf,kmp_section_name); + if ( cmd && !abbrev_count && cmd->id >= 0 ) + { + const kmp_list_head_t *list = (kmp_list_head_t*) ( data + off ); + if ( cmd->id < KMP_N_SECT ) + { + const uint n = be16(&list->n_entry); + it->size = kmp_entry_size[cmd->id] * n + sizeof(kmp_list_head_t); + if ( cmd->id == KMP_POTI ) + it->size += be16(&list->value) * sizeof(kmp_poti_point_t); + } + else if ( cmd->id == KMP_WIM0 ) + it->size = be32(&list->n_entry) + sizeof(kmp_list_head_t); + } + + snprintf(it->path,sizeof(it->path), "KMP.section-%02u.%s", + sect, id_buf ); + stat = it->func_it(it,false); + } + + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PatchSZS() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int transform_collect_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if ( !term && !it->is_dir ) + { + int *dirty = it->param; + DASSERT(dirty); + szs_file_t *szs = it->szs; + DASSERT(szs); + u8 * data = szs->data + it->off; +// [[analyse-magic]] + switch (GetByMagicFF(data,it->size,it->size)) + { + case FF_BMG: + if (!have_bmg_patch_count) + { + PRINT("### Patch/NOT BMG: %s\n",it->name); + break; + } + + PRINT("### Patch BMG: %s/%s\n",szs->fname,it->name); + { + bmg_t bmg; + enumError err = ScanBMG(&bmg,true,it->name,data,it->size); + if (!err) + { + bmg.szs = szs; + err = UsePatchingListBMG(&bmg); + if ( err == ERR_DIFFER && !CreateRawBMG(&bmg) ) + { + PRINT("### BMG: %u -> %u\n",it->size,bmg.raw_data_size); + if ( bmg.raw_data_size == it->size + || !szs->allow_ext_data && bmg.raw_data_size < it->size ) + { + PATCH_ACTION_LOG("Patch","BMG/RAW","%s\n",it->name); + memcpy(data,bmg.raw_data,it->size); + it->size = bmg.raw_data_size; + *dirty = 1; + } + else if ( szs->allow_ext_data ) + { + PATCH_ACTION_LOG("Patch","BMG","%s\n",it->name); + szs_subfile_t * file + = AppendSubFileList(&szs->ext_data,it->path,false); + DASSERT(file); + file->load_path = (ccp)bmg.raw_data; + file->size = bmg.raw_data_size; + it->off += bmg.raw_data - data; + it->size = bmg.raw_data_size; + bmg.raw_data = 0; + *dirty = 1; + } + else + ERROR0(ERR_WARNING, + "Can't patch BMG because of size [old=%u.new=%u]: %s\n", + it->size, bmg.raw_data_size, it->name ); + } + } + ResetBMG(&bmg); + } + break; + + case FF_KCL: + if (!PatchFileClass(FF_KCL,it->name)) + { + noPRINT("### Patch/NOT KCL: %s\n",it->name); + break; + } + + PRINT("### Patch KCL: %s/%s\n",szs->fname,it->name); + if ( szs->allow_ext_data && !(KCL_MODE&KCLMD_INPLACE) ) + { + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = FF_KCL; + enumError err = ScanRawKCL(&kcl,false,data,it->size,true); + kcl.fast = false; + + if ( !err + && PatchKCL(&kcl) + && CreateRawKCL(&kcl,false) == ERR_OK ) + { + PATCH_ACTION_LOG("Patch","KCL","%s\n",it->name); + if ( it->size == kcl.raw_data_size ) + { + KCL_ACTION_LOG("Overwrite KCL, size %u.\n",it->size); + memcpy(data,kcl.raw_data,it->size); + } + else + { + KCL_ACTION_LOG("Replace KCL, size %u -> %u.\n", + it->size, kcl.raw_data_size ); + szs_subfile_t * file + = AppendSubFileList(&szs->ext_data,it->path,false); + DASSERT(file); + if (kcl.raw_data_alloced) + { + file->load_path = (ccp)kcl.raw_data; + kcl.raw_data = 0; + kcl.raw_data_alloced = 0; + } + else + file->load_path = MEMDUP(kcl.raw_data,kcl.raw_data_size); + it->off += (u8*)file->load_path - data; + it->size = file->size = kcl.raw_data_size; + } + *dirty = 1; + } + ResetKCL(&kcl); + } + else if (PatchRawDataKCL(data,it->size,it->name)) + { + PATCH_ACTION_LOG("Patch","KCL/RAW","%s\n",it->name); + *dirty = 1; + } + break; + + + case FF_KMP: + if (!PatchFileClass(FF_KMP,it->name)) + { + noPRINT("### Patch/NOT KMP: %s\n",it->name); + break; + } + + PRINT("### Patch KMP: %s\n",it->name); + if ( szs->allow_ext_data && !(KMP_MODE&KMPMD_INPLACE) ) + { + kmp_t kmp; + enumError err = ScanRawKMP(&kmp,true,data,it->size); + if ( !err + && PatchKMP(&kmp) + && CreateRawKMP(&kmp) == ERR_OK ) + { + PATCH_ACTION_LOG("Patch","KMP","%s\n",it->name); + if ( it->size == kmp.raw_data_size ) + { + KCL_ACTION_LOG("Overwrite KMP, size %u.\n",it->size); + memcpy(data,kmp.raw_data,it->size); + } + else + { + KMP_ACTION_LOG(false,"Replace KMP, size %u -> %u.\n", + it->size, kmp.raw_data_size ); + + szs_subfile_t * file + = AppendSubFileList(&szs->ext_data,it->path,false); + DASSERT(file); + file->load_path = (ccp)kmp.raw_data; + file->size = kmp.raw_data_size; + it->off += kmp.raw_data - data; + it->size = kmp.raw_data_size; + kmp.raw_data = 0; + } + *dirty = 1; + } + ResetKMP(&kmp); + } + else if (PatchRawDataKMP(data,it->size)) + { + PATCH_ACTION_LOG("Patch","KMP/RAW","%s\n",it->name); + *dirty = 1; + } + break; + + case FF_LEX: + if ( !szs->allow_ext_data || !HavePatchTestLEX()) + { + PRINT("### Patch/NOT LEX: %s\n",it->name); + break; + } + + PRINT("### Patch LEX: %s/%s\n",szs->fname,it->name); + { + lex_t lex; + enumError err = ScanRawLEX(&lex,true,data,it->size,0); + if ( !err && PatchLEX(&lex) && CreateRawLEX(&lex) == ERR_OK ) + { + if (!lex.have_lex) + { + LEX_ACTION_LOG(false,"Remove LEX, old_size %u.\n",it->size); + + szs_subfile_t * file + = AppendSubFileList(&szs->ext_data,it->path,false); + DASSERT(file); + file->removed = true; + it->off = 0; + it->size = 0; + } + else if ( it->size == lex.raw_data_size ) + { + LEX_ACTION_LOG(false,"Overwrite LEX, size %u.\n",it->size); + memcpy(data,lex.raw_data,it->size); + } + else + { + LEX_ACTION_LOG(false,"Replace LEX, size %u -> %u.\n", + it->size, lex.raw_data_size ); + + szs_subfile_t * file + = AppendSubFileList(&szs->ext_data,it->path,false); + DASSERT(file); + file->load_path = (ccp)lex.raw_data; + file->size = lex.raw_data_size; + it->off += lex.raw_data - data; + it->size = lex.raw_data_size; + lex.raw_data = 0; + } + *dirty = 1; + } + PRINT("PATCH/LEX: modified=%d, have_lex:%x\n", + lex.modified, lex.have_lex ); + ResetLEX(&lex); + } + break; + + case FF_BRRES: + szs->brres_type = GetFileClass(FF_BRRES,it->name); + szs->dont_patch_mdl = !( szs->brres_type & PATCH_FILE_MODE ) + || disable_patch_on_load > 0 + || !HavePatchMDL(); + + noPRINT("### Patch%s BRRES: %s [%x]\n", + szs->dont_patch_mdl ? "/NOT" : "", it->name, + szs->brres_type ); + if (!szs->dont_patch_mdl) + PATCH_ACTION_LOG("Enable","BRRES","%s\n",it->name); + break; + + + case FF_MDL: + if ( szs->fform_arch == FF_BRRES && szs->dont_patch_mdl + || disable_patch_on_load > 0 + || !HavePatchMDL() ) + { + noPRINT("### Patch/NOT MDL: %s\n",it->name); + break; + } + + { + noPRINT("### Patch MDL: %s [%x]\n",it->name,szs->brres_type); + const uint stat + = PatchRawDataMDL(data,it->size,szs->brres_type, + szs && szs->fname && *szs->fname ? szs->fname : it->name ); + if (stat) + { + PATCH_ACTION_LOG(" Patch","MDL/RAW","%s%s\n", + it->name, + stat&2 ? " (vector transformation recognized)" : "" ); + *dirty = 1; + } + } + break; + + default: + break; + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PatchSZS ( szs_file_t * szs ) +{ + DASSERT(szs); + const bool patch_lex = HaveActivePatchLEX(); + #ifdef TEST + static int done = 0; + if (!done++) + { + printf("PATCH-COUNT: *=%d, bmg=%d, kcl=%d, kmp=%d, mdl=%d, lex=%d\n", + have_patch_count, + have_bmg_patch_count, + have_kcl_patch_count, + have_kmp_patch_count, + have_mdl_patch_count, + patch_lex ); + } + #endif + + int dirty = 0; + if ( szs->data && szs->size ) + { + if (patch_lex) + dirty |= NormalizeExSZS(szs,0,0,0); + + if ( have_patch_count > 0 || opt_lex_purge ) + { + PRINT("** PatchSZS() **\n"); + ScanTformBegin(); + IterateFilesSZS(szs,transform_collect_func,&dirty,false,-1,-1,SORT_NONE); + TformScriptEnd(); + ClearSpecialFilesSZS(szs); + } + } + + return dirty != 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SetupExtendedSZS /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupExtendedSZS() +{ + static bool done = false; + if (done) + return; + done = true; + + SetupStandardSZS(); + + cut_iter_func[FF_CT1_DATA] = IterateFilesCTCODE; + cut_iter_func[FF_BMG] = IterateFilesBMG; + cut_iter_func[FF_BREFT_IMG] = IterateFilesBREFT_IMG; + cut_iter_func[FF_KCL] = IterateFilesKCL; + cut_iter_func[FF_KMP] = IterateFilesKMP; +// cut_iter_func[FF_LEX] = IterateFilesLEX; // [[lex]] + cut_iter_func[FF_MDL] = IterateFilesMDL; + cut_iter_func[FF_PAT] = IterateFilesPAT; + cut_iter_func[FF_TEX] = IterateFilesTEX; + cut_iter_func[FF_TEX_CT] = IterateFilesTEX; + cut_iter_func[FF_TPL] = IterateFilesTPL; + cut_iter_func[FF_BTI] = IterateFilesBTI; + + //std_iter_func[FF_TEX_CT] = IterateFilesTEX; + //std_iter_func[FF_CT1_DATA] = IterateFilesCTCODE; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CheckSZS /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[check_szs_t]] + +typedef struct check_szs_t +{ + //--- param + + szs_file_t * szs; // pointer to valid SZS + CheckMode_t mode; // print mode + + //--- status + + int warn_count; // number of warnings + int hint_count; // number of hints + int info_count; // number of hints + bool head_printed; // true: header line printed + + //--- misc data + + ColorSet_t col; // color setup + uint img_count; // number of checked images + + //--- vertex counter + + uint *num_vertex; // NULL or pointer to vertex counter + uint *num_mdl; // NULL or pointer to MDL counter + + uint nv_model; // number of vert. in 'course_model.brres' + uint nm_model; // number of MDL's in 'course_model.brres' + uint nv_map; // number of vert. in 'map_model.brres' + uint nm_map; // number of MDL's in 'map_model.brres' + uint nv_vrcorn; // number of vert. in 'vrcorn_model.brres' + uint nm_vrcorn; // number of MDL's in 'vrcorn_model.brres' + + uint nv_total; // total vertex count + uint nm_total; // total number of scanned MDL files + +} check_szs_t; + +/////////////////////////////////////////////////////////////////////////////// + +// predefine function to avoid a warning because of __attribute__ usage +static void print_check_error +( + check_szs_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_WARN|CMOD_HINT|CMOD_SLOT|CMOD_INFO + ccp format, // format of message + ... // arguments + +) __attribute__ ((__format__(__printf__,3,4))); + +static void print_check_error +( + check_szs_t * chk, // valid data structure + CheckMode_t print_mode, // NULL | CMOD_WARN|CMOD_HINT|CMOD_SLOT|CMOD_INFO + ccp format, // format of message + ... // arguments + +) +{ + DASSERT(chk); + DASSERT(chk->szs); + + ccp col; // message color + ccp bol; // begin of line + switch (print_mode) + { + case CMOD_WARNING: + if (!( chk->mode & CMOD_WARNING )) + return; + col = chk->col.warn; + bol = check_bowl; + chk->warn_count++; + break; + + case CMOD_HINT: + if (!( chk->mode & CMOD_HINT )) + return; + col = chk->col.hint; + bol = check_bohl; + chk->hint_count++; + break; + + case CMOD_SLOT: + if (!( chk->mode & CMOD_SLOT )) + return; + col = chk->col.info; + bol = check_bosl; + chk->info_count++; + break; + + case CMOD_INFO: + if (!( chk->mode & CMOD_INFO )) + return; + col = chk->col.info; + bol = check_boil; + chk->info_count++; + break; + + default: + col = 0; + bol = 0; // print head only + break; + } + + if (!chk->head_printed) + { + chk->head_printed = true; + if (chk->mode & (CMOD_HEADER|CMOD_FORCE_HEADER) ) + fprintf(stdlog,"%s* CHECK %s:%s%s\n", + chk->col.heading, + GetNameFF(chk->szs->fform_file,chk->szs->fform_arch), + chk->szs->fname, chk->col.reset ); + } + + if ( bol && format ) + { + fputs(col,stdlog); + fputs(bol,stdlog); + fputs(chk->col.reset,stdlog); + + va_list arg; + va_start(arg,format); + vfprintf(stdlog,format,arg); + va_end(arg); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_vertex_count +( + check_szs_t *chk, + uint vert_counter, + uint mdl_counter, + uint hint_level, + uint warn_level, + ccp model_name +) +{ + DASSERT(chk); + + if (vert_counter) + { + CheckMode_t mode = vert_counter >= warn_level + ? CMOD_WARNING : vert_counter >= hint_level + ? CMOD_HINT : CMOD_INFO; + + if (model_name) + print_check_error( chk, mode, + "File '%s_model.brres' has %u vertices in %u MDL file%s.\n", + model_name, vert_counter, mdl_counter, + mdl_counter == 1 ? "" : "s" ); + else + print_check_error( chk, mode, + "Total of %u vertices in %u MDL file%s found.\n", + vert_counter, mdl_counter, + mdl_counter == 1 ? "" : "s" ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static int check_mdl_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if ( term || it->is_dir ) + return 0; + + if ( it->group == 2 && it->entry >= 0 ) + { + check_szs_t *chk = it->param; + DASSERT(chk); + + mdl_sect2_t *ms = (mdl_sect2_t*)( it->szs->data + it->off ); + const u32 nv = be16(&ms->n_vertex); + chk->nv_total += nv; + if (chk->num_vertex) + *chk->num_vertex += nv; + noPRINT("MDL group %d, entry %d, N=%u: %s\n", + it->group, it->entry, nv, it->path ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int check_brres_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if ( term || it->is_dir ) + return 0; + + const u8 *data = it->szs->data + it->off; + const file_format_t fform = GetByMagicFF(data,it->size,it->size); + if ( fform == FF_BRRES ) + { + check_szs_t *chk = it->param; + DASSERT(chk); + + szs_file_t subszs; + InitializeSubSZS(&subszs,it->szs,it->off,it->size,fform,it->path,false); + subszs.fname = it->path; + CheckBRRES( &subszs, chk->mode | CMOD_FORCE_HEADER, chk ); + subszs.fname = 0; + ResetSZS(&subszs); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int check_file_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if ( term || it->is_dir ) + return 0; + + const u8 *data = it->szs->data+it->off; +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,it->size,it->size); + check_szs_t *chk = it->param; + DASSERT(chk); + + switch(fform) + { + case FF_BRRES: + switch (GetFileClass(FF_BRRES,it->path)) + { + case PFILE_MODEL: + chk->num_vertex = &chk->nv_model; + chk->num_mdl = &chk->nm_model; + break; + + case PFILE_MINIMAP: + chk->num_vertex = &chk->nv_map; + chk->num_mdl = &chk->nm_map; + break; + + case PFILE_VRCORN: + chk->num_vertex = &chk->nv_vrcorn; + chk->num_mdl = &chk->nm_vrcorn; + break; + + default: + chk->num_vertex = 0; + chk->num_mdl = 0; + break; + + } + noPRINT("### %d %s %s\n",it->parent_it!=0,GetNameFF(fform,0),it->path); + break; + + case FF_MDL: + { + noPRINT("### %d %s %s\n",it->parent_it!=0,GetNameFF(fform,0),it->path); + chk->nm_total++; + if (chk->num_mdl) + chk->num_mdl[0]++; + szs_file_t subszs; +// [[fname+]] + InitializeSubSZS(&subszs,it->szs,it->off,it->size,fform,it->path,false); + subszs.fname = it->path; + IterateFilesSZS(&subszs,check_mdl_func,chk,true,0,1,SORT_NONE); + subszs.fname = 0; + ResetSZS(&subszs); + } + break; + + default: + break; + } + + Image_t img; + ScanDataIMG(&img,true,data,it->size); + const ImageGeometry_t *geo = GetImageGeometry(img.iform); + + if (geo) + { + chk->img_count++; + + if ( !img.width || !img.height ) + { + print_check_error( chk, CMOD_WARNING, + "%s %ux%u: Illegal image size: %s/%s\n", + PrintFormat3(img.info_fform,img.iform,img.pform), + img.width, img.height, + it->parent_it ? it->parent_it->path : ".", + it->path ); + goto exit; + } + + uint wd, ht; + for ( wd = 1; !(img.width&wd); wd <<= 1 ) + ; + for ( ht = 1; !(img.height&ht); ht <<= 1 ) + ; + if ( wd != img.width || ht != img.height ) + { + print_check_error( chk, CMOD_WARNING, + "%s %ux%u: Image size not power of 2: %s/%s\n", + PrintFormat3(img.info_fform,img.iform,img.pform), + img.width, img.height, + it->parent_it ? it->parent_it->path : ".", + it->path ); + goto exit; + } + + if ( img.width > 1024 || img.height > 1024 ) + { + print_check_error( chk, CMOD_HINT, + "%s %ux%u: Large image found: %s/%s\n", + PrintFormat3(img.info_fform,img.iform,img.pform), + img.width, img.height, + it->parent_it ? it->parent_it->path : ".", + it->path ); + goto exit; + } + } + + exit: + ResetIMG(&img); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void PrintCheckSummarySZS ( const check_szs_t *chk ) +{ + DASSERT(chk); + DASSERT(chk->szs); + + if ( chk->mode & (CMOD_FOOTER|CMOD_FORCE_FOOTER) ) + { + ccp bind; + char hbuf[60], ibuf[60]; + if (chk->info_count) + { + snprintf(ibuf,sizeof(ibuf)," and %s%u info%s%s", + chk->col.info, + chk->info_count, chk->info_count>1 ? "s" : "", + chk->col.heading ); + bind = ","; + } + else + { + *ibuf = 0; + bind = " and"; + } + + if (chk->hint_count) + snprintf(hbuf,sizeof(hbuf),"%s %s%u hint%s%s", + bind, chk->col.hint, + chk->hint_count, chk->hint_count>1 ? "s" : "", + chk->col.heading ); + else + *hbuf = 0; + + if (chk->warn_count) + fprintf(stdlog, + " %s=> %s%u warning%s%s%s%s for %s:%s%s\n\n", + chk->col.heading, chk->col.warn, + chk->warn_count, chk->warn_count == 1 ? "" : "s", + chk->col.heading, + hbuf, ibuf, GetNameFF_SZS(chk->szs), + chk->szs->fname, chk->col.reset ); + else if ( chk->mode & CMOD_FORCE_FOOTER || chk->hint_count ) + fprintf(stdlog," %s=> no warnings%s%s for %s:%s%s\n\n", + chk->col.heading, + hbuf, ibuf, GetNameFF_SZS(chk->szs), + chk->szs->fname, chk->col.reset ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckSZS +( + // returns number of errors + + szs_file_t * szs, // valid szs + CheckMode_t szs_mode, // print mode for szs file + CheckMode_t sub_mode // print mode for sub file (KCL,KMP,...) +) +{ + DASSERT(szs); + PRINT("CheckSZS(%d,%d)\n",szs_mode,sub_mode); + szs_mode = GetMainCheckMode(szs_mode,true); + sub_mode = GetSubCheckMode(sub_mode,true); + + + //--- setup + + check_szs_t chk; + memset(&chk,0,sizeof(chk)); + SetupColorSet(&chk.col,stdlog); + chk.szs = szs; + chk.mode = szs_mode; + + enum + { + F_REQUIRED = 1, + F_REQUIRED2 = 2, + F_OPTIONAL = 4, + F_IS_OBJECT = 8, + F_FOUND = 0x10, + F_MODIFIED = 0x20, + + F_M_ADDITIONAL = F_REQUIRED | F_REQUIRED2 | F_OPTIONAL | F_FOUND, + F_M_OPTIONAL = F_OPTIONAL | F_FOUND, + }; + + + //--- check BRRES + BRSUB + + IterateFilesSZS(szs,check_brres_func,&chk,false,-1,0,SORT_AUTO); + + + //--- find KCL + + FindSpecialFilesSZS(szs,false); + + int max_cannon = -1; + bool need_ice_brres = false; + bool kcl_valid = false; + kcl_t kcl; + + InitializeKCL(&kcl); + if ( szs->course_kcl_data ) + { + kcl.fname = STRDUP2(szs->fname,"/course.kcl"); + kcl_valid = !ScanKCL( &kcl, false, szs->course_kcl_data, + szs->course_kcl_size, true, sub_mode); + + if (kcl_valid) + { + //---- calc statistics + + uint n = kcl.tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl.tridata.list; + for ( ; n-- > 0; td++ ) + { + if ( td->cur_flag == 0x70 ) + { + need_ice_brres = true; + } + else if (IS_KCL_TYPE(td->cur_flag,0x11)) + { + const int idx = td->cur_flag >> 5; + if ( max_cannon < idx ) + max_cannon = idx; + } + } + max_cannon++; + } + } + + + //--- find LEX + + lex_info_t lexinfo; + if (szs->course_lex_data) + SetupLexInfoByData(&lexinfo,szs->course_lex_data,szs->course_lex_size); + else + InitializeLexInfo(&lexinfo); + + + //--- find KMP + + enumError err = ERR_WARNING; + if (szs->course_kmp_data) + err = LoadObjFileListSZS( szs, szs->course_kmp_data, + szs->course_kmp_size, 0, false, sub_mode, &lexinfo ); + + //--- find MINIMAP + + uint minimap_stat = 0; + if (szs->map_model_data) + { + minimap_stat = 4; + + szs_file_t mapszs; +// [[fname-]] + InitializeSubSZS( &mapszs, szs, szs->map_model_data - szs->data, + szs->map_model_size, FF_UNKNOWN, 0, false ); + mdl_minimap_t mmap; + FindMinimapData(&mmap,&mapszs); + if (mmap.posLD) + minimap_stat |= 1; + if (mmap.posRU) + minimap_stat |= 2; + ResetSZS(&mapszs); + } + + + //--- term on error + + if ( err || !szs->used_file ) + return -1; + + + //--- add global statistics + + if ( global_warn_count || global_hint_count || szs_mode & CMOD_FORCE_HEADER ) + { + chk.mode |= CMOD_FORCE_HEADER; + print_check_error(&chk,0,0); + } + + chk.warn_count += global_warn_count; + chk.hint_count += global_hint_count; + chk.info_count += global_info_count; + + + //--- test unknown files + + DbFlags_t found_flags = 0; + szs_subfile_t *file, *file_end = szs->subfile.list + szs->subfile.used; + for ( file = szs->subfile.list; file < file_end; file++ ) + { + if (file->is_dir) + continue; + + ccp path = file->path; + if ( path[0] == '.' && path[1] == '/' ) + path += 2; + int fidx = FindDbFile(path); + if ( fidx < 0 ) + { + bool found = false; + int i; + for ( i = 0; i < HAVESZS__N; i++ ) + if (strcasecmp(path,have_szs_file[i])) + { + found = true; + break; + } + + if (!found) + print_check_error( &chk, CMOD_HINT, + "Unknown file: ./%s\n", path ); + } + else + { + u8 *flags = szs->used_file->d + fidx; + *flags |= F_FOUND; + + const DbFileFILE_t *ptr = DbFileFILE + fidx; + found_flags |= ptr->flags; + + if (IsFileOptionalSZS(szs,ptr)) + *flags |= F_OPTIONAL; + if ( ptr->flags & DBF_REQUIRED ) + *flags |= F_REQUIRED; + if ( ptr->flags & DBF_REQUIRED2 ) + *flags |= F_REQUIRED2; + + if (DBF_ARCH_SUPPORT(ptr->flags)) + { + *flags |= F_IS_OBJECT; + + sha1_hash_t hash; + SHA1( szs->data+file->offset, file->size, hash ); + + bool found = false; + const s16 *ref = DbFileRefFILE + ptr->ref; + while ( !found && *ref >= 0 ) + { + DASSERT( *ref < N_DB_FILE ); + const DbFile_t *db = DbFile + *ref++; + DASSERT( db->sha1 < N_DB_FILE_SHA1 ); + found = !memcmp(hash,DbFileSHA1[db->sha1].sha1,sizeof(hash)); + } + if (!found) + *flags |= F_MODIFIED; + } + } + } + + + if ( chk.mode & CMOD_HINT ) + { + //--- test special files + + int i; + for ( i = 0; i < HAVESZS__N; i++ ) + if (szs->szs_special[i]) + print_check_error( &chk, CMOD_HINT, + "Special file: ./%s\n",have_szs_file[i]); + + //--- test optional files + + if (need_ice_brres) + { + int i = FindDbFile("ice.brres"); + if ( i > 0 ) + szs->used_file->d[i] = szs->used_file->d[i] & ~F_OPTIONAL | F_REQUIRED; + } + + for ( i = 0; i < N_DB_FILE_FILE; i++ ) + { + if ( ( szs->used_file->d[i] & F_M_OPTIONAL ) == F_M_OPTIONAL ) + { + const DbFileFILE_t *ptr = DbFileFILE + i; + #ifdef TEST0 + print_check_error( &chk, CMOD_HINT, + "Optional file [%02x]: ./%s\n", + szs->used_file->d[i], ptr->file ); + #else + print_check_error( &chk, CMOD_HINT, + "Optional file: ./%s\n", ptr->file ); + #endif + } + } + + //--- test additional files + + for ( i = 0; i < N_DB_FILE_FILE; i++ ) + if ( ( szs->used_file->d[i] & F_M_ADDITIONAL ) == F_FOUND ) + { + const DbFileFILE_t *ptr = DbFileFILE + i; + #ifdef TEST0 + print_check_error( &chk, CMOD_HINT, + "Additional file [%02x]: ./%s\n", + szs->used_file->d[i], ptr->file ); + #else + print_check_error( &chk, CMOD_HINT, + "Additional file: ./%s\n", ptr->file ); + #endif + } + + //--- test modified files + + const DbFileFILE_t *ptr = DbFileFILE; + for ( i = 0; i < N_DB_FILE_FILE; i++, ptr++ ) + if ( szs->used_file->d[i] & F_MODIFIED ) + print_check_error( &chk, CMOD_HINT, + "Modified file: ./%s\n", ptr->file ); + } + + + //--- test missing files + + if ( !(found_flags & DBF_REQUIRED2) ) + print_check_error( &chk, CMOD_WARNING, + "Missing file: ./course_model.brres (or '_d' variant)\n" ); + + int i; + const DbFileFILE_t *ptr = DbFileFILE; + for ( i = 0; i < N_DB_FILE_FILE; i++, ptr++ ) + { + const u8 flag = szs->used_file->d[i]; + if ( flag & F_FOUND ) + continue; + if ( flag & F_REQUIRED || ptr->flags & DBF_REQUIRED ) + { + if (!IsFileOptionalSZS(szs,ptr)) + print_check_error( &chk, CMOD_WARNING, + "Missing file: ./%s\n", ptr->file ); + } + } + + if (szs->required_file) + { + ParamFieldItem_t *ptr = szs->required_file->field, *end; + for ( end = ptr + szs->required_file->used; ptr < end; ptr++ ) + if (!ptr->num) + print_check_error( &chk, CMOD_WARNING, + "Missing unusual file: ./%s\n", ptr->key ); + } + + + + //--- check images + + IterateFilesSZS(szs,check_file_func,&chk,false,-1,0,SORT_AUTO); + if (chk.img_count) + print_check_error( &chk, CMOD_INFO, + "Geometry of %u images checked.\n",chk.img_count); + + + //--- check number of vertices + + print_vertex_count( &chk, chk.nv_model, chk.nm_model, 20000, 50000, "course"); + print_vertex_count( &chk, chk.nv_map, chk.nm_map, 2500, 5000, "map"); + print_vertex_count( &chk, chk.nv_vrcorn, chk.nm_vrcorn, 300, 1000, "vrcorn"); + print_vertex_count( &chk, chk.nv_total, chk.nm_total, 25000, 60000, 0); + + + //--- compare KMP and KCL data + + if (kcl_valid) + { + //--- check cannons + + if ( szs->n_cannon >= 0 ) + { + PRINT("N-CANNON: kmp=%d, kcl=%d\n",szs->n_cannon,max_cannon); + if ( max_cannon > szs->n_cannon ) + print_check_error( &chk, CMOD_WARNING, + "KMP defines only %u cannon%s, but KCL needs %u.\n", + szs->n_cannon, szs->n_cannon == 1 ? "" : "s", + max_cannon ); + else if ( max_cannon < szs->n_cannon ) + print_check_error( &chk, CMOD_HINT, + "KMP defines %u cannon%s, but KCL needs only %u.\n", + szs->n_cannon, szs->n_cannon == 1 ? "" : "s", + max_cannon ); + } + + + //--- check slot 6.1 and ice.brres + + if (need_ice_brres) + { + if (szs->have_ice_brres) + print_check_error( &chk, CMOD_SLOT, + "Track will only run at slot 6.1 (sherbet)," + " because KCL uses 'ice.brres'.\n" ); + else + print_check_error( &chk, CMOD_WARNING, + "KCL need missing file 'ice.brres'" + " ==> Wii may freeze on fall down.\n" ); + } + else if (szs->have_ice_brres) + print_check_error( &chk, CMOD_SLOT, + "Track will also run at slot 6.1 (sherbet).\n" ); + else if ( szs->slot_analyzed && szs_mode & CMOD_VERBOSE ) + print_check_error( &chk, CMOD_SLOT, + "Track will not run at slot 6.1 (sherbet).\n" ); + } + ResetKCL(&kcl); + + + //--- minimap status + + noPRINT("MINIMAP STATUS: %x\n",minimap_stat); + switch (minimap_stat) + { + case 4: + print_check_error( &chk, CMOD_WARNING, + "Missing bones 'posLD' and 'posRU' in minimap MDL.\n" ); + break; + + case 5: + print_check_error( &chk, CMOD_WARNING, + "Missing bone 'posRU' in minimap MDL.\n" ); + break; + + case 6: + print_check_error( &chk, CMOD_WARNING, + "Missing bone 'posLD' in minimap MDL.\n" ); + break; + } + + + //--- slot 3.1 and 7.1 + + if (szs->slot_31_71) + { + if ( szs->slot_31_71 == (SLOT_31_71_SUNDS|SLOT_31_71_PYLON01) ) + print_check_error( &chk, CMOD_SLOT, + "Track will only run at slots 3.1 and 7.1," + " because objects 'sunDS' and 'pylon01' are used.\n" ); + else + print_check_error( &chk, CMOD_SLOT, + "Track will only run at slots 3.1 and 7.1," + " because object '%s' is used.\n", + szs->slot_31_71 & SLOT_31_71_SUNDS ? "sunDS" : "pylon01" ); + } + + + //--- slot 4.2 + + if ( szs->moonview_mdl_stat < 3 ) + { + if ( szs->slot_analyzed && szs_mode & CMOD_VERBOSE ) + { + if ( szs->moonview_mdl_stat == 2 ) + print_check_error( &chk, CMOD_SLOT, + "Track most likely run at slot 4.2." + " All MDL materials found, but some are modified.\n" ); + else if ( szs->moonview_mdl_stat == 1 ) + print_check_error( &chk, CMOD_SLOT, + "Track will not run at slot 4.2," + " because of some missed MDL materials.\n" ); + else + print_check_error( &chk, CMOD_SLOT, + "Track will not run at slot 4.2," + " because no needed MDL material was found.\n" ); + + if ( !(szs->slot_42 & SLOT_42_ALL_PF) ) + print_check_error( &chk, CMOD_SLOT, + "... Also objects 'car_body' and/or 'kart_truck' aren't used.\n" ); + } + else if ( szs->moonview_mdl_stat == 2 && szs->slot_42 & SLOT_42_ALL_PF ) + print_check_error( &chk, CMOD_SLOT, + "Track most likely run at slot 4.2." + " All MDL materials found, but some are modified.\n" ); + } + else if ( szs->slot_42 & SLOT_42_ALL_PF ) + { + print_check_error( &chk, CMOD_SLOT, + "Track will run at slot 4.2," + " because car+truck and all needed MDL materials are defined.\n" ); + } + else if ( szs->slot_42 & SLOT_42_ALL_U ) + { + print_check_error( &chk, CMOD_SLOT, + "Track may run at slot 4.2," + " because car+truck and all needed MDL materials are defined.\n" ); + print_check_error( &chk, CMOD_SLOT, + "... However, the presence flags are not set to 0x3f" + " to support all variants.\n" ); + } + else if ( szs->slot_analyzed && szs_mode & CMOD_VERBOSE ) + { + if ( szs->slot_42 & SLOT_42_KART_TRUCK_U ) + print_check_error( &chk, CMOD_SLOT, + "Track will not run at slot 4.2," + " because object 'car_body' isn't used.\n" ); + else if ( szs->slot_42 & SLOT_42_CAR_BODY_U ) + print_check_error( &chk, CMOD_SLOT, + "Track will not run at slot 4.2," + " because object 'kart_truck' isn't used.\n" ); + else + print_check_error( &chk, CMOD_SLOT, + "Track will not run at slot 4.2," + " because objects 'car_body' and 'kart_truck' aren't used.\n" ); + } + + + //--- slot 6.2 + + if (szs->slot_62) + print_check_error( &chk, CMOD_SLOT, + "Track will only run at slot 6.2," + " because object 'HeyhoShipGBA' is used.\n" ); + else if ( szs->slot_analyzed && szs_mode & CMOD_VERBOSE ) + print_check_error( &chk, CMOD_SLOT, + "Track will not run at slot 6.2," + " because object 'HeyhoShipGBA' isn't used.\n" ); + + + //--- terminate + +#if 1 + PrintCheckSummarySZS(&chk); +#else + + if ( szs_mode & (CMOD_FOOTER|CMOD_FORCE_FOOTER) ) + { + ccp bind; + char hbuf[60], ibuf[60]; + if (chk.info_count) + { + snprintf(ibuf,sizeof(ibuf)," and %s%u info%s%s", + chk.col.info, + chk.info_count, chk.info_count>1 ? "s" : "", + chk.col.heading ); + bind = ","; + } + else + { + *ibuf = 0; + bind = " and"; + } + + if (chk.hint_count) + snprintf(hbuf,sizeof(hbuf),"%s %s%u hint%s%s", + bind, chk.col.hint, + chk.hint_count, chk.hint_count>1 ? "s" : "", + chk.col.heading ); + else + *hbuf = 0; + + if (chk.warn_count) + fprintf(stdlog, + " %s=> %s%u warning%s%s%s%s for %s:%s%s\n\n", + chk.col.heading, chk.col.warn, + chk.warn_count, chk.warn_count == 1 ? "" : "s", + chk.col.heading, + hbuf, ibuf, GetNameFF(szs->fform_file,szs->fform_arch), + szs->fname, chk.col.reset ); + else if ( szs_mode & CMOD_FORCE_FOOTER || chk.hint_count ) + fprintf(stdlog," %s=> no warnings%s%s for %s:%s%s\n\n", + chk.col.heading, + hbuf, ibuf, GetNameFF(szs->fform_file,szs->fform_arch), + szs->fname, chk.col.reset ); + } +#endif + + return chk.warn_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CheckBRRES /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int check_brres +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + if ( term || it->size < 0x0c ) + return 0; + + u8 *data = it->szs->data + it->off; + const file_format_t fform = GetByMagicFF(data,it->size,it->size); + if (IsBRSUB(fform)) + { + check_szs_t *chk = (check_szs_t*)it->param; + DASSERT(chk); + + const int version = it->endian->rd32(data+8); + const brsub_info_t *bi = GetInfoBRSUB(fform,version); + const int ns = GetSectionNumBRSUB(data,it->size,it->endian); + + if ( !bi || bi->warn >= BIMD_FATAL || ns != bi->good_sect ) + print_check_error( chk, CMOD_WARNING, + "%s v%d (%d section%s) will freeze track: %s\n", + GetNameFF(0,fform), version, + ns, ns == 1 ? "" : "s", it->path ); + else if ( bi->warn >= BIMD_FAIL ) + print_check_error( chk, CMOD_WARNING, + "%s v%d (%d section%s) is not displayed correctly: %s\n", + GetNameFF(0,fform), version, + ns, ns == 1 ? "" : "s", it->path ); + else if ( bi->warn >= BIMD_HINT ) + print_check_error( chk, CMOD_HINT, + "Unusual %s version %d: %s\n", + GetNameFF(0,fform), version, it->path ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CheckBRRES +( + // returns number of errors + + szs_file_t * szs, // valid szs + CheckMode_t check_mode, // print mode + check_szs_t * parent_chk // NULL or parent +) +{ + DASSERT(szs); + PRINT("CheckBRRES(0x%x)\n",check_mode); + + check_szs_t chk; + memset(&chk,0,sizeof(chk)); + SetupColorSet(&chk.col,stdlog); + chk.szs = szs; + chk.mode = check_mode; + IterateFilesSZS(szs,check_brres,&chk,false,0,-1,SORT_NONE); + + if (parent_chk) + { + parent_chk->warn_count += chk.warn_count; + parent_chk->hint_count += chk.hint_count; + parent_chk->info_count += chk.info_count; + } + else + PrintCheckSummarySZS(&chk); + + return chk.warn_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// FindSlotsSZS /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef SLOT +#undef ISLOT + +#define SLOT(a,b) slot[a*4+b-5] +#define ISLOT(a,b) (a*4+b-5) + +/////////////////////////////////////////////////////////////////////////////// + +static void set_required_slot +( + int slot[MKW_N_TRACKS], // status vector + uint slot1, // first required slot index + uint slot2 // second required slot index +) +{ + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + if ( i != slot1 && i != slot2 ) + slot[i] = -1; + else if (!slot[i]) + slot[i] = 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindSlotsSZS +( + // returns -1, 0 or +1 + + szs_file_t * szs, // valid szs + int slot[MKW_N_TRACKS], // status vector: + // -1: slot not possible + // 0: unknown + // +1: slot possible + bool *res_special // not NULL: return TRUE, if an other slot + // than 4.2|6.1|6.2 is needed +) +{ + DASSERT(szs); + DASSERT(slot); + + //--- load data + + FindSpecialFilesSZS(szs,false); + + if ( !szs->course_kcl_data || !szs->course_kmp_data ) + { + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + slot[i] = 0; + return 0; + } + + LoadObjFileListSZS( szs, szs->course_kmp_data, + szs->course_kmp_size, 0, false, 0, 0 ); + + bool need_ice_brres = false; + + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = FF_KCL; + enable_kcl_drop_auto++; + if (!ScanKCL( &kcl, false, szs->course_kcl_data, + szs->course_kcl_size, true, 0 )) + { + uint n = kcl.tridata.used; + kcl_tridata_t *td = (kcl_tridata_t*)kcl.tridata.list; + + for ( ; n-- > 0; td++ ) + if ( td->cur_flag == 0x70 ) + { + need_ice_brres = true; + break; + } + ResetKCL(&kcl); + } + enable_kcl_drop_auto--; + + + //--- predefine + + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + slot[i] = 1; + SLOT(4,2) = 0; + SLOT(6,1) = 0; + SLOT(6,2) = 0; + + if (res_special) + *res_special = false; + + //uint pos_count = MKW_N_TRACKS - 3; + //uint neg_count = 0; + + int stat = 1; + + + //--- analyze slots 3.1 & 7.1 + + if (szs->slot_31_71) + { + set_required_slot(slot,ISLOT(3,1),ISLOT(7,1)); + stat = -1; + if (res_special) + *res_special = true; + } + + + //--- analyze slot 4.2 + + if ( !szs->slot_42 || szs->moonview_mdl_stat < 2 ) + SLOT(4,2) = -1; + else // if ( szs->moonview_mdl_stat == 3 ) + SLOT(4,2) = +1; + + + //--- analyze slot 6.1 + + if (need_ice_brres) + { + set_required_slot(slot,ISLOT(6,1),ISLOT(6,1)); + if (!szs->have_ice_brres) + SLOT(6,1) = -1; + stat = -1; + } + else if ( !SLOT(6,1) && szs->have_ice_brres ) + SLOT(6,1) = 1; + + + //--- analyze slot 6.2 + + if (szs->slot_62) + { + set_required_slot(slot,ISLOT(6,2),ISLOT(6,2)); + stat = -1; + } + + + //--- adjust special slots & terminate + + //if (!SLOT(4,2)) SLOT(4,2) = -1; + if (!SLOT(6,1)) SLOT(6,1) = -1; + if (!SLOT(6,2)) SLOT(6,2) = -1; + + return stat; +} + +#undef SLOT +#undef ISLOT + +/////////////////////////////////////////////////////////////////////////////// + +ccp CreateSlotInfo ( szs_file_t * szs ) +{ + DASSERT(szs); + int slot[MKW_N_TRACKS]; + bool need_special; + const int stat = FindSlotsSZS(szs,slot,&need_special); + + if ( szs->is_arena >= ARENA_FOUND ) + return "arena"; + + static char buf[500]; + char *dest = buf; + + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + if ( slot[i] != stat ) + { + if ( dest > buf ) + *dest++ = ','; + dest = snprintfE(dest,buf+sizeof(buf),"%c%u.%u", + slot[i] < 0 ? '-' : slot[i] > 0 ? '+' : '?' , + i/4+1, i%4+1 ); + } + + if ( dest == buf ) + *dest++ = '%'; + *dest++ = 0; + const uint size = dest - buf; + char *res = GetCircBuf(size); + memcpy(res,buf,size); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp CreateBriefSlotInfo ( szs_file_t * szs ) +{ + DASSERT(szs); + int slot[MKW_N_TRACKS]; + bool need_special; + const int stat = FindSlotsSZS(szs,slot,&need_special); + + static char buf[500], *dest = buf; + + uint i, count = 0; + const char sign = stat < 0 ? '+' : '-'; + + for ( i = 0; i < MKW_N_TRACKS; i++ ) + if ( slot[i] && slot[i] != stat ) + { + dest = snprintfE(dest,buf+sizeof(buf),"%c%u%u", + count ? ',' : sign, i/4+1, i%4+1 ); + count++; + } + + *dest++ = 0; + const uint size = dest - buf; + char *res = GetCircBuf(size); + memcpy(res,buf,size); + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ExtractFilesSZS() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct extract_param_t +{ + ccp dest; // destination path + bool have_pt_dir; // true: '.' directory found + bool is_cutting; // true: is cutting a file + bool extract; // true: extract subfiles + bool decode; // true: store subfiles for decoding + bool mipmap; // true: extract mipmaps too + int recurse_level; // current recurse level + int indent; // indention of log messages + u32 align; // found alignment + + ccp basedir; // not NULL: extract only basedir/* + uint basedir_off; // >0: ... + + SubDir_t *sdir; // not NULL: store files here + + file_format_t parent_fform; // file format of parent + + StringField_t include_list; // include usually hidden files + StringField_t exclude_list; // exclude files + FormatField_t decode_list; // list with decode jobs + StringField_t extract_list; // list with extract jobs + FormatField_t order_list; // list of extracted files in sort order + +} extract_param_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int extract_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->param); + szs_file_t * szs = it->szs; + DASSERT(szs); + DASSERT( !szs->file_size || szs->file_size >= szs->size ); + extract_param_t * ep = it->param; + DASSERT(ep); + DASSERT(ep->dest); + + if ( it->size > 0x40000000 ) + it->size = 0; + + + //--- report files with trailing '.' + + const bool is_string_pool = !strcmp(it->path,BRRES_STRING_POOL_FNAME); + + noPRINT("trail=%s\n",it->trail_path); + if ( !is_string_pool && it->trail_path && *it->trail_path == '.' ) + { + if ( it->trail_path[1] == '/' && !it->trail_path[2] ) + ep->have_pt_dir = true; + else + { + char * end = it->trail_path + strlen(it->trail_path) - 1; + const bool have_slash = *end == '/'; + if ( have_slash ) + *end = 0; + noPRINT("TRAILING POINT: %s\n",it->path); + InsertStringField(&ep->include_list,it->path,false); + if ( have_slash ) + *end = '/'; + } + } + + + //--- check basedir + + ccp local_path = it->path; + if ( *local_path == '.' && local_path[1] == '/' ) + local_path += 2; + + if (ep->basedir) + { + if (ep->basedir_off) + { + if (strncasecmp(local_path,ep->basedir,ep->basedir_off)) + ep->basedir_off = 0; + } + else if (it->is_dir) + ep->basedir_off = strcasecmp(local_path,ep->basedir) + ? 0 : strlen(local_path); + + if (!ep->basedir_off) + return 0; + + local_path += ep->basedir_off; + } + + + //--- create dir or file + + FormatFieldItem_t * order_item + = AppendFormatField(&ep->order_list,local_path,false,false); + + char pathbuf[PATH_MAX]; +// [[sdir]] +// ccp pathptr = sdir ? local_path +// : PathCatPP(pathbuf,sizeof(pathbuf),ep->dest,local_path); + ccp pathptr = PathCatPP(pathbuf,sizeof(pathbuf),ep->dest,local_path); + + if (it->is_dir) + { + if ( verbose > 1 || testmode && !analyze_fname ) + { + fprintf(stdlog,"%*s- %sCREATE %s\n", + ep->indent,"", testmode ? "WOULD " : "", pathptr ); + fflush(stdlog); + } + if (ep->sdir) + InsertSubDir(ep->sdir,MemByString(local_path),0); + else if (!testmode) + CreatePath(pathptr,true); + return 0; + } + + + //--- file handling + + DASSERT(!it->is_dir); // now we have only files + DASSERT(order_item); + order_item->num = it->off; + + szs_file_t subszs; +// [[fname+]] + InitializeSubSZS(&subszs,szs,it->off,it->size,FF_UNKNOWN,local_path,false); + + if ( verbose > 1 || testmode && !analyze_fname ) + { + fprintf(stdlog,"%*s- %sEXTRACT%s %s:%s\n", + ep->indent,"", testmode ? "WOULD " : "", + ep->is_cutting ? "/CUT" : "", + GetNameFF(0,subszs.fform_arch), pathptr ); + fflush(stdlog); + } + + ep->align |= it->off; + + if ( analyze_fname && IsBRSUB(subszs.fform_arch) ) + AnalyzeBRSUB(szs,subszs.data,subszs.size,it->name); + + + //--- write extracted file, but don't close file here + + MEM_CHECK; + + string_pool_t sp; + enum { SPM_OFF = 0, SPM_BRRES } sp_mode = SPM_OFF; + if (!opt_raw) + { + switch ((int)szs->fform_arch) + { + case FF_BRRES: + sp_mode = SPM_BRRES; + CollectStringsBRSUB(&sp,true,&subszs,true); + break; + } + } + + File_t F; + if (ep->sdir) + { + memset(&F,0,sizeof(F)); + + bool new_file; + SubFile_t *sf = InsertSubFile(ep->sdir,MemByString(local_path),&new_file); + if ( sf && new_file && subszs.size ) + { + sf->data_alloced = true; + sf->type = szs->fform_arch; + if (sp_mode) + { + sf->size = subszs.size + sp.size; + sf->data = MALLOC(sf->size); + memcpy(sf->data,subszs.data,subszs.size); + memcpy(sf->data+subszs.size,sp.data,sp.size); + } + else + { + sf->data = MEMDUP(subszs.data,subszs.size); + sf->size = subszs.size; + } + } + } + else + { + pathptr = PathCatPP(pathbuf,sizeof(pathbuf),ep->dest,local_path); + + if (opt_links) + { + szs_subfile_t *lptr = FindLinkSZS(szs,it->size,it->off,0); + if (lptr) + { + PRINT("LINK: %s -> %s\n",pathptr,lptr->path); + if (!link(lptr->path,pathptr)) + goto no_create; + } + + szs_subfile_t *file = AppendSubfileSZS(it->szs,it,0); + file->path = STRDUP(pathptr); + file->device = it->size; + file->inode = it->off; + } + + CreateFileOpt(&F,true,pathptr,testmode,0); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs->fatt,0); + size_t wstat = fwrite(subszs.data,1,subszs.size,F.f); + enumError err = wstat != subszs.size ? ERR_WRITE_FAILED : ERR_OK; + + if ( !err && sp_mode && sp.data ) + { + wstat = fwrite(sp.data,1,sp.size,F.f); + if ( wstat != sp.size ) + err = ERR_WRITE_FAILED; + } + + if (err) + FILEERROR1(&F,err, + "Writing %zu bytes failed: %s\n", + subszs.size, pathptr); + } + } + no_create:; + if (sp_mode) + ResetStringPool(&sp); + + if (is_string_pool) + { + char *dest = StringCopyS(pathbuf,sizeof(pathbuf),pathptr) - 4; + if ( dest < pathbuf ) + dest = pathbuf; + StringCopyE(dest,pathbuf+sizeof(pathbuf),".txt"); + + ccp fpath = strrchr(pathbuf,'/'); + InsertStringField(&ep->exclude_list,fpath?fpath+1:pathbuf,false); + InsertStringField(&ep->exclude_list,local_path,false); + + if (!ep->sdir) + { + File_t F; + CreateFileOpt(&F,true,pathbuf,testmode,0); + if (F.f) + { + u32 off = 0; + while ( off < subszs.size && !it->endian->rd32(subszs.data+off) ) + off += 4; + off += 4; + while ( off < subszs.size ) + { + ccp sptr = (ccp)subszs.data + off; + if (!*sptr) + break; + fprintf(F.f,"%s\r\n",sptr); + off += ALIGN32(strlen(sptr)+5,4); + } + } + ResetFile(&F,opt_preserve); + } + } + + + //--- prepare ... + + TryDecompressSZS(&subszs,true); + + bool extract = false, cut_file = false; + if (!ep->is_cutting) + { + //--- decode + + MEM_CHECK; + noPRINT("FF=%d,%s %s\n",subszs.fform_arch, + GetNameFF(0,subszs.fform_arch),PrintID(subszs.data,4,0)); + switch (ep->parent_fform) + { + case FF_BREFT: + if (!memcmp(local_path,"files/",6)) + { + const ccp dest_path = STRDUP2(local_path,".png"); + FormatFieldItem_t *item + = InsertFormatFieldFF(&ep->decode_list, + local_path,FF_BREFT,false,false,0); + + uint n_image; + ExportPNG( + ep->dest, + dest_path, + opt_preserve ? &szs->fatt : 0, + subszs.data, + subszs.size, + 0, + ep->mipmap, + &n_image, + it->endian, + item, + ep->decode, + &ep->exclude_list ); + item->num = n_image ? n_image : 1; + FreeString(dest_path); + } + break; + + default: + break; + } + + ccp ext = ".txt"; + switch (subszs.fform_arch) + { + case FF_BMG: + { + InsertStringField(&ep->exclude_list,STRDUP2(local_path,".txt"),true); + FormatFieldItem_t * item + = InsertFormatField(&ep->decode_list,local_path,false,false,0); + DASSERT(item); + item->fform = subszs.fform_arch; + + if (ep->decode) + { + bmg_t bmg; + ScanBMG(&bmg,true,pathptr,subszs.data,subszs.size); + pathptr = PathCatPPE(pathbuf,sizeof(pathbuf),ep->dest,local_path,".txt"); + if ( verbose > 1 || testmode ) + { + fprintf(stdlog,"%*s- %sDECODE %s:%s\n", + ep->indent,"", testmode ? "WOULD " : "", + GetNameFF(0,subszs.fform_arch), pathptr ); + fflush(stdlog); + } + if (!testmode) + SaveTextXBMG(&bmg,pathptr,true); + ResetBMG(&bmg); + } + } + break; + + + case FF_KCL: + InsertStringField(&ep->exclude_list,STRDUP2(local_path,KCL_MTL_EXT),true); + InsertStringField(&ep->exclude_list,STRDUP2(local_path,KCL_FLAG_EXT),true); + ext = ".obj"; + // fall through + + case FF_KMP: + case FF_LEX: + case FF_MDL: + case FF_PAT: + { + InsertStringField(&ep->exclude_list,STRDUP2(local_path,ext),true); + FormatFieldItem_t * item + = InsertFormatField(&ep->decode_list,local_path,false,false,0); + DASSERT(item); + item->fform = subszs.fform_arch; + + if (ep->decode) + { + pathptr = PathCatPPE(pathbuf,sizeof(pathbuf),ep->dest,local_path,ext); + if ( verbose > 1 || testmode ) + { + fprintf(stdlog,"%*s- %sDECODE %s:%s\n", + ep->indent,"", testmode ? "WOULD " : "", + GetNameFF(0,subszs.fform_arch), pathptr ); + fflush(stdlog); + } + + switch ((int)subszs.fform_arch) + { + case FF_KCL: + { + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fname = pathptr; + ScanKCL(&kcl,false,subszs.data,subszs.size,true,global_check_mode); + if (!testmode) + SaveTextKCL(&kcl,pathptr,true); + if (analyze_fname) + AnalyzeKCL(&kcl); + kcl.fname = 0; + ResetKCL(&kcl); + } + break; + + case FF_KMP: + { + kmp_t kmp; + InitializeKMP(&kmp); + kmp.fname = pathptr; + ScanKMP(&kmp,false,subszs.data,subszs.size,global_check_mode); + if (!testmode) + SaveTextKMP(&kmp,pathptr,true); + if (analyze_fname) + AnalyzeKMP(&kmp,szs->fname); + kmp.fname = 0; + ResetKMP(&kmp); + } + break; + + case FF_LEX: + { + lex_t lex; + InitializeLEX(&lex); + lex.fname = pathptr; + ScanLEX(&lex,false,subszs.data,subszs.size); + if (!testmode) + SaveTextLEX(&lex,pathptr,true); + lex.fname = 0; + ResetLEX(&lex); + } + break; + + case FF_MDL: + { + mdl_t mdl; + InitializeMDL(&mdl); + mdl.fname = pathptr; + #if USE_NEW_CONTAINER_MDL + ScanMDL(&mdl,false,subszs.data,subszs.size, + LinkContainerSZS(szs),global_check_mode); + #else + ScanMDL(&mdl,false,subszs.data,subszs.size, + ContainerSZS(szs),global_check_mode); + #endif + if (!testmode) + SaveTextMDL(&mdl,pathptr,true); + mdl.fname = 0; + ResetMDL(&mdl); + } + break; + + case FF_PAT: + { + pat_t pat; + InitializePAT(&pat); + pat.fname = pathptr; + #if USE_NEW_CONTAINER_PAT + ScanPAT(&pat,false,subszs.data,subszs.size, + LinkContainerSZS(szs),global_check_mode); + #else + ScanPAT(&pat,false,subszs.data,subszs.size, + ContainerSZS(szs),global_check_mode); + #endif + if (!testmode) + SaveTextPAT(&pat,pathptr,true); + pat.fname = 0; + ResetPAT(&pat); + } + break; + } + } + } + break; + + case FF_TPL: + if (analyze_fname) + AnalyzeTPL(szs,subszs.data,subszs.size,it->name); + + // fall through + + case FF_BTI: + case FF_TEX: + case FF_TEX_CT: + { + const ccp dest_path = STRDUP2(local_path,".png"); + FormatFieldItem_t * item + = InsertFormatField(&ep->decode_list,local_path,false,false,0); + DASSERT(item); + item->fform = subszs.fform_arch; + + uint n_image; + ExportPNG(ep->dest, + dest_path, + opt_preserve ? &szs->fatt : 0, + subszs.data, + subszs.size, + 0, + ep->mipmap, + &n_image, + it->endian, + item, + ep->decode, + &ep->exclude_list ); + item->num = n_image ? n_image : 1; + FreeString(dest_path); + } + break; + + default: + break; + } + + if ( subszs.ff_attrib & FFT_EXTRACT ) + { + InsertStringField(&ep->extract_list,local_path,false); + InsertStringField(&ep->exclude_list,STRDUP2(local_path,".d"),true); + extract = ep->extract; + } + else if ( subszs.ff_attrib & FFT_CUT ) + { + InsertStringField(&ep->exclude_list,STRDUP2(local_path,".d"),true); + cut_file = opt_cut && IsValidSZS(&subszs,false) < VALID_ERROR; + } + } + + + //--- extract sub archives or cut file + + if ( extract || cut_file ) + { + pathptr = PathCatPP(pathbuf,sizeof(pathbuf),ep->dest,local_path); + subszs.fname = pathptr; + PRINT("EXTRACT/%s[%s]: %s\n",__FUNCTION__,GetNameFF_SZS(&subszs),subszs.fname); + if (extract) + ExtractFilesSZS(&subszs,ep->recurse_level+1,false,ep->sdir,0); + if (cut_file) + ExtractFilesSZS(&subszs,ep->recurse_level+1,true,ep->sdir,0); + + subszs.fname = EmptyString; + } + ResetSZS(&subszs); + + + //--- now close file (because of time stamps) + + ResetFile(&F,opt_preserve); + return 0; +} + +// local_path local_path it->path local_path it->path local_path it->path local_path it->path local_path + +/////////////////////////////////////////////////////////////////////////////// + +enumError ExtractFilesSZS +( + szs_file_t *szs, // valid szs file + int recurse_level, // recursion level + bool is_cutting, // true: is cutting a file + SubDir_t *sdir, // not NULL: store files here + ccp basedir // not NULL: Extract only files if "/BASEDIR" or "./BASEDIR". + // BASEDIR is like "a/b/c/" without leading slash + // If set, don't create support files. +) +{ + DASSERT(szs); + MEM_CHECK; + PRINT("JOB EXTRACT: %d/%d %d ff=%d,%d[%s], %zu/%zu %s\n", + recurse_level, opt_recurse, is_cutting, + szs->fform_file, szs->fform_arch, + GetNameFF(szs->fform_file,szs->fform_arch), + szs->size, szs->file_size, szs->fname ); + DASSERT( !szs->file_size || szs->file_size >= szs->size ); + + if (analyze_fname) + switch ((int)szs->fform_arch) + { + case FF_BREFF: + case FF_BREFT: + AnalyzeBREFF(szs,szs->data,szs->size,szs->fname); + break; + } + + char dest[PATH_MAX]; + StringCat2S(dest,sizeof(dest),szs->fname,".d"); + PRINT("rec=%d isdir=%d dest=%d,%s\n", + recurse_level, IsDirectory(dest,false), opt_dest!=0, opt_dest ); + if ( !recurse_level && ( opt_dest || !IsDirectory(dest,false) ) ) + { + ccp my_dest = opt_dest; + if (!my_dest) + { + switch (szs->fform_arch) + { + case FF_U8: + case FF_WU8: + case FF_RARC: + case FF_PACK: + case FF_RKC: + my_dest = "\1P/\1N.d/"; + break; + + default: + my_dest = "\1P/\1F.d/"; + break; + } + } + SubstDest(dest,sizeof(dest),szs->fname,my_dest,0,0,true); + noPRINT("new-dest=%s\n",dest); + } + //else if ( recurse_level && !IsArchiveFF(szs->fform_arch) ) + else if (is_cutting) + { + ccp file = strrchr(szs->fname,'/'); + file = file ? file+1 : szs->fname; + snprintf(dest,sizeof(dest),"%.*s.%s.d", + (int)(file - szs->fname), szs->fname, file ); + PRINT("CUT TO: %s\n",dest); + } + + const int indent = 2 * recurse_level; + if ( verbose >= 0 && !recurse_level || verbose > 0 || testmode && !analyze_fname ) + { + fprintf(stdlog,"%s%*s%sEXTRACT %s:%s -> %s\n", + verbose > 1 ? "\n" : "", + indent,"", + testmode ? "WOULD " : "", + GetNameFF_SZS(szs), szs->fname, dest ); + fflush(stdlog); + } + + if ( !opt_overwrite && !opt_update && !sdir ) + { + struct stat st; + if (!stat(dest,&st)) + return ERROR0(ERR_ALREADY_EXISTS,"Destination already exists: %s",dest); + } + + extract_param_t ep; + memset(&ep,0,sizeof(ep)); + InitializeStringField(&ep.include_list); + InitializeStringField(&ep.exclude_list); + InitializeFormatField(&ep.decode_list); + InitializeStringField(&ep.extract_list); + InitializeFormatField(&ep.order_list); + InsertStringField(&ep.exclude_list,SZS_SETUP_FILE,false); + + ep.parent_fform = szs->fform_arch; + ep.sdir = sdir; + ep.dest = dest; + ep.decode = opt_decode; + ep.mipmap = opt_mipmaps >= 0; + ep.recurse_level = recurse_level; + ep.extract = recurse_level < opt_recurse; + ep.is_cutting = is_cutting; + ep.indent = indent + 1; + ep.align = 0x80000000; + ep.basedir = basedir; + + switch (szs->fform_arch) + { + case FF_U8: + case FF_WU8: + InsertStringField(&ep.exclude_list,"course.kcl.d",false); + InsertStringField(&ep.exclude_list,"course.kcl.obj",false); + InsertStringField(&ep.exclude_list,"course.kcl.mtl",false); + InsertStringField(&ep.exclude_list,"course.kcl.flag",false); + InsertStringField(&ep.exclude_list,"course.kmp.d",false); + InsertStringField(&ep.exclude_list,"course.kmp.txt",false); + InsertStringField(&ep.exclude_list,"course.lex.txt",false); + InsertFormatFieldFF(&ep.decode_list,"course.kcl",FF_KCL,false,false,0); + InsertFormatFieldFF(&ep.decode_list,"course.kmp",FF_KMP,false,false,0); + InsertFormatFieldFF(&ep.decode_list,"course.lex",FF_LEX,false,false,0); + break; + + default: + break; + } + + const int cut_files = is_cutting + ? 1 + : recurse_level || IsArchiveFF(szs->fform_arch) ? -1 : 0; + PRINT("cut_files=%d\n",cut_files); + IterateFilesSZS(szs,extract_func,&ep,false,0,cut_files,SORT_NONE); + + + //----- write setup file + + #ifndef __APPLE__ + char *f_data = 0; + size_t f_size = 0; + #endif + + ccp destptr; + FILE *f = 0; + if (!basedir) + { + if (!sdir) + { + destptr = PathCatPP(dest,sizeof(dest),dest,SZS_SETUP_FILE); + f = fopen(destptr,"w"); + } + #ifndef __APPLE__ + else + { + destptr = SZS_SETUP_FILE; + f = open_memstream(&f_data,&f_size); + } + #endif + } + + if (f) + { + u32 align = 1; + if ( ep.align ) + while ( ! (ep.align & align) ) + align <<= 1; + + fputs(text_setup_header_cr,f); + if (szs->obj_name) + fprintf(f, + "# Name of the object:\r\n" + "object-name = %s\r\n" + "\r\n" + , + szs->obj_name ); + + if (szs->obj_id_used) + fprintf(f, + "# ID of the object:\r\n" + "object-id = 0x%x\r\n" + "\r\n" + , + szs->obj_id ); + + fprintf(f, + "# The internal archive format (U8, WU8, BRRES, BREFF or BREFT):\r\n" + "archive-format = %s\r\n" + "\r\n" + "# The file format (YAZ0, YAZ1 or BZ for a compressed archive, other ignored):\r\n" + "file-format = %s\r\n" + "\r\n" + "# An archive format related version number (-1: unknown or not relevant)\r\n" + "version = %d\r\n" + "\r\n" + "# For U8+WU8 archives: Is there a special '.' base directory:\r\n" + "have-pt-dir = %d\r\n" + "\r\n" + "# The minimum and maximum file data positions:\r\n" + "min-data-offset = 0x%x\r\n" + "max-data-offset = 0x%x\r\n" + "\r\n" + "# The calculated alignment of all files:\r\n" + "data-align = 0x%x\r\n" + , + GetNameFF(szs->fform_arch,szs->fform_arch), + GetNameFF(szs->fform_file,szs->fform_file), +// [[version-suffix]] + GetVersionFF(szs->fform_arch,szs->data,szs->size,0), + ep.have_pt_dir, + szs->min_data_off, szs->max_data_off, align ); + + fputs(text_setup_include_cr,f); + WriteStringField(f,destptr,&ep.include_list,"./","\r\n"); + fputs(text_setup_exclude_cr,f); + WriteStringField(f,destptr,&ep.exclude_list,"./","\r\n"); + fputs(text_setup_encode_cr,f); + WriteFormatField(f,destptr,&ep.decode_list,0,"./","\r\n",0); + fputs(text_setup_create_cr,f); + WriteStringField(f,destptr,&ep.extract_list,"./","\r\n"); + fputs(text_setup_order_cr,f); + WriteFormatField(f,destptr,&ep.order_list,0,"./","\r\n",10); + fputs( "\r\n#---------------------------------------" + "---------------------------------------\r\n\r\n",f); + fclose(f); + + #ifndef __APPLE__ + if (sdir) + { + bool new_file; + SubFile_t *sf = InsertSubFile(sdir,MemByString(SZS_SETUP_FILE),&new_file); + if ( sf && new_file ) + { + #if TRACE_ALLOC_MODE > 2 + RegisterAlloc(__FUNCTION__,__FILE__,__LINE__,f_data,f_size,false); + #endif + sf->data_alloced = true; + sf->data = (u8*)f_data; + sf->size = f_size; + } + else + FREE(f_data); + } + #endif + } + + ResetStringField(&ep.include_list); + ResetStringField(&ep.exclude_list); + ResetFormatField(&ep.decode_list); + ResetStringField(&ep.extract_list); + ResetFormatField(&ep.order_list); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetOptBasedir() +{ + if (!opt_basedir) + return 0; + + while ( *opt_basedir == '/' || *opt_basedir == '\\' || *opt_basedir == '.' ) + opt_basedir++; + + char *src, *dest; + int have_slash = 0; + for ( src = dest = opt_basedir; *src; src++ ) + { + if ( *src == '/' || *src == '\\' ) + have_slash++; + else + { + if (have_slash) + { + have_slash = 0; + *dest++ = '/'; + } + *dest++ = *src; + } + } + + if ( dest < src ) + { + *dest++ = '/'; + *dest = 0; + } + else + { + const uint pos = dest - opt_basedir; + opt_basedir = MEMDUP(opt_basedir,pos+1); + opt_basedir[pos] = '/'; + } + + return opt_basedir; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-szs.c b/project/src/lib-szs.c new file mode 100644 index 0000000..4347e51 --- /dev/null +++ b/project/src/lib-szs.c @@ -0,0 +1,4567 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include + +#include "lib-szs.h" +#include "lib-rarc.h" +#include "lib-pack.h" +#include "lib-rkc.h" +#include "lib-brres.h" +#include "lib-breff.h" +#include "lib-image.h" +#include "lib-bzip2.h" +#include "lib-checksum.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// have_szs_file /////////////// +/////////////////////////////////////////////////////////////////////////////// +// order is important, compare enum have_szs_file_t and ct.wiimm.de + +const ccp have_szs_name[HAVESZS__N] = +{ + "lex", // HAVESZS_COURSE_LEX + "objflow", // HAVESZS_OBJFLOW + "ght_item", // HAVESZS_GHT_ITEM + "ght_itemobj", // HAVESZS_GHT_ITEM_OBJ + "ght_kart", // HAVESZS_GHT_KART + "ght_kartobj", // HAVESZS_GHT_KART_OBJ + "itemslot", // HAVESZS_ITEM_SLOT_TABLE + "minigame", // HAVESZS_MINIGAME +}; + +const ccp have_szs_file[HAVESZS__N] = +{ + "course.lex", // HAVESZS_COURSE_LEX + "common/ObjFlow.bin", // HAVESZS_OBJFLOW + "common/GeoHitTableItem.bin", // HAVESZS_GHT_ITEM + "common/GeoHitTableItemObj.bin", // HAVESZS_GHT_ITEM_OBJ + "common/GeoHitTableKart.bin", // HAVESZS_GHT_KART + "common/GeoHitTableKartObj.bin", // HAVESZS_GHT_KART_OBJ + "ItemSlotTable/ItemSlotTable.slt", // HAVESZS_ITEM_SLOT_TABLE + "common/minigame.kmg", // HAVESZS_MINIGAME +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs_file_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSZS ( szs_file_t * szs ) +{ + DASSERT(szs); + memset(szs,0,sizeof(*szs)); + szs->fname = EmptyString; + InitializeStringPool(&szs->string_pool); + szs->endian = &be_func; + szs->n_cannon = -1; + szs->ff_version = -1; + + InitializeSubfileList(&szs->subfile); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSubSZS +( + szs_file_t *szs, // SZS to initialize + szs_file_t *base, // valid base SZS + uint off, // offset of selected data within 'base' + uint size, // not NULL: size of selected data + file_format_t fform, // not FF_UNKNOWN: use this type + ccp fname, // NULL or filename + bool decompress // true: decompress if possible +) +{ + DASSERT(szs); + DASSERT(base); + DASSERT( off <= base->size ); + DASSERT( off + size <= base->size ); + + InitializeSZS(szs); + szs->parent = base; + + if ( off <= base->size ) + { + szs->data = base->data + off; + szs->size = base->size - off; + szs->file_size = base->file_size ? base->file_size - off : 0; + szs->parent_off = off; + + if ( size && szs->size > size ) + szs->size = size; + } + + if ( fform == FF_UNKNOWN ) + { + fform = RepairMagicByOpt(0,szs->data,szs->file_size,szs->file_size, + FF_UNKNOWN, fname ); + PRINT("InitializeSubSZS() new-ff=%s by %s\n", + GetNameFF(0,fform), fname ); + } + + szs->fform_file = szs->fform_arch = szs->fform_current = fform; + szs->ff_attrib = GetAttribFF(szs->fform_arch); +// [[version-suffix]] + szs->ff_version = GetVersionFF(szs->fform_arch,szs->data,szs->size,0); + + PRINT("InitializeSubSZS() ff=%s, vers=%d\n", + GetNameFF(szs->fform_file,szs->fform_arch), szs->ff_version ); + + if (decompress) + TryDecompressSZS(szs,true); + + UseContainerData(&szs->container,false,SetupContainerSZS(base)); + + szs->brres_type = base->brres_type; + szs->dont_patch_mdl = base->dont_patch_mdl; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignSZS +( + szs_file_t *szs, // SZS to initialize + bool init_szs, // true: InitializeSZS(), false: ResetSZS() + void *data, // data to assign + uint size, // size of 'data' + bool move_data, // true: free 'data' on reset + file_format_t fform, // not FF_UNKNOWN: use this type + ccp fname // NULL or file names +) +{ + DASSERT(szs); + + if (!init_szs) + ResetSZS(szs); + InitializeSZS(szs); + szs->file_size = data ? size : 0; + if (fname) + szs->fname = STRDUP(fname); + +// [[analyse-magic+]] +// [[ff_fallback]] + if ( fform == FF_UNKNOWN ) + fform = RepairMagicByOpt(0,data,size,size,FF_UNKNOWN,szs->fname); + + if (IsCompressedFF(fform)) + { + szs->cdata = data; + szs->csize = szs->file_size; + szs->cdata_alloced = data && move_data; + szs->fform_file = fform; + DecompressSZS(szs,true,0); + } + else + { + szs->data = data; + szs->size = szs->file_size; + szs->data_alloced = data && move_data; + szs->fform_file = fform; + szs->fform_arch = fform; + szs->fform_current = fform; + szs->ff_attrib = GetAttribFF(fform); +// [[version-suffix]] + szs->ff_version = GetVersionFF(szs->fform_arch,szs->data,szs->size,0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSZS ( szs_file_t * szs ) +{ + if (szs) + { + if (szs->cdata_alloced) + FREE(szs->cdata); + if (szs->data_alloced) + FREE(szs->data); + if (szs->obj_name_alloced) + FreeString(szs->obj_name); + FreeContainer(szs->old_container); + ResetContainer(&szs->container); + ResetFileSZS(szs,true); + FreeString(szs->fname); + FreeString(szs->dest_fname); + FreeString(szs->cache_fname); + FREE(szs->used_file); + if (szs->required_file) + { + ResetParamField(szs->required_file); + FREE(szs->required_file); + } + if (szs->special_file) + { + ResetParamField(szs->special_file); + FREE(szs->special_file); + } + ResetSubfileList(&szs->ext_data); + + InitializeSZS(szs); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetFileSZS ( szs_file_t * szs, bool remove_list ) +{ + if (szs) + { + while (szs->brsub_list) + { + brsub_list_t * ptr = szs->brsub_list; + szs->brsub_list = ptr->next; + FREE(ptr); + } + + ResetStringPool(&szs->string_pool); + + if (remove_list) + ResetSubfileList(&szs->subfile); + else + PurgeSubfileList(&szs->subfile); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +szs_subfile_t * FindLinkSZS + ( szs_file_t *szs, dev_t device, ino_t inode, const szs_subfile_t *exclude ) +{ + DASSERT(szs); + if ( !inode && !device ) + return 0; + + szs_subfile_t *ptr = szs->subfile.list; + szs_subfile_t *end = ptr + szs->subfile.used; + for ( ; ptr < end; ptr++ ) + if ( ptr != exclude && ptr->inode == inode && ptr->device == device ) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ClearContainerSZS ( szs_file_t * szs ) +{ + DASSERT(szs); + FreeContainer(szs->old_container); + ResetContainer(&szs->container); +} + +/////////////////////////////////////////////////////////////////////////////// + +Container_t * SetupContainerSZS ( szs_file_t * szs ) +{ + DASSERT(szs); + + u8 *data; + uint size; + CopyMode_t mode; + + if (szs->data) + { + data = szs->data; + size = szs->size; + mode = szs->data_alloced ? CPM_MOVE : CPM_LINK; + szs->data_alloced = false; + } + else + { + data = szs->cdata; + size = szs->csize; + mode = szs->cdata_alloced ? CPM_MOVE : CPM_LINK; + szs->cdata_alloced = false; + } + + #ifdef TEST + if (AssignContainer(&szs->container,szs->container.protect_level,data,size,mode)) + DumpInfoContainer(stdlog,collog,2,"SZS: ",&szs->container,0); + #else + AssignContainer(&szs->container,szs->container.protect_level,data,size,mode); + #endif + return &szs->container; +} + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * LinkContainerSZS ( szs_file_t * szs ) +{ + DASSERT(szs); + if (!IsValidContainer(&szs->container)) + SetupContainerSZS(szs); + return LinkContainerData(&szs->container); +} + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * MoveContainerSZS ( szs_file_t * szs ) +{ + DASSERT(szs); + if (!IsValidContainer(&szs->container)) + SetupContainerSZS(szs); + return MoveContainerData(&szs->container); +} + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * ContainerSZS ( szs_file_t * szs ) +{ + // old [[container]] will be [[obsolete+]] 2016-06 -> or in 2018 + + DASSERT(szs); + if (!szs->old_container) + { + if (!IsValidContainer(&szs->container)) + SetupContainerSZS(szs); + szs->old_container = UseSubContainer(0,szs->container.cdata); + } + + return AllocContainer(szs->old_container); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadSZS +( + szs_file_t * szs, // valid szs + ccp fname, // valid pointer to filenname + bool decompress, // decompress after loading + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + bool mark_readonly // true: the file will never written +) +{ + MEM_CHECK; + DASSERT(szs); + DASSERT(fname); + PRINT("LoadSZS(%d,%d,%d) fname=%s\n",decompress,ignore_no_file,mark_readonly,fname); + + ResetSZS(szs); + if (mark_readonly) + MarkReadonlySZS(szs); + + File_t F; + enumError err = OpenFILE(&F,true,fname,ignore_no_file,false); + if (err) + return err; + + if (F.is_stdio) + { + uint size = 0, used = 0; + u8 *data = 0; + + while (!feof(F.f)) + { + uint space = size - used; + if ( space < 0x100 ) + { + PRINT("READ STDIN, REALLOC, used: %u/%u\n",used,size); + size = 12*size/10 + 1024*1024; + data = REALLOC(data,size); + space = size - used; + } + const size_t rd_size = fread(data+used,1,space,F.f); + if ( rd_size > 0 ) + used += rd_size; + else + if (ferror(F.f)) + break; + } + + szs->cdata_alloced = true; + szs->csize = used; + szs->cdata = REALLOC(data,used); + } + else + { + szs->cdata_alloced = true; + szs->csize = F.st.st_size; + szs->cdata = MALLOC(szs->csize); + + const size_t rd_size = fread(szs->cdata,1,szs->csize,F.f); + if ( rd_size != szs->csize ) + { + ERROR1(ERR_READ_FAILED,"Can't read file: %s\n",fname); + ResetFile(&F,false); + return ERR_READ_FAILED; + } + szs->fname = F.fname; + F.fname = 0; + SetFileAttrib(&szs->fatt,&F.fatt,0); + ResetFile(&F,false); + } + +// [[analyse-magic+]] + szs->fform_file + = RepairMagicByOpt(0,szs->cdata,szs->csize,szs->csize,FF_UNKNOWN,fname); + + if (!IsCompressedFF(szs->fform_file)) + { + szs->fform_arch = szs->fform_file; + szs->fform_current = szs->fform_file; + szs->data = szs->cdata; + szs->size = szs->csize; + szs->file_size = szs->csize; + szs->data_alloced = szs->cdata_alloced; + + szs->cdata = 0; + szs->csize = 0; + szs->cdata_alloced = false; + } + szs->ff_attrib = GetAttribFF(szs->fform_file); +// [[version-suffix]] + szs->ff_version = GetVersionFF(szs->fform_arch,szs->data,szs->size,0); + + if (decompress) + { + enumError err = DecompressSZS(szs,true,0); + return err ? err : DecodeWU8(szs); + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveSZS +( + szs_file_t * szs, // valid szs + ccp fname, // valid pointer to filenname + bool overwrite, // true: overwrite data + bool compress // false: store decompressed + // true: store compressed +) +{ + MEM_CHECK; + DASSERT(szs); + DASSERT(fname); + + u8 * data; + size_t size; + + if (compress) + { + CompressSZS(szs,true); + data = szs->cdata; + size = szs->csize; + } + else + { + DecompressSZS(szs,true,0); + data = szs->data; + size = szs->size; + } + + if (!data) + return ERROR0(ERR_INVALID_DATA,"No SZS data"); + + File_t F; + enumError err = CreateFILE(&F,true,fname,testmode,false,overwrite,true,false); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs->fatt,0); + size_t wstat = fwrite(data,1,size,F.f); + if ( wstat != size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Saving file (%zu bytes) failed: %s\n",size,fname); + } + enumError err2 = ResetFile(&F,opt_preserve); + + if ( compress && !err ) + LinkCacheSZS(szs,fname); + + return err ? err : err2; +} + +/////////////////////////////////////////////////////////////////////////////// + +void LinkCacheRAW +( + // link/copy only files with data! + + ccp cache_fname, // NULL or path to cache file + ccp src_fname, // source filename + cvp data, // data of link() failed + uint size // size of data +) +{ + if ( cache_fname && src_fname && data && size ) + { + PRINT("LinkCacheRAW(%d,%u) %s -> %s\n",data!=0,size,src_fname,cache_fname); + unlink(cache_fname); + if ( link(src_fname,cache_fname)) + { + File_t F; + CreateFile(&F,false,cache_fname,FM_SILENT|FM_REMOVE); + if (F.f) + fwrite(data,1,size,F.f); + ResetFile(&F,false); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MarkReadonlySZS +( + szs_file_t * szs // valid szs +) +{ + noPRINT("MarkReadonlySZS()\n"); + DASSERT(szs); + + szs->readonly = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +size_t GetDecompressedSizeYAZ +( + const void * data, // source data + size_t data_size // size of 'data' +) +{ + DASSERT(data); + + const u8 * src = data; + const u8 * src_end = src + data_size; + u8 code = 0; + int code_len = 0; + size_t total_size = 0; + + while ( src < src_end ) + { + if (!code_len--) + { + code = *src++; + code_len = 7; + } + + if ( code & 0x80 ) + { + // 1 byte direct + total_size++; + } + else + { + // rle part + + int n = *src++ >> 4; + if (!n) + n = *src++ + 0x12; + else + n += 2; + DASSERT( n >= 3 && n <= 0x111 ); + + if ( src > src_end ) + break; + total_size += n; + } + code <<= 1; + } + ASSERT( src <= src_end+2 ); + return total_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecompressYAZ +( + // returns: + // ERR_OK: compression done + // ERR_WARNING: silent==true: dest buffer too small + // ERR_INVALID_DATA: invalid source data + + const void * data, // source data + size_t data_size, // size of 'data' + void * dest_buf, // destination buffer (decompressed data) + size_t dest_buf_size, // size of 'dest_buf' + size_t *write_status, // number of written bytes + ccp fname, // file name for error messages + int yaz_version, // yaz version for error messages (0|1) + bool silent, // true: don't print error messages + FILE *hexdump // not NULL: write decrompression hex-dump +) +{ + DASSERT(data); + DASSERT(dest_buf); + DASSERT(dest_buf_size); + TRACE("DecompressYAZ(vers=%d) src=%p+%zu, dest=%p+%zu\n", + yaz_version, data, data_size, dest_buf, dest_buf_size ); + + const u8 * src = data; + const u8 * src_end = src + data_size; + u8 * dest = dest_buf; + u8 * dest_end = dest + dest_buf_size; + u8 code = 0; + int code_len = 0; + + uint count0 = 0; + uint count1 = 0; + uint count2 = 0; + uint count3 = 0; + + int addr_fw = 0; + char buf[12]; + if (hexdump) + { + fprintf(hexdump,"\n" + "# size of compressed data: %#8zx = %9zu\n" + "# size of de-compressed data: %#8zx = %9zu\n" + "\n", + data_size, data_size, dest_buf_size, dest_buf_size ); + addr_fw = snprintf(buf,sizeof(buf),"%zx",dest_buf_size-1)+1; + } + + while ( src < src_end && dest < dest_end ) + { + if (!code_len--) + { + if (hexdump) + { + count0++; + fprintf(hexdump, + "%*x: %02x -- -- : type byte\n", + addr_fw, (uint)(src-(u8*)data), *src ); + } + + code = *src++; + code_len = 7; + } + + if ( code & 0x80 ) + { + if (hexdump) + { + count1++; + fprintf(hexdump, + "%*x: %02x -- -- : copy direct\n", + addr_fw, (uint)(src-(u8*)data), *src ); + } + + // copy 1 byte direct + *dest++ = *src++; + } + else + { + // rle part + + const u8 b1 = *src++; + const u8 b2 = *src++; + const u8 * copy_src = dest - ( ( b1 & 0x0f ) << 8 | b2 ) - 1; + + int n = b1 >> 4; + if (!n) + n = *src++ + 0x12; + else + n += 2; + DASSERT( n >= 3 && n <= 0x111 ); + + noPRINT_IF ( copy_src + n > dest, + "RLE OVERLAP: copy %zu..%zu -> %zu\n", + copy_src - szs->data, + copy_src + n - szs->data, + dest - szs->data ); + + if ( copy_src < (u8*)dest_buf ) + { + if (write_status) + *write_status = dest - (u8*)dest_buf; + if (!silent) + ERROR0(ERR_INVALID_DATA, + "YAZ%u data corrupted:" + " Back reference points before beginning of data: %s\n", + yaz_version, fname ? fname : "?" ); + return ERR_INVALID_DATA; + } + + if ( dest + n > dest_end ) + { + // first copy as much as possible + while ( dest < dest_end ) + *dest++ = *copy_src++; + + if (write_status) + *write_status = dest - (u8*)dest_buf; + return silent + ? ERR_WARNING + : ERROR0(ERR_INVALID_DATA, + "YAZ%u data corrupted:" + " Decompressed data larger than specified (%zu>%zu): %s\n", + yaz_version, + GetDecompressedSizeYAZ(data,data_size), + dest_buf_size, + fname ? fname : "?" ); + } + + if (hexdump) + { + if ( n < 0x12 ) + { + count2++; + fprintf(hexdump,"%*x: %02x %02x --", + addr_fw, (uint)(src-2-(u8*)data), src[-2], src[-1] ); + } + else + { + count3++; + fprintf(hexdump,"%*x: %02x %02x %02x", + addr_fw, (uint)(src-3-(u8*)data), + src[-3], src[-2], src[-1] ); + } + fprintf(hexdump," : copy %03x off %04d:", + n, (uint)(copy_src-dest) ); + int max = n < 10 ? n : 10; + const u8 *hex_src = copy_src; + + // copy data before hexdump + while ( n-- > 0 ) + *dest++ = *copy_src++; + + while ( max-- > 0 ) + fprintf(hexdump," %02x",*hex_src++); + if ( hex_src != copy_src ) + fputs(" ...\n",hexdump); + else + fputc('\n',hexdump); + } + else + { + // don't use memcpy() or memmove() here because + // they don't work with self referencing chunks. + while ( n-- > 0 ) + *dest++ = *copy_src++; + } + } + + code <<= 1; + } + ASSERT( src <= src_end ); + ASSERT( dest <= dest_end ); + + if (hexdump) + fprintf(hexdump,"\n" + "# %u type bytes, %u single bytes, %u+%u back references\n" + "\n", + count0, count1, count2, count3 ); + + if (write_status) + *write_status = dest - (u8*)dest_buf; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearCompressedSZS ( szs_file_t * szs ) +{ + if (szs->cdata_alloced) + { + szs->cdata_alloced = false; + FREE(szs->cdata); + } + szs->cdata = 0; + szs->csize = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DecompressSZS +( + szs_file_t *szs, // valid SZS source, use cdata + bool rm_compressed, // true: remove compressed data + FILE *hexdump // not NULL: write decrompression hex-dump +) +{ + TRACE("DecompressSZS(%p,%d)\n",szs,rm_compressed); + DASSERT(szs); + + if ( !szs->csize || !szs->cdata || szs->data ) + return ERR_OK; + + u8 *cdata = szs->cdata; + if (!memcmp(cdata,BZ_MAGIC,4)) + return DecompressBZ(szs,rm_compressed); + + const yaz0_header_t *yaz0 = (yaz0_header_t*)cdata; + + if ( !memcmp(yaz0->magic,XYZ0_MAGIC,sizeof(yaz0->magic)) ) + { + cdata = DecodeXYZ(0,cdata,szs->csize); + yaz0 = (yaz0_header_t*)cdata; + } + + if ( memcmp(yaz0->magic,YAZ0_MAGIC,sizeof(yaz0->magic)) + && memcmp(yaz0->magic,YAZ1_MAGIC,sizeof(yaz0->magic)) ) + { + return ERROR0(ERR_INVALID_DATA,"Invalid Yaz* magic!\n"); + } + + szs->data_alloced = true; + szs->file_size = szs->size = ntohl(yaz0->uncompressed_size); + TRACE("U-SIZE: %zu\n",szs->size); + szs->data = CALLOC(1,szs->size); + + enumError err + = DecompressYAZ( cdata + sizeof(yaz0_header_t), + szs->csize - sizeof(yaz0_header_t), + szs->data, + szs->size, + &szs->size, + szs->fname, + yaz0->magic[3] - '0', + false, + hexdump ); + if (err) + return err; + +// [[analyse-magic]] + szs->fform_arch = szs->fform_current = GetByMagicFF(szs->data,szs->size,szs->size); + szs->ff_attrib = GetAttribFF(szs->fform_arch); +// [[version-suffix]] + szs->ff_version = GetVersionFF(szs->fform_arch,szs->data,szs->size,0); + + if ( cdata != szs->cdata ) + FREE(cdata); + + ClearContainerSZS(szs); + if (rm_compressed) + ClearCompressedSZS(szs); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool TryDecompressSZS +( + szs_file_t *szs, // valid SZS source, use data if not NULL + bool rm_compressed // true: remove compressed data +) +{ + DASSERT(szs); + if (!IsCompressedFF(szs->fform_arch)) + return false; + + if (szs->data) + { + if ( szs->cdata && szs->cdata_alloced ) + FREE(szs->cdata); + + szs->cdata = szs->data; + szs->data = 0; + szs->csize = szs->size; + szs->size = 0; + szs->cdata_alloced = szs->data_alloced; + szs->data_alloced = false; + } + + return DecompressSZS(szs,rm_compressed,0) == ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ClearUncompressedSZS ( szs_file_t * szs ) +{ + DASSERT(szs); + + ClearSpecialFilesSZS(szs); + if (szs->data_alloced) + { + szs->data_alloced = false; + FREE(szs->data); + } + szs->data = 0; + szs->file_size = szs->size = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ClearSpecialFilesSZS +( + szs_file_t * szs // valid szs +) +{ + DASSERT(szs); + + szs->special_done = false; + szs->course_kcl_data = 0; + szs->course_kcl_size = 0; + szs->course_kmp_data = 0; + szs->course_kmp_size = 0; + szs->course_lex_data = 0; + szs->course_lex_size = 0; + szs->course_model_data = 0; + szs->course_model_size = 0; + szs->course_d_model_data = 0; + szs->course_d_model_size = 0; + szs->vrcorn_model_data = 0; + szs->vrcorn_model_size = 0; + szs->map_model_data = 0; + szs->map_model_size = 0; + szs->have_ice_brres = 0; + + if (szs->special_file) + ResetParamField(szs->special_file); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// YAZ helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct yaz_res_t +{ + u16 ref; + u16 len; +} +yaz_res_t; + +//----------------------------------------------------------------------------- + +typedef struct yaz_compr_t +{ + uint secure_size; + + uint dest_buf_size; + u8 * dest_buf; + u8 * dest_ptr; + u8 * dest_end; + + uint src_len; + const u8 * src; + const u8 * src_end; + + szs_file_t * szs; // reference file for cache operations + +} +yaz_compr_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void SetupDestYAZ ( yaz_compr_t *yaz, u32 secure_size ) +{ + DASSERT(yaz); + DASSERT(secure_sizesecure_size = secure_size; + yaz->dest_buf_size = sizeof(iobuf) - secure_size; + yaz->dest_buf = (u8*)iobuf; + yaz->dest_ptr = yaz->dest_buf; + yaz->dest_end = yaz->dest_buf + yaz->dest_buf_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void GrowDestYAZ ( yaz_compr_t *yaz ) +{ + DASSERT(yaz); + + const uint dest_pos = yaz->dest_ptr - yaz->dest_buf; + + yaz->dest_buf_size *= 2; + const uint new_size = yaz->dest_buf_size + yaz->secure_size; + + TRACE("GrowDestYAZ(), new size; 0x%x = %u\n",new_size,new_size); + + if ( yaz->dest_buf == (u8*)iobuf ) + { + yaz->dest_buf = MALLOC(new_size); + memcpy(yaz->dest_buf,iobuf,dest_pos); + } + else + yaz->dest_buf = REALLOC(yaz->dest_buf,new_size); + + yaz->dest_ptr = yaz->dest_buf + dest_pos; + yaz->dest_end = yaz->dest_buf + yaz->dest_buf_size; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// YAZ compression, Back Tracking /////////////// +/////////////////////////////////////////////////////////////////////////////// +// back tracking + +const uint BACK_TRACK_MAX_DEPTH = 50; + +/////////////////////////////////////////////////////////////////////////////// + +static uint BackTrackVal +( + // returns a evaluation value + + yaz_res_t *yaz_buf, // yaz buffer + uint yaz_size, // number of elements in 'yaz_buf' + yaz_res_t *yptr, // pointer the next point to analyze + int max_depth, // max depth of analysis + uint first_step, // true: force byte copy + uint abort_val // abort if best_val > abort_val +) +{ + //--- setup + + DASSERT( max_depth <= BACK_TRACK_MAX_DEPTH); + + yaz_res_t *yaz_end = yaz_buf + yaz_size; + if (!abort_val) + abort_val = M1(abort_val); + + struct stack_t + { + yaz_res_t *yptr; // pointer to yaz_res_t + int clen; // code len + int depth; // count down counter + }; + struct stack_t stack[BACK_TRACK_MAX_DEPTH+1]; + + + //--- put the first job to the stack + + struct stack_t *sp = stack; + if ( yptr->len >= 3 && !first_step ) + { + sp->yptr = yptr + yptr->len; + sp->clen = yptr->len < 0x12 ? 17 : 25; + } + else + { + sp->yptr = yptr + 1; + sp->clen = 9; + } + sp->depth = max_depth - 1; + sp++; + + + //--- main loop + + uint best_val = 0; + + do + { + //--- get last element from stack + + sp--; + yaz_res_t *yp = sp->yptr; + int clen = sp->clen; + int depth = sp->depth; + + while ( yp < yaz_end && depth > 0 ) + { + depth--; + if ( yp->len >= 3 ) + { + // put new alternative to stack + + sp->yptr = yp + yp->len; + sp->clen = clen + ( yp->len < 0x12 ? 17 : 25 ); + sp->depth = depth; + sp++; + } + + //--- process single byte variant directly + + yp++; + clen += 9; + } + + + //-- evaluate + + const uint val = ((yp-yptr+1)<<11) - clen; + #if 0 + if (verbose>0) + fprintf(stdlog,">> %6zx: %6zx cl=%3x, depth=%2u/%u -> %5x/%5x, stack=%zd\n", + yptr-yaz_buf, yp-yptr, clen, depth, max_depth, + val, best_val, sp-stack ); + #endif + if ( best_val < val ) + { + best_val = val; + if ( best_val > abort_val ) + return best_val; + } + } + while ( sp > stack ); + + #if 0 + if (verbose>0) + fprintf(stdlog,">> return %x\n",best_val); + #endif + return best_val; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError BackTrackYAZ ( yaz_compr_t *yaz, uint max_depth ) +{ + if ( max_depth > BACK_TRACK_MAX_DEPTH ) + max_depth = BACK_TRACK_MAX_DEPTH; + + if (logging) + { + fprintf(stdlog,">>YAZ back tracking: depth=%u, max-depth=%u\n", + max_depth, BACK_TRACK_MAX_DEPTH ); + fflush(stdlog); + } + + SetupDestYAZ(yaz,0x40); + + + //--- setup yaz_buf + + const int range = 0x1000; + const int max_len = 0x111; + + yaz_res_t *yaz_buf = CALLOC(sizeof(*yaz_buf),yaz->src_len); + yaz_res_t *yref = yaz_buf; + + const u8 *src = yaz->src; + const u8 *src_end = yaz->src_end; + + while ( src + 2 < src_end ) + { + const u8 * search = src - range; + if ( search < yaz->src ) + search = yaz->src; + + const u8 * cmp_end = src + max_len; + if ( cmp_end > src_end ) + cmp_end = src_end; + + uint found_len = 1; + const u8 * found = src; + const u8 c1 = *src; + + for ( ; search < src; search++ ) + { + search = memchr(search,c1,src-search); + if (!search) + break; + DASSERT( search < src ); + const u8 * cmp1 = search + 1; + const u8 * cmp2 = src + 1; + while ( cmp2 < cmp_end && *cmp1 == *cmp2 ) + cmp1++, cmp2++; + const int len = cmp2 - src; + DASSERT( cmp1 - search == len ); + + if ( len > 2 && found_len < len ) + { + found_len = len; + found = search; + if ( found_len == max_len ) + break; + } + } + yref->ref = src - found - 1; + yref->len = found_len; + yref++; + src++; + } + + while ( src < src_end ) + { + yref->ref = 0; + yref->len = 1; + yref++; + src++; + } + + + //--- create yaz0 stream + + u8 * dest = yaz->dest_ptr; + u8 * dest_end = yaz->dest_end; + src = yaz->src; + yref = yaz_buf; + u8 mask = 0; + u8 *code_byte = dest; + + while ( src < src_end ) + { + if (!yref->len) + { + fprintf(stdlog,"src=%x, y=%zx,%d,%d\n", + (uint)(src-yaz->src), sizeof(*yref)*(yref-yaz_buf), + yref->ref, yref->len ); + HexDump16(stdlog,0,0,(u8*)yref-0x20,0x40); + ASSERT(yref->len); + } + + if ( dest > dest_end ) + { + const uint code_pos = code_byte - yaz->dest_buf; + yaz->dest_ptr = dest; + + GrowDestYAZ(yaz); + + dest = yaz->dest_ptr; + dest_end = yaz->dest_end; + code_byte = yaz->dest_buf + code_pos; + } + + if (!mask) + { + code_byte = dest; + *dest++ = 0; + mask = 0x80; + } + + if ( yref->len < 3 ) + { + *code_byte |= mask; + *dest++ = *src++; + yref++; + } + else + { + uint val0 = BackTrackVal(yaz_buf,yaz->src_len,yref,max_depth,true,0); + uint val1 = BackTrackVal(yaz_buf,yaz->src_len,yref,max_depth,false,val0); + + if ( val0 >= val1 ) + { + *code_byte |= mask; + *dest++ = *src++; + yref++; + } + else + { + DASSERT( yref->ref < 0x1000 ); + if ( yref->len < 0x12 ) + { + *dest++ = yref->ref >> 8 | ( yref->len - 2 ) << 4; + *dest++ = yref->ref; + noPRINT(" %5zx -> %02x %02x\n", + src-szs->data, dest[-2], dest[-1] ); + } + else + { + *dest++ = yref->ref >> 8; + *dest++ = yref->ref; + *dest++ = yref->len - 0x12; + noPRINT(" %5zx -> %02x %02x %02x\n", + src-szs->data, dest[-3], dest[-2], dest[-1] ); + } + src += yref->len; + yref += yref->len; + } + } + mask >>= 1; + } + + yaz->dest_ptr = dest; + FREE(yaz_buf); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ClassicCompressYAZ() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ClassicCompressYAZ ( yaz_compr_t *yaz ) +{ + DASSERT(yaz); + SetupDestYAZ(yaz,0x40); + + //--- calc range + + const u32 range = opt_fast + ? 0x100 + : opt_norm + ? 0x1000 + : !opt_compr + ? 0 + : opt_compr < 9 + ? 0x10e0 * opt_compr / 9 - 0x0e0 + : 0x1000; + + + //--- load from cache? + + if ( range == 0x1000 && IsSZSCacheEnabled() && yaz->szs ) + { + ccp cache_fname = 0; + szs_file_t *szs = yaz->szs; + PRINT("ClassicCompressYAZ() - check cache for %s\n",szs->fname); + LoadSZSCache(); + CreateSSChecksumDBByData(iobuf,sizeof(iobuf),yaz->src,yaz->src_len); + + ccp dest_fname; + if ( opt_cname && *opt_cname ) + { + dest_fname = opt_cname; + opt_cname = 0; + } + else + dest_fname = szs->dest_fname ? szs->dest_fname : szs->fname; + + bool exists; + const ParamFieldItem_t *it + = StoreSZSCache( dest_fname, iobuf, true, &exists ); + PRINT("checklsum = %s, it=%d, exists=%d\n",iobuf,it!=0,exists); + if ( exists && it ) + { + PRINT("CACHE FOUND: %s\n",(ccp)it->data); + cache_fname = PathAllocPP(szs_cache_dir,it->data); + + szs_file_t cache; + InitializeSZS(&cache); + + if ( !LoadSZS(&cache,cache_fname,false,true,true) + && cache.cdata + && cache.csize + && cache.fform_file == FF_YAZ0 ) + { + PRINT("CACHE LOADED, %zu bytes\n",cache.csize); + //HEXDUMP16(0,0,cache.cdata,32); + DecompressSZS(&cache,false,0); + if ( cache.size == yaz->src_len + && !memcmp(yaz->src,cache.data,cache.size)) + { + PRINT("CACHE USED\n"); + cache.csize -= sizeof(yaz0_header_t); + memmove(cache.cdata,cache.cdata+sizeof(yaz0_header_t),cache.csize); + + yaz->dest_buf_size = cache.csize; + yaz->dest_buf = cache.cdata; + yaz->dest_ptr = + yaz->dest_end = cache.cdata + cache.csize; + szs->cache_used = true; + + cache.cdata = 0; + cache.csize = 0; + ResetSZS(&cache); + FreeString(cache_fname); + return ERR_OK; + } + } + ResetSZS(&cache); + } + + if (it) + { + FreeString(szs->cache_fname); + szs->cache_fname = cache_fname + ? cache_fname : PathAllocPP(szs_cache_dir,it->data); + PRINT("szs->cache_fname = %s\n",szs->cache_fname); + } + } + + + //--- setup, use local vars for optimization + + const bool optimize = opt_compr >= 10; + + u8 * dest = yaz->dest_ptr; + u8 * dest_end = yaz->dest_end; + const u8 *src = yaz->src; + const u8 *src_end = yaz->src_end; + + u8 mask = 0; + u8 *code_byte = dest; + + + //--- main loop + + uint saved_len = 0; + const u8 * saved = 0; + + while ( src < src_end ) + { + if ( dest > dest_end ) + { + const uint code_pos = code_byte - yaz->dest_buf; + yaz->dest_ptr = dest; + + GrowDestYAZ(yaz); + + dest = yaz->dest_ptr; + dest_end = yaz->dest_end; + code_byte = yaz->dest_buf + code_pos; + } + + if (!mask) + { + code_byte = dest; + *dest++ = 0; + mask = 0x80; + } + + const int max_len = 0x111; + uint found_len = 1; + const u8 * found = 0; + + if ( range && src + 2 < src_end ) + { + if (saved_len) + { + found_len = saved_len; + saved_len = 0; + found = saved; + } + else + { + const u8 * search = src - range; + if ( search < yaz->src ) + search = yaz->src; + + const u8 * cmp_end = src + max_len; + if ( cmp_end > src_end ) + cmp_end = src_end; + + const u8 c1 = *src; + for ( ; search < src; search++ ) + { + search = memchr(search,c1,src-search); + if (!search) + break; + DASSERT( search < src ); + const u8 * cmp1 = search + 1; + const u8 * cmp2 = src + 1; + while ( cmp2 < cmp_end && *cmp1 == *cmp2 ) + cmp1++, cmp2++; + const int len = cmp2 - src; + DASSERT( cmp1 - search == len ); + noPRINT_IF ( search + len > src, + "FOUND l=%u,%u pos=%zu,%zu\n", + len, found_len, search-szs->data, src-szs->data ); + + if ( found_len < len ) + { + found_len = len; + found = search; + if ( found_len == max_len ) + break; + } + } + } + + if ( optimize && found_len >= 3 ) + { + uint found_len2 = 0; + //const u8 * found2 = 0; + const u8 * src2 = src + 1; + + const u8 * search = src2 - range; + if ( search < yaz->src ) + search = yaz->src; + + const u8 * cmp_end = src2 + max_len; + if ( cmp_end > src_end ) + cmp_end = src_end; + + const u8 c1 = *src2; + for ( ; search < src2; search++ ) + { + search = memchr(search,c1,src2-search); + if (!search) + break; + DASSERT( search < src2 ); + const u8 * cmp1 = search + 1; + const u8 * cmp2 = src2 + 1; + while ( cmp2 < cmp_end && *cmp1 == *cmp2 ) + cmp1++, cmp2++; + const int len = cmp2 - src2; + DASSERT( cmp1 - search == len ); + noPRINT_IF ( search + len > src2, + "FOUND l=%u,%u pos=%zu,%zu\n", + len, found_len2, search-szs->data, src2-szs->data ); + if ( found_len2 < len ) + { + found_len2 = len; + saved = search; + if ( found_len2 == max_len ) + break; + } + } + + if ( found_len2 > found_len ) + { + found_len = 1; + saved_len = found_len2; + } + } + } + else + saved_len = 0; + + if ( found_len >= 3 ) + { + DASSERT(found); + const u32 delta = src - found - 1; + DASSERT( delta < 0x1000 ); + if ( found_len < 0x12 ) + { + *dest++ = delta >> 8 | ( found_len - 2 ) << 4; + *dest++ = delta; + noPRINT(" %5zx -> %02x %02x\n", + src-szs->data, dest[-2], dest[-1] ); + } + else + { + *dest++ = delta >> 8; + *dest++ = delta; + *dest++ = found_len - 0x12; + noPRINT(" %5zx -> %02x %02x %02x\n", + src-szs->data, dest[-3], dest[-2], dest[-1] ); + } + src += found_len; + } + else + { + *code_byte |= mask; + *dest++ = *src++; + } + + mask >>= 1; + } + + yaz->dest_ptr = dest; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SZS/YAZ compression /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CompressSZS ( szs_file_t * szs, bool remove_uncompressed ) +{ + PRINT("CompressSZS(), compr=%d.%02d\n", + opt_compr_mode, opt_compr ); + + TRACE("CompressSZS(%p,%d)\n",szs,remove_uncompressed); + DASSERT(szs); + + if ( !szs->size || !szs->data || szs->cdata ) + return ERR_OK; + + if ( fform_compr_force == FF_BZ + || !fform_compr_force && szs->fform_file == FF_BZ ) + { + return CompressBZ(szs,remove_uncompressed); + } + + + //--- setup yaz-control record + + yaz_compr_t yaz; + memset(&yaz,0,sizeof(yaz)); + yaz.src_len = szs->size; + yaz.src = szs->data; + yaz.src_end = szs->data + szs->size; + yaz.szs = szs; + + + //--- compress data + + enumError err; + switch(opt_compr_mode) + { + case 11: err = BackTrackYAZ(&yaz,opt_compr); break; // back tracking + default: err = ClassicCompressYAZ(&yaz); + } + + + //--- store compressed data + + if ( err == ERR_OK ) + { + const uint used_len = yaz.dest_ptr - yaz.dest_buf; + szs->csize = sizeof(yaz0_header_t) + used_len; + szs->cdata = MALLOC(szs->csize); + memcpy( szs->cdata + sizeof(yaz0_header_t), yaz.dest_buf, used_len ); + szs->cdata_alloced = true; + + yaz0_header_t * yaz0 = (yaz0_header_t*)szs->cdata; + memset(yaz0,0,sizeof(*yaz0)); + yaz0->uncompressed_size = htonl(szs->size); + + szs->fform_file = GetYazFF(szs->fform_file); + if ( szs->fform_file == FF_XYZ ) + EncodeXYZ(szs->cdata,szs->cdata,szs->csize); + else + memcpy( yaz0->magic, + szs->fform_file == FF_YAZ1 ? YAZ1_MAGIC : YAZ0_MAGIC, + sizeof(yaz0->magic) ); + + if (remove_uncompressed) + { + ClearContainerSZS(szs); + ClearUncompressedSZS(szs); + } + } + + if ( yaz.dest_buf != (u8*)iobuf ) + FREE(yaz.dest_buf); + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// wu8 coding /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[wu8_info_t]] + +typedef struct wu8_info_t +{ + szs_file_t *szs; // the szs + u8 *orig; // a copy of the original data + u32 size; // data size + u8 *temp; // temp buffer + uint temp_size; // size of 'temp' + u8 base; // base xor value + u8 conv; // xor value for additionally files + bool encode; // false: decode, true: encode + uint pass1_count; // number of files in pass1 + uint err_count; // number of errors + +} wu8_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError SetupWU8Info ( wu8_info_t *wu8, szs_file_t *szs, bool encode ) +{ + DASSERT(wu8); + DASSERT(szs); + + memset(wu8,0,sizeof(*wu8)); + + if (!SetupAutoAdd()) + return ERROR0(ERR_CANT_OPEN, + "WU8-%sCODE: Can't find autoadd library.\n", + encode ? "EN" : "DE" ); + + wu8->szs = szs; + wu8->orig = MEMDUP(szs->data,szs->size); + wu8->size = szs->size; + wu8->encode = encode; + + const u8 *p = (u8*)&wu8->size; + wu8->conv = wu8->base = p[0] ^ p[1] ^ p[2] ^ p[3]; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ResetWU8Info ( wu8_info_t *wu8, file_format_t fform ) +{ + DASSERT(wu8); + DASSERT(wu8->szs); + DASSERT(wu8->orig); + DASSERT(wu8->size == wu8->szs->size); + + enumError err = ERR_OK; + if (wu8->err_count) + { + memcpy(wu8->szs->data,wu8->orig,wu8->szs->size); + err = ERR_ERROR; + } + else if ( fform != FF_UNKNOWN ) + wu8->szs->fform_current = fform; + FREE(wu8->orig); + FREE(wu8->temp); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError load_wu8 +( + wu8_info_t *wu8, // valud wu8 + ccp path, // relative sub file path + s64 *ret_size // not NULL: return file size +) +{ + char load_path[PATH_MAX]; + s64 size = FindAutoAdd(path,0,load_path,sizeof(load_path)); + if (ret_size) + *ret_size = size; + + if ( size <= 0 ) + { + wu8->err_count++; + return ERROR0(ERR_CANT_OPEN, + "WU8-%sCODE: Missing file in autoadd library: %s\n", + wu8->encode ? "EN" : "DE", path ); + } + + if ( wu8->temp_size < size ) + { + FREE(wu8->temp); + wu8->temp_size = ALIGN32(size,0x200000); + wu8->temp = MALLOC(wu8->temp_size); + } + + enumError err = LoadFILE( load_path, 0, 0, wu8->temp, size, 0, 0, false ); + if (err) + { + wu8->err_count++; + return err; + } + + wu8->conv ^= wu8->temp[size/2] ^ wu8->temp[size/3] ^ wu8->temp[size/4]; + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int code_wu8_pass_1 +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if ( term || it->is_dir ) + return 0; + + wu8_info_t *wu8 = it->param; + DASSERT(wu8); + DASSERT(it->szs); + DASSERT( wu8->szs == it->szs ); + + ccp path = it->path; + if ( path[0] == '.' && path[1] == '/' ) + path += 2; + int fidx = FindDbFile(path); + if ( fidx < 0 ) + return 0; + + const DbFileFILE_t *df = DbFileFILE+fidx; + if (!DBF_ARCH_REQUIRED(df->flags)) + return 0; + + noPRINT("%sCODE-1 %s\n", wu8->encode ? "EN" : "DE", path ); + + s64 size; + enumError err = load_wu8(wu8,path,&size); + if (err) + return 0; + + u8 *xptr = wu8->temp; + u8 *xend = xptr + size; + wu8->pass1_count++; + + u8 *ptr = it->szs->data + it->off; + u8 *end = ptr + it->size; + + while ( ptr < end ) + { + if ( xptr == xend ) + xptr = wu8->temp; + *ptr++ ^= wu8->base ^ *xptr++; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int code_wu8_pass_2 +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if ( term || it->is_dir ) + return 0; + + wu8_info_t *wu8 = it->param; + DASSERT(wu8); + DASSERT(it->szs); + DASSERT( wu8->szs == it->szs ); + + ccp path = it->path; + if ( path[0] == '.' && path[1] == '/' ) + path += 2; + int fidx = FindDbFile(path); + if ( fidx >= 0 ) + { + const DbFileFILE_t *df = DbFileFILE+fidx; + if (DBF_ARCH_REQUIRED(df->flags)) + return 0; + } + + noPRINT("%sCODE-2 %s\n", wu8->encode ? "EN" : "DE", path ); + + u8 *ptr = it->szs->data + it->off; + u8 *end = ptr + it->size; + while ( ptr < end ) + *ptr++ ^= wu8->conv; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void iterate_wu8 +( + szs_file_t *szs, // valid szs + wu8_info_t *wu8 // valud wu8 +) +{ + IterateFilesSZS(szs,code_wu8_pass_1,wu8,false,0,-1,SORT_NONE); + PRINT("PASS1-COUNT = %d, ERR-COUNT = %d\n",wu8->pass1_count,wu8->err_count); + if (!wu8->err_count) + { + if (!wu8->pass1_count) + load_wu8(wu8,"itembox.brres",0); + IterateFilesSZS(szs,code_wu8_pass_2,wu8,false,0,-1,SORT_NONE); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DecodeWU8 ( szs_file_t * szs ) +{ + DASSERT(szs); + if ( szs->fform_current != FF_WU8 || !szs->data || szs->size < sizeof(u8_header_t) ) + return ERR_OK; + + u8_header_t * u8p = (u8_header_t*)szs->data; + if ( ntohl(u8p->magic) != WU8_MAGIC_NUM ) + return ERR_OK; + + PRINT("DecodeWU8()\n"); + + wu8_info_t wu8; + enumError err = SetupWU8Info(&wu8,szs,false); + if (err) + return err; + + u8p->magic = htonl(U8_MAGIC_NUM); + u8 *ptr = szs->data + ntohl(u8p->node_offset); + u8 *end = ptr + ntohl(u8p->fst_size); + while ( ptr < end ) + *ptr++ ^= wu8.base; + + iterate_wu8(szs,&wu8); + return ResetWU8Info(&wu8,FF_U8); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError EncodeWU8 ( szs_file_t * szs ) +{ + DASSERT(szs); + if ( szs->fform_current != FF_U8 || !szs->data || szs->size < sizeof(u8_header_t) ) + return ERR_OK; + + u8_header_t * u8p = (u8_header_t*)szs->data; + if ( ntohl(u8p->magic) != U8_MAGIC_NUM ) + return ERR_OK; + + PRINT("EncodeWU8()\n"); + + wu8_info_t wu8; + enumError err = SetupWU8Info(&wu8,szs,true); + if (err) + return err; + + iterate_wu8(szs,&wu8); + u8p->magic = htonl(WU8_MAGIC_NUM); + u8 *ptr = szs->data + ntohl(u8p->node_offset); + u8 *end = ptr + ntohl(u8p->fst_size); + while ( ptr < end ) + *ptr++ ^= wu8.base; + return ResetWU8Info(&wu8,FF_WU8); +} + +/////////////////////////////////////////////////////////////////////////////// + +u8 * DecodeXYZ +( + u8 *dest, // destination buffer, + // If NULL: malloc() is used. + const u8 *src, // source data + uint size // size of dest and source + +) +{ + DASSERT(src); + DASSERT( size >= sizeof(yaz0_header_t) ); + + if (!dest) + dest = MALLOC(size); + + u8 *dptr = dest; + u8 *dend = dest + size; + while ( dptr < dend ) + *dptr++ = *src++ ^ 0xdc; + + memcpy(dest,YAZ0_MAGIC,4); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +u8 * EncodeXYZ +( + u8 *dest, // destination buffer, + // If NULL: malloc() is used. + const u8 *src, // source data + uint size // size of dest and source + +) +{ + DASSERT(src); + DASSERT( size >= sizeof(yaz0_header_t) ); + + dest = DecodeXYZ(dest,src,size); + memcpy(dest,XYZ0_MAGIC,4); + return dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZ support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DecompressBZ ( szs_file_t * szs, bool rm_compressed ) +{ + TRACE("DecompressBZ(%p,%d)\n",szs,rm_compressed); + DASSERT(szs); + + if ( !szs->csize || !szs->cdata || szs->data ) + return ERR_OK; + + const wbz_header_t *wh = (wbz_header_t*)szs->cdata; + if (memcmp(wh->magic,BZ_MAGIC,sizeof(wh->magic))) + return ERROR0(ERR_INVALID_DATA,"Invalid BZ magic!\n"); + + u8 *data; + uint size; + enumError err = DecodeBZIP2( &data, &size, 0, + wh->bz_data, szs->csize - sizeof(*wh) ); + if (err) + return err; + + szs->data = data; + szs->size = size; +// [[analyse-magic]] + szs->fform_arch = szs->fform_current = GetByMagicFF(data,size,size); + szs->ff_attrib = GetAttribFF(szs->fform_arch); +// [[version-suffix]] + szs->ff_version = GetVersionFF(szs->fform_arch,szs->data,szs->size,0); + + ClearContainerSZS(szs); + if (rm_compressed) + ClearCompressedSZS(szs); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError CompressBZ ( szs_file_t * szs, bool remove_uncompressed ) +{ + PRINT("CompressBZ(%p,%d)\n",szs,remove_uncompressed); + DASSERT(szs); + + if ( !szs->size || !szs->data || szs->cdata ) + return ERR_OK; + + u8 *cdata; + uint csize; + const int max_compr = szs->size / 100000 + 1; + + if ( opt_norm || opt_compr_mode > 1 ) + { + cdata = 0; + csize = UINT_MAX; + + static int ctab[] = { 1, 9, 8, 2, 5, 0 }, *cptr; + int n = opt_norm ? 2 : opt_compr_mode; + for ( cptr = ctab; *cptr > 0 && n > 0; cptr++, n-- ) + { + int compr = *cptr; + if ( compr >= max_compr ) + { + if ( compr < 9 ) + continue; + compr = max_compr; + } + PRINT("TRY COMPRESSION %u => %u/%u\n",*cptr,compr,max_compr); + + u8 *temp_data; + uint temp_size; + enumError err = EncodeBZIP2( &temp_data, &temp_size, true, + sizeof(wbz_header_t), szs->data, szs->size, compr ); + if (err) + continue; + if ( temp_size < csize ) + { + PRINT(" => USE COMPRESSION %u\n",compr); + FREE(cdata); + cdata = temp_data; + csize = temp_size; + } + else + FREE(temp_data); + } + if (!cdata) + return ERR_BZIP2; + } + else + { + int compr = opt_compr_mode > 0 ? opt_compr : BZIP2_DEFAULT_COMPR; + if ( compr > max_compr ) + compr = max_compr; + + PRINT(" => USE COMPRESSION %u/%u\n",compr,max_compr); + enumError err = EncodeBZIP2( &cdata, &csize, true, + sizeof(wbz_header_t), szs->data, szs->size, compr ); + if (err) + return err; + } + + szs->cdata = cdata; + szs->csize = csize; + + wbz_header_t *wh = (wbz_header_t*)cdata; + memcpy(wh->magic,BZ_MAGIC,sizeof(wh->magic)); + memcpy(wh->first_8,szs->data,sizeof(wh->first_8)); + szs->fform_file = FF_BZ; + + ClearContainerSZS(szs); + if (remove_uncompressed) + ClearUncompressedSZS(szs); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// create u8 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintFileHeadSZS() +{ + printf("\n idx offset/hex size/hex size/dec magic f.form vers file or directory\n" + "%.119s\n", Minus300 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintVersionSZS ( char *buf, uint buf_size, struct szs_iterator_t *it ) +{ + DASSERT(buf); + DASSERT(it); + *buf = 0; + +// [[version-suffix]] + + const u8 * data = it->szs->data + it->off; + file_format_t fform = GetByMagicFF(data,it->size,0); + if ( IsBRSUB(fform) && it->size >= 12 ) + { + const int vers = be32(data+8); + const brsub_info_t *bi = GetInfoBRSUB(fform,vers); + if ( !bi || bi->warn >= BIMD_FAIL ) + snprintf(buf,buf_size,"%s%3d!%s", + colout->fatal, vers, colout->reset ); + else if ( bi->warn >= BIMD_HINT ) + snprintf(buf,buf_size,"%s%3d %s", + colout->hint, vers, colout->reset ); + else + snprintf(buf,buf_size,"%3d ",vers); + return; + } + else + { + const int vers = GetVersionFF(fform,data,it->size,0); + if ( vers >= 0 ) + { + snprintf(buf,buf_size,"%3u ",vers); + return; + } + } + + strncpy(buf," - ",buf_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintFileSZS +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + if (term) + return 0; + + if (it->is_dir) + { + printf("%s%3x: %7x< -%8x> - - DIR - %.*s%s%s\n", + colset->cmd, + it->index, it->off, it->size, + 2 * it->recurse_level, indent_msg, it->path, + colset->reset ); + } + else + { + const ColorSet_t * col = GetFileColorSet( it->has_subfiles ? stdout : 0 ); + + const u8 * data = it->szs->data+it->off; +// [[analyse-magic+]] +// [[ff_fallback]] + const file_format_t fform + = AnalyseMagicByOpt(0,data,it->size,it->szs->size-it->off, + FF_UNKNOWN, it->path); + + char vers_buf[50]; + PrintVersionSZS(vers_buf,sizeof(vers_buf),it); + + printf("%s%3x: %7x..%7x %7x %8u %-4s %-6s %s %.*s%s%s\n", + col->heading, + it->index, it->off, it->off+it->size, it->size, it->size, + PrintID(data, it->size < 4 ? it->size : 4, 0 ), + GetNameFF(0,fform), + vers_buf, + 2 * it->recurse_level, indent_msg, it->path, + col->reset ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError CreateU8 +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 namepool_size, // total namepool_size + u32 total_size, // total file size + bool create_pt_dir // create directory '.' as base +) +{ + PRINT("CreateU8() namesize=%x, totsize=%x, align=%x, .dir=%d\n", + namepool_size, total_size, opt_align_u8, create_pt_dir ); + + //--- sort + + SortSubFilesSZS(szs,SORT_U8); + + + //--- alloc data and setup + + if ( opt_pt_dir < 0 ) + create_pt_dir = false; + else if ( opt_pt_dir > 0 ) + create_pt_dir = true; + + namepool_size += create_pt_dir ? 3 : 1; // extra byte for root node and "." + + const size_t n_files = szs->subfile.used + 1 + create_pt_dir; + const size_t dir_tab_size = n_files * sizeof(u8_node_t); + const size_t fst_size = ALIGN32( dir_tab_size + namepool_size, opt_align_u8 ); + const size_t u8_head_size = ALIGN32( sizeof(u8_header_t), opt_align_u8 ); + const size_t head_size = ALIGN32( u8_head_size + fst_size, opt_align_u8 ); + + szs->fform_arch = FF_U8; + szs->fform_current = FF_U8; + szs->ff_attrib = GetAttribFF(szs->fform_arch); + szs->ff_version = -1; + szs->data_alloced = true; + szs->file_size = szs->size = head_size + total_size; + szs->data = CALLOC(1,szs->size); + + u8_header_t * u8head = (u8_header_t*)szs->data; + u8_node_t * u8node0 = (u8_node_t*)(szs->data+u8_head_size); + u8_node_t * u8node = u8node0; + char * name_pool = (char*)u8node + dir_tab_size; + u8 * data_ptr = szs->data + head_size; + + PRINT("ADDR: %zx..%zx..%zx..%zx..%zx\n", + (u8*)u8head - szs->data, + (u8*)u8node - szs->data, + (u8*)name_pool - szs->data, + (u8*)data_ptr - szs->data, + szs->size ); + + u8head->magic = htonl( U8_MAGIC_NUM ); + u8head->node_offset = htonl( u8_head_size ); + u8head->fst_size = htonl( dir_tab_size + namepool_size ); + u8head->data_offset = htonl( data_ptr - szs->data ); + + if ( u8_head_size > sizeof(u8_header_t) ) + memset(u8head->padding,0xcc,u8_head_size-sizeof(u8_header_t)); + + const int MAX_DIR_DEPTH = 50; + u32 basedir[MAX_DIR_DEPTH+1]; + memset(basedir,0,sizeof(basedir)); + + + //--- root file + + char * name_ptr = name_pool; + u8node->name_off = htonl(name_ptr-name_pool); + *name_ptr++ = 0; + u8node->is_dir = 1; + u8node->size = htonl(n_files); + u8node++; + + + //--- "." file + + if (create_pt_dir) + { + u8node->name_off = htonl(name_ptr-name_pool); + *name_ptr++ = '.'; + *name_ptr++ = 0; + u8node->is_dir = 1; + u8node->size = htonl(n_files); + u8node++; + basedir[1] = 1; + } + + + //--- prepare link support + + u8_node_t **u8link = 0; + if (szs->subfile.link_count) + u8link = CALLOC(szs->subfile.link_count+1,sizeof(*u8link)); + + + //--- iterate files + + enumError max_err = ERR_OK; + int idx; + for ( idx = 0; idx < szs->subfile.used; idx++ ) + { + szs_subfile_t * f = szs->subfile.list + idx; + u8node->name_off = htonl(name_ptr-name_pool); + + u8node->is_dir = f->is_dir; + bool append_name = true; + if (f->is_dir) + { + u8node->size = htonl( idx + f->size + 2 + create_pt_dir ); + noPRINT("DIR: %d..%d\n",idx,ntohl(u8node->size)); + + u32 depth = f->offset; + if ( depth < MAX_DIR_DEPTH ) + { + noPRINT("depth=%d -> %d\n",depth,basedir[depth]); + u8node->offset = htonl(basedir[depth]); + basedir[depth+1] = u8node - u8node0; + } + } + else if ( u8link && u8link[f->link_index] ) + { + u8_node_t *u8src = u8link[f->link_index]; + PRINT0("LINK/USE[%u]: %s -> #%zu\n", + f->link_index, f->path, u8src-u8node0 ); + u8node->offset = u8src->offset; + u8node->size = u8src->size; + } + else + { + PRINT0("** %#x %#x %s\n",f->offset,f->size,f->path); + //DASSERT ( data_ptr + f->size <= szs->data + szs->size ); + uint relevant_size; + if (f->ext) + { + if (f->ext->removed) + { + append_name = false; + relevant_size = 0; + } + else + { + relevant_size = f->ext->size; + PRINT("USE EXT: %s, %u B, %p\n",f->path,relevant_size,f->ext->load_path); + DASSERT(data_ptr + relevant_size <= szs->data + szs->size ); + memcpy(data_ptr,f->ext->load_path,relevant_size); + } + } + else if ( f->data && !f->load_path ) + { + relevant_size = f->size; + memcpy(data_ptr,f->data,relevant_size); + } + else if ( source_data && !f->load_path ) + { + relevant_size = f->size; + memcpy(data_ptr,source_data+f->offset,relevant_size); + } + else + { + relevant_size = f->size; +//DEL if (f->data) +//DEL { +//DEL memcpy(data_ptr,f->data,relevant_size); +//DEL } +//DEL else + { + enumError err = f->load_path + ? LoadFILE( f->load_path, 0, 0, + data_ptr, relevant_size, 0, &szs->fatt, true ) + : LoadFILE( source_dir, f->path, 0, + data_ptr, relevant_size, 0, &szs->fatt, true ); + if ( max_err < err ) + max_err = err; + } + } + f->offset = data_ptr - szs->data; + u8node->offset = htonl(f->offset); + u8node->size = htonl( relevant_size ); + data_ptr += ALIGN32(relevant_size,opt_align_u8); + + if (f->link_index) + { + PRINT("LINK/SET[%u]: %s -> #%zu\n", + f->link_index, f->path, u8node-u8node0 ); + u8link[f->link_index] = u8node; + } + + DASSERT_MSG ( data_ptr <= szs->data + szs->size, + "data overlow: %zu=0x%zx bytes\n", + data_ptr - (szs->data + szs->size), + data_ptr - (szs->data + szs->size) ); + } + + if (append_name) + { + ccp pend = f->path + strlen(f->path); + if ( pend > f->path && pend[-1] == '/' ) + pend--; + ccp pstart = pend; + while ( pstart > f->path && pstart[-1] != '/' ) + pstart--; + noPRINT("NAME: |%.*s| %zu+%zu/%zu\n", + (int)(pend-pstart), pstart, + name_ptr-name_pool, pend-pstart, namepool_size ); + while ( pstart < pend ) + *name_ptr++ = *pstart++; + *name_ptr++ = 0; + DASSERT_MSG( name_ptr <= name_pool + namepool_size, + "name pool overflow: %zx/%x\n", name_ptr - name_pool, namepool_size ); + } + + u8node++; + DASSERT( (ccp)u8node <= name_pool ); + } + DASSERT( (ccp)u8node == name_pool ); + DASSERT ( data_ptr <= szs->data + szs->size ); + DASSERT_MSG( name_ptr == name_pool + namepool_size, + "name pool underflow: %zx/%x\n", name_ptr - name_pool, namepool_size ); + + if (u8link) + FREE(u8link); + + ClearSpecialFilesSZS(szs); + + + //--- debugging + + if ( logging > 0 ) + { + printf("----- internal file list -----\n"); + IterateFilesSZS(szs,PrintFileSZS,0,true,0,0,SORT_NONE); + printf("------------------------------\n"); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs_extract_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeExtractSZS ( szs_extract_t * eszs ) +{ + DASSERT(eszs); + memset(eszs,0,sizeof(*eszs)); + eszs->exlevel = EXLEV_N; // == not found + eszs->endian = &be_func; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetExtractSZS ( szs_extract_t * eszs ) +{ + DASSERT(eszs); + if (eszs->data_alloced) + FREE(eszs->data); + FreeString(eszs->fname); + FreeString(eszs->found_path); + FreeContainer(eszs->old_container); + ResetContainer(&eszs->container); + InitializeExtractSZS(eszs); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +Container_t * SetupContainerESZS ( szs_extract_t * eszs ) +{ + DASSERT(eszs); + AssignContainer( &eszs->container, eszs->container.protect_level, + eszs->data, eszs->data_size, + eszs->data_alloced ? CPM_MOVE : CPM_LINK ); + eszs->data_alloced = false; + #ifdef TEST + DumpInfoContainer(stdlog,collog,2,"ESZS: ",&eszs->container,0); + #endif + return &eszs->container; +} + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * LinkContainerESZS ( szs_extract_t * eszs ) +{ + DASSERT(eszs); + if (!IsValidContainer(&eszs->container)) + SetupContainerESZS(eszs); + return LinkContainerData(&eszs->container); +} + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * MoveContainerESZS ( szs_extract_t * eszs ) +{ + DASSERT(eszs); + if (!IsValidContainer(&eszs->container)) + SetupContainerESZS(eszs); + return MoveContainerData(&eszs->container); +} + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * GetOldContainerESZS ( szs_extract_t * eszs ) +{ + DASSERT(eszs); +// old [[container]] will be replaced by Container_t & ContainerData_t + if (!eszs->old_container) + { + if (!IsValidContainer(&eszs->container)) + SetupContainerESZS(eszs); + eszs->old_container = UseSubContainer(0,eszs->container.cdata); + } + + return AllocContainer(eszs->old_container); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int node_found +( + struct szs_iterator_t *it, // iterator struct with all infos + szs_extract_level_t level // found level +) +{ + DASSERT(it); + szs_extract_t * eszs = it->param; + DASSERT(eszs); + + if ( eszs->exlevel > level ) + { + eszs->exlevel = level; + eszs->found_offset = it->off; + eszs->found_size = it->size; + eszs->found_count = 1; + FreeString(eszs->found_path); + eszs->found_path = STRDUP(it->path); + eszs->endian = it->endian; + } + else if ( eszs->exlevel == level ) + { + DASSERT(eszs->found_count); + DASSERT(eszs->found_path); + eszs->found_count++; + } + + PRINT("*** FOUND: lev=%d n=%d off=%x siz=%x %s\n", + eszs->exlevel, eszs->found_count, + eszs->found_offset, eszs->found_size, eszs->found_path ); + + return eszs->exlevel == 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int extract_data_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + if ( !term && !it->is_dir ) + { + szs_extract_t * eszs = it->param; + DASSERT(eszs); + DASSERT(eszs->subpath); + DASSERT( eszs->exlevel > 0 && eszs->exlevel <= EXLEV_N ); + + if (!strcmp(it->path,eszs->subpath)) + return node_found(it,0); + + ccp nopt_path = it->path[0] == '.' && it->path[1] == '/' ? it->path + 2 : 0; + if ( nopt_path && !strcmp(nopt_path,eszs->subpath) ) + return node_found(it,EXLEV_F_RMPT); + + if ( eszs->exlevel >= EXLEV_F_ICASE + && !strcasecmp(it->path,eszs->subpath)) + return node_found(it,EXLEV_F_ICASE); + + if ( eszs->exlevel >= (EXLEV_F_ICASE|EXLEV_F_RMPT) + && nopt_path + && !strcasecmp(nopt_path,eszs->subpath) ) + return node_found(it,EXLEV_F_ICASE|EXLEV_F_RMPT); + + if ( eszs->exlevel >= EXLEV_F_MATCH ) + { + uint path_len = strlen(it->path); + if ( path_len > eszs->subpath_len ) + { + ccp cmp_path = it->path + path_len - eszs->subpath_len; + + if (!strcmp(cmp_path,eszs->subpath)) + return node_found(it,EXLEV_F_MATCH); + + if ( eszs->exlevel >= (EXLEV_F_MATCH|EXLEV_F_ICASE) + && !strcasecmp(cmp_path,eszs->subpath) ) + return node_found(it,EXLEV_F_MATCH|EXLEV_F_ICASE); + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ExtractSZS +( + szs_extract_t * eszs, // valid data structure + bool initialize, // true: initialize 'eszs' + ccp fname, // filename of source + ccp autoname, // not NULL: Use this filename + // if subfile name is "" or "/" + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(eszs); + DASSERT(fname); + + if (initialize) + InitializeExtractSZS(eszs); + else + ResetExtractSZS(eszs); + + char path[PATH_MAX]; + ccp subpath = SplitSubPath(path,sizeof(path),fname); + + PRINT0("ExtractSZS() FF: %s -> %s\n", + GetNameFF(eszs->fform_file,eszs->fform_arch), + GetNameFF(0,eszs->fform_current) ); + + if ( !subpath || !*subpath && !autoname ) + return ERR_OK; + + eszs->subfile_found = true; + noPRINT("SUBPATH: %s\n",subpath); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,path,true,false,true); + if (err) + { + ResetSZS(&szs); + return err; + } + + const bool is_arch = IsArchiveFF(szs.fform_current); + if ( !is_arch || szs.fform_current == FF_BRRES ) + autoname = 0; + else if ( autoname && ( !*subpath || !strcmp(subpath,"/") )) + subpath = autoname; + PRINT0("SUBPATH 2: %s\n",subpath); + + if ( !is_arch || !subpath || !*subpath ) + { + eszs->subfile_found = false; + ResetSZS(&szs); + return ERR_OK; + } + + eszs->szs_found = true; + eszs->subpath = subpath + 1; + eszs->subpath_len = strlen(eszs->subpath); + IterateFilesSZS(&szs,extract_data_func,eszs,false,0,-1,SORT_NONE); + + PRINT("### FOUND: lev=%d n=%d off=%x siz=%x %s\n", + eszs->exlevel, eszs->found_count, + eszs->found_offset, eszs->found_size, eszs->found_path ); + + if ( eszs->found_count == 1 ) + { + DASSERT(eszs->exlevel < EXLEV_N ); + DASSERT(eszs->found_path); + + uint p1len = strlen(path); + uint p2len = strlen(eszs->found_path); + eszs->fname = MALLOC(p1len+p2len+2); + sprintf((char*)eszs->fname,"%s/%s",path,eszs->found_path); + + eszs->data_alloced = true; + eszs->data_size = eszs->found_size; + eszs->data = MALLOC(eszs->found_size); + memcpy( eszs->data, szs.data + eszs->found_offset, eszs->found_size ); + } + else if ( ignore_no_file ) + err = ERR_NOT_EXISTS; + else if (!eszs->found_count) + err = ERROR0(ERR_CANT_OPEN, + "Sub file not exist: %s -> %s\n", path, subpath+1 ); + else + err = ERROR0(ERR_CANT_OPEN, + "Sub file ambiguous (N=%d): %s -> %s\n", eszs->found_count, path, subpath+1 ); + + eszs->subpath = 0; + eszs->subpath_len = 0; + eszs->fform_file = szs.fform_file; + eszs->fform_arch = szs.fform_arch; + eszs->fform_current = szs.fform_arch; + ResetSZS(&szs); + + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintErrorExtractSZS +( + szs_extract_t * eszs, // valid data structure + ccp fname // filename of source +) +{ + DASSERT(eszs); + DASSERT(fname); + + if (eszs->data) + return ERR_OK; + + if (eszs->szs_found) + return ERROR0(ERR_CANT_OPEN,"Sub file not found: %s\n",eszs->subpath); + + if (eszs->subfile_found) + return ERROR0(ERR_CANT_OPEN,"No SZS/U8 file found: %s\n",fname); + + return ERROR0(ERR_CANT_OPEN,"No sub file specified: %s\n",fname); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// raw_data_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeRawData ( raw_data_t * raw ) +{ + DASSERT(raw); + memset(raw,0,sizeof(*raw)); + raw->fname = EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetRawData ( raw_data_t * raw ) +{ + DASSERT(raw); + FreeString(raw->fname); + if (raw->data_alloced) + FREE(raw->data); + FreeContainer(raw->old_container); + ResetContainer(&raw->container); + InitializeRawData(raw); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +Container_t * SetupContainerRawData ( raw_data_t * raw ) +{ + DASSERT(raw); + AssignContainer( &raw->container, raw->container.protect_level, + raw->data, raw->data_size, + raw->data_alloced ? CPM_MOVE : CPM_LINK ); + raw->data_alloced = false; + #ifdef TEST + DumpInfoContainer(stdlog,collog,2,"ESZS: ",&raw->container,0); + #endif + return &raw->container; +} + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * LinkContainerRawData ( raw_data_t * raw ) +{ + DASSERT(raw); + if (!IsValidContainer(&raw->container)) + SetupContainerRawData(raw); + return LinkContainerData(&raw->container); +} + +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * MoveContainerRawData ( raw_data_t * raw ) +{ + DASSERT(raw); + if (!IsValidContainer(&raw->container)) + SetupContainerRawData(raw); + return MoveContainerData(&raw->container); +} + +/////////////////////////////////////////////////////////////////////////////// + +DataContainer_t * ContainerRawData ( raw_data_t * raw ) +{ + DASSERT(raw); +// old [[container]] will be replaced by Container_t & ContainerData_t + if (!raw->old_container) + { + if (!IsValidContainer(&raw->container)) + SetupContainerRawData(raw); + raw->old_container = UseSubContainer(0,raw->container.cdata); + } + + return AllocContainer(raw->old_container); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadRawData +( + raw_data_t * raw, // data structure + bool init_raw, // true: initialize 'raw' first + ccp fname, // valid pointer to filenname + ccp autoname, // not NULL: Use this filename + // if subfile name is "" or "/" + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + file_format_t allow_0 // not NULL (FF_UNKNOWN): + // allow "0" for an empty file and set FF +) +{ + DASSERT(raw); + DASSERT(fname); + if (init_raw) + InitializeRawData(raw); + else + ResetRawData(raw); + + + //--- special case "0" + + if ( allow_0 && !strcmp(fname,"0") ) + { + raw->fform = allow_0; + raw->is_0 = true; + raw->fname = STRDUP(""); + return ERR_OK; + } + + + //--- load data + + szs_extract_t eszs; + enumError err = ExtractSZS(&eszs,true,fname,autoname,ignore_no_file); + if (err) + return err; + + if (eszs.data) + { + raw->data_alloced = eszs.data_alloced; + raw->data_size = eszs.data_size; + raw->data = eszs.data; + eszs.data = 0; + raw->fname = eszs.fname; + eszs.fname = 0; + ResetExtractSZS(&eszs); + } + else + { + szs_file_t szs; // use 'szs_file_t' for automatic decompression + InitializeSZS(&szs); + err = LoadSZS(&szs,fname,true,ignore_no_file,true); + raw->fform_file = szs.fform_file; + raw->data = szs.data; + raw->data_size = szs.size; + raw->data_alloced = szs.data_alloced; + raw->fatt = szs.fatt; + raw->fname = szs.fname; + szs.data = 0; + szs.data_alloced= 0; + szs.fname = 0; + ResetSZS(&szs); + + if (err) + return err; + } +// [[analyse-magic]] + raw->fform = GetByMagicFF(raw->data,raw->data_size,raw->data_size); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IterateFilesU8() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesU8 +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if ( !szs->data || szs->size < sizeof(u8_header_t) ) + return -1; + + const u8_header_t * u8p = (u8_header_t*)szs->data; + if ( ntohl(u8p->magic) != U8_MAGIC_NUM ) + return -1; + + //PRINT("U8:"); HEXDUMP16(6,0,u8p,sizeof(*u8p)); + + + //----- setup stack + + const int MAX_DEPTH = 25; // maximum supported directory depth + typedef struct stack_t + { + const u8_node_t * dir_end; + char * path; + } stack_t; + stack_t stack_buf[MAX_DEPTH]; + stack_t *stack = stack_buf; + stack_t *stack_max = stack_buf + MAX_DEPTH; + + + //----- setup fst + path + + const u8_node_t *fst_base = (u8_node_t*)(szs->data+ntohl(u8p->node_offset)); + const int n_fst = ntohl(fst_base->size); + const u8_node_t *fst_end = fst_base + n_fst; + const u8_node_t *dir_end = fst_end; + char * path_ptr = it->path; + char * path_end = it->path + sizeof(it->path) - MAX_DEPTH; + + TRACE("N=%d\n",n_fst); + if ( (u8*)fst_end > szs->data + szs->size ) + return -1; + //HEXDUMP(0,(u8*)fst_base-szs->data,6,sizeof(u8_node_t),fst_base,n_fst*sizeof(u8_node_t)); + //HEXDUMP16(0,(u8*)fst_end-szs->data,fst_end,32); + + + //----- cut files? + + if (it->cut_files) + { + it->index = 0; + it->fst_item = 0; + it->is_dir = 0; + + it->off = 0; + it->size = sizeof(u8_header_t); + StringCopyS(it->path,sizeof(it->path),".U8.header"); + it->func_it(it,false); + + it->off = ntohl(u8p->node_offset); + it->size = (u8*)fst_end - (u8*)fst_base; + StringCopyS(it->path,sizeof(it->path),".U8.file-table"); + it->func_it(it,false); + + uint maxoff = 0; + const u8_node_t *fst; + for ( fst = fst_base; fst < fst_end; fst++ ) + { + uint nameoff = ntohl(fst->name_off) & 0xffffff; + if ( maxoff <= nameoff ) + { + ccp fname = (ccp)fst_end + nameoff; + maxoff = nameoff + strlen(fname) + 1; + } + } + + it->off = (u8*)fst_end - szs->data; + it->size = maxoff; + StringCopyS(it->path,sizeof(it->path),".U8.string-pool"); + it->func_it(it,false); + } + + + //----- main loop + + int stat = 0; + const u8_node_t *fst; + for ( fst = fst_base + !it->show_root_node; fst < fst_end && !stat; fst++ ) + { + while ( fst >= dir_end && stack > stack_buf ) + { + // leave a directory + stack--; + it->depth = stack - stack_buf; + dir_end = stack->dir_end; + path_ptr = stack->path; + } + + ccp fname = (ccp)fst_end + (ntohl(fst->name_off)&0xffffff); + it->name = fname; + char * path_dest = it->trail_path = path_ptr; + while ( path_dest < path_end && *fname ) + *path_dest++ = *fname++; + + it->index = fst - fst_base; + it->fst_item = (u8_node_t*)fst; + it->off = ntohl(fst->offset); + it->size = ntohl(fst->size); + it->is_dir = fst->is_dir; + if (fst->is_dir) + { + if ( fst > fst_base ) + *path_dest++ = '/'; + *path_dest = 0; + + ASSERT(stackdir_end = dir_end; + stack->path = path_ptr; + stack++; + it->depth = stack - stack_buf; + dir_end = fst_base + ntohl(fst->size); + path_ptr = path_dest; + } + } + else + { + *path_dest = 0; + if ( szs->min_data_off > it->off ) + szs->min_data_off = it->off; + + const u32 max = it->off + it->size; + if ( szs->max_data_off < max ) + szs->max_data_off = max; + } + + stat = it->func_it(it,false); + } + + it->fst_item = 0; + it->name = 0; + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// IterateFilesDOL() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesDOL +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->func_it); + szs_file_t * szs = it->szs; + DASSERT(szs); + + if (!IsDolHeader(szs->data,szs->size,0)) + return FF_DOL; + + it->index = 0; + it->off = 0; + it->size = sizeof(dol_header_t); + it->is_dir = 0; + StringCopyS(it->path,sizeof(it->path),"DOL.header"); + it->func_it(it,false); + + dol_header_t *dh = (dol_header_t*)szs->data; + int sect, stat = 0; + for ( sect = 0; sect < DOL_N_SECTIONS && !stat; sect++ ) + { + const u32 size = ntohl(dh->sect_size[sect]); + if (size) + { + const u32 off = ntohl(dh->sect_off[sect]); + if ( off < szs->size ) + { + uint real_size = szs->size - off; + if ( real_size > size ) + real_size = size; + + it->index++; + it->off = off; + it->size = real_size; + StringCopyS(it->path,sizeof(it->path),GetDolSectionName(sect)); + stat = it->func_it(it,false); + } + } + } + + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Iterate Files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +szs_iterator_func GetIteratorFunction +( + file_format_t fform, // file format format + bool cut_files // true: support of cut files +) +{ + PRINT0("GetIteratorFunction(%x=%s)\n",fform,GetNameFF(fform,fform)); + + if (!std_iter_func[FF_U8]) + SetupStandardSZS(); + + return (uint)fform >= FF_N + ? 0 + : cut_files + ? cut_iter_func[fform] + : std_iter_func[fform]; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * UpdateIteratorFF ( struct szs_iterator_t *it ) +{ + if (!it) + return 0; + +// [[analyse-magic+]] +// [[ff_fallback]] + u8 * data = it->szs->data + it->off; + it->fform = RepairMagicByOpt( 0, data, it->size, it->szs->size - it->off, + FF_UNKNOWN, it->path ); + return data; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int iterate_sort_files +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + DASSERT(it->func_sort); + DASSERT( it->func_sort != iterate_sort_files ); + + if (!term) + { + if ( it->off > it->szs->size || it->off + it->size > it->szs->size ) + { + if ( it->size != M1(it->size) ) + ERROR0(ERR_WARNING, + "Invalid offset [%x..%x, size=%zx] for subfile.\n" + "=> File ignored: %s%s%s\n", + it->off, it->off+it->size, it->szs->size, + it->szs->fname, *it->szs->fname ? "/" : "", + it->path ); + return 0; + } + + AppendSubfileSZS(it->szs,it,0); + return 0; + } + + SortSubFilesSZS(it->szs,it->sort_mode); + + const szs_subfile_t * ptr = it->szs->subfile.list; + const szs_subfile_t * end = ptr + it->szs->subfile.used; + int stat = 0; + uint i; + for ( i = 0; ptr < end && !stat; i++, ptr++ ) + { + it->index = i; + it->is_dir = ptr->is_dir; + it->has_subfiles = ptr->has_subfiles; + it->fform = ptr->fform; + it->name = ptr->name; + it->brsub_version = ptr->brsub_version; + it->group = ptr->group; + it->entry = ptr->entry; + it->off = ptr->offset; + it->size = ptr->size; +// it->device = ptr->device; +// it->inode = ptr->inode; + + if (IS_M1(it->size)) + { + it->size = ( ptr+1 < end ? ptr[1].offset : it->szs->size ) - it->off; + noPRINT("SIZE==-1 => ptr=%zd/%d => %x+%x=%x | %zx\n", + ptr-(szs_subfile_t*)it->szs->subfile.list, it->szs->subfile.used, + it->off, it->size, it->off+it->size, it->szs->size ); + if ( it->off + it->size > it->szs->size ) + it->size = 0; + } + StringCopyS(it->path,sizeof(it->path),ptr->path); + stat = it->func_sort(it,false); + } + ResetFileSZS(it->szs,false); + + *it->path = 0; + it->brsub_version = 0; + it->group = GROUP_INVALID; + it->entry = ENTRY_INVALID; + it->name = 0; + const int term_stat = it->func_sort(it,true); + return stat ? stat : term_stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int iterate_sub_files +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT( it->func_sub ); + DASSERT( it->func_sub != iterate_sub_files ); + + if ( !term && it->no_recurse <= 0 && !it->is_dir ) + { + if ( it->off > it->szs->size || it->off + it->size > it->szs->size ) + { + ERROR0(ERR_WARNING, + "Illegal offset [%x..%x, size=%zx] for subfile.\n" + "=> File ignored: %s%s%s\n", + it->off, it->off+it->size, it->szs->size, + it->szs->fname, *it->szs->fname ? "/" : "", + it->path ); + return 0; + } + + UpdateIteratorFF(it); + const file_format_t fform = it->fform; + + szs_iterator_func it_func = 0; + if ( it->recurse_level < it->recurse_max ) + { + it_func = GetIteratorFunction(fform,it->cut_files); + } + else if ( it->cut_files ) + { + it_func = GetIteratorFunction(fform,true); + if (GetIteratorFunction(fform,false)) + it_func = 0; + } + PRINT0("it_func=%p\n",it_func); + + if (it_func) + { + it->has_subfiles = true; + const int stat = it->func_sub(it,false); + it->has_subfiles = false; + + szs_file_t szs2; +// [[fname+]] + InitializeSubSZS(&szs2,it->szs,it->off,it->size,fform,it->path,false); + + szs_iterator_t it2; + memcpy(&it2,it,sizeof(it2)); + it2.szs = &szs2; + it2.index = 0; + it2.client_int = 0; + it2.parent_it = it; + it2.fform = fform; + it2.recurse_level++; + + const int stat2 = it_func(&it2,false); + it2.name = 0; + *it2.path = 0; + if ( stat2 != -1 ) + it2.func_it(&it2,true); // ignore status + ResetSZS(&szs2); + + return stat; + } + } + return it->func_sub(it,term); +} + +/////////////////////////////////////////////////////////////////////////////// +// [[IterateFilesSZS]] + +int IterateFilesSZS +( + szs_file_t * szs, // valid szs + szs_iterator_func func, // call back function + void * param, // user defined parameter + bool show_root_node, // true: include root node in iteration + int recurse, // 0:off, <0:unlimited, >0:max depth + int cut_files, // <0:never, =0:auto(first level), >0:always + SortMode_t sort_mode // sort mode +) +{ + DASSERT(szs); + DASSERT(func); + + PRINT("IterateFilesSZS(rec=%d,cut=%d,sort=%d) ff=%s\n", + recurse, cut_files, sort_mode, + GetNameFF(szs->fform_file,szs->fform_arch)); + + szs_iterator_func ifunc = GetIteratorFunction(szs->fform_arch,cut_files>=0); + if (!ifunc) + return -1; + + if ( sort_mode == SORT_NONE && IsBRSUB(szs->fform_arch) ) + sort_mode = SORT_OFFSET; + const bool sort_files = sort_mode != SORT_NONE; + if ( sort_files && szs->subfile.used ) + ResetFileSZS(szs,false); + + szs_iterator_t it; + memset(&it,0,sizeof(it)); + it.szs = szs; + it.cut_files = cut_files > 0; + it.sort_mode = sort_mode; + it.func_sub = func; + it.func_sort = recurse || it.cut_files ? iterate_sub_files : func; + it.func_it = sort_files ? iterate_sort_files : it.func_sort; + it.param = param; + it.endian = &be_func; + it.show_root_node = show_root_node; + it.recurse_max = recurse >= 0 ? recurse : INT_MAX; + it.group = GROUP_INVALID; + it.entry = ENTRY_INVALID; + + const int stat = ifunc(&it,false); + it.name = 0; + *it.path = 0; + const int stat_term = it.func_it(&it,true); + return stat ? stat : stat_term; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define MAX_COLLECT_RECURSE 100 + +typedef struct collect_recurse_t +{ + szs_file_t * szs; // base szs: collect ehre + + uint level; // file depth level + + char path[PATH_MAX]; // path + char * path_ptr[MAX_COLLECT_RECURSE+1]; // save path pointers + char * path_end; // current end of path + ccp last_path; // pointer to last source path + + u32 off[MAX_COLLECT_RECURSE+1]; // save 'add_off' values + u32 add_off; // add this to get abpsoulte offset + u32 last_off; // pointer to last source offset + +} collect_recurse_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int collect_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + if (!term) + AppendSubfileSZS(it->szs,it,0); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int collect_r_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + + collect_recurse_t *cr = it->param; + DASSERT(cr); + + + if (term) + { + DASSERT( cr->level >= 0 ); + if ( cr->level > 0 && --cr->level < MAX_COLLECT_RECURSE ) + { + cr->path_end = cr->path_ptr[cr->level]; + cr->add_off = cr->off[cr->level]; + } + } + else + { + if (!it->client_int++) + { + if ( cr->level < MAX_COLLECT_RECURSE ) + { + cr->path_ptr[cr->level] = cr->path_end; + char *dest = cr->path_end + strlen(cr->path_end); + if ( dest > cr->path && dest < cr->path + sizeof(cr->path) - 3 ) + { + *dest++ = '/'; + *dest++ = '/'; + } + *dest = 0; + cr->path_end = dest; + + cr->off[cr->level] = cr->add_off; + cr->add_off = cr->last_off; + } + cr->level++; + } + + StringCopyE(cr->path_end,cr->path+sizeof(cr->path),it->path); + cr->last_off = it->off + cr->add_off; + + szs_subfile_t * file = AppendSubfileSZS(cr->szs,it,STRDUP(cr->path)); + DASSERT(file); + file->offset = cr->last_off; + + noPRINT("%2u %c%c %6x %6x %s\n", + cr->level, file->is_dir ? 'd' : '-', file->has_subfiles ? 's' : '-', + file->offset, file->size, file->path ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CollectFilesSZS +( + szs_file_t * szs, // valid szs + bool clear, // true: clear list before adding + int recurse, // 0:off, <0:unlimited, >0:max depth + int cut_files, // <0:never, =0:auto, >0:always + SortMode_t sort_mode // sort subfiles +) +{ + DASSERT(szs); + if ( clear && szs->subfile.used ) + ResetFileSZS(szs,false); + const uint base = szs->subfile.used; + DecompressSZS(szs,true,0); + + if ( recurse < 0 || recurse > MAX_COLLECT_RECURSE ) + recurse = MAX_COLLECT_RECURSE; + if ( recurse > 0 || cut_files >= 0 ) + { + collect_recurse_t cr; + memset(&cr,0,sizeof(cr)); + cr.szs = szs; + cr.path_end = cr.path; + cr.last_path = EmptyString; + IterateFilesSZS(szs,collect_r_func,&cr,false,recurse,cut_files,SORT_NONE); + } + else + IterateFilesSZS(szs,collect_func,0,false,0,-1,SORT_NONE); + + SortSubFilesSZS(szs,sort_mode); + noPRINT("%d/%d files collected.\n", szs->subfile.used - base, szs->subfile.used ); + return szs->subfile.used - base; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesData +( + const void *data, // valid data pointer + uint data_size, // size of data + szs_iterator_func func, // call back function + void * param, // user defined parameter + file_format_t fform, // not FF_UNKNOWN: use this type + ccp fname // NULL or filename +) +{ + DASSERT(data); + DASSERT(func); + + szs_file_t szs; + AssignSZS(&szs,true,(u8*)data,data_size,false,fform,fname); + const int stat + = IterateFilesSZS ( &szs, func, param, true, -1, 1, SORT_OFFSET ); + ResetSZS(&szs); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cut files: BRSUB /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool find_next_brsub_group +( + brsub_cut_t * bcut // valid data +) +{ + DASSERT(bcut); + DASSERT(bcut->bh); + DASSERT(bcut->endian); + + const uint last_off = bcut->found_off; + bcut->found_off = bcut->brsub_size; + const int last_grp = bcut->found_grp; + bcut->found_grp = -1; + + int grp; + for ( grp = 0; grp < bcut->n_grp; grp++ ) + { + const uint off = bcut->endian->rd32(bcut->bh->grp_offset+grp); + //if ( off > last_off && off < bcut->found_off ) + if ( ( off > last_off || off > 0 && off == last_off && grp > last_grp ) + && off < bcut->found_off ) + { + bcut->found_grp = grp; + bcut->found_off = off; + } + } + return bcut->found_grp >= 0; +} + +//----------------------------------------------------------------------------- +// [[CutFilesBRSUB]] + +#undef GETSTR +#define GETSTR(n,d) ( n >= bcut.brsub_size && n < bcut.file_size ? (ccp)bcut.data+n : d ) + +int CutFilesBRSUB +( + szs_iterator_t *it, // iterator struct with all infos + brsub_cut_func_t func, // call back function + void *param, // user parameter + ccp sname_tab[], // NULL or section name table + uint n_sname // number of elements in 'sname_tab' + // if 0: 'sname_tab' is terminated by NULL +) +{ + DASSERT(it); + DASSERT(it->endian); + szs_file_t * szs = it->szs; + DASSERT(szs); + const u8 * data = szs->data; + DASSERT(data); + DASSERT(func); + + if ( !szs->data || szs->size <= sizeof(brsub_header_t) ) + return -1; + + const int n_grp = GetSectionNumBRSUB(data,szs->size,it->endian); + if ( n_grp <= 0 ) + return -1; + const uint brsub_header_size = sizeof(brsub_header_t) + 4*n_grp + 4; + + brsub_cut_t bcut; + memset(&bcut,0,sizeof(bcut)); + bcut.bh = (brsub_header_t*)data; + bcut.it = it; + bcut.szs = szs; + bcut.data = data; + bcut.size = szs->size; + bcut.file_size = szs->file_size; +// [[analyse-magic]] + bcut.fform = GetByMagicFF(data,szs->size,szs->size); + bcut.version = it->endian->rd32(&bcut.bh->version); + bcut.fattrib = GetAttribFF(bcut.fform); + bcut.n_grp = n_grp; + bcut.endian = it->endian; + bcut.param = param; + + int stat = 0; + it->brsub_version = bcut.version; + it->group = GROUP_INVALID; + it->entry = ENTRY_INVALID; + + PrintID(bcut.bh->magic,sizeof(bcut.id)-1,bcut.id); + bcut.brsub_size = szs->size; + uint off = be32(&bcut.bh->size); + if ( off > sizeof(*bcut.bh) && off < bcut.brsub_size ) + bcut.brsub_size = off; + + it->no_recurse++; + it->index = 0; + it->off = 0; + it->size = brsub_header_size; + it->is_dir = 0; + PrintHeaderNameBRSUB(it->path,sizeof(it->path), + (brsub_header_t*)data,n_grp,it->endian); + u32 name_off = bcut.endian->rd32(data+brsub_header_size-4); + ccp name_str = GETSTR(name_off,0); + if (it->cut_files) + { + it->name = name_str; + stat = func(&bcut,GROUP_IDX_BRSUB_HEADER,ENTRY_IDX_ETC); + if (stat) + goto abort; + } + + if ( sname_tab && !n_sname ) + while ( sname_tab[n_sname] ) + n_sname++; + + find_next_brsub_group(&bcut); + if ( bcut.found_grp >= 0 && bcut.found_off > it->size ) + { + it->is_dir = 0; + it->off = it->size; + it->size = bcut.found_off - it->off; + bcut.path_end = it->path + + snprintf(it->path,sizeof(it->path),"%s.header.bin",bcut.id); + stat = func(&bcut,GROUP_IDX_SUB_HEADER,ENTRY_IDX_DATA); + if (stat) + goto abort; + } + + while ( bcut.found_grp >= 0 ) + { + int cur_grp = bcut.found_grp; + uint cur_off = bcut.found_off; + find_next_brsub_group(&bcut); + + it->is_dir = 0; + it->off = cur_off; + + ccp sname = cur_grp < n_sname ? sname_tab[cur_grp] : 0; + if (!sname) + sname = ""; + + if ( bcut.fattrib & FFT_BRSUB2 ) + { + bcut.gptr = (brres_group_t*)( bcut.data + it->off ); + const u32 bg_size = bcut.endian->rd32(&bcut.gptr->size); + const u32 bg_n_entry = bcut.endian->rd32(&bcut.gptr->n_entries); + + const u32 x_size = sizeof(brres_group_t) + (bg_n_entry+1)*sizeof(bg_n_entry); + if ( x_size > bg_size ) + goto plain; + + if (it->cut_files) + { + bcut.path_end = it->path + + snprintf(it->path,sizeof(it->path),".%s.s%0*u%s.header.n%u.bin", + bcut.id, n_grp > 10 ? 2 : 1, cur_grp, sname, bg_n_entry ); + it->size = bg_size; + it->name = 0; + stat = func(&bcut,cur_grp,ENTRY_IDX_GROUP); + if (stat) + goto abort; + } + + bcut.eptr = bcut.gptr->entry + 1; + int cur_entry; + for ( cur_entry = 0; cur_entry < bg_n_entry; cur_entry++, bcut.eptr++ ) + { + u32 data_off = bcut.endian->rd32(&bcut.eptr->data_off); + u32 name_off = bcut.endian->rd32(&bcut.eptr->name_off); + ccp name_ptr = GETSTR(name_off+((u8*)bcut.gptr-data),""); + { + ccp ptr; + for ( ptr = name_ptr; *ptr; ptr++ ) + if ( ptr-name_ptr > 100 || *(uchar*)ptr >= 0x7f ) + { + PRINT("!!!!!!!!!! CutFilesBRSUB() => |%.30s|\n",name_ptr); + name_ptr = ""; // [[2do]] should never happen + break; + } + } + const u8 *dptr = (u8*)bcut.gptr + data_off; + + it->off = dptr - bcut.data; + #ifdef TEST0 // [[2do]] + it->size = 1; + #else + it->size = M1(it->size); + #endif + if ( it->off <= bcut.brsub_size - 4 ) + { + const u32 len = bcut.endian->rd32(data+it->off); + if ( !(len&3) && it->off + len <= bcut.brsub_size ) + it->size = len; + } + + bcut.path_end = it->path + + snprintf(it->path,sizeof(it->path),"%s.s%0*u_%0*u%s.%s.bin", + bcut.id, + n_grp > 10 ? 2 : 1, cur_grp, + bg_n_entry > 10 ? 2 : 1, cur_entry, + sname, name_ptr ); + it->name = name_ptr; + stat = func(&bcut,cur_grp,cur_entry); + if (stat) + goto abort; + } + bcut.gptr = 0; + bcut.eptr = 0; + } + else + { + plain: + #if 1 // [[2do]] + it->size = bcut.found_off - cur_off; + #else + it->size = bcut.found_off > cur_off + ? bcut.found_off - cur_off + : bcut.size - cur_off; + #endif + noPRINT("--> %x + %x = %x <= %x\n", + it->off,it->size,it->off+it->size,bcut.brsub_size); + if (it->size) + { + bcut.path_end = it->path + + snprintf(it->path,sizeof(it->path),"%s.s%0*u%s.data.bin", + bcut.id, n_grp > 10 ? 2 : 1, cur_grp, sname ); + it->name = 0; + stat = func(&bcut,cur_grp,ENTRY_IDX_DATA); + if (stat) + goto abort; + } + } + } + + if ( it->cut_files && bcut.brsub_size < bcut.size ) + { + it->off = bcut.brsub_size; + it->size = bcut.size - bcut.brsub_size; + it->name = 0; + snprintf(it->path,sizeof(it->path),".%s.string-pool.bin",bcut.id); + int stat = func(&bcut,GROUP_IDX_STRING_POOL,ENTRY_IDX_ETC); + if (stat) + return stat; + } + + abort: + it->brsub_version = 0; + it->group = GROUP_INVALID; + it->entry = ENTRY_INVALID; + it->name = 0; + return stat; +} + +#undef GETSTR + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int IterateFilesFuncBRSUB +( + brsub_cut_t *bcut, // pointer to data structure + int grp, // index of group, <0: grp_entry_t + int entry // index of entry, -1: no entry +) +{ + DASSERT(bcut); + szs_iterator_t *it = bcut->it; + DASSERT(it); + DASSERT(it->func_it); + + it->group = grp; + it->entry = entry; + const int stat = it->func_it(it,false); + it->index++; + return stat; +} + +//----------------------------------------------------------------------------- + +int IterateFilesBRSUB +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + return CutFilesBRSUB(it,IterateFilesFuncBRSUB,0,0,0); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs_subfile_list_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSubfileList ( szs_subfile_list_t * sl ) +{ + DASSERT(sl); + memset(sl,0,sizeof(*sl)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetSubfileList ( szs_subfile_list_t * sl ) +{ + DASSERT(sl); + DASSERT( sl->used <= sl->size ); + DASSERT( !sl->size == !sl->list ); + + PurgeSubfileList(sl); + FREE(sl->list); + InitializeSubfileList(sl); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PurgeSubfileList ( szs_subfile_list_t * sl ) +{ + DASSERT(sl); + DASSERT( sl->used <= sl->size ); + DASSERT( !sl->size == !sl->list ); + + uint i; + for ( i = 0; i < sl->used; i++ ) + { + szs_subfile_t *sf = sl->list + i; + if ( sf->data_alloced && sf->data ) + FREE(sf->data); + FreeString(sf->path); + FreeString(sf->load_path); + } + sl->used = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +szs_subfile_t * InsertSubFileList +( + szs_subfile_list_t * sl, // valid subfile list + uint insert_pos, // insert position index, robust + ccp path, // NULL of path name + bool move_path // ignored if 'path' is NULL + // false: make a copy of path + // true: use path and free it on reset +) +{ + DASSERT(sl); + DASSERT( sl->used <= sl->size ); + DASSERT( !sl->size == !sl->list ); + + if ( sl->used == sl->size ) + { + sl->size += sl->size/4 + 1000; + sl->list = REALLOC(sl->list,sl->size*sizeof(*sl->list)); + TRACE("REALLOC(sl->list): %u (size=%zu) -> %p\n", + sl->size,sl->size*sizeof(*sl->list),sl->list); + } + + sl->sort_mode = SORT_NONE; + + szs_subfile_t * file; + if ( insert_pos < sl->used ) + { + // insert + memmove( sl->list + insert_pos + 1, + sl->list + insert_pos, + ( sl->used - insert_pos ) * sizeof(*sl->list) ); + sl->used++; + file = sl->list + insert_pos; + } + else // append + file = sl->list + sl->used++; + + memset(file,0,sizeof(*file)); + if (path) + file->path = move_path ? path : STRDUP(path); + return file; +} + +/////////////////////////////////////////////////////////////////////////////// + +szs_subfile_t * InsertSubfileSZS +( + szs_file_t *szs, // valid data structure + uint insert_pos, // insert position index, robust + szs_iterator_t *it, // not NULL: copy source info + ccp path // not NULL: copy to member 'path' +) +{ + DASSERT(szs); + szs_subfile_t *file = InsertSubFileList(&szs->subfile,insert_pos,0,0); + DASSERT(file); + if (it) + { + file->is_dir = it->is_dir; + file->has_subfiles = it->has_subfiles; + file->fform = it->fform; + file->offset = it->off; + file->size = it->size; + file->path = path ? path : STRDUP(it->path); + file->name = it->name; + file->brsub_version = it->brsub_version; + file->group = it->group; + file->entry = it->entry; + } + else + file->path = path; + return file; +} + +/////////////////////////////////////////////////////////////////////////////// + +szs_subfile_t * FindSubFileSZS +( + szs_subfile_list_t * sl, // valid subfile list + ccp path, // path name to search + uint start_index // start seearch at this index +) +{ + DASSERT(sl); + DASSERT( sl->used <= sl->size ); + DASSERT( !sl->size == !sl->list ); + + if ( sl->used > start_index && path ) + { + szs_subfile_t *ptr, *end = sl->list + sl->used; + for ( ptr = sl->list + start_index; ptr < end; ptr++ ) + if (!strcmp(path,ptr->path)) + return ptr; + } + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sort sub files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum // sort order in BRRES files +{ + SORTVAL_3DMODELS, + SORTVAL_TEXTURES, + SORTVAL_ANMCHR, + SORTVAL_ANMCLR, + SORTVAL_ANMTEXPAT, + SORTVAL_ANMTEXSRT, + SORTVAL_ANMSCN, + + SORTVAL_ANMSHP, // only known: SORTVAL_ANMSHP > SORTVAL_ANMCHR + + SORTVAL_OTHER +}; + +//----------------------------------------------------------------------------- + +static int get_brres_sort_value ( ccp name ) +{ + switch (name[0]) + { + case '3': + return !memcmp(name,"3DModels(NW4R)/",15) + ? SORTVAL_3DMODELS + : SORTVAL_OTHER; + + case 'T': + return !memcmp(name,"Textures(NW4R)/",15) + ? SORTVAL_TEXTURES + : SORTVAL_OTHER; + + case 'A': + switch(name[3]) + { + case 'C': + return !memcmp(name,"AnmChr(NW4R)/",13) + ? SORTVAL_ANMCHR + : !memcmp(name,"AnmClr(NW4R)/",13) + ? SORTVAL_ANMCLR + : SORTVAL_OTHER; + + case 'T': + return !memcmp(name,"AnmTexPat(NW4R)/",16) + ? SORTVAL_ANMTEXPAT + : !memcmp(name,"AnmTexSrt(NW4R)/",16) + ? SORTVAL_ANMTEXSRT + : SORTVAL_OTHER; + + case 'S': + return !memcmp(name,"AnmScn(NW4R)/",13) + ? SORTVAL_ANMSCN + : !memcmp(name,"AnmShp(NW4R)/",13) + ? SORTVAL_ANMSCN + : SORTVAL_OTHER; + } + } + + return SORTVAL_OTHER; +} + +/////////////////////////////////////////////////////////////////////////////// +#ifdef TEST +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_iname ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + return PathCmp(a->path,b->path,2); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_u8 ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + return NintendoCmp(a->path,b->path); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_brres ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + const int aval = get_brres_sort_value(a->path); + const int bval = get_brres_sort_value(b->path); + if ( aval != bval ) + return aval < bval ? -1 : +1; + + if ( !a->is_dir && !b->is_dir && aval == SORTVAL_3DMODELS ) + { + const uint alen = strlen(a->path); + const uint blen = strlen(b->path); + if ( alen < blen ) + { + if (!memcmp(a->path,b->path,alen)) + return +1; + } + else if ( alen > blen ) + { + if (!memcmp(a->path,b->path,blen)) + return -1; + } + } + + return NintendoCmp(a->path,b->path); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_offset ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + if ( a->offset < b->offset ) return -1; + if ( a->offset > b->offset ) return +1; + + if ( a->size < b->size ) return -1; + if ( a->size > b->size ) return +1; + + if ( a->is_dir != b->is_dir ) return a->is_dir ? -1 : 1; + + if ( a->group < b->group ) return -1; + if ( a->group > b->group ) return +1; + + if ( a->entry < b->entry ) return -1; + if ( a->entry > b->entry ) return +1; + + return PathCmp(a->path,b->path,2); +} + +/////////////////////////////////////////////////////////////////////////////// +#else +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_iname ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + const int stat = strcasecmp(a->path,b->path); + return stat ? stat : strcmp(a->path,b->path); +} + +static int cmp_subfile_u8 ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + // try to sort in a nintendo like way + // 1.) ignoring case but sort carefully directories + // 2.) files before sub directories + + static char transform[0x100] = {0}; + if (!transform[1]) // ==> setup needed once! + { + // define some special characters + + uint index = 1; + transform[(u8)'/'] = index++; + transform[(u8)'.'] = index++; + + // define digits + + uint i; + for ( i = '0'; i <= '9'; i++ ) + transform[i] = index++; + + // define letters + + for ( i = 'A'; i <= 'Z'; i++ ) + transform[i] = transform[i-'A'+'a'] = index++; + + // define all other + + for ( i = 1; i < sizeof(transform); i++ ) + if (!transform[i]) + transform[i] = index++; + + DASSERT( index <= sizeof(transform) ); + //HexDump16(stderr,0,0,transform,sizeof(transform)); + } + + //--- setup + + const u8 * ap = (u8*)a->path; + const u8 * bp = (u8*)b->path; + const u8 * a_last_slash = (u8*)strrchr(a->path,'/'); + const u8 * b_last_slash = (u8*)strrchr(b->path,'/'); + const uint a_dir_len = a_last_slash ? a_last_slash-ap+1 : 0; + const uint b_dir_len = b_last_slash ? b_last_slash-bp+1 : 0; + const uint min_dir_len = a_dir_len < b_dir_len ? a_dir_len : b_dir_len; + const u8 * a_end_dir_cmp = ap + min_dir_len; + + //--- first compare directory part + + while ( ap < a_end_dir_cmp ) + { + if ( transform[*ap] != transform[*bp] ) + { + const int stat = (int)(transform[*ap]) - (int)(transform[*bp]); + return stat; + } + ap++, bp++; + } + + if ( a_dir_len != b_dir_len ) + return a_dir_len - b_dir_len; + + int stat = strncmp(a->path,b->path,min_dir_len); + if (stat) + return stat; + + + //--- and now compare the file part + + while ( *ap || *bp ) + { + if ( transform[*ap] != transform[*bp] ) + { + const int stat = (int)(transform[*ap]) - (int)(transform[*bp]); + return stat; + } + ap++, bp++; + } + + return strcmp(a->path,b->path); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_brres ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + const int aval = get_brres_sort_value(a->path); + const int bval = get_brres_sort_value(b->path); + if ( aval != bval ) + return aval < bval ? -1 : +1; + + if ( !a->is_dir && !b->is_dir && aval == SORTVAL_3DMODELS ) + { + const uint alen = strlen(a->path); + const uint blen = strlen(b->path); + if ( alen < blen ) + { + if (!memcmp(a->path,b->path,alen)) + return +1; + } + else if ( alen > blen ) + { + if (!memcmp(a->path,b->path,blen)) + return -1; + } + } + + return cmp_subfile_u8(a,b); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_offset ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + if ( a->offset < b->offset ) return -1; + if ( a->offset > b->offset ) return +1; + + if ( a->size < b->size ) return -1; + if ( a->size > b->size ) return +1; + + if ( a->is_dir != b->is_dir ) return a->is_dir ? -1 : 1; + + if ( a->group < b->group ) return -1; + if ( a->group > b->group ) return +1; + + if ( a->entry < b->entry ) return -1; + if ( a->entry > b->entry ) return +1; + + return strcmp(a->path,b->path); +} + +/////////////////////////////////////////////////////////////////////////////// +#endif +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int cmp_subfile_pack ( const szs_subfile_t * a, const szs_subfile_t * b ) +{ + ccp sa = strrchr(a->path,'/'); + if (!sa) + sa = a->path; + ccp pa = strrchr(sa,'.'); + + ccp sb = strrchr(b->path,'/'); + if (!sb) + sb = b->path; + ccp pb = strrchr(sb,'.'); + + int stat = strcmp( pa ? pa : EmptyString, pb ? pb : EmptyString ); + return stat ? stat : PathCmp(a->path,b->path,2); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +CmpFuncSubFile GetCmpFunc +( + SortMode_t sort_mode, // wanted sort mode + file_format_t fform // use it if 'sort_mode==SORT_AUTO' +) +{ + switch (GetSortMode(sort_mode,fform,SORT_INAME)) + { + case SORT_U8: + return cmp_subfile_u8; + + case SORT_BRRES: + return cmp_subfile_brres; + + case SORT_PACK: + return cmp_subfile_pack; + + case SORT_OFFSET: + return cmp_subfile_offset; + + //case SORT_BREFF: + default: + return cmp_subfile_iname; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool SortSubFiles +( + // returns true if new sorting done + + szs_subfile_list_t * sl, // valid subfile list + SortMode_t sort_mode, // wanted sort mode + file_format_t fform, // use it if sort_mode==SORT_AUTO + FormatField_t * order_list // NULL or a priority sort list +) +{ + DASSERT(sl); + DASSERT( sl->used <= sl->size ); + DASSERT( !sl->size == !sl->list ); + + sort_mode = GetSortMode(sort_mode,fform,SORT_INAME); + if ( sl->sort_mode != sort_mode && sort_mode != SORT_NONE ) + { + sl->sort_mode = sort_mode; + uint n = sl->used; + if ( n > 1 ) + { + szs_subfile_t * list = sl->list; + if ( order_list && order_list->used > 0 ) + { + uint count = 0; + FormatFieldItem_t *ptr = order_list->list, *end; + for ( end = ptr + order_list->used; ptr < end; ptr++ ) + { + ccp fname = ptr->key; + if ( *fname == '.' && fname[1] == '/' ) + fname += 2; + szs_subfile_t *found = FindSubFileSZS(sl,fname,count); + if (found) + { + noPRINT(" %s <- %s\n",list->path, found->path); + szs_subfile_t temp; + memcpy(&temp,list,sizeof(temp)); + memcpy(list,found,sizeof(*list)); + memcpy(found,&temp,sizeof(*found)); + count++; + list++; + n--; + } + } + } + + if ( n > 1 ) + qsort( list, n, sizeof(*sl->list), + (qsort_func)GetCmpFunc(sort_mode,0) ); + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool SortSubFilesSZS +( + szs_file_t * szs, // valid szs structure + SortMode_t sort_mode // wanted sort mode +) +{ + DASSERT(szs); + return SortSubFiles( &szs->subfile, sort_mode, + szs->fform_arch, szs->order_list ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DiffSZS() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError DiffSZS +( + szs_file_t * szs1, // first szs to compare + szs_file_t * szs2, // second szs tp compare + int recurse, // 0:off, <0:unlimited, >0:max depth + int cut_files, // <0:never, =0:auto, >0:always + bool quiet // true: be quiet +) +{ + PRINT("DiffSZS(%p,%p,%d,%d,%d)\n",szs1,szs2,recurse,cut_files,quiet); + DASSERT(szs1); + DASSERT(szs2); + + if (!szs1->subfile.used) + CollectFilesSZS(szs1,false,recurse,cut_files,SORT_INAME); + if (!szs2->subfile.used) + CollectFilesSZS(szs2,false,recurse,cut_files,SORT_INAME); + + const szs_subfile_t * p1 = szs1->subfile.list; + const szs_subfile_t * e1 = szs1->subfile.list + szs1->subfile.used; + const szs_subfile_t * p2 = szs2->subfile.list; + const szs_subfile_t * e2 = szs2->subfile.list + szs2->subfile.used; + + noPRINT("PTR: %p..%p/%u, %p..%p/%u\n", + p1, e1, szs1->subfile.used, + p2, e2, szs2->subfile.used ); + + static char only_message[] = "* Only in source #%u: %s\n"; + + int differ = 0; + while ( p1 && p1 < e1 && p2 && p2 < e2 ) + { + noPRINT("\t%s : %s\n",p1->path,p2->path); + const int stat = cmp_subfile_iname(p1,p2); + + if ( stat < 0 ) + { + if (quiet) + return ERR_DIFFER; + differ++; + printf(only_message,1,p1->path); + p1++; + continue; + } + + if ( stat > 0 ) + { + if (quiet) + return ERR_DIFFER; + differ++; + printf(only_message,2,p2->path); + p2++; + continue; + } + + if ( p1->is_dir != p2->is_dir ) + { + if (quiet) + return ERR_DIFFER; + differ++; + printf("* File types differ: %s\n",p2->path); + p1++; + p2++; + continue; + } + + if (!p1->is_dir) + { + if ( p1->size != p2->size ) + { + if (quiet) + return ERR_DIFFER; + differ++; + printf("* File size differ: %s [%u%+d=%u]\n", + p1->path, p1->size, p2->size - p1->size, p2->size ); + } + else if ( p1->size + && p1->size <= szs1->size + && p1->offset + p1->size <= szs1->size + && memcmp( szs1->data + p1->offset, + szs2->data + p2->offset, p1->size )) + { + if (quiet) + return ERR_DIFFER; + differ++; + printf("* File data differ: %s\n", p1->path ); + } + else if ( !quiet && verbose > 1 ) + { + printf(" + Files identical: %s\n", p1->path ); + } + } + + p1++; + p2++; + } + + while ( p1 && p1 < e1 ) + { + if (quiet) + return ERR_DIFFER; + differ++; + printf(only_message,1,p1->path); + p1++; + } + + while ( p2 && p2 < e2 ) + { + if (quiet) + return ERR_DIFFER; + differ++; + printf(only_message,2,p2->path); + p2++; + } + + return differ ? ERR_DIFFER : ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string_pool_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeStringPool ( string_pool_t * sp ) +{ + DASSERT(sp); + memset(sp,0,sizeof(*sp)); + InitializeParamField(&sp->pf); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetStringPool ( string_pool_t * sp ) +{ + DASSERT(sp); + ResetParamField(&sp->pf); + FREE(sp->n2s_list); + FREE(sp->data); + FREE(sp->offset); + InitializeStringPool(sp); +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 InsertStringPool + ( string_pool_t * sp, ccp string, bool move_string, ccp data ) +{ + DASSERT(sp); + if (string) + { + PRINT_IF( !strcmp(string,"-"),"!!! MINUS ADDED !!!\n"); + if (!sp->data) + { + ParamFieldItem_t *pi + = InsertParamFieldEx(&sp->pf,string,move_string,sp->pf.used+1,0); + if (pi) + { + if (data) + pi->data = (void*)data; + if ( pi->num >= sp->n2s_size ) + { + const uint new_size = 3*pi->num/2 + 0x40; + sp->n2s_list + = REALLOC( sp->n2s_list, sizeof(*sp->n2s_list)*new_size ); + memset( sp->n2s_list + sp->n2s_size, 0, + ( new_size - sp->n2s_size ) * sizeof(*sp->n2s_list) ); + sp->n2s_size = new_size; + } + DASSERT( pi->num < sp->n2s_size ); + sp->n2s_list[pi->num] = pi->key; + noPRINT("++ %d->%s [%s]\n",pi->num,pi->key,string); + return pi->num; + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CalcStringPool + ( string_pool_t * sp, u32 base_offset, const endian_func_t * endian ) +{ + DASSERT(sp); + DASSERT(!sp->data); + DASSERT(!sp->offset); + + //HEXDUMP16(0,0,sp->n2s_list,sizeof(*sp->n2s_list)*sp->n2s_size); + + u32 * offset = CALLOC(sp->pf.used,sizeof(*offset)); + sp->offset = offset; + + // align string pool + u32 off = ALIGN32(base_offset,4) - base_offset; + uint size = off; + + ParamFieldItem_t *ptr, *end = sp->pf.field + sp->pf.used; + for ( ptr = sp->pf.field; ptr < end; ptr++ ) + size += ALIGN32(strlen(ptr->key)+5,4); + sp->size = size; + sp->data = CALLOC(1,size); + + for ( ptr = sp->pf.field; ptr < end; ptr++ ) + { + *offset++ = base_offset + off + 4; + u8 * str_ptr = sp->data + off; + const uint slen = strlen(ptr->key); + endian->wr32(str_ptr,slen); + memcpy(str_ptr+4,ptr->key,slen); + off += ALIGN32(slen+5,4); + } + DASSERT( offset == sp->offset + sp->pf.used ); +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 FindStringPool ( string_pool_t * sp, ccp string, u32 not_found_value ) +{ + DASSERT(sp); + if (sp->data) + { + DASSERT(sp->offset); + const int str_idx = FindParamFieldIndex(&sp->pf,string,-1); + noPRINT("STRING-IDX=%d %s\n",str_idx,string); + if ( str_idx >= 0 ) + return sp->offset[str_idx]; + } + return not_found_value; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SetupStandardSZS /////////////// +/////////////////////////////////////////////////////////////////////////////// + +szs_iterator_func std_iter_func[FF_N] = {0}; +szs_iterator_func cut_iter_func[FF_N] = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +void SetupStandardSZS() +{ + static bool done = false; + if (done) + return; + done = true; + + std_iter_func[FF_U8] = IterateFilesU8; + std_iter_func[FF_WU8] = IterateFilesU8; + std_iter_func[FF_RARC] = IterateFilesRARC; + std_iter_func[FF_RKC] = IterateFilesRKC; + std_iter_func[FF_PACK] = IterateFilesPACK; + std_iter_func[FF_BRRES] = IterateFilesBRRES; + std_iter_func[FF_BREFF] = IterateFilesBREFF; + std_iter_func[FF_BREFT] = IterateFilesBREFF; + #if 0 + std_iter_func[FF_TEX] = IterateFilesBRRES; + std_iter_func[FF_TEX_CT] = IterateFilesBRRES; + #endif + + memcpy(cut_iter_func,std_iter_func,sizeof(cut_iter_func)); + cut_iter_func[FF_DOL] = IterateFilesDOL; + + int i; + for ( i = FF__FIRST_BRSUB; i <= FF__LAST_BRSUB; i++ ) + cut_iter_func[i] = IterateFilesBRSUB; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-szs.h b/project/src/lib-szs.h new file mode 100644 index 0000000..fbdad30 --- /dev/null +++ b/project/src/lib-szs.h @@ -0,0 +1,1258 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// all about SZS, YAZ0 and U8 files: +// http://wiki.tockdom.de/index.php?title=SZS +// http://wiki.tockdom.de/index.php?title=YAZ0 +// http://wiki.tockdom.de/index.php?title=U8 + +/////////////////////////////////////////////////////////////////////////////// +// +// Some notes about the name 'szs': +// +// At project beginning the szs_* data structures were implemented to +// manage SZS files. Meanwhile they become a general data container. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_SZS_H +#define SZS_LIB_SZS_H 1 + +#include "lib-std.h" +#include "lib-object.h" +#include "lib-lecode.h" +#include "db-mkw.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// yaz0_header_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define YAZ0_MAGIC "Yaz0" +#define YAZ1_MAGIC "Yaz1" +#define XYZ0_MAGIC "xYz0" + +#define YAZ0_MAGIC_NUM 0x59617a30 +#define YAZ1_MAGIC_NUM 0x59617a31 +#define XYZ0_MAGIC_NUM 0x78597a30 + +typedef struct yaz0_header_t +{ + char magic[4]; // = YAZ*_MAGIC + be32_t uncompressed_size; // total size of uncompressed data + char padding[8]; // always 0? +} +__attribute__ ((packed)) yaz0_header_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// u8 support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define WU8_MAGIC_NUM 0x57553861 +#define U8_MAGIC_NUM 0x55AA382D +#define U8_DEFAULT_ALIGN 0x20 + +typedef struct u8_header_t +{ + be32_t magic; // = U8_MAGIC_NUM + be32_t node_offset; // offset of first node + be32_t fst_size; // size of all nodes including the string table. + be32_t data_offset; // file offset of data + char padding[0]; // filled with 0xcc until the first node +} +__attribute__ ((packed)) u8_header_t; + +//----------------------------------------------------------------------------- + +typedef struct u8_node_t +{ + union + { + u8 is_dir; // directory flag + be32_t name_off; // mask with 0x00ffffff + }; + + be32_t offset; + be32_t size; +} +__attribute__ ((packed)) u8_node_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// string_pool_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[string_pool_t]] + +typedef struct string_pool_t +{ + //--- incomming data + ParamField_t pf; // collected strings + + //--- num-to-string reference; not used elements are NULL + ccp *n2s_list; // reference list + uint n2s_size; // alloced elements of 'n2s_list' + + //--- created string pool + u8 * data; // NULL or string pool + uint size; // size of 'data' + u32 * offset; // offset for each string in 'sf' + +} string_pool_t; + +//----------------------------------------------------------------------------- + +void InitializeStringPool ( string_pool_t * sp ); +void ResetStringPool ( string_pool_t * sp ); +u32 InsertStringPool ( string_pool_t * sp, ccp string, bool move_string, ccp data ); +void CalcStringPool ( string_pool_t * sp, u32 base_offset, + const endian_func_t * endian ); +u32 FindStringPool ( string_pool_t * sp, ccp string, u32 not_found_value ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs_subfile_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[szs_subfile_t]] + +typedef struct szs_subfile_t +{ + bool is_dir; // true: directory entry + bool has_subfiles; // true: this file has enabled sub files + bool removed; // true: file marked as removed + bool data_alloced; // true: 'data' is alloced + u16 fform; // NULL or file format + u16 dir_id; // unique directory id + u32 offset; // offset + u32 size; // size of file + u8 *data; // not NULL: data of 'size' bytes + ccp path; // alloced path name + ccp load_path; // not null: load file from here (alloced) + struct + szs_subfile_t *ext; // related external data + + dev_t device; // link support: device number + ino_t inode; // link support: inode number + uint link_index; // >0: is linked to others with same index + + ccp name; // pointer to name of object + int brsub_version; // 0 or version of parent brsub file + s16 group; // group index, type grp_entry_t + s16 entry; // entry index, type grp_entry_t + +} szs_subfile_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[szs_subfile_list_t]] + +typedef struct szs_subfile_list_t +{ + szs_subfile_t * list; // alloced list of sub files + uint used; // number of used elements in 'list' + uint size; // number of allocated elements in 'list' + SortMode_t sort_mode; // current sort mode + uint link_count; // last used link index + +} szs_subfile_list_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeSubfileList ( szs_subfile_list_t * sl ); +void ResetSubfileList ( szs_subfile_list_t * sl ); +void PurgeSubfileList ( szs_subfile_list_t * sl ); + +szs_subfile_t * InsertSubFileList +( + szs_subfile_list_t * sl, // valid subfile list + uint insert_pos, // insert position index, robust + ccp path, // NULL of path name + bool move_path // ignored if 'path' is NULL + // false: make a copy of path + // true: use path and free it on reset +); + +static inline szs_subfile_t * AppendSubFileList +( + szs_subfile_list_t * sl, // valid subfile list + ccp path, // NULL of path name + bool move_path // ignored if 'path' is NULL + // false: make a copy of path + // true: use path and free it on reset +) +{ + return InsertSubFileList(sl,~0,path,move_path); +} + +szs_subfile_t * FindSubFileSZS +( + szs_subfile_list_t * sl, // valid subfile list + ccp path, // path name to search + uint start_index // start seearch at this index +); + +bool SortSubFiles +( + // returns true if new sorting done + + szs_subfile_list_t * sl, // valid subfile list + SortMode_t sort_mode, // wanted sort mode + file_format_t fform, // use it if sort_mode==SORT_AUTO + FormatField_t * order_list // NULL or a priority sort list +); + +/////////////////////////////////////////////////////////////////////////////// + +typedef int (*CmpFuncSubFile) ( const szs_subfile_t * a, const szs_subfile_t * b ); + +CmpFuncSubFile GetCmpFunc +( + SortMode_t sort_mode, // wanted sort mode + file_format_t fform // use it if 'sort_mode==SORT_AUTO' +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum mark /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// Flags: +// _U : object used +// _PF : object active with full presence flag + +enum szs_marker_t +{ + SLOT_31_71_SUNDS = 0x01, // bit for 'szs_file_t:slot_31_71' + SLOT_31_71_PYLON01 = 0x02, // bit for 'szs_file_t:slot_31_71' + + SLOT_42_KART_TRUCK_U = 0x01, // bit for 'szs_file_t:slot_42' + SLOT_42_CAR_BODY_U = 0x02, // bit for 'szs_file_t:slot_42' + SLOT_42_ALL_U = 0x04, // bit for 'szs_file_t:slot_42' + SLOT_42_KART_TRUCK_PF = 0x10, // bit for 'szs_file_t:slot_42' + SLOT_42_CAR_BODY_PF = 0x20, // bit for 'szs_file_t:slot_42' + SLOT_42_ALL_PF = 0x40, // bit for 'szs_file_t:slot_42' + + SLOT_62_HEYHOSHIP = 0x01, // bit for 'szs_file_t:slot_62' +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// have_szs_file_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[have_szs_file_t]] +// order is important, compare have_szs_name[], have_szs_file[] and ct.wiimm.de + +typedef enum have_szs_file_t +{ + HAVESZS_COURSE_LEX, // course.lex + HAVESZS_OBJFLOW, // common/ObjFlow.bin + HAVESZS_GHT_ITEM, // common/GeoHitTableItem.bin + HAVESZS_GHT_ITEM_OBJ, // common/GeoHitTableItemObj.bin + HAVESZS_GHT_KART, // common/GeoHitTableKart.bin + HAVESZS_GHT_KART_OBJ, // common/GeoHitTableKartObj.bin + HAVESZS_ITEM_SLOT_TABLE, // ItemSlotTable/ItemSlotTable.slt + HAVESZS_MINIGAME, // common/minigame.kmg + HAVESZS__N +} +__attribute__ ((packed)) have_szs_file_t; + +extern const ccp have_szs_name[HAVESZS__N]; +extern const ccp have_szs_file[HAVESZS__N]; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs_file_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[szs_file_t]] + +struct brsub_list_t; + +typedef struct szs_file_t +{ + //--- info + + ccp fname; // path of read file, never NULL + ccp dest_fname; // not NULL: planned destination file name + ccp cache_fname; // not NULL: link to the cache on SaveSZS() + FileAttrib_t fatt; // file attribute + file_format_t fform_file; // source file format + file_format_t fform_arch; // (decompressed) archive format + file_format_t fform_current; // current format of 'data' + ff_attrib_t ff_attrib; // attributes of 'fform_arch' + int ff_version; // -1 or version of 'fform_arch' + const endian_func_t * endian; // endian functions + bool links; // true: support of hardlinks enabled + bool readonly; // true: file was opened for readonnly purpose + bool cache_used; // true: compression by cache copy + + + //--- parent + + struct szs_file_t * parent; // NULL or pointer to parent SZS + uint parent_off; // offset to parent data + + + //--- data sections + + u8 * cdata; // compressed data + u8 * data; // plain data + + size_t csize; // size of 'cdata' + size_t size; // size of 'data' + size_t file_size; // size of container file + + bool cdata_alloced; // true: 'cdata' must be freed + bool data_alloced; // true: 'data' must be freed + + u32 min_data_off; // minimum found data offset + u32 max_data_off; // maximum found data offset + // the string pool does not start before + + ccp obj_name; // NULL or name of object + u32 obj_id; // id of object + bool obj_name_alloced; // true: name must be freed + bool obj_id_used; // false: 'obj_id' not used or set + + DataContainer_t *old_container; // old data [[container]] support, init with NULL + Container_t container; // container data + + + //--- statistics, determined by LoadObjFileListSZS() + + UsedFileFILE_t *used_file; // NULL or alloced and set vector + ParamField_t *required_file; // list with required files by setting + int n_cannon; // number of defined cannons, -1: unknown + IsArena_t is_arena; // one of ARENA_* + + bool slot_analyzed; // true: slot values are valid + uint slot_31_71; // slot required because of objects + uint slot_42; // slot possible because of objects + uint slot_62; // slot required because of objects + + + //--- patching support + + patch_file_t brres_type; // type of last detected BRRES file + bool dont_patch_mdl; // true: Don't MDL patch MDL files + // of BRRES, because `disabled´ + bool aiparam_removed;// true: AIParam removed + bool allow_ext_data; // allow the following external data + szs_subfile_list_t ext_data; // list of sub files with external data + + + //--- composing support + + szs_subfile_list_t subfile; // list of sub files + struct brsub_list_t * brsub_list; // list of brres sub objects + string_pool_t string_pool; // string pool + FormatField_t * order_list; // not NULL: Use list for primary order + + + //--- specific subfiles, set by FindSpecialFilesSZS() + + bool special_done; // true: scanning for special files already done + + u8* course_kcl_data; // not NULL: pointer and size + uint course_kcl_size; // of file 'course.kcl' + u8* course_kmp_data; // not NULL: pointer and size + uint course_kmp_size; // of file 'course.kmp' + u8* course_lex_data; // not NULL: pointer and size + uint course_lex_size; // of file 'course.lex' + u8* course_model_data; // not NULL: pointer and size + uint course_model_size; // of file 'course_model.brres' + u8* course_d_model_data; // not NULL: pointer and size + uint course_d_model_size; // of file 'course_d_model.brres' + u8* vrcorn_model_data; // not NULL: pointer and size + uint vrcorn_model_size; // of file 'vrcorn_model.brres' + u8* map_model_data; // not NULL: pointer and size + uint map_model_size; // of file 'map_model.brres' + + bool have_ice_brres; // true: file 'ice.brres' found + u8 moonview_mdl_stat; // 0: none found + // 1: some materials found + // 2: all materials found + // 3: all materials found & content ok + + bool szs_special[HAVESZS__N]; // true: special file found + kmp_special_t kmp_special; // list: >0: number of special KMP objects found + uint have_lex; // bit field for found lex eleemnts + + ParamField_t *special_file; // list with files of directory /common/ + +} szs_file_t; + +//----------------------------------------------------------------------------- +// [[szs_norm_t]] + +typedef struct szs_norm_t +{ + u32 namepool_size; + u32 total_size; + bool have_pt_dir; + bool rm_aiparam; + bool clean_lex; + +} szs_norm_t; + +//----------------------------------------------------------------------------- + +void SetupStandardSZS(); +void SetupExtendedSZS(); + +void InitializeSZS ( szs_file_t * szs ); +void ResetSZS ( szs_file_t * szs ); +void ResetFileSZS ( szs_file_t * szs, bool remove_list ); + +szs_subfile_t * FindLinkSZS + ( szs_file_t *szs, dev_t device, ino_t inode, const szs_subfile_t *exclude ); + +// [[container+]] +DataContainer_t * ContainerSZS ( szs_file_t * szs ); +Container_t * SetupContainerSZS ( szs_file_t * szs ); +ContainerData_t * LinkContainerSZS ( szs_file_t * szs ); +ContainerData_t * MoveContainerSZS ( szs_file_t * szs ); +void ClearContainerSZS ( szs_file_t * szs ); + +void InitializeSubSZS +( + szs_file_t *szs, // SZS to initialize + szs_file_t *base, // valid base SZS + uint off, // offset of selected data within 'base' + uint size, // not NULL: size of selected data + file_format_t fform, // not FF_UNKNOWN: use this type + ccp fname, // NULL or file names + bool decompress // true: decompress if possible +); + +void AssignSZS +( + szs_file_t *szs, // SZS to initialize + bool init_szs, // true: InitializeSZS(), false: ResetSZS() + void *data, // data to assign + uint size, // size of 'data' + bool move_data, // true: free 'data' on reset + file_format_t fform, // not FF_UNKNOWN: use this type + ccp fname // NULL or file names +); + +enumError LoadSZS +( + szs_file_t * szs, // valid szs + ccp fname, // valid pointer to filenname + bool decompress, // decompress after loading + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + bool mark_readonly // true: the file will never written +); + +enumError SaveSZS +( + szs_file_t * szs, // valid szs + ccp fname, // valid pointer to filenname + bool overwrite, // true: overwrite data + bool compress // false: store decompressed + // true: store compressed + +); + +void LinkCacheRAW +( + // link/copy only files with data! + + ccp cache_fname, // NULL or path to cache file + ccp src_fname, // source filename + cvp data, // data of link() failed + uint size // size of data +); + +static inline void LinkCacheSZS +( + szs_file_t * szs, // valid szs + ccp src_fname // source filename +) +{ + DASSERT(szs); + LinkCacheRAW(szs->cache_fname,src_fname,szs->cdata,szs->csize); +} + +//----------------------------------------------------------------------------- + +void MarkReadonlySZS ( szs_file_t * szs ); +enumError CompressSZS ( szs_file_t * szs, bool remove_uncompressed ); +bool NormalizeSZS ( szs_file_t *szs ); +bool NormalizeExSZS ( szs_file_t *szs, bool rm_aiparam, bool clean_lex, bool autoadd ); +bool PatchSZS ( szs_file_t * szs ); + +enumError DecompressSZS +( + szs_file_t *szs, // valid SZS source, use cdata + bool rm_compressed, // true: remove compressed data + FILE *hexdump // not NULL: write decrompression hex-dump +); + +bool TryDecompressSZS +( + szs_file_t *szs, // valid SZS source, use data if not NULL + bool rm_compressed // true: remove compressed data +); + +void ClearCompressedSZS ( szs_file_t * szs ); +void ClearUncompressedSZS ( szs_file_t * szs ); + +enumError DecodeWU8 ( szs_file_t * szs ); +enumError EncodeWU8 ( szs_file_t * szs ); + +u8 * DecodeXYZ +( + u8 *dest, // destination buffer, + // If NULL: malloc() is used. + const u8 *src, // source data + uint size // size of dest and source + +); + +u8 * EncodeXYZ +( + u8 *dest, // destination buffer, + // If NULL: malloc() is used. + const u8 *src, // source data + uint size // size of dest and source + +); + +size_t GetDecompressedSizeYAZ +( + const void * data, // source data + size_t data_size // size of 'data' +); + +enumError DecompressYAZ +( + // returns: + // ERR_OK: compression done + // ERR_WARNING: silent==true: dest buffer too small + // ERR_INVALID_DATA: invalid source data + + const void * data, // source data + size_t data_size, // size of 'data' + void * dest_buf, // destination buffer (decompressed data) + size_t dest_buf_size, // size of 'dest_buf' + size_t *write_status, // number of written bytes + ccp fname, // file name for error messages + int yaz_version, // yaz version for error messages (0|1) + bool silent, // true: don't print error messages + FILE *hexdump // not NULL: write decrompression dump +); + +bool IsFileOptional ( szs_file_t *szs, const DbFileFILE_t *file ); + +struct szs_norm_t; +int AddMissingFileSZS +( + szs_file_t * szs, // valid szs + ccp fname, // filename to add + file_format_t fform, // valid file format + struct szs_norm_t *norm, // NULL or norm struct + int log_indent // >-1: print log with indention +); + +struct scan_data_t; +struct szs_norm_t; +enumError AddMissingFiles +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to base directory + struct scan_data_t * sd, // NULL or valid scan data + struct szs_norm_t * norm, // NULL or valid norm data + int log_indent // >-1: print log with indention +); + +enumError CreateU8 +( + szs_file_t * szs, // valid szs + ccp source_dir, // NULL or path to source dir + u8 * source_data, // NULL or source data + u32 namepool_size, // total namepool_size + u32 total_size, // total file size + bool create_pt_dir // create directory '.' as base +); + +enumError CreateSZS +( + szs_file_t * szs, // valid szs + ccp dest_fname, // NULL or planned destination file name + ccp source_dir, // path to base directory + SubDir_t * sdir, // not NULL: read from here and ignore source_dir + SetupParam_t * setup_param, // setup parameters + // if NULL: read SZS_SETUP_FILE + uint depth, // creation depth + uint log_depth, // print creation log if depthrequired_file + szs_file_t * szs, // valid szs + const DbFileFILE_t * file // file to proof +); + +bool IsFileOptionalSZS +( + // check DBF_OPTIONAL && DBF_SPECIAL && szs->required_file + szs_file_t * szs, // valid szs + const DbFileFILE_t * file // NULL or file to proof +); + +enumError DiffSZS +( + szs_file_t * szs1, // first szs to compare + szs_file_t * szs2, // second szs tp compare + int recurse, // 0:off, <0:unlimited, >0:max depth + int cut_files, // <0:never, =0:auto, >0:always + bool quiet // true: be quiet +); + +bool SortSubFilesSZS +( + szs_file_t * szs, // valid szs structure + SortMode_t sort_mode // wanted sort mode +); + +typedef struct check_szs_t check_szs_t; + +int CheckSZS +( + // returns number of errors + + szs_file_t * szs, // valid szs + CheckMode_t szs_mode, // print mode for szs file + CheckMode_t sub_mode // print mode for sub file (KCL,KMP,...) +); + +int CheckBRRES +( + // returns number of errors + + szs_file_t * szs, // valid szs + CheckMode_t check_mode, // print mode + check_szs_t * parent_chk // NULL or parent +); + +//----------------------------------------------------------------------------- + +static inline ccp GetNameFF_SZS ( const szs_file_t *szs ) +{ + DASSERT(szs); + return GetNameFFv(szs->fform_file,szs->fform_arch,szs->ff_version); +} + +static inline ccp GetNameFF_SZScurrent ( const szs_file_t *szs ) +{ + DASSERT(szs); + return GetNameFF(szs->fform_file,szs->fform_current); +} + +//----------------------------------------------------------------------------- + +int FindSlotsSZS +( + // returns -1, 0 or +1 + + szs_file_t * szs, // valid szs + int slot[MKW_N_TRACKS], // status vector: + // -1: slot not possible + // 0: unknown + // +1: slot possible + bool *res_special // not NULL: return TRUE, if an other slot + // than 4.2|6.1|6.2 is needed +); + +ccp CreateSlotInfo ( szs_file_t * szs ); +ccp CreateBriefSlotInfo ( szs_file_t * szs ); + +//----------------------------------------------------------------------------- + +void ClearSpecialFilesSZS +( + szs_file_t * szs // valid szs +); + +void FindSpecialFilesSZS +( + szs_file_t * szs, // valid szs + bool force // true: force new scanning +); + +ccp CreateSpecialFileInfo + ( szs_file_t * szs, bool add_value, ccp return_if_empty ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs file iterator /////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct szs_iterator_t; +typedef int (*szs_iterator_func) +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +//----------------------------------------------------------------------------- +// [[szs_iterator_t]] + +typedef struct szs_iterator_t +{ + //--- global parameters + + szs_file_t * szs; // valid szs pointer + const endian_func_t * endian; // endian functions + bool show_root_node; // true: show root node + bool cut_files; // true: cut files into peaces + SortMode_t sort_mode; // sort files + + //--- client values + + void * param; // user defined parameter + int client_int; // initial set to null + + //--- call back functions + + szs_iterator_func func_sub; // called by sub files functions + szs_iterator_func func_sort; // called by sort functions + szs_iterator_func func_it; // called by iterator functions + + //--- recursive handling + + struct + szs_iterator_t * parent_it; // NULL or parent iterator + int recurse_level; // current recursive level + int recurse_max; // max recursive level + int no_recurse; // >0: disable recurse handling + + //--- file specific parameters + + bool is_dir; // true: directory argument + bool has_subfiles; // true: this file has enabled sub files + u16 fform; // NULL or file format + int index; // index of 'fst_item' + int depth; // directory depth + u32 off; // offset of object + u32 size; // size of object + const u8_node_t * fst_item; // pointer to FST item + + //--- filename + + char path[ARCH_FILE_MAX]; // full path + char * trail_path; // trailing part of path + ccp name; // raw name of the current object + + //--- brres iteration data + + const u8 * root_end; // pointer to end of root + + //--- brsub support + + int brsub_version; // 0 or version of parent brsub file + s16 group; // group index, type grp_entry_t + s16 entry; // entry index, type grp_entry_t + +} szs_iterator_t; + +//----------------------------------------------------------------------------- + +szs_iterator_func GetIteratorFunction +( + file_format_t fform, // file format format + bool cut_files // true: support of cutted files +); + +typedef szs_iterator_func (*get_iterator_func)(file_format_t,bool); +extern szs_iterator_func std_iter_func[FF_N]; +extern szs_iterator_func cut_iter_func[FF_N]; + +//----------------------------------------------------------------------------- + +int IterateFilesSZS +( + szs_file_t * szs, // valid szs + szs_iterator_func func, // call back function + void * param, // user defined parameter + bool show_root_node, // true: include root node in iteration + int recurse, // 0:off, <0:unlimited, >0:max depth + int cut_files, // <0:never, =0:auto(first level), >0:always + SortMode_t sort_mode // sort mode +); + +uint CollectFilesSZS +( + szs_file_t * szs, // valid szs + bool clear, // true: clear list before adding + int recurse, // 0:off, <0:unlimited, >0:max depth + int cut_files, // <0:never, =0:auto, >0:always + SortMode_t sort_mode // sort subfiles +); + +void PrintFileHeadSZS(); +int PrintFileSZS +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +int IterateFilesU8 +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +); + +void PrintVersionSZS ( char *buf, uint buf_size, struct szs_iterator_t *it ); + +//----------------------------------------------------------------------------- + +int IterateFilesData +( + const void *data, // valid data pointer + uint data_size, // size of data + szs_iterator_func func, // call back function + void * param, // user defined parameter + file_format_t fform, // not FF_UNKNOWN: use this type + ccp fname // NULL or filename +); + +//----------------------------------------------------------------------------- + +szs_subfile_t * InsertSubfileSZS +( + szs_file_t *szs, // valid data structure + uint insert_pos, // insert position index, robust + szs_iterator_t *it, // not NULL: copy source info + ccp path // not NULL: copy to member 'path' +); + +static inline szs_subfile_t * AppendSubfileSZS +( + szs_file_t *szs, // valid data structure + szs_iterator_t *it, // not NULL: copy source info + ccp path // not NULL: copy to member 'path' +) +{ + return InsertSubfileSZS(szs,~0,it,path); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRRES structures /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BRRES_MAGIC "bres" +#define BRRES_MAGIC_NUM 0x62726573 +#define BRRES_DEFAULT_ALIGN 0x04 +#define BRRES_STRING_POOL_FNAME ".string-pool.bin" + +//----------------------------------------------------------------------------- +// [[brres_header_t]] + +// the data is 'bom' dependent big or little endian + +typedef struct brres_header_t +{ + char magic[4]; // = BRRES_MAGIC + u16 bom; // byte order mark + u16 version; // usally BRESS=0 / BREFF+BREFT=9(MKW)|11(NSMB) + u32 size; // file size + u16 root_off; // offset of root section + u16 n_sections; // number of sections +} +__attribute__ ((packed)) brres_header_t; + +//----------------------------------------------------------------------------- +// [[brres_entry_t]] + +typedef struct brres_entry_t +{ + u16 id; // entry id + u16 unknown; // always 0? + u16 left_idx; // index to left element in virtual tree + u16 right_idx; // index to right element in virtual tree + u32 name_off; // offset into string table + u32 data_off; // offset to data +} +__attribute__ ((packed)) brres_entry_t; + +//----------------------------------------------------------------------------- +// [[brres_group_t]] + +typedef struct brres_group_t +{ + u32 size; // size of complete group + u32 n_entries; // number of entires + brres_entry_t entry[0]; // list of entries, N := 1 + n_entries + // entry with index #0 is a special + // root node not counting in 'n_entries' +} +__attribute__ ((packed)) brres_group_t; + +//----------------------------------------------------------------------------- +// [[brres_root_t]] + +#define BRRES_ROOT_MAGIC "root" + +typedef struct brres_root_t +{ + char magic[4]; // = BRRES_ROOT_MAGIC + u32 size; // size of complete header + //brres_group_t group[0]; // first group -> disabled because MVC +} +__attribute__ ((packed)) brres_root_t; + +#define BRRES_ROOT_GROUP(r) ((brres_group_t*)((brres_root_t*)r+1)) + +//----------------------------------------------------------------------------- +// [[brsub_header_t]] + +typedef struct brsub_header_t +{ + /* 0x00 */ char magic[4]; // magic of sub file + /* 0x04 */ u32 size; // size of sub file + /* 0x08 */ u32 version; // version of sub file + /* 0x0c */ u32 brres_offset; // negative value to begin of brres + /* 0x10 */ u32 grp_offset[0]; // offsets of other sections -> brres_group_t + // this field is limited until the start + // of the first group or until the 'n_sect' + // non zero entry. + // 0xNN u32 name_off; // offset to name of object + + // There is more data dependent of the sub file type. This data starts + // behind 'grp_offset' and ends at the first brres_group_t data. +} +__attribute__ ((packed)) brsub_header_t; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cut files: BRSUB /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[brsub_cut_t]] + +typedef struct brsub_cut_t +{ + //--- input data + + szs_iterator_t *it; // iterator + szs_file_t *szs; // extracted from 'it' + const u8 *data; // extracted from 'szs' + uint size; // size of 'data', extracted from 'szs' + uint file_size; // size of containing file + file_format_t fform; // subfile format + int version; // subfile version + ff_attrib_t fattrib; // calculated + const endian_func_t *endian; // endian functions + void *param; // user parameter + + + //--- base data + + char id[5]; // ID, created from magic + const brsub_header_t *bh; // brsub header + uint brsub_size; // fixed size of brsub data + uint n_grp; // total number of groups + + + //--- group + entry iterator + + brres_group_t *gptr; // NULL or pointer to current group + brres_entry_t *eptr; // NULL or pointer to current entry + char *path_end; // pointer to end of string of 'it->path' + + //--- group iterator, private data + + int found_grp; // index of found group + uint found_off; // offset of found group + +} brsub_cut_t; + +//----------------------------------------------------------------------------- + +typedef enum grp_entry_t +{ + GROUP_INVALID = -99, + ENTRY_INVALID = GROUP_INVALID, + + GROUP_IDX_BRSUB_HEADER = -9, + GROUP_IDX_SUB_HEADER, + GROUP_IDX_STRING_POOL, + + ENTRY_IDX_GROUP, + ENTRY_IDX_DATA, + ENTRY_IDX_ETC, + +} grp_entry_t; + +//----------------------------------------------------------------------------- + +typedef int (*brsub_cut_func_t) +( + brsub_cut_t *bcut, // pointer to data structure + int grp, // index of group, <0: grp_entry_t + int entry // index of entry, -1: no entry +); + +//----------------------------------------------------------------------------- + +int CutFilesBRSUB +( + szs_iterator_t *it, // iterator struct with all infos + brsub_cut_func_t func, // call back function + void *param, // user parameter + ccp sname_tab[], // NULL or section name table + uint n_sname // number of elements in 'sname_tab' + // if 0: 'sname_tab' is terminated by NULL +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// szs_extract_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[szs_extract_level_t]] + +typedef enum szs_extract_level_t +{ + //--- flags + + EXLEV_F_RMPT = 0x01, // point "./" removed + EXLEV_F_ICASE = 0x02, // case ignored + EXLEV_F_MATCH = 0x04, // match a file + + //--- summary + + EXLEV_EXACT = 0x00, + EXLEV_MASK = 0x07, + EXLEV_N = EXLEV_MASK + 1, + +} szs_extract_level_t; + +//----------------------------------------------------------------------------- +// [[szs_extract_t]] + +typedef struct szs_extract_t +{ + //--- status + + bool subfile_found; // true: subfile found in path + bool szs_found; // true: base file is valid SZS + + //--- extract level support + + szs_extract_level_t exlevel; // best level found until now + ccp found_path; // found sub path + u32 found_offset; // found offset + u32 found_size; // found size + uint found_count; // number of found files of 'exlevel' + + //--- helpers + + ccp subpath; // sub file path + uint subpath_len; // length of subpath + + //--- szs info + + ccp fname; // path of read file, never NULL + file_format_t fform_file; // source file format + file_format_t fform_arch; // decompressed file format + file_format_t fform_current; // current format of 'data' + const endian_func_t * endian; // endian functions + + //--- extracted data + + u8 * data; // NULL or extracted data + u32 data_size; // size of 'data' + bool data_alloced; // true: 'data' must be freed + + DataContainer_t *old_container; // old data [[container]] support, init with NULL + Container_t container; // container data + +} szs_extract_t; + +//----------------------------------------------------------------------------- + +void InitializeExtractSZS ( szs_extract_t * eszs ); +void ResetExtractSZS ( szs_extract_t * eszs ); + +// [[container+]] +DataContainer_t * GetOldContainerESZS ( szs_extract_t * eszs ); +Container_t * SetupContainerESZS ( szs_extract_t * eszs ); +ContainerData_t * LinkContainerESZS ( szs_extract_t * eszs ); +ContainerData_t * MoveContainerESZS ( szs_extract_t * eszs ); + +enumError ExtractSZS +( + szs_extract_t * eszs, // valid data structure + bool initialize, // true: initialize 'eszs' + ccp fname, // filename of source + ccp autoname, // not NULL: Use this filename + // if subfile name is "" or "/" + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +enumError PrintErrorExtractSZS +( + szs_extract_t * eszs, // valid data structure + ccp fname // filename of source +); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ExtractFilesSZS +( + szs_file_t *szs, // valid szs file + int recurse_level, // recursion level + bool is_cutting, // true: is cutting a file + SubDir_t *sdir, // not NULL: store files here + ccp basedir // not NULL: Extract only files if "/BASEDIR" or "./BASEDIR". + // BASEDIR is like "a/b/c/" without leading slash + // If set, don't create support files. +); + +ccp GetOptBasedir(); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// raw_data_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[raw_data_t]] + +typedef struct raw_data_t +{ + //--- base info + + ccp fname; // alloced filename of loaded file + FileAttrib_t fatt; // file attribute + file_format_t fform; // file format of 'data' + file_format_t fform_file; // file format of file + bool is_0; // an empty file detected, fname is "0" + + u8 *data; // raw data + bool data_alloced; // true: 'data' is alloced + uint data_size; // size of 'data' + + DataContainer_t *old_container; // old data [[container]] support, init with NULL + Container_t container; // container data + +} raw_data_t; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeRawData ( raw_data_t * raw ); +void ResetRawData ( raw_data_t * raw ); +// [[container+]] +DataContainer_t * ContainerRawData ( raw_data_t * raw ); +Container_t * SetupContainerRawData ( raw_data_t * raw ); +ContainerData_t * LinkContainerRawData ( raw_data_t * raw ); +ContainerData_t * MoveContainerRawData ( raw_data_t * raw ); + +enumError LoadRawData +( + raw_data_t * raw, // data structure + bool init_raw, // true: initialize 'raw' first + ccp fname, // valid pointer to filenname + ccp autoname, // not NULL: Use this filename + // if subfile name is "" or "/" + bool ignore_no_file, // ignore if file does not exists + // and return warning ERR_NOT_EXISTS + file_format_t allow_0 // not NULL (FF_UNKNOWN): + // allow "0" for an empty file and set FF +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BZ support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BZ_MAGIC "WBZa" +#define BZ_MAGIC_NUM 0x57425a61 + +typedef struct wbz_header_t +{ + char magic[4]; // = BZ_MAGIC + char first_8[8]; // first 8 bytes of uncompressed sub file + // (fast magic detection) + u8 bz_data[]; // compressed data +} +__attribute__ ((packed)) wbz_header_t; + +//----------------------------------------------------------------------------- + +enumError DecompressBZ ( szs_file_t * szs, bool rm_compressed ); +enumError CompressBZ ( szs_file_t * szs, bool remove_uncompressed ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// BRASD support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define BRASD_MAGIC "RASD" +#define BRASD_MAGIC_NUM 0x52415344 + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_SZS_H diff --git a/project/src/lib-transform.c b/project/src/lib-transform.c new file mode 100644 index 0000000..e463621 --- /dev/null +++ b/project/src/lib-transform.c @@ -0,0 +1,1391 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-std.h" +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u32 set_flags = M1(u32); +bool have_set_scale = false; +bool have_set_rot = false; +DEFINE_VAR(set_scale); +DEFINE_VAR(set_rot); + +uint have_set_value = 0; // x=bit-0, y=bit-1, z=bit-2 +double3 set_value_min, set_value_max; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptSetFlags ( ccp arg ) +{ + return ScanSizeOptU32( + &set_flags, // u32 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + "set-flag", // ccp opt_name + 0, // u64 min + 0xffff, // u64 max + 0, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ) != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptSetScale ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --set-scale",0); + enumError err = ScanVectorExprSI(&si,&set_scale,1.0,1); + DASSERT( set_scale.mode == VAR_VECTOR ); + have_set_scale = !err && IsNormalD3(set_scale.v); + if ( err != ERR_OK ) + err = CheckEolSI(&si); + ResetSI(&si); + + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptSetRot ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --set-rot",0); + enumError err = ScanVectorExprSI(&si,&set_rot,0.0,1); + DASSERT( set_rot.mode == VAR_VECTOR ); + have_set_rot = !err && IsNormalD3(set_rot.v); + if ( err != ERR_OK ) + err = CheckEolSI(&si); + ResetSI(&si); + + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptSet ( uint xyz, ccp arg ) +{ + DASSERT( xyz < 3 ); + if ( !arg || xyz >= 3 ) + return 0; + + char optname[10]; + snprintf(optname,sizeof(optname),"%cset",'x'+xyz); + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),optname,0); + DEFINE_VAR(min); + DEFINE_VAR(max); + + const enumError err = ScanMinMaxExprSI(&si,&min,&max); + CheckEolSI(&si); + ResetSI(&si); + if (err) + return 1; + + set_value_min.v[xyz] = min.v[xyz]; + set_value_max.v[xyz] = max.v[xyz]; + have_set_value |= 1 << xyz; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptShift ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --shift",0); + + DEFINE_VAR(temp); + const enumError err = ScanVectorExprSI(&si,&temp,0.0,1); + DASSERT( temp.mode == VAR_VECTOR ); + CheckEolSI(&si); + ResetSI(&si); + + if (!err) + SetShiftMatrixD(&opt_transform,&temp.d3); + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTranslate ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --translate",0); + + DEFINE_VAR(temp); + const enumError err = ScanVectorExprSI(&si,&temp,0.0,1); + DASSERT( temp.mode == VAR_VECTOR ); + CheckEolSI(&si); + ResetSI(&si); + + if (!err) + SetTranslateMatrixD(&opt_transform,&temp.d3); + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptScale ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --scale",0); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + DEFINE_VAR(scale); + DEFINE_VAR(origin); + enumError err = ScanVectorExprSI(&si,&scale,1.0,1); + DASSERT( scale.mode == VAR_VECTOR ); + if ( !err && NextCharSI(&si,false) == '@' ) + { + sf->ptr++; + err = ScanVectorExprSI(&si,&origin,0.0,1); + DASSERT( origin.mode == VAR_VECTOR ); + } + else + { + origin.x = origin.y = origin.z = 0.0; + origin.mode = VAR_VECTOR; + } + + CheckEolSI(&si); + ResetSI(&si); + + if (!err) + SetScaleMatrixD(&opt_transform,&scale.d3,&origin.d3); + + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptRotate ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --rot",0); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + DEFINE_VAR(rotate); + DEFINE_VAR(origin); + enumError err = ScanVectorExprSI(&si,&rotate,1.0,1); + DASSERT( rotate.mode == VAR_VECTOR ); + if ( !err && NextCharSI(&si,false) == '@' ) + { + sf->ptr++; + err = ScanVectorExprSI(&si,&origin,0.0,1); + DASSERT( origin.mode == VAR_VECTOR ); + } + else + { + origin.x = origin.y = origin.z = 0.0; + origin.mode = VAR_VECTOR; + } + + CheckEolSI(&si); + ResetSI(&si); + + if (!err) + { + SetRotateMatrixD(&opt_transform,0,rotate.x,0.0,&origin.d3); + SetRotateMatrixD(&opt_transform,1,rotate.y,0.0,&origin.d3); + SetRotateMatrixD(&opt_transform,2,rotate.z,0.0,&origin.d3); + } + + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptXRotate ( uint xyz, ccp arg ) +{ + DASSERT(xyz<3); + if ( !arg || xyz >= 3 ) + return 0; + + static ccp tab_info[] = { "Option --xrot", "Option --yrot", "Option --zrot" }; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),tab_info[xyz],0); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + double degree = GetDoubleV(&temp); + + if ( !err && NextCharSI(&si,false) == '@' ) + { + sf->ptr++; + err = ScanVectorExprSI(&si,&temp,0.0,xyz); + DASSERT( temp.mode == VAR_VECTOR ); + } + else + { + temp.x = temp.y = temp.z = 0.0; + temp.mode = VAR_VECTOR; + } + CheckEolSI(&si); + ResetSI(&si); + + if (!err) + SetRotateMatrixD(&opt_transform,xyz,degree,0.0,&temp.d3); + + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptAScale ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --ascale",0); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + if (err) + goto abort; + const double scale = GetDoubleV(&temp); + if ( fabs(scale) < NULL_EPSILON ) + { + err = ERROR0(ERR_SEMANTIC, + "Option --ascale: Scale factor too small: %s\n",arg); + goto abort; + } + + err = CheckWarnSI(&si,'@',err); + if (err) + goto abort; + ccp arg_vector =sf->ptr; + + DEFINE_VAR(dir); + err = ScanVectorExprSI(&si,&dir,0.0,1); + if (err) + goto abort; + DASSERT( dir.mode == VAR_VECTOR ); + + const double dir_len = Length3(dir); + if ( dir_len < NULL_EPSILON ) + { + err = ERROR0(ERR_SEMANTIC, + "Option --ascale: Vector is too small: %s\n",arg_vector); + goto abort; + } + err = CheckEolSI(&si); + if (err) + goto abort; + + //--- valid axis scale entered + + ascale_valid = fabs(scale) >= NULL_EPSILON && fabs(scale-10.0) >= NULL_EPSILON; + ascale = scale; + Div3f(ascale_dir,dir,dir_len); + NextTransformation(false); + + abort: + ResetSI(&si); + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptARotate ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Option --arot",0); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + if (err) + goto abort; + const double degree = fmod(GetDoubleV(&temp)+180.0,360.0)-180.0; + + err = CheckWarnSI(&si,'@',err); + if (err) + goto abort; + ccp arg_vector =sf->ptr; + + DEFINE_VAR(p1); + DEFINE_VAR(p2); + err = ScanVectorExprSI(&si,&p2,0.0,1); + if (err) + goto abort; + DASSERT( p2.mode == VAR_VECTOR ); + + if ( NextCharSI(&si,false) == '@' ) + { + p1 = p2; + sf->ptr++; + err = ScanVectorExprSI(&si,&p2,0.0,1); + if (err) + goto abort; + DASSERT( p2.mode == VAR_VECTOR ); + } + else + p1.x = p1.y = p1.z = 0.0; + + double3 delta; + Sub3(delta,p1,p2); + const double len = Length3(delta); + if ( len < NULL_EPSILON ) + { + err = ERROR0(ERR_SEMANTIC, + "Option --arot: Points to near: %s\n",arg_vector); + goto abort; + } + err = CheckEolSI(&si); + if (err) + goto abort; + + //--- valid axis rotation entered + + arot_valid = true; //fabs(degree) >= MIN_DEGREE; + arot_deg = degree; + arot1 = p1.d3; + arot2 = p2.d3; + NextTransformation(false); + + abort: + ResetSI(&si); + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptYPos ( ccp arg ) +{ + if (!arg) + return 0; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),"Options --ypos",0); + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + yposition_valid = temp.mode != VAR_UNSET; + yposition = GetYDoubleV(&temp); + CheckEolSI(&si); + ResetSI(&si); + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptXSS ( uint xyz, ccp arg ) +{ + DASSERT( xyz < 3 ); + if ( !arg || xyz >= 3 ) + return 0; + + static ccp tab_info[] = { "Option --xss", "Option --yss", "Option --zss" }; + ccp opt_info = tab_info[xyz]; + + ScanInfo_t si; + InitializeSI(&si,arg,strlen(arg),opt_info,0); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + sf->disable_comma++; + + DEFINE_VAR(temp); + enumError err = ScanExprSI(&si,&temp); + err = CheckWarnSI(&si,',',err); + if (err) + goto abort; + double old1 = GetDoubleV(&temp); + + err = ScanExprSI(&si,&temp); + err = CheckWarnSI(&si,',',err); + if (err) + goto abort; + double new1 = GetDoubleV(&temp); + + err = ScanExprSI(&si,&temp); + err = CheckWarnSI(&si,',',err); + if (err) + goto abort; + double old2 = GetDoubleV(&temp); + + err = ScanExprSI(&si,&temp); + if (err) + goto abort; + double new2 = GetDoubleV(&temp); + + CheckEolSI(&si); + + if ( fabs(old1-old2) < 1e-6 ) + { + err = ERROR0(ERR_SEMANTIC, + "%s: 'old' values are to close together, delta=%6.3e", + opt_info, fabs(old1-old2) ); + goto abort; + } + + SetScaleShiftMatrixD(&opt_transform,xyz,old1,new1,old2,new2); + + abort: + ResetSI(&si); + return err != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transformation Script /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp tfs_filename = 0; +static u8 *tfs_data = 0; +static uint tfs_size = 0; +static ScanInfo_t tfs_si = {0}; +static ScanMacro_t *tfs_macro = 0; +static uint tfs_index = 0; + +#if HAVE_PRINT + static uint tfs_count_f = 0; + static uint tfs_count_d = 0; +#endif + +bool tform_script_enabled = false; + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptTformScript ( ccp arg ) +{ + tfs_filename = arg; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError ScanTformScript() +{ + tfs_macro = 0; + tform_script_enabled = false; + + if ( !tfs_filename || !*tfs_filename ) + return ERR_NOTHING_TO_DO; + + enumError err = OpenReadFILE(tfs_filename,0,false,&tfs_data,&tfs_size,0,0); + if ( err || !tfs_data || !tfs_size ) + { + abort: + FREE(tfs_data); + tfs_data = 0; + tfs_size = 0; + tfs_filename = 0; + return err; + } + + InitializeSI(&tfs_si,(ccp)tfs_data,tfs_size,tfs_filename,REVISION_NUM); + while (NextLineSI(&tfs_si,false,true)) + ; //--- scan source + + const Var_t *vp = FindVarMap(&tfs_si.macro,"TRANSFORM",0); + if ( !vp || !vp->macro ) + { + ResetSI(&tfs_si); + err = ERR_NOTHING_TO_DO; + goto abort; + } + + tfs_macro = vp->macro; + PRINT("MACRO TRANSFORM found\n"); + tform_script_enabled = true; + transform_active = true; + have_patch_count++; + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool tfs_call_macro +( + const ScanMacro_t *macro, // macro to call + uint dimension // '2' for a 2D and '3' for a 3D vector +) +{ + DASSERT(tform_script_enabled); + DASSERT(macro); + + + //--- define local variables, '$P' is already defined + + Var_t *var = InsertVarMap(&tfs_si.lvar,"$D",false,0,0); + DASSERT(var); + var->mode = VAR_INT; + var->i = dimension; + + var = InsertVarMap(&tfs_si.lvar,"$I",false,0,0); + DASSERT(var); + var->mode = VAR_INT; + var->i = tfs_index++; + + + //--- setup macro + + ScanFile_t *sf = tfs_si.cur_file; + DASSERT(sf); + + const uint saved_n_files = tfs_si.n_files; + tfs_si.n_files = 0; + tfs_si.cur_file = &empty_scan_file; + tfs_si.last_result.mode = VAR_UNSET; + + ScanFile_t *newsf = AddSF( &tfs_si, macro->data, macro->data_size, + macro->src_name, REVISION_NUM, 0 ); + DASSERT(newsf); + + newsf->active_macro = macro; + newsf->line = macro->line0; + + //--- scan source + + while (NextLineSI(&tfs_si,false,true)) + ; + + //--- close source + + while (DropSF(&tfs_si)) + ; + tfs_si.cur_file = sf; + tfs_si.n_files = saved_n_files; + + //--- terminate + + return tfs_si.last_result.mode == VAR_VECTOR; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ScanTformBegin() +{ + if (!tform_script_enabled) + return false; + DASSERT(tfs_macro); + + ClearVarMap(&tfs_si.lvar); + //ClearVarMap(&tfs_si.gvar); + + const Var_t *vp = FindVarMap(&tfs_si.macro,"BEGIN",0); + if ( vp && vp->macro ) + { + tfs_index = -1; + tfs_call_macro(vp->macro,0); + } + + tfs_index = 0; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool TformScriptEnd() +{ + if (!tform_script_enabled) + return false; + + PRINT("### TFROM-STAT: %llu + %llu\n", + N_MatrixD_forward, N_MatrixD_inverse ); + PRINT("### SCRIPT-STAT: %u | %u + %u = %u\n", + tfs_index, + tfs_count_f, tfs_count_d, tfs_count_f + tfs_count_d ); + + const Var_t *vp = FindVarMap(&tfs_si.macro,"END",0); + if ( vp && vp->macro ) + { + tfs_call_macro(vp->macro,0); + tfs_index--; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError TformScriptCallF +( + uint dimension, // '2' for a 2D and '3' for a 3D vector + float3 *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(tform_script_enabled); + DASSERT(tfs_macro); + + if ( n <= 0 || !tfs_macro ) + return ERR_NOTHING_TO_DO; + DASSERT(v); + + #if HAVE_PRINT + tfs_count_f += n; + #endif + + for(;;) + { + ClearVarMap(&tfs_si.lvar); + Var_t *var = InsertVarMap(&tfs_si.lvar,"$P",false,0,0); + DASSERT(var); + var->mode = VAR_VECTOR; + var->x = v->x; + var->y = v->y; + var->z = v->z; + + if (tfs_call_macro(tfs_macro,dimension)) + { + v->x = tfs_si.last_result.x; + v->y = tfs_si.last_result.y; + v->z = tfs_si.last_result.z; + } + + if (!--n) + return ERR_OK; + v = (typeof(v))( (u8*)v + off ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError TformScriptCallD +( + uint dimension, // '2' for a 2D and '3' for a 3D vector + double3 *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(tform_script_enabled); + DASSERT(tfs_macro); + + if ( n <= 0 || !tfs_macro ) + return ERR_NOTHING_TO_DO; + DASSERT(v); + + #if HAVE_PRINT + tfs_count_d += n; + #endif + + for(;;) + { + ClearVarMap(&tfs_si.lvar); + Var_t *var = InsertVarMap(&tfs_si.lvar,"$P",false,0,0); + DASSERT(var); + var->mode = VAR_VECTOR; + var->x = v->x; + var->y = v->y; + var->z = v->z; + + if (tfs_call_macro(tfs_macro,dimension)) + { + v->x = tfs_si.last_result.x; + v->y = tfs_si.last_result.y; + v->z = tfs_si.last_result.z; + } + + if (!--n) + return ERR_OK; + v = (typeof(v))( (u8*)v + off ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transformation Setup /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint force_transform = 0; // bit 0: force tform even if not needed + // bit 1: ignore it + +MatrixD_t opt_transform = {0}; // current transform values +MatrixD_t transform_list[N_TRANSFORM] = {{0}}; // list with stored tforms + +uint n_transform_list = 0; // number of elements in list +bool transform_active = false; // true: transformation active +int disable_transformation = 0; // >0: transformation is temporary disabled + +double yposition = 0.0; // defined y position +bool yposition_valid = false; // 'yposition' is valid + +bool ascale_valid = false; // true: --ascale defined +double ascale = 0.0; // scaling factor +double3 ascale_dir; // scaling direction (vector) + +bool arot_valid = false; // true: --arot defined +double arot_deg = 0.0; // angle of arot in degree +double3 arot1, arot2; // points to define rotation axis + +uint mdl_switched_to_vertex = 0; // 0: default + // 1: switch to 'VERTEX' needed + // 2: MDL_MODE switched to 'VERTEX' + // 3: '2' and message printed + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetTransformation() // reset all transformation steps +{ + if (transform_active) + { + transform_active = false; + have_patch_count--; + } + + n_transform_list = 0; + InitializeMatrixD(&opt_transform); +} + +/////////////////////////////////////////////////////////////////////////////// + +int NextTransformation ( bool for_close ) // enter next transformation step +{ + CalcMatrixD(&opt_transform,true); + if ( opt_transform.transform_enabled || force_transform == 1 && for_close ) + { + force_transform |= 2; + if ( n_transform_list >= N_TRANSFORM-!for_close ) + { + static bool err_done = false; + if (!err_done) + { + err_done = true; + ERROR0(ERR_SEMANTIC, + "Maximum number of --next (%u) already reached.\n", + N_TRANSFORM-1 ); + } + return 1; + } + + memcpy( transform_list + n_transform_list, + &opt_transform, + sizeof(*transform_list) ); + + n_transform_list++; + PRINT("--next: level %u/%u\n",n_transform_list,N_TRANSFORM); + } + + InitializeMatrixD(&opt_transform); + + + //--- special: axis scaling + + if (ascale_valid) + { + ascale_valid = false; + SetAScaleMatrixD(&opt_transform,ascale,&ascale_dir); + NextTransformation(for_close); + } + + + //--- special: axis rotation + + if (arot_valid) + { + arot_valid = false; + SetARotateMatrixD(&opt_transform,arot_deg,0.0,&arot1,&arot2); + NextTransformation(for_close); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseTransformation() // close transformation input +{ + NextTransformation(true); + transform_active = n_transform_list > 0; + + if (!transform_active) + InitializeMatrixD(&opt_transform); + else + { + have_patch_count++; + memcpy( &opt_transform, transform_list, sizeof(opt_transform) ); + + uint i; + for ( i = 1; i < n_transform_list; i++ ) + MultiplyMatrixD(&opt_transform,0,transform_list+i,0); + } + CalcMatrixD(&opt_transform,true); + mdl_switched_to_vertex = !opt_transform.norm_valid; + + ScanTformScript(); + + if (transform_active) + PATCH_ACTION_LOG("Setup","-","%u transformation step%s defined.\n", + n_transform_list, n_transform_list == 1 ? "" : "s" ); + +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpTransformationOpt() +{ + if (have_set_scale) + printf(" set-scale: %11.3f %11.3f %11.3f\n", + set_scale.x, set_scale.y, set_scale.z ); + + if (have_set_rot) + printf(" set-rot: %11.3f %11.3f %11.3f\n", + set_rot.x, set_rot.y, set_rot.z ); + + if (have_set_value) + printf( " set-xyz-min: %11.3f %11.3f %11.3f\n" + " set-xyz-max: %11.3f %11.3f %11.3f\n", + set_value_min.x, set_value_min.y, set_value_min.z, + set_value_max.x, set_value_max.y, set_value_max.z ); + + if ( ascale != 0.0 ) + { + printf( " last axis-scale: %11.3f @ %11.3f %11.3f %11.3f\n", + ascale, + ascale_dir.x, ascale_dir.y, ascale_dir.z ); + } + + if ( arot_deg != 0.0 ) + { + printf( " last axis-rot: %11.3f @ %11.3f %11.3f %11.3f\n" + "%33s %11.3f %11.3f %11.3f\n", + arot_deg, arot1.x, arot1.y, arot1.z, + "->", arot2.x, arot2.y, arot2.z ); + } + + if (!transform_active) + return; + + if ( n_transform_list > 1 ) + { + uint i; + for ( i = 0; i < n_transform_list; i++ ) + { + printf("\nTransformation step %u/%u:\n",i+1,n_transform_list); + if ( verbose > 0 ) + PrintMatrixD(stdout,2,0,transform_list+i,~0,0x1f); + else + PrintMatrixD(stdout,2,0,transform_list,1,3); + } + printf("\nFinal Transformation:\n"); + } + else + printf("\nTransformation:\n"); + + if ( verbose > 0 ) + PrintMatrixD(stdout,2,0,&opt_transform,~0,0x1f); + else + PrintMatrixD(stdout,2,0,&opt_transform,1,3); +} + +/////////////////////////////////////////////////////////////////////////////// + +double3 TransformD3( uint mode, double3 *val ) // transformate a single vector +{ + DASSERT(val); + + uint i; + double3 res; + switch (mode) + { + case 1: + res = *val; + for ( i = 0; i < n_transform_list; i++ ) + res = BaseTransformD3MatrixD(transform_list+i,&res); + break; + + case 2: + res = *val; + for ( i = 0; i < n_transform_list; i++ ) + res = NormTransformD3MatrixD(transform_list+i,&res); + break; + + case 3: + res = *val; + for ( i = 0; i < n_transform_list; i++ ) + res = TransformD3MatrixD(transform_list+i,&res); + break; + + default: + res = TransformD3MatrixD(&opt_transform,val); + break; + } + if (tform_script_enabled) + TformScriptCallD(3,&res,1,0); + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Transformations /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void TransformPosFloat2D +( + float *v, // pointer to list of 2D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(v); + DASSERT(n>0); + DASSERT( n <= 1 || off >= 2 * sizeof(*v) ); + + CalcNormMatrixD(&opt_transform); // calc 'norm_pos2d' + const double ypos = yposition_valid ? yposition : opt_transform.norm_pos2d.y; + + off -= 2 * sizeof(*v); + while ( n-- > 0 ) + { + float3 f; + f.x = v[0]; + f.y = ypos; + f.z = v[1]; + f = TransformF3MatrixD(&opt_transform,&f); + if (tform_script_enabled) + TformScriptCallF(2,&f,1,0); + *v++ = f.x; + *v++ = f.z; + v = (float*)( (u8*)v + off ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void TransformPosDouble2D +( + double *v, // pointer to list of 2D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(v); + DASSERT(n>0); + DASSERT( n <= 1 || off >= 2 * sizeof(*v) ); + + CalcNormMatrixD(&opt_transform); // calc 'norm_pos2d' + const double ypos = yposition_valid ? yposition : opt_transform.norm_pos2d.y; + + off -= 2 * sizeof(*v); + while ( n-- > 0 ) + { + double3 d; + d.x = v[0]; + d.y = ypos; + d.z = v[1]; + d = TransformD3MatrixD(&opt_transform,&d); + if (tform_script_enabled) + TformScriptCallD(2,&d,1,0); + *v++ = d.x; + *v++ = d.z; + v = (double*)( (u8*)v + off ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool IsNormValid() +{ + CalcNormMatrixD(&opt_transform); + if (opt_transform.norm_valid) + return true; + + static bool done = false; + if ( !done && opt_transform.tmatrix_valid ) + { + done = true; + ERROR0(ERR_WARNING, + "Because of the complex transformation matrix, it is impossible" + " to modify the scale and rotate vectors of different KMP sections;" + " the vectors keep unchanged. A manual correction is needed." + " The position vectors can always be transformed." ); + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void TransformScaleFloat1D +( + float *v, // pointer to list of 3D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(v); + DASSERT(n>0); + DASSERT( n <= 1 || off >= sizeof(*v) ); + + if ( IsNormValid() && opt_transform.scale_enabled ) + { + // only for KMP ENPT+ITPT scales: + const float scale_1d = ( fabs(opt_transform.norm_scale.x) + + fabs(opt_transform.norm_scale.z) ) / 2; + + while ( n-- > 0 ) + { + *v *= scale_1d; + v = (float*)( (u8*)v + off ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void TransformScaleFloat3D +( + float *v, // pointer to list of 3D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(v); + DASSERT(n>0); + DASSERT( n <= 1 || off >= 3 * sizeof(*v) ); + + if ( IsNormValid() && opt_transform.scale_enabled ) + { + const double3 *scale = &opt_transform.norm_scale; + + while ( n-- > 0 ) + { + v[0] *= scale->x; + v[1] *= scale->y; + v[2] *= scale->z; + v = (float*)( (u8*)v + off ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void TransformScaleDouble3D +( + double *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(v); + DASSERT(n>0); + DASSERT( n <= 1 || off >= 3 * sizeof(*v) ); + + if ( IsNormValid() && opt_transform.scale_enabled ) + { + const double3 *scale = &opt_transform.norm_scale; + while ( n-- > 0 ) + { + v[0] *= scale->x; + v[1] *= scale->y; + v[2] *= scale->z; + v = (double*)( (u8*)v + off ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void TransformRotFloat3D +( + float *v, // pointer to list of 3D vectors + int n, // number of 2D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(v); + DASSERT(n>0); + DASSERT( n <= 1 || off >= 3 * sizeof(*v) ); + + if ( IsNormValid() && opt_transform.rotate_enabled ) + { + const double3 *rotate = &opt_transform.norm_rotate_deg; + + while ( n-- > 0 ) + { + v[0] = fmod( v[0] + rotate->x, 360.0 ); + v[1] = fmod( v[1] + rotate->y, 360.0 ); + v[2] = fmod( v[2] + rotate->z, 360.0 ); + v = (float*)( (u8*)v + off ); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void TransformRotDouble3D +( + double *v, // pointer to list of 3D vectors + int n, // number of 3D vectors + uint off // if n>1: offset from one to next vector +) +{ + DASSERT(v); + DASSERT(n>0); + DASSERT( n <= 1 || off >= 3 * sizeof(*v) ); + + if ( IsNormValid() && opt_transform.rotate_enabled ) + { + const double3 *rotate = &opt_transform.norm_rotate_deg; + + while ( n-- > 0 ) + { + v[0] = fmod( v[0] + rotate->x, 360.0 ); + v[1] = fmod( v[1] + rotate->y, 360.0 ); + v[2] = fmod( v[2] + rotate->z, 360.0 ); + v = (double*)( (u8*)v + off ); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command matrix /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_matrix() +{ + enumError stat = ERR_OK; + const uint print_mode = verbose < -1 ? 0 : verbose < 0 ? 1 : 2; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + ScanInfo_t si; + InitializeSI(&si,param->arg,strlen(param->arg),"TEST",0); + DEFINE_VAR(val); + const enumError err = ScanVectorExprSI(&si,&val,0.0,1); + if (!err) + TestMatrix(&val.d3,print_mode,true); + } + + if ( verbose >= 0 ) + PrintTestMatrix(true); + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool TestMatrix +( + // returns false on error + + double3 *val, // NULL or value to test + uint print_mode, // 0: silent + // 1: print vectors on error + // 2: print vectors always + bool print_matrix // true && if vector shall print: + // print matrix before, but only the first time +) +{ + if (!val) + return true; + + enum { max_mode = 4 }; + + uint mode; + double3 inv, tr[max_mode]; + for ( mode = 0; mode < max_mode; mode++ ) + tr[mode] = TransformD3(mode,val); + inv = InvTransformD3MatrixD(&opt_transform,tr); + + const double null_epsilon = 1e-7; + const double diff_epsilon = 1e-6; + bool inv_ok = IsEqualD3( &inv, val, null_epsilon, diff_epsilon ); + + bool all_ok = inv_ok, tr_ok[max_mode]; + for ( mode = 1; mode < max_mode; mode++ ) + { + tr_ok[mode] = IsEqualD3(tr,tr+mode,null_epsilon,diff_epsilon); + if (!tr_ok[mode]) + all_ok = false; + } + + if ( print_mode > all_ok ) + { + if (print_matrix) + PrintTestMatrix(true); + + printf(" Base vector: %11.3f %11.3f %11.3f\n", + val->x, val->y, val->z ); + if ( verbose > 0 || !inv_ok ) + printf(" T.form+Inverse: %11.3f %11.3f %11.3f [%d]\n", + inv.x, inv.y, inv.z, inv_ok ); + printf(" Transformation: %11.3f %11.3f %11.3f\n", + tr->x, tr->y, tr->z ); + for ( mode = 1; mode < max_mode; mode++ ) + if ( verbose > 0 || !tr_ok[mode] ) + printf(" Transform[%u]: %11.3f %11.3f %11.3f [%d]\n", + mode, tr[mode].x, tr[mode].y, tr[mode].z, tr_ok[mode] ); + printf("%.57s\n",Minus300); + } + return all_ok; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool PrintTestMatrix ( bool print_sep ) +{ + static uint seq_num = ~0; + if ( seq_num == opt_transform.sequence_number ) + return false; + seq_num = opt_transform.sequence_number; + putchar('\n'); + + static char xyz[8][4] = { "---", "x--", "-y-", "xy-", "--z", "x-z", "-yz", "xyz" }; + static char use[4] = "-us?"; + + uint pmode_step, pmode_final; + if ( brief_count > 1 ) + { + pmode_step = 0x08; + pmode_final = 0x08; + } + else if ( brief_count > 0 ) + { + pmode_step = 0x08; + pmode_final = 0x16; + } + else if ( long_count > 1 ) + { + pmode_step = 0x1f; + pmode_final = 0x1e; + } + else if ( long_count > 0 ) + { + pmode_step = 0x17; + pmode_final = 0x1e; + } + else + { + pmode_step = 0x17; + pmode_final = n_transform_list > 1 ? 0x16 : 0x1f; + } + + ccp sep; + static char sep_eq[] = "========================================" + "========================================"; + + if ( n_transform_list <= 1 ) + { + pmode_final |= pmode_step; + sep = Minus300; + } + else + { + sep = sep_eq; + uint i; + for ( i = 0; i < n_transform_list; i++ ) + { + MatrixD_t *t = transform_list+i; + printf("--------------- transformation matrix %u/%u [%s,%s,%s,%c] ---------------\n", + i+1, n_transform_list, + xyz[t->scale_enabled&7], + xyz[t->rotate_enabled&7], + xyz[t->translate_enabled&7], + use[t->use_matrix&3] ); + PrintMatrixD(stdout,0,0,t,0,pmode_step); + } + } + + printf("%.14s Final transformation matrix [%s,%s,%s,%c] %.14s\n", + sep, + xyz[opt_transform.scale_enabled&7], + xyz[opt_transform.rotate_enabled&7], + xyz[opt_transform.translate_enabled&7], + use[opt_transform.use_matrix&3], + sep ); + PrintMatrixD(stdout,0,0,&opt_transform,0,pmode_final); + + #ifdef TEST + fputs("Vectors calculated by Matrix:\n",stdout); + MatrixD_t temp = opt_transform; + CalcVectorsMatrixD(&temp,true); + PrintMatrixD(stdout,0,0,&temp,0,2); + #endif + + if (print_sep) + printf("%.73s\n",sep); + + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/lib-xbmg.c b/project/src/lib-xbmg.c new file mode 100644 index 0000000..3f5417e --- /dev/null +++ b/project/src/lib-xbmg.c @@ -0,0 +1,1511 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "lib-xbmg.h" +#include "lib-szs.h" +#include "dclib-utf8.h" +#include "lib-mkw.h" +#include "db-mkw.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MKW hooks /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int GetTrackIndexXBMG ( uint idx, int result_on_invalid ) +{ + return idx < BMG_N_TRACK + ? track_pos[idx] + : idx < BMG_N_CT_TRACK + ? idx + : result_on_invalid; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int GetArenaIndexXBMG ( uint idx, int result_on_invalid ) +{ + return idx < BMG_N_ARENA + ? arena_pos[idx] + : result_on_invalid; +} + + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetContainerNameXBMG ( const bmg_t *bmg ) +{ + DASSERT(bmg); + + if ( bmg->szs && bmg->szs->fname && *bmg->szs->fname ) + { + ccp name = strrchr(bmg->szs->fname,'/'); + return name ? name + 1 : bmg->szs->fname; + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupBMG ( ccp opt_filter_bmg ) +{ + DASSERT( MID_CT_END <= MAX_FILTER_MSG ); + DASSERT( MID_CT_CUP_END <= MAX_FILTER_MSG ); + DASSERT( MID_CT_TRACK_END <= MAX_FILTER_MSG ); + DASSERT( MID_CT_CUP_REF_END <= MAX_FILTER_MSG ); + + DASSERT( MID_LE_END <= MAX_FILTER_MSG ); + DASSERT( MID_LE_CUP_END <= MAX_FILTER_MSG ); + DASSERT( MID_LE_TRACK_END <= MAX_FILTER_MSG ); + DASSERT( MID_LE_CUP_REF_END <= MAX_FILTER_MSG ); + + //--- general setting + + opt_bmg_force_count = force_count; + opt_bmg_support_mkw = true; + opt_bmg_support_ctcode = ctcode_enabled; + opt_bmg_support_lecode = lecode_enabled; + opt_bmg_rcup_fill_limit = 0x80; + opt_bmg_track_fill_limit = 0x200; + opt_bmg_export = export_count > 0 ; + #ifdef TEST + opt_bmg_allow_print = true; + #else + opt_bmg_allow_print = verbose > 1; + #endif + opt_bmg_use_slots = true; + opt_bmg_use_raw_sections = true; + + if (opt_align) + opt_bmg_align = ALIGN32(opt_align,4); + + GetTrackIndexBMG = GetTrackIndexXBMG; + GetArenaIndexBMG = GetArenaIndexXBMG; + GetContainerNameBMG = GetContainerNameXBMG; + + + //--- compatibility settings + + if ( compatible < COMPAT_2_08 ) + { + opt_bmg_use_slots = false; + opt_bmg_use_raw_sections = false; + } + + if ( compatible < COMPAT_1_44 ) + { + opt_bmg_old_escapes = true; + } + + if ( compatible < COMPAT_1_39 ) + { + opt_bmg_inline_attrib = false; + opt_bmg_color_name = BMG_CNL_BASICS; + } + + if ( compatible < COMPAT_1_23 ) + { + opt_bmg_colors = 0; + opt_bmg_color_name = BMG_CNL_NONE; + } + + if (opt_filter_bmg) + ScanOptFilterBMG(opt_filter_bmg); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanTextBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[obsolete]] ? + +static char * scan_attrib ( u8 *dest, uint size, ccp src ) +{ + DASSERT(dest); + DASSERT( size <= BMG_ATTRIB_SIZE ); + memset(dest,0,BMG_ATTRIB_SIZE); + + while ( *src == ' ' || *src == '\t' ) + src++; + const bool have_brackets = *src == '['; + if ( have_brackets ) + src++; + + int idx = 0, add_idx = 0; + for(;;) + { + while ( *src == ' ' || *src == '\t' ) + src++; + + if ( *src == ',' ) + { + src++; + idx++; + add_idx = 0; + } + else if ( *src == '/' ) + { + src++; + idx = ALIGN32(idx+1,4); + add_idx = 0; + } + else if ( *src == '%' ) + { + src++; + char *end; + u32 num = str2ul(src,&end,16); + if ( src == end ) + break; + + idx = ALIGN32(idx+add_idx,4); + noPRINT("U32[0x%02x/%02x]: %8x\n",idx,size,num); + + char buf[4]; + write_be32(buf,num); + const int max_copy = (int)size - (int)idx; + if ( max_copy > 0 ) + memcpy(dest+idx,buf, max_copy < 4 ? max_copy : 4 ); + idx += 3; + add_idx = 1; + src = end; + } + else + { + char *end; + u8 num = str2ul(src,&end,16); + if ( src == end ) + break; + + idx += add_idx; + noPRINT("U8[0x%02x/%02x]: %8x\n",idx,size,num); + if ( idx < size ) + dest[idx] = num; + add_idx = 1; + src = end; + } + } + //HexDump16(stderr,0,0,dest,size); + + if ( have_brackets && *src == ']' ) + src++; + return (char*)src; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LoadXBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int read_bmg +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if (term) + return 0; + + const u8 * data = it->szs->data + it->off; +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,it->size,it->size); + if ( fform != FF_BMG && fform != FF_BMG_TXT ) + return 0; + + bmg_t *bmg = it->param; + DASSERT(bmg); + bmg->src_count++; + PRINT("BMG #%u found: %s\n",bmg->src_count,it->name); + + bmg_t temp; + enumError err = ScanBMG(&temp,true,it->name,data,it->size); + if (!err) + PatchBMG(bmg,&temp,BMG_PM_OVERWRITE,0,true); + else if ( bmg->max_err < err ) + bmg->max_err = err; + ResetBMG(&temp); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadXBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp fname, // filename of source + bool allow_archive, // true: concat BMGs of archives to 1 source + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +) +{ + DASSERT(bmg); + DASSERT(fname); + PRINT("LoadXBMG(%p,init=%d,,aa=%d,ign=%d) fname=%s\n", + bmg, initialize, allow_archive, ignore_no_file, fname ); + + if (initialize) + InitializeBMG(bmg); + else + ResetBMG(bmg); + + if (!strcmp(fname,"0")) + { + bmg->is_text_src = false; + bmg->fname = STRDUP(fname); + return ERR_OK; + } + + szs_extract_t eszs; + enumError err = ExtractSZS(&eszs,true,fname,0,ignore_no_file); + if (err) + return err; + + if (eszs.data) + { + bmg->data_alloced = eszs.data_alloced; + bmg->data_size = eszs.data_size; + bmg->data = eszs.data; + eszs.data = 0; + bmg->fname = eszs.fname; + eszs.fname = 0; + ResetExtractSZS(&eszs); + } + else + { + szs_file_t szs; // use 'szs_file_t' for automatic decompression + InitializeSZS(&szs); + err = LoadSZS(&szs,fname,true,ignore_no_file,true); + if ( allow_archive && IsArchiveFF(szs.fform_arch) ) + { + bmg->src_is_arch = true; + IterateFilesSZS(&szs,read_bmg,bmg,false,0,-1,SORT_NONE); + bmg->fname = szs.fname; + szs.fname = 0; + ResetSZS(&szs); + if ( !bmg->max_err && !bmg->src_count ) + bmg->max_err = ERR_NO_SOURCE_FOUND; + noPRINT("BMG/FNAME=%s\n",bmg->fname); + return bmg->max_err; + } + + bmg->data = szs.data; + bmg->data_size = szs.size; + bmg->data_alloced = szs.data_alloced; + bmg->fatt = szs.fatt; + bmg->fname = szs.fname; + szs.data = 0; + szs.data_alloced= 0; + szs.fname = 0; + ResetSZS(&szs); + + if (err) + return err; + } + + return ScanBMG(bmg,false,0,0,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanRawDataBMG +( + bmg_t * bmg, // BMG data structure + bool init_bmg, // true: initialize 'bmg' first + struct raw_data_t * raw // valid raw data +) +{ + DASSERT(bmg); + DASSERT(raw); + + PRINT("BMG/FF: %u = %s\n",raw->fform,GetNameFF(raw->fform,0)); + + if (init_bmg) + InitializeBMG(bmg); + else + ResetBMG(bmg); + if (!raw->data) + return ERR_OK; + + if (IsArchiveFF(raw->fform)) + { + bmg->src_is_arch = true; + szs_file_t szs; +// [[fname+]] + AssignSZS(&szs,true,raw->data,raw->data_size,false,raw->fform,bmg->fname); + szs.fname = raw->fname; + raw->fname = 0; + IterateFilesSZS(&szs,read_bmg,bmg,false,0,-1,SORT_NONE); + bmg->fname = szs.fname; + szs.fname = 0; + noPRINT("BMG/FNAME=%s\n",bmg->fname); + ResetSZS(&szs); + if ( !bmg->max_err && !bmg->src_count ) + bmg->max_err = ERR_NO_SOURCE_FOUND; + return bmg->max_err; + } + + return ScanBMG(bmg,init_bmg,raw->fname,raw->data,raw->data_size); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveRawXBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveRawXBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(bmg); + DASSERT(fname); + + return SaveRawBMG( bmg, fname, + GetFileModeByOpt(testmode,fname,bmg->fname), set_time ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SaveTextXBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveTextXBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + bool set_time // true: set time stamps +) +{ + DASSERT(bmg); + DASSERT(fname); + + return SaveTextBMG( bmg, fname, + GetFileModeByOpt(testmode,fname,bmg->fname), + set_time, long_count > 0, + brief_count > 0 ? brief_count : !print_header ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Patching /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[patch_bmg_cond_t]] + +typedef struct patch_bmg_cond_t +{ + bool not; // true: enable patching, if not any mid is found + u32 mid[3]; // up to 3 MIDs +} +patch_bmg_cond_t; + +//----------------------------------------------------------------------------- + +static bmg_t *patch_bmg = 0; // list with loaded BMG files for --patch-bmg +static int n_patch_bmg = 0; // number of elements of 'patch_bmg' +int have_bmg_patch_count = 0; // >0: have bmg patch + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptPatchMessage ( ccp arg ) +{ + ccp eq = strchr(arg,'='); + if (!eq) + eq = strchr(arg,','); // only for compatibility, 2011-07 => leave it + + char keyword[100]; + StringCopyS(keyword,sizeof(keyword),arg); + if ( eq && eq-arg < sizeof(keyword) ) + keyword[eq-arg] = 0; + + + //--- analyse condition + + patch_bmg_cond_t cond; + cond.not = false; + + char *sep = strchr(keyword,'?'); + if (!sep) + { + cond.not = true; + sep = strchr(keyword,'!'); + } + + if (sep) + *sep++ = 0; + + + //--- analyse keyword + + const KeywordTab_t *cmd = ScanKeyword(0,keyword,PatchKeysBMG); + if (!cmd) + { + ERROR0(ERR_SYNTAX,"Option --patch-bmg: Invalid patch mode: '%s'\n",keyword); + return 1; + } + + if ( cmd->opt && !eq ) + { + ERROR0(ERR_SYNTAX, + "Option --patch-bmg: Missing '=%s' for keyword '%s': %s\n", + cmd->opt == 1 ? "filename" : "parameter", cmd->name1, arg ); + return 1; + } + else if ( !cmd->opt && eq ) + { + ERROR0(ERR_SYNTAX, + "Option --patch-bmg: Parameter not allowed for '%s': %s\n", + cmd->name1, arg ); + return 1; + } + + + //--- complete condition scanning + + bool cond_valid = false; + + if ( sep && *sep ) + { + char *end; + int stat = ScanMidBMG(0,cond.mid,cond.mid+1,cond.mid+2,sep,0,&end); + if ( stat < 0 || *end ) + ERROR0(ERR_WARNING, + "Option --patch-bmg: Invalid message condition: %s %c %s\n", + cmd->name1, cond.not ? '!' : '?', sep ); + else + cond_valid = stat > 0; + PRINT("STAT=%d, NOT=%d, MID=%x,%x,%x => valid=%d\n", + stat, cond.not, cond.mid[0], cond.mid[1], cond.mid[2], cond_valid ); + } + + + //--- store record + + patch_bmg_cond_t *pcond = cond_valid ? ALLOCDUP(&cond,sizeof(cond)) : 0; + + const uint num = cmd->id | cmd->opt << BMG_PM__SHIFT_OPT; + if ( cmd->opt == 2 ) + { + char buf[10000]; + ScanEscapedString(buf,sizeof(buf),eq+1,-1,true,0,0); + AppendParamField(&patch_bmg_list,buf,false,num,pcond); + } + else if (cmd->opt) + AppendParamField(&patch_bmg_list,eq+1,false,num,pcond); + else + AppendParamField(&patch_bmg_list,EmptyString,true,num,pcond); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptMacroBMG ( ccp arg ) +{ + + bmg_t temp; + enumError stat = LoadXBMG(&temp,true,arg,true,false); + if (!stat) + { + if (!bmg_macros) + { + bmg_macros = MALLOC(sizeof(*bmg_macros)); + InitializeBMG(bmg_macros); + } + PatchOverwriteBMG(bmg_macros,&temp,true); + } + + ResetBMG(&temp); + return stat != ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SetupPatchingListBMG() +{ + uint max_err = ERR_OK; + + if ( n_patch_bmg != patch_bmg_list.used ) + { + if (n_patch_bmg) + have_patch_count--, have_bmg_patch_count--; + + ResetPatchingListBMG(); + n_patch_bmg = patch_bmg_list.used; + patch_bmg = CALLOC(n_patch_bmg,sizeof(*patch_bmg)); + + int i; + for ( i = 0; i < n_patch_bmg; i++ ) + { + ParamFieldItem_t *pi = patch_bmg_list.field + i; + if ( pi->num >> BMG_PM__SHIFT_OPT == 1 ) + { + enumError err = LoadXBMG(patch_bmg+i,true,pi->key,true,false); + if ( err > ERR_WARNING && err > max_err ) + max_err = err; + } + } + + if (n_patch_bmg) + have_patch_count++, have_bmg_patch_count++; + } + + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetPatchingListBMG() +{ + int i; + for ( i = 0; i < n_patch_bmg; i++ ) + ResetBMG(patch_bmg+i); + FREE(patch_bmg); + patch_bmg = 0; + n_patch_bmg = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError UsePatchingListBMG ( bmg_t * dest ) +{ + if (!n_patch_bmg) + SetupPatchingListBMG(); + DASSERT( n_patch_bmg == patch_bmg_list.used ); + + int i; + enumError stat = ERR_OK; + for ( i = 0; i < n_patch_bmg; i++ ) + { + ParamFieldItem_t *pi = patch_bmg_list.field + i; + if (pi->data) + { + patch_bmg_cond_t *cond = pi->data; + bool found = 0; + uint i; + for ( i = 0; !found && i < sizeof(cond->mid)/sizeof(*cond->mid); i++ ) + found = cond->mid[i] && FindItemBMG(dest,cond->mid[i]); + + PRINT("COND FOUND: mid=%x,%x,%x, not=%d => found=%d => %s\n", + cond->mid[0], cond->mid[1], cond->mid[2], cond->not, + found, found^cond->not ? "EXEC" : "SKIP" ); + + if (!(found^cond->not)) + continue; + } + + enumError err = PatchBMG( dest, patch_bmg+i, + pi->num & BMG_PM__MASK_CMD, pi->key, false ); + if ( err > ERR_WARNING ) + return err; + if ( err == ERR_DIFFER ) + stat = ERR_DIFFER; + } + + if (opt_points_used) + { + char buf[500]; + strcpy(buf,"points="); + PrintMkwPointsLIST(buf+7,sizeof(buf)-7,MkwPointsTab,2); + + bmg_item_t * dptr = InsertItemBMG(dest,MID_VERSUS_POINTS,0,0,0); + if (dptr) + AssignItemTextBMG(dptr,buf,-1); + } + + if (filter_message) + { + bmg_item_t * dptr = dest->item; + bmg_item_t * dend = dptr + dest->item_used; + + bool dirty = false; + for ( ; dptr < dend; dptr++ ) + { + if ( dptr->mid >= MAX_FILTER_MSG || !filter_message[dptr->mid] ) + { + FreeItemBMG(dptr); + dptr->text = 0; + ResetAttribBMG(dest,dptr); + dirty = true; + } + } + if ( dirty && stat < ERR_DIFFER ) + stat = ERR_DIFFER; + } + + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ListPatchingListBMG ( FILE *f, int indent, ccp title ) +{ + DASSERT(f); + if (!patch_bmg_list.used) + return 0; + + indent = NormalizeIndent(indent); + + if (title && *title) + fprintf(f,"%*s%s [N=%u]\n",indent,"",title,patch_bmg_list.used); + indent += 3; + + uint i; + for ( i = 0; i < patch_bmg_list.used; i++ ) + { + ParamFieldItem_t *pi = patch_bmg_list.field + i; + fprintf(f,"%*s%-9s",indent,"",GetPatchNameBMG(pi->num)); + + char buf[1000]; + PrintEscapedString(buf,sizeof(buf),pi->key,-1,CHMD__ALL,0,0); + switch( pi->num >> BMG_PM__SHIFT_OPT ) + { + case 1: fprintf(f," file: %s\n",buf); break; + case 2: fprintf(f," parameter: %s\n",buf); break; + default: fputc('\n',f); break; + } + } + + return patch_bmg_list.used; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetPatchNameBMG ( uint cmd ) +{ + cmd &= BMG_PM__MASK_CMD; + const KeywordTab_t *kt; + for ( kt = PatchKeysBMG; kt->name1; kt++ ) + if ( kt->id == cmd ) + return kt->name1; + + const uint buf_size = 6; + char *buf = GetCircBuf(buf_size); + snprintf(buf,buf_size,"[%u]",cmd); + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DiffBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_only_in +( + uint index, // index of relevant bmg (1|2) + const bmg_item_t * bi, // relevant bmg item + int width // width out output lines +) +{ + DASSERT(bi); + char msg[300], attrib[BMG_ATTRIB_BUF_SIZE], text[1000]; + PrintAttribBMG(attrib,sizeof(attrib),bi->attrib,bi->attrib_used,0,true); + const int msg_len = snprintfS(msg,sizeof(msg), + "* Only in source #%u:%6x %s", + index, bi->mid, attrib ); + + width -= msg_len + 7; + if ( width <= 0 ) + printf("%s\n",msg); + else + { + PrintString16BMG(text,sizeof(text),bi->text,bi->len, + BMG_UTF8_MAX_DIFF,0,opt_bmg_colors); + printf("%s %.*s\n",msg,width,text); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +static void print_differ +( + const bmg_item_t * bi1, // first bmg item + const bmg_item_t * bi2, // second bmg item + ccp message, // differ message + bool text_differ, // true: print text of both + int width // width out output lines +) +{ + DASSERT(bi1); + DASSERT(bi2); + DASSERT( bi1->mid == bi2->mid ); + + char attrib1[BMG_ATTRIB_BUF_SIZE], attrib2[BMG_ATTRIB_BUF_SIZE]; + PrintAttribBMG(attrib1,sizeof(attrib1),bi1->attrib,bi1->attrib_used,0,true); + PrintAttribBMG(attrib2,sizeof(attrib2),bi2->attrib,bi2->attrib_used,0,true); + + char msg[50+2*BMG_ATTRIB_BUF_SIZE], text[1000]; + const int msg_len = !strcmp(attrib1,attrib2) + ? snprintf(msg,sizeof(msg), + "* %-18s%6x %s", message, bi1->mid, attrib1 ) + : snprintf(msg,sizeof(msg), + "* %-18s%6x %s %s", message, bi1->mid, attrib1, attrib2 ); + + if (text_differ) + { + width -= 8; + if ( width < 20 ) + width = 20; + + printf("%s\n",msg); + int min_len, max_len; + if ( bi1->len < bi2->len ) + { + min_len = bi1->len; + max_len = bi2->len; + } + else + { + min_len = bi2->len; + max_len = bi1->len; + } + + u16 *t1 = bi1->text; + u16 *t2 = bi2->text; + u16 *end = t1 + min_len; + while ( t1 < end && *t1 == *t2 ) + t1++, t2++; + int diff_idx = t1 - bi1->text; + + if ( diff_idx > max_len - width/2 ) + diff_idx = max_len - width/2; + if ( diff_idx < 0 ) + diff_idx = 0; + + PrintString16BMG(text,sizeof(text), bi1->text+diff_idx, + bi1->len-diff_idx, BMG_UTF8_MAX_DIFF, 0, opt_bmg_colors ); + printf(" 1: %.*s\n",width,text); + PrintString16BMG(text,sizeof(text), bi2->text+diff_idx, + bi2->len-diff_idx, BMG_UTF8_MAX_DIFF, 0, opt_bmg_colors ); + printf(" 2: %.*s\n",width,text); + } + else + { + width -= msg_len + 7; + if ( width <= 0 ) + printf("%s\n",msg); + else + { + PrintString16BMG(text,sizeof(text),bi1->text,bi1->len, + BMG_UTF8_MAX,0,opt_bmg_colors); + printf("%s %.*s\n",msg,width,text); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError DiffBMG +( + const bmg_t * bmg1, // first bmg + const bmg_t * bmg2, // second bmg + bool quiet, // true: be quiet + int width // width out output lines +) +{ + PRINT("DiffSZS(%p,%p,%d,%d)\n",bmg1,bmg2,quiet,width); + DASSERT(bmg1); + DASSERT(bmg2); + + + const bmg_item_t * p1 = bmg1->item; + const bmg_item_t * e1 = p1 + bmg1->item_used; + const bmg_item_t * p2 = bmg2->item; + const bmg_item_t * e2 = p2 + bmg2->item_used; + + PRINT("PTR: %p..%p/%u, %p..%p/%u\n", + p1, e1, bmg1->item_used, + p2, e2, bmg2->item_used ); + + int differ = 0; + while ( p1 && p1 < e1 && p2 && p2 < e2 ) + { + noPRINT("\t%s : %s\n",p1->path,p2->path); + + if ( p1->mid < p2->mid ) + { + if (quiet) + return ERR_DIFFER; + differ++; + print_only_in(1,p1,width); + p1++; + continue; + } + + if ( p1->mid > p2->mid ) + { + if (quiet) + return ERR_DIFFER; + differ++; + print_only_in(2,p2,width); + p2++; + continue; + } + + const bool attrib_differ + = p1->attrib_used != p2->attrib_used + || memcmp(p1->attrib,p2->attrib,p1->attrib_used); + if ( p1->len != p2->len + || memcmp(p1->text,p2->text,p1->len*sizeof(*p1->text)) ) + { + if (quiet) + return ERR_DIFFER; + differ++; + ccp msg = attrib_differ ? "Text+attr differ:" : "Text differ:"; + print_differ(p1,p2,msg,true,width); + } + else if ( attrib_differ ) + { + if (quiet) + return ERR_DIFFER; + differ++; + print_differ(p1,p2,"Attributes differ:",false,width); + } + + p1++; + p2++; + } + + while ( p1 && p1 < e1 ) + { + if (quiet) + return ERR_DIFFER; + differ++; + print_only_in(1,p1,width); + p1++; + } + + while ( p2 && p2 < e2 ) + { + if (quiet) + return ERR_DIFFER; + differ++; + print_only_in(2,p2,width); + p2++; + } + + return differ ? ERR_DIFFER : ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanOptLoadBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bmg_t opt_load_bmg = {0}; +bool opt_patch_names = false; + +//----------------------------------------------------------------------------- + +int ScanOptLoadBMG ( ccp arg ) +{ + if ( !arg || !*arg ) + return 0; + + bmg_t temp; + enumError err = LoadXBMG(&temp,true,arg,true,false); + if (!err) + PatchBMG(&opt_load_bmg,&temp,BMG_PM_OVERWRITE,0,true); + ResetBMG(&temp); + return err > ERR_WARNING; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanOptFilterBMG() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +u8 * filter_message = 0; // NULL or field with 'MAX_FILTER_MSG' elements + +//----------------------------------------------------------------------------- + +enum +{ + MD_NONE, MD_ID, MD_PARAM, MD_GENERIC, MD_ALL, MD_CHAT, + + MD_CUPS, MD_TRACKS, MD_ARENAS, + MD_CTCODE, MD_CTCUPS, MD_CTTRACKS, MD_CTARENAS, MD_CTREFS, + MD_LECODE, MD_LECUPS, MD_LETRACKS, MD_LEARENAS, MD_LEREFS, + MD_XCODE, MD_XCUPS, MD_XTRACKS, MD_XARENAS, MD_XREFS, + MD_ALLCODE, MD_ALLCUPS, MD_ALLTRACKS, MD_ALLARENAS, MD_ALLREFS, +}; + +//----------------------------------------------------------------------------- + +static void ScanOptMsgJob ( int mode, u8 value ) +{ + if ( mode >= MD_XCODE && mode <= MD_XREFS ) + { + if (opt_bmg_support_lecode) + mode = mode - MD_XCODE + MD_LECODE; + else if (opt_bmg_support_ctcode) + mode = mode - MD_XCODE + MD_CTCODE; + } + + switch(mode) + { + case MD_NONE: + PRINT("SET ALL = %d\n",!value); + memset(filter_message,!value,MAX_FILTER_MSG); + break; + + case MD_ID: + filter_message[MID_PARAM_IDENTIFY] = value; + break; + + case MD_PARAM: + filter_message[MID_PARAM_IDENTIFY] = value; + memset( filter_message + MID_PARAM_BEG, value, + MID_PARAM_END - MID_PARAM_BEG ); + break; + + case MD_CUPS: + PRINT("SET CUPS = %d\n",value); + memset( filter_message + MID_RCUP_BEG, value, + MID_RCUP_END - MID_RCUP_BEG ); + memset( filter_message + MID_BCUP_BEG, value, + MID_BCUP_END - MID_BCUP_BEG ); + ScanOptMsgJob(MD_XCUPS,value); + ScanOptMsgJob(MD_XREFS,value); + break; + + case MD_TRACKS: + PRINT("SET TRACKS = %d\n",value); + memset(filter_message+MKW_TRACK_MID_1,value,MKW_N_TRACKS); + memset(filter_message+MKW_TRACK_MID_2,value,MKW_N_TRACKS); + ScanOptMsgJob(MD_XTRACKS,value); + ScanOptMsgJob(MD_XREFS,value); + break; + + case MD_ARENAS: + PRINT("SET ARENAS = %d\n",value); + memset(filter_message+MKW_ARENA_MID_1,value,MKW_N_ARENAS); + memset(filter_message+MKW_ARENA_MID_2,value,MKW_N_ARENAS); + ScanOptMsgJob(MD_XARENAS,value); + break; + + case MD_CHAT: + PRINT("SET CHAT = %d\n",value); + memset(filter_message+MID_CHAT_BEG,value,BMG_N_CHAT); + break; + + //-- ct-code + + case MD_CTCODE: + PRINT("SET CT-CODE = %d\n",value); + memset( filter_message + MID_CT_BEG, value, + MID_CT_END - MID_CT_BEG ); + break; + + case MD_CTCUPS: + PRINT("SET CT-CUPS = %d\n",value); + memset( filter_message + MID_CT_CUP_BEG, value, + MID_CT_CUP_END - MID_CT_CUP_BEG ); + break; + + case MD_CTTRACKS: + PRINT("SET CT-TRACKS = %d\n",value); + memset( filter_message + MID_CT_TRACK_BEG, value, + MID_CT_TRACK_END - MID_CT_TRACK_BEG ); + break; + + case MD_CTARENAS: + PRINT("SET CT-ARENAS = %d\n",value); + memset( filter_message + MID_CT_ARENA_BEG, value, + MID_CT_ARENA_END - MID_CT_ARENA_BEG ); + break; + + case MD_CTREFS: + PRINT("SET CT-REFS = %d\n",value); + memset( filter_message + MID_CT_CUP_REF_BEG, value, + MID_CT_CUP_REF_END - MID_CT_CUP_REF_BEG ); + break; + + //-- le-code + + case MD_LECODE: + PRINT("SET LE-CODE = %d\n",value); + memset( filter_message + MID_LE_BEG, + value, MID_LE_END - MID_LE_BEG ); + break; + + case MD_LECUPS: + PRINT("SET LE-CUPS = %d\n",value); + memset( filter_message + MID_LE_CUP_BEG, value, + MID_LE_CUP_END - MID_LE_CUP_BEG ); + break; + + case MD_LETRACKS: + PRINT("SET LE-TRACKS = %d\n",value); + memset( filter_message + MID_LE_TRACK_BEG, value, + MID_LE_TRACK_END - MID_LE_TRACK_BEG ); + break; + + case MD_LEARENAS: + PRINT("SET LE-ARENAS = %d\n",value); + memset( filter_message + MID_LE_ARENA_BEG, value, + MID_LE_ARENA_END - MID_LE_ARENA_BEG ); + break; + + case MD_LEREFS: + PRINT("SET LE-REFS = %d\n",value); + memset( filter_message + MID_LE_CUP_REF_BEG, value, + MID_LE_CUP_REF_END - MID_LE_CUP_REF_BEG ); + break; + + //-- all* + + case MD_ALLCODE: + ScanOptMsgJob(MD_CTCODE,value); + ScanOptMsgJob(MD_LECODE,value); + break; + + case MD_ALLCUPS: + ScanOptMsgJob(MD_CUPS,value); + ScanOptMsgJob(MD_CTCUPS,value); + ScanOptMsgJob(MD_LECUPS,value); + break; + + case MD_ALLTRACKS: + ScanOptMsgJob(MD_TRACKS,value); + ScanOptMsgJob(MD_CTTRACKS,value); + ScanOptMsgJob(MD_LETRACKS,value); + break; + + case MD_ALLARENAS: + ScanOptMsgJob(MD_ARENAS,value); + ScanOptMsgJob(MD_CTARENAS,value); + ScanOptMsgJob(MD_LEARENAS,value); + break; + + case MD_ALLREFS: + ScanOptMsgJob(MD_CTREFS,value); + ScanOptMsgJob(MD_LEREFS,value); + break; + + + //-- misc + + case MD_GENERIC: + PRINT("SET GENRIC = %d\n",value); + memset(filter_message+MID_GENERIC_BEG,value,BMG_N_GENERIC); + break; + + case MD_ALL: + PRINT("SET ALL = %d\n",value); + memset(filter_message,value,MAX_FILTER_MSG); + break; + } +} + +//----------------------------------------------------------------------------- + +int ScanOptFilterBMG ( ccp arg ) +{ + // >>> see SetupBMG() for MID assertions! + + if ( !arg || !*arg ) + return 0; + + if (!filter_message) + { + while ( *arg > 0 && *arg <= ' ' ) + arg++; + + PRINT("FILTER-MESSAGE: ALLOC SET %d\n",*arg == '-'); + filter_message = MALLOC(MAX_FILTER_MSG); + memset( filter_message, *arg == '-', MAX_FILTER_MSG ); + } + + //------------------------------------------------------------------ + // MD_CTCODE..MD_CTREFS & MD_LECODE..MD_LEREFS & MD_XCODE..MD_XREFS + // must have the same order to support X-calcualtions below! + //------------------------------------------------------------------ + + static const KeywordTab_t opt_msg[] = + { + { MD_NONE, "NONE", 0, 0 }, + { MD_ID, "IDENTIFICATION", "ID", 0 }, + { MD_PARAM, "PARAM", 0, 0 }, + { MD_GENERIC, "GENERIC", 0, 0 }, + { MD_ALL, "ALL", 0, 0 }, + { MD_CUPS, "CUPS", 0, 0 }, + { MD_TRACKS, "TRACKS", 0, 0 }, + { MD_ARENAS, "ARENAS", 0, 0 }, + { MD_CHAT, "CHAT", 0, 0 }, + { MD_CTCODE, "CTCODE", "CT", 0 }, + { MD_CTCUPS, "CTCUPS", 0, 0 }, + { MD_CTTRACKS, "CTTRACKS", 0, 0 }, + { MD_CTARENAS, "CTARENAS", 0, 0 }, + { MD_CTREFS, "CTREFS", 0, 0 }, + { MD_LECODE, "LECODE", "LE", 0 }, + { MD_LECUPS, "LECUPS", 0, 0 }, + { MD_LETRACKS, "LETRACKS", 0, 0 }, + { MD_LEARENAS, "LEARENAS", 0, 0 }, + { MD_LEREFS, "LEREFS", 0, 0 }, + { MD_XCODE, "XCODE", "X", 0 }, + { MD_XCUPS, "XCUPS", 0, 0 }, + { MD_XTRACKS, "XTRACKS", 0, 0 }, + { MD_XARENAS, "XARENAS", 0, 0 }, + { MD_XREFS, "XREFS", 0, 0 }, + { MD_ALLCODE, "ALLCODE", 0, 0 }, + { MD_ALLCUPS, "ALLCUPS", 0, 0 }, + { MD_ALLTRACKS, "ALLTRACKS", 0, 0 }, + { MD_ALLARENAS, "ALLARENAS", 0, 0 }, + { MD_ALLREFS, "ALLREFS", 0, 0 }, + {0,0,0,0} + }; + + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == ',' || *arg == '+' ) + arg++; + if (!*arg) + break; + + const u8 value = *arg != '-'; + if (!value) + arg++; + + char keyname[20], *dest = keyname; + while (isalnum((int)*arg)) + { + if ( dest < keyname + sizeof(keyname)-1 ) + *dest++ = *arg; + arg++; + } + *dest = 0; + + if ( dest > keyname+1 ) + { + const KeywordTab_t * key = ScanKeyword(0,keyname,opt_msg); + if (key) + { + ScanOptMsgJob(key->id,value); + continue; + } + } + + u32 a1, a2, a3; + if ( ScanMidBMG(0,&a1,&a2,&a3,keyname,dest,0) < 1 ) + { + ERROR0(ERR_SYNTAX,"Option --msg: Invalid parameter: %s\n",keyname); + return 1; + } + if (!a2) + a2 = a1; + if (!a3) + a3 = a1; + + while ( *arg > 0 && *arg <= ' ' ) + arg++; + if ( *arg != ':' ) + { + PRINT("MID FOUND: %x,%x,%x := %u\n",a1,a2,a3,value); + if ( a1 < MAX_FILTER_MSG ) + filter_message[a1] = value; + if ( a2 < MAX_FILTER_MSG ) + filter_message[a2] = value; + if ( a3 < MAX_FILTER_MSG ) + filter_message[a3] = value; + continue; + } + arg++; + + u32 b1, b2, b3; + if ( ScanMidBMG(0,&b1,&b2,&b3,arg,0,(char**)&arg) < 1 ) + { + ERROR0(ERR_SYNTAX,"Option --msg: Invalid parameter: %s\n",keyname); + return 1; + } + if (!b2) + b2 = b1; + if (!b3) + b3 = b1; + + PRINT("MID FOUND: %x..%x, %x..%x, %x..%x := %u\n",a1,b1,a2,b2,a3,b3,value); + + if ( ++b1 > MAX_FILTER_MSG ) + b1 = MAX_FILTER_MSG; + if ( a1 < b1 ) + memset( filter_message+a1, value, b1-a1 ); + + if ( ++b2 > MAX_FILTER_MSG ) + b2 = MAX_FILTER_MSG; + if ( a2 < b2 ) + memset( filter_message+a2, value, b2-a2 ); + + if ( ++b3 > MAX_FILTER_MSG ) + b3 = MAX_FILTER_MSG; + if ( a3 < b3 ) + memset( filter_message+a3, value, b3-a3 ); + } + + if ( logging > 1 ) + HexDump016(stderr,0,0,filter_message,MAX_FILTER_MSG); + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cmd_bmg_cat() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_bmg_cat ( bool mix ) +{ + stdlog = stderr; + + enumError err = SetupPatchingListBMG(); + if ( err > ERR_WARNING ) + return err; + + bmg_t mix_bmg; + InitializeBMG(&mix_bmg); + + raw_data_t raw; + InitializeRawData(&raw); + + uint mix_count = 0; + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,FF_BMG); + + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + + bmg_t bmg; + err = ScanRawDataBMG(&bmg,true,&raw); + if ( err > ERR_WARNING ) + return err; + + err = UsePatchingListBMG(&bmg); + if ( err > ERR_WARNING ) + return err; + + if (mix) + { + PatchBMG(&mix_bmg,&bmg,BMG_PM_OVERWRITE,0,true); + mix_count++; + } + else if (!testmode) + { + err = SaveTextXBMG(&bmg,"-",false); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + ResetBMG(&bmg); + } + + if ( !testmode && mix ) + { + if (!mix_count) + { + err = UsePatchingListBMG(&mix_bmg); + if ( err > ERR_WARNING ) + return err; + } + + err = SaveTextXBMG(&mix_bmg,"-",false); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + + ResetBMG(&mix_bmg); + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cmd_bmg_identifier() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_bmg_identifier() +{ + stdlog = stderr; + + ct_bmg_t ctb; + SetupCtBMG(&ctb,CTM_OPTIONS,CTM_OPTIONS); + + bmg_t bmg; + InitializeBMG(&bmg); + PatchIdentificationBMG(&bmg,&ctb); + SaveTextXBMG(&bmg,"-",false); + fflush(stdout); + + ResetBMG(&bmg); + ResetCtBMG(&ctb); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptBmgEncoding ( ccp arg ) +{ + const KeywordTab_t *cmd = ScanKeyword(0,arg,TabEncodingBMG); + if (!cmd) + { + ERROR0(ERR_SYNTAX, + "Option --bmg-encoding: Invalid mode: '%s'\n",arg); + return 1; + } + + if (cmd->opt) + ERROR0(ERR_WARNING, + "Option --bmg-encoding: Mode »%s« not supported yet!\n", + GetEncodingNameBMG(cmd->id,"?") ); + + opt_bmg_encoding = cmd->id; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptBmgInfSize ( ccp arg, bool is_wbmgt ) +{ + if ( !arg || !*arg ) + { + opt_bmg_inf_size = 0; + return 0; + } + + char *end; + uint num = str2ul(arg,&end,10); + if ( end == arg || *end ) + { + ERROR0(ERR_SYNTAX, + "Option --%sinf-size: Not a number: %s\n", + is_wbmgt ? "" : "bmg-", arg ); + return 1; + } + + if ( num < 4 || num > BMG_INF_LIMIT ) + { + ERROR0(ERR_SEMANTIC, + "Option --%sinf-size: Number must be in range 4..%u: %s\n", + is_wbmgt ? "" : "bmg-", BMG_INF_LIMIT, arg ); + return 1; + } + opt_bmg_inf_size = num; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanOptBmgMid ( ccp arg ) +{ + const int stat = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_FORCE,"Option --bmg-mid"); + if ( stat == OFFON_ERROR ) + return 1; + + opt_bmg_mid = stat; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int scan_opt_attrib ( bool *flag, u8* attrib, ccp opt, ccp arg ) +{ + DASSERT(flag); + DASSERT(attrib); + DASSERT(opt); + + *flag = false; + memset(attrib,0,BMG_ATTRIB_SIZE); + if ( !arg || !*arg ) + return 0; + + ccp end = scan_attrib(attrib,BMG_ATTRIB_SIZE,arg); + if (end) + while ( *end == ' ' || *end == '\t' ) + end++; + if ( !end || *end ) + { + ERROR0(ERR_SYNTAX, + "Option --%s: Invalid attribute vector: %s\n", opt, arg ); + return 1; + } + + *flag = true; + return 0; +} + +//----------------------------------------------------------------------------- + +int ScanOptForceAttrib ( ccp arg ) +{ + return scan_opt_attrib(&opt_bmg_force_attrib,bmg_force_attrib,"force-attrib",arg); +} + +//----------------------------------------------------------------------------- + +int ScanOptDefAttrib ( ccp arg ) +{ + return scan_opt_attrib(&opt_bmg_def_attrib,bmg_def_attrib,"def-attrib",arg); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/lib-xbmg.h b/project/src/lib-xbmg.h new file mode 100644 index 0000000..6c16316 --- /dev/null +++ b/project/src/lib-xbmg.h @@ -0,0 +1,148 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// all about BMG files: +// http://wiki.tockdom.de/index.php?title=BMG + +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SZS_LIB_BMG_H +#define SZS_LIB_BMG_H 1 + +#include "lib-bmg.h" +#include "lib-std.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct bmg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupBMG ( ccp opt_msg ); + +enumError LoadXBMG +( + bmg_t * bmg, // pointer to valid bmg + bool initialize, // true: initialize 'bmg' + ccp fname, // filename of source + bool allow_archive, // true: concat BMGs of archives to 1 source + bool ignore_no_file // ignore if file does not exists + // and return warning ERR_NOT_EXISTS +); + +struct raw_data_t; +enumError ScanRawDataBMG +( + bmg_t * bmg, // BMG data structure + bool init_bmg, // true: initialize 'bmg' first + struct raw_data_t * raw // valid raw data +); + +static inline file_format_t GetFF_BMG ( const bmg_t * bmg ) +{ + DASSERT(bmg); + return bmg->is_text_src ? FF_BMG_TXT : FF_BMG; +} + +static inline ccp GetNameFF_BMG ( const bmg_t * bmg ) +{ + DASSERT(bmg); + return GetNameFF( bmg->is_text_src ? FF_BMG_TXT : FF_BMG, 0 ); +} + +//----------------------------------------------------------------------------- + +enumError SaveRawXBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +enumError SaveTextXBMG +( + bmg_t * bmg, // pointer to valid bmg + ccp fname, // filename of destination + bool set_time // true: set time stamps +); + +//----------------------------------------------------------------------------- + +extern int have_bmg_patch_count; + +int ScanOptPatchMessage ( ccp arg ); +int ScanOptMacroBMG ( ccp arg ); +enumError SetupPatchingListBMG(); +void ResetPatchingListBMG(); +enumError UsePatchingListBMG ( bmg_t * dest ); +uint ListPatchingListBMG ( FILE *f, int indent, ccp title ); +ccp GetPatchNameBMG ( uint cmd ); + + +enumError DiffBMG +( + const bmg_t * bmg1, // first bmg + const bmg_t * bmg2, // second bmg + bool quiet, // true: be quiet + int width // width out output lines +); + +//----------------------------------------------------------------------------- + +int ScanOptBmgEncoding ( ccp arg ); +int ScanOptBmgInfSize ( ccp arg, bool is_wbmgt ); +int ScanOptBmgMid ( ccp arg ); +int ScanOptForceAttrib ( ccp arg ); +int ScanOptDefAttrib ( ccp arg ); +int ScanOptLoadBMG ( ccp arg ); + +extern bmg_t opt_load_bmg; +extern bool opt_patch_names; + +int ScanOptFilterBMG ( ccp arg ); + +#define MAX_FILTER_MSG 0x10000 +extern u8 * filter_message; // NULL or field with 'MAX_FILTER_MSG' elements + +enumError cmd_bmg_cat ( bool mix ); +enumError cmd_bmg_identifier(); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_LIB_BMG_H diff --git a/project/src/libbz2/LICENSE b/project/src/libbz2/LICENSE new file mode 100644 index 0000000..cc61417 --- /dev/null +++ b/project/src/libbz2/LICENSE @@ -0,0 +1,42 @@ + +-------------------------------------------------------------------------- + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2010 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, jseward@bzip.org +bzip2/libbzip2 version 1.0.6 of 6 September 2010 + +-------------------------------------------------------------------------- diff --git a/project/src/libbz2/blocksort.c b/project/src/libbz2/blocksort.c new file mode 100644 index 0000000..a7b193a --- /dev/null +++ b/project/src/libbz2/blocksort.c @@ -0,0 +1,1094 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/bzlib.c b/project/src/libbz2/bzlib.c new file mode 100644 index 0000000..5e0289b --- /dev/null +++ b/project/src/libbz2/bzlib.c @@ -0,0 +1,1572 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + fixed bzWrite/bzRead to ignore zero-length requests. + fixed bzread to correctly handle read requests after EOF. + wrong parameter order in call to bzDecompressInit in + bzBuffToBuffDecompress. Fixed. +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ + fprintf(stderr, + "\n\nbzip2/libbzip2: internal error number %d.\n" + "This is a bug in bzip2/libbzip2, %s.\n" + "Please report it to me at: jseward@bzip.org. If this happened\n" + "when you were using some program which uses libbzip2 as a\n" + "component, you should also report this bug to the author(s)\n" + "of that program. Please make an effort to report this bug;\n" + "timely and accurate bug reports eventually lead to higher\n" + "quality software. Thanks. Julian Seward, 10 December 2007.\n\n", + errcode, + BZ2_bzlibVersion() + ); + + if (errcode == 1007) { + fprintf(stderr, + "\n*** A special note about internal error number 1007 ***\n" + "\n" + "Experience suggests that a common cause of i.e. 1007\n" + "is unreliable memory or other hardware. The 1007 assertion\n" + "just happens to cross-check the results of huge numbers of\n" + "memory reads/writes, and so acts (unintendedly) as a stress\n" + "test of your memory system.\n" + "\n" + "I suggest the following: try compressing the file again,\n" + "possibly monitoring progress in detail with the -vv flag.\n" + "\n" + "* If the error cannot be reproduced, and/or happens at different\n" + " points in compression, you may have a flaky memory system.\n" + " Try a memory-test program. I have used Memtest86\n" + " (www.memtest86.com). At the time of writing it is free (GPLd).\n" + " Memtest86 tests memory much more thorougly than your BIOSs\n" + " power-on test, and may find failures that the BIOS doesn't.\n" + "\n" + "* If the error can be repeatably reproduced, this is a bug in\n" + " bzip2, and I would very much like to hear about it. Please\n" + " let me know, and, ideally, save a copy of the file causing the\n" + " problem -- without which I will be unable to investigate it.\n" + "\n" + ); + } + + exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ + if (sizeof(int) != 4) return 0; + if (sizeof(short) != 2) return 0; + if (sizeof(char) != 1) return 0; + return 1; +} + + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + void* v = malloc ( items * size ); + return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) free ( addr ); +} + + +/*---------------------------------------------------*/ +static +void prepare_new_block ( EState* s ) +{ + Int32 i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC ( s->blockCRC ); + for (i = 0; i < 256; i++) s->inUse[i] = False; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ + if (s->state_in_ch < 256 && s->state_in_len > 0) + return False; else + return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit) + ( bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 n; + EState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL || + blockSize100k < 1 || blockSize100k > 9 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(EState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->arr1 = NULL; + s->arr2 = NULL; + s->ftab = NULL; + + n = 100000 * blockSize100k; + s->arr1 = BZALLOC( n * sizeof(UInt32) ); + s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + s->ftab = BZALLOC( 65537 * sizeof(UInt32) ); + + if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + if (s != NULL) BZFREE(s); + return BZ_MEM_ERROR; + } + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->blockSize100k = blockSize100k; + s->nblockMAX = 100000 * blockSize100k - 19; + s->verbosity = verbosity; + s->workFactor = workFactor; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ + Int32 i; + UChar ch = (UChar)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC( s->blockCRC, ch ); + } + s->inUse[s->state_in_ch] = True; + switch (s->state_in_len) { + case 1: + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 2: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 3: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len-4] = True; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = ((UChar)(s->state_in_len-4)); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ + if (s->state_in_ch < 256) add_pair_to_block ( s ); + init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0) \ +{ \ + UInt32 zchh = (UInt32)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && \ + zs->state_in_len == 1) { \ + UChar ch = (UChar)(zs->state_in_ch); \ + BZ_UPDATE_CRC( zs->blockCRC, ch ); \ + zs->inUse[zs->state_in_ch] = True; \ + zs->block[zs->nblock] = (UChar)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || \ + zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block ( zs ); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ + Bool progress_in = False; + + if (s->mode == BZ_M_RUNNING) { + + /*-- fast track the common case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + } + + } else { + + /*-- general, uncommon case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- flush/finish end? --*/ + if (s->avail_in_expect == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + s->avail_in_expect--; + } + } + return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ + Bool progress_out = False; + + while (True) { + + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + progress_out = True; + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ + Bool progress_in = False; + Bool progress_out = False; + EState* s = strm->state; + + while (True) { + + if (s->state == BZ_S_OUTPUT) { + progress_out |= copy_output_until_stop ( s ); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + prepare_new_block ( s ); + s->state = BZ_S_INPUT; + if (s->mode == BZ_M_FLUSHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + } + + if (s->state == BZ_S_INPUT) { + progress_in |= copy_input_until_stop ( s ); + if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + flush_RL ( s ); + BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); + s->state = BZ_S_OUTPUT; + } + else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock ( s, False ); + s->state = BZ_S_OUTPUT; + } + else + if (s->strm->avail_in == 0) { + break; + } + } + + } + + return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ + Bool progress; + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + preswitch: + switch (s->mode) { + + case BZ_M_IDLE: + return BZ_SEQUENCE_ERROR; + + case BZ_M_RUNNING: + if (action == BZ_RUN) { + progress = handle_compress ( strm ); + return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; + } + else + if (action == BZ_FLUSH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto preswitch; + } + else + if (action == BZ_FINISH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto preswitch; + } + else + return BZ_PARAM_ERROR; + + case BZ_M_FLUSHING: + if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; + + case BZ_M_FINISHING: + if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (!progress) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FINISH_OK; + s->mode = BZ_M_IDLE; + return BZ_STREAM_END; + } + return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm ) +{ + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + BZFREE(strm->state); + + strm->state = NULL; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/*--- Decompression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit) + ( bz_stream* strm, + int verbosity, + int small ) +{ + DState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + if (small != 0 && small != 1) return BZ_PARAM_ERROR; + if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(DState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + strm->state = s; + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + s->smallDecompress = (Bool)small; + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + s->verbosity = verbosity; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + /* restore */ + UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC; + UChar c_state_out_ch = s->state_out_ch; + Int32 c_state_out_len = s->state_out_len; + Int32 c_nblock_used = s->nblock_used; + Int32 c_k0 = s->k0; + UInt32* c_tt = s->tt; + UInt32 c_tPos = s->tPos; + char* cs_next_out = s->strm->next_out; + unsigned int cs_avail_out = s->strm->avail_out; + Int32 ro_blockSize100k = s->blockSize100k; + /* end restore */ + + UInt32 avail_out_INIT = cs_avail_out; + Int32 s_save_nblockPP = s->save_nblock+1; + unsigned int total_out_lo32_old; + + while (True) { + + /* try to finish existing run */ + if (c_state_out_len > 0) { + while (True) { + if (cs_avail_out == 0) goto return_notr; + if (c_state_out_len == 1) break; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + c_state_out_len--; + cs_next_out++; + cs_avail_out--; + } + s_state_out_len_eq_one: + { + if (cs_avail_out == 0) { + c_state_out_len = 1; goto return_notr; + }; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + cs_next_out++; + cs_avail_out--; + } + } + /* Only caused by corrupt data stream? */ + if (c_nblock_used > s_save_nblockPP) + return True; + + /* can a new run be started? */ + if (c_nblock_used == s_save_nblockPP) { + c_state_out_len = 0; goto return_notr; + }; + c_state_out_ch = c_k0; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (k1 != c_k0) { + c_k0 = k1; goto s_state_out_len_eq_one; + }; + if (c_nblock_used == s_save_nblockPP) + goto s_state_out_len_eq_one; + + c_state_out_len = 2; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + c_state_out_len = 3; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + BZ_GET_FAST_C(k1); c_nblock_used++; + c_state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST_C(c_k0); c_nblock_used++; + } + + return_notr: + total_out_lo32_old = s->strm->total_out_lo32; + s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); + if (s->strm->total_out_lo32 < total_out_lo32_old) + s->strm->total_out_hi32++; + + /* save */ + s->calculatedBlockCRC = c_calculatedBlockCRC; + s->state_out_ch = c_state_out_ch; + s->state_out_len = c_state_out_len; + s->nblock_used = c_nblock_used; + s->k0 = c_k0; + s->tt = c_tt; + s->tPos = c_tPos; + s->strm->next_out = cs_next_out; + s->strm->avail_out = cs_avail_out; + /* end save */ + } + return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ + Int32 nb, na, mid; + nb = 0; + na = 256; + do { + mid = (nb + na) >> 1; + if (indx >= cftab[mid]) nb = mid; else na = mid; + } + while (na - nb != 1); + return nb; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ + Bool corrupt; + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + while (True) { + if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; + if (s->state == BZ_X_OUTPUT) { + if (s->smallDecompress) + corrupt = unRLE_obuf_to_output_SMALL ( s ); else + corrupt = unRLE_obuf_to_output_FAST ( s ); + if (corrupt) return BZ_DATA_ERROR; + if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { + BZ_FINALISE_CRC ( s->calculatedBlockCRC ); + if (s->verbosity >= 3) + VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, + s->calculatedBlockCRC ); + if (s->verbosity >= 2) VPrintf0 ( "]" ); + if (s->calculatedBlockCRC != s->storedBlockCRC) + return BZ_DATA_ERROR; + s->calculatedCombinedCRC + = (s->calculatedCombinedCRC << 1) | + (s->calculatedCombinedCRC >> 31); + s->calculatedCombinedCRC ^= s->calculatedBlockCRC; + s->state = BZ_X_BLKHDR_1; + } else { + return BZ_OK; + } + } + if (s->state >= BZ_X_MAGIC_1) { + Int32 r = BZ2_decompress ( s ); + if (r == BZ_STREAM_END) { + if (s->verbosity >= 3) + VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x", + s->storedCombinedCRC, s->calculatedCombinedCRC ); + if (s->calculatedCombinedCRC != s->storedCombinedCRC) + return BZ_DATA_ERROR; + return r; + } + if (s->state != BZ_X_OUTPUT) return r; + } + } + + AssertH ( 0, 6001 ); + + return 0; /*NOTREACHED*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm ) +{ + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->tt != NULL) BZFREE(s->tt); + if (s->ll16 != NULL) BZFREE(s->ll16); + if (s->ll4 != NULL) BZFREE(s->ll4); + + BZFREE(strm->state); + strm->state = NULL; + + return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee) \ +{ \ + if (bzerror != NULL) *bzerror = eee; \ + if (bzf != NULL) bzf->lastErr = eee; \ +} + +typedef + struct { + FILE* handle; + Char buf[BZ_MAX_UNUSED]; + Int32 bufN; + Bool writing; + bz_stream strm; + Int32 lastErr; + Bool initialisedOk; + } + bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen) + ( int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 ret; + bzFile* bzf = NULL; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (blockSize100k < 1 || blockSize100k > 9) || + (workFactor < 0 || workFactor > 250) || + (verbosity < 0 || verbosity > 4)) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + bzf->initialisedOk = False; + bzf->bufN = 0; + bzf->handle = f; + bzf->writing = True; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + if (workFactor == 0) workFactor = 30; + ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = 0; + bzf->initialisedOk = True; + return bzf; +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return; }; + + bzf->strm.avail_in = len; + bzf->strm.next_in = buf; + + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); + if (ret != BZ_RUN_OK) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (bzf->strm.avail_in == 0) + { BZ_SETERR(BZ_OK); return; }; + } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out ) +{ + BZ2_bzWriteClose64 ( bzerror, b, abandon, + nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; + if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; + if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; + if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + + if ((!abandon) && bzf->lastErr == BZ_OK) { + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (ret == BZ_STREAM_END) break; + } + } + + if ( !abandon && !ferror ( bzf->handle ) ) { + fflush ( bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (nbytes_in_lo32 != NULL) + *nbytes_in_lo32 = bzf->strm.total_in_lo32; + if (nbytes_in_hi32 != NULL) + *nbytes_in_hi32 = bzf->strm.total_in_hi32; + if (nbytes_out_lo32 != NULL) + *nbytes_out_lo32 = bzf->strm.total_out_lo32; + if (nbytes_out_hi32 != NULL) + *nbytes_out_hi32 = bzf->strm.total_out_hi32; + + BZ_SETERR(BZ_OK); + BZ2_bzCompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen) + ( int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused ) +{ + bzFile* bzf = NULL; + int ret; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (small != 0 && small != 1) || + (verbosity < 0 || verbosity > 4) || + (unused == NULL && nUnused != 0) || + (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + + bzf->initialisedOk = False; + bzf->handle = f; + bzf->bufN = 0; + bzf->writing = False; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + while (nUnused > 0) { + bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; + unused = ((void*)( 1 + ((UChar*)(unused)) )); + nUnused--; + } + + ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + + bzf->initialisedOk = True; + return bzf; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + + if (bzf->initialisedOk) + (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return 0; }; + + bzf->strm.avail_out = len; + bzf->strm.next_out = buf; + + while (True) { + + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + + if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { + n = fread ( bzf->buf, sizeof(UChar), + BZ_MAX_UNUSED, bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + bzf->bufN = n; + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + } + + ret = BZ2_bzDecompress ( &(bzf->strm) ); + + if (ret != BZ_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return 0; }; + + if (ret == BZ_OK && myfeof(bzf->handle) && + bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) + { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + + if (ret == BZ_STREAM_END) + { BZ_SETERR(BZ_STREAM_END); + return len - bzf->strm.avail_out; }; + if (bzf->strm.avail_out == 0) + { BZ_SETERR(BZ_OK); return len; }; + + } + + return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused) + ( int* bzerror, + BZFILE* b, + void** unused, + int* nUnused ) +{ + bzFile* bzf = (bzFile*)b; + if (bzf == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (bzf->lastErr != BZ_STREAM_END) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (unused == NULL || nUnused == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + + BZ_SETERR(BZ_OK); + *nUnused = bzf->strm.avail_in; + *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffCompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + blockSize100k < 1 || blockSize100k > 9 || + verbosity < 0 || verbosity > 4 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzCompressInit ( &strm, blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress ( &strm, BZ_FINISH ); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd ( &strm ); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + (small != 0 && small != 1) || + verbosity < 0 || verbosity > 4) + return BZ_PARAM_ERROR; + + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzDecompress ( &strm ); + if (ret == BZ_OK) goto output_overflow_or_eof; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzDecompressEnd ( &strm ); + return BZ_OK; + + output_overflow_or_eof: + if (strm.avail_out > 0) { + BZ2_bzDecompressEnd ( &strm ); + return BZ_UNEXPECTED_EOF; + } else { + BZ2_bzDecompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + }; + + errhandler: + BZ2_bzDecompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- + return version like "0.9.5d, 4-Sept-1999". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ + return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file),O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen + ( const char *path, /* no use when bzdopen */ + int fd, /* no use when bzdopen */ + const char *mode, + int open_mode) /* bzopen: 0, bzdopen:1 */ +{ + int bzerr; + char unused[BZ_MAX_UNUSED]; + int blockSize100k = 9; + int writing = 0; + char mode2[10] = ""; + FILE *fp = NULL; + BZFILE *bzfp = NULL; + int verbosity = 0; + int workFactor = 30; + int smallMode = 0; + int nUnused = 0; + + if (mode == NULL) return NULL; + while (*mode) { + switch (*mode) { + case 'r': + writing = 0; break; + case 'w': + writing = 1; break; + case 's': + smallMode = 1; break; + default: + if (isdigit((int)(*mode))) { + blockSize100k = *mode-BZ_HDR_0; + } + } + mode++; + } + strcat(mode2, writing ? "w" : "r" ); + strcat(mode2,"b"); /* binary mode */ + + if (open_mode==0) { + if (path==NULL || strcmp(path,"")==0) { + fp = (writing ? stdout : stdin); + SET_BINARY_MODE(fp); + } else { + fp = fopen(path,mode2); + } + } else { +#ifdef BZ_STRICT_ANSI + fp = NULL; +#else + fp = fdopen(fd,mode2); +#endif + } + if (fp == NULL) return NULL; + + if (writing) { + /* Guard against total chaos and anarchy -- JRS */ + if (blockSize100k < 1) blockSize100k = 1; + if (blockSize100k > 9) blockSize100k = 9; + bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, + verbosity,workFactor); + } else { + bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, + unused,nUnused); + } + if (bzfp == NULL) { + if (fp != stdin && fp != stdout) fclose(fp); + return NULL; + } + return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- + open file for read or write. + ex) bzopen("file","w9") + case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) + ( const char *path, + const char *mode ) +{ + return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) + ( int fd, + const char *mode ) +{ + return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ + int bzerr, nread; + if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; + nread = BZ2_bzRead(&bzerr,b,buf,len); + if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { + return nread; + } else { + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ + int bzerr; + + BZ2_bzWrite(&bzerr,b,buf,len); + if(bzerr == BZ_OK){ + return len; + }else{ + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ + /* do nothing now... */ + return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ + int bzerr; + FILE *fp; + + if (b==NULL) {return;} + fp = ((bzFile *)b)->handle; + if(((bzFile*)b)->writing){ + BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); + if(bzerr != BZ_OK){ + BZ2_bzWriteClose(NULL,b,1,NULL,NULL); + } + }else{ + BZ2_bzReadClose(&bzerr,b); + } + if(fp!=stdin && fp!=stdout){ + fclose(fp); + } +} + + +/*---------------------------------------------------*/ +/*-- + return last error code +--*/ +static const char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ + int err = ((bzFile *)b)->lastErr; + + if(err>0) err = 0; + *errnum = err; + return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/bzlib.h b/project/src/libbz2/bzlib.h new file mode 100644 index 0000000..9c72d2d --- /dev/null +++ b/project/src/libbz2/bzlib.h @@ -0,0 +1,282 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef + struct { + char *next_in; + unsigned int avail_in; + unsigned int total_in_lo32; + unsigned int total_in_hi32; + + char *next_out; + unsigned int avail_out; + unsigned int total_out_lo32; + unsigned int total_out_hi32; + + void *state; + + void *(*bzalloc)(void *,int,int); + void (*bzfree)(void *,void *); + void *opaque; + } + bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include +#endif + +#ifdef _WIN32 +# include +# ifdef small + /* windows.h define small to char */ +# undef small +# endif +# ifdef BZ_EXPORT +# define BZ_API(func) WINAPI func +# define BZ_EXTERN extern +# else + /* import windows dll dynamically */ +# define BZ_API(func) (WINAPI * func) +# define BZ_EXTERN +# endif +#else +# define BZ_API(func) func +# define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( + bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) ( + bz_stream* strm, + int action + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( + bz_stream *strm, + int verbosity, + int small + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( + bz_stream *strm + ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( + int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( + int* bzerror, + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( + int* bzerror, + BZFILE* b, + void** unused, + int* nUnused + ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( + int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 + ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity + ); + + +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( + void + ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( + const char *path, + const char *mode + ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( + int fd, + const char *mode + ); + +BZ_EXTERN int BZ_API(BZ2_bzread) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( + BZFILE* b + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( + BZFILE *b, + int *errnum + ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/bzlib_private.h b/project/src/libbz2/bzlib_private.h new file mode 100644 index 0000000..b0abc37 --- /dev/null +++ b/project/src/libbz2/bzlib_private.h @@ -0,0 +1,515 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +/****************************************/ +/***** This is an extension for WIT *****/ +/****************************************/ +#define _GNU_SOURCE 1 +/****************************************/ + +#include + +#ifndef BZ_NO_STDIO +#include +#include +#include +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION "1.0.6, 6-Sept-2010" + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__ /* */ +#endif + +#ifndef BZ_NO_STDIO + +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } + +#if BZ_DEBUG +#define AssertD(cond,msg) \ + { if (!(cond)) { \ + fprintf ( stderr, \ + "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ + exit(1); \ + }} +#else +#define AssertD(cond,msg) /* */ +#endif + +#define VPrintf0(zf) \ + fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ + fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ + fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ + fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ + fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ + fprintf(stderr,zf,za1,za2,za3,za4,za5) + +#else + +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) bz_internal_error ( errcode ); } +#define AssertD(cond,msg) do { } while (0) +#define VPrintf0(zf) do { } while (0) +#define VPrintf1(zf,za1) do { } while (0) +#define VPrintf2(zf,za1,za2) do { } while (0) +#define VPrintf3(zf,za1,za2,za3) do { } while (0) +#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0) +#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0) + +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS \ + Int32 rNToGo; \ + Int32 rTPos \ + +#define BZ_RAND_INIT_MASK \ + s->rNToGo = 0; \ + s->rTPos = 0 \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK \ + if (s->rNToGo == 0) { \ + s->rNToGo = BZ2_rNums[s->rTPos]; \ + s->rTPos++; \ + if (s->rTPos == 512) s->rTPos = 0; \ + } \ + s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(crcVar,cha) \ +{ \ + crcVar = (crcVar << 8) ^ \ + BZ2_crc32Table[(crcVar >> 24) ^ \ + ((UChar)cha)]; \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + Int32 mode; + Int32 state; + + /* remembers avail_in when flush/finish requested */ + UInt32 avail_in_expect; + + /* for doing the block sorting */ + UInt32* arr1; + UInt32* arr2; + UInt32* ftab; + Int32 origPtr; + + /* aliases for arr1 and arr2 */ + UInt32* ptr; + UChar* block; + UInt16* mtfv; + UChar* zbits; + + /* for deciding when to use the fallback sorting algorithm */ + Int32 workFactor; + + /* run-length-encoding of the input */ + UInt32 state_in_ch; + Int32 state_in_len; + BZ_RAND_DECLS; + + /* input and output limits and current posns */ + Int32 nblock; + Int32 nblockMAX; + Int32 numZ; + Int32 state_out_pos; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + UChar unseqToSeq[256]; + + /* the buffer for bit stream creation */ + UInt32 bsBuff; + Int32 bsLive; + + /* block and combined CRCs */ + UInt32 blockCRC; + UInt32 combinedCRC; + + /* misc administratium */ + Int32 verbosity; + Int32 blockNo; + Int32 blockSize100k; + + /* stuff for coding the MTF values */ + Int32 nMTF; + Int32 mtfFreq [BZ_MAX_ALPHA_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* second dimension: only 3 needed; 4 makes index calculations faster */ + UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4]; + + } + EState; + + + +/*-- externs for compression. --*/ + +extern void +BZ2_blockSort ( EState* ); + +extern void +BZ2_compressBlock ( EState*, Bool ); + +extern void +BZ2_bsInitWrite ( EState* ); + +extern void +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE 1 +#define BZ_X_OUTPUT 2 + +#define BZ_X_MAGIC_1 10 +#define BZ_X_MAGIC_2 11 +#define BZ_X_MAGIC_3 12 +#define BZ_X_MAGIC_4 13 +#define BZ_X_BLKHDR_1 14 +#define BZ_X_BLKHDR_2 15 +#define BZ_X_BLKHDR_3 16 +#define BZ_X_BLKHDR_4 17 +#define BZ_X_BLKHDR_5 18 +#define BZ_X_BLKHDR_6 19 +#define BZ_X_BCRC_1 20 +#define BZ_X_BCRC_2 21 +#define BZ_X_BCRC_3 22 +#define BZ_X_BCRC_4 23 +#define BZ_X_RANDBIT 24 +#define BZ_X_ORIGPTR_1 25 +#define BZ_X_ORIGPTR_2 26 +#define BZ_X_ORIGPTR_3 27 +#define BZ_X_MAPPING_1 28 +#define BZ_X_MAPPING_2 29 +#define BZ_X_SELECTOR_1 30 +#define BZ_X_SELECTOR_2 31 +#define BZ_X_SELECTOR_3 32 +#define BZ_X_CODING_1 33 +#define BZ_X_CODING_2 34 +#define BZ_X_CODING_3 35 +#define BZ_X_MTF_1 36 +#define BZ_X_MTF_2 37 +#define BZ_X_MTF_3 38 +#define BZ_X_MTF_4 39 +#define BZ_X_MTF_5 40 +#define BZ_X_MTF_6 41 +#define BZ_X_ENDHDR_2 42 +#define BZ_X_ENDHDR_3 43 +#define BZ_X_ENDHDR_4 44 +#define BZ_X_ENDHDR_5 45 +#define BZ_X_ENDHDR_6 46 +#define BZ_X_CCRC_1 47 +#define BZ_X_CCRC_2 48 +#define BZ_X_CCRC_3 49 +#define BZ_X_CCRC_4 50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* state indicator for this stream */ + Int32 state; + + /* for doing the final run-length decoding */ + UChar state_out_ch; + Int32 state_out_len; + Bool blockRandomised; + BZ_RAND_DECLS; + + /* the buffer for bit stream reading */ + UInt32 bsBuff; + Int32 bsLive; + + /* misc administratium */ + Int32 blockSize100k; + Bool smallDecompress; + Int32 currBlockNo; + Int32 verbosity; + + /* for undoing the Burrows-Wheeler transform */ + Int32 origPtr; + UInt32 tPos; + Int32 k0; + Int32 unzftab[256]; + Int32 nblock_used; + Int32 cftab[257]; + Int32 cftabCopy[257]; + + /* for undoing the Burrows-Wheeler transform (FAST) */ + UInt32 *tt; + + /* for undoing the Burrows-Wheeler transform (SMALL) */ + UInt16 *ll16; + UChar *ll4; + + /* stored and calculated CRCs */ + UInt32 storedBlockCRC; + UInt32 storedCombinedCRC; + UInt32 calculatedBlockCRC; + UInt32 calculatedCombinedCRC; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + Bool inUse16[16]; + UChar seqToUnseq[256]; + + /* for decoding the MTF values */ + UChar mtfa [MTFA_SIZE]; + Int32 mtfbase[256 / MTFL_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 minLens[BZ_N_GROUPS]; + + /* save area for scalars in the main decompress code */ + Int32 save_i; + Int32 save_j; + Int32 save_t; + Int32 save_alphaSize; + Int32 save_nGroups; + Int32 save_nSelectors; + Int32 save_EOB; + Int32 save_groupNo; + Int32 save_groupPos; + Int32 save_nextSym; + Int32 save_nblockMAX; + Int32 save_nblock; + Int32 save_es; + Int32 save_N; + Int32 save_curr; + Int32 save_zt; + Int32 save_zn; + Int32 save_zvec; + Int32 save_zj; + Int32 save_gSel; + Int32 save_gMinlen; + Int32* save_gLimit; + Int32* save_gBase; + Int32* save_gPerm; + + } + DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + s->tPos = s->tt[s->tPos]; \ + cccc = (UChar)(s->tPos & 0xff); \ + s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \ + c_tPos = c_tt[c_tPos]; \ + cccc = (UChar)(c_tPos & 0xff); \ + c_tPos >>= 8; + +#define SET_LL4(i,n) \ + { if (((i) & 0x1) == 0) \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \ + } + +#define GET_LL4(i) \ + ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n) \ + { s->ll16[i] = (UInt16)(n & 0x0000ffff); \ + SET_LL4(i, n >> 16); \ + } + +#define GET_LL(i) \ + (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \ + s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32 +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32 +BZ2_decompress ( DState* ); + +extern void +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, + Int32, Int32, Int32 ); + + +#endif + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/compress.c b/project/src/libbz2/compress.c new file mode 100644 index 0000000..81c18fa --- /dev/null +++ b/project/src/libbz2/compress.c @@ -0,0 +1,672 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- changed setting of nGroups in sendMTFValues() + so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/crctable.c b/project/src/libbz2/crctable.c new file mode 100644 index 0000000..868fc01 --- /dev/null +++ b/project/src/libbz2/crctable.c @@ -0,0 +1,104 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs ---*/ +/*--- crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*-- + I think this is an implementation of the AUTODIN-II, + Ethernet & FDDI 32-bit CRC standard. Vaguely derived + from code by Rob Warnock, in Section 51 of the + comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + + /*-- Ugly, innit? --*/ + + 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, + 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, + 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, + 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, + 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, + 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, + 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, + 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, + 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, + 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, + 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, + 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, + 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, + 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, + 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, + 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, + 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, + 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, + 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, + 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, + 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, + 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, + 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, + 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, + 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, + 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, + 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, + 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, + 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, + 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, + 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, + 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, + 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, + 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, + 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, + 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, + 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, + 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, + 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, + 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, + 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, + 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, + 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, + 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, + 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, + 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, + 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, + 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, + 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, + 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, + 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, + 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, + 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, + 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, + 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, + 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, + 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, + 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, + 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, + 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, + 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, + 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, + 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, + 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +}; + + +/*-------------------------------------------------------------*/ +/*--- end crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/decompress.c b/project/src/libbz2/decompress.c new file mode 100644 index 0000000..88065fe --- /dev/null +++ b/project/src/libbz2/decompress.c @@ -0,0 +1,646 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery ---*/ +/*--- decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->seqToUnseq[s->nInUse] = i; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr) \ + { retVal = rrr; goto save_state_and_return; }; + +#define GET_BITS(lll,vvv,nnn) \ + case lll: s->state = lll; \ + while (True) { \ + if (s->bsLive >= nnn) { \ + UInt32 v; \ + v = (s->bsBuff >> \ + (s->bsLive-nnn)) & ((1 << nnn)-1); \ + s->bsLive -= nnn; \ + vvv = v; \ + break; \ + } \ + if (s->strm->avail_in == 0) RETURN(BZ_OK); \ + s->bsBuff \ + = (s->bsBuff << 8) | \ + ((UInt32) \ + (*((UChar*)(s->strm->next_in)))); \ + s->bsLive += 8; \ + s->strm->next_in++; \ + s->strm->avail_in--; \ + s->strm->total_in_lo32++; \ + if (s->strm->total_in_lo32 == 0) \ + s->strm->total_in_hi32++; \ + } + +#define GET_UCHAR(lll,uuu) \ + GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu) \ + GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval) \ +{ \ + if (groupPos == 0) { \ + groupNo++; \ + if (groupNo >= nSelectors) \ + RETURN(BZ_DATA_ERROR); \ + groupPos = BZ_G_SIZE; \ + gSel = s->selector[groupNo]; \ + gMinlen = s->minLens[gSel]; \ + gLimit = &(s->limit[gSel][0]); \ + gPerm = &(s->perm[gSel][0]); \ + gBase = &(s->base[gSel][0]); \ + } \ + groupPos--; \ + zn = gMinlen; \ + GET_BITS(label1, zvec, zn); \ + while (1) { \ + if (zn > 20 /* the longest code */) \ + RETURN(BZ_DATA_ERROR); \ + if (zvec <= gLimit[zn]) break; \ + zn++; \ + GET_BIT(label2, zj); \ + zvec = (zvec << 1) | zj; \ + }; \ + if (zvec - gBase[zn] < 0 \ + || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \ + RETURN(BZ_DATA_ERROR); \ + lval = gPerm[zvec - gBase[zn]]; \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ + UChar uc; + Int32 retVal; + Int32 minLen, maxLen; + bz_stream* strm = s->strm; + + /* stuff that needs to be saved/restored */ + Int32 i; + Int32 j; + Int32 t; + Int32 alphaSize; + Int32 nGroups; + Int32 nSelectors; + Int32 EOB; + Int32 groupNo; + Int32 groupPos; + Int32 nextSym; + Int32 nblockMAX; + Int32 nblock; + Int32 es; + Int32 N; + Int32 curr; + Int32 zt; + Int32 zn; + Int32 zvec; + Int32 zj; + Int32 gSel; + Int32 gMinlen; + Int32* gLimit; + Int32* gBase; + Int32* gPerm; + + if (s->state == BZ_X_MAGIC_1) { + /*initialise the save area*/ + s->save_i = 0; + s->save_j = 0; + s->save_t = 0; + s->save_alphaSize = 0; + s->save_nGroups = 0; + s->save_nSelectors = 0; + s->save_EOB = 0; + s->save_groupNo = 0; + s->save_groupPos = 0; + s->save_nextSym = 0; + s->save_nblockMAX = 0; + s->save_nblock = 0; + s->save_es = 0; + s->save_N = 0; + s->save_curr = 0; + s->save_zt = 0; + s->save_zn = 0; + s->save_zvec = 0; + s->save_zj = 0; + s->save_gSel = 0; + s->save_gMinlen = 0; + s->save_gLimit = NULL; + s->save_gBase = NULL; + s->save_gPerm = NULL; + } + + /*restore from the save area*/ + i = s->save_i; + j = s->save_j; + t = s->save_t; + alphaSize = s->save_alphaSize; + nGroups = s->save_nGroups; + nSelectors = s->save_nSelectors; + EOB = s->save_EOB; + groupNo = s->save_groupNo; + groupPos = s->save_groupPos; + nextSym = s->save_nextSym; + nblockMAX = s->save_nblockMAX; + nblock = s->save_nblock; + es = s->save_es; + N = s->save_N; + curr = s->save_curr; + zt = s->save_zt; + zn = s->save_zn; + zvec = s->save_zvec; + zj = s->save_zj; + gSel = s->save_gSel; + gMinlen = s->save_gMinlen; + gLimit = s->save_gLimit; + gBase = s->save_gBase; + gPerm = s->save_gPerm; + + retVal = BZ_OK; + + switch (s->state) { + + GET_UCHAR(BZ_X_MAGIC_1, uc); + if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_2, uc); + if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_3, uc) + if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) + if (s->blockSize100k < (BZ_HDR_0 + 1) || + s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); + s->blockSize100k -= BZ_HDR_0; + + if (s->smallDecompress) { + s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); + s->ll4 = BZALLOC( + ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) + ); + if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); + } else { + s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); + if (s->tt == NULL) RETURN(BZ_MEM_ERROR); + } + + GET_UCHAR(BZ_X_BLKHDR_1, uc); + + if (uc == 0x17) goto endhdr_2; + if (uc != 0x31) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_2, uc); + if (uc != 0x41) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_3, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_4, uc); + if (uc != 0x26) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_5, uc); + if (uc != 0x53) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_6, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + + s->currBlockNo++; + if (s->verbosity >= 2) + VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo ); + + s->storedBlockCRC = 0; + GET_UCHAR(BZ_X_BCRC_1, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_2, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_3, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_4, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + + GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + + s->origPtr = 0; + GET_UCHAR(BZ_X_ORIGPTR_1, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_2, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_3, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + + if (s->origPtr < 0) + RETURN(BZ_DATA_ERROR); + if (s->origPtr > 10 + 100000*s->blockSize100k) + RETURN(BZ_DATA_ERROR); + + /*--- Receive the mapping table ---*/ + for (i = 0; i < 16; i++) { + GET_BIT(BZ_X_MAPPING_1, uc); + if (uc == 1) + s->inUse16[i] = True; else + s->inUse16[i] = False; + } + + for (i = 0; i < 256; i++) s->inUse[i] = False; + + for (i = 0; i < 16; i++) + if (s->inUse16[i]) + for (j = 0; j < 16; j++) { + GET_BIT(BZ_X_MAPPING_2, uc); + if (uc == 1) s->inUse[i * 16 + j] = True; + } + makeMaps_d ( s ); + if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); + alphaSize = s->nInUse+2; + + /*--- Now the selectors ---*/ + GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); + if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); + GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); + if (nSelectors < 1) RETURN(BZ_DATA_ERROR); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (True) { + GET_BIT(BZ_X_SELECTOR_3, uc); + if (uc == 0) break; + j++; + if (j >= nGroups) RETURN(BZ_DATA_ERROR); + } + s->selectorMtf[i] = j; + } + + /*--- Undo the MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], tmp, v; + for (v = 0; v < nGroups; v++) pos[v] = v; + + for (i = 0; i < nSelectors; i++) { + v = s->selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { pos[v] = pos[v-1]; v--; } + pos[0] = tmp; + s->selector[i] = tmp; + } + } + + /*--- Now the coding tables ---*/ + for (t = 0; t < nGroups; t++) { + GET_BITS(BZ_X_CODING_1, curr, 5); + for (i = 0; i < alphaSize; i++) { + while (True) { + if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); + GET_BIT(BZ_X_CODING_2, uc); + if (uc == 0) break; + GET_BIT(BZ_X_CODING_3, uc); + if (uc == 0) curr++; else curr--; + } + s->len[t][i] = curr; + } + } + + /*--- Create the Huffman decoding tables ---*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + BZ2_hbCreateDecodeTables ( + &(s->limit[t][0]), + &(s->base[t][0]), + &(s->perm[t][0]), + &(s->len[t][0]), + minLen, maxLen, alphaSize + ); + s->minLens[t] = minLen; + } + + /*--- Now the MTF values ---*/ + + EOB = s->nInUse+1; + nblockMAX = 100000 * s->blockSize100k; + groupNo = -1; + groupPos = 0; + + for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + + /*-- MTF init --*/ + { + Int32 ii, jj, kk; + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + /*-- end MTF init --*/ + + nblock = 0; + GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + + while (True) { + + if (nextSym == EOB) break; + + if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + + es = -1; + N = 1; + do { + /* Check that N doesn't get too big, so that es doesn't + go negative. The maximum value that can be + RUNA/RUNB encoded is equal to the block size (post + the initial RLE), viz, 900k, so bounding N at 2 + million should guard against overflow without + rejecting any legitimate inputs. */ + if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR); + if (nextSym == BZ_RUNA) es = es + (0+1) * N; else + if (nextSym == BZ_RUNB) es = es + (1+1) * N; + N = N * 2; + GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); + } + while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + + es++; + uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; + s->unzftab[uc] += es; + + if (s->smallDecompress) + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->ll16[nblock] = (UInt16)uc; + nblock++; + es--; + } + else + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->tt[nblock] = (UInt32)uc; + nblock++; + es--; + }; + + continue; + + } else { + + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + + /*-- uc = MTF ( nextSym-1 ) --*/ + { + Int32 ii, jj, kk, pp, lno, off; + UInt32 nn; + nn = (UInt32)(nextSym - 1); + + if (nn < MTFL_SIZE) { + /* avoid general-case expense */ + pp = s->mtfbase[0]; + uc = s->mtfa[pp+nn]; + while (nn > 3) { + Int32 z = pp+nn; + s->mtfa[(z) ] = s->mtfa[(z)-1]; + s->mtfa[(z)-1] = s->mtfa[(z)-2]; + s->mtfa[(z)-2] = s->mtfa[(z)-3]; + s->mtfa[(z)-3] = s->mtfa[(z)-4]; + nn -= 4; + } + while (nn > 0) { + s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; + }; + s->mtfa[pp] = uc; + } else { + /* general case */ + lno = nn / MTFL_SIZE; + off = nn % MTFL_SIZE; + pp = s->mtfbase[lno] + off; + uc = s->mtfa[pp]; + while (pp > s->mtfbase[lno]) { + s->mtfa[pp] = s->mtfa[pp-1]; pp--; + }; + s->mtfbase[lno]++; + while (lno > 0) { + s->mtfbase[lno]--; + s->mtfa[s->mtfbase[lno]] + = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; + lno--; + } + s->mtfbase[0]--; + s->mtfa[s->mtfbase[0]] = uc; + if (s->mtfbase[0] == 0) { + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + } + } + /*-- end uc = MTF ( nextSym-1 ) --*/ + + s->unzftab[s->seqToUnseq[uc]]++; + if (s->smallDecompress) + s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else + s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]); + nblock++; + + GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); + continue; + } + } + + /* Now we know what nblock is, we can do a better sanity + check on s->origPtr. + */ + if (s->origPtr < 0 || s->origPtr >= nblock) + RETURN(BZ_DATA_ERROR); + + /*-- Set up cftab to facilitate generation of T^(-1) --*/ + /* Check: unzftab entries in range. */ + for (i = 0; i <= 255; i++) { + if (s->unzftab[i] < 0 || s->unzftab[i] > nblock) + RETURN(BZ_DATA_ERROR); + } + /* Actually generate cftab. */ + s->cftab[0] = 0; + for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; + for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + /* Check: cftab entries in range. */ + for (i = 0; i <= 256; i++) { + if (s->cftab[i] < 0 || s->cftab[i] > nblock) { + /* s->cftab[i] can legitimately be == nblock */ + RETURN(BZ_DATA_ERROR); + } + } + /* Check: cftab entries non-descending. */ + for (i = 1; i <= 256; i++) { + if (s->cftab[i-1] > s->cftab[i]) { + RETURN(BZ_DATA_ERROR); + } + } + + s->state_out_len = 0; + s->state_out_ch = 0; + BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); + s->state = BZ_X_OUTPUT; + if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + + if (s->smallDecompress) { + + /*-- Make a copy of cftab, used in generation of T --*/ + for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + + /*-- compute the T vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->ll16[i]); + SET_LL(i, s->cftabCopy[uc]); + s->cftabCopy[uc]++; + } + + /*-- Compute T^(-1) by pointer reversal on T --*/ + i = s->origPtr; + j = GET_LL(i); + do { + Int32 tmp = GET_LL(j); + SET_LL(j, i); + i = j; + j = tmp; + } + while (i != s->origPtr); + + s->tPos = s->origPtr; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_SMALL(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } else { + + /*-- compute the T^(-1) vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->tt[i] & 0xff); + s->tt[s->cftab[uc]] |= (i << 8); + s->cftab[uc]++; + } + + s->tPos = s->tt[s->origPtr] >> 8; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_FAST(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_FAST(s->k0); s->nblock_used++; + } + + } + + RETURN(BZ_OK); + + + + endhdr_2: + + GET_UCHAR(BZ_X_ENDHDR_2, uc); + if (uc != 0x72) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_3, uc); + if (uc != 0x45) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_4, uc); + if (uc != 0x38) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_5, uc); + if (uc != 0x50) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_6, uc); + if (uc != 0x90) RETURN(BZ_DATA_ERROR); + + s->storedCombinedCRC = 0; + GET_UCHAR(BZ_X_CCRC_1, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_2, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_3, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_4, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + + s->state = BZ_X_IDLE; + RETURN(BZ_STREAM_END); + + default: AssertH ( False, 4001 ); + } + + AssertH ( False, 4002 ); + + save_state_and_return: + + s->save_i = i; + s->save_j = j; + s->save_t = t; + s->save_alphaSize = alphaSize; + s->save_nGroups = nGroups; + s->save_nSelectors = nSelectors; + s->save_EOB = EOB; + s->save_groupNo = groupNo; + s->save_groupPos = groupPos; + s->save_nextSym = nextSym; + s->save_nblockMAX = nblockMAX; + s->save_nblock = nblock; + s->save_es = es; + s->save_N = N; + s->save_curr = curr; + s->save_zt = zt; + s->save_zn = zn; + s->save_zvec = zvec; + s->save_zj = zj; + s->save_gSel = gSel; + s->save_gMinlen = gMinlen; + s->save_gLimit = gLimit; + s->save_gBase = gBase; + s->save_gPerm = gPerm; + + return retVal; +} + + +/*-------------------------------------------------------------*/ +/*--- end decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/huffman.c b/project/src/libbz2/huffman.c new file mode 100644 index 0000000..d56abeb --- /dev/null +++ b/project/src/libbz2/huffman.c @@ -0,0 +1,205 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + Int32 zz, tmp; \ + zz = z; tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + +#define DOWNHEAP(z) \ +{ \ + Int32 zz, yy, tmp; \ + zz = z; tmp = heap[zz]; \ + while (True) { \ + yy = zz << 1; \ + if (yy > nHeap) break; \ + if (yy < nHeap && \ + weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len, + Int32 *freq, + Int32 alphaSize, + Int32 maxLen ) +{ + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + Int32 nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ]; + Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; + Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (True) { + + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { k = parent[k]; j++; } + len[i-1] = j; + if (j > maxLen) tooLong = True; + } + + if (! tooLong) break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimum effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) + if (length[i] == n) { code[i] = vec; vec++; }; + vec <<= 1; + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, + Int32 *base, + Int32 *perm, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + for (j = 0; j < alphaSize; j++) + if (length[j] == i) { perm[pp] = j; pp++; }; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; + for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + + for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i+1] - base[i]); + limit[i] = vec-1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/libbz2/randtable.c b/project/src/libbz2/randtable.c new file mode 100644 index 0000000..90c5bd1 --- /dev/null +++ b/project/src/libbz2/randtable.c @@ -0,0 +1,84 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks ---*/ +/*--- randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/project/src/system.h b/project/src/system.h new file mode 100644 index 0000000..375c75d --- /dev/null +++ b/project/src/system.h @@ -0,0 +1,88 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_SYSTEM_H +#define SZS_SYSTEM_H 1 + +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumSystemID +{ + SYSID_UNKNOWN = 0x00000000, + SYSID_I386 = 0x01000000, + SYSID_X86_64 = 0x02000000, + SYSID_CYGWIN = 0x03000000, + SYSID_APPLE = 0x04000000, + SYSID_LINUX = 0x05000000, + SYSID_UNIX = 0x06000000, + +} enumSystemID; + +/////////////////////////////////////////////////////////////////////////////// + +#undef SYSTEM_LINUX + +#ifdef __CYGWIN__ + #define SYSTEM "cygwin" + #define SYSTEMID SYSID_CYGWIN +#elif __APPLE__ + #define SYSTEM "mac" + #define SYSTEMID SYSID_APPLE +#elif __linux__ + #define SYSTEM_LINUX 1 + #ifdef __i386__ + #define SYSTEM "i386" + #define SYSTEMID SYSID_I386 + #elif __x86_64__ + #define SYSTEM "x86_64" + #define SYSTEMID SYSID_X86_64 + #else + #define SYSTEM "linux" + #define SYSTEMID SYSID_LINUX + #endif +#elif __unix__ + #define SYSTEM "unix" + #define SYSTEMID SYSID_UNIX +#else + #define SYSTEM "unknown" + #define SYSTEMID SYSID_UNKNOWN +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_SYSTEM_H 1 + diff --git a/project/src/types.h b/project/src/types.h new file mode 100644 index 0000000..74b43b9 --- /dev/null +++ b/project/src/types.h @@ -0,0 +1,62 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_TYPES_H +#define SZS_TYPES_H 1 + +#include "dclib-types.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// constants /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// maximum characters for a subfile in an archive +#define ARCH_FILE_MAX 2000 + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// types /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_TYPES_H + diff --git a/project/src/ui/gen-ui.c b/project/src/ui/gen-ui.c new file mode 100644 index 0000000..823595c --- /dev/null +++ b/project/src/ui/gen-ui.c @@ -0,0 +1,185 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include + +#include "lib-std.h" +#include "lib-bmg.h" +#include "lib-ctcode.h" +#include "dclib-basics.h" +#include "dclib-xdump.h" +#include "dclib-debug.h" +#include "ui.h" // [[dclib]] wrapper +#include "dclib-ui.h" +#include "version.h" + +#include "dclib-gen-ui.h" +#include "ui-head.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// functions not neded by gen-ui /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool DefineIntVar ( VarMap_t * vm, ccp varname, int value ) { return false; } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// some helper macros /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define UI_TO_TEXT(x) UI_TO_TEXT1(x) +#define UI_TO_TEXT1(x) #x + +#define ARCHIVES_OR "SZS, U8, PACK, BRRES, BREFF, BREFT or RARC" +#define ARCHIVES_AND "SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" +#define ARCHIVES_COMMA "SZS, U8, PACK, BRRES, BREFF, BREFT, RARC" + +#define MAX_GAMEMODES "1573" + +#define NO_HEAD_SYNTAX "Suppress the syntax information section in decoded text files." + +#define PATCH_BMG_TEXT(x) \ + "This option specifies a BMG patch mode." \ + " Some of the modes need a parameter or a file name of a BMG patch file" \ + " (raw or text BMG), both separated by an equal sign." \ + " Modes with required file names are @PRINT, REPLACE, INSERT," \ + " OVERWRITE, DELETE, MASK, EQUAL@ and @NOTEQUAL@." \ + " Modes with text parameter are @FORMAT@, @REGEXP@ and @RM-REGEXP@. " \ + " Standalone modes are @ID, ID-ALL, UNICODE, RM-ESCAPES, RM-CUPS," \ + " CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY, LE-FILL," \ + " X-COPY, X-FORCE-COPY@ and @X-FILL@." \ + " Unique abbreviations are allowed." \ + "\n " \ + " The optional condition @COND@ is either '@?MID@' or '@!MID@'." \ + " In case of '@?MID@', the patch is only applied" \ + " if the message id MID already exists." \ + " In case of '@!MID@', the patch is only applied" \ + " if the message id MID does not exists." \ + "\n " \ + " If this option is used multiple times," \ + " all patch files will be processed in the entered order." \ + x \ + "\1 Read https://szs.wiimm.de/opt/patch-bmg for more details." + +#define BMG_BRIEF_TEXT \ + "If set, the information header in decoded text files" \ + " is suppressed (for historical reasons same as {--no-header})." \ + " If set at least twice, all comments are suppressed" \ + " and the output is packed without empty lines." \ + " If set 3 times, the @#BMG@ file indentification is also suppressed." + +#define BMG_LONG_TEXT \ + "Print long numeric message IDs instead of" \ + " alternative message names like Txx, Uxx or Mxx." + +#define OPT_POINTS \ + "defines a new table for the points assigned to players after" \ + " a versus or room race." \ + " The parameter is a blank or comma and slash (next row) separated list" \ + " with numbers (points) between 0 and 255 and the following keywords:" \ + " @NINTENDO, LINEAR, WIN, WIN15, WIN25@ to select a predefined table;" \ + " @NO-BONUS, BONUS, NULL, ONE@ as options;" \ + " @N1..N12@ to select the row for N players." \ + "\1 See https://szs.wiimm.de/opt/points for details." + +#define OPT_CT_DIR(x) \ + "Define a search directory for CT-CODE parts and use the internal" \ + " copies only, if no valid file is found." \ + " Each file is searched without and then with extension '.bz2'." \ + " For both cases bzip2 files are detected and decompressed." \ + " Only files with correct file type are accepted." \ + x \ + "\n " \ + " Use this option multiple times to define more than one search directory." + +#define FUNCTION_BRIEF \ + "Suppress the output of the description and print only function type and syntax." + +#define FUNCTION_LONG \ + "Usually only the function syntax is compared to the keywords." \ + " But if --long is set, the descriptions are compared too." + +#define CT_CODE_SOURCE \ + "BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN" + +#define DEPRECATED "[DEPRECATED] " + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// the info table /////////////// +/////////////////////////////////////////////////////////////////////////////// + +info_t info_tab[] = +{ + #include "tab-wszst.inc" + #include "tab-wbmgt.inc" + #include "tab-wctct.inc" + #include "tab-wimgt.inc" + #include "tab-wkclt.inc" + #include "tab-wkmpt.inc" + #include "tab-wlect.inc" + #include "tab-wmdlt.inc" + #include "tab-wpatt.inc" + #include "tab-wstrt.inc" + + { T_END, 0,0,0,0 } +}; + +#include "dclib-gen-ui.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int main ( int argc, char ** argv ) +{ + return ui_main(argc,argv); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/tab-wbmgt.inc b/project/src/ui/tab-wbmgt.inc new file mode 100644 index 0000000..86eb2ba --- /dev/null +++ b/project/src/ui/tab-wbmgt.inc @@ -0,0 +1,605 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wbmgt ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wbmgt", 0, + "wbmgt [option]... command [option|parameter|@file]...", + WBMGT_LONG " :" + " Convert and patch raw and text BMG (message) files." }, + + // + //---------- list of all wbmgt commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wbmgt VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wbmgt HELP [--width=wd] OPT|OPTION ...\n" + "wbmgt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wbmgt HELP [--width=wd] command [-b] [ignored]...\n" + "wbmgt HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wbmgt ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wbmgt TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wbmgt COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wbmgt ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wbmgt FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wbmgt FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "POINTS", "POINTS|PTS", + "wbmgt POINTS [--points=list]", + "Print a table with the points assigned to player after" + " a versus or room race." }, + + { T_DEF_CMD, "REGEXP", "REGEXP|RE", + "wbmgt REGEXP pattern string...", + "Test implementation of regular expressions." + " PATTERN is the regular expression to find matches." + " Each STRING is object of the match and replace operation." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "EXTRACT", "EXTRACT", + "wbmgt EXTRACT name source", + "Extract BMG section NAME of file SOURCE and write" + " the binary data to standard output." + " If NAME consists of less than 4 characters, then the first" + " section that starts with these characters is dumped." }, + + { T_DEF_CMD, "SECTIONS", "SECTIONS", + "wbmgt SECTIONS [source]...", + "List all sections of each binary BMG file." + " Print offset, size, magic and a short info." + " If known, print number and size of elements too." }, + + { T_DEF_CMD, "LIST", "LIST|LS|LL", + "wbmgt LIST [source]...", + "List messages to proof the content." + " Patches are applied before listing." + " The lines are cut to fit the terminal width." }, + + { T_DEF_CMD, "SLOTS", "SLOTS", + "wbmgt SLOTS [source]...", + "Print a slot ordered list with slot number, message id," + " delta and attributes. Patches are applied before listing." }, + + { T_DEF_CMD, "DIFF", "DIFF|CMP", + "wbmgt DIFF source1 source2\n" + "wbmgt DIFF source... --dest dest\n", + "Compare two or more BMG (raw or text) files" + " on string level and report mismatches for each string." + " If option {--dest} is set, then each source file" + " is compared with a file in the destination." + " Patches are applied to all sources before comparing." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "CAT", "CAT", + "wbmgt CAT [source]...", + "Read the entered BMG files (text, binary or szs sub file) and print" + " them as decoded text files to standard output." + " All BMG files of a single archive (SZS,...) are combined to 1 source." + " The output of all source files is concatenated." }, + + { T_DEF_CMD, "MIX", "MIX", + "wbmgt MIX [source]...", + "Read the entered BMG files (text, binary or szs sub file) and print" + " them as decoded text files to standard output." + " All BMG files of all archives (SZS,...) are read." + " The output of all source files is combined" + " and strings of later source files override prior defined strings." }, + + { T_DEF_CMD, "IDENTIFIER", "IDENTIFIER|ID|IDENTIFY", + "wbmgt IDENTIFIER", + "Create a simple text BMG with the identifier message @3DEF@ only" + " with respect to options {--le-code} and {--ct-code}." + " Print the result to standard output." }, + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wbmgt DECODE [szs]...", + "Read the entered message files and decode them to text files." + " All BMG files of a single archive (SZS,...) are concatenated to 1 source." + " This command is similar to {PATCH}," + " but the result is always decoded." + " The default destination is @'%P/%N.txt'@." }, + + { T_DEF_CMD, "ENCODE", "ENCODE|ENC", + "wbmgt ENCODE [source]...", + "Read the entered message files and encode them to BMG files." + " All BMG files of a single archive (SZS,...) are concatenated to 1 source." + " This command is similar to {PATCH}," + " but the result is always encoded." + " The default destination is @'%P/%N.bmg'@." }, + + { T_DEF_CMD, "PATCH", "PATCH|PAT", + "wbmgt PATCH [source]...", + "Patch the entered files by using the patch list" + " specified by option {--patch-bmg}." + " The coding of the source files is not changed." + " The default destination is @'%P/%F'@." + "\n " + " Use {wszst patch --patch-bmg ...} to patch BMG files within a SZS file."}, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + // + //---------- list of all wbmgt options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, BMG_LONG_TEXT }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, "Suppress the syntax information section in BMG text files." }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, BMG_BRIEF_TEXT }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TRACKS", "T|tracks", + 0, + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "ARENAS", "A|arenas", + 0, + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "POINTS", "points|pts", + "list", + "This option creates the new message 3FF0 to hold" + " a description of the versus points table." + " It " OPT_POINTS }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ALIGN", "align", + "size", + "Define an align value for the sub sections in the archive." + " The value must be a power of 2 and the default value is 32 (0x20)" }, + + { T_OPT_CMP, "PATCH_BMG", "P|patch-bmg|patchbmg|pb|patch", + "mode[cond][=param]", + PATCH_BMG_TEXT(" @--patch@ is a short cut.") }, + + { T_OPT_CMP, "MACRO_BMG", "macro-bmg|macrobmg|mb", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CMP, "FILTER_BMG", "filter-bmg|filterbmg|fb|msg", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "BMG_ENCODING", "encoding|bmg-encoding|bmgencoding", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "BMG_INF_SIZE", "inf-size|infsize|bmg-inf-size|bmginfsize", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CO, "BMG_MID", "bmg-mid|bmgmid", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "FORCE_ATTRIB", "force-attrib|forceattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEF_ATTRIB", "def-attrib|defattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ATTRIB", "no-attrib|noattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "X_ESCAPES", "x-escapes|xescapes", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OLD_ESCAPES", "old-escapes|oldescapes", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "SINGLE_LINE", "1|single-line|singleline", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_BMG_COLORS","no-bmg-colors|nobmgcolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BMG_COLORS", "bmg-colors|bmgcolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_BMG_INLINE","no-bmg-inline|nobmginline", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "EXPORT", "X|export", + 0, + "Enable the export mode and create small and machine readable text files" + " for easy post processing." + " The option works similar like @-HBl11@ for BMG text files." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + //---------- wbmgt GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + // + //---------- wbmgt GROUP TEXTOUT ---------- + + { T_GRP_BEG, "TEXTOUT", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0,0 }, + { T_COPT, "BMG_ENCODING", 0,0,0 }, + { T_COPT, "BMG_INF_SIZE", 0,0,0 }, + { T_COPT, "BMG_MID", 0,0,0 }, + { T_COPT, "FORCE_ATTRIB", 0,0,0 }, + { T_COPT, "DEF_ATTRIB", 0,0,0 }, + { T_COPT, "NO_ATTRIB", 0,0,0 }, + { T_COPT, "X_ESCAPES", 0,0,0 }, + { T_COPT, "OLD_ESCAPES", 0,0,0 }, + { T_COPT, "SINGLE_LINE", 0,0,0 }, + { T_COPT, "NO_BMG_COLORS",0,0,0 }, + { T_COPT, "BMG_COLORS", 0,0,0 }, + { T_COPT, "NO_BMG_INLINE",0,0,0 }, + { T_COPT, "LONG", 0,0,0 }, + { T_COPT, "EXPORT", 0,0,0 }, + + // + //---------- COMMAND wbmgt VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wbmgt HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wbmgt ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wbmgt TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wbmgt COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wbmgt ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wbmgt FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wbmgt FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wbmgt POINTS ---------- + + { T_CMD_BEG, "POINTS", 0,0,0 }, + { T_COPT, "POINTS", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Print the table as comma separated lists in C notation." }, + { T_COPT, "LONG", 0,0, + "Append a string in {--points} format that defines the table." + " If set twice, append up to 3 strings in different modes." }, + + //---------- COMMAND wbmgt REGEXP ---------- + + { T_CMD_BEG, "REGEXP", 0,0,0 }, + + //---------- COMMAND wbmgt EXTRACT ---------- + + { T_CMD_BEG, "EXTRACT", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Exclude the section header from output." }, + + //---------- COMMAND wbmgt SECTIONS ---------- + + { T_CMD_BEG, "SECTIONS", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wbmgt LIST ---------- + + { T_CMD_BEG, "LIST", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "WIDTH", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + + //---------- COMMAND wbmgt SLOTS ---------- + + { T_CMD_BEG, "SLOTS", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "WIDTH", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + + //---------- COMMAND wbmgt DIFF ---------- + + { T_CMD_BEG, "DIFF", 0,0,0 }, + { T_COPT, "WIDTH", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + + //---------- COMMAND wbmgt CAT ---------- + + { T_CMD_BEG, "CAT", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + + //---------- COMMAND wbmgt MIX ---------- + + { T_CMD_BEG, "MIX", 0,0,0 }, + { T_COPY_CMD, "CAT", 0,0,0 }, + + //---------- COMMAND wbmgt IDENTIFIER ---------- + + { T_CMD_BEG, "IDENTIFIER", 0,0,0 }, + { T_COPT, "LE_CODE", 0,0,0 }, + { T_COPT, "CT_CODE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + + //---------- COMMAND wbmgt DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + + //---------- COMMAND wbmgt ENCODE ---------- + + { T_CMD_BEG, "ENCODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "BMG_ENCODING", 0,0,0 }, + { T_COPT, "BMG_INF_SIZE", 0,0,0 }, + { T_COPT, "BMG_MID", 0,0,0 }, + { T_COPT, "FORCE_ATTRIB", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + { T_COPT, "ALIGN", 0,0,0 }, + + //---------- COMMAND wbmgt PATCH ---------- + + { T_CMD_BEG, "PATCH", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + { T_COPT, "ALIGN", 0,0,0 }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wctct.inc b/project/src/ui/tab-wctct.inc new file mode 100644 index 0000000..1a4c166 --- /dev/null +++ b/project/src/ui/tab-wctct.inc @@ -0,0 +1,780 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wctct ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wctct", 0, + "wctct [option]... command [option|parameter|@file]...", + WCTCT_LONG " :" + " Manage the $CT-CODE$ extension." + " " CT_CODE_SOURCE " files are accepted as input." }, + + // + //---------- list of all wctct commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wctct VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wctct HELP [--width=wd] OPT|OPTION ...\n" + "wctct HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wctct HELP [--width=wd] command [-b] [ignored]...\n" + "wctct HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wctct ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wctct TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wctct COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wctct ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wctct FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wctct FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "SYMBOLS", "SYMBOLS", + "wctct SYMBOLS", + "Print predefined symbols of text parser to standard output." + " The CT-CODE specific symbols are included." }, + + { T_DEF_CMD, "FUNCTIONS", "FUNCTIONS", + "wctct FUNCTIONS [key]...", + "Print a list with all parser functions including" + " the CT-CODE specific functions." + " If one key ore more key entered," + " then only functions that match at least one key are printed." }, + + { T_DEF_CMD, "CALCULATE", "CALCULATE", + "wctct CALCULATE expression...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FLOAT", "FLOAT", + "wctct FLOAT number...", + 0 /* copy of wszst */ }, + + { H_DEF_CMD, "EXPORT", "_EXPORT", + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output." + " Use keyword @HELP@ to get a list of possible keywords." }, + + { H_DEF_CMD, "RAWDUMP", "_RAWDUMP", + "wszst _RAWDUMP [-d|-D] destdir", + "This hidden command creates the destination directory " + " and create several files as copy of internal data." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "DUMP", "D|DUMP", + "wctct DUMP [source]...", + "Print an information dump for the CT-CODE of each source file." + " " CT_CODE_SOURCE " are accepted as input." }, + + { T_DEF_CMD, "CAT", "CAT", + "wctct CAT [source]...", + "Read the source files and print the CT-CODE part" + " them as decoded text files to standard output." + " The output of all source files is concatenated." + " " CT_CODE_SOURCE " are accepted as input." }, + + { T_DEF_CMD, "BMG", "BMG", + "wctct BMG [source]...", + "Read the source files and print a BMG message listing to standard output." + " " CT_CODE_SOURCE " are accepted as input." }, + + { T_DEF_CMD, "TRACKS", "TRACKS", + "wctct TRACKS [source]...", + "Read the source files and print a track listing to standard output." + " " CT_CODE_SOURCE " are accepted as input." }, + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wctct DECODE [source]...", + "Read the source files and decode them to CT-TEXT files." + " The default destination is @'%P/%N.txt'@." + " " CT_CODE_SOURCE " are accepted as input." }, + + { T_DEF_CMD, "CREATE", "CREATE|C", + "wctct CREATE keyword [source]...", + "Read the source files, scan the CT-CODE part" + " and create an output file controlled by the keyword." + " BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN files are accepted as input." + " Possible keywords are: @CUP1, CRS1, r-MOD1, r-MOD2, r-OVR1," + " r-BAD1CODE, r-BAD1DATA (=r-CODE), r-TEX0, r-BRRES, r-SZS," + " BMG, TRACKS, LIST, REF@ and @FULL@." + " 'r' defines the region and is one of" + " @EU, RMCP, US, RMCE, JP, RMCJ, KO@ or @RMCK@." + "\n " + " The Korean support needs external CT-CODE files." + " Use option {--ct-dir} to define at least one search path." }, + + { T_DEF_CMD, "PATCH", "PATCH|P", + "wctct PATCH [source]...", + "Read each source file, find and scan the CT-CODE part" + " and patch it." + " " CT_CODE_SOURCE " are accepted as input." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + // + //---------- list of all wctct options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "OLD_SPINY", "old-spiny|oldspiny", + 0, + "Disable the new feature to hold a Spiny Shell at the tail." + "\1 https://szs.wiimm.de/opt/ctcode for details." }, + + { T_OPT_GP, "CRS1", "crs1", + "value", + "Store this value into all 8 members of the property array" + " of the CRS1 header if reading a CT-TEXT." + " The default value is 0x800." }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "ROUND", "round", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, + "Suppress cross reference comments." }, + + { T_OPT_C, "RAW", "raw", + 0, + "Print BMG messages as UTF-8 text without escape sequences." }, + + { T_OPT_CP, "BMG_ENCODING", "bmg-encoding|bmgencoding", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "BMG_INF_SIZE", "bmg-inf-size|bmginfsize", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CO, "BMG_MID", "bmg-mid|bmgmid", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "FORCE_ATTRIB", "force-attrib|forceattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEF_ATTRIB", "def-attrib|defattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ATTRIB", "no-attrib|noattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "X_ESCAPES", "x-escapes|xescapes", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OLD_ESCAPES", "old-escapes|oldescapes", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "SINGLE_LINE", "1|single-line|singleline", + 0, + "If set, don't print continuation lines for BMG text output." + " If set twice, print only single text lines but not value lines." }, + + { T_OPT_C, "NO_BMG_COLORS","no-bmg-colors|nobmgcolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BMG_COLORS", "bmg-colors|bmgcolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_BMG_INLINE","no-bmg-inline|nobmginline", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "EXPORT", "X|export", + 0, + "Enable the export mode and create small and machine readable text files" + " for easy post processing." + " The option works similar like @-HB@ for CT-CODE output." }, + + { T_OPT_C, "NO_PARAM", "P|no-param|noparam", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ECHO", "no-echo|noecho", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_CHECK", "N|no-check|nocheck", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "TRACKS", "T|tracks", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "ARENAS", "A|arenas", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "LIST", "list", + 0, + "Cancel all previous {--rel} and {--full}" + " and enter LIST mode (default) for text output." + "\n " + " In LIST mode only a track list is generated." + " On scanning the tracks are automatically distributed to the cups." + " This makes rearanging very simple." }, + + { T_OPT_C, "REF", "ref", + 0, + "Cancel all previous {--list} and {--full}" + " and enter REFERENCE mode for text output." + "\n " + " In REFERENCE mode all tracks and cups are printed separately." + " After scanning the cup and track layout is the same" + " as the original code file." }, + + { T_OPT_C, "FULL", "full", + 0, + "Cancel all previous {--list} and {--rel}" + " and enter FULL mode for text output." + "\n " + " FULL mode is a combination of LIST and REF modes." }, + + { T_OPT_C, "HEX", "hex", + 0, + "Normally and if available, names are printed for music and property" + " slots. If this option is set, hex numbers are printed always." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GP, "CT_DIR", "ct-dir|ctdir", + "dir", + OPT_CT_DIR("\n wctct will search in the sub directory @'rmc[pujk]/'@" \ + " for files @'mod1.bin', 'mod2.bin', 'ovr1.bin'@" \ + " and @'bad1code.bin'@.") }, + + { T_OPT_GO, "CT_LOG", "ct-log|ctlog", + "[=level]", + "Define a log level for option {--ct-dir}." + " Without parameter, the current level is increased." + " Allowed keywords are: 0=0FF, 1=SUCCESS, 2=FOUND, 3=ALL" }, + + { T_OPT_GP, "ALLOW_SLOTS", "allow-slots|allowslots", + "list", + "Allow and disallow slots for automatic slot assigning." + " A comma separated list of slots (A), slot ranges (A:B) or keywords" + " is expected. If preceded by a minus sign, the slots are disabled." + " The keywords are: @TRACKS@ (slots 0:0x1f), @ARENAS@ (0x20:0x29)" + " and @SPECIAL@ (0x36:0x3a)." + " @CTCODE@ is an abbreviation for @ARENAS,SPECIAL@." + "\n " + " The default is to enable slot ranges" + " @0x00:0x1f@, @0x2a:0x35@, @0x3b:0x41@ and @0x44:0xfe@." }, + + { T_OPT_CP, "IMAGES", "images", + "dir", + "If creating a CT-CODE BRRES or SZS file," + " then search the 8 strap images in the specified directory." + " The images are searched with extensions .png, .tex, .tex0" + " and without extension in this order until a valid image is found." + " All images will be resized to the correct width and height " + " and converted to format TEX.RGB565 without mipmaps." + " If no valid image is found, a hard coded default image is used." }, + + { T_OPT_CMP, "LOAD_BMG", "load-bmg|loadbmg", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CMP, "PATCH_BMG", "patch-bmg|patchbmg|pb", + "mode[cond][=param]", + PATCH_BMG_TEXT("") }, + + { T_OPT_CMP, "MACRO_BMG", "macro-bmg|macrobmg|mb", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CMP, "FILTER_BMG", "filter-bmg|filterbmg|fb", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PATCH_NAMES", "patch-names|patchnames", + 0, + "If set, the names of the CUP1 and CSR1 sections are patched " + " by the loaded BMG files directly after loading the source files." }, + + { T_OPT_CP, "ORDER_BY", "order-by|orderby|order", + "bmg", + "Load the BMG file and filter track names." + " Then before creating output files," + " re-distribute the tracks of the additonal cups (9-)" + " to the same cups by the order of the read track names." + " Tracks without BMG reference are placed at the end of all tracks." + " This option modifes only the track order of the cups," + " but will not change any name." }, + + { T_OPT_C, "ORDER_ALL", "order-all|orderall", + 0, + "Used together with option {--order-by}:" + " Re-distribute the tracks of all cups beginning with Mushroom Cup." }, + + { H_OPT_C, "DYNAMIC", "dynamic", + 0, + "Enable dynamic length calculation for the @CTGP_CODE@ part." + " The surrounding TEX and BRRES files are also adjusted." + " The usage of this option is EXPERIMENTAL." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "WRITE_TRACKS", "write-tracks|writetracks|wtracks", + "file", + "Create a machine readable file reflecting" + " the racing track distribution to the cups." + " One line of format @'IDX|MID|NAME'@ is written for each track." + " --wtracks is a short cut." }, + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + //---------- wctct GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "WRITE_TRACKS", 0,0,0 }, + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + //---------- wctct GROUP PATCH ---------- + + { T_GRP_BEG, "PATCH", 0,0,0 }, + + { T_COPT, "LOAD_BMG", 0,0,0 }, + { T_COPT, "PATCH_BMG", 0,0,0 }, + { T_COPT, "MACRO_BMG", 0,0,0 }, + { T_COPT, "FILTER_BMG", 0,0,0 }, + { T_COPT, "PATCH_NAMES", 0,0,0 }, + { T_COPT, "ORDER_BY", 0,0,0 }, + { T_COPT, "ORDER_ALL", 0,0,0 }, + //{ T_COPT, "TRACKS", 0,0,0 }, + //{ T_COPT, "ARENAS", 0,0,0 }, + + //---------- wctct GROUP TEXTOPT ---------- + + { T_GRP_BEG, "TEXTOPT", 0,0,0 }, + + { T_COPT, "HEX", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Suppress the syntax information section in CTC text files." }, + { T_COPT, "BRIEF", 0,0,0 }, + { T_COPT, "EXPORT", 0,0,0 }, + + //---------- wctct GROUP TEXTOUT ---------- + + { T_GRP_BEG, "TEXTOUT", 0,0,0 }, + + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "LIST", 0,0,0 }, + { T_COPT, "REF", 0,0,0 }, + { T_COPT, "FULL", 0,0,0 }, + { T_COPY_GRP, "TEXTOPT", 0,0,0 }, + + // + //---------- COMMAND wctct VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wctct HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wctct ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wctct TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wctct COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wctct ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wctct FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wctct FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wctct SYMBOLS ---------- + + { T_CMD_BEG, "SYMBOLS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wctct FUNCTIONS ---------- + + { T_CMD_BEG, "FUNCTIONS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, FUNCTION_BRIEF }, + { T_COPT, "LONG", 0,0, FUNCTION_LONG }, + + //---------- COMMAND wctct CALCULATE ---------- + + { T_CMD_BEG, "CALCULATE", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + + //---------- COMMAND wctct FLOAT ---------- + + { T_CMD_BEG, "FLOAT", 0,0,0 }, + { T_COPT, "ROUND", 0,0,0 }, + + //---------- COMMAND wctct _EXPORT ---------- + + { T_CMD_BEG, "EXPORT", 0,0,0 }, + + //---------- COMMAND wctct _RAWDUMP ---------- + + { T_CMD_BEG, "RAWDUMP", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + + //---------- COMMAND wctct DUMP ---------- + + { T_CMD_BEG, "DUMP", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print headers as hexdump." }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wctct BMG ---------- + + { T_CMD_BEG, "BMG", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Filter the message by tracks, that are used a racing track." + " If set twice, suppress also tracks created by the 'N' command" + " of CT-TEXT files." + " If set 3 times, suppress always the output of first 32 tracks." }, + { T_COPT, "LONG", 0,0, + "Normally, only the track and arena names are exported." + " The message id (MID) range is 4000-40FF." + " If set, cup names are exported too as MID 4200-423F." + " If set twice, cup/track relations are also exported as MID 4300-43FF."}, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "BMG_ENCODING", 0,0,0 }, + { T_COPT, "BMG_INF_SIZE", 0,0,0 }, + { T_COPT, "BMG_MID", 0,0,0 }, + { T_COPT, "FORCE_ATTRIB", 0,0,0 }, + { T_COPT, "DEF_ATTRIB", 0,0,0 }, + { T_COPT, "NO_ATTRIB", 0,0,0 }, + { T_COPT, "X_ESCAPES", 0,0,0 }, + { T_COPT, "OLD_ESCAPES", 0,0,0 }, + { T_COPT, "SINGLE_LINE", 0,0,0 }, + { T_COPT, "NO_BMG_COLORS",0,0,0 }, + { T_COPT, "BMG_COLORS", 0,0,0 }, + { T_COPT, "NO_BMG_INLINE",0,0,0 }, + { T_COPT, "WRITE_TRACKS", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wctct TRACKS ---------- + + { T_CMD_BEG, "TRACKS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Suppress the table header." }, + { T_COPT, "BRIEF", 0,0, + "Suppress the output of the music and property slots." }, + { T_COPT, "LONG", 0,0, + "Include the first 32 standard tracks into the output." + " If set twice, also include the battle arenas." }, + { T_COPT, "RAW", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "WRITE_TRACKS", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wctct CAT ---------- + + { T_CMD_BEG, "CAT", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "WRITE_TRACKS", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wctct DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wctct CREATE ---------- + + { T_CMD_BEG, "CREATE", 0,0,0 }, + + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "TEXTOPT", 0,0,0 }, + { T_COPT, "BMG_ENCODING", 0,0,0 }, + { T_COPT, "BMG_INF_SIZE", 0,0,0 }, + { T_COPT, "BMG_MID", 0,0,0 }, + { T_COPT, "FORCE_ATTRIB", 0,0,0 }, + { T_COPT, "DEF_ATTRIB", 0,0,0 }, + { T_COPT, "NO_ATTRIB", 0,0,0 }, + { T_COPT, "X_ESCAPES", 0,0,0 }, + { T_COPT, "OLD_ESCAPES", 0,0,0 }, + { T_COPT, "SINGLE_LINE", 0,0,0 }, + { T_COPT, "NO_BMG_COLORS",0,0,0 }, + { T_COPT, "BMG_COLORS", 0,0,0 }, + { T_COPT, "NO_BMG_INLINE",0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "BMG: Filter the message by tracks, that are used a racing track." + " If set twice, suppress also tracks created by the 'N' command" + " of CT-TEXT files." + " If set 3 times, suppress always the output of first 32 tracks." }, + { T_COPT, "LONG", 0,0, + "BMG: Normally, only the track and arena names are exported." + " The message id (MID) range is 4000-40FF." + " If set, cup names are exported too as MID 4200-423F." + " If set twice, cup/track relations are also exported as MID 4300-43FF."}, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "CT_DIR", 0,0,0 }, + { T_COPT, "CT_LOG", 0,0,0 }, + { T_COPT, "ALLOW_SLOTS", 0,0,0 }, + { T_COPT, "IMAGES", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + { H_COPT, "DYNAMIC", 0,0,0 }, + + //---------- COMMAND wctct PATCH ---------- + + { T_CMD_BEG, "PATCH", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wimgt.inc b/project/src/ui/tab-wimgt.inc new file mode 100644 index 0000000..2d6286a --- /dev/null +++ b/project/src/ui/tab-wimgt.inc @@ -0,0 +1,497 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wimgt ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wimgt", 0, + "wimgt [option]... command [option|parameter|@file]...", + WIMGT_LONG " :" + " Extract and convert graphic images." + " The file formats TPL, TEX, BTI, BREFT and PNG are supported." }, + + // + //---------- list of all wimgt commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wimgt VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wimgt HELP [--width=wd] OPT|OPTION ...\n" + "wimgt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wimgt HELP [--width=wd] command [-b] [ignored]...\n" + "wimgt HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wimgt ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wimgt TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wimgt COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wimgt ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wimgt FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wimgt FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "LIST", "LIST|LS", + "wimgt LIST [source]...", + "List images files and print file and image type." }, + + { T_DEF_CMD, "LIST_L", "LIST-L|LL|LISTL", + "wit LIST-L [source]...", + "List images files and print file and image type" + " and geometry data." + " 'LIST-L' is a short cut for {LIST --long}." }, + + { T_DEF_CMD, "LIST_LL", "LIST-LL|LLL|LISTLL", + "wit LIST-LL [source]...", + "List images files and print file and image type" + " and advanced geometry data." + " 'LIST-LL' is a short cut for {LIST --long --long}." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wimgt DECODE [source]...", + "Decode (export) the entered image files into PNG files." + " The default destination is @'%P/%F.png'@." }, + + { T_DEF_CMD, "ENCODE", "ENCODE|ENC", + "wimgt ENCODE [source]...", + "Encode (import) the entered PNG files and convert them to" + " to an internal image format (TPL, TEX, BREFT)." + " The default destination is @'%P/%N'@ (removed extension)." }, + + { T_DEF_CMD, "CONVERT", "CONVERT|CV", + "wimgt CONVERT [source]...", + "Convert the entered image files into new file and image formats." + " The old file is replaced by the new one." + " The difference to {ENCODE} is only the default file name handling." }, + + { T_DEF_CMD, "COPY", "COPY|CP", + "wimgt COPY source dest\n" + "wimgt COPY source... [-d|-D] dest", + "Copy and convert an image from 'source' to 'dest'." + " Multiple sources can be used, if 'dest' is a directory" + " or contains at least one %-pattern." + "\n " + " This is an alternative for the ENCODE and DECODE commands." + " The default destination file format is selected by scanning the" + " file extension of the destination." + " Option --transform may override this." + " Option --overwrite is set implicitly." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + // + //---------- list of all wimgt options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, + "Create smaller output." }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "TRACKS", "T|tracks", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "ARENAS", "A|arenas", + 0, + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "ALL", "a|all", + 0, + "Convert all files and not only the files with changed file" + " or image formats." }, + + { T_OPT_C, "MIPMAPS", "mipmaps", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_MIPMAPS", "no-mipmaps|nomipmaps|no-mm|nomm", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "N_MIPMAPS", "n-mipmaps|nmipmaps|n-mm|nmm", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "MAX_MIPMAPS", "max-mipmaps|maxmipmaps|max-mm|maxmm", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "MIPMAP_SIZE", "mipmap-size|mipmapsize|mm-size|mmsize", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "FAST_MIPMAPS", "fast-mipmaps|fastmipmaps|fast-mm|fastmm", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "CMPR_DEFAULT", "cmpr-default", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRE_CONVERT", "pre-convert|preconvert", + 0, + "Convert the image before the image format for the destination file" + " is selected. This results in a perhaps worser graphic" + " and is only useful to control graphic conversions." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CMP, "PATCH", "P|patch", + "mode1.mode2...=file", + "This option specifies patch mode list and a file," + " separated by an equal sign." + " Mode is a point separated list of the keywords:" + " @TOP@ (@T@), @BOTTOM@ (@B@), @VCENTER@ (@V@), @INS-TOP@ (@IT@)" + " and @INS-BOTTOM@ (@IB@) for the vertical alignment," + " @LEFT@ (@L@), @RIGHT@ (@R@), @HCENTER@ (@H@), @INS-LEFT@ (@IL@)" + " and @INS-RIGHT@ (@IR@) for the horizontal alignment," + " @COPY@ (@CP@), @BACKGROUND@ (@BG@), @FOREGROUND@ (@FG@)" + " and @MIX@ (@MX@) as color calculation mode and" + " @SHRINK@ (@SH@), @GROW@ (@GR@) and @LEAVE@ (@LV@)" + " as image size mode." + "\n " + " Abbreviations are allowed." + " The default is @VCENTER.HCENTER.MIX.LEAVE@" + " or in short form @V.H.MX.LV@." + " If this option is used multiple times all patch files" + " will be used in the entered order." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "TRANSFORM", "x|transform", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "STRIP", "strip", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + //---------- wimgt GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + //---------- wimgt GROUP CONVERT ---------- + + { T_GRP_BEG, "TRANSFORM", 0,0,0 }, + + { T_COPT, "N_MIPMAPS", 0,0,0 }, + { T_COPT, "MAX_MIPMAPS", 0,0,0 }, + { T_COPT, "MIPMAP_SIZE", 0,0,0 }, + { T_COPT, "PRE_CONVERT", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "PATCH", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "MIPMAPS", 0,0,0 }, + { T_COPT, "NO_MIPMAPS", 0,0,0 }, + { T_COPT, "FAST_MIPMAPS", 0,0,0 }, + { T_COPT, "CMPR_DEFAULT", 0,0,0 }, + { T_COPT, "TRANSFORM", 0,0,0 }, + { T_COPT, "STRIP", 0,0,0 }, + + // + //---------- COMMAND wimgt VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wimgt HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wimgt ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wimgt TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wimgt COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wimgt ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wimgt FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wimgt FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wimgt LIST ---------- + + { T_CMD_BEG, "LIST", 0,0,0 }, + { T_COPT_M, "LONG", 0,0, + "If set, geometry data of the image is printed." + " If set twice, advanced geometry data is printed." }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wszst LIST-L ---------- + + { T_CMD_BEG, "LIST_L", 0,0,0 }, + { T_COPY_CMD, "LIST", 0,0,0 }, + + //---------- COMMAND wszst LIST-LL ---------- + + { T_CMD_BEG, "LIST_LL", 0,0,0 }, + { T_COPY_CMD, "LIST_L", 0,0,0 }, + + //---------- COMMAND wimgt DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wimgt ENCODE ---------- + + { T_CMD_BEG, "ENCODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "ALL", 0,0, + "If source and destination file are the same," + " the file is only written on modifications (transform, patch, ...)." + " If --all is set, then the file is always written." }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wimgt CONVERT ---------- + + { T_CMD_BEG, "CONVERT", 0,0,0 }, + { T_COPY_CMD, "ENCODE", 0,0,0 }, + + //---------- COMMAND wimgt COPY ---------- + + { T_CMD_BEG, "COPY", 0,0,0 }, + { T_COPY_CMD, "ENCODE", 0,0,0 }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wkclt.inc b/project/src/ui/tab-wkclt.inc new file mode 100644 index 0000000..04476f2 --- /dev/null +++ b/project/src/ui/tab-wkclt.inc @@ -0,0 +1,793 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wkclt ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wkclt", 0, + "wkclt [option]... command [option|parameter|@file]...", + WKCLT_LONG " :" + " Analyze, modify, export and create KCL and OBJ files." }, + + // + //---------- list of all wkclt commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wkclt VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wkclt HELP [--width=wd] OPT|OPTION ...\n" + "wkclt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wkclt HELP [--width=wd] command [-b] [ignored]...\n" + "wkclt HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wkclt ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wkclt TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wkclt COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wkclt ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wkclt FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wkclt FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "SYMBOLS", "SYMBOLS", + "wkclt SYMBOLS", + "Print predefined symbols of text parser to standard output." + " The KCL specific symbols are included." }, + + { T_DEF_CMD, "FUNCTIONS", "FUNCTIONS", + "wkclt FUNCTIONS [key]...", + "Print a list with all parser functions including the KCL specific functions." + " If one key ore more key entered," + " then only functions that match at least one key are printed." }, + + { T_DEF_CMD, "CALCULATE", "CALCULATE", + "wkclt CALCULATE expression...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "MATRIX", "MATRIX", + "wkclt MATRIX [patch-option]... [vector]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FLOAT", "FLOAT", + "wkclt FLOAT number...", + 0 /* copy of wszst */ }, + + { H_DEF_CMD, "EXPORT", "_EXPORT", + "wkclt _EXPORT key...", + "This hidden command exports some definitions to standard output." + " Use keyword @HELP@ to get a list of possible keywords." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + + { T_DEF_CMD, "CAT", "CAT", + "wkclt CAT source...", + "Read the entered KCL and OBJ files and print" + " them as OBJ files to standard output." + " The output of all source files is concatenated." + "\n " + " Automated dropping of bad KCL triangles is enabled" + " if no other drop option is specified by option {--kcl}." }, + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wkclt DECODE source...", + "Read the entered KCL and OBJ files" + " and export them to OBJ files." + " The default destination is @'%P/%N.txt'@." + "\n " + " Automated dropping of bad KCL triangles is enabled" + " if no other drop option is specified by option {--kcl}." }, + + { T_DEF_CMD, "ENCODE", "ENCODE|ENC", + "wkclt ENCODE source...", + "Read the entered KCL and OBJ files" + " and encode (compile) them to binary KCL files." + " The default destination is @'%P/%N.kcl'@." + "\n " + " See https://szs.wiimm.de/doc/kcl for encoding details." }, + + { T_DEF_CMD, "COPY", "COPY|CP", + "wkclt COPY source dest\n" + "wkclt COPY source... [-d|-D] dest", + "Copy and convert a collision file from 'source' to 'dest'." + " Multiple KCL and OBJ sources can be used," + " if 'dest' is a directory or contains at least one %-pattern." + "\n " + " This is an alternative for the DECODE and ENCODE commands." + " The default destination file format is selected by scanning the" + " file extension of the destination." + " Is it '.kcl', a binary KCL file is created." + " Is it '.txt' or '.obj', an OBJ file is created." + " Otherwise the output format is the same as the source." + "\n " + " Automated dropping of bad KCL triangles is enabled" + " if no other drop option is specified and the destination" + " is an OBJ file." }, + + { T_DEF_CMD, "CFF", "CFF", + "wkclt CFF source...", + "CFF is an abbreviation of 'create flag file' and" + " will create a flag file template for each entered source file." + " If the flag file already exists, only missing group names" + " are added at the end of file." }, + + { T_DEF_CMD, "FLAGS", "FLAGS|F", + "wkclt FLAGS source...", + "Print a KCL type and flag usage statistic for each source" + " to standard output." }, + + { T_DEF_CMD, "DUMP", "DUMP|D", + "wkclt DUMP source...", + "This debugging command prints an information dump about" + " the internal triangle data base and the octree" + " in human and machine readable text format." + " The output of all source files is concatenated" + " and printed to standard output." }, + + { T_DEF_CMD, "DBRIEF", "DBRIEF|DB", + "wkclt DBRIEF source...", + "Command @DBRIEF@ is a short cut for {DUMP --brief}." + " The output of the triangle list is suppressed" + " and only the header data and statistics are printed." }, + + { T_DEF_CMD, "LIST", "LIST", + "wkclt LIST source...", + "This debugging command prints a list of all triangles" + " with metrics in human and machine readable text format." + " It is like DUMP, but reduced to the list of triangles." + " The output of all source files is concatenated" + " and printed to standard output." }, + + { T_DEF_CMD, "TRIANGLES", "TRIANGLES", + "wkclt TRIANGLES source...", + "This debugging commands prints one data line for each triangle." + " The goal is to compare data. Therefor and to create smaller lines" + " the precision of the floating points numbers is reduced." + "\n " + " The print order is: flag used length | 3 points | 4 normals." + " For each point and normal 3 coordinates are printed." }, + + { T_DEF_CMD, "TRAVERSE", "TRAVERSE", + "wkclt TRAVERSE source point...", + "For each point, the octree of the source file is traversed" + " to find and print the related triangles." + " A point is either a comma separated list with 3 parser" + " expressions (one for each coordinate) or a vector expression." }, + + { T_DEF_CMD, "FALL", "FALL", + "wkclt FALL source point...", + "For each entered point, the lowest point is searched," + " that can be reached without collision." + " The point is expanded to a cube with edge length of 100.0" + " for the collision tests." }, + + { T_DEF_CMD, "CHECK", "CHECK|CHK", + "wkclt CHECK source...", + "Read the entered KCL files (text or binary) and" + " and make a plausibility check." + " Hints and warnings are written to standard output." + " The exit status is @'DIFFER'@ if at least one KMP warning found." }, + + { H_DEF_CMD, "ANALYZE", "ANALYZE", + "wkclt ANALYZE source...", + "Analyze some KCL data." }, + + { T_DEF_CMD, "BLOW", "BLOW", + "wkclt BLOW source...", + "Try to calculate the used blow size (KCL_BLOW) of a KCL." + " Print one line for each source file." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + // + //---------- list of all wkclt options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SCALE", "scale", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SHIFT", "shift", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "XSS", "xss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "YSS", "yss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ZSS", "zss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ROT", "rot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "XROT", "xrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "YROT", "yrot|hrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ZROT", "zrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "TRANSLATE", "translate", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NULL", "null", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NEXT", "next", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ASCALE", "ascale", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "AROT", "arot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "TFORM_SCRIPT", "tform-script|tformscript", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "KCL", "kcl", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "KCL_FLAG", "kcl-flag|kclflag", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "KCL_SCRIPT", "kcl-script|kclscript", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TRI_AREA", "tri-area|triarea", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TRI_HEIGHT", "tri-height|triheight|tri-ht|triht|kcl-min|kclmin", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "FLAG_FILE", "flag-file|flagfile", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GO, "XTRIDATA", "xtridata|xtri", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SLOT", "slot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "ROUND", "round", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_PARAM", "P|no-param|noparam", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ECHO", "no-echo|noecho", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_CHECK", "N|no-check|nocheck", + 0, + "Do not make plausibility checks for KCL files." }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "TRACKS", "T|tracks", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "ARENAS", "A|arenas", + 0, + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TINY", "tiny", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + // + //---------- wkclt GROUP TRANSFORM ---------- + + { T_GRP_BEG, "TRANSFORM", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + { T_COPT, "SCALE", 0,0,0 }, + { T_COPT, "SHIFT", 0,0,0 }, + { T_COPT, "XSS", 0,0,0 }, + { T_COPT, "YSS", 0,0,0 }, + { T_COPT, "ZSS", 0,0,0 }, + { T_COPT, "ROT", 0,0,0 }, + { T_COPT, "XROT", 0,0,0 }, + { T_COPT, "YROT", 0,0,0 }, + { T_COPT, "ZROT", 0,0,0 }, + { T_COPT, "TRANSLATE", 0,0,0 }, + { T_COPT, "NULL", 0,0,0 }, + { T_COPT, "NEXT", 0,0,0 }, + { T_COPT, "ASCALE", 0,0,0 }, + { T_COPT, "AROT", 0,0,0 }, + { T_COPT, "TFORM_SCRIPT", 0,0,0 }, + { T_COPT, "KCL", 0,0,0 }, + { T_COPT, "KCL_FLAG", 0,0,0 }, + { T_COPT, "KCL_SCRIPT", 0,0,0 }, + { T_COPT, "TRI_AREA", 0,0,0 }, + { T_COPT, "TRI_HEIGHT", 0,0,0 }, + { T_COPT, "FLAG_FILE", 0,0,0 }, + { T_COPT, "SLOT", 0,0,0 }, + + //---------- wkclt GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + // + //---------- wkclt GROUP TEXTOUT ---------- + + { T_GRP_BEG, "TEXTOUT", 0,0,0 }, + + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Suppress the syntax information section in KCL text files." }, + { T_COPT, "BRIEF", 0,0,0 }, + + // + //---------- COMMAND wkclt VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wkclt HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wkclt ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wkclt TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_COPT, "VERBOSE", 0,0, + "Print the matrices of transformations." }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wkclt COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wkclt ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wkclt FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wkclt FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wkclt SYMBOLS ---------- + + { T_CMD_BEG, "SYMBOLS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wkclt FUNCTIONS ---------- + + { T_CMD_BEG, "FUNCTIONS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, FUNCTION_BRIEF }, + { T_COPT, "LONG", 0,0, FUNCTION_LONG }, + + //---------- COMMAND wkclt CALCULATE ---------- + + { T_CMD_BEG, "CALCULATE", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + + //---------- COMMAND wkclt MATRIX ---------- + + { T_CMD_BEG, "MATRIX", 0,0,0 }, + + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "If set once, suppress vector dumps for transformation steps" + " and ignore option --long." + " If set twice, suppress vector dump for final transformation too." }, + { T_COPT, "LONG", 0,0, + "If set once, print inverse matrix for the final transformation." + " If set twice, print inverse matrix for all transformations." }, + { T_COPT, "QUIET", 0,0, + "Option --quiet is dedicated to automatic tests comparing" + " the old and new transformation algorithms." + " If set once, the tables are only printed, if the results differ." + " If set twice, nothing is printed at all" + " and only the exit status will inform about the results." }, + + //---------- COMMAND wkclt FLOAT ---------- + + { T_CMD_BEG, "FLOAT", 0,0,0 }, + { T_COPT, "ROUND", 0,0,0 }, + + //---------- COMMAND wkclt EXPORT ---------- + + { T_CMD_BEG, "EXPORT", 0,0,0 }, + + //---------- COMMAND wkclt CAT ---------- + + { T_CMD_BEG, "CAT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt ENCODE ---------- + + { T_CMD_BEG, "ENCODE", 0,0,0 }, + { T_COPY_CMD, "DECODE", 0,0,0 }, + + //---------- COMMAND wkclt COPY ---------- + + { T_CMD_BEG, "COPY", 0,0,0 }, + { T_COPY_CMD, "ENCODE", 0,0,0 }, + + //---------- COMMAND wkclt CFF ---------- + + { T_CMD_BEG, "CFF", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Force usage of @'f(typ,variant)'@." + " If set twice, force usage of @'a(typ,w,x,y,z)'@." }, + { T_COPT, "BRIEF", 0,0, + "Suppress the comments with the alternative expression formats." }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "CONST", 0,0,0 }, + { T_COPT, "FLAG_FILE", 0,0,0 }, + + //---------- COMMAND wkclt FLAGS ---------- + + { T_CMD_BEG, "FLAGS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Suppress the output of detailed flag list" + " and print only the type summary." }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt DUMP ---------- + + { T_CMD_BEG, "DUMP", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Suppress the output of the triangle list" + " and print only the header data and statistics." }, + { T_COPT, "LONG", 0,0, + "If set, append an octree dump to the output." + " If set twice, add a triangle usage list behind the octree dump." }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt DBRIEF ---------- + + { T_CMD_BEG, "DBRIEF", 0,0,0 }, + { T_COPY_CMD, "DUMP", 0,0,0 }, + + //---------- COMMAND wkclt LIST ---------- + + { T_CMD_BEG, "LIST", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt TRIANGLES ---------- + + { T_CMD_BEG, "TRIANGLES", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Suppress the output of the 'normals' data." }, + { T_COPT, "LONG", 0,0, + "For each occurrence of option --long increase the precision of" + " the floating points numbers by adding 1 fractional digit," + " but only up to additionally 5 digits." }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt TRAVERSE ---------- + + { T_CMD_BEG, "TRAVERSE", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Print the 0-based triangle index instead of the triangle coordinates." }, + { T_COPT, "LONG", 0,0, + "If set, print a calculation log. If set twice," + " print a calculation log for each octree traversing step." }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt FALL ---------- + + { T_CMD_BEG, "FALL", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Print only the height (y coordinate) of the result." }, + { T_COPT, "LONG", 0,0, + "If set, print a calculation log." + " If set twice, be more verbose." }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt CHECK ---------- + + { T_CMD_BEG, "CHECK", 0,0,0 }, + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Print only warnings and suppress hints." }, + { T_COPT, "LONG", 0,0, + "Be verbose and print more statistics." }, + { T_COPT, "QUIET", 0,0, + "Be quiet until the first warning or hint is found." }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wkclt ANALYZE ---------- + + { T_CMD_BEG, "ANALYZE", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkclt BLOW ---------- + + { T_CMD_BEG, "BLOW", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wkmpt.inc b/project/src/ui/tab-wkmpt.inc new file mode 100644 index 0000000..97070b7 --- /dev/null +++ b/project/src/ui/tab-wkmpt.inc @@ -0,0 +1,932 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wkmpt ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wkmpt", 0, + "wkmpt [option]... command [option|parameter|@file]...", + WKMPT_LONG " :" + " Analyze, modify, export and create (compile) KMP files." }, + + // + //---------- list of all wkmpt commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wkmpt VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wkmpt HELP [--width=wd] OPT|OPTION ...\n" + "wkmpt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wkmpt HELP [--width=wd] command [-b] [ignored]...\n" + "wkmpt HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wkmpt ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wkmpt TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wkmpt COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wkmpt ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wkmpt FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wkmpt FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "SYMBOLS", "SYMBOLS", + "wkmpt SYMBOLS", + "Print predefined symbols of text parser to standard output." + " The KMP specific symbols are included." }, + + { T_DEF_CMD, "FUNCTIONS", "FUNCTIONS", + "wkmpt FUNCTIONS [key]...", + "Print a list with all parser functions including the KMP specific functions." + " If one key ore more key entered," + " then only functions that match at least one key are printed." }, + + { T_DEF_CMD, "CALCULATE", "CALCULATE", + "wkmpt CALCULATE expression...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "MATRIX", "MATRIX", + "wkmpt MATRIX [patch-option]... [vector]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FLOAT", "FLOAT", + "wkmpt FLOAT number...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "STARTPOS", "STARTPOS|SP", + "wkmpt STARTPOS [patch-option]... key_or_file...", + "Print an ASCII image or a table of the start positions." + " The parameter list is a wild mix of filenames, keywords and numbers." + "\n " + " If a parameter contains a slash ('/' or '\\')," + " it is a filename of a KMP or SZS file and a KMP is loaded." + " The KMP is used to print absolute positions and to find" + " the settings for @LEFT/RIGHT@ and @WIDE/NARROW@ automatically." + "\n" + " A parameter may also be one of the following keywords" + " to define a setting:" + " @RESET, IMAGE, TABLE, RELATIVE, ABSOLUTE, LEFT, RIGHT, WIDE, NARROW," + " AUTO, NO-AUTO, DECIMAL, HEXADECIMAL, Z0, Z1, Z2, Z3@." + " A table or image is only printed on numbers 1 to 12." + "\1\n See https://szs.wiimm.de/cmd/wkmpt/startpos for details." }, + + { T_DEF_CMD, "OBJECTS", "OBJECTS|OBJ", + "wkmpt OBJECTS key...", + "Infos about all objects, that match any of the entered keys, are printed." + " A key match, if the text is part of the object name, file list" + " or info (ignoring case). If a key is a number (decimal or hex)," + " the object with that id matches too." + " This command uses an internal object and file database." + "\1 An online query is also available:" + " https://szs.wiimm.de/cgi/mkw/object" }, + + { H_DEF_CMD, "EXPORT", "_EXPORT", + "wkmpt _EXPORT key...", + 0 /* copy of wszst */ }, + + { H_DEF_CMD, "XEXPORT", "_XEXPORT", + "wkmpt _XEXPORT", + "Export some tables for LE-CODE." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "CAT", "CAT", + "wkmpt CAT [source]...", + "Read the entered KMP files (text, binary or szs) and print" + " them as decoded text files to standard output." + " The output of all source files is concatenated." }, + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wkmpt DECODE [source]...", + "Read the entered KMP files (text or binary)" + " and decode them to text files." + " The default destination is @'%P/%N.txt'@." }, + + { T_DEF_CMD, "ENCODE", "ENCODE|ENC", + "wkmpt ENCODE source...", + "Read the entered KMP files (text or binary)" + " and encode (compile) them to binary files." + " The default destination is @'%P/%N.kmp'@." }, + + { T_DEF_CMD, "DIFF", "DIFF|CMP", + "wkmpt DIFF source1 source2\n" + "wkmpt DIFF source... --dest dest", + "Compare the KMP of two or more sources (KMP, KMP-text or SZS)." + " Each KMP will be converted into the internal format," + " and then fixed and patched controlled by options." + " Only the known sections are compared." + " If option SORT (see {--diff}) is set," + " elements of section GOBJ are sorted by object-id." + "\n " + " This command is in development. So for the section @POTI@" + " only a simple notice about the different data record is printed." + "\n " + " If option {--dest} is set, then each source file" + " is compared with a file in the destination." }, + + { T_DEF_CMD, "DRAW", "DRAW|D|COBJECT|COB", + "wkmpt DRAW source...", + "Read the entered KMP files (text or binary)," + " of needed load the related KCL file," + " draw KMP objects and create an OBJ file." + " The default destination is @'%P/%N.kmp.obj'@." + "\n " + " Automated dropping of bad KCL triangles is enabled" + " if no drop option is specified by option {--kcl}." + }, + + { T_DEF_CMD, "CHECK", "CHECK|CHK", + "wkmpt CHECK source...", + "Read the entered KMP files (text or binary) and" + " and make a plausibility check." + " Hints and warnings are written to standard output." + " The exit status is @'DIFFER'@ if at least one KMP warning found." }, + + { T_DEF_CMD, "STGI", "STGI", + "wkmpt STGI file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "KTPT", "KTPT", + "wkmpt KTPT [source]...", + "Analyse the start positions (KMP:KTPT) and lap counters" + " (KMP:CKPT with mode 0) and print a simple information list." + " Use enemy points (KMP:ENPT) to find recommendations." + "\n " + " This command is dedicated to LE-CODE, which supports an" + " alternative position for the finish line at the minimap." }, + + { T_DEF_CMD, "ROUTES", "ROUTES", + "wkmpt ROUTES file...", + "Print the internal cross references for routes of KMP sections" + " CKPH, CKPT, ENPH, ENPT, ITPH and ITPT." }, + + { H_DEF_CMD, "GOBJ", "GOBJ", + "wkmpt GOBJ [source]...", + "Analyze the objects of KMP section GOBJ" + " and print a status line for each object. ..." }, + + { T_DEF_CMD, "GAMEMODES", "GAMEMODES|GMODES", + "wkmpt GAMEMODES [source]...", + "Analyze the presence flags combinations" + " with and without extension support and print a list." + " The lists consists of index, scenario version and scenario name." + " Scenario versions are only equal, if the scenarios are duplicates." + " Duplicates happens only, if {--gamemodes} option TT or FULL is set." }, + + { T_DEF_CMD, "WIM0", "WIM0|W0", + "wkmpt WIM0 [source]...", + "Analyze the WIM0 section of each KMP file (text, binary or szs)" + " and print an info about it." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + // + //---------- list of all wkmpt options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SCALE", "scale", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SHIFT", "shift", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "XSS", "xss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "YSS", "yss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ZSS", "zss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ROT", "rot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "XROT", "xrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "YROT", "yrot|hrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ZROT", "zrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "YPOS", "ypos", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "TRANSLATE", "translate", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NULL", "null", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NEXT", "next", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ASCALE", "ascale", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "AROT", "arot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "TFORM_SCRIPT", "tform-script|tformscript", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "RM_GOBJ", "rm-gobj|rmgobj", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "BATTLE", "battle", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "EXPORT_FLAGS", "export-flags|exportflags|exflags", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "ROUTE_OPTIONS", "route-options|routeoptions|ropt", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "WIM0", "wim0", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SLOT", "slot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DRAW", "w|draw", + "list", + "The parameter is a comma separated list of keywords to select" + " the elements to draw." + " Allowed keywords are: @CKPT, CJGPT, JGPT, KTPT, ENPT, ITPT," + " POTI, CNPT, ITEMBOXES, COINS, ROADOBJECTS, SOLIDOBJECTS, DECORATION," + " BLACK, WHITE, KCL, NONE@ and @ALL@ (default);" + " the classes @POINTS, OBJECTS@ and @RESPAWN@;" + " the presence-flag selectors @[1-3]OFFLINE@ and @[1-3]ONLINE@;" + " the special options @DETAILED, DISPATCH@ and @WARNINGS@." + " If a keyword is prefixed by a minus sign, the option is disabled." + " Option @KCL@ decides, if the reference KCL is included in the output." + " If @DETAILED@ is disabled, an output with less faces is created." + " If @DISPATCH@ is enabled, the settings of dispatch points are visualized." + " If @WARNINGS@ is enabled, some objects are drawn in red." + "\1 See https://szs.wiimm.de/opt/draw for details." }, + + { T_OPT_CP, "POS_MODE", "pos-mode|posmode", + "list", + "Define a comma separated list of modes for the next {--pos-file} options." + " Each element of the list begins with a keyword followed by" + " a column index and/or a parameter list:" + " @X, Y, Z, D, C, T," + " FA=, FB=, FC=, @" + "\1\n See https://szs.wiimm.de/opt/pos-mode for details." }, + + { T_OPT_CP, "POS_FILE", "pos-file|posfile", + "file", + "Load a position file created by mkw-ana and" + " print the positions into the map." + " Define the modes first," + " because the last defined modes set by {--pos-mode} are used!" }, + + { T_OPT_CP, "PNG", "png", + "pix_size ['a' align] ['x' x1[:x2]] ['y' y1[:y2]] ['t' types]", + "Draw a PNG with a top view of the track instead of an OBJ file." + " Transformations are done after the KMP 3D drawing, but before PNG creation." + "\n " + " @'pix_size'@ is the number of world units (sqare) used for 1 PNG pixel." + " @'align'@ is a factor as anti-aliasing pixel multiplicator (>1 is on)." + " @'x'@ and @'y'@ defines the part of the world that is displayed." + " If not set, the check points (KMP/CKPT) are used to find a good area." + " @'t'@ is a bit field with 32 bits to enable the the KCL types." + " Each bit corresponds to one of the KCL types and only ." + " The default is to display all but not walls and solid fall (" + CONVERT_TO_STRING(OPT_PNG_TYPE_CLASS) ")." + "\n " + " All numeric values can be any expression allowed by the parser." }, + + { T_OPT_GP, "LOAD_KCL", "load-kcl|loadkcl", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "KCL", "kcl", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "KCL_FLAG", "kcl-flag|kclflag", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "KCL_SCRIPT", "kcl-script|kclscript", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TRI_AREA", "tri-area|triarea", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TRI_HEIGHT", "tri-height|triheight|tri-ht|triht|kcl-min|kclmin", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "FLAG_FILE", "flag-file|flagfile", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GO, "XTRIDATA", "xtridata|xtri", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "KMP", "kmp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "SPEED_MOD", "speed-mod|speedmod", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "KTPT2", "ktpt2", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TFORM_KMP", "tform-kmp|tformkmp|tkmp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "REPAIR_XPF", "repair-xpflags|repairxpflags|repxpf", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "GAMEMODES", "gamemodes|gmodes", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "ROUND", "round", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + +// { T_OPT_CM, "FULL", "full", +// 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, + "Suppress information lines in decoded KMP text files." + " This includes syntax information ({--no-header})." + " If set twice, the output of unneeded sections is also suppressed." }, + + { T_OPT_C, "EXPORT", "X|export", + 0, + "Enable the export mode and create small and machine readable text files" + " for easy post processing." + " The option works similar like @-HBB@ for KMP text files." }, + + { T_OPT_C, "NO_PARAM", "P|no-param|noparam", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "EPSILON", "epsilon", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DIFF", "diff", + "list", + "List of KMP sections names that should be compared." + " To select a section, use a combiantion of the following keywords:" + " @AREA, CAME, CNPT, CKPH, CKPT, ENPH, ENPT, GOBJ, ITPH, ITPT," + " JGPT, KTPT, MSPT, POTI, STGI, NONE, GOOD, DEFAULT@ of @ALL@ (default)." + " Additional keywords are @SORT@ (default) and @NOSORT@" }, + + { T_OPT_C, "GENERIC", "G|generic", + 0, + "Forget scanned entry names and use generic names for output." }, + + { T_OPT_C, "NO_ECHO", "no-echo|noecho", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_CHECK", "N|no-check|nocheck", + 0, + "Do not make plausibility checks for KMP files." }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "TRACKS", "T|tracks", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "ARENAS", "A|arenas", + 0, + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TINY", "tiny", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + //---------- wkmpt GROUP TRANSFORM ---------- + + { T_GRP_BEG, "TRANSFORM", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + { T_COPT, "SCALE", 0,0,0 }, + { T_COPT, "SHIFT", 0,0,0 }, + { T_COPT, "XSS", 0,0,0 }, + { T_COPT, "YSS", 0,0,0 }, + { T_COPT, "ZSS", 0,0,0 }, + { T_COPT, "ROT", 0,0,0 }, + { T_COPT, "XROT", 0,0,0 }, + { T_COPT, "YROT", 0,0,0 }, + { T_COPT, "ZROT", 0,0,0 }, + { T_COPT, "YPOS", 0,0,0 }, + { T_COPT, "TRANSLATE", 0,0,0 }, + { T_COPT, "NULL", 0,0,0 }, + { T_COPT, "NEXT", 0,0,0 }, + { T_COPT, "ASCALE", 0,0,0 }, + { T_COPT, "AROT", 0,0,0 }, + { T_COPT, "TFORM_SCRIPT", 0,0,0 }, + { T_COPT, "KMP", 0,0,0 }, + { T_COPT, "SPEED_MOD", 0,0,0 }, + { T_COPT, "KTPT2", 0,0,0 }, + { T_COPT, "TFORM_KMP", 0,0,0 }, + { T_COPT, "REPAIR_XPF", 0,0,0 }, + { T_COPT, "RM_GOBJ", 0,0,0 }, + { T_COPT, "BATTLE", 0,0,0 }, + { T_COPT, "EXPORT_FLAGS", 0,0,0 }, + { T_COPT, "ROUTE_OPTIONS",0,0,0 }, + { T_COPT, "WIM0", 0,0,0 }, + { T_COPT, "SLOT", 0,0,0 }, + { T_COPT, "LOAD_KCL", 0,0,0 }, + + //---------- wkmpt GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + //---------- wkmpt GROUP TEXTOUT ---------- + + { T_GRP_BEG, "TEXTOUT", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0, + "Suppress the syntax information section in KMP text files." }, + { T_COPT, "BRIEF", 0,0,0 }, + { T_COPT, "EXPORT", 0,0,0 }, + { T_COPT, "NO_PARAM", 0,0,0 }, + { T_COPT, "GENERIC", 0,0,0 }, + { T_COPT, "BATTLE", 0,0,0 }, + { T_COPT, "EXPORT_FLAGS", 0,0,0 }, + { T_COPT, "ROUTE_OPTIONS",0,0,0 }, + { T_COPT, "WIM0", 0,0,0 }, + + // + //---------- COMMAND wkmpt VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wkmpt HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wkmpt ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wkmpt TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_COPT, "VERBOSE", 0,0, + "Print the matrices of transformations." }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wkmpt COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wkmpt ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wkmpt FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wkmpt FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wkmpt SYMBOLS ---------- + + { T_CMD_BEG, "SYMBOLS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wkmpt FUNCTIONS ---------- + + { T_CMD_BEG, "FUNCTIONS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, FUNCTION_BRIEF }, + { T_COPT, "LONG", 0,0, FUNCTION_LONG }, + + //---------- COMMAND wkmpt CALCULATE ---------- + + { T_CMD_BEG, "CALCULATE", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + + //---------- COMMAND wkmpt MATRIX ---------- + + { T_CMD_BEG, "MATRIX", 0,0,0 }, + + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "If set once, suppress vector dumps for transformation steps" + " and ignore option --long." + " If set twice, suppress vector dump for final transformation too." }, + { T_COPT, "LONG", 0,0, + "If set once, print inverse matrix for the final transformation." + " If set twice, print inverse matrix for all transformations." }, + { T_COPT, "QUIET", 0,0, + "Option --quiet is dedicated to automatic tests comparing" + " the old and new transformation algorithms." + " If set once, the tables are only printed, if the results differ." + " If set twice, nothing is printed at all" + " and only the exit status will inform about the results." }, + + //---------- COMMAND wkmpt FLOAT ---------- + + { T_CMD_BEG, "FLOAT", 0,0,0 }, + { T_COPT, "ROUND", 0,0,0 }, + + //---------- COMMAND wkmpt STARTPOS ---------- + + { T_CMD_BEG, "STARTPOS", 0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkmpt OBJECTS ---------- + + { T_CMD_BEG, "OBJECTS", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Don't print the file list and the info line(s)." + " If set twice, only ID and object name is printed" + " and option {--long} is ignored." }, + { T_COPT, "LONG", 0,0, + "Print each setting starting on a new line." + " --long and {--brief} can be used together." }, + + //---------- COMMAND wkmpt EXPORT ---------- + + { T_CMD_BEG, "EXPORT", 0,0,0 }, + + //---------- COMMAND wkmpt EXPORT ---------- + + { T_CMD_BEG, "XEXPORT", 0,0,0 }, + + //---------- COMMAND wkmpt CAT ---------- + + { T_CMD_BEG, "CAT", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkmpt DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkmpt ENCODE ---------- + + { T_CMD_BEG, "ENCODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wkmpt DIFF ---------- + + { T_CMD_BEG, "DIFF", 0,0,0 }, + { T_COPT, "QUIET", 0,0, + "If set once, only a summary status is printed." + " If set twice, the status is only reported as exit status." }, + { T_COPT, "EPSILON", 0,0,0 }, + { T_COPT, "DIFF", 0,0,0 }, + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + + //---------- COMMAND wkmpt DRAW ---------- + + { T_CMD_BEG, "DRAW", 0,0,0 }, + { T_COPT, "DRAW", 0,0,0 }, + { T_COPT, "POS_MODE", 0,0,0 }, + { T_COPT, "POS_FILE", 0,0,0 }, + { T_COPT, "PNG", 0,0,0 }, + { T_COPY_CMD, "DECODE", 0,0,0 }, + { T_COPT, "KCL_SCRIPT", 0,0,0 }, + + //---------- COMMAND wkmpt CHECK ---------- + + { T_CMD_BEG, "CHECK", 0,0,0 }, + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Print only warnings and suppress hints." }, + { T_COPT, "LONG", 0,0, + "Be verbose and print more statistics." }, + { T_COPT, "QUIET", 0,0, + "Be quiet until the first warning or hint is found." }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "GENERIC", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wkmpt STGI ---------- + + { T_CMD_BEG, "STGI", 0,0,0 }, + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "IGNORE", 0,0, + "If set, ignore non existing source files" + " and files without KMP without warning." + " If set twice, ignore files with standard settings too." }, + + //---------- COMMAND wszst KTPT ---------- + + { T_CMD_BEG, "KTPT", 0,0,0 }, + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "KTPT2", 0,0,0 }, + { T_COPT, "IGNORE", 0,0, + "If set, ignore non existing source files" + " and files without KMP without warning." }, + { T_COPT, "LONG", 0,0, + "If set, compare each KTPT with each lap counter (some CKPT) and print" + " positions, directions, distances and recommendations as wide table." }, + + //---------- COMMAND wkmpt ROUTES ---------- + + { T_CMD_BEG, "ROUTES", 0,0,0 }, + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "IGNORE", 0,0, + "If set, ignore non existing source files" + " and files without KMP without warning." }, + + //---------- COMMAND wkmpt GAMEMODES ---------- + + { T_CMD_BEG, "GOBJ", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Suppress head and foot lines." }, + { T_COPT, "BRIEF", 0,0, + "Print only summary for each object category." + " If set twice, print a status vector only." }, + { T_COPT, "LONG", 0,0, + "?" }, + + //---------- COMMAND wkmpt GAMEMODES ---------- + + { T_CMD_BEG, "GAMEMODES", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "GAMEMODES", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Suppress head and foot lines." }, + { T_COPT, "BRIEF", 0,0, + "Print only the scenario version and configuration name." + " If set twice, suppress ead and foot lines too." }, + { T_COPT, "LONG", 0,0, + "If set, print a status vector for each GOBJ element too:" + " '-': Object diabled;" + " '=': Object diabled by presence flags, but visible before race;" + " '+': Object enabled;" + " 'e': Depends on engine;" + " 'r': Depends on random scenario;" + " 'b': Defintion object of type BITS;" + " 'o': Defintion object of type OR;" + " 'a': Defintion object of type AND." }, + + //---------- COMMAND wkmpt WIM0 ---------- + + { T_CMD_BEG, "WIM0", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "If set, disable table format and append a full hexdump for each WIM0 section." }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wlect.inc b/project/src/ui/tab-wlect.inc new file mode 100644 index 0000000..1eec891 --- /dev/null +++ b/project/src/ui/tab-wlect.inc @@ -0,0 +1,837 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wlect ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wlect", 0, + "wlect [option]... command [option|parameter|@file]...", + WLECT_LONG " :" + " Manage the $LE-CODE$ and LEX extensions." }, + + // + //---------- list of all wlect commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wlect VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wlect HELP [--width=wd] OPT|OPTION ...\n" + "wlect HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wlect HELP [--width=wd] command [-b] [ignored]...\n" + "wlect HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wlect ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wlect TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wlect COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wlect ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wlect FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wlect FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "SYMBOLS", "SYMBOLS", + "wlect SYMBOLS", + "Print predefined symbols of text parser to standard output." + " The LE-CODE specific symbols are included." }, + + { T_DEF_CMD, "FUNCTIONS", "FUNCTIONS", + "wlect FUNCTIONS [key]...", + "Print a list with all parser functions including" + " the LE-CODE specific functions." + " If one key ore more key entered," + " then only functions that match at least one key are printed." }, + + { T_DEF_CMD, "CALCULATE", "CALCULATE", + "wlect CALCULATE expression...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FLOAT", "FLOAT", + "wlect FLOAT number...", + 0 /* copy of wszst */ }, + + { H_DEF_CMD, "EXPORT", "_EXPORT", + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output." + " Use keyword @HELP@ to get a list of possible keywords." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "DUMP", "DUMP|D", + "wlect DUMP [source]...", + "Print an information dump for each source file." + " LE-CODE binaries and LEX binaries are accepted as input." }, + + { T_DEF_CMD, "DL", "DL", + "wlect DL [source]...", + "Short cut for {DUMP --long}." }, + + { T_DEF_CMD, "DLL", "DLL", + "wlect DLL [source]...", + "Short cut for {DUMP --long --long}." }, + + { T_DEF_CMD, "BIN_DIFF", "BIN-DIFF|BINDIFF|BD", + "wlect BIN-DIFF settings binary1 binary2", + "Compare parts of 2 different LECODE binary files." + " @SETTINGS@ is a string with plus and minus signs to switch between" + " plus/minus modes, and some key letters to select parts:" + " s:size, h:header, t:timestamp, b:body, p:parameters," + " d:data, c:code and a:all." + " In plus-mode, a test is enabled, in minus-mode it is disabled." + " Scanning starts in plus-mode." + "\n " + " Example: @a-t@: compare all except timestamps." + "\n " + " The exist status is @OK@ (0) if nothing differ," + " @DIFFER@ (2) if at least one test failed, or an error code." }, + + { T_DEF_CMD, "PATCH", "PATCH|P", + "wlect PATCH [le_bin]...", + "Read each LE-BIN file and patch it." + " Use {--le-define} to setup cups and tracks." }, + + { T_DEF_CMD, "LPAR", "LPAR", + "wlect LPAR [le_bin]...", + "Read each LE-BIN file and patch it." + " Then extract the LE-CODE parameters and store them as LPAR" + " text file. Print the result to standard output if no other" + " destination is defined." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "CREATE", "CREATE|CR", + "wlect CREATE [keyword]", + "Create a file and print it to standard output." + " The kind of file is defined by KEYWORD." + " Use command without keywords to get a list of possible keywords." + "\n " + " The following keywords are possible:" + " @LEX@ for a LEX text file with all known sections except @TEST@." + " @LEX+@ for a LEX text file with all known sections including @TEST@." + " @SET1, CANN, HIPT@ and @TEST@ (section names)" + " for a LEX text file with 1 section only." + " @LPAR@ for an example LE-CODE parameter file." }, + + { T_DEF_CMD, "CAT", "CAT", + "wlect CAT [source]...", + "Read the entered LEX and LE-CODE files (text, binary or szs)" + " and print them as decoded text files to standard output." + " The output of all source files is concatenated." }, + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wlect DECODE [source]...", + "Read the entered LEX files (text or binary)" + " and decode them to text files." + " The default destination is @'%P/%N.txt'@." }, + + { T_DEF_CMD, "ENCODE", "ENCODE|ENC", + "wlect ENCODE source...", + "Read the entered LEX files (text or binary)" + " and encode (compile) them to binary files." + " The default destination is @'%P/%N.lex'@." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + // + //---------- list of all wlect options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + "[=param]", + "This option is completely ignored by wlect." + " It only exists due to compatibility with the other tools." }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "ROUND", "round", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, + "Suppress cross reference comments." }, + + #if 0 // BMG disabled for wlect + + { T_OPT_C, "RAW", "raw", + 0, + "Print BMG messages as UTF-8 text without escape sequences." }, + + { T_OPT_CP, "BMG_ENCODING", "bmg-encoding|bmgencoding", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "BMG_INF_SIZE", "bmg-inf-size|bmginfsize", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CO, "BMG_MID", "bmg-mid|bmgmid", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "FORCE_ATTRIB", "force-attrib|forceattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEF_ATTRIB", "def-attrib|defattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ATTRIB", "no-attrib|noattrib", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "X_ESCAPES", "x-escapes|xescapes", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OLD_ESCAPES", "old-escapes|oldescapes", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "SINGLE_LINE", "1|single-line|singleline", + 0, + "If set, don't print continuation lines for BMG text output." + " If set twice, print only single text lines but not value lines." }, + + { T_OPT_C, "NO_BMG_COLORS","no-bmg-colors|nobmgcolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BMG_COLORS", "bmg-colors|bmgcolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_BMG_INLINE","no-bmg-inline|nobmginline", + 0, 0 /* copy of wszst */ }, + #endif + + { T_OPT_C, "EXPORT", "X|export", + 0, + "Enable the export mode and create small and machine readable text files" + " for easy post processing." }, + + { T_OPT_C, "NO_PARAM", "P|no-param|noparam", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ECHO", "no-echo|noecho", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "LT_CLEAR", "lt-clear|ltclear", + 0, + "Clear LEX section TEST and remove the section if empty." + " This option is executed before all other --lt-* options." }, + + { T_OPT_CP, "LT_ONLINE", "lt-online|ltonline", + "value", + "Set LEX/TEST parameter OFFLINE-ONLINE to this MODE." + " MODE is one of @AUTO@ (default), @NEVER@ or @ALWAYS@." + " If needed, LEX section TEST is created or removed if empty." }, + + { T_OPT_CP, "LT_N_PLAYERS", "lt-n-players|lt-nplayers|ltnplayers", + "offline,online", + "Set LEX/TEST parameters N-OFFLINE and N-ONLINE to these integer values." + " If needed, LEX section TEST is created or removed if empty." }, + + { T_OPT_GP, "LT_COND_BIT", "lt-cond-bit|lt-condbit|ltcondbit", + "bitnum", + "Set LEX/TEST parameter COND-BIT to this integer value." + " Use -1 to deactivate this COND-BIT." + " If needed, LEX section TEST are created or removed if empty." }, + + { T_OPT_GP, "LT_GAME_MODE", "lt-game-mode|lt-gamemode|ltgamemode", + "mode", + "Set LEX/TEST parameter GAME-MODE to this MODE." + " MODE is one of @AUTO@ (default), @Balloon, COIN, VERSUS@ or @ITEMRAIN@." + " If needed, LEX section TEST is created or removed if empty." }, + + { T_OPT_GP, "LT_ENGINE", "lt-engine|ltengine", + "mode", + "Set LEX/TEST parameter ENGINE to this MODE." + " MODE is one of @AUTO@ (default), @BATTLE, 50CC, 100CC, 150CC, 200CC," + " 150M@ or @200M@. Only track.szs files are modified." + " If needed, LEX section TEST is created or removed if empty." }, + + { T_OPT_GP, "LT_RANDOM", "lt-random|ltrandom", + "index", + "Force a random scenario, if between 1 and 8." + " Value 0 enables auto selection and -1 disables this option." + " If needed, LEX section TEST is created or removed if empty." }, + + { T_OPT_G, "LEX_PURGE", "lex-purge|lexpurge", + 0, + "Delete LEX sections without any impact." + " This option is executed after all --lt-* options." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "LE_DEFINE", "le-define|ledefine", + "file", + "Use this file as definition file to patch LE-CODE." + " The file format can be LE-BIN or any supported CT-CODE file" + " (BRRES, TEX0, CT-CODE or CT-TEXT)." }, + + { T_OPT_CP, "LPAR", "lpar", + "file", + "Read a LPAR file to overwrite settings of LE-CODE section LPAR." }, + + { T_OPT_CP, "ALIAS", "alias", + "list", + "The parameter is a comma separated list of @SLOT=ALIAS@ elements." + " SLOT defines the slot, for which the ALIAS is defined." + " ALIAS itself is a slot number to be executed, if SLOT is selected." + " Empty and invalid elements are ignored." + " Multiple usage is possible: the lists are concatenated." }, + + { T_OPT_CP, "ENGINE", "engine", + "list", + "Define probabilities for engine classes." + " @LIST@ is a vector for @100cc,150cc,mirror@" + " If 200cc is enabled by {--200cc}," + " then vector is interpreted as @150cc,200cc,mirror@." + " Any numbers are accepted." + " They are normalized to get a total of 100 percent." + " Use @0,1,0@ to force 150cc only." + " Use @0,0,0@ to reset to Nintendos VR based choice." }, + + { T_OPT_CO, "200CC", "200cc", + "[=mode]", + "Enable or disable 200cc support." + " Mode is one of @OFF@, @AUTO@ (default) or @ON@" + " (default, if option is used without parameter)." + " LE-CODE hasn't implemented this feature yet!" }, + + { T_OPT_CO, "PERFMON", "perf-mon|perfmon", + "[=mode]", + "Enable or disable the performance monitor." + " Mode is one of @OFF@, @AUTO@ (default, don't change setting)," + " @ON@ (enable it for Wii only; default, if option is used without parameter)" + " or @FORCE@ (enable it for Wii and for Dolphin)." }, + + { T_OPT_CO, "CUSTOM_TT", "custom-tt|customtt", + "[=mode]", + "Enable or disable time trial for custom tracks." + " Mode is one of @OFF@, @AUTO@ (default) or @ON@" + " (default, if option is used without parameter)." }, + + { T_OPT_CO, "XPFLAGS", "xpflags", + "[=mode]", + "Enable or disable support for Extended Presence Flags." + " Mode is one of @OFF@, @AUTO@ (default) or @ON@" + " (default, if option is used without parameter)." + " Disable it only for tests!" }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "TRACK_DIR", "track-dir|trackdir", + "directory", + "Define a destination directory," + " where track files are copied, moved or linked to." + " Usually it is @.../Race/Course/@." + "\n " + " Files are searched in directories defined by {--copy-tracks}," + " {--move-tracks}, {--move1-tracks} and {--link-tracks}" + " in definition order." + " Already existing files are removed before operation." }, + + { T_OPT_CMP, "COPY_TRACKS", "copy-tracks|copytracks", + "directory", + "Define a search directory for all included tracks." + " Files found in this directory are copied to the" + " directory specified by option {--track-dir}." }, + + { T_OPT_CMP, "MOVE_TRACKS", "move-tracks|movetracks", + "directory", + "Define a search directory for all included tracks." + " Files found in this directory are moved to the" + " directory specified by option {--track-dir}." + " If moving fails, the file is copied and the source removed." }, + + { T_OPT_CMP, "MOVE1_TRACKS", "move1-tracks|move1tracks", + "directory", + "Define a search directory for all included tracks." + " Files found in this directory are moved to the" + " directory specified by option {--track-dir}," + " but only if a file has not more than one hard link." + " This guarantees an unique version of the file." + " If the file has more hard links or moving fails," + " the file is copied and the source removed." }, + + { T_OPT_CMP, "LINK_TRACKS", "link-tracks|linktracks", + "directory", + "Define a search directory for all included tracks." + " Files found in this directory are linked to the" + " directory specified by option {--track-dir}." + " If linking fails, the file is copied." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "COMPLETE", "C|complete", + 0, + " Append missing sections to the LEX file." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CMP, "LOAD_BMG", "load-bmg|loadbmg", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CMP, "PATCH_BMG", "patch-bmg|patchbmg|pb", + "mode[cond][=param]", + PATCH_BMG_TEXT("") }, + + { T_OPT_CMP, "MACRO_BMG", "macro-bmg|macrobmg|mb", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PATCH_NAMES", "patch-names|patchnames", + 0, + "If set, the names of the CUP1 and CSR1 sections are patched " + " by the loaded BMG files directly after loading the source files." }, + + { T_OPT_CP, "ORDER_BY", "order-by|orderby|order", + "bmg", + "Load the BMG file and filter track names." + " Then before creating output files," + " re-distribute the tracks of the additonal cups (9-)" + " to the same cups by the order of the read track names." + " Tracks without BMG reference are placed at the end of all tracks." + " This option modifes only the track order of the cups," + " but will not change any name." }, + + { T_OPT_C, "ORDER_ALL", "order-all|orderall", + 0, + "Used together with option {--order-by}:" + " Re-distribute the tracks of all cups beginning with Mushroom Cup." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + //---------- wlect GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + //---------- wlect GROUP PATCH ---------- + + { T_GRP_BEG, "PATCH", 0,0,0 }, + + { T_COPT, "LE_DEFINE", 0,0,0 }, + { T_COPT, "LPAR", 0,0,0 }, + { T_COPT, "ALIAS", 0,0,0 }, + { T_COPT, "ENGINE", 0,0,0 }, + { T_COPT, "200CC", 0,0,0 }, + { T_COPT, "PERFMON", 0,0,0 }, + { T_COPT, "CUSTOM_TT", 0,0,0 }, + { T_COPT, "XPFLAGS", 0,0,0 }, + + //---------- wlect GROUP PATCH_LEX ---------- + +// { T_GRP_BEG, "PATCH_LEX", 0,0,0 }, +// { T_COPT, "LT_CLEAR", 0,0,0 }, +// { T_COPT, "LT_ONLINE", 0,0,0 }, +// { T_COPT, "LT_N_PLAYERS", 0,0,0 }, +// { T_COPT, "LT_COND_BIT", 0,0,0 }, +// { T_COPT, "LT_GAME_MODE", 0,0,0 }, +// { T_COPT, "LT_ENGINE", 0,0,0 }, +// { T_COPT, "LT_RANDOM", 0,0,0 }, +// { T_COPT, "LEX_PURGE", 0,0,0 }, + + //---------- wlect GROUP TRACK_COPY ---------- + + { T_GRP_BEG, "TRACK_COPY", 0,0,0 }, + + { T_COPT, "TRACK_DIR", 0,0,0 }, + { T_COPT, "COPY_TRACKS", 0,0,0 }, + { T_COPT, "MOVE_TRACKS", 0,0,0 }, + { T_COPT, "MOVE1_TRACKS", 0,0,0 }, + { T_COPT, "LINK_TRACKS", 0,0,0 }, + + //---------- wlect GROUP TEXTOPT ---------- + + { T_GRP_BEG, "TEXTOPT", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0, + "Suppress the syntax information section in LE text files." }, + { T_COPT, "BRIEF", 0,0,0 }, + { T_COPT, "EXPORT", 0,0,0 }, + + //---------- wlect GROUP TEXTOUT ---------- + + { T_GRP_BEG, "TEXTOUT", 0,0,0 }, + + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPY_GRP, "TEXTOPT", 0,0,0 }, + + // + //---------- COMMAND wlect VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wlect HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wlect ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wlect TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wlect COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wlect ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wlect FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wlect FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wlect SYMBOLS ---------- + + { T_CMD_BEG, "SYMBOLS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wlect FUNCTIONS ---------- + + { T_CMD_BEG, "FUNCTIONS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, FUNCTION_BRIEF }, + { T_COPT, "LONG", 0,0, FUNCTION_LONG }, + + //---------- COMMAND wlect CALCULATE ---------- + + { T_CMD_BEG, "CALCULATE", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + + //---------- COMMAND wlect FLOAT ---------- + + { T_CMD_BEG, "FLOAT", 0,0,0 }, + { T_COPT, "ROUND", 0,0,0 }, + + //---------- COMMAND wlect _EXPORT ---------- + + { T_CMD_BEG, "EXPORT", 0,0,0 }, + + //---------- COMMAND wlect DUMP ---------- + + { T_CMD_BEG, "DUMP", 0,0,0 }, + + { T_COPY_GRP, "PATCH", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "LONG", 0,0, + "For LE-CODE binaries:" + " If set, print cups with tracks or arenas." + " For each track, property and music slot is printed in brackets." + " If set twice, print a track list too." + "\n " + " For LEX binaries:" + " If set, print a hex dump of max 16 bytes (1 line) of the element data." + " If set twice, print a hex dump of max 64 bytes (4 lines)." + " If set 3 times ore more, print a complete hex dump." }, + { T_COPT, "VERBOSE", 0,0, + "For LE-CODE binaries: If set, print a track slot usage table." }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wlect DL ---------- + + { T_CMD_BEG, "DL", 0,0,0 }, + { T_COPY_CMD, "DUMP", 0,0,0 }, + + //---------- COMMAND wlect DLL ---------- + + { T_CMD_BEG, "DLL", 0,0,0 }, + { T_COPY_CMD, "DUMP", 0,0,0 }, + + //---------- COMMAND wlect BIN-DIFF ---------- + + { T_CMD_BEG, "BIN_DIFF", 0,0,0 }, + + { T_COPT, "QUIET", 0,0, + "Suppress all messages and tell result as exit status only." }, + { T_COPT, "VERBOSE", 0,0, + "Be more verbose and tell, what tests are planned." }, + + //---------- COMMAND wlect PATCH ---------- + + { T_CMD_BEG, "PATCH", 0,0,0 }, + + { T_COPY_GRP, "PATCH", 0,0,0 }, + { T_COPY_GRP, "TRACK_COPY", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, +// { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wlect LPAR ---------- + + { T_CMD_BEG, "LPAR", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + + //---------- COMMAND wlect CREATE ---------- + + { T_CMD_BEG, "CREATE", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "LPAR", 0,0,0 }, + { T_COPT, "LT_CLEAR", 0,0,0 }, + { T_COPT, "LT_ONLINE", 0,0,0 }, + { T_COPT, "LT_N_PLAYERS", 0,0,0 }, + { T_COPT, "LT_COND_BIT", 0,0,0 }, + { T_COPT, "LT_GAME_MODE", 0,0,0 }, + { T_COPT, "LT_ENGINE", 0,0,0 }, + { T_COPT, "LT_RANDOM", 0,0,0 }, + { T_COPT, "LEX_PURGE", 0,0,0 }, + + //---------- COMMAND wlect CAT ---------- + + { T_CMD_BEG, "CAT", 0,0,0 }, + { T_COPT, "COMPLETE", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wlect DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPT, "COMPLETE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wlect ENCODE ---------- + + { T_CMD_BEG, "ENCODE", 0,0,0 }, + { T_COPT, "COMPLETE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wmdlt.inc b/project/src/ui/tab-wmdlt.inc new file mode 100644 index 0000000..c4a8e1c --- /dev/null +++ b/project/src/ui/tab-wmdlt.inc @@ -0,0 +1,600 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wmdlt ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wmdlt", 0, + "wmdlt [option]... command [option|parameter|@file]...", + WMDLT_LONG " :" + " Decode raw MDL and encode text MDL files." }, + + // + //---------- list of all wmdlt commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wmdlt VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wmdlt HELP [--width=wd] OPT|OPTION ...\n" + "wmdlt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wmdlt HELP [--width=wd] command [-b] [ignored]...\n" + "wmdlt HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wmdlt ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wmdlt TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wmdlt COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wmdlt ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wmdlt FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wmdlt FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "SYMBOLS", "SYMBOLS", + "wmdlt SYMBOLS", + "Print predefined symbols of text parser to standard output." + " The MDL specific symbols are included." }, + + { T_DEF_CMD, "FUNCTIONS", "FUNCTIONS", + "wmdlt FUNCTIONS [key]...", + "Print a list with all parser functions including the MDL specific functions." + " If one key ore more key entered," + " then only functions that match at least one key are printed." }, + + { T_DEF_CMD, "CALCULATE", "CALCULATE", + "wmdlt CALCULATE expression...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "MATRIX", "MATRIX", + "wmdlt MATRIX [patch-option]... [vector]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FLOAT", "FLOAT", + "wmdlt FLOAT number...", + 0 /* copy of wszst */ }, + + { H_DEF_CMD, "EXPORT", "_EXPORT", + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output." + " Use keyword @HELP@ to get a list of possible keywords." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "CAT", "CAT", + "wmdlt CAT [source]...", + "Read the entered MDL files (text or binary) and print" + " them as decoded text files to standard output (stdout)." + " The output of all source files is concatenated." + "\n " + " A decoded MDL0 file can only be used for analysis." + " Encoding or creating a new MDL0 file is not supported." }, + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wmdlt DECODE [source]...", + "Read the entered MDL files (text or binary)" + " and decode them to text files." + " The default destination is @'%P/%N.txt'@." + "\n " + " A decoded MDL0 file can only be used for analysis." + " Encoding or creating a new MDL0 file is not supported." }, + + { H_DEF_CMD, "ENCODE", "ENCODE|ENC", + "wmdlt ENCODE [source]...", + "Read the entered MDL files (text or binary)" + " and encode (compile) them to binary files." + " The default destination is @'%P/%N.mdl'@." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "STRINGS", "STRINGS", + "wmdlt STRINGS [source]...", + "Read the entered MDL files (text or binary)" + " and print all strings of all sections." + "\n " + " For archives like SZS or BREES," + " the strings of each containing MDL are printed." + " It is also possible to address an internal sub-file like" + " @track.szs/course_model.brres@." }, + + { H_DEF_CMD, "GEOMETRY", "GEOMETRY|GEO", + "wmdlt GEOMETRY [source]...", + "Read the entered MDL files (text or binary) and print" + " some geometry information to the standard output (stdout)." + " The output of all source files is concatenated." }, + + { H_DEF_CMD, "XTEST", "_TEST", + "wszst _TEST [files]", + "Test something." }, + + // + //---------- list of all wmdlt options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "MDL", "mdl", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SCALE", "scale", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "SHIFT", "shift", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "XSS", "xss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "YSS", "yss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ZSS", "zss", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ROT", "rot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "XROT", "xrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "YROT", "yrot|hrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ZROT", "zrot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "TRANSLATE", "translate", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NULL", "null", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NEXT", "next", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ASCALE", "ascale", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "AROT", "arot", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "TFORM_SCRIPT", "tform-script|tformscript", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "ROUND", "round", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_PARAM", "P|no-param|noparam", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ECHO", "no-echo|noecho", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_CHECK", "N|no-check|nocheck", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "TRACKS", "T|tracks", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "ARENAS", "A|arenas", + 0, + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TINY", "tiny", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + //---------- wmdlt GROUP TRANSFORM ---------- + + { T_GRP_BEG, "TRANSFORM", 0,0,0 }, + + //{ T_COPT, "CONST", 0,0,0 }, + { T_COPT, "SCALE", 0,0,0 }, + { T_COPT, "SHIFT", 0,0,0 }, + { T_COPT, "XSS", 0,0,0 }, + { T_COPT, "YSS", 0,0,0 }, + { T_COPT, "ZSS", 0,0,0 }, + { T_COPT, "ROT", 0,0,0 }, + { T_COPT, "XROT", 0,0,0 }, + { T_COPT, "YROT", 0,0,0 }, + { T_COPT, "ZROT", 0,0,0 }, + { T_COPT, "TRANSLATE", 0,0,0 }, + { T_COPT, "NULL", 0,0,0 }, + { T_COPT, "ASCALE", 0,0,0 }, + { T_COPT, "AROT", 0,0,0 }, + { T_COPT, "TFORM_SCRIPT", 0,0,0 }, + { T_COPT, "NEXT", 0,0,0 }, + { T_COPT, "MDL", 0,0,0 }, + + + //---------- wmdlt GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + // + //---------- wmdlt GROUP TEXTOUT ---------- + + { T_GRP_BEG, "TEXTOUT", 0,0,0 }, + + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Suppress the syntax information section in MDL text files." }, + { T_COPT, "BRIEF", 0,0,0 }, + + // + //---------- COMMAND wmdlt VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wmdlt HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wmdlt ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wmdlt TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_COPT, "VERBOSE", 0,0, + "Print the matrices of transformations." }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wmdlt COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wmdlt ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wmdlt FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wmdlt FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wmdlt SYMBOLS ---------- + + { T_CMD_BEG, "SYMBOLS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wmdlt FUNCTIONS ---------- + + { T_CMD_BEG, "FUNCTIONS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, FUNCTION_BRIEF }, + { T_COPT, "LONG", 0,0, FUNCTION_LONG }, + + //---------- COMMAND wmdlt CALCULATE ---------- + + { T_CMD_BEG, "CALCULATE", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + + //---------- COMMAND wmdlt MATRIX ---------- + + { T_CMD_BEG, "MATRIX", 0,0,0 }, + + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "If set once, suppress vector dumps for transformation steps" + " and ignore option --long." + " If set twice, suppress vector dump for final transformation too." }, + { T_COPT, "LONG", 0,0, + "If set once, print inverse matrix for the final transformation." + " If set twice, print inverse matrix for all transformations." }, + { T_COPT, "QUIET", 0,0, + "Option --quiet is dedicated to automatic tests comparing" + " the old and new transformation algorithms." + " If set once, the tables are only printed, if the results differ." + " If set twice, nothing is printed at all" + " and only the exit status will inform about the results." }, + + //---------- COMMAND wmdlt FLOAT ---------- + + { T_CMD_BEG, "FLOAT", 0,0,0 }, + { T_COPT, "ROUND", 0,0,0 }, + + //---------- COMMAND wmdlt EXPORT ---------- + + { T_CMD_BEG, "EXPORT", 0,0,0 }, + + //---------- COMMAND wmdlt CAT ---------- + + { T_CMD_BEG, "CAT", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wmdlt DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wmdlt ENCODE ---------- + + { T_CMD_BEG, "ENCODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wmdlt STRINGS ---------- + + { T_CMD_BEG, "STRINGS", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wmdlt GEOMETRY ---------- + + { T_CMD_BEG, "GEOMETRY", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + + //---------- COMMAND wmdlt _TEST ---------- + + { T_CMD_BEG, "XTEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "TRANSFORM", 0,0,0 }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wpatt.inc b/project/src/ui/tab-wpatt.inc new file mode 100644 index 0000000..a55b2bf --- /dev/null +++ b/project/src/ui/tab-wpatt.inc @@ -0,0 +1,441 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wpatt ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wpatt", 0, + "wpatt [option]... command [option|parameter|@file]...", + WPATT_LONG " :" + " Decode raw PAT and encode text PAT files." }, + + // + //---------- list of all wpatt commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wpatt VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wpatt HELP [--width=wd] OPT|OPTION ...\n" + "wpatt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wpatt HELP [--width=wd] command [-b] [ignored]...\n" + "wpatt HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wpatt ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wpatt TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wpatt COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wpatt ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wpatt FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wpatt FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "SYMBOLS", "SYMBOLS", + "wpatt SYMBOLS", + "Print predefined symbols of text parser to standard output." + " The PAT specific symbols are included." }, + + { T_DEF_CMD, "FUNCTIONS", "FUNCTIONS", + "wpatt FUNCTIONS [key]...", + "Print a list with all parser functions including the PAT specific functions." + " If one key ore more key entered," + " then only functions that match at least one key are printed." }, + + { T_DEF_CMD, "CALCULATE", "CALCULATE", + "wpatt CALCULATE expression...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FLOAT", "FLOAT", + "wpatt FLOAT number...", + 0 /* copy of wszst */ }, + + { H_DEF_CMD, "EXPORT", "_EXPORT", + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output." + " Use keyword @HELP@ to get a list of possible keywords." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "CAT", "CAT", + "wpatt CAT [source]...", + "Read the entered PAT files (text or binary) and print" + " them as decoded text files to standard output (stdout)." + " The output of all source files is concatenated." }, + + { T_DEF_CMD, "DECODE", "DECODE|DEC", + "wpatt DECODE [source]...", + "Read the entered PAT files (text or binary)" + " and decode them to text files." + " The default destination is @'%P/%N.txt'@." }, + + { T_DEF_CMD, "ENCODE", "ENCODE|ENC", + "wpatt ENCODE [source]...", + "Read the entered PAT files (text or binary)" + " and encode (compile) them to binary files." + " The default destination is @'%P/%N.pat'@." }, + + // + //---------- list of all wpatt options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "ROUND", "round", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_PARAM", "P|no-param|noparam", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_ECHO", "no-echo|noecho", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NO_CHECK", "N|no-check|nocheck", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "TRACKS", "T|tracks", + 0, + 0, 0 /* copy of wszst */ }, + + { H_OPT_GP, "ARENAS", "A|arenas", + 0, + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "PAT", "pat", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + // + //---------- wpatt GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_COPT, "PAT", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + // + //---------- wpatt GROUP TEXTOUT ---------- + + { T_GRP_BEG, "TEXTOUT", 0,0,0 }, + + { T_COPT, "IGNORE", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0, + "Suppress the syntax information section in PAT text files." }, + { T_COPT, "BRIEF", 0,0,0 }, + + // + //---------- COMMAND wpatt VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wpatt HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wpatt ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wpatt TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wpatt COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wpatt ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wpatt FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wpatt FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wpatt SYMBOLS ---------- + + { T_CMD_BEG, "SYMBOLS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wpatt FUNCTIONS ---------- + + { T_CMD_BEG, "FUNCTIONS", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, FUNCTION_BRIEF }, + { T_COPT, "LONG", 0,0, FUNCTION_LONG }, + + //---------- COMMAND wpatt CALCULATE ---------- + + { T_CMD_BEG, "CALCULATE", 0,0,0 }, + + { T_COPT, "CONST", 0,0,0 }, + + //---------- COMMAND wpatt FLOAT ---------- + + { T_CMD_BEG, "FLOAT", 0,0,0 }, + { T_COPT, "ROUND", 0,0,0 }, + + //---------- COMMAND wpatt EXPORT ---------- + + { T_CMD_BEG, "EXPORT", 0,0,0 }, + + //---------- COMMAND wpatt CAT ---------- + + { T_CMD_BEG, "CAT", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wpatt DECODE ---------- + + { T_CMD_BEG, "DECODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPY_GRP, "TEXTOUT", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wpatt ENCODE ---------- + + { T_CMD_BEG, "ENCODE", 0,0,0 }, + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "NO_ECHO", 0,0,0 }, + { T_COPT, "NO_CHECK", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wstrt.inc b/project/src/ui/tab-wstrt.inc new file mode 100644 index 0000000..841a4e7 --- /dev/null +++ b/project/src/ui/tab-wstrt.inc @@ -0,0 +1,1038 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wstrt ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wstrt", 0, + "wstrt [option]... command [option|parameter|@file]...", + WSTRT_LONG " :" + " Manipulate the 'main.dol' and 'StaticR.rel' files" + " of Mario Kart Wii." }, + + // + //---------- list of all wstrt commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wstrt VERSION [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wstrt HELP [--width=wd] OPT|OPTION ...\n" + "wstrt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wstrt HELP [--width=wd] command [-b] [ignored]...\n" + "wstrt HELP [--width=wd] [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wstrt ARGTEST ...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "TEST", "TEST", + "wstrt TEST [options]... [ignored]...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wstrt COLORS", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wstrt ERROR [error_code]", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wstrt FILETYPE file...", + 0 /* copy of wszst */ }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wstrt FILEATTRIB [keyword]...", + 0 /* copy of wszst */ }, + + { H_DEF_CMD, "RAWDUMP", "_RAWDUMP", + "wszst _RAWDUMP [-d|-D] destdir", + "This hidden command creates the destination directory " + " and create several files as copy of internal data." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "DUMP", "DUMP", + "wstrt DUMP file...", + "Dump the structure of 'StaticR.rel' (in development)" + " and of DOL files as text." }, + + { H_DEF_CMD, "CDUMP", "_CDUMP", + "wstrt _CDUMP file...", + "Dump the structure of 'StaticR.rel' (in development)" + " and of DOL files as C source fragments." }, + + { T_DEF_CMD, "HEXDUMP", "HEXDUMP|XDUMP", + "wstrt HEXDUMP file...", + "Dump the content of DOL files as hex dump and use virtual addresses." + " Select parts by --vaddr, --faddr and --sname." + " Without selectors, the whole file is dumped." }, + + { H_DEF_CMD, "HTTPS", "_HTTPS", + "wstrt _HTTPS file...", + "Collect data about https-requests and print a total summary" + " for all accepted files." }, + + { T_DEF_CMD, "EXTRACT", "EXTRACT|X", + "wstrt EXTRACT dol...", + "Extract the sections of a DOL file into a directory." + " StaticR.rel files are ignored." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "TRACKS", "TRACKS", + "wstrt TRACKS [file]...", + "Show track infos in the order of the 'StaticR.rel' files." + " If no file is entered, the standard order is used." }, + + { T_DEF_CMD, "ARENAS", "ARENAS", + "wstrt ARENAS [file]...", + "Show arena infos in the order of the 'StaticR.rel' files." + " If no file is entered, the standard order is used." }, + + { T_DEF_CMD, "FILES", "FILES", + "wstrt FILES [file]...", + "List track and arena file names in machine readable format." + " Use the order of the entered 'StaticR.rel' files." + " If no file is entered, the standard order is used." }, + + { T_DEF_CMD, "FIND", "FIND", + "wstrt FIND [+]TRACK keyword...\n" + "wstrt FIND [+]ARENA keyword...", + "Find a track or arena by keyword and print one line for each keyword" + " in machine readable format @'type slot'@" + " or, if not found, a simple @'-'@." + " The name is searched in the entered context (tracks or arenas)." + " If @TRACK@ or @ARENA@ is prefixed by a plus, then a failed search" + " will be continued in the other context." + " If at least one name is not found," + " the tool terminates with exit code @NOT_EXISTS@." }, + + { T_DEF_CMD, "POINTS", "POINTS|PTS", + "wstrt POINTS [--points=list] [file]...", + "Print a table with the points assigned to player after" + " a versus or room race for each entered file." + " If no file is entered, the standard table is used." + " However, if option {--points} is set only the resulting table" + " is printed." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "ANALYZE", "ANALYZE|ANA|ANALYSE", + "wstrt ANALYZE [file]...", + "Open 'StaticR.rel' and DOL files and analyze them." + " The files will be classified using SHA1 checksums." }, + + { T_DEF_CMD, "PATCH", "PATCH", + "wstrt PATCH [file]...", + "Patch 'main.dol and 'StaticR.rel' files of Mario Kart Wii." + " No backup is made." }, + + // + //---------- list of all wstrt options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "HELP", "h|help", + 0, 0 /* copy of wszst */ }, + + { T_OPT_S, "XHELP", "xhelp", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "AT_DUMMY", "@|@", 0, "" }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "DE", "de", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CHDIR", "chdir", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GMP, "CONST", "c|const", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CM, "LONG", "l|long", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, + "List only the track file names and suppress table header and footer." }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "TRACKS", "T|tracks", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "ARENAS", "A|arenas", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GP, "REGION", "R|region", + "region", + "If 'region' is an unsigned number, the region" + " for online versus races and for battles is patched" + " to the entered number." + " If the number is prefixed by the letter @'t'@, a test mode" + " is enabled and the places are patched with ascending numbers." + " You can patch StaticR.rel and main.dol." + "\n " + " Otherwise a keyword is expected." + " Official regions are: @JAPAN(0), AMERICA(1), EUROPE(2)," + " AUSTRALIA(OCEANIA,3), KOREA(4), TAIWAN(5)@ and @CHINA(6)@." + " @RESTORE@ restores the original Nintendo values" + " and @NONE@ disables region patching (default)." + "\n " + " See https://wiimmfi.de/reg-list for other regions." }, + + { T_OPT_GP, "VS_REGION", "vs-region|vsregion|vregion", + "region", + "Same as {--region}, but only for online versus races." }, + + { T_OPT_GP, "BT_REGION", "bt-region|btregion|bregion", + "region", + "Same as {--region}, but only for online battles." }, + // " If the region is prefixed by the letter @'x'@," + // " than a fourth experimental place is patched too." }, + + { T_OPT_GO, "ALL_RANKS", "all-ranks|allranks", + "[=mode]", + "If set to @ON@ (default without parameter)," + " the server query to Nintendos servers is manipulated," + " so that the player will find all players in online races" + " independent of their ranking points." + " Value @RESTORE@ disables this feature and restores Nintendos original query." + " Value @NONE@ disables this kind of patching (default)." }, + + { T_OPT_GP, "POINTS", "points|pts", + "list", + "This patch option " OPT_POINTS }, + + { H_OPT_CP, "CHEAT", "cheat", + "region", + "Change the output format and create a cheat code for the region." + " Allowed keywords: JAPAN; AMERICA, NTSC; EUROPE, AUSTRALIA, PAL; KOREA." }, + + { T_OPT_GP, "HTTPS", "https", + "mode", + "Patch the https strings of REL and DOL files." + " 'mode' is one of the following keywords:" + "\n" + "@RESTORE@ restores the original URLs and domains." + "\n" + "@HTTP@ replaces HTTPS requests by HTTP and sub-domain" + " '@naswii.@' by '@nas.@'." + "\n" + "@DOMAIN@ replace HTTPS requests by HTTP" + " and the domain '@nintendowifi.net@'" + " by a domain specified by option {--domain}." + "\n" + "@SAKE0@ same as DOMAIN, but don't replace URLs with SAKE domain." + "\n" + "@SAKE1@ same as SAKE0, but replace HTTPS of SAKE domains." }, + + { T_OPT_GP, "DOMAIN", "domain", + "dom", + "Define a domain for option {--https}." + " The default is '@wiimmfi.de@'." }, + + { T_OPT_G, "WIIMMFI", "wiimmfi", + 0, + "Patch the files for the new custom server." + " It is a short cut for {--https domain --domain wiimmfi.de}." }, + + { H_OPT_G, "TWIIMMFI", "twiimmfi", + 0, + "Patch the files for the test server." + " It is a short cut for {--https domain --domain test.wiimmfi.de}." }, + + { T_OPT_G, "WC24", "wc24", + 0, + "Exclude 2 strings of file StaticR.rel from --wiimmfi" + " and --domain patch to enable WC24 support." }, + + { H_OPT_GO, "WCODE", "wcode", + "[=mode]", + "Enable (mode=ON) or disable (mode=OFF) wiimmfi-code patching." + " Default mode is AUTO." + " In this case, patching is enabled if any WCODE is defined" + " or option --domain is set either to 'wiimmfi.de'" + " or to 'test.wiimmfi.de'." }, + + { H_OPT_CP, "ADD_WCODE", "@add-wcode|@addwcode", + "file", + "Each option call adds the filename to an internal list." + " Each file must be of type GCT and is added to the internal wcode stock." + "\n " + " If a single file is not found and the filename (not path)" + " contains at least 1 '@@' character, then all '@@' characters of" + " it are replaced by the region code (one of P,E,J,K) for a second try." }, + + { T_OPT_GP, "PB_MODE", "pb-mode|pbmode", + "mode", + "Mode for the patched-by string:" + " @OFF@ disables modifications." + " @RESET@ resets the string to stars." + " @WIIMMFI@ defines it only, if patched for Wiimmfi." + " @AUTO@ (default) defines it only, if patched." + " @ALWAYS@ defines it always." }, + + { H_OPT_GP, "PATCHED_BY", "@patched-by|@patchedby", + "string", + "Define a string for @patched-by@." + " Default is: @wstrt vVERSION rREVISION@" }, + + { T_OPT_GP, "VS", "vs", + "char", + "Reset (str is empty) or replace the second character of 'vs'" + " to create an own online world for versus races." + " The file StaticR.rel will be patched at 4 places." }, + + { H_OPT_GP, "VS2", "vs2", + "str", + "Reset (str is empty) or replace the 2 characters of 'vs'" + " to create an own online world for versus races." + " If the parameter contains only 1 character," + " use 'v' as first character." + " The file StaticR.rel will be patched at 4 places." + "\n " + " Wiimmfi will only detect 'Versus' if the first letter is 'v' or 'V'." }, + + { T_OPT_GP, "BT", "bt", + "char", + "Reset (str is empty) or replace the second character of 'bt'" + " to create an own online world for battles." + " The file StaticR.rel will be patched at 4 places." }, + + { H_OPT_GP, "BT2", "bt2", + "str", + "Reset (str is empty) or replace the 2 characters of 'bt'" + " to create an own online world for battles." + " If the parameter contains only 1 character," + " use 'b' as first character." + " The file StaticR.rel will be patched at 4 places." + "\n " + " Wiimmfi will only detect 'Battle' if the first letter is 'b' or 'B'." }, + + { T_OPT_GP, "CANNON", "cannon", + "idx,p1,p2,p3,p4", + "Patch parameters for cannon with index IDX (0..2)." + " Parameters P1..P4 are floating point numbers." + " Repeat this option for each cannon, that should be modified." }, + + { T_OPT_G, "MENO", "meno", + 0, + "Patch some 'MenuSingle' and 'MenuMulti' strings of file StaticR.rel" + " for online usage to 'MenO*' (MenuOnline)" + " to distinguish between offline and online races." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, 0 /* copy of wszst */ }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GM, "FORCE", "force", + 0, 0 /* copy of wszst */ }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + 0, 0 /* copy of wszst */ }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "STD", "STD", + 0, 0 /* copy of wszst */ }, + + { H_OPT_GM, "NEW", "NEW", + 0, 0 /* copy of wszst */ }, + #endif + + { H_OPT_GM, "EXTRACT", "EXTRACT", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "DEST", "d|dest", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "DEST2", "D|DEST", + 0, 0 /* copy of wszst */ }, + + { T_OPT_CP, "ESC", "E|esc", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "NUMBER", "number|num", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "UPDATE", "u|update", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, 0 /* copy of wszst */ }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, 0 /* copy of wszst */ }, + + { T_OPT_G, "CLEAN_DOL", "clean-dol|cleandol", + 0, + "Remove additional DOL sections, reset entry point and VBI address," + " and try to restore other patches known by SZS tools." + " This is done after loading a DOL and before analysing" + " and patching to allow re-patching." }, + + { H_OPT_C, "ADD_LECODE", "add-lecode|addlecode", + 0, + "Add LE-CODE starter to main.dol." }, + + { T_OPT_C, "ADD_CTCODE", "add-ctcode|addctcode", + 0, + "If source is an original main.dol file of MKWii," + " then append 2 sections (usually T2 and D8) with @CT-CODE@." + " The CTCODE is inserted after sections defined by {--add-section}" + " and then only, if there are no address collisions." + "\n " + " If {--ct-dir} is used, then files named" + " @rmc[ejp]/boot_code.bin@ and @boot_code.bin@ for T2," + " and @rmc[ejp]/boot_data.bin@ and @boot_data.bin@ for D8" + " are searched." }, + + { T_OPT_GP, "CT_DIR", "ct-dir|ctdir", + "dir", + OPT_CT_DIR("") }, + + { T_OPT_C, "MOVE_D8", "move-d8|moved8", + 0, + "Move the @CT-CODE@ section D8 to another place" + " at the middle of binary." + " It is done automatically, if the original place overlaps" + " other existing sections." }, + + { T_OPT_CP, "ADD_SECTION", "add-section|addsection", + "file", + "Each option call adds the filename to an internal list." + " Each file must be of type GCT, GCH or WCH." + " All GCT files together are combined with an internal code handler" + " to one section (like a GCH file)." + " GCH and WCH define sections and patches. The defined sections" + " are added and VBI hooks are optionally initialized." + " Use option {--force} to allow overlapped sections." + " Use option {--verbose} up to 2 times for a log." + "\n " + " If a single file is not found and the filename (not path)" + " contains at least 1 '@@' character, then all '@@' characters of" + " it are replaced by the region code (one of P,E,J,K) for a second try." + "\n " + " The intention of this option is to add a Gecko Code Handler" + " and cheat codes to a main.dol for automatic execution." + "\1 See https://szs.wiimm.de/doc/addsect for details." }, + + { H_OPT_C, "FULL_GCH", "full-gch|fullgch", + 0, + "Use the full 'codehandler' instead of 'codehandleronly'." }, + + { H_OPT_C, "GCT_NO_SEP", "gct-no-sep|gctnosep", + 0, + "Ignore cheat code separators completly" + " and don't filter @'F0000001 0000xxxx'@ codes." + " Options --gct-sep and --gct-asm-sep are ignored too." }, + + { H_OPT_C, "GCT_SEP", "gct-sep|gctsep", + 0, + "Detect and remove cheat code separators" + " of format @'F0000001 0000xxxx'@." + " The separators are used to split the cheat codes" + " into several blocks if the total size is too large." + " ASM blocks are skipped if searching separators." }, + + { H_OPT_C, "GCT_ASM_SEP", "gct-asm-sep|gctasmsep", + 0, + "Detect the beginning of ASM blocks and use them" + " as cheat code separators." + " The separators are used to split the cheat codes" + " into several blocks if the total size is too large." }, + + { H_OPT_C, "GCT_LIST", "gct-list|gctlist", + 0, + "If GCT separators active, print a summary table about" + " the distribution of the segments." + " Be more verbose if set twice." }, + + { T_OPT_CO, "GCT_MOVE", "gct-move|gctmove", + "[=mode]", + "If a section is of type gecko code handler + cheat code, then make" + " the heap smaller and move the cheat codes before the FST section." + "\n " + " Parameter MODE is optional." + " If set, OFF disables and ON enables this feature." + " For mode AUTO (default) this feature is enabled," + " if the cheat codes are too large or for internal reasons." }, + + { T_OPT_CP, "GCT_ADDR", "gct-addr|gctaddr", + "address", + "If {--gct-move} is active, a text section is created" + " to manage the code handling. This section is only needed at start" + " and is placed by default at address 0c802c0000" + " (good address for all 4 regions) as part of BSS." + " This option changes the start address of the section." + " If address is @0@ or invalid, then unused BSS space is searched." }, + + { T_OPT_CP, "GCT_SPACE", "gct-space|gctspace", + "size", + "Define the minimal space for the code part of {--gct-move}." + " This value is ignored," + " if it is smaller than the size of the cheat codes." + " This option was only implemented to test the " + " memory allocation by {--gct-move} with large blocks." }, + + { T_OPT_CO, "ALLOW_USER_GCH", "allow-user-gch|allowusergch", + "[=mode]", + " This option is only relevant, if main.dol is patched for wiimmfi.de." + " It is dedicated to custom distribution creators for test purposes." + " If enabled, a user can load an own instance of Gecko Code Handler" + " and cheat codes and can use them online." + " If disabled, pachcing for online modus is disabled if an" + " alternative Gecko Code Handler is detected." + "\n " + " Parameter MODE is optional." + " If set, OFF disables and ON enables this feature." + " For mode AUTO (default) it depends on other options." }, + + { H_OPT_CP, "CREATE_SECT", "create-sect|createsect|cs", + "mode=file", + "Create a new DOL section by loading the binary file." + " MODE is a list of 'P' (parameter support)," + " 'T' or 'D' with optional digit (define section)" + " and an address (auto if not set)." + " The parts are separated by spaces or commas." + "\n " + " If MODE is 'P', then 4 32-bit parameters are expected at offset 0x10:" + " The section address at 0x10 (read)," + " the relative entry point at 0x14 (read, used if >0)," + " the original DOL entry point at 0x18 (written)," + " the address of the last defined '.dol' section at 0x1c (written)." + "\n " + " If the file name is '.dol', then it points not to a file." + " Instead a copy of the DOL header beginning with the section" + " addresses and ending behind the entry point (156B total) is copied." + " MODE 'P' is ignored for this section." + " All other MODE 'P' sections are updated at offset 0x1c." + "\n " + " --cs is a short cut." }, + + { H_OPT_CP, "WPF", "wpf", + "file", + "Each option call adds the filename to an internal list." + " Each file must be of type WPF (Wiimms Patch File) and defines" + " a list of conditionals and patches for staticr.rel and main.dol." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "SECTIONS", "sections", + 0, 0 /* copy of wszst */ }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "VADDR", "vaddr|va", + "RANGE", + "Comma separated list of virtual addresses to select the hex-dump ranges." + " Each element is either @ADDRESS@ (1 byte)" + " or @START:END@ or @START#SIZE@." }, + + { T_OPT_CP, "FADDR", "faddr|fa", + "RANGE", + "Comma separated list of file addresses to select the hex-dump ranges." + " Each element is either @ADDRESS@ (1 byte)" + " or @START:END@ or @START#SIZE@." }, + + { T_OPT_CP, "SNAME", "sname|sn", + "LIST", + "Comma separated list of section names to select the hex-dump ranges." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_C, "INT1", "i1", + 0, + "Format: Print single bytes as integer number (default)." }, + + { T_OPT_C, "INT2", "i2", + 0, + "Format: Print each 2 bytes as integer number." }, + + { T_OPT_C, "INT3", "i3", + 0, + "Format: Print each 3 bytes as integer number." }, + + { T_OPT_C, "INT4", "i4", + 0, + "Format: Print each 4 bytes as integer number." }, + + { T_OPT_C, "INT5", "i5", + 0, + "Format: Print each 5 bytes as integer number." }, + + { T_OPT_C, "INT6", "i6", + 0, + "Format: Print each 6 bytes as integer number." }, + + { T_OPT_C, "INT7", "i7", + 0, + "Format: Print each 7 bytes as integer number." }, + + { T_OPT_C, "INT8", "i8", + 0, + "Format: Print each 8 bytes as integer number." }, + + { T_OPT_C, "FLOAT", "float", + 0, + "Format: Print each 4 bytes as floating point number." }, + + { T_OPT_C, "DOUBLE", "double", + 0, + "Format: Print each 8 bytes as double floating point number." }, + + { T_OPT_C, "LE", "le", + 0, + "Use little endian for binary data." }, + + { T_OPT_C, "BE", "be", + 0, + "Use big endian for binary data (default)." }, + + { T_OPT_C, "ZEROS", "zeros", + 0, + "Print integer numbers with leading zeros." }, + + { T_OPT_C, "HEX", "hex", + 0, + "Print integer numbers as hexadecimal numbers (default)." }, + + { T_OPT_C, "DEC", "dec", + 0, + "Print integer numbers as decimal numbers." }, + + { T_OPT_C, "C_SYNTAX", "c-syntax|csyntax", + 0, + "Print output in `C' syntax." }, + + { T_OPT_CP, "ADDR", "address", + "addr", + "Use @ADDR@ instead of null as start address for the dump." }, + + { T_OPT_C, "ALIGN", "align", + 0, + "Try to align the dump, so that the addresses (printed or not)" + " are multiple of bytes-per-line." }, + + { T_OPT_CP, "TRIGGER", "trigger", + "num", + "If the number @NUM@ is found and printed," + " close the current line and start with a new one." + " If {--align} is set too," + " continue the next line with the current column." + " The trigger works only for integer formats, but not for floats." }, + + { T_OPT_C, "NO_ADDR", "no-address|noaddress", + 0, + "Suppress the address column of the dump." }, + + { T_OPT_C, "NO_NUMBERS", "no-numbers|nonumbers", + 0, + "Suppress the numbers column of the dump." }, + + { T_OPT_C, "NO_TEXT", "no-text|notext", + 0, + "Suppress the text column of the dump." }, + + { T_OPT_C, "FORMAT", "format", + 0, + "Add @'#FORMAT:'@ info lines." }, + + { T_OPT_C, "NO_NULL", "no-null|nonull", + 0, + "Ignore data lines with NULL bytes only and" + " print a summary for each NULL block. (@'#NULL: ...'@)" }, + + { T_OPT_C, "NARROW", "narrow", + 0, + "Suppress the spaces that are usually inserted for readability." }, + + { T_OPT_C, "SMALL", "small", + 0, + "Each call will halve the number of bytes per line." + " Usually, 16 bytes per lines are reduced to 8, then to 4, 2 and 1." + " The effective number of bytes per line is format dependent." }, + + { T_OPT_C, "WIDE", "wide", + 0, + "Each call will round down the current bytes per line value" + " to the next multiple of 16 and then increment it by 16." + " Usually, 16 bytes per lines are enhanced to 32," + " then to 48, 64, 80 and so on." + " The effective number of bytes per line is format dependent." }, + + { T_OPT_CP, "BYTES", "bytes", + "num", + "Print up to @NUM@ bytes per line instead of 16." + " The maximum allowed value is " + CONVERT_TO_STRING(XDUMP_MAX_BYTES_PER_LINE) "." + " The effective number of bytes per line is format dependent" + " (multiple of format size)." }, + + + // + //---------- wstrt GROUP DEST ---------- + + { T_GRP_BEG, "DEST", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "ESC", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + { T_COPT, "NUMBER", 0,0,0 }, + { T_COPT, "REMOVE_DEST", 0,0,0 }, + { T_COPT, "UPDATE", 0,0,0 }, + { T_COPT, "PRESERVE", 0,0,0 }, + + //---------- wstrt GROUP PATCH ---------- + + { T_GRP_BEG, "PATCH", 0,0,0 }, + + { T_COPT, "TRACKS", 0,0,0 }, + { T_COPT, "ARENAS", 0,0,0 }, + { T_COPT, "REGION", 0,0,0 }, + { T_COPT, "VS_REGION", 0,0,0 }, + { T_COPT, "BT_REGION", 0,0,0 }, + { T_COPT, "ALL_RANKS", 0,0,0 }, + { T_COPT, "HTTPS", 0,0,0 }, + { T_COPT, "DOMAIN", 0,0,0 }, + { T_COPT, "WIIMMFI", 0,0,0 }, + { H_COPT, "TWIIMMFI", 0,0,0 }, + { T_COPT, "PB_MODE", 0,0,0 }, + { T_COPT, "WC24", 0,0,0 }, + { H_COPT, "WCODE", 0,0,0 }, + { T_COPT, "VS", 0,0,0 }, + { H_COPT, "VS2", 0,0,0 }, + { T_COPT, "BT", 0,0,0 }, + { H_COPT, "BT2", 0,0,0 }, + { T_COPT, "CANNON", 0,0,0 }, + { T_COPT, "MENO", 0,0,0 }, + + //---------- wstrt GROUP HEXDUMP ---------- + + { T_GRP_BEG, "HEXDUMP", 0,0,0 }, + { T_COPT, "INT1", 0,0,0 }, + { T_COPT, "INT2", 0,0,0 }, + { T_COPT, "INT3", 0,0,0 }, + { T_COPT, "INT4", 0,0,0 }, + { T_COPT, "INT5", 0,0,0 }, + { T_COPT, "INT6", 0,0,0 }, + { T_COPT, "INT7", 0,0,0 }, + { T_COPT, "INT8", 0,0,0 }, + { T_COPT, "FLOAT", 0,0,0 }, + { T_COPT, "DOUBLE", 0,0,0 }, + { T_COPT, "LE", 0,0,0 }, + { T_COPT, "BE", 0,0,0 }, + { T_COPT, "ZEROS", 0,0,0 }, + { T_COPT, "HEX", 0,0,0 }, + { T_COPT, "DEC", 0,0,0 }, + { T_COPT, "C_SYNTAX", 0,0,0 }, + { T_COPT, "ADDR", 0,0,0 }, + { T_COPT, "ALIGN", 0,0,0 }, + { T_COPT, "TRIGGER", 0,0,0 }, + { T_COPT, "NO_ADDR", 0,0,0 }, + { T_COPT, "NO_NUMBERS", 0,0,0 }, + { T_COPT, "NO_TEXT", 0,0,0 }, + { T_COPT, "FORMAT", 0,0,0 }, + { T_COPT, "NO_NULL", 0,0,0 }, + { T_COPT, "NARROW", 0,0,0 }, + { T_COPT, "SMALL", 0,0,0 }, + { T_COPT, "WIDE", 0,0,0 }, + { T_COPT, "BYTES", 0,0,0 }, + + // + //---------- COMMAND wstrt VERSION ---------- + + { T_CMD_BEG, "VERSION", 0,0,0 }, + + { T_COPT, "BRIEF", 0,0, + "Print only the version number, revision and system." + " If set twice, print only the version number." }, + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print in long format. Ignored if option {--sections} is set." }, + + //---------- COMMAND wstrt HELP ---------- + + { T_CMD_BEG, "HELP", 0,0,0 }, + + { T_COPT, "WIDTH", 0,0,0 }, + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wstrt ARGTEST ---------- + + { T_CMD_BEG, "ARGTEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wstrt TEST ---------- + + { T_CMD_BEG, "TEST", 0,0,0 }, + + { T_ALL_OPT, 0, 0,0,0 }, + + //---------- COMMAND wstrt COLORS ---------- + + { T_CMD_BEG, "COLORS", 0,0,0 }, + + { T_COPT, "COLORS", 0,0,0 }, + { T_COPT, "NO_COLORS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print also a list of all colored text types." + " If set twice, include alternative names." + " If set three times, include raw color names." }, + { T_COPT, "BRIEF", 0,0, + "Ignore option --long and print a single status line about current color settings." }, + + //---------- COMMAND wstrt ERROR ---------- + + { T_CMD_BEG, "ERROR", 0,0,0 }, + + { T_COPT, "SECTIONS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print message text instead of message name." }, + { T_COPT, "BRIEF", 0,0, + "Print message number instead of message name." }, + + //---------- COMMAND wstrt FILETYPE ---------- + + { T_CMD_BEG, "FILETYPE", 0,0,0 }, + + { T_COPT, "LONG", 0,0, + "Print an additional column with the decompressed file type and version." + " If entered twice, a validation status column is added." }, + { T_COPT, "IGNORE", 0,0,0 }, + + //---------- COMMAND wstrt FILEATTRIB ---------- + + { T_CMD_BEG, "FILEATTRIB", 0,0,0 }, + + { T_COPT, "NO_HEADER", 0,0,0 }, + + //---------- COMMAND wstrt _RAWDUMP ---------- + + { T_CMD_BEG, "RAWDUMP", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + + //---------- COMMAND wstrt DUMP ---------- + + { T_CMD_BEG, "DUMP", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "For DOL files:" + " If set, a second table with the image map is printed." + " If set twice, a third table with delta values is printed too." }, + + //---------- COMMAND wstrt HEXDUMP ---------- + + { T_CMD_BEG, "HEXDUMP", 0,0,0 }, + { T_COPT, "VADDR", 0,0,0 }, + { T_COPT, "FADDR", 0,0,0 }, + { T_COPT, "SNAME", 0,0,0 }, + { T_COPT, "LOGGING", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPY_GRP, "HEXDUMP", 0,0,0 }, + + //---------- COMMAND wstrt CDUMP ---------- + + { T_CMD_BEG, "CDUMP", 0,0,0 }, + + //---------- COMMAND wstrt HTTPS ---------- + + { T_CMD_BEG, "HTTPS", 0,0,0 }, + { T_COPT, "LONG", 0,0, + " Print output a C source" }, + { T_COPT, "VERBOSE", 0,0, + " Print hex dump if an entry differs." }, + + //---------- COMMAND wstrt EXTRACT ---------- + + { T_CMD_BEG, "EXTRACT", 0,0,0 }, + + { T_COPT, "DEST", 0,0,0 }, + { T_COPT, "DEST2", 0,0,0 }, + { T_COPT, "OVERWRITE", 0,0,0 }, + + //---------- COMMAND wstrt TRACKS ---------- + + { T_CMD_BEG, "TRACKS", 0,0,0 }, + { T_COPT, "NO_HEADER", 0,0,0 }, + { T_COPT, "BRIEF", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Add columns for music slot and short name." }, + { T_COPT, "DE", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wstrt ARENAS ---------- + + { T_CMD_BEG, "ARENAS", 0,0,0 }, + { T_COPY_CMD, "TRACKS", 0,0,0 }, + + //---------- COMMAND wstrt FILES ---------- + + { T_CMD_BEG, "FILES", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + + //---------- COMMAND wstrt FIND ---------- + + { T_CMD_BEG, "FIND", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Add a user friendly track or arena name at the end of each line." + " If set twice, add also the file names of the track and sound files" + " before the track name." + " If set three times, add the slot index and the internal index" + " (both zero based) before the file names." }, + { T_COPT, "DE", 0,0,0 }, + + //---------- COMMAND wstrt POINTS ---------- + + { T_CMD_BEG, "POINTS", 0,0,0 }, + { T_COPT, "POINTS", 0,0,0 }, + { T_COPT, "BRIEF", 0,0, + "Print the table as comma separated lists in C notation." }, + { T_COPT, "LONG", 0,0, + "Append a string in {--points} format that defines the table." + " If set twice, append up to 3 strings in different modes." }, + { H_COPT, "CHEAT", 0,0,0 }, + + //---------- COMMAND wstrt ANALYZE ---------- + + { T_CMD_BEG, "ANALYZE", 0,0,0 }, + { T_COPT, "CLEAN_DOL", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + { T_COPT, "LONG", 0,0, + "Print different lists if bugs found." + " If set twice, print the lists always." }, + + //---------- COMMAND wstrt PATCH ---------- + + { T_CMD_BEG, "PATCH", 0,0,0 }, + + { T_COPY_GRP, "DEST", 0,0,0 }, + { T_COPT, "IGNORE", 0,0,0 }, + { T_SEP_OPT, 0,0,0,0 }, + { T_COPT, "CLEAN_DOL", 0,0,0 }, + { T_COPY_GRP, "PATCH", 0,0,0 }, + { H_COPT, "ADD_LECODE", 0,0,0 }, + { T_COPT, "ADD_CTCODE", 0,0,0 }, + { T_COPT, "CT_DIR", 0,0,0 }, + { T_COPT, "MOVE_D8", 0,0,0 }, + { T_COPT, "ADD_SECTION", 0,0,0 }, + { H_COPT, "ADD_WCODE", 0,0,0 }, + { H_COPT, "FULL_GCH", 0,0,0 }, + { H_COPT, "GCT_NO_SEP", 0,0,0 }, + { H_COPT, "GCT_SEP", 0,0,0 }, + { H_COPT, "GCT_ASM_SEP", 0,0,0 }, + { H_COPT, "GCT_LIST", 0,0,0 }, + { T_COPT, "GCT_MOVE", 0,0,0 }, + { T_COPT, "GCT_ADDR", 0,0,0 }, + { T_COPT, "GCT_SPACE", 0,0,0 }, + { T_COPT, "ALLOW_USER_GCH",0,0,0 }, + { T_COPT, "FORCE", 0,0, + "Accept overlapped sections.\n>>> THE RESULT IS MOST LIKELY INVALID! <<<" }, + { H_COPT, "CREATE_SECT", 0,0,0 }, + { H_COPT, "WPF", 0,0,0 }, + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// E N D /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/ui/tab-wszst.inc b/project/src/ui/tab-wszst.inc new file mode 100644 index 0000000..4dcea57 --- /dev/null +++ b/project/src/ui/tab-wszst.inc @@ -0,0 +1,3238 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +// +///////////////////////////////////////////////////////////////////////////// +///////////// wszst ///////////// +///////////////////////////////////////////////////////////////////////////// + + { T_DEF_TOOL, "wszst", 0, + "wszst [option]... command [option|parameter|@file]...", + WSZST_LONG " :" + " List, compare, (de-)compress, extract and create" + " " ARCHIVES_AND " archives." + " It is also a wrapper to all other tools." }, + + // + //---------- list of all wszst commands ---------- + + { T_DEF_CMD, "VERSION", "VERSION", + "wszst VERSION [ignored]...", + "Print program name and version and exit." }, + + { T_DEF_CMD, "HELP", "HELP|H|?", + "wszst HELP [--width=wd] OPT|OPTION [option]...\n" + "wszst HELP [--width=wd] CMD|COMMAND ...\n" + "wszst HELP [--width=wd] command ...\n" + "wszst HELP [--width=wd] ...", + "Print help for commands and options." + "\f" // <-------- separator: before built-in help & below full help + "Print help for commands and options." + "\n " + " The first variant (with keyword @OPT@ or @OPTION@)" + " prints a list of all global and command specific options." + " If at least one option is set," + " only info about the declared options are printed." + " The second variant (with keyword @CMD@ or @COMMAND@)" + " prints an overview about all commands." + " The third variant (with a valid command name)" + " prints details about the declared command." + " The fourth variant prints an overview about all commands" + " and all global options." }, + + { T_DEF_CMD, "ARGTEST", "ARGTEST", + "wszst ARGTEST ...", + "This debug command accepts all kinds of parameters" + " and prints one line for each parameter." }, + + { T_DEF_CMD, "TEST", "TEST", + "wszst TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters" + " and prints a list of the internal option values." + " The command is designed to test the option parameters." + " The non option parameters are completely ignored." }, + + { T_DEF_CMD, "COLORS", "COLORS", + "wszst COLORS", + "Ignore all parameters and print colored text for testing." }, + + { T_DEF_CMD, "ERROR", "ERROR|ERR", + "wszst ERROR [error_code]", + "Translate an exit code to a message name." + " If no exit code is entered, print a table with all error messages." }, + + { T_DEF_CMD, "FILETYPE", "FILETYPE|FT", + "wszst FILETYPE file...", + "Scan the header of the entered files and print file type" + " and path for each file as list." }, + + { T_DEF_CMD, "FILEATTRIB", "FILEATTRIB|FA", + "wszst FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types." + " If at least one keyword is entered, only matching file types" + " are printed." + " A keyword is either a file type (like 'BRRES' or 'BMG')" + " or one of the printed keywords (like 'ARCHIVE' or 'EXTRACT')." }, + + { T_DEF_CMD, "BRSUB", "BRSUB", + "wszst BRSUB [file]...", + " If no file is defined, print a table with known BRRES" + " sub files consisting of" + " file format, version, number of sections and warn level." + " Otherwise analyse the version numbers of each BRRES sub file" + " of each source and print warnings about invalid versions." }, + + { T_DEF_CMD, "SYMBOLS", "SYMBOLS", + "wszst SYMBOLS", + "Print predefined symbols of text parser to standard output." }, + + { T_DEF_CMD, "FUNCTIONS", "FUNCTIONS", + "wszst FUNCTIONS [key]...", + "Print a list with all parser functions." + " If one or more keys entered," + " then only functions that match at least one key are printed." }, + + { T_DEF_CMD, "CALCULATE", "CALCULATE", + "wszst CALCULATE expression...", + "Calculate each expression and print the result to standard output." }, + + { T_DEF_CMD, "MATRIX", "MATRIX", + "wszst MATRIX [patch-option]... [vector]...", + "Use the transformation options as base and calculate and print" + " the related vectors and the transformation matrices." + " Then do a forward and backward transformation for each @vector@" + " parameter using the matrices and print the results." + "\f" // <-------- separator: before built-in help & below full help + "Use the transformation options as base and calculate and print" + " the related vectors and the transformation matrices." + " Then do a forward and backward transformation for each @vector@" + " parameter using the matrices and print the results." + " The transformations are done with 4 different methods" + " to confirm the algorithms." + " Only the main results and results, which differ, are printed." + "\n" + " Normally, the exit status is @OK@ (null)." + " If at least one old transformation returns different results" + " for the different algorithms," + " then status @DIFFER@ (not null) is returned." }, + + { T_DEF_CMD, "FLOAT", "FLOAT", + "wszst FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa." + " For each non empty parameter, a line with 4 bytes hex and a float," + " 8 bytes hex and a double float, and the parameter itself is printed." + " If a parameter begins with 'x' or '0x', it is a hex string," + " otherwise it is a floating point number." + "\n " + " The special parameters \"+\" and \"-\" mean:" + " Increment or decrement the previous value by the smallest" + " possible value. Use different increments for floats and doubles." + "\n " + " All conversions are done using big endian." }, + + { T_DEF_CMD, "VR_CALC", "VR-CALC|VRCALC", + "wszst VR-CALC delta...", + " Calculate a VR increment for each given delta value" + " (@=VR_winner-VR_loser@). Print results as table." }, + + { T_DEF_CMD, "VR_RACE", "VR-RACE|VRRACE", + "wszst VR-RACE vr1 vr2...", + "Print a table with VR calculations for a race." + " Each @VR@ is the current rating of a player." + " The vr-list is well ordered: " + " The first VR is for the winner and the last VR for the looser." + " Up to 30 VR values will be accpeted." + " Each VR can be preceeded by @\"NUM*\"@ to define a repeat factor." }, + + { T_DEF_CMD, "AUTOADD", "AUTOADD", + "wszst AUTOADD [source]...", + "Manage the auto-add library." + " Primarily add files to the main auto-add archive for future" + " use of option {--auto-add} or to convert WU8 files." + " If @SOURCE@ is a directory, all standard track and arena" + " file names are searched for the missed auto-add files." + " Without source files, the status of the auto-add archive is printed." + "\n " + " Try {wszst AUTOADD ./path_to/Race/Course/}" + " to add all supported files." }, + + { T_DEF_CMD, "TRACKS", "TRACKS", + "wszst TRACKS [selection_mask]", + "Print a list of all known track SZS files of Mario Kart" + " internal directory './Race/Course'." + " These files are 32 standard track files and 10 arena files." + " The first column shows an unique ID of exact 3 upper case" + " characters or digits. For '_d' files, a 'd' is appended." + " The second column shows the internal file name." + " Use BMG files to print real names as third column." }, + + { T_DEF_CMD, "SCANCACHE", "SCANCACHE", + "wszst SCANCACHE directory\n" + "wszst SCANCACHE --cache directory", + "Scan the cache directory for SZS files and re-build the" + " checksum table. Store the result as file 'cache-content.txt'" + " in the same directory." + " The file is used by option {--cache} to optimize file access." }, + + { H_DEF_CMD, "EXPORT", "_EXPORT", + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output." + " Use keyword @HELP@ to get a list of possible keywords." }, + + { H_DEF_CMD, "CODE", "_CODE", + "wszst _CODE [files]", + "All bytes of all input files are xor'ed with 0xDC and copied to stdout." + " If no source file is given, standard input is read." }, + + { H_DEF_CMD, "RECODE", "_RECODE", + "wszst _RECODE mode [files]", + "All bytes of all input files are interpreted as 1 file and re-coded." + " @MODE@ defines the re-code mode." + " If no source file is given, standard input is read." }, + + { H_DEF_CMD, "SUBFILE", "_SUBFILE", + "wszst _SUBFILE [files]", + "Test sub-file system." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "LIST", "LIST|LS", + "wszst LIST [source]...", + "List all sub files of " ARCHIVES_AND " archives." + " If listing a directory, then a virtual SZS file" + " is created (like CREATE does it) and listed." }, + + { T_DEF_CMD, "LIST_L", "LIST-L|LL|LISTL", + "wszst LIST-L [source]...", + "List all sub files with size." + " 'LIST-L' and 'LL' are short cuts for {LIST --long}." }, + + { T_DEF_CMD, "LIST_LL", "LIST-LL|LLL|LISTLL", + "wszst LIST-LL [source]...", + "List all sub files with offset and size." + " 'LIST-LL' and 'LLL' are short cuts for {LIST --long --long}." }, + + { T_DEF_CMD, "LIST_LLL", "LIST-LLL|LLLL|LISTLLL", + "wszst LIST-LLL [source]...", + "List all sub files with offset and size in debugging mode." + " 'LIST-LLL' and 'LLLL' are short cuts" + " for {LIST --long --long --long}." }, + + { T_DEF_CMD, "LIST_A", "LIST-A|LA|LISTA", + "wszst LIST-A [source]...", + "List recursive all sub files with size." + " 'LIST-A' and 'LA' are short cuts for {LIST --long --all --all}." }, + + { T_DEF_CMD, "LIST_LA", "LIST-LA|LLA|LISTLA", + "wszst LIST-LA [source]...", + "List recursive all sub files with offset and size." + " 'LIST-LA' and 'LLA' are short cuts" + " for {LIST --long --long --all --all}." }, + + { T_DEF_CMD, "NAME_REF", "NAME-REF|NAMEREF", + "wszst NAME-REF [source]...", + "For each BRRES file or subfile, create a name based cross reference" + " and print the result as list ordered by names in BRRES order." + " For U8 archives (e.g. track SZS files)," + " sub file @course_model.brres@ is analysed." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "ILIST", "ILIST|IL", + "wszst ILIST [source]...", + "List the images files of all sub files of " ARCHIVES_AND + " archives; non image files are ignored." + " If listing a directory, then a virtual SZS file" + " is created (like CREATE does it) and listed." }, + + { T_DEF_CMD, "ILIST_L", "ILIST-L|ILL|ILISTL", + "wit ILIST-L [source]...", + "List all image files with geometry data." + " 'ILIST-L' and 'ILL' are short cuts for {ILIST --long}." }, + + { T_DEF_CMD, "ILIST_LL", "ILIST-LL|ILLL|ILISTLL", + "wit ILIST-LL [source]...", + "List all sub files with advanced geometry data." + " 'ILIST-LL' and 'ILLL' are short cuts for {ILIST --long --long}." }, + + { T_DEF_CMD, "ILIST_A", "ILIST-A|ILA", + "wszst ILIST-A [source]...", + "List recursive all image files." + " 'ILIST-A' and 'ILA' are short cuts for {ILIST --all}." }, + + { T_DEF_CMD, "ILIST_LA", "ILIST-LA|ILLA|ILISTLA", + "wit ILIST-LA [source]...", + "List recursive all image files with geometry data." + " 'ILIST-LA' and 'ILLA' are short cuts for {ILIST --long --all}." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "MEMORY", "MEMORY|MEM", + "wszst MEMORY [source]...", + "Dump a memory map of each source file." + " Virtual images (extracted directories) are also supported." }, + + { T_DEF_CMD, "MEMORY_A", "MEMORY-A|MEMA", + "wszst MEMORY-A [source]...", + "Dump recursive a memory map of each source file." + " 'MEMORY-A' and 'MEMA' are short cuts for {MEMORY --all --all}." }, + + { T_DEF_CMD, "DUMP", "DUMP", + "wszst DUMP [source]...", + "Dump the internal structure of a BRRES files, compressed or not." + " Other archives are ignored." + " The main goal is to verify string relocation." + " Virtual images (extracted directories) are also supported." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "SHA1", "SHA1", + "wszst SHA1 [source]...", + "Print a SHA1 checksum for each source." + " The checksum is calculated by using the uncompressed data." }, + + { T_DEF_CMD, "ANALYZE", "ANALYZE|ANA|ANALYSE", + "wszst ANALYZE [source]...", + "Analyse track files and print results in machine readable format." }, + + { T_DEF_CMD, "DISTRIBUTION", "DISTRIBUTION|DIST", + "wszst DISTRIBUTION result source...\n" + "wszst DISTRIBUTION directory [source]...", + "Load each @SOURCE@ (SZS, WBZ, U8 or WU8) and calculate a checksum" + " of the uncompressed data." + " If @SOURCE@ is a directory, scan it for SZS, WBZ, U8 and WU8 files." + " If a source directory starts with \"arena\"," + " all tracks of the directory are assumed to be battle arenas." + " Finally print the results into file @RESULT@." + "\n " + " The default name for @RESULT@ is \"distribution.txt\"." + " Extension .txt is appended to the filename if not defined." + " If @RESULT@ is a directory, it is used as first source too." + "\n " + " If file @RESULT@ exists, it is automatically added as last source" + " to the list of option --source, but only if not already added." + " On scanning of file @result@, parameters are scanned too." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "DIFF", "DIFF|CMP", + "wszst DIFF source1 source2\n" + "wszst DIFF source... --dest dest", + "Compare two or more sources (each " ARCHIVES_COMMA " or directory)" + " on file level and report mismatches for each sub file." + " If option {--dest} is set, then each source file" + " is compared with a file in the destination." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "CHECK", "CHECK", + "wszst CHECK [source]...", + "Make a validity check for track files:" + " Try to read KCL and KMP (and check both) and find unknown, modified," + " needed and unneeded files." + " Also analyse cannons and make slot proposals." + " This command uses an internal object and file database." + " The source can be a SZS, U8, WU8 or WBZ file, or an U8 like directory." }, + + { T_DEF_CMD, "SLOTS", "SLOTS", + "wszst SLOTS [source]...", + "Try to find out the slots, that will run a track." + " Each source can be a SZS, U8, WU8 or WBZ file, or an U8 like directory." + " For each source one status line is printed." + " The status line contains either a positive or a negative" + " slot list, whatever is shorter." + " For battle arenas, the keyword @ARENA@ is printed." }, + + { T_DEF_CMD, "STGI", "STGI", + "wszst STGI [source]...", + "Find the first KMP:STGI of each file" + " and print an analysis of it as single line statistic." + " Additional the number of lap counter check points ('LC') is printed." + " Track and KMP (binary and text) files are accepted as input." }, + + { T_DEF_CMD, "ISARENA", "ISARENA", + "wszst ISARENA [source]...", + "Analyse each track for its arena status and print a line with" + " a status code and the track name." + " The status code is: »-RACE« for a racing track," + " »?MAYBE« for likely an arena, »+ARENA« for an arena," + " »+DISPATCH« for an arena with dispatch points" + " or any error hint beginning with an »!«." + " So the very first character (one of »!-?+«)" + " can be used to distinguish the status class." + "\n " + " The exit status is >2 on error," + " 2 (DIFFER), if at least one file is a racing track," + " or 0 (OK), if all sources are arenas." }, + + { T_DEF_CMD, "NORMALIZE", "NORMALIZE|NORM", + "wszst NORMALIZE [source]...", + "Normalize an U8 archive, compressed or not:" + " The files are sorted in `Nintendo like' order" + " and unused data holes are removed." + " If directory '.' is available it becomes the root directory" + " for all others. Compressed sources are compressed again with" + " maximum compression (level 9)." + " The destination is only written if any data changed." + "\n " + " {wszst NORMALIZE} is the same as {wszst PATCH --norm}." }, + + { T_DEF_CMD, "PATCH", "PATCH", + "wszst PATCH [source]...", + "Patch an U8 archive, compressed or not:" + " Load the archive, apply all patching and transforming options" + " and store the file again." + " Compressed sources are compressed again." + " The destination is only written if any data changed." }, + + { T_DEF_CMD, "COPY", "COPY|CP", + "wszst COPY source dest\n" + "wszst COPY source... [-d|-D] dest", + "COPY is an alternative command for copying and patching files." + " It uses the same internal procedure as command PATCH," + " but has another command line syntax." + "\n " + "The command copies all source files to the destination." + " If more than 1 source is defined, the destination must be" + " either a directory or a destination filename with escapes." + " Patching is also possible and option --overwrite is set implicitly." }, + + { T_DEF_CMD, "DUPLICATE", "DUPLICATE|DUP", + "wszst DUPLICATE source [-d|-D] dest", + "The command reads a single track file and duplicates it several" + " times to DEST. Therefor it analyzes the presence flags combinations" + " with and without extension support and creates one file for each" + " relevant scenario. A LEX section TEST will be inserted to force" + " the execution mode for offline tests." + "\n " + " The destination filename (not directory part) is searched" + " for the first occurance of @'@@'@." + " If not found, the place before the last point is used." + " At this place an identification string is inserted." + "\n " + " Depending on option {--gamemodes} up to " MAX_GAMEMODES + " files will be created. So the use of option {--fast}" + " is recommended to speed up the process." + " Use command {wkmpt GAMEMODES} to list all variants" + " and to check them beforehand." }, + + { T_DEF_CMD, "MINIMAP", "MINIMAP", + "wszst MINIMAP [source]...", + "Print minimap data (more exact: the visual field of the minimap)" + " of a SZS, U8, BRRES or MDL file." + " If patching options are used, modify the data and write back" + " the file using its original file format." + " Only the translation vector is modified and" + " new translation and inverse matrices are calculated." }, + + { T_DEF_CMD, "COMPRESS", "COMPRESS|COM", + "wszst COMPRESS [source]...", + "Compress and create a SZS archives." + " If the source is already compressed," + " then decompress and compress it again." + " The default destination is @'%P/%N.szs'@." }, + + { T_DEF_CMD, "DECOMPRESS", "DECOMPRESS|DEC", + "wszst DECOMPRESS [szs]...", + "Decompress SZS files." + " The default destination is @'%P/%N.u8'@." }, + + { T_DEF_CMD, "ENCODE", "ENCODE|E", + "wszst ENCODE [source_dir]...", + " Create and/or encode sub files if necessary." + " It works like command CREATE," + " but without creating a new first level archive file." }, + + { T_DEF_CMD, "CREATE", "CREATE|C", + "wszst CREATE [source_dir]...", + "Create " ARCHIVES_OR " archives by adding all not hidden files" + " of the directory structure." + " Create and/or encode sub files if necessary." + " The default destination is @'%P/%N%T'@." }, + + { T_DEF_CMD, "UPDATE", "UPDATE|U", + "wszst UPDATE [source]... [--source=] [--dest=]", + "Update an existing " ARCHIVES_AND "archive." + " This is done in similar way as the {EXTRACT} command," + " but instead of extracting, source files are searched" + " in a prior extracted archive." + " Only files with the same size are updated." + " The secondary source is used to find the source sub files." + " The default secondary source is @'%P/%N.d/'@." }, + + { T_DEF_CMD, "EXTRACT", "EXTRACT|X", + "wszst EXTRACT [source]...", + "Extract all sub files of " ARCHIVES_AND " archives." + " The default destination is @'%P/%N.d/'@." }, + + { T_DEF_CMD, "XDECODE", "XDECODE|XD", + "wszst XDECODE [source]...", + "Command @XDECODE@ is a short cut for {EXTRACT --decode}." + " It extract all sub files of " ARCHIVES_AND " archives" + " and decodes all supported files." + " The default destination is @'%P/%N.d/'@." }, + + { T_DEF_CMD, "XEXPORT", "XEXPORT|XX", + "wszst XEXPORT [source]...", + "Command @XEXPORT@ is a short cut for {EXTRACT --decode --export}." + " It extract all sub files of " ARCHIVES_AND " archives" + " and decodes all supported files with enabled export mode." + " The default destination is @'%P/%N.d/'@." }, + + { T_DEF_CMD, "XALL", "XALL|XA", + "wszst XALL [source]...", + "Command @XALL@ is a short cut for {EXTRACT --all}." + " It extract all sub files of " ARCHIVES_AND " archives" + " recursively and decodes all supported files." + " The default destination is @'%P/%N.d/'@." }, + + { T_DEF_CMD, "XCOMMON", "XCOMMON", + "wszst XCOMMON [source]...", + "Command @XCOMMON@ is a short cut for {EXTRACT --basedir=common}." + " Scan archives and extract files from archives, that are below" + " directory @'common'@." + " The default destination is @'%P/%N.d/'@." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { T_DEF_CMD, "BINARY", "BINARY|BIN", + "wszst BINARY [source]...", + "Read each source file (binary or text, but never archives)" + " and write the content as binary file." + " The file extension of the new file depends on the file type," + " but is usually @.bin@." + " Currently supported file formats are @BMG@, @GEOHIT@, @ITEMSLOT@," + " @KCL@, @KMG@, @KMP@, @LEX@, @OBJFLOW@, @PAT@" + " and the related text formats." }, + + { T_DEF_CMD, "TEXT", "TEXT|TXT", + "wszst TEXT [source]...", + "Read each source file (binary or text, but never archives)" + " and write the content as text file." + " The file extension of the new file is usually @.txt@." + " Currently supported file formats are @BMG@, @GEOHIT@, @ITEMSLOT@," + " @KCL@, @KMG@, @KMP@, @LEX@, @OBJFLOW@, @PAT@" + " and the related text formats." }, + + { T_DEF_CMD, "CAT", "CAT", + "wszst CAT [source/subfile]...", + "Extract one or more sub files of " ARCHIVES_AND " archives." + " The output of all source files is concatenated and written to stdout." }, + + { T_DEF_CMD, "BMG", "BMG", + "wszst BMG [source]...", + "For each archive all BMG sub files are searched" + " and, if found, decoded and printed as combined BMG text file." + " The command works exactly like {wbmgt MIX}." }, + + { T_DEF_CMD, "KCL", "KCL", + "wszst KCL [source]...", + "For each archive the KCL sub file @course.kcl@ is searched" + " and, if found, decoded and printed (copied to stdout)." + "\n " + " Automated dropping of bad KCL triangles is enabled" + " if no other drop option is specified by option {--kcl}." }, + + { T_DEF_CMD, "KMP", "KMP", + "wszst KMP [source]...", + "For each archive the KMP sub file @course.kmp@ is searched" + " and, if found, decoded and printed (copied to stdout)." }, + + { T_DEF_CMD, "LEX", "LEX", + "wszst LEX [source]...", + "For each archive the LEX sub file @course.lex@ is searched" + " and, if found, decoded and printed (copied to stdout)." }, + + { T_DEF_CMD, "INFO", "INFO", + "wszst INFO [source]...", + "For each archive a file named @info.txt@ and, if not found," + " @credits.txt@ is searched, ignoring case." + " The first found file of each archive is printed (copied to stdout)." }, + + { H_DEF_CMD, "GHOST", "GHOSTS", + "wszst GHOST [source]...", + "Analyse each file and print a summary about the ghost data." }, + + { T_DEF_CMD, "YAZDUMP", "YAZDUMP|YD", + "wszst YAZDUMP [szs]...", + "Analyze YAZ compression print a dump to stdout." + " Ignore files without YAZ compression." }, + + { T_SEP_CMD, 0,0,0,0 }, //----- separator ----- + + { H_DEF_CMD, "VEHICLE", "VEHICLE", + "wszst VEHICLE job ...", + "Hidden vehicle settings command." }, + + // + //---------- list of all wszst options ---------- + + { T_OPT_S, "VERSION", "V|version", + 0, + "Stop parsing the command line, print a version info and exit." }, + + { T_OPT_S, "HELP", "h|help", + 0, + "Stop parsing the command line, print a help message and exit." }, + + { T_OPT_S, "XHELP", "xhelp", + 0, + "Stop parsing the command line and print a help message" + " with all commands included. Exit after printing." }, + + { T_OPT_G, "ALLOW_ALL", "allow-all|allowall", + 0, + "Usually commands accept only options with impact to the command." + " All other options fire a syntax error." + " But if @--allow-all@ is set, all commands accept all options." + "\n " + " This makes changing the command of a long command line" + " without removing useless options easier." + " It also helps to override wrong option permissions." }, + + { T_OPT_GP, "COMPATIBLE", "compatible", + "vers", + "The option expects a version number (format @'#.##'@ or @'v#.##'@)" + " or a revision number (format @'r#'@) as parameter." + " If set, the tools try to create BMG and KMP text files," + " that are compatible to the entered version of the tools." + " This may override other legacy options." }, + + { T_OPT_GP|F_IGNORE, + "WIDTH", "width", + "width", + "Define the terminal width (number of columns)" + " for help and some other messages" + " and disable the automatic detection of the terminal width." }, + + { T_OPT_GM, "QUIET", "q|quiet", + 0, + "Be quiet and print only error messages." + " Multiple usage is possible." + " The impact is command dependent." + " In general: If set three times," + " different file format warnings are suppressed." }, + + { T_OPT_GM, "VERBOSE", "v|verbose", + 0, + "Be verbose and print more progress information." + " Multiple usage is possible." + " The impact is command dependent." }, + + { T_OPT_GM, "LOGGING", "L|logging", + 0, + "This debug option enables the logging of internal lists and maps." }, + + { T_OPT_G, "DE", "de", + 0, + "Use german track and arena names." }, + + { T_OPT_GO|F_IGNORE, + "COLORS", "colors", + "[=modus]", + "Define the modus for colored text output." + " Allowed keywords are:" + " @OFF@ or @NO-COLORS@ to disable colors," + " @AUTO@ (default) for automatic detection," + " @ON@ for automatic detection but never @OFF@," + " @8-COLORS@ and @256-COLORS@ to force 8 or 256 color support." + " Without parameter, @ON@ is used." + "\n " + " @AUTO@ will enable colorized output only for terminals." + " @AUTO@ and @ON@ use environment variable @TERM@" + " to find the correct color modus." + "\n " + " If a command is prefixed by @'C-'@," + " then @--color=ON@ is used implicitly as default." }, + + { T_OPT_G|F_IGNORE, + "NO_COLORS", "no-colors|nocolors", + 0, + "Deactive colorized text." + " This is the default, if an output file is not a terminal." }, + + { T_OPT_G, "CT_CODE", "ct-code|ctcode", + 0, + "Enable CT-CODE support." + "\1 See https://szs.wiimm.de/opt/ctcode for details." }, + + { T_OPT_GO, "LE_CODE", "le-code|lecode", + "[=param]", + "Enable LE-CODE and CT-CODE support." + " The optional parameter is deprecated and ignored." + "\1 See https://szs.wiimm.de/opt/lecode for details." }, + + { T_OPT_GMP, "CHDIR", "chdir", + "dir", + "Set a new working directory for all following options," + " for all parameters and for the command execution." + " This option can be used multiple times," + " but for parameter parsing and for command execution only" + " the last usage is relevant." + " Nevertheless, @'@@param'@ values are scanned while reading" + " the command line." }, + + { T_OPT_GMP, "CONST", "c|const", + "list", + "Define constant values, that are used by the internal encoders" + " and by the numeric options as predefined global variables." + " This option allows a conditional encoding of text files." + " It can be used multiple times for multiple definitions." + "\n " + " The parameter is a comma separated list of terms" + " and a term is '@name=expression@'." + " The expression is calculated by the text parser." }, + + { T_OPT_C, "TOUCH", "touch", + 0, + "Mark the minimap as modified to force a new calculation" + " of the translation and inverse matrices." }, + + { T_OPT_C, "AUTO", "auto", + 0, + "Calculate the minimap translations automatically by" + " using the minimum and maximum vertex coordinates." + " The 'scale' and 'rotation' fields are reset." + " This is done as first action" + " before the SET, CENTER and the other transformations." }, + + { T_OPT_CP, "SET_FLAGS", "set-flags|setflags", + "flag", + "Define the flag values (default 0x31f) of the minimap." }, + + { T_OPT_CP, "SET_SCALE", "set-scale|setscale", + "vector", + "Define the scale values (default 1.0) of the minimap." + " The parameter is either a vector expression" + " or a comma separated list of coordinates ('x,z' or 'x,y,z')." }, + + { T_OPT_CP, "SET_ROT", "set-rot|setrot", + "vector", + "Define the rotation values (default 0.0) of the minimap." + " The parameter is either a vector expression" + " or a comma separated list of coordinates ('x,z' or 'x,y,z')." }, + + { T_OPT_CP, "SET_X", "set-x|setx", + "v1[,v2]", + "Define 2 values and sort them to define the minimum" + " and maximum x-coordinates of the minimap." + " If 'v2' is not entered, '-v1' is used as second value." }, + + { T_OPT_CP, "SET_Y", "set-y|sety", + "v1[,v2]", + "Define 2 values and sort them to define the minimum" + " and maximum y-coordinates of the minimap." + " If 'v2' is not entered, '-v1' is used as second value." }, + + { T_OPT_CP, "SET_Z", "set-z|setz", + "v1[,v2]", + "Define 2 values and sort them to define the minimum" + " and maximum z-coordinates of the minimap." + " If 'v2' is not entered, '-v1' is used as second value." }, + + { T_OPT_C, "XCENTER", "xcenter", + 0, + "Center the minimap in x-direction." + " This is done after AUTO and SET, but before SCALE and SHIFT." }, + + { T_OPT_C, "YCENTER", "ycenter", + 0, + "Center the minimap in y-direction." + " This is done after AUTO and SET, but before SCALE and SHIFT." }, + + { T_OPT_C, "ZCENTER", "zcenter", + 0, + "Center the minimap in z-direction." + " This is done after AUTO and SET, but before SCALE and SHIFT." }, + + { T_OPT_C, "CENTER", "center", + 0, + "Center the minimap in all 3 directions." + " This is a short cut for {--xcenter --ycenter --zcenter}." }, + + { T_OPT_CP, "SCALE", "scale", + "vector[@origin]", + "Transform the data:" + " Scale all coordinates and size values by 'vector'" + " relative to the point 'origin'." + " If the origin is not set, @0,0,0@ is used instead." + "\n " + " Negative values mirror the coordinates." + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z')." + "\n " + " Transformations are calculated in the order" + " SCALE, SHIFT, ROTATE, TRANSLATE." }, + + { T_OPT_CP, "SHIFT", "shift", + "vector", + "Transform the data: Add 'vector' to all coordinates." + " It's simlar to {--translate}, but the addition is done before rotation." + " The parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z')." + "\n " + " Transformations are calculated in the order" + " SCALE, SHIFT, ROTATE, TRANSLATE." }, + + { T_OPT_CP, "XSS", "xss", + "x1old,x1new,x2old,x2new", + "--xss (x-scale-shift) calculates the X values of {--scale} and {--shift}," + " so that old values are transformed to the new values." + " The parameters are numbers or expression." }, + + { T_OPT_CP, "YSS", "yss", + "y1old,y1new,y2old,y2new", + "--yss (y-scale-shift) calculates the Y values of {--scale} and {--shift}," + " so that old values are transformed to the new values." + " The parameters are numbers or expression." }, + + { T_OPT_CP, "ZSS", "zss", + "z1old,z1new,z2old,z2new", + "--zss (z-scale-shift) calculates the Z values of {--scale} and {--shift}," + " so that old values are transformed to the new values." + " The parameters are numbers or expression." }, + + { T_OPT_CP, "ROT", "rot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values" + " by the angle 'degree' (is a vector) around the 3 axes." + " All 3 axes goes through the point 'origin'." + " If the origin is not set, @0,0,0@ is used instead." + "\n " + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y,z')." + "\n " + " Option --rot is an alternative for {--xrot}, {--yrot} and {--zrot}" + " to define all 3 rotations in one step." + " Transformations are calculated in the order" + " SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." }, + + { T_OPT_CP, "XROT", "xrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values" + " by the angle 'degree' around the x-axis, that goes through the" + " point 'origin'. If the origin is not set, @0,0,0@ is used instead." + "\n " + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('y,z' or 'x,y,z')." + "\n " + " Transformations are calculated in the order" + " SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." }, + + { T_OPT_CP, "YROT", "yrot|hrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values" + " by the angle 'degree' around the x-axis, that goes through the" + " point 'origin'. If the origin is not set, @0,0,0@ is used instead." + " This is a horizontal counterclockwise rotation" + " and the old option name --hrot can also used." + "\n " + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z')." + "\n " + " Transformations are calculated in the order" + " SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." }, + + { T_OPT_CP, "ZROT", "zrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values" + " by the angle 'degree' around the z-axis, that goes through the" + " point 'origin'. If the origin is not set, @0,0,0@ is used instead." + "\n " + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y' or 'x,y,z')." + "\n " + " Transformations are calculated in the order" + " SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." }, + + { T_OPT_CP, "YPOS", "ypos", + "pos", + "This option defines an Y position for KMP:CKPT transformations." + " It has only impact to X and Z rotations" + " (Options {--xrot} and {--zrot})." + " If not set, the mean y of the active rotation origins are used." }, + + { T_OPT_CP, "TRANSLATE", "translate|trans", + "vector", + "Transform the data: Add 'vector' to all coordinates." + " It's simlar to {--shift}, but the addition is done after rotation." + " The parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z')." + " @--trans@ is a short cut." + "\n " + " Transformations are calculated in the order" + " SCALE, SHIFT, ROTATE, TRANSLATE." }, + + { T_OPT_C, "NULL", "null", + 0, + "Create a neutral transformation without affecting the coordinates." + " The only influence is, that the dependent values (like maximum)" + " are re-calculated as if a transformation has taken place." }, + + { T_OPT_C, "NEXT", "next", + 0, + "Close the current transformation step with all scaling, shifting," + " rotation and translation options" + " and open a new step with cleared options." + " On transformation each step is logical done one by one." + " In real, one total transformation matrix is calculated" + " and used for fast transformations." + "\n " + " If option --next is used, the ability for modifying the scale and" + " rotation vectors (not the positions) of different KMP sections" + " is nearly always lost. A warning is printed if this occurs." }, + + { T_OPT_CP, "ASCALE", "ascale", + "factor@dir", + "Transform the data and do an axis scale:" + " Close the current transformation step like {--next} and scale the" + " data by @'factor'@ into the direction given by the vector @'dir'@." + " Store the resulting matrix as single transformation step" + " and open a new one." + "\n " + " This kind of transformation is EXPERIMENTAL!" }, + + { T_OPT_CP, "AROT", "arot", + "degree[@pos1]@pos2", + "Transform the data and do an axis rotation:" + " Close the current transformation step like {--next} and rotate" + " the data by @'degree'@ around the axis defined by the 2 points." + " If @'pos1'@ is not set, use @0,0,0@ instead." + " Store the resulting matrix as single transformation step" + " and open a new one." + "\n " + " This kind of transformation is EXPERIMENTAL!" }, + + { T_OPT_CP, "TFORM_SCRIPT", "tform-script|tformscript", + "script", + "Load the script and execute it by the text parser." + "\n" + " Then for each coordinate (2D and 3D)," + " call the macro @TRANSFORM@ after all other transformations." + " Call it without parameters" + " and set variable @$$I@ with a zero based index of all vertices," + " variable @$$D@ to the dimension (2 or 3)" + " and the variable @$$P@ to the vector of the current vertex." + " If the macro returns a vector, it is the new position." + "\n " + " For each file, macro @BEGIN@ is called at the beginning" + " and @END@ after processing all points," + " but only, if the macros are defined." + " For @END@, variable @$$I@ is set to the" + " number of processed vertices in the current file." + "\n " + " Before each macro call, the private and local variables are cleared" + " and only global parameters are permanent." + " The zero based index is cleared for each new source." + " The parameters of the macro calls are defined in the local name space." }, + + { T_OPT_C, "MINIMAP", "minimap", + 0, + "Fix the minimap position in the same way as {wszst MINIMAP --auto}." + " All other minimap related options are ignored for the minimap processing." + "\1 See https://szs.wiimm.de/cmd/wszst/minimap for details." }, + + { T_OPT_CP, "RM_GOBJ", "rm-gobj|rmgobj", + "objlist", + "Remove objects from the KMP section @GOBJ@." + " Therefor a comma separated object list with single values" + " like @'a'@ or ranges like @'a:b'@ is expected." + " @'a'@ and @'b'@ are numbers, object names (prefixed by 'o$$')" + " or any other parser expressions." }, + + { T_OPT_GO, "BATTLE", "battle", + "[=mode]", + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@." + " Without value, @ON@ is used." + "\n " + " If mode is @ON@, assume a battle arena and enable a spüecial battle support." + " If mode is @OFF@, disable special battle arena support." + " If mode is @AUTO@, analyse the KMP and estimate, if it is a battle areana." + " Modes @ON@ and @OFF@ override KMP parameter @BATTLE-MODE@." }, + + { T_OPT_GO, "EXPORT_FLAGS", "export-flags|exportflags|exflags", + "[=mode]", + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@." + " Without value, @ON@ is used." + " --exflags is an alternative name for the option." + "\n " + " If mode is @ON@, this feature is forced:" + " On creating a KMP text file, the option @EXPORT-FLAGS@ is always set to 1." + " On creating a binary KMP file, the flags are always exported as lowest byte" + " of the scale factors or x-rotation." + "\n " + " If mode is @OFF@, this feature is totally disabled." + " If mode is not @AUTO@, KMP parameter @EXPORT-FLAGS@ is overridden." }, + + // 2018-08: may be [[obsolete]] in 1 year + { T_OPT_GO, "ROUTE_OPTIONS", "route-options|routeoptions|ropt", + "[=mode]", + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@." + " Without value, @ON@ is used." + "\n " + " On mode @ON@, the new commands @$$PREV@ and @$$NEXT@ are printed" + " for each group of KMP sections ENPT and ITPT." + " On mode @AUTO@, the command is only printed," + " if any option is set or if parameter @SHOW-OPTIONS@ is >0." + " On mode @OFF@, this feature is totally disabled to create KMP text files" + " that are compatible with versions <1.60a." + "\n " + " This option exists only for a transition period." + " Its behaviour will be changed in the future." + " @--ropt@ is a short cut." + }, + + { T_OPT_GO, "WIM0", "wim0", + "[=mode]", + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@." + " Without value, @ON@ is used." + "\n " + " If mode is @ON@, a KMP section called WIM0 is added on binary KMP creation." + " The sections contains information for Wiimms tools." + "\n " + " If mode is @OFF@, this feature is totally disabled." + " If mode is not @AUTO@, KMP parameter @KMP-WIM0@ is overridden." }, + + { T_OPT_CP, "SLOT", "slot", + "keyword", + "Patch a track file to run at the specified slot." + " KMP and KCL may be modified." + " BRRES files may be added to or removed from SZS." + " The main keywords are @DAISY, DESERT, SHERBET, SHYGUY," + " STANDARD@ and @MOST@." + " Slot numbers (except '42' and '4.2')" + " and different other track names are also accepted." + " For some conversions, the auto-add library is needed." + "\1 See https://szs.wiimm.de/opt/slot for details." }, + + { T_OPT_GP, "LOAD_KCL", "load-kcl|loadkcl", + "file", + "Load a KCL or OBJ file for reference issues." }, + + { T_OPT_GP, "KCL", "kcl", + "list", + "Set global options for KCL processing." + " Parameter @'list'@ is a comma separated list of keywords." + " A minus sign before a keyword disables a setting." + " Each occurrence of the option will only change entered settings" + " and all other settings are untouched." + "\n " + " Keyword @DEFAULT@ resets the default settings" + " and @CLEAR@ disables all." + " Keywords @SMALL, MEDIUM@ and @CHARY@ select" + " the default parameters for the octree creation." + " The other allowed keywords are:" + " @FAST, NEW, CENTER, ROUND, NORMALS, MTL," + " WIIMM, TRIANGLES, OUT-SWAP, G, USEMTL, CLIP, IN-SWAP, AUTO, HEX4," + " HEX23, HEX, DROP-UNUSED, DROP-FIXED, DROP-INVALID, DROP," + " RM-FACEDOWN, RM-FACEUP, FIX-ALL," + " CONV-FACEUP, WEAK-WALLS, SORT, INPLACE, SILENT@ and @LOG@." + "\1 See https://szs.wiimm.de/opt/kcl for details." }, + + { T_OPT_CP, "KCL_FLAG", "kcl-flag|kclflag", + "joblist", + "Modify the KCL flag of KCL files." + " A comma separated job list in the format @'srclist=dest'@" + " is expected. @'srclist'@ is a plus sign separated list" + " with single values like @@, @T@ and @/@" + " or ranges like @:@, @T:@" + " and @:/@." + " @''@ is a complete KCL flag in the range of @0..0xffff@." + " @''@ are only the base types in the range of @0..0x1f@." + " If @@ is present, it marks the relevant bits." + " For the @T@ variant, the mask is set to @0x1f@." + "\n " + " All KCL flags of the source list are assigned to the new value @'dest'@," + " but only the set bits of @''@ are modified." + " A later definition overrides the previous one." + "\1 See https://szs.wiimm.de/opt/kcl-flag for details." }, + + { T_OPT_CP, "KCL_SCRIPT", "kcl-script|kclscript", + "script", + "After reading a KCL source file and executing all transformations," + " the text file @'script'@ is executed by the internal text parser." + " The script should use the parser functions @tri$$*()@" + " to read and modify the triangle positions and flags," + " or to add or remove complete triangles." + "\n " + " If this option is used multiple times," + " each script is executed in the entered order." + "\1 See https://szs.wiimm.de/opt/kcl-script for details." }, + + { T_OPT_GP, "TRI_AREA", "tri-area|triarea", + "EXPR", + "Define the minimal area size of KCL triangles." + " The intention is to ignore triangles that are generally to small." + " @EXPR@ is a floating point number or expression." + " Triangles are invalidated if their area size is smaller than @EXPR@." + " The test is executed after reading files, after transformations," + " and after calculating normals and lengths." + " Values between 0.01 and 4.0 are recommended." + " The careful value 1.0 is used as default." + " Value 0 disables this filter functionality." }, + + { T_OPT_GP, "TRI_HEIGHT", "tri-height|triheight|tri-ht|triht", + "EXPR", + "Define the minimal height of KCL triangles." + " The intention is to ignore deformed triangles (very slim, but long)." + " @EXPR@ is a floating point number or expression." + " Triangles are invalidated if at least 1 of the 3 heights" + " is smaller than @EXPR@." + " The test is executed after reading files, after transformations," + " and after calculating normals and lengths." + " Values between 0.01 and 2.0 are recommended." + " The careful value 1.0 is used as default." + " Value 0 disables this filter functionality." + "\n " + " --tri-ht is an alternative name for the option." }, + + { T_OPT_CP, "FLAG_FILE", "flag-file|flagfile", + "filename", + "Define a flag file if scanning an OBJ file (KCL processing)." + " The file name may contain %-escapes." + " Set it to an empty string to disable flag files at all." + "\n " + " The tool search the flag file in the following order until found:" + " File defined by --flag-file;" + " OBJ file name with replaced extension ('a.obj' becomes 'a.flag');" + " OBJ file name with appended extension ('a.obj' becomes 'a.obj.flag')." + "\1 See https://szs.wiimm.de/opt/flag-file for details." }, + + { H_OPT_GO, "XTRIDATA", "xtridata|xtri", + "[=level]", + "Enable extended KCL triangles tests. The default level is 1." }, + + { T_OPT_GP, "KMP", "kmp", + "list", + "Set global options for KMP processing." + " Parameter @'list'@ is a comma separated list of keywords." + " A minus sign before a keyword disables a setting." + " Each occurrence of the option will only change entered settings" + " and all other settings are untouched." + "\n " + " Keyword @DEFAULT@ resets the default settings" + " and @CLEAR@ disables all." + " The other allowed keywords are:" + " @FORCE, NEW, RM-SPCITEM, LEFT, RIGHT, WIDE, NARROW," + " FIX-CKPH, FIX-ENPH, FIX-ITPH, FIX-PH," + " FIX-CKNEXT, FIX-CKJGPT, FIX-CK, FIX-ALL," + " MASK-PFLAGS, RM-LECODE, PURGE-GOBJ, FULL-DEFOBJ," + " DUMP-CLASS, DUMP-ONEWAY, DUMP-ALL," + " 1LAP ... 9LAPS, MAX-LAPS, RM-EMPTY," + " INPLACE, SILENT@ and @LOG@." + "\1 See https://szs.wiimm.de/opt/kmp for details." }, + + { T_OPT_GP, "SPEED_MOD", "speed-mod|speedmod", + "factor", + "The speed modifier is a user extensions to change the basic speed" + " of all vehicles. The speed factor itself is stored into" + " KMP:STGI section of a track." + "\n " + " If --speed-mod is set to a value >0.0, then the last 2 bytes " + " of the STGI section are patched. The value 0.0 reset" + " these 2 bytes and deactivates the speed modifier." }, + + { T_OPT_GP, "KTPT2", "ktpt2", + "vector", + "Insert or replace a second KTPT. Use VECTOR as new position." + " VECTOR is either @X,Z@ or @X,Y,Z@ or an expression." + " Set Y to 0 for an automatic defined height." + "\n " + " If the first 4 characters of VECTOR are 'AUTO' (ignoring case)," + " then the position is calculated automatically" + " and set to the recommendation of command @wkmpt KTPT@." + "\n " + " The tool looks for the nearest lap counter (CKPT with mode 0)" + " and adjust the KTPT: The direction of the lap counter" + " is copied and the KTPT is moved to the lap counter line" + " using the new direction." + "\n " + " LE-CODE uses the second KTPT to draw the finish line" + " at another place away from the start position." }, + + { T_OPT_GP, "TFORM_KMP", "tform-kmp|tformkmp|tkmp", + "list", + "Select KMP sections and objects for a transformation." + " @--tkmp@ is a short cut." + " The option expects a comma separated list of keywords." + " Non ambiguous of keywords are allowed and minus signs within" + " keywords are optional (e.g. @GOBJS@)." + " A minus sign before a keyword disables transformation of" + " the related section or object." + "\n " + " Sections are selected by the KMP section names" + " @AREA, CAME, CKPT, CNPT, ENPT, GOBJ, ITPT, JGPT," + " KTPT, MSPT@ and @POTI@." + "\n " + " Objects are selected by the keywords" + " @AREA-POSITION, AREA-ROTATE, AREA-SCALE, CAME-POSITION, CKPT-POSITION," + " CNPT-POSITION, CNPT-ROTATE, ENPT-POSITION, ENPT-SCALE," + " GOBJ-POSITION, GOBJ-ROTATE, GOBJ-SCALE, ITPT-POSITION, ITPT-SCALE," + " JGPT-POSITION, JGPT-ROTATE, KTPT-POSITION, KTPT-ROTATE," + " MSPT-POSITION, MSPT-ROTATE@ and @POTI-POSITION@." + "\n " + " The keywords @NONE@ and @ALL@ (default) complete the field." }, + + { T_OPT_CP, "REPAIR_XPF", "repair-xpflags|repairxpflags|repxpf", + "file", + "Load a KMP file and use it to repair the settings of the extended" + " presence flags, if they are destroyed by another KMP tool." + " @--repxpf@ is a short cut for this option." }, + + { T_OPT_CP, "GAMEMODES", "gamemodes|gmodes", + "list", + "Filter the list of combinations." + " The parameter is a comma separated list of keywords:" + " @AUTO@: Add a automatic mode without conditions first." + " @STANDARD@ (or @STD@): Add standard modes." + " @1STANDARD@ (or @1STD@): Add standard modes first." + " @BALLOON@: Add balloon battle modes." + " @COIN@: Add coin runners modes." + " @BATTLE@: Short cut for 'BALLOON,COIN'." + " @VERSUS@ (or @VS@): Add versus modes." + " @ITEMRAIN@: Add itemrain modes." + " @RACE@: Short cut for 'VERSUS,ITEMRAIN'." + " @ALL@ (default): Short cut for 'STANDARD,BATTLE,RACE'." + " @NO-MODES@: Exclude 'ALL' and create auto modes only." + " @ENGINE@: Include engine classes (default)." + " @NO-ENGINE@: Exclude engine classes." + " @RANDOM@: Include random modes (default)." + " @NO-RANDOM@: Exclude random modes." + " @OFFLINE@: Filter offline modes." + " @ONLINE@: Filter online modes." + " @NO-TT@: Exclude time trial." + " @TT@: Force a time trial scenario, even if it is a duplicate." + " @FULL@: Don't eliminate duplicates from list (up to " + MAX_GAMEMODES " modes)." + " @NSORT@: Sort the list by name." + " @ISORT@: Sort the list by index." + " @DEBUG@: Print a debug info about this option." + }, + + { T_OPT_GP, "MDL", "mdl", + "list", + "Set global options for MDL processing." + " To enable MDL patching, use option {--patch-file}" + " and select BRRES files for patching." + "\n " + " Parameter @'list'@ is a comma separated list of keywords." + " A minus sign before a keyword disables a setting." + " Each occurrence of the option will only change entered settings" + " and all other settings are untouched." + "\n " + " A MDL transformation (controlled by the transformation options)" + " is only done, if at least one of the keywords" + " @VECTOR@ or @VERTEX@ is set." + "\n " + " Keyword @DEFAULT@ resets the default settings (@VERTEX@)" + " and @CLEAR@ disables all." + " The other allowed keywords are: @VECTOR, MATRIX," + " VERTEX, PARENT, CHILD@ and @LOG@." + "\1 See https://szs.wiimm.de/opt/mdl for details." }, + + { T_OPT_GP, "PAT", "pat", + "list", + "Set global options for PAT processing." + " Parameter @'list'@ is a comma separated list of keywords." + " A minus sign before a keyword disables a setting." + " Each occurrence of the option will only change entered settings" + " and all other settings are untouched." + "\n " + " Keyword @DEFAULT@ resets the default settings" + " and @CLEAR@ disables all." + " The other allowed keywords are:" + " @AUTO, SIMPLE, COMPLEX, BOTH, SILENT@ and @LOG@." + "\1 See https://szs.wiimm.de/opt/pat for details." }, + + { T_OPT_GP, "PATCH_FILE", "patch-file|patchfile|pfile", + "list", + "Define, which kind of KMP, KCL and BRRES/MDL files are objects" + " for patching. @--pfile@ is a short cut." + "\n " + " Parameter @'list'@ is a comma separated list of keywords." + " A minus sign before a keyword disables a setting." + " Each occurrence of the option will only change entered settings" + " and all other settings are untouched." + "\n " + " Keyword @DEFAULT@ resets the default settings (@TRACK,LOG@)" + " and @CLEAR@ disables all." + " The other allowed keywords are:" + " @KMP, KCL, OTHER-KCL, ALL-KCL, MODEL, MAP, VRCORN," + " OTHER-BRRES, ALL-BRRES, TRACK (=KMP,KCL,MODEL,MAP,VRCORN)," + " ALL, LOG@ and @LOG-ALL@." + "\1 See https://szs.wiimm.de/opt/patch for details." }, + + { T_OPT_GP, "KMG_LIMIT", "kmg-limit|kmglimit", + "seconds", + "Limit the run time for Ballon Battle and Coin Runners" + " to the given time in SECONDS. Smaller values are not changed." + " The limitation is carried out after loading a binary file" + " and before scanning a text file." + " Value 0 disables this option." }, + + { T_OPT_GP, "KMG_COPY", "kmg-copy|kmgcopy", + "id", + "Make all 10 slots of a KMG file equal after loading a binary file" + " and before scanning a text file." + " ID is either a slot index (0..9) or a slot name (A11..A25)" + " and declares the source slot. Value -1 disables this option." + " If ID contains brackets [...], then it is analysed as an arena name and" + " the part between the brackets is searched for attributes A11..A25." }, + +// { H_OPT_GP, "KMG_AUTO_COPY","kmg-auto-copy|kmgautocopy", +// 0, +// "Analyze the attributes of the source file name to find attributes" +// " from @a21@ to @a25@. If found, use is as slot name ..." }, + + { T_OPT_G, "LT_CLEAR", "lt-clear|ltclear", + 0, + "Clear the LEX/TEST section and remove the section and course.lex if empty." + " This option is executed before all other --lt-* options." }, + + { T_OPT_GP, "LT_ONLINE", "lt-online|ltonline", + "mode", + "Set LEX/TEST parameter OFFLINE-ONLINE to this MODE." + " MODE is one of @AUTO@ (default), @NEVER@ or @ALWAYS@." + " Only track.szs files are modified." + " If needed, file course.lex and section TEST" + " are created or removed if empty." }, + + { T_OPT_GP, "LT_N_PLAYERS", "lt-n-players|lt-nplayers|ltnplayers", + "offline,online", + "Set LEX/TEST parameters N-OFFLINE and N-ONLINE to these integer values." + " Only track.szs files are modified." + " If needed, file course.lex and section TEST" + " are created or removed if empty." }, + + { T_OPT_GP, "LT_COND_BIT", "lt-cond-bit|lt-condbit|ltcondbit", + "bitnum", + "Set LEX/TEST parameter COND-BIT to this integer value." + " Use -1 to deactivate this COND-BIT." + " Only track.szs files are modified." + " If needed, file course.lex and section TEST" + " are created or removed if empty." }, + + { T_OPT_GP, "LT_GAME_MODE", "lt-game-mode|lt-gamemode|ltgamemode", + "mode", + "Set LEX/TEST parameter GAME-MODE to this MODE." + " MODE is one of @AUTO@ (default), @BALLOON, COIN, VERSUS@ or @ITEMRAIN@." + " Only track.szs files are modified." + " If needed, file course.lex and section TEST" + " are created or removed if empty." }, + + { T_OPT_GP, "LT_ENGINE", "lt-engine|ltengine", + "mode", + "Set LEX/TEST parameter ENGINE to this MODE." + " MODE is one of @AUTO@ (default), @BATTLE, 50CC, 100CC, 150CC, 200CC," + " 150M@ or @200M@. Only track.szs files are modified." + " If needed, file course.lex and section TEST" + " are created or removed if empty." }, + + { T_OPT_GP, "LT_RANDOM", "lt-random|ltrandom", + "index", + "Force a random scenario, if between 1 and 8." + " Value 0 enables auto selection and -1 disables this option." + " Only track.szs files are modified." + " If needed, file course.lex and section TEST" + " are created or removed if empty." }, + + { T_OPT_G, "LEX_PURGE", "lex-purge|lexpurge", + 0, + "Delete LEX sections without any impact." + " Delete sub-file course.lex if it no longer contains a section." + " This option is executed after all --lt-* options." }, + + { T_OPT_CMP, "LOAD_BMG", "load-bmg|loadbmg", + "file", + "Load immediately the file to fill an internal message pool." + " The option can be used multiple times" + " to fill the message pool by different sources." + " A later message overrides the previous message." + "\n " + " The internal message pool is patched by all" + " {--patch-bmg} options." }, + + { T_OPT_CMP, "PATCH_BMG", "patch-bmg|patchbmg|pb", + "mode[cond][=param]", + PATCH_BMG_TEXT("") }, + + { T_OPT_CMP, "MACRO_BMG", "macro-bmg|macrobmg|mb", + "file", + "Load a BMG file and add the messages to the macro library;" + " already existing entries are replaced." + " The macro library is accessed by escape sequence @\\m{{MID}@" + " as fallback, if the active file has not already defined the" + " message @MID@ by itself." }, + + { T_OPT_CMP, "FILTER_BMG", "filter-bmg|filterbmg|fb|msg", + "list", + "If this option is set, the BMG messages are filtered by this list." + " Only enabled messages are exported to the output BMG file" + " (binary or text)." + "\n " + " The parameter is a list of message ids" + " (short MID, 'Txx' or 'Uxx' or Mxx' or hex number)" + " or message ranges (MID:MID) or one of the keywords" + " @NONE, IDENT (=ID), PARAM, CUPS, TRACKS, ARENAS, CHAT," + " CTCODE (=CT), CTUPS, CTTRACKS, CTARENAS, CTREFS," + " LECODE (=LE), LEUPS, LETRACKS, LEARENAS, LEREFS," + " XCODE (=X), XUPS, XTRACKS, XARENAS, XREFS," + " ALLCODE, ALLUPS, ALLTRACKS, ALLARENAS, ALLREFS," + " GENERIC@ or @ALL@." + " If an element is preceded by a minus sign, " + " it is removed from the filter list (disabled)." + "\n " + " With v2.01a, this option was renamed from --msg to the more" + " meaningful name --filter-bmg. The old name is still available." + "\1\n Read https://szs.wiimm.de/opt/filter-bmg for more details." }, + + { T_OPT_GP, "CACHE", "cache", + "dir", + "Define a directory for a SZS cache." + "If creating a YAZ0 compressed file with standard compression (level 9)," + " the cache is searched for the compressed version." + " File cache-content.txt is used as file list with hash values." + " If the cache file is not found or the content differ," + " the file is (re-)created and exit status 2=differ is returned." + "\n " + " Background: Loading a compressed SZS and content verification" + " is much faster than YAZ compression." + "\n " + " For legay reasons:" + " If parameter DIR is a file, it is split into directory and base name." + " The directory is used for the cache directory and the base name" + " for option {--cname}, but only if {--cname} is not used." }, + + { T_OPT_GP, "CNAME", "cname", + "name", + "Name of the first cache file." + " This parameter overrides the generated cache name." + " The name is used only once for the very first cache candidate." }, + + { T_OPT_C, "ID", "id", + 0, + "Print an ID with 8 characters instead of a SHA-1 sum with" + " 40 characters. The SHA-1 sum is used to calculate the ID." + " The ID consists of '0'-'9' and 'a'-'w' (32 characters)." }, + + { T_OPT_CO, "BASE64", "base64", + "[=mode]", + "Encode the the checksum string as BASE64 with 27 characters" + " instead of hex." + " If a parameter is set, it acts like {--coding=mode}." }, + + { T_OPT_CO, "DB64", "db64", + "[=mode]", + "Encode the the checksum string and some length as BASE64" + " with 32 characters instead of hex for database searches." + " This options implies {--coding=URL} if no other coding is defined." + " If a parameter is set, it acts like {--coding=mode}." + " Option {--db64} has priority over {--base64}." }, + + { T_OPT_CP, "CODING", "coding", + "mode", + "Define the coding of the non alphanumeric characters for BASE64:" + " STANDART (+/=, default), URL (-_=), STAR (-_*) or XML (.-*)." }, + + { T_OPT_C, "VERIFY", "verify", + 0, + "Calculate the checksum and compare the filename and return" + " exit status (0=valid, 2=differ)." + " The result is valid, if the checksum is found" + " with a leading and a following point or minus sign." + " If {--quiet} is set, error messages are suppressed." }, + + { T_OPT_C, "ROUND", "round", + 0, + "Print additional lines with rounded values" + " (to 3 and 2 bytes for floats and 7 and 6 bytes for doubles)."}, + + { T_OPT_CM, "LONG", "l|long", + 0, "Print in long format. Multiple usage possible." }, + +// { T_OPT_CM, "FULL", "full", +// 0, +// "Print full lists instead of partial lists." }, + + { T_OPT_C, "NO_HEADER", "H|no-header|noheader", + 0, + "Suppress printing of header and footer." }, + + { T_OPT_C, "BRIEF", "B|brief", + 0, + "Suppress information lines in decoded text files." + " This includes syntax information ({--no-header})." + " If set twice, the output of unneeded sections is also suppressed." }, + + { T_OPT_CM, "PIPE", "pipe", + 0, "Use pipe sign instead of spaces to separate columns." }, + + { T_OPT_C, "DELTA", "delta", + 0, + "Print only modified records or sections on text output." + " Supported file formats until now: @GEOHIT@, @ITEMSLOT@, @KMG@" + " and @OBJFLOW@." + " If set twice, limit output to modified records" + " (only relevant for @ITEMSLOT@)." }, + + { T_OPT_C, "DIFF", "diff", + 0, + "On text output, print only differences from original files." + " It's a short cut for --no-header --brief --brief --delta --delta." + " Additional, the output is limited to data sections." }, + + { H_OPT_CP, "EPSILON", "epsilon", + "float", + "When comparing floating point numbers, 2 numbers are considered identical" + " if their difference is less than or equal to epsilon." + " The default value is @" CONVERT_TO_STRING(DEF_EPSILON_POS) "@." }, + + { T_OPT_CP, "BMG_ENCODING", "bmg-encoding|bmgencoding", + "name", + "Force a BMG encoding for new BMG files." + " Accepted encoding names are: @CP-1252@, @UTF-16BE@ (or @UTF-16@)," + " @SHIFT-JIS@, @UTF-8@ and @AUTO@ for automatic detection (default)." + " Same keywords without minus signs are accepted too." + "\n " + " Mario Kart Wii uses encoding @UTF-16BE@;" + " the default if no other encoding is defined." }, + + { T_OPT_CP, "BMG_INF_SIZE", "bmg-inf-size|bmginfsize", + "size", + "Defines the 'INF1' size of new BMG files between 4 and " + UI_TO_TEXT(BMG_INF_LIMIT) "." + " The first 4 bytes of a INF1 record is an offset into the" + " string pool and the remaining bytes the attributes." + " Maximal " UI_TO_TEXT(BMG_ATTRIB_SIZE) + " attribute bytes are supported and additional bytes are" + " assumed to be NULL. All BMG files of MKW have a size of 8." }, + + { T_OPT_CO, "BMG_MID", "bmg-mid|bmgmid", + "[mode]", + "Control the creation of BMG section @MID1@." + " Suppress the creation, if mode is @OFF@." + " Force the creation, if mode is @ON@." + " On mode @AUTO@ (default), the source is analysed for the decision." }, + + { T_OPT_CP, "FORCE_ATTRIB", "force-attrib|forceattrib", + "attrib", + "Forces, that all attributes are set to this vector." + "\1 See https://szs.wiimm.de/doc/bmg/attrib for the syntax." }, + + { T_OPT_CP, "DEF_ATTRIB", "def-attrib|defattrib", + "attrib", + "Define the default attributes for BMG files." + " If not set, the default attributes are estimated." + " On text output, strings with default attributes are printed" + " without attribute vector." + " The usual default attribute for MKW is @[1]@." + "\1 See https://szs.wiimm.de/doc/bmg/attrib for the syntax." }, + + { T_OPT_C, "NO_ATTRIB", "no-attrib|noattrib", + 0, + "Suppress the output of any BMG attributes if creating BMG text files." }, + + { T_OPT_C, "X_ESCAPES", "x-escapes|xescapes", + 0, + "Use \\x{{} escapes instead of \\z{{} escapes." }, + + { T_OPT_C, "OLD_ESCAPES", "old-escapes|oldescapes", + 0, + "To be compatible with v1.43 and earlier," + " print 1A escape sequences with total size of >12 bytes" + " as single words using \\x{{}." + " Also don't use \\x{{} and \\u{{} with parameter lists." }, + + { T_OPT_C, "SINGLE_LINE", "1|single-line|singleline", + 0, + "If set, don't print continuation lines for BMG text output." + " If set twice, print only single text lines and suppress attributes" + " like option {--no-attrib} does it." }, + + { T_OPT_C, "NO_BMG_COLORS","no-bmg-colors|nobmgcolors", + 0, + "If set, suppress the output of '\\c{{color}'" + " escape sequences for colors definitions in BMG text output" + " to be compatible with old versions of the tools." }, + + { T_OPT_C, "BMG_COLORS", "bmg-colors|bmgcolors", + 0, + "Force output of '\\c{{color}' escape sequences with names," + " even for foreign (not MKWii) BMG files." }, + + { T_OPT_C, "NO_BMG_INLINE","no-bmg-inline|nobmginline", + 0, + "Print BMG attributes as separate line before the message definition" + " to be compatible with versions until v1.39." + " The output for the standard value 0x01000000 is always suppressed." + " This was the default until v1.43." }, + + { T_OPT_C, "EXPORT", "X|export", + 0, + "Enable the export mode and create small and machine readable" + " text files for easy post processing." + " The option works similar like @-HBB@ for KMP" + " and like @-HBl1@ for BMG text files." }, + + { T_OPT_C, "NO_PARAM", "P|no-param|noparam", + 0, + "Disable parameter support in decoded KMP text files." }, + + { T_OPT_CP, "SORT", "S|sort", + "mode", + "Define a sort mode. Allowed keywords are:" + " @NONE@ (default), @NAME, OFFSET, U8, PACK, BRRES, BREFT@ and @AUTO@" }, + + { T_OPT_GP, "MAX_FILE_SIZE","M|max-file-size|maxfilesize", + "size", + "This security option defines the maximum file size" + " for input files; larger files are ignored with a warning." + " The default unit is MiB and the default size is 100 MiB;" + " that should be enough for standard usage." }, + + { T_OPT_GP, "TRACKS", "T|tracks", + "tracks", + "Define a new track order." + " A comma separated list in the format @[a=]b@ or @a:=b@ is expected." + " @'a'@ and @'b'@ are track names or slot numbers." + " @'a'@ is the destination slot and @'b'@ the assigned track." + " If @'a'@ is omitted, the next slot is used;" + " this means @[a=]b,c,d@ is possible." + "\n " + " Tracks will only be exchanged so that always all tracks are assigned." + " But if using @':='@ the new slots are assigned without exchanging" + " and the old values are lost." }, + + { T_OPT_GP, "ARENAS", "A|arenas", + "arenas", + "Define a new arena order." + " A comma separated list in the format @[a=]b@ or @a:=b@ is expected." + " @'a'@ and @'b'@ are arena names or slot numbers." + " @'a'@ is the destination slot and @'b'@ the assigned arena." + " If @'a'@ is omitted, the next slot is used;" + " this means @[a=]b,c,d@ is possible." + "\n " + " Arenas will only be exchanged so that always all arenas are assigned." + " But if using @':='@ the new slots are assigned without exchanging" + " and the old values are lost." }, + + { T_OPT_GP, "AUTOADD_PATH", "autoadd-path|autoaddpath", + "directory", + "Define an alternative directory as autoadd library." + " The default is, that the sub-directory called './auto-add/'" + " of all search paths are used." + " Use {wszst test -vvvv} for tests." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "TRANSFORM", "x|transform", + "list", + "Convert image formats." + " A comma separated list with terms is expected." + " A term have the syntax @'[SRC=]DEST'@." + " It means: convert @SRC@ format into @DEST@ format." + " If @SRC=@ is not set, the destination format is used" + " for all source formats. This makes only sense as last rule." + " If @DEST@ is empty no transformation is done for the source." + " Examples: '@TPL@' or '@GRAY@' or '@BTI.IA8@' or '@TPL.I4+I8=IA8,TEX=,CMPR@'" + "\n " + " @SRC@ and @DEST@ are tuple of file, image and palette formats" + " and of 2 more attributes. All parts are optional and separated" + " by points, the order is irrelevant." + "\n " + " Allowed file formats are: @TPL, TEX, BTI, BREFT-IMG (=BTIMG)," + " PNG@ (import)." + " Allowed image formats are: @I4, I8, IA4, IA8, RGB565 (=R565)," + " RGB5A3 (=R3), RGBA32 (=R32), C4, C8, C14X2, CMPR@." + " Allowed palette formats are: @P-IA8 (=P8), P-RGB565 (=P565)," + " P-RGB5A3 (=P3)@." + " Attributtes are @PALETTE@, @NO-PALETTE@, @GRAY@, @COLOR@," + " @ALPHA@ and @NO-ALPHA@." + "\n " + " All terms are managed as list." + " For each graphic the list is iterated until the first SRC tuple" + " of a term matches the graphic." + " Then the destination tuple is used for the transformation." + "\n " + " The command {wimgt TEST} prints a clearly arranged readable table" + " as result of this option." }, + + { T_OPT_C, "STRIP", "strip", + 0, + "If creating a PNG file," + " don't add comments about creator and source formats." }, + + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_G, "UTF_8", "utf-8|utf8", + 0, + "Enables UTF-8 support for filenames (default)." }, + + { H_OPT_G, "NO_UTF_8", "no-utf-8|no-utf8|noutf8", + 0, + "Disables UTF-8 support for filenames." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_GM, "TEST", "t|test", + 0, + "Run in test mode, modify nothing." + "\n" + ">>> USE THIS OPTION IF UNSURE! <<<" }, + + { T_OPT_GM, "FORCE", "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed." + "\n" + "Option {--kmp force} does the same, but only for KMP files." + "\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" }, + + { T_OPT_GO, "REPAIR_MAGICS", "repair-magics|repairmagics", + "[=mode]", + "Control the reparing of missing or wrong magics of files" + " and sub-files by analysing file path and file extension." + " @MODE@ is one of @OFF@ (default), @ANALYZE@" + " or @REPAIR@ (default, if set without mode)." + " @OFF@ disables this feature, @ANALYZE@ enables it for analysis only," + " and @REPAIR@ repairs all magics while reading the files." + " Keyword @FNAME@ can be appended (separated by a comma)" + " to give filename scanning the highest priority." + "\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" }, + + { T_OPT_GP, "TINY", "tiny", + "mode", + "Set tiny mode to a level between 0 and 7." + "\1 See https://szs.wiimm.de/opt/tiny for details." }, + + { T_OPT_G, "YAZ0", "yaz0", + 0, + "If creating a compressed file, force Yaz0 compression." + " Yaz0 compression is the default," + " if no compression method is defined by option or setup file." }, + + { T_OPT_G, "YAZ1", "yaz1", + 0, + "If creating a compressed file, force Yaz1 compression." + " The difference to Yaz0 is only the magic," + " which is set to 'Yaz1'." }, + + { T_OPT_G, "XYZ", "xyz", + 0, + "If creating a compressed file," + " force XYZ compression (an experimental Yaz0 variant)." }, + + { T_OPT_G, "BZ", "bz", + 0, + "If creating a compressed file," + " force BZ compression (a BZIP2 variant)." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_G, "SH", "sh", + 0, + "Dump info using SH syntax." + " Use {--var name} as prefix." }, + + { T_OPT_G, "BASH", "bash", + 0, + "Dump info using BASH syntax." + " Use {--var name} as prefix." }, + + { T_OPT_G, "JSON", "json", + 0, + "Dump info using JSON syntax." + " Options {--var} and {--array} are ignored." }, + + { T_OPT_G, "PHP", "php", + 0, + "Dump infos using PHP syntax." + " Use {--var name} as variable name of the resulting object." }, + + { T_OPT_G, "MAKEDOC", "makedoc", + 0, + "Dump infos using MakeDoc syntax." + " Use {--var name} as variable name of the resulting map." }, + + { T_OPT_GP, "VAR", "var", + "varname", + "Use @VARNAME@ as variable name or prefix on script output." + " The default variabe name is 'res'." }, + + { T_OPT_G, "ARRAY", "array", + 0, + "Use an array on script output if possible." + " If arrays are not supported, then append a '_#' suffix with an zero" + " based index to the variable name defined by {--var name}." + " In this case, only one output file is created." }, + + { T_OPT_GP, "AVAR", "avar", + "varname", + "Shortcut for: {--array --var name}" }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { H_OPT_CP, "ANALYZE", "analyze|analyse", + "outfile", + "Run in analyze mode and write results to the entered file." }, + + { H_OPT_CP, "ANALYZE_MODE", "analyze-mode|ana-mode|anamode|amd", + "modelist", + "Comma separated list with objects to analyse." + " Objects are @BRRES, MDL0, PAT0, TEX0, BREFF, BREFT, TPL," + " KCL, KMP, GOBJ, KTPT, MKWANA@" + " and @ALL@ for all (=default)." + " @--ana-mode@ is a short cut." }, + + #if OPT_OLD_NEW + { H_OPT_GM, "OLD", "OLD", + 0, + "Developer option:" + " Cancel --NEW and use old implementation if available." }, + + { H_OPT_GM, "STD", "STD", + 0, + "Developer option: Cancel --OLD and --NEW" + " and use standard implementation (default)." }, + + { H_OPT_GM, "NEW", "NEW", + 0, + "Developer option:" + " Cancel --OLD and use new implementation if available." }, + #endif + + { H_OPT_GP, "EXTRACT", "EXTRACT", + "dir", + "Create the directory and extract different objects" + " in different formats at the moment when they are seen." + " This is a development helper option." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "SOURCE", "s|source|src", + "path", + "Define a secondary source path (directory/file)." + "\n " + " The path may contain escape sequences:" + " @%Q@ is replaced by the fully qualified primary source name." + " @%P@ and @%F@ are replaced by the primary source path" + " or by the filename. @%N@ and @%E@ are replaced by primary source" + " filename without extension or by the source extension." + " Finally, @%T@ is replaced by the default extension" + " of a virtual destination format." + "\n " + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions" + " avoid that the same extension is used twice in row." + "\1 Read https://szs.wiimm.de/opt/source for more details." }, + + + { T_OPT_CP, "DEST", "d|dest", + "path", + "Define a destination path (directory/file)." + " The destination @-@ means: write to standard output." + "\n " + " The path may contain escape sequences:" + " @%Q@ is replaced by the fully qualified source name." + " @%P@ and @%F@ are replaced by the source path or by the filename." + " @%N@ and @%E@ are replaced by source filename without extension" + " or by the source extension." + " Finally, @%T@ is replaced by the default extension" + " of the destination format." + "\n " + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions" + " avoid that the same extension is used twice in row." + "\1 Read https://szs.wiimm.de/opt/dest for more details." }, + + { T_OPT_CP, "DEST2", "D|DEST", + "path", + "Like {--dest}, but create the directory path automatically." }, + + { T_OPT_CP, "ESC", "E|esc", + "char", + "Define an alternative escape character for destination files." + " The default is '%'." + " For Windows batch files it is a good choice to set @'-E$$'@." }, + + { T_OPT_C, "OVERWRITE", "o|overwrite", + 0, + "Overwrite already existing files without warning" + " and ignore option {--number}." }, + + { T_OPT_C, "NUMBER", "number|num", + 0, + "If a file already exist, append a number directly before" + " the file extension to make the filename unique." + " If other numbered files already exist (ignoring case)," + " use the maximum existing index+1." + " @--num@ is a short cut." }, + + { T_OPT_C, "REMOVE_SRC", "remove-src|removesrc|rm-src|rmsrc", + 0, + "Remove the source file after successful operation." + " @--rm-src@ is a short cut." }, + + { T_OPT_C, "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", + 0, + "Remove already existing files before creating it." + " If set, {--overwrite} is ignored." + " @--rm-dest@ is a short cut." }, + + { T_OPT_C, "UPDATE", "u|update", + 0, + "Update only existing files and don't create new files." + " If set, {--overwrite} and {--remove-dest} are ignored." }, + + { T_OPT_C, "PRESERVE", "p|preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." }, + + { T_OPT_C, "IGNORE", "i|ignore", + 0, + "Ignore non existing source files without warning." }, + + { T_OPT_C, "IGNORE_SETUP", "ignore-setup|ignoresetup", + 0, + "Ignore hidden setup file '@" SZS_SETUP_FILE "@'." + " This file is used to determine some default settings and to find" + " out sub archives and files to encode." + " Use this option only if you know what you do." }, + + { T_OPT_C, "PURGE", "purge", + 0, + "Remove repeated files with same checksum." }, + + { T_OPT_CP, "ALIGN_U8", "align-u8|alignu8", + "size", + "Define an align value for the sub files in U8 archives." + " The value must be a power of 2 and the default value is 32 (0x20)" + " This value is only relevant if creating or normalizing U8 archives." }, + + { T_OPT_CP, "ALIGN_PACK", "align-pack|alignpack", + "size", + "Define an align value for the sub files in PACK archives." + " The value must be a power of 2 and the default value is 32 (0x20)" + " This value is only relevant if creating or normalizing OACK archives." }, + + { T_OPT_CP, "ALIGN_BRRES", "align-brres|alignbrres", + "size", + "Define an align value for the sub files in BRRES archives." + " The value must be a power of 2 and the default value is 4" + " This value is only relevant if creating or normalizing BRRES archives." }, + + { T_OPT_CP, "ALIGN_BREFF", "align-breff|alignbreff", + "size", + "Define an align value for the sub files in BREFF archives." + " The value must be a power of 2 and the default value is 4" + " This value is only relevant if creating or normalizing BREFF archives." }, + + { T_OPT_CP, "ALIGN_BREFT", "align-breft|alignbreft", + "size", + "Define an align value for the sub files in BREFT archives." + " The value must be a power of 2 and the default value is 0x20" + " This value is only relevant if creating or normalizing BREFT archives." }, + + { T_OPT_CP, "ALIGN", "align", + "size", + "Define an align value for the sub files in all archives." + " This is a short cut for" + " {--align-u8=size --align-pack=size" + " --align-brres=size --align-breff=size --align-breft=size}." }, + + { T_OPT_C, "ENCODE_ALL", "encode-all|encodeall|eall", + 0, + "Encode all files. Normally a file is only encoded, if the source" + " is younger than the destination or the destination is missing." + " Only files in the @[encode]@ section of the file '@" SZS_SETUP_FILE "@'" + " are candidates for encoding." + " @--eall@ is a short cut." + " Option {--no-encode} takes precedence over {--encode-all}." }, + + { T_OPT_C, "ENCODE_IMG", "encode-img|encodeimg|eimg", + 0, + "Encode all image files. Normally a image file is only encoded, if the source" + " is younger than the destination or the destination is missing." + " Only files in the @[encode]@ section of the file '@" SZS_SETUP_FILE "@'" + " are candidates for encoding." + " @--eimg@ is a short cut." + " If set, option {--no-encode} is ignored for images." }, + + { T_OPT_C, "NO_ENCODE", "no-encode|noencode|noenc", + 0, + "Do not encode files before creating an archive." + " @--noenc@ is a short cut." + " Option {--no-encode} takes precedence over {--encode-all}," + " but {--encode-img} takes precedence for images." }, + + { T_OPT_C, "NO_RECURSE", "no-recurse|norecurse|norec", + 0, + "Do not create archives recursively." + " Normally files in the @[create]@ section of the file" + " '@" SZS_SETUP_FILE "@' are created first." + " @--norec@ is a short cut." }, + + { T_OPT_C, "AUTO_ADD", "auto-add|autoadd|aadd", + 0, + "Analyze the KMP (if exist) and add missing BRRES, BREFF, BREFT," + " BRASD and KCL files automatically, if the tool can find the files" + " in any sub directory named 'auto-add'." + " This sub directory is searched in all directories" + " of @'SEARCH_PATH'@; use command @{wszst AUTOADD}@ to view them." + " Command {wszst AUTOADD} may also create an auto-add database." + " @--aadd@ is a short cut." }, + + { T_OPT_C, "NO_ECHO", "no-echo|noecho", + 0, + "Suppress output of @@ECHO commands while scanning text files." }, + + { T_OPT_C, "NO_CHECK", "N|no-check|nocheck", + 0, + "Do not make plausibility checks for KCL and KMP files." }, + + { T_OPT_CO, "PT_DIR", "pt-dir|ptdir|pdir", + "[=mode]", + "Set one of the `point directory' modes @REMOVE@ (=@0@)," + " @FORCE@ (=@1@) or @AUTO@ (default)." + " If --pt-dir is used without parameter, then @FORCE@ is assumed." + "\n " + " This option is only relevant if creating an U8 archive." + " It decides, if a special directory with name '.'" + " will be added as base for all other files." }, + + { T_OPT_C, "RM_AIPARAM", "rm-aiparam|rmaiparam|rmai", + 0, + "Remove the directory AIParam and all files from the archive." + " This option has only impact, if creating a new archive or" + " normalizing an existing archive." + " @--rmai@ is a short cut." }, + + { T_OPT_C, "U8", "u8", + 0, + "Create an U8 archive, if possible." + " If the source is a WU8 archive, convert it to an U8 archive." + " This is the default if the setup file '@" SZS_SETUP_FILE "@'" + " has no other definition." + " The default compression mode is set to Yaz0 (see {--yaz0})." }, + + { T_OPT_C, "SZS", "szs", + 0, + "Short cut for {--u8 --yaz0}:" + " Create an U8 archive, if possible," + " and set the compression method to Yaz0." }, + + { T_OPT_C, "WU8", "wu8", + 0, + "Create a WU8 archive, if possible." + " If the source is an U8 archive, convert it to a WU8 archive." }, + + { H_OPT_C, "XWU8", "xwu8", + 0, + "Short cut for {--wu8 --xyz}:" + " Create a WU8 archive, if possible," + " and set the compression method to XYZ." }, + + { T_OPT_C, "WBZ", "wbz", + 0, + "Short cut for {--wu8 --bz}:" + " Create a WU8 archive, if possible," + " and set the compression method to BZ." }, + + { T_OPT_C, "PACK", "pack", + 0, + "Create a PACK archive." }, + + { T_OPT_C, "BRRES", "brres|bres", + 0, + "Create a BRRES archive." }, + + { T_OPT_C, "BREFF", "breff", + 0, + "Create a BREFF archive." }, + + { T_OPT_C, "BREFT", "breft", + 0, + "Create a BREFT archive." }, + + { T_OPT_C, "NO_COMPRESS", "no-compress|nocompress|noc", + 0, + "Create an uncompressed file instead of a compressed SZS file." + " The default destination changes to @'%P/%N%T'@." + " @--noc@ is a short cut." }, + + { T_OPT_CP, "COMPRESS", "C|compr|compress", + "level", + "Define a compression level between @0..9@:" + " @0@ (or @NOCHUNKS@) disables finding chunks," + " @1@ (or @FAST@) for the fastest standard compression and" + " @9@ (or @BEST@) for the best (=default) compression." + " @10@ (or @ULTRA@) and @100-150@ are special time-consuming" + " compression modes." + " They are dedicated to competitions with size limitations." + " The special value @UNCOMPRESSED@ acts like option {--no-compress}." + "\n " + " Because of many repeated data, the best bz-compression mode varies." + " Therefor the levels @TRY2..TRY5@ (or short @T2..T5@) are defined" + " to find the best compression mode with testing the first" + " N modes of 9, 1, 8, 2, 5. @TRY2@ is used for normalizing." + " For non bz-compression, @TRY*@ are the same as @BEST@." + "\n " + " Option {--norm} takes precedence over {--compr}" + " and sets the compression level to @TRY2@ (@BEST@)." + "\n " + " For more modes and details type {wszst -C list}." + " To force colorized output type {wszst -C clist}." }, + + { T_OPT_C, "FAST", "fast", + 0, + "Short cut for {--compr=fast}: Set the fastest real compression." + " It also overrides compression rates set by {--norm} or {--compr}." }, + + { T_OPT_C, "NORM", "n|norm", + 0, + "The uncompressed data will be normalized." + " See command {NORMALIZE} for more details." }, + + { T_OPT_C, "LINKS", "links", + 0, + "Support hardlinks while creating U8 and WU8 archives." + " Keep hardlinks if normalizing U8 and WU8 archives." + " On extracting, search hardlinks in every archive and try" + " to create hardlinks at local file system." }, + + { T_SEP_OPT, 0,0,0,0 }, //----- separator ----- + + { T_OPT_CP, "BASEDIR", "basedir|bdir", + "directory", + "Extract only files from archives, that are below the DIRECTORY." + " Leading points and slashes and trailing slahes are removed" + " from the parameter. --bdir is short cut for --basedir." }, + + { T_OPT_CO, "RECURSE", "R|recurse", + "[=level]", + "If a extracted file is a known and supported archive" + ", than extract it recursively until the entered level is reached." + " Level 0 (=@NONE@) deactivates this." + " If --recurse is used without parameter," + " then @UNLIMITED@ is assumed." + "\n " + " -R does'nt accept a parameter" + " and is a short cut for {--recurse=unlimited}." }, + + { T_OPT_C, "EXT", "e|ext", + 0, + "For BRRES archives only:" + " If a file is extracted, add a handy file extension like '.mdl'" + " to the file name. If set twice and a textual file magic is" + " available, use the file magic in lower case instead like '.mdl0'." }, + + { T_OPT_C, "DECODE", "decode|dec", + 0, + "If a extracted file is known and can be decoded, do it." + " @--dec@ is a short cut." }, + + { T_OPT_C, "MIPMAPS", "mipmaps|mm", + 0, + "If reading a source, scan for mipmaps and load them too." + " For PNG files, files named @NAME.mm#.EXT@ with #=1.. are searched." + " This is the default. @--mm@ is a short cut." }, + + { T_OPT_C, "NO_MIPMAPS", "no-mipmaps|nomipmaps|no-mm|nomm", + 0, + "If reading a source, ignore mipmaps." + " This disables not the creation of mipmaps (see {-n-mipmaps})." + " @--no-mm@ is a short cut." }, + + { T_OPT_CP, "N_MIPMAPS", "n-mipmaps|nmipmaps|n-mm|nmm", + "num", + "Force the number of mipmaps. Values between 0 and 20 are allowed." + " Value @OFF@ (default) disables this option." + " If enabled, options @--max-mipmaps@ and @--mipmap-size@ are ignored." + " The creation of mipmaps stops if the width or the height" + " of the mipmap becomes 0. @--n-mm@ is a short cut." }, + + { T_OPT_CP, "MAX_MIPMAPS", "max-mipmaps|maxmipmaps|max-mm|maxmm", + "num", + "Control the automatic mipmap creation." + " Values between 0 and 20 are allowed and the default is 4." + " The mipmap creation for an image stops, if the set number is reached." + " This option is ignored, if @--n-mipmaps@ is enabled." + " @--max-mm@ is a short cut." }, + + { T_OPT_CP, "MIPMAP_SIZE", "mipmap-size|mipmapsize|mm-size|mmsize", + "num", + "Define the minimal width and minimal height of a generated mipmap." + " Any value >0 is allowed. The default is 8." + " This option is ignored, if @--n-mipmaps@ is enabled." + " @--mm-size@ is a short cut." }, + + { T_OPT_C, "FAST_MIPMAPS", "fast-mipmaps|fastmipmaps|fast-mm|fastmm", + 0, + "If resizing an image, use the old fast resize algorithm" + " (default until v1.64a) instead of the new smart one." + " Both algorithms differ only, when creating a mipmap for images" + " with odd width or odd height." + " The old algorithm is faster (factor 2-4) than the default algorithm," + " but the new algorithm creates better resized images." + "\1 See https://szs.wiimm.de/doc/img/resize for details." + "\3 @--fast-mm@ is a short cut." }, + + { T_OPT_CP, "CMPR_DEFAULT", "cmpr-default", + "rgb1[,rgb2]", + "Define 2 colors for the case, that all 16 pixels of a CMPR block" + " are transparent. The default is to calculate an average color" + " of all transparent pixels. Before v2.04 white was used." + "\n " + " 2 RGB values (hex values with 6 digits each) are expected as parameter." + " If optional @RGB2@ is missed, a copy of @RGB1@ is used." + " As first step, both colors are converted to RGB565 colors @C1@ and @C2@" + " and sorted, so that @C1 * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wbmgt.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "If set, the information header in decoded text files is suppressed" + " (for historical reasons same as --no-header). If set at least twice," + " all comments are suppressed and the output is packed without empty" + " lines. If set 3 times, the #BMG file indentification is also" + " suppressed." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_ALIGN, false, false, false, false, false, 0, "align", + "size", + "Define an align value for the sub sections in the archive. The value" + " must be a power of 2 and the default value is 32 (0x20)" + }, + + { OPT_PATCH_BMG, false, false, false, false, false, 'P', "patch-bmg", + "mode[cond][=param]", + "This option specifies a BMG patch mode. Some of the modes need a" + " parameter or a file name of a BMG patch file (raw or text BMG), both" + " separated by an equal sign. Modes with required file names are" + " PRINT, REPLACE, INSERT, OVERWRITE, DELETE, MASK, EQUAL and NOTEQUAL." + " Modes with text parameter are FORMAT, REGEXP and RM-REGEXP. " + " Standalone modes are ID, ID-ALL, UNICODE, RM-ESCAPES, RM-CUPS," + " CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY, LE-FILL," + " X-COPY, X-FORCE-COPY and X-FILL. Unique abbreviations are allowed.\n" + " The optional condition COND is either '?MID' or '!MID'. In case of" + " '?MID', the patch is only applied if the message id MID already" + " exists. In case of '!MID', the patch is only applied if the message" + " id MID does not exists.\n" + " If this option is used multiple times, all patch files will be" + " processed in the entered order. --patch is a short cut. Read" + " https://szs.wiimm.de/opt/patch-bmg for more details." + }, + + { OPT_MACRO_BMG, false, false, false, false, false, 0, "macro-bmg", + "file", + "Load a BMG file and add the messages to the macro library; already" + " existing entries are replaced. The macro library is accessed by" + " escape sequence \\m{MID} as fallback, if the active file has not" + " already defined the message MID by itself." + }, + + { OPT_FILTER_BMG, false, false, false, false, false, 0, "filter-bmg", + "list", + "If this option is set, the BMG messages are filtered by this list." + " Only enabled messages are exported to the output BMG file (binary or" + " text).\n" + " The parameter is a list of message ids (short MID, 'Txx' or 'Uxx'" + " or Mxx' or hex number) or message ranges (MID:MID) or one of the" + " keywords NONE, IDENT (=ID), PARAM, CUPS, TRACKS, ARENAS, CHAT," + " CTCODE (=CT), CTUPS, CTTRACKS, CTARENAS, CTREFS, LECODE (=LE)," + " LEUPS, LETRACKS, LEARENAS, LEREFS, XCODE (=X), XUPS, XTRACKS," + " XARENAS, XREFS, ALLCODE, ALLUPS, ALLTRACKS, ALLARENAS, ALLREFS," + " GENERIC or ALL. If an element is preceded by a minus sign, it is" + " removed from the filter list (disabled).\n" + " With v2.01a, this option was renamed from --msg to the more" + " meaningful name --filter-bmg. The old name is still available.\n" + " Read https://szs.wiimm.de/opt/filter-bmg for more details." + }, + + { OPT_BMG_ENCODING, false, false, false, false, false, 0, "encoding", + "name", + "Force a BMG encoding for new BMG files. Accepted encoding names are:" + " CP-1252, UTF-16BE (or UTF-16), SHIFT-JIS, UTF-8 and AUTO for" + " automatic detection (default). Same keywords without minus signs are" + " accepted too.\n" + " Mario Kart Wii uses encoding UTF-16BE; the default if no other" + " encoding is defined." + }, + + { OPT_BMG_INF_SIZE, false, false, false, false, false, 0, "inf-size", + "size", + "Defines the 'INF1' size of new BMG files between 4 and 1000. The" + " first 4 bytes of a INF1 record is an offset into the string pool and" + " the remaining bytes the attributes. Maximal 40 attribute bytes are" + " supported and additional bytes are assumed to be NULL. All BMG files" + " of MKW have a size of 8." + }, + + { OPT_BMG_MID, true, false, false, false, false, 0, "bmg-mid", + "[mode]", + "Control the creation of BMG section MID1. Suppress the creation, if" + " mode is OFF. Force the creation, if mode is ON. On mode AUTO" + " (default), the source is analysed for the decision." + }, + + { OPT_FORCE_ATTRIB, false, false, false, false, false, 0, "force-attrib", + "attrib", + "Forces, that all attributes are set to this vector. See" + " https://szs.wiimm.de/doc/bmg/attrib for the syntax." + }, + + { OPT_DEF_ATTRIB, false, false, false, false, false, 0, "def-attrib", + "attrib", + "Define the default attributes for BMG files. If not set, the default" + " attributes are estimated. On text output, strings with default" + " attributes are printed without attribute vector. The usual default" + " attribute for MKW is [1]. See https://szs.wiimm.de/doc/bmg/attrib" + " for the syntax." + }, + + { OPT_NO_ATTRIB, false, false, false, false, false, 0, "no-attrib", + 0, + "Suppress the output of any BMG attributes if creating BMG text files." + }, + + { OPT_X_ESCAPES, false, false, false, false, false, 0, "x-escapes", + 0, + "Use \\x{} escapes instead of \\z{} escapes." + }, + + { OPT_OLD_ESCAPES, false, false, false, false, false, 0, "old-escapes", + 0, + "To be compatible with v1.43 and earlier, print 1A escape sequences" + " with total size of >12 bytes as single words using \\x{}. Also don't" + " use \\x{} and \\u{} with parameter lists." + }, + + { OPT_SINGLE_LINE, false, false, false, false, false, '1', "single-line", + 0, + "If set, don't print continuation lines for BMG text output. If set" + " twice, print only single text lines and suppress attributes like" + " option --no-attrib does it." + }, + + { OPT_NO_BMG_COLORS, false, false, false, false, false, 0, "no-bmg-colors", + 0, + "If set, suppress the output of '\\c{color}' escape sequences for" + " colors definitions in BMG text output to be compatible with old" + " versions of the tools." + }, + + { OPT_BMG_COLORS, false, false, false, false, false, 0, "bmg-colors", + 0, + "Force output of '\\c{color}' escape sequences with names, even for" + " foreign (not MKWii) BMG files." + }, + + { OPT_NO_BMG_INLINE, false, false, false, false, false, 0, "no-bmg-inline", + 0, + "Print BMG attributes as separate line before the message definition" + " to be compatible with versions until v1.39. The output for the" + " standard value 0x01000000 is always suppressed. This was the default" + " until v1.43." + }, + + { OPT_EXPORT, false, false, false, false, false, 'X', "export", + 0, + "Enable the export mode and create small and machine readable text" + " files for easy post processing. The option works similar like -HBl11" + " for BMG text files." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 31 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "Enable LE-CODE and CT-CODE support. The optional parameter is" + " deprecated and ignored. See https://szs.wiimm.de/opt/lecode for" + " details." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_TRACKS, false, false, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, false, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_POINTS, false, false, false, false, false, 0, "points", + "list", + "This option creates the new message 3FF0 to hold a description of the" + " versus points table. It defines a new table for the points assigned" + " to players after a versus or room race. The parameter is a blank or" + " comma and slash (next row) separated list with numbers (points)" + " between 0 and 255 and the following keywords: NINTENDO, LINEAR, WIN," + " WIN15, WIN25 to select a predefined table; NO-BONUS, BONUS, NULL," + " ONE as options; N1..N12 to select the row for N players. See" + " https://szs.wiimm.de/opt/points for details." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 60 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_POINTS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print the table as comma separated lists in C notation." + }; + +static const InfoOption_t option_cmd_POINTS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Append a string in --points format that defines the table. If set" + " twice, append up to 3 strings in different modes." + }; + +static const InfoOption_t option_cmd_EXTRACT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Exclude the section header from output." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_POINTS, "POINTS", "PTS", 0 }, + { CMD_REGEXP, "REGEXP", "RE", 0 }, + { CMD_EXTRACT, "EXTRACT", 0, 0 }, + { CMD_SECTIONS, "SECTIONS", 0, 0 }, + { CMD_LIST, "LIST", "LS", 0 }, + { CMD_LIST, "LL", 0, 0 }, + { CMD_SLOTS, "SLOTS", 0, 0 }, + { CMD_DIFF, "DIFF", "CMP", 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_MIX, "MIX", 0, 0 }, + { CMD_IDENTIFIER, "IDENTIFIER", "ID", 0 }, + { CMD_IDENTIFIER, "IDENTIFY", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "ENC", 0 }, + { CMD_PATCH, "PATCH", "PAT", 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHBM:T:A:td:D:E:orupiP:1X"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "points", 1, 0, GO_POINTS }, + { "pts", 1, 0, GO_POINTS }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "align", 1, 0, GO_ALIGN }, + { "patch-bmg", 1, 0, 'P' }, + { "patchbmg", 1, 0, 'P' }, + { "pb", 1, 0, 'P' }, + { "patch", 1, 0, 'P' }, + { "macro-bmg", 1, 0, GO_MACRO_BMG }, + { "macrobmg", 1, 0, GO_MACRO_BMG }, + { "mb", 1, 0, GO_MACRO_BMG }, + { "filter-bmg", 1, 0, GO_FILTER_BMG }, + { "filterbmg", 1, 0, GO_FILTER_BMG }, + { "fb", 1, 0, GO_FILTER_BMG }, + { "msg", 1, 0, GO_FILTER_BMG }, + { "encoding", 1, 0, GO_BMG_ENCODING }, + { "bmg-encoding", 1, 0, GO_BMG_ENCODING }, + { "bmgencoding", 1, 0, GO_BMG_ENCODING }, + { "inf-size", 1, 0, GO_BMG_INF_SIZE }, + { "infsize", 1, 0, GO_BMG_INF_SIZE }, + { "bmg-inf-size", 1, 0, GO_BMG_INF_SIZE }, + { "bmginfsize", 1, 0, GO_BMG_INF_SIZE }, + { "bmg-mid", 2, 0, GO_BMG_MID }, + { "bmgmid", 2, 0, GO_BMG_MID }, + { "force-attrib", 1, 0, GO_FORCE_ATTRIB }, + { "forceattrib", 1, 0, GO_FORCE_ATTRIB }, + { "def-attrib", 1, 0, GO_DEF_ATTRIB }, + { "defattrib", 1, 0, GO_DEF_ATTRIB }, + { "no-attrib", 0, 0, GO_NO_ATTRIB }, + { "noattrib", 0, 0, GO_NO_ATTRIB }, + { "x-escapes", 0, 0, GO_X_ESCAPES }, + { "xescapes", 0, 0, GO_X_ESCAPES }, + { "old-escapes", 0, 0, GO_OLD_ESCAPES }, + { "oldescapes", 0, 0, GO_OLD_ESCAPES }, + { "single-line", 0, 0, '1' }, + { "singleline", 0, 0, '1' }, + { "no-bmg-colors", 0, 0, GO_NO_BMG_COLORS }, + { "nobmgcolors", 0, 0, GO_NO_BMG_COLORS }, + { "bmg-colors", 0, 0, GO_BMG_COLORS }, + { "bmgcolors", 0, 0, GO_BMG_COLORS }, + { "no-bmg-inline", 0, 0, GO_NO_BMG_INLINE }, + { "nobmginline", 0, 0, GO_NO_BMG_INLINE }, + { "export", 0, 0, 'X' }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0, + /* 0x031 1 */ OPT_SINGLE_LINE, + /* 0x032 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e */ 0,0, + /* 0x050 P */ OPT_PATCH_BMG, + /* 0x051 */ 0,0,0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0, + /* 0x058 X */ OPT_EXPORT, + /* 0x059 */ 0,0,0,0, 0,0,0,0, 0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_POINTS, + /* 0x08b */ OPT_UTF_8, + /* 0x08c */ OPT_NO_UTF_8, + /* 0x08d */ OPT_FORCE, + /* 0x08e */ OPT_REPAIR_MAGICS, + /* 0x08f */ OPT_OLD, + /* 0x090 */ OPT_STD, + /* 0x091 */ OPT_NEW, + /* 0x092 */ OPT_EXTRACT, + /* 0x093 */ OPT_NUMBER, + /* 0x094 */ OPT_ALIGN, + /* 0x095 */ OPT_MACRO_BMG, + /* 0x096 */ OPT_FILTER_BMG, + /* 0x097 */ OPT_BMG_ENCODING, + /* 0x098 */ OPT_BMG_INF_SIZE, + /* 0x099 */ OPT_BMG_MID, + /* 0x09a */ OPT_FORCE_ATTRIB, + /* 0x09b */ OPT_DEF_ATTRIB, + /* 0x09c */ OPT_NO_ATTRIB, + /* 0x09d */ OPT_X_ESCAPES, + /* 0x09e */ OPT_OLD_ESCAPES, + /* 0x09f */ OPT_NO_BMG_COLORS, + /* 0x0a0 */ OPT_BMG_COLORS, + /* 0x0a1 */ OPT_NO_BMG_INLINE, + /* 0x0a2 */ OPT_SECTIONS, + /* 0x0a3 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0, + /* 0x0b0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[31] = // cmd #1 +{ + 0,1,0,1,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, + 1 +}; + +static u8 option_allowed_cmd_HELP[31] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1 +}; + +static u8 option_allowed_cmd_ARGTEST[31] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1 +}; + +static u8 option_allowed_cmd_TEST[31] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1 +}; + +static u8 option_allowed_cmd_COLORS[31] = // cmd #5 +{ + 0,1,0,1,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 u8 option_allowed_cmd_ERROR[31] = // cmd #6 +{ + 0,1,1,1,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, + 1 +}; + +static u8 option_allowed_cmd_FILETYPE[31] = // cmd #7 +{ + 0,1,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[31] = // cmd #8 +{ + 0,0,1,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 u8 option_allowed_cmd_POINTS[31] = // cmd #9 +{ + 0,1,0,1,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 u8 option_allowed_cmd_REGEXP[31] = // cmd #10 +{ + 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 u8 option_allowed_cmd_EXTRACT[31] = // cmd #11 +{ + 0,0,1,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 u8 option_allowed_cmd_SECTIONS[31] = // cmd #12 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0 +}; + +static u8 option_allowed_cmd_LIST[31] = // cmd #13 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,1, 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0 +}; + +static u8 option_allowed_cmd_SLOTS[31] = // cmd #14 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,1, 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0 +}; + +static u8 option_allowed_cmd_DIFF[31] = // cmd #15 +{ + 0,0,0,0,1, 0,1,0,0,0, 0,0,0,0,1, 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0 +}; + +static u8 option_allowed_cmd_CAT[31] = // cmd #16 +{ + 0,1,1,1,0, 0,0,0,0,0, 0,0,1,0,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 0 +}; + +static u8 option_allowed_cmd_MIX[31] = // cmd #17 +{ + 0,1,1,1,0, 0,0,0,0,0, 0,0,1,0,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 0 +}; + +static u8 option_allowed_cmd_IDENTIFIER[31] = // cmd #18 +{ + 0,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 0 +}; + +static u8 option_allowed_cmd_DECODE[31] = // cmd #19 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,0,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 0 +}; + +static u8 option_allowed_cmd_ENCODE[31] = // cmd #20 +{ + 0,0,0,0,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,0,0,0, 0,0,0,0,0, + 0 +}; + +static u8 option_allowed_cmd_PATCH[31] = // cmd #21 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_MAX_FILE_SIZE, + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_POINTS, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_POINTS[] = +{ + OptionInfo + OPT_POINTS, + &option_cmd_POINTS_BRIEF, + &option_cmd_POINTS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_REGEXP[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXTRACT[] = +{ + &option_cmd_EXTRACT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SECTIONS[] = +{ + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SLOTS[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DIFF[] = +{ + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_DEST, + OptionInfo + OPT_ESC, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + OptionInfo + OPT_LONG, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MIX[] = +{ + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + OptionInfo + OPT_LONG, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_IDENTIFIER[] = +{ + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CT_CODE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + OptionInfo + OPT_LONG, + OptionInfo + OPT_EXPORT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + OptionInfo + OPT_LONG, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_ALIGN, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_PATCH[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + OptionInfo + OPT_LONG, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_ALIGN, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wbmgt", + 0, + "wbmgt [option]... command [option|parameter|file]...", + "Wiimms BMG Tool : Convert and patch raw and text BMG (message) files.", + 0, + 23, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wbmgt VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wbmgt HELP [--width=wd] OPT|OPTION ...\n" + "wbmgt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wbmgt HELP [--width=wd] command [-b] [ignored]...\n" + "wbmgt HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wbmgt ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wbmgt TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 0, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wbmgt COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wbmgt ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wbmgt FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wbmgt FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_POINTS, + false, + false, + false, + "POINTS", + "PTS", + "wbmgt POINTS [--points=list]", + "Print a table with the points assigned to player after a versus or" + " room race.", + 0, + 3, + option_tab_cmd_POINTS, + option_allowed_cmd_POINTS + }, + + { CMD_REGEXP, + false, + false, + false, + "REGEXP", + "RE", + "wbmgt REGEXP pattern string...", + "Test implementation of regular expressions. PATTERN is the regular" + " expression to find matches. Each STRING is object of the match and" + " replace operation.", + 0, + 0, + option_tab_cmd_REGEXP, + option_allowed_cmd_REGEXP + }, + + { CMD_EXTRACT, + false, + false, + true, + "EXTRACT", + 0, + "wbmgt EXTRACT name source", + "Extract BMG section NAME of file SOURCE and write the binary data to" + " standard output. If NAME consists of less than 4 characters, then" + " the first section that starts with these characters is dumped.", + 0, + 1, + option_tab_cmd_EXTRACT, + option_allowed_cmd_EXTRACT + }, + + { CMD_SECTIONS, + false, + false, + false, + "SECTIONS", + 0, + "wbmgt SECTIONS [source]...", + "List all sections of each binary BMG file. Print offset, size, magic" + " and a short info. If known, print number and size of elements too.", + 0, + 1, + option_tab_cmd_SECTIONS, + option_allowed_cmd_SECTIONS + }, + + { CMD_LIST, + false, + false, + false, + "LIST", + "LS", + "wbmgt LIST [source]...", + "List messages to proof the content. Patches are applied before" + " listing. The lines are cut to fit the terminal width.", + 0, + 5, + option_tab_cmd_LIST, + option_allowed_cmd_LIST + }, + + { CMD_SLOTS, + false, + false, + false, + "SLOTS", + 0, + "wbmgt SLOTS [source]...", + "Print a slot ordered list with slot number, message id, delta and" + " attributes. Patches are applied before listing.", + 0, + 5, + option_tab_cmd_SLOTS, + option_allowed_cmd_SLOTS + }, + + { CMD_DIFF, + false, + false, + false, + "DIFF", + "CMP", + "wbmgt DIFF source1 source2\n" + "wbmgt DIFF source... --dest dest\n", + "Compare two or more BMG (raw or text) files on string level and" + " report mismatches for each string. If option --dest is set, then" + " each source file is compared with a file in the destination. Patches" + " are applied to all sources before comparing.", + 0, + 6, + option_tab_cmd_DIFF, + option_allowed_cmd_DIFF + }, + + { CMD_CAT, + false, + false, + true, + "CAT", + 0, + "wbmgt CAT [source]...", + "Read the entered BMG files (text, binary or szs sub file) and print" + " them as decoded text files to standard output. All BMG files of a" + " single archive (SZS,...) are combined to 1 source. The output of all" + " source files is concatenated.", + 0, + 20, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_MIX, + false, + false, + false, + "MIX", + 0, + "wbmgt MIX [source]...", + "Read the entered BMG files (text, binary or szs sub file) and print" + " them as decoded text files to standard output. All BMG files of all" + " archives (SZS,...) are read. The output of all source files is" + " combined and strings of later source files override prior defined" + " strings.", + 0, + 20, + option_tab_cmd_MIX, + option_allowed_cmd_MIX + }, + + { CMD_IDENTIFIER, + false, + false, + false, + "IDENTIFIER", + "ID", + "wbmgt IDENTIFIER", + "Create a simple text BMG with the identifier message 3DEF only with" + " respect to options --le-code and --ct-code. Print the result to" + " standard output.", + 0, + 18, + option_tab_cmd_IDENTIFIER, + option_allowed_cmd_IDENTIFIER + }, + + { CMD_DECODE, + false, + false, + false, + "DECODE", + "DEC", + "wbmgt DECODE [szs]...", + "Read the entered message files and decode them to text files. All BMG" + " files of a single archive (SZS,...) are concatenated to 1 source." + " This command is similar to PATCH, but the result is always decoded." + " The default destination is '%P/%N.txt'.", + 0, + 28, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_ENCODE, + false, + false, + false, + "ENCODE", + "ENC", + "wbmgt ENCODE [source]...", + "Read the entered message files and encode them to BMG files. All BMG" + " files of a single archive (SZS,...) are concatenated to 1 source." + " This command is similar to PATCH, but the result is always encoded." + " The default destination is '%P/%N.bmg'.", + 0, + 17, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + { CMD_PATCH, + false, + false, + false, + "PATCH", + "PAT", + "wbmgt PATCH [source]...", + "Patch the entered files by using the patch list specified by option" + " --patch-bmg. The coding of the source files is not changed. The" + " default destination is '%P/%F'.\n" + " Use 'wszst patch --patch-bmg ...' to patch BMG files within a SZS" + " file.", + 0, + 29, + option_tab_cmd_PATCH, + option_allowed_cmd_PATCH + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wbmgt = +{ + "wbmgt", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wbmgt.h b/project/src/ui/ui-wbmgt.h new file mode 100644 index 0000000..4dceb36 --- /dev/null +++ b/project/src/ui/ui-wbmgt.h @@ -0,0 +1,417 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WBMGT_H +#define SZS_UI_WBMGT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_ALIGN, + OPT_PATCH_BMG, + OPT_MACRO_BMG, + OPT_FILTER_BMG, + OPT_BMG_ENCODING, + OPT_BMG_INF_SIZE, + OPT_BMG_MID, + OPT_FORCE_ATTRIB, + OPT_DEF_ATTRIB, + OPT_NO_ATTRIB, + OPT_X_ESCAPES, + OPT_OLD_ESCAPES, + OPT_SINGLE_LINE, + OPT_NO_BMG_COLORS, + OPT_BMG_COLORS, + OPT_NO_BMG_INLINE, + OPT_EXPORT, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 31 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_POINTS, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 60 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_ALIGN = 1llu << OPT_ALIGN, +// OB_PATCH_BMG = 1llu << OPT_PATCH_BMG, +// OB_MACRO_BMG = 1llu << OPT_MACRO_BMG, +// OB_FILTER_BMG = 1llu << OPT_FILTER_BMG, +// OB_BMG_ENCODING = 1llu << OPT_BMG_ENCODING, +// OB_BMG_INF_SIZE = 1llu << OPT_BMG_INF_SIZE, +// OB_BMG_MID = 1llu << OPT_BMG_MID, +// OB_FORCE_ATTRIB = 1llu << OPT_FORCE_ATTRIB, +// OB_DEF_ATTRIB = 1llu << OPT_DEF_ATTRIB, +// OB_NO_ATTRIB = 1llu << OPT_NO_ATTRIB, +// OB_X_ESCAPES = 1llu << OPT_X_ESCAPES, +// OB_OLD_ESCAPES = 1llu << OPT_OLD_ESCAPES, +// OB_SINGLE_LINE = 1llu << OPT_SINGLE_LINE, +// OB_NO_BMG_COLORS = 1llu << OPT_NO_BMG_COLORS, +// OB_BMG_COLORS = 1llu << OPT_BMG_COLORS, +// OB_NO_BMG_INLINE = 1llu << OPT_NO_BMG_INLINE, +// OB_EXPORT = 1llu << OPT_EXPORT, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_TEXTOUT = OB_NO_HEADER +// | OB_BRIEF +// | OB_BMG_ENCODING +// | OB_BMG_INF_SIZE +// | OB_BMG_MID +// | OB_FORCE_ATTRIB +// | OB_DEF_ATTRIB +// | OB_NO_ATTRIB +// | OB_X_ESCAPES +// | OB_OLD_ESCAPES +// | OB_SINGLE_LINE +// | OB_NO_BMG_COLORS +// | OB_BMG_COLORS +// | OB_NO_BMG_INLINE +// | OB_LONG +// | OB_EXPORT, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_POINTS = OB_BRIEF +// | OB_LONG, +// +// OB_CMD_REGEXP = 0, +// +// OB_CMD_EXTRACT = OB_NO_HEADER, +// +// OB_CMD_SECTIONS = OB_IGNORE, +// +// OB_CMD_LIST = OB_IGNORE +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG, +// +// OB_CMD_SLOTS = OB_IGNORE +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG, +// +// OB_CMD_DIFF = OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG +// | OB_DEST +// | OB_ESC, +// +// OB_CMD_CAT = OB_GRP_TEXTOUT +// | OB_IGNORE +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG, +// +// OB_CMD_MIX = OB_CMD_CAT, +// +// OB_CMD_IDENTIFIER = OB_GRP_TEXTOUT, +// +// OB_CMD_DECODE = OB_GRP_DEST +// | OB_GRP_TEXTOUT +// | OB_IGNORE +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG, +// +// OB_CMD_ENCODE = OB_GRP_DEST +// | OB_BMG_ENCODING +// | OB_BMG_INF_SIZE +// | OB_BMG_MID +// | OB_FORCE_ATTRIB +// | OB_IGNORE +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG +// | OB_ALIGN, +// +// OB_CMD_PATCH = OB_GRP_DEST +// | OB_GRP_TEXTOUT +// | OB_IGNORE +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG +// | OB_ALIGN, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_POINTS, + CMD_REGEXP, + + CMD_EXTRACT, + CMD_SECTIONS, + CMD_LIST, + CMD_SLOTS, + CMD_DIFF, + + CMD_CAT, + CMD_MIX, + CMD_IDENTIFIER, + CMD_DECODE, + CMD_ENCODE, + CMD_PATCH, + + + CMD__N // == 22 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO_SINGLE_LINE = '1', + + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_PATCH_BMG = 'P', + GO_TRACKS = 'T', + GO_VERSION = 'V', + GO_EXPORT = 'X', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_POINTS, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_ALIGN, + GO_MACRO_BMG, + GO_FILTER_BMG, + GO_BMG_ENCODING, + GO_BMG_INF_SIZE, + GO_BMG_MID, + GO_FORCE_ATTRIB, + GO_DEF_ATTRIB, + GO_NO_ATTRIB, + GO_X_ESCAPES, + GO_OLD_ESCAPES, + GO_NO_BMG_COLORS, + GO_BMG_COLORS, + GO_NO_BMG_INLINE, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wbmgt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WBMGT_H + diff --git a/project/src/ui/ui-wctct.c b/project/src/ui/ui-wctct.c new file mode 100644 index 0000000..cd7f2d6 --- /dev/null +++ b/project/src/ui/ui-wctct.c @@ -0,0 +1,1952 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wctct.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_ROUND, false, false, false, false, false, 0, "round", + 0, + "Print additional lines with rounded values (to 3 and 2 bytes for" + " floats and 7 and 6 bytes for doubles)." + }, + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress cross reference comments." + }, + + { OPT_RAW, false, false, false, false, false, 0, "raw", + 0, + "Print BMG messages as UTF-8 text without escape sequences." + }, + + { OPT_BMG_ENCODING, false, false, false, false, false, 0, "bmg-encoding", + "name", + "Force a BMG encoding for new BMG files. Accepted encoding names are:" + " CP-1252, UTF-16BE (or UTF-16), SHIFT-JIS, UTF-8 and AUTO for" + " automatic detection (default). Same keywords without minus signs are" + " accepted too.\n" + " Mario Kart Wii uses encoding UTF-16BE; the default if no other" + " encoding is defined." + }, + + { OPT_BMG_INF_SIZE, false, false, false, false, false, 0, "bmg-inf-size", + "size", + "Defines the 'INF1' size of new BMG files between 4 and 1000. The" + " first 4 bytes of a INF1 record is an offset into the string pool and" + " the remaining bytes the attributes. Maximal 40 attribute bytes are" + " supported and additional bytes are assumed to be NULL. All BMG files" + " of MKW have a size of 8." + }, + + { OPT_BMG_MID, true, false, false, false, false, 0, "bmg-mid", + "[mode]", + "Control the creation of BMG section MID1. Suppress the creation, if" + " mode is OFF. Force the creation, if mode is ON. On mode AUTO" + " (default), the source is analysed for the decision." + }, + + { OPT_FORCE_ATTRIB, false, false, false, false, false, 0, "force-attrib", + "attrib", + "Forces, that all attributes are set to this vector. See" + " https://szs.wiimm.de/doc/bmg/attrib for the syntax." + }, + + { OPT_DEF_ATTRIB, false, false, false, false, false, 0, "def-attrib", + "attrib", + "Define the default attributes for BMG files. If not set, the default" + " attributes are estimated. On text output, strings with default" + " attributes are printed without attribute vector. The usual default" + " attribute for MKW is [1]. See https://szs.wiimm.de/doc/bmg/attrib" + " for the syntax." + }, + + { OPT_NO_ATTRIB, false, false, false, false, false, 0, "no-attrib", + 0, + "Suppress the output of any BMG attributes if creating BMG text files." + }, + + { OPT_X_ESCAPES, false, false, false, false, false, 0, "x-escapes", + 0, + "Use \\x{} escapes instead of \\z{} escapes." + }, + + { OPT_OLD_ESCAPES, false, false, false, false, false, 0, "old-escapes", + 0, + "To be compatible with v1.43 and earlier, print 1A escape sequences" + " with total size of >12 bytes as single words using \\x{}. Also don't" + " use \\x{} and \\u{} with parameter lists." + }, + + { OPT_SINGLE_LINE, false, false, false, false, false, '1', "single-line", + 0, + "If set, don't print continuation lines for BMG text output. If set" + " twice, print only single text lines but not value lines." + }, + + { OPT_NO_BMG_COLORS, false, false, false, false, false, 0, "no-bmg-colors", + 0, + "If set, suppress the output of '\\c{color}' escape sequences for" + " colors definitions in BMG text output to be compatible with old" + " versions of the tools." + }, + + { OPT_BMG_COLORS, false, false, false, false, false, 0, "bmg-colors", + 0, + "Force output of '\\c{color}' escape sequences with names, even for" + " foreign (not MKWii) BMG files." + }, + + { OPT_NO_BMG_INLINE, false, false, false, false, false, 0, "no-bmg-inline", + 0, + "Print BMG attributes as separate line before the message definition" + " to be compatible with versions until v1.39. The output for the" + " standard value 0x01000000 is always suppressed. This was the default" + " until v1.43." + }, + + { OPT_EXPORT, false, false, false, false, false, 'X', "export", + 0, + "Enable the export mode and create small and machine readable text" + " files for easy post processing. The option works similar like -HB" + " for CT-CODE output." + }, + + { OPT_NO_PARAM, false, false, false, false, false, 'P', "no-param", + 0, + "Disable parameter support in decoded KMP text files." + }, + + { OPT_NO_ECHO, false, false, false, false, false, 0, "no-echo", + 0, + "Suppress output of @ECHO commands while scanning text files." + }, + + { OPT_NO_CHECK, false, false, false, false, false, 'N', "no-check", + 0, + "Do not make plausibility checks for KCL and KMP files." + }, + + { OPT_LIST, false, false, false, false, true, 0, "list", + 0, + "Cancel all previous --rel and --full and enter LIST mode (default)" + " for text output.\n" + " In LIST mode only a track list is generated. On scanning the tracks" + " are automatically distributed to the cups. This makes rearanging" + " very simple." + }, + + { OPT_REF, false, false, false, false, false, 0, "ref", + 0, + "Cancel all previous --list and --full and enter REFERENCE mode for" + " text output.\n" + " In REFERENCE mode all tracks and cups are printed separately. After" + " scanning the cup and track layout is the same as the original code" + " file." + }, + + { OPT_FULL, false, false, false, false, false, 0, "full", + 0, + "Cancel all previous --list and --rel and enter FULL mode for text" + " output.\n" + " FULL mode is a combination of LIST and REF modes." + }, + + { OPT_HEX, false, false, false, false, false, 0, "hex", + 0, + "Normally and if available, names are printed for music and property" + " slots. If this option is set, hex numbers are printed always." + }, + + { OPT_IMAGES, false, false, false, false, false, 0, "images", + "dir", + "If creating a CT-CODE BRRES or SZS file, then search the 8 strap" + " images in the specified directory. The images are searched with" + " extensions .png, .tex, .tex0 and without extension in this order" + " until a valid image is found. All images will be resized to the" + " correct width and height and converted to format TEX.RGB565 without" + " mipmaps. If no valid image is found, a hard coded default image is" + " used." + }, + + { OPT_LOAD_BMG, false, false, false, false, false, 0, "load-bmg", + "file", + "Load immediately the file to fill an internal message pool. The" + " option can be used multiple times to fill the message pool by" + " different sources. A later message overrides the previous message.\n" + " The internal message pool is patched by all --patch-bmg options." + }, + + { OPT_PATCH_BMG, false, false, false, false, false, 0, "patch-bmg", + "mode[cond][=param]", + "This option specifies a BMG patch mode. Some of the modes need a" + " parameter or a file name of a BMG patch file (raw or text BMG), both" + " separated by an equal sign. Modes with required file names are" + " PRINT, REPLACE, INSERT, OVERWRITE, DELETE, MASK, EQUAL and NOTEQUAL." + " Modes with text parameter are FORMAT, REGEXP and RM-REGEXP. " + " Standalone modes are ID, ID-ALL, UNICODE, RM-ESCAPES, RM-CUPS," + " CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY, LE-FILL," + " X-COPY, X-FORCE-COPY and X-FILL. Unique abbreviations are allowed.\n" + " The optional condition COND is either '?MID' or '!MID'. In case of" + " '?MID', the patch is only applied if the message id MID already" + " exists. In case of '!MID', the patch is only applied if the message" + " id MID does not exists.\n" + " If this option is used multiple times, all patch files will be" + " processed in the entered order. Read" + " https://szs.wiimm.de/opt/patch-bmg for more details." + }, + + { OPT_MACRO_BMG, false, false, false, false, false, 0, "macro-bmg", + "file", + "Load a BMG file and add the messages to the macro library; already" + " existing entries are replaced. The macro library is accessed by" + " escape sequence \\m{MID} as fallback, if the active file has not" + " already defined the message MID by itself." + }, + + { OPT_FILTER_BMG, false, false, false, false, false, 0, "filter-bmg", + "list", + "If this option is set, the BMG messages are filtered by this list." + " Only enabled messages are exported to the output BMG file (binary or" + " text).\n" + " The parameter is a list of message ids (short MID, 'Txx' or 'Uxx'" + " or Mxx' or hex number) or message ranges (MID:MID) or one of the" + " keywords NONE, IDENT (=ID), PARAM, CUPS, TRACKS, ARENAS, CHAT," + " CTCODE (=CT), CTUPS, CTTRACKS, CTARENAS, CTREFS, LECODE (=LE)," + " LEUPS, LETRACKS, LEARENAS, LEREFS, XCODE (=X), XUPS, XTRACKS," + " XARENAS, XREFS, ALLCODE, ALLUPS, ALLTRACKS, ALLARENAS, ALLREFS," + " GENERIC or ALL. If an element is preceded by a minus sign, it is" + " removed from the filter list (disabled).\n" + " With v2.01a, this option was renamed from --msg to the more" + " meaningful name --filter-bmg. The old name is still available.\n" + " Read https://szs.wiimm.de/opt/filter-bmg for more details." + }, + + { OPT_PATCH_NAMES, false, false, false, false, false, 0, "patch-names", + 0, + "If set, the names of the CUP1 and CSR1 sections are patched by the" + " loaded BMG files directly after loading the source files." + }, + + { OPT_ORDER_BY, false, false, false, false, false, 0, "order-by", + "bmg", + "Load the BMG file and filter track names. Then before creating output" + " files, re-distribute the tracks of the additonal cups (9-) to the" + " same cups by the order of the read track names. Tracks without BMG" + " reference are placed at the end of all tracks. This option modifes" + " only the track order of the cups, but will not change any name." + }, + + { OPT_ORDER_ALL, false, false, false, false, false, 0, "order-all", + 0, + "Used together with option --order-by: Re-distribute the tracks of all" + " cups beginning with Mushroom Cup." + }, + + { OPT_DYNAMIC, false, true, false, false, false, 0, "dynamic", + 0, + "Enable dynamic length calculation for the CTGP_CODE part. The" + " surrounding TEX and BRRES files are also adjusted. The usage of this" + " option is EXPERIMENTAL." + }, + + { OPT_WRITE_TRACKS, false, false, false, false, true, 0, "write-tracks", + "file", + "Create a machine readable file reflecting the racing track" + " distribution to the cups. One line of format 'IDX|MID|NAME' is" + " written for each track. --wtracks is a short cut." + }, + + { OPT_DEST, false, false, false, false, false, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 46 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "Enable LE-CODE and CT-CODE support. The optional parameter is" + " deprecated and ignored. See https://szs.wiimm.de/opt/lecode for" + " details." + }, + + { OPT_OLD_SPINY, false, false, false, false, false, 0, "old-spiny", + 0, + "Disable the new feature to hold a Spiny Shell at the tail." + " https://szs.wiimm.de/opt/ctcode for details." + }, + + { OPT_CRS1, false, false, false, false, false, 0, "crs1", + "value", + "Store this value into all 8 members of the property array of the CRS1" + " header if reading a CT-TEXT. The default value is 0x800." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_TRACKS, false, true, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, true, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + { OPT_CT_DIR, false, false, false, false, true, 0, "ct-dir", + "dir", + "Define a search directory for CT-CODE parts and use the internal" + " copies only, if no valid file is found. Each file is searched" + " without and then with extension '.bz2'. For both cases bzip2 files" + " are detected and decompressed. Only files with correct file type are" + " accepted.\n" + " wctct will search in the sub directory 'rmc[pujk]/' for files" + " 'mod1.bin', 'mod2.bin', 'ovr1.bin' and 'bad1code.bin'.\n" + " Use this option multiple times to define more than one search" + " directory." + }, + + { OPT_CT_LOG, true, false, false, false, false, 0, "ct-log", + "[=level]", + "Define a log level for option --ct-dir. Without parameter, the" + " current level is increased. Allowed keywords are: 0=0FF, 1=SUCCESS," + " 2=FOUND, 3=ALL" + }, + + { OPT_ALLOW_SLOTS, false, false, false, false, false, 0, "allow-slots", + "list", + "Allow and disallow slots for automatic slot assigning. A comma" + " separated list of slots (A), slot ranges (A:B) or keywords is" + " expected. If preceded by a minus sign, the slots are disabled. The" + " keywords are: TRACKS (slots 0:0x1f), ARENAS (0x20:0x29) and SPECIAL" + " (0x36:0x3a). CTCODE is an abbreviation for ARENAS,SPECIAL.\n" + " The default is to enable slot ranges 0x00:0x1f, 0x2a:0x35," + " 0x3b:0x41 and 0x44:0xfe." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 79 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_grp_TEXTOPT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in CTC text files." + }; + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the description and print only function type" + " and syntax." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Usually only the function syntax is compared to the keywords. But if" + " --long is set, the descriptions are compared too." + }; + +static const InfoOption_t option_cmd_DUMP_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print headers as hexdump." + }; + +static const InfoOption_t option_cmd_BMG_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Filter the message by tracks, that are used a racing track. If set" + " twice, suppress also tracks created by the 'N' command of CT-TEXT" + " files. If set 3 times, suppress always the output of first 32" + " tracks." + }; + +static const InfoOption_t option_cmd_BMG_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Normally, only the track and arena names are exported. The message id" + " (MID) range is 4000-40FF. If set, cup names are exported too as MID" + " 4200-423F. If set twice, cup/track relations are also exported as" + " MID 4300-43FF." + }; + +static const InfoOption_t option_cmd_TRACKS_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the table header." + }; + +static const InfoOption_t option_cmd_TRACKS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the music and property slots." + }; + +static const InfoOption_t option_cmd_TRACKS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Include the first 32 standard tracks into the output. If set twice," + " also include the battle arenas." + }; + +static const InfoOption_t option_cmd_CREATE_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "BMG: Filter the message by tracks, that are used a racing track. If" + " set twice, suppress also tracks created by the 'N' command of" + " CT-TEXT files. If set 3 times, suppress always the output of first" + " 32 tracks." + }; + +static const InfoOption_t option_cmd_CREATE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "BMG: Normally, only the track and arena names are exported. The" + " message id (MID) range is 4000-40FF. If set, cup names are exported" + " too as MID 4200-423F. If set twice, cup/track relations are also" + " exported as MID 4300-43FF." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_SYMBOLS, "SYMBOLS", 0, 0 }, + { CMD_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { CMD_CALCULATE, "CALCULATE", 0, 0 }, + { CMD_FLOAT, "FLOAT", 0, 0 }, + { CMD_EXPORT, "_EXPORT", 0, 0 }, + { CMD_RAWDUMP, "_RAWDUMP", 0, 0 }, + { CMD_DUMP, "D", "DUMP", 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_BMG, "BMG", 0, 0 }, + { CMD_TRACKS, "TRACKS", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_CREATE, "CREATE", "C", 0 }, + { CMD_PATCH, "PATCH", "P", 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHB1XPNM:T:A:td:D:E:orupi"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "old-spiny", 0, 0, GO_OLD_SPINY }, + { "oldspiny", 0, 0, GO_OLD_SPINY }, + { "crs1", 1, 0, GO_CRS1 }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "round", 0, 0, GO_ROUND }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "raw", 0, 0, GO_RAW }, + { "bmg-encoding", 1, 0, GO_BMG_ENCODING }, + { "bmgencoding", 1, 0, GO_BMG_ENCODING }, + { "bmg-inf-size", 1, 0, GO_BMG_INF_SIZE }, + { "bmginfsize", 1, 0, GO_BMG_INF_SIZE }, + { "bmg-mid", 2, 0, GO_BMG_MID }, + { "bmgmid", 2, 0, GO_BMG_MID }, + { "force-attrib", 1, 0, GO_FORCE_ATTRIB }, + { "forceattrib", 1, 0, GO_FORCE_ATTRIB }, + { "def-attrib", 1, 0, GO_DEF_ATTRIB }, + { "defattrib", 1, 0, GO_DEF_ATTRIB }, + { "no-attrib", 0, 0, GO_NO_ATTRIB }, + { "noattrib", 0, 0, GO_NO_ATTRIB }, + { "x-escapes", 0, 0, GO_X_ESCAPES }, + { "xescapes", 0, 0, GO_X_ESCAPES }, + { "old-escapes", 0, 0, GO_OLD_ESCAPES }, + { "oldescapes", 0, 0, GO_OLD_ESCAPES }, + { "single-line", 0, 0, '1' }, + { "singleline", 0, 0, '1' }, + { "no-bmg-colors", 0, 0, GO_NO_BMG_COLORS }, + { "nobmgcolors", 0, 0, GO_NO_BMG_COLORS }, + { "bmg-colors", 0, 0, GO_BMG_COLORS }, + { "bmgcolors", 0, 0, GO_BMG_COLORS }, + { "no-bmg-inline", 0, 0, GO_NO_BMG_INLINE }, + { "nobmginline", 0, 0, GO_NO_BMG_INLINE }, + { "export", 0, 0, 'X' }, + { "no-param", 0, 0, 'P' }, + { "noparam", 0, 0, 'P' }, + { "no-echo", 0, 0, GO_NO_ECHO }, + { "noecho", 0, 0, GO_NO_ECHO }, + { "no-check", 0, 0, 'N' }, + { "nocheck", 0, 0, 'N' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "list", 0, 0, GO_LIST }, + { "ref", 0, 0, GO_REF }, + { "full", 0, 0, GO_FULL }, + { "hex", 0, 0, GO_HEX }, + { "ct-dir", 1, 0, GO_CT_DIR }, + { "ctdir", 1, 0, GO_CT_DIR }, + { "ct-log", 2, 0, GO_CT_LOG }, + { "ctlog", 2, 0, GO_CT_LOG }, + { "allow-slots", 1, 0, GO_ALLOW_SLOTS }, + { "allowslots", 1, 0, GO_ALLOW_SLOTS }, + { "images", 1, 0, GO_IMAGES }, + { "load-bmg", 1, 0, GO_LOAD_BMG }, + { "loadbmg", 1, 0, GO_LOAD_BMG }, + { "patch-bmg", 1, 0, GO_PATCH_BMG }, + { "patchbmg", 1, 0, GO_PATCH_BMG }, + { "pb", 1, 0, GO_PATCH_BMG }, + { "macro-bmg", 1, 0, GO_MACRO_BMG }, + { "macrobmg", 1, 0, GO_MACRO_BMG }, + { "mb", 1, 0, GO_MACRO_BMG }, + { "filter-bmg", 1, 0, GO_FILTER_BMG }, + { "filterbmg", 1, 0, GO_FILTER_BMG }, + { "fb", 1, 0, GO_FILTER_BMG }, + { "patch-names", 0, 0, GO_PATCH_NAMES }, + { "patchnames", 0, 0, GO_PATCH_NAMES }, + { "order-by", 1, 0, GO_ORDER_BY }, + { "orderby", 1, 0, GO_ORDER_BY }, + { "order", 1, 0, GO_ORDER_BY }, + { "order-all", 0, 0, GO_ORDER_ALL }, + { "orderall", 0, 0, GO_ORDER_ALL }, + { "dynamic", 0, 0, GO_DYNAMIC }, + { "write-tracks", 1, 0, GO_WRITE_TRACKS }, + { "writetracks", 1, 0, GO_WRITE_TRACKS }, + { "wtracks", 1, 0, GO_WRITE_TRACKS }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0, + /* 0x031 1 */ OPT_SINGLE_LINE, + /* 0x032 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e N */ OPT_NO_CHECK, + /* 0x04f */ 0, + /* 0x050 P */ OPT_NO_PARAM, + /* 0x051 */ 0,0,0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0, + /* 0x058 X */ OPT_EXPORT, + /* 0x059 */ 0,0,0,0, 0,0,0,0, 0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_OLD_SPINY, + /* 0x08a */ OPT_CRS1, + /* 0x08b */ OPT_CHDIR, + /* 0x08c */ OPT_ROUND, + /* 0x08d */ OPT_RAW, + /* 0x08e */ OPT_BMG_ENCODING, + /* 0x08f */ OPT_BMG_INF_SIZE, + /* 0x090 */ OPT_BMG_MID, + /* 0x091 */ OPT_FORCE_ATTRIB, + /* 0x092 */ OPT_DEF_ATTRIB, + /* 0x093 */ OPT_NO_ATTRIB, + /* 0x094 */ OPT_X_ESCAPES, + /* 0x095 */ OPT_OLD_ESCAPES, + /* 0x096 */ OPT_NO_BMG_COLORS, + /* 0x097 */ OPT_BMG_COLORS, + /* 0x098 */ OPT_NO_BMG_INLINE, + /* 0x099 */ OPT_NO_ECHO, + /* 0x09a */ OPT_UTF_8, + /* 0x09b */ OPT_NO_UTF_8, + /* 0x09c */ OPT_FORCE, + /* 0x09d */ OPT_REPAIR_MAGICS, + /* 0x09e */ OPT_OLD, + /* 0x09f */ OPT_STD, + /* 0x0a0 */ OPT_NEW, + /* 0x0a1 */ OPT_EXTRACT, + /* 0x0a2 */ OPT_LIST, + /* 0x0a3 */ OPT_REF, + /* 0x0a4 */ OPT_FULL, + /* 0x0a5 */ OPT_HEX, + /* 0x0a6 */ OPT_CT_DIR, + /* 0x0a7 */ OPT_CT_LOG, + /* 0x0a8 */ OPT_ALLOW_SLOTS, + /* 0x0a9 */ OPT_IMAGES, + /* 0x0aa */ OPT_LOAD_BMG, + /* 0x0ab */ OPT_PATCH_BMG, + /* 0x0ac */ OPT_MACRO_BMG, + /* 0x0ad */ OPT_FILTER_BMG, + /* 0x0ae */ OPT_PATCH_NAMES, + /* 0x0af */ OPT_ORDER_BY, + /* 0x0b0 */ OPT_ORDER_ALL, + /* 0x0b1 */ OPT_DYNAMIC, + /* 0x0b2 */ OPT_WRITE_TRACKS, + /* 0x0b3 */ OPT_NUMBER, + /* 0x0b4 */ OPT_SECTIONS, + /* 0x0b5 */ 0,0,0,0, 0,0,0,0, 0,0,0, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[46] = // cmd #1 +{ + 0,0,1,0,1, 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, 1 +}; + +static u8 option_allowed_cmd_HELP[46] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_ARGTEST[46] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_TEST[46] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_COLORS[46] = // cmd #5 +{ + 0,0,1,0,1, 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 u8 option_allowed_cmd_ERROR[46] = // cmd #6 +{ + 0,0,1,1,1, 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, 1 +}; + +static u8 option_allowed_cmd_FILETYPE[46] = // cmd #7 +{ + 0,0,1,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,1, 0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[46] = // cmd #8 +{ + 0,0,0,1,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 u8 option_allowed_cmd_SYMBOLS[46] = // cmd #9 +{ + 0,0,0,1,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 u8 option_allowed_cmd_FUNCTIONS[46] = // cmd #10 +{ + 0,0,1,1,1, 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 u8 option_allowed_cmd_CALCULATE[46] = // cmd #11 +{ + 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 u8 option_allowed_cmd_FLOAT[46] = // cmd #12 +{ + 0,1,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 u8 option_allowed_cmd_EXPORT[46] = // cmd #13 +{ + 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 u8 option_allowed_cmd_RAWDUMP[46] = // cmd #14 +{ + 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,1,1,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_DUMP[46] = // cmd #15 +{ + 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,1, + 1,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_CAT[46] = // cmd #16 +{ + 0,0,0,1,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 1,1,1,1,1, 1,0,1,1,1, + 1,1,1,1,0, 1,0,0,0,0, 0,0,0,0,1, 0 +}; + +static u8 option_allowed_cmd_BMG[46] = // cmd #17 +{ + 0,0,1,1,1, 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 1,1,1,1,1, 1,0,1,1,1, + 1,1,1,1,0, 1,0,0,0,0, 0,0,0,0,1, 0 +}; + +static u8 option_allowed_cmd_TRACKS[46] = // cmd #18 +{ + 0,0,1,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 1,1,1,1,1, 1,0,1,1,1, + 1,1,1,1,0, 1,0,0,0,0, 0,0,0,0,1, 0 +}; + +static u8 option_allowed_cmd_DECODE[46] = // cmd #19 +{ + 0,0,0,1,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 1,1,1,1,1, 1,0,1,1,1, + 1,1,1,1,0, 1,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_CREATE[46] = // cmd #20 +{ + 0,0,1,1,1, 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 1,1,0,0,0, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_PATCH[46] = // cmd #21 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,0,0,0, 0,0,1,1,1, + 1,1,1,1,0, 1,1,1,1,1, 1,1,1,1,1, 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_OLD_SPINY, + OptionInfo + OPT_CRS1, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_MAX_FILE_SIZE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CT_DIR, + OptionInfo + OPT_CT_LOG, + OptionInfo + OPT_ALLOW_SLOTS, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SYMBOLS[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FUNCTIONS[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_FUNCTIONS_BRIEF, + &option_cmd_FUNCTIONS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CALCULATE[] = +{ + OptionInfo + OPT_CONST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLOAT[] = +{ + OptionInfo + OPT_ROUND, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_RAWDUMP[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DUMP[] = +{ + &option_cmd_DUMP_LONG, + OptionInfo + OPT_LOAD_BMG, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_PATCH_NAMES, + OptionInfo + OPT_ORDER_BY, + OptionInfo + OPT_ORDER_ALL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_LIST, + OptionInfo + OPT_REF, + OptionInfo + OPT_FULL, + OptionInfo + OPT_HEX, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_WRITE_TRACKS, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_LOAD_BMG, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_PATCH_NAMES, + OptionInfo + OPT_ORDER_BY, + OptionInfo + OPT_ORDER_ALL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_BMG[] = +{ + &option_cmd_BMG_BRIEF, + &option_cmd_BMG_LONG, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_LIST, + OptionInfo + OPT_REF, + OptionInfo + OPT_FULL, + OptionInfo + OPT_HEX, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + OptionInfo + OPT_WRITE_TRACKS, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_LOAD_BMG, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_PATCH_NAMES, + OptionInfo + OPT_ORDER_BY, + OptionInfo + OPT_ORDER_ALL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TRACKS[] = +{ + &option_cmd_TRACKS_NO_HEADER, + &option_cmd_TRACKS_BRIEF, + &option_cmd_TRACKS_LONG, + OptionInfo + OPT_RAW, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_LIST, + OptionInfo + OPT_REF, + OptionInfo + OPT_FULL, + OptionInfo + OPT_HEX, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_WRITE_TRACKS, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_LOAD_BMG, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_PATCH_NAMES, + OptionInfo + OPT_ORDER_BY, + OptionInfo + OPT_ORDER_ALL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_WRITE_TRACKS, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_LIST, + OptionInfo + OPT_REF, + OptionInfo + OPT_FULL, + OptionInfo + OPT_HEX, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_LOAD_BMG, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_PATCH_NAMES, + OptionInfo + OPT_ORDER_BY, + OptionInfo + OPT_ORDER_ALL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CREATE[] = +{ + OptionInfo + OPT_WRITE_TRACKS, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_HEX, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + &option_cmd_CREATE_LONG, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_CT_DIR, + OptionInfo + OPT_CT_LOG, + OptionInfo + OPT_ALLOW_SLOTS, + OptionInfo + OPT_IMAGES, + OptionInfo + OPT_LOAD_BMG, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_PATCH_NAMES, + OptionInfo + OPT_ORDER_BY, + OptionInfo + OPT_ORDER_ALL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_PATCH[] = +{ + OptionInfo + OPT_WRITE_TRACKS, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_LOAD_BMG, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + OptionInfo + OPT_PATCH_NAMES, + OptionInfo + OPT_ORDER_BY, + OptionInfo + OPT_ORDER_ALL, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wctct", + 0, + "wctct [option]... command [option|parameter|file]...", + "Wiimms CT-CODE Tool : Manage the CT-CODE extension. BRRES, TEX0," + " CT-CODE, CT-TEXT and LE-BIN files are accepted as input.", + 0, + 25, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wctct VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wctct HELP [--width=wd] OPT|OPTION ...\n" + "wctct HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wctct HELP [--width=wd] command [-b] [ignored]...\n" + "wctct HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wctct ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wctct TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 0, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wctct COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wctct ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wctct FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wctct FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_SYMBOLS, + false, + false, + false, + "SYMBOLS", + 0, + "wctct SYMBOLS", + "Print predefined symbols of text parser to standard output. The" + " CT-CODE specific symbols are included.", + 0, + 1, + option_tab_cmd_SYMBOLS, + option_allowed_cmd_SYMBOLS + }, + + { CMD_FUNCTIONS, + false, + false, + false, + "FUNCTIONS", + 0, + "wctct FUNCTIONS [key]...", + "Print a list with all parser functions including the CT-CODE specific" + " functions. If one key ore more key entered, then only functions that" + " match at least one key are printed.", + 0, + 3, + option_tab_cmd_FUNCTIONS, + option_allowed_cmd_FUNCTIONS + }, + + { CMD_CALCULATE, + false, + false, + false, + "CALCULATE", + 0, + "wctct CALCULATE expression...", + "Calculate each expression and print the result to standard output.", + 0, + 1, + option_tab_cmd_CALCULATE, + option_allowed_cmd_CALCULATE + }, + + { CMD_FLOAT, + false, + false, + false, + "FLOAT", + 0, + "wctct FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa. For each non empty parameter, a line with 4 bytes" + " hex and a float, 8 bytes hex and a double float, and the parameter" + " itself is printed. If a parameter begins with 'x' or '0x', it is a" + " hex string, otherwise it is a floating point number.\n" + " The special parameters \"+\" and \"-\" mean: Increment or decrement" + " the previous value by the smallest possible value. Use different" + " increments for floats and doubles.\n" + " All conversions are done using big endian.", + 0, + 1, + option_tab_cmd_FLOAT, + option_allowed_cmd_FLOAT + }, + + { CMD_EXPORT, + true, + false, + false, + "_EXPORT", + 0, + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output. Use" + " keyword HELP to get a list of possible keywords.", + 0, + 0, + option_tab_cmd_EXPORT, + option_allowed_cmd_EXPORT + }, + + { CMD_RAWDUMP, + true, + false, + false, + "_RAWDUMP", + 0, + "wszst _RAWDUMP [-d|-D] destdir", + "This hidden command creates the destination directory and create" + " several files as copy of internal data.", + 0, + 2, + option_tab_cmd_RAWDUMP, + option_allowed_cmd_RAWDUMP + }, + + { CMD_DUMP, + false, + false, + true, + "D", + "DUMP", + "wctct DUMP [source]...", + "Print an information dump for the CT-CODE of each source file. BRRES," + " TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as input.", + 0, + 8, + option_tab_cmd_DUMP, + option_allowed_cmd_DUMP + }, + + { CMD_CAT, + false, + false, + false, + "CAT", + 0, + "wctct CAT [source]...", + "Read the source files and print the CT-CODE part them as decoded text" + " files to standard output. The output of all source files is" + " concatenated. BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted" + " as input.", + 0, + 18, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_BMG, + false, + false, + false, + "BMG", + 0, + "wctct BMG [source]...", + "Read the source files and print a BMG message listing to standard" + " output. BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as" + " input.", + 0, + 31, + option_tab_cmd_BMG, + option_allowed_cmd_BMG + }, + + { CMD_TRACKS, + false, + false, + false, + "TRACKS", + 0, + "wctct TRACKS [source]...", + "Read the source files and print a track listing to standard output." + " BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as input.", + 0, + 20, + option_tab_cmd_TRACKS, + option_allowed_cmd_TRACKS + }, + + { CMD_DECODE, + false, + false, + false, + "DECODE", + "DEC", + "wctct DECODE [source]...", + "Read the source files and decode them to CT-TEXT files. The default" + " destination is '%P/%N.txt'. BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN" + " are accepted as input.", + 0, + 26, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_CREATE, + false, + false, + false, + "CREATE", + "C", + "wctct CREATE keyword [source]...", + "Read the source files, scan the CT-CODE part and create an output" + " file controlled by the keyword. BRRES, TEX0, CT-CODE, CT-TEXT and" + " LE-BIN files are accepted as input. Possible keywords are: CUP1," + " CRS1, r-MOD1, r-MOD2, r-OVR1, r-BAD1CODE, r-BAD1DATA (=r-CODE)," + " r-TEX0, r-BRRES, r-SZS, BMG, TRACKS, LIST, REF and FULL. 'r' defines" + " the region and is one of EU, RMCP, US, RMCE, JP, RMCJ, KO or RMCK.\n" + " The Korean support needs external CT-CODE files. Use option" + " --ct-dir to define at least one search path.", + 0, + 40, + option_tab_cmd_CREATE, + option_allowed_cmd_CREATE + }, + + { CMD_PATCH, + false, + false, + false, + "PATCH", + "P", + "wctct PATCH [source]...", + "Read each source file, find and scan the CT-CODE part and patch it." + " BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as input.", + 0, + 18, + option_tab_cmd_PATCH, + option_allowed_cmd_PATCH + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wctct = +{ + "wctct", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wctct.h b/project/src/ui/ui-wctct.h new file mode 100644 index 0000000..4e0e4d0 --- /dev/null +++ b/project/src/ui/ui-wctct.h @@ -0,0 +1,505 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WCTCT_H +#define SZS_UI_WCTCT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_ROUND, + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_RAW, + OPT_BMG_ENCODING, + OPT_BMG_INF_SIZE, + OPT_BMG_MID, + OPT_FORCE_ATTRIB, + OPT_DEF_ATTRIB, + OPT_NO_ATTRIB, + OPT_X_ESCAPES, + OPT_OLD_ESCAPES, + OPT_SINGLE_LINE, + OPT_NO_BMG_COLORS, + OPT_BMG_COLORS, + OPT_NO_BMG_INLINE, + OPT_EXPORT, + OPT_NO_PARAM, + OPT_NO_ECHO, + OPT_NO_CHECK, + OPT_LIST, + OPT_REF, + OPT_FULL, + OPT_HEX, + OPT_IMAGES, + OPT_LOAD_BMG, + OPT_PATCH_BMG, + OPT_MACRO_BMG, + OPT_FILTER_BMG, + OPT_PATCH_NAMES, + OPT_ORDER_BY, + OPT_ORDER_ALL, + OPT_DYNAMIC, + OPT_WRITE_TRACKS, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 46 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_OLD_SPINY, + OPT_CRS1, + OPT_CHDIR, + OPT_CONST, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + OPT_CT_DIR, + OPT_CT_LOG, + OPT_ALLOW_SLOTS, + + OPT__N_TOTAL // == 79 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_ROUND = 1llu << OPT_ROUND, +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_RAW = 1llu << OPT_RAW, +// OB_BMG_ENCODING = 1llu << OPT_BMG_ENCODING, +// OB_BMG_INF_SIZE = 1llu << OPT_BMG_INF_SIZE, +// OB_BMG_MID = 1llu << OPT_BMG_MID, +// OB_FORCE_ATTRIB = 1llu << OPT_FORCE_ATTRIB, +// OB_DEF_ATTRIB = 1llu << OPT_DEF_ATTRIB, +// OB_NO_ATTRIB = 1llu << OPT_NO_ATTRIB, +// OB_X_ESCAPES = 1llu << OPT_X_ESCAPES, +// OB_OLD_ESCAPES = 1llu << OPT_OLD_ESCAPES, +// OB_SINGLE_LINE = 1llu << OPT_SINGLE_LINE, +// OB_NO_BMG_COLORS = 1llu << OPT_NO_BMG_COLORS, +// OB_BMG_COLORS = 1llu << OPT_BMG_COLORS, +// OB_NO_BMG_INLINE = 1llu << OPT_NO_BMG_INLINE, +// OB_EXPORT = 1llu << OPT_EXPORT, +// OB_NO_PARAM = 1llu << OPT_NO_PARAM, +// OB_NO_ECHO = 1llu << OPT_NO_ECHO, +// OB_NO_CHECK = 1llu << OPT_NO_CHECK, +// OB_LIST = 1llu << OPT_LIST, +// OB_REF = 1llu << OPT_REF, +// OB_FULL = 1llu << OPT_FULL, +// OB_HEX = 1llu << OPT_HEX, +// OB_IMAGES = 1llu << OPT_IMAGES, +// OB_LOAD_BMG = 1llu << OPT_LOAD_BMG, +// OB_PATCH_BMG = 1llu << OPT_PATCH_BMG, +// OB_MACRO_BMG = 1llu << OPT_MACRO_BMG, +// OB_FILTER_BMG = 1llu << OPT_FILTER_BMG, +// OB_PATCH_NAMES = 1llu << OPT_PATCH_NAMES, +// OB_ORDER_BY = 1llu << OPT_ORDER_BY, +// OB_ORDER_ALL = 1llu << OPT_ORDER_ALL, +// OB_DYNAMIC = 1llu << OPT_DYNAMIC, +// OB_WRITE_TRACKS = 1llu << OPT_WRITE_TRACKS, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_DEST = OB_WRITE_TRACKS +// | OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_PATCH = OB_LOAD_BMG +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG +// | OB_PATCH_NAMES +// | OB_ORDER_BY +// | OB_ORDER_ALL, +// +// OB_GRP_TEXTOPT = OB_HEX +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_EXPORT, +// +// OB_GRP_TEXTOUT = OB_IGNORE +// | OB_LIST +// | OB_REF +// | OB_FULL +// | OB_GRP_TEXTOPT, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_SYMBOLS = OB_NO_HEADER, +// +// OB_CMD_FUNCTIONS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_CALCULATE = 0, +// +// OB_CMD_FLOAT = OB_ROUND, +// +// OB_CMD_EXPORT = 0, +// +// OB_CMD_RAWDUMP = OB_DEST +// | OB_DEST2, +// +// OB_CMD_DUMP = OB_LONG +// | OB_GRP_PATCH, +// +// OB_CMD_BMG = OB_BRIEF +// | OB_LONG +// | OB_GRP_TEXTOUT +// | OB_BMG_ENCODING +// | OB_BMG_INF_SIZE +// | OB_BMG_MID +// | OB_FORCE_ATTRIB +// | OB_DEF_ATTRIB +// | OB_NO_ATTRIB +// | OB_X_ESCAPES +// | OB_OLD_ESCAPES +// | OB_SINGLE_LINE +// | OB_NO_BMG_COLORS +// | OB_BMG_COLORS +// | OB_NO_BMG_INLINE +// | OB_WRITE_TRACKS +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_PATCH, +// +// OB_CMD_TRACKS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG +// | OB_RAW +// | OB_GRP_TEXTOUT +// | OB_WRITE_TRACKS +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_PATCH, +// +// OB_CMD_CAT = OB_GRP_TEXTOUT +// | OB_WRITE_TRACKS +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_PATCH, +// +// OB_CMD_DECODE = OB_GRP_DEST +// | OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_PATCH, +// +// OB_CMD_CREATE = OB_GRP_DEST +// | OB_IGNORE +// | OB_GRP_TEXTOPT +// | OB_BMG_ENCODING +// | OB_BMG_INF_SIZE +// | OB_BMG_MID +// | OB_FORCE_ATTRIB +// | OB_DEF_ATTRIB +// | OB_NO_ATTRIB +// | OB_X_ESCAPES +// | OB_OLD_ESCAPES +// | OB_SINGLE_LINE +// | OB_NO_BMG_COLORS +// | OB_BMG_COLORS +// | OB_NO_BMG_INLINE +// | OB_BRIEF +// | OB_LONG +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_IMAGES +// | OB_GRP_PATCH +// | OB_DYNAMIC, +// +// OB_CMD_PATCH = OB_GRP_DEST +// | OB_IGNORE +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_PATCH, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_SYMBOLS, + CMD_FUNCTIONS, + CMD_CALCULATE, + CMD_FLOAT, + CMD_EXPORT, + CMD_RAWDUMP, + + CMD_DUMP, + CMD_CAT, + CMD_BMG, + CMD_TRACKS, + CMD_DECODE, + CMD_CREATE, + CMD_PATCH, + + + CMD__N // == 22 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO_SINGLE_LINE = '1', + + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_NO_CHECK = 'N', + GO_NO_PARAM = 'P', + GO_TRACKS = 'T', + GO_VERSION = 'V', + GO_EXPORT = 'X', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_OLD_SPINY, + GO_CRS1, + GO_CHDIR, + GO_ROUND, + GO_RAW, + GO_BMG_ENCODING, + GO_BMG_INF_SIZE, + GO_BMG_MID, + GO_FORCE_ATTRIB, + GO_DEF_ATTRIB, + GO_NO_ATTRIB, + GO_X_ESCAPES, + GO_OLD_ESCAPES, + GO_NO_BMG_COLORS, + GO_BMG_COLORS, + GO_NO_BMG_INLINE, + GO_NO_ECHO, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_LIST, + GO_REF, + GO_FULL, + GO_HEX, + GO_CT_DIR, + GO_CT_LOG, + GO_ALLOW_SLOTS, + GO_IMAGES, + GO_LOAD_BMG, + GO_PATCH_BMG, + GO_MACRO_BMG, + GO_FILTER_BMG, + GO_PATCH_NAMES, + GO_ORDER_BY, + GO_ORDER_ALL, + GO_DYNAMIC, + GO_WRITE_TRACKS, + GO_NUMBER, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wctct; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WCTCT_H + diff --git a/project/src/ui/ui-wimgt.c b/project/src/ui/ui-wimgt.c new file mode 100644 index 0000000..0788b7f --- /dev/null +++ b/project/src/ui/ui-wimgt.c @@ -0,0 +1,1400 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wimgt.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Create smaller output." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_ALL, false, false, false, false, false, 'a', "all", + 0, + "Convert all files and not only the files with changed file or image" + " formats." + }, + + { OPT_MIPMAPS, false, false, false, false, false, 0, "mipmaps", + 0, + "If reading a source, scan for mipmaps and load them too. For PNG" + " files, files named NAME.mm#.EXT with #=1.. are searched. This is the" + " default. --mm is a short cut." + }, + + { OPT_NO_MIPMAPS, false, false, false, false, false, 0, "no-mipmaps", + 0, + "If reading a source, ignore mipmaps. This disables not the creation" + " of mipmaps (see -n-mipmaps). --no-mm is a short cut." + }, + + { OPT_N_MIPMAPS, false, false, false, false, false, 0, "n-mipmaps", + "num", + "Force the number of mipmaps. Values between 0 and 20 are allowed." + " Value OFF (default) disables this option. If enabled, options" + " --max-mipmaps and --mipmap-size are ignored. The creation of mipmaps" + " stops if the width or the height of the mipmap becomes 0. --n-mm is" + " a short cut." + }, + + { OPT_MAX_MIPMAPS, false, false, false, false, false, 0, "max-mipmaps", + "num", + "Control the automatic mipmap creation. Values between 0 and 20 are" + " allowed and the default is 4. The mipmap creation for an image" + " stops, if the set number is reached. This option is ignored, if" + " --n-mipmaps is enabled. --max-mm is a short cut." + }, + + { OPT_MIPMAP_SIZE, false, false, false, false, false, 0, "mipmap-size", + "num", + "Define the minimal width and minimal height of a generated mipmap." + " Any value >0 is allowed. The default is 8. This option is ignored," + " if --n-mipmaps is enabled. --mm-size is a short cut." + }, + + { OPT_FAST_MIPMAPS, false, false, false, false, false, 0, "fast-mipmaps", + 0, + "If resizing an image, use the old fast resize algorithm (default" + " until v1.64a) instead of the new smart one. Both algorithms differ" + " only, when creating a mipmap for images with odd width or odd" + " height. The old algorithm is faster (factor 2-4) than the default" + " algorithm, but the new algorithm creates better resized images. See" + " https://szs.wiimm.de/doc/img/resize for details. --fast-mm is a" + " short cut." + }, + + { OPT_CMPR_DEFAULT, false, false, false, false, false, 0, "cmpr-default", + "rgb1[,rgb2]", + "Define 2 colors for the case, that all 16 pixels of a CMPR block are" + " transparent. The default is to calculate an average color of all" + " transparent pixels. Before v2.04 white was used.\n" + " 2 RGB values (hex values with 6 digits each) are expected as" + " parameter. If optional RGB2 is missed, a copy of RGB1 is used. As" + " first step, both colors are converted to RGB565 colors C1 and C2 and" + " sorted, so that C1>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 54 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_LIST_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, geometry data of the image is printed. If set twice, advanced" + " geometry data is printed." + }; + +static const InfoOption_t option_cmd_ENCODE_ALL = + { OPT_ALL, false, false, false, false, false, 'a', "all", + 0, + "If source and destination file are the same, the file is only written" + " on modifications (transform, patch, ...). If --all is set, then the" + " file is always written." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_LIST, "LIST", "LS", 0 }, + { CMD_LIST_L, "LIST-L", "LL", 0 }, + { CMD_LIST_L, "LISTL", 0, 0 }, + { CMD_LIST_LL, "LIST-LL", "LLL", 0 }, + { CMD_LIST_LL, "LISTLL", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "ENC", 0 }, + { CMD_CONVERT, "CONVERT", "CV", 0 }, + { CMD_COPY, "COPY", "CP", 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHBM:T:A:td:D:E:orupiaP:x:"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "all", 0, 0, 'a' }, + { "mipmaps", 0, 0, GO_MIPMAPS }, + { "no-mipmaps", 0, 0, GO_NO_MIPMAPS }, + { "nomipmaps", 0, 0, GO_NO_MIPMAPS }, + { "no-mm", 0, 0, GO_NO_MIPMAPS }, + { "nomm", 0, 0, GO_NO_MIPMAPS }, + { "n-mipmaps", 1, 0, GO_N_MIPMAPS }, + { "nmipmaps", 1, 0, GO_N_MIPMAPS }, + { "n-mm", 1, 0, GO_N_MIPMAPS }, + { "nmm", 1, 0, GO_N_MIPMAPS }, + { "max-mipmaps", 1, 0, GO_MAX_MIPMAPS }, + { "maxmipmaps", 1, 0, GO_MAX_MIPMAPS }, + { "max-mm", 1, 0, GO_MAX_MIPMAPS }, + { "maxmm", 1, 0, GO_MAX_MIPMAPS }, + { "mipmap-size", 1, 0, GO_MIPMAP_SIZE }, + { "mipmapsize", 1, 0, GO_MIPMAP_SIZE }, + { "mm-size", 1, 0, GO_MIPMAP_SIZE }, + { "mmsize", 1, 0, GO_MIPMAP_SIZE }, + { "fast-mipmaps", 0, 0, GO_FAST_MIPMAPS }, + { "fastmipmaps", 0, 0, GO_FAST_MIPMAPS }, + { "fast-mm", 0, 0, GO_FAST_MIPMAPS }, + { "fastmm", 0, 0, GO_FAST_MIPMAPS }, + { "cmpr-default", 1, 0, GO_CMPR_DEFAULT }, + { "pre-convert", 0, 0, GO_PRE_CONVERT }, + { "preconvert", 0, 0, GO_PRE_CONVERT }, + { "patch", 1, 0, 'P' }, + { "transform", 1, 0, 'x' }, + { "strip", 0, 0, GO_STRIP }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x040 */ 0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e */ 0,0, + /* 0x050 P */ OPT_PATCH, + /* 0x051 */ 0,0,0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0,0,0,0, 0,0,0,0, 0,0, + /* 0x061 a */ OPT_ALL, + /* 0x062 */ 0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0, + /* 0x078 x */ OPT_TRANSFORM, + /* 0x079 */ 0,0,0,0, 0,0,0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_UTF_8, + /* 0x08b */ OPT_NO_UTF_8, + /* 0x08c */ OPT_FORCE, + /* 0x08d */ OPT_REPAIR_MAGICS, + /* 0x08e */ OPT_OLD, + /* 0x08f */ OPT_STD, + /* 0x090 */ OPT_NEW, + /* 0x091 */ OPT_EXTRACT, + /* 0x092 */ OPT_NUMBER, + /* 0x093 */ OPT_MIPMAPS, + /* 0x094 */ OPT_NO_MIPMAPS, + /* 0x095 */ OPT_N_MIPMAPS, + /* 0x096 */ OPT_MAX_MIPMAPS, + /* 0x097 */ OPT_MIPMAP_SIZE, + /* 0x098 */ OPT_FAST_MIPMAPS, + /* 0x099 */ OPT_CMPR_DEFAULT, + /* 0x09a */ OPT_PRE_CONVERT, + /* 0x09b */ OPT_STRIP, + /* 0x09c */ OPT_SECTIONS, + /* 0x09d */ 0,0,0, + /* 0x0a0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0b0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[26] = // cmd #1 +{ + 0,1,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 1 +}; + +static u8 option_allowed_cmd_HELP[26] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_ARGTEST[26] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_TEST[26] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_COLORS[26] = // cmd #5 +{ + 0,1,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_ERROR[26] = // cmd #6 +{ + 0,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 1 +}; + +static u8 option_allowed_cmd_FILETYPE[26] = // cmd #7 +{ + 0,1,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[26] = // cmd #8 +{ + 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_LIST[26] = // cmd #9 +{ + 0,1,1,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_LIST_L[26] = // cmd #10 +{ + 0,1,1,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_LIST_LL[26] = // cmd #11 +{ + 0,1,1,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_DECODE[26] = // cmd #12 +{ + 0,0,0,0,1, 1,1,1,1,1, 1,1,1,0,1, 1,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_ENCODE[26] = // cmd #13 +{ + 0,0,0,0,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_CONVERT[26] = // cmd #14 +{ + 0,0,0,0,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_COPY[26] = // cmd #15 +{ + 0,0,0,0,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_MAX_FILE_SIZE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST[] = +{ + &option_cmd_LIST_LONG, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST_L[] = +{ + &option_cmd_LIST_LONG, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST_LL[] = +{ + &option_cmd_LIST_LONG, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_PRE_CONVERT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PATCH, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + &option_cmd_ENCODE_ALL, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_PRE_CONVERT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PATCH, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CONVERT[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + &option_cmd_ENCODE_ALL, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_PRE_CONVERT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PATCH, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COPY[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + &option_cmd_ENCODE_ALL, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_PRE_CONVERT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PATCH, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wimgt", + 0, + "wimgt [option]... command [option|parameter|file]...", + "Wiimms Image Tool : Extract and convert graphic images. The file" + " formats TPL, TEX, BTI, BREFT and PNG are supported.", + 0, + 20, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wimgt VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wimgt HELP [--width=wd] OPT|OPTION ...\n" + "wimgt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wimgt HELP [--width=wd] command [-b] [ignored]...\n" + "wimgt HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wimgt ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wimgt TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 0, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wimgt COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wimgt ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wimgt FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wimgt FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_LIST, + false, + false, + true, + "LIST", + "LS", + "wimgt LIST [source]...", + "List images files and print file and image type.", + 0, + 3, + option_tab_cmd_LIST, + option_allowed_cmd_LIST + }, + + { CMD_LIST_L, + false, + false, + false, + "LIST-L", + "LL", + "wit LIST-L [source]...", + "List images files and print file and image type and geometry data." + " 'LIST-L' is a short cut for 'LIST --long'.", + 0, + 3, + option_tab_cmd_LIST_L, + option_allowed_cmd_LIST_L + }, + + { CMD_LIST_LL, + false, + false, + false, + "LIST-LL", + "LLL", + "wit LIST-LL [source]...", + "List images files and print file and image type and advanced geometry" + " data. 'LIST-LL' is a short cut for 'LIST --long --long'.", + 0, + 3, + option_tab_cmd_LIST_LL, + option_allowed_cmd_LIST_LL + }, + + { CMD_DECODE, + false, + false, + true, + "DECODE", + "DEC", + "wimgt DECODE [source]...", + "Decode (export) the entered image files into PNG files. The default" + " destination is '%P/%F.png'.", + 0, + 20, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_ENCODE, + false, + false, + false, + "ENCODE", + "ENC", + "wimgt ENCODE [source]...", + "Encode (import) the entered PNG files and convert them to to an" + " internal image format (TPL, TEX, BREFT). The default destination is" + " '%P/%N' (removed extension).", + 0, + 21, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + { CMD_CONVERT, + false, + false, + false, + "CONVERT", + "CV", + "wimgt CONVERT [source]...", + "Convert the entered image files into new file and image formats. The" + " old file is replaced by the new one. The difference to ENCODE is" + " only the default file name handling.", + 0, + 21, + option_tab_cmd_CONVERT, + option_allowed_cmd_CONVERT + }, + + { CMD_COPY, + false, + false, + false, + "COPY", + "CP", + "wimgt COPY source dest\n" + "wimgt COPY source... [-d|-D] dest", + "Copy and convert an image from 'source' to 'dest'. Multiple sources" + " can be used, if 'dest' is a directory or contains at least one" + " %-pattern.\n" + " This is an alternative for the ENCODE and DECODE commands. The" + " default destination file format is selected by scanning the file" + " extension of the destination. Option --transform may override this." + " Option --overwrite is set implicitly.", + 0, + 21, + option_tab_cmd_COPY, + option_allowed_cmd_COPY + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wimgt = +{ + "wimgt", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wimgt.h b/project/src/ui/ui-wimgt.h new file mode 100644 index 0000000..5cb93b9 --- /dev/null +++ b/project/src/ui/ui-wimgt.h @@ -0,0 +1,348 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WIMGT_H +#define SZS_UI_WIMGT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_ALL, + OPT_MIPMAPS, + OPT_NO_MIPMAPS, + OPT_N_MIPMAPS, + OPT_MAX_MIPMAPS, + OPT_MIPMAP_SIZE, + OPT_FAST_MIPMAPS, + OPT_CMPR_DEFAULT, + OPT_PRE_CONVERT, + OPT_PATCH, + OPT_TRANSFORM, + OPT_STRIP, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 26 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 54 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_ALL = 1llu << OPT_ALL, +// OB_MIPMAPS = 1llu << OPT_MIPMAPS, +// OB_NO_MIPMAPS = 1llu << OPT_NO_MIPMAPS, +// OB_N_MIPMAPS = 1llu << OPT_N_MIPMAPS, +// OB_MAX_MIPMAPS = 1llu << OPT_MAX_MIPMAPS, +// OB_MIPMAP_SIZE = 1llu << OPT_MIPMAP_SIZE, +// OB_FAST_MIPMAPS = 1llu << OPT_FAST_MIPMAPS, +// OB_CMPR_DEFAULT = 1llu << OPT_CMPR_DEFAULT, +// OB_PRE_CONVERT = 1llu << OPT_PRE_CONVERT, +// OB_PATCH = 1llu << OPT_PATCH, +// OB_TRANSFORM = 1llu << OPT_TRANSFORM, +// OB_STRIP = 1llu << OPT_STRIP, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_TRANSFORM = OB_N_MIPMAPS +// | OB_MAX_MIPMAPS +// | OB_MIPMAP_SIZE +// | OB_PRE_CONVERT +// | OB_PATCH +// | OB_MIPMAPS +// | OB_NO_MIPMAPS +// | OB_FAST_MIPMAPS +// | OB_CMPR_DEFAULT +// | OB_TRANSFORM +// | OB_STRIP, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_LIST = OB_LONG +// | OB_NO_HEADER +// | OB_IGNORE, +// +// OB_CMD_LIST_L = OB_CMD_LIST, +// +// OB_CMD_LIST_LL = OB_CMD_LIST_L, +// +// OB_CMD_DECODE = OB_GRP_DEST +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_ENCODE = OB_GRP_DEST +// | OB_IGNORE +// | OB_ALL +// | OB_GRP_TRANSFORM, +// +// OB_CMD_CONVERT = OB_CMD_ENCODE, +// +// OB_CMD_COPY = OB_CMD_ENCODE, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + + CMD_LIST, + CMD_LIST_L, + CMD_LIST_LL, + + CMD_DECODE, + CMD_ENCODE, + CMD_CONVERT, + CMD_COPY, + + + CMD__N // == 16 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_PATCH = 'P', + GO_TRACKS = 'T', + GO_VERSION = 'V', + + GO_ALL = 'a', + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + GO_TRANSFORM = 'x', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_MIPMAPS, + GO_NO_MIPMAPS, + GO_N_MIPMAPS, + GO_MAX_MIPMAPS, + GO_MIPMAP_SIZE, + GO_FAST_MIPMAPS, + GO_CMPR_DEFAULT, + GO_PRE_CONVERT, + GO_STRIP, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wimgt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WIMGT_H + diff --git a/project/src/ui/ui-wkclt.c b/project/src/ui/ui-wkclt.c new file mode 100644 index 0000000..6678074 --- /dev/null +++ b/project/src/ui/ui-wkclt.c @@ -0,0 +1,2482 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wkclt.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_SCALE, false, false, false, false, false, 0, "scale", + "vector[@origin]", + "Transform the data: Scale all coordinates and size values by 'vector'" + " relative to the point 'origin'. If the origin is not set, 0,0,0 is" + " used instead.\n" + " Negative values mirror the coordinates. Each parameter is either a" + " vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_SHIFT, false, false, false, false, false, 0, "shift", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --translate, but the addition is done before rotation. The parameter" + " is either a vector expression or a comma separated expression list" + " of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_XSS, false, false, false, false, false, 0, "xss", + "x1old,x1new,x2old,x2new", + "--xss (x-scale-shift) calculates the X values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_YSS, false, false, false, false, false, 0, "yss", + "y1old,y1new,y2old,y2new", + "--yss (y-scale-shift) calculates the Y values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ZSS, false, false, false, false, false, 0, "zss", + "z1old,z1new,z2old,z2new", + "--zss (z-scale-shift) calculates the Z values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ROT, false, false, false, false, false, 0, "rot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' (is a vector) around the 3 axes. All 3 axes goes" + " through the point 'origin'. If the origin is not set, 0,0,0 is used" + " instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y,z').\n" + " Option --rot is an alternative for --xrot, --yrot and --zrot to" + " define all 3 rotations in one step. Transformations are calculated" + " in the order SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_XROT, false, false, false, false, false, 0, "xrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('y,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_YROT, false, false, false, false, false, 0, "yrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead. This is a" + " horizontal counterclockwise rotation and the old option name --hrot" + " can also used.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_ZROT, false, false, false, false, false, 0, "zrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the z-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_TRANSLATE, false, false, false, false, false, 0, "translate", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --shift, but the addition is done after rotation. The parameter is" + " either a vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z'). --trans is a short cut.\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_NULL, false, false, false, false, false, 0, "null", + 0, + "Create a neutral transformation without affecting the coordinates." + " The only influence is, that the dependent values (like maximum) are" + " re-calculated as if a transformation has taken place." + }, + + { OPT_NEXT, false, false, false, false, false, 0, "next", + 0, + "Close the current transformation step with all scaling, shifting," + " rotation and translation options and open a new step with cleared" + " options. On transformation each step is logical done one by one. In" + " real, one total transformation matrix is calculated and used for" + " fast transformations.\n" + " If option --next is used, the ability for modifying the scale and" + " rotation vectors (not the positions) of different KMP sections is" + " nearly always lost. A warning is printed if this occurs." + }, + + { OPT_ASCALE, false, false, false, false, false, 0, "ascale", + "factor@dir", + "Transform the data and do an axis scale: Close the current" + " transformation step like --next and scale the data by 'factor' into" + " the direction given by the vector 'dir'. Store the resulting matrix" + " as single transformation step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_AROT, false, false, false, false, false, 0, "arot", + "degree[@pos1]@pos2", + "Transform the data and do an axis rotation: Close the current" + " transformation step like --next and rotate the data by 'degree'" + " around the axis defined by the 2 points. If 'pos1' is not set, use" + " 0,0,0 instead. Store the resulting matrix as single transformation" + " step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_TFORM_SCRIPT, false, false, false, false, false, 0, "tform-script", + "script", + "Load the script and execute it by the text parser.\n" + " Then for each coordinate (2D and 3D), call the macro TRANSFORM after" + " all other transformations. Call it without parameters and set" + " variable $I with a zero based index of all vertices, variable $D to" + " the dimension (2 or 3) and the variable $P to the vector of the" + " current vertex. If the macro returns a vector, it is the new" + " position.\n" + " For each file, macro BEGIN is called at the beginning and END after" + " processing all points, but only, if the macros are defined. For END," + " variable $I is set to the number of processed vertices in the" + " current file.\n" + " Before each macro call, the private and local variables are cleared" + " and only global parameters are permanent. The zero based index is" + " cleared for each new source. The parameters of the macro calls are" + " defined in the local name space." + }, + + { OPT_KCL_FLAG, false, false, false, false, false, 0, "kcl-flag", + "joblist", + "Modify the KCL flag of KCL files. A comma separated job list in the" + " format 'srclist=dest' is expected. 'srclist' is a plus sign" + " separated list with single values like , T and" + " / or ranges like :, T: and" + " :/. '' is a complete KCL flag in the range of" + " 0..0xffff. '' are only the base types in the range of 0..0x1f." + " If is present, it marks the relevant bits. For the T" + " variant, the mask is set to 0x1f.\n" + " All KCL flags of the source list are assigned to the new value" + " 'dest', but only the set bits of '' are modified. A later" + " definition overrides the previous one. See" + " https://szs.wiimm.de/opt/kcl-flag for details." + }, + + { OPT_KCL_SCRIPT, false, false, false, false, false, 0, "kcl-script", + "script", + "After reading a KCL source file and executing all transformations," + " the text file 'script' is executed by the internal text parser. The" + " script should use the parser functions tri$*() to read and modify" + " the triangle positions and flags, or to add or remove complete" + " triangles.\n" + " If this option is used multiple times, each script is executed in" + " the entered order. See https://szs.wiimm.de/opt/kcl-script for" + " details." + }, + + { OPT_FLAG_FILE, false, false, false, false, false, 0, "flag-file", + "filename", + "Define a flag file if scanning an OBJ file (KCL processing). The file" + " name may contain %-escapes. Set it to an empty string to disable" + " flag files at all.\n" + " The tool search the flag file in the following order until found:" + " File defined by --flag-file; OBJ file name with replaced extension" + " ('a.obj' becomes 'a.flag'); OBJ file name with appended extension" + " ('a.obj' becomes 'a.obj.flag'). See" + " https://szs.wiimm.de/opt/flag-file for details." + }, + + { OPT_SLOT, false, false, false, false, false, 0, "slot", + "keyword", + "Patch a track file to run at the specified slot. KMP and KCL may be" + " modified. BRRES files may be added to or removed from SZS. The main" + " keywords are DAISY, DESERT, SHERBET, SHYGUY, STANDARD and MOST. Slot" + " numbers (except '42' and '4.2') and different other track names are" + " also accepted. For some conversions, the auto-add library is needed." + " See https://szs.wiimm.de/opt/slot for details." + }, + + { OPT_ROUND, false, false, false, false, false, 0, "round", + 0, + "Print additional lines with rounded values (to 3 and 2 bytes for" + " floats and 7 and 6 bytes for doubles)." + }, + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Create smaller output." + }, + + { OPT_NO_PARAM, false, false, false, false, false, 'P', "no-param", + 0, + "Disable parameter support in decoded KMP text files." + }, + + { OPT_NO_ECHO, false, false, false, false, false, 0, "no-echo", + 0, + "Suppress output of @ECHO commands while scanning text files." + }, + + { OPT_NO_CHECK, false, false, false, false, false, 'N', "no-check", + 0, + "Do not make plausibility checks for KCL files." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 37 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "Enable LE-CODE and CT-CODE support. The optional parameter is" + " deprecated and ignored. See https://szs.wiimm.de/opt/lecode for" + " details." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_KCL, false, false, false, false, false, 0, "kcl", + "list", + "Set global options for KCL processing. Parameter 'list' is a comma" + " separated list of keywords. A minus sign before a keyword disables a" + " setting. Each occurrence of the option will only change entered" + " settings and all other settings are untouched.\n" + " Keyword DEFAULT resets the default settings and CLEAR disables all." + " Keywords SMALL, MEDIUM and CHARY select the default parameters for" + " the octree creation. The other allowed keywords are: FAST, NEW," + " CENTER, ROUND, NORMALS, MTL, WIIMM, TRIANGLES, OUT-SWAP, G, USEMTL," + " CLIP, IN-SWAP, AUTO, HEX4, HEX23, HEX, DROP-UNUSED, DROP-FIXED," + " DROP-INVALID, DROP, RM-FACEDOWN, RM-FACEUP, FIX-ALL, CONV-FACEUP," + " WEAK-WALLS, SORT, INPLACE, SILENT and LOG. See" + " https://szs.wiimm.de/opt/kcl for details." + }, + + { OPT_TRI_AREA, false, false, false, false, false, 0, "tri-area", + "EXPR", + "Define the minimal area size of KCL triangles. The intention is to" + " ignore triangles that are generally to small. EXPR is a floating" + " point number or expression. Triangles are invalidated if their area" + " size is smaller than EXPR. The test is executed after reading files," + " after transformations, and after calculating normals and lengths." + " Values between 0.01 and 4.0 are recommended. The careful value 1.0" + " is used as default. Value 0 disables this filter functionality." + }, + + { OPT_TRI_HEIGHT, false, false, false, false, false, 0, "tri-height", + "EXPR", + "Define the minimal height of KCL triangles. The intention is to" + " ignore deformed triangles (very slim, but long). EXPR is a floating" + " point number or expression. Triangles are invalidated if at least 1" + " of the 3 heights is smaller than EXPR. The test is executed after" + " reading files, after transformations, and after calculating normals" + " and lengths. Values between 0.01 and 2.0 are recommended. The" + " careful value 1.0 is used as default. Value 0 disables this filter" + " functionality.\n" + " --tri-ht is an alternative name for the option." + }, + + { OPT_XTRIDATA, true, true, false, false, false, 0, "xtridata", + "[=level]", + "Enable extended KCL triangles tests. The default level is 1." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_TRACKS, false, true, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, true, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_TINY, false, false, false, false, false, 0, "tiny", + "mode", + "Set tiny mode to a level between 0 and 7. See" + " https://szs.wiimm.de/opt/tiny for details." + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 70 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_grp_TEXTOUT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in KCL text files." + }; + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_TEST_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Print the matrices of transformations." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the description and print only function type" + " and syntax." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Usually only the function syntax is compared to the keywords. But if" + " --long is set, the descriptions are compared too." + }; + +static const InfoOption_t option_cmd_MATRIX_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "If set once, suppress vector dumps for transformation steps and" + " ignore option --long. If set twice, suppress vector dump for final" + " transformation too." + }; + +static const InfoOption_t option_cmd_MATRIX_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set once, print inverse matrix for the final transformation. If" + " set twice, print inverse matrix for all transformations." + }; + +static const InfoOption_t option_cmd_MATRIX_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Option --quiet is dedicated to automatic tests comparing the old and" + " new transformation algorithms. If set once, the tables are only" + " printed, if the results differ. If set twice, nothing is printed at" + " all and only the exit status will inform about the results." + }; + +static const InfoOption_t option_cmd_CFF_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Force usage of 'f(typ,variant)'. If set twice, force usage of" + " 'a(typ,w,x,y,z)'." + }; + +static const InfoOption_t option_cmd_CFF_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the comments with the alternative expression formats." + }; + +static const InfoOption_t option_cmd_FLAGS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of detailed flag list and print only the type" + " summary." + }; + +static const InfoOption_t option_cmd_DUMP_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the triangle list and print only the header" + " data and statistics." + }; + +static const InfoOption_t option_cmd_DUMP_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, append an octree dump to the output. If set twice, add a" + " triangle usage list behind the octree dump." + }; + +static const InfoOption_t option_cmd_TRIANGLES_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the 'normals' data." + }; + +static const InfoOption_t option_cmd_TRIANGLES_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "For each occurrence of option --long increase the precision of the" + " floating points numbers by adding 1 fractional digit, but only up to" + " additionally 5 digits." + }; + +static const InfoOption_t option_cmd_TRAVERSE_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print the 0-based triangle index instead of the triangle coordinates." + }; + +static const InfoOption_t option_cmd_TRAVERSE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, print a calculation log. If set twice, print a calculation" + " log for each octree traversing step." + }; + +static const InfoOption_t option_cmd_FALL_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the height (y coordinate) of the result." + }; + +static const InfoOption_t option_cmd_FALL_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, print a calculation log. If set twice, be more verbose." + }; + +static const InfoOption_t option_cmd_CHECK_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only warnings and suppress hints." + }; + +static const InfoOption_t option_cmd_CHECK_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Be verbose and print more statistics." + }; + +static const InfoOption_t option_cmd_CHECK_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet until the first warning or hint is found." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_SYMBOLS, "SYMBOLS", 0, 0 }, + { CMD_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { CMD_CALCULATE, "CALCULATE", 0, 0 }, + { CMD_MATRIX, "MATRIX", 0, 0 }, + { CMD_FLOAT, "FLOAT", 0, 0 }, + { CMD_EXPORT, "_EXPORT", 0, 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "ENC", 0 }, + { CMD_COPY, "COPY", "CP", 0 }, + { CMD_CFF, "CFF", 0, 0 }, + { CMD_FLAGS, "FLAGS", "F", 0 }, + { CMD_DUMP, "DUMP", "D", 0 }, + { CMD_DBRIEF, "DBRIEF", "DB", 0 }, + { CMD_LIST, "LIST", 0, 0 }, + { CMD_TRIANGLES, "TRIANGLES", 0, 0 }, + { CMD_TRAVERSE, "TRAVERSE", 0, 0 }, + { CMD_FALL, "FALL", 0, 0 }, + { CMD_CHECK, "CHECK", "CHK", 0 }, + { CMD_ANALYZE, "ANALYZE", 0, 0 }, + { CMD_BLOW, "BLOW", 0, 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHBPNM:T:A:td:D:E:orupi"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "scale", 1, 0, GO_SCALE }, + { "shift", 1, 0, GO_SHIFT }, + { "xss", 1, 0, GO_XSS }, + { "yss", 1, 0, GO_YSS }, + { "zss", 1, 0, GO_ZSS }, + { "rot", 1, 0, GO_ROT }, + { "xrot", 1, 0, GO_XROT }, + { "yrot", 1, 0, GO_YROT }, + { "hrot", 1, 0, GO_YROT }, + { "zrot", 1, 0, GO_ZROT }, + { "translate", 1, 0, GO_TRANSLATE }, + { "null", 0, 0, GO_NULL }, + { "next", 0, 0, GO_NEXT }, + { "ascale", 1, 0, GO_ASCALE }, + { "arot", 1, 0, GO_AROT }, + { "tform-script", 1, 0, GO_TFORM_SCRIPT }, + { "tformscript", 1, 0, GO_TFORM_SCRIPT }, + { "kcl", 1, 0, GO_KCL }, + { "kcl-flag", 1, 0, GO_KCL_FLAG }, + { "kclflag", 1, 0, GO_KCL_FLAG }, + { "kcl-script", 1, 0, GO_KCL_SCRIPT }, + { "kclscript", 1, 0, GO_KCL_SCRIPT }, + { "tri-area", 1, 0, GO_TRI_AREA }, + { "triarea", 1, 0, GO_TRI_AREA }, + { "tri-height", 1, 0, GO_TRI_HEIGHT }, + { "triheight", 1, 0, GO_TRI_HEIGHT }, + { "tri-ht", 1, 0, GO_TRI_HEIGHT }, + { "triht", 1, 0, GO_TRI_HEIGHT }, + { "kcl-min", 1, 0, GO_TRI_HEIGHT }, + { "kclmin", 1, 0, GO_TRI_HEIGHT }, + { "flag-file", 1, 0, GO_FLAG_FILE }, + { "flagfile", 1, 0, GO_FLAG_FILE }, + { "xtridata", 2, 0, GO_XTRIDATA }, + { "xtri", 2, 0, GO_XTRIDATA }, + { "slot", 1, 0, GO_SLOT }, + { "round", 0, 0, GO_ROUND }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "no-param", 0, 0, 'P' }, + { "noparam", 0, 0, 'P' }, + { "no-echo", 0, 0, GO_NO_ECHO }, + { "noecho", 0, 0, GO_NO_ECHO }, + { "no-check", 0, 0, 'N' }, + { "nocheck", 0, 0, 'N' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "tiny", 1, 0, GO_TINY }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x040 */ 0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e N */ OPT_NO_CHECK, + /* 0x04f */ 0, + /* 0x050 P */ OPT_NO_PARAM, + /* 0x051 */ 0,0,0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_SCALE, + /* 0x08b */ OPT_SHIFT, + /* 0x08c */ OPT_XSS, + /* 0x08d */ OPT_YSS, + /* 0x08e */ OPT_ZSS, + /* 0x08f */ OPT_ROT, + /* 0x090 */ OPT_XROT, + /* 0x091 */ OPT_YROT, + /* 0x092 */ OPT_ZROT, + /* 0x093 */ OPT_TRANSLATE, + /* 0x094 */ OPT_NULL, + /* 0x095 */ OPT_NEXT, + /* 0x096 */ OPT_ASCALE, + /* 0x097 */ OPT_AROT, + /* 0x098 */ OPT_TFORM_SCRIPT, + /* 0x099 */ OPT_KCL, + /* 0x09a */ OPT_KCL_FLAG, + /* 0x09b */ OPT_KCL_SCRIPT, + /* 0x09c */ OPT_TRI_AREA, + /* 0x09d */ OPT_TRI_HEIGHT, + /* 0x09e */ OPT_FLAG_FILE, + /* 0x09f */ OPT_XTRIDATA, + /* 0x0a0 */ OPT_SLOT, + /* 0x0a1 */ OPT_ROUND, + /* 0x0a2 */ OPT_NO_ECHO, + /* 0x0a3 */ OPT_UTF_8, + /* 0x0a4 */ OPT_NO_UTF_8, + /* 0x0a5 */ OPT_FORCE, + /* 0x0a6 */ OPT_REPAIR_MAGICS, + /* 0x0a7 */ OPT_TINY, + /* 0x0a8 */ OPT_OLD, + /* 0x0a9 */ OPT_STD, + /* 0x0aa */ OPT_NEW, + /* 0x0ab */ OPT_EXTRACT, + /* 0x0ac */ OPT_NUMBER, + /* 0x0ad */ OPT_SECTIONS, + /* 0x0ae */ 0,0, + /* 0x0b0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[37] = // cmd #1 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,0,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1 +}; + +static u8 option_allowed_cmd_HELP[37] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1 +}; + +static u8 option_allowed_cmd_ARGTEST[37] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1 +}; + +static u8 option_allowed_cmd_TEST[37] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1 +}; + +static u8 option_allowed_cmd_COLORS[37] = // cmd #5 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,0,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0 +}; + +static u8 option_allowed_cmd_ERROR[37] = // cmd #6 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,1,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1 +}; + +static u8 option_allowed_cmd_FILETYPE[37] = // cmd #7 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[37] = // cmd #8 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0 +}; + +static u8 option_allowed_cmd_SYMBOLS[37] = // cmd #9 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0 +}; + +static u8 option_allowed_cmd_FUNCTIONS[37] = // cmd #10 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,1,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0 +}; + +static u8 option_allowed_cmd_CALCULATE[37] = // cmd #11 +{ + 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 u8 option_allowed_cmd_MATRIX[37] = // cmd #12 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,1,0,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0 +}; + +static u8 option_allowed_cmd_FLOAT[37] = // cmd #13 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 1,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0 +}; + +static u8 option_allowed_cmd_EXPORT[37] = // cmd #14 +{ + 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 u8 option_allowed_cmd_CAT[37] = // cmd #15 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,0, 1,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_DECODE[37] = // cmd #16 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,0, 1,1,1,1,1, + 1,1,1,1,1, 1,0 +}; + +static u8 option_allowed_cmd_ENCODE[37] = // cmd #17 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,0, 1,1,1,1,1, + 1,1,1,1,1, 1,0 +}; + +static u8 option_allowed_cmd_COPY[37] = // cmd #18 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,0, 1,1,1,1,1, + 1,1,1,1,1, 1,0 +}; + +static u8 option_allowed_cmd_CFF[37] = // cmd #19 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 0,1,0,1,0, 1,1,1,1,1, + 1,1,1,1,1, 1,0 +}; + +static u8 option_allowed_cmd_FLAGS[37] = // cmd #20 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,1,1,0, 0,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_DUMP[37] = // cmd #21 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,1,0,1,0, 0,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_DBRIEF[37] = // cmd #22 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,1,0,1,0, 0,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_LIST[37] = // cmd #23 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,0, 0,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_TRIANGLES[37] = // cmd #24 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,1,0,1,0, 0,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_TRAVERSE[37] = // cmd #25 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,1,0,1,0, 0,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_FALL[37] = // cmd #26 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,1,0,1,0, 0,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_CHECK[37] = // cmd #27 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,0,1,0, 1,1,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_ANALYZE[37] = // cmd #28 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 1,0 +}; + +static u8 option_allowed_cmd_BLOW[37] = // cmd #29 +{ + 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, 1,0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_KCL, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_MAX_FILE_SIZE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + OptionInfo + OPT_TINY, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + &option_cmd_TEST_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SYMBOLS[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FUNCTIONS[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_FUNCTIONS_BRIEF, + &option_cmd_FUNCTIONS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CALCULATE[] = +{ + OptionInfo + OPT_CONST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MATRIX[] = +{ + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + OptionInfo + OPT_NONE, // separator + + &option_cmd_MATRIX_BRIEF, + &option_cmd_MATRIX_LONG, + &option_cmd_MATRIX_QUIET, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLOAT[] = +{ + OptionInfo + OPT_ROUND, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COPY[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CFF[] = +{ + &option_cmd_CFF_LONG, + &option_cmd_CFF_BRIEF, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_FLAG_FILE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLAGS[] = +{ + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + &option_cmd_FLAGS_BRIEF, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DUMP[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_CHECK, + &option_cmd_DUMP_BRIEF, + &option_cmd_DUMP_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DBRIEF[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_CHECK, + &option_cmd_DUMP_BRIEF, + &option_cmd_DUMP_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TRIANGLES[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_CHECK, + &option_cmd_TRIANGLES_BRIEF, + &option_cmd_TRIANGLES_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TRAVERSE[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_CHECK, + &option_cmd_TRAVERSE_BRIEF, + &option_cmd_TRAVERSE_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FALL[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_CHECK, + &option_cmd_FALL_BRIEF, + &option_cmd_FALL_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CHECK[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_CHECK_BRIEF, + &option_cmd_CHECK_LONG, + &option_cmd_CHECK_QUIET, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ANALYZE[] = +{ + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_FLAG_FILE, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_BLOW[] = +{ + OptionInfo + OPT_IGNORE, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wkclt", + 0, + "wkclt [option]... command [option|parameter|file]...", + "Wiimms KCL Tool : Analyze, modify, export and create KCL and OBJ" + " files.", + 0, + 24, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wkclt VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wkclt HELP [--width=wd] OPT|OPTION ...\n" + "wkclt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wkclt HELP [--width=wd] command [-b] [ignored]...\n" + "wkclt HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wkclt ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wkclt TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 1, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wkclt COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wkclt ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wkclt FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wkclt FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_SYMBOLS, + false, + false, + false, + "SYMBOLS", + 0, + "wkclt SYMBOLS", + "Print predefined symbols of text parser to standard output. The KCL" + " specific symbols are included.", + 0, + 1, + option_tab_cmd_SYMBOLS, + option_allowed_cmd_SYMBOLS + }, + + { CMD_FUNCTIONS, + false, + false, + false, + "FUNCTIONS", + 0, + "wkclt FUNCTIONS [key]...", + "Print a list with all parser functions including the KCL specific" + " functions. If one key ore more key entered, then only functions that" + " match at least one key are printed.", + 0, + 3, + option_tab_cmd_FUNCTIONS, + option_allowed_cmd_FUNCTIONS + }, + + { CMD_CALCULATE, + false, + false, + false, + "CALCULATE", + 0, + "wkclt CALCULATE expression...", + "Calculate each expression and print the result to standard output.", + 0, + 1, + option_tab_cmd_CALCULATE, + option_allowed_cmd_CALCULATE + }, + + { CMD_MATRIX, + false, + false, + false, + "MATRIX", + 0, + "wkclt MATRIX [patch-option]... [vector]...", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results.", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results. The transformations are done with 4" + " different methods to confirm the algorithms. Only the main results" + " and results, which differ, are printed.\n" + " Normally, the exit status is OK (null). If at least one old" + " transformation returns different results for the different" + " algorithms, then status DIFFER (not null) is returned.", + 26, + option_tab_cmd_MATRIX, + option_allowed_cmd_MATRIX + }, + + { CMD_FLOAT, + false, + false, + false, + "FLOAT", + 0, + "wkclt FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa. For each non empty parameter, a line with 4 bytes" + " hex and a float, 8 bytes hex and a double float, and the parameter" + " itself is printed. If a parameter begins with 'x' or '0x', it is a" + " hex string, otherwise it is a floating point number.\n" + " The special parameters \"+\" and \"-\" mean: Increment or decrement" + " the previous value by the smallest possible value. Use different" + " increments for floats and doubles.\n" + " All conversions are done using big endian.", + 0, + 1, + option_tab_cmd_FLOAT, + option_allowed_cmd_FLOAT + }, + + { CMD_EXPORT, + true, + false, + false, + "_EXPORT", + 0, + "wkclt _EXPORT key...", + "This hidden command exports some definitions to standard output. Use" + " keyword HELP to get a list of possible keywords.", + 0, + 0, + option_tab_cmd_EXPORT, + option_allowed_cmd_EXPORT + }, + + { CMD_CAT, + false, + false, + true, + "CAT", + 0, + "wkclt CAT source...", + "Read the entered KCL and OBJ files and print them as OBJ files to" + " standard output. The output of all source files is concatenated.\n" + " Automated dropping of bad KCL triangles is enabled if no other drop" + " option is specified by option --kcl.", + 0, + 26, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_DECODE, + false, + false, + false, + "DECODE", + "DEC", + "wkclt DECODE source...", + "Read the entered KCL and OBJ files and export them to OBJ files. The" + " default destination is '%P/%N.txt'.\n" + " Automated dropping of bad KCL triangles is enabled if no other drop" + " option is specified by option --kcl.", + 0, + 34, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_ENCODE, + false, + false, + false, + "ENCODE", + "ENC", + "wkclt ENCODE source...", + "Read the entered KCL and OBJ files and encode (compile) them to" + " binary KCL files. The default destination is '%P/%N.kcl'.\n" + " See https://szs.wiimm.de/doc/kcl for encoding details.", + 0, + 34, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + { CMD_COPY, + false, + false, + false, + "COPY", + "CP", + "wkclt COPY source dest\n" + "wkclt COPY source... [-d|-D] dest", + "Copy and convert a collision file from 'source' to 'dest'. Multiple" + " KCL and OBJ sources can be used, if 'dest' is a directory or" + " contains at least one %-pattern.\n" + " This is an alternative for the DECODE and ENCODE commands. The" + " default destination file format is selected by scanning the file" + " extension of the destination. Is it '.kcl', a binary KCL file is" + " created. Is it '.txt' or '.obj', an OBJ file is created. Otherwise" + " the output format is the same as the source.\n" + " Automated dropping of bad KCL triangles is enabled if no other drop" + " option is specified and the destination is an OBJ file.", + 0, + 34, + option_tab_cmd_COPY, + option_allowed_cmd_COPY + }, + + { CMD_CFF, + false, + false, + false, + "CFF", + 0, + "wkclt CFF source...", + "CFF is an abbreviation of 'create flag file' and will create a flag" + " file template for each entered source file. If the flag file already" + " exists, only missing group names are added at the end of file.", + 0, + 15, + option_tab_cmd_CFF, + option_allowed_cmd_CFF + }, + + { CMD_FLAGS, + false, + false, + false, + "FLAGS", + "F", + "wkclt FLAGS source...", + "Print a KCL type and flag usage statistic for each source to standard" + " output.", + 0, + 27, + option_tab_cmd_FLAGS, + option_allowed_cmd_FLAGS + }, + + { CMD_DUMP, + false, + false, + false, + "DUMP", + "D", + "wkclt DUMP source...", + "This debugging command prints an information dump about the internal" + " triangle data base and the octree in human and machine readable text" + " format. The output of all source files is concatenated and printed" + " to standard output.", + 0, + 27, + option_tab_cmd_DUMP, + option_allowed_cmd_DUMP + }, + + { CMD_DBRIEF, + false, + false, + false, + "DBRIEF", + "DB", + "wkclt DBRIEF source...", + "Command DBRIEF is a short cut for 'DUMP --brief'. The output of the" + " triangle list is suppressed and only the header data and statistics" + " are printed.", + 0, + 27, + option_tab_cmd_DBRIEF, + option_allowed_cmd_DBRIEF + }, + + { CMD_LIST, + false, + false, + false, + "LIST", + 0, + "wkclt LIST source...", + "This debugging command prints a list of all triangles with metrics in" + " human and machine readable text format. It is like DUMP, but reduced" + " to the list of triangles. The output of all source files is" + " concatenated and printed to standard output.", + 0, + 25, + option_tab_cmd_LIST, + option_allowed_cmd_LIST + }, + + { CMD_TRIANGLES, + false, + false, + false, + "TRIANGLES", + 0, + "wkclt TRIANGLES source...", + "This debugging commands prints one data line for each triangle. The" + " goal is to compare data. Therefor and to create smaller lines the" + " precision of the floating points numbers is reduced.\n" + " The print order is: flag used length | 3 points | 4 normals. For" + " each point and normal 3 coordinates are printed.", + 0, + 27, + option_tab_cmd_TRIANGLES, + option_allowed_cmd_TRIANGLES + }, + + { CMD_TRAVERSE, + false, + false, + false, + "TRAVERSE", + 0, + "wkclt TRAVERSE source point...", + "For each point, the octree of the source file is traversed to find" + " and print the related triangles. A point is either a comma separated" + " list with 3 parser expressions (one for each coordinate) or a vector" + " expression.", + 0, + 27, + option_tab_cmd_TRAVERSE, + option_allowed_cmd_TRAVERSE + }, + + { CMD_FALL, + false, + false, + false, + "FALL", + 0, + "wkclt FALL source point...", + "For each entered point, the lowest point is searched, that can be" + " reached without collision. The point is expanded to a cube with edge" + " length of 100.0 for the collision tests.", + 0, + 27, + option_tab_cmd_FALL, + option_allowed_cmd_FALL + }, + + { CMD_CHECK, + false, + false, + false, + "CHECK", + "CHK", + "wkclt CHECK source...", + "Read the entered KCL files (text or binary) and and make a" + " plausibility check. Hints and warnings are written to standard" + " output. The exit status is 'DIFFER' if at least one KMP warning" + " found.", + 0, + 8, + option_tab_cmd_CHECK, + option_allowed_cmd_CHECK + }, + + { CMD_ANALYZE, + true, + false, + false, + "ANALYZE", + 0, + "wkclt ANALYZE source...", + "Analyze some KCL data.", + 0, + 24, + option_tab_cmd_ANALYZE, + option_allowed_cmd_ANALYZE + }, + + { CMD_BLOW, + false, + false, + false, + "BLOW", + 0, + "wkclt BLOW source...", + "Try to calculate the used blow size (KCL_BLOW) of a KCL. Print one" + " line for each source file.", + 0, + 1, + option_tab_cmd_BLOW, + option_allowed_cmd_BLOW + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wkclt = +{ + "wkclt", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wkclt.h b/project/src/ui/ui-wkclt.h new file mode 100644 index 0000000..a55da8b --- /dev/null +++ b/project/src/ui/ui-wkclt.h @@ -0,0 +1,481 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WKCLT_H +#define SZS_UI_WKCLT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_SCALE, + OPT_SHIFT, + OPT_XSS, + OPT_YSS, + OPT_ZSS, + OPT_ROT, + OPT_XROT, + OPT_YROT, + OPT_ZROT, + OPT_TRANSLATE, + OPT_NULL, + OPT_NEXT, + OPT_ASCALE, + OPT_AROT, + OPT_TFORM_SCRIPT, + OPT_KCL_FLAG, + OPT_KCL_SCRIPT, + OPT_FLAG_FILE, + OPT_SLOT, + OPT_ROUND, + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_NO_PARAM, + OPT_NO_ECHO, + OPT_NO_CHECK, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 37 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_KCL, + OPT_TRI_AREA, + OPT_TRI_HEIGHT, + OPT_XTRIDATA, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_TINY, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 70 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_SCALE = 1llu << OPT_SCALE, +// OB_SHIFT = 1llu << OPT_SHIFT, +// OB_XSS = 1llu << OPT_XSS, +// OB_YSS = 1llu << OPT_YSS, +// OB_ZSS = 1llu << OPT_ZSS, +// OB_ROT = 1llu << OPT_ROT, +// OB_XROT = 1llu << OPT_XROT, +// OB_YROT = 1llu << OPT_YROT, +// OB_ZROT = 1llu << OPT_ZROT, +// OB_TRANSLATE = 1llu << OPT_TRANSLATE, +// OB_NULL = 1llu << OPT_NULL, +// OB_NEXT = 1llu << OPT_NEXT, +// OB_ASCALE = 1llu << OPT_ASCALE, +// OB_AROT = 1llu << OPT_AROT, +// OB_TFORM_SCRIPT = 1llu << OPT_TFORM_SCRIPT, +// OB_KCL_FLAG = 1llu << OPT_KCL_FLAG, +// OB_KCL_SCRIPT = 1llu << OPT_KCL_SCRIPT, +// OB_FLAG_FILE = 1llu << OPT_FLAG_FILE, +// OB_SLOT = 1llu << OPT_SLOT, +// OB_ROUND = 1llu << OPT_ROUND, +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_NO_PARAM = 1llu << OPT_NO_PARAM, +// OB_NO_ECHO = 1llu << OPT_NO_ECHO, +// OB_NO_CHECK = 1llu << OPT_NO_CHECK, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_TRANSFORM = OB_SCALE +// | OB_SHIFT +// | OB_XSS +// | OB_YSS +// | OB_ZSS +// | OB_ROT +// | OB_XROT +// | OB_YROT +// | OB_ZROT +// | OB_TRANSLATE +// | OB_NULL +// | OB_NEXT +// | OB_ASCALE +// | OB_AROT +// | OB_TFORM_SCRIPT +// | OB_KCL_FLAG +// | OB_KCL_SCRIPT +// | OB_FLAG_FILE +// | OB_SLOT, +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_TEXTOUT = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_SYMBOLS = OB_NO_HEADER, +// +// OB_CMD_FUNCTIONS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_CALCULATE = 0, +// +// OB_CMD_MATRIX = OB_GRP_TRANSFORM +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_FLOAT = OB_ROUND, +// +// OB_CMD_EXPORT = 0, +// +// OB_CMD_CAT = OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_DECODE = OB_GRP_DEST +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_ENCODE = OB_CMD_DECODE, +// +// OB_CMD_COPY = OB_CMD_ENCODE, +// +// OB_CMD_CFF = OB_LONG +// | OB_BRIEF +// | OB_GRP_DEST +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_FLAG_FILE, +// +// OB_CMD_FLAGS = OB_NO_HEADER +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_BRIEF +// | OB_GRP_TRANSFORM, +// +// OB_CMD_DUMP = OB_IGNORE +// | OB_NO_CHECK +// | OB_BRIEF +// | OB_LONG +// | OB_GRP_TRANSFORM, +// +// OB_CMD_DBRIEF = OB_CMD_DUMP, +// +// OB_CMD_LIST = OB_IGNORE +// | OB_NO_CHECK +// | OB_GRP_TRANSFORM, +// +// OB_CMD_TRIANGLES = OB_IGNORE +// | OB_NO_CHECK +// | OB_BRIEF +// | OB_LONG +// | OB_GRP_TRANSFORM, +// +// OB_CMD_TRAVERSE = OB_IGNORE +// | OB_NO_CHECK +// | OB_BRIEF +// | OB_LONG +// | OB_GRP_TRANSFORM, +// +// OB_CMD_FALL = OB_IGNORE +// | OB_NO_CHECK +// | OB_BRIEF +// | OB_LONG +// | OB_GRP_TRANSFORM, +// +// OB_CMD_CHECK = OB_BRIEF +// | OB_LONG +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE, +// +// OB_CMD_ANALYZE = OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_BLOW = OB_IGNORE, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_SYMBOLS, + CMD_FUNCTIONS, + CMD_CALCULATE, + CMD_MATRIX, + CMD_FLOAT, + CMD_EXPORT, + + CMD_CAT, + CMD_DECODE, + CMD_ENCODE, + CMD_COPY, + CMD_CFF, + CMD_FLAGS, + CMD_DUMP, + CMD_DBRIEF, + CMD_LIST, + CMD_TRIANGLES, + CMD_TRAVERSE, + CMD_FALL, + CMD_CHECK, + CMD_ANALYZE, + CMD_BLOW, + + + CMD__N // == 30 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_NO_CHECK = 'N', + GO_NO_PARAM = 'P', + GO_TRACKS = 'T', + GO_VERSION = 'V', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_SCALE, + GO_SHIFT, + GO_XSS, + GO_YSS, + GO_ZSS, + GO_ROT, + GO_XROT, + GO_YROT, + GO_ZROT, + GO_TRANSLATE, + GO_NULL, + GO_NEXT, + GO_ASCALE, + GO_AROT, + GO_TFORM_SCRIPT, + GO_KCL, + GO_KCL_FLAG, + GO_KCL_SCRIPT, + GO_TRI_AREA, + GO_TRI_HEIGHT, + GO_FLAG_FILE, + GO_XTRIDATA, + GO_SLOT, + GO_ROUND, + GO_NO_ECHO, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_TINY, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wkclt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WKCLT_H + diff --git a/project/src/ui/ui-wkmpt.c b/project/src/ui/ui-wkmpt.c new file mode 100644 index 0000000..1ce32eb --- /dev/null +++ b/project/src/ui/ui-wkmpt.c @@ -0,0 +1,2661 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wkmpt.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_SCALE, false, false, false, false, false, 0, "scale", + "vector[@origin]", + "Transform the data: Scale all coordinates and size values by 'vector'" + " relative to the point 'origin'. If the origin is not set, 0,0,0 is" + " used instead.\n" + " Negative values mirror the coordinates. Each parameter is either a" + " vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_SHIFT, false, false, false, false, false, 0, "shift", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --translate, but the addition is done before rotation. The parameter" + " is either a vector expression or a comma separated expression list" + " of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_XSS, false, false, false, false, false, 0, "xss", + "x1old,x1new,x2old,x2new", + "--xss (x-scale-shift) calculates the X values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_YSS, false, false, false, false, false, 0, "yss", + "y1old,y1new,y2old,y2new", + "--yss (y-scale-shift) calculates the Y values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ZSS, false, false, false, false, false, 0, "zss", + "z1old,z1new,z2old,z2new", + "--zss (z-scale-shift) calculates the Z values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ROT, false, false, false, false, false, 0, "rot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' (is a vector) around the 3 axes. All 3 axes goes" + " through the point 'origin'. If the origin is not set, 0,0,0 is used" + " instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y,z').\n" + " Option --rot is an alternative for --xrot, --yrot and --zrot to" + " define all 3 rotations in one step. Transformations are calculated" + " in the order SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_XROT, false, false, false, false, false, 0, "xrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('y,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_YROT, false, false, false, false, false, 0, "yrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead. This is a" + " horizontal counterclockwise rotation and the old option name --hrot" + " can also used.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_ZROT, false, false, false, false, false, 0, "zrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the z-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_YPOS, false, false, false, false, false, 0, "ypos", + "pos", + "This option defines an Y position for KMP:CKPT transformations. It" + " has only impact to X and Z rotations (Options --xrot and --zrot). If" + " not set, the mean y of the active rotation origins are used." + }, + + { OPT_TRANSLATE, false, false, false, false, false, 0, "translate", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --shift, but the addition is done after rotation. The parameter is" + " either a vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z'). --trans is a short cut.\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_NULL, false, false, false, false, false, 0, "null", + 0, + "Create a neutral transformation without affecting the coordinates." + " The only influence is, that the dependent values (like maximum) are" + " re-calculated as if a transformation has taken place." + }, + + { OPT_NEXT, false, false, false, false, false, 0, "next", + 0, + "Close the current transformation step with all scaling, shifting," + " rotation and translation options and open a new step with cleared" + " options. On transformation each step is logical done one by one. In" + " real, one total transformation matrix is calculated and used for" + " fast transformations.\n" + " If option --next is used, the ability for modifying the scale and" + " rotation vectors (not the positions) of different KMP sections is" + " nearly always lost. A warning is printed if this occurs." + }, + + { OPT_ASCALE, false, false, false, false, false, 0, "ascale", + "factor@dir", + "Transform the data and do an axis scale: Close the current" + " transformation step like --next and scale the data by 'factor' into" + " the direction given by the vector 'dir'. Store the resulting matrix" + " as single transformation step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_AROT, false, false, false, false, false, 0, "arot", + "degree[@pos1]@pos2", + "Transform the data and do an axis rotation: Close the current" + " transformation step like --next and rotate the data by 'degree'" + " around the axis defined by the 2 points. If 'pos1' is not set, use" + " 0,0,0 instead. Store the resulting matrix as single transformation" + " step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_TFORM_SCRIPT, false, false, false, false, false, 0, "tform-script", + "script", + "Load the script and execute it by the text parser.\n" + " Then for each coordinate (2D and 3D), call the macro TRANSFORM after" + " all other transformations. Call it without parameters and set" + " variable $I with a zero based index of all vertices, variable $D to" + " the dimension (2 or 3) and the variable $P to the vector of the" + " current vertex. If the macro returns a vector, it is the new" + " position.\n" + " For each file, macro BEGIN is called at the beginning and END after" + " processing all points, but only, if the macros are defined. For END," + " variable $I is set to the number of processed vertices in the" + " current file.\n" + " Before each macro call, the private and local variables are cleared" + " and only global parameters are permanent. The zero based index is" + " cleared for each new source. The parameters of the macro calls are" + " defined in the local name space." + }, + + { OPT_RM_GOBJ, false, false, false, false, false, 0, "rm-gobj", + "objlist", + "Remove objects from the KMP section GOBJ. Therefor a comma separated" + " object list with single values like 'a' or ranges like 'a:b' is" + " expected. 'a' and 'b' are numbers, object names (prefixed by 'o$')" + " or any other parser expressions." + }, + + { OPT_SLOT, false, false, false, false, false, 0, "slot", + "keyword", + "Patch a track file to run at the specified slot. KMP and KCL may be" + " modified. BRRES files may be added to or removed from SZS. The main" + " keywords are DAISY, DESERT, SHERBET, SHYGUY, STANDARD and MOST. Slot" + " numbers (except '42' and '4.2') and different other track names are" + " also accepted. For some conversions, the auto-add library is needed." + " See https://szs.wiimm.de/opt/slot for details." + }, + + { OPT_DRAW, false, false, false, false, false, 'w', "draw", + "list", + "The parameter is a comma separated list of keywords to select the" + " elements to draw. Allowed keywords are: CKPT, CJGPT, JGPT, KTPT," + " ENPT, ITPT, POTI, CNPT, ITEMBOXES, COINS, ROADOBJECTS, SOLIDOBJECTS," + " DECORATION, BLACK, WHITE, KCL, NONE and ALL (default); the classes" + " POINTS, OBJECTS and RESPAWN; the presence-flag selectors" + " [1-3]OFFLINE and [1-3]ONLINE; the special options DETAILED, DISPATCH" + " and WARNINGS. If a keyword is prefixed by a minus sign, the option" + " is disabled. Option KCL decides, if the reference KCL is included in" + " the output. If DETAILED is disabled, an output with less faces is" + " created. If DISPATCH is enabled, the settings of dispatch points are" + " visualized. If WARNINGS is enabled, some objects are drawn in red." + " See https://szs.wiimm.de/opt/draw for details." + }, + + { OPT_POS_MODE, false, false, false, false, false, 0, "pos-mode", + "list", + "Define a comma separated list of modes for the next --pos-file" + " options. Each element of the list begins with a keyword followed by" + " a column index and/or a parameter list: X, Y, Z," + " D, C, T, FA=, FB=, FC=," + " \n" + " See https://szs.wiimm.de/opt/pos-mode for details." + }, + + { OPT_POS_FILE, false, false, false, false, false, 0, "pos-file", + "file", + "Load a position file created by mkw-ana and print the positions into" + " the map. Define the modes first, because the last defined modes set" + " by --pos-mode are used!" + }, + + { OPT_PNG, false, false, false, false, false, 0, "png", + "pix_size ['a' align] ['x' x1[:x2]] ['y' y1[:y2]] ['t' types]", + "Draw a PNG with a top view of the track instead of an OBJ file." + " Transformations are done after the KMP 3D drawing, but before PNG" + " creation.\n" + " 'pix_size' is the number of world units (sqare) used for 1 PNG" + " pixel. 'align' is a factor as anti-aliasing pixel multiplicator (>1" + " is on). 'x' and 'y' defines the part of the world that is displayed." + " If not set, the check points (KMP/CKPT) are used to find a good" + " area. 't' is a bit field with 32 bits to enable the the KCL types." + " Each bit corresponds to one of the KCL types and only . The default" + " is to display all but not walls and solid fall (0x7fef0bff).\n" + " All numeric values can be any expression allowed by the parser." + }, + + { OPT_KCL_FLAG, false, false, false, false, false, 0, "kcl-flag", + "joblist", + "Modify the KCL flag of KCL files. A comma separated job list in the" + " format 'srclist=dest' is expected. 'srclist' is a plus sign" + " separated list with single values like , T and" + " / or ranges like :, T: and" + " :/. '' is a complete KCL flag in the range of" + " 0..0xffff. '' are only the base types in the range of 0..0x1f." + " If is present, it marks the relevant bits. For the T" + " variant, the mask is set to 0x1f.\n" + " All KCL flags of the source list are assigned to the new value" + " 'dest', but only the set bits of '' are modified. A later" + " definition overrides the previous one. See" + " https://szs.wiimm.de/opt/kcl-flag for details." + }, + + { OPT_KCL_SCRIPT, false, false, false, false, false, 0, "kcl-script", + "script", + "After reading a KCL source file and executing all transformations," + " the text file 'script' is executed by the internal text parser. The" + " script should use the parser functions tri$*() to read and modify" + " the triangle positions and flags, or to add or remove complete" + " triangles.\n" + " If this option is used multiple times, each script is executed in" + " the entered order. See https://szs.wiimm.de/opt/kcl-script for" + " details." + }, + + { OPT_FLAG_FILE, false, false, false, false, false, 0, "flag-file", + "filename", + "Define a flag file if scanning an OBJ file (KCL processing). The file" + " name may contain %-escapes. Set it to an empty string to disable" + " flag files at all.\n" + " The tool search the flag file in the following order until found:" + " File defined by --flag-file; OBJ file name with replaced extension" + " ('a.obj' becomes 'a.flag'); OBJ file name with appended extension" + " ('a.obj' becomes 'a.obj.flag'). See" + " https://szs.wiimm.de/opt/flag-file for details." + }, + + { OPT_REPAIR_XPF, false, false, false, false, false, 0, "repair-xpflags", + "file", + "Load a KMP file and use it to repair the settings of the extended" + " presence flags, if they are destroyed by another KMP tool. --repxpf" + " is a short cut for this option." + }, + + { OPT_GAMEMODES, false, false, false, false, false, 0, "gamemodes", + "list", + "Filter the list of combinations. The parameter is a comma separated" + " list of keywords: AUTO: Add a automatic mode without conditions" + " first. STANDARD (or STD): Add standard modes. 1STANDARD (or 1STD):" + " Add standard modes first. BALLOON: Add balloon battle modes. COIN:" + " Add coin runners modes. BATTLE: Short cut for 'BALLOON,COIN'. VERSUS" + " (or VS): Add versus modes. ITEMRAIN: Add itemrain modes. RACE: Short" + " cut for 'VERSUS,ITEMRAIN'. ALL (default): Short cut for" + " 'STANDARD,BATTLE,RACE'. NO-MODES: Exclude 'ALL' and create auto" + " modes only. ENGINE: Include engine classes (default). NO-ENGINE:" + " Exclude engine classes. RANDOM: Include random modes (default)." + " NO-RANDOM: Exclude random modes. OFFLINE: Filter offline modes." + " ONLINE: Filter online modes. NO-TT: Exclude time trial. TT: Force a" + " time trial scenario, even if it is a duplicate. FULL: Don't" + " eliminate duplicates from list (up to 1573 modes). NSORT: Sort the" + " list by name. ISORT: Sort the list by index. DEBUG: Print a debug" + " info about this option." + }, + + { OPT_ROUND, false, false, false, false, false, 0, "round", + 0, + "Print additional lines with rounded values (to 3 and 2 bytes for" + " floats and 7 and 6 bytes for doubles)." + }, + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress information lines in decoded KMP text files. This includes" + " syntax information (--no-header). If set twice, the output of" + " unneeded sections is also suppressed." + }, + + { OPT_EXPORT, false, false, false, false, false, 'X', "export", + 0, + "Enable the export mode and create small and machine readable text" + " files for easy post processing. The option works similar like -HBB" + " for KMP text files." + }, + + { OPT_NO_PARAM, false, false, false, false, false, 'P', "no-param", + 0, + "Disable parameter support in decoded KMP text files." + }, + + { OPT_EPSILON, false, false, false, false, false, 0, "epsilon", + "float", + "When comparing floating point numbers, 2 numbers are considered" + " identical if their difference is less than or equal to epsilon. The" + " default value is 0.01." + }, + + { OPT_DIFF, false, false, false, false, false, 0, "diff", + "list", + "List of KMP sections names that should be compared. To select a" + " section, use a combiantion of the following keywords: AREA, CAME," + " CNPT, CKPH, CKPT, ENPH, ENPT, GOBJ, ITPH, ITPT, JGPT, KTPT, MSPT," + " POTI, STGI, NONE, GOOD, DEFAULT of ALL (default). Additional" + " keywords are SORT (default) and NOSORT" + }, + + { OPT_GENERIC, false, false, false, false, false, 'G', "generic", + 0, + "Forget scanned entry names and use generic names for output." + }, + + { OPT_NO_ECHO, false, false, false, false, false, 0, "no-echo", + 0, + "Suppress output of @ECHO commands while scanning text files." + }, + + { OPT_NO_CHECK, false, false, false, false, false, 'N', "no-check", + 0, + "Do not make plausibility checks for KMP files." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 49 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "Enable LE-CODE and CT-CODE support. The optional parameter is" + " deprecated and ignored. See https://szs.wiimm.de/opt/lecode for" + " details." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_BATTLE, true, false, false, false, false, 0, "battle", + "[=mode]", + "MODE is either OFF, AUTO (default) or ON. Without value, ON is used.\n" + " If mode is ON, assume a battle arena and enable a spüecial battle" + " support. If mode is OFF, disable special battle arena support. If" + " mode is AUTO, analyse the KMP and estimate, if it is a battle" + " areana. Modes ON and OFF override KMP parameter BATTLE-MODE." + }, + + { OPT_EXPORT_FLAGS, true, false, false, false, false, 0, "export-flags", + "[=mode]", + "MODE is either OFF, AUTO (default) or ON. Without value, ON is used." + " --exflags is an alternative name for the option.\n" + " If mode is ON, this feature is forced: On creating a KMP text file," + " the option EXPORT-FLAGS is always set to 1. On creating a binary KMP" + " file, the flags are always exported as lowest byte of the scale" + " factors or x-rotation.\n" + " If mode is OFF, this feature is totally disabled. If mode is not" + " AUTO, KMP parameter EXPORT-FLAGS is overridden." + }, + + { OPT_ROUTE_OPTIONS, true, false, false, false, false, 0, "route-options", + "[=mode]", + "MODE is either OFF, AUTO (default) or ON. Without value, ON is used.\n" + " On mode ON, the new commands $PREV and $NEXT are printed for each" + " group of KMP sections ENPT and ITPT. On mode AUTO, the command is" + " only printed, if any option is set or if parameter SHOW-OPTIONS is" + " >0. On mode OFF, this feature is totally disabled to create KMP text" + " files that are compatible with versions <1.60a.\n" + " This option exists only for a transition period. Its behaviour will" + " be changed in the future. --ropt is a short cut." + }, + + { OPT_WIM0, true, false, false, false, false, 0, "wim0", + "[=mode]", + "MODE is either OFF, AUTO (default) or ON. Without value, ON is used.\n" + " If mode is ON, a KMP section called WIM0 is added on binary KMP" + " creation. The sections contains information for Wiimms tools.\n" + " If mode is OFF, this feature is totally disabled. If mode is not" + " AUTO, KMP parameter KMP-WIM0 is overridden." + }, + + { OPT_LOAD_KCL, false, false, false, false, false, 0, "load-kcl", + "file", + "Load a KCL or OBJ file for reference issues." + }, + + { OPT_KCL, false, false, false, false, false, 0, "kcl", + "list", + "Set global options for KCL processing. Parameter 'list' is a comma" + " separated list of keywords. A minus sign before a keyword disables a" + " setting. Each occurrence of the option will only change entered" + " settings and all other settings are untouched.\n" + " Keyword DEFAULT resets the default settings and CLEAR disables all." + " Keywords SMALL, MEDIUM and CHARY select the default parameters for" + " the octree creation. The other allowed keywords are: FAST, NEW," + " CENTER, ROUND, NORMALS, MTL, WIIMM, TRIANGLES, OUT-SWAP, G, USEMTL," + " CLIP, IN-SWAP, AUTO, HEX4, HEX23, HEX, DROP-UNUSED, DROP-FIXED," + " DROP-INVALID, DROP, RM-FACEDOWN, RM-FACEUP, FIX-ALL, CONV-FACEUP," + " WEAK-WALLS, SORT, INPLACE, SILENT and LOG. See" + " https://szs.wiimm.de/opt/kcl for details." + }, + + { OPT_TRI_AREA, false, false, false, false, false, 0, "tri-area", + "EXPR", + "Define the minimal area size of KCL triangles. The intention is to" + " ignore triangles that are generally to small. EXPR is a floating" + " point number or expression. Triangles are invalidated if their area" + " size is smaller than EXPR. The test is executed after reading files," + " after transformations, and after calculating normals and lengths." + " Values between 0.01 and 4.0 are recommended. The careful value 1.0" + " is used as default. Value 0 disables this filter functionality." + }, + + { OPT_TRI_HEIGHT, false, false, false, false, false, 0, "tri-height", + "EXPR", + "Define the minimal height of KCL triangles. The intention is to" + " ignore deformed triangles (very slim, but long). EXPR is a floating" + " point number or expression. Triangles are invalidated if at least 1" + " of the 3 heights is smaller than EXPR. The test is executed after" + " reading files, after transformations, and after calculating normals" + " and lengths. Values between 0.01 and 2.0 are recommended. The" + " careful value 1.0 is used as default. Value 0 disables this filter" + " functionality.\n" + " --tri-ht is an alternative name for the option." + }, + + { OPT_XTRIDATA, true, true, false, false, false, 0, "xtridata", + "[=level]", + "Enable extended KCL triangles tests. The default level is 1." + }, + + { OPT_KMP, false, false, false, false, false, 0, "kmp", + "list", + "Set global options for KMP processing. Parameter 'list' is a comma" + " separated list of keywords. A minus sign before a keyword disables a" + " setting. Each occurrence of the option will only change entered" + " settings and all other settings are untouched.\n" + " Keyword DEFAULT resets the default settings and CLEAR disables all." + " The other allowed keywords are: FORCE, NEW, RM-SPCITEM, LEFT, RIGHT," + " WIDE, NARROW, FIX-CKPH, FIX-ENPH, FIX-ITPH, FIX-PH, FIX-CKNEXT," + " FIX-CKJGPT, FIX-CK, FIX-ALL, MASK-PFLAGS, RM-LECODE, PURGE-GOBJ," + " FULL-DEFOBJ, DUMP-CLASS, DUMP-ONEWAY, DUMP-ALL, 1LAP ... 9LAPS," + " MAX-LAPS, RM-EMPTY, INPLACE, SILENT and LOG. See" + " https://szs.wiimm.de/opt/kmp for details." + }, + + { OPT_SPEED_MOD, false, false, false, false, false, 0, "speed-mod", + "factor", + "The speed modifier is a user extensions to change the basic speed of" + " all vehicles. The speed factor itself is stored into KMP:STGI" + " section of a track.\n" + " If --speed-mod is set to a value >0.0, then the last 2 bytes of" + " the STGI section are patched. The value 0.0 reset these 2 bytes and" + " deactivates the speed modifier." + }, + + { OPT_KTPT2, false, false, false, false, false, 0, "ktpt2", + "vector", + "Insert or replace a second KTPT. Use VECTOR as new position. VECTOR" + " is either X,Z or X,Y,Z or an expression. Set Y to 0 for an automatic" + " defined height.\n" + " If the first 4 characters of VECTOR are 'AUTO' (ignoring case)," + " then the position is calculated automatically and set to the" + " recommendation of command wkmpt KTPT.\n" + " The tool looks for the nearest lap counter (CKPT with mode 0) and" + " adjust the KTPT: The direction of the lap counter is copied and the" + " KTPT is moved to the lap counter line using the new direction.\n" + " LE-CODE uses the second KTPT to draw the finish line at another" + " place away from the start position." + }, + + { OPT_TFORM_KMP, false, false, false, false, false, 0, "tform-kmp", + "list", + "Select KMP sections and objects for a transformation. --tkmp is a" + " short cut. The option expects a comma separated list of keywords." + " Non ambiguous of keywords are allowed and minus signs within" + " keywords are optional (e.g. GOBJS). A minus sign before a keyword" + " disables transformation of the related section or object.\n" + " Sections are selected by the KMP section names AREA, CAME, CKPT," + " CNPT, ENPT, GOBJ, ITPT, JGPT, KTPT, MSPT and POTI.\n" + " Objects are selected by the keywords AREA-POSITION, AREA-ROTATE," + " AREA-SCALE, CAME-POSITION, CKPT-POSITION, CNPT-POSITION," + " CNPT-ROTATE, ENPT-POSITION, ENPT-SCALE, GOBJ-POSITION, GOBJ-ROTATE," + " GOBJ-SCALE, ITPT-POSITION, ITPT-SCALE, JGPT-POSITION, JGPT-ROTATE," + " KTPT-POSITION, KTPT-ROTATE, MSPT-POSITION, MSPT-ROTATE and" + " POTI-POSITION.\n" + " The keywords NONE and ALL (default) complete the field." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_TRACKS, false, true, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, true, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_TINY, false, false, false, false, false, 0, "tiny", + "mode", + "Set tiny mode to a level between 0 and 7. See" + " https://szs.wiimm.de/opt/tiny for details." + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 91 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_grp_TEXTOUT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in KMP text files." + }; + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_TEST_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Print the matrices of transformations." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the description and print only function type" + " and syntax." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Usually only the function syntax is compared to the keywords. But if" + " --long is set, the descriptions are compared too." + }; + +static const InfoOption_t option_cmd_MATRIX_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "If set once, suppress vector dumps for transformation steps and" + " ignore option --long. If set twice, suppress vector dump for final" + " transformation too." + }; + +static const InfoOption_t option_cmd_MATRIX_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set once, print inverse matrix for the final transformation. If" + " set twice, print inverse matrix for all transformations." + }; + +static const InfoOption_t option_cmd_MATRIX_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Option --quiet is dedicated to automatic tests comparing the old and" + " new transformation algorithms. If set once, the tables are only" + " printed, if the results differ. If set twice, nothing is printed at" + " all and only the exit status will inform about the results." + }; + +static const InfoOption_t option_cmd_OBJECTS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Don't print the file list and the info line(s). If set twice, only ID" + " and object name is printed and option --long is ignored." + }; + +static const InfoOption_t option_cmd_OBJECTS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print each setting starting on a new line. --long and --brief can be" + " used together." + }; + +static const InfoOption_t option_cmd_DIFF_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "If set once, only a summary status is printed. If set twice, the" + " status is only reported as exit status." + }; + +static const InfoOption_t option_cmd_CHECK_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only warnings and suppress hints." + }; + +static const InfoOption_t option_cmd_CHECK_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Be verbose and print more statistics." + }; + +static const InfoOption_t option_cmd_CHECK_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet until the first warning or hint is found." + }; + +static const InfoOption_t option_cmd_STGI_IGNORE = + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "If set, ignore non existing source files and files without KMP" + " without warning. If set twice, ignore files with standard settings" + " too." + }; + +static const InfoOption_t option_cmd_KTPT_IGNORE = + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "If set, ignore non existing source files and files without KMP" + " without warning." + }; + +static const InfoOption_t option_cmd_KTPT_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, compare each KTPT with each lap counter (some CKPT) and print" + " positions, directions, distances and recommendations as wide table." + }; + +static const InfoOption_t option_cmd_ROUTES_IGNORE = + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "If set, ignore non existing source files and files without KMP" + " without warning." + }; + +static const InfoOption_t option_cmd_GOBJ_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress head and foot lines." + }; + +static const InfoOption_t option_cmd_GOBJ_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only summary for each object category. If set twice, print a" + " status vector only." + }; + +static const InfoOption_t option_cmd_GOBJ_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "?" + }; + +static const InfoOption_t option_cmd_GAMEMODES_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress head and foot lines." + }; + +static const InfoOption_t option_cmd_GAMEMODES_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the scenario version and configuration name. If set twice," + " suppress ead and foot lines too." + }; + +static const InfoOption_t option_cmd_GAMEMODES_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, print a status vector for each GOBJ element too: '-': Object" + " diabled; '=': Object diabled by presence flags, but visible before" + " race; '+': Object enabled; 'e': Depends on engine; 'r': Depends on" + " random scenario; 'b': Defintion object of type BITS; 'o': Defintion" + " object of type OR; 'a': Defintion object of type AND." + }; + +static const InfoOption_t option_cmd_WIM0_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, disable table format and append a full hexdump for each WIM0" + " section." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_SYMBOLS, "SYMBOLS", 0, 0 }, + { CMD_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { CMD_CALCULATE, "CALCULATE", 0, 0 }, + { CMD_MATRIX, "MATRIX", 0, 0 }, + { CMD_FLOAT, "FLOAT", 0, 0 }, + { CMD_STARTPOS, "STARTPOS", "SP", 0 }, + { CMD_OBJECTS, "OBJECTS", "OBJ", 0 }, + { CMD_EXPORT, "_EXPORT", 0, 0 }, + { CMD_XEXPORT, "_XEXPORT", 0, 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "ENC", 0 }, + { CMD_DIFF, "DIFF", "CMP", 0 }, + { CMD_DRAW, "DRAW", "D", 0 }, + { CMD_DRAW, "COBJECT", "COB", 0 }, + { CMD_CHECK, "CHECK", "CHK", 0 }, + { CMD_STGI, "STGI", 0, 0 }, + { CMD_KTPT, "KTPT", 0, 0 }, + { CMD_ROUTES, "ROUTES", 0, 0 }, + { CMD_GOBJ, "GOBJ", 0, 0 }, + { CMD_GAMEMODES, "GAMEMODES", "GMODES", 0 }, + { CMD_WIM0, "WIM0", "W0", 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:w:lHBXPGNM:T:A:td:D:E:orupi"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "scale", 1, 0, GO_SCALE }, + { "shift", 1, 0, GO_SHIFT }, + { "xss", 1, 0, GO_XSS }, + { "yss", 1, 0, GO_YSS }, + { "zss", 1, 0, GO_ZSS }, + { "rot", 1, 0, GO_ROT }, + { "xrot", 1, 0, GO_XROT }, + { "yrot", 1, 0, GO_YROT }, + { "hrot", 1, 0, GO_YROT }, + { "zrot", 1, 0, GO_ZROT }, + { "ypos", 1, 0, GO_YPOS }, + { "translate", 1, 0, GO_TRANSLATE }, + { "null", 0, 0, GO_NULL }, + { "next", 0, 0, GO_NEXT }, + { "ascale", 1, 0, GO_ASCALE }, + { "arot", 1, 0, GO_AROT }, + { "tform-script", 1, 0, GO_TFORM_SCRIPT }, + { "tformscript", 1, 0, GO_TFORM_SCRIPT }, + { "rm-gobj", 1, 0, GO_RM_GOBJ }, + { "rmgobj", 1, 0, GO_RM_GOBJ }, + { "battle", 2, 0, GO_BATTLE }, + { "export-flags", 2, 0, GO_EXPORT_FLAGS }, + { "exportflags", 2, 0, GO_EXPORT_FLAGS }, + { "exflags", 2, 0, GO_EXPORT_FLAGS }, + { "route-options", 2, 0, GO_ROUTE_OPTIONS }, + { "routeoptions", 2, 0, GO_ROUTE_OPTIONS }, + { "ropt", 2, 0, GO_ROUTE_OPTIONS }, + { "wim0", 2, 0, GO_WIM0 }, + { "slot", 1, 0, GO_SLOT }, + { "draw", 1, 0, 'w' }, + { "pos-mode", 1, 0, GO_POS_MODE }, + { "posmode", 1, 0, GO_POS_MODE }, + { "pos-file", 1, 0, GO_POS_FILE }, + { "posfile", 1, 0, GO_POS_FILE }, + { "png", 1, 0, GO_PNG }, + { "load-kcl", 1, 0, GO_LOAD_KCL }, + { "loadkcl", 1, 0, GO_LOAD_KCL }, + { "kcl", 1, 0, GO_KCL }, + { "kcl-flag", 1, 0, GO_KCL_FLAG }, + { "kclflag", 1, 0, GO_KCL_FLAG }, + { "kcl-script", 1, 0, GO_KCL_SCRIPT }, + { "kclscript", 1, 0, GO_KCL_SCRIPT }, + { "tri-area", 1, 0, GO_TRI_AREA }, + { "triarea", 1, 0, GO_TRI_AREA }, + { "tri-height", 1, 0, GO_TRI_HEIGHT }, + { "triheight", 1, 0, GO_TRI_HEIGHT }, + { "tri-ht", 1, 0, GO_TRI_HEIGHT }, + { "triht", 1, 0, GO_TRI_HEIGHT }, + { "kcl-min", 1, 0, GO_TRI_HEIGHT }, + { "kclmin", 1, 0, GO_TRI_HEIGHT }, + { "flag-file", 1, 0, GO_FLAG_FILE }, + { "flagfile", 1, 0, GO_FLAG_FILE }, + { "xtridata", 2, 0, GO_XTRIDATA }, + { "xtri", 2, 0, GO_XTRIDATA }, + { "kmp", 1, 0, GO_KMP }, + { "speed-mod", 1, 0, GO_SPEED_MOD }, + { "speedmod", 1, 0, GO_SPEED_MOD }, + { "ktpt2", 1, 0, GO_KTPT2 }, + { "tform-kmp", 1, 0, GO_TFORM_KMP }, + { "tformkmp", 1, 0, GO_TFORM_KMP }, + { "tkmp", 1, 0, GO_TFORM_KMP }, + { "repair-xpflags", 1, 0, GO_REPAIR_XPF }, + { "repairxpflags", 1, 0, GO_REPAIR_XPF }, + { "repxpf", 1, 0, GO_REPAIR_XPF }, + { "gamemodes", 1, 0, GO_GAMEMODES }, + { "gmodes", 1, 0, GO_GAMEMODES }, + { "round", 0, 0, GO_ROUND }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "export", 0, 0, 'X' }, + { "no-param", 0, 0, 'P' }, + { "noparam", 0, 0, 'P' }, + { "epsilon", 1, 0, GO_EPSILON }, + { "diff", 1, 0, GO_DIFF }, + { "generic", 0, 0, 'G' }, + { "no-echo", 0, 0, GO_NO_ECHO }, + { "noecho", 0, 0, GO_NO_ECHO }, + { "no-check", 0, 0, 'N' }, + { "nocheck", 0, 0, 'N' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "tiny", 1, 0, GO_TINY }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x040 */ 0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0, + /* 0x047 G */ OPT_GENERIC, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e N */ OPT_NO_CHECK, + /* 0x04f */ 0, + /* 0x050 P */ OPT_NO_PARAM, + /* 0x051 */ 0,0,0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0, + /* 0x058 X */ OPT_EXPORT, + /* 0x059 */ 0,0,0,0, 0,0,0,0, 0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 w */ OPT_DRAW, + /* 0x078 */ 0,0,0,0, 0,0,0,0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_SCALE, + /* 0x08b */ OPT_SHIFT, + /* 0x08c */ OPT_XSS, + /* 0x08d */ OPT_YSS, + /* 0x08e */ OPT_ZSS, + /* 0x08f */ OPT_ROT, + /* 0x090 */ OPT_XROT, + /* 0x091 */ OPT_YROT, + /* 0x092 */ OPT_ZROT, + /* 0x093 */ OPT_YPOS, + /* 0x094 */ OPT_TRANSLATE, + /* 0x095 */ OPT_NULL, + /* 0x096 */ OPT_NEXT, + /* 0x097 */ OPT_ASCALE, + /* 0x098 */ OPT_AROT, + /* 0x099 */ OPT_TFORM_SCRIPT, + /* 0x09a */ OPT_RM_GOBJ, + /* 0x09b */ OPT_BATTLE, + /* 0x09c */ OPT_EXPORT_FLAGS, + /* 0x09d */ OPT_ROUTE_OPTIONS, + /* 0x09e */ OPT_WIM0, + /* 0x09f */ OPT_SLOT, + /* 0x0a0 */ OPT_POS_MODE, + /* 0x0a1 */ OPT_POS_FILE, + /* 0x0a2 */ OPT_PNG, + /* 0x0a3 */ OPT_LOAD_KCL, + /* 0x0a4 */ OPT_KCL, + /* 0x0a5 */ OPT_KCL_FLAG, + /* 0x0a6 */ OPT_KCL_SCRIPT, + /* 0x0a7 */ OPT_TRI_AREA, + /* 0x0a8 */ OPT_TRI_HEIGHT, + /* 0x0a9 */ OPT_FLAG_FILE, + /* 0x0aa */ OPT_XTRIDATA, + /* 0x0ab */ OPT_KMP, + /* 0x0ac */ OPT_SPEED_MOD, + /* 0x0ad */ OPT_KTPT2, + /* 0x0ae */ OPT_TFORM_KMP, + /* 0x0af */ OPT_REPAIR_XPF, + /* 0x0b0 */ OPT_GAMEMODES, + /* 0x0b1 */ OPT_ROUND, + /* 0x0b2 */ OPT_EPSILON, + /* 0x0b3 */ OPT_DIFF, + /* 0x0b4 */ OPT_NO_ECHO, + /* 0x0b5 */ OPT_UTF_8, + /* 0x0b6 */ OPT_NO_UTF_8, + /* 0x0b7 */ OPT_FORCE, + /* 0x0b8 */ OPT_REPAIR_MAGICS, + /* 0x0b9 */ OPT_TINY, + /* 0x0ba */ OPT_OLD, + /* 0x0bb */ OPT_STD, + /* 0x0bc */ OPT_NEW, + /* 0x0bd */ OPT_EXTRACT, + /* 0x0be */ OPT_NUMBER, + /* 0x0bf */ OPT_SECTIONS, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[49] = // cmd #1 +{ + 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,1, + 0,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1 +}; + +static u8 option_allowed_cmd_HELP[49] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1 +}; + +static u8 option_allowed_cmd_ARGTEST[49] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1 +}; + +static u8 option_allowed_cmd_TEST[49] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1 +}; + +static u8 option_allowed_cmd_COLORS[49] = // cmd #5 +{ + 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,1, + 0,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_ERROR[49] = // cmd #6 +{ + 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,1, + 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1 +}; + +static u8 option_allowed_cmd_FILETYPE[49] = // cmd #7 +{ + 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,1, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[49] = // cmd #8 +{ + 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, + 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_SYMBOLS[49] = // cmd #9 +{ + 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, + 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_FUNCTIONS[49] = // cmd #10 +{ + 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,1, + 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_CALCULATE[49] = // cmd #11 +{ + 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 u8 option_allowed_cmd_MATRIX[49] = // cmd #12 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,1,0,0,1, + 0,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_FLOAT[49] = // cmd #13 +{ + 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,1,0, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_STARTPOS[49] = // cmd #14 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,1,0,0,0, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_OBJECTS[49] = // cmd #15 +{ + 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,1, + 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_EXPORT[49] = // cmd #16 +{ + 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 u8 option_allowed_cmd_XEXPORT[49] = // cmd #17 +{ + 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 u8 option_allowed_cmd_CAT[49] = // cmd #18 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,1,0,0,0, + 1,1,1,1,0, 0,1,1,1,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_DECODE[49] = // cmd #19 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,1,0,0,0, + 1,1,1,1,0, 0,1,1,1,1, 1,1,1,1,1, 1,1,1,0 +}; + +static u8 option_allowed_cmd_ENCODE[49] = // cmd #20 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,1,0,0,0, + 0,0,0,0,0, 0,0,1,1,1, 1,1,1,1,1, 1,1,1,0 +}; + +static u8 option_allowed_cmd_DIFF[49] = // cmd #21 +{ + 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,1, 1,0,0,0,1, 0,1,0,0,0, 0,0,0,0 +}; + +static u8 option_allowed_cmd_DRAW[49] = // cmd #22 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,0,1, 0,1,0,0,0, + 1,1,1,1,0, 0,1,1,1,1, 1,1,1,1,1, 1,1,1,0 +}; + +static u8 option_allowed_cmd_CHECK[49] = // cmd #23 +{ + 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,1, + 0,1,0,0,0, 0,1,1,1,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_STGI[49] = // cmd #24 +{ + 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, + 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_KTPT[49] = // cmd #25 +{ + 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,1, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_ROUTES[49] = // cmd #26 +{ + 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, + 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_GOBJ[49] = // cmd #27 +{ + 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,1, + 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_GAMEMODES[49] = // cmd #28 +{ + 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,1,0,1, + 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0 +}; + +static u8 option_allowed_cmd_WIM0[49] = // cmd #29 +{ + 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,1, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_MAX_FILE_SIZE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + OptionInfo + OPT_TINY, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + &option_cmd_TEST_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SYMBOLS[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FUNCTIONS[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_FUNCTIONS_BRIEF, + &option_cmd_FUNCTIONS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CALCULATE[] = +{ + OptionInfo + OPT_CONST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MATRIX[] = +{ + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + + OptionInfo + OPT_NONE, // separator + + &option_cmd_MATRIX_BRIEF, + &option_cmd_MATRIX_LONG, + &option_cmd_MATRIX_QUIET, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLOAT[] = +{ + OptionInfo + OPT_ROUND, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_STARTPOS[] = +{ + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_OBJECTS[] = +{ + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_NO_HEADER, + &option_cmd_OBJECTS_BRIEF, + &option_cmd_OBJECTS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_XEXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_GENERIC, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_GENERIC, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DIFF[] = +{ + &option_cmd_DIFF_QUIET, + OptionInfo + OPT_EPSILON, + OptionInfo + OPT_DIFF, + OptionInfo + OPT_DEST, + OptionInfo + OPT_ESC, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DRAW[] = +{ + OptionInfo + OPT_DRAW, + OptionInfo + OPT_POS_MODE, + OptionInfo + OPT_POS_FILE, + OptionInfo + OPT_PNG, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_GENERIC, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL_SCRIPT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CHECK[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_CHECK_BRIEF, + &option_cmd_CHECK_LONG, + &option_cmd_CHECK_QUIET, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_GENERIC, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_STGI[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_STGI_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_KTPT[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_KTPT2, + &option_cmd_KTPT_IGNORE, + &option_cmd_KTPT_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ROUTES[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ROUTES_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_GOBJ[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_GOBJ_NO_HEADER, + &option_cmd_GOBJ_BRIEF, + &option_cmd_GOBJ_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_GAMEMODES[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_GAMEMODES, + &option_cmd_GAMEMODES_NO_HEADER, + &option_cmd_GAMEMODES_BRIEF, + &option_cmd_GAMEMODES_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_WIM0[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_WIM0_LONG, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wkmpt", + 0, + "wkmpt [option]... command [option|parameter|file]...", + "Wiimms KMP Tool : Analyze, modify, export and create (compile) KMP" + " files.", + 0, + 33, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wkmpt VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wkmpt HELP [--width=wd] OPT|OPTION ...\n" + "wkmpt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wkmpt HELP [--width=wd] command [-b] [ignored]...\n" + "wkmpt HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wkmpt ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wkmpt TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 1, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wkmpt COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wkmpt ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wkmpt FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wkmpt FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_SYMBOLS, + false, + false, + false, + "SYMBOLS", + 0, + "wkmpt SYMBOLS", + "Print predefined symbols of text parser to standard output. The KMP" + " specific symbols are included.", + 0, + 1, + option_tab_cmd_SYMBOLS, + option_allowed_cmd_SYMBOLS + }, + + { CMD_FUNCTIONS, + false, + false, + false, + "FUNCTIONS", + 0, + "wkmpt FUNCTIONS [key]...", + "Print a list with all parser functions including the KMP specific" + " functions. If one key ore more key entered, then only functions that" + " match at least one key are printed.", + 0, + 3, + option_tab_cmd_FUNCTIONS, + option_allowed_cmd_FUNCTIONS + }, + + { CMD_CALCULATE, + false, + false, + false, + "CALCULATE", + 0, + "wkmpt CALCULATE expression...", + "Calculate each expression and print the result to standard output.", + 0, + 1, + option_tab_cmd_CALCULATE, + option_allowed_cmd_CALCULATE + }, + + { CMD_MATRIX, + false, + false, + false, + "MATRIX", + 0, + "wkmpt MATRIX [patch-option]... [vector]...", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results.", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results. The transformations are done with 4" + " different methods to confirm the algorithms. Only the main results" + " and results, which differ, are printed.\n" + " Normally, the exit status is OK (null). If at least one old" + " transformation returns different results for the different" + " algorithms, then status DIFFER (not null) is returned.", + 32, + option_tab_cmd_MATRIX, + option_allowed_cmd_MATRIX + }, + + { CMD_FLOAT, + false, + false, + false, + "FLOAT", + 0, + "wkmpt FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa. For each non empty parameter, a line with 4 bytes" + " hex and a float, 8 bytes hex and a double float, and the parameter" + " itself is printed. If a parameter begins with 'x' or '0x', it is a" + " hex string, otherwise it is a floating point number.\n" + " The special parameters \"+\" and \"-\" mean: Increment or decrement" + " the previous value by the smallest possible value. Use different" + " increments for floats and doubles.\n" + " All conversions are done using big endian.", + 0, + 1, + option_tab_cmd_FLOAT, + option_allowed_cmd_FLOAT + }, + + { CMD_STARTPOS, + false, + false, + false, + "STARTPOS", + "SP", + "wkmpt STARTPOS [patch-option]... key_or_file...", + "Print an ASCII image or a table of the start positions. The parameter" + " list is a wild mix of filenames, keywords and numbers.\n" + " If a parameter contains a slash ('/' or '\\'), it is a filename of" + " a KMP or SZS file and a KMP is loaded. The KMP is used to print" + " absolute positions and to find the settings for LEFT/RIGHT and" + " WIDE/NARROW automatically.\n" + " A parameter may also be one of the following keywords to define a" + " setting: RESET, IMAGE, TABLE, RELATIVE, ABSOLUTE, LEFT, RIGHT, WIDE," + " NARROW, AUTO, NO-AUTO, DECIMAL, HEXADECIMAL, Z0, Z1, Z2, Z3. A table" + " or image is only printed on numbers 1 to 12.\n" + " See https://szs.wiimm.de/cmd/wkmpt/startpos for details.", + 0, + 29, + option_tab_cmd_STARTPOS, + option_allowed_cmd_STARTPOS + }, + + { CMD_OBJECTS, + false, + false, + false, + "OBJECTS", + "OBJ", + "wkmpt OBJECTS key...", + "Infos about all objects, that match any of the entered keys, are" + " printed. A key match, if the text is part of the object name, file" + " list or info (ignoring case). If a key is a number (decimal or hex)," + " the object with that id matches too. This command uses an internal" + " object and file database. An online query is also available:" + " https://szs.wiimm.de/cgi/mkw/object", + 0, + 4, + option_tab_cmd_OBJECTS, + option_allowed_cmd_OBJECTS + }, + + { CMD_EXPORT, + true, + false, + false, + "_EXPORT", + 0, + "wkmpt _EXPORT key...", + "This hidden command exports some definitions to standard output. Use" + " keyword HELP to get a list of possible keywords.", + 0, + 0, + option_tab_cmd_EXPORT, + option_allowed_cmd_EXPORT + }, + + { CMD_XEXPORT, + true, + false, + false, + "_XEXPORT", + 0, + "wkmpt _XEXPORT", + "Export some tables for LE-CODE.", + 0, + 0, + option_tab_cmd_XEXPORT, + option_allowed_cmd_XEXPORT + }, + + { CMD_CAT, + false, + false, + true, + "CAT", + 0, + "wkmpt CAT [source]...", + "Read the entered KMP files (text, binary or szs) and print them as" + " decoded text files to standard output. The output of all source" + " files is concatenated.", + 0, + 37, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_DECODE, + false, + false, + false, + "DECODE", + "DEC", + "wkmpt DECODE [source]...", + "Read the entered KMP files (text or binary) and decode them to text" + " files. The default destination is '%P/%N.txt'.", + 0, + 45, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_ENCODE, + false, + false, + false, + "ENCODE", + "ENC", + "wkmpt ENCODE source...", + "Read the entered KMP files (text or binary) and encode (compile) them" + " to binary files. The default destination is '%P/%N.kmp'.", + 0, + 40, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + { CMD_DIFF, + false, + false, + false, + "DIFF", + "CMP", + "wkmpt DIFF source1 source2\n" + "wkmpt DIFF source... --dest dest", + "Compare the KMP of two or more sources (KMP, KMP-text or SZS). Each" + " KMP will be converted into the internal format, and then fixed and" + " patched controlled by options. Only the known sections are compared." + " If option SORT (see --diff) is set, elements of section GOBJ are" + " sorted by object-id.\n" + " This command is in development. So for the section POTI only a" + " simple notice about the different data record is printed.\n" + " If option --dest is set, then each source file is compared with a" + " file in the destination.", + 0, + 5, + option_tab_cmd_DIFF, + option_allowed_cmd_DIFF + }, + + { CMD_DRAW, + false, + false, + false, + "DRAW", + "D", + "wkmpt DRAW source...", + "Read the entered KMP files (text or binary), of needed load the" + " related KCL file, draw KMP objects and create an OBJ file. The" + " default destination is '%P/%N.kmp.obj'.\n" + " Automated dropping of bad KCL triangles is enabled if no drop" + " option is specified by option --kcl.", + 0, + 50, + option_tab_cmd_DRAW, + option_allowed_cmd_DRAW + }, + + { CMD_CHECK, + false, + false, + false, + "CHECK", + "CHK", + "wkmpt CHECK source...", + "Read the entered KMP files (text or binary) and and make a" + " plausibility check. Hints and warnings are written to standard" + " output. The exit status is 'DIFFER' if at least one KMP warning" + " found.", + 0, + 9, + option_tab_cmd_CHECK, + option_allowed_cmd_CHECK + }, + + { CMD_STGI, + false, + false, + false, + "STGI", + 0, + "wkmpt STGI file...", + "Find the first KMP:STGI of each file and print an analysis of it as" + " single line statistic. Additional the number of lap counter check" + " points ('LC') is printed. Track and KMP (binary and text) files are" + " accepted as input.", + 0, + 4, + option_tab_cmd_STGI, + option_allowed_cmd_STGI + }, + + { CMD_KTPT, + false, + false, + false, + "KTPT", + 0, + "wkmpt KTPT [source]...", + "Analyse the start positions (KMP:KTPT) and lap counters (KMP:CKPT" + " with mode 0) and print a simple information list. Use enemy points" + " (KMP:ENPT) to find recommendations.\n" + " This command is dedicated to LE-CODE, which supports an alternative" + " position for the finish line at the minimap.", + 0, + 5, + option_tab_cmd_KTPT, + option_allowed_cmd_KTPT + }, + + { CMD_ROUTES, + false, + false, + false, + "ROUTES", + 0, + "wkmpt ROUTES file...", + "Print the internal cross references for routes of KMP sections CKPH," + " CKPT, ENPH, ENPT, ITPH and ITPT.", + 0, + 4, + option_tab_cmd_ROUTES, + option_allowed_cmd_ROUTES + }, + + { CMD_GOBJ, + true, + false, + false, + "GOBJ", + 0, + "wkmpt GOBJ [source]...", + "Analyze the objects of KMP section GOBJ and print a status line for" + " each object. ...", + 0, + 4, + option_tab_cmd_GOBJ, + option_allowed_cmd_GOBJ + }, + + { CMD_GAMEMODES, + false, + false, + false, + "GAMEMODES", + "GMODES", + "wkmpt GAMEMODES [source]...", + "Analyze the presence flags combinations with and without extension" + " support and print a list. The lists consists of index, scenario" + " version and scenario name. Scenario versions are only equal, if the" + " scenarios are duplicates. Duplicates happens only, if --gamemodes" + " option TT or FULL is set.", + 0, + 5, + option_tab_cmd_GAMEMODES, + option_allowed_cmd_GAMEMODES + }, + + { CMD_WIM0, + false, + false, + false, + "WIM0", + "W0", + "wkmpt WIM0 [source]...", + "Analyze the WIM0 section of each KMP file (text, binary or szs) and" + " print an info about it.", + 0, + 2, + option_tab_cmd_WIM0, + option_allowed_cmd_WIM0 + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wkmpt = +{ + "wkmpt", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wkmpt.h b/project/src/ui/ui-wkmpt.h new file mode 100644 index 0000000..34468b7 --- /dev/null +++ b/project/src/ui/ui-wkmpt.h @@ -0,0 +1,536 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WKMPT_H +#define SZS_UI_WKMPT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_SCALE, + OPT_SHIFT, + OPT_XSS, + OPT_YSS, + OPT_ZSS, + OPT_ROT, + OPT_XROT, + OPT_YROT, + OPT_ZROT, + OPT_YPOS, + OPT_TRANSLATE, + OPT_NULL, + OPT_NEXT, + OPT_ASCALE, + OPT_AROT, + OPT_TFORM_SCRIPT, + OPT_RM_GOBJ, + OPT_SLOT, + OPT_DRAW, + OPT_POS_MODE, + OPT_POS_FILE, + OPT_PNG, + OPT_KCL_FLAG, + OPT_KCL_SCRIPT, + OPT_FLAG_FILE, + OPT_REPAIR_XPF, + OPT_GAMEMODES, + OPT_ROUND, + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_EXPORT, + OPT_NO_PARAM, + OPT_EPSILON, + OPT_DIFF, + OPT_GENERIC, + OPT_NO_ECHO, + OPT_NO_CHECK, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 49 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_BATTLE, + OPT_EXPORT_FLAGS, + OPT_ROUTE_OPTIONS, + OPT_WIM0, + OPT_LOAD_KCL, + OPT_KCL, + OPT_TRI_AREA, + OPT_TRI_HEIGHT, + OPT_XTRIDATA, + OPT_KMP, + OPT_SPEED_MOD, + OPT_KTPT2, + OPT_TFORM_KMP, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_TINY, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 91 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_SCALE = 1llu << OPT_SCALE, +// OB_SHIFT = 1llu << OPT_SHIFT, +// OB_XSS = 1llu << OPT_XSS, +// OB_YSS = 1llu << OPT_YSS, +// OB_ZSS = 1llu << OPT_ZSS, +// OB_ROT = 1llu << OPT_ROT, +// OB_XROT = 1llu << OPT_XROT, +// OB_YROT = 1llu << OPT_YROT, +// OB_ZROT = 1llu << OPT_ZROT, +// OB_YPOS = 1llu << OPT_YPOS, +// OB_TRANSLATE = 1llu << OPT_TRANSLATE, +// OB_NULL = 1llu << OPT_NULL, +// OB_NEXT = 1llu << OPT_NEXT, +// OB_ASCALE = 1llu << OPT_ASCALE, +// OB_AROT = 1llu << OPT_AROT, +// OB_TFORM_SCRIPT = 1llu << OPT_TFORM_SCRIPT, +// OB_RM_GOBJ = 1llu << OPT_RM_GOBJ, +// OB_SLOT = 1llu << OPT_SLOT, +// OB_DRAW = 1llu << OPT_DRAW, +// OB_POS_MODE = 1llu << OPT_POS_MODE, +// OB_POS_FILE = 1llu << OPT_POS_FILE, +// OB_PNG = 1llu << OPT_PNG, +// OB_KCL_FLAG = 1llu << OPT_KCL_FLAG, +// OB_KCL_SCRIPT = 1llu << OPT_KCL_SCRIPT, +// OB_FLAG_FILE = 1llu << OPT_FLAG_FILE, +// OB_REPAIR_XPF = 1llu << OPT_REPAIR_XPF, +// OB_GAMEMODES = 1llu << OPT_GAMEMODES, +// OB_ROUND = 1llu << OPT_ROUND, +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_EXPORT = 1llu << OPT_EXPORT, +// OB_NO_PARAM = 1llu << OPT_NO_PARAM, +// OB_EPSILON = 1llu << OPT_EPSILON, +// OB_DIFF = 1llu << OPT_DIFF, +// OB_GENERIC = 1llu << OPT_GENERIC, +// OB_NO_ECHO = 1llu << OPT_NO_ECHO, +// OB_NO_CHECK = 1llu << OPT_NO_CHECK, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_TRANSFORM = OB_SCALE +// | OB_SHIFT +// | OB_XSS +// | OB_YSS +// | OB_ZSS +// | OB_ROT +// | OB_XROT +// | OB_YROT +// | OB_ZROT +// | OB_YPOS +// | OB_TRANSLATE +// | OB_NULL +// | OB_NEXT +// | OB_ASCALE +// | OB_AROT +// | OB_TFORM_SCRIPT +// | OB_REPAIR_XPF +// | OB_RM_GOBJ +// | OB_SLOT, +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_TEXTOUT = OB_NO_HEADER +// | OB_BRIEF +// | OB_EXPORT +// | OB_NO_PARAM +// | OB_GENERIC, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_SYMBOLS = OB_NO_HEADER, +// +// OB_CMD_FUNCTIONS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_CALCULATE = 0, +// +// OB_CMD_MATRIX = OB_GRP_TRANSFORM +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_FLOAT = OB_ROUND, +// +// OB_CMD_STARTPOS = OB_GRP_TRANSFORM, +// +// OB_CMD_OBJECTS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_EXPORT = 0, +// +// OB_CMD_XEXPORT = 0, +// +// OB_CMD_CAT = OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_DECODE = OB_GRP_DEST +// | OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_ENCODE = OB_GRP_DEST +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_DIFF = OB_EPSILON +// | OB_DIFF +// | OB_DEST +// | OB_ESC, +// +// OB_CMD_DRAW = OB_DRAW +// | OB_POS_MODE +// | OB_POS_FILE +// | OB_PNG +// | OB_CMD_DECODE +// | OB_KCL_SCRIPT, +// +// OB_CMD_CHECK = OB_BRIEF +// | OB_LONG +// | OB_NO_ECHO +// | OB_GENERIC +// | OB_NO_CHECK +// | OB_IGNORE, +// +// OB_CMD_STGI = OB_NO_HEADER +// | OB_IGNORE, +// +// OB_CMD_KTPT = OB_IGNORE +// | OB_LONG, +// +// OB_CMD_ROUTES = OB_NO_HEADER +// | OB_IGNORE, +// +// OB_CMD_GOBJ = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_GAMEMODES = OB_IGNORE +// | OB_GAMEMODES +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_WIM0 = OB_IGNORE +// | OB_LONG, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_SYMBOLS, + CMD_FUNCTIONS, + CMD_CALCULATE, + CMD_MATRIX, + CMD_FLOAT, + CMD_STARTPOS, + CMD_OBJECTS, + CMD_EXPORT, + CMD_XEXPORT, + + CMD_CAT, + CMD_DECODE, + CMD_ENCODE, + CMD_DIFF, + CMD_DRAW, + CMD_CHECK, + CMD_STGI, + CMD_KTPT, + CMD_ROUTES, + CMD_GOBJ, + CMD_GAMEMODES, + CMD_WIM0, + + + CMD__N // == 30 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_GENERIC = 'G', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_NO_CHECK = 'N', + GO_NO_PARAM = 'P', + GO_TRACKS = 'T', + GO_VERSION = 'V', + GO_EXPORT = 'X', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + GO_DRAW = 'w', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_SCALE, + GO_SHIFT, + GO_XSS, + GO_YSS, + GO_ZSS, + GO_ROT, + GO_XROT, + GO_YROT, + GO_ZROT, + GO_YPOS, + GO_TRANSLATE, + GO_NULL, + GO_NEXT, + GO_ASCALE, + GO_AROT, + GO_TFORM_SCRIPT, + GO_RM_GOBJ, + GO_BATTLE, + GO_EXPORT_FLAGS, + GO_ROUTE_OPTIONS, + GO_WIM0, + GO_SLOT, + GO_POS_MODE, + GO_POS_FILE, + GO_PNG, + GO_LOAD_KCL, + GO_KCL, + GO_KCL_FLAG, + GO_KCL_SCRIPT, + GO_TRI_AREA, + GO_TRI_HEIGHT, + GO_FLAG_FILE, + GO_XTRIDATA, + GO_KMP, + GO_SPEED_MOD, + GO_KTPT2, + GO_TFORM_KMP, + GO_REPAIR_XPF, + GO_GAMEMODES, + GO_ROUND, + GO_EPSILON, + GO_DIFF, + GO_NO_ECHO, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_TINY, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wkmpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WKMPT_H + diff --git a/project/src/ui/ui-wlect.c b/project/src/ui/ui-wlect.c new file mode 100644 index 0000000..e590351 --- /dev/null +++ b/project/src/ui/ui-wlect.c @@ -0,0 +1,1865 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wlect.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_ROUND, false, false, false, false, false, 0, "round", + 0, + "Print additional lines with rounded values (to 3 and 2 bytes for" + " floats and 7 and 6 bytes for doubles)." + }, + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress cross reference comments." + }, + + { OPT_EXPORT, false, false, false, false, false, 'X', "export", + 0, + "Enable the export mode and create small and machine readable text" + " files for easy post processing." + }, + + { OPT_NO_PARAM, false, false, false, false, false, 'P', "no-param", + 0, + "Disable parameter support in decoded KMP text files." + }, + + { OPT_NO_ECHO, false, false, false, false, false, 0, "no-echo", + 0, + "Suppress output of @ECHO commands while scanning text files." + }, + + { OPT_LT_CLEAR, false, false, false, false, true, 0, "lt-clear", + 0, + "Clear LEX section TEST and remove the section if empty. This option" + " is executed before all other --lt-* options." + }, + + { OPT_LT_ONLINE, false, false, false, false, false, 0, "lt-online", + "value", + "Set LEX/TEST parameter OFFLINE-ONLINE to this MODE. MODE is one of" + " AUTO (default), NEVER or ALWAYS. If needed, LEX section TEST is" + " created or removed if empty." + }, + + { OPT_LT_N_PLAYERS, false, false, false, false, false, 0, "lt-n-players", + "offline,online", + "Set LEX/TEST parameters N-OFFLINE and N-ONLINE to these integer" + " values. If needed, LEX section TEST is created or removed if empty." + }, + + { OPT_LE_DEFINE, false, false, false, false, true, 0, "le-define", + "file", + "Use this file as definition file to patch LE-CODE. The file format" + " can be LE-BIN or any supported CT-CODE file (BRRES, TEX0, CT-CODE or" + " CT-TEXT)." + }, + + { OPT_LPAR, false, false, false, false, false, 0, "lpar", + "file", + "Read a LPAR file to overwrite settings of LE-CODE section LPAR." + }, + + { OPT_ALIAS, false, false, false, false, false, 0, "alias", + "list", + "The parameter is a comma separated list of SLOT=ALIAS elements. SLOT" + " defines the slot, for which the ALIAS is defined. ALIAS itself is a" + " slot number to be executed, if SLOT is selected. Empty and invalid" + " elements are ignored. Multiple usage is possible: the lists are" + " concatenated." + }, + + { OPT_ENGINE, false, false, false, false, false, 0, "engine", + "list", + "Define probabilities for engine classes. LIST is a vector for" + " 100cc,150cc,mirror If 200cc is enabled by --200cc, then vector is" + " interpreted as 150cc,200cc,mirror. Any numbers are accepted. They" + " are normalized to get a total of 100 percent. Use 0,1,0 to force" + " 150cc only. Use 0,0,0 to reset to Nintendos VR based choice." + }, + + { OPT_200CC, true, false, false, false, false, 0, "200cc", + "[=mode]", + "Enable or disable 200cc support. Mode is one of OFF, AUTO (default)" + " or ON (default, if option is used without parameter). LE-CODE hasn't" + " implemented this feature yet!" + }, + + { OPT_PERFMON, true, false, false, false, false, 0, "perf-mon", + "[=mode]", + "Enable or disable the performance monitor. Mode is one of OFF, AUTO" + " (default, don't change setting), ON (enable it for Wii only;" + " default, if option is used without parameter) or FORCE (enable it" + " for Wii and for Dolphin)." + }, + + { OPT_CUSTOM_TT, true, false, false, false, false, 0, "custom-tt", + "[=mode]", + "Enable or disable time trial for custom tracks. Mode is one of OFF," + " AUTO (default) or ON (default, if option is used without parameter)." + }, + + { OPT_XPFLAGS, true, false, false, false, false, 0, "xpflags", + "[=mode]", + "Enable or disable support for Extended Presence Flags. Mode is one of" + " OFF, AUTO (default) or ON (default, if option is used without" + " parameter). Disable it only for tests!" + }, + + { OPT_TRACK_DIR, false, false, false, false, true, 0, "track-dir", + "directory", + "Define a destination directory, where track files are copied, moved" + " or linked to. Usually it is .../Race/Course/.\n" + " Files are searched in directories defined by --copy-tracks," + " --move-tracks, --move1-tracks and --link-tracks in definition order." + " Already existing files are removed before operation." + }, + + { OPT_COPY_TRACKS, false, false, false, false, false, 0, "copy-tracks", + "directory", + "Define a search directory for all included tracks. Files found in" + " this directory are copied to the directory specified by option" + " --track-dir." + }, + + { OPT_MOVE_TRACKS, false, false, false, false, false, 0, "move-tracks", + "directory", + "Define a search directory for all included tracks. Files found in" + " this directory are moved to the directory specified by option" + " --track-dir. If moving fails, the file is copied and the source" + " removed." + }, + + { OPT_MOVE1_TRACKS, false, false, false, false, false, 0, "move1-tracks", + "directory", + "Define a search directory for all included tracks. Files found in" + " this directory are moved to the directory specified by option" + " --track-dir, but only if a file has not more than one hard link." + " This guarantees an unique version of the file. If the file has more" + " hard links or moving fails, the file is copied and the source" + " removed." + }, + + { OPT_LINK_TRACKS, false, false, false, false, false, 0, "link-tracks", + "directory", + "Define a search directory for all included tracks. Files found in" + " this directory are linked to the directory specified by option" + " --track-dir. If linking fails, the file is copied." + }, + + { OPT_COMPLETE, false, false, false, false, true, 'C', "complete", + 0, + " Append missing sections to the LEX file." + }, + + { OPT_LOAD_BMG, false, false, false, false, true, 0, "load-bmg", + "file", + "Load immediately the file to fill an internal message pool. The" + " option can be used multiple times to fill the message pool by" + " different sources. A later message overrides the previous message.\n" + " The internal message pool is patched by all --patch-bmg options." + }, + + { OPT_PATCH_BMG, false, false, false, false, false, 0, "patch-bmg", + "mode[cond][=param]", + "This option specifies a BMG patch mode. Some of the modes need a" + " parameter or a file name of a BMG patch file (raw or text BMG), both" + " separated by an equal sign. Modes with required file names are" + " PRINT, REPLACE, INSERT, OVERWRITE, DELETE, MASK, EQUAL and NOTEQUAL." + " Modes with text parameter are FORMAT, REGEXP and RM-REGEXP. " + " Standalone modes are ID, ID-ALL, UNICODE, RM-ESCAPES, RM-CUPS," + " CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY, LE-FILL," + " X-COPY, X-FORCE-COPY and X-FILL. Unique abbreviations are allowed.\n" + " The optional condition COND is either '?MID' or '!MID'. In case of" + " '?MID', the patch is only applied if the message id MID already" + " exists. In case of '!MID', the patch is only applied if the message" + " id MID does not exists.\n" + " If this option is used multiple times, all patch files will be" + " processed in the entered order. Read" + " https://szs.wiimm.de/opt/patch-bmg for more details." + }, + + { OPT_MACRO_BMG, false, false, false, false, false, 0, "macro-bmg", + "file", + "Load a BMG file and add the messages to the macro library; already" + " existing entries are replaced. The macro library is accessed by" + " escape sequence \\m{MID} as fallback, if the active file has not" + " already defined the message MID by itself." + }, + + { OPT_PATCH_NAMES, false, false, false, false, false, 0, "patch-names", + 0, + "If set, the names of the CUP1 and CSR1 sections are patched by the" + " loaded BMG files directly after loading the source files." + }, + + { OPT_ORDER_BY, false, false, false, false, false, 0, "order-by", + "bmg", + "Load the BMG file and filter track names. Then before creating output" + " files, re-distribute the tracks of the additonal cups (9-) to the" + " same cups by the order of the read track names. Tracks without BMG" + " reference are placed at the end of all tracks. This option modifes" + " only the track order of the cups, but will not change any name." + }, + + { OPT_ORDER_ALL, false, false, false, false, false, 0, "order-all", + 0, + "Used together with option --order-by: Re-distribute the tracks of all" + " cups beginning with Mushroom Cup." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 41 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "This option is completely ignored by wlect. It only exists due to" + " compatibility with the other tools." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_LT_COND_BIT, false, false, false, false, false, 0, "lt-cond-bit", + "bitnum", + "Set LEX/TEST parameter COND-BIT to this integer value. Use -1 to" + " deactivate this COND-BIT. If needed, LEX section TEST are created or" + " removed if empty." + }, + + { OPT_LT_GAME_MODE, false, false, false, false, false, 0, "lt-game-mode", + "mode", + "Set LEX/TEST parameter GAME-MODE to this MODE. MODE is one of AUTO" + " (default), Balloon, COIN, VERSUS or ITEMRAIN. If needed, LEX section" + " TEST is created or removed if empty." + }, + + { OPT_LT_ENGINE, false, false, false, false, false, 0, "lt-engine", + "mode", + "Set LEX/TEST parameter ENGINE to this MODE. MODE is one of AUTO" + " (default), BATTLE, 50CC, 100CC, 150CC, 200CC, 150M or 200M. Only" + " track.szs files are modified. If needed, LEX section TEST is created" + " or removed if empty." + }, + + { OPT_LT_RANDOM, false, false, false, false, false, 0, "lt-random", + "index", + "Force a random scenario, if between 1 and 8. Value 0 enables auto" + " selection and -1 disables this option. If needed, LEX section TEST" + " is created or removed if empty." + }, + + { OPT_LEX_PURGE, false, false, false, false, false, 0, "lex-purge", + 0, + "Delete LEX sections without any impact. This option is executed after" + " all --lt-* options." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 72 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_grp_TEXTOPT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in LE text files." + }; + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the description and print only function type" + " and syntax." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Usually only the function syntax is compared to the keywords. But if" + " --long is set, the descriptions are compared too." + }; + +static const InfoOption_t option_cmd_DUMP_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "For LE-CODE binaries: If set, print cups with tracks or arenas. For" + " each track, property and music slot is printed in brackets. If set" + " twice, print a track list too.\n" + " For LEX binaries: If set, print a hex dump of max 16 bytes (1 line)" + " of the element data. If set twice, print a hex dump of max 64 bytes" + " (4 lines). If set 3 times ore more, print a complete hex dump." + }; + +static const InfoOption_t option_cmd_DUMP_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "For LE-CODE binaries: If set, print a track slot usage table." + }; + +static const InfoOption_t option_cmd_BIN_DIFF_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Suppress all messages and tell result as exit status only." + }; + +static const InfoOption_t option_cmd_BIN_DIFF_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be more verbose and tell, what tests are planned." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_SYMBOLS, "SYMBOLS", 0, 0 }, + { CMD_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { CMD_CALCULATE, "CALCULATE", 0, 0 }, + { CMD_FLOAT, "FLOAT", 0, 0 }, + { CMD_EXPORT, "_EXPORT", 0, 0 }, + { CMD_DUMP, "DUMP", "D", 0 }, + { CMD_DL, "DL", 0, 0 }, + { CMD_DLL, "DLL", 0, 0 }, + { CMD_BIN_DIFF, "BIN-DIFF", "BINDIFF", 0 }, + { CMD_BIN_DIFF, "BD", 0, 0 }, + { CMD_PATCH, "PATCH", "P", 0 }, + { CMD_LPAR, "LPAR", 0, 0 }, + { CMD_CREATE, "CREATE", "CR", 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "ENC", 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHBXPM:tCd:D:E:orupi"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "round", 0, 0, GO_ROUND }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "export", 0, 0, 'X' }, + { "no-param", 0, 0, 'P' }, + { "noparam", 0, 0, 'P' }, + { "no-echo", 0, 0, GO_NO_ECHO }, + { "noecho", 0, 0, GO_NO_ECHO }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "lt-clear", 0, 0, GO_LT_CLEAR }, + { "ltclear", 0, 0, GO_LT_CLEAR }, + { "lt-online", 1, 0, GO_LT_ONLINE }, + { "ltonline", 1, 0, GO_LT_ONLINE }, + { "lt-n-players", 1, 0, GO_LT_N_PLAYERS }, + { "lt-nplayers", 1, 0, GO_LT_N_PLAYERS }, + { "ltnplayers", 1, 0, GO_LT_N_PLAYERS }, + { "lt-cond-bit", 1, 0, GO_LT_COND_BIT }, + { "lt-condbit", 1, 0, GO_LT_COND_BIT }, + { "ltcondbit", 1, 0, GO_LT_COND_BIT }, + { "lt-game-mode", 1, 0, GO_LT_GAME_MODE }, + { "lt-gamemode", 1, 0, GO_LT_GAME_MODE }, + { "ltgamemode", 1, 0, GO_LT_GAME_MODE }, + { "lt-engine", 1, 0, GO_LT_ENGINE }, + { "ltengine", 1, 0, GO_LT_ENGINE }, + { "lt-random", 1, 0, GO_LT_RANDOM }, + { "ltrandom", 1, 0, GO_LT_RANDOM }, + { "lex-purge", 0, 0, GO_LEX_PURGE }, + { "lexpurge", 0, 0, GO_LEX_PURGE }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "le-define", 1, 0, GO_LE_DEFINE }, + { "ledefine", 1, 0, GO_LE_DEFINE }, + { "lpar", 1, 0, GO_LPAR }, + { "alias", 1, 0, GO_ALIAS }, + { "engine", 1, 0, GO_ENGINE }, + { "200cc", 2, 0, GO_200CC }, + { "perf-mon", 2, 0, GO_PERFMON }, + { "perfmon", 2, 0, GO_PERFMON }, + { "custom-tt", 2, 0, GO_CUSTOM_TT }, + { "customtt", 2, 0, GO_CUSTOM_TT }, + { "xpflags", 2, 0, GO_XPFLAGS }, + { "track-dir", 1, 0, GO_TRACK_DIR }, + { "trackdir", 1, 0, GO_TRACK_DIR }, + { "copy-tracks", 1, 0, GO_COPY_TRACKS }, + { "copytracks", 1, 0, GO_COPY_TRACKS }, + { "move-tracks", 1, 0, GO_MOVE_TRACKS }, + { "movetracks", 1, 0, GO_MOVE_TRACKS }, + { "move1-tracks", 1, 0, GO_MOVE1_TRACKS }, + { "move1tracks", 1, 0, GO_MOVE1_TRACKS }, + { "link-tracks", 1, 0, GO_LINK_TRACKS }, + { "linktracks", 1, 0, GO_LINK_TRACKS }, + { "complete", 0, 0, 'C' }, + { "load-bmg", 1, 0, GO_LOAD_BMG }, + { "loadbmg", 1, 0, GO_LOAD_BMG }, + { "patch-bmg", 1, 0, GO_PATCH_BMG }, + { "patchbmg", 1, 0, GO_PATCH_BMG }, + { "pb", 1, 0, GO_PATCH_BMG }, + { "macro-bmg", 1, 0, GO_MACRO_BMG }, + { "macrobmg", 1, 0, GO_MACRO_BMG }, + { "mb", 1, 0, GO_MACRO_BMG }, + { "patch-names", 0, 0, GO_PATCH_NAMES }, + { "patchnames", 0, 0, GO_PATCH_NAMES }, + { "order-by", 1, 0, GO_ORDER_BY }, + { "orderby", 1, 0, GO_ORDER_BY }, + { "order", 1, 0, GO_ORDER_BY }, + { "order-all", 0, 0, GO_ORDER_ALL }, + { "orderall", 0, 0, GO_ORDER_ALL }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x040 */ 0,0, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 C */ OPT_COMPLETE, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e */ 0,0, + /* 0x050 P */ OPT_NO_PARAM, + /* 0x051 */ 0,0,0,0, 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0, + /* 0x058 X */ OPT_EXPORT, + /* 0x059 */ 0,0,0,0, 0,0,0,0, 0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_ROUND, + /* 0x08b */ OPT_NO_ECHO, + /* 0x08c */ OPT_UTF_8, + /* 0x08d */ OPT_NO_UTF_8, + /* 0x08e */ OPT_LT_CLEAR, + /* 0x08f */ OPT_LT_ONLINE, + /* 0x090 */ OPT_LT_N_PLAYERS, + /* 0x091 */ OPT_LT_COND_BIT, + /* 0x092 */ OPT_LT_GAME_MODE, + /* 0x093 */ OPT_LT_ENGINE, + /* 0x094 */ OPT_LT_RANDOM, + /* 0x095 */ OPT_LEX_PURGE, + /* 0x096 */ OPT_FORCE, + /* 0x097 */ OPT_REPAIR_MAGICS, + /* 0x098 */ OPT_OLD, + /* 0x099 */ OPT_STD, + /* 0x09a */ OPT_NEW, + /* 0x09b */ OPT_EXTRACT, + /* 0x09c */ OPT_LE_DEFINE, + /* 0x09d */ OPT_LPAR, + /* 0x09e */ OPT_ALIAS, + /* 0x09f */ OPT_ENGINE, + /* 0x0a0 */ OPT_200CC, + /* 0x0a1 */ OPT_PERFMON, + /* 0x0a2 */ OPT_CUSTOM_TT, + /* 0x0a3 */ OPT_XPFLAGS, + /* 0x0a4 */ OPT_TRACK_DIR, + /* 0x0a5 */ OPT_COPY_TRACKS, + /* 0x0a6 */ OPT_MOVE_TRACKS, + /* 0x0a7 */ OPT_MOVE1_TRACKS, + /* 0x0a8 */ OPT_LINK_TRACKS, + /* 0x0a9 */ OPT_LOAD_BMG, + /* 0x0aa */ OPT_PATCH_BMG, + /* 0x0ab */ OPT_MACRO_BMG, + /* 0x0ac */ OPT_PATCH_NAMES, + /* 0x0ad */ OPT_ORDER_BY, + /* 0x0ae */ OPT_ORDER_ALL, + /* 0x0af */ OPT_NUMBER, + /* 0x0b0 */ OPT_SECTIONS, + /* 0x0b1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[41] = // cmd #1 +{ + 0,0,1,0,1, 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, 1 +}; + +static u8 option_allowed_cmd_HELP[41] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_ARGTEST[41] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_TEST[41] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1 +}; + +static u8 option_allowed_cmd_COLORS[41] = // cmd #5 +{ + 0,0,1,0,1, 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 u8 option_allowed_cmd_ERROR[41] = // cmd #6 +{ + 0,0,1,1,1, 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, 1 +}; + +static u8 option_allowed_cmd_FILETYPE[41] = // cmd #7 +{ + 0,0,1,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,1, 0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[41] = // cmd #8 +{ + 0,0,0,1,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 u8 option_allowed_cmd_SYMBOLS[41] = // cmd #9 +{ + 0,0,0,1,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 u8 option_allowed_cmd_FUNCTIONS[41] = // cmd #10 +{ + 0,0,1,1,1, 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 u8 option_allowed_cmd_CALCULATE[41] = // cmd #11 +{ + 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 u8 option_allowed_cmd_FLOAT[41] = // cmd #12 +{ + 0,1,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 u8 option_allowed_cmd_EXPORT[41] = // cmd #13 +{ + 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 u8 option_allowed_cmd_DUMP[41] = // cmd #14 +{ + 0,0,1,0,0, 0,0,0,0,0, 0,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_DL[41] = // cmd #15 +{ + 0,0,1,0,0, 0,0,0,0,0, 0,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_DLL[41] = // cmd #16 +{ + 0,0,1,0,0, 0,0,0,0,0, 0,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0, 0 +}; + +static u8 option_allowed_cmd_BIN_DIFF[41] = // cmd #17 +{ + 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 u8 option_allowed_cmd_PATCH[41] = // cmd #18 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, + 0,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_LPAR[41] = // cmd #19 +{ + 0,0,0,1,1, 1,0,0,0,0, 0,1,1,1,1, 1,1,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_CREATE[41] = // cmd #20 +{ + 0,0,0,1,1, 1,0,1,1,1, 1,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_CAT[41] = // cmd #21 +{ + 0,0,0,1,1, 1,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1, 0 +}; + +static u8 option_allowed_cmd_DECODE[41] = // cmd #22 +{ + 0,0,0,1,1, 1,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 0,0,0,0,0, + 0,1,1,1,1, 1,1,1,1,1, 0 +}; + +static u8 option_allowed_cmd_ENCODE[41] = // cmd #23 +{ + 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 0,0,0,0,0, + 0,1,1,1,1, 1,1,1,1,1, 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_MAX_FILE_SIZE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SYMBOLS[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FUNCTIONS[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_FUNCTIONS_BRIEF, + &option_cmd_FUNCTIONS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CALCULATE[] = +{ + OptionInfo + OPT_CONST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLOAT[] = +{ + OptionInfo + OPT_ROUND, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DUMP[] = +{ + OptionInfo + OPT_LE_DEFINE, + OptionInfo + OPT_LPAR, + OptionInfo + OPT_ALIAS, + OptionInfo + OPT_ENGINE, + OptionInfo + OPT_200CC, + OptionInfo + OPT_PERFMON, + OptionInfo + OPT_CUSTOM_TT, + OptionInfo + OPT_XPFLAGS, + + OptionInfo + OPT_NONE, // separator + + &option_cmd_DUMP_LONG, + &option_cmd_DUMP_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DL[] = +{ + OptionInfo + OPT_LE_DEFINE, + OptionInfo + OPT_LPAR, + OptionInfo + OPT_ALIAS, + OptionInfo + OPT_ENGINE, + OptionInfo + OPT_200CC, + OptionInfo + OPT_PERFMON, + OptionInfo + OPT_CUSTOM_TT, + OptionInfo + OPT_XPFLAGS, + + OptionInfo + OPT_NONE, // separator + + &option_cmd_DUMP_LONG, + &option_cmd_DUMP_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DLL[] = +{ + OptionInfo + OPT_LE_DEFINE, + OptionInfo + OPT_LPAR, + OptionInfo + OPT_ALIAS, + OptionInfo + OPT_ENGINE, + OptionInfo + OPT_200CC, + OptionInfo + OPT_PERFMON, + OptionInfo + OPT_CUSTOM_TT, + OptionInfo + OPT_XPFLAGS, + + OptionInfo + OPT_NONE, // separator + + &option_cmd_DUMP_LONG, + &option_cmd_DUMP_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_BIN_DIFF[] = +{ + &option_cmd_BIN_DIFF_QUIET, + &option_cmd_BIN_DIFF_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_PATCH[] = +{ + OptionInfo + OPT_LE_DEFINE, + OptionInfo + OPT_LPAR, + OptionInfo + OPT_ALIAS, + OptionInfo + OPT_ENGINE, + OptionInfo + OPT_200CC, + OptionInfo + OPT_PERFMON, + OptionInfo + OPT_CUSTOM_TT, + OptionInfo + OPT_XPFLAGS, + OptionInfo + OPT_TRACK_DIR, + OptionInfo + OPT_COPY_TRACKS, + OptionInfo + OPT_MOVE_TRACKS, + OptionInfo + OPT_MOVE1_TRACKS, + OptionInfo + OPT_LINK_TRACKS, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LPAR[] = +{ + OptionInfo + OPT_LE_DEFINE, + OptionInfo + OPT_LPAR, + OptionInfo + OPT_ALIAS, + OptionInfo + OPT_ENGINE, + OptionInfo + OPT_200CC, + OptionInfo + OPT_PERFMON, + OptionInfo + OPT_CUSTOM_TT, + OptionInfo + OPT_XPFLAGS, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CREATE[] = +{ + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_LPAR, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + OptionInfo + OPT_COMPLETE, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_ECHO, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_COMPLETE, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_ECHO, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_COMPLETE, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_IGNORE, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wlect", + 0, + "wlect [option]... command [option|parameter|file]...", + "Wiimms LE-CODE Tool : Manage the LE-CODE and LEX extensions.", + 0, + 25, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wlect VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wlect HELP [--width=wd] OPT|OPTION ...\n" + "wlect HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wlect HELP [--width=wd] command [-b] [ignored]...\n" + "wlect HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wlect ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wlect TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 0, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wlect COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wlect ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wlect FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wlect FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_SYMBOLS, + false, + false, + false, + "SYMBOLS", + 0, + "wlect SYMBOLS", + "Print predefined symbols of text parser to standard output. The" + " LE-CODE specific symbols are included.", + 0, + 1, + option_tab_cmd_SYMBOLS, + option_allowed_cmd_SYMBOLS + }, + + { CMD_FUNCTIONS, + false, + false, + false, + "FUNCTIONS", + 0, + "wlect FUNCTIONS [key]...", + "Print a list with all parser functions including the LE-CODE specific" + " functions. If one key ore more key entered, then only functions that" + " match at least one key are printed.", + 0, + 3, + option_tab_cmd_FUNCTIONS, + option_allowed_cmd_FUNCTIONS + }, + + { CMD_CALCULATE, + false, + false, + false, + "CALCULATE", + 0, + "wlect CALCULATE expression...", + "Calculate each expression and print the result to standard output.", + 0, + 1, + option_tab_cmd_CALCULATE, + option_allowed_cmd_CALCULATE + }, + + { CMD_FLOAT, + false, + false, + false, + "FLOAT", + 0, + "wlect FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa. For each non empty parameter, a line with 4 bytes" + " hex and a float, 8 bytes hex and a double float, and the parameter" + " itself is printed. If a parameter begins with 'x' or '0x', it is a" + " hex string, otherwise it is a floating point number.\n" + " The special parameters \"+\" and \"-\" mean: Increment or decrement" + " the previous value by the smallest possible value. Use different" + " increments for floats and doubles.\n" + " All conversions are done using big endian.", + 0, + 1, + option_tab_cmd_FLOAT, + option_allowed_cmd_FLOAT + }, + + { CMD_EXPORT, + true, + false, + false, + "_EXPORT", + 0, + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output. Use" + " keyword HELP to get a list of possible keywords.", + 0, + 0, + option_tab_cmd_EXPORT, + option_allowed_cmd_EXPORT + }, + + { CMD_DUMP, + false, + false, + true, + "DUMP", + "D", + "wlect DUMP [source]...", + "Print an information dump for each source file. LE-CODE binaries and" + " LEX binaries are accepted as input.", + 0, + 10, + option_tab_cmd_DUMP, + option_allowed_cmd_DUMP + }, + + { CMD_DL, + false, + false, + false, + "DL", + 0, + "wlect DL [source]...", + "Short cut for 'DUMP --long'.", + 0, + 10, + option_tab_cmd_DL, + option_allowed_cmd_DL + }, + + { CMD_DLL, + false, + false, + false, + "DLL", + 0, + "wlect DLL [source]...", + "Short cut for 'DUMP --long --long'.", + 0, + 10, + option_tab_cmd_DLL, + option_allowed_cmd_DLL + }, + + { CMD_BIN_DIFF, + false, + false, + false, + "BIN-DIFF", + "BINDIFF", + "wlect BIN-DIFF settings binary1 binary2", + "Compare parts of 2 different LECODE binary files. SETTINGS is a" + " string with plus and minus signs to switch between plus/minus modes," + " and some key letters to select parts: s:size, h:header, t:timestamp," + " b:body, p:parameters, d:data, c:code and a:all. In plus-mode, a test" + " is enabled, in minus-mode it is disabled. Scanning starts in" + " plus-mode.\n" + " Example: a-t: compare all except timestamps.\n" + " The exist status is OK (0) if nothing differ, DIFFER (2) if at" + " least one test failed, or an error code.", + 0, + 2, + option_tab_cmd_BIN_DIFF, + option_allowed_cmd_BIN_DIFF + }, + + { CMD_PATCH, + false, + false, + false, + "PATCH", + "P", + "wlect PATCH [le_bin]...", + "Read each LE-BIN file and patch it. Use --le-define to setup cups and" + " tracks.", + 0, + 22, + option_tab_cmd_PATCH, + option_allowed_cmd_PATCH + }, + + { CMD_LPAR, + false, + false, + false, + "LPAR", + 0, + "wlect LPAR [le_bin]...", + "Read each LE-BIN file and patch it. Then extract the LE-CODE" + " parameters and store them as LPAR text file. Print the result to" + " standard output if no other destination is defined.", + 0, + 20, + option_tab_cmd_LPAR, + option_allowed_cmd_LPAR + }, + + { CMD_CREATE, + false, + false, + true, + "CREATE", + "CR", + "wlect CREATE [keyword]", + "Create a file and print it to standard output. The kind of file is" + " defined by KEYWORD. Use command without keywords to get a list of" + " possible keywords.\n" + " The following keywords are possible: LEX for a LEX text file with" + " all known sections except TEST. LEX+ for a LEX text file with all" + " known sections including TEST. SET1, CANN, HIPT and TEST (section" + " names) for a LEX text file with 1 section only. LPAR for an example" + " LE-CODE parameter file.", + 0, + 22, + option_tab_cmd_CREATE, + option_allowed_cmd_CREATE + }, + + { CMD_CAT, + false, + false, + false, + "CAT", + 0, + "wlect CAT [source]...", + "Read the entered LEX and LE-CODE files (text, binary or szs) and" + " print them as decoded text files to standard output. The output of" + " all source files is concatenated.", + 0, + 6, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_DECODE, + false, + false, + false, + "DECODE", + "DEC", + "wlect DECODE [source]...", + "Read the entered LEX files (text or binary) and decode them to text" + " files. The default destination is '%P/%N.txt'.", + 0, + 14, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_ENCODE, + false, + false, + false, + "ENCODE", + "ENC", + "wlect ENCODE source...", + "Read the entered LEX files (text or binary) and encode (compile) them" + " to binary files. The default destination is '%P/%N.lex'.", + 0, + 11, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wlect = +{ + "wlect", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wlect.h b/project/src/ui/ui-wlect.h new file mode 100644 index 0000000..94a0f2e --- /dev/null +++ b/project/src/ui/ui-wlect.h @@ -0,0 +1,451 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WLECT_H +#define SZS_UI_WLECT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_ROUND, + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_EXPORT, + OPT_NO_PARAM, + OPT_NO_ECHO, + OPT_LT_CLEAR, + OPT_LT_ONLINE, + OPT_LT_N_PLAYERS, + OPT_LE_DEFINE, + OPT_LPAR, + OPT_ALIAS, + OPT_ENGINE, + OPT_200CC, + OPT_PERFMON, + OPT_CUSTOM_TT, + OPT_XPFLAGS, + OPT_TRACK_DIR, + OPT_COPY_TRACKS, + OPT_MOVE_TRACKS, + OPT_MOVE1_TRACKS, + OPT_LINK_TRACKS, + OPT_COMPLETE, + OPT_LOAD_BMG, + OPT_PATCH_BMG, + OPT_MACRO_BMG, + OPT_PATCH_NAMES, + OPT_ORDER_BY, + OPT_ORDER_ALL, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 41 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_MAX_FILE_SIZE, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_LT_COND_BIT, + OPT_LT_GAME_MODE, + OPT_LT_ENGINE, + OPT_LT_RANDOM, + OPT_LEX_PURGE, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 72 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_ROUND = 1llu << OPT_ROUND, +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_EXPORT = 1llu << OPT_EXPORT, +// OB_NO_PARAM = 1llu << OPT_NO_PARAM, +// OB_NO_ECHO = 1llu << OPT_NO_ECHO, +// OB_LT_CLEAR = 1llu << OPT_LT_CLEAR, +// OB_LT_ONLINE = 1llu << OPT_LT_ONLINE, +// OB_LT_N_PLAYERS = 1llu << OPT_LT_N_PLAYERS, +// OB_LE_DEFINE = 1llu << OPT_LE_DEFINE, +// OB_LPAR = 1llu << OPT_LPAR, +// OB_ALIAS = 1llu << OPT_ALIAS, +// OB_ENGINE = 1llu << OPT_ENGINE, +// OB_200CC = 1llu << OPT_200CC, +// OB_PERFMON = 1llu << OPT_PERFMON, +// OB_CUSTOM_TT = 1llu << OPT_CUSTOM_TT, +// OB_XPFLAGS = 1llu << OPT_XPFLAGS, +// OB_TRACK_DIR = 1llu << OPT_TRACK_DIR, +// OB_COPY_TRACKS = 1llu << OPT_COPY_TRACKS, +// OB_MOVE_TRACKS = 1llu << OPT_MOVE_TRACKS, +// OB_MOVE1_TRACKS = 1llu << OPT_MOVE1_TRACKS, +// OB_LINK_TRACKS = 1llu << OPT_LINK_TRACKS, +// OB_COMPLETE = 1llu << OPT_COMPLETE, +// OB_LOAD_BMG = 1llu << OPT_LOAD_BMG, +// OB_PATCH_BMG = 1llu << OPT_PATCH_BMG, +// OB_MACRO_BMG = 1llu << OPT_MACRO_BMG, +// OB_PATCH_NAMES = 1llu << OPT_PATCH_NAMES, +// OB_ORDER_BY = 1llu << OPT_ORDER_BY, +// OB_ORDER_ALL = 1llu << OPT_ORDER_ALL, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_PATCH = OB_LE_DEFINE +// | OB_LPAR +// | OB_ALIAS +// | OB_ENGINE +// | OB_200CC +// | OB_PERFMON +// | OB_CUSTOM_TT +// | OB_XPFLAGS, +// +// OB_GRP_TRACK_COPY = OB_TRACK_DIR +// | OB_COPY_TRACKS +// | OB_MOVE_TRACKS +// | OB_MOVE1_TRACKS +// | OB_LINK_TRACKS, +// +// OB_GRP_TEXTOPT = OB_NO_HEADER +// | OB_BRIEF +// | OB_EXPORT, +// +// OB_GRP_TEXTOUT = OB_IGNORE +// | OB_GRP_TEXTOPT, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_SYMBOLS = OB_NO_HEADER, +// +// OB_CMD_FUNCTIONS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_CALCULATE = 0, +// +// OB_CMD_FLOAT = OB_ROUND, +// +// OB_CMD_EXPORT = 0, +// +// OB_CMD_DUMP = OB_GRP_PATCH +// | OB_LONG +// | OB_GRP_PATCH, +// +// OB_CMD_DL = OB_CMD_DUMP, +// +// OB_CMD_DLL = OB_CMD_DUMP, +// +// OB_CMD_BIN_DIFF = 0, +// +// OB_CMD_PATCH = OB_GRP_PATCH +// | OB_GRP_TRACK_COPY +// | OB_GRP_DEST +// | OB_IGNORE, +// +// OB_CMD_LPAR = OB_GRP_PATCH +// | OB_GRP_DEST +// | OB_GRP_TEXTOUT, +// +// OB_CMD_CREATE = OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_GRP_DEST +// | OB_LPAR +// | OB_LT_CLEAR +// | OB_LT_ONLINE +// | OB_LT_N_PLAYERS, +// +// OB_CMD_CAT = OB_COMPLETE +// | OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_IGNORE, +// +// OB_CMD_DECODE = OB_COMPLETE +// | OB_GRP_DEST +// | OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_IGNORE, +// +// OB_CMD_ENCODE = OB_COMPLETE +// | OB_GRP_DEST +// | OB_NO_ECHO +// | OB_IGNORE, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_SYMBOLS, + CMD_FUNCTIONS, + CMD_CALCULATE, + CMD_FLOAT, + CMD_EXPORT, + + CMD_DUMP, + CMD_DL, + CMD_DLL, + CMD_BIN_DIFF, + CMD_PATCH, + CMD_LPAR, + + CMD_CREATE, + CMD_CAT, + CMD_DECODE, + CMD_ENCODE, + + + CMD__N // == 24 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO__ERR = '?', + + GO_BRIEF = 'B', + GO_COMPLETE = 'C', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_NO_PARAM = 'P', + GO_VERSION = 'V', + GO_EXPORT = 'X', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_ROUND, + GO_NO_ECHO, + GO_UTF_8, + GO_NO_UTF_8, + GO_LT_CLEAR, + GO_LT_ONLINE, + GO_LT_N_PLAYERS, + GO_LT_COND_BIT, + GO_LT_GAME_MODE, + GO_LT_ENGINE, + GO_LT_RANDOM, + GO_LEX_PURGE, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_LE_DEFINE, + GO_LPAR, + GO_ALIAS, + GO_ENGINE, + GO_200CC, + GO_PERFMON, + GO_CUSTOM_TT, + GO_XPFLAGS, + GO_TRACK_DIR, + GO_COPY_TRACKS, + GO_MOVE_TRACKS, + GO_MOVE1_TRACKS, + GO_LINK_TRACKS, + GO_LOAD_BMG, + GO_PATCH_BMG, + GO_MACRO_BMG, + GO_PATCH_NAMES, + GO_ORDER_BY, + GO_ORDER_ALL, + GO_NUMBER, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wlect; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WLECT_H + diff --git a/project/src/ui/ui-wmdlt.c b/project/src/ui/ui-wmdlt.c new file mode 100644 index 0000000..15be6fc --- /dev/null +++ b/project/src/ui/ui-wmdlt.c @@ -0,0 +1,1756 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wmdlt.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_SCALE, false, false, false, false, false, 0, "scale", + "vector[@origin]", + "Transform the data: Scale all coordinates and size values by 'vector'" + " relative to the point 'origin'. If the origin is not set, 0,0,0 is" + " used instead.\n" + " Negative values mirror the coordinates. Each parameter is either a" + " vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_SHIFT, false, false, false, false, false, 0, "shift", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --translate, but the addition is done before rotation. The parameter" + " is either a vector expression or a comma separated expression list" + " of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_XSS, false, false, false, false, false, 0, "xss", + "x1old,x1new,x2old,x2new", + "--xss (x-scale-shift) calculates the X values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_YSS, false, false, false, false, false, 0, "yss", + "y1old,y1new,y2old,y2new", + "--yss (y-scale-shift) calculates the Y values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ZSS, false, false, false, false, false, 0, "zss", + "z1old,z1new,z2old,z2new", + "--zss (z-scale-shift) calculates the Z values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ROT, false, false, false, false, false, 0, "rot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' (is a vector) around the 3 axes. All 3 axes goes" + " through the point 'origin'. If the origin is not set, 0,0,0 is used" + " instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y,z').\n" + " Option --rot is an alternative for --xrot, --yrot and --zrot to" + " define all 3 rotations in one step. Transformations are calculated" + " in the order SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_XROT, false, false, false, false, false, 0, "xrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('y,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_YROT, false, false, false, false, false, 0, "yrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead. This is a" + " horizontal counterclockwise rotation and the old option name --hrot" + " can also used.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_ZROT, false, false, false, false, false, 0, "zrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the z-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_TRANSLATE, false, false, false, false, false, 0, "translate", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --shift, but the addition is done after rotation. The parameter is" + " either a vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z'). --trans is a short cut.\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_NULL, false, false, false, false, false, 0, "null", + 0, + "Create a neutral transformation without affecting the coordinates." + " The only influence is, that the dependent values (like maximum) are" + " re-calculated as if a transformation has taken place." + }, + + { OPT_NEXT, false, false, false, false, false, 0, "next", + 0, + "Close the current transformation step with all scaling, shifting," + " rotation and translation options and open a new step with cleared" + " options. On transformation each step is logical done one by one. In" + " real, one total transformation matrix is calculated and used for" + " fast transformations.\n" + " If option --next is used, the ability for modifying the scale and" + " rotation vectors (not the positions) of different KMP sections is" + " nearly always lost. A warning is printed if this occurs." + }, + + { OPT_ASCALE, false, false, false, false, false, 0, "ascale", + "factor@dir", + "Transform the data and do an axis scale: Close the current" + " transformation step like --next and scale the data by 'factor' into" + " the direction given by the vector 'dir'. Store the resulting matrix" + " as single transformation step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_AROT, false, false, false, false, false, 0, "arot", + "degree[@pos1]@pos2", + "Transform the data and do an axis rotation: Close the current" + " transformation step like --next and rotate the data by 'degree'" + " around the axis defined by the 2 points. If 'pos1' is not set, use" + " 0,0,0 instead. Store the resulting matrix as single transformation" + " step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_TFORM_SCRIPT, false, false, false, false, false, 0, "tform-script", + "script", + "Load the script and execute it by the text parser.\n" + " Then for each coordinate (2D and 3D), call the macro TRANSFORM after" + " all other transformations. Call it without parameters and set" + " variable $I with a zero based index of all vertices, variable $D to" + " the dimension (2 or 3) and the variable $P to the vector of the" + " current vertex. If the macro returns a vector, it is the new" + " position.\n" + " For each file, macro BEGIN is called at the beginning and END after" + " processing all points, but only, if the macros are defined. For END," + " variable $I is set to the number of processed vertices in the" + " current file.\n" + " Before each macro call, the private and local variables are cleared" + " and only global parameters are permanent. The zero based index is" + " cleared for each new source. The parameters of the macro calls are" + " defined in the local name space." + }, + + { OPT_ROUND, false, false, false, false, false, 0, "round", + 0, + "Print additional lines with rounded values (to 3 and 2 bytes for" + " floats and 7 and 6 bytes for doubles)." + }, + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress cross reference comments." + }, + + { OPT_NO_PARAM, false, false, false, false, false, 'P', "no-param", + 0, + "Disable parameter support in decoded KMP text files." + }, + + { OPT_NO_ECHO, false, false, false, false, false, 0, "no-echo", + 0, + "Suppress output of @ECHO commands while scanning text files." + }, + + { OPT_NO_CHECK, false, false, false, false, false, 'N', "no-check", + 0, + "Do not make plausibility checks for KMP files." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 33 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "This option is completely ignored by wlect. It only exists due to" + " compatibility with the other tools." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_MDL, false, false, false, false, false, 0, "mdl", + "list", + "Set global options for MDL processing. To enable MDL patching, use" + " option --patch-file and select BRRES files for patching.\n" + " Parameter 'list' is a comma separated list of keywords. A minus" + " sign before a keyword disables a setting. Each occurrence of the" + " option will only change entered settings and all other settings are" + " untouched.\n" + " A MDL transformation (controlled by the transformation options) is" + " only done, if at least one of the keywords VECTOR or VERTEX is set.\n" + " Keyword DEFAULT resets the default settings (VERTEX) and CLEAR" + " disables all. The other allowed keywords are: VECTOR, MATRIX," + " VERTEX, PARENT, CHILD and LOG. See https://szs.wiimm.de/opt/mdl for" + " details." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_TRACKS, false, true, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, true, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_TINY, false, false, false, false, false, 0, "tiny", + "mode", + "Set tiny mode to a level between 0 and 7. See" + " https://szs.wiimm.de/opt/tiny for details." + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 63 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_grp_TEXTOUT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in MDL text files." + }; + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_TEST_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Print the matrices of transformations." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the description and print only function type" + " and syntax." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Usually only the function syntax is compared to the keywords. But if" + " --long is set, the descriptions are compared too." + }; + +static const InfoOption_t option_cmd_MATRIX_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "If set once, suppress vector dumps for transformation steps and" + " ignore option --long. If set twice, suppress vector dump for final" + " transformation too." + }; + +static const InfoOption_t option_cmd_MATRIX_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set once, print inverse matrix for the final transformation. If" + " set twice, print inverse matrix for all transformations." + }; + +static const InfoOption_t option_cmd_MATRIX_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Option --quiet is dedicated to automatic tests comparing the old and" + " new transformation algorithms. If set once, the tables are only" + " printed, if the results differ. If set twice, nothing is printed at" + " all and only the exit status will inform about the results." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_SYMBOLS, "SYMBOLS", 0, 0 }, + { CMD_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { CMD_CALCULATE, "CALCULATE", 0, 0 }, + { CMD_MATRIX, "MATRIX", 0, 0 }, + { CMD_FLOAT, "FLOAT", 0, 0 }, + { CMD_EXPORT, "_EXPORT", 0, 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "ENC", 0 }, + { CMD_STRINGS, "STRINGS", 0, 0 }, + { CMD_GEOMETRY, "GEOMETRY", "GEO", 0 }, + { CMD_XTEST, "_TEST", 0, 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHBPNM:T:A:td:D:E:orupi"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "mdl", 1, 0, GO_MDL }, + { "scale", 1, 0, GO_SCALE }, + { "shift", 1, 0, GO_SHIFT }, + { "xss", 1, 0, GO_XSS }, + { "yss", 1, 0, GO_YSS }, + { "zss", 1, 0, GO_ZSS }, + { "rot", 1, 0, GO_ROT }, + { "xrot", 1, 0, GO_XROT }, + { "yrot", 1, 0, GO_YROT }, + { "hrot", 1, 0, GO_YROT }, + { "zrot", 1, 0, GO_ZROT }, + { "translate", 1, 0, GO_TRANSLATE }, + { "null", 0, 0, GO_NULL }, + { "next", 0, 0, GO_NEXT }, + { "ascale", 1, 0, GO_ASCALE }, + { "arot", 1, 0, GO_AROT }, + { "tform-script", 1, 0, GO_TFORM_SCRIPT }, + { "tformscript", 1, 0, GO_TFORM_SCRIPT }, + { "round", 0, 0, GO_ROUND }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "no-param", 0, 0, 'P' }, + { "noparam", 0, 0, 'P' }, + { "no-echo", 0, 0, GO_NO_ECHO }, + { "noecho", 0, 0, GO_NO_ECHO }, + { "no-check", 0, 0, 'N' }, + { "nocheck", 0, 0, 'N' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "tiny", 1, 0, GO_TINY }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x040 */ 0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e N */ OPT_NO_CHECK, + /* 0x04f */ 0, + /* 0x050 P */ OPT_NO_PARAM, + /* 0x051 */ 0,0,0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_MDL, + /* 0x08b */ OPT_SCALE, + /* 0x08c */ OPT_SHIFT, + /* 0x08d */ OPT_XSS, + /* 0x08e */ OPT_YSS, + /* 0x08f */ OPT_ZSS, + /* 0x090 */ OPT_ROT, + /* 0x091 */ OPT_XROT, + /* 0x092 */ OPT_YROT, + /* 0x093 */ OPT_ZROT, + /* 0x094 */ OPT_TRANSLATE, + /* 0x095 */ OPT_NULL, + /* 0x096 */ OPT_NEXT, + /* 0x097 */ OPT_ASCALE, + /* 0x098 */ OPT_AROT, + /* 0x099 */ OPT_TFORM_SCRIPT, + /* 0x09a */ OPT_ROUND, + /* 0x09b */ OPT_NO_ECHO, + /* 0x09c */ OPT_UTF_8, + /* 0x09d */ OPT_NO_UTF_8, + /* 0x09e */ OPT_FORCE, + /* 0x09f */ OPT_REPAIR_MAGICS, + /* 0x0a0 */ OPT_TINY, + /* 0x0a1 */ OPT_OLD, + /* 0x0a2 */ OPT_STD, + /* 0x0a3 */ OPT_NEW, + /* 0x0a4 */ OPT_EXTRACT, + /* 0x0a5 */ OPT_NUMBER, + /* 0x0a6 */ OPT_SECTIONS, + /* 0x0a7 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x0b0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[33] = // cmd #1 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,1 +}; + +static u8 option_allowed_cmd_HELP[33] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1 +}; + +static u8 option_allowed_cmd_ARGTEST[33] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1 +}; + +static u8 option_allowed_cmd_TEST[33] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1 +}; + +static u8 option_allowed_cmd_COLORS[33] = // cmd #5 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0 +}; + +static u8 option_allowed_cmd_ERROR[33] = // cmd #6 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,1 +}; + +static u8 option_allowed_cmd_FILETYPE[33] = // cmd #7 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,1,0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[33] = // cmd #8 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0 +}; + +static u8 option_allowed_cmd_SYMBOLS[33] = // cmd #9 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0 +}; + +static u8 option_allowed_cmd_FUNCTIONS[33] = // cmd #10 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0 +}; + +static u8 option_allowed_cmd_CALCULATE[33] = // cmd #11 +{ + 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 u8 option_allowed_cmd_MATRIX[33] = // cmd #12 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0 +}; + +static u8 option_allowed_cmd_FLOAT[33] = // cmd #13 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0 +}; + +static u8 option_allowed_cmd_EXPORT[33] = // cmd #14 +{ + 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 u8 option_allowed_cmd_CAT[33] = // cmd #15 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,1,0 +}; + +static u8 option_allowed_cmd_DECODE[33] = // cmd #16 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,0,1,1, 0,1,1,1,1, 1,1,1,1,1, + 1,1,0 +}; + +static u8 option_allowed_cmd_ENCODE[33] = // cmd #17 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,0,0,0, 0,1,1,1,1, 1,1,1,1,1, + 1,1,0 +}; + +static u8 option_allowed_cmd_STRINGS[33] = // cmd #18 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,1,0 +}; + +static u8 option_allowed_cmd_GEOMETRY[33] = // cmd #19 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,1,0 +}; + +static u8 option_allowed_cmd_XTEST[33] = // cmd #20 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,1,0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MAX_FILE_SIZE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + OptionInfo + OPT_TINY, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + &option_cmd_TEST_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SYMBOLS[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FUNCTIONS[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_FUNCTIONS_BRIEF, + &option_cmd_FUNCTIONS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CALCULATE[] = +{ + OptionInfo + OPT_CONST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MATRIX[] = +{ + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_MDL, + + OptionInfo + OPT_NONE, // separator + + &option_cmd_MATRIX_BRIEF, + &option_cmd_MATRIX_LONG, + &option_cmd_MATRIX_QUIET, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLOAT[] = +{ + OptionInfo + OPT_ROUND, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_MDL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_MDL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_MDL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_STRINGS[] = +{ + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_MDL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_GEOMETRY[] = +{ + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_MDL, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_XTEST[] = +{ + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_MDL, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wmdlt", + 0, + "wmdlt [option]... command [option|parameter|file]...", + "Wiimms MDL Tool : Decode raw MDL and encode text MDL files.", + 0, + 22, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wmdlt VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wmdlt HELP [--width=wd] OPT|OPTION ...\n" + "wmdlt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wmdlt HELP [--width=wd] command [-b] [ignored]...\n" + "wmdlt HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wmdlt ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wmdlt TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 1, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wmdlt COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wmdlt ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wmdlt FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wmdlt FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_SYMBOLS, + false, + false, + false, + "SYMBOLS", + 0, + "wmdlt SYMBOLS", + "Print predefined symbols of text parser to standard output. The MDL" + " specific symbols are included.", + 0, + 1, + option_tab_cmd_SYMBOLS, + option_allowed_cmd_SYMBOLS + }, + + { CMD_FUNCTIONS, + false, + false, + false, + "FUNCTIONS", + 0, + "wmdlt FUNCTIONS [key]...", + "Print a list with all parser functions including the MDL specific" + " functions. If one key ore more key entered, then only functions that" + " match at least one key are printed.", + 0, + 3, + option_tab_cmd_FUNCTIONS, + option_allowed_cmd_FUNCTIONS + }, + + { CMD_CALCULATE, + false, + false, + false, + "CALCULATE", + 0, + "wmdlt CALCULATE expression...", + "Calculate each expression and print the result to standard output.", + 0, + 1, + option_tab_cmd_CALCULATE, + option_allowed_cmd_CALCULATE + }, + + { CMD_MATRIX, + false, + false, + false, + "MATRIX", + 0, + "wmdlt MATRIX [patch-option]... [vector]...", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results.", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results. The transformations are done with 4" + " different methods to confirm the algorithms. Only the main results" + " and results, which differ, are printed.\n" + " Normally, the exit status is OK (null). If at least one old" + " transformation returns different results for the different" + " algorithms, then status DIFFER (not null) is returned.", + 19, + option_tab_cmd_MATRIX, + option_allowed_cmd_MATRIX + }, + + { CMD_FLOAT, + false, + false, + false, + "FLOAT", + 0, + "wmdlt FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa. For each non empty parameter, a line with 4 bytes" + " hex and a float, 8 bytes hex and a double float, and the parameter" + " itself is printed. If a parameter begins with 'x' or '0x', it is a" + " hex string, otherwise it is a floating point number.\n" + " The special parameters \"+\" and \"-\" mean: Increment or decrement" + " the previous value by the smallest possible value. Use different" + " increments for floats and doubles.\n" + " All conversions are done using big endian.", + 0, + 1, + option_tab_cmd_FLOAT, + option_allowed_cmd_FLOAT + }, + + { CMD_EXPORT, + true, + false, + false, + "_EXPORT", + 0, + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output. Use" + " keyword HELP to get a list of possible keywords.", + 0, + 0, + option_tab_cmd_EXPORT, + option_allowed_cmd_EXPORT + }, + + { CMD_CAT, + false, + false, + true, + "CAT", + 0, + "wmdlt CAT [source]...", + "Read the entered MDL files (text or binary) and print them as decoded" + " text files to standard output (stdout). The output of all source" + " files is concatenated.\n" + " A decoded MDL0 file can only be used for analysis. Encoding or" + " creating a new MDL0 file is not supported.", + 0, + 21, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_DECODE, + false, + false, + false, + "DECODE", + "DEC", + "wmdlt DECODE [source]...", + "Read the entered MDL files (text or binary) and decode them to text" + " files. The default destination is '%P/%N.txt'.\n" + " A decoded MDL0 file can only be used for analysis. Encoding or" + " creating a new MDL0 file is not supported.", + 0, + 29, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_ENCODE, + true, + false, + false, + "ENCODE", + "ENC", + "wmdlt ENCODE [source]...", + "Read the entered MDL files (text or binary) and encode (compile) them" + " to binary files. The default destination is '%P/%N.mdl'.", + 0, + 27, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + { CMD_STRINGS, + false, + false, + true, + "STRINGS", + 0, + "wmdlt STRINGS [source]...", + "Read the entered MDL files (text or binary) and print all strings of" + " all sections.\n" + " For archives like SZS or BREES, the strings of each containing MDL" + " are printed. It is also possible to address an internal sub-file" + " like track.szs/course_model.brres.", + 0, + 21, + option_tab_cmd_STRINGS, + option_allowed_cmd_STRINGS + }, + + { CMD_GEOMETRY, + true, + false, + false, + "GEOMETRY", + "GEO", + "wmdlt GEOMETRY [source]...", + "Read the entered MDL files (text or binary) and print some geometry" + " information to the standard output (stdout). The output of all" + " source files is concatenated.", + 0, + 21, + option_tab_cmd_GEOMETRY, + option_allowed_cmd_GEOMETRY + }, + + { CMD_XTEST, + true, + false, + false, + "_TEST", + 0, + "wszst _TEST [files]", + "Test something.", + 0, + 21, + option_tab_cmd_XTEST, + option_allowed_cmd_XTEST + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wmdlt = +{ + "wmdlt", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wmdlt.h b/project/src/ui/ui-wmdlt.h new file mode 100644 index 0000000..7bca204 --- /dev/null +++ b/project/src/ui/ui-wmdlt.h @@ -0,0 +1,417 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WMDLT_H +#define SZS_UI_WMDLT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_SCALE, + OPT_SHIFT, + OPT_XSS, + OPT_YSS, + OPT_ZSS, + OPT_ROT, + OPT_XROT, + OPT_YROT, + OPT_ZROT, + OPT_TRANSLATE, + OPT_NULL, + OPT_NEXT, + OPT_ASCALE, + OPT_AROT, + OPT_TFORM_SCRIPT, + OPT_ROUND, + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_NO_PARAM, + OPT_NO_ECHO, + OPT_NO_CHECK, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 33 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_MDL, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_TINY, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 63 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_SCALE = 1llu << OPT_SCALE, +// OB_SHIFT = 1llu << OPT_SHIFT, +// OB_XSS = 1llu << OPT_XSS, +// OB_YSS = 1llu << OPT_YSS, +// OB_ZSS = 1llu << OPT_ZSS, +// OB_ROT = 1llu << OPT_ROT, +// OB_XROT = 1llu << OPT_XROT, +// OB_YROT = 1llu << OPT_YROT, +// OB_ZROT = 1llu << OPT_ZROT, +// OB_TRANSLATE = 1llu << OPT_TRANSLATE, +// OB_NULL = 1llu << OPT_NULL, +// OB_NEXT = 1llu << OPT_NEXT, +// OB_ASCALE = 1llu << OPT_ASCALE, +// OB_AROT = 1llu << OPT_AROT, +// OB_TFORM_SCRIPT = 1llu << OPT_TFORM_SCRIPT, +// OB_ROUND = 1llu << OPT_ROUND, +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_NO_PARAM = 1llu << OPT_NO_PARAM, +// OB_NO_ECHO = 1llu << OPT_NO_ECHO, +// OB_NO_CHECK = 1llu << OPT_NO_CHECK, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_TRANSFORM = OB_SCALE +// | OB_SHIFT +// | OB_XSS +// | OB_YSS +// | OB_ZSS +// | OB_ROT +// | OB_XROT +// | OB_YROT +// | OB_ZROT +// | OB_TRANSLATE +// | OB_NULL +// | OB_ASCALE +// | OB_AROT +// | OB_TFORM_SCRIPT +// | OB_NEXT, +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_TEXTOUT = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_SYMBOLS = OB_NO_HEADER, +// +// OB_CMD_FUNCTIONS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_CALCULATE = 0, +// +// OB_CMD_MATRIX = OB_GRP_TRANSFORM +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_FLOAT = OB_ROUND, +// +// OB_CMD_EXPORT = 0, +// +// OB_CMD_CAT = OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_DECODE = OB_GRP_DEST +// | OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_ENCODE = OB_GRP_DEST +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_STRINGS = OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_GEOMETRY = OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +// OB_CMD_XTEST = OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE +// | OB_GRP_TRANSFORM, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_SYMBOLS, + CMD_FUNCTIONS, + CMD_CALCULATE, + CMD_MATRIX, + CMD_FLOAT, + CMD_EXPORT, + + CMD_CAT, + CMD_DECODE, + CMD_ENCODE, + + CMD_STRINGS, + CMD_GEOMETRY, + CMD_XTEST, + + CMD__N // == 21 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_NO_CHECK = 'N', + GO_NO_PARAM = 'P', + GO_TRACKS = 'T', + GO_VERSION = 'V', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_MDL, + GO_SCALE, + GO_SHIFT, + GO_XSS, + GO_YSS, + GO_ZSS, + GO_ROT, + GO_XROT, + GO_YROT, + GO_ZROT, + GO_TRANSLATE, + GO_NULL, + GO_NEXT, + GO_ASCALE, + GO_AROT, + GO_TFORM_SCRIPT, + GO_ROUND, + GO_NO_ECHO, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_TINY, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wmdlt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WMDLT_H + diff --git a/project/src/ui/ui-wpatt.c b/project/src/ui/ui-wpatt.c new file mode 100644 index 0000000..4d27f28 --- /dev/null +++ b/project/src/ui/ui-wpatt.c @@ -0,0 +1,1228 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wpatt.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_ROUND, false, false, false, false, false, 0, "round", + 0, + "Print additional lines with rounded values (to 3 and 2 bytes for" + " floats and 7 and 6 bytes for doubles)." + }, + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress cross reference comments." + }, + + { OPT_NO_PARAM, false, false, false, false, false, 'P', "no-param", + 0, + "Disable parameter support in decoded KMP text files." + }, + + { OPT_NO_ECHO, false, false, false, false, false, 0, "no-echo", + 0, + "Suppress output of @ECHO commands while scanning text files." + }, + + { OPT_NO_CHECK, false, false, false, false, false, 'N', "no-check", + 0, + "Do not make plausibility checks for KMP files." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 18 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "This option is completely ignored by wlect. It only exists due to" + " compatibility with the other tools." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_TRACKS, false, true, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, true, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_PAT, false, false, false, false, false, 0, "pat", + "list", + "Set global options for PAT processing. Parameter 'list' is a comma" + " separated list of keywords. A minus sign before a keyword disables a" + " setting. Each occurrence of the option will only change entered" + " settings and all other settings are untouched.\n" + " Keyword DEFAULT resets the default settings and CLEAR disables all." + " The other allowed keywords are: AUTO, SIMPLE, COMPLEX, BOTH, SILENT" + " and LOG. See https://szs.wiimm.de/opt/pat for details." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 47 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_grp_TEXTOUT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in PAT text files." + }; + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the description and print only function type" + " and syntax." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Usually only the function syntax is compared to the keywords. But if" + " --long is set, the descriptions are compared too." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_SYMBOLS, "SYMBOLS", 0, 0 }, + { CMD_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { CMD_CALCULATE, "CALCULATE", 0, 0 }, + { CMD_FLOAT, "FLOAT", 0, 0 }, + { CMD_EXPORT, "_EXPORT", 0, 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_DECODE, "DECODE", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "ENC", 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHBPNM:T:A:td:D:E:orupi"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "round", 0, 0, GO_ROUND }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "no-param", 0, 0, 'P' }, + { "noparam", 0, 0, 'P' }, + { "no-echo", 0, 0, GO_NO_ECHO }, + { "noecho", 0, 0, GO_NO_ECHO }, + { "no-check", 0, 0, 'N' }, + { "nocheck", 0, 0, 'N' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "pat", 1, 0, GO_PAT }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x040 */ 0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e N */ OPT_NO_CHECK, + /* 0x04f */ 0, + /* 0x050 P */ OPT_NO_PARAM, + /* 0x051 */ 0,0,0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_ROUND, + /* 0x08b */ OPT_NO_ECHO, + /* 0x08c */ OPT_PAT, + /* 0x08d */ OPT_UTF_8, + /* 0x08e */ OPT_NO_UTF_8, + /* 0x08f */ OPT_FORCE, + /* 0x090 */ OPT_REPAIR_MAGICS, + /* 0x091 */ OPT_OLD, + /* 0x092 */ OPT_STD, + /* 0x093 */ OPT_NEW, + /* 0x094 */ OPT_EXTRACT, + /* 0x095 */ OPT_NUMBER, + /* 0x096 */ OPT_SECTIONS, + /* 0x097 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x0a0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0b0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0c0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0d0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[18] = // cmd #1 +{ + 0,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,1 +}; + +static u8 option_allowed_cmd_HELP[18] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1 +}; + +static u8 option_allowed_cmd_ARGTEST[18] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1 +}; + +static u8 option_allowed_cmd_TEST[18] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1 +}; + +static u8 option_allowed_cmd_COLORS[18] = // cmd #5 +{ + 0,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 +}; + +static u8 option_allowed_cmd_ERROR[18] = // cmd #6 +{ + 0,0,1,1,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,1 +}; + +static u8 option_allowed_cmd_FILETYPE[18] = // cmd #7 +{ + 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,1,0 +}; + +static u8 option_allowed_cmd_FILEATTRIB[18] = // cmd #8 +{ + 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 +}; + +static u8 option_allowed_cmd_SYMBOLS[18] = // cmd #9 +{ + 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 +}; + +static u8 option_allowed_cmd_FUNCTIONS[18] = // cmd #10 +{ + 0,0,1,1,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 +}; + +static u8 option_allowed_cmd_CALCULATE[18] = // cmd #11 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 +}; + +static u8 option_allowed_cmd_FLOAT[18] = // cmd #12 +{ + 0,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 +}; + +static u8 option_allowed_cmd_EXPORT[18] = // cmd #13 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0 +}; + +static u8 option_allowed_cmd_CAT[18] = // cmd #14 +{ + 0,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, 0,1,0 +}; + +static u8 option_allowed_cmd_DECODE[18] = // cmd #15 +{ + 0,0,0,1,1, 0,1,1,1,1, 1,1,1,1,1, 1,1,0 +}; + +static u8 option_allowed_cmd_ENCODE[18] = // cmd #16 +{ + 0,0,0,0,0, 0,1,1,1,1, 1,1,1,1,1, 1,1,0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_MAX_FILE_SIZE, + OptionInfo + OPT_PAT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SYMBOLS[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FUNCTIONS[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_FUNCTIONS_BRIEF, + &option_cmd_FUNCTIONS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CALCULATE[] = +{ + OptionInfo + OPT_CONST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLOAT[] = +{ + OptionInfo + OPT_ROUND, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + OptionInfo + OPT_PAT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + OptionInfo + OPT_PAT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_IGNORE, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wpatt", + 0, + "wpatt [option]... command [option|parameter|file]...", + "Wiimms PAT Tool : Decode raw PAT and encode text PAT files.", + 0, + 21, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wpatt VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wpatt HELP [--width=wd] OPT|OPTION ...\n" + "wpatt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wpatt HELP [--width=wd] command [-b] [ignored]...\n" + "wpatt HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wpatt ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wpatt TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 0, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wpatt COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wpatt ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wpatt FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wpatt FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_SYMBOLS, + false, + false, + false, + "SYMBOLS", + 0, + "wpatt SYMBOLS", + "Print predefined symbols of text parser to standard output. The PAT" + " specific symbols are included.", + 0, + 1, + option_tab_cmd_SYMBOLS, + option_allowed_cmd_SYMBOLS + }, + + { CMD_FUNCTIONS, + false, + false, + false, + "FUNCTIONS", + 0, + "wpatt FUNCTIONS [key]...", + "Print a list with all parser functions including the PAT specific" + " functions. If one key ore more key entered, then only functions that" + " match at least one key are printed.", + 0, + 3, + option_tab_cmd_FUNCTIONS, + option_allowed_cmd_FUNCTIONS + }, + + { CMD_CALCULATE, + false, + false, + false, + "CALCULATE", + 0, + "wpatt CALCULATE expression...", + "Calculate each expression and print the result to standard output.", + 0, + 1, + option_tab_cmd_CALCULATE, + option_allowed_cmd_CALCULATE + }, + + { CMD_FLOAT, + false, + false, + false, + "FLOAT", + 0, + "wpatt FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa. For each non empty parameter, a line with 4 bytes" + " hex and a float, 8 bytes hex and a double float, and the parameter" + " itself is printed. If a parameter begins with 'x' or '0x', it is a" + " hex string, otherwise it is a floating point number.\n" + " The special parameters \"+\" and \"-\" mean: Increment or decrement" + " the previous value by the smallest possible value. Use different" + " increments for floats and doubles.\n" + " All conversions are done using big endian.", + 0, + 1, + option_tab_cmd_FLOAT, + option_allowed_cmd_FLOAT + }, + + { CMD_EXPORT, + true, + false, + false, + "_EXPORT", + 0, + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output. Use" + " keyword HELP to get a list of possible keywords.", + 0, + 0, + option_tab_cmd_EXPORT, + option_allowed_cmd_EXPORT + }, + + { CMD_CAT, + false, + false, + true, + "CAT", + 0, + "wpatt CAT [source]...", + "Read the entered PAT files (text or binary) and print them as decoded" + " text files to standard output (stdout). The output of all source" + " files is concatenated.", + 0, + 5, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_DECODE, + false, + false, + false, + "DECODE", + "DEC", + "wpatt DECODE [source]...", + "Read the entered PAT files (text or binary) and decode them to text" + " files. The default destination is '%P/%N.txt'.", + 0, + 14, + option_tab_cmd_DECODE, + option_allowed_cmd_DECODE + }, + + { CMD_ENCODE, + false, + false, + false, + "ENCODE", + "ENC", + "wpatt ENCODE [source]...", + "Read the entered PAT files (text or binary) and encode (compile) them" + " to binary files. The default destination is '%P/%N.pat'.", + 0, + 12, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wpatt = +{ + "wpatt", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wpatt.h b/project/src/ui/ui-wpatt.h new file mode 100644 index 0000000..89befd5 --- /dev/null +++ b/project/src/ui/ui-wpatt.h @@ -0,0 +1,324 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WPATT_H +#define SZS_UI_WPATT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_ROUND, + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_NO_PARAM, + OPT_NO_ECHO, + OPT_NO_CHECK, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 18 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_PAT, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 47 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_ROUND = 1llu << OPT_ROUND, +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_NO_PARAM = 1llu << OPT_NO_PARAM, +// OB_NO_ECHO = 1llu << OPT_NO_ECHO, +// OB_NO_CHECK = 1llu << OPT_NO_CHECK, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_TEXTOUT = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_SYMBOLS = OB_NO_HEADER, +// +// OB_CMD_FUNCTIONS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_CALCULATE = 0, +// +// OB_CMD_FLOAT = OB_ROUND, +// +// OB_CMD_EXPORT = 0, +// +// OB_CMD_CAT = OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE, +// +// OB_CMD_DECODE = OB_GRP_DEST +// | OB_GRP_TEXTOUT +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE, +// +// OB_CMD_ENCODE = OB_GRP_DEST +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_IGNORE, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_SYMBOLS, + CMD_FUNCTIONS, + CMD_CALCULATE, + CMD_FLOAT, + CMD_EXPORT, + + CMD_CAT, + CMD_DECODE, + CMD_ENCODE, + + CMD__N // == 17 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_NO_CHECK = 'N', + GO_NO_PARAM = 'P', + GO_TRACKS = 'T', + GO_VERSION = 'V', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_ROUND, + GO_NO_ECHO, + GO_PAT, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wpatt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WPATT_H + diff --git a/project/src/ui/ui-wstrt.c b/project/src/ui/ui-wstrt.c new file mode 100644 index 0000000..463ab65 --- /dev/null +++ b/project/src/ui/ui-wstrt.c @@ -0,0 +1,2207 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wstrt.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "List only the track file names and suppress table header and footer." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in BMG text files." + }, + + { OPT_CHEAT, false, true, false, false, false, 0, "cheat", + "region", + "Change the output format and create a cheat code for the region." + " Allowed keywords: JAPAN; AMERICA, NTSC; EUROPE, AUSTRALIA, PAL;" + " KOREA." + }, + + { OPT_ADD_WCODE, false, true, false, false, false, 0, "@add-wcode", + "file", + "Each option call adds the filename to an internal list. Each file" + " must be of type GCT and is added to the internal wcode stock.\n" + " If a single file is not found and the filename (not path) contains" + " at least 1 '@' character, then all '@' characters of it are replaced" + " by the region code (one of P,E,J,K) for a second try." + }, + + { OPT_DEST, false, false, false, false, true, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_ADD_LECODE, false, true, false, false, false, 0, "add-lecode", + 0, + "Add LE-CODE starter to main.dol." + }, + + { OPT_ADD_CTCODE, false, false, false, false, false, 0, "add-ctcode", + 0, + "If source is an original main.dol file of MKWii, then append 2" + " sections (usually T2 and D8) with CT-CODE. The CTCODE is inserted" + " after sections defined by --add-section and then only, if there are" + " no address collisions.\n" + " If --ct-dir is used, then files named rmc[ejp]/boot_code.bin and" + " boot_code.bin for T2, and rmc[ejp]/boot_data.bin and boot_data.bin" + " for D8 are searched." + }, + + { OPT_MOVE_D8, false, false, false, false, false, 0, "move-d8", + 0, + "Move the CT-CODE section D8 to another place at the middle of binary." + " It is done automatically, if the original place overlaps other" + " existing sections." + }, + + { OPT_ADD_SECTION, false, false, false, false, false, 0, "add-section", + "file", + "Each option call adds the filename to an internal list. Each file" + " must be of type GCT, GCH or WCH. All GCT files together are combined" + " with an internal code handler to one section (like a GCH file). GCH" + " and WCH define sections and patches. The defined sections are added" + " and VBI hooks are optionally initialized. Use option --force to" + " allow overlapped sections. Use option --verbose up to 2 times for a" + " log.\n" + " If a single file is not found and the filename (not path) contains" + " at least 1 '@' character, then all '@' characters of it are replaced" + " by the region code (one of P,E,J,K) for a second try.\n" + " The intention of this option is to add a Gecko Code Handler and" + " cheat codes to a main.dol for automatic execution. See" + " https://szs.wiimm.de/doc/addsect for details." + }, + + { OPT_FULL_GCH, false, true, false, false, false, 0, "full-gch", + 0, + "Use the full 'codehandler' instead of 'codehandleronly'." + }, + + { OPT_GCT_NO_SEP, false, true, false, false, false, 0, "gct-no-sep", + 0, + "Ignore cheat code separators completly and don't filter 'F0000001" + " 0000xxxx' codes. Options --gct-sep and --gct-asm-sep are ignored" + " too." + }, + + { OPT_GCT_SEP, false, true, false, false, false, 0, "gct-sep", + 0, + "Detect and remove cheat code separators of format 'F0000001" + " 0000xxxx'. The separators are used to split the cheat codes into" + " several blocks if the total size is too large. ASM blocks are" + " skipped if searching separators." + }, + + { OPT_GCT_ASM_SEP, false, true, false, false, false, 0, "gct-asm-sep", + 0, + "Detect the beginning of ASM blocks and use them as cheat code" + " separators. The separators are used to split the cheat codes into" + " several blocks if the total size is too large." + }, + + { OPT_GCT_LIST, false, true, false, false, false, 0, "gct-list", + 0, + "If GCT separators active, print a summary table about the" + " distribution of the segments. Be more verbose if set twice." + }, + + { OPT_GCT_MOVE, true, false, false, false, false, 0, "gct-move", + "[=mode]", + "If a section is of type gecko code handler + cheat code, then make" + " the heap smaller and move the cheat codes before the FST section.\n" + " Parameter MODE is optional. If set, OFF disables and ON enables" + " this feature. For mode AUTO (default) this feature is enabled, if" + " the cheat codes are too large or for internal reasons." + }, + + { OPT_GCT_ADDR, false, false, false, false, false, 0, "gct-addr", + "address", + "If --gct-move is active, a text section is created to manage the code" + " handling. This section is only needed at start and is placed by" + " default at address 0c802c0000 (good address for all 4 regions) as" + " part of BSS. This option changes the start address of the section." + " If address is 0 or invalid, then unused BSS space is searched." + }, + + { OPT_GCT_SPACE, false, false, false, false, false, 0, "gct-space", + "size", + "Define the minimal space for the code part of --gct-move. This value" + " is ignored, if it is smaller than the size of the cheat codes. This" + " option was only implemented to test the memory allocation by" + " --gct-move with large blocks." + }, + + { OPT_ALLOW_USER_GCH, true, false, false, false, false, 0, "allow-user-gch", + "[=mode]", + " This option is only relevant, if main.dol is patched for wiimmfi.de." + " It is dedicated to custom distribution creators for test purposes." + " If enabled, a user can load an own instance of Gecko Code Handler" + " and cheat codes and can use them online. If disabled, pachcing for" + " online modus is disabled if an alternative Gecko Code Handler is" + " detected.\n" + " Parameter MODE is optional. If set, OFF disables and ON enables" + " this feature. For mode AUTO (default) it depends on other options." + }, + + { OPT_CREATE_SECT, false, true, false, false, false, 0, "create-sect", + "mode=file", + "Create a new DOL section by loading the binary file. MODE is a list" + " of 'P' (parameter support), 'T' or 'D' with optional digit (define" + " section) and an address (auto if not set). The parts are separated" + " by spaces or commas.\n" + " If MODE is 'P', then 4 32-bit parameters are expected at offset" + " 0x10: The section address at 0x10 (read), the relative entry point" + " at 0x14 (read, used if >0), the original DOL entry point at 0x18" + " (written), the address of the last defined '.dol' section at 0x1c" + " (written).\n" + " If the file name is '.dol', then it points not to a file. Instead a" + " copy of the DOL header beginning with the section addresses and" + " ending behind the entry point (156B total) is copied. MODE 'P' is" + " ignored for this section. All other MODE 'P' sections are updated at" + " offset 0x1c.\n" + " --cs is a short cut." + }, + + { OPT_WPF, false, true, false, false, false, 0, "wpf", + "file", + "Each option call adds the filename to an internal list. Each file" + " must be of type WPF (Wiimms Patch File) and defines a list of" + " conditionals and patches for staticr.rel and main.dol." + }, + + { OPT_SECTIONS, false, false, false, false, true, 0, "sections", + 0, + "Print in machine readable sections and parameter lines." + }, + + { OPT_VADDR, false, false, false, false, true, 0, "vaddr", + "RANGE", + "Comma separated list of virtual addresses to select the hex-dump" + " ranges. Each element is either ADDRESS (1 byte) or START:END or" + " START#SIZE." + }, + + { OPT_FADDR, false, false, false, false, false, 0, "faddr", + "RANGE", + "Comma separated list of file addresses to select the hex-dump ranges." + " Each element is either ADDRESS (1 byte) or START:END or START#SIZE." + }, + + { OPT_SNAME, false, false, false, false, false, 0, "sname", + "LIST", + "Comma separated list of section names to select the hex-dump ranges." + }, + + { OPT_INT1, false, false, false, false, true, 0, "i1", + 0, + "Format: Print single bytes as integer number (default)." + }, + + { OPT_INT2, false, false, false, false, false, 0, "i2", + 0, + "Format: Print each 2 bytes as integer number." + }, + + { OPT_INT3, false, false, false, false, false, 0, "i3", + 0, + "Format: Print each 3 bytes as integer number." + }, + + { OPT_INT4, false, false, false, false, false, 0, "i4", + 0, + "Format: Print each 4 bytes as integer number." + }, + + { OPT_INT5, false, false, false, false, false, 0, "i5", + 0, + "Format: Print each 5 bytes as integer number." + }, + + { OPT_INT6, false, false, false, false, false, 0, "i6", + 0, + "Format: Print each 6 bytes as integer number." + }, + + { OPT_INT7, false, false, false, false, false, 0, "i7", + 0, + "Format: Print each 7 bytes as integer number." + }, + + { OPT_INT8, false, false, false, false, false, 0, "i8", + 0, + "Format: Print each 8 bytes as integer number." + }, + + { OPT_FLOAT, false, false, false, false, false, 0, "float", + 0, + "Format: Print each 4 bytes as floating point number." + }, + + { OPT_DOUBLE, false, false, false, false, false, 0, "double", + 0, + "Format: Print each 8 bytes as double floating point number." + }, + + { OPT_LE, false, false, false, false, false, 0, "le", + 0, + "Use little endian for binary data." + }, + + { OPT_BE, false, false, false, false, false, 0, "be", + 0, + "Use big endian for binary data (default)." + }, + + { OPT_ZEROS, false, false, false, false, false, 0, "zeros", + 0, + "Print integer numbers with leading zeros." + }, + + { OPT_HEX, false, false, false, false, false, 0, "hex", + 0, + "Print integer numbers as hexadecimal numbers (default)." + }, + + { OPT_DEC, false, false, false, false, false, 0, "dec", + 0, + "Print integer numbers as decimal numbers." + }, + + { OPT_C_SYNTAX, false, false, false, false, false, 0, "c-syntax", + 0, + "Print output in `C' syntax." + }, + + { OPT_ADDR, false, false, false, false, false, 0, "address", + "addr", + "Use ADDR instead of null as start address for the dump." + }, + + { OPT_ALIGN, false, false, false, false, false, 0, "align", + 0, + "Try to align the dump, so that the addresses (printed or not) are" + " multiple of bytes-per-line." + }, + + { OPT_TRIGGER, false, false, false, false, false, 0, "trigger", + "num", + "If the number NUM is found and printed, close the current line and" + " start with a new one. If --align is set too, continue the next line" + " with the current column. The trigger works only for integer formats," + " but not for floats." + }, + + { OPT_NO_ADDR, false, false, false, false, false, 0, "no-address", + 0, + "Suppress the address column of the dump." + }, + + { OPT_NO_NUMBERS, false, false, false, false, false, 0, "no-numbers", + 0, + "Suppress the numbers column of the dump." + }, + + { OPT_NO_TEXT, false, false, false, false, false, 0, "no-text", + 0, + "Suppress the text column of the dump." + }, + + { OPT_FORMAT, false, false, false, false, false, 0, "format", + 0, + "Add '#FORMAT:' info lines." + }, + + { OPT_NO_NULL, false, false, false, false, false, 0, "no-null", + 0, + "Ignore data lines with NULL bytes only and print a summary for each" + " NULL block. ('#NULL: ...')" + }, + + { OPT_NARROW, false, false, false, false, false, 0, "narrow", + 0, + "Suppress the spaces that are usually inserted for readability." + }, + + { OPT_SMALL, false, false, false, false, false, 0, "small", + 0, + "Each call will halve the number of bytes per line. Usually, 16 bytes" + " per lines are reduced to 8, then to 4, 2 and 1. The effective number" + " of bytes per line is format dependent." + }, + + { OPT_WIDE, false, false, false, false, false, 0, "wide", + 0, + "Each call will round down the current bytes per line value to the" + " next multiple of 16 and then increment it by 16. Usually, 16 bytes" + " per lines are enhanced to 32, then to 48, 64, 80 and so on. The" + " effective number of bytes per line is format dependent." + }, + + { OPT_BYTES, false, false, false, false, false, 0, "bytes", + "num", + "Print up to NUM bytes per line instead of 16. The maximum allowed" + " value is 600. The effective number of bytes per line is format" + " dependent (multiple of format size)." + }, + + {0,0,0,0,0,0,0,0,0,0}, // OPT__N_SPECIFIC == 62 + + //----- global options ----- + + { OPT_VERSION, false, false, false, false, false, 'V', "version", + 0, + "Stop parsing the command line, print a version info and exit." + }, + + { OPT_HELP, false, false, false, false, false, 'h', "help", + 0, + "Stop parsing the command line, print a help message and exit." + }, + + { OPT_XHELP, false, false, false, false, false, 0, "xhelp", + 0, + "Stop parsing the command line and print a help message with all" + " commands included. Exit after printing." + }, + + { OPT_AT_DUMMY, false, true, false, false, false, '@', "@", + 0, + "" + }, + + { OPT_ALLOW_ALL, false, false, false, false, false, 0, "allow-all", + 0, + "Usually commands accept only options with impact to the command. All" + " other options fire a syntax error. But if --allow-all is set, all" + " commands accept all options.\n" + " This makes changing the command of a long command line without" + " removing useless options easier. It also helps to override wrong" + " option permissions." + }, + + { OPT_COMPATIBLE, false, false, false, false, false, 0, "compatible", + "vers", + "The option expects a version number (format '#.##' or 'v#.##') or a" + " revision number (format 'r#') as parameter. If set, the tools try to" + " create BMG and KMP text files, that are compatible to the entered" + " version of the tools. This may override other legacy options." + }, + + { OPT_WIDTH, false, false, false, true, false, 0, "width", + "width", + "Define the terminal width (number of columns) for help and some other" + " messages and disable the automatic detection of the terminal width." + }, + + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet and print only error messages. Multiple usage is possible." + " The impact is command dependent. In general: If set three times," + " different file format warnings are suppressed." + }, + + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Be verbose and print more progress information. Multiple usage is" + " possible. The impact is command dependent." + }, + + { OPT_LOGGING, false, false, false, false, false, 'L', "logging", + 0, + "This debug option enables the logging of internal lists and maps." + }, + + { OPT_DE, false, false, false, false, false, 0, "de", + 0, + "Use german track and arena names." + }, + + { OPT_COLORS, true, false, false, true, false, 0, "colors", + "[=modus]", + "Define the modus for colored text output. Allowed keywords are: OFF" + " or NO-COLORS to disable colors, AUTO (default) for automatic" + " detection, ON for automatic detection but never OFF, 8-COLORS and" + " 256-COLORS to force 8 or 256 color support. Without parameter, ON is" + " used.\n" + " AUTO will enable colorized output only for terminals. AUTO and ON" + " use environment variable TERM to find the correct color modus.\n" + " If a command is prefixed by 'C-', then --color=ON is used" + " implicitly as default." + }, + + { OPT_NO_COLORS, false, false, false, true, false, 0, "no-colors", + 0, + "Deactive colorized text. This is the default, if an output file is" + " not a terminal." + }, + + { OPT_CT_CODE, false, false, false, false, false, 0, "ct-code", + 0, + "Enable CT-CODE support. See https://szs.wiimm.de/opt/ctcode for" + " details." + }, + + { OPT_LE_CODE, true, false, false, false, false, 0, "le-code", + "[=param]", + "This option is completely ignored by wlect. It only exists due to" + " compatibility with the other tools." + }, + + { OPT_CHDIR, false, false, false, false, false, 0, "chdir", + "dir", + "Set a new working directory for all following options, for all" + " parameters and for the command execution. This option can be used" + " multiple times, but for parameter parsing and for command execution" + " only the last usage is relevant. Nevertheless, '@param' values are" + " scanned while reading the command line." + }, + + { OPT_CONST, false, false, false, false, false, 'c', "const", + "list", + "Define constant values, that are used by the internal encoders and by" + " the numeric options as predefined global variables. This option" + " allows a conditional encoding of text files. It can be used multiple" + " times for multiple definitions.\n" + " The parameter is a comma separated list of terms and a term is" + " 'name=expression'. The expression is calculated by the text parser." + }, + + { OPT_TRACKS, false, false, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, false, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_REGION, false, false, false, false, false, 'R', "region", + "region", + "If 'region' is an unsigned number, the region for online versus races" + " and for battles is patched to the entered number. If the number is" + " prefixed by the letter 't', a test mode is enabled and the places" + " are patched with ascending numbers. You can patch StaticR.rel and" + " main.dol.\n" + " Otherwise a keyword is expected. Official regions are: JAPAN(0)," + " AMERICA(1), EUROPE(2), AUSTRALIA(OCEANIA,3), KOREA(4), TAIWAN(5) and" + " CHINA(6). RESTORE restores the original Nintendo values and NONE" + " disables region patching (default).\n" + " See https://wiimmfi.de/reg-list for other regions." + }, + + { OPT_VS_REGION, false, false, false, false, false, 0, "vs-region", + "region", + "Same as --region, but only for online versus races." + }, + + { OPT_BT_REGION, false, false, false, false, false, 0, "bt-region", + "region", + "Same as --region, but only for online battles." + }, + + { OPT_ALL_RANKS, true, false, false, false, false, 0, "all-ranks", + "[=mode]", + "If set to ON (default without parameter), the server query to" + " Nintendos servers is manipulated, so that the player will find all" + " players in online races independent of their ranking points. Value" + " RESTORE disables this feature and restores Nintendos original query." + " Value NONE disables this kind of patching (default)." + }, + + { OPT_POINTS, false, false, false, false, false, 0, "points", + "list", + "This patch option defines a new table for the points assigned to" + " players after a versus or room race. The parameter is a blank or" + " comma and slash (next row) separated list with numbers (points)" + " between 0 and 255 and the following keywords: NINTENDO, LINEAR, WIN," + " WIN15, WIN25 to select a predefined table; NO-BONUS, BONUS, NULL," + " ONE as options; N1..N12 to select the row for N players. See" + " https://szs.wiimm.de/opt/points for details." + }, + + { OPT_HTTPS, false, false, false, false, false, 0, "https", + "mode", + "Patch the https strings of REL and DOL files. 'mode' is one of the" + " following keywords:\n" + "RESTORE restores the original URLs and domains.\n" + "HTTP replaces HTTPS requests by HTTP and sub-domain 'naswii.' by" + " 'nas.'.\n" + "DOMAIN replace HTTPS requests by HTTP and the domain" + " 'nintendowifi.net' by a domain specified by option --domain.\n" + "SAKE0 same as DOMAIN, but don't replace URLs with SAKE domain.\n" + "SAKE1 same as SAKE0, but replace HTTPS of SAKE domains." + }, + + { OPT_DOMAIN, false, false, false, false, false, 0, "domain", + "dom", + "Define a domain for option --https. The default is 'wiimmfi.de'." + }, + + { OPT_WIIMMFI, false, false, false, false, false, 0, "wiimmfi", + 0, + "Patch the files for the new custom server. It is a short cut for" + " '--https domain --domain wiimmfi.de'." + }, + + { OPT_TWIIMMFI, false, true, false, false, false, 0, "twiimmfi", + 0, + "Patch the files for the test server. It is a short cut for '--https" + " domain --domain test.wiimmfi.de'." + }, + + { OPT_WC24, false, false, false, false, false, 0, "wc24", + 0, + "Exclude 2 strings of file StaticR.rel from --wiimmfi and --domain" + " patch to enable WC24 support." + }, + + { OPT_WCODE, true, true, false, false, false, 0, "wcode", + "[=mode]", + "Enable (mode=ON) or disable (mode=OFF) wiimmfi-code patching. Default" + " mode is AUTO. In this case, patching is enabled if any WCODE is" + " defined or option --domain is set either to 'wiimmfi.de' or to" + " 'test.wiimmfi.de'." + }, + + { OPT_PB_MODE, false, false, false, false, false, 0, "pb-mode", + "mode", + "Mode for the patched-by string: OFF disables modifications. RESET" + " resets the string to stars. WIIMMFI defines it only, if patched for" + " Wiimmfi. AUTO (default) defines it only, if patched. ALWAYS defines" + " it always." + }, + + { OPT_PATCHED_BY, false, true, false, false, false, 0, "@patched-by", + "string", + "Define a string for patched-by. Default is: wstrt vVERSION rREVISION" + }, + + { OPT_VS, false, false, false, false, false, 0, "vs", + "char", + "Reset (str is empty) or replace the second character of 'vs' to" + " create an own online world for versus races. The file StaticR.rel" + " will be patched at 4 places." + }, + + { OPT_VS2, false, true, false, false, false, 0, "vs2", + "str", + "Reset (str is empty) or replace the 2 characters of 'vs' to create an" + " own online world for versus races. If the parameter contains only 1" + " character, use 'v' as first character. The file StaticR.rel will be" + " patched at 4 places.\n" + " Wiimmfi will only detect 'Versus' if the first letter is 'v' or" + " 'V'." + }, + + { OPT_BT, false, false, false, false, false, 0, "bt", + "char", + "Reset (str is empty) or replace the second character of 'bt' to" + " create an own online world for battles. The file StaticR.rel will be" + " patched at 4 places." + }, + + { OPT_BT2, false, true, false, false, false, 0, "bt2", + "str", + "Reset (str is empty) or replace the 2 characters of 'bt' to create an" + " own online world for battles. If the parameter contains only 1" + " character, use 'b' as first character. The file StaticR.rel will be" + " patched at 4 places.\n" + " Wiimmfi will only detect 'Battle' if the first letter is 'b' or" + " 'B'." + }, + + { OPT_CANNON, false, false, false, false, false, 0, "cannon", + "idx,p1,p2,p3,p4", + "Patch parameters for cannon with index IDX (0..2). Parameters P1..P4" + " are floating point numbers. Repeat this option for each cannon, that" + " should be modified." + }, + + { OPT_MENO, false, false, false, false, false, 0, "meno", + 0, + "Patch some 'MenuSingle' and 'MenuMulti' strings of file StaticR.rel" + " for online usage to 'MenO*' (MenuOnline) to distinguish between" + " offline and online races." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + 0, + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + { OPT_CLEAN_DOL, false, false, false, false, false, 0, "clean-dol", + 0, + "Remove additional DOL sections, reset entry point and VBI address," + " and try to restore other patches known by SZS tools. This is done" + " after loading a DOL and before analysing and patching to allow" + " re-patching." + }, + + { OPT_CT_DIR, false, false, false, false, false, 0, "ct-dir", + "dir", + "Define a search directory for CT-CODE parts and use the internal" + " copies only, if no valid file is found. Each file is searched" + " without and then with extension '.bz2'. For both cases bzip2 files" + " are detected and decompressed. Only files with correct file type are" + " accepted.\n" + " Use this option multiple times to define more than one search" + " directory." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 111 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_DUMP_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "For DOL files: If set, a second table with the image map is printed." + " If set twice, a third table with delta values is printed too." + }; + +static const InfoOption_t option_cmd_HTTPS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + " Print output a C source" + }; + +static const InfoOption_t option_cmd_HTTPS_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + " Print hex dump if an entry differs." + }; + +static const InfoOption_t option_cmd_TRACKS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Add columns for music slot and short name." + }; + +static const InfoOption_t option_cmd_FIND_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Add a user friendly track or arena name at the end of each line. If" + " set twice, add also the file names of the track and sound files" + " before the track name. If set three times, add the slot index and" + " the internal index (both zero based) before the file names." + }; + +static const InfoOption_t option_cmd_POINTS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print the table as comma separated lists in C notation." + }; + +static const InfoOption_t option_cmd_POINTS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Append a string in --points format that defines the table. If set" + " twice, append up to 3 strings in different modes." + }; + +static const InfoOption_t option_cmd_ANALYZE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print different lists if bugs found. If set twice, print the lists" + " always." + }; + +static const InfoOption_t option_cmd_PATCH_FORCE = + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "Accept overlapped sections.\n" + ">>> THE RESULT IS MOST LIKELY INVALID! <<<" + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_RAWDUMP, "_RAWDUMP", 0, 0 }, + { CMD_DUMP, "DUMP", 0, 0 }, + { CMD_CDUMP, "_CDUMP", 0, 0 }, + { CMD_HEXDUMP, "HEXDUMP", "XDUMP", 0 }, + { CMD_HTTPS, "_HTTPS", 0, 0 }, + { CMD_EXTRACT, "EXTRACT", "X", 0 }, + { CMD_TRACKS, "TRACKS", 0, 0 }, + { CMD_ARENAS, "ARENAS", 0, 0 }, + { CMD_FILES, "FILES", 0, 0 }, + { CMD_FIND, "FIND", 0, 0 }, + { CMD_POINTS, "POINTS", "PTS", 0 }, + { CMD_ANALYZE, "ANALYZE", "ANA", 0 }, + { CMD_ANALYZE, "ANALYSE", 0, 0 }, + { CMD_PATCH, "PATCH", 0, 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "Vh@qvLc:lBHT:A:R:td:D:E:orupi"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "@", 0, 0, '@' }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "long", 0, 0, 'l' }, + { "brief", 0, 0, 'B' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "region", 1, 0, 'R' }, + { "vs-region", 1, 0, GO_VS_REGION }, + { "vsregion", 1, 0, GO_VS_REGION }, + { "vregion", 1, 0, GO_VS_REGION }, + { "bt-region", 1, 0, GO_BT_REGION }, + { "btregion", 1, 0, GO_BT_REGION }, + { "bregion", 1, 0, GO_BT_REGION }, + { "all-ranks", 2, 0, GO_ALL_RANKS }, + { "allranks", 2, 0, GO_ALL_RANKS }, + { "points", 1, 0, GO_POINTS }, + { "pts", 1, 0, GO_POINTS }, + { "cheat", 1, 0, GO_CHEAT }, + { "https", 1, 0, GO_HTTPS }, + { "domain", 1, 0, GO_DOMAIN }, + { "wiimmfi", 0, 0, GO_WIIMMFI }, + { "twiimmfi", 0, 0, GO_TWIIMMFI }, + { "wc24", 0, 0, GO_WC24 }, + { "wcode", 2, 0, GO_WCODE }, + { "@add-wcode", 1, 0, GO_ADD_WCODE }, + { "@addwcode", 1, 0, GO_ADD_WCODE }, + { "pb-mode", 1, 0, GO_PB_MODE }, + { "pbmode", 1, 0, GO_PB_MODE }, + { "@patched-by", 1, 0, GO_PATCHED_BY }, + { "@patchedby", 1, 0, GO_PATCHED_BY }, + { "vs", 1, 0, GO_VS }, + { "vs2", 1, 0, GO_VS2 }, + { "bt", 1, 0, GO_BT }, + { "bt2", 1, 0, GO_BT2 }, + { "cannon", 1, 0, GO_CANNON }, + { "meno", 0, 0, GO_MENO }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 0, 0, GO_EXTRACT }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "clean-dol", 0, 0, GO_CLEAN_DOL }, + { "cleandol", 0, 0, GO_CLEAN_DOL }, + { "add-lecode", 0, 0, GO_ADD_LECODE }, + { "addlecode", 0, 0, GO_ADD_LECODE }, + { "add-ctcode", 0, 0, GO_ADD_CTCODE }, + { "addctcode", 0, 0, GO_ADD_CTCODE }, + { "ct-dir", 1, 0, GO_CT_DIR }, + { "ctdir", 1, 0, GO_CT_DIR }, + { "move-d8", 0, 0, GO_MOVE_D8 }, + { "moved8", 0, 0, GO_MOVE_D8 }, + { "add-section", 1, 0, GO_ADD_SECTION }, + { "addsection", 1, 0, GO_ADD_SECTION }, + { "full-gch", 0, 0, GO_FULL_GCH }, + { "fullgch", 0, 0, GO_FULL_GCH }, + { "gct-no-sep", 0, 0, GO_GCT_NO_SEP }, + { "gctnosep", 0, 0, GO_GCT_NO_SEP }, + { "gct-sep", 0, 0, GO_GCT_SEP }, + { "gctsep", 0, 0, GO_GCT_SEP }, + { "gct-asm-sep", 0, 0, GO_GCT_ASM_SEP }, + { "gctasmsep", 0, 0, GO_GCT_ASM_SEP }, + { "gct-list", 0, 0, GO_GCT_LIST }, + { "gctlist", 0, 0, GO_GCT_LIST }, + { "gct-move", 2, 0, GO_GCT_MOVE }, + { "gctmove", 2, 0, GO_GCT_MOVE }, + { "gct-addr", 1, 0, GO_GCT_ADDR }, + { "gctaddr", 1, 0, GO_GCT_ADDR }, + { "gct-space", 1, 0, GO_GCT_SPACE }, + { "gctspace", 1, 0, GO_GCT_SPACE }, + { "allow-user-gch", 2, 0, GO_ALLOW_USER_GCH }, + { "allowusergch", 2, 0, GO_ALLOW_USER_GCH }, + { "create-sect", 1, 0, GO_CREATE_SECT }, + { "createsect", 1, 0, GO_CREATE_SECT }, + { "cs", 1, 0, GO_CREATE_SECT }, + { "wpf", 1, 0, GO_WPF }, + { "sections", 0, 0, GO_SECTIONS }, + { "vaddr", 1, 0, GO_VADDR }, + { "va", 1, 0, GO_VADDR }, + { "faddr", 1, 0, GO_FADDR }, + { "fa", 1, 0, GO_FADDR }, + { "sname", 1, 0, GO_SNAME }, + { "sn", 1, 0, GO_SNAME }, + { "i1", 0, 0, GO_INT1 }, + { "i2", 0, 0, GO_INT2 }, + { "i3", 0, 0, GO_INT3 }, + { "i4", 0, 0, GO_INT4 }, + { "i5", 0, 0, GO_INT5 }, + { "i6", 0, 0, GO_INT6 }, + { "i7", 0, 0, GO_INT7 }, + { "i8", 0, 0, GO_INT8 }, + { "float", 0, 0, GO_FLOAT }, + { "double", 0, 0, GO_DOUBLE }, + { "le", 0, 0, GO_LE }, + { "be", 0, 0, GO_BE }, + { "zeros", 0, 0, GO_ZEROS }, + { "hex", 0, 0, GO_HEX }, + { "dec", 0, 0, GO_DEC }, + { "c-syntax", 0, 0, GO_C_SYNTAX }, + { "csyntax", 0, 0, GO_C_SYNTAX }, + { "address", 1, 0, GO_ADDR }, + { "align", 0, 0, GO_ALIGN }, + { "trigger", 1, 0, GO_TRIGGER }, + { "no-address", 0, 0, GO_NO_ADDR }, + { "noaddress", 0, 0, GO_NO_ADDR }, + { "no-numbers", 0, 0, GO_NO_NUMBERS }, + { "nonumbers", 0, 0, GO_NO_NUMBERS }, + { "no-text", 0, 0, GO_NO_TEXT }, + { "notext", 0, 0, GO_NO_TEXT }, + { "format", 0, 0, GO_FORMAT }, + { "no-null", 0, 0, GO_NO_NULL }, + { "nonull", 0, 0, GO_NO_NULL }, + { "narrow", 0, 0, GO_NARROW }, + { "small", 0, 0, GO_SMALL }, + { "wide", 0, 0, GO_WIDE }, + { "bytes", 1, 0, GO_BYTES }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x040 @ */ OPT_AT_DUMMY, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 */ 0, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d */ 0,0,0,0, 0, + /* 0x052 R */ OPT_REGION, + /* 0x053 */ 0, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 */ 0,0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0,0, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 */ 0, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0,0,0,0, 0,0,0,0, 0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_VS_REGION, + /* 0x08b */ OPT_BT_REGION, + /* 0x08c */ OPT_ALL_RANKS, + /* 0x08d */ OPT_POINTS, + /* 0x08e */ OPT_CHEAT, + /* 0x08f */ OPT_HTTPS, + /* 0x090 */ OPT_DOMAIN, + /* 0x091 */ OPT_WIIMMFI, + /* 0x092 */ OPT_TWIIMMFI, + /* 0x093 */ OPT_WC24, + /* 0x094 */ OPT_WCODE, + /* 0x095 */ OPT_ADD_WCODE, + /* 0x096 */ OPT_PB_MODE, + /* 0x097 */ OPT_PATCHED_BY, + /* 0x098 */ OPT_VS, + /* 0x099 */ OPT_VS2, + /* 0x09a */ OPT_BT, + /* 0x09b */ OPT_BT2, + /* 0x09c */ OPT_CANNON, + /* 0x09d */ OPT_MENO, + /* 0x09e */ OPT_UTF_8, + /* 0x09f */ OPT_NO_UTF_8, + /* 0x0a0 */ OPT_FORCE, + /* 0x0a1 */ OPT_REPAIR_MAGICS, + /* 0x0a2 */ OPT_OLD, + /* 0x0a3 */ OPT_STD, + /* 0x0a4 */ OPT_NEW, + /* 0x0a5 */ OPT_EXTRACT, + /* 0x0a6 */ OPT_NUMBER, + /* 0x0a7 */ OPT_CLEAN_DOL, + /* 0x0a8 */ OPT_ADD_LECODE, + /* 0x0a9 */ OPT_ADD_CTCODE, + /* 0x0aa */ OPT_CT_DIR, + /* 0x0ab */ OPT_MOVE_D8, + /* 0x0ac */ OPT_ADD_SECTION, + /* 0x0ad */ OPT_FULL_GCH, + /* 0x0ae */ OPT_GCT_NO_SEP, + /* 0x0af */ OPT_GCT_SEP, + /* 0x0b0 */ OPT_GCT_ASM_SEP, + /* 0x0b1 */ OPT_GCT_LIST, + /* 0x0b2 */ OPT_GCT_MOVE, + /* 0x0b3 */ OPT_GCT_ADDR, + /* 0x0b4 */ OPT_GCT_SPACE, + /* 0x0b5 */ OPT_ALLOW_USER_GCH, + /* 0x0b6 */ OPT_CREATE_SECT, + /* 0x0b7 */ OPT_WPF, + /* 0x0b8 */ OPT_SECTIONS, + /* 0x0b9 */ OPT_VADDR, + /* 0x0ba */ OPT_FADDR, + /* 0x0bb */ OPT_SNAME, + /* 0x0bc */ OPT_INT1, + /* 0x0bd */ OPT_INT2, + /* 0x0be */ OPT_INT3, + /* 0x0bf */ OPT_INT4, + /* 0x0c0 */ OPT_INT5, + /* 0x0c1 */ OPT_INT6, + /* 0x0c2 */ OPT_INT7, + /* 0x0c3 */ OPT_INT8, + /* 0x0c4 */ OPT_FLOAT, + /* 0x0c5 */ OPT_DOUBLE, + /* 0x0c6 */ OPT_LE, + /* 0x0c7 */ OPT_BE, + /* 0x0c8 */ OPT_ZEROS, + /* 0x0c9 */ OPT_HEX, + /* 0x0ca */ OPT_DEC, + /* 0x0cb */ OPT_C_SYNTAX, + /* 0x0cc */ OPT_ADDR, + /* 0x0cd */ OPT_ALIGN, + /* 0x0ce */ OPT_TRIGGER, + /* 0x0cf */ OPT_NO_ADDR, + /* 0x0d0 */ OPT_NO_NUMBERS, + /* 0x0d1 */ OPT_NO_TEXT, + /* 0x0d2 */ OPT_FORMAT, + /* 0x0d3 */ OPT_NO_NULL, + /* 0x0d4 */ OPT_NARROW, + /* 0x0d5 */ OPT_SMALL, + /* 0x0d6 */ OPT_WIDE, + /* 0x0d7 */ OPT_BYTES, + /* 0x0d8 */ 0,0,0,0, 0,0,0,0, + /* 0x0e0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x0f0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x100 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x110 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x120 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[62] = // cmd #1 +{ + 0,1,1,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, + 1,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 u8 option_allowed_cmd_HELP[62] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1 +}; + +static u8 option_allowed_cmd_ARGTEST[62] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1 +}; + +static u8 option_allowed_cmd_TEST[62] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1 +}; + +static u8 option_allowed_cmd_COLORS[62] = // cmd #5 +{ + 0,1,1,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 u8 option_allowed_cmd_ERROR[62] = // cmd #6 +{ + 0,1,1,1,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, + 1,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 u8 option_allowed_cmd_FILETYPE[62] = // cmd #7 +{ + 0,1,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 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 u8 option_allowed_cmd_FILEATTRIB[62] = // cmd #8 +{ + 0,0,0,1,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 u8 option_allowed_cmd_RAWDUMP[62] = // cmd #9 +{ + 0,0,0,0,0, 0,1,1,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 u8 option_allowed_cmd_DUMP[62] = // cmd #10 +{ + 0,1,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 u8 option_allowed_cmd_CDUMP[62] = // cmd #11 +{ + 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, + 0,0 +}; + +static u8 option_allowed_cmd_HEXDUMP[62] = // cmd #12 +{ + 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,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1 +}; + +static u8 option_allowed_cmd_HTTPS[62] = // cmd #13 +{ + 0,1,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 u8 option_allowed_cmd_EXTRACT[62] = // cmd #14 +{ + 0,0,0,0,0, 0,1,1,0,1, 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 u8 option_allowed_cmd_TRACKS[62] = // cmd #15 +{ + 0,1,1,1,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 u8 option_allowed_cmd_ARENAS[62] = // cmd #16 +{ + 0,1,1,1,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 u8 option_allowed_cmd_FILES[62] = // cmd #17 +{ + 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, + 0,0 +}; + +static u8 option_allowed_cmd_FIND[62] = // cmd #18 +{ + 0,1,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 u8 option_allowed_cmd_POINTS[62] = // cmd #19 +{ + 0,1,1,0,1, 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 u8 option_allowed_cmd_ANALYZE[62] = // cmd #20 +{ + 0,1,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 u8 option_allowed_cmd_PATCH[62] = // cmd #21 +{ + 0,0,0,0,0, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 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 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_REGION, + OptionInfo + OPT_VS_REGION, + OptionInfo + OPT_BT_REGION, + OptionInfo + OPT_ALL_RANKS, + OptionInfo + OPT_POINTS, + OptionInfo + OPT_HTTPS, + OptionInfo + OPT_DOMAIN, + OptionInfo + OPT_WIIMMFI, + OptionInfo + OPT_WC24, + OptionInfo + OPT_PB_MODE, + OptionInfo + OPT_VS, + OptionInfo + OPT_BT, + OptionInfo + OPT_CANNON, + OptionInfo + OPT_MENO, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CLEAN_DOL, + OptionInfo + OPT_CT_DIR, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_RAWDUMP[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DUMP[] = +{ + &option_cmd_DUMP_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CDUMP[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HEXDUMP[] = +{ + OptionInfo + OPT_VADDR, + OptionInfo + OPT_FADDR, + OptionInfo + OPT_SNAME, + OptionInfo + OPT_LOGGING, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_INT1, + OptionInfo + OPT_INT2, + OptionInfo + OPT_INT3, + OptionInfo + OPT_INT4, + OptionInfo + OPT_INT5, + OptionInfo + OPT_INT6, + OptionInfo + OPT_INT7, + OptionInfo + OPT_INT8, + OptionInfo + OPT_FLOAT, + OptionInfo + OPT_DOUBLE, + OptionInfo + OPT_LE, + OptionInfo + OPT_BE, + OptionInfo + OPT_ZEROS, + OptionInfo + OPT_HEX, + OptionInfo + OPT_DEC, + OptionInfo + OPT_C_SYNTAX, + OptionInfo + OPT_ADDR, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_TRIGGER, + OptionInfo + OPT_NO_ADDR, + OptionInfo + OPT_NO_NUMBERS, + OptionInfo + OPT_NO_TEXT, + OptionInfo + OPT_FORMAT, + OptionInfo + OPT_NO_NULL, + OptionInfo + OPT_NARROW, + OptionInfo + OPT_SMALL, + OptionInfo + OPT_WIDE, + OptionInfo + OPT_BYTES, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HTTPS[] = +{ + &option_cmd_HTTPS_LONG, + &option_cmd_HTTPS_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXTRACT[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_OVERWRITE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TRACKS[] = +{ + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + &option_cmd_TRACKS_LONG, + OptionInfo + OPT_DE, + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_REGION, + OptionInfo + OPT_VS_REGION, + OptionInfo + OPT_BT_REGION, + OptionInfo + OPT_ALL_RANKS, + OptionInfo + OPT_HTTPS, + OptionInfo + OPT_DOMAIN, + OptionInfo + OPT_WIIMMFI, + OptionInfo + OPT_PB_MODE, + OptionInfo + OPT_WC24, + OptionInfo + OPT_VS, + OptionInfo + OPT_BT, + OptionInfo + OPT_CANNON, + OptionInfo + OPT_MENO, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARENAS[] = +{ + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_BRIEF, + &option_cmd_TRACKS_LONG, + OptionInfo + OPT_DE, + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_REGION, + OptionInfo + OPT_VS_REGION, + OptionInfo + OPT_BT_REGION, + OptionInfo + OPT_ALL_RANKS, + OptionInfo + OPT_HTTPS, + OptionInfo + OPT_DOMAIN, + OptionInfo + OPT_WIIMMFI, + OptionInfo + OPT_PB_MODE, + OptionInfo + OPT_WC24, + OptionInfo + OPT_VS, + OptionInfo + OPT_BT, + OptionInfo + OPT_CANNON, + OptionInfo + OPT_MENO, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILES[] = +{ + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_REGION, + OptionInfo + OPT_VS_REGION, + OptionInfo + OPT_BT_REGION, + OptionInfo + OPT_ALL_RANKS, + OptionInfo + OPT_HTTPS, + OptionInfo + OPT_DOMAIN, + OptionInfo + OPT_WIIMMFI, + OptionInfo + OPT_PB_MODE, + OptionInfo + OPT_WC24, + OptionInfo + OPT_VS, + OptionInfo + OPT_BT, + OptionInfo + OPT_CANNON, + OptionInfo + OPT_MENO, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FIND[] = +{ + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_REGION, + OptionInfo + OPT_VS_REGION, + OptionInfo + OPT_BT_REGION, + OptionInfo + OPT_ALL_RANKS, + OptionInfo + OPT_HTTPS, + OptionInfo + OPT_DOMAIN, + OptionInfo + OPT_WIIMMFI, + OptionInfo + OPT_PB_MODE, + OptionInfo + OPT_WC24, + OptionInfo + OPT_VS, + OptionInfo + OPT_BT, + OptionInfo + OPT_CANNON, + OptionInfo + OPT_MENO, + &option_cmd_FIND_LONG, + OptionInfo + OPT_DE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_POINTS[] = +{ + OptionInfo + OPT_POINTS, + &option_cmd_POINTS_BRIEF, + &option_cmd_POINTS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ANALYZE[] = +{ + OptionInfo + OPT_CLEAN_DOL, + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_REGION, + OptionInfo + OPT_VS_REGION, + OptionInfo + OPT_BT_REGION, + OptionInfo + OPT_ALL_RANKS, + OptionInfo + OPT_HTTPS, + OptionInfo + OPT_DOMAIN, + OptionInfo + OPT_WIIMMFI, + OptionInfo + OPT_PB_MODE, + OptionInfo + OPT_WC24, + OptionInfo + OPT_VS, + OptionInfo + OPT_BT, + OptionInfo + OPT_CANNON, + OptionInfo + OPT_MENO, + &option_cmd_ANALYZE_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_PATCH[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CLEAN_DOL, + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_REGION, + OptionInfo + OPT_VS_REGION, + OptionInfo + OPT_BT_REGION, + OptionInfo + OPT_ALL_RANKS, + OptionInfo + OPT_HTTPS, + OptionInfo + OPT_DOMAIN, + OptionInfo + OPT_WIIMMFI, + OptionInfo + OPT_PB_MODE, + OptionInfo + OPT_WC24, + OptionInfo + OPT_VS, + OptionInfo + OPT_BT, + OptionInfo + OPT_CANNON, + OptionInfo + OPT_MENO, + OptionInfo + OPT_ADD_CTCODE, + OptionInfo + OPT_CT_DIR, + OptionInfo + OPT_MOVE_D8, + OptionInfo + OPT_ADD_SECTION, + OptionInfo + OPT_GCT_MOVE, + OptionInfo + OPT_GCT_ADDR, + OptionInfo + OPT_GCT_SPACE, + OptionInfo + OPT_ALLOW_USER_GCH, + &option_cmd_PATCH_FORCE, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wstrt", + 0, + "wstrt [option]... command [option|parameter|file]...", + "Wiimms StaticR Tool : Manipulate the 'main.dol' and 'StaticR.rel'" + " files of Mario Kart Wii.", + 0, + 37, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wstrt VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wstrt HELP [--width=wd] OPT|OPTION ...\n" + "wstrt HELP [--width=wd] CMD|COMMAND [ignored]...\n" + "wstrt HELP [--width=wd] command [-b] [ignored]...\n" + "wstrt HELP [--width=wd] [ignored]...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wstrt ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wstrt TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 0, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wstrt COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wstrt ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wstrt FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 2, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wstrt FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_RAWDUMP, + true, + false, + false, + "_RAWDUMP", + 0, + "wszst _RAWDUMP [-d|-D] destdir", + "This hidden command creates the destination directory and create" + " several files as copy of internal data.", + 0, + 2, + option_tab_cmd_RAWDUMP, + option_allowed_cmd_RAWDUMP + }, + + { CMD_DUMP, + false, + false, + true, + "DUMP", + 0, + "wstrt DUMP file...", + "Dump the structure of 'StaticR.rel' (in development) and of DOL files" + " as text.", + 0, + 1, + option_tab_cmd_DUMP, + option_allowed_cmd_DUMP + }, + + { CMD_CDUMP, + true, + false, + false, + "_CDUMP", + 0, + "wstrt _CDUMP file...", + "Dump the structure of 'StaticR.rel' (in development) and of DOL files" + " as C source fragments.", + 0, + 0, + option_tab_cmd_CDUMP, + option_allowed_cmd_CDUMP + }, + + { CMD_HEXDUMP, + false, + false, + false, + "HEXDUMP", + "XDUMP", + "wstrt HEXDUMP file...", + "Dump the content of DOL files as hex dump and use virtual addresses." + " Select parts by --vaddr, --faddr and --sname. Without selectors, the" + " whole file is dumped.", + 0, + 32, + option_tab_cmd_HEXDUMP, + option_allowed_cmd_HEXDUMP + }, + + { CMD_HTTPS, + true, + false, + false, + "_HTTPS", + 0, + "wstrt _HTTPS file...", + "Collect data about https-requests and print a total summary for all" + " accepted files.", + 0, + 2, + option_tab_cmd_HTTPS, + option_allowed_cmd_HTTPS + }, + + { CMD_EXTRACT, + false, + false, + false, + "EXTRACT", + "X", + "wstrt EXTRACT dol...", + "Extract the sections of a DOL file into a directory. StaticR.rel" + " files are ignored.", + 0, + 3, + option_tab_cmd_EXTRACT, + option_allowed_cmd_EXTRACT + }, + + { CMD_TRACKS, + false, + false, + true, + "TRACKS", + 0, + "wstrt TRACKS [file]...", + "Show track infos in the order of the 'StaticR.rel' files. If no file" + " is entered, the standard order is used.", + 0, + 19, + option_tab_cmd_TRACKS, + option_allowed_cmd_TRACKS + }, + + { CMD_ARENAS, + false, + false, + false, + "ARENAS", + 0, + "wstrt ARENAS [file]...", + "Show arena infos in the order of the 'StaticR.rel' files. If no file" + " is entered, the standard order is used.", + 0, + 19, + option_tab_cmd_ARENAS, + option_allowed_cmd_ARENAS + }, + + { CMD_FILES, + false, + false, + false, + "FILES", + 0, + "wstrt FILES [file]...", + "List track and arena file names in machine readable format. Use the" + " order of the entered 'StaticR.rel' files. If no file is entered, the" + " standard order is used.", + 0, + 15, + option_tab_cmd_FILES, + option_allowed_cmd_FILES + }, + + { CMD_FIND, + false, + false, + false, + "FIND", + 0, + "wstrt FIND [+]TRACK keyword...\n" + "wstrt FIND [+]ARENA keyword...", + "Find a track or arena by keyword and print one line for each keyword" + " in machine readable format 'type slot' or, if not found, a simple" + " '-'. The name is searched in the entered context (tracks or arenas)." + " If TRACK or ARENA is prefixed by a plus, then a failed search will" + " be continued in the other context. If at least one name is not" + " found, the tool terminates with exit code NOT_EXISTS.", + 0, + 17, + option_tab_cmd_FIND, + option_allowed_cmd_FIND + }, + + { CMD_POINTS, + false, + false, + false, + "POINTS", + "PTS", + "wstrt POINTS [--points=list] [file]...", + "Print a table with the points assigned to player after a versus or" + " room race for each entered file. If no file is entered, the standard" + " table is used. However, if option --points is set only the resulting" + " table is printed.", + 0, + 3, + option_tab_cmd_POINTS, + option_allowed_cmd_POINTS + }, + + { CMD_ANALYZE, + false, + false, + true, + "ANALYZE", + "ANA", + "wstrt ANALYZE [file]...", + "Open 'StaticR.rel' and DOL files and analyze them. The files will be" + " classified using SHA1 checksums.", + 0, + 17, + option_tab_cmd_ANALYZE, + option_allowed_cmd_ANALYZE + }, + + { CMD_PATCH, + false, + false, + false, + "PATCH", + 0, + "wstrt PATCH [file]...", + "Patch 'main.dol and 'StaticR.rel' files of Mario Kart Wii. No backup" + " is made.", + 0, + 34, + option_tab_cmd_PATCH, + option_allowed_cmd_PATCH + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wstrt = +{ + "wstrt", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wstrt.h b/project/src/ui/ui-wstrt.h new file mode 100644 index 0000000..e1a2f2b --- /dev/null +++ b/project/src/ui/ui-wstrt.h @@ -0,0 +1,560 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WSTRT_H +#define SZS_UI_WSTRT_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_LONG, + OPT_BRIEF, + OPT_NO_HEADER, + OPT_CHEAT, + OPT_ADD_WCODE, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_ADD_LECODE, + OPT_ADD_CTCODE, + OPT_MOVE_D8, + OPT_ADD_SECTION, + OPT_FULL_GCH, + OPT_GCT_NO_SEP, + OPT_GCT_SEP, + OPT_GCT_ASM_SEP, + OPT_GCT_LIST, + OPT_GCT_MOVE, + OPT_GCT_ADDR, + OPT_GCT_SPACE, + OPT_ALLOW_USER_GCH, + OPT_CREATE_SECT, + OPT_WPF, + OPT_SECTIONS, + OPT_VADDR, + OPT_FADDR, + OPT_SNAME, + OPT_INT1, + OPT_INT2, + OPT_INT3, + OPT_INT4, + OPT_INT5, + OPT_INT6, + OPT_INT7, + OPT_INT8, + OPT_FLOAT, + OPT_DOUBLE, + OPT_LE, + OPT_BE, + OPT_ZEROS, + OPT_HEX, + OPT_DEC, + OPT_C_SYNTAX, + OPT_ADDR, + OPT_ALIGN, + OPT_TRIGGER, + OPT_NO_ADDR, + OPT_NO_NUMBERS, + OPT_NO_TEXT, + OPT_FORMAT, + OPT_NO_NULL, + OPT_NARROW, + OPT_SMALL, + OPT_WIDE, + OPT_BYTES, + + OPT__N_SPECIFIC, // == 62 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_AT_DUMMY, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_TRACKS, + OPT_ARENAS, + OPT_REGION, + OPT_VS_REGION, + OPT_BT_REGION, + OPT_ALL_RANKS, + OPT_POINTS, + OPT_HTTPS, + OPT_DOMAIN, + OPT_WIIMMFI, + OPT_TWIIMMFI, + OPT_WC24, + OPT_WCODE, + OPT_PB_MODE, + OPT_PATCHED_BY, + OPT_VS, + OPT_VS2, + OPT_BT, + OPT_BT2, + OPT_CANNON, + OPT_MENO, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + OPT_CLEAN_DOL, + OPT_CT_DIR, + + OPT__N_TOTAL // == 111 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_LONG = 1llu << OPT_LONG, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_CHEAT = 1llu << OPT_CHEAT, +// OB_ADD_WCODE = 1llu << OPT_ADD_WCODE, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_ADD_LECODE = 1llu << OPT_ADD_LECODE, +// OB_ADD_CTCODE = 1llu << OPT_ADD_CTCODE, +// OB_MOVE_D8 = 1llu << OPT_MOVE_D8, +// OB_ADD_SECTION = 1llu << OPT_ADD_SECTION, +// OB_FULL_GCH = 1llu << OPT_FULL_GCH, +// OB_GCT_NO_SEP = 1llu << OPT_GCT_NO_SEP, +// OB_GCT_SEP = 1llu << OPT_GCT_SEP, +// OB_GCT_ASM_SEP = 1llu << OPT_GCT_ASM_SEP, +// OB_GCT_LIST = 1llu << OPT_GCT_LIST, +// OB_GCT_MOVE = 1llu << OPT_GCT_MOVE, +// OB_GCT_ADDR = 1llu << OPT_GCT_ADDR, +// OB_GCT_SPACE = 1llu << OPT_GCT_SPACE, +// OB_ALLOW_USER_GCH = 1llu << OPT_ALLOW_USER_GCH, +// OB_CREATE_SECT = 1llu << OPT_CREATE_SECT, +// OB_WPF = 1llu << OPT_WPF, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// OB_VADDR = 1llu << OPT_VADDR, +// OB_FADDR = 1llu << OPT_FADDR, +// OB_SNAME = 1llu << OPT_SNAME, +// OB_INT1 = 1llu << OPT_INT1, +// OB_INT2 = 1llu << OPT_INT2, +// OB_INT3 = 1llu << OPT_INT3, +// OB_INT4 = 1llu << OPT_INT4, +// OB_INT5 = 1llu << OPT_INT5, +// OB_INT6 = 1llu << OPT_INT6, +// OB_INT7 = 1llu << OPT_INT7, +// OB_INT8 = 1llu << OPT_INT8, +// OB_FLOAT = 1llu << OPT_FLOAT, +// OB_DOUBLE = 1llu << OPT_DOUBLE, +// OB_LE = 1llu << OPT_LE, +// OB_BE = 1llu << OPT_BE, +// OB_ZEROS = 1llu << OPT_ZEROS, +// OB_HEX = 1llu << OPT_HEX, +// OB_DEC = 1llu << OPT_DEC, +// OB_C_SYNTAX = 1llu << OPT_C_SYNTAX, +// OB_ADDR = 1llu << OPT_ADDR, +// OB_ALIGN = 1llu << OPT_ALIGN, +// OB_TRIGGER = 1llu << OPT_TRIGGER, +// OB_NO_ADDR = 1llu << OPT_NO_ADDR, +// OB_NO_NUMBERS = 1llu << OPT_NO_NUMBERS, +// OB_NO_TEXT = 1llu << OPT_NO_TEXT, +// OB_FORMAT = 1llu << OPT_FORMAT, +// OB_NO_NULL = 1llu << OPT_NO_NULL, +// OB_NARROW = 1llu << OPT_NARROW, +// OB_SMALL = 1llu << OPT_SMALL, +// OB_WIDE = 1llu << OPT_WIDE, +// OB_BYTES = 1llu << OPT_BYTES, +// +// //----- group & command options ----- +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST +// | OB_UPDATE +// | OB_PRESERVE, +// +// OB_GRP_PATCH = 0, +// +// OB_GRP_HEXDUMP = OB_INT1 +// | OB_INT2 +// | OB_INT3 +// | OB_INT4 +// | OB_INT5 +// | OB_INT6 +// | OB_INT7 +// | OB_INT8 +// | OB_FLOAT +// | OB_DOUBLE +// | OB_LE +// | OB_BE +// | OB_ZEROS +// | OB_HEX +// | OB_DEC +// | OB_C_SYNTAX +// | OB_ADDR +// | OB_ALIGN +// | OB_TRIGGER +// | OB_NO_ADDR +// | OB_NO_NUMBERS +// | OB_NO_TEXT +// | OB_FORMAT +// | OB_NO_NULL +// | OB_NARROW +// | OB_SMALL +// | OB_WIDE +// | OB_BYTES, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_RAWDUMP = OB_DEST +// | OB_DEST2, +// +// OB_CMD_DUMP = OB_LONG, +// +// OB_CMD_HEXDUMP = OB_VADDR +// | OB_FADDR +// | OB_SNAME +// | OB_GRP_HEXDUMP, +// +// OB_CMD_CDUMP = 0, +// +// OB_CMD_HTTPS = OB_LONG, +// +// OB_CMD_EXTRACT = OB_DEST +// | OB_DEST2 +// | OB_OVERWRITE, +// +// OB_CMD_TRACKS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG +// | OB_GRP_PATCH, +// +// OB_CMD_ARENAS = OB_CMD_TRACKS, +// +// OB_CMD_FILES = OB_GRP_PATCH, +// +// OB_CMD_FIND = OB_GRP_PATCH +// | OB_LONG, +// +// OB_CMD_POINTS = OB_BRIEF +// | OB_LONG +// | OB_CHEAT, +// +// OB_CMD_ANALYZE = OB_GRP_PATCH +// | OB_LONG, +// +// OB_CMD_PATCH = OB_GRP_DEST +// | OB_IGNORE +// | OB_GRP_PATCH +// | OB_ADD_LECODE +// | OB_ADD_CTCODE +// | OB_MOVE_D8 +// | OB_ADD_SECTION +// | OB_ADD_WCODE +// | OB_FULL_GCH +// | OB_GCT_NO_SEP +// | OB_GCT_SEP +// | OB_GCT_ASM_SEP +// | OB_GCT_LIST +// | OB_GCT_MOVE +// | OB_GCT_ADDR +// | OB_GCT_SPACE +// | OB_ALLOW_USER_GCH +// | OB_CREATE_SECT +// | OB_WPF, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_RAWDUMP, + + CMD_DUMP, + CMD_CDUMP, + CMD_HEXDUMP, + CMD_HTTPS, + CMD_EXTRACT, + + CMD_TRACKS, + CMD_ARENAS, + CMD_FILES, + CMD_FIND, + CMD_POINTS, + + CMD_ANALYZE, + CMD_PATCH, + + CMD__N // == 22 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO__ERR = '?', + + GO_AT_DUMMY = '@', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_REGION = 'R', + GO_TRACKS = 'T', + GO_VERSION = 'V', + + GO_CONST = 'c', + GO_DEST = 'd', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_VS_REGION, + GO_BT_REGION, + GO_ALL_RANKS, + GO_POINTS, + GO_CHEAT, + GO_HTTPS, + GO_DOMAIN, + GO_WIIMMFI, + GO_TWIIMMFI, + GO_WC24, + GO_WCODE, + GO_ADD_WCODE, + GO_PB_MODE, + GO_PATCHED_BY, + GO_VS, + GO_VS2, + GO_BT, + GO_BT2, + GO_CANNON, + GO_MENO, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_CLEAN_DOL, + GO_ADD_LECODE, + GO_ADD_CTCODE, + GO_CT_DIR, + GO_MOVE_D8, + GO_ADD_SECTION, + GO_FULL_GCH, + GO_GCT_NO_SEP, + GO_GCT_SEP, + GO_GCT_ASM_SEP, + GO_GCT_LIST, + GO_GCT_MOVE, + GO_GCT_ADDR, + GO_GCT_SPACE, + GO_ALLOW_USER_GCH, + GO_CREATE_SECT, + GO_WPF, + GO_SECTIONS, + GO_VADDR, + GO_FADDR, + GO_SNAME, + GO_INT1, + GO_INT2, + GO_INT3, + GO_INT4, + GO_INT5, + GO_INT6, + GO_INT7, + GO_INT8, + GO_FLOAT, + GO_DOUBLE, + GO_LE, + GO_BE, + GO_ZEROS, + GO_HEX, + GO_DEC, + GO_C_SYNTAX, + GO_ADDR, + GO_ALIGN, + GO_TRIGGER, + GO_NO_ADDR, + GO_NO_NUMBERS, + GO_NO_TEXT, + GO_FORMAT, + GO_NO_NULL, + GO_NARROW, + GO_SMALL, + GO_WIDE, + GO_BYTES, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wstrt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WSTRT_H + diff --git a/project/src/ui/ui-wszst.c b/project/src/ui/ui-wszst.c new file mode 100644 index 0000000..c4dc814 --- /dev/null +++ b/project/src/ui/ui-wszst.c @@ -0,0 +1,7805 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + +#include +#include "ui-wszst.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionInfo[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t OptionInfo[OPT__N_TOTAL+1] = +{ + {0,0,0,0,0,0,0,0,0,0}, // OPT_NONE, + + //----- command specific options ----- + + { OPT_TOUCH, false, false, false, false, false, 0, "touch", + 0, + "Mark the minimap as modified to force a new calculation of the" + " translation and inverse matrices." + }, + + { OPT_AUTO, false, false, false, false, false, 0, "auto", + 0, + "Calculate the minimap translations automatically by using the minimum" + " and maximum vertex coordinates. The 'scale' and 'rotation' fields" + " are reset. This is done as first action before the SET, CENTER and" + " the other transformations." + }, + + { OPT_SET_FLAGS, false, false, false, false, false, 0, "set-flags", + "flag", + "Define the flag values (default 0x31f) of the minimap." + }, + + { OPT_SET_SCALE, false, false, false, false, false, 0, "set-scale", + "vector", + "Define the scale values (default 1.0) of the minimap. The parameter" + " is either a vector expression or a comma separated list of" + " coordinates ('x,z' or 'x,y,z')." + }, + + { OPT_SET_ROT, false, false, false, false, false, 0, "set-rot", + "vector", + "Define the rotation values (default 0.0) of the minimap. The" + " parameter is either a vector expression or a comma separated list of" + " coordinates ('x,z' or 'x,y,z')." + }, + + { OPT_SET_X, false, false, false, false, false, 0, "set-x", + "v1[,v2]", + "Define 2 values and sort them to define the minimum and maximum" + " x-coordinates of the minimap. If 'v2' is not entered, '-v1' is used" + " as second value." + }, + + { OPT_SET_Y, false, false, false, false, false, 0, "set-y", + "v1[,v2]", + "Define 2 values and sort them to define the minimum and maximum" + " y-coordinates of the minimap. If 'v2' is not entered, '-v1' is used" + " as second value." + }, + + { OPT_SET_Z, false, false, false, false, false, 0, "set-z", + "v1[,v2]", + "Define 2 values and sort them to define the minimum and maximum" + " z-coordinates of the minimap. If 'v2' is not entered, '-v1' is used" + " as second value." + }, + + { OPT_XCENTER, false, false, false, false, false, 0, "xcenter", + 0, + "Center the minimap in x-direction. This is done after AUTO and SET," + " but before SCALE and SHIFT." + }, + + { OPT_YCENTER, false, false, false, false, false, 0, "ycenter", + 0, + "Center the minimap in y-direction. This is done after AUTO and SET," + " but before SCALE and SHIFT." + }, + + { OPT_ZCENTER, false, false, false, false, false, 0, "zcenter", + 0, + "Center the minimap in z-direction. This is done after AUTO and SET," + " but before SCALE and SHIFT." + }, + + { OPT_CENTER, false, false, false, false, false, 0, "center", + 0, + "Center the minimap in all 3 directions. This is a short cut for" + " '--xcenter --ycenter --zcenter'." + }, + + { OPT_SCALE, false, false, false, false, false, 0, "scale", + "vector[@origin]", + "Transform the data: Scale all coordinates and size values by 'vector'" + " relative to the point 'origin'. If the origin is not set, 0,0,0 is" + " used instead.\n" + " Negative values mirror the coordinates. Each parameter is either a" + " vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_SHIFT, false, false, false, false, false, 0, "shift", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --translate, but the addition is done before rotation. The parameter" + " is either a vector expression or a comma separated expression list" + " of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_XSS, false, false, false, false, false, 0, "xss", + "x1old,x1new,x2old,x2new", + "--xss (x-scale-shift) calculates the X values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_YSS, false, false, false, false, false, 0, "yss", + "y1old,y1new,y2old,y2new", + "--yss (y-scale-shift) calculates the Y values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ZSS, false, false, false, false, false, 0, "zss", + "z1old,z1new,z2old,z2new", + "--zss (z-scale-shift) calculates the Z values of --scale and --shift," + " so that old values are transformed to the new values. The parameters" + " are numbers or expression." + }, + + { OPT_ROT, false, false, false, false, false, 0, "rot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' (is a vector) around the 3 axes. All 3 axes goes" + " through the point 'origin'. If the origin is not set, 0,0,0 is used" + " instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y,z').\n" + " Option --rot is an alternative for --xrot, --yrot and --zrot to" + " define all 3 rotations in one step. Transformations are calculated" + " in the order SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_XROT, false, false, false, false, false, 0, "xrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('y,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_YROT, false, false, false, false, false, 0, "yrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the x-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead. This is a" + " horizontal counterclockwise rotation and the old option name --hrot" + " can also used.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,z' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_ZROT, false, false, false, false, false, 0, "zrot", + "degree[@origin]", + "Transform the data: Rotate all coordinates and rotation values by the" + " angle 'degree' around the z-axis, that goes through the point" + " 'origin'. If the origin is not set, 0,0,0 is used instead.\n" + " Each parameter is either a vector expression or a comma separated" + " expression list of coordinates ('x,y' or 'x,y,z').\n" + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," + " Y-ROTATE, Z-ROTATE, TRANSLATE." + }, + + { OPT_YPOS, false, false, false, false, false, 0, "ypos", + "pos", + "This option defines an Y position for KMP:CKPT transformations. It" + " has only impact to X and Z rotations (Options --xrot and --zrot). If" + " not set, the mean y of the active rotation origins are used." + }, + + { OPT_TRANSLATE, false, false, false, false, false, 0, "translate", + "vector", + "Transform the data: Add 'vector' to all coordinates. It's simlar to" + " --shift, but the addition is done after rotation. The parameter is" + " either a vector expression or a comma separated expression list of" + " coordinates ('x,z' or 'x,y,z'). --trans is a short cut.\n" + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," + " TRANSLATE." + }, + + { OPT_NULL, false, false, false, false, false, 0, "null", + 0, + "Create a neutral transformation without affecting the coordinates." + " The only influence is, that the dependent values (like maximum) are" + " re-calculated as if a transformation has taken place." + }, + + { OPT_NEXT, false, false, false, false, false, 0, "next", + 0, + "Close the current transformation step with all scaling, shifting," + " rotation and translation options and open a new step with cleared" + " options. On transformation each step is logical done one by one. In" + " real, one total transformation matrix is calculated and used for" + " fast transformations.\n" + " If option --next is used, the ability for modifying the scale and" + " rotation vectors (not the positions) of different KMP sections is" + " nearly always lost. A warning is printed if this occurs." + }, + + { OPT_ASCALE, false, false, false, false, false, 0, "ascale", + "factor@dir", + "Transform the data and do an axis scale: Close the current" + " transformation step like --next and scale the data by 'factor' into" + " the direction given by the vector 'dir'. Store the resulting matrix" + " as single transformation step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_AROT, false, false, false, false, false, 0, "arot", + "degree[@pos1]@pos2", + "Transform the data and do an axis rotation: Close the current" + " transformation step like --next and rotate the data by 'degree'" + " around the axis defined by the 2 points. If 'pos1' is not set, use" + " 0,0,0 instead. Store the resulting matrix as single transformation" + " step and open a new one.\n" + " This kind of transformation is EXPERIMENTAL!" + }, + + { OPT_TFORM_SCRIPT, false, false, false, false, false, 0, "tform-script", + "script", + "Load the script and execute it by the text parser.\n" + " Then for each coordinate (2D and 3D), call the macro TRANSFORM after" + " all other transformations. Call it without parameters and set" + " variable $I with a zero based index of all vertices, variable $D to" + " the dimension (2 or 3) and the variable $P to the vector of the" + " current vertex. If the macro returns a vector, it is the new" + " position.\n" + " For each file, macro BEGIN is called at the beginning and END after" + " processing all points, but only, if the macros are defined. For END," + " variable $I is set to the number of processed vertices in the" + " current file.\n" + " Before each macro call, the private and local variables are cleared" + " and only global parameters are permanent. The zero based index is" + " cleared for each new source. The parameters of the macro calls are" + " defined in the local name space." + }, + + { OPT_MINIMAP, false, false, false, false, false, 0, "minimap", + 0, + "Fix the minimap position in the same way as 'wszst MINIMAP --auto'." + " All other minimap related options are ignored for the minimap" + " processing. See https://szs.wiimm.de/cmd/wszst/minimap for details." + }, + + { OPT_RM_GOBJ, false, false, false, false, false, 0, "rm-gobj", + "objlist", + "Remove objects from the KMP section GOBJ. Therefor a comma separated" + " object list with single values like 'a' or ranges like 'a:b' is" + " expected. 'a' and 'b' are numbers, object names (prefixed by 'o$')" + " or any other parser expressions." + }, + + { OPT_SLOT, false, false, false, false, false, 0, "slot", + "keyword", + "Patch a track file to run at the specified slot. KMP and KCL may be" + " modified. BRRES files may be added to or removed from SZS. The main" + " keywords are DAISY, DESERT, SHERBET, SHYGUY, STANDARD and MOST. Slot" + " numbers (except '42' and '4.2') and different other track names are" + " also accepted. For some conversions, the auto-add library is needed." + " See https://szs.wiimm.de/opt/slot for details." + }, + + { OPT_KCL_FLAG, false, false, false, false, false, 0, "kcl-flag", + "joblist", + "Modify the KCL flag of KCL files. A comma separated job list in the" + " format 'srclist=dest' is expected. 'srclist' is a plus sign" + " separated list with single values like , T and" + " / or ranges like :, T: and" + " :/. '' is a complete KCL flag in the range of" + " 0..0xffff. '' are only the base types in the range of 0..0x1f." + " If is present, it marks the relevant bits. For the T" + " variant, the mask is set to 0x1f.\n" + " All KCL flags of the source list are assigned to the new value" + " 'dest', but only the set bits of '' are modified. A later" + " definition overrides the previous one. See" + " https://szs.wiimm.de/opt/kcl-flag for details." + }, + + { OPT_KCL_SCRIPT, false, false, false, false, false, 0, "kcl-script", + "script", + "After reading a KCL source file and executing all transformations," + " the text file 'script' is executed by the internal text parser. The" + " script should use the parser functions tri$*() to read and modify" + " the triangle positions and flags, or to add or remove complete" + " triangles.\n" + " If this option is used multiple times, each script is executed in" + " the entered order. See https://szs.wiimm.de/opt/kcl-script for" + " details." + }, + + { OPT_FLAG_FILE, false, false, false, false, false, 0, "flag-file", + "filename", + "Define a flag file if scanning an OBJ file (KCL processing). The file" + " name may contain %-escapes. Set it to an empty string to disable" + " flag files at all.\n" + " The tool search the flag file in the following order until found:" + " File defined by --flag-file; OBJ file name with replaced extension" + " ('a.obj' becomes 'a.flag'); OBJ file name with appended extension" + " ('a.obj' becomes 'a.obj.flag'). See" + " https://szs.wiimm.de/opt/flag-file for details." + }, + + { OPT_REPAIR_XPF, false, false, false, false, false, 0, "repair-xpflags", + "file", + "Load a KMP file and use it to repair the settings of the extended" + " presence flags, if they are destroyed by another KMP tool. --repxpf" + " is a short cut for this option." + }, + + { OPT_GAMEMODES, false, false, false, false, false, 0, "gamemodes", + "list", + "Filter the list of combinations. The parameter is a comma separated" + " list of keywords: AUTO: Add a automatic mode without conditions" + " first. STANDARD (or STD): Add standard modes. 1STANDARD (or 1STD):" + " Add standard modes first. BALLOON: Add balloon battle modes. COIN:" + " Add coin runners modes. BATTLE: Short cut for 'BALLOON,COIN'. VERSUS" + " (or VS): Add versus modes. ITEMRAIN: Add itemrain modes. RACE: Short" + " cut for 'VERSUS,ITEMRAIN'. ALL (default): Short cut for" + " 'STANDARD,BATTLE,RACE'. NO-MODES: Exclude 'ALL' and create auto" + " modes only. ENGINE: Include engine classes (default). NO-ENGINE:" + " Exclude engine classes. RANDOM: Include random modes (default)." + " NO-RANDOM: Exclude random modes. OFFLINE: Filter offline modes." + " ONLINE: Filter online modes. NO-TT: Exclude time trial. TT: Force a" + " time trial scenario, even if it is a duplicate. FULL: Don't" + " eliminate duplicates from list (up to 1573 modes). NSORT: Sort the" + " list by name. ISORT: Sort the list by index. DEBUG: Print a debug" + " info about this option." + }, + + { OPT_LOAD_BMG, false, false, false, false, false, 0, "load-bmg", + "file", + "Load immediately the file to fill an internal message pool. The" + " option can be used multiple times to fill the message pool by" + " different sources. A later message overrides the previous message.\n" + " The internal message pool is patched by all --patch-bmg options." + }, + + { OPT_PATCH_BMG, false, false, false, false, false, 0, "patch-bmg", + "mode[cond][=param]", + "This option specifies a BMG patch mode. Some of the modes need a" + " parameter or a file name of a BMG patch file (raw or text BMG), both" + " separated by an equal sign. Modes with required file names are" + " PRINT, REPLACE, INSERT, OVERWRITE, DELETE, MASK, EQUAL and NOTEQUAL." + " Modes with text parameter are FORMAT, REGEXP and RM-REGEXP. " + " Standalone modes are ID, ID-ALL, UNICODE, RM-ESCAPES, RM-CUPS," + " CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY, LE-FILL," + " X-COPY, X-FORCE-COPY and X-FILL. Unique abbreviations are allowed.\n" + " The optional condition COND is either '?MID' or '!MID'. In case of" + " '?MID', the patch is only applied if the message id MID already" + " exists. In case of '!MID', the patch is only applied if the message" + " id MID does not exists.\n" + " If this option is used multiple times, all patch files will be" + " processed in the entered order. Read" + " https://szs.wiimm.de/opt/patch-bmg for more details." + }, + + { OPT_MACRO_BMG, false, false, false, false, false, 0, "macro-bmg", + "file", + "Load a BMG file and add the messages to the macro library; already" + " existing entries are replaced. The macro library is accessed by" + " escape sequence \\m{MID} as fallback, if the active file has not" + " already defined the message MID by itself." + }, + + { OPT_FILTER_BMG, false, false, false, false, false, 0, "filter-bmg", + "list", + "If this option is set, the BMG messages are filtered by this list." + " Only enabled messages are exported to the output BMG file (binary or" + " text).\n" + " The parameter is a list of message ids (short MID, 'Txx' or 'Uxx'" + " or Mxx' or hex number) or message ranges (MID:MID) or one of the" + " keywords NONE, IDENT (=ID), PARAM, CUPS, TRACKS, ARENAS, CHAT," + " CTCODE (=CT), CTUPS, CTTRACKS, CTARENAS, CTREFS, LECODE (=LE)," + " LEUPS, LETRACKS, LEARENAS, LEREFS, XCODE (=X), XUPS, XTRACKS," + " XARENAS, XREFS, ALLCODE, ALLUPS, ALLTRACKS, ALLARENAS, ALLREFS," + " GENERIC or ALL. If an element is preceded by a minus sign, it is" + " removed from the filter list (disabled).\n" + " With v2.01a, this option was renamed from --msg to the more" + " meaningful name --filter-bmg. The old name is still available.\n" + " Read https://szs.wiimm.de/opt/filter-bmg for more details." + }, + + { OPT_ID, false, false, false, false, false, 0, "id", + 0, + "Print an ID with 8 characters instead of a SHA-1 sum with 40" + " characters. The SHA-1 sum is used to calculate the ID. The ID" + " consists of '0'-'9' and 'a'-'w' (32 characters)." + }, + + { OPT_BASE64, true, false, false, false, false, 0, "base64", + "[=mode]", + "Encode the the checksum string as BASE64 with 27 characters instead" + " of hex. If a parameter is set, it acts like --coding=mode." + }, + + { OPT_DB64, true, false, false, false, false, 0, "db64", + "[=mode]", + "Encode the the checksum string and some length as BASE64 with 32" + " characters instead of hex for database searches. This options" + " implies --coding=URL if no other coding is defined. If a parameter" + " is set, it acts like --coding=mode. Option --db64 has priority over" + " --base64." + }, + + { OPT_CODING, false, false, false, false, false, 0, "coding", + "mode", + "Define the coding of the non alphanumeric characters for BASE64:" + " STANDART (+/=, default), URL (-_=), STAR (-_*) or XML (.-*)." + }, + + { OPT_VERIFY, false, false, false, false, false, 0, "verify", + 0, + "Calculate the checksum and compare the filename and return exit" + " status (0=valid, 2=differ). The result is valid, if the checksum is" + " found with a leading and a following point or minus sign. If --quiet" + " is set, error messages are suppressed." + }, + + { OPT_ROUND, false, false, false, false, false, 0, "round", + 0, + "Print additional lines with rounded values (to 3 and 2 bytes for" + " floats and 7 and 6 bytes for doubles)." + }, + + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Multiple usage possible." + }, + + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress printing of header and footer." + }, + + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress information lines in decoded text files. This includes" + " syntax information (--no-header). If set twice, the output of" + " unneeded sections is also suppressed." + }, + + { OPT_PIPE, false, false, false, false, false, 0, "pipe", + 0, + "Use pipe sign instead of spaces to separate columns." + }, + + { OPT_DELTA, false, false, false, false, false, 0, "delta", + 0, + "Print only modified records or sections on text output. Supported" + " file formats until now: GEOHIT, ITEMSLOT, KMG and OBJFLOW. If set" + " twice, limit output to modified records (only relevant for" + " ITEMSLOT)." + }, + + { OPT_DIFF, false, false, false, false, false, 0, "diff", + 0, + "On text output, print only differences from original files. It's a" + " short cut for --no-header --brief --brief --delta --delta." + " Additional, the output is limited to data sections." + }, + + { OPT_EPSILON, false, true, false, false, false, 0, "epsilon", + "float", + "When comparing floating point numbers, 2 numbers are considered" + " identical if their difference is less than or equal to epsilon. The" + " default value is 0.01." + }, + + { OPT_BMG_ENCODING, false, false, false, false, false, 0, "bmg-encoding", + "name", + "Force a BMG encoding for new BMG files. Accepted encoding names are:" + " CP-1252, UTF-16BE (or UTF-16), SHIFT-JIS, UTF-8 and AUTO for" + " automatic detection (default). Same keywords without minus signs are" + " accepted too.\n" + " Mario Kart Wii uses encoding UTF-16BE; the default if no other" + " encoding is defined." + }, + + { OPT_BMG_INF_SIZE, false, false, false, false, false, 0, "bmg-inf-size", + "size", + "Defines the 'INF1' size of new BMG files between 4 and 1000. The" + " first 4 bytes of a INF1 record is an offset into the string pool and" + " the remaining bytes the attributes. Maximal 40 attribute bytes are" + " supported and additional bytes are assumed to be NULL. All BMG files" + " of MKW have a size of 8." + }, + + { OPT_BMG_MID, true, false, false, false, false, 0, "bmg-mid", + "[mode]", + "Control the creation of BMG section MID1. Suppress the creation, if" + " mode is OFF. Force the creation, if mode is ON. On mode AUTO" + " (default), the source is analysed for the decision." + }, + + { OPT_FORCE_ATTRIB, false, false, false, false, false, 0, "force-attrib", + "attrib", + "Forces, that all attributes are set to this vector. See" + " https://szs.wiimm.de/doc/bmg/attrib for the syntax." + }, + + { OPT_DEF_ATTRIB, false, false, false, false, false, 0, "def-attrib", + "attrib", + "Define the default attributes for BMG files. If not set, the default" + " attributes are estimated. On text output, strings with default" + " attributes are printed without attribute vector. The usual default" + " attribute for MKW is [1]. See https://szs.wiimm.de/doc/bmg/attrib" + " for the syntax." + }, + + { OPT_NO_ATTRIB, false, false, false, false, false, 0, "no-attrib", + 0, + "Suppress the output of any BMG attributes if creating BMG text files." + }, + + { OPT_X_ESCAPES, false, false, false, false, false, 0, "x-escapes", + 0, + "Use \\x{} escapes instead of \\z{} escapes." + }, + + { OPT_OLD_ESCAPES, false, false, false, false, false, 0, "old-escapes", + 0, + "To be compatible with v1.43 and earlier, print 1A escape sequences" + " with total size of >12 bytes as single words using \\x{}. Also don't" + " use \\x{} and \\u{} with parameter lists." + }, + + { OPT_SINGLE_LINE, false, false, false, false, false, '1', "single-line", + 0, + "If set, don't print continuation lines for BMG text output. If set" + " twice, print only single text lines and suppress attributes like" + " option --no-attrib does it." + }, + + { OPT_NO_BMG_COLORS, false, false, false, false, false, 0, "no-bmg-colors", + 0, + "If set, suppress the output of '\\c{color}' escape sequences for" + " colors definitions in BMG text output to be compatible with old" + " versions of the tools." + }, + + { OPT_BMG_COLORS, false, false, false, false, false, 0, "bmg-colors", + 0, + "Force output of '\\c{color}' escape sequences with names, even for" + " foreign (not MKWii) BMG files." + }, + + { OPT_NO_BMG_INLINE, false, false, false, false, false, 0, "no-bmg-inline", + 0, + "Print BMG attributes as separate line before the message definition" + " to be compatible with versions until v1.39. The output for the" + " standard value 0x01000000 is always suppressed. This was the default" + " until v1.43." + }, + + { OPT_EXPORT, false, false, false, false, false, 'X', "export", + 0, + "Enable the export mode and create small and machine readable text" + " files for easy post processing. The option works similar like -HBB" + " for KMP and like -HBl1 for BMG text files." + }, + + { OPT_NO_PARAM, false, false, false, false, false, 'P', "no-param", + 0, + "Disable parameter support in decoded KMP text files." + }, + + { OPT_SORT, false, false, false, false, false, 'S', "sort", + "mode", + "Define a sort mode. Allowed keywords are: NONE (default), NAME," + " OFFSET, U8, PACK, BRRES, BREFT and AUTO" + }, + + { OPT_TRANSFORM, false, false, false, false, true, 'x', "transform", + "list", + "Convert image formats. A comma separated list with terms is expected." + " A term have the syntax '[SRC=]DEST'. It means: convert SRC format" + " into DEST format. If SRC= is not set, the destination format is used" + " for all source formats. This makes only sense as last rule. If DEST" + " is empty no transformation is done for the source. Examples: 'TPL'" + " or 'GRAY' or 'BTI.IA8' or 'TPL.I4+I8=IA8,TEX=,CMPR'\n" + " SRC and DEST are tuple of file, image and palette formats and of 2" + " more attributes. All parts are optional and separated by points, the" + " order is irrelevant.\n" + " Allowed file formats are: TPL, TEX, BTI, BREFT-IMG (=BTIMG), PNG" + " (import). Allowed image formats are: I4, I8, IA4, IA8, RGB565" + " (=R565), RGB5A3 (=R3), RGBA32 (=R32), C4, C8, C14X2, CMPR. Allowed" + " palette formats are: P-IA8 (=P8), P-RGB565 (=P565), P-RGB5A3 (=P3)." + " Attributtes are PALETTE, NO-PALETTE, GRAY, COLOR, ALPHA and" + " NO-ALPHA.\n" + " All terms are managed as list. For each graphic the list is" + " iterated until the first SRC tuple of a term matches the graphic." + " Then the destination tuple is used for the transformation.\n" + " The command 'wimgt TEST' prints a clearly arranged readable table" + " as result of this option." + }, + + { OPT_STRIP, false, false, false, false, false, 0, "strip", + 0, + "If creating a PNG file, don't add comments about creator and source" + " formats." + }, + + { OPT_ANALYZE, false, true, false, false, true, 0, "analyze", + "outfile", + "Run in analyze mode and write results to the entered file." + }, + + { OPT_ANALYZE_MODE, false, true, false, false, false, 0, "analyze-mode", + "modelist", + "Comma separated list with objects to analyse. Objects are BRRES," + " MDL0, PAT0, TEX0, BREFF, BREFT, TPL, KCL, KMP, GOBJ, KTPT, MKWANA" + " and ALL for all (=default). --ana-mode is a short cut." + }, + + { OPT_SOURCE, false, false, false, false, true, 's', "source", + "path", + "Define a secondary source path (directory/file).\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified primary source name. %P and %F are replaced by the primary" + " source path or by the filename. %N and %E are replaced by primary" + " source filename without extension or by the source extension." + " Finally, %T is replaced by the default extension of a virtual" + " destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/source for more details." + }, + + { OPT_DEST, false, false, false, false, false, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output.\n" + " The path may contain escape sequences: %Q is replaced by the fully" + " qualified source name. %P and %F are replaced by the source path or" + " by the filename. %N and %E are replaced by source filename without" + " extension or by the source extension. Finally, %T is replaced by the" + " default extension of the destination format.\n" + " A '?' direct behind '%' in %E and %T conversions avoid that the" + " same extension is used twice in row. Read" + " https://szs.wiimm.de/opt/dest for more details." + }, + + { OPT_DEST2, false, false, false, false, false, 'D', "DEST", + "path", + "Like --dest, but create the directory path automatically." + }, + + { OPT_ESC, false, false, false, false, false, 'E', "esc", + "char", + "Define an alternative escape character for destination files. The" + " default is '%'. For Windows batch files it is a good choice to set" + " '-E$'." + }, + + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files without warning and ignore option" + " --number." + }, + + { OPT_NUMBER, false, false, false, false, false, 0, "number", + 0, + "If a file already exist, append a number directly before the file" + " extension to make the filename unique. If other numbered files" + " already exist (ignoring case), use the maximum existing index+1." + " --num is a short cut." + }, + + { OPT_REMOVE_SRC, false, false, false, false, false, 0, "remove-src", + 0, + "Remove the source file after successful operation. --rm-src is a" + " short cut." + }, + + { OPT_REMOVE_DEST, false, false, false, false, false, 'r', "remove-dest", + 0, + "Remove already existing files before creating it. If set, --overwrite" + " is ignored. --rm-dest is a short cut." + }, + + { OPT_UPDATE, false, false, false, false, false, 'u', "update", + 0, + "Update only existing files and don't create new files. If set," + " --overwrite and --remove-dest are ignored." + }, + + { OPT_PRESERVE, false, false, false, false, false, 'p', "preserve", + 0, + "Preserve file times (atime+mtime) while converting or copying files." + }, + + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "Ignore non existing source files without warning." + }, + + { OPT_IGNORE_SETUP, false, false, false, false, false, 0, "ignore-setup", + 0, + "Ignore hidden setup file 'wszst-setup.txt'. This file is used to" + " determine some default settings and to find out sub archives and" + " files to encode. Use this option only if you know what you do." + }, + + { OPT_PURGE, false, false, false, false, false, 0, "purge", + 0, + "Remove repeated files with same checksum." + }, + + { OPT_ALIGN_U8, false, false, false, false, false, 0, "align-u8", + "size", + "Define an align value for the sub files in U8 archives. The value" + " must be a power of 2 and the default value is 32 (0x20) This value" + " is only relevant if creating or normalizing U8 archives." + }, + + { OPT_ALIGN_PACK, false, false, false, false, false, 0, "align-pack", + "size", + "Define an align value for the sub files in PACK archives. The value" + " must be a power of 2 and the default value is 32 (0x20) This value" + " is only relevant if creating or normalizing OACK archives." + }, + + { OPT_ALIGN_BRRES, false, false, false, false, false, 0, "align-brres", + "size", + "Define an align value for the sub files in BRRES archives. The value" + " must be a power of 2 and the default value is 4 This value is only" + " relevant if creating or normalizing BRRES archives." + }, + + { OPT_ALIGN_BREFF, false, false, false, false, false, 0, "align-breff", + "size", + "Define an align value for the sub files in BREFF archives. The value" + " must be a power of 2 and the default value is 4 This value is only" + " relevant if creating or normalizing BREFF archives." + }, + + { OPT_ALIGN_BREFT, false, false, false, false, false, 0, "align-breft", + "size", + "Define an align value for the sub files in BREFT archives. The value" + " must be a power of 2 and the default value is 0x20 This value is" + " only relevant if creating or normalizing BREFT archives." + }, + + { OPT_ALIGN, false, false, false, false, false, 0, "align", + "size", + "Define an align value for the sub files in all archives. This is a" + " short cut for '--align-u8=size --align-pack=size --align-brres=size" + " --align-breff=size --align-breft=size'." + }, + + { OPT_ENCODE_ALL, false, false, false, false, false, 0, "encode-all", + 0, + "Encode all files. Normally a file is only encoded, if the source is" + " younger than the destination or the destination is missing. Only" + " files in the [encode] section of the file 'wszst-setup.txt' are" + " candidates for encoding. --eall is a short cut. Option --no-encode" + " takes precedence over --encode-all." + }, + + { OPT_ENCODE_IMG, false, false, false, false, false, 0, "encode-img", + 0, + "Encode all image files. Normally a image file is only encoded, if the" + " source is younger than the destination or the destination is" + " missing. Only files in the [encode] section of the file" + " 'wszst-setup.txt' are candidates for encoding. --eimg is a short" + " cut. If set, option --no-encode is ignored for images." + }, + + { OPT_NO_ENCODE, false, false, false, false, false, 0, "no-encode", + 0, + "Do not encode files before creating an archive. --noenc is a short" + " cut. Option --no-encode takes precedence over --encode-all, but" + " --encode-img takes precedence for images." + }, + + { OPT_NO_RECURSE, false, false, false, false, false, 0, "no-recurse", + 0, + "Do not create archives recursively. Normally files in the [create]" + " section of the file 'wszst-setup.txt' are created first. --norec is" + " a short cut." + }, + + { OPT_AUTO_ADD, false, false, false, false, false, 0, "auto-add", + 0, + "Analyze the KMP (if exist) and add missing BRRES, BREFF, BREFT, BRASD" + " and KCL files automatically, if the tool can find the files in any" + " sub directory named 'auto-add'. This sub directory is searched in" + " all directories of 'SEARCH_PATH'; use command 'wszst AUTOADD' to" + " view them. Command 'wszst AUTOADD' may also create an auto-add" + " database. --aadd is a short cut." + }, + + { OPT_NO_ECHO, false, false, false, false, false, 0, "no-echo", + 0, + "Suppress output of @ECHO commands while scanning text files." + }, + + { OPT_NO_CHECK, false, false, false, false, false, 'N', "no-check", + 0, + "Do not make plausibility checks for KCL and KMP files." + }, + + { OPT_PT_DIR, true, false, false, false, false, 0, "pt-dir", + "[=mode]", + "Set one of the `point directory' modes REMOVE (=0), FORCE (=1) or" + " AUTO (default). If --pt-dir is used without parameter, then FORCE is" + " assumed.\n" + " This option is only relevant if creating an U8 archive. It decides," + " if a special directory with name '.' will be added as base for all" + " other files." + }, + + { OPT_RM_AIPARAM, false, false, false, false, false, 0, "rm-aiparam", + 0, + "Remove the directory AIParam and all files from the archive. This" + " option has only impact, if creating a new archive or normalizing an" + " existing archive. --rmai is a short cut." + }, + + { OPT_U8, false, false, false, false, false, 0, "u8", + 0, + "Create an U8 archive, if possible. If the source is a WU8 archive," + " convert it to an U8 archive. This is the default if the setup file" + " 'wszst-setup.txt' has no other definition. The default compression" + " mode is set to Yaz0 (see --yaz0)." + }, + + { OPT_SZS, false, false, false, false, false, 0, "szs", + 0, + "Short cut for '--u8 --yaz0': Create an U8 archive, if possible, and" + " set the compression method to Yaz0." + }, + + { OPT_WU8, false, false, false, false, false, 0, "wu8", + 0, + "Create a WU8 archive, if possible. If the source is an U8 archive," + " convert it to a WU8 archive." + }, + + { OPT_XWU8, false, true, false, false, false, 0, "xwu8", + 0, + "Short cut for '--wu8 --xyz': Create a WU8 archive, if possible, and" + " set the compression method to XYZ." + }, + + { OPT_WBZ, false, false, false, false, false, 0, "wbz", + 0, + "Short cut for '--wu8 --bz': Create a WU8 archive, if possible, and" + " set the compression method to BZ." + }, + + { OPT_PACK, false, false, false, false, false, 0, "pack", + 0, + "Create a PACK archive." + }, + + { OPT_BRRES, false, false, false, false, false, 0, "brres", + 0, + "Create a BRRES archive." + }, + + { OPT_BREFF, false, false, false, false, false, 0, "breff", + 0, + "Create a BREFF archive." + }, + + { OPT_BREFT, false, false, false, false, false, 0, "breft", + 0, + "Create a BREFT archive." + }, + + { OPT_NO_COMPRESS, false, false, false, false, false, 0, "no-compress", + 0, + "Create an uncompressed file instead of a compressed SZS file. The" + " default destination changes to '%P/%N%T'. --noc is a short cut." + }, + + { OPT_COMPRESS, false, false, false, false, false, 'C', "compr", + "level", + "Define a compression level between 0..9: 0 (or NOCHUNKS) disables" + " finding chunks, 1 (or FAST) for the fastest standard compression and" + " 9 (or BEST) for the best (=default) compression. 10 (or ULTRA) and" + " 100-150 are special time-consuming compression modes. They are" + " dedicated to competitions with size limitations. The special value" + " UNCOMPRESSED acts like option --no-compress.\n" + " Because of many repeated data, the best bz-compression mode varies." + " Therefor the levels TRY2..TRY5 (or short T2..T5) are defined to find" + " the best compression mode with testing the first N modes of 9, 1, 8," + " 2, 5. TRY2 is used for normalizing. For non bz-compression, TRY* are" + " the same as BEST.\n" + " Option --norm takes precedence over --compr and sets the" + " compression level to TRY2 (BEST).\n" + " For more modes and details type 'wszst -C list'. To force colorized" + " output type 'wszst -C clist'." + }, + + { OPT_FAST, false, false, false, false, false, 0, "fast", + 0, + "Short cut for --compr=fast: Set the fastest real compression. It also" + " overrides compression rates set by --norm or --compr." + }, + + { OPT_NORM, false, false, false, false, false, 'n', "norm", + 0, + "The uncompressed data will be normalized. See command NORMALIZE for" + " more details." + }, + + { OPT_LINKS, false, false, false, false, false, 0, "links", + 0, + "Support hardlinks while creating U8 and WU8 archives. Keep hardlinks" + " if normalizing U8 and WU8 archives. On extracting, search hardlinks" + " in every archive and try to create hardlinks at local file system." + }, + + { OPT_BASEDIR, false, false, false, false, true, 0, "basedir", + "directory", + "Extract only files from archives, that are below the DIRECTORY." + " Leading points and slashes and trailing slahes are removed from the" + " parameter. --bdir is short cut for --basedir." + }, + + { OPT_RECURSE, true, false, false, false, false, 'R', "recurse", + "[=level]", + "If a extracted file is a known and supported archive, than extract it" + " recursively until the entered level is reached. Level 0 (=NONE)" + " deactivates this. If --recurse is used without parameter, then" + " UNLIMITED is assumed.\n" + " -R does'nt accept a parameter and is a short cut for" + " --recurse=unlimited." + }, + + { OPT_EXT, false, false, false, false, false, 'e', "ext", + 0, + "For BRRES archives only: If a file is extracted, add a handy file" + " extension like '.mdl' to the file name. If set twice and a textual" + " file magic is available, use the file magic in lower case instead" + " like '.mdl0'." + }, + + { OPT_DECODE, false, false, false, false, false, 0, "decode", + 0, + "If a extracted file is known and can be decoded, do it. --dec is a" + " short cut." + }, + + { OPT_MIPMAPS, false, false, false, false, false, 0, "mipmaps", + 0, + "If reading a source, scan for mipmaps and load them too. For PNG" + " files, files named NAME.mm#.EXT with #=1.. are searched. This is the" + " default. --mm is a short cut." + }, + + { OPT_NO_MIPMAPS, false, false, false, false, false, 0, "no-mipmaps", + 0, + "If reading a source, ignore mipmaps. This disables not the creation" + " of mipmaps (see -n-mipmaps). --no-mm is a short cut." + }, + + { OPT_N_MIPMAPS, false, false, false, false, false, 0, "n-mipmaps", + "num", + "Force the number of mipmaps. Values between 0 and 20 are allowed." + " Value OFF (default) disables this option. If enabled, options" + " --max-mipmaps and --mipmap-size are ignored. The creation of mipmaps" + " stops if the width or the height of the mipmap becomes 0. --n-mm is" + " a short cut." + }, + + { OPT_MAX_MIPMAPS, false, false, false, false, false, 0, "max-mipmaps", + "num", + "Control the automatic mipmap creation. Values between 0 and 20 are" + " allowed and the default is 4. The mipmap creation for an image" + " stops, if the set number is reached. This option is ignored, if" + " --n-mipmaps is enabled. --max-mm is a short cut." + }, + + { OPT_MIPMAP_SIZE, false, false, false, false, false, 0, "mipmap-size", + "num", + "Define the minimal width and minimal height of a generated mipmap." + " Any value >0 is allowed. The default is 8. This option is ignored," + " if --n-mipmaps is enabled. --mm-size is a short cut." + }, + + { OPT_FAST_MIPMAPS, false, false, false, false, false, 0, "fast-mipmaps", + 0, + "If resizing an image, use the old fast resize algorithm (default" + " until v1.64a) instead of the new smart one. Both algorithms differ" + " only, when creating a mipmap for images with odd width or odd" + " height. The old algorithm is faster (factor 2-4) than the default" + " algorithm, but the new algorithm creates better resized images. See" + " https://szs.wiimm.de/doc/img/resize for details. --fast-mm is a" + " short cut." + }, + + { OPT_CMPR_DEFAULT, false, false, false, false, false, 0, "cmpr-default", + "rgb1[,rgb2]", + "Define 2 colors for the case, that all 16 pixels of a CMPR block are" + " transparent. The default is to calculate an average color of all" + " transparent pixels. Before v2.04 white was used.\n" + " 2 RGB values (hex values with 6 digits each) are expected as" + " parameter. If optional RGB2 is missed, a copy of RGB1 is used. As" + " first step, both colors are converted to RGB565 colors C1 and C2 and" + " sorted, so that C10. On mode OFF, this feature is totally disabled to create KMP text" + " files that are compatible with versions <1.60a.\n" + " This option exists only for a transition period. Its behaviour will" + " be changed in the future. --ropt is a short cut." + }, + + { OPT_WIM0, true, false, false, false, false, 0, "wim0", + "[=mode]", + "MODE is either OFF, AUTO (default) or ON. Without value, ON is used.\n" + " If mode is ON, a KMP section called WIM0 is added on binary KMP" + " creation. The sections contains information for Wiimms tools.\n" + " If mode is OFF, this feature is totally disabled. If mode is not" + " AUTO, KMP parameter KMP-WIM0 is overridden." + }, + + { OPT_LOAD_KCL, false, false, false, false, false, 0, "load-kcl", + "file", + "Load a KCL or OBJ file for reference issues." + }, + + { OPT_KCL, false, false, false, false, false, 0, "kcl", + "list", + "Set global options for KCL processing. Parameter 'list' is a comma" + " separated list of keywords. A minus sign before a keyword disables a" + " setting. Each occurrence of the option will only change entered" + " settings and all other settings are untouched.\n" + " Keyword DEFAULT resets the default settings and CLEAR disables all." + " Keywords SMALL, MEDIUM and CHARY select the default parameters for" + " the octree creation. The other allowed keywords are: FAST, NEW," + " CENTER, ROUND, NORMALS, MTL, WIIMM, TRIANGLES, OUT-SWAP, G, USEMTL," + " CLIP, IN-SWAP, AUTO, HEX4, HEX23, HEX, DROP-UNUSED, DROP-FIXED," + " DROP-INVALID, DROP, RM-FACEDOWN, RM-FACEUP, FIX-ALL, CONV-FACEUP," + " WEAK-WALLS, SORT, INPLACE, SILENT and LOG. See" + " https://szs.wiimm.de/opt/kcl for details." + }, + + { OPT_TRI_AREA, false, false, false, false, false, 0, "tri-area", + "EXPR", + "Define the minimal area size of KCL triangles. The intention is to" + " ignore triangles that are generally to small. EXPR is a floating" + " point number or expression. Triangles are invalidated if their area" + " size is smaller than EXPR. The test is executed after reading files," + " after transformations, and after calculating normals and lengths." + " Values between 0.01 and 4.0 are recommended. The careful value 1.0" + " is used as default. Value 0 disables this filter functionality." + }, + + { OPT_TRI_HEIGHT, false, false, false, false, false, 0, "tri-height", + "EXPR", + "Define the minimal height of KCL triangles. The intention is to" + " ignore deformed triangles (very slim, but long). EXPR is a floating" + " point number or expression. Triangles are invalidated if at least 1" + " of the 3 heights is smaller than EXPR. The test is executed after" + " reading files, after transformations, and after calculating normals" + " and lengths. Values between 0.01 and 2.0 are recommended. The" + " careful value 1.0 is used as default. Value 0 disables this filter" + " functionality.\n" + " --tri-ht is an alternative name for the option." + }, + + { OPT_XTRIDATA, true, true, false, false, false, 0, "xtridata", + "[=level]", + "Enable extended KCL triangles tests. The default level is 1." + }, + + { OPT_KMP, false, false, false, false, false, 0, "kmp", + "list", + "Set global options for KMP processing. Parameter 'list' is a comma" + " separated list of keywords. A minus sign before a keyword disables a" + " setting. Each occurrence of the option will only change entered" + " settings and all other settings are untouched.\n" + " Keyword DEFAULT resets the default settings and CLEAR disables all." + " The other allowed keywords are: FORCE, NEW, RM-SPCITEM, LEFT, RIGHT," + " WIDE, NARROW, FIX-CKPH, FIX-ENPH, FIX-ITPH, FIX-PH, FIX-CKNEXT," + " FIX-CKJGPT, FIX-CK, FIX-ALL, MASK-PFLAGS, RM-LECODE, PURGE-GOBJ," + " FULL-DEFOBJ, DUMP-CLASS, DUMP-ONEWAY, DUMP-ALL, 1LAP ... 9LAPS," + " MAX-LAPS, RM-EMPTY, INPLACE, SILENT and LOG. See" + " https://szs.wiimm.de/opt/kmp for details." + }, + + { OPT_SPEED_MOD, false, false, false, false, false, 0, "speed-mod", + "factor", + "The speed modifier is a user extensions to change the basic speed of" + " all vehicles. The speed factor itself is stored into KMP:STGI" + " section of a track.\n" + " If --speed-mod is set to a value >0.0, then the last 2 bytes of" + " the STGI section are patched. The value 0.0 reset these 2 bytes and" + " deactivates the speed modifier." + }, + + { OPT_KTPT2, false, false, false, false, false, 0, "ktpt2", + "vector", + "Insert or replace a second KTPT. Use VECTOR as new position. VECTOR" + " is either X,Z or X,Y,Z or an expression. Set Y to 0 for an automatic" + " defined height.\n" + " If the first 4 characters of VECTOR are 'AUTO' (ignoring case)," + " then the position is calculated automatically and set to the" + " recommendation of command wkmpt KTPT.\n" + " The tool looks for the nearest lap counter (CKPT with mode 0) and" + " adjust the KTPT: The direction of the lap counter is copied and the" + " KTPT is moved to the lap counter line using the new direction.\n" + " LE-CODE uses the second KTPT to draw the finish line at another" + " place away from the start position." + }, + + { OPT_TFORM_KMP, false, false, false, false, false, 0, "tform-kmp", + "list", + "Select KMP sections and objects for a transformation. --tkmp is a" + " short cut. The option expects a comma separated list of keywords." + " Non ambiguous of keywords are allowed and minus signs within" + " keywords are optional (e.g. GOBJS). A minus sign before a keyword" + " disables transformation of the related section or object.\n" + " Sections are selected by the KMP section names AREA, CAME, CKPT," + " CNPT, ENPT, GOBJ, ITPT, JGPT, KTPT, MSPT and POTI.\n" + " Objects are selected by the keywords AREA-POSITION, AREA-ROTATE," + " AREA-SCALE, CAME-POSITION, CKPT-POSITION, CNPT-POSITION," + " CNPT-ROTATE, ENPT-POSITION, ENPT-SCALE, GOBJ-POSITION, GOBJ-ROTATE," + " GOBJ-SCALE, ITPT-POSITION, ITPT-SCALE, JGPT-POSITION, JGPT-ROTATE," + " KTPT-POSITION, KTPT-ROTATE, MSPT-POSITION, MSPT-ROTATE and" + " POTI-POSITION.\n" + " The keywords NONE and ALL (default) complete the field." + }, + + { OPT_MDL, false, false, false, false, false, 0, "mdl", + "list", + "Set global options for MDL processing. To enable MDL patching, use" + " option --patch-file and select BRRES files for patching.\n" + " Parameter 'list' is a comma separated list of keywords. A minus" + " sign before a keyword disables a setting. Each occurrence of the" + " option will only change entered settings and all other settings are" + " untouched.\n" + " A MDL transformation (controlled by the transformation options) is" + " only done, if at least one of the keywords VECTOR or VERTEX is set.\n" + " Keyword DEFAULT resets the default settings (VERTEX) and CLEAR" + " disables all. The other allowed keywords are: VECTOR, MATRIX," + " VERTEX, PARENT, CHILD and LOG. See https://szs.wiimm.de/opt/mdl for" + " details." + }, + + { OPT_PAT, false, false, false, false, false, 0, "pat", + "list", + "Set global options for PAT processing. Parameter 'list' is a comma" + " separated list of keywords. A minus sign before a keyword disables a" + " setting. Each occurrence of the option will only change entered" + " settings and all other settings are untouched.\n" + " Keyword DEFAULT resets the default settings and CLEAR disables all." + " The other allowed keywords are: AUTO, SIMPLE, COMPLEX, BOTH, SILENT" + " and LOG. See https://szs.wiimm.de/opt/pat for details." + }, + + { OPT_PATCH_FILE, false, false, false, false, false, 0, "patch-file", + "list", + "Define, which kind of KMP, KCL and BRRES/MDL files are objects for" + " patching. --pfile is a short cut.\n" + " Parameter 'list' is a comma separated list of keywords. A minus" + " sign before a keyword disables a setting. Each occurrence of the" + " option will only change entered settings and all other settings are" + " untouched.\n" + " Keyword DEFAULT resets the default settings (TRACK,LOG) and CLEAR" + " disables all. The other allowed keywords are: KMP, KCL, OTHER-KCL," + " ALL-KCL, MODEL, MAP, VRCORN, OTHER-BRRES, ALL-BRRES, TRACK" + " (=KMP,KCL,MODEL,MAP,VRCORN), ALL, LOG and LOG-ALL. See" + " https://szs.wiimm.de/opt/patch for details." + }, + + { OPT_KMG_LIMIT, false, false, false, false, false, 0, "kmg-limit", + "seconds", + "Limit the run time for Ballon Battle and Coin Runners to the given" + " time in SECONDS. Smaller values are not changed. The limitation is" + " carried out after loading a binary file and before scanning a text" + " file. Value 0 disables this option." + }, + + { OPT_KMG_COPY, false, false, false, false, false, 0, "kmg-copy", + "id", + "Make all 10 slots of a KMG file equal after loading a binary file and" + " before scanning a text file. ID is either a slot index (0..9) or a" + " slot name (A11..A25) and declares the source slot. Value -1 disables" + " this option. If ID contains brackets [...], then it is analysed as" + " an arena name and the part between the brackets is searched for" + " attributes A11..A25." + }, + + { OPT_LT_CLEAR, false, false, false, false, false, 0, "lt-clear", + 0, + "Clear the LEX/TEST section and remove the section and course.lex if" + " empty. This option is executed before all other --lt-* options." + }, + + { OPT_LT_ONLINE, false, false, false, false, false, 0, "lt-online", + "mode", + "Set LEX/TEST parameter OFFLINE-ONLINE to this MODE. MODE is one of" + " AUTO (default), NEVER or ALWAYS. Only track.szs files are modified." + " If needed, file course.lex and section TEST are created or removed" + " if empty." + }, + + { OPT_LT_N_PLAYERS, false, false, false, false, false, 0, "lt-n-players", + "offline,online", + "Set LEX/TEST parameters N-OFFLINE and N-ONLINE to these integer" + " values. Only track.szs files are modified. If needed, file" + " course.lex and section TEST are created or removed if empty." + }, + + { OPT_LT_COND_BIT, false, false, false, false, false, 0, "lt-cond-bit", + "bitnum", + "Set LEX/TEST parameter COND-BIT to this integer value. Use -1 to" + " deactivate this COND-BIT. Only track.szs files are modified. If" + " needed, file course.lex and section TEST are created or removed if" + " empty." + }, + + { OPT_LT_GAME_MODE, false, false, false, false, false, 0, "lt-game-mode", + "mode", + "Set LEX/TEST parameter GAME-MODE to this MODE. MODE is one of AUTO" + " (default), BALLOON, COIN, VERSUS or ITEMRAIN. Only track.szs files" + " are modified. If needed, file course.lex and section TEST are" + " created or removed if empty." + }, + + { OPT_LT_ENGINE, false, false, false, false, false, 0, "lt-engine", + "mode", + "Set LEX/TEST parameter ENGINE to this MODE. MODE is one of AUTO" + " (default), BATTLE, 50CC, 100CC, 150CC, 200CC, 150M or 200M. Only" + " track.szs files are modified. If needed, file course.lex and section" + " TEST are created or removed if empty." + }, + + { OPT_LT_RANDOM, false, false, false, false, false, 0, "lt-random", + "index", + "Force a random scenario, if between 1 and 8. Value 0 enables auto" + " selection and -1 disables this option. Only track.szs files are" + " modified. If needed, file course.lex and section TEST are created or" + " removed if empty." + }, + + { OPT_LEX_PURGE, false, false, false, false, false, 0, "lex-purge", + 0, + "Delete LEX sections without any impact. Delete sub-file course.lex if" + " it no longer contains a section. This option is executed after all" + " --lt-* options." + }, + + { OPT_CACHE, false, false, false, false, false, 0, "cache", + "dir", + "Define a directory for a SZS cache.If creating a YAZ0 compressed file" + " with standard compression (level 9), the cache is searched for the" + " compressed version. File cache-content.txt is used as file list with" + " hash values. If the cache file is not found or the content differ," + " the file is (re-)created and exit status 2=differ is returned.\n" + " Background: Loading a compressed SZS and content verification is" + " much faster than YAZ compression.\n" + " For legay reasons: If parameter DIR is a file, it is split into" + " directory and base name. The directory is used for the cache" + " directory and the base name for option --cname, but only if --cname" + " is not used." + }, + + { OPT_CNAME, false, false, false, false, false, 0, "cname", + "name", + "Name of the first cache file. This parameter overrides the generated" + " cache name. The name is used only once for the very first cache" + " candidate." + }, + + { OPT_MAX_FILE_SIZE, false, false, false, false, false, 'M', "max-file-size", + "size", + "This security option defines the maximum file size for input files;" + " larger files are ignored with a warning. The default unit is MiB and" + " the default size is 100 MiB; that should be enough for standard" + " usage." + }, + + { OPT_TRACKS, false, false, false, false, false, 'T', "tracks", + "tracks", + "Define a new track order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are track names or slot numbers." + " 'a' is the destination slot and 'b' the assigned track. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Tracks will only be exchanged so that always all tracks are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_ARENAS, false, false, false, false, false, 'A', "arenas", + "arenas", + "Define a new arena order. A comma separated list in the format [a=]b" + " or a:=b is expected. 'a' and 'b' are arena names or slot numbers." + " 'a' is the destination slot and 'b' the assigned arena. If 'a' is" + " omitted, the next slot is used; this means [a=]b,c,d is possible.\n" + " Arenas will only be exchanged so that always all arenas are" + " assigned. But if using ':=' the new slots are assigned without" + " exchanging and the old values are lost." + }, + + { OPT_AUTOADD_PATH, false, false, false, false, false, 0, "autoadd-path", + "directory", + "Define an alternative directory as autoadd library. The default is," + " that the sub-directory called './auto-add/' of all search paths are" + " used. Use 'wszst test -vvvv' for tests." + }, + + { OPT_UTF_8, false, true, false, false, true, 0, "utf-8", + 0, + "Enables UTF-8 support for filenames (default)." + }, + + { OPT_NO_UTF_8, false, true, false, false, false, 0, "no-utf-8", + 0, + "Disables UTF-8 support for filenames." + }, + + { OPT_TEST, false, false, false, false, true, 't', "test", + 0, + "Run in test mode, modify nothing.\n" + ">>> USE THIS OPTION IF UNSURE! <<<" + }, + + { OPT_FORCE, false, false, false, false, false, 0, "force", + 0, + "The tools analyse input files and reject files with invalid data" + " structures. If --force is set, little repairable issues are ignored" + " and a warning is printed.\n" + "Option '--kmp force' does the same, but only for KMP files.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_REPAIR_MAGICS, true, false, false, false, false, 0, "repair-magics", + "[=mode]", + "Control the reparing of missing or wrong magics of files and" + " sub-files by analysing file path and file extension. MODE is one of" + " OFF (default), ANALYZE or REPAIR (default, if set without mode). OFF" + " disables this feature, ANALYZE enables it for analysis only, and" + " REPAIR repairs all magics while reading the files. Keyword FNAME can" + " be appended (separated by a comma) to give filename scanning the" + " highest priority.\n" + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" + }, + + { OPT_TINY, false, false, false, false, false, 0, "tiny", + "mode", + "Set tiny mode to a level between 0 and 7. See" + " https://szs.wiimm.de/opt/tiny for details." + }, + + { OPT_YAZ0, false, false, false, false, false, 0, "yaz0", + 0, + "If creating a compressed file, force Yaz0 compression. Yaz0" + " compression is the default, if no compression method is defined by" + " option or setup file." + }, + + { OPT_YAZ1, false, false, false, false, false, 0, "yaz1", + 0, + "If creating a compressed file, force Yaz1 compression. The difference" + " to Yaz0 is only the magic, which is set to 'Yaz1'." + }, + + { OPT_XYZ, false, false, false, false, false, 0, "xyz", + 0, + "If creating a compressed file, force XYZ compression (an experimental" + " Yaz0 variant)." + }, + + { OPT_BZ, false, false, false, false, false, 0, "bz", + 0, + "If creating a compressed file, force BZ compression (a BZIP2" + " variant)." + }, + + { OPT_SH, false, false, false, false, true, 0, "sh", + 0, + "Dump info using SH syntax. Use '--var name' as prefix." + }, + + { OPT_BASH, false, false, false, false, false, 0, "bash", + 0, + "Dump info using BASH syntax. Use '--var name' as prefix." + }, + + { OPT_JSON, false, false, false, false, false, 0, "json", + 0, + "Dump info using JSON syntax. Options --var and --array are ignored." + }, + + { OPT_PHP, false, false, false, false, false, 0, "php", + 0, + "Dump infos using PHP syntax. Use '--var name' as variable name of the" + " resulting object." + }, + + { OPT_MAKEDOC, false, false, false, false, false, 0, "makedoc", + 0, + "Dump infos using MakeDoc syntax. Use '--var name' as variable name of" + " the resulting map." + }, + + { OPT_VAR, false, false, false, false, false, 0, "var", + "varname", + "Use VARNAME as variable name or prefix on script output. The default" + " variabe name is 'res'." + }, + + { OPT_ARRAY, false, false, false, false, false, 0, "array", + 0, + "Use an array on script output if possible. If arrays are not" + " supported, then append a '_#' suffix with an zero based index to the" + " variable name defined by '--var name'. In this case, only one output" + " file is created." + }, + + { OPT_AVAR, false, false, false, false, false, 0, "avar", + "varname", + "Shortcut for: '--array --var name'" + }, + + { OPT_OLD, false, true, false, false, false, 0, "OLD", + 0, + "Developer option: Cancel --NEW and use old implementation if" + " available." + }, + + { OPT_STD, false, true, false, false, false, 0, "STD", + 0, + "Developer option: Cancel --OLD and --NEW and use standard" + " implementation (default)." + }, + + { OPT_NEW, false, true, false, false, false, 0, "NEW", + 0, + "Developer option: Cancel --OLD and use new implementation if" + " available." + }, + + { OPT_EXTRACT, false, true, false, false, false, 0, "EXTRACT", + "dir", + "Create the directory and extract different objects in different" + " formats at the moment when they are seen. This is a development" + " helper option." + }, + + {0,0,0,0,0,0,0,0,0,0} // OPT__N_TOTAL == 200 + +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alternate option infos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t option_grp_TEXTOUT_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in decoded text files." + }; + +static const InfoOption_t option_grp_BMG_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print long numeric message IDs instead of alternative message names" + " like Txx, Uxx or Mxx." + }; + +static const InfoOption_t option_grp_ENCODE_ALL = + { OPT_ALL, false, false, false, false, false, 'a', "all", + 0, + " Encode all files. This is a short cut for --encode-all (and more" + " options in the future)." + }; + +static const InfoOption_t option_cmd_VERSION_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only the version number, revision and system. If set twice," + " print only the version number." + }; + +static const InfoOption_t option_cmd_VERSION_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print in long format. Ignored if option --sections is set." + }; + +static const InfoOption_t option_cmd_TEST_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Print the matrices of transformations." + }; + +static const InfoOption_t option_cmd_COLORS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print also a list of all colored text types. If set twice, include" + " alternative names. If set three times, include raw color names." + }; + +static const InfoOption_t option_cmd_COLORS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Ignore option --long and print a single status line about current" + " color settings." + }; + +static const InfoOption_t option_cmd_ERROR_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print message text instead of message name." + }; + +static const InfoOption_t option_cmd_ERROR_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print message number instead of message name." + }; + +static const InfoOption_t option_cmd_FILETYPE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print an additional column with the decompressed file type and" + " version. If entered twice, a validation status column is added." + }; + +static const InfoOption_t option_cmd_BRSUB_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, print hints about unusual versions too. If set twice, print a" + " status line for each found BRRES sub file." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress the output of the description and print only function type" + " and syntax." + }; + +static const InfoOption_t option_cmd_FUNCTIONS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Usually only the function syntax is compared to the keywords. But if" + " --long is set, the descriptions are compared too." + }; + +static const InfoOption_t option_cmd_MATRIX_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "If set once, suppress vector dumps for transformation steps and" + " ignore option --long. If set twice, suppress vector dump for final" + " transformation too." + }; + +static const InfoOption_t option_cmd_MATRIX_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set once, print inverse matrix for the final transformation. If" + " set twice, print inverse matrix for all transformations." + }; + +static const InfoOption_t option_cmd_MATRIX_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Option --quiet is dedicated to automatic tests comparing the old and" + " new transformation algorithms. If set once, the tables are only" + " printed, if the results differ. If set twice, nothing is printed at" + " all and only the exit status will inform about the results." + }; + +static const InfoOption_t option_cmd_VR_CALC_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress header and print only results for VR_winner-VR_looser." + }; + +static const InfoOption_t option_cmd_VR_RACE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print full matrix of delta values." + }; + +static const InfoOption_t option_cmd_AUTOADD_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + " If set, suppress logging." + }; + +static const InfoOption_t option_cmd_AUTOADD_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "If set, list all missed and invalid files of the current auto-add" + " archive after the adding operation. If set twice, then additionally" + " list all already existing files in the current auto-add archive." + }; + +static const InfoOption_t option_cmd_AUTOADD_DEST = + { OPT_DEST, false, false, false, false, false, 'd', "dest", + "path", + "Define a destination directory as current auto-add archive. The" + " default is the standard auto-add path. Try AUTOADD without sources" + " to view the path." + }; + +static const InfoOption_t option_cmd_AUTOADD_OVERWRITE = + { OPT_OVERWRITE, false, false, false, false, false, 'o', "overwrite", + 0, + "Overwrite already existing files instead of only adding new files to" + " the auto-add archive." + }; + +static const InfoOption_t option_cmd_TRACKS_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Print an additonally hex status value as second column before the" + " file name. These values can be used to find a selection mask." + }; + +static const InfoOption_t option_cmd_TRACKS_ALL = + { OPT_ALL, false, false, false, false, false, 'a', "all", + 0, + "Print the '_d' files too. If set twice, print all files (same as" + " -all)." + }; + +static const InfoOption_t option_cmd_TRACKS_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Print 5 non track files too. If set twice, print 4 more files with" + " old BRRES sub file formats too." + }; + +static const InfoOption_t option_cmd_LIST_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, the size of each file is printed. If set twice, offset and" + " size are printed. If set three times, debugging mode is enabled." + }; + +static const InfoOption_t option_cmd_NAME_REF_SORT = + { OPT_SORT, false, false, false, false, false, 'S', "sort", + "mode", + "Define the sort order of the reference. NONE (default) uses the order" + " of the string pool. BRRES orders in standard BRRES order by ASCCI" + " value and case sensitive. NAME similar to BRRES, but case" + " insensitive. OFFSET orders by the file offset of first usage." + }; + +static const InfoOption_t option_cmd_NAME_REF_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "If set, suppress the offsets of the strings. This makes comparisons" + " after name changes easier. If set twice, suppress reference offsets" + " too. If set 3 times, suppress all offsets." + }; + +static const InfoOption_t option_cmd_NAME_REF_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, a list ordered by NAME or BRRES (default) and a second list" + " ordered by OFFSET is printed. If set twice, a third list with order" + " NONE is appended." + }; + +static const InfoOption_t option_cmd_ILIST_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, image width, height and size are printed. If set twice, a" + " block info and attribites are printed too." + }; + +static const InfoOption_t option_cmd_MEMORY_DECODE = + { OPT_DECODE, false, false, false, false, false, 0, "decode", + 0, + "Decode binary files to find memory addresses of sections." + }; + +static const InfoOption_t option_cmd_MEMORY_MIPMAPS = + { OPT_MIPMAPS, false, false, false, false, false, 0, "mipmaps", + 0, + "Analyze images and dump mipmap adresses too." + }; + +static const InfoOption_t option_cmd_MEMORY_ALL = + { OPT_ALL, false, false, false, false, false, 'a', "all", + 0, + "A short cut for '--recurse --decode --mipmaps'. If entered twice," + " option --cut is included too." + }; + +static const InfoOption_t option_cmd_MEMORY_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, print absolute offsets too." + }; + +static const InfoOption_t option_cmd_SHA1_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress output of the filename." + }; + +static const InfoOption_t option_cmd_SHA1_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set and --db64 is not active, start the line with the length of" + " the decompressed source as decimal number (field width 9) separated" + " with a comma from the SHA1 checksum. If set twice, prefix each line" + " with the modification time in the format 'yyyy-mm-dd HH:MM:SS '.\n" + " If set and --db64 is active, then CKPT lap counter, STGI lap" + " counter, speed factor and slot info are inserted beteen checksum and" + " filename. All columns are separated by one or two spaces." + }; + +static const InfoOption_t option_cmd_ANALYZE_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Append a list of special files with size, SHA1 and file name." + }; + +static const InfoOption_t option_cmd_ANALYZE_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Suppress logging of input and output files." + }; + +static const InfoOption_t option_cmd_DISTRIBUTION_SOURCE = + { OPT_SOURCE, false, false, false, false, false, 's', "source", + "path", + "Define a text file for filename to slot translation. A »slot" + " filename« list is expected. Use this option multiple times to" + " define more than one source file. A definition of a later source" + " overwrites previous definitions. The file RESULT of the command line" + " is appended automatically as last entry, if exists and not already" + " included." + }; + +static const InfoOption_t option_cmd_DIFF_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "If set once, only a summary status for each section is printed. If" + " set twice, only a total summary status is printed. If set three" + " times, the status is only reported as exit status." + }; + +static const InfoOption_t option_cmd_CHECK_NO_CHECK = + { OPT_NO_CHECK, false, false, false, false, false, 'N', "no-check", + 0, + "Don't check the KCL and KMP files." + }; + +static const InfoOption_t option_cmd_CHECK_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print only warnings and suppress hints." + }; + +static const InfoOption_t option_cmd_CHECK_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "Be verbose and print more statistics." + }; + +static const InfoOption_t option_cmd_CHECK_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Be quiet until the first warning or hint is found." + }; + +static const InfoOption_t option_cmd_SLOTS_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Print the slot list in a compressed form without spaces." + }; + +static const InfoOption_t option_cmd_STGI_IGNORE = + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "If set, ignore non existing source files and files without KMP" + " without warning. If set twice, ignore files with standard settings" + " too." + }; + +static const InfoOption_t option_cmd_ISARENA_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "If set, suppress the output of header and filenames." + }; + +static const InfoOption_t option_cmd_ISARENA_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "If set, print nothing and set only the exit status." + }; + +static const InfoOption_t option_cmd_ISARENA_IGNORE = + { OPT_IGNORE, false, false, false, false, false, 'i', "ignore", + 0, + "If set, ignore non existing source files and files without KMP" + " without warning." + }; + +static const InfoOption_t option_cmd_MINIMAP_LONG = + { OPT_LONG, false, false, false, false, false, 'l', "long", + 0, + "If set, print the matrices too. If set twice, print minimum and" + " maximum values too." + }; + +static const InfoOption_t option_cmd_BMG_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in decoded text files." + }; + +static const InfoOption_t option_cmd_KCL_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in decoded text files." + }; + +static const InfoOption_t option_cmd_KMP_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in decoded text files." + }; + +static const InfoOption_t option_cmd_LEX_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress the syntax information section in decoded text files." + }; + +static const InfoOption_t option_cmd_INFO_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress output of the printed filename." + }; + +static const InfoOption_t option_cmd_GHOST_NO_HEADER = + { OPT_NO_HEADER, false, false, false, false, false, 'H', "no-header", + 0, + "Suppress output of the printed filename." + }; + +static const InfoOption_t option_cmd_VEHICLE_SOURCE = + { OPT_SOURCE, false, false, false, false, false, 's', "source", + "path", + "Load the file instead of using the internal base." + }; + +static const InfoOption_t option_cmd_VEHICLE_DEST = + { OPT_DEST, false, false, false, false, false, 'd', "dest", + "path", + "Define a destination path (directory/file). The destination - means:" + " write to standard output." + }; + +static const InfoOption_t option_cmd_VEHICLE_QUIET = + { OPT_QUIET, false, false, false, false, false, 'q', "quiet", + 0, + "Suppress logging." + }; + +static const InfoOption_t option_cmd_VEHICLE_VERBOSE = + { OPT_VERBOSE, false, false, false, false, false, 'v', "verbose", + 0, + "Log patching." + }; + +static const InfoOption_t option_cmd_VEHICLE_BRIEF = + { OPT_BRIEF, false, false, false, false, false, 'B', "brief", + 0, + "Suppress min/max counts on dumps." + }; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CommandTab[] /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_VERSION, "VERSION", 0, 0 }, + { CMD_HELP, "HELP", "H", 0 }, + { CMD_HELP, "?", 0, 0 }, + { CMD_ARGTEST, "ARGTEST", 0, 0 }, + { CMD_TEST, "TEST", 0, 0 }, + { CMD_COLORS, "COLORS", 0, 0 }, + { CMD_ERROR, "ERROR", "ERR", 0 }, + { CMD_FILETYPE, "FILETYPE", "FT", 0 }, + { CMD_FILEATTRIB, "FILEATTRIB", "FA", 0 }, + { CMD_BRSUB, "BRSUB", 0, 0 }, + { CMD_SYMBOLS, "SYMBOLS", 0, 0 }, + { CMD_FUNCTIONS, "FUNCTIONS", 0, 0 }, + { CMD_CALCULATE, "CALCULATE", 0, 0 }, + { CMD_MATRIX, "MATRIX", 0, 0 }, + { CMD_FLOAT, "FLOAT", 0, 0 }, + { CMD_VR_CALC, "VR-CALC", "VRCALC", 0 }, + { CMD_VR_RACE, "VR-RACE", "VRRACE", 0 }, + { CMD_AUTOADD, "AUTOADD", 0, 0 }, + { CMD_TRACKS, "TRACKS", 0, 0 }, + { CMD_SCANCACHE, "SCANCACHE", 0, 0 }, + { CMD_EXPORT, "_EXPORT", 0, 0 }, + { CMD_CODE, "_CODE", 0, 0 }, + { CMD_RECODE, "_RECODE", 0, 0 }, + { CMD_SUBFILE, "_SUBFILE", 0, 0 }, + { CMD_LIST, "LIST", "LS", 0 }, + { CMD_LIST_L, "LIST-L", "LL", 0 }, + { CMD_LIST_L, "LISTL", 0, 0 }, + { CMD_LIST_LL, "LIST-LL", "LLL", 0 }, + { CMD_LIST_LL, "LISTLL", 0, 0 }, + { CMD_LIST_LLL, "LIST-LLL", "LLLL", 0 }, + { CMD_LIST_LLL, "LISTLLL", 0, 0 }, + { CMD_LIST_A, "LIST-A", "LA", 0 }, + { CMD_LIST_A, "LISTA", 0, 0 }, + { CMD_LIST_LA, "LIST-LA", "LLA", 0 }, + { CMD_LIST_LA, "LISTLA", 0, 0 }, + { CMD_NAME_REF, "NAME-REF", "NAMEREF", 0 }, + { CMD_ILIST, "ILIST", "IL", 0 }, + { CMD_ILIST_L, "ILIST-L", "ILL", 0 }, + { CMD_ILIST_L, "ILISTL", 0, 0 }, + { CMD_ILIST_LL, "ILIST-LL", "ILLL", 0 }, + { CMD_ILIST_LL, "ILISTLL", 0, 0 }, + { CMD_ILIST_A, "ILIST-A", "ILA", 0 }, + { CMD_ILIST_LA, "ILIST-LA", "ILLA", 0 }, + { CMD_ILIST_LA, "ILISTLA", 0, 0 }, + { CMD_MEMORY, "MEMORY", "MEM", 0 }, + { CMD_MEMORY_A, "MEMORY-A", "MEMA", 0 }, + { CMD_DUMP, "DUMP", 0, 0 }, + { CMD_SHA1, "SHA1", 0, 0 }, + { CMD_ANALYZE, "ANALYZE", "ANA", 0 }, + { CMD_ANALYZE, "ANALYSE", 0, 0 }, + { CMD_DISTRIBUTION, "DISTRIBUTION", "DIST", 0 }, + { CMD_DIFF, "DIFF", "CMP", 0 }, + { CMD_CHECK, "CHECK", 0, 0 }, + { CMD_SLOTS, "SLOTS", 0, 0 }, + { CMD_STGI, "STGI", 0, 0 }, + { CMD_ISARENA, "ISARENA", 0, 0 }, + { CMD_NORMALIZE, "NORMALIZE", "NORM", 0 }, + { CMD_PATCH, "PATCH", 0, 0 }, + { CMD_COPY, "COPY", "CP", 0 }, + { CMD_DUPLICATE, "DUPLICATE", "DUP", 0 }, + { CMD_MINIMAP, "MINIMAP", 0, 0 }, + { CMD_COMPRESS, "COMPRESS", "COM", 0 }, + { CMD_DECOMPRESS, "DECOMPRESS", "DEC", 0 }, + { CMD_ENCODE, "ENCODE", "E", 0 }, + { CMD_CREATE, "CREATE", "C", 0 }, + { CMD_UPDATE, "UPDATE", "U", 0 }, + { CMD_EXTRACT, "EXTRACT", "X", 0 }, + { CMD_XDECODE, "XDECODE", "XD", 0 }, + { CMD_XEXPORT, "XEXPORT", "XX", 0 }, + { CMD_XALL, "XALL", "XA", 0 }, + { CMD_XCOMMON, "XCOMMON", 0, 0 }, + { CMD_BINARY, "BINARY", "BIN", 0 }, + { CMD_TEXT, "TEXT", "TXT", 0 }, + { CMD_CAT, "CAT", 0, 0 }, + { CMD_BMG, "BMG", 0, 0 }, + { CMD_KCL, "KCL", 0, 0 }, + { CMD_KMP, "KMP", 0, 0 }, + { CMD_LEX, "LEX", 0, 0 }, + { CMD_INFO, "INFO", 0, 0 }, + { CMD_GHOST, "GHOSTS", 0, 0 }, + { CMD_YAZDUMP, "YAZDUMP", "YD", 0 }, + { CMD_VEHICLE, "VEHICLE", 0, 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionShort & OptionLong /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const char OptionShort[] = "VhqvLc:lHB1XPS:M:T:A:x:ts:d:D:E:orupiNC:nRea"; + +static const struct option OptionLong[] = +{ + { "version", 0, 0, 'V' }, + { "help", 0, 0, 'h' }, + { "xhelp", 0, 0, GO_XHELP }, + { "allow-all", 0, 0, GO_ALLOW_ALL }, + { "allowall", 0, 0, GO_ALLOW_ALL }, + { "compatible", 1, 0, GO_COMPATIBLE }, + { "width", 1, 0, GO_WIDTH }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "logging", 0, 0, 'L' }, + { "de", 0, 0, GO_DE }, + { "colors", 2, 0, GO_COLORS }, + { "no-colors", 0, 0, GO_NO_COLORS }, + { "nocolors", 0, 0, GO_NO_COLORS }, + { "ct-code", 0, 0, GO_CT_CODE }, + { "ctcode", 0, 0, GO_CT_CODE }, + { "le-code", 2, 0, GO_LE_CODE }, + { "lecode", 2, 0, GO_LE_CODE }, + { "chdir", 1, 0, GO_CHDIR }, + { "const", 1, 0, 'c' }, + { "touch", 0, 0, GO_TOUCH }, + { "auto", 0, 0, GO_AUTO }, + { "set-flags", 1, 0, GO_SET_FLAGS }, + { "setflags", 1, 0, GO_SET_FLAGS }, + { "set-scale", 1, 0, GO_SET_SCALE }, + { "setscale", 1, 0, GO_SET_SCALE }, + { "set-rot", 1, 0, GO_SET_ROT }, + { "setrot", 1, 0, GO_SET_ROT }, + { "set-x", 1, 0, GO_SET_X }, + { "setx", 1, 0, GO_SET_X }, + { "set-y", 1, 0, GO_SET_Y }, + { "sety", 1, 0, GO_SET_Y }, + { "set-z", 1, 0, GO_SET_Z }, + { "setz", 1, 0, GO_SET_Z }, + { "xcenter", 0, 0, GO_XCENTER }, + { "ycenter", 0, 0, GO_YCENTER }, + { "zcenter", 0, 0, GO_ZCENTER }, + { "center", 0, 0, GO_CENTER }, + { "scale", 1, 0, GO_SCALE }, + { "shift", 1, 0, GO_SHIFT }, + { "xss", 1, 0, GO_XSS }, + { "yss", 1, 0, GO_YSS }, + { "zss", 1, 0, GO_ZSS }, + { "rot", 1, 0, GO_ROT }, + { "xrot", 1, 0, GO_XROT }, + { "yrot", 1, 0, GO_YROT }, + { "hrot", 1, 0, GO_YROT }, + { "zrot", 1, 0, GO_ZROT }, + { "ypos", 1, 0, GO_YPOS }, + { "translate", 1, 0, GO_TRANSLATE }, + { "trans", 1, 0, GO_TRANSLATE }, + { "null", 0, 0, GO_NULL }, + { "next", 0, 0, GO_NEXT }, + { "ascale", 1, 0, GO_ASCALE }, + { "arot", 1, 0, GO_AROT }, + { "tform-script", 1, 0, GO_TFORM_SCRIPT }, + { "tformscript", 1, 0, GO_TFORM_SCRIPT }, + { "minimap", 0, 0, GO_MINIMAP }, + { "rm-gobj", 1, 0, GO_RM_GOBJ }, + { "rmgobj", 1, 0, GO_RM_GOBJ }, + { "battle", 2, 0, GO_BATTLE }, + { "export-flags", 2, 0, GO_EXPORT_FLAGS }, + { "exportflags", 2, 0, GO_EXPORT_FLAGS }, + { "exflags", 2, 0, GO_EXPORT_FLAGS }, + { "route-options", 2, 0, GO_ROUTE_OPTIONS }, + { "routeoptions", 2, 0, GO_ROUTE_OPTIONS }, + { "ropt", 2, 0, GO_ROUTE_OPTIONS }, + { "wim0", 2, 0, GO_WIM0 }, + { "slot", 1, 0, GO_SLOT }, + { "load-kcl", 1, 0, GO_LOAD_KCL }, + { "loadkcl", 1, 0, GO_LOAD_KCL }, + { "kcl", 1, 0, GO_KCL }, + { "kcl-flag", 1, 0, GO_KCL_FLAG }, + { "kclflag", 1, 0, GO_KCL_FLAG }, + { "kcl-script", 1, 0, GO_KCL_SCRIPT }, + { "kclscript", 1, 0, GO_KCL_SCRIPT }, + { "tri-area", 1, 0, GO_TRI_AREA }, + { "triarea", 1, 0, GO_TRI_AREA }, + { "tri-height", 1, 0, GO_TRI_HEIGHT }, + { "triheight", 1, 0, GO_TRI_HEIGHT }, + { "tri-ht", 1, 0, GO_TRI_HEIGHT }, + { "triht", 1, 0, GO_TRI_HEIGHT }, + { "flag-file", 1, 0, GO_FLAG_FILE }, + { "flagfile", 1, 0, GO_FLAG_FILE }, + { "xtridata", 2, 0, GO_XTRIDATA }, + { "xtri", 2, 0, GO_XTRIDATA }, + { "kmp", 1, 0, GO_KMP }, + { "speed-mod", 1, 0, GO_SPEED_MOD }, + { "speedmod", 1, 0, GO_SPEED_MOD }, + { "ktpt2", 1, 0, GO_KTPT2 }, + { "tform-kmp", 1, 0, GO_TFORM_KMP }, + { "tformkmp", 1, 0, GO_TFORM_KMP }, + { "tkmp", 1, 0, GO_TFORM_KMP }, + { "repair-xpflags", 1, 0, GO_REPAIR_XPF }, + { "repairxpflags", 1, 0, GO_REPAIR_XPF }, + { "repxpf", 1, 0, GO_REPAIR_XPF }, + { "gamemodes", 1, 0, GO_GAMEMODES }, + { "gmodes", 1, 0, GO_GAMEMODES }, + { "mdl", 1, 0, GO_MDL }, + { "pat", 1, 0, GO_PAT }, + { "patch-file", 1, 0, GO_PATCH_FILE }, + { "patchfile", 1, 0, GO_PATCH_FILE }, + { "pfile", 1, 0, GO_PATCH_FILE }, + { "kmg-limit", 1, 0, GO_KMG_LIMIT }, + { "kmglimit", 1, 0, GO_KMG_LIMIT }, + { "kmg-copy", 1, 0, GO_KMG_COPY }, + { "kmgcopy", 1, 0, GO_KMG_COPY }, + { "lt-clear", 0, 0, GO_LT_CLEAR }, + { "ltclear", 0, 0, GO_LT_CLEAR }, + { "lt-online", 1, 0, GO_LT_ONLINE }, + { "ltonline", 1, 0, GO_LT_ONLINE }, + { "lt-n-players", 1, 0, GO_LT_N_PLAYERS }, + { "lt-nplayers", 1, 0, GO_LT_N_PLAYERS }, + { "ltnplayers", 1, 0, GO_LT_N_PLAYERS }, + { "lt-cond-bit", 1, 0, GO_LT_COND_BIT }, + { "lt-condbit", 1, 0, GO_LT_COND_BIT }, + { "ltcondbit", 1, 0, GO_LT_COND_BIT }, + { "lt-game-mode", 1, 0, GO_LT_GAME_MODE }, + { "lt-gamemode", 1, 0, GO_LT_GAME_MODE }, + { "ltgamemode", 1, 0, GO_LT_GAME_MODE }, + { "lt-engine", 1, 0, GO_LT_ENGINE }, + { "ltengine", 1, 0, GO_LT_ENGINE }, + { "lt-random", 1, 0, GO_LT_RANDOM }, + { "ltrandom", 1, 0, GO_LT_RANDOM }, + { "lex-purge", 0, 0, GO_LEX_PURGE }, + { "lexpurge", 0, 0, GO_LEX_PURGE }, + { "load-bmg", 1, 0, GO_LOAD_BMG }, + { "loadbmg", 1, 0, GO_LOAD_BMG }, + { "patch-bmg", 1, 0, GO_PATCH_BMG }, + { "patchbmg", 1, 0, GO_PATCH_BMG }, + { "pb", 1, 0, GO_PATCH_BMG }, + { "macro-bmg", 1, 0, GO_MACRO_BMG }, + { "macrobmg", 1, 0, GO_MACRO_BMG }, + { "mb", 1, 0, GO_MACRO_BMG }, + { "filter-bmg", 1, 0, GO_FILTER_BMG }, + { "filterbmg", 1, 0, GO_FILTER_BMG }, + { "fb", 1, 0, GO_FILTER_BMG }, + { "msg", 1, 0, GO_FILTER_BMG }, + { "cache", 1, 0, GO_CACHE }, + { "cname", 1, 0, GO_CNAME }, + { "id", 0, 0, GO_ID }, + { "base64", 2, 0, GO_BASE64 }, + { "db64", 2, 0, GO_DB64 }, + { "coding", 1, 0, GO_CODING }, + { "verify", 0, 0, GO_VERIFY }, + { "round", 0, 0, GO_ROUND }, + { "long", 0, 0, 'l' }, + { "no-header", 0, 0, 'H' }, + { "noheader", 0, 0, 'H' }, + { "brief", 0, 0, 'B' }, + { "pipe", 0, 0, GO_PIPE }, + { "delta", 0, 0, GO_DELTA }, + { "diff", 0, 0, GO_DIFF }, + { "epsilon", 1, 0, GO_EPSILON }, + { "bmg-encoding", 1, 0, GO_BMG_ENCODING }, + { "bmgencoding", 1, 0, GO_BMG_ENCODING }, + { "bmg-inf-size", 1, 0, GO_BMG_INF_SIZE }, + { "bmginfsize", 1, 0, GO_BMG_INF_SIZE }, + { "bmg-mid", 2, 0, GO_BMG_MID }, + { "bmgmid", 2, 0, GO_BMG_MID }, + { "force-attrib", 1, 0, GO_FORCE_ATTRIB }, + { "forceattrib", 1, 0, GO_FORCE_ATTRIB }, + { "def-attrib", 1, 0, GO_DEF_ATTRIB }, + { "defattrib", 1, 0, GO_DEF_ATTRIB }, + { "no-attrib", 0, 0, GO_NO_ATTRIB }, + { "noattrib", 0, 0, GO_NO_ATTRIB }, + { "x-escapes", 0, 0, GO_X_ESCAPES }, + { "xescapes", 0, 0, GO_X_ESCAPES }, + { "old-escapes", 0, 0, GO_OLD_ESCAPES }, + { "oldescapes", 0, 0, GO_OLD_ESCAPES }, + { "single-line", 0, 0, '1' }, + { "singleline", 0, 0, '1' }, + { "no-bmg-colors", 0, 0, GO_NO_BMG_COLORS }, + { "nobmgcolors", 0, 0, GO_NO_BMG_COLORS }, + { "bmg-colors", 0, 0, GO_BMG_COLORS }, + { "bmgcolors", 0, 0, GO_BMG_COLORS }, + { "no-bmg-inline", 0, 0, GO_NO_BMG_INLINE }, + { "nobmginline", 0, 0, GO_NO_BMG_INLINE }, + { "export", 0, 0, 'X' }, + { "no-param", 0, 0, 'P' }, + { "noparam", 0, 0, 'P' }, + { "sort", 1, 0, 'S' }, + { "max-file-size", 1, 0, 'M' }, + { "maxfilesize", 1, 0, 'M' }, + { "tracks", 1, 0, 'T' }, + { "arenas", 1, 0, 'A' }, + { "autoadd-path", 1, 0, GO_AUTOADD_PATH }, + { "autoaddpath", 1, 0, GO_AUTOADD_PATH }, + { "transform", 1, 0, 'x' }, + { "strip", 0, 0, GO_STRIP }, + { "utf-8", 0, 0, GO_UTF_8 }, + { "utf8", 0, 0, GO_UTF_8 }, + { "no-utf-8", 0, 0, GO_NO_UTF_8 }, + { "no-utf8", 0, 0, GO_NO_UTF_8 }, + { "noutf8", 0, 0, GO_NO_UTF_8 }, + { "test", 0, 0, 't' }, + { "force", 0, 0, GO_FORCE }, + { "repair-magics", 2, 0, GO_REPAIR_MAGICS }, + { "repairmagics", 2, 0, GO_REPAIR_MAGICS }, + { "tiny", 1, 0, GO_TINY }, + { "yaz0", 0, 0, GO_YAZ0 }, + { "yaz1", 0, 0, GO_YAZ1 }, + { "xyz", 0, 0, GO_XYZ }, + { "bz", 0, 0, GO_BZ }, + { "sh", 0, 0, GO_SH }, + { "bash", 0, 0, GO_BASH }, + { "json", 0, 0, GO_JSON }, + { "php", 0, 0, GO_PHP }, + { "makedoc", 0, 0, GO_MAKEDOC }, + { "var", 1, 0, GO_VAR }, + { "array", 0, 0, GO_ARRAY }, + { "avar", 1, 0, GO_AVAR }, + { "analyze", 1, 0, GO_ANALYZE }, + { "analyse", 1, 0, GO_ANALYZE }, + { "analyze-mode", 1, 0, GO_ANALYZE_MODE }, + { "ana-mode", 1, 0, GO_ANALYZE_MODE }, + { "anamode", 1, 0, GO_ANALYZE_MODE }, + { "amd", 1, 0, GO_ANALYZE_MODE }, + { "OLD", 0, 0, GO_OLD }, + { "STD", 0, 0, GO_STD }, + { "NEW", 0, 0, GO_NEW }, + { "EXTRACT", 1, 0, GO_EXTRACT }, + { "source", 1, 0, 's' }, + { "src", 1, 0, 's' }, + { "dest", 1, 0, 'd' }, + { "DEST", 1, 0, 'D' }, + { "esc", 1, 0, 'E' }, + { "overwrite", 0, 0, 'o' }, + { "number", 0, 0, GO_NUMBER }, + { "num", 0, 0, GO_NUMBER }, + { "remove-src", 0, 0, GO_REMOVE_SRC }, + { "removesrc", 0, 0, GO_REMOVE_SRC }, + { "rm-src", 0, 0, GO_REMOVE_SRC }, + { "rmsrc", 0, 0, GO_REMOVE_SRC }, + { "remove-dest", 0, 0, 'r' }, + { "removedest", 0, 0, 'r' }, + { "rm-dest", 0, 0, 'r' }, + { "rmdest", 0, 0, 'r' }, + { "remove", 0, 0, 'r' }, + { "update", 0, 0, 'u' }, + { "preserve", 0, 0, 'p' }, + { "ignore", 0, 0, 'i' }, + { "ignore-setup", 0, 0, GO_IGNORE_SETUP }, + { "ignoresetup", 0, 0, GO_IGNORE_SETUP }, + { "purge", 0, 0, GO_PURGE }, + { "align-u8", 1, 0, GO_ALIGN_U8 }, + { "alignu8", 1, 0, GO_ALIGN_U8 }, + { "align-pack", 1, 0, GO_ALIGN_PACK }, + { "alignpack", 1, 0, GO_ALIGN_PACK }, + { "align-brres", 1, 0, GO_ALIGN_BRRES }, + { "alignbrres", 1, 0, GO_ALIGN_BRRES }, + { "align-breff", 1, 0, GO_ALIGN_BREFF }, + { "alignbreff", 1, 0, GO_ALIGN_BREFF }, + { "align-breft", 1, 0, GO_ALIGN_BREFT }, + { "alignbreft", 1, 0, GO_ALIGN_BREFT }, + { "align", 1, 0, GO_ALIGN }, + { "encode-all", 0, 0, GO_ENCODE_ALL }, + { "encodeall", 0, 0, GO_ENCODE_ALL }, + { "eall", 0, 0, GO_ENCODE_ALL }, + { "encode-img", 0, 0, GO_ENCODE_IMG }, + { "encodeimg", 0, 0, GO_ENCODE_IMG }, + { "eimg", 0, 0, GO_ENCODE_IMG }, + { "no-encode", 0, 0, GO_NO_ENCODE }, + { "noencode", 0, 0, GO_NO_ENCODE }, + { "noenc", 0, 0, GO_NO_ENCODE }, + { "no-recurse", 0, 0, GO_NO_RECURSE }, + { "norecurse", 0, 0, GO_NO_RECURSE }, + { "norec", 0, 0, GO_NO_RECURSE }, + { "auto-add", 0, 0, GO_AUTO_ADD }, + { "autoadd", 0, 0, GO_AUTO_ADD }, + { "aadd", 0, 0, GO_AUTO_ADD }, + { "no-echo", 0, 0, GO_NO_ECHO }, + { "noecho", 0, 0, GO_NO_ECHO }, + { "no-check", 0, 0, 'N' }, + { "nocheck", 0, 0, 'N' }, + { "pt-dir", 2, 0, GO_PT_DIR }, + { "ptdir", 2, 0, GO_PT_DIR }, + { "pdir", 2, 0, GO_PT_DIR }, + { "rm-aiparam", 0, 0, GO_RM_AIPARAM }, + { "rmaiparam", 0, 0, GO_RM_AIPARAM }, + { "rmai", 0, 0, GO_RM_AIPARAM }, + { "u8", 0, 0, GO_U8 }, + { "szs", 0, 0, GO_SZS }, + { "wu8", 0, 0, GO_WU8 }, + { "xwu8", 0, 0, GO_XWU8 }, + { "wbz", 0, 0, GO_WBZ }, + { "pack", 0, 0, GO_PACK }, + { "brres", 0, 0, GO_BRRES }, + { "bres", 0, 0, GO_BRRES }, + { "breff", 0, 0, GO_BREFF }, + { "breft", 0, 0, GO_BREFT }, + { "no-compress", 0, 0, GO_NO_COMPRESS }, + { "nocompress", 0, 0, GO_NO_COMPRESS }, + { "noc", 0, 0, GO_NO_COMPRESS }, + { "compr", 1, 0, 'C' }, + { "compress", 1, 0, 'C' }, + { "fast", 0, 0, GO_FAST }, + { "norm", 0, 0, 'n' }, + { "links", 0, 0, GO_LINKS }, + { "basedir", 1, 0, GO_BASEDIR }, + { "bdir", 1, 0, GO_BASEDIR }, + { "recurse", 2, 0, 'R' }, + { "ext", 0, 0, 'e' }, + { "decode", 0, 0, GO_DECODE }, + { "dec", 0, 0, GO_DECODE }, + { "mipmaps", 0, 0, GO_MIPMAPS }, + { "mm", 0, 0, GO_MIPMAPS }, + { "no-mipmaps", 0, 0, GO_NO_MIPMAPS }, + { "nomipmaps", 0, 0, GO_NO_MIPMAPS }, + { "no-mm", 0, 0, GO_NO_MIPMAPS }, + { "nomm", 0, 0, GO_NO_MIPMAPS }, + { "n-mipmaps", 1, 0, GO_N_MIPMAPS }, + { "nmipmaps", 1, 0, GO_N_MIPMAPS }, + { "n-mm", 1, 0, GO_N_MIPMAPS }, + { "nmm", 1, 0, GO_N_MIPMAPS }, + { "max-mipmaps", 1, 0, GO_MAX_MIPMAPS }, + { "maxmipmaps", 1, 0, GO_MAX_MIPMAPS }, + { "max-mm", 1, 0, GO_MAX_MIPMAPS }, + { "maxmm", 1, 0, GO_MAX_MIPMAPS }, + { "mipmap-size", 1, 0, GO_MIPMAP_SIZE }, + { "mipmapsize", 1, 0, GO_MIPMAP_SIZE }, + { "mm-size", 1, 0, GO_MIPMAP_SIZE }, + { "mmsize", 1, 0, GO_MIPMAP_SIZE }, + { "fast-mipmaps", 0, 0, GO_FAST_MIPMAPS }, + { "fastmipmaps", 0, 0, GO_FAST_MIPMAPS }, + { "fast-mm", 0, 0, GO_FAST_MIPMAPS }, + { "fastmm", 0, 0, GO_FAST_MIPMAPS }, + { "cmpr-default", 1, 0, GO_CMPR_DEFAULT }, + { "cut", 0, 0, GO_CUT }, + { "all", 0, 0, 'a' }, + { "raw", 0, 0, GO_RAW }, + { "sections", 0, 0, GO_SECTIONS }, + + {0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OptionUsed & OptionIndex /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 OptionUsed[OPT__N_TOTAL+1] = {0}; + +static const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE] = +{ + /* 0x000 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x010 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x020 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, + /* 0x030 */ 0, + /* 0x031 1 */ OPT_SINGLE_LINE, + /* 0x032 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0, + /* 0x041 A */ OPT_ARENAS, + /* 0x042 B */ OPT_BRIEF, + /* 0x043 C */ OPT_COMPRESS, + /* 0x044 D */ OPT_DEST2, + /* 0x045 E */ OPT_ESC, + /* 0x046 */ 0,0, + /* 0x048 H */ OPT_NO_HEADER, + /* 0x049 */ 0,0,0, + /* 0x04c L */ OPT_LOGGING, + /* 0x04d M */ OPT_MAX_FILE_SIZE, + /* 0x04e N */ OPT_NO_CHECK, + /* 0x04f */ 0, + /* 0x050 P */ OPT_NO_PARAM, + /* 0x051 */ 0, + /* 0x052 R */ OPT_RECURSE, + /* 0x053 S */ OPT_SORT, + /* 0x054 T */ OPT_TRACKS, + /* 0x055 */ 0, + /* 0x056 V */ OPT_VERSION, + /* 0x057 */ 0, + /* 0x058 X */ OPT_EXPORT, + /* 0x059 */ 0,0,0,0, 0,0,0,0, + /* 0x061 a */ OPT_ALL, + /* 0x062 */ 0, + /* 0x063 c */ OPT_CONST, + /* 0x064 d */ OPT_DEST, + /* 0x065 e */ OPT_EXT, + /* 0x066 */ 0,0, + /* 0x068 h */ OPT_HELP, + /* 0x069 i */ OPT_IGNORE, + /* 0x06a */ 0,0, + /* 0x06c l */ OPT_LONG, + /* 0x06d */ 0, + /* 0x06e n */ OPT_NORM, + /* 0x06f o */ OPT_OVERWRITE, + /* 0x070 p */ OPT_PRESERVE, + /* 0x071 q */ OPT_QUIET, + /* 0x072 r */ OPT_REMOVE_DEST, + /* 0x073 s */ OPT_SOURCE, + /* 0x074 t */ OPT_TEST, + /* 0x075 u */ OPT_UPDATE, + /* 0x076 v */ OPT_VERBOSE, + /* 0x077 */ 0, + /* 0x078 x */ OPT_TRANSFORM, + /* 0x079 */ 0,0,0,0, 0,0,0, + /* 0x080 */ OPT_XHELP, + /* 0x081 */ OPT_ALLOW_ALL, + /* 0x082 */ OPT_COMPATIBLE, + /* 0x083 */ OPT_WIDTH, + /* 0x084 */ OPT_DE, + /* 0x085 */ OPT_COLORS, + /* 0x086 */ OPT_NO_COLORS, + /* 0x087 */ OPT_CT_CODE, + /* 0x088 */ OPT_LE_CODE, + /* 0x089 */ OPT_CHDIR, + /* 0x08a */ OPT_TOUCH, + /* 0x08b */ OPT_AUTO, + /* 0x08c */ OPT_SET_FLAGS, + /* 0x08d */ OPT_SET_SCALE, + /* 0x08e */ OPT_SET_ROT, + /* 0x08f */ OPT_SET_X, + /* 0x090 */ OPT_SET_Y, + /* 0x091 */ OPT_SET_Z, + /* 0x092 */ OPT_XCENTER, + /* 0x093 */ OPT_YCENTER, + /* 0x094 */ OPT_ZCENTER, + /* 0x095 */ OPT_CENTER, + /* 0x096 */ OPT_SCALE, + /* 0x097 */ OPT_SHIFT, + /* 0x098 */ OPT_XSS, + /* 0x099 */ OPT_YSS, + /* 0x09a */ OPT_ZSS, + /* 0x09b */ OPT_ROT, + /* 0x09c */ OPT_XROT, + /* 0x09d */ OPT_YROT, + /* 0x09e */ OPT_ZROT, + /* 0x09f */ OPT_YPOS, + /* 0x0a0 */ OPT_TRANSLATE, + /* 0x0a1 */ OPT_NULL, + /* 0x0a2 */ OPT_NEXT, + /* 0x0a3 */ OPT_ASCALE, + /* 0x0a4 */ OPT_AROT, + /* 0x0a5 */ OPT_TFORM_SCRIPT, + /* 0x0a6 */ OPT_MINIMAP, + /* 0x0a7 */ OPT_RM_GOBJ, + /* 0x0a8 */ OPT_BATTLE, + /* 0x0a9 */ OPT_EXPORT_FLAGS, + /* 0x0aa */ OPT_ROUTE_OPTIONS, + /* 0x0ab */ OPT_WIM0, + /* 0x0ac */ OPT_SLOT, + /* 0x0ad */ OPT_LOAD_KCL, + /* 0x0ae */ OPT_KCL, + /* 0x0af */ OPT_KCL_FLAG, + /* 0x0b0 */ OPT_KCL_SCRIPT, + /* 0x0b1 */ OPT_TRI_AREA, + /* 0x0b2 */ OPT_TRI_HEIGHT, + /* 0x0b3 */ OPT_FLAG_FILE, + /* 0x0b4 */ OPT_XTRIDATA, + /* 0x0b5 */ OPT_KMP, + /* 0x0b6 */ OPT_SPEED_MOD, + /* 0x0b7 */ OPT_KTPT2, + /* 0x0b8 */ OPT_TFORM_KMP, + /* 0x0b9 */ OPT_REPAIR_XPF, + /* 0x0ba */ OPT_GAMEMODES, + /* 0x0bb */ OPT_MDL, + /* 0x0bc */ OPT_PAT, + /* 0x0bd */ OPT_PATCH_FILE, + /* 0x0be */ OPT_KMG_LIMIT, + /* 0x0bf */ OPT_KMG_COPY, + /* 0x0c0 */ OPT_LT_CLEAR, + /* 0x0c1 */ OPT_LT_ONLINE, + /* 0x0c2 */ OPT_LT_N_PLAYERS, + /* 0x0c3 */ OPT_LT_COND_BIT, + /* 0x0c4 */ OPT_LT_GAME_MODE, + /* 0x0c5 */ OPT_LT_ENGINE, + /* 0x0c6 */ OPT_LT_RANDOM, + /* 0x0c7 */ OPT_LEX_PURGE, + /* 0x0c8 */ OPT_LOAD_BMG, + /* 0x0c9 */ OPT_PATCH_BMG, + /* 0x0ca */ OPT_MACRO_BMG, + /* 0x0cb */ OPT_FILTER_BMG, + /* 0x0cc */ OPT_CACHE, + /* 0x0cd */ OPT_CNAME, + /* 0x0ce */ OPT_ID, + /* 0x0cf */ OPT_BASE64, + /* 0x0d0 */ OPT_DB64, + /* 0x0d1 */ OPT_CODING, + /* 0x0d2 */ OPT_VERIFY, + /* 0x0d3 */ OPT_ROUND, + /* 0x0d4 */ OPT_PIPE, + /* 0x0d5 */ OPT_DELTA, + /* 0x0d6 */ OPT_DIFF, + /* 0x0d7 */ OPT_EPSILON, + /* 0x0d8 */ OPT_BMG_ENCODING, + /* 0x0d9 */ OPT_BMG_INF_SIZE, + /* 0x0da */ OPT_BMG_MID, + /* 0x0db */ OPT_FORCE_ATTRIB, + /* 0x0dc */ OPT_DEF_ATTRIB, + /* 0x0dd */ OPT_NO_ATTRIB, + /* 0x0de */ OPT_X_ESCAPES, + /* 0x0df */ OPT_OLD_ESCAPES, + /* 0x0e0 */ OPT_NO_BMG_COLORS, + /* 0x0e1 */ OPT_BMG_COLORS, + /* 0x0e2 */ OPT_NO_BMG_INLINE, + /* 0x0e3 */ OPT_AUTOADD_PATH, + /* 0x0e4 */ OPT_STRIP, + /* 0x0e5 */ OPT_UTF_8, + /* 0x0e6 */ OPT_NO_UTF_8, + /* 0x0e7 */ OPT_FORCE, + /* 0x0e8 */ OPT_REPAIR_MAGICS, + /* 0x0e9 */ OPT_TINY, + /* 0x0ea */ OPT_YAZ0, + /* 0x0eb */ OPT_YAZ1, + /* 0x0ec */ OPT_XYZ, + /* 0x0ed */ OPT_BZ, + /* 0x0ee */ OPT_SH, + /* 0x0ef */ OPT_BASH, + /* 0x0f0 */ OPT_JSON, + /* 0x0f1 */ OPT_PHP, + /* 0x0f2 */ OPT_MAKEDOC, + /* 0x0f3 */ OPT_VAR, + /* 0x0f4 */ OPT_ARRAY, + /* 0x0f5 */ OPT_AVAR, + /* 0x0f6 */ OPT_ANALYZE, + /* 0x0f7 */ OPT_ANALYZE_MODE, + /* 0x0f8 */ OPT_OLD, + /* 0x0f9 */ OPT_STD, + /* 0x0fa */ OPT_NEW, + /* 0x0fb */ OPT_EXTRACT, + /* 0x0fc */ OPT_NUMBER, + /* 0x0fd */ OPT_REMOVE_SRC, + /* 0x0fe */ OPT_IGNORE_SETUP, + /* 0x0ff */ OPT_PURGE, + /* 0x100 */ OPT_ALIGN_U8, + /* 0x101 */ OPT_ALIGN_PACK, + /* 0x102 */ OPT_ALIGN_BRRES, + /* 0x103 */ OPT_ALIGN_BREFF, + /* 0x104 */ OPT_ALIGN_BREFT, + /* 0x105 */ OPT_ALIGN, + /* 0x106 */ OPT_ENCODE_ALL, + /* 0x107 */ OPT_ENCODE_IMG, + /* 0x108 */ OPT_NO_ENCODE, + /* 0x109 */ OPT_NO_RECURSE, + /* 0x10a */ OPT_AUTO_ADD, + /* 0x10b */ OPT_NO_ECHO, + /* 0x10c */ OPT_PT_DIR, + /* 0x10d */ OPT_RM_AIPARAM, + /* 0x10e */ OPT_U8, + /* 0x10f */ OPT_SZS, + /* 0x110 */ OPT_WU8, + /* 0x111 */ OPT_XWU8, + /* 0x112 */ OPT_WBZ, + /* 0x113 */ OPT_PACK, + /* 0x114 */ OPT_BRRES, + /* 0x115 */ OPT_BREFF, + /* 0x116 */ OPT_BREFT, + /* 0x117 */ OPT_NO_COMPRESS, + /* 0x118 */ OPT_FAST, + /* 0x119 */ OPT_LINKS, + /* 0x11a */ OPT_BASEDIR, + /* 0x11b */ OPT_DECODE, + /* 0x11c */ OPT_MIPMAPS, + /* 0x11d */ OPT_NO_MIPMAPS, + /* 0x11e */ OPT_N_MIPMAPS, + /* 0x11f */ OPT_MAX_MIPMAPS, + /* 0x120 */ OPT_MIPMAP_SIZE, + /* 0x121 */ OPT_FAST_MIPMAPS, + /* 0x122 */ OPT_CMPR_DEFAULT, + /* 0x123 */ OPT_CUT, + /* 0x124 */ OPT_RAW, + /* 0x125 */ OPT_SECTIONS, + /* 0x126 */ 0,0,0,0, 0,0, +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// opt_allowed_cmd_* /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static u8 option_allowed_cmd_VERSION[130] = // cmd #1 +{ + 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,1,0,1, 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, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1 +}; + +static u8 option_allowed_cmd_HELP[130] = // cmd #2 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1 +}; + +static u8 option_allowed_cmd_ARGTEST[130] = // cmd #3 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1 +}; + +static u8 option_allowed_cmd_TEST[130] = // cmd #4 +{ + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1 +}; + +static u8 option_allowed_cmd_COLORS[130] = // cmd #5 +{ + 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,1,0,1, 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, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_ERROR[130] = // cmd #6 +{ + 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,1,1,1, 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, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1 +}; + +static u8 option_allowed_cmd_FILETYPE[130] = // cmd #7 +{ + 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,1,1,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,1,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 u8 option_allowed_cmd_FILEATTRIB[130] = // cmd #8 +{ + 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,1,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,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_BRSUB[130] = // cmd #9 +{ + 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,1,1,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,1,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 u8 option_allowed_cmd_SYMBOLS[130] = // cmd #10 +{ + 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,1,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,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_FUNCTIONS[130] = // cmd #11 +{ + 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,1,1,1, 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, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_CALCULATE[130] = // cmd #12 +{ + 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, + 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, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_MATRIX[130] = // cmd #13 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,1, 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, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_FLOAT[130] = // cmd #14 +{ + 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,1,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,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_VR_CALC[130] = // cmd #15 +{ + 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,1,1, 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, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_VR_RACE[130] = // cmd #16 +{ + 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,1,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,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_AUTOADD[130] = // cmd #17 +{ + 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, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,0,1,0,0, 1,0,1,1,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 u8 option_allowed_cmd_TRACKS[130] = // cmd #18 +{ + 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,1,0,0, 0,0,0,0,0, 0,0,1,0,0, 1,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,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,1,0,0 +}; + +static u8 option_allowed_cmd_SCANCACHE[130] = // cmd #19 +{ + 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, + 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, 1,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 u8 option_allowed_cmd_EXPORT[130] = // cmd #20 +{ + 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, + 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, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_CODE[130] = // cmd #21 +{ + 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, + 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, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_RECODE[130] = // cmd #22 +{ + 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, + 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, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_SUBFILE[130] = // cmd #23 +{ + 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, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,0, 1,1,1,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 u8 option_allowed_cmd_LIST[130] = // cmd #24 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,0,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_LIST_L[130] = // cmd #25 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,0,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_LIST_LL[130] = // cmd #26 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,0,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_LIST_LLL[130] = // cmd #27 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,0,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_LIST_A[130] = // cmd #28 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,0,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_LIST_LA[130] = // cmd #29 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,0,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_NAME_REF[130] = // cmd #30 +{ + 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,1,0,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,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,0 +}; + +static u8 option_allowed_cmd_ILIST[130] = // cmd #31 +{ + 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,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_ILIST_L[130] = // cmd #32 +{ + 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,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_ILIST_LL[130] = // cmd #33 +{ + 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,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_ILIST_A[130] = // cmd #34 +{ + 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,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_ILIST_LA[130] = // cmd #35 +{ + 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,1,1,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_MEMORY[130] = // cmd #36 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_MEMORY_A[130] = // cmd #37 +{ + 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,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,0,0 +}; + +static u8 option_allowed_cmd_DUMP[130] = // cmd #38 +{ + 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, + 0,0,0,0,0, 0,0,0,0,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,0,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_SHA1[130] = // cmd #39 +{ + 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,1,1,1,1, 1,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,0,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_ANALYZE[130] = // cmd #40 +{ + 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,1,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,1, 1,0,0,0,0, 0,0,0,1,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 u8 option_allowed_cmd_DISTRIBUTION[130] = // cmd #41 +{ + 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,1,1,1, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1, 1,0,0,1,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,1,1, 0,0,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_DIFF[130] = // cmd #42 +{ + 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, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 0,1,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,1,0,0,0, + 0,0,0,0,0, 0,1,1,0,0 +}; + +static u8 option_allowed_cmd_CHECK[130] = // cmd #43 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,1,0,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,1, 0,0,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_SLOTS[130] = // cmd #44 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,0,0,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 0,0,0,0,1, 0,0,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_STGI[130] = // cmd #45 +{ + 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,1,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,1,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 u8 option_allowed_cmd_ISARENA[130] = // cmd #46 +{ + 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,1,1, 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,1,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 u8 option_allowed_cmd_NORMALIZE[130] = // cmd #47 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,1,1,1,1, 1,1,1,1,0, 0,1,1,1,1, + 1,1,0,0,0, 0,1,0,0,1, 1,1,1,1,1, 1,0,0,0,0, 0,0,1,1,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_PATCH[130] = // cmd #48 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,1,1,1,1, 1,1,1,1,0, 0,1,1,1,1, + 1,1,0,0,0, 0,1,0,0,1, 1,1,1,1,1, 1,0,0,0,0, 0,1,1,1,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_COPY[130] = // cmd #49 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,1,1,1,1, 1,1,1,1,0, 0,1,1,1,1, + 1,1,0,0,0, 0,1,0,0,1, 1,1,1,1,1, 1,0,0,0,0, 0,1,1,1,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_DUPLICATE[130] = // cmd #50 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,1,0,1,1, 1,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,1, 1,1,1,1,1, 1,1,1,1,0, 0,1,1,1,1, + 1,1,0,0,0, 0,1,0,0,1, 1,1,1,1,1, 1,0,0,0,0, 0,1,1,1,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_MINIMAP[130] = // cmd #51 +{ + 0,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,0, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,1,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, 1,0,1,1,0, 0,1,1,1,1, + 1,1,0,0,0, 0,1,0,0,1, 1,0,0,0,0, 0,0,0,0,0, 0,1,1,1,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_COMPRESS[130] = // cmd #52 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,1,1,1,1, 1,1,1,1,0, 0,1,1,1,1, + 1,1,0,0,0, 0,1,0,0,1, 1,1,1,1,1, 1,0,0,0,0, 0,1,1,1,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_DECOMPRESS[130] = // cmd #53 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,1,1,1,1, 1,1,1,1,0, 0,1,1,1,1, + 1,1,0,0,0, 0,1,0,0,1, 1,1,0,1,0, 0,0,0,0,0, 0,0,0,1,1, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_ENCODE[130] = // cmd #54 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 0,1,1,1,1, + 1,1,1,1,1, 1,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_CREATE[130] = // cmd #55 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,0,0,0,1, 1,1,1,1,0, 1,1,1,0,1, 0,1,1,1,1, + 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,0,1, 0,0,0,0,1, + 1,1,1,1,1, 1,0,1,0,0 +}; + +static u8 option_allowed_cmd_UPDATE[130] = // cmd #56 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,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,1, 1,0,0,1,1, 1,1,1,1,0, 1,1,1,1,0, 0,0,0,0,0, + 0,0,1,1,1, 1,0,1,1,0, 0,0,0,0,0, 0,0,0,0,0, 1,1,1,0,0, 0,1,0,0,0, + 0,0,0,0,0, 0,0,1,0,0 +}; + +static u8 option_allowed_cmd_EXTRACT[130] = // cmd #57 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,1, 1,1,1,1,0, 1,1,1,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,1, + 1,0,0,0,1, 1,1,1,1,0 +}; + +static u8 option_allowed_cmd_XDECODE[130] = // cmd #58 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,1, 1,1,1,1,0, 1,1,1,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,1, + 1,0,0,0,1, 1,1,1,1,0 +}; + +static u8 option_allowed_cmd_XEXPORT[130] = // cmd #59 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,1, 1,1,1,1,0, 1,1,1,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,1, + 1,0,0,0,1, 1,1,1,1,0 +}; + +static u8 option_allowed_cmd_XALL[130] = // cmd #60 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,0,1,1, 0,1,1,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1,1,0,0, 0,1,1,0,1, 1,1,1,1,0, 1,1,1,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,1, + 1,0,0,0,1, 1,1,1,1,0 +}; + +static u8 option_allowed_cmd_XCOMMON[130] = // cmd #61 +{ + 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, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,0, 1,1,1,1,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 u8 option_allowed_cmd_BINARY[130] = // cmd #62 +{ + 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, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,0, 1,1,1,1,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 u8 option_allowed_cmd_TEXT[130] = // cmd #63 +{ + 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,1,1,1, 0,1,1,0,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,0,0, 0,0,0,0,1, 1,1,1,1,0, 1,1,1,1,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,1,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_CAT[130] = // cmd #64 +{ + 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,1,1,1, 0,1,1,0,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,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,1,0, + 0,0,0,0,0, 0,0,0,0,0 +}; + +static u8 option_allowed_cmd_BMG[130] = // cmd #65 +{ + 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,1,1,1, 0,0,0,0,1, 1,1,1,1,1, + 1,1,1,1,1, 1,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,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 u8 option_allowed_cmd_KCL[130] = // cmd #66 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,0,1,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,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 u8 option_allowed_cmd_KMP[130] = // cmd #67 +{ + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, + 1,1,1,1,0, 1,0,0,1,1, 1,0,0,0,0, 0,0,0,1,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1,1,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,1,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 u8 option_allowed_cmd_LEX[130] = // cmd #68 +{ + 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,1,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,1,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,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 u8 option_allowed_cmd_INFO[130] = // cmd #69 +{ + 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,1,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,1,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 u8 option_allowed_cmd_GHOST[130] = // cmd #70 +{ + 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,1,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,1, 1,0,0,0,0, 0,0,0,1,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 u8 option_allowed_cmd_YAZDUMP[130] = // cmd #71 +{ + 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, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,1, 1,1,1,1,0, 1,1,1,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 u8 option_allowed_cmd_VEHICLE[130] = // cmd #72 +{ + 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,1, 0,0,0,0,0, 0,0,0,0,0, + 0,0,0,0,0, 0,0,0,0,0, 0,0,0,1,1, 1,0,1,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 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoOption tabs /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoOption_t * option_tab_tool[] = +{ + OptionInfo + OPT_VERSION, + OptionInfo + OPT_HELP, + OptionInfo + OPT_XHELP, + OptionInfo + OPT_ALLOW_ALL, + OptionInfo + OPT_COMPATIBLE, + OptionInfo + OPT_WIDTH, + OptionInfo + OPT_QUIET, + OptionInfo + OPT_VERBOSE, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_DE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_CT_CODE, + OptionInfo + OPT_LE_CODE, + OptionInfo + OPT_CHDIR, + OptionInfo + OPT_CONST, + OptionInfo + OPT_BATTLE, + OptionInfo + OPT_EXPORT_FLAGS, + OptionInfo + OPT_ROUTE_OPTIONS, + OptionInfo + OPT_WIM0, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_MDL, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_KMG_LIMIT, + OptionInfo + OPT_KMG_COPY, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_CACHE, + OptionInfo + OPT_CNAME, + OptionInfo + OPT_MAX_FILE_SIZE, + OptionInfo + OPT_TRACKS, + OptionInfo + OPT_ARENAS, + OptionInfo + OPT_AUTOADD_PATH, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TEST, + OptionInfo + OPT_FORCE, + OptionInfo + OPT_REPAIR_MAGICS, + OptionInfo + OPT_TINY, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_XYZ, + OptionInfo + OPT_BZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SH, + OptionInfo + OPT_BASH, + OptionInfo + OPT_JSON, + OptionInfo + OPT_PHP, + OptionInfo + OPT_MAKEDOC, + OptionInfo + OPT_VAR, + OptionInfo + OPT_ARRAY, + OptionInfo + OPT_AVAR, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VERSION[] = +{ + &option_cmd_VERSION_BRIEF, + OptionInfo + OPT_SECTIONS, + &option_cmd_VERSION_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_HELP[] = +{ + OptionInfo + OPT_WIDTH, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ARGTEST[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEST[] = +{ + &option_cmd_TEST_VERBOSE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COLORS[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_COLORS_LONG, + &option_cmd_COLORS_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ERROR[] = +{ + OptionInfo + OPT_SECTIONS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ERROR_LONG, + &option_cmd_ERROR_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILETYPE[] = +{ + &option_cmd_FILETYPE_LONG, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FILEATTRIB[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_BRSUB[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NO_HEADER, + &option_cmd_BRSUB_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SYMBOLS[] = +{ + OptionInfo + OPT_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FUNCTIONS[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_FUNCTIONS_BRIEF, + &option_cmd_FUNCTIONS_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CALCULATE[] = +{ + OptionInfo + OPT_CONST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MATRIX[] = +{ + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + + OptionInfo + OPT_NONE, // separator + + &option_cmd_MATRIX_BRIEF, + &option_cmd_MATRIX_LONG, + &option_cmd_MATRIX_QUIET, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_FLOAT[] = +{ + OptionInfo + OPT_ROUND, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VR_CALC[] = +{ + OptionInfo + OPT_NO_HEADER, + &option_cmd_VR_CALC_BRIEF, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VR_RACE[] = +{ + &option_cmd_VR_RACE_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_AUTOADD[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_AUTOADD_QUIET, + &option_cmd_AUTOADD_VERBOSE, + &option_cmd_AUTOADD_DEST, + OptionInfo + OPT_DEST2, + &option_cmd_AUTOADD_OVERWRITE, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_PRESERVE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TRACKS[] = +{ + &option_cmd_TRACKS_VERBOSE, + &option_cmd_TRACKS_ALL, + &option_cmd_TRACKS_LONG, + OptionInfo + OPT_PIPE, + OptionInfo + OPT_LOAD_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SCANCACHE[] = +{ + OptionInfo + OPT_CACHE, + OptionInfo + OPT_PURGE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXPORT[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CODE[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_RECODE[] = +{ + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SUBFILE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + &option_cmd_LIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST_L[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + &option_cmd_LIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST_LL[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + &option_cmd_LIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST_LLL[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + &option_cmd_LIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST_A[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + &option_cmd_LIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LIST_LA[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + &option_cmd_LIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_NAME_REF[] = +{ + &option_cmd_NAME_REF_SORT, + &option_cmd_NAME_REF_BRIEF, + &option_cmd_NAME_REF_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ILIST[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_ALL, + &option_cmd_ILIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ILIST_L[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_ALL, + &option_cmd_ILIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ILIST_LL[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_ALL, + &option_cmd_ILIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ILIST_A[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_ALL, + &option_cmd_ILIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ILIST_LA[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_ALL, + &option_cmd_ILIST_LONG, + OptionInfo + OPT_SORT, + OptionInfo + OPT_NO_HEADER, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MEMORY[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + &option_cmd_MEMORY_DECODE, + &option_cmd_MEMORY_MIPMAPS, + OptionInfo + OPT_CUT, + &option_cmd_MEMORY_ALL, + &option_cmd_MEMORY_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MEMORY_A[] = +{ + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + &option_cmd_MEMORY_DECODE, + &option_cmd_MEMORY_MIPMAPS, + OptionInfo + OPT_CUT, + &option_cmd_MEMORY_ALL, + &option_cmd_MEMORY_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_SLOT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DUMP[] = +{ + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + &option_grp_ENCODE_ALL, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SHA1[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + &option_grp_ENCODE_ALL, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_ID, + OptionInfo + OPT_BASE64, + OptionInfo + OPT_DB64, + OptionInfo + OPT_CODING, + OptionInfo + OPT_VERIFY, + &option_cmd_SHA1_BRIEF, + &option_cmd_SHA1_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ANALYZE[] = +{ + &option_cmd_ANALYZE_LONG, + OptionInfo + OPT_IGNORE, + &option_cmd_ANALYZE_QUIET, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SH, + OptionInfo + OPT_BASH, + OptionInfo + OPT_JSON, + OptionInfo + OPT_PHP, + OptionInfo + OPT_MAKEDOC, + OptionInfo + OPT_VAR, + OptionInfo + OPT_ARRAY, + OptionInfo + OPT_AVAR, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DISTRIBUTION[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + &option_grp_ENCODE_ALL, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_BASE64, + OptionInfo + OPT_DB64, + OptionInfo + OPT_CODING, + &option_cmd_DISTRIBUTION_SOURCE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DIFF[] = +{ + &option_cmd_DIFF_QUIET, + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + OptionInfo + OPT_DEST, + OptionInfo + OPT_ESC, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CHECK[] = +{ + &option_cmd_CHECK_NO_CHECK, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + &option_cmd_CHECK_BRIEF, + &option_cmd_CHECK_LONG, + &option_cmd_CHECK_QUIET, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + &option_grp_ENCODE_ALL, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_SLOTS[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_SLOTS_BRIEF, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + &option_grp_ENCODE_ALL, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_STGI[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_STGI_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ISARENA[] = +{ + OptionInfo + OPT_COLORS, + OptionInfo + OPT_NO_COLORS, + OptionInfo + OPT_NO_HEADER, + &option_cmd_ISARENA_BRIEF, + &option_cmd_ISARENA_QUIET, + &option_cmd_ISARENA_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_NORMALIZE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_REMOVE_SRC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_FAST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_PATCH[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_REMOVE_SRC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_COMPRESS, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_FAST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COPY[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_REMOVE_SRC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_COMPRESS, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_FAST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DUPLICATE[] = +{ + OptionInfo + OPT_GAMEMODES, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_REMOVE_SRC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_COMPRESS, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_FAST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_MINIMAP[] = +{ + &option_cmd_MINIMAP_LONG, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_COMPRESS, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_FAST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_TOUCH, + OptionInfo + OPT_AUTO, + OptionInfo + OPT_SET_FLAGS, + OptionInfo + OPT_SET_SCALE, + OptionInfo + OPT_SET_ROT, + OptionInfo + OPT_SET_X, + OptionInfo + OPT_SET_Y, + OptionInfo + OPT_SET_Z, + OptionInfo + OPT_XCENTER, + OptionInfo + OPT_YCENTER, + OptionInfo + OPT_ZCENTER, + OptionInfo + OPT_CENTER, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_COMPRESS[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_REMOVE_SRC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_COMPRESS, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_FAST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_DECOMPRESS[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_REMOVE_SRC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_NORM, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_AUTO_ADD, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_ENCODE[] = +{ + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + &option_grp_ENCODE_ALL, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CREATE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_IGNORE_SETUP, + OptionInfo + OPT_U8, + OptionInfo + OPT_WU8, + OptionInfo + OPT_PACK, + OptionInfo + OPT_BRRES, + OptionInfo + OPT_BREFF, + OptionInfo + OPT_BREFT, + OptionInfo + OPT_YAZ0, + OptionInfo + OPT_YAZ1, + OptionInfo + OPT_BZ, + OptionInfo + OPT_SZS, + OptionInfo + OPT_WBZ, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PT_DIR, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_RM_AIPARAM, + OptionInfo + OPT_ALIGN_U8, + OptionInfo + OPT_ALIGN_PACK, + OptionInfo + OPT_ALIGN_BRRES, + OptionInfo + OPT_ALIGN_BREFF, + OptionInfo + OPT_ALIGN_BREFT, + OptionInfo + OPT_ALIGN, + OptionInfo + OPT_LOGGING, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + &option_grp_ENCODE_ALL, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_AUTO_ADD, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_N_MIPMAPS, + OptionInfo + OPT_MAX_MIPMAPS, + OptionInfo + OPT_MIPMAP_SIZE, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_NO_COMPRESS, + OptionInfo + OPT_COMPRESS, + OptionInfo + OPT_FAST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_UPDATE[] = +{ + OptionInfo + OPT_SOURCE, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_NO_COMPRESS, + OptionInfo + OPT_COMPRESS, + OptionInfo + OPT_FAST, + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_ALL, + OptionInfo + OPT_ENCODE_ALL, + OptionInfo + OPT_ENCODE_IMG, + OptionInfo + OPT_NO_ENCODE, + OptionInfo + OPT_NO_RECURSE, + OptionInfo + OPT_NO_ECHO, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_TRANSFORM, + OptionInfo + OPT_STRIP, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_EXTRACT[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_BASEDIR, + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_DECODE, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_DELTA, + OptionInfo + OPT_DIFF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_RAW, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_XDECODE[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_BASEDIR, + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_DECODE, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_DELTA, + OptionInfo + OPT_DIFF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_RAW, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_XEXPORT[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_BASEDIR, + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_DECODE, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_DELTA, + OptionInfo + OPT_DIFF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_RAW, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_XALL[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_BASEDIR, + OptionInfo + OPT_RECURSE, + OptionInfo + OPT_EXT, + OptionInfo + OPT_LINKS, + OptionInfo + OPT_DECODE, + OptionInfo + OPT_MIPMAPS, + OptionInfo + OPT_NO_MIPMAPS, + OptionInfo + OPT_FAST_MIPMAPS, + OptionInfo + OPT_CMPR_DEFAULT, + OptionInfo + OPT_CUT, + OptionInfo + OPT_ALL, + OptionInfo + OPT_IGNORE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_DELTA, + OptionInfo + OPT_DIFF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_NO_CHECK, + OptionInfo + OPT_RAW, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_XCOMMON[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + OptionInfo + OPT_IGNORE, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_BINARY[] = +{ + OptionInfo + OPT_KMG_LIMIT, + OptionInfo + OPT_KMG_COPY, + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_TEXT[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_DECODE, + OptionInfo + OPT_KMG_LIMIT, + OptionInfo + OPT_KMG_COPY, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_DELTA, + OptionInfo + OPT_DIFF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + &option_grp_BMG_LONG, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_CAT[] = +{ + OptionInfo + OPT_IGNORE, + OptionInfo + OPT_DECODE, + &option_grp_TEXTOUT_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_DELTA, + OptionInfo + OPT_DIFF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + &option_grp_BMG_LONG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_BMG[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_BMG_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_BMG_ENCODING, + OptionInfo + OPT_BMG_INF_SIZE, + OptionInfo + OPT_BMG_MID, + OptionInfo + OPT_FORCE_ATTRIB, + OptionInfo + OPT_DEF_ATTRIB, + OptionInfo + OPT_NO_ATTRIB, + OptionInfo + OPT_X_ESCAPES, + OptionInfo + OPT_OLD_ESCAPES, + OptionInfo + OPT_SINGLE_LINE, + OptionInfo + OPT_NO_BMG_COLORS, + OptionInfo + OPT_BMG_COLORS, + OptionInfo + OPT_NO_BMG_INLINE, + &option_grp_BMG_LONG, + OptionInfo + OPT_EXPORT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_KCL[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_KCL_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_KMP[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_KMP_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + OptionInfo + OPT_NO_PARAM, + OptionInfo + OPT_NO_CHECK, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_CONST, + OptionInfo + OPT_SCALE, + OptionInfo + OPT_SHIFT, + OptionInfo + OPT_XSS, + OptionInfo + OPT_YSS, + OptionInfo + OPT_ZSS, + OptionInfo + OPT_ROT, + OptionInfo + OPT_XROT, + OptionInfo + OPT_YROT, + OptionInfo + OPT_ZROT, + OptionInfo + OPT_YPOS, + OptionInfo + OPT_TRANSLATE, + OptionInfo + OPT_NULL, + OptionInfo + OPT_NEXT, + OptionInfo + OPT_ASCALE, + OptionInfo + OPT_AROT, + OptionInfo + OPT_TFORM_SCRIPT, + OptionInfo + OPT_RM_GOBJ, + OptionInfo + OPT_SLOT, + OptionInfo + OPT_LOAD_KCL, + OptionInfo + OPT_KCL, + OptionInfo + OPT_KCL_FLAG, + OptionInfo + OPT_KCL_SCRIPT, + OptionInfo + OPT_TRI_AREA, + OptionInfo + OPT_TRI_HEIGHT, + OptionInfo + OPT_KMP, + OptionInfo + OPT_SPEED_MOD, + OptionInfo + OPT_KTPT2, + OptionInfo + OPT_TFORM_KMP, + OptionInfo + OPT_REPAIR_XPF, + OptionInfo + OPT_MDL, + OptionInfo + OPT_MINIMAP, + OptionInfo + OPT_PAT, + OptionInfo + OPT_PATCH_FILE, + OptionInfo + OPT_LT_CLEAR, + OptionInfo + OPT_LT_ONLINE, + OptionInfo + OPT_LT_N_PLAYERS, + OptionInfo + OPT_LT_COND_BIT, + OptionInfo + OPT_LT_GAME_MODE, + OptionInfo + OPT_LT_ENGINE, + OptionInfo + OPT_LT_RANDOM, + OptionInfo + OPT_LEX_PURGE, + OptionInfo + OPT_PATCH_BMG, + OptionInfo + OPT_MACRO_BMG, + OptionInfo + OPT_FILTER_BMG, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_LEX[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_LEX_NO_HEADER, + OptionInfo + OPT_BRIEF, + OptionInfo + OPT_EXPORT, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_INFO[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_INFO_NO_HEADER, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_GHOST[] = +{ + OptionInfo + OPT_IGNORE, + &option_cmd_GHOST_NO_HEADER, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_SH, + OptionInfo + OPT_BASH, + OptionInfo + OPT_JSON, + OptionInfo + OPT_PHP, + OptionInfo + OPT_MAKEDOC, + OptionInfo + OPT_VAR, + OptionInfo + OPT_ARRAY, + OptionInfo + OPT_AVAR, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_YAZDUMP[] = +{ + OptionInfo + OPT_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_ESC, + + OptionInfo + OPT_NONE, // separator + + OptionInfo + OPT_PRESERVE, + OptionInfo + OPT_UPDATE, + OptionInfo + OPT_OVERWRITE, + OptionInfo + OPT_NUMBER, + OptionInfo + OPT_REMOVE_DEST, + + 0 +}; + +static const InfoOption_t * option_tab_cmd_VEHICLE[] = +{ + &option_cmd_VEHICLE_SOURCE, + &option_cmd_VEHICLE_DEST, + OptionInfo + OPT_DEST2, + OptionInfo + OPT_OVERWRITE, + &option_cmd_VEHICLE_QUIET, + &option_cmd_VEHICLE_VERBOSE, + &option_cmd_VEHICLE_BRIEF, + + 0 +}; + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoCommand /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const InfoCommand_t CommandInfo[CMD__N+1] = +{ + { 0, + false, + false, + false, + "wszst", + 0, + "wszst [option]... command [option|parameter|file]...", + "Wiimms SZS Tool : List, compare, (de-)compress, extract and create" + " SZS, U8, PACK, BRRES, BREFF, BREFT and RARC archives. It is also a" + " wrapper to all other tools.", + 0, + 63, + option_tab_tool, + 0 + }, + + { CMD_VERSION, + false, + false, + false, + "VERSION", + 0, + "wszst VERSION [ignored]...", + "Print program name and version and exit.", + 0, + 3, + option_tab_cmd_VERSION, + option_allowed_cmd_VERSION + }, + + { CMD_HELP, + false, + false, + false, + "HELP", + "H", + "wszst HELP [--width=wd] OPT|OPTION [option]...\n" + "wszst HELP [--width=wd] CMD|COMMAND ...\n" + "wszst HELP [--width=wd] command ...\n" + "wszst HELP [--width=wd] ...", + "Print help for commands and options.", + "Print help for commands and options.\n" + " The first variant (with keyword OPT or OPTION) prints a list of all" + " global and command specific options. If at least one option is set," + " only info about the declared options are printed. The second variant" + " (with keyword CMD or COMMAND) prints an overview about all commands." + " The third variant (with a valid command name) prints details about" + " the declared command. The fourth variant prints an overview about" + " all commands and all global options.", + 1, + option_tab_cmd_HELP, + option_allowed_cmd_HELP + }, + + { CMD_ARGTEST, + false, + false, + false, + "ARGTEST", + 0, + "wszst ARGTEST ...", + "This debug command accepts all kinds of parameters and prints one" + " line for each parameter.", + 0, + 0, + option_tab_cmd_ARGTEST, + option_allowed_cmd_ARGTEST + }, + + { CMD_TEST, + false, + false, + false, + "TEST", + 0, + "wszst TEST [options]... [ignored]...", + "This debugging command accepts all options and parameters and prints" + " a list of the internal option values. The command is designed to" + " test the option parameters. The non option parameters are completely" + " ignored.", + 0, + 1, + option_tab_cmd_TEST, + option_allowed_cmd_TEST + }, + + { CMD_COLORS, + false, + false, + false, + "COLORS", + 0, + "wszst COLORS", + "Ignore all parameters and print colored text for testing.", + 0, + 4, + option_tab_cmd_COLORS, + option_allowed_cmd_COLORS + }, + + { CMD_ERROR, + false, + false, + false, + "ERROR", + "ERR", + "wszst ERROR [error_code]", + "Translate an exit code to a message name. If no exit code is entered," + " print a table with all error messages.", + 0, + 4, + option_tab_cmd_ERROR, + option_allowed_cmd_ERROR + }, + + { CMD_FILETYPE, + false, + false, + false, + "FILETYPE", + "FT", + "wszst FILETYPE file...", + "Scan the header of the entered files and print file type and path for" + " each file as list.", + 0, + 3, + option_tab_cmd_FILETYPE, + option_allowed_cmd_FILETYPE + }, + + { CMD_FILEATTRIB, + false, + false, + false, + "FILEATTRIB", + "FA", + "wszst FILEATTRIB [keyword]...", + "Print a table with attributes for all known file types. If at least" + " one keyword is entered, only matching file types are printed. A" + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" + " printed keywords (like 'ARCHIVE' or 'EXTRACT').", + 0, + 1, + option_tab_cmd_FILEATTRIB, + option_allowed_cmd_FILEATTRIB + }, + + { CMD_BRSUB, + false, + false, + false, + "BRSUB", + 0, + "wszst BRSUB [file]...", + " If no file is defined, print a table with known BRRES sub files" + " consisting of file format, version, number of sections and warn" + " level. Otherwise analyse the version numbers of each BRRES sub file" + " of each source and print warnings about invalid versions.", + 0, + 3, + option_tab_cmd_BRSUB, + option_allowed_cmd_BRSUB + }, + + { CMD_SYMBOLS, + false, + false, + false, + "SYMBOLS", + 0, + "wszst SYMBOLS", + "Print predefined symbols of text parser to standard output.", + 0, + 1, + option_tab_cmd_SYMBOLS, + option_allowed_cmd_SYMBOLS + }, + + { CMD_FUNCTIONS, + false, + false, + false, + "FUNCTIONS", + 0, + "wszst FUNCTIONS [key]...", + "Print a list with all parser functions. If one or more keys entered," + " then only functions that match at least one key are printed.", + 0, + 3, + option_tab_cmd_FUNCTIONS, + option_allowed_cmd_FUNCTIONS + }, + + { CMD_CALCULATE, + false, + false, + false, + "CALCULATE", + 0, + "wszst CALCULATE expression...", + "Calculate each expression and print the result to standard output.", + 0, + 1, + option_tab_cmd_CALCULATE, + option_allowed_cmd_CALCULATE + }, + + { CMD_MATRIX, + false, + false, + false, + "MATRIX", + 0, + "wszst MATRIX [patch-option]... [vector]...", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results.", + "Use the transformation options as base and calculate and print the" + " related vectors and the transformation matrices. Then do a forward" + " and backward transformation for each vector parameter using the" + " matrices and print the results. The transformations are done with 4" + " different methods to confirm the algorithms. Only the main results" + " and results, which differ, are printed.\n" + " Normally, the exit status is OK (null). If at least one old" + " transformation returns different results for the different" + " algorithms, then status DIFFER (not null) is returned.", + 20, + option_tab_cmd_MATRIX, + option_allowed_cmd_MATRIX + }, + + { CMD_FLOAT, + false, + false, + false, + "FLOAT", + 0, + "wszst FLOAT number...", + "This commands helps to convert hex values into floating point numbers" + " and vice versa. For each non empty parameter, a line with 4 bytes" + " hex and a float, 8 bytes hex and a double float, and the parameter" + " itself is printed. If a parameter begins with 'x' or '0x', it is a" + " hex string, otherwise it is a floating point number.\n" + " The special parameters \"+\" and \"-\" mean: Increment or decrement" + " the previous value by the smallest possible value. Use different" + " increments for floats and doubles.\n" + " All conversions are done using big endian.", + 0, + 1, + option_tab_cmd_FLOAT, + option_allowed_cmd_FLOAT + }, + + { CMD_VR_CALC, + false, + false, + false, + "VR-CALC", + "VRCALC", + "wszst VR-CALC delta...", + " Calculate a VR increment for each given delta value" + " (=VR_winner-VR_loser). Print results as table.", + 0, + 2, + option_tab_cmd_VR_CALC, + option_allowed_cmd_VR_CALC + }, + + { CMD_VR_RACE, + false, + false, + false, + "VR-RACE", + "VRRACE", + "wszst VR-RACE vr1 vr2...", + "Print a table with VR calculations for a race. Each VR is the current" + " rating of a player. The vr-list is well ordered: The first VR is" + " for the winner and the last VR for the looser. Up to 30 VR values" + " will be accpeted. Each VR can be preceeded by \"NUM*\" to define a" + " repeat factor.", + 0, + 1, + option_tab_cmd_VR_RACE, + option_allowed_cmd_VR_RACE + }, + + { CMD_AUTOADD, + false, + false, + false, + "AUTOADD", + 0, + "wszst AUTOADD [source]...", + "Manage the auto-add library. Primarily add files to the main auto-add" + " archive for future use of option --auto-add or to convert WU8 files." + " If SOURCE is a directory, all standard track and arena file names" + " are searched for the missed auto-add files. Without source files," + " the status of the auto-add archive is printed.\n" + " Try 'wszst AUTOADD ./path_to/Race/Course/' to add all supported" + " files.", + 0, + 8, + option_tab_cmd_AUTOADD, + option_allowed_cmd_AUTOADD + }, + + { CMD_TRACKS, + false, + false, + false, + "TRACKS", + 0, + "wszst TRACKS [selection_mask]", + "Print a list of all known track SZS files of Mario Kart internal" + " directory './Race/Course'. These files are 32 standard track files" + " and 10 arena files. The first column shows an unique ID of exact 3" + " upper case characters or digits. For '_d' files, a 'd' is appended." + " The second column shows the internal file name. Use BMG files to" + " print real names as third column.", + 0, + 5, + option_tab_cmd_TRACKS, + option_allowed_cmd_TRACKS + }, + + { CMD_SCANCACHE, + false, + false, + false, + "SCANCACHE", + 0, + "wszst SCANCACHE directory\n" + "wszst SCANCACHE --cache directory", + "Scan the cache directory for SZS files and re-build the checksum" + " table. Store the result as file 'cache-content.txt' in the same" + " directory. The file is used by option --cache to optimize file" + " access.", + 0, + 2, + option_tab_cmd_SCANCACHE, + option_allowed_cmd_SCANCACHE + }, + + { CMD_EXPORT, + true, + false, + false, + "_EXPORT", + 0, + "wszst _EXPORT key...", + "This hidden command exports some definitions to standard output. Use" + " keyword HELP to get a list of possible keywords.", + 0, + 0, + option_tab_cmd_EXPORT, + option_allowed_cmd_EXPORT + }, + + { CMD_CODE, + true, + false, + false, + "_CODE", + 0, + "wszst _CODE [files]", + "All bytes of all input files are xor'ed with 0xDC and copied to" + " stdout. If no source file is given, standard input is read.", + 0, + 0, + option_tab_cmd_CODE, + option_allowed_cmd_CODE + }, + + { CMD_RECODE, + true, + false, + false, + "_RECODE", + 0, + "wszst _RECODE mode [files]", + "All bytes of all input files are interpreted as 1 file and re-coded." + " MODE defines the re-code mode. If no source file is given, standard" + " input is read.", + 0, + 0, + option_tab_cmd_RECODE, + option_allowed_cmd_RECODE + }, + + { CMD_SUBFILE, + true, + false, + false, + "_SUBFILE", + 0, + "wszst _SUBFILE [files]", + "Test sub-file system.", + 0, + 8, + option_tab_cmd_SUBFILE, + option_allowed_cmd_SUBFILE + }, + + { CMD_LIST, + false, + false, + true, + "LIST", + "LS", + "wszst LIST [source]...", + "List all sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" + " archives. If listing a directory, then a virtual SZS file is created" + " (like CREATE does it) and listed.", + 0, + 48, + option_tab_cmd_LIST, + option_allowed_cmd_LIST + }, + + { CMD_LIST_L, + false, + false, + false, + "LIST-L", + "LL", + "wszst LIST-L [source]...", + "List all sub files with size. 'LIST-L' and 'LL' are short cuts for" + " 'LIST --long'.", + 0, + 48, + option_tab_cmd_LIST_L, + option_allowed_cmd_LIST_L + }, + + { CMD_LIST_LL, + false, + false, + false, + "LIST-LL", + "LLL", + "wszst LIST-LL [source]...", + "List all sub files with offset and size. 'LIST-LL' and 'LLL' are" + " short cuts for 'LIST --long --long'.", + 0, + 48, + option_tab_cmd_LIST_LL, + option_allowed_cmd_LIST_LL + }, + + { CMD_LIST_LLL, + false, + false, + false, + "LIST-LLL", + "LLLL", + "wszst LIST-LLL [source]...", + "List all sub files with offset and size in debugging mode. 'LIST-LLL'" + " and 'LLLL' are short cuts for 'LIST --long --long --long'.", + 0, + 48, + option_tab_cmd_LIST_LLL, + option_allowed_cmd_LIST_LLL + }, + + { CMD_LIST_A, + false, + false, + false, + "LIST-A", + "LA", + "wszst LIST-A [source]...", + "List recursive all sub files with size. 'LIST-A' and 'LA' are short" + " cuts for 'LIST --long --all --all'.", + 0, + 48, + option_tab_cmd_LIST_A, + option_allowed_cmd_LIST_A + }, + + { CMD_LIST_LA, + false, + false, + false, + "LIST-LA", + "LLA", + "wszst LIST-LA [source]...", + "List recursive all sub files with offset and size. 'LIST-LA' and" + " 'LLA' are short cuts for 'LIST --long --long --all --all'.", + 0, + 48, + option_tab_cmd_LIST_LA, + option_allowed_cmd_LIST_LA + }, + + { CMD_NAME_REF, + false, + false, + false, + "NAME-REF", + "NAMEREF", + "wszst NAME-REF [source]...", + "For each BRRES file or subfile, create a name based cross reference" + " and print the result as list ordered by names in BRRES order. For U8" + " archives (e.g. track SZS files), sub file course_model.brres is" + " analysed.", + 0, + 3, + option_tab_cmd_NAME_REF, + option_allowed_cmd_NAME_REF + }, + + { CMD_ILIST, + false, + false, + true, + "ILIST", + "IL", + "wszst ILIST [source]...", + "List the images files of all sub files of SZS, U8, PACK, BRRES," + " BREFF, BREFT and RARC archives; non image files are ignored. If" + " listing a directory, then a virtual SZS file is created (like CREATE" + " does it) and listed.", + 0, + 45, + option_tab_cmd_ILIST, + option_allowed_cmd_ILIST + }, + + { CMD_ILIST_L, + false, + false, + false, + "ILIST-L", + "ILL", + "wit ILIST-L [source]...", + "List all image files with geometry data. 'ILIST-L' and 'ILL' are" + " short cuts for 'ILIST --long'.", + 0, + 45, + option_tab_cmd_ILIST_L, + option_allowed_cmd_ILIST_L + }, + + { CMD_ILIST_LL, + false, + false, + false, + "ILIST-LL", + "ILLL", + "wit ILIST-LL [source]...", + "List all sub files with advanced geometry data. 'ILIST-LL' and 'ILLL'" + " are short cuts for 'ILIST --long --long'.", + 0, + 45, + option_tab_cmd_ILIST_LL, + option_allowed_cmd_ILIST_LL + }, + + { CMD_ILIST_A, + false, + false, + false, + "ILIST-A", + "ILA", + "wszst ILIST-A [source]...", + "List recursive all image files. 'ILIST-A' and 'ILA' are short cuts" + " for 'ILIST --all'.", + 0, + 45, + option_tab_cmd_ILIST_A, + option_allowed_cmd_ILIST_A + }, + + { CMD_ILIST_LA, + false, + false, + false, + "ILIST-LA", + "ILLA", + "wit ILIST-LA [source]...", + "List recursive all image files with geometry data. 'ILIST-LA' and" + " 'ILLA' are short cuts for 'ILIST --long --all'.", + 0, + 45, + option_tab_cmd_ILIST_LA, + option_allowed_cmd_ILIST_LA + }, + + { CMD_MEMORY, + false, + false, + true, + "MEMORY", + "MEM", + "wszst MEMORY [source]...", + "Dump a memory map of each source file. Virtual images (extracted" + " directories) are also supported.", + 0, + 47, + option_tab_cmd_MEMORY, + option_allowed_cmd_MEMORY + }, + + { CMD_MEMORY_A, + false, + false, + false, + "MEMORY-A", + "MEMA", + "wszst MEMORY-A [source]...", + "Dump recursive a memory map of each source file. 'MEMORY-A' and" + " 'MEMA' are short cuts for 'MEMORY --all --all'.", + 0, + 47, + option_tab_cmd_MEMORY_A, + option_allowed_cmd_MEMORY_A + }, + + { CMD_DUMP, + false, + false, + false, + "DUMP", + 0, + "wszst DUMP [source]...", + "Dump the internal structure of a BRRES files, compressed or not." + " Other archives are ignored. The main goal is to verify string" + " relocation. Virtual images (extracted directories) are also" + " supported.", + 0, + 41, + option_tab_cmd_DUMP, + option_allowed_cmd_DUMP + }, + + { CMD_SHA1, + false, + false, + true, + "SHA1", + 0, + "wszst SHA1 [source]...", + "Print a SHA1 checksum for each source. The checksum is calculated by" + " using the uncompressed data.", + 0, + 48, + option_tab_cmd_SHA1, + option_allowed_cmd_SHA1 + }, + + { CMD_ANALYZE, + false, + false, + false, + "ANALYZE", + "ANA", + "wszst ANALYZE [source]...", + "Analyse track files and print results in machine readable format.", + 0, + 13, + option_tab_cmd_ANALYZE, + option_allowed_cmd_ANALYZE + }, + + { CMD_DISTRIBUTION, + false, + false, + false, + "DISTRIBUTION", + "DIST", + "wszst DISTRIBUTION result source...\n" + "wszst DISTRIBUTION directory [source]...", + "Load each SOURCE (SZS, WBZ, U8 or WU8) and calculate a checksum of" + " the uncompressed data. If SOURCE is a directory, scan it for SZS," + " WBZ, U8 and WU8 files. If a source directory starts with \"arena\"," + " all tracks of the directory are assumed to be battle arenas. Finally" + " print the results into file RESULT.\n" + " The default name for RESULT is \"distribution.txt\". Extension .txt" + " is appended to the filename if not defined. If RESULT is a" + " directory, it is used as first source too.\n" + " If file RESULT exists, it is automatically added as last source to" + " the list of option --source, but only if not already added. On" + " scanning of file result, parameters are scanned too.", + 0, + 45, + option_tab_cmd_DISTRIBUTION, + option_allowed_cmd_DISTRIBUTION + }, + + { CMD_DIFF, + false, + false, + true, + "DIFF", + "CMP", + "wszst DIFF source1 source2\n" + "wszst DIFF source... --dest dest", + "Compare two or more sources (each SZS, U8, PACK, BRRES, BREFF, BREFT," + " RARC or directory) on file level and report mismatches for each sub" + " file. If option --dest is set, then each source file is compared" + " with a file in the destination.", + 0, + 6, + option_tab_cmd_DIFF, + option_allowed_cmd_DIFF + }, + + { CMD_CHECK, + false, + false, + true, + "CHECK", + 0, + "wszst CHECK [source]...", + "Make a validity check for track files: Try to read KCL and KMP (and" + " check both) and find unknown, modified, needed and unneeded files." + " Also analyse cannons and make slot proposals. This command uses an" + " internal object and file database. The source can be a SZS, U8, WU8" + " or WBZ file, or an U8 like directory.", + 0, + 90, + option_tab_cmd_CHECK, + option_allowed_cmd_CHECK + }, + + { CMD_SLOTS, + false, + false, + false, + "SLOTS", + 0, + "wszst SLOTS [source]...", + "Try to find out the slots, that will run a track. Each source can be" + " a SZS, U8, WU8 or WBZ file, or an U8 like directory. For each source" + " one status line is printed. The status line contains either a" + " positive or a negative slot list, whatever is shorter. For battle" + " arenas, the keyword ARENA is printed.", + 0, + 86, + option_tab_cmd_SLOTS, + option_allowed_cmd_SLOTS + }, + + { CMD_STGI, + false, + false, + false, + "STGI", + 0, + "wszst STGI [source]...", + "Find the first KMP:STGI of each file and print an analysis of it as" + " single line statistic. Additional the number of lap counter check" + " points ('LC') is printed. Track and KMP (binary and text) files are" + " accepted as input.", + 0, + 4, + option_tab_cmd_STGI, + option_allowed_cmd_STGI + }, + + { CMD_ISARENA, + false, + false, + false, + "ISARENA", + 0, + "wszst ISARENA [source]...", + "Analyse each track for its arena status and print a line with a" + " status code and the track name. The status code is: »-RACE« for a" + " racing track, »?MAYBE« for likely an arena, »+ARENA« for an" + " arena, »+DISPATCH« for an arena with dispatch points or any error" + " hint beginning with an »!«. So the very first character (one of" + " »!-?+«) can be used to distinguish the status class.\n" + " The exit status is >2 on error, 2 (DIFFER), if at least one file is" + " a racing track, or 0 (OK), if all sources are arenas.", + 0, + 6, + option_tab_cmd_ISARENA, + option_allowed_cmd_ISARENA + }, + + { CMD_NORMALIZE, + false, + false, + false, + "NORMALIZE", + "NORM", + "wszst NORMALIZE [source]...", + "Normalize an U8 archive, compressed or not: The files are sorted in" + " `Nintendo like' order and unused data holes are removed. If" + " directory '.' is available it becomes the root directory for all" + " others. Compressed sources are compressed again with maximum" + " compression (level 9). The destination is only written if any data" + " changed.\n" + " 'wszst NORMALIZE' is the same as 'wszst PATCH --norm'.", + 0, + 74, + option_tab_cmd_NORMALIZE, + option_allowed_cmd_NORMALIZE + }, + + { CMD_PATCH, + false, + false, + false, + "PATCH", + 0, + "wszst PATCH [source]...", + "Patch an U8 archive, compressed or not: Load the archive, apply all" + " patching and transforming options and store the file again." + " Compressed sources are compressed again. The destination is only" + " written if any data changed.", + 0, + 75, + option_tab_cmd_PATCH, + option_allowed_cmd_PATCH + }, + + { CMD_COPY, + false, + false, + false, + "COPY", + "CP", + "wszst COPY source dest\n" + "wszst COPY source... [-d|-D] dest", + "COPY is an alternative command for copying and patching files. It" + " uses the same internal procedure as command PATCH, but has another" + " command line syntax.\n" + " The command copies all source files to the destination. If more than" + " 1 source is defined, the destination must be either a directory or a" + " destination filename with escapes. Patching is also possible and" + " option --overwrite is set implicitly.", + 0, + 75, + option_tab_cmd_COPY, + option_allowed_cmd_COPY + }, + + { CMD_DUPLICATE, + false, + false, + false, + "DUPLICATE", + "DUP", + "wszst DUPLICATE source [-d|-D] dest", + "The command reads a single track file and duplicates it several times" + " to DEST. Therefor it analyzes the presence flags combinations with" + " and without extension support and creates one file for each relevant" + " scenario. A LEX section TEST will be inserted to force the execution" + " mode for offline tests.\n" + " The destination filename (not directory part) is searched for the" + " first occurance of '@'. If not found, the place before the last" + " point is used. At this place an identification string is inserted.\n" + " Depending on option --gamemodes up to 1573 files will be created." + " So the use of option --fast is recommended to speed up the process." + " Use command 'wkmpt GAMEMODES' to list all variants and to check them" + " beforehand.", + 0, + 76, + option_tab_cmd_DUPLICATE, + option_allowed_cmd_DUPLICATE + }, + + { CMD_MINIMAP, + false, + false, + false, + "MINIMAP", + 0, + "wszst MINIMAP [source]...", + "Print minimap data (more exact: the visual field of the minimap) of a" + " SZS, U8, BRRES or MDL file. If patching options are used, modify the" + " data and write back the file using its original file format. Only" + " the translation vector is modified and new translation and inverse" + " matrices are calculated.", + 0, + 46, + option_tab_cmd_MINIMAP, + option_allowed_cmd_MINIMAP + }, + + { CMD_COMPRESS, + false, + false, + false, + "COMPRESS", + "COM", + "wszst COMPRESS [source]...", + "Compress and create a SZS archives. If the source is already" + " compressed, then decompress and compress it again. The default" + " destination is '%P/%N.szs'.", + 0, + 75, + option_tab_cmd_COMPRESS, + option_allowed_cmd_COMPRESS + }, + + { CMD_DECOMPRESS, + false, + false, + false, + "DECOMPRESS", + "DEC", + "wszst DECOMPRESS [szs]...", + "Decompress SZS files. The default destination is '%P/%N.u8'.", + 0, + 68, + option_tab_cmd_DECOMPRESS, + option_allowed_cmd_DECOMPRESS + }, + + { CMD_ENCODE, + false, + false, + false, + "ENCODE", + "E", + "wszst ENCODE [source_dir]...", + " Create and/or encode sub files if necessary. It works like command" + " CREATE, but without creating a new first level archive file.", + 0, + 69, + option_tab_cmd_ENCODE, + option_allowed_cmd_ENCODE + }, + + { CMD_CREATE, + false, + false, + false, + "CREATE", + "C", + "wszst CREATE [source_dir]...", + "Create SZS, U8, PACK, BRRES, BREFF, BREFT or RARC archives by adding" + " all not hidden files of the directory structure. Create and/or" + " encode sub files if necessary. The default destination is '%P/%N%T'.", + 0, + 95, + option_tab_cmd_CREATE, + option_allowed_cmd_CREATE + }, + + { CMD_UPDATE, + false, + false, + false, + "UPDATE", + "U", + "wszst UPDATE [source]... [--source=] [--dest=]", + "Update an existing SZS, U8, PACK, BRRES, BREFF, BREFT and" + " RARCarchive. This is done in similar way as the EXTRACT command, but" + " instead of extracting, source files are searched in a prior" + " extracted archive. Only files with the same size are updated. The" + " secondary source is used to find the source sub files. The default" + " secondary source is '%P/%N.d/'.", + 0, + 68, + option_tab_cmd_UPDATE, + option_allowed_cmd_UPDATE + }, + + { CMD_EXTRACT, + false, + false, + false, + "EXTRACT", + "X", + "wszst EXTRACT [source]...", + "Extract all sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" + " archives. The default destination is '%P/%N.d/'.", + 0, + 73, + option_tab_cmd_EXTRACT, + option_allowed_cmd_EXTRACT + }, + + { CMD_XDECODE, + false, + false, + false, + "XDECODE", + "XD", + "wszst XDECODE [source]...", + "Command XDECODE is a short cut for 'EXTRACT --decode'. It extract all" + " sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC archives" + " and decodes all supported files. The default destination is" + " '%P/%N.d/'.", + 0, + 73, + option_tab_cmd_XDECODE, + option_allowed_cmd_XDECODE + }, + + { CMD_XEXPORT, + false, + false, + false, + "XEXPORT", + "XX", + "wszst XEXPORT [source]...", + "Command XEXPORT is a short cut for 'EXTRACT --decode --export'. It" + " extract all sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" + " archives and decodes all supported files with enabled export mode." + " The default destination is '%P/%N.d/'.", + 0, + 73, + option_tab_cmd_XEXPORT, + option_allowed_cmd_XEXPORT + }, + + { CMD_XALL, + false, + false, + false, + "XALL", + "XA", + "wszst XALL [source]...", + "Command XALL is a short cut for 'EXTRACT --all'. It extract all sub" + " files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC archives" + " recursively and decodes all supported files. The default destination" + " is '%P/%N.d/'.", + 0, + 73, + option_tab_cmd_XALL, + option_allowed_cmd_XALL + }, + + { CMD_XCOMMON, + false, + false, + false, + "XCOMMON", + 0, + "wszst XCOMMON [source]...", + "Command XCOMMON is a short cut for 'EXTRACT --basedir=common'. Scan" + " archives and extract files from archives, that are below directory" + " 'common'. The default destination is '%P/%N.d/'.", + 0, + 9, + option_tab_cmd_XCOMMON, + option_allowed_cmd_XCOMMON + }, + + { CMD_BINARY, + false, + false, + true, + "BINARY", + "BIN", + "wszst BINARY [source]...", + "Read each source file (binary or text, but never archives) and write" + " the content as binary file. The file extension of the new file" + " depends on the file type, but is usually .bin. Currently supported" + " file formats are BMG, GEOHIT, ITEMSLOT, KCL, KMG, KMP, LEX, OBJFLOW," + " PAT and the related text formats.", + 0, + 11, + option_tab_cmd_BINARY, + option_allowed_cmd_BINARY + }, + + { CMD_TEXT, + false, + false, + false, + "TEXT", + "TXT", + "wszst TEXT [source]...", + "Read each source file (binary or text, but never archives) and write" + " the content as text file. The file extension of the new file is" + " usually .txt. Currently supported file formats are BMG, GEOHIT," + " ITEMSLOT, KCL, KMG, KMP, LEX, OBJFLOW, PAT and the related text" + " formats.", + 0, + 31, + option_tab_cmd_TEXT, + option_allowed_cmd_TEXT + }, + + { CMD_CAT, + false, + false, + false, + "CAT", + 0, + "wszst CAT [source/subfile]...", + "Extract one or more sub files of SZS, U8, PACK, BRRES, BREFF, BREFT" + " and RARC archives. The output of all source files is concatenated" + " and written to stdout.", + 0, + 21, + option_tab_cmd_CAT, + option_allowed_cmd_CAT + }, + + { CMD_BMG, + false, + false, + false, + "BMG", + 0, + "wszst BMG [source]...", + "For each archive all BMG sub files are searched and, if found," + " decoded and printed as combined BMG text file. The command works" + " exactly like 'wbmgt MIX'.", + 0, + 17, + option_tab_cmd_BMG, + option_allowed_cmd_BMG + }, + + { CMD_KCL, + false, + false, + false, + "KCL", + 0, + "wszst KCL [source]...", + "For each archive the KCL sub file course.kcl is searched and, if" + " found, decoded and printed (copied to stdout).\n" + " Automated dropping of bad KCL triangles is enabled if no other drop" + " option is specified by option --kcl.", + 0, + 50, + option_tab_cmd_KCL, + option_allowed_cmd_KCL + }, + + { CMD_KMP, + false, + false, + false, + "KMP", + 0, + "wszst KMP [source]...", + "For each archive the KMP sub file course.kmp is searched and, if" + " found, decoded and printed (copied to stdout).", + 0, + 51, + option_tab_cmd_KMP, + option_allowed_cmd_KMP + }, + + { CMD_LEX, + false, + false, + false, + "LEX", + 0, + "wszst LEX [source]...", + "For each archive the LEX sub file course.lex is searched and, if" + " found, decoded and printed (copied to stdout).", + 0, + 4, + option_tab_cmd_LEX, + option_allowed_cmd_LEX + }, + + { CMD_INFO, + false, + false, + false, + "INFO", + 0, + "wszst INFO [source]...", + "For each archive a file named info.txt and, if not found, credits.txt" + " is searched, ignoring case. The first found file of each archive is" + " printed (copied to stdout).", + 0, + 2, + option_tab_cmd_INFO, + option_allowed_cmd_INFO + }, + + { CMD_GHOST, + true, + false, + false, + "GHOSTS", + 0, + "wszst GHOST [source]...", + "Analyse each file and print a summary about the ghost data.", + 0, + 12, + option_tab_cmd_GHOST, + option_allowed_cmd_GHOST + }, + + { CMD_YAZDUMP, + false, + false, + false, + "YAZDUMP", + "YD", + "wszst YAZDUMP [szs]...", + "Analyze YAZ compression print a dump to stdout. Ignore files without" + " YAZ compression.", + 0, + 8, + option_tab_cmd_YAZDUMP, + option_allowed_cmd_YAZDUMP + }, + + { CMD_VEHICLE, + true, + false, + true, + "VEHICLE", + 0, + "wszst VEHICLE job ...", + "Hidden vehicle settings command.", + 0, + 7, + option_tab_cmd_VEHICLE, + option_allowed_cmd_VEHICLE + }, + + {0,0,0,0,0,0,0,0,0,0,0,0} +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// InfoUI /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const InfoUI_t InfoUI_wszst = +{ + "wszst", + CMD__N, + CommandTab, + CommandInfo, + OPT__N_SPECIFIC, + OPT__N_TOTAL, + OptionInfo, + OptionUsed, + OptionIndex, + OptionShort, + OptionLong +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/ui/ui-wszst.h b/project/src/ui/ui-wszst.h new file mode 100644 index 0000000..ee85a24 --- /dev/null +++ b/project/src/ui/ui-wszst.h @@ -0,0 +1,1226 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + + +#ifndef SZS_UI_WSZST_H +#define SZS_UI_WSZST_H +#include "dclib-basics.h" +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumOptions +{ + OPT_NONE, + + //----- command specific options ----- + + OPT_TOUCH, + OPT_AUTO, + OPT_SET_FLAGS, + OPT_SET_SCALE, + OPT_SET_ROT, + OPT_SET_X, + OPT_SET_Y, + OPT_SET_Z, + OPT_XCENTER, + OPT_YCENTER, + OPT_ZCENTER, + OPT_CENTER, + OPT_SCALE, + OPT_SHIFT, + OPT_XSS, + OPT_YSS, + OPT_ZSS, + OPT_ROT, + OPT_XROT, + OPT_YROT, + OPT_ZROT, + OPT_YPOS, + OPT_TRANSLATE, + OPT_NULL, + OPT_NEXT, + OPT_ASCALE, + OPT_AROT, + OPT_TFORM_SCRIPT, + OPT_MINIMAP, + OPT_RM_GOBJ, + OPT_SLOT, + OPT_KCL_FLAG, + OPT_KCL_SCRIPT, + OPT_FLAG_FILE, + OPT_REPAIR_XPF, + OPT_GAMEMODES, + OPT_LOAD_BMG, + OPT_PATCH_BMG, + OPT_MACRO_BMG, + OPT_FILTER_BMG, + OPT_ID, + OPT_BASE64, + OPT_DB64, + OPT_CODING, + OPT_VERIFY, + OPT_ROUND, + OPT_LONG, + OPT_NO_HEADER, + OPT_BRIEF, + OPT_PIPE, + OPT_DELTA, + OPT_DIFF, + OPT_EPSILON, + OPT_BMG_ENCODING, + OPT_BMG_INF_SIZE, + OPT_BMG_MID, + OPT_FORCE_ATTRIB, + OPT_DEF_ATTRIB, + OPT_NO_ATTRIB, + OPT_X_ESCAPES, + OPT_OLD_ESCAPES, + OPT_SINGLE_LINE, + OPT_NO_BMG_COLORS, + OPT_BMG_COLORS, + OPT_NO_BMG_INLINE, + OPT_EXPORT, + OPT_NO_PARAM, + OPT_SORT, + OPT_TRANSFORM, + OPT_STRIP, + OPT_ANALYZE, + OPT_ANALYZE_MODE, + OPT_SOURCE, + OPT_DEST, + OPT_DEST2, + OPT_ESC, + OPT_OVERWRITE, + OPT_NUMBER, + OPT_REMOVE_SRC, + OPT_REMOVE_DEST, + OPT_UPDATE, + OPT_PRESERVE, + OPT_IGNORE, + OPT_IGNORE_SETUP, + OPT_PURGE, + OPT_ALIGN_U8, + OPT_ALIGN_PACK, + OPT_ALIGN_BRRES, + OPT_ALIGN_BREFF, + OPT_ALIGN_BREFT, + OPT_ALIGN, + OPT_ENCODE_ALL, + OPT_ENCODE_IMG, + OPT_NO_ENCODE, + OPT_NO_RECURSE, + OPT_AUTO_ADD, + OPT_NO_ECHO, + OPT_NO_CHECK, + OPT_PT_DIR, + OPT_RM_AIPARAM, + OPT_U8, + OPT_SZS, + OPT_WU8, + OPT_XWU8, + OPT_WBZ, + OPT_PACK, + OPT_BRRES, + OPT_BREFF, + OPT_BREFT, + OPT_NO_COMPRESS, + OPT_COMPRESS, + OPT_FAST, + OPT_NORM, + OPT_LINKS, + OPT_BASEDIR, + OPT_RECURSE, + OPT_EXT, + OPT_DECODE, + OPT_MIPMAPS, + OPT_NO_MIPMAPS, + OPT_N_MIPMAPS, + OPT_MAX_MIPMAPS, + OPT_MIPMAP_SIZE, + OPT_FAST_MIPMAPS, + OPT_CMPR_DEFAULT, + OPT_CUT, + OPT_ALL, + OPT_RAW, + OPT_SECTIONS, + + OPT__N_SPECIFIC, // == 130 + + //----- global options ----- + + OPT_VERSION, + OPT_HELP, + OPT_XHELP, + OPT_ALLOW_ALL, + OPT_COMPATIBLE, + OPT_WIDTH, + OPT_QUIET, + OPT_VERBOSE, + OPT_LOGGING, + OPT_DE, + OPT_COLORS, + OPT_NO_COLORS, + OPT_CT_CODE, + OPT_LE_CODE, + OPT_CHDIR, + OPT_CONST, + OPT_BATTLE, + OPT_EXPORT_FLAGS, + OPT_ROUTE_OPTIONS, + OPT_WIM0, + OPT_LOAD_KCL, + OPT_KCL, + OPT_TRI_AREA, + OPT_TRI_HEIGHT, + OPT_XTRIDATA, + OPT_KMP, + OPT_SPEED_MOD, + OPT_KTPT2, + OPT_TFORM_KMP, + OPT_MDL, + OPT_PAT, + OPT_PATCH_FILE, + OPT_KMG_LIMIT, + OPT_KMG_COPY, + OPT_LT_CLEAR, + OPT_LT_ONLINE, + OPT_LT_N_PLAYERS, + OPT_LT_COND_BIT, + OPT_LT_GAME_MODE, + OPT_LT_ENGINE, + OPT_LT_RANDOM, + OPT_LEX_PURGE, + OPT_CACHE, + OPT_CNAME, + OPT_MAX_FILE_SIZE, + OPT_TRACKS, + OPT_ARENAS, + OPT_AUTOADD_PATH, + OPT_UTF_8, + OPT_NO_UTF_8, + OPT_TEST, + OPT_FORCE, + OPT_REPAIR_MAGICS, + OPT_TINY, + OPT_YAZ0, + OPT_YAZ1, + OPT_XYZ, + OPT_BZ, + OPT_SH, + OPT_BASH, + OPT_JSON, + OPT_PHP, + OPT_MAKEDOC, + OPT_VAR, + OPT_ARRAY, + OPT_AVAR, + OPT_OLD, + OPT_STD, + OPT_NEW, + OPT_EXTRACT, + + OPT__N_TOTAL // == 200 + +} enumOptions; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumOptionsBit /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// ***** only for verification ***** + +//typedef enum enumOptionsBit +//{ +// //----- command specific options ----- +// +// OB_TOUCH = 1llu << OPT_TOUCH, +// OB_AUTO = 1llu << OPT_AUTO, +// OB_SET_FLAGS = 1llu << OPT_SET_FLAGS, +// OB_SET_SCALE = 1llu << OPT_SET_SCALE, +// OB_SET_ROT = 1llu << OPT_SET_ROT, +// OB_SET_X = 1llu << OPT_SET_X, +// OB_SET_Y = 1llu << OPT_SET_Y, +// OB_SET_Z = 1llu << OPT_SET_Z, +// OB_XCENTER = 1llu << OPT_XCENTER, +// OB_YCENTER = 1llu << OPT_YCENTER, +// OB_ZCENTER = 1llu << OPT_ZCENTER, +// OB_CENTER = 1llu << OPT_CENTER, +// OB_SCALE = 1llu << OPT_SCALE, +// OB_SHIFT = 1llu << OPT_SHIFT, +// OB_XSS = 1llu << OPT_XSS, +// OB_YSS = 1llu << OPT_YSS, +// OB_ZSS = 1llu << OPT_ZSS, +// OB_ROT = 1llu << OPT_ROT, +// OB_XROT = 1llu << OPT_XROT, +// OB_YROT = 1llu << OPT_YROT, +// OB_ZROT = 1llu << OPT_ZROT, +// OB_YPOS = 1llu << OPT_YPOS, +// OB_TRANSLATE = 1llu << OPT_TRANSLATE, +// OB_NULL = 1llu << OPT_NULL, +// OB_NEXT = 1llu << OPT_NEXT, +// OB_ASCALE = 1llu << OPT_ASCALE, +// OB_AROT = 1llu << OPT_AROT, +// OB_TFORM_SCRIPT = 1llu << OPT_TFORM_SCRIPT, +// OB_MINIMAP = 1llu << OPT_MINIMAP, +// OB_RM_GOBJ = 1llu << OPT_RM_GOBJ, +// OB_SLOT = 1llu << OPT_SLOT, +// OB_KCL_FLAG = 1llu << OPT_KCL_FLAG, +// OB_KCL_SCRIPT = 1llu << OPT_KCL_SCRIPT, +// OB_FLAG_FILE = 1llu << OPT_FLAG_FILE, +// OB_REPAIR_XPF = 1llu << OPT_REPAIR_XPF, +// OB_GAMEMODES = 1llu << OPT_GAMEMODES, +// OB_LOAD_BMG = 1llu << OPT_LOAD_BMG, +// OB_PATCH_BMG = 1llu << OPT_PATCH_BMG, +// OB_MACRO_BMG = 1llu << OPT_MACRO_BMG, +// OB_FILTER_BMG = 1llu << OPT_FILTER_BMG, +// OB_ID = 1llu << OPT_ID, +// OB_BASE64 = 1llu << OPT_BASE64, +// OB_DB64 = 1llu << OPT_DB64, +// OB_CODING = 1llu << OPT_CODING, +// OB_VERIFY = 1llu << OPT_VERIFY, +// OB_ROUND = 1llu << OPT_ROUND, +// OB_LONG = 1llu << OPT_LONG, +// OB_NO_HEADER = 1llu << OPT_NO_HEADER, +// OB_BRIEF = 1llu << OPT_BRIEF, +// OB_PIPE = 1llu << OPT_PIPE, +// OB_DELTA = 1llu << OPT_DELTA, +// OB_DIFF = 1llu << OPT_DIFF, +// OB_EPSILON = 1llu << OPT_EPSILON, +// OB_BMG_ENCODING = 1llu << OPT_BMG_ENCODING, +// OB_BMG_INF_SIZE = 1llu << OPT_BMG_INF_SIZE, +// OB_BMG_MID = 1llu << OPT_BMG_MID, +// OB_FORCE_ATTRIB = 1llu << OPT_FORCE_ATTRIB, +// OB_DEF_ATTRIB = 1llu << OPT_DEF_ATTRIB, +// OB_NO_ATTRIB = 1llu << OPT_NO_ATTRIB, +// OB_X_ESCAPES = 1llu << OPT_X_ESCAPES, +// OB_OLD_ESCAPES = 1llu << OPT_OLD_ESCAPES, +// OB_SINGLE_LINE = 1llu << OPT_SINGLE_LINE, +// OB_NO_BMG_COLORS = 1llu << OPT_NO_BMG_COLORS, +// OB_BMG_COLORS = 1llu << OPT_BMG_COLORS, +// OB_NO_BMG_INLINE = 1llu << OPT_NO_BMG_INLINE, +// OB_EXPORT = 1llu << OPT_EXPORT, +// OB_NO_PARAM = 1llu << OPT_NO_PARAM, +// OB_SORT = 1llu << OPT_SORT, +// OB_TRANSFORM = 1llu << OPT_TRANSFORM, +// OB_STRIP = 1llu << OPT_STRIP, +// OB_ANALYZE = 1llu << OPT_ANALYZE, +// OB_ANALYZE_MODE = 1llu << OPT_ANALYZE_MODE, +// OB_SOURCE = 1llu << OPT_SOURCE, +// OB_DEST = 1llu << OPT_DEST, +// OB_DEST2 = 1llu << OPT_DEST2, +// OB_ESC = 1llu << OPT_ESC, +// OB_OVERWRITE = 1llu << OPT_OVERWRITE, +// OB_NUMBER = 1llu << OPT_NUMBER, +// OB_REMOVE_SRC = 1llu << OPT_REMOVE_SRC, +// OB_REMOVE_DEST = 1llu << OPT_REMOVE_DEST, +// OB_UPDATE = 1llu << OPT_UPDATE, +// OB_PRESERVE = 1llu << OPT_PRESERVE, +// OB_IGNORE = 1llu << OPT_IGNORE, +// OB_IGNORE_SETUP = 1llu << OPT_IGNORE_SETUP, +// OB_PURGE = 1llu << OPT_PURGE, +// OB_ALIGN_U8 = 1llu << OPT_ALIGN_U8, +// OB_ALIGN_PACK = 1llu << OPT_ALIGN_PACK, +// OB_ALIGN_BRRES = 1llu << OPT_ALIGN_BRRES, +// OB_ALIGN_BREFF = 1llu << OPT_ALIGN_BREFF, +// OB_ALIGN_BREFT = 1llu << OPT_ALIGN_BREFT, +// OB_ALIGN = 1llu << OPT_ALIGN, +// OB_ENCODE_ALL = 1llu << OPT_ENCODE_ALL, +// OB_ENCODE_IMG = 1llu << OPT_ENCODE_IMG, +// OB_NO_ENCODE = 1llu << OPT_NO_ENCODE, +// OB_NO_RECURSE = 1llu << OPT_NO_RECURSE, +// OB_AUTO_ADD = 1llu << OPT_AUTO_ADD, +// OB_NO_ECHO = 1llu << OPT_NO_ECHO, +// OB_NO_CHECK = 1llu << OPT_NO_CHECK, +// OB_PT_DIR = 1llu << OPT_PT_DIR, +// OB_RM_AIPARAM = 1llu << OPT_RM_AIPARAM, +// OB_U8 = 1llu << OPT_U8, +// OB_SZS = 1llu << OPT_SZS, +// OB_WU8 = 1llu << OPT_WU8, +// OB_XWU8 = 1llu << OPT_XWU8, +// OB_WBZ = 1llu << OPT_WBZ, +// OB_PACK = 1llu << OPT_PACK, +// OB_BRRES = 1llu << OPT_BRRES, +// OB_BREFF = 1llu << OPT_BREFF, +// OB_BREFT = 1llu << OPT_BREFT, +// OB_NO_COMPRESS = 1llu << OPT_NO_COMPRESS, +// OB_COMPRESS = 1llu << OPT_COMPRESS, +// OB_FAST = 1llu << OPT_FAST, +// OB_NORM = 1llu << OPT_NORM, +// OB_LINKS = 1llu << OPT_LINKS, +// OB_BASEDIR = 1llu << OPT_BASEDIR, +// OB_RECURSE = 1llu << OPT_RECURSE, +// OB_EXT = 1llu << OPT_EXT, +// OB_DECODE = 1llu << OPT_DECODE, +// OB_MIPMAPS = 1llu << OPT_MIPMAPS, +// OB_NO_MIPMAPS = 1llu << OPT_NO_MIPMAPS, +// OB_N_MIPMAPS = 1llu << OPT_N_MIPMAPS, +// OB_MAX_MIPMAPS = 1llu << OPT_MAX_MIPMAPS, +// OB_MIPMAP_SIZE = 1llu << OPT_MIPMAP_SIZE, +// OB_FAST_MIPMAPS = 1llu << OPT_FAST_MIPMAPS, +// OB_CMPR_DEFAULT = 1llu << OPT_CMPR_DEFAULT, +// OB_CUT = 1llu << OPT_CUT, +// OB_ALL = 1llu << OPT_ALL, +// OB_RAW = 1llu << OPT_RAW, +// OB_SECTIONS = 1llu << OPT_SECTIONS, +// +// //----- group & command options ----- +// +// OB_GRP_TRANSFORM = OB_SCALE +// | OB_SHIFT +// | OB_XSS +// | OB_YSS +// | OB_ZSS +// | OB_ROT +// | OB_XROT +// | OB_YROT +// | OB_ZROT +// | OB_YPOS +// | OB_TRANSLATE +// | OB_NULL +// | OB_NEXT +// | OB_ASCALE +// | OB_AROT +// | OB_TFORM_SCRIPT, +// +// OB_GRP_TRANSFORM2 = OB_GRP_TRANSFORM +// | OB_RM_GOBJ +// | OB_SLOT +// | OB_KCL_FLAG +// | OB_KCL_SCRIPT +// | OB_REPAIR_XPF +// | OB_MINIMAP +// | OB_PATCH_BMG +// | OB_MACRO_BMG +// | OB_FILTER_BMG, +// +// OB_GRP_DEST = OB_DEST +// | OB_DEST2 +// | OB_ESC +// | OB_PRESERVE +// | OB_UPDATE +// | OB_OVERWRITE +// | OB_NUMBER +// | OB_REMOVE_DEST, +// +// OB_GRP_FFORMAT1 = OB_U8 +// | OB_WU8 +// | OB_SZS +// | OB_XWU8 +// | OB_WBZ, +// +// OB_GRP_FFORMAT2 = OB_U8 +// | OB_WU8 +// | OB_PACK +// | OB_BRRES +// | OB_BREFF +// | OB_BREFT +// | OB_SZS +// | OB_XWU8 +// | OB_WBZ, +// +// OB_GRP_SCRIPT = 0, +// +// OB_GRP_ALIGN = OB_ALIGN_U8 +// | OB_ALIGN_PACK +// | OB_ALIGN_BRRES +// | OB_ALIGN_BREFF +// | OB_ALIGN_BREFT +// | OB_ALIGN, +// +// OB_GRP_TEXTOUT = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_DELTA +// | OB_DIFF +// | OB_EXPORT +// | OB_NO_PARAM, +// +// OB_GRP_BMG = OB_BMG_ENCODING +// | OB_BMG_INF_SIZE +// | OB_BMG_MID +// | OB_FORCE_ATTRIB +// | OB_DEF_ATTRIB +// | OB_NO_ATTRIB +// | OB_X_ESCAPES +// | OB_OLD_ESCAPES +// | OB_SINGLE_LINE +// | OB_NO_BMG_COLORS +// | OB_BMG_COLORS +// | OB_NO_BMG_INLINE +// | OB_LONG +// | OB_EXPORT, +// +// OB_GRP_ENCODE = OB_ENCODE_ALL +// | OB_ENCODE_IMG +// | OB_ALL +// | OB_NO_ENCODE +// | OB_NO_RECURSE +// | OB_NO_ECHO +// | OB_NO_CHECK +// | OB_TRANSFORM +// | OB_STRIP, +// +// OB_GRP_CREATE = OB_IGNORE_SETUP +// | OB_GRP_FFORMAT2 +// | OB_PT_DIR +// | OB_LINKS +// | OB_RM_AIPARAM +// | OB_GRP_ALIGN +// | OB_GRP_ENCODE +// | OB_AUTO_ADD +// | OB_MIPMAPS +// | OB_NO_MIPMAPS +// | OB_N_MIPMAPS +// | OB_MAX_MIPMAPS +// | OB_MIPMAP_SIZE +// | OB_FAST_MIPMAPS +// | OB_CMPR_DEFAULT, +// +// OB_GRP_NORM = OB_NORM +// | OB_LINKS +// | OB_GRP_ALIGN +// | OB_PT_DIR +// | OB_RM_AIPARAM +// | OB_AUTO_ADD, +// +// OB_GRP_EXTRACT = OB_BASEDIR +// | OB_RECURSE +// | OB_EXT +// | OB_LINKS +// | OB_DECODE +// | OB_MIPMAPS +// | OB_NO_MIPMAPS +// | OB_FAST_MIPMAPS +// | OB_CMPR_DEFAULT +// | OB_CUT +// | OB_ALL +// | OB_GRP_TEXTOUT +// | OB_NO_CHECK +// | OB_RAW, +// +// OB_CMD_VERSION = OB_BRIEF +// | OB_SECTIONS +// | OB_LONG, +// +// OB_CMD_HELP = ~(u64)0, +// +// OB_CMD_ARGTEST = ~(u64)0, +// +// OB_CMD_TEST = ~(u64)0, +// +// OB_CMD_COLORS = OB_LONG +// | OB_BRIEF, +// +// OB_CMD_ERROR = OB_SECTIONS +// | OB_NO_HEADER +// | OB_LONG +// | OB_BRIEF, +// +// OB_CMD_FILETYPE = OB_LONG +// | OB_NO_HEADER +// | OB_IGNORE, +// +// OB_CMD_FILEATTRIB = OB_NO_HEADER, +// +// OB_CMD_BRSUB = OB_IGNORE +// | OB_NO_HEADER +// | OB_LONG, +// +// OB_CMD_SYMBOLS = OB_NO_HEADER, +// +// OB_CMD_FUNCTIONS = OB_NO_HEADER +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_CALCULATE = 0, +// +// OB_CMD_MATRIX = OB_GRP_TRANSFORM +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_FLOAT = OB_ROUND, +// +// OB_CMD_VR_CALC = OB_NO_HEADER +// | OB_BRIEF, +// +// OB_CMD_VR_RACE = OB_LONG, +// +// OB_CMD_AUTOADD = OB_IGNORE +// | OB_DEST +// | OB_DEST2 +// | OB_OVERWRITE +// | OB_REMOVE_DEST +// | OB_PRESERVE, +// +// OB_CMD_TRACKS = OB_ALL +// | OB_LONG +// | OB_PIPE +// | OB_LOAD_BMG, +// +// OB_CMD_SCANCACHE = OB_PURGE, +// +// OB_CMD_EXPORT = 0, +// +// OB_CMD_CODE = 0, +// +// OB_CMD_RECODE = 0, +// +// OB_CMD_SUBFILE = OB_GRP_DEST, +// +// OB_CMD_LIST = OB_RECURSE +// | OB_EXT +// | OB_CUT +// | OB_ALL +// | OB_LONG +// | OB_SORT +// | OB_NO_HEADER +// | OB_GRP_CREATE +// | OB_IGNORE +// | OB_NORM +// | OB_LINKS +// | OB_RM_AIPARAM +// | OB_SLOT, +// +// OB_CMD_LIST_L = OB_CMD_LIST, +// +// OB_CMD_LIST_LL = OB_CMD_LIST_L, +// +// OB_CMD_LIST_LLL = OB_CMD_LIST_LL, +// +// OB_CMD_LIST_A = OB_CMD_LIST_L, +// +// OB_CMD_LIST_LA = OB_CMD_LIST_A, +// +// OB_CMD_NAME_REF = OB_SORT +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_ILIST = OB_RECURSE +// | OB_ALL +// | OB_LONG +// | OB_SORT +// | OB_NO_HEADER +// | OB_GRP_CREATE +// | OB_IGNORE +// | OB_NORM +// | OB_LINKS, +// +// OB_CMD_ILIST_L = OB_CMD_ILIST, +// +// OB_CMD_ILIST_LL = OB_CMD_ILIST_L, +// +// OB_CMD_ILIST_A = OB_CMD_ILIST, +// +// OB_CMD_ILIST_LA = OB_CMD_ILIST_A, +// +// OB_CMD_MEMORY = OB_RECURSE +// | OB_EXT +// | OB_DECODE +// | OB_MIPMAPS +// | OB_CUT +// | OB_ALL +// | OB_LONG +// | OB_GRP_CREATE +// | OB_IGNORE +// | OB_NORM +// | OB_LINKS +// | OB_RM_AIPARAM +// | OB_SLOT, +// +// OB_CMD_MEMORY_A = OB_CMD_MEMORY, +// +// OB_CMD_DUMP = OB_GRP_CREATE +// | OB_IGNORE +// | OB_NORM +// | OB_LINKS, +// +// OB_CMD_SHA1 = OB_IGNORE +// | OB_NORM +// | OB_LINKS +// | OB_GRP_CREATE +// | OB_ID +// | OB_BASE64 +// | OB_DB64 +// | OB_CODING +// | OB_VERIFY +// | OB_BRIEF +// | OB_LONG, +// +// OB_CMD_ANALYZE = OB_LONG +// | OB_IGNORE +// | OB_GRP_SCRIPT +// | OB_DEST +// | OB_DEST2, +// +// OB_CMD_DISTRIBUTION = OB_IGNORE +// | OB_NORM +// | OB_LINKS +// | OB_GRP_CREATE +// | OB_BASE64 +// | OB_DB64 +// | OB_CODING +// | OB_SOURCE, +// +// OB_CMD_DIFF = OB_RECURSE +// | OB_CUT +// | OB_ALL +// | OB_DEST +// | OB_ESC, +// +// OB_CMD_CHECK = OB_NO_CHECK +// | OB_IGNORE +// | OB_BRIEF +// | OB_LONG +// | OB_GRP_CREATE +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_SLOTS = OB_IGNORE +// | OB_BRIEF +// | OB_GRP_CREATE +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_STGI = OB_NO_HEADER +// | OB_IGNORE, +// +// OB_CMD_ISARENA = OB_NO_HEADER +// | OB_BRIEF +// | OB_IGNORE, +// +// OB_CMD_NORMALIZE = OB_GRP_DEST +// | OB_REMOVE_SRC +// | OB_GRP_FFORMAT1 +// | OB_IGNORE +// | OB_GRP_NORM +// | OB_FAST +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_PATCH = OB_GRP_DEST +// | OB_REMOVE_SRC +// | OB_GRP_FFORMAT1 +// | OB_IGNORE +// | OB_COMPRESS +// | OB_GRP_NORM +// | OB_FAST +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_COPY = OB_CMD_PATCH, +// +// OB_CMD_DUPLICATE = OB_GAMEMODES +// | OB_CMD_PATCH, +// +// OB_CMD_MINIMAP = OB_LONG +// | OB_REMOVE_DEST +// | OB_PRESERVE +// | OB_IGNORE +// | OB_COMPRESS +// | OB_GRP_NORM +// | OB_FAST +// | OB_TOUCH +// | OB_AUTO +// | OB_SET_FLAGS +// | OB_SET_SCALE +// | OB_SET_ROT +// | OB_SET_X +// | OB_SET_Y +// | OB_SET_Z +// | OB_XCENTER +// | OB_YCENTER +// | OB_ZCENTER +// | OB_CENTER +// | OB_GRP_TRANSFORM, +// +// OB_CMD_COMPRESS = OB_GRP_DEST +// | OB_REMOVE_SRC +// | OB_GRP_FFORMAT1 +// | OB_IGNORE +// | OB_COMPRESS +// | OB_GRP_NORM +// | OB_FAST +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_DECOMPRESS = OB_GRP_DEST +// | OB_REMOVE_SRC +// | OB_U8 +// | OB_WU8 +// | OB_IGNORE +// | OB_GRP_NORM +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_ENCODE = OB_IGNORE_SETUP +// | OB_GRP_ALIGN +// | OB_GRP_ENCODE +// | OB_MIPMAPS +// | OB_NO_MIPMAPS +// | OB_N_MIPMAPS +// | OB_MAX_MIPMAPS +// | OB_MIPMAP_SIZE +// | OB_FAST_MIPMAPS +// | OB_CMPR_DEFAULT +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_CREATE = OB_GRP_DEST +// | OB_GRP_CREATE +// | OB_NO_COMPRESS +// | OB_COMPRESS +// | OB_FAST +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_UPDATE = OB_SOURCE +// | OB_IGNORE +// | OB_GRP_DEST +// | OB_ESC +// | OB_NO_COMPRESS +// | OB_COMPRESS +// | OB_FAST +// | OB_RECURSE +// | OB_ALL +// | OB_GRP_ENCODE +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_EXTRACT = OB_GRP_DEST +// | OB_GRP_EXTRACT +// | OB_IGNORE +// | OB_ANALYZE +// | OB_ANALYZE_MODE +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_XDECODE = OB_CMD_EXTRACT, +// +// OB_CMD_XEXPORT = OB_CMD_EXTRACT, +// +// OB_CMD_XALL = OB_CMD_EXTRACT, +// +// OB_CMD_XCOMMON = OB_GRP_DEST +// | OB_IGNORE, +// +// OB_CMD_BINARY = OB_IGNORE +// | OB_GRP_DEST, +// +// OB_CMD_TEXT = OB_IGNORE +// | OB_DECODE +// | OB_GRP_TEXTOUT +// | OB_GRP_BMG +// | OB_GRP_DEST, +// +// OB_CMD_CAT = OB_IGNORE +// | OB_DECODE +// | OB_GRP_TEXTOUT +// | OB_GRP_BMG, +// +// OB_CMD_BMG = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_GRP_BMG, +// +// OB_CMD_KCL = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_NO_PARAM +// | OB_NO_CHECK +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_KMP = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_EXPORT +// | OB_NO_PARAM +// | OB_NO_CHECK +// | OB_GRP_TRANSFORM2, +// +// OB_CMD_LEX = OB_IGNORE +// | OB_NO_HEADER +// | OB_BRIEF +// | OB_EXPORT, +// +// OB_CMD_INFO = OB_IGNORE +// | OB_NO_HEADER, +// +// OB_CMD_GHOST = OB_IGNORE +// | OB_NO_HEADER +// | OB_GRP_SCRIPT +// | OB_DEST +// | OB_DEST2, +// +// OB_CMD_YAZDUMP = OB_GRP_DEST, +// +// OB_CMD_VEHICLE = OB_SOURCE +// | OB_DEST +// | OB_DEST2 +// | OB_OVERWRITE +// | OB_BRIEF, +// +//} enumOptionsBit; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum enumCommands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumCommands +{ + CMD__NONE, + + CMD_VERSION, + CMD_HELP, + CMD_ARGTEST, + CMD_TEST, + CMD_COLORS, + CMD_ERROR, + CMD_FILETYPE, + CMD_FILEATTRIB, + CMD_BRSUB, + CMD_SYMBOLS, + CMD_FUNCTIONS, + CMD_CALCULATE, + CMD_MATRIX, + CMD_FLOAT, + CMD_VR_CALC, + CMD_VR_RACE, + CMD_AUTOADD, + CMD_TRACKS, + CMD_SCANCACHE, + CMD_EXPORT, + CMD_CODE, + CMD_RECODE, + CMD_SUBFILE, + + CMD_LIST, + CMD_LIST_L, + CMD_LIST_LL, + CMD_LIST_LLL, + CMD_LIST_A, + CMD_LIST_LA, + CMD_NAME_REF, + + CMD_ILIST, + CMD_ILIST_L, + CMD_ILIST_LL, + CMD_ILIST_A, + CMD_ILIST_LA, + + CMD_MEMORY, + CMD_MEMORY_A, + CMD_DUMP, + + CMD_SHA1, + CMD_ANALYZE, + CMD_DISTRIBUTION, + + CMD_DIFF, + + CMD_CHECK, + CMD_SLOTS, + CMD_STGI, + CMD_ISARENA, + CMD_NORMALIZE, + CMD_PATCH, + CMD_COPY, + CMD_DUPLICATE, + CMD_MINIMAP, + CMD_COMPRESS, + CMD_DECOMPRESS, + CMD_ENCODE, + CMD_CREATE, + CMD_UPDATE, + CMD_EXTRACT, + CMD_XDECODE, + CMD_XEXPORT, + CMD_XALL, + CMD_XCOMMON, + + CMD_BINARY, + CMD_TEXT, + CMD_CAT, + CMD_BMG, + CMD_KCL, + CMD_KMP, + CMD_LEX, + CMD_INFO, + CMD_GHOST, + CMD_YAZDUMP, + + CMD_VEHICLE, + + CMD__N // == 73 + +} enumCommands; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enumGetOpt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum enumGetOpt +{ + GO_SINGLE_LINE = '1', + + GO__ERR = '?', + + GO_ARENAS = 'A', + GO_BRIEF = 'B', + GO_COMPRESS = 'C', + GO_DEST2 = 'D', + GO_ESC = 'E', + GO_NO_HEADER = 'H', + GO_LOGGING = 'L', + GO_MAX_FILE_SIZE = 'M', + GO_NO_CHECK = 'N', + GO_NO_PARAM = 'P', + GO_RECURSE = 'R', + GO_SORT = 'S', + GO_TRACKS = 'T', + GO_VERSION = 'V', + GO_EXPORT = 'X', + + GO_ALL = 'a', + GO_CONST = 'c', + GO_DEST = 'd', + GO_EXT = 'e', + GO_HELP = 'h', + GO_IGNORE = 'i', + GO_LONG = 'l', + GO_NORM = 'n', + GO_OVERWRITE = 'o', + GO_PRESERVE = 'p', + GO_QUIET = 'q', + GO_REMOVE_DEST = 'r', + GO_SOURCE = 's', + GO_TEST = 't', + GO_UPDATE = 'u', + GO_VERBOSE = 'v', + GO_TRANSFORM = 'x', + + GO_XHELP = 0x80, + GO_ALLOW_ALL, + GO_COMPATIBLE, + GO_WIDTH, + GO_DE, + GO_COLORS, + GO_NO_COLORS, + GO_CT_CODE, + GO_LE_CODE, + GO_CHDIR, + GO_TOUCH, + GO_AUTO, + GO_SET_FLAGS, + GO_SET_SCALE, + GO_SET_ROT, + GO_SET_X, + GO_SET_Y, + GO_SET_Z, + GO_XCENTER, + GO_YCENTER, + GO_ZCENTER, + GO_CENTER, + GO_SCALE, + GO_SHIFT, + GO_XSS, + GO_YSS, + GO_ZSS, + GO_ROT, + GO_XROT, + GO_YROT, + GO_ZROT, + GO_YPOS, + GO_TRANSLATE, + GO_NULL, + GO_NEXT, + GO_ASCALE, + GO_AROT, + GO_TFORM_SCRIPT, + GO_MINIMAP, + GO_RM_GOBJ, + GO_BATTLE, + GO_EXPORT_FLAGS, + GO_ROUTE_OPTIONS, + GO_WIM0, + GO_SLOT, + GO_LOAD_KCL, + GO_KCL, + GO_KCL_FLAG, + GO_KCL_SCRIPT, + GO_TRI_AREA, + GO_TRI_HEIGHT, + GO_FLAG_FILE, + GO_XTRIDATA, + GO_KMP, + GO_SPEED_MOD, + GO_KTPT2, + GO_TFORM_KMP, + GO_REPAIR_XPF, + GO_GAMEMODES, + GO_MDL, + GO_PAT, + GO_PATCH_FILE, + GO_KMG_LIMIT, + GO_KMG_COPY, + GO_LT_CLEAR, + GO_LT_ONLINE, + GO_LT_N_PLAYERS, + GO_LT_COND_BIT, + GO_LT_GAME_MODE, + GO_LT_ENGINE, + GO_LT_RANDOM, + GO_LEX_PURGE, + GO_LOAD_BMG, + GO_PATCH_BMG, + GO_MACRO_BMG, + GO_FILTER_BMG, + GO_CACHE, + GO_CNAME, + GO_ID, + GO_BASE64, + GO_DB64, + GO_CODING, + GO_VERIFY, + GO_ROUND, + GO_PIPE, + GO_DELTA, + GO_DIFF, + GO_EPSILON, + GO_BMG_ENCODING, + GO_BMG_INF_SIZE, + GO_BMG_MID, + GO_FORCE_ATTRIB, + GO_DEF_ATTRIB, + GO_NO_ATTRIB, + GO_X_ESCAPES, + GO_OLD_ESCAPES, + GO_NO_BMG_COLORS, + GO_BMG_COLORS, + GO_NO_BMG_INLINE, + GO_AUTOADD_PATH, + GO_STRIP, + GO_UTF_8, + GO_NO_UTF_8, + GO_FORCE, + GO_REPAIR_MAGICS, + GO_TINY, + GO_YAZ0, + GO_YAZ1, + GO_XYZ, + GO_BZ, + GO_SH, + GO_BASH, + GO_JSON, + GO_PHP, + GO_MAKEDOC, + GO_VAR, + GO_ARRAY, + GO_AVAR, + GO_ANALYZE, + GO_ANALYZE_MODE, + GO_OLD, + GO_STD, + GO_NEW, + GO_EXTRACT, + GO_NUMBER, + GO_REMOVE_SRC, + GO_IGNORE_SETUP, + GO_PURGE, + GO_ALIGN_U8, + GO_ALIGN_PACK, + GO_ALIGN_BRRES, + GO_ALIGN_BREFF, + GO_ALIGN_BREFT, + GO_ALIGN, + GO_ENCODE_ALL, + GO_ENCODE_IMG, + GO_NO_ENCODE, + GO_NO_RECURSE, + GO_AUTO_ADD, + GO_NO_ECHO, + GO_PT_DIR, + GO_RM_AIPARAM, + GO_U8, + GO_SZS, + GO_WU8, + GO_XWU8, + GO_WBZ, + GO_PACK, + GO_BRRES, + GO_BREFF, + GO_BREFT, + GO_NO_COMPRESS, + GO_FAST, + GO_LINKS, + GO_BASEDIR, + GO_DECODE, + GO_MIPMAPS, + GO_NO_MIPMAPS, + GO_N_MIPMAPS, + GO_MAX_MIPMAPS, + GO_MIPMAP_SIZE, + GO_FAST_MIPMAPS, + GO_CMPR_DEFAULT, + GO_CUT, + GO_RAW, + GO_SECTIONS, + +} enumGetOpt; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//extern const InfoOption_t OptionInfo[OPT__N_TOTAL+1]; +//extern const KeywordTab_t CommandTab[]; +//extern const char OptionShort[]; +//extern const struct option OptionLong[]; +//extern u8 OptionUsed[OPT__N_TOTAL+1]; +//extern const OptionIndex_t OptionIndex[UIOPT_INDEX_SIZE]; +//UIOPT_INDEX_SIZE := 0x12c = 300 +//extern const InfoCommand_t CommandInfo[CMD__N+1]; +extern const InfoUI_t InfoUI_wszst; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_WSZST_H + diff --git a/project/src/ui/ui.def b/project/src/ui/ui.def new file mode 100644 index 0000000..85ba906 --- /dev/null +++ b/project/src/ui/ui.def @@ -0,0 +1,23034 @@ + +## +############################################################################### +############### Tool 'wszst' ############### +############################################################################### + +#:def_tool( "wszst", \ + "wszst [option]... command [option|parameter|@file]...", \ + "Wiimms SZS Tool : List, compare, (de-)compress, extract and create" \ + " SZS, U8, PACK, BRRES, BREFF, BREFT and RARC archives. It is also a" \ + " wrapper to all other tools." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wszst VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wszst HELP [--width=wd] OPT|OPTION [option]...\n" \ + "wszst HELP [--width=wd] CMD|COMMAND ...\n" \ + "wszst HELP [--width=wd] command ...\n" \ + "wszst HELP [--width=wd] ...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wszst ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wszst TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wszst COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wszst ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wszst FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wszst FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "BRSUB", "BRSUB", \ + "wszst BRSUB [file]...", \ + " If no file is defined, print a table with known BRRES sub files" \ + " consisting of file format, version, number of sections and warn" \ + " level. Otherwise analyse the version numbers of each BRRES sub file" \ + " of each source and print warnings about invalid versions." ) + +#:def_cmd( "SYMBOLS", "SYMBOLS", \ + "wszst SYMBOLS", \ + "Print predefined symbols of text parser to standard output." ) + +#:def_cmd( "FUNCTIONS", "FUNCTIONS", \ + "wszst FUNCTIONS [key]...", \ + "Print a list with all parser functions. If one or more keys entered," \ + " then only functions that match at least one key are printed." ) + +#:def_cmd( "CALCULATE", "CALCULATE", \ + "wszst CALCULATE expression...", \ + "Calculate each expression and print the result to standard output." ) + +#:def_cmd( "MATRIX", "MATRIX", \ + "wszst MATRIX [patch-option]... [vector]...", \ + "Use the transformation options as base and calculate and print the" \ + " related vectors and the transformation matrices. Then do a forward" \ + " and backward transformation for each @vector@ parameter using the" \ + " matrices and print the results.", \ + "Use the transformation options as base and calculate and print the" \ + " related vectors and the transformation matrices. Then do a forward" \ + " and backward transformation for each @vector@ parameter using the" \ + " matrices and print the results. The transformations are done with 4" \ + " different methods to confirm the algorithms. Only the main results" \ + " and results, which differ, are printed.\n" \ + " Normally, the exit status is @OK@ (null). If at least one old" \ + " transformation returns different results for the different" \ + " algorithms, then status @DIFFER@ (not null) is returned." ) + +#:def_cmd( "FLOAT", "FLOAT", \ + "wszst FLOAT number...", \ + "This commands helps to convert hex values into floating point numbers" \ + " and vice versa. For each non empty parameter, a line with 4 bytes" \ + " hex and a float, 8 bytes hex and a double float, and the parameter" \ + " itself is printed. If a parameter begins with 'x' or '0x', it is a" \ + " hex string, otherwise it is a floating point number.\n" \ + " The special parameters \"+\" and \"-\" mean: Increment or decrement" \ + " the previous value by the smallest possible value. Use different" \ + " increments for floats and doubles.\n" \ + " All conversions are done using big endian." ) + +#:def_cmd( "VR_CALC", "VR-CALC|VRCALC", \ + "wszst VR-CALC delta...", \ + " Calculate a VR increment for each given delta value" \ + " (@=VR_winner-VR_loser@). Print results as table." ) + +#:def_cmd( "VR_RACE", "VR-RACE|VRRACE", \ + "wszst VR-RACE vr1 vr2...", \ + "Print a table with VR calculations for a race. Each @VR@ is the" \ + " current rating of a player. The vr-list is well ordered: The first" \ + " VR is for the winner and the last VR for the looser. Up to 30 VR" \ + " values will be accpeted. Each VR can be preceeded by @\"NUM*\"@ to" \ + " define a repeat factor." ) + +#:def_cmd( "AUTOADD", "AUTOADD", \ + "wszst AUTOADD [source]...", \ + "Manage the auto-add library. Primarily add files to the main auto-add" \ + " archive for future use of option {--auto-add} or to convert WU8" \ + " files. If @SOURCE@ is a directory, all standard track and arena file" \ + " names are searched for the missed auto-add files. Without source" \ + " files, the status of the auto-add archive is printed.\n" \ + " Try {wszst AUTOADD ./path_to/Race/Course/} to add all supported" \ + " files." ) + +#:def_cmd( "TRACKS", "TRACKS", \ + "wszst TRACKS [selection_mask]", \ + "Print a list of all known track SZS files of Mario Kart internal" \ + " directory './Race/Course'. These files are 32 standard track files" \ + " and 10 arena files. The first column shows an unique ID of exact 3" \ + " upper case characters or digits. For '_d' files, a 'd' is appended." \ + " The second column shows the internal file name. Use BMG files to" \ + " print real names as third column." ) + +#:def_cmd( "SCANCACHE", "SCANCACHE", \ + "wszst SCANCACHE directory\n" \ + "wszst SCANCACHE --cache directory", \ + "Scan the cache directory for SZS files and re-build the checksum" \ + " table. Store the result as file 'cache-content.txt' in the same" \ + " directory. The file is used by option {--cache} to optimize file" \ + " access." ) + +#:def_cmd( "LIST", "LIST|LS", \ + "wszst LIST [source]...", \ + "List all sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" \ + " archives. If listing a directory, then a virtual SZS file is created" \ + " (like CREATE does it) and listed." ) + +#:def_cmd( "LIST_L", "LIST-L|LL|LISTL", \ + "wszst LIST-L [source]...", \ + "List all sub files with size. 'LIST-L' and 'LL' are short cuts for" \ + " {LIST --long}." ) + +#:def_cmd( "LIST_LL", "LIST-LL|LLL|LISTLL", \ + "wszst LIST-LL [source]...", \ + "List all sub files with offset and size. 'LIST-LL' and 'LLL' are" \ + " short cuts for {LIST --long --long}." ) + +#:def_cmd( "LIST_LLL", "LIST-LLL|LLLL|LISTLLL", \ + "wszst LIST-LLL [source]...", \ + "List all sub files with offset and size in debugging mode. 'LIST-LLL'" \ + " and 'LLLL' are short cuts for {LIST --long --long --long}." ) + +#:def_cmd( "LIST_A", "LIST-A|LA|LISTA", \ + "wszst LIST-A [source]...", \ + "List recursive all sub files with size. 'LIST-A' and 'LA' are short" \ + " cuts for {LIST --long --all --all}." ) + +#:def_cmd( "LIST_LA", "LIST-LA|LLA|LISTLA", \ + "wszst LIST-LA [source]...", \ + "List recursive all sub files with offset and size. 'LIST-LA' and" \ + " 'LLA' are short cuts for {LIST --long --long --all --all}." ) + +#:def_cmd( "NAME_REF", "NAME-REF|NAMEREF", \ + "wszst NAME-REF [source]...", \ + "For each BRRES file or subfile, create a name based cross reference" \ + " and print the result as list ordered by names in BRRES order. For U8" \ + " archives (e.g. track SZS files), sub file @course_model.brres@ is" \ + " analysed." ) + +#:def_cmd( "ILIST", "ILIST|IL", \ + "wszst ILIST [source]...", \ + "List the images files of all sub files of SZS, U8, PACK, BRRES," \ + " BREFF, BREFT and RARC archives; non image files are ignored. If" \ + " listing a directory, then a virtual SZS file is created (like CREATE" \ + " does it) and listed." ) + +#:def_cmd( "ILIST_L", "ILIST-L|ILL|ILISTL", \ + "wit ILIST-L [source]...", \ + "List all image files with geometry data. 'ILIST-L' and 'ILL' are" \ + " short cuts for {ILIST --long}." ) + +#:def_cmd( "ILIST_LL", "ILIST-LL|ILLL|ILISTLL", \ + "wit ILIST-LL [source]...", \ + "List all sub files with advanced geometry data. 'ILIST-LL' and 'ILLL'" \ + " are short cuts for {ILIST --long --long}." ) + +#:def_cmd( "ILIST_A", "ILIST-A|ILA", \ + "wszst ILIST-A [source]...", \ + "List recursive all image files. 'ILIST-A' and 'ILA' are short cuts" \ + " for {ILIST --all}." ) + +#:def_cmd( "ILIST_LA", "ILIST-LA|ILLA|ILISTLA", \ + "wit ILIST-LA [source]...", \ + "List recursive all image files with geometry data. 'ILIST-LA' and" \ + " 'ILLA' are short cuts for {ILIST --long --all}." ) + +#:def_cmd( "MEMORY", "MEMORY|MEM", \ + "wszst MEMORY [source]...", \ + "Dump a memory map of each source file. Virtual images (extracted" \ + " directories) are also supported." ) + +#:def_cmd( "MEMORY_A", "MEMORY-A|MEMA", \ + "wszst MEMORY-A [source]...", \ + "Dump recursive a memory map of each source file. 'MEMORY-A' and" \ + " 'MEMA' are short cuts for {MEMORY --all --all}." ) + +#:def_cmd( "DUMP", "DUMP", \ + "wszst DUMP [source]...", \ + "Dump the internal structure of a BRRES files, compressed or not." \ + " Other archives are ignored. The main goal is to verify string" \ + " relocation. Virtual images (extracted directories) are also" \ + " supported." ) + +#:def_cmd( "SHA1", "SHA1", \ + "wszst SHA1 [source]...", \ + "Print a SHA1 checksum for each source. The checksum is calculated by" \ + " using the uncompressed data." ) + +#:def_cmd( "ANALYZE", "ANALYZE|ANA|ANALYSE", \ + "wszst ANALYZE [source]...", \ + "Analyse track files and print results in machine readable format." ) + +#:def_cmd( "DISTRIBUTION", "DISTRIBUTION|DIST", \ + "wszst DISTRIBUTION result source...\n" \ + "wszst DISTRIBUTION directory [source]...", \ + "Load each @SOURCE@ (SZS, WBZ, U8 or WU8) and calculate a checksum of" \ + " the uncompressed data. If @SOURCE@ is a directory, scan it for SZS," \ + " WBZ, U8 and WU8 files. If a source directory starts with \"arena\"," \ + " all tracks of the directory are assumed to be battle arenas. Finally" \ + " print the results into file @RESULT@.\n" \ + " The default name for @RESULT@ is \"distribution.txt\". Extension" \ + " .txt is appended to the filename if not defined. If @RESULT@ is a" \ + " directory, it is used as first source too.\n" \ + " If file @RESULT@ exists, it is automatically added as last source" \ + " to the list of option --source, but only if not already added. On" \ + " scanning of file @result@, parameters are scanned too." ) + +#:def_cmd( "DIFF", "DIFF|CMP", \ + "wszst DIFF source1 source2\n" \ + "wszst DIFF source... --dest dest", \ + "Compare two or more sources (each SZS, U8, PACK, BRRES, BREFF, BREFT," \ + " RARC or directory) on file level and report mismatches for each sub" \ + " file. If option {--dest} is set, then each source file is compared" \ + " with a file in the destination." ) + +#:def_cmd( "CHECK", "CHECK", \ + "wszst CHECK [source]...", \ + "Make a validity check for track files: Try to read KCL and KMP (and" \ + " check both) and find unknown, modified, needed and unneeded files." \ + " Also analyse cannons and make slot proposals. This command uses an" \ + " internal object and file database. The source can be a SZS, U8, WU8" \ + " or WBZ file, or an U8 like directory." ) + +#:def_cmd( "SLOTS", "SLOTS", \ + "wszst SLOTS [source]...", \ + "Try to find out the slots, that will run a track. Each source can be" \ + " a SZS, U8, WU8 or WBZ file, or an U8 like directory. For each source" \ + " one status line is printed. The status line contains either a" \ + " positive or a negative slot list, whatever is shorter. For battle" \ + " arenas, the keyword @ARENA@ is printed." ) + +#:def_cmd( "STGI", "STGI", \ + "wszst STGI [source]...", \ + "Find the first KMP:STGI of each file and print an analysis of it as" \ + " single line statistic. Additional the number of lap counter check" \ + " points ('LC') is printed. Track and KMP (binary and text) files are" \ + " accepted as input." ) + +#:def_cmd( "ISARENA", "ISARENA", \ + "wszst ISARENA [source]...", \ + "Analyse each track for its arena status and print a line with a" \ + " status code and the track name. The status code is: »-RACE« for a" \ + " racing track, »?MAYBE« for likely an arena, »+ARENA« for an" \ + " arena, »+DISPATCH« for an arena with dispatch points or any error" \ + " hint beginning with an »!«. So the very first character (one of" \ + " »!-?+«) can be used to distinguish the status class.\n" \ + " The exit status is >2 on error, 2 (DIFFER), if at least one file is" \ + " a racing track, or 0 (OK), if all sources are arenas." ) + +#:def_cmd( "NORMALIZE", "NORMALIZE|NORM", \ + "wszst NORMALIZE [source]...", \ + "Normalize an U8 archive, compressed or not: The files are sorted in" \ + " `Nintendo like' order and unused data holes are removed. If" \ + " directory '.' is available it becomes the root directory for all" \ + " others. Compressed sources are compressed again with maximum" \ + " compression (level 9). The destination is only written if any data" \ + " changed.\n" \ + " {wszst NORMALIZE} is the same as {wszst PATCH --norm}." ) + +#:def_cmd( "PATCH", "PATCH", \ + "wszst PATCH [source]...", \ + "Patch an U8 archive, compressed or not: Load the archive, apply all" \ + " patching and transforming options and store the file again." \ + " Compressed sources are compressed again. The destination is only" \ + " written if any data changed." ) + +#:def_cmd( "COPY", "COPY|CP", \ + "wszst COPY source dest\n" \ + "wszst COPY source... [-d|-D] dest", \ + "COPY is an alternative command for copying and patching files. It" \ + " uses the same internal procedure as command PATCH, but has another" \ + " command line syntax.\n" \ + " The command copies all source files to the destination. If more than" \ + " 1 source is defined, the destination must be either a directory or a" \ + " destination filename with escapes. Patching is also possible and" \ + " option --overwrite is set implicitly." ) + +#:def_cmd( "DUPLICATE", "DUPLICATE|DUP", \ + "wszst DUPLICATE source [-d|-D] dest", \ + "The command reads a single track file and duplicates it several times" \ + " to DEST. Therefor it analyzes the presence flags combinations with" \ + " and without extension support and creates one file for each relevant" \ + " scenario. A LEX section TEST will be inserted to force the execution" \ + " mode for offline tests.\n" \ + " The destination filename (not directory part) is searched for the" \ + " first occurance of @'@@'@. If not found, the place before the last" \ + " point is used. At this place an identification string is inserted.\n" \ + " Depending on option {--gamemodes} up to 1573 files will be created." \ + " So the use of option {--fast} is recommended to speed up the" \ + " process. Use command {wkmpt GAMEMODES} to list all variants and to" \ + " check them beforehand." ) + +#:def_cmd( "MINIMAP", "MINIMAP", \ + "wszst MINIMAP [source]...", \ + "Print minimap data (more exact: the visual field of the minimap) of a" \ + " SZS, U8, BRRES or MDL file. If patching options are used, modify the" \ + " data and write back the file using its original file format. Only" \ + " the translation vector is modified and new translation and inverse" \ + " matrices are calculated." ) + +#:def_cmd( "COMPRESS", "COMPRESS|COM", \ + "wszst COMPRESS [source]...", \ + "Compress and create a SZS archives. If the source is already" \ + " compressed, then decompress and compress it again. The default" \ + " destination is @'%P/%N.szs'@." ) + +#:def_cmd( "DECOMPRESS", "DECOMPRESS|DEC", \ + "wszst DECOMPRESS [szs]...", \ + "Decompress SZS files. The default destination is @'%P/%N.u8'@." ) + +#:def_cmd( "ENCODE", "ENCODE|E", \ + "wszst ENCODE [source_dir]...", \ + " Create and/or encode sub files if necessary. It works like command" \ + " CREATE, but without creating a new first level archive file." ) + +#:def_cmd( "CREATE", "CREATE|C", \ + "wszst CREATE [source_dir]...", \ + "Create SZS, U8, PACK, BRRES, BREFF, BREFT or RARC archives by adding" \ + " all not hidden files of the directory structure. Create and/or" \ + " encode sub files if necessary. The default destination is" \ + " @'%P/%N%T'@." ) + +#:def_cmd( "UPDATE", "UPDATE|U", \ + "wszst UPDATE [source]... [--source=] [--dest=]", \ + "Update an existing SZS, U8, PACK, BRRES, BREFF, BREFT and" \ + " RARCarchive. This is done in similar way as the {EXTRACT} command," \ + " but instead of extracting, source files are searched in a prior" \ + " extracted archive. Only files with the same size are updated. The" \ + " secondary source is used to find the source sub files. The default" \ + " secondary source is @'%P/%N.d/'@." ) + +#:def_cmd( "EXTRACT", "EXTRACT|X", \ + "wszst EXTRACT [source]...", \ + "Extract all sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" \ + " archives. The default destination is @'%P/%N.d/'@." ) + +#:def_cmd( "XDECODE", "XDECODE|XD", \ + "wszst XDECODE [source]...", \ + "Command @XDECODE@ is a short cut for {EXTRACT --decode}. It extract" \ + " all sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" \ + " archives and decodes all supported files. The default destination is" \ + " @'%P/%N.d/'@." ) + +#:def_cmd( "XEXPORT", "XEXPORT|XX", \ + "wszst XEXPORT [source]...", \ + "Command @XEXPORT@ is a short cut for {EXTRACT --decode --export}. It" \ + " extract all sub files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC" \ + " archives and decodes all supported files with enabled export mode." \ + " The default destination is @'%P/%N.d/'@." ) + +#:def_cmd( "XALL", "XALL|XA", \ + "wszst XALL [source]...", \ + "Command @XALL@ is a short cut for {EXTRACT --all}. It extract all sub" \ + " files of SZS, U8, PACK, BRRES, BREFF, BREFT and RARC archives" \ + " recursively and decodes all supported files. The default destination" \ + " is @'%P/%N.d/'@." ) + +#:def_cmd( "XCOMMON", "XCOMMON", \ + "wszst XCOMMON [source]...", \ + "Command @XCOMMON@ is a short cut for {EXTRACT --basedir=common}. Scan" \ + " archives and extract files from archives, that are below directory" \ + " @'common'@. The default destination is @'%P/%N.d/'@." ) + +#:def_cmd( "BINARY", "BINARY|BIN", \ + "wszst BINARY [source]...", \ + "Read each source file (binary or text, but never archives) and write" \ + " the content as binary file. The file extension of the new file" \ + " depends on the file type, but is usually @.bin@. Currently supported" \ + " file formats are @BMG@, @GEOHIT@, @ITEMSLOT@, @KCL@, @KMG@, @KMP@," \ + " @LEX@, @OBJFLOW@, @PAT@ and the related text formats." ) + +#:def_cmd( "TEXT", "TEXT|TXT", \ + "wszst TEXT [source]...", \ + "Read each source file (binary or text, but never archives) and write" \ + " the content as text file. The file extension of the new file is" \ + " usually @.txt@. Currently supported file formats are @BMG@," \ + " @GEOHIT@, @ITEMSLOT@, @KCL@, @KMG@, @KMP@, @LEX@, @OBJFLOW@, @PAT@" \ + " and the related text formats." ) + +#:def_cmd( "CAT", "CAT", \ + "wszst CAT [source/subfile]...", \ + "Extract one or more sub files of SZS, U8, PACK, BRRES, BREFF, BREFT" \ + " and RARC archives. The output of all source files is concatenated" \ + " and written to stdout." ) + +#:def_cmd( "BMG", "BMG", \ + "wszst BMG [source]...", \ + "For each archive all BMG sub files are searched and, if found," \ + " decoded and printed as combined BMG text file. The command works" \ + " exactly like {wbmgt MIX}." ) + +#:def_cmd( "KCL", "KCL", \ + "wszst KCL [source]...", \ + "For each archive the KCL sub file @course.kcl@ is searched and, if" \ + " found, decoded and printed (copied to stdout).\n" \ + " Automated dropping of bad KCL triangles is enabled if no other drop" \ + " option is specified by option {--kcl}." ) + +#:def_cmd( "KMP", "KMP", \ + "wszst KMP [source]...", \ + "For each archive the KMP sub file @course.kmp@ is searched and, if" \ + " found, decoded and printed (copied to stdout)." ) + +#:def_cmd( "LEX", "LEX", \ + "wszst LEX [source]...", \ + "For each archive the LEX sub file @course.lex@ is searched and, if" \ + " found, decoded and printed (copied to stdout)." ) + +#:def_cmd( "INFO", "INFO", \ + "wszst INFO [source]...", \ + "For each archive a file named @info.txt@ and, if not found," \ + " @credits.txt@ is searched, ignoring case. The first found file of" \ + " each archive is printed (copied to stdout)." ) + +#:def_cmd( "YAZDUMP", "YAZDUMP|YD", \ + "wszst YAZDUMP [szs]...", \ + "Analyze YAZ compression print a dump to stdout. Ignore files without" \ + " YAZ compression." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "Enable LE-CODE and CT-CODE support. The optional parameter is" \ + " deprecated and ignored." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "TOUCH", "touch", "C", \ + "", \ + "Mark the minimap as modified to force a new calculation of the" \ + " translation and inverse matrices." ) + +#:def_opt( "AUTO", "auto", "C", \ + "", \ + "Calculate the minimap translations automatically by using the minimum" \ + " and maximum vertex coordinates. The 'scale' and 'rotation' fields" \ + " are reset. This is done as first action before the SET, CENTER and" \ + " the other transformations." ) + +#:def_opt( "SET_FLAGS", "set-flags|setflags", "CP", \ + "flag", \ + "Define the flag values (default 0x31f) of the minimap." ) + +#:def_opt( "SET_SCALE", "set-scale|setscale", "CP", \ + "vector", \ + "Define the scale values (default 1.0) of the minimap. The parameter" \ + " is either a vector expression or a comma separated list of" \ + " coordinates ('x,z' or 'x,y,z')." ) + +#:def_opt( "SET_ROT", "set-rot|setrot", "CP", \ + "vector", \ + "Define the rotation values (default 0.0) of the minimap. The" \ + " parameter is either a vector expression or a comma separated list of" \ + " coordinates ('x,z' or 'x,y,z')." ) + +#:def_opt( "SET_X", "set-x|setx", "CP", \ + "v1[,v2]", \ + "Define 2 values and sort them to define the minimum and maximum" \ + " x-coordinates of the minimap. If 'v2' is not entered, '-v1' is used" \ + " as second value." ) + +#:def_opt( "SET_Y", "set-y|sety", "CP", \ + "v1[,v2]", \ + "Define 2 values and sort them to define the minimum and maximum" \ + " y-coordinates of the minimap. If 'v2' is not entered, '-v1' is used" \ + " as second value." ) + +#:def_opt( "SET_Z", "set-z|setz", "CP", \ + "v1[,v2]", \ + "Define 2 values and sort them to define the minimum and maximum" \ + " z-coordinates of the minimap. If 'v2' is not entered, '-v1' is used" \ + " as second value." ) + +#:def_opt( "XCENTER", "xcenter", "C", \ + "", \ + "Center the minimap in x-direction. This is done after AUTO and SET," \ + " but before SCALE and SHIFT." ) + +#:def_opt( "YCENTER", "ycenter", "C", \ + "", \ + "Center the minimap in y-direction. This is done after AUTO and SET," \ + " but before SCALE and SHIFT." ) + +#:def_opt( "ZCENTER", "zcenter", "C", \ + "", \ + "Center the minimap in z-direction. This is done after AUTO and SET," \ + " but before SCALE and SHIFT." ) + +#:def_opt( "CENTER", "center", "C", \ + "", \ + "Center the minimap in all 3 directions. This is a short cut for" \ + " {--xcenter --ycenter --zcenter}." ) + +#:def_opt( "SCALE", "scale", "CP", \ + "vector[@origin]", \ + "Transform the data: Scale all coordinates and size values by 'vector'" \ + " relative to the point 'origin'. If the origin is not set, @0,0,0@ is" \ + " used instead.\n" \ + " Negative values mirror the coordinates. Each parameter is either a" \ + " vector expression or a comma separated expression list of" \ + " coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "SHIFT", "shift", "CP", \ + "vector", \ + "Transform the data: Add 'vector' to all coordinates. It's simlar to" \ + " {--translate}, but the addition is done before rotation. The" \ + " parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "XSS", "xss", "CP", \ + "x1old,x1new,x2old,x2new", \ + "--xss (x-scale-shift) calculates the X values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "YSS", "yss", "CP", \ + "y1old,y1new,y2old,y2new", \ + "--yss (y-scale-shift) calculates the Y values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "ZSS", "zss", "CP", \ + "z1old,z1new,z2old,z2new", \ + "--zss (z-scale-shift) calculates the Z values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "ROT", "rot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' (is a vector) around the 3 axes. All 3 axes goes" \ + " through the point 'origin'. If the origin is not set, @0,0,0@ is" \ + " used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,y,z').\n" \ + " Option --rot is an alternative for {--xrot}, {--yrot} and {--zrot}" \ + " to define all 3 rotations in one step. Transformations are" \ + " calculated in the order SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "XROT", "xrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the x-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('y,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "YROT", "yrot|hrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the x-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead. This is" \ + " a horizontal counterclockwise rotation and the old option name" \ + " --hrot can also used.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "ZROT", "zrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the z-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,y' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "YPOS", "ypos", "CP", \ + "pos", \ + "This option defines an Y position for KMP:CKPT transformations. It" \ + " has only impact to X and Z rotations (Options {--xrot} and" \ + " {--zrot}). If not set, the mean y of the active rotation origins are" \ + " used." ) + +#:def_opt( "TRANSLATE", "translate|trans", "CP", \ + "vector", \ + "Transform the data: Add 'vector' to all coordinates. It's simlar to" \ + " {--shift}, but the addition is done after rotation. The parameter is" \ + " either a vector expression or a comma separated expression list of" \ + " coordinates ('x,z' or 'x,y,z'). @--trans@ is a short cut.\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "NULL", "null", "C", \ + "", \ + "Create a neutral transformation without affecting the coordinates." \ + " The only influence is, that the dependent values (like maximum) are" \ + " re-calculated as if a transformation has taken place." ) + +#:def_opt( "NEXT", "next", "C", \ + "", \ + "Close the current transformation step with all scaling, shifting," \ + " rotation and translation options and open a new step with cleared" \ + " options. On transformation each step is logical done one by one. In" \ + " real, one total transformation matrix is calculated and used for" \ + " fast transformations.\n" \ + " If option --next is used, the ability for modifying the scale and" \ + " rotation vectors (not the positions) of different KMP sections is" \ + " nearly always lost. A warning is printed if this occurs." ) + +#:def_opt( "ASCALE", "ascale", "CP", \ + "factor@dir", \ + "Transform the data and do an axis scale: Close the current" \ + " transformation step like {--next} and scale the data by @'factor'@" \ + " into the direction given by the vector @'dir'@. Store the resulting" \ + " matrix as single transformation step and open a new one.\n" \ + " This kind of transformation is EXPERIMENTAL!" ) + +#:def_opt( "AROT", "arot", "CP", \ + "degree[@pos1]@pos2", \ + "Transform the data and do an axis rotation: Close the current" \ + " transformation step like {--next} and rotate the data by @'degree'@" \ + " around the axis defined by the 2 points. If @'pos1'@ is not set, use" \ + " @0,0,0@ instead. Store the resulting matrix as single transformation" \ + " step and open a new one.\n" \ + " This kind of transformation is EXPERIMENTAL!" ) + +#:def_opt( "TFORM_SCRIPT", "tform-script|tformscript", "CP", \ + "script", \ + "Load the script and execute it by the text parser.\n" \ + " Then for each coordinate (2D and 3D), call the macro @TRANSFORM@" \ + " after all other transformations. Call it without parameters and set" \ + " variable @$$I@ with a zero based index of all vertices, variable" \ + " @$$D@ to the dimension (2 or 3) and the variable @$$P@ to the vector" \ + " of the current vertex. If the macro returns a vector, it is the new" \ + " position.\n" \ + " For each file, macro @BEGIN@ is called at the beginning and @END@" \ + " after processing all points, but only, if the macros are defined." \ + " For @END@, variable @$$I@ is set to the number of processed vertices" \ + " in the current file.\n" \ + " Before each macro call, the private and local variables are cleared" \ + " and only global parameters are permanent. The zero based index is" \ + " cleared for each new source. The parameters of the macro calls are" \ + " defined in the local name space." ) + +#:def_opt( "MINIMAP", "minimap", "C", \ + "", \ + "Fix the minimap position in the same way as {wszst MINIMAP --auto}." \ + " All other minimap related options are ignored for the minimap" \ + " processing." ) + +#:def_opt( "RM_GOBJ", "rm-gobj|rmgobj", "CP", \ + "objlist", \ + "Remove objects from the KMP section @GOBJ@. Therefor a comma" \ + " separated object list with single values like @'a'@ or ranges like" \ + " @'a:b'@ is expected. @'a'@ and @'b'@ are numbers, object names" \ + " (prefixed by 'o$$') or any other parser expressions." ) + +#:def_opt( "BATTLE", "battle", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used.\n" \ + " If mode is @ON@, assume a battle arena and enable a spüecial" \ + " battle support. If mode is @OFF@, disable special battle arena" \ + " support. If mode is @AUTO@, analyse the KMP and estimate, if it is a" \ + " battle areana. Modes @ON@ and @OFF@ override KMP parameter" \ + " @BATTLE-MODE@." ) + +#:def_opt( "EXPORT_FLAGS", "export-flags|exportflags|exflags", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used. --exflags is an alternative name for the option.\n" \ + " If mode is @ON@, this feature is forced: On creating a KMP text" \ + " file, the option @EXPORT-FLAGS@ is always set to 1. On creating a" \ + " binary KMP file, the flags are always exported as lowest byte of the" \ + " scale factors or x-rotation.\n" \ + " If mode is @OFF@, this feature is totally disabled. If mode is not" \ + " @AUTO@, KMP parameter @EXPORT-FLAGS@ is overridden." ) + +#:def_opt( "ROUTE_OPTIONS", "route-options|routeoptions|ropt", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used.\n" \ + " On mode @ON@, the new commands @$$PREV@ and @$$NEXT@ are printed" \ + " for each group of KMP sections ENPT and ITPT. On mode @AUTO@, the" \ + " command is only printed, if any option is set or if parameter" \ + " @SHOW-OPTIONS@ is >0. On mode @OFF@, this feature is totally" \ + " disabled to create KMP text files that are compatible with versions" \ + " <1.60a.\n" \ + " This option exists only for a transition period. Its behaviour will" \ + " be changed in the future. @--ropt@ is a short cut." ) + +#:def_opt( "WIM0", "wim0", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used.\n" \ + " If mode is @ON@, a KMP section called WIM0 is added on binary KMP" \ + " creation. The sections contains information for Wiimms tools.\n" \ + " If mode is @OFF@, this feature is totally disabled. If mode is not" \ + " @AUTO@, KMP parameter @KMP-WIM0@ is overridden." ) + +#:def_opt( "SLOT", "slot", "CP", \ + "keyword", \ + "Patch a track file to run at the specified slot. KMP and KCL may be" \ + " modified. BRRES files may be added to or removed from SZS. The main" \ + " keywords are @DAISY, DESERT, SHERBET, SHYGUY, STANDARD@ and @MOST@." \ + " Slot numbers (except '42' and '4.2') and different other track names" \ + " are also accepted. For some conversions, the auto-add library is" \ + " needed." ) + +#:def_opt( "LOAD_KCL", "load-kcl|loadkcl", "GP", \ + "file", \ + "Load a KCL or OBJ file for reference issues." ) + +#:def_opt( "KCL", "kcl", "GP", \ + "list", \ + "Set global options for KCL processing. Parameter @'list'@ is a comma" \ + " separated list of keywords. A minus sign before a keyword disables a" \ + " setting. Each occurrence of the option will only change entered" \ + " settings and all other settings are untouched.\n" \ + " Keyword @DEFAULT@ resets the default settings and @CLEAR@ disables" \ + " all. Keywords @SMALL, MEDIUM@ and @CHARY@ select the default" \ + " parameters for the octree creation. The other allowed keywords are:" \ + " @FAST, NEW, CENTER, ROUND, NORMALS, MTL, WIIMM, TRIANGLES, OUT-SWAP," \ + " G, USEMTL, CLIP, IN-SWAP, AUTO, HEX4, HEX23, HEX, DROP-UNUSED," \ + " DROP-FIXED, DROP-INVALID, DROP, RM-FACEDOWN, RM-FACEUP, FIX-ALL," \ + " CONV-FACEUP, WEAK-WALLS, SORT, INPLACE, SILENT@ and @LOG@." ) + +#:def_opt( "KCL_FLAG", "kcl-flag|kclflag", "CP", \ + "joblist", \ + "Modify the KCL flag of KCL files. A comma separated job list in the" \ + " format @'srclist=dest'@ is expected. @'srclist'@ is a plus sign" \ + " separated list with single values like @@, @T@ and" \ + " @/@ or ranges like @:@, @T:@ and" \ + " @:/@. @''@ is a complete KCL flag in the range" \ + " of @0..0xffff@. @''@ are only the base types in the range of" \ + " @0..0x1f@. If @@ is present, it marks the relevant bits. For" \ + " the @T@ variant, the mask is set to @0x1f@.\n" \ + " All KCL flags of the source list are assigned to the new value" \ + " @'dest'@, but only the set bits of @''@ are modified. A later" \ + " definition overrides the previous one." ) + +#:def_opt( "KCL_SCRIPT", "kcl-script|kclscript", "CP", \ + "script", \ + "After reading a KCL source file and executing all transformations," \ + " the text file @'script'@ is executed by the internal text parser." \ + " The script should use the parser functions @tri$$*()@ to read and" \ + " modify the triangle positions and flags, or to add or remove" \ + " complete triangles.\n" \ + " If this option is used multiple times, each script is executed in" \ + " the entered order." ) + +#:def_opt( "TRI_AREA", "tri-area|triarea", "GP", \ + "EXPR", \ + "Define the minimal area size of KCL triangles. The intention is to" \ + " ignore triangles that are generally to small. @EXPR@ is a floating" \ + " point number or expression. Triangles are invalidated if their area" \ + " size is smaller than @EXPR@. The test is executed after reading" \ + " files, after transformations, and after calculating normals and" \ + " lengths. Values between 0.01 and 4.0 are recommended. The careful" \ + " value 1.0 is used as default. Value 0 disables this filter" \ + " functionality." ) + +#:def_opt( "TRI_HEIGHT", "tri-height|triheight|tri-ht|triht", "GP", \ + "EXPR", \ + "Define the minimal height of KCL triangles. The intention is to" \ + " ignore deformed triangles (very slim, but long). @EXPR@ is a" \ + " floating point number or expression. Triangles are invalidated if at" \ + " least 1 of the 3 heights is smaller than @EXPR@. The test is" \ + " executed after reading files, after transformations, and after" \ + " calculating normals and lengths. Values between 0.01 and 2.0 are" \ + " recommended. The careful value 1.0 is used as default. Value 0" \ + " disables this filter functionality.\n" \ + " --tri-ht is an alternative name for the option." ) + +#:def_opt( "FLAG_FILE", "flag-file|flagfile", "CP", \ + "filename", \ + "Define a flag file if scanning an OBJ file (KCL processing). The file" \ + " name may contain %-escapes. Set it to an empty string to disable" \ + " flag files at all.\n" \ + " The tool search the flag file in the following order until found:" \ + " File defined by --flag-file; OBJ file name with replaced extension" \ + " ('a.obj' becomes 'a.flag'); OBJ file name with appended extension" \ + " ('a.obj' becomes 'a.obj.flag')." ) + +#:def_opt( "KMP", "kmp", "GP", \ + "list", \ + "Set global options for KMP processing. Parameter @'list'@ is a comma" \ + " separated list of keywords. A minus sign before a keyword disables a" \ + " setting. Each occurrence of the option will only change entered" \ + " settings and all other settings are untouched.\n" \ + " Keyword @DEFAULT@ resets the default settings and @CLEAR@ disables" \ + " all. The other allowed keywords are: @FORCE, NEW, RM-SPCITEM, LEFT," \ + " RIGHT, WIDE, NARROW, FIX-CKPH, FIX-ENPH, FIX-ITPH, FIX-PH," \ + " FIX-CKNEXT, FIX-CKJGPT, FIX-CK, FIX-ALL, MASK-PFLAGS, RM-LECODE," \ + " PURGE-GOBJ, FULL-DEFOBJ, DUMP-CLASS, DUMP-ONEWAY, DUMP-ALL, 1LAP ..." \ + " 9LAPS, MAX-LAPS, RM-EMPTY, INPLACE, SILENT@ and @LOG@." ) + +#:def_opt( "SPEED_MOD", "speed-mod|speedmod", "GP", \ + "factor", \ + "The speed modifier is a user extensions to change the basic speed of" \ + " all vehicles. The speed factor itself is stored into KMP:STGI" \ + " section of a track.\n" \ + " If --speed-mod is set to a value >0.0, then the last 2 bytes of" \ + " the STGI section are patched. The value 0.0 reset these 2 bytes and" \ + " deactivates the speed modifier." ) + +#:def_opt( "KTPT2", "ktpt2", "GP", \ + "vector", \ + "Insert or replace a second KTPT. Use VECTOR as new position. VECTOR" \ + " is either @X,Z@ or @X,Y,Z@ or an expression. Set Y to 0 for an" \ + " automatic defined height.\n" \ + " If the first 4 characters of VECTOR are 'AUTO' (ignoring case)," \ + " then the position is calculated automatically and set to the" \ + " recommendation of command @wkmpt KTPT@.\n" \ + " The tool looks for the nearest lap counter (CKPT with mode 0) and" \ + " adjust the KTPT: The direction of the lap counter is copied and the" \ + " KTPT is moved to the lap counter line using the new direction.\n" \ + " LE-CODE uses the second KTPT to draw the finish line at another" \ + " place away from the start position." ) + +#:def_opt( "TFORM_KMP", "tform-kmp|tformkmp|tkmp", "GP", \ + "list", \ + "Select KMP sections and objects for a transformation. @--tkmp@ is a" \ + " short cut. The option expects a comma separated list of keywords." \ + " Non ambiguous of keywords are allowed and minus signs within" \ + " keywords are optional (e.g. @GOBJS@). A minus sign before a keyword" \ + " disables transformation of the related section or object.\n" \ + " Sections are selected by the KMP section names @AREA, CAME, CKPT," \ + " CNPT, ENPT, GOBJ, ITPT, JGPT, KTPT, MSPT@ and @POTI@.\n" \ + " Objects are selected by the keywords @AREA-POSITION, AREA-ROTATE," \ + " AREA-SCALE, CAME-POSITION, CKPT-POSITION, CNPT-POSITION," \ + " CNPT-ROTATE, ENPT-POSITION, ENPT-SCALE, GOBJ-POSITION, GOBJ-ROTATE," \ + " GOBJ-SCALE, ITPT-POSITION, ITPT-SCALE, JGPT-POSITION, JGPT-ROTATE," \ + " KTPT-POSITION, KTPT-ROTATE, MSPT-POSITION, MSPT-ROTATE@ and" \ + " @POTI-POSITION@.\n" \ + " The keywords @NONE@ and @ALL@ (default) complete the field." ) + +#:def_opt( "REPAIR_XPF", "repair-xpflags|repairxpflags|repxpf", "CP", \ + "file", \ + "Load a KMP file and use it to repair the settings of the extended" \ + " presence flags, if they are destroyed by another KMP tool." \ + " @--repxpf@ is a short cut for this option." ) + +#:def_opt( "GAMEMODES", "gamemodes|gmodes", "CP", \ + "list", \ + "Filter the list of combinations. The parameter is a comma separated" \ + " list of keywords: @AUTO@: Add a automatic mode without conditions" \ + " first. @STANDARD@ (or @STD@): Add standard modes. @1STANDARD@ (or" \ + " @1STD@): Add standard modes first. @BALLOON@: Add balloon battle" \ + " modes. @COIN@: Add coin runners modes. @BATTLE@: Short cut for" \ + " 'BALLOON,COIN'. @VERSUS@ (or @VS@): Add versus modes. @ITEMRAIN@:" \ + " Add itemrain modes. @RACE@: Short cut for 'VERSUS,ITEMRAIN'. @ALL@" \ + " (default): Short cut for 'STANDARD,BATTLE,RACE'. @NO-MODES@: Exclude" \ + " 'ALL' and create auto modes only. @ENGINE@: Include engine classes" \ + " (default). @NO-ENGINE@: Exclude engine classes. @RANDOM@: Include" \ + " random modes (default). @NO-RANDOM@: Exclude random modes." \ + " @OFFLINE@: Filter offline modes. @ONLINE@: Filter online modes." \ + " @NO-TT@: Exclude time trial. @TT@: Force a time trial scenario, even" \ + " if it is a duplicate. @FULL@: Don't eliminate duplicates from list" \ + " (up to 1573 modes). @NSORT@: Sort the list by name. @ISORT@: Sort" \ + " the list by index. @DEBUG@: Print a debug info about this option." ) + +#:def_opt( "MDL", "mdl", "GP", \ + "list", \ + "Set global options for MDL processing. To enable MDL patching, use" \ + " option {--patch-file} and select BRRES files for patching.\n" \ + " Parameter @'list'@ is a comma separated list of keywords. A minus" \ + " sign before a keyword disables a setting. Each occurrence of the" \ + " option will only change entered settings and all other settings are" \ + " untouched.\n" \ + " A MDL transformation (controlled by the transformation options) is" \ + " only done, if at least one of the keywords @VECTOR@ or @VERTEX@ is" \ + " set.\n" \ + " Keyword @DEFAULT@ resets the default settings (@VERTEX@) and" \ + " @CLEAR@ disables all. The other allowed keywords are: @VECTOR," \ + " MATRIX, VERTEX, PARENT, CHILD@ and @LOG@." ) + +#:def_opt( "PAT", "pat", "GP", \ + "list", \ + "Set global options for PAT processing. Parameter @'list'@ is a comma" \ + " separated list of keywords. A minus sign before a keyword disables a" \ + " setting. Each occurrence of the option will only change entered" \ + " settings and all other settings are untouched.\n" \ + " Keyword @DEFAULT@ resets the default settings and @CLEAR@ disables" \ + " all. The other allowed keywords are: @AUTO, SIMPLE, COMPLEX, BOTH," \ + " SILENT@ and @LOG@." ) + +#:def_opt( "PATCH_FILE", "patch-file|patchfile|pfile", "GP", \ + "list", \ + "Define, which kind of KMP, KCL and BRRES/MDL files are objects for" \ + " patching. @--pfile@ is a short cut.\n" \ + " Parameter @'list'@ is a comma separated list of keywords. A minus" \ + " sign before a keyword disables a setting. Each occurrence of the" \ + " option will only change entered settings and all other settings are" \ + " untouched.\n" \ + " Keyword @DEFAULT@ resets the default settings (@TRACK,LOG@) and" \ + " @CLEAR@ disables all. The other allowed keywords are: @KMP, KCL," \ + " OTHER-KCL, ALL-KCL, MODEL, MAP, VRCORN, OTHER-BRRES, ALL-BRRES," \ + " TRACK (=KMP,KCL,MODEL,MAP,VRCORN), ALL, LOG@ and @LOG-ALL@." ) + +#:def_opt( "KMG_LIMIT", "kmg-limit|kmglimit", "GP", \ + "seconds", \ + "Limit the run time for Ballon Battle and Coin Runners to the given" \ + " time in SECONDS. Smaller values are not changed. The limitation is" \ + " carried out after loading a binary file and before scanning a text" \ + " file. Value 0 disables this option." ) + +#:def_opt( "KMG_COPY", "kmg-copy|kmgcopy", "GP", \ + "id", \ + "Make all 10 slots of a KMG file equal after loading a binary file and" \ + " before scanning a text file. ID is either a slot index (0..9) or a" \ + " slot name (A11..A25) and declares the source slot. Value -1 disables" \ + " this option. If ID contains brackets [...], then it is analysed as" \ + " an arena name and the part between the brackets is searched for" \ + " attributes A11..A25." ) + +#:def_opt( "LT_CLEAR", "lt-clear|ltclear", "G", \ + "", \ + "Clear the LEX/TEST section and remove the section and course.lex if" \ + " empty. This option is executed before all other --lt-* options." ) + +#:def_opt( "LT_ONLINE", "lt-online|ltonline", "GP", \ + "mode", \ + "Set LEX/TEST parameter OFFLINE-ONLINE to this MODE. MODE is one of" \ + " @AUTO@ (default), @NEVER@ or @ALWAYS@. Only track.szs files are" \ + " modified. If needed, file course.lex and section TEST are created or" \ + " removed if empty." ) + +#:def_opt( "LT_N_PLAYERS", "lt-n-players|lt-nplayers|ltnplayers", "GP", \ + "offline,online", \ + "Set LEX/TEST parameters N-OFFLINE and N-ONLINE to these integer" \ + " values. Only track.szs files are modified. If needed, file" \ + " course.lex and section TEST are created or removed if empty." ) + +#:def_opt( "LT_COND_BIT", "lt-cond-bit|lt-condbit|ltcondbit", "GP", \ + "bitnum", \ + "Set LEX/TEST parameter COND-BIT to this integer value. Use -1 to" \ + " deactivate this COND-BIT. Only track.szs files are modified. If" \ + " needed, file course.lex and section TEST are created or removed if" \ + " empty." ) + +#:def_opt( "LT_GAME_MODE", "lt-game-mode|lt-gamemode|ltgamemode", "GP", \ + "mode", \ + "Set LEX/TEST parameter GAME-MODE to this MODE. MODE is one of @AUTO@" \ + " (default), @BALLOON, COIN, VERSUS@ or @ITEMRAIN@. Only track.szs" \ + " files are modified. If needed, file course.lex and section TEST are" \ + " created or removed if empty." ) + +#:def_opt( "LT_ENGINE", "lt-engine|ltengine", "GP", \ + "mode", \ + "Set LEX/TEST parameter ENGINE to this MODE. MODE is one of @AUTO@" \ + " (default), @BATTLE, 50CC, 100CC, 150CC, 200CC, 150M@ or @200M@. Only" \ + " track.szs files are modified. If needed, file course.lex and section" \ + " TEST are created or removed if empty." ) + +#:def_opt( "LT_RANDOM", "lt-random|ltrandom", "GP", \ + "index", \ + "Force a random scenario, if between 1 and 8. Value 0 enables auto" \ + " selection and -1 disables this option. Only track.szs files are" \ + " modified. If needed, file course.lex and section TEST are created or" \ + " removed if empty." ) + +#:def_opt( "LEX_PURGE", "lex-purge|lexpurge", "G", \ + "", \ + "Delete LEX sections without any impact. Delete sub-file course.lex if" \ + " it no longer contains a section. This option is executed after all" \ + " --lt-* options." ) + +#:def_opt( "LOAD_BMG", "load-bmg|loadbmg", "CMP", \ + "file", \ + "Load immediately the file to fill an internal message pool. The" \ + " option can be used multiple times to fill the message pool by" \ + " different sources. A later message overrides the previous message.\n" \ + " The internal message pool is patched by all {--patch-bmg} options." ) + +#:def_opt( "PATCH_BMG", "patch-bmg|patchbmg|pb", "CMP", \ + "mode[cond][=param]", \ + "This option specifies a BMG patch mode. Some of the modes need a" \ + " parameter or a file name of a BMG patch file (raw or text BMG), both" \ + " separated by an equal sign. Modes with required file names are" \ + " @PRINT, REPLACE, INSERT, OVERWRITE, DELETE, MASK, EQUAL@ and" \ + " @NOTEQUAL@. Modes with text parameter are @FORMAT@, @REGEXP@ and" \ + " @RM-REGEXP@. Standalone modes are @ID, ID-ALL, UNICODE, RM-ESCAPES," \ + " RM-CUPS, CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY," \ + " LE-FILL, X-COPY, X-FORCE-COPY@ and @X-FILL@. Unique abbreviations" \ + " are allowed.\n" \ + " The optional condition @COND@ is either '@?MID@' or '@!MID@'. In" \ + " case of '@?MID@', the patch is only applied if the message id MID" \ + " already exists. In case of '@!MID@', the patch is only applied if" \ + " the message id MID does not exists.\n" \ + " If this option is used multiple times, all patch files will be" \ + " processed in the entered order." ) + +#:def_opt( "MACRO_BMG", "macro-bmg|macrobmg|mb", "CMP", \ + "file", \ + "Load a BMG file and add the messages to the macro library; already" \ + " existing entries are replaced. The macro library is accessed by" \ + " escape sequence @\\m{{MID}@ as fallback, if the active file has not" \ + " already defined the message @MID@ by itself." ) + +#:def_opt( "FILTER_BMG", "filter-bmg|filterbmg|fb|msg", "CMP", \ + "list", \ + "If this option is set, the BMG messages are filtered by this list." \ + " Only enabled messages are exported to the output BMG file (binary or" \ + " text).\n" \ + " The parameter is a list of message ids (short MID, 'Txx' or 'Uxx'" \ + " or Mxx' or hex number) or message ranges (MID:MID) or one of the" \ + " keywords @NONE, IDENT (=ID), PARAM, CUPS, TRACKS, ARENAS, CHAT," \ + " CTCODE (=CT), CTUPS, CTTRACKS, CTARENAS, CTREFS, LECODE (=LE)," \ + " LEUPS, LETRACKS, LEARENAS, LEREFS, XCODE (=X), XUPS, XTRACKS," \ + " XARENAS, XREFS, ALLCODE, ALLUPS, ALLTRACKS, ALLARENAS, ALLREFS," \ + " GENERIC@ or @ALL@. If an element is preceded by a minus sign, it is" \ + " removed from the filter list (disabled).\n" \ + " With v2.01a, this option was renamed from --msg to the more" \ + " meaningful name --filter-bmg. The old name is still available." ) + +#:def_opt( "CACHE", "cache", "GP", \ + "dir", \ + "Define a directory for a SZS cache.If creating a YAZ0 compressed file" \ + " with standard compression (level 9), the cache is searched for the" \ + " compressed version. File cache-content.txt is used as file list with" \ + " hash values. If the cache file is not found or the content differ," \ + " the file is (re-)created and exit status 2=differ is returned.\n" \ + " Background: Loading a compressed SZS and content verification is" \ + " much faster than YAZ compression.\n" \ + " For legay reasons: If parameter DIR is a file, it is split into" \ + " directory and base name. The directory is used for the cache" \ + " directory and the base name for option {--cname}, but only if" \ + " {--cname} is not used." ) + +#:def_opt( "CNAME", "cname", "GP", \ + "name", \ + "Name of the first cache file. This parameter overrides the generated" \ + " cache name. The name is used only once for the very first cache" \ + " candidate." ) + +#:def_opt( "ID", "id", "C", \ + "", \ + "Print an ID with 8 characters instead of a SHA-1 sum with 40" \ + " characters. The SHA-1 sum is used to calculate the ID. The ID" \ + " consists of '0'-'9' and 'a'-'w' (32 characters)." ) + +#:def_opt( "BASE64", "base64", "CO", \ + "[=mode]", \ + "Encode the the checksum string as BASE64 with 27 characters instead" \ + " of hex. If a parameter is set, it acts like {--coding=mode}." ) + +#:def_opt( "DB64", "db64", "CO", \ + "[=mode]", \ + "Encode the the checksum string and some length as BASE64 with 32" \ + " characters instead of hex for database searches. This options" \ + " implies {--coding=URL} if no other coding is defined. If a parameter" \ + " is set, it acts like {--coding=mode}. Option {--db64} has priority" \ + " over {--base64}." ) + +#:def_opt( "CODING", "coding", "CP", \ + "mode", \ + "Define the coding of the non alphanumeric characters for BASE64:" \ + " STANDART (+/=, default), URL (-_=), STAR (-_*) or XML (.-*)." ) + +#:def_opt( "VERIFY", "verify", "C", \ + "", \ + "Calculate the checksum and compare the filename and return exit" \ + " status (0=valid, 2=differ). The result is valid, if the checksum is" \ + " found with a leading and a following point or minus sign. If" \ + " {--quiet} is set, error messages are suppressed." ) + +#:def_opt( "ROUND", "round", "C", \ + "", \ + "Print additional lines with rounded values (to 3 and 2 bytes for" \ + " floats and 7 and 6 bytes for doubles)." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print in long format. Multiple usage possible." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress printing of header and footer." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Suppress information lines in decoded text files. This includes" \ + " syntax information ({--no-header}). If set twice, the output of" \ + " unneeded sections is also suppressed." ) + +#:def_opt( "PIPE", "pipe", "CM", \ + "", \ + "Use pipe sign instead of spaces to separate columns." ) + +#:def_opt( "DELTA", "delta", "C", \ + "", \ + "Print only modified records or sections on text output. Supported" \ + " file formats until now: @GEOHIT@, @ITEMSLOT@, @KMG@ and @OBJFLOW@." \ + " If set twice, limit output to modified records (only relevant for" \ + " @ITEMSLOT@)." ) + +#:def_opt( "DIFF", "diff", "C", \ + "", \ + "On text output, print only differences from original files. It's a" \ + " short cut for --no-header --brief --brief --delta --delta." \ + " Additional, the output is limited to data sections." ) + +#:def_opt( "BMG_ENCODING", "bmg-encoding|bmgencoding", "CP", \ + "name", \ + "Force a BMG encoding for new BMG files. Accepted encoding names are:" \ + " @CP-1252@, @UTF-16BE@ (or @UTF-16@), @SHIFT-JIS@, @UTF-8@ and @AUTO@" \ + " for automatic detection (default). Same keywords without minus signs" \ + " are accepted too.\n" \ + " Mario Kart Wii uses encoding @UTF-16BE@; the default if no other" \ + " encoding is defined." ) + +#:def_opt( "BMG_INF_SIZE", "bmg-inf-size|bmginfsize", "CP", \ + "size", \ + "Defines the 'INF1' size of new BMG files between 4 and 1000. The" \ + " first 4 bytes of a INF1 record is an offset into the string pool and" \ + " the remaining bytes the attributes. Maximal 40 attribute bytes are" \ + " supported and additional bytes are assumed to be NULL. All BMG files" \ + " of MKW have a size of 8." ) + +#:def_opt( "BMG_MID", "bmg-mid|bmgmid", "CO", \ + "[mode]", \ + "Control the creation of BMG section @MID1@. Suppress the creation, if" \ + " mode is @OFF@. Force the creation, if mode is @ON@. On mode @AUTO@" \ + " (default), the source is analysed for the decision." ) + +#:def_opt( "FORCE_ATTRIB", "force-attrib|forceattrib", "CP", \ + "attrib", \ + "Forces, that all attributes are set to this vector." ) + +#:def_opt( "DEF_ATTRIB", "def-attrib|defattrib", "CP", \ + "attrib", \ + "Define the default attributes for BMG files. If not set, the default" \ + " attributes are estimated. On text output, strings with default" \ + " attributes are printed without attribute vector. The usual default" \ + " attribute for MKW is @[1]@." ) + +#:def_opt( "NO_ATTRIB", "no-attrib|noattrib", "C", \ + "", \ + "Suppress the output of any BMG attributes if creating BMG text files." ) + +#:def_opt( "X_ESCAPES", "x-escapes|xescapes", "C", \ + "", \ + "Use \\x{{} escapes instead of \\z{{} escapes." ) + +#:def_opt( "OLD_ESCAPES", "old-escapes|oldescapes", "C", \ + "", \ + "To be compatible with v1.43 and earlier, print 1A escape sequences" \ + " with total size of >12 bytes as single words using \\x{{}. Also" \ + " don't use \\x{{} and \\u{{} with parameter lists." ) + +#:def_opt( "SINGLE_LINE", "1|single-line|singleline", "C", \ + "", \ + "If set, don't print continuation lines for BMG text output. If set" \ + " twice, print only single text lines and suppress attributes like" \ + " option {--no-attrib} does it." ) + +#:def_opt( "NO_BMG_COLORS", "no-bmg-colors|nobmgcolors", "C", \ + "", \ + "If set, suppress the output of '\\c{{color}' escape sequences for" \ + " colors definitions in BMG text output to be compatible with old" \ + " versions of the tools." ) + +#:def_opt( "BMG_COLORS", "bmg-colors|bmgcolors", "C", \ + "", \ + "Force output of '\\c{{color}' escape sequences with names, even for" \ + " foreign (not MKWii) BMG files." ) + +#:def_opt( "NO_BMG_INLINE", "no-bmg-inline|nobmginline", "C", \ + "", \ + "Print BMG attributes as separate line before the message definition" \ + " to be compatible with versions until v1.39. The output for the" \ + " standard value 0x01000000 is always suppressed. This was the default" \ + " until v1.43." ) + +#:def_opt( "EXPORT", "X|export", "C", \ + "", \ + "Enable the export mode and create small and machine readable text" \ + " files for easy post processing. The option works similar like @-HBB@" \ + " for KMP and like @-HBl1@ for BMG text files." ) + +#:def_opt( "NO_PARAM", "P|no-param|noparam", "C", \ + "", \ + "Disable parameter support in decoded KMP text files." ) + +#:def_opt( "SORT", "S|sort", "CP", \ + "mode", \ + "Define a sort mode. Allowed keywords are: @NONE@ (default), @NAME," \ + " OFFSET, U8, PACK, BRRES, BREFT@ and @AUTO@" ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "TRACKS", "T|tracks", "GP", \ + "tracks", \ + "Define a new track order. A comma separated list in the format" \ + " @[a=]b@ or @a:=b@ is expected. @'a'@ and @'b'@ are track names or" \ + " slot numbers. @'a'@ is the destination slot and @'b'@ the assigned" \ + " track. If @'a'@ is omitted, the next slot is used; this means" \ + " @[a=]b,c,d@ is possible.\n" \ + " Tracks will only be exchanged so that always all tracks are" \ + " assigned. But if using @':='@ the new slots are assigned without" \ + " exchanging and the old values are lost." ) + +#:def_opt( "ARENAS", "A|arenas", "GP", \ + "arenas", \ + "Define a new arena order. A comma separated list in the format" \ + " @[a=]b@ or @a:=b@ is expected. @'a'@ and @'b'@ are arena names or" \ + " slot numbers. @'a'@ is the destination slot and @'b'@ the assigned" \ + " arena. If @'a'@ is omitted, the next slot is used; this means" \ + " @[a=]b,c,d@ is possible.\n" \ + " Arenas will only be exchanged so that always all arenas are" \ + " assigned. But if using @':='@ the new slots are assigned without" \ + " exchanging and the old values are lost." ) + +#:def_opt( "AUTOADD_PATH", "autoadd-path|autoaddpath", "GP", \ + "directory", \ + "Define an alternative directory as autoadd library. The default is," \ + " that the sub-directory called './auto-add/' of all search paths are" \ + " used. Use {wszst test -vvvv} for tests." ) + +#:def_opt( "TRANSFORM", "x|transform", "CP", \ + "list", \ + "Convert image formats. A comma separated list with terms is expected." \ + " A term have the syntax @'[SRC=]DEST'@. It means: convert @SRC@" \ + " format into @DEST@ format. If @SRC=@ is not set, the destination" \ + " format is used for all source formats. This makes only sense as last" \ + " rule. If @DEST@ is empty no transformation is done for the source." \ + " Examples: '@TPL@' or '@GRAY@' or '@BTI.IA8@' or" \ + " '@TPL.I4+I8=IA8,TEX=,CMPR@'\n" \ + " @SRC@ and @DEST@ are tuple of file, image and palette formats and" \ + " of 2 more attributes. All parts are optional and separated by" \ + " points, the order is irrelevant.\n" \ + " Allowed file formats are: @TPL, TEX, BTI, BREFT-IMG (=BTIMG), PNG@" \ + " (import). Allowed image formats are: @I4, I8, IA4, IA8, RGB565" \ + " (=R565), RGB5A3 (=R3), RGBA32 (=R32), C4, C8, C14X2, CMPR@. Allowed" \ + " palette formats are: @P-IA8 (=P8), P-RGB565 (=P565), P-RGB5A3" \ + " (=P3)@. Attributtes are @PALETTE@, @NO-PALETTE@, @GRAY@, @COLOR@," \ + " @ALPHA@ and @NO-ALPHA@.\n" \ + " All terms are managed as list. For each graphic the list is" \ + " iterated until the first SRC tuple of a term matches the graphic." \ + " Then the destination tuple is used for the transformation.\n" \ + " The command {wimgt TEST} prints a clearly arranged readable table" \ + " as result of this option." ) + +#:def_opt( "STRIP", "strip", "C", \ + "", \ + "If creating a PNG file, don't add comments about creator and source" \ + " formats." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "TINY", "tiny", "GP", \ + "mode", \ + "Set tiny mode to a level between 0 and 7." ) + +#:def_opt( "YAZ0", "yaz0", "G", \ + "", \ + "If creating a compressed file, force Yaz0 compression. Yaz0" \ + " compression is the default, if no compression method is defined by" \ + " option or setup file." ) + +#:def_opt( "YAZ1", "yaz1", "G", \ + "", \ + "If creating a compressed file, force Yaz1 compression. The difference" \ + " to Yaz0 is only the magic, which is set to 'Yaz1'." ) + +#:def_opt( "XYZ", "xyz", "G", \ + "", \ + "If creating a compressed file, force XYZ compression (an experimental" \ + " Yaz0 variant)." ) + +#:def_opt( "BZ", "bz", "G", \ + "", \ + "If creating a compressed file, force BZ compression (a BZIP2" \ + " variant)." ) + +#:def_opt( "SH", "sh", "G", \ + "", \ + "Dump info using SH syntax. Use {--var name} as prefix." ) + +#:def_opt( "BASH", "bash", "G", \ + "", \ + "Dump info using BASH syntax. Use {--var name} as prefix." ) + +#:def_opt( "JSON", "json", "G", \ + "", \ + "Dump info using JSON syntax. Options {--var} and {--array} are" \ + " ignored." ) + +#:def_opt( "PHP", "php", "G", \ + "", \ + "Dump infos using PHP syntax. Use {--var name} as variable name of the" \ + " resulting object." ) + +#:def_opt( "MAKEDOC", "makedoc", "G", \ + "", \ + "Dump infos using MakeDoc syntax. Use {--var name} as variable name of" \ + " the resulting map." ) + +#:def_opt( "VAR", "var", "GP", \ + "varname", \ + "Use @VARNAME@ as variable name or prefix on script output. The" \ + " default variabe name is 'res'." ) + +#:def_opt( "ARRAY", "array", "G", \ + "", \ + "Use an array on script output if possible. If arrays are not" \ + " supported, then append a '_#' suffix with an zero based index to the" \ + " variable name defined by {--var name}. In this case, only one output" \ + " file is created." ) + +#:def_opt( "AVAR", "avar", "GP", \ + "varname", \ + "Shortcut for: {--array --var name}" ) + +#:def_opt( "SOURCE", "s|source|src", "CP", \ + "path", \ + "Define a secondary source path (directory/file).\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified primary source name. @%P@ and @%F@ are replaced by" \ + " the primary source path or by the filename. @%N@ and @%E@ are" \ + " replaced by primary source filename without extension or by the" \ + " source extension. Finally, @%T@ is replaced by the default extension" \ + " of a virtual destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_SRC", "remove-src|removesrc|rm-src|rmsrc", "C", \ + "", \ + "Remove the source file after successful operation. @--rm-src@ is a" \ + " short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "IGNORE_SETUP", "ignore-setup|ignoresetup", "C", \ + "", \ + "Ignore hidden setup file '@wszst-setup.txt@'. This file is used to" \ + " determine some default settings and to find out sub archives and" \ + " files to encode. Use this option only if you know what you do." ) + +#:def_opt( "PURGE", "purge", "C", \ + "", \ + "Remove repeated files with same checksum." ) + +#:def_opt( "ALIGN_U8", "align-u8|alignu8", "CP", \ + "size", \ + "Define an align value for the sub files in U8 archives. The value" \ + " must be a power of 2 and the default value is 32 (0x20) This value" \ + " is only relevant if creating or normalizing U8 archives." ) + +#:def_opt( "ALIGN_PACK", "align-pack|alignpack", "CP", \ + "size", \ + "Define an align value for the sub files in PACK archives. The value" \ + " must be a power of 2 and the default value is 32 (0x20) This value" \ + " is only relevant if creating or normalizing OACK archives." ) + +#:def_opt( "ALIGN_BRRES", "align-brres|alignbrres", "CP", \ + "size", \ + "Define an align value for the sub files in BRRES archives. The value" \ + " must be a power of 2 and the default value is 4 This value is only" \ + " relevant if creating or normalizing BRRES archives." ) + +#:def_opt( "ALIGN_BREFF", "align-breff|alignbreff", "CP", \ + "size", \ + "Define an align value for the sub files in BREFF archives. The value" \ + " must be a power of 2 and the default value is 4 This value is only" \ + " relevant if creating or normalizing BREFF archives." ) + +#:def_opt( "ALIGN_BREFT", "align-breft|alignbreft", "CP", \ + "size", \ + "Define an align value for the sub files in BREFT archives. The value" \ + " must be a power of 2 and the default value is 0x20 This value is" \ + " only relevant if creating or normalizing BREFT archives." ) + +#:def_opt( "ALIGN", "align", "CP", \ + "size", \ + "Define an align value for the sub files in all archives. This is a" \ + " short cut for {--align-u8=size --align-pack=size --align-brres=size" \ + " --align-breff=size --align-breft=size}." ) + +#:def_opt( "ENCODE_ALL", "encode-all|encodeall|eall", "C", \ + "", \ + "Encode all files. Normally a file is only encoded, if the source is" \ + " younger than the destination or the destination is missing. Only" \ + " files in the @[encode]@ section of the file '@wszst-setup.txt@' are" \ + " candidates for encoding. @--eall@ is a short cut. Option" \ + " {--no-encode} takes precedence over {--encode-all}." ) + +#:def_opt( "ENCODE_IMG", "encode-img|encodeimg|eimg", "C", \ + "", \ + "Encode all image files. Normally a image file is only encoded, if the" \ + " source is younger than the destination or the destination is" \ + " missing. Only files in the @[encode]@ section of the file" \ + " '@wszst-setup.txt@' are candidates for encoding. @--eimg@ is a short" \ + " cut. If set, option {--no-encode} is ignored for images." ) + +#:def_opt( "NO_ENCODE", "no-encode|noencode|noenc", "C", \ + "", \ + "Do not encode files before creating an archive. @--noenc@ is a short" \ + " cut. Option {--no-encode} takes precedence over {--encode-all}, but" \ + " {--encode-img} takes precedence for images." ) + +#:def_opt( "NO_RECURSE", "no-recurse|norecurse|norec", "C", \ + "", \ + "Do not create archives recursively. Normally files in the @[create]@" \ + " section of the file '@wszst-setup.txt@' are created first. @--norec@" \ + " is a short cut." ) + +#:def_opt( "AUTO_ADD", "auto-add|autoadd|aadd", "C", \ + "", \ + "Analyze the KMP (if exist) and add missing BRRES, BREFF, BREFT, BRASD" \ + " and KCL files automatically, if the tool can find the files in any" \ + " sub directory named 'auto-add'. This sub directory is searched in" \ + " all directories of @'SEARCH_PATH'@; use command @{wszst AUTOADD}@ to" \ + " view them. Command {wszst AUTOADD} may also create an auto-add" \ + " database. @--aadd@ is a short cut." ) + +#:def_opt( "NO_ECHO", "no-echo|noecho", "C", \ + "", \ + "Suppress output of @@ECHO commands while scanning text files." ) + +#:def_opt( "NO_CHECK", "N|no-check|nocheck", "C", \ + "", \ + "Do not make plausibility checks for KCL and KMP files." ) + +#:def_opt( "PT_DIR", "pt-dir|ptdir|pdir", "CO", \ + "[=mode]", \ + "Set one of the `point directory' modes @REMOVE@ (=@0@), @FORCE@" \ + " (=@1@) or @AUTO@ (default). If --pt-dir is used without parameter," \ + " then @FORCE@ is assumed.\n" \ + " This option is only relevant if creating an U8 archive. It decides," \ + " if a special directory with name '.' will be added as base for all" \ + " other files." ) + +#:def_opt( "RM_AIPARAM", "rm-aiparam|rmaiparam|rmai", "C", \ + "", \ + "Remove the directory AIParam and all files from the archive. This" \ + " option has only impact, if creating a new archive or normalizing an" \ + " existing archive. @--rmai@ is a short cut." ) + +#:def_opt( "U8", "u8", "C", \ + "", \ + "Create an U8 archive, if possible. If the source is a WU8 archive," \ + " convert it to an U8 archive. This is the default if the setup file" \ + " '@wszst-setup.txt@' has no other definition. The default compression" \ + " mode is set to Yaz0 (see {--yaz0})." ) + +#:def_opt( "SZS", "szs", "C", \ + "", \ + "Short cut for {--u8 --yaz0}: Create an U8 archive, if possible, and" \ + " set the compression method to Yaz0." ) + +#:def_opt( "WU8", "wu8", "C", \ + "", \ + "Create a WU8 archive, if possible. If the source is an U8 archive," \ + " convert it to a WU8 archive." ) + +#:def_opt( "WBZ", "wbz", "C", \ + "", \ + "Short cut for {--wu8 --bz}: Create a WU8 archive, if possible, and" \ + " set the compression method to BZ." ) + +#:def_opt( "PACK", "pack", "C", \ + "", \ + "Create a PACK archive." ) + +#:def_opt( "BRRES", "brres|bres", "C", \ + "", \ + "Create a BRRES archive." ) + +#:def_opt( "BREFF", "breff", "C", \ + "", \ + "Create a BREFF archive." ) + +#:def_opt( "BREFT", "breft", "C", \ + "", \ + "Create a BREFT archive." ) + +#:def_opt( "NO_COMPRESS", "no-compress|nocompress|noc", "C", \ + "", \ + "Create an uncompressed file instead of a compressed SZS file. The" \ + " default destination changes to @'%P/%N%T'@. @--noc@ is a short cut." ) + +#:def_opt( "COMPRESS", "C|compr|compress", "CP", \ + "level", \ + "Define a compression level between @0..9@: @0@ (or @NOCHUNKS@)" \ + " disables finding chunks, @1@ (or @FAST@) for the fastest standard" \ + " compression and @9@ (or @BEST@) for the best (=default) compression." \ + " @10@ (or @ULTRA@) and @100-150@ are special time-consuming" \ + " compression modes. They are dedicated to competitions with size" \ + " limitations. The special value @UNCOMPRESSED@ acts like option" \ + " {--no-compress}.\n" \ + " Because of many repeated data, the best bz-compression mode varies." \ + " Therefor the levels @TRY2..TRY5@ (or short @T2..T5@) are defined to" \ + " find the best compression mode with testing the first N modes of 9," \ + " 1, 8, 2, 5. @TRY2@ is used for normalizing. For non bz-compression," \ + " @TRY*@ are the same as @BEST@.\n" \ + " Option {--norm} takes precedence over {--compr} and sets the" \ + " compression level to @TRY2@ (@BEST@).\n" \ + " For more modes and details type {wszst -C list}. To force colorized" \ + " output type {wszst -C clist}." ) + +#:def_opt( "FAST", "fast", "C", \ + "", \ + "Short cut for {--compr=fast}: Set the fastest real compression. It" \ + " also overrides compression rates set by {--norm} or {--compr}." ) + +#:def_opt( "NORM", "n|norm", "C", \ + "", \ + "The uncompressed data will be normalized. See command {NORMALIZE} for" \ + " more details." ) + +#:def_opt( "LINKS", "links", "C", \ + "", \ + "Support hardlinks while creating U8 and WU8 archives. Keep hardlinks" \ + " if normalizing U8 and WU8 archives. On extracting, search hardlinks" \ + " in every archive and try to create hardlinks at local file system." ) + +#:def_opt( "BASEDIR", "basedir|bdir", "CP", \ + "directory", \ + "Extract only files from archives, that are below the DIRECTORY." \ + " Leading points and slashes and trailing slahes are removed from the" \ + " parameter. --bdir is short cut for --basedir." ) + +#:def_opt( "RECURSE", "R|recurse", "CO", \ + "[=level]", \ + "If a extracted file is a known and supported archive, than extract it" \ + " recursively until the entered level is reached. Level 0 (=@NONE@)" \ + " deactivates this. If --recurse is used without parameter, then" \ + " @UNLIMITED@ is assumed.\n" \ + " -R does'nt accept a parameter and is a short cut for" \ + " {--recurse=unlimited}." ) + +#:def_opt( "EXT", "e|ext", "C", \ + "", \ + "For BRRES archives only: If a file is extracted, add a handy file" \ + " extension like '.mdl' to the file name. If set twice and a textual" \ + " file magic is available, use the file magic in lower case instead" \ + " like '.mdl0'." ) + +#:def_opt( "DECODE", "decode|dec", "C", \ + "", \ + "If a extracted file is known and can be decoded, do it. @--dec@ is a" \ + " short cut." ) + +#:def_opt( "MIPMAPS", "mipmaps|mm", "C", \ + "", \ + "If reading a source, scan for mipmaps and load them too. For PNG" \ + " files, files named @NAME.mm#.EXT@ with #=1.. are searched. This is" \ + " the default. @--mm@ is a short cut." ) + +#:def_opt( "NO_MIPMAPS", "no-mipmaps|nomipmaps|no-mm|nomm", "C", \ + "", \ + "If reading a source, ignore mipmaps. This disables not the creation" \ + " of mipmaps (see {-n-mipmaps}). @--no-mm@ is a short cut." ) + +#:def_opt( "N_MIPMAPS", "n-mipmaps|nmipmaps|n-mm|nmm", "CP", \ + "num", \ + "Force the number of mipmaps. Values between 0 and 20 are allowed." \ + " Value @OFF@ (default) disables this option. If enabled, options" \ + " @--max-mipmaps@ and @--mipmap-size@ are ignored. The creation of" \ + " mipmaps stops if the width or the height of the mipmap becomes 0." \ + " @--n-mm@ is a short cut." ) + +#:def_opt( "MAX_MIPMAPS", "max-mipmaps|maxmipmaps|max-mm|maxmm", "CP", \ + "num", \ + "Control the automatic mipmap creation. Values between 0 and 20 are" \ + " allowed and the default is 4. The mipmap creation for an image" \ + " stops, if the set number is reached. This option is ignored, if" \ + " @--n-mipmaps@ is enabled. @--max-mm@ is a short cut." ) + +#:def_opt( "MIPMAP_SIZE", "mipmap-size|mipmapsize|mm-size|mmsize", "CP", \ + "num", \ + "Define the minimal width and minimal height of a generated mipmap." \ + " Any value >0 is allowed. The default is 8. This option is ignored," \ + " if @--n-mipmaps@ is enabled. @--mm-size@ is a short cut." ) + +#:def_opt( "FAST_MIPMAPS", "fast-mipmaps|fastmipmaps|fast-mm|fastmm", "C", \ + "", \ + "If resizing an image, use the old fast resize algorithm (default" \ + " until v1.64a) instead of the new smart one. Both algorithms differ" \ + " only, when creating a mipmap for images with odd width or odd" \ + " height. The old algorithm is faster (factor 2-4) than the default" \ + " algorithm, but the new algorithm creates better resized images." \ + " @--fast-mm@ is a short cut." ) + +#:def_opt( "CMPR_DEFAULT", "cmpr-default", "CP", \ + "rgb1[,rgb2]", \ + "Define 2 colors for the case, that all 16 pixels of a CMPR block are" \ + " transparent. The default is to calculate an average color of all" \ + " transparent pixels. Before v2.04 white was used.\n" \ + " 2 RGB values (hex values with 6 digits each) are expected as" \ + " parameter. If optional @RGB2@ is missed, a copy of @RGB1@ is used." \ + " As first step, both colors are converted to RGB565 colors @C1@ and" \ + " @C2@ and sorted, so that @C112 bytes as single words using \\x{{}. Also" \ + " don't use \\x{{} and \\u{{} with parameter lists." ) + +#:def_opt( "SINGLE_LINE", "1|single-line|singleline", "C", \ + "", \ + "If set, don't print continuation lines for BMG text output. If set" \ + " twice, print only single text lines and suppress attributes like" \ + " option {--no-attrib} does it." ) + +#:def_opt( "NO_BMG_COLORS", "no-bmg-colors|nobmgcolors", "C", \ + "", \ + "If set, suppress the output of '\\c{{color}' escape sequences for" \ + " colors definitions in BMG text output to be compatible with old" \ + " versions of the tools." ) + +#:def_opt( "BMG_COLORS", "bmg-colors|bmgcolors", "C", \ + "", \ + "Force output of '\\c{{color}' escape sequences with names, even for" \ + " foreign (not MKWii) BMG files." ) + +#:def_opt( "NO_BMG_INLINE", "no-bmg-inline|nobmginline", "C", \ + "", \ + "Print BMG attributes as separate line before the message definition" \ + " to be compatible with versions until v1.39. The output for the" \ + " standard value 0x01000000 is always suppressed. This was the default" \ + " until v1.43." ) + +#:def_opt( "EXPORT", "X|export", "C", \ + "", \ + "Enable the export mode and create small and machine readable text" \ + " files for easy post processing. The option works similar like" \ + " @-HBl11@ for BMG text files." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "POINTS", "POINTS", \ + "", \ + "" ) + +#:def_cmd_opt( "POINTS", "BRIEF", \ + "", \ + "Print the table as comma separated lists in C notation." ) + +#:def_cmd_opt( "POINTS", "LONG", \ + "", \ + "Append a string in {--points} format that defines the table. If set" \ + " twice, append up to 3 strings in different modes." ) + +#:def_cmd_opt( "EXTRACT", "NO_HEADER", \ + "", \ + "Exclude the section header from output." ) + +#:def_cmd_opt( "SECTIONS", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "SLOTS", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "SLOTS", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "SLOTS", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "SLOTS", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "SLOTS", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "DEF_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "X_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "OLD_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SINGLE_LINE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_BMG_INLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "LONG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "DEF_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "NO_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "X_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "OLD_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "SINGLE_LINE", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "NO_BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "NO_BMG_INLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "LONG", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "MIX", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "LE_CODE", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "CT_CODE", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "DEF_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "NO_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "X_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "OLD_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "SINGLE_LINE", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "NO_BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "NO_BMG_INLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "LONG", \ + "", \ + "" ) + +#:def_cmd_opt( "IDENTIFIER", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEF_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "X_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OLD_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SINGLE_LINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_BMG_INLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "LONG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ALIGN", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEF_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NO_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "X_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "OLD_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "SINGLE_LINE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NO_BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NO_BMG_INLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "LONG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ALIGN", \ + "", \ + "" ) + + +## +############################################################################### +############### Tool 'wctct' ############### +############################################################################### + +#:def_tool( "wctct", \ + "wctct [option]... command [option|parameter|@file]...", \ + "Wiimms CT-CODE Tool : Manage the $CT-CODE$ extension. BRRES, TEX0," \ + " CT-CODE, CT-TEXT and LE-BIN files are accepted as input." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wctct VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wctct HELP [--width=wd] OPT|OPTION ...\n" \ + "wctct HELP [--width=wd] CMD|COMMAND [ignored]...\n" \ + "wctct HELP [--width=wd] command [-b] [ignored]...\n" \ + "wctct HELP [--width=wd] [ignored]...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wctct ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wctct TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wctct COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wctct ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wctct FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wctct FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "SYMBOLS", "SYMBOLS", \ + "wctct SYMBOLS", \ + "Print predefined symbols of text parser to standard output. The" \ + " CT-CODE specific symbols are included." ) + +#:def_cmd( "FUNCTIONS", "FUNCTIONS", \ + "wctct FUNCTIONS [key]...", \ + "Print a list with all parser functions including the CT-CODE specific" \ + " functions. If one key ore more key entered, then only functions that" \ + " match at least one key are printed." ) + +#:def_cmd( "CALCULATE", "CALCULATE", \ + "wctct CALCULATE expression...", \ + "Calculate each expression and print the result to standard output." ) + +#:def_cmd( "FLOAT", "FLOAT", \ + "wctct FLOAT number...", \ + "This commands helps to convert hex values into floating point numbers" \ + " and vice versa. For each non empty parameter, a line with 4 bytes" \ + " hex and a float, 8 bytes hex and a double float, and the parameter" \ + " itself is printed. If a parameter begins with 'x' or '0x', it is a" \ + " hex string, otherwise it is a floating point number.\n" \ + " The special parameters \"+\" and \"-\" mean: Increment or decrement" \ + " the previous value by the smallest possible value. Use different" \ + " increments for floats and doubles.\n" \ + " All conversions are done using big endian." ) + +#:def_cmd( "DUMP", "D|DUMP", \ + "wctct DUMP [source]...", \ + "Print an information dump for the CT-CODE of each source file. BRRES," \ + " TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as input." ) + +#:def_cmd( "CAT", "CAT", \ + "wctct CAT [source]...", \ + "Read the source files and print the CT-CODE part them as decoded text" \ + " files to standard output. The output of all source files is" \ + " concatenated. BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted" \ + " as input." ) + +#:def_cmd( "BMG", "BMG", \ + "wctct BMG [source]...", \ + "Read the source files and print a BMG message listing to standard" \ + " output. BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as" \ + " input." ) + +#:def_cmd( "TRACKS", "TRACKS", \ + "wctct TRACKS [source]...", \ + "Read the source files and print a track listing to standard output." \ + " BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as input." ) + +#:def_cmd( "DECODE", "DECODE|DEC", \ + "wctct DECODE [source]...", \ + "Read the source files and decode them to CT-TEXT files. The default" \ + " destination is @'%P/%N.txt'@. BRRES, TEX0, CT-CODE, CT-TEXT and" \ + " LE-BIN are accepted as input." ) + +#:def_cmd( "CREATE", "CREATE|C", \ + "wctct CREATE keyword [source]...", \ + "Read the source files, scan the CT-CODE part and create an output" \ + " file controlled by the keyword. BRRES, TEX0, CT-CODE, CT-TEXT and" \ + " LE-BIN files are accepted as input. Possible keywords are: @CUP1," \ + " CRS1, r-MOD1, r-MOD2, r-OVR1, r-BAD1CODE, r-BAD1DATA (=r-CODE)," \ + " r-TEX0, r-BRRES, r-SZS, BMG, TRACKS, LIST, REF@ and @FULL@. 'r'" \ + " defines the region and is one of @EU, RMCP, US, RMCE, JP, RMCJ, KO@" \ + " or @RMCK@.\n" \ + " The Korean support needs external CT-CODE files. Use option" \ + " {--ct-dir} to define at least one search path." ) + +#:def_cmd( "PATCH", "PATCH|P", \ + "wctct PATCH [source]...", \ + "Read each source file, find and scan the CT-CODE part and patch it." \ + " BRRES, TEX0, CT-CODE, CT-TEXT and LE-BIN are accepted as input." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "Enable LE-CODE and CT-CODE support. The optional parameter is" \ + " deprecated and ignored." ) + +#:def_opt( "OLD_SPINY", "old-spiny|oldspiny", "G", \ + "", \ + "Disable the new feature to hold a Spiny Shell at the tail." ) + +#:def_opt( "CRS1", "crs1", "GP", \ + "value", \ + "Store this value into all 8 members of the property array of the CRS1" \ + " header if reading a CT-TEXT. The default value is 0x800." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "ROUND", "round", "C", \ + "", \ + "Print additional lines with rounded values (to 3 and 2 bytes for" \ + " floats and 7 and 6 bytes for doubles)." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Suppress cross reference comments." ) + +#:def_opt( "RAW", "raw", "C", \ + "", \ + "Print BMG messages as UTF-8 text without escape sequences." ) + +#:def_opt( "BMG_ENCODING", "bmg-encoding|bmgencoding", "CP", \ + "name", \ + "Force a BMG encoding for new BMG files. Accepted encoding names are:" \ + " @CP-1252@, @UTF-16BE@ (or @UTF-16@), @SHIFT-JIS@, @UTF-8@ and @AUTO@" \ + " for automatic detection (default). Same keywords without minus signs" \ + " are accepted too.\n" \ + " Mario Kart Wii uses encoding @UTF-16BE@; the default if no other" \ + " encoding is defined." ) + +#:def_opt( "BMG_INF_SIZE", "bmg-inf-size|bmginfsize", "CP", \ + "size", \ + "Defines the 'INF1' size of new BMG files between 4 and 1000. The" \ + " first 4 bytes of a INF1 record is an offset into the string pool and" \ + " the remaining bytes the attributes. Maximal 40 attribute bytes are" \ + " supported and additional bytes are assumed to be NULL. All BMG files" \ + " of MKW have a size of 8." ) + +#:def_opt( "BMG_MID", "bmg-mid|bmgmid", "CO", \ + "[mode]", \ + "Control the creation of BMG section @MID1@. Suppress the creation, if" \ + " mode is @OFF@. Force the creation, if mode is @ON@. On mode @AUTO@" \ + " (default), the source is analysed for the decision." ) + +#:def_opt( "FORCE_ATTRIB", "force-attrib|forceattrib", "CP", \ + "attrib", \ + "Forces, that all attributes are set to this vector." ) + +#:def_opt( "DEF_ATTRIB", "def-attrib|defattrib", "CP", \ + "attrib", \ + "Define the default attributes for BMG files. If not set, the default" \ + " attributes are estimated. On text output, strings with default" \ + " attributes are printed without attribute vector. The usual default" \ + " attribute for MKW is @[1]@." ) + +#:def_opt( "NO_ATTRIB", "no-attrib|noattrib", "C", \ + "", \ + "Suppress the output of any BMG attributes if creating BMG text files." ) + +#:def_opt( "X_ESCAPES", "x-escapes|xescapes", "C", \ + "", \ + "Use \\x{{} escapes instead of \\z{{} escapes." ) + +#:def_opt( "OLD_ESCAPES", "old-escapes|oldescapes", "C", \ + "", \ + "To be compatible with v1.43 and earlier, print 1A escape sequences" \ + " with total size of >12 bytes as single words using \\x{{}. Also" \ + " don't use \\x{{} and \\u{{} with parameter lists." ) + +#:def_opt( "SINGLE_LINE", "1|single-line|singleline", "C", \ + "", \ + "If set, don't print continuation lines for BMG text output. If set" \ + " twice, print only single text lines but not value lines." ) + +#:def_opt( "NO_BMG_COLORS", "no-bmg-colors|nobmgcolors", "C", \ + "", \ + "If set, suppress the output of '\\c{{color}' escape sequences for" \ + " colors definitions in BMG text output to be compatible with old" \ + " versions of the tools." ) + +#:def_opt( "BMG_COLORS", "bmg-colors|bmgcolors", "C", \ + "", \ + "Force output of '\\c{{color}' escape sequences with names, even for" \ + " foreign (not MKWii) BMG files." ) + +#:def_opt( "NO_BMG_INLINE", "no-bmg-inline|nobmginline", "C", \ + "", \ + "Print BMG attributes as separate line before the message definition" \ + " to be compatible with versions until v1.39. The output for the" \ + " standard value 0x01000000 is always suppressed. This was the default" \ + " until v1.43." ) + +#:def_opt( "EXPORT", "X|export", "C", \ + "", \ + "Enable the export mode and create small and machine readable text" \ + " files for easy post processing. The option works similar like @-HB@" \ + " for CT-CODE output." ) + +#:def_opt( "NO_PARAM", "P|no-param|noparam", "C", \ + "", \ + "Disable parameter support in decoded KMP text files." ) + +#:def_opt( "NO_ECHO", "no-echo|noecho", "C", \ + "", \ + "Suppress output of @@ECHO commands while scanning text files." ) + +#:def_opt( "NO_CHECK", "N|no-check|nocheck", "C", \ + "", \ + "Do not make plausibility checks for KCL and KMP files." ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "LIST", "list", "C", \ + "", \ + "Cancel all previous {--rel} and {--full} and enter LIST mode" \ + " (default) for text output.\n" \ + " In LIST mode only a track list is generated. On scanning the tracks" \ + " are automatically distributed to the cups. This makes rearanging" \ + " very simple." ) + +#:def_opt( "REF", "ref", "C", \ + "", \ + "Cancel all previous {--list} and {--full} and enter REFERENCE mode" \ + " for text output.\n" \ + " In REFERENCE mode all tracks and cups are printed separately. After" \ + " scanning the cup and track layout is the same as the original code" \ + " file." ) + +#:def_opt( "FULL", "full", "C", \ + "", \ + "Cancel all previous {--list} and {--rel} and enter FULL mode for text" \ + " output.\n" \ + " FULL mode is a combination of LIST and REF modes." ) + +#:def_opt( "HEX", "hex", "C", \ + "", \ + "Normally and if available, names are printed for music and property" \ + " slots. If this option is set, hex numbers are printed always." ) + +#:def_opt( "CT_DIR", "ct-dir|ctdir", "GP", \ + "dir", \ + "Define a search directory for CT-CODE parts and use the internal" \ + " copies only, if no valid file is found. Each file is searched" \ + " without and then with extension '.bz2'. For both cases bzip2 files" \ + " are detected and decompressed. Only files with correct file type are" \ + " accepted.\n" \ + " wctct will search in the sub directory @'rmc[pujk]/'@ for files" \ + " @'mod1.bin', 'mod2.bin', 'ovr1.bin'@ and @'bad1code.bin'@.\n" \ + " Use this option multiple times to define more than one search" \ + " directory." ) + +#:def_opt( "CT_LOG", "ct-log|ctlog", "GO", \ + "[=level]", \ + "Define a log level for option {--ct-dir}. Without parameter, the" \ + " current level is increased. Allowed keywords are: 0=0FF, 1=SUCCESS," \ + " 2=FOUND, 3=ALL" ) + +#:def_opt( "ALLOW_SLOTS", "allow-slots|allowslots", "GP", \ + "list", \ + "Allow and disallow slots for automatic slot assigning. A comma" \ + " separated list of slots (A), slot ranges (A:B) or keywords is" \ + " expected. If preceded by a minus sign, the slots are disabled. The" \ + " keywords are: @TRACKS@ (slots 0:0x1f), @ARENAS@ (0x20:0x29) and" \ + " @SPECIAL@ (0x36:0x3a). @CTCODE@ is an abbreviation for" \ + " @ARENAS,SPECIAL@.\n" \ + " The default is to enable slot ranges @0x00:0x1f@, @0x2a:0x35@," \ + " @0x3b:0x41@ and @0x44:0xfe@." ) + +#:def_opt( "IMAGES", "images", "CP", \ + "dir", \ + "If creating a CT-CODE BRRES or SZS file, then search the 8 strap" \ + " images in the specified directory. The images are searched with" \ + " extensions .png, .tex, .tex0 and without extension in this order" \ + " until a valid image is found. All images will be resized to the" \ + " correct width and height and converted to format TEX.RGB565 without" \ + " mipmaps. If no valid image is found, a hard coded default image is" \ + " used." ) + +#:def_opt( "LOAD_BMG", "load-bmg|loadbmg", "CMP", \ + "file", \ + "Load immediately the file to fill an internal message pool. The" \ + " option can be used multiple times to fill the message pool by" \ + " different sources. A later message overrides the previous message.\n" \ + " The internal message pool is patched by all {--patch-bmg} options." ) + +#:def_opt( "PATCH_BMG", "patch-bmg|patchbmg|pb", "CMP", \ + "mode[cond][=param]", \ + "This option specifies a BMG patch mode. Some of the modes need a" \ + " parameter or a file name of a BMG patch file (raw or text BMG), both" \ + " separated by an equal sign. Modes with required file names are" \ + " @PRINT, REPLACE, INSERT, OVERWRITE, DELETE, MASK, EQUAL@ and" \ + " @NOTEQUAL@. Modes with text parameter are @FORMAT@, @REGEXP@ and" \ + " @RM-REGEXP@. Standalone modes are @ID, ID-ALL, UNICODE, RM-ESCAPES," \ + " RM-CUPS, CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY," \ + " LE-FILL, X-COPY, X-FORCE-COPY@ and @X-FILL@. Unique abbreviations" \ + " are allowed.\n" \ + " The optional condition @COND@ is either '@?MID@' or '@!MID@'. In" \ + " case of '@?MID@', the patch is only applied if the message id MID" \ + " already exists. In case of '@!MID@', the patch is only applied if" \ + " the message id MID does not exists.\n" \ + " If this option is used multiple times, all patch files will be" \ + " processed in the entered order." ) + +#:def_opt( "MACRO_BMG", "macro-bmg|macrobmg|mb", "CMP", \ + "file", \ + "Load a BMG file and add the messages to the macro library; already" \ + " existing entries are replaced. The macro library is accessed by" \ + " escape sequence @\\m{{MID}@ as fallback, if the active file has not" \ + " already defined the message @MID@ by itself." ) + +#:def_opt( "FILTER_BMG", "filter-bmg|filterbmg|fb", "CMP", \ + "list", \ + "If this option is set, the BMG messages are filtered by this list." \ + " Only enabled messages are exported to the output BMG file (binary or" \ + " text).\n" \ + " The parameter is a list of message ids (short MID, 'Txx' or 'Uxx'" \ + " or Mxx' or hex number) or message ranges (MID:MID) or one of the" \ + " keywords @NONE, IDENT (=ID), PARAM, CUPS, TRACKS, ARENAS, CHAT," \ + " CTCODE (=CT), CTUPS, CTTRACKS, CTARENAS, CTREFS, LECODE (=LE)," \ + " LEUPS, LETRACKS, LEARENAS, LEREFS, XCODE (=X), XUPS, XTRACKS," \ + " XARENAS, XREFS, ALLCODE, ALLUPS, ALLTRACKS, ALLARENAS, ALLREFS," \ + " GENERIC@ or @ALL@. If an element is preceded by a minus sign, it is" \ + " removed from the filter list (disabled).\n" \ + " With v2.01a, this option was renamed from --msg to the more" \ + " meaningful name --filter-bmg. The old name is still available." ) + +#:def_opt( "PATCH_NAMES", "patch-names|patchnames", "C", \ + "", \ + "If set, the names of the CUP1 and CSR1 sections are patched by the" \ + " loaded BMG files directly after loading the source files." ) + +#:def_opt( "ORDER_BY", "order-by|orderby|order", "CP", \ + "bmg", \ + "Load the BMG file and filter track names. Then before creating output" \ + " files, re-distribute the tracks of the additonal cups (9-) to the" \ + " same cups by the order of the read track names. Tracks without BMG" \ + " reference are placed at the end of all tracks. This option modifes" \ + " only the track order of the cups, but will not change any name." ) + +#:def_opt( "ORDER_ALL", "order-all|orderall", "C", \ + "", \ + "Used together with option {--order-by}: Re-distribute the tracks of" \ + " all cups beginning with Mushroom Cup." ) + +#:def_opt( "WRITE_TRACKS", "write-tracks|writetracks|wtracks", "CP", \ + "file", \ + "Create a machine readable file reflecting the racing track" \ + " distribution to the cups. One line of format @'IDX|MID|NAME'@ is" \ + " written for each track. --wtracks is a short cut." ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "SYMBOLS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "BRIEF", \ + "", \ + "Suppress the output of the description and print only function type" \ + " and syntax." ) + +#:def_cmd_opt( "FUNCTIONS", "LONG", \ + "", \ + "Usually only the function syntax is compared to the keywords. But if" \ + " --long is set, the descriptions are compared too." ) + +#:def_cmd_opt( "CALCULATE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "FLOAT", "ROUND", \ + "", \ + "" ) + +#:def_cmd_opt( "RAWDUMP", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "RAWDUMP", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "LONG", \ + "", \ + "Print headers as hexdump." ) + +#:def_cmd_opt( "DUMP", "LOAD_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "PATCH_NAMES", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ORDER_BY", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ORDER_ALL", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "LIST", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "REF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "FULL", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "HEX", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in CTC text files." ) + +#:def_cmd_opt( "CAT", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "WRITE_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "LOAD_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "PATCH_NAMES", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ORDER_BY", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ORDER_ALL", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "BRIEF", \ + "", \ + "Filter the message by tracks, that are used a racing track. If set" \ + " twice, suppress also tracks created by the 'N' command of CT-TEXT" \ + " files. If set 3 times, suppress always the output of first 32" \ + " tracks." ) + +#:def_cmd_opt( "BMG", "LONG", \ + "", \ + "Normally, only the track and arena names are exported. The message id" \ + " (MID) range is 4000-40FF. If set, cup names are exported too as MID" \ + " 4200-423F. If set twice, cup/track relations are also exported as" \ + " MID 4300-43FF." ) + +#:def_cmd_opt( "BMG", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "LIST", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "REF", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "FULL", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "HEX", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in CTC text files." ) + +#:def_cmd_opt( "BMG", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "DEF_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "NO_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "X_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "OLD_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "SINGLE_LINE", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "NO_BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "NO_BMG_INLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "WRITE_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "LOAD_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "PATCH_NAMES", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "ORDER_BY", \ + "", \ + "" ) + +#:def_cmd_opt( "BMG", "ORDER_ALL", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "NO_HEADER", \ + "", \ + "Suppress the table header." ) + +#:def_cmd_opt( "TRACKS", "BRIEF", \ + "", \ + "Suppress the output of the music and property slots." ) + +#:def_cmd_opt( "TRACKS", "LONG", \ + "", \ + "Include the first 32 standard tracks into the output. If set twice," \ + " also include the battle arenas." ) + +#:def_cmd_opt( "TRACKS", "RAW", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "LIST", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "REF", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "FULL", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "HEX", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "WRITE_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "LOAD_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "PATCH_NAMES", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "ORDER_BY", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "ORDER_ALL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "WRITE_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "LIST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "FULL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "HEX", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in CTC text files." ) + +#:def_cmd_opt( "DECODE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "LOAD_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PATCH_NAMES", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ORDER_BY", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ORDER_ALL", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "WRITE_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "HEX", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in CTC text files." ) + +#:def_cmd_opt( "CREATE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "BMG_ENCODING", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "BMG_INF_SIZE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "BMG_MID", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "FORCE_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "DEF_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NO_ATTRIB", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "X_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "OLD_ESCAPES", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "SINGLE_LINE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NO_BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "BMG_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NO_BMG_INLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LONG", \ + "", \ + "BMG: Normally, only the track and arena names are exported. The" \ + " message id (MID) range is 4000-40FF. If set, cup names are exported" \ + " too as MID 4200-423F. If set twice, cup/track relations are also" \ + " exported as MID 4300-43FF." ) + +#:def_cmd_opt( "CREATE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "CT_DIR", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "CT_LOG", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "ALLOW_SLOTS", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "IMAGES", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LOAD_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "PATCH_NAMES", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "ORDER_BY", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "ORDER_ALL", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "WRITE_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "LOAD_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PATCH_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "MACRO_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "FILTER_BMG", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PATCH_NAMES", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ORDER_BY", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ORDER_ALL", \ + "", \ + "" ) + + +## +############################################################################### +############### Tool 'wimgt' ############### +############################################################################### + +#:def_tool( "wimgt", \ + "wimgt [option]... command [option|parameter|@file]...", \ + "Wiimms Image Tool : Extract and convert graphic images. The file" \ + " formats TPL, TEX, BTI, BREFT and PNG are supported." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wimgt VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wimgt HELP [--width=wd] OPT|OPTION ...\n" \ + "wimgt HELP [--width=wd] CMD|COMMAND [ignored]...\n" \ + "wimgt HELP [--width=wd] command [-b] [ignored]...\n" \ + "wimgt HELP [--width=wd] [ignored]...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wimgt ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wimgt TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wimgt COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wimgt ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wimgt FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wimgt FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "LIST", "LIST|LS", \ + "wimgt LIST [source]...", \ + "List images files and print file and image type." ) + +#:def_cmd( "LIST_L", "LIST-L|LL|LISTL", \ + "wit LIST-L [source]...", \ + "List images files and print file and image type and geometry data." \ + " 'LIST-L' is a short cut for {LIST --long}." ) + +#:def_cmd( "LIST_LL", "LIST-LL|LLL|LISTLL", \ + "wit LIST-LL [source]...", \ + "List images files and print file and image type and advanced geometry" \ + " data. 'LIST-LL' is a short cut for {LIST --long --long}." ) + +#:def_cmd( "DECODE", "DECODE|DEC", \ + "wimgt DECODE [source]...", \ + "Decode (export) the entered image files into PNG files. The default" \ + " destination is @'%P/%F.png'@." ) + +#:def_cmd( "ENCODE", "ENCODE|ENC", \ + "wimgt ENCODE [source]...", \ + "Encode (import) the entered PNG files and convert them to to an" \ + " internal image format (TPL, TEX, BREFT). The default destination is" \ + " @'%P/%N'@ (removed extension)." ) + +#:def_cmd( "CONVERT", "CONVERT|CV", \ + "wimgt CONVERT [source]...", \ + "Convert the entered image files into new file and image formats. The" \ + " old file is replaced by the new one. The difference to {ENCODE} is" \ + " only the default file name handling." ) + +#:def_cmd( "COPY", "COPY|CP", \ + "wimgt COPY source dest\n" \ + "wimgt COPY source... [-d|-D] dest", \ + "Copy and convert an image from 'source' to 'dest'. Multiple sources" \ + " can be used, if 'dest' is a directory or contains at least one" \ + " %-pattern.\n" \ + " This is an alternative for the ENCODE and DECODE commands. The" \ + " default destination file format is selected by scanning the file" \ + " extension of the destination. Option --transform may override this." \ + " Option --overwrite is set implicitly." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "Enable LE-CODE and CT-CODE support. The optional parameter is" \ + " deprecated and ignored." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Create smaller output." ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "ALL", "a|all", "C", \ + "", \ + "Convert all files and not only the files with changed file or image" \ + " formats." ) + +#:def_opt( "MIPMAPS", "mipmaps", "C", \ + "", \ + "If reading a source, scan for mipmaps and load them too. For PNG" \ + " files, files named @NAME.mm#.EXT@ with #=1.. are searched. This is" \ + " the default. @--mm@ is a short cut." ) + +#:def_opt( "NO_MIPMAPS", "no-mipmaps|nomipmaps|no-mm|nomm", "C", \ + "", \ + "If reading a source, ignore mipmaps. This disables not the creation" \ + " of mipmaps (see {-n-mipmaps}). @--no-mm@ is a short cut." ) + +#:def_opt( "N_MIPMAPS", "n-mipmaps|nmipmaps|n-mm|nmm", "CP", \ + "num", \ + "Force the number of mipmaps. Values between 0 and 20 are allowed." \ + " Value @OFF@ (default) disables this option. If enabled, options" \ + " @--max-mipmaps@ and @--mipmap-size@ are ignored. The creation of" \ + " mipmaps stops if the width or the height of the mipmap becomes 0." \ + " @--n-mm@ is a short cut." ) + +#:def_opt( "MAX_MIPMAPS", "max-mipmaps|maxmipmaps|max-mm|maxmm", "CP", \ + "num", \ + "Control the automatic mipmap creation. Values between 0 and 20 are" \ + " allowed and the default is 4. The mipmap creation for an image" \ + " stops, if the set number is reached. This option is ignored, if" \ + " @--n-mipmaps@ is enabled. @--max-mm@ is a short cut." ) + +#:def_opt( "MIPMAP_SIZE", "mipmap-size|mipmapsize|mm-size|mmsize", "CP", \ + "num", \ + "Define the minimal width and minimal height of a generated mipmap." \ + " Any value >0 is allowed. The default is 8. This option is ignored," \ + " if @--n-mipmaps@ is enabled. @--mm-size@ is a short cut." ) + +#:def_opt( "FAST_MIPMAPS", "fast-mipmaps|fastmipmaps|fast-mm|fastmm", "C", \ + "", \ + "If resizing an image, use the old fast resize algorithm (default" \ + " until v1.64a) instead of the new smart one. Both algorithms differ" \ + " only, when creating a mipmap for images with odd width or odd" \ + " height. The old algorithm is faster (factor 2-4) than the default" \ + " algorithm, but the new algorithm creates better resized images." \ + " @--fast-mm@ is a short cut." ) + +#:def_opt( "CMPR_DEFAULT", "cmpr-default", "CP", \ + "rgb1[,rgb2]", \ + "Define 2 colors for the case, that all 16 pixels of a CMPR block are" \ + " transparent. The default is to calculate an average color of all" \ + " transparent pixels. Before v2.04 white was used.\n" \ + " 2 RGB values (hex values with 6 digits each) are expected as" \ + " parameter. If optional @RGB2@ is missed, a copy of @RGB1@ is used." \ + " As first step, both colors are converted to RGB565 colors @C1@ and" \ + " @C2@ and sorted, so that @C1@, @T@ and" \ + " @/@ or ranges like @:@, @T:@ and" \ + " @:/@. @''@ is a complete KCL flag in the range" \ + " of @0..0xffff@. @''@ are only the base types in the range of" \ + " @0..0x1f@. If @@ is present, it marks the relevant bits. For" \ + " the @T@ variant, the mask is set to @0x1f@.\n" \ + " All KCL flags of the source list are assigned to the new value" \ + " @'dest'@, but only the set bits of @''@ are modified. A later" \ + " definition overrides the previous one." ) + +#:def_opt( "KCL_SCRIPT", "kcl-script|kclscript", "CP", \ + "script", \ + "After reading a KCL source file and executing all transformations," \ + " the text file @'script'@ is executed by the internal text parser." \ + " The script should use the parser functions @tri$$*()@ to read and" \ + " modify the triangle positions and flags, or to add or remove" \ + " complete triangles.\n" \ + " If this option is used multiple times, each script is executed in" \ + " the entered order." ) + +#:def_opt( "TRI_AREA", "tri-area|triarea", "GP", \ + "EXPR", \ + "Define the minimal area size of KCL triangles. The intention is to" \ + " ignore triangles that are generally to small. @EXPR@ is a floating" \ + " point number or expression. Triangles are invalidated if their area" \ + " size is smaller than @EXPR@. The test is executed after reading" \ + " files, after transformations, and after calculating normals and" \ + " lengths. Values between 0.01 and 4.0 are recommended. The careful" \ + " value 1.0 is used as default. Value 0 disables this filter" \ + " functionality." ) + +#:def_opt( "TRI_HEIGHT", "tri-height|triheight|tri-ht|triht|kcl-min|kclmin", "GP", \ + "EXPR", \ + "Define the minimal height of KCL triangles. The intention is to" \ + " ignore deformed triangles (very slim, but long). @EXPR@ is a" \ + " floating point number or expression. Triangles are invalidated if at" \ + " least 1 of the 3 heights is smaller than @EXPR@. The test is" \ + " executed after reading files, after transformations, and after" \ + " calculating normals and lengths. Values between 0.01 and 2.0 are" \ + " recommended. The careful value 1.0 is used as default. Value 0" \ + " disables this filter functionality.\n" \ + " --tri-ht is an alternative name for the option." ) + +#:def_opt( "FLAG_FILE", "flag-file|flagfile", "CP", \ + "filename", \ + "Define a flag file if scanning an OBJ file (KCL processing). The file" \ + " name may contain %-escapes. Set it to an empty string to disable" \ + " flag files at all.\n" \ + " The tool search the flag file in the following order until found:" \ + " File defined by --flag-file; OBJ file name with replaced extension" \ + " ('a.obj' becomes 'a.flag'); OBJ file name with appended extension" \ + " ('a.obj' becomes 'a.obj.flag')." ) + +#:def_opt( "SLOT", "slot", "CP", \ + "keyword", \ + "Patch a track file to run at the specified slot. KMP and KCL may be" \ + " modified. BRRES files may be added to or removed from SZS. The main" \ + " keywords are @DAISY, DESERT, SHERBET, SHYGUY, STANDARD@ and @MOST@." \ + " Slot numbers (except '42' and '4.2') and different other track names" \ + " are also accepted. For some conversions, the auto-add library is" \ + " needed." ) + +#:def_opt( "ROUND", "round", "C", \ + "", \ + "Print additional lines with rounded values (to 3 and 2 bytes for" \ + " floats and 7 and 6 bytes for doubles)." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Create smaller output." ) + +#:def_opt( "NO_PARAM", "P|no-param|noparam", "C", \ + "", \ + "Disable parameter support in decoded KMP text files." ) + +#:def_opt( "NO_ECHO", "no-echo|noecho", "C", \ + "", \ + "Suppress output of @@ECHO commands while scanning text files." ) + +#:def_opt( "NO_CHECK", "N|no-check|nocheck", "C", \ + "", \ + "Do not make plausibility checks for KCL files." ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "TINY", "tiny", "GP", \ + "mode", \ + "Set tiny mode to a level between 0 and 7." ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "TEST", "VERBOSE", \ + "", \ + "Print the matrices of transformations." ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "SYMBOLS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "BRIEF", \ + "", \ + "Suppress the output of the description and print only function type" \ + " and syntax." ) + +#:def_cmd_opt( "FUNCTIONS", "LONG", \ + "", \ + "Usually only the function syntax is compared to the keywords. But if" \ + " --long is set, the descriptions are compared too." ) + +#:def_cmd_opt( "CALCULATE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "BRIEF", \ + "", \ + "If set once, suppress vector dumps for transformation steps and" \ + " ignore option --long. If set twice, suppress vector dump for final" \ + " transformation too." ) + +#:def_cmd_opt( "MATRIX", "LONG", \ + "", \ + "If set once, print inverse matrix for the final transformation. If" \ + " set twice, print inverse matrix for all transformations." ) + +#:def_cmd_opt( "MATRIX", "QUIET", \ + "", \ + "Option --quiet is dedicated to automatic tests comparing the old and" \ + " new transformation algorithms. If set once, the tables are only" \ + " printed, if the results differ. If set twice, nothing is printed at" \ + " all and only the exit status will inform about the results." ) + +#:def_cmd_opt( "FLOAT", "ROUND", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "COPY", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "LONG", \ + "", \ + "Force usage of @'f(typ,variant)'@. If set twice, force usage of" \ + " @'a(typ,w,x,y,z)'@." ) + +#:def_cmd_opt( "CFF", "BRIEF", \ + "", \ + "Suppress the comments with the alternative expression formats." ) + +#:def_cmd_opt( "CFF", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "CFF", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "BRIEF", \ + "", \ + "Suppress the output of detailed flag list and print only the type" \ + " summary." ) + +#:def_cmd_opt( "FLAGS", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "FLAGS", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "BRIEF", \ + "", \ + "Suppress the output of the triangle list and print only the header" \ + " data and statistics." ) + +#:def_cmd_opt( "DUMP", "LONG", \ + "", \ + "If set, append an octree dump to the output. If set twice, add a" \ + " triangle usage list behind the octree dump." ) + +#:def_cmd_opt( "DUMP", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "BRIEF", \ + "", \ + "Suppress the output of the triangle list and print only the header" \ + " data and statistics." ) + +#:def_cmd_opt( "DBRIEF", "LONG", \ + "", \ + "If set, append an octree dump to the output. If set twice, add a" \ + " triangle usage list behind the octree dump." ) + +#:def_cmd_opt( "DBRIEF", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "DBRIEF", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "LIST", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "BRIEF", \ + "", \ + "Suppress the output of the 'normals' data." ) + +#:def_cmd_opt( "TRIANGLES", "LONG", \ + "", \ + "For each occurrence of option --long increase the precision of the" \ + " floating points numbers by adding 1 fractional digit, but only up to" \ + " additionally 5 digits." ) + +#:def_cmd_opt( "TRIANGLES", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRIANGLES", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "BRIEF", \ + "", \ + "Print the 0-based triangle index instead of the triangle coordinates." ) + +#:def_cmd_opt( "TRAVERSE", "LONG", \ + "", \ + "If set, print a calculation log. If set twice, print a calculation" \ + " log for each octree traversing step." ) + +#:def_cmd_opt( "TRAVERSE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRAVERSE", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "BRIEF", \ + "", \ + "Print only the height (y coordinate) of the result." ) + +#:def_cmd_opt( "FALL", "LONG", \ + "", \ + "If set, print a calculation log. If set twice, be more verbose." ) + +#:def_cmd_opt( "FALL", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "FALL", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "BRIEF", \ + "", \ + "Print only warnings and suppress hints." ) + +#:def_cmd_opt( "CHECK", "LONG", \ + "", \ + "Be verbose and print more statistics." ) + +#:def_cmd_opt( "CHECK", "QUIET", \ + "", \ + "Be quiet until the first warning or hint is found." ) + +#:def_cmd_opt( "CHECK", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "KCL_FLAG", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "TRI_AREA", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "TRI_HEIGHT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "FLAG_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "BLOW", "IGNORE", \ + "", \ + "" ) + + +## +############################################################################### +############### Tool 'wkmpt' ############### +############################################################################### + +#:def_tool( "wkmpt", \ + "wkmpt [option]... command [option|parameter|@file]...", \ + "Wiimms KMP Tool : Analyze, modify, export and create (compile) KMP" \ + " files." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wkmpt VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wkmpt HELP [--width=wd] OPT|OPTION ...\n" \ + "wkmpt HELP [--width=wd] CMD|COMMAND [ignored]...\n" \ + "wkmpt HELP [--width=wd] command [-b] [ignored]...\n" \ + "wkmpt HELP [--width=wd] [ignored]...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wkmpt ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wkmpt TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wkmpt COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wkmpt ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wkmpt FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wkmpt FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "SYMBOLS", "SYMBOLS", \ + "wkmpt SYMBOLS", \ + "Print predefined symbols of text parser to standard output. The KMP" \ + " specific symbols are included." ) + +#:def_cmd( "FUNCTIONS", "FUNCTIONS", \ + "wkmpt FUNCTIONS [key]...", \ + "Print a list with all parser functions including the KMP specific" \ + " functions. If one key ore more key entered, then only functions that" \ + " match at least one key are printed." ) + +#:def_cmd( "CALCULATE", "CALCULATE", \ + "wkmpt CALCULATE expression...", \ + "Calculate each expression and print the result to standard output." ) + +#:def_cmd( "MATRIX", "MATRIX", \ + "wkmpt MATRIX [patch-option]... [vector]...", \ + "Use the transformation options as base and calculate and print the" \ + " related vectors and the transformation matrices. Then do a forward" \ + " and backward transformation for each @vector@ parameter using the" \ + " matrices and print the results.", \ + "Use the transformation options as base and calculate and print the" \ + " related vectors and the transformation matrices. Then do a forward" \ + " and backward transformation for each @vector@ parameter using the" \ + " matrices and print the results. The transformations are done with 4" \ + " different methods to confirm the algorithms. Only the main results" \ + " and results, which differ, are printed.\n" \ + " Normally, the exit status is @OK@ (null). If at least one old" \ + " transformation returns different results for the different" \ + " algorithms, then status @DIFFER@ (not null) is returned." ) + +#:def_cmd( "FLOAT", "FLOAT", \ + "wkmpt FLOAT number...", \ + "This commands helps to convert hex values into floating point numbers" \ + " and vice versa. For each non empty parameter, a line with 4 bytes" \ + " hex and a float, 8 bytes hex and a double float, and the parameter" \ + " itself is printed. If a parameter begins with 'x' or '0x', it is a" \ + " hex string, otherwise it is a floating point number.\n" \ + " The special parameters \"+\" and \"-\" mean: Increment or decrement" \ + " the previous value by the smallest possible value. Use different" \ + " increments for floats and doubles.\n" \ + " All conversions are done using big endian." ) + +#:def_cmd( "STARTPOS", "STARTPOS|SP", \ + "wkmpt STARTPOS [patch-option]... key_or_file...", \ + "Print an ASCII image or a table of the start positions. The parameter" \ + " list is a wild mix of filenames, keywords and numbers.\n" \ + " If a parameter contains a slash ('/' or '\\'), it is a filename of" \ + " a KMP or SZS file and a KMP is loaded. The KMP is used to print" \ + " absolute positions and to find the settings for @LEFT/RIGHT@ and" \ + " @WIDE/NARROW@ automatically.\n" \ + " A parameter may also be one of the following keywords to define a" \ + " setting: @RESET, IMAGE, TABLE, RELATIVE, ABSOLUTE, LEFT, RIGHT," \ + " WIDE, NARROW, AUTO, NO-AUTO, DECIMAL, HEXADECIMAL, Z0, Z1, Z2, Z3@." \ + " A table or image is only printed on numbers 1 to 12." ) + +#:def_cmd( "OBJECTS", "OBJECTS|OBJ", \ + "wkmpt OBJECTS key...", \ + "Infos about all objects, that match any of the entered keys, are" \ + " printed. A key match, if the text is part of the object name, file" \ + " list or info (ignoring case). If a key is a number (decimal or hex)," \ + " the object with that id matches too. This command uses an internal" \ + " object and file database." ) + +#:def_cmd( "CAT", "CAT", \ + "wkmpt CAT [source]...", \ + "Read the entered KMP files (text, binary or szs) and print them as" \ + " decoded text files to standard output. The output of all source" \ + " files is concatenated." ) + +#:def_cmd( "DECODE", "DECODE|DEC", \ + "wkmpt DECODE [source]...", \ + "Read the entered KMP files (text or binary) and decode them to text" \ + " files. The default destination is @'%P/%N.txt'@." ) + +#:def_cmd( "ENCODE", "ENCODE|ENC", \ + "wkmpt ENCODE source...", \ + "Read the entered KMP files (text or binary) and encode (compile) them" \ + " to binary files. The default destination is @'%P/%N.kmp'@." ) + +#:def_cmd( "DIFF", "DIFF|CMP", \ + "wkmpt DIFF source1 source2\n" \ + "wkmpt DIFF source... --dest dest", \ + "Compare the KMP of two or more sources (KMP, KMP-text or SZS). Each" \ + " KMP will be converted into the internal format, and then fixed and" \ + " patched controlled by options. Only the known sections are compared." \ + " If option SORT (see {--diff}) is set, elements of section GOBJ are" \ + " sorted by object-id.\n" \ + " This command is in development. So for the section @POTI@ only a" \ + " simple notice about the different data record is printed.\n" \ + " If option {--dest} is set, then each source file is compared with a" \ + " file in the destination." ) + +#:def_cmd( "DRAW", "DRAW|D|COBJECT|COB", \ + "wkmpt DRAW source...", \ + "Read the entered KMP files (text or binary), of needed load the" \ + " related KCL file, draw KMP objects and create an OBJ file. The" \ + " default destination is @'%P/%N.kmp.obj'@.\n" \ + " Automated dropping of bad KCL triangles is enabled if no drop" \ + " option is specified by option {--kcl}." ) + +#:def_cmd( "CHECK", "CHECK|CHK", \ + "wkmpt CHECK source...", \ + "Read the entered KMP files (text or binary) and and make a" \ + " plausibility check. Hints and warnings are written to standard" \ + " output. The exit status is @'DIFFER'@ if at least one KMP warning" \ + " found." ) + +#:def_cmd( "STGI", "STGI", \ + "wkmpt STGI file...", \ + "Find the first KMP:STGI of each file and print an analysis of it as" \ + " single line statistic. Additional the number of lap counter check" \ + " points ('LC') is printed. Track and KMP (binary and text) files are" \ + " accepted as input." ) + +#:def_cmd( "KTPT", "KTPT", \ + "wkmpt KTPT [source]...", \ + "Analyse the start positions (KMP:KTPT) and lap counters (KMP:CKPT" \ + " with mode 0) and print a simple information list. Use enemy points" \ + " (KMP:ENPT) to find recommendations.\n" \ + " This command is dedicated to LE-CODE, which supports an alternative" \ + " position for the finish line at the minimap." ) + +#:def_cmd( "ROUTES", "ROUTES", \ + "wkmpt ROUTES file...", \ + "Print the internal cross references for routes of KMP sections CKPH," \ + " CKPT, ENPH, ENPT, ITPH and ITPT." ) + +#:def_cmd( "GAMEMODES", "GAMEMODES|GMODES", \ + "wkmpt GAMEMODES [source]...", \ + "Analyze the presence flags combinations with and without extension" \ + " support and print a list. The lists consists of index, scenario" \ + " version and scenario name. Scenario versions are only equal, if the" \ + " scenarios are duplicates. Duplicates happens only, if {--gamemodes}" \ + " option TT or FULL is set." ) + +#:def_cmd( "WIM0", "WIM0|W0", \ + "wkmpt WIM0 [source]...", \ + "Analyze the WIM0 section of each KMP file (text, binary or szs) and" \ + " print an info about it." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "Enable LE-CODE and CT-CODE support. The optional parameter is" \ + " deprecated and ignored." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "SCALE", "scale", "CP", \ + "vector[@origin]", \ + "Transform the data: Scale all coordinates and size values by 'vector'" \ + " relative to the point 'origin'. If the origin is not set, @0,0,0@ is" \ + " used instead.\n" \ + " Negative values mirror the coordinates. Each parameter is either a" \ + " vector expression or a comma separated expression list of" \ + " coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "SHIFT", "shift", "CP", \ + "vector", \ + "Transform the data: Add 'vector' to all coordinates. It's simlar to" \ + " {--translate}, but the addition is done before rotation. The" \ + " parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "XSS", "xss", "CP", \ + "x1old,x1new,x2old,x2new", \ + "--xss (x-scale-shift) calculates the X values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "YSS", "yss", "CP", \ + "y1old,y1new,y2old,y2new", \ + "--yss (y-scale-shift) calculates the Y values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "ZSS", "zss", "CP", \ + "z1old,z1new,z2old,z2new", \ + "--zss (z-scale-shift) calculates the Z values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "ROT", "rot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' (is a vector) around the 3 axes. All 3 axes goes" \ + " through the point 'origin'. If the origin is not set, @0,0,0@ is" \ + " used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,y,z').\n" \ + " Option --rot is an alternative for {--xrot}, {--yrot} and {--zrot}" \ + " to define all 3 rotations in one step. Transformations are" \ + " calculated in the order SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "XROT", "xrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the x-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('y,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "YROT", "yrot|hrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the x-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead. This is" \ + " a horizontal counterclockwise rotation and the old option name" \ + " --hrot can also used.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "ZROT", "zrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the z-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,y' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "YPOS", "ypos", "CP", \ + "pos", \ + "This option defines an Y position for KMP:CKPT transformations. It" \ + " has only impact to X and Z rotations (Options {--xrot} and" \ + " {--zrot}). If not set, the mean y of the active rotation origins are" \ + " used." ) + +#:def_opt( "TRANSLATE", "translate", "CP", \ + "vector", \ + "Transform the data: Add 'vector' to all coordinates. It's simlar to" \ + " {--shift}, but the addition is done after rotation. The parameter is" \ + " either a vector expression or a comma separated expression list of" \ + " coordinates ('x,z' or 'x,y,z'). @--trans@ is a short cut.\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "NULL", "null", "C", \ + "", \ + "Create a neutral transformation without affecting the coordinates." \ + " The only influence is, that the dependent values (like maximum) are" \ + " re-calculated as if a transformation has taken place." ) + +#:def_opt( "NEXT", "next", "C", \ + "", \ + "Close the current transformation step with all scaling, shifting," \ + " rotation and translation options and open a new step with cleared" \ + " options. On transformation each step is logical done one by one. In" \ + " real, one total transformation matrix is calculated and used for" \ + " fast transformations.\n" \ + " If option --next is used, the ability for modifying the scale and" \ + " rotation vectors (not the positions) of different KMP sections is" \ + " nearly always lost. A warning is printed if this occurs." ) + +#:def_opt( "ASCALE", "ascale", "CP", \ + "factor@dir", \ + "Transform the data and do an axis scale: Close the current" \ + " transformation step like {--next} and scale the data by @'factor'@" \ + " into the direction given by the vector @'dir'@. Store the resulting" \ + " matrix as single transformation step and open a new one.\n" \ + " This kind of transformation is EXPERIMENTAL!" ) + +#:def_opt( "AROT", "arot", "CP", \ + "degree[@pos1]@pos2", \ + "Transform the data and do an axis rotation: Close the current" \ + " transformation step like {--next} and rotate the data by @'degree'@" \ + " around the axis defined by the 2 points. If @'pos1'@ is not set, use" \ + " @0,0,0@ instead. Store the resulting matrix as single transformation" \ + " step and open a new one.\n" \ + " This kind of transformation is EXPERIMENTAL!" ) + +#:def_opt( "TFORM_SCRIPT", "tform-script|tformscript", "CP", \ + "script", \ + "Load the script and execute it by the text parser.\n" \ + " Then for each coordinate (2D and 3D), call the macro @TRANSFORM@" \ + " after all other transformations. Call it without parameters and set" \ + " variable @$$I@ with a zero based index of all vertices, variable" \ + " @$$D@ to the dimension (2 or 3) and the variable @$$P@ to the vector" \ + " of the current vertex. If the macro returns a vector, it is the new" \ + " position.\n" \ + " For each file, macro @BEGIN@ is called at the beginning and @END@" \ + " after processing all points, but only, if the macros are defined." \ + " For @END@, variable @$$I@ is set to the number of processed vertices" \ + " in the current file.\n" \ + " Before each macro call, the private and local variables are cleared" \ + " and only global parameters are permanent. The zero based index is" \ + " cleared for each new source. The parameters of the macro calls are" \ + " defined in the local name space." ) + +#:def_opt( "RM_GOBJ", "rm-gobj|rmgobj", "CP", \ + "objlist", \ + "Remove objects from the KMP section @GOBJ@. Therefor a comma" \ + " separated object list with single values like @'a'@ or ranges like" \ + " @'a:b'@ is expected. @'a'@ and @'b'@ are numbers, object names" \ + " (prefixed by 'o$$') or any other parser expressions." ) + +#:def_opt( "BATTLE", "battle", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used.\n" \ + " If mode is @ON@, assume a battle arena and enable a spüecial" \ + " battle support. If mode is @OFF@, disable special battle arena" \ + " support. If mode is @AUTO@, analyse the KMP and estimate, if it is a" \ + " battle areana. Modes @ON@ and @OFF@ override KMP parameter" \ + " @BATTLE-MODE@." ) + +#:def_opt( "EXPORT_FLAGS", "export-flags|exportflags|exflags", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used. --exflags is an alternative name for the option.\n" \ + " If mode is @ON@, this feature is forced: On creating a KMP text" \ + " file, the option @EXPORT-FLAGS@ is always set to 1. On creating a" \ + " binary KMP file, the flags are always exported as lowest byte of the" \ + " scale factors or x-rotation.\n" \ + " If mode is @OFF@, this feature is totally disabled. If mode is not" \ + " @AUTO@, KMP parameter @EXPORT-FLAGS@ is overridden." ) + +#:def_opt( "ROUTE_OPTIONS", "route-options|routeoptions|ropt", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used.\n" \ + " On mode @ON@, the new commands @$$PREV@ and @$$NEXT@ are printed" \ + " for each group of KMP sections ENPT and ITPT. On mode @AUTO@, the" \ + " command is only printed, if any option is set or if parameter" \ + " @SHOW-OPTIONS@ is >0. On mode @OFF@, this feature is totally" \ + " disabled to create KMP text files that are compatible with versions" \ + " <1.60a.\n" \ + " This option exists only for a transition period. Its behaviour will" \ + " be changed in the future. @--ropt@ is a short cut." ) + +#:def_opt( "WIM0", "wim0", "GO", \ + "[=mode]", \ + "@MODE@ is either @OFF@, @AUTO@ (default) or @ON@. Without value, @ON@" \ + " is used.\n" \ + " If mode is @ON@, a KMP section called WIM0 is added on binary KMP" \ + " creation. The sections contains information for Wiimms tools.\n" \ + " If mode is @OFF@, this feature is totally disabled. If mode is not" \ + " @AUTO@, KMP parameter @KMP-WIM0@ is overridden." ) + +#:def_opt( "SLOT", "slot", "CP", \ + "keyword", \ + "Patch a track file to run at the specified slot. KMP and KCL may be" \ + " modified. BRRES files may be added to or removed from SZS. The main" \ + " keywords are @DAISY, DESERT, SHERBET, SHYGUY, STANDARD@ and @MOST@." \ + " Slot numbers (except '42' and '4.2') and different other track names" \ + " are also accepted. For some conversions, the auto-add library is" \ + " needed." ) + +#:def_opt( "DRAW", "w|draw", "CP", \ + "list", \ + "The parameter is a comma separated list of keywords to select the" \ + " elements to draw. Allowed keywords are: @CKPT, CJGPT, JGPT, KTPT," \ + " ENPT, ITPT, POTI, CNPT, ITEMBOXES, COINS, ROADOBJECTS, SOLIDOBJECTS," \ + " DECORATION, BLACK, WHITE, KCL, NONE@ and @ALL@ (default); the" \ + " classes @POINTS, OBJECTS@ and @RESPAWN@; the presence-flag selectors" \ + " @[1-3]OFFLINE@ and @[1-3]ONLINE@; the special options @DETAILED," \ + " DISPATCH@ and @WARNINGS@. If a keyword is prefixed by a minus sign," \ + " the option is disabled. Option @KCL@ decides, if the reference KCL" \ + " is included in the output. If @DETAILED@ is disabled, an output with" \ + " less faces is created. If @DISPATCH@ is enabled, the settings of" \ + " dispatch points are visualized. If @WARNINGS@ is enabled, some" \ + " objects are drawn in red." ) + +#:def_opt( "POS_MODE", "pos-mode|posmode", "CP", \ + "list", \ + "Define a comma separated list of modes for the next {--pos-file}" \ + " options. Each element of the list begins with a keyword followed by" \ + " a column index and/or a parameter list: @X, Y, Z," \ + " D, C, T, FA=, FB=, FC=," \ + " @" ) + +#:def_opt( "POS_FILE", "pos-file|posfile", "CP", \ + "file", \ + "Load a position file created by mkw-ana and print the positions into" \ + " the map. Define the modes first, because the last defined modes set" \ + " by {--pos-mode} are used!" ) + +#:def_opt( "PNG", "png", "CP", \ + "pix_size ['a' align] ['x' x1[:x2]] ['y' y1[:y2]] ['t' types]", \ + "Draw a PNG with a top view of the track instead of an OBJ file." \ + " Transformations are done after the KMP 3D drawing, but before PNG" \ + " creation.\n" \ + " @'pix_size'@ is the number of world units (sqare) used for 1 PNG" \ + " pixel. @'align'@ is a factor as anti-aliasing pixel multiplicator" \ + " (>1 is on). @'x'@ and @'y'@ defines the part of the world that is" \ + " displayed. If not set, the check points (KMP/CKPT) are used to find" \ + " a good area. @'t'@ is a bit field with 32 bits to enable the the KCL" \ + " types. Each bit corresponds to one of the KCL types and only . The" \ + " default is to display all but not walls and solid fall (0x7fef0bff).\n" \ + " All numeric values can be any expression allowed by the parser." ) + +#:def_opt( "LOAD_KCL", "load-kcl|loadkcl", "GP", \ + "file", \ + "Load a KCL or OBJ file for reference issues." ) + +#:def_opt( "KCL", "kcl", "GP", \ + "list", \ + "Set global options for KCL processing. Parameter @'list'@ is a comma" \ + " separated list of keywords. A minus sign before a keyword disables a" \ + " setting. Each occurrence of the option will only change entered" \ + " settings and all other settings are untouched.\n" \ + " Keyword @DEFAULT@ resets the default settings and @CLEAR@ disables" \ + " all. Keywords @SMALL, MEDIUM@ and @CHARY@ select the default" \ + " parameters for the octree creation. The other allowed keywords are:" \ + " @FAST, NEW, CENTER, ROUND, NORMALS, MTL, WIIMM, TRIANGLES, OUT-SWAP," \ + " G, USEMTL, CLIP, IN-SWAP, AUTO, HEX4, HEX23, HEX, DROP-UNUSED," \ + " DROP-FIXED, DROP-INVALID, DROP, RM-FACEDOWN, RM-FACEUP, FIX-ALL," \ + " CONV-FACEUP, WEAK-WALLS, SORT, INPLACE, SILENT@ and @LOG@." ) + +#:def_opt( "KCL_FLAG", "kcl-flag|kclflag", "CP", \ + "joblist", \ + "Modify the KCL flag of KCL files. A comma separated job list in the" \ + " format @'srclist=dest'@ is expected. @'srclist'@ is a plus sign" \ + " separated list with single values like @@, @T@ and" \ + " @/@ or ranges like @:@, @T:@ and" \ + " @:/@. @''@ is a complete KCL flag in the range" \ + " of @0..0xffff@. @''@ are only the base types in the range of" \ + " @0..0x1f@. If @@ is present, it marks the relevant bits. For" \ + " the @T@ variant, the mask is set to @0x1f@.\n" \ + " All KCL flags of the source list are assigned to the new value" \ + " @'dest'@, but only the set bits of @''@ are modified. A later" \ + " definition overrides the previous one." ) + +#:def_opt( "KCL_SCRIPT", "kcl-script|kclscript", "CP", \ + "script", \ + "After reading a KCL source file and executing all transformations," \ + " the text file @'script'@ is executed by the internal text parser." \ + " The script should use the parser functions @tri$$*()@ to read and" \ + " modify the triangle positions and flags, or to add or remove" \ + " complete triangles.\n" \ + " If this option is used multiple times, each script is executed in" \ + " the entered order." ) + +#:def_opt( "TRI_AREA", "tri-area|triarea", "GP", \ + "EXPR", \ + "Define the minimal area size of KCL triangles. The intention is to" \ + " ignore triangles that are generally to small. @EXPR@ is a floating" \ + " point number or expression. Triangles are invalidated if their area" \ + " size is smaller than @EXPR@. The test is executed after reading" \ + " files, after transformations, and after calculating normals and" \ + " lengths. Values between 0.01 and 4.0 are recommended. The careful" \ + " value 1.0 is used as default. Value 0 disables this filter" \ + " functionality." ) + +#:def_opt( "TRI_HEIGHT", "tri-height|triheight|tri-ht|triht|kcl-min|kclmin", "GP", \ + "EXPR", \ + "Define the minimal height of KCL triangles. The intention is to" \ + " ignore deformed triangles (very slim, but long). @EXPR@ is a" \ + " floating point number or expression. Triangles are invalidated if at" \ + " least 1 of the 3 heights is smaller than @EXPR@. The test is" \ + " executed after reading files, after transformations, and after" \ + " calculating normals and lengths. Values between 0.01 and 2.0 are" \ + " recommended. The careful value 1.0 is used as default. Value 0" \ + " disables this filter functionality.\n" \ + " --tri-ht is an alternative name for the option." ) + +#:def_opt( "FLAG_FILE", "flag-file|flagfile", "CP", \ + "filename", \ + "Define a flag file if scanning an OBJ file (KCL processing). The file" \ + " name may contain %-escapes. Set it to an empty string to disable" \ + " flag files at all.\n" \ + " The tool search the flag file in the following order until found:" \ + " File defined by --flag-file; OBJ file name with replaced extension" \ + " ('a.obj' becomes 'a.flag'); OBJ file name with appended extension" \ + " ('a.obj' becomes 'a.obj.flag')." ) + +#:def_opt( "KMP", "kmp", "GP", \ + "list", \ + "Set global options for KMP processing. Parameter @'list'@ is a comma" \ + " separated list of keywords. A minus sign before a keyword disables a" \ + " setting. Each occurrence of the option will only change entered" \ + " settings and all other settings are untouched.\n" \ + " Keyword @DEFAULT@ resets the default settings and @CLEAR@ disables" \ + " all. The other allowed keywords are: @FORCE, NEW, RM-SPCITEM, LEFT," \ + " RIGHT, WIDE, NARROW, FIX-CKPH, FIX-ENPH, FIX-ITPH, FIX-PH," \ + " FIX-CKNEXT, FIX-CKJGPT, FIX-CK, FIX-ALL, MASK-PFLAGS, RM-LECODE," \ + " PURGE-GOBJ, FULL-DEFOBJ, DUMP-CLASS, DUMP-ONEWAY, DUMP-ALL, 1LAP ..." \ + " 9LAPS, MAX-LAPS, RM-EMPTY, INPLACE, SILENT@ and @LOG@." ) + +#:def_opt( "SPEED_MOD", "speed-mod|speedmod", "GP", \ + "factor", \ + "The speed modifier is a user extensions to change the basic speed of" \ + " all vehicles. The speed factor itself is stored into KMP:STGI" \ + " section of a track.\n" \ + " If --speed-mod is set to a value >0.0, then the last 2 bytes of" \ + " the STGI section are patched. The value 0.0 reset these 2 bytes and" \ + " deactivates the speed modifier." ) + +#:def_opt( "KTPT2", "ktpt2", "GP", \ + "vector", \ + "Insert or replace a second KTPT. Use VECTOR as new position. VECTOR" \ + " is either @X,Z@ or @X,Y,Z@ or an expression. Set Y to 0 for an" \ + " automatic defined height.\n" \ + " If the first 4 characters of VECTOR are 'AUTO' (ignoring case)," \ + " then the position is calculated automatically and set to the" \ + " recommendation of command @wkmpt KTPT@.\n" \ + " The tool looks for the nearest lap counter (CKPT with mode 0) and" \ + " adjust the KTPT: The direction of the lap counter is copied and the" \ + " KTPT is moved to the lap counter line using the new direction.\n" \ + " LE-CODE uses the second KTPT to draw the finish line at another" \ + " place away from the start position." ) + +#:def_opt( "TFORM_KMP", "tform-kmp|tformkmp|tkmp", "GP", \ + "list", \ + "Select KMP sections and objects for a transformation. @--tkmp@ is a" \ + " short cut. The option expects a comma separated list of keywords." \ + " Non ambiguous of keywords are allowed and minus signs within" \ + " keywords are optional (e.g. @GOBJS@). A minus sign before a keyword" \ + " disables transformation of the related section or object.\n" \ + " Sections are selected by the KMP section names @AREA, CAME, CKPT," \ + " CNPT, ENPT, GOBJ, ITPT, JGPT, KTPT, MSPT@ and @POTI@.\n" \ + " Objects are selected by the keywords @AREA-POSITION, AREA-ROTATE," \ + " AREA-SCALE, CAME-POSITION, CKPT-POSITION, CNPT-POSITION," \ + " CNPT-ROTATE, ENPT-POSITION, ENPT-SCALE, GOBJ-POSITION, GOBJ-ROTATE," \ + " GOBJ-SCALE, ITPT-POSITION, ITPT-SCALE, JGPT-POSITION, JGPT-ROTATE," \ + " KTPT-POSITION, KTPT-ROTATE, MSPT-POSITION, MSPT-ROTATE@ and" \ + " @POTI-POSITION@.\n" \ + " The keywords @NONE@ and @ALL@ (default) complete the field." ) + +#:def_opt( "REPAIR_XPF", "repair-xpflags|repairxpflags|repxpf", "CP", \ + "file", \ + "Load a KMP file and use it to repair the settings of the extended" \ + " presence flags, if they are destroyed by another KMP tool." \ + " @--repxpf@ is a short cut for this option." ) + +#:def_opt( "GAMEMODES", "gamemodes|gmodes", "CP", \ + "list", \ + "Filter the list of combinations. The parameter is a comma separated" \ + " list of keywords: @AUTO@: Add a automatic mode without conditions" \ + " first. @STANDARD@ (or @STD@): Add standard modes. @1STANDARD@ (or" \ + " @1STD@): Add standard modes first. @BALLOON@: Add balloon battle" \ + " modes. @COIN@: Add coin runners modes. @BATTLE@: Short cut for" \ + " 'BALLOON,COIN'. @VERSUS@ (or @VS@): Add versus modes. @ITEMRAIN@:" \ + " Add itemrain modes. @RACE@: Short cut for 'VERSUS,ITEMRAIN'. @ALL@" \ + " (default): Short cut for 'STANDARD,BATTLE,RACE'. @NO-MODES@: Exclude" \ + " 'ALL' and create auto modes only. @ENGINE@: Include engine classes" \ + " (default). @NO-ENGINE@: Exclude engine classes. @RANDOM@: Include" \ + " random modes (default). @NO-RANDOM@: Exclude random modes." \ + " @OFFLINE@: Filter offline modes. @ONLINE@: Filter online modes." \ + " @NO-TT@: Exclude time trial. @TT@: Force a time trial scenario, even" \ + " if it is a duplicate. @FULL@: Don't eliminate duplicates from list" \ + " (up to 1573 modes). @NSORT@: Sort the list by name. @ISORT@: Sort" \ + " the list by index. @DEBUG@: Print a debug info about this option." ) + +#:def_opt( "ROUND", "round", "C", \ + "", \ + "Print additional lines with rounded values (to 3 and 2 bytes for" \ + " floats and 7 and 6 bytes for doubles)." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Suppress information lines in decoded KMP text files. This includes" \ + " syntax information ({--no-header}). If set twice, the output of" \ + " unneeded sections is also suppressed." ) + +#:def_opt( "EXPORT", "X|export", "C", \ + "", \ + "Enable the export mode and create small and machine readable text" \ + " files for easy post processing. The option works similar like @-HBB@" \ + " for KMP text files." ) + +#:def_opt( "NO_PARAM", "P|no-param|noparam", "C", \ + "", \ + "Disable parameter support in decoded KMP text files." ) + +#:def_opt( "EPSILON", "epsilon", "CP", \ + "float", \ + "When comparing floating point numbers, 2 numbers are considered" \ + " identical if their difference is less than or equal to epsilon. The" \ + " default value is @0.01@." ) + +#:def_opt( "DIFF", "diff", "CP", \ + "list", \ + "List of KMP sections names that should be compared. To select a" \ + " section, use a combiantion of the following keywords: @AREA, CAME," \ + " CNPT, CKPH, CKPT, ENPH, ENPT, GOBJ, ITPH, ITPT, JGPT, KTPT, MSPT," \ + " POTI, STGI, NONE, GOOD, DEFAULT@ of @ALL@ (default). Additional" \ + " keywords are @SORT@ (default) and @NOSORT@" ) + +#:def_opt( "GENERIC", "G|generic", "C", \ + "", \ + "Forget scanned entry names and use generic names for output." ) + +#:def_opt( "NO_ECHO", "no-echo|noecho", "C", \ + "", \ + "Suppress output of @@ECHO commands while scanning text files." ) + +#:def_opt( "NO_CHECK", "N|no-check|nocheck", "C", \ + "", \ + "Do not make plausibility checks for KMP files." ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "TINY", "tiny", "GP", \ + "mode", \ + "Set tiny mode to a level between 0 and 7." ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "TEST", "VERBOSE", \ + "", \ + "Print the matrices of transformations." ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "SYMBOLS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "BRIEF", \ + "", \ + "Suppress the output of the description and print only function type" \ + " and syntax." ) + +#:def_cmd_opt( "FUNCTIONS", "LONG", \ + "", \ + "Usually only the function syntax is compared to the keywords. But if" \ + " --long is set, the descriptions are compared too." ) + +#:def_cmd_opt( "CALCULATE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "YPOS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SPEED_MOD", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "KTPT2", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TFORM_KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "REPAIR_XPF", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "RM_GOBJ", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "BATTLE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "EXPORT_FLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ROUTE_OPTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "WIM0", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "LOAD_KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "BRIEF", \ + "", \ + "If set once, suppress vector dumps for transformation steps and" \ + " ignore option --long. If set twice, suppress vector dump for final" \ + " transformation too." ) + +#:def_cmd_opt( "MATRIX", "LONG", \ + "", \ + "If set once, print inverse matrix for the final transformation. If" \ + " set twice, print inverse matrix for all transformations." ) + +#:def_cmd_opt( "MATRIX", "QUIET", \ + "", \ + "Option --quiet is dedicated to automatic tests comparing the old and" \ + " new transformation algorithms. If set once, the tables are only" \ + " printed, if the results differ. If set twice, nothing is printed at" \ + " all and only the exit status will inform about the results." ) + +#:def_cmd_opt( "FLOAT", "ROUND", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "YPOS", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "SPEED_MOD", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "KTPT2", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "TFORM_KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "REPAIR_XPF", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "RM_GOBJ", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "BATTLE", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "EXPORT_FLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "ROUTE_OPTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "WIM0", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "STARTPOS", "LOAD_KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "OBJECTS", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "OBJECTS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "OBJECTS", "BRIEF", \ + "", \ + "Don't print the file list and the info line(s). If set twice, only ID" \ + " and object name is printed and option {--long} is ignored." ) + +#:def_cmd_opt( "OBJECTS", "LONG", \ + "", \ + "Print each setting starting on a new line. --long and {--brief} can" \ + " be used together." ) + +#:def_cmd_opt( "CAT", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in KMP text files." ) + +#:def_cmd_opt( "CAT", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_PARAM", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "GENERIC", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "BATTLE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "EXPORT_FLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ROUTE_OPTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "WIM0", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "YPOS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SPEED_MOD", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "KTPT2", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TFORM_KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "REPAIR_XPF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "RM_GOBJ", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "LOAD_KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in KMP text files." ) + +#:def_cmd_opt( "DECODE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_PARAM", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "GENERIC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "BATTLE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "EXPORT_FLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ROUTE_OPTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "WIM0", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "YPOS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SPEED_MOD", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "KTPT2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TFORM_KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REPAIR_XPF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "RM_GOBJ", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "LOAD_KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "YPOS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SPEED_MOD", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "KTPT2", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TFORM_KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "REPAIR_XPF", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "RM_GOBJ", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "BATTLE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "EXPORT_FLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ROUTE_OPTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "WIM0", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "LOAD_KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "QUIET", \ + "", \ + "If set once, only a summary status is printed. If set twice, the" \ + " status is only reported as exit status." ) + +#:def_cmd_opt( "DIFF", "EPSILON", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "DIFF", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DIFF", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "DRAW", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "POS_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "POS_FILE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "PNG", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in KMP text files." ) + +#:def_cmd_opt( "DRAW", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "NO_PARAM", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "GENERIC", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "BATTLE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "EXPORT_FLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "ROUTE_OPTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "WIM0", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "YPOS", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "SPEED_MOD", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "KTPT2", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "TFORM_KMP", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "REPAIR_XPF", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "RM_GOBJ", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "SLOT", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "LOAD_KCL", \ + "", \ + "" ) + +#:def_cmd_opt( "DRAW", "KCL_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "BRIEF", \ + "", \ + "Print only warnings and suppress hints." ) + +#:def_cmd_opt( "CHECK", "LONG", \ + "", \ + "Be verbose and print more statistics." ) + +#:def_cmd_opt( "CHECK", "QUIET", \ + "", \ + "Be quiet until the first warning or hint is found." ) + +#:def_cmd_opt( "CHECK", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "GENERIC", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CHECK", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "STGI", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "STGI", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "STGI", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "STGI", "IGNORE", \ + "", \ + "If set, ignore non existing source files and files without KMP" \ + " without warning. If set twice, ignore files with standard settings" \ + " too." ) + +#:def_cmd_opt( "KTPT", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "KTPT", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "KTPT", "KTPT2", \ + "", \ + "" ) + +#:def_cmd_opt( "KTPT", "IGNORE", \ + "", \ + "If set, ignore non existing source files and files without KMP" \ + " without warning." ) + +#:def_cmd_opt( "KTPT", "LONG", \ + "", \ + "If set, compare each KTPT with each lap counter (some CKPT) and print" \ + " positions, directions, distances and recommendations as wide table." ) + +#:def_cmd_opt( "ROUTES", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "ROUTES", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "ROUTES", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ROUTES", "IGNORE", \ + "", \ + "If set, ignore non existing source files and files without KMP" \ + " without warning." ) + +#:def_cmd_opt( "GOBJ", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "GOBJ", "NO_HEADER", \ + "", \ + "Suppress head and foot lines." ) + +#:def_cmd_opt( "GOBJ", "BRIEF", \ + "", \ + "Print only summary for each object category. If set twice, print a" \ + " status vector only." ) + +#:def_cmd_opt( "GOBJ", "LONG", \ + "", \ + "?" ) + +#:def_cmd_opt( "GAMEMODES", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "GAMEMODES", "GAMEMODES", \ + "", \ + "" ) + +#:def_cmd_opt( "GAMEMODES", "NO_HEADER", \ + "", \ + "Suppress head and foot lines." ) + +#:def_cmd_opt( "GAMEMODES", "BRIEF", \ + "", \ + "Print only the scenario version and configuration name. If set twice," \ + " suppress ead and foot lines too." ) + +#:def_cmd_opt( "GAMEMODES", "LONG", \ + "", \ + "If set, print a status vector for each GOBJ element too: '-': Object" \ + " diabled; '=': Object diabled by presence flags, but visible before" \ + " race; '+': Object enabled; 'e': Depends on engine; 'r': Depends on" \ + " random scenario; 'b': Defintion object of type BITS; 'o': Defintion" \ + " object of type OR; 'a': Defintion object of type AND." ) + +#:def_cmd_opt( "WIM0", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "WIM0", "LONG", \ + "", \ + "If set, disable table format and append a full hexdump for each WIM0" \ + " section." ) + + +## +############################################################################### +############### Tool 'wlect' ############### +############################################################################### + +#:def_tool( "wlect", \ + "wlect [option]... command [option|parameter|@file]...", \ + "Wiimms LE-CODE Tool : Manage the $LE-CODE$ and LEX extensions." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wlect VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wlect HELP [--width=wd] OPT|OPTION ...\n" \ + "wlect HELP [--width=wd] CMD|COMMAND [ignored]...\n" \ + "wlect HELP [--width=wd] command [-b] [ignored]...\n" \ + "wlect HELP [--width=wd] [ignored]...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wlect ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wlect TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wlect COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wlect ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wlect FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wlect FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "SYMBOLS", "SYMBOLS", \ + "wlect SYMBOLS", \ + "Print predefined symbols of text parser to standard output. The" \ + " LE-CODE specific symbols are included." ) + +#:def_cmd( "FUNCTIONS", "FUNCTIONS", \ + "wlect FUNCTIONS [key]...", \ + "Print a list with all parser functions including the LE-CODE specific" \ + " functions. If one key ore more key entered, then only functions that" \ + " match at least one key are printed." ) + +#:def_cmd( "CALCULATE", "CALCULATE", \ + "wlect CALCULATE expression...", \ + "Calculate each expression and print the result to standard output." ) + +#:def_cmd( "FLOAT", "FLOAT", \ + "wlect FLOAT number...", \ + "This commands helps to convert hex values into floating point numbers" \ + " and vice versa. For each non empty parameter, a line with 4 bytes" \ + " hex and a float, 8 bytes hex and a double float, and the parameter" \ + " itself is printed. If a parameter begins with 'x' or '0x', it is a" \ + " hex string, otherwise it is a floating point number.\n" \ + " The special parameters \"+\" and \"-\" mean: Increment or decrement" \ + " the previous value by the smallest possible value. Use different" \ + " increments for floats and doubles.\n" \ + " All conversions are done using big endian." ) + +#:def_cmd( "DUMP", "DUMP|D", \ + "wlect DUMP [source]...", \ + "Print an information dump for each source file. LE-CODE binaries and" \ + " LEX binaries are accepted as input." ) + +#:def_cmd( "DL", "DL", \ + "wlect DL [source]...", \ + "Short cut for {DUMP --long}." ) + +#:def_cmd( "DLL", "DLL", \ + "wlect DLL [source]...", \ + "Short cut for {DUMP --long --long}." ) + +#:def_cmd( "BIN_DIFF", "BIN-DIFF|BINDIFF|BD", \ + "wlect BIN-DIFF settings binary1 binary2", \ + "Compare parts of 2 different LECODE binary files. @SETTINGS@ is a" \ + " string with plus and minus signs to switch between plus/minus modes," \ + " and some key letters to select parts: s:size, h:header, t:timestamp," \ + " b:body, p:parameters, d:data, c:code and a:all. In plus-mode, a test" \ + " is enabled, in minus-mode it is disabled. Scanning starts in" \ + " plus-mode.\n" \ + " Example: @a-t@: compare all except timestamps.\n" \ + " The exist status is @OK@ (0) if nothing differ, @DIFFER@ (2) if at" \ + " least one test failed, or an error code." ) + +#:def_cmd( "PATCH", "PATCH|P", \ + "wlect PATCH [le_bin]...", \ + "Read each LE-BIN file and patch it. Use {--le-define} to setup cups" \ + " and tracks." ) + +#:def_cmd( "LPAR", "LPAR", \ + "wlect LPAR [le_bin]...", \ + "Read each LE-BIN file and patch it. Then extract the LE-CODE" \ + " parameters and store them as LPAR text file. Print the result to" \ + " standard output if no other destination is defined." ) + +#:def_cmd( "CREATE", "CREATE|CR", \ + "wlect CREATE [keyword]", \ + "Create a file and print it to standard output. The kind of file is" \ + " defined by KEYWORD. Use command without keywords to get a list of" \ + " possible keywords.\n" \ + " The following keywords are possible: @LEX@ for a LEX text file with" \ + " all known sections except @TEST@. @LEX+@ for a LEX text file with" \ + " all known sections including @TEST@. @SET1, CANN, HIPT@ and @TEST@" \ + " (section names) for a LEX text file with 1 section only. @LPAR@ for" \ + " an example LE-CODE parameter file." ) + +#:def_cmd( "CAT", "CAT", \ + "wlect CAT [source]...", \ + "Read the entered LEX and LE-CODE files (text, binary or szs) and" \ + " print them as decoded text files to standard output. The output of" \ + " all source files is concatenated." ) + +#:def_cmd( "DECODE", "DECODE|DEC", \ + "wlect DECODE [source]...", \ + "Read the entered LEX files (text or binary) and decode them to text" \ + " files. The default destination is @'%P/%N.txt'@." ) + +#:def_cmd( "ENCODE", "ENCODE|ENC", \ + "wlect ENCODE source...", \ + "Read the entered LEX files (text or binary) and encode (compile) them" \ + " to binary files. The default destination is @'%P/%N.lex'@." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "This option is completely ignored by wlect. It only exists due to" \ + " compatibility with the other tools." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "ROUND", "round", "C", \ + "", \ + "Print additional lines with rounded values (to 3 and 2 bytes for" \ + " floats and 7 and 6 bytes for doubles)." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Suppress cross reference comments." ) + +#:def_opt( "EXPORT", "X|export", "C", \ + "", \ + "Enable the export mode and create small and machine readable text" \ + " files for easy post processing." ) + +#:def_opt( "NO_PARAM", "P|no-param|noparam", "C", \ + "", \ + "Disable parameter support in decoded KMP text files." ) + +#:def_opt( "NO_ECHO", "no-echo|noecho", "C", \ + "", \ + "Suppress output of @@ECHO commands while scanning text files." ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "LT_CLEAR", "lt-clear|ltclear", "C", \ + "", \ + "Clear LEX section TEST and remove the section if empty. This option" \ + " is executed before all other --lt-* options." ) + +#:def_opt( "LT_ONLINE", "lt-online|ltonline", "CP", \ + "value", \ + "Set LEX/TEST parameter OFFLINE-ONLINE to this MODE. MODE is one of" \ + " @AUTO@ (default), @NEVER@ or @ALWAYS@. If needed, LEX section TEST" \ + " is created or removed if empty." ) + +#:def_opt( "LT_N_PLAYERS", "lt-n-players|lt-nplayers|ltnplayers", "CP", \ + "offline,online", \ + "Set LEX/TEST parameters N-OFFLINE and N-ONLINE to these integer" \ + " values. If needed, LEX section TEST is created or removed if empty." ) + +#:def_opt( "LT_COND_BIT", "lt-cond-bit|lt-condbit|ltcondbit", "GP", \ + "bitnum", \ + "Set LEX/TEST parameter COND-BIT to this integer value. Use -1 to" \ + " deactivate this COND-BIT. If needed, LEX section TEST are created or" \ + " removed if empty." ) + +#:def_opt( "LT_GAME_MODE", "lt-game-mode|lt-gamemode|ltgamemode", "GP", \ + "mode", \ + "Set LEX/TEST parameter GAME-MODE to this MODE. MODE is one of @AUTO@" \ + " (default), @Balloon, COIN, VERSUS@ or @ITEMRAIN@. If needed, LEX" \ + " section TEST is created or removed if empty." ) + +#:def_opt( "LT_ENGINE", "lt-engine|ltengine", "GP", \ + "mode", \ + "Set LEX/TEST parameter ENGINE to this MODE. MODE is one of @AUTO@" \ + " (default), @BATTLE, 50CC, 100CC, 150CC, 200CC, 150M@ or @200M@. Only" \ + " track.szs files are modified. If needed, LEX section TEST is created" \ + " or removed if empty." ) + +#:def_opt( "LT_RANDOM", "lt-random|ltrandom", "GP", \ + "index", \ + "Force a random scenario, if between 1 and 8. Value 0 enables auto" \ + " selection and -1 disables this option. If needed, LEX section TEST" \ + " is created or removed if empty." ) + +#:def_opt( "LEX_PURGE", "lex-purge|lexpurge", "G", \ + "", \ + "Delete LEX sections without any impact. This option is executed after" \ + " all --lt-* options." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "LE_DEFINE", "le-define|ledefine", "CP", \ + "file", \ + "Use this file as definition file to patch LE-CODE. The file format" \ + " can be LE-BIN or any supported CT-CODE file (BRRES, TEX0, CT-CODE or" \ + " CT-TEXT)." ) + +#:def_opt( "LPAR", "lpar", "CP", \ + "file", \ + "Read a LPAR file to overwrite settings of LE-CODE section LPAR." ) + +#:def_opt( "ALIAS", "alias", "CP", \ + "list", \ + "The parameter is a comma separated list of @SLOT=ALIAS@ elements." \ + " SLOT defines the slot, for which the ALIAS is defined. ALIAS itself" \ + " is a slot number to be executed, if SLOT is selected. Empty and" \ + " invalid elements are ignored. Multiple usage is possible: the lists" \ + " are concatenated." ) + +#:def_opt( "ENGINE", "engine", "CP", \ + "list", \ + "Define probabilities for engine classes. @LIST@ is a vector for" \ + " @100cc,150cc,mirror@ If 200cc is enabled by {--200cc}, then vector" \ + " is interpreted as @150cc,200cc,mirror@. Any numbers are accepted." \ + " They are normalized to get a total of 100 percent. Use @0,1,0@ to" \ + " force 150cc only. Use @0,0,0@ to reset to Nintendos VR based choice." ) + +#:def_opt( "200CC", "200cc", "CO", \ + "[=mode]", \ + "Enable or disable 200cc support. Mode is one of @OFF@, @AUTO@" \ + " (default) or @ON@ (default, if option is used without parameter)." \ + " LE-CODE hasn't implemented this feature yet!" ) + +#:def_opt( "PERFMON", "perf-mon|perfmon", "CO", \ + "[=mode]", \ + "Enable or disable the performance monitor. Mode is one of @OFF@," \ + " @AUTO@ (default, don't change setting), @ON@ (enable it for Wii" \ + " only; default, if option is used without parameter) or @FORCE@" \ + " (enable it for Wii and for Dolphin)." ) + +#:def_opt( "CUSTOM_TT", "custom-tt|customtt", "CO", \ + "[=mode]", \ + "Enable or disable time trial for custom tracks. Mode is one of @OFF@," \ + " @AUTO@ (default) or @ON@ (default, if option is used without" \ + " parameter)." ) + +#:def_opt( "XPFLAGS", "xpflags", "CO", \ + "[=mode]", \ + "Enable or disable support for Extended Presence Flags. Mode is one of" \ + " @OFF@, @AUTO@ (default) or @ON@ (default, if option is used without" \ + " parameter). Disable it only for tests!" ) + +#:def_opt( "TRACK_DIR", "track-dir|trackdir", "CP", \ + "directory", \ + "Define a destination directory, where track files are copied, moved" \ + " or linked to. Usually it is @.../Race/Course/@.\n" \ + " Files are searched in directories defined by {--copy-tracks}," \ + " {--move-tracks}, {--move1-tracks} and {--link-tracks} in definition" \ + " order. Already existing files are removed before operation." ) + +#:def_opt( "COPY_TRACKS", "copy-tracks|copytracks", "CMP", \ + "directory", \ + "Define a search directory for all included tracks. Files found in" \ + " this directory are copied to the directory specified by option" \ + " {--track-dir}." ) + +#:def_opt( "MOVE_TRACKS", "move-tracks|movetracks", "CMP", \ + "directory", \ + "Define a search directory for all included tracks. Files found in" \ + " this directory are moved to the directory specified by option" \ + " {--track-dir}. If moving fails, the file is copied and the source" \ + " removed." ) + +#:def_opt( "MOVE1_TRACKS", "move1-tracks|move1tracks", "CMP", \ + "directory", \ + "Define a search directory for all included tracks. Files found in" \ + " this directory are moved to the directory specified by option" \ + " {--track-dir}, but only if a file has not more than one hard link." \ + " This guarantees an unique version of the file. If the file has more" \ + " hard links or moving fails, the file is copied and the source" \ + " removed." ) + +#:def_opt( "LINK_TRACKS", "link-tracks|linktracks", "CMP", \ + "directory", \ + "Define a search directory for all included tracks. Files found in" \ + " this directory are linked to the directory specified by option" \ + " {--track-dir}. If linking fails, the file is copied." ) + +#:def_opt( "COMPLETE", "C|complete", "C", \ + "", \ + " Append missing sections to the LEX file." ) + +#:def_opt( "LOAD_BMG", "load-bmg|loadbmg", "CMP", \ + "file", \ + "Load immediately the file to fill an internal message pool. The" \ + " option can be used multiple times to fill the message pool by" \ + " different sources. A later message overrides the previous message.\n" \ + " The internal message pool is patched by all {--patch-bmg} options." ) + +#:def_opt( "PATCH_BMG", "patch-bmg|patchbmg|pb", "CMP", \ + "mode[cond][=param]", \ + "This option specifies a BMG patch mode. Some of the modes need a" \ + " parameter or a file name of a BMG patch file (raw or text BMG), both" \ + " separated by an equal sign. Modes with required file names are" \ + " @PRINT, REPLACE, INSERT, OVERWRITE, DELETE, MASK, EQUAL@ and" \ + " @NOTEQUAL@. Modes with text parameter are @FORMAT@, @REGEXP@ and" \ + " @RM-REGEXP@. Standalone modes are @ID, ID-ALL, UNICODE, RM-ESCAPES," \ + " RM-CUPS, CT-COPY, CT-FORCE-COPY, CT-FILL, LE-COPY, LE-FORCE-COPY," \ + " LE-FILL, X-COPY, X-FORCE-COPY@ and @X-FILL@. Unique abbreviations" \ + " are allowed.\n" \ + " The optional condition @COND@ is either '@?MID@' or '@!MID@'. In" \ + " case of '@?MID@', the patch is only applied if the message id MID" \ + " already exists. In case of '@!MID@', the patch is only applied if" \ + " the message id MID does not exists.\n" \ + " If this option is used multiple times, all patch files will be" \ + " processed in the entered order." ) + +#:def_opt( "MACRO_BMG", "macro-bmg|macrobmg|mb", "CMP", \ + "file", \ + "Load a BMG file and add the messages to the macro library; already" \ + " existing entries are replaced. The macro library is accessed by" \ + " escape sequence @\\m{{MID}@ as fallback, if the active file has not" \ + " already defined the message @MID@ by itself." ) + +#:def_opt( "PATCH_NAMES", "patch-names|patchnames", "C", \ + "", \ + "If set, the names of the CUP1 and CSR1 sections are patched by the" \ + " loaded BMG files directly after loading the source files." ) + +#:def_opt( "ORDER_BY", "order-by|orderby|order", "CP", \ + "bmg", \ + "Load the BMG file and filter track names. Then before creating output" \ + " files, re-distribute the tracks of the additonal cups (9-) to the" \ + " same cups by the order of the read track names. Tracks without BMG" \ + " reference are placed at the end of all tracks. This option modifes" \ + " only the track order of the cups, but will not change any name." ) + +#:def_opt( "ORDER_ALL", "order-all|orderall", "C", \ + "", \ + "Used together with option {--order-by}: Re-distribute the tracks of" \ + " all cups beginning with Mushroom Cup." ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "SYMBOLS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "BRIEF", \ + "", \ + "Suppress the output of the description and print only function type" \ + " and syntax." ) + +#:def_cmd_opt( "FUNCTIONS", "LONG", \ + "", \ + "Usually only the function syntax is compared to the keywords. But if" \ + " --long is set, the descriptions are compared too." ) + +#:def_cmd_opt( "CALCULATE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "FLOAT", "ROUND", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "LE_DEFINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "LPAR", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ALIAS", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "ENGINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "200CC", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "PERFMON", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "CUSTOM_TT", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "XPFLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "LONG", \ + "", \ + "For LE-CODE binaries: If set, print cups with tracks or arenas. For" \ + " each track, property and music slot is printed in brackets. If set" \ + " twice, print a track list too.\n" \ + " For LEX binaries: If set, print a hex dump of max 16 bytes (1 line)" \ + " of the element data. If set twice, print a hex dump of max 64 bytes" \ + " (4 lines). If set 3 times ore more, print a complete hex dump." ) + +#:def_cmd_opt( "DUMP", "VERBOSE", \ + "", \ + "For LE-CODE binaries: If set, print a track slot usage table." ) + +#:def_cmd_opt( "DL", "LE_DEFINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "LPAR", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "ALIAS", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "ENGINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "200CC", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "PERFMON", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "CUSTOM_TT", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "XPFLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "DL", "LONG", \ + "", \ + "For LE-CODE binaries: If set, print cups with tracks or arenas. For" \ + " each track, property and music slot is printed in brackets. If set" \ + " twice, print a track list too.\n" \ + " For LEX binaries: If set, print a hex dump of max 16 bytes (1 line)" \ + " of the element data. If set twice, print a hex dump of max 64 bytes" \ + " (4 lines). If set 3 times ore more, print a complete hex dump." ) + +#:def_cmd_opt( "DL", "VERBOSE", \ + "", \ + "For LE-CODE binaries: If set, print a track slot usage table." ) + +#:def_cmd_opt( "DLL", "LE_DEFINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "LPAR", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "ALIAS", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "ENGINE", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "200CC", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "PERFMON", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "CUSTOM_TT", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "XPFLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "DLL", "LONG", \ + "", \ + "For LE-CODE binaries: If set, print cups with tracks or arenas. For" \ + " each track, property and music slot is printed in brackets. If set" \ + " twice, print a track list too.\n" \ + " For LEX binaries: If set, print a hex dump of max 16 bytes (1 line)" \ + " of the element data. If set twice, print a hex dump of max 64 bytes" \ + " (4 lines). If set 3 times ore more, print a complete hex dump." ) + +#:def_cmd_opt( "DLL", "VERBOSE", \ + "", \ + "For LE-CODE binaries: If set, print a track slot usage table." ) + +#:def_cmd_opt( "BIN_DIFF", "QUIET", \ + "", \ + "Suppress all messages and tell result as exit status only." ) + +#:def_cmd_opt( "BIN_DIFF", "VERBOSE", \ + "", \ + "Be more verbose and tell, what tests are planned." ) + +#:def_cmd_opt( "PATCH", "LE_DEFINE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "LPAR", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ALIAS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ENGINE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "200CC", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PERFMON", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "CUSTOM_TT", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "XPFLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "TRACK_DIR", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "COPY_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "MOVE_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "MOVE1_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "LINK_TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "LE_DEFINE", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "LPAR", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "ALIAS", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "ENGINE", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "200CC", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "PERFMON", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "CUSTOM_TT", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "XPFLAGS", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in LE text files." ) + +#:def_cmd_opt( "LPAR", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "LPAR", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in LE text files." ) + +#:def_cmd_opt( "CREATE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LPAR", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LT_CLEAR", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LT_ONLINE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LT_N_PLAYERS", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LT_COND_BIT", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LT_GAME_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LT_ENGINE", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LT_RANDOM", \ + "", \ + "" ) + +#:def_cmd_opt( "CREATE", "LEX_PURGE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "COMPLETE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in LE text files." ) + +#:def_cmd_opt( "CAT", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "COMPLETE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in LE text files." ) + +#:def_cmd_opt( "DECODE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "EXPORT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "COMPLETE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "IGNORE", \ + "", \ + "" ) + + +## +############################################################################### +############### Tool 'wmdlt' ############### +############################################################################### + +#:def_tool( "wmdlt", \ + "wmdlt [option]... command [option|parameter|@file]...", \ + "Wiimms MDL Tool : Decode raw MDL and encode text MDL files." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wmdlt VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wmdlt HELP [--width=wd] OPT|OPTION ...\n" \ + "wmdlt HELP [--width=wd] CMD|COMMAND [ignored]...\n" \ + "wmdlt HELP [--width=wd] command [-b] [ignored]...\n" \ + "wmdlt HELP [--width=wd] [ignored]...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wmdlt ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wmdlt TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wmdlt COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wmdlt ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wmdlt FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wmdlt FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "SYMBOLS", "SYMBOLS", \ + "wmdlt SYMBOLS", \ + "Print predefined symbols of text parser to standard output. The MDL" \ + " specific symbols are included." ) + +#:def_cmd( "FUNCTIONS", "FUNCTIONS", \ + "wmdlt FUNCTIONS [key]...", \ + "Print a list with all parser functions including the MDL specific" \ + " functions. If one key ore more key entered, then only functions that" \ + " match at least one key are printed." ) + +#:def_cmd( "CALCULATE", "CALCULATE", \ + "wmdlt CALCULATE expression...", \ + "Calculate each expression and print the result to standard output." ) + +#:def_cmd( "MATRIX", "MATRIX", \ + "wmdlt MATRIX [patch-option]... [vector]...", \ + "Use the transformation options as base and calculate and print the" \ + " related vectors and the transformation matrices. Then do a forward" \ + " and backward transformation for each @vector@ parameter using the" \ + " matrices and print the results.", \ + "Use the transformation options as base and calculate and print the" \ + " related vectors and the transformation matrices. Then do a forward" \ + " and backward transformation for each @vector@ parameter using the" \ + " matrices and print the results. The transformations are done with 4" \ + " different methods to confirm the algorithms. Only the main results" \ + " and results, which differ, are printed.\n" \ + " Normally, the exit status is @OK@ (null). If at least one old" \ + " transformation returns different results for the different" \ + " algorithms, then status @DIFFER@ (not null) is returned." ) + +#:def_cmd( "FLOAT", "FLOAT", \ + "wmdlt FLOAT number...", \ + "This commands helps to convert hex values into floating point numbers" \ + " and vice versa. For each non empty parameter, a line with 4 bytes" \ + " hex and a float, 8 bytes hex and a double float, and the parameter" \ + " itself is printed. If a parameter begins with 'x' or '0x', it is a" \ + " hex string, otherwise it is a floating point number.\n" \ + " The special parameters \"+\" and \"-\" mean: Increment or decrement" \ + " the previous value by the smallest possible value. Use different" \ + " increments for floats and doubles.\n" \ + " All conversions are done using big endian." ) + +#:def_cmd( "CAT", "CAT", \ + "wmdlt CAT [source]...", \ + "Read the entered MDL files (text or binary) and print them as decoded" \ + " text files to standard output (stdout). The output of all source" \ + " files is concatenated.\n" \ + " A decoded MDL0 file can only be used for analysis. Encoding or" \ + " creating a new MDL0 file is not supported." ) + +#:def_cmd( "DECODE", "DECODE|DEC", \ + "wmdlt DECODE [source]...", \ + "Read the entered MDL files (text or binary) and decode them to text" \ + " files. The default destination is @'%P/%N.txt'@.\n" \ + " A decoded MDL0 file can only be used for analysis. Encoding or" \ + " creating a new MDL0 file is not supported." ) + +#:def_cmd( "STRINGS", "STRINGS", \ + "wmdlt STRINGS [source]...", \ + "Read the entered MDL files (text or binary) and print all strings of" \ + " all sections.\n" \ + " For archives like SZS or BREES, the strings of each containing MDL" \ + " are printed. It is also possible to address an internal sub-file" \ + " like @track.szs/course_model.brres@." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "This option is completely ignored by wlect. It only exists due to" \ + " compatibility with the other tools." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "MDL", "mdl", "GP", \ + "list", \ + "Set global options for MDL processing. To enable MDL patching, use" \ + " option {--patch-file} and select BRRES files for patching.\n" \ + " Parameter @'list'@ is a comma separated list of keywords. A minus" \ + " sign before a keyword disables a setting. Each occurrence of the" \ + " option will only change entered settings and all other settings are" \ + " untouched.\n" \ + " A MDL transformation (controlled by the transformation options) is" \ + " only done, if at least one of the keywords @VECTOR@ or @VERTEX@ is" \ + " set.\n" \ + " Keyword @DEFAULT@ resets the default settings (@VERTEX@) and" \ + " @CLEAR@ disables all. The other allowed keywords are: @VECTOR," \ + " MATRIX, VERTEX, PARENT, CHILD@ and @LOG@." ) + +#:def_opt( "SCALE", "scale", "CP", \ + "vector[@origin]", \ + "Transform the data: Scale all coordinates and size values by 'vector'" \ + " relative to the point 'origin'. If the origin is not set, @0,0,0@ is" \ + " used instead.\n" \ + " Negative values mirror the coordinates. Each parameter is either a" \ + " vector expression or a comma separated expression list of" \ + " coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "SHIFT", "shift", "CP", \ + "vector", \ + "Transform the data: Add 'vector' to all coordinates. It's simlar to" \ + " {--translate}, but the addition is done before rotation. The" \ + " parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "XSS", "xss", "CP", \ + "x1old,x1new,x2old,x2new", \ + "--xss (x-scale-shift) calculates the X values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "YSS", "yss", "CP", \ + "y1old,y1new,y2old,y2new", \ + "--yss (y-scale-shift) calculates the Y values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "ZSS", "zss", "CP", \ + "z1old,z1new,z2old,z2new", \ + "--zss (z-scale-shift) calculates the Z values of {--scale} and" \ + " {--shift}, so that old values are transformed to the new values. The" \ + " parameters are numbers or expression." ) + +#:def_opt( "ROT", "rot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' (is a vector) around the 3 axes. All 3 axes goes" \ + " through the point 'origin'. If the origin is not set, @0,0,0@ is" \ + " used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,y,z').\n" \ + " Option --rot is an alternative for {--xrot}, {--yrot} and {--zrot}" \ + " to define all 3 rotations in one step. Transformations are" \ + " calculated in the order SCALE, SHIFT, X-ROTATE, Y-ROTATE, Z-ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "XROT", "xrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the x-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('y,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "YROT", "yrot|hrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the x-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead. This is" \ + " a horizontal counterclockwise rotation and the old option name" \ + " --hrot can also used.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,z' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "ZROT", "zrot", "CP", \ + "degree[@origin]", \ + "Transform the data: Rotate all coordinates and rotation values by the" \ + " angle 'degree' around the z-axis, that goes through the point" \ + " 'origin'. If the origin is not set, @0,0,0@ is used instead.\n" \ + " Each parameter is either a vector expression or a comma separated" \ + " expression list of coordinates ('x,y' or 'x,y,z').\n" \ + " Transformations are calculated in the order SCALE, SHIFT, X-ROTATE," \ + " Y-ROTATE, Z-ROTATE, TRANSLATE." ) + +#:def_opt( "TRANSLATE", "translate", "CP", \ + "vector", \ + "Transform the data: Add 'vector' to all coordinates. It's simlar to" \ + " {--shift}, but the addition is done after rotation. The parameter is" \ + " either a vector expression or a comma separated expression list of" \ + " coordinates ('x,z' or 'x,y,z'). @--trans@ is a short cut.\n" \ + " Transformations are calculated in the order SCALE, SHIFT, ROTATE," \ + " TRANSLATE." ) + +#:def_opt( "NULL", "null", "C", \ + "", \ + "Create a neutral transformation without affecting the coordinates." \ + " The only influence is, that the dependent values (like maximum) are" \ + " re-calculated as if a transformation has taken place." ) + +#:def_opt( "NEXT", "next", "C", \ + "", \ + "Close the current transformation step with all scaling, shifting," \ + " rotation and translation options and open a new step with cleared" \ + " options. On transformation each step is logical done one by one. In" \ + " real, one total transformation matrix is calculated and used for" \ + " fast transformations.\n" \ + " If option --next is used, the ability for modifying the scale and" \ + " rotation vectors (not the positions) of different KMP sections is" \ + " nearly always lost. A warning is printed if this occurs." ) + +#:def_opt( "ASCALE", "ascale", "CP", \ + "factor@dir", \ + "Transform the data and do an axis scale: Close the current" \ + " transformation step like {--next} and scale the data by @'factor'@" \ + " into the direction given by the vector @'dir'@. Store the resulting" \ + " matrix as single transformation step and open a new one.\n" \ + " This kind of transformation is EXPERIMENTAL!" ) + +#:def_opt( "AROT", "arot", "CP", \ + "degree[@pos1]@pos2", \ + "Transform the data and do an axis rotation: Close the current" \ + " transformation step like {--next} and rotate the data by @'degree'@" \ + " around the axis defined by the 2 points. If @'pos1'@ is not set, use" \ + " @0,0,0@ instead. Store the resulting matrix as single transformation" \ + " step and open a new one.\n" \ + " This kind of transformation is EXPERIMENTAL!" ) + +#:def_opt( "TFORM_SCRIPT", "tform-script|tformscript", "CP", \ + "script", \ + "Load the script and execute it by the text parser.\n" \ + " Then for each coordinate (2D and 3D), call the macro @TRANSFORM@" \ + " after all other transformations. Call it without parameters and set" \ + " variable @$$I@ with a zero based index of all vertices, variable" \ + " @$$D@ to the dimension (2 or 3) and the variable @$$P@ to the vector" \ + " of the current vertex. If the macro returns a vector, it is the new" \ + " position.\n" \ + " For each file, macro @BEGIN@ is called at the beginning and @END@" \ + " after processing all points, but only, if the macros are defined." \ + " For @END@, variable @$$I@ is set to the number of processed vertices" \ + " in the current file.\n" \ + " Before each macro call, the private and local variables are cleared" \ + " and only global parameters are permanent. The zero based index is" \ + " cleared for each new source. The parameters of the macro calls are" \ + " defined in the local name space." ) + +#:def_opt( "ROUND", "round", "C", \ + "", \ + "Print additional lines with rounded values (to 3 and 2 bytes for" \ + " floats and 7 and 6 bytes for doubles)." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Suppress cross reference comments." ) + +#:def_opt( "NO_PARAM", "P|no-param|noparam", "C", \ + "", \ + "Disable parameter support in decoded KMP text files." ) + +#:def_opt( "NO_ECHO", "no-echo|noecho", "C", \ + "", \ + "Suppress output of @@ECHO commands while scanning text files." ) + +#:def_opt( "NO_CHECK", "N|no-check|nocheck", "C", \ + "", \ + "Do not make plausibility checks for KMP files." ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "TINY", "tiny", "GP", \ + "mode", \ + "Set tiny mode to a level between 0 and 7." ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "TEST", "VERBOSE", \ + "", \ + "Print the matrices of transformations." ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "SYMBOLS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "BRIEF", \ + "", \ + "Suppress the output of the description and print only function type" \ + " and syntax." ) + +#:def_cmd_opt( "FUNCTIONS", "LONG", \ + "", \ + "Usually only the function syntax is compared to the keywords. But if" \ + " --long is set, the descriptions are compared too." ) + +#:def_cmd_opt( "CALCULATE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "MDL", \ + "", \ + "" ) + +#:def_cmd_opt( "MATRIX", "BRIEF", \ + "", \ + "If set once, suppress vector dumps for transformation steps and" \ + " ignore option --long. If set twice, suppress vector dump for final" \ + " transformation too." ) + +#:def_cmd_opt( "MATRIX", "LONG", \ + "", \ + "If set once, print inverse matrix for the final transformation. If" \ + " set twice, print inverse matrix for all transformations." ) + +#:def_cmd_opt( "MATRIX", "QUIET", \ + "", \ + "Option --quiet is dedicated to automatic tests comparing the old and" \ + " new transformation algorithms. If set once, the tables are only" \ + " printed, if the results differ. If set twice, nothing is printed at" \ + " all and only the exit status will inform about the results." ) + +#:def_cmd_opt( "FLOAT", "ROUND", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in MDL text files." ) + +#:def_cmd_opt( "CAT", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "MDL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in MDL text files." ) + +#:def_cmd_opt( "DECODE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "MDL", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "MDL", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in MDL text files." ) + +#:def_cmd_opt( "STRINGS", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "STRINGS", "MDL", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in MDL text files." ) + +#:def_cmd_opt( "GEOMETRY", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "GEOMETRY", "MDL", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in MDL text files." ) + +#:def_cmd_opt( "XTEST", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "SCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "SHIFT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "XSS", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "YSS", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "ZSS", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "ROT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "XROT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "YROT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "ZROT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "TRANSLATE", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "ASCALE", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "AROT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "TFORM_SCRIPT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "NEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "XTEST", "MDL", \ + "", \ + "" ) + + +## +############################################################################### +############### Tool 'wpatt' ############### +############################################################################### + +#:def_tool( "wpatt", \ + "wpatt [option]... command [option|parameter|@file]...", \ + "Wiimms PAT Tool : Decode raw PAT and encode text PAT files." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wpatt VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wpatt HELP [--width=wd] OPT|OPTION ...\n" \ + "wpatt HELP [--width=wd] CMD|COMMAND [ignored]...\n" \ + "wpatt HELP [--width=wd] command [-b] [ignored]...\n" \ + "wpatt HELP [--width=wd] [ignored]...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wpatt ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wpatt TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wpatt COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wpatt ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wpatt FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wpatt FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "SYMBOLS", "SYMBOLS", \ + "wpatt SYMBOLS", \ + "Print predefined symbols of text parser to standard output. The PAT" \ + " specific symbols are included." ) + +#:def_cmd( "FUNCTIONS", "FUNCTIONS", \ + "wpatt FUNCTIONS [key]...", \ + "Print a list with all parser functions including the PAT specific" \ + " functions. If one key ore more key entered, then only functions that" \ + " match at least one key are printed." ) + +#:def_cmd( "CALCULATE", "CALCULATE", \ + "wpatt CALCULATE expression...", \ + "Calculate each expression and print the result to standard output." ) + +#:def_cmd( "FLOAT", "FLOAT", \ + "wpatt FLOAT number...", \ + "This commands helps to convert hex values into floating point numbers" \ + " and vice versa. For each non empty parameter, a line with 4 bytes" \ + " hex and a float, 8 bytes hex and a double float, and the parameter" \ + " itself is printed. If a parameter begins with 'x' or '0x', it is a" \ + " hex string, otherwise it is a floating point number.\n" \ + " The special parameters \"+\" and \"-\" mean: Increment or decrement" \ + " the previous value by the smallest possible value. Use different" \ + " increments for floats and doubles.\n" \ + " All conversions are done using big endian." ) + +#:def_cmd( "CAT", "CAT", \ + "wpatt CAT [source]...", \ + "Read the entered PAT files (text or binary) and print them as decoded" \ + " text files to standard output (stdout). The output of all source" \ + " files is concatenated." ) + +#:def_cmd( "DECODE", "DECODE|DEC", \ + "wpatt DECODE [source]...", \ + "Read the entered PAT files (text or binary) and decode them to text" \ + " files. The default destination is @'%P/%N.txt'@." ) + +#:def_cmd( "ENCODE", "ENCODE|ENC", \ + "wpatt ENCODE [source]...", \ + "Read the entered PAT files (text or binary) and encode (compile) them" \ + " to binary files. The default destination is @'%P/%N.pat'@." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "This option is completely ignored by wlect. It only exists due to" \ + " compatibility with the other tools." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "ROUND", "round", "C", \ + "", \ + "Print additional lines with rounded values (to 3 and 2 bytes for" \ + " floats and 7 and 6 bytes for doubles)." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "Suppress cross reference comments." ) + +#:def_opt( "NO_PARAM", "P|no-param|noparam", "C", \ + "", \ + "Disable parameter support in decoded KMP text files." ) + +#:def_opt( "NO_ECHO", "no-echo|noecho", "C", \ + "", \ + "Suppress output of @@ECHO commands while scanning text files." ) + +#:def_opt( "NO_CHECK", "N|no-check|nocheck", "C", \ + "", \ + "Do not make plausibility checks for KMP files." ) + +#:def_opt( "MAX_FILE_SIZE", "M|max-file-size|maxfilesize", "GP", \ + "size", \ + "This security option defines the maximum file size for input files;" \ + " larger files are ignored with a warning. The default unit is MiB and" \ + " the default size is 100 MiB; that should be enough for standard" \ + " usage." ) + +#:def_opt( "PAT", "pat", "GP", \ + "list", \ + "Set global options for PAT processing. Parameter @'list'@ is a comma" \ + " separated list of keywords. A minus sign before a keyword disables a" \ + " setting. Each occurrence of the option will only change entered" \ + " settings and all other settings are untouched.\n" \ + " Keyword @DEFAULT@ resets the default settings and @CLEAR@ disables" \ + " all. The other allowed keywords are: @AUTO, SIMPLE, COMPLEX, BOTH," \ + " SILENT@ and @LOG@." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "SYMBOLS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "FUNCTIONS", "BRIEF", \ + "", \ + "Suppress the output of the description and print only function type" \ + " and syntax." ) + +#:def_cmd_opt( "FUNCTIONS", "LONG", \ + "", \ + "Usually only the function syntax is compared to the keywords. But if" \ + " --long is set, the descriptions are compared too." ) + +#:def_cmd_opt( "CALCULATE", "CONST", \ + "", \ + "" ) + +#:def_cmd_opt( "FLOAT", "ROUND", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in PAT text files." ) + +#:def_cmd_opt( "CAT", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "CAT", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PAT", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_HEADER", \ + "", \ + "Suppress the syntax information section in PAT text files." ) + +#:def_cmd_opt( "DECODE", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "DECODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PAT", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_ECHO", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "NO_CHECK", \ + "", \ + "" ) + +#:def_cmd_opt( "ENCODE", "IGNORE", \ + "", \ + "" ) + + +## +############################################################################### +############### Tool 'wstrt' ############### +############################################################################### + +#:def_tool( "wstrt", \ + "wstrt [option]... command [option|parameter|@file]...", \ + "Wiimms StaticR Tool : Manipulate the 'main.dol' and 'StaticR.rel'" \ + " files of Mario Kart Wii." ) + +#:def_cmd( "VERSION", "VERSION", \ + "wstrt VERSION [ignored]...", \ + "Print program name and version and exit." ) + +#:def_cmd( "HELP", "HELP|H|?", \ + "wstrt HELP [--width=wd] OPT|OPTION ...\n" \ + "wstrt HELP [--width=wd] CMD|COMMAND [ignored]...\n" \ + "wstrt HELP [--width=wd] command [-b] [ignored]...\n" \ + "wstrt HELP [--width=wd] [ignored]...", \ + "Print help for commands and options.", \ + "Print help for commands and options.\n" \ + " The first variant (with keyword @OPT@ or @OPTION@) prints a list of" \ + " all global and command specific options. If at least one option is" \ + " set, only info about the declared options are printed. The second" \ + " variant (with keyword @CMD@ or @COMMAND@) prints an overview about" \ + " all commands. The third variant (with a valid command name) prints" \ + " details about the declared command. The fourth variant prints an" \ + " overview about all commands and all global options." ) + +#:def_cmd( "ARGTEST", "ARGTEST", \ + "wstrt ARGTEST ...", \ + "This debug command accepts all kinds of parameters and prints one" \ + " line for each parameter." ) + +#:def_cmd( "TEST", "TEST", \ + "wstrt TEST [options]... [ignored]...", \ + "This debugging command accepts all options and parameters and prints" \ + " a list of the internal option values. The command is designed to" \ + " test the option parameters. The non option parameters are completely" \ + " ignored." ) + +#:def_cmd( "COLORS", "COLORS", \ + "wstrt COLORS", \ + "Ignore all parameters and print colored text for testing." ) + +#:def_cmd( "ERROR", "ERROR|ERR", \ + "wstrt ERROR [error_code]", \ + "Translate an exit code to a message name. If no exit code is entered," \ + " print a table with all error messages." ) + +#:def_cmd( "FILETYPE", "FILETYPE|FT", \ + "wstrt FILETYPE file...", \ + "Scan the header of the entered files and print file type and path for" \ + " each file as list." ) + +#:def_cmd( "FILEATTRIB", "FILEATTRIB|FA", \ + "wstrt FILEATTRIB [keyword]...", \ + "Print a table with attributes for all known file types. If at least" \ + " one keyword is entered, only matching file types are printed. A" \ + " keyword is either a file type (like 'BRRES' or 'BMG') or one of the" \ + " printed keywords (like 'ARCHIVE' or 'EXTRACT')." ) + +#:def_cmd( "DUMP", "DUMP", \ + "wstrt DUMP file...", \ + "Dump the structure of 'StaticR.rel' (in development) and of DOL files" \ + " as text." ) + +#:def_cmd( "HEXDUMP", "HEXDUMP|XDUMP", \ + "wstrt HEXDUMP file...", \ + "Dump the content of DOL files as hex dump and use virtual addresses." \ + " Select parts by --vaddr, --faddr and --sname. Without selectors, the" \ + " whole file is dumped." ) + +#:def_cmd( "EXTRACT", "EXTRACT|X", \ + "wstrt EXTRACT dol...", \ + "Extract the sections of a DOL file into a directory. StaticR.rel" \ + " files are ignored." ) + +#:def_cmd( "TRACKS", "TRACKS", \ + "wstrt TRACKS [file]...", \ + "Show track infos in the order of the 'StaticR.rel' files. If no file" \ + " is entered, the standard order is used." ) + +#:def_cmd( "ARENAS", "ARENAS", \ + "wstrt ARENAS [file]...", \ + "Show arena infos in the order of the 'StaticR.rel' files. If no file" \ + " is entered, the standard order is used." ) + +#:def_cmd( "FILES", "FILES", \ + "wstrt FILES [file]...", \ + "List track and arena file names in machine readable format. Use the" \ + " order of the entered 'StaticR.rel' files. If no file is entered, the" \ + " standard order is used." ) + +#:def_cmd( "FIND", "FIND", \ + "wstrt FIND [+]TRACK keyword...\n" \ + "wstrt FIND [+]ARENA keyword...", \ + "Find a track or arena by keyword and print one line for each keyword" \ + " in machine readable format @'type slot'@ or, if not found, a simple" \ + " @'-'@. The name is searched in the entered context (tracks or" \ + " arenas). If @TRACK@ or @ARENA@ is prefixed by a plus, then a failed" \ + " search will be continued in the other context. If at least one name" \ + " is not found, the tool terminates with exit code @NOT_EXISTS@." ) + +#:def_cmd( "POINTS", "POINTS|PTS", \ + "wstrt POINTS [--points=list] [file]...", \ + "Print a table with the points assigned to player after a versus or" \ + " room race for each entered file. If no file is entered, the standard" \ + " table is used. However, if option {--points} is set only the" \ + " resulting table is printed." ) + +#:def_cmd( "ANALYZE", "ANALYZE|ANA|ANALYSE", \ + "wstrt ANALYZE [file]...", \ + "Open 'StaticR.rel' and DOL files and analyze them. The files will be" \ + " classified using SHA1 checksums." ) + +#:def_cmd( "PATCH", "PATCH", \ + "wstrt PATCH [file]...", \ + "Patch 'main.dol and 'StaticR.rel' files of Mario Kart Wii. No backup" \ + " is made." ) + +#:def_opt( "VERSION", "V|version", "G", \ + "", \ + "Stop parsing the command line, print a version info and exit." ) + +#:def_opt( "HELP", "h|help", "G", \ + "", \ + "Stop parsing the command line, print a help message and exit." ) + +#:def_opt( "XHELP", "xhelp", "G", \ + "", \ + "Stop parsing the command line and print a help message with all" \ + " commands included. Exit after printing." ) + +#:def_opt( "ALLOW_ALL", "allow-all|allowall", "G", \ + "", \ + "Usually commands accept only options with impact to the command. All" \ + " other options fire a syntax error. But if @--allow-all@ is set, all" \ + " commands accept all options.\n" \ + " This makes changing the command of a long command line without" \ + " removing useless options easier. It also helps to override wrong" \ + " option permissions." ) + +#:def_opt( "COMPATIBLE", "compatible", "GP", \ + "vers", \ + "The option expects a version number (format @'#.##'@ or @'v#.##'@) or" \ + " a revision number (format @'r#'@) as parameter. If set, the tools" \ + " try to create BMG and KMP text files, that are compatible to the" \ + " entered version of the tools. This may override other legacy" \ + " options." ) + +#:def_opt( "WIDTH", "width", "GP", \ + "width", \ + "Define the terminal width (number of columns) for help and some other" \ + " messages and disable the automatic detection of the terminal width." ) + +#:def_opt( "QUIET", "q|quiet", "GM", \ + "", \ + "Be quiet and print only error messages. Multiple usage is possible." \ + " The impact is command dependent. In general: If set three times," \ + " different file format warnings are suppressed." ) + +#:def_opt( "VERBOSE", "v|verbose", "GM", \ + "", \ + "Be verbose and print more progress information. Multiple usage is" \ + " possible. The impact is command dependent." ) + +#:def_opt( "LOGGING", "L|logging", "GM", \ + "", \ + "This debug option enables the logging of internal lists and maps." ) + +#:def_opt( "DE", "de", "G", \ + "", \ + "Use german track and arena names." ) + +#:def_opt( "COLORS", "colors", "GO", \ + "[=modus]", \ + "Define the modus for colored text output. Allowed keywords are: @OFF@" \ + " or @NO-COLORS@ to disable colors, @AUTO@ (default) for automatic" \ + " detection, @ON@ for automatic detection but never @OFF@, @8-COLORS@" \ + " and @256-COLORS@ to force 8 or 256 color support. Without parameter," \ + " @ON@ is used.\n" \ + " @AUTO@ will enable colorized output only for terminals. @AUTO@ and" \ + " @ON@ use environment variable @TERM@ to find the correct color" \ + " modus.\n" \ + " If a command is prefixed by @'C-'@, then @--color=ON@ is used" \ + " implicitly as default." ) + +#:def_opt( "NO_COLORS", "no-colors|nocolors", "G", \ + "", \ + "Deactive colorized text. This is the default, if an output file is" \ + " not a terminal." ) + +#:def_opt( "CT_CODE", "ct-code|ctcode", "G", \ + "", \ + "Enable CT-CODE support." ) + +#:def_opt( "LE_CODE", "le-code|lecode", "GO", \ + "[=param]", \ + "This option is completely ignored by wlect. It only exists due to" \ + " compatibility with the other tools." ) + +#:def_opt( "CHDIR", "chdir", "GMP", \ + "dir", \ + "Set a new working directory for all following options, for all" \ + " parameters and for the command execution. This option can be used" \ + " multiple times, but for parameter parsing and for command execution" \ + " only the last usage is relevant. Nevertheless, @'@@param'@ values" \ + " are scanned while reading the command line." ) + +#:def_opt( "CONST", "c|const", "GMP", \ + "list", \ + "Define constant values, that are used by the internal encoders and by" \ + " the numeric options as predefined global variables. This option" \ + " allows a conditional encoding of text files. It can be used multiple" \ + " times for multiple definitions.\n" \ + " The parameter is a comma separated list of terms and a term is" \ + " '@name=expression@'. The expression is calculated by the text" \ + " parser." ) + +#:def_opt( "LONG", "l|long", "CM", \ + "", \ + "Print long numeric message IDs instead of alternative message names" \ + " like Txx, Uxx or Mxx." ) + +#:def_opt( "BRIEF", "B|brief", "C", \ + "", \ + "List only the track file names and suppress table header and footer." ) + +#:def_opt( "NO_HEADER", "H|no-header|noheader", "C", \ + "", \ + "Suppress the syntax information section in BMG text files." ) + +#:def_opt( "TRACKS", "T|tracks", "GP", \ + "tracks", \ + "Define a new track order. A comma separated list in the format" \ + " @[a=]b@ or @a:=b@ is expected. @'a'@ and @'b'@ are track names or" \ + " slot numbers. @'a'@ is the destination slot and @'b'@ the assigned" \ + " track. If @'a'@ is omitted, the next slot is used; this means" \ + " @[a=]b,c,d@ is possible.\n" \ + " Tracks will only be exchanged so that always all tracks are" \ + " assigned. But if using @':='@ the new slots are assigned without" \ + " exchanging and the old values are lost." ) + +#:def_opt( "ARENAS", "A|arenas", "GP", \ + "arenas", \ + "Define a new arena order. A comma separated list in the format" \ + " @[a=]b@ or @a:=b@ is expected. @'a'@ and @'b'@ are arena names or" \ + " slot numbers. @'a'@ is the destination slot and @'b'@ the assigned" \ + " arena. If @'a'@ is omitted, the next slot is used; this means" \ + " @[a=]b,c,d@ is possible.\n" \ + " Arenas will only be exchanged so that always all arenas are" \ + " assigned. But if using @':='@ the new slots are assigned without" \ + " exchanging and the old values are lost." ) + +#:def_opt( "REGION", "R|region", "GP", \ + "region", \ + "If 'region' is an unsigned number, the region for online versus races" \ + " and for battles is patched to the entered number. If the number is" \ + " prefixed by the letter @'t'@, a test mode is enabled and the places" \ + " are patched with ascending numbers. You can patch StaticR.rel and" \ + " main.dol.\n" \ + " Otherwise a keyword is expected. Official regions are: @JAPAN(0)," \ + " AMERICA(1), EUROPE(2), AUSTRALIA(OCEANIA,3), KOREA(4), TAIWAN(5)@" \ + " and @CHINA(6)@. @RESTORE@ restores the original Nintendo values and" \ + " @NONE@ disables region patching (default).\n" \ + " See https://wiimmfi.de/reg-list for other regions." ) + +#:def_opt( "VS_REGION", "vs-region|vsregion|vregion", "GP", \ + "region", \ + "Same as {--region}, but only for online versus races." ) + +#:def_opt( "BT_REGION", "bt-region|btregion|bregion", "GP", \ + "region", \ + "Same as {--region}, but only for online battles." ) + +#:def_opt( "ALL_RANKS", "all-ranks|allranks", "GO", \ + "[=mode]", \ + "If set to @ON@ (default without parameter), the server query to" \ + " Nintendos servers is manipulated, so that the player will find all" \ + " players in online races independent of their ranking points. Value" \ + " @RESTORE@ disables this feature and restores Nintendos original" \ + " query. Value @NONE@ disables this kind of patching (default)." ) + +#:def_opt( "POINTS", "points|pts", "GP", \ + "list", \ + "This patch option defines a new table for the points assigned to" \ + " players after a versus or room race. The parameter is a blank or" \ + " comma and slash (next row) separated list with numbers (points)" \ + " between 0 and 255 and the following keywords: @NINTENDO, LINEAR," \ + " WIN, WIN15, WIN25@ to select a predefined table; @NO-BONUS, BONUS," \ + " NULL, ONE@ as options; @N1..N12@ to select the row for N players." ) + +#:def_opt( "HTTPS", "https", "GP", \ + "mode", \ + "Patch the https strings of REL and DOL files. 'mode' is one of the" \ + " following keywords:\n" \ + "@RESTORE@ restores the original URLs and domains.\n" \ + "@HTTP@ replaces HTTPS requests by HTTP and sub-domain '@naswii.@' by" \ + " '@nas.@'.\n" \ + "@DOMAIN@ replace HTTPS requests by HTTP and the domain" \ + " '@nintendowifi.net@' by a domain specified by option {--domain}.\n" \ + "@SAKE0@ same as DOMAIN, but don't replace URLs with SAKE domain.\n" \ + "@SAKE1@ same as SAKE0, but replace HTTPS of SAKE domains." ) + +#:def_opt( "DOMAIN", "domain", "GP", \ + "dom", \ + "Define a domain for option {--https}. The default is '@wiimmfi.de@'." ) + +#:def_opt( "WIIMMFI", "wiimmfi", "G", \ + "", \ + "Patch the files for the new custom server. It is a short cut for" \ + " {--https domain --domain wiimmfi.de}." ) + +#:def_opt( "WC24", "wc24", "G", \ + "", \ + "Exclude 2 strings of file StaticR.rel from --wiimmfi and --domain" \ + " patch to enable WC24 support." ) + +#:def_opt( "PB_MODE", "pb-mode|pbmode", "GP", \ + "mode", \ + "Mode for the patched-by string: @OFF@ disables modifications. @RESET@" \ + " resets the string to stars. @WIIMMFI@ defines it only, if patched" \ + " for Wiimmfi. @AUTO@ (default) defines it only, if patched. @ALWAYS@" \ + " defines it always." ) + +#:def_opt( "VS", "vs", "GP", \ + "char", \ + "Reset (str is empty) or replace the second character of 'vs' to" \ + " create an own online world for versus races. The file StaticR.rel" \ + " will be patched at 4 places." ) + +#:def_opt( "BT", "bt", "GP", \ + "char", \ + "Reset (str is empty) or replace the second character of 'bt' to" \ + " create an own online world for battles. The file StaticR.rel will be" \ + " patched at 4 places." ) + +#:def_opt( "CANNON", "cannon", "GP", \ + "idx,p1,p2,p3,p4", \ + "Patch parameters for cannon with index IDX (0..2). Parameters P1..P4" \ + " are floating point numbers. Repeat this option for each cannon, that" \ + " should be modified." ) + +#:def_opt( "MENO", "meno", "G", \ + "", \ + "Patch some 'MenuSingle' and 'MenuMulti' strings of file StaticR.rel" \ + " for online usage to 'MenO*' (MenuOnline) to distinguish between" \ + " offline and online races." ) + +#:def_opt( "TEST", "t|test", "GM", \ + "", \ + "Run in test mode, modify nothing.\n" \ + ">>> USE THIS OPTION IF UNSURE! <<<" ) + +#:def_opt( "FORCE", "force", "GM", \ + "", \ + "The tools analyse input files and reject files with invalid data" \ + " structures. If --force is set, little repairable issues are ignored" \ + " and a warning is printed.\n" \ + "Option {--kmp force} does the same, but only for KMP files.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "REPAIR_MAGICS", "repair-magics|repairmagics", "GO", \ + "[=mode]", \ + "Control the reparing of missing or wrong magics of files and" \ + " sub-files by analysing file path and file extension. @MODE@ is one" \ + " of @OFF@ (default), @ANALYZE@ or @REPAIR@ (default, if set without" \ + " mode). @OFF@ disables this feature, @ANALYZE@ enables it for" \ + " analysis only, and @REPAIR@ repairs all magics while reading the" \ + " files. Keyword @FNAME@ can be appended (separated by a comma) to" \ + " give filename scanning the highest priority.\n" \ + ">>> THE RESULT MAY BE INVALID OUTPUT FILES! <<<" ) + +#:def_opt( "DEST", "d|dest", "CP", \ + "path", \ + "Define a destination path (directory/file). The destination @-@" \ + " means: write to standard output.\n" \ + " The path may contain escape sequences: @%Q@ is replaced by the" \ + " fully qualified source name. @%P@ and @%F@ are replaced by the" \ + " source path or by the filename. @%N@ and @%E@ are replaced by source" \ + " filename without extension or by the source extension. Finally, @%T@" \ + " is replaced by the default extension of the destination format.\n" \ + " A @'?'@ direct behind @'%'@ in @%E@ and @%T@ conversions avoid that" \ + " the same extension is used twice in row." ) + +#:def_opt( "DEST2", "D|DEST", "CP", \ + "path", \ + "Like {--dest}, but create the directory path automatically." ) + +#:def_opt( "ESC", "E|esc", "CP", \ + "char", \ + "Define an alternative escape character for destination files. The" \ + " default is '%'. For Windows batch files it is a good choice to set" \ + " @'-E$$'@." ) + +#:def_opt( "OVERWRITE", "o|overwrite", "C", \ + "", \ + "Overwrite already existing files without warning and ignore option" \ + " {--number}." ) + +#:def_opt( "NUMBER", "number|num", "C", \ + "", \ + "If a file already exist, append a number directly before the file" \ + " extension to make the filename unique. If other numbered files" \ + " already exist (ignoring case), use the maximum existing index+1." \ + " @--num@ is a short cut." ) + +#:def_opt( "REMOVE_DEST", "r|remove-dest|removedest|rm-dest|rmdest|remove", "C", \ + "", \ + "Remove already existing files before creating it. If set," \ + " {--overwrite} is ignored. @--rm-dest@ is a short cut." ) + +#:def_opt( "UPDATE", "u|update", "C", \ + "", \ + "Update only existing files and don't create new files. If set," \ + " {--overwrite} and {--remove-dest} are ignored." ) + +#:def_opt( "PRESERVE", "p|preserve", "C", \ + "", \ + "Preserve file times (atime+mtime) while converting or copying files." ) + +#:def_opt( "IGNORE", "i|ignore", "C", \ + "", \ + "Ignore non existing source files without warning." ) + +#:def_opt( "CLEAN_DOL", "clean-dol|cleandol", "G", \ + "", \ + "Remove additional DOL sections, reset entry point and VBI address," \ + " and try to restore other patches known by SZS tools. This is done" \ + " after loading a DOL and before analysing and patching to allow" \ + " re-patching." ) + +#:def_opt( "ADD_CTCODE", "add-ctcode|addctcode", "C", \ + "", \ + "If source is an original main.dol file of MKWii, then append 2" \ + " sections (usually T2 and D8) with @CT-CODE@. The CTCODE is inserted" \ + " after sections defined by {--add-section} and then only, if there" \ + " are no address collisions.\n" \ + " If {--ct-dir} is used, then files named @rmc[ejp]/boot_code.bin@" \ + " and @boot_code.bin@ for T2, and @rmc[ejp]/boot_data.bin@ and" \ + " @boot_data.bin@ for D8 are searched." ) + +#:def_opt( "CT_DIR", "ct-dir|ctdir", "GP", \ + "dir", \ + "Define a search directory for CT-CODE parts and use the internal" \ + " copies only, if no valid file is found. Each file is searched" \ + " without and then with extension '.bz2'. For both cases bzip2 files" \ + " are detected and decompressed. Only files with correct file type are" \ + " accepted.\n" \ + " Use this option multiple times to define more than one search" \ + " directory." ) + +#:def_opt( "MOVE_D8", "move-d8|moved8", "C", \ + "", \ + "Move the @CT-CODE@ section D8 to another place at the middle of" \ + " binary. It is done automatically, if the original place overlaps" \ + " other existing sections." ) + +#:def_opt( "ADD_SECTION", "add-section|addsection", "CP", \ + "file", \ + "Each option call adds the filename to an internal list. Each file" \ + " must be of type GCT, GCH or WCH. All GCT files together are combined" \ + " with an internal code handler to one section (like a GCH file). GCH" \ + " and WCH define sections and patches. The defined sections are added" \ + " and VBI hooks are optionally initialized. Use option {--force} to" \ + " allow overlapped sections. Use option {--verbose} up to 2 times for" \ + " a log.\n" \ + " If a single file is not found and the filename (not path) contains" \ + " at least 1 '@@' character, then all '@@' characters of it are" \ + " replaced by the region code (one of P,E,J,K) for a second try.\n" \ + " The intention of this option is to add a Gecko Code Handler and" \ + " cheat codes to a main.dol for automatic execution." ) + +#:def_opt( "GCT_MOVE", "gct-move|gctmove", "CO", \ + "[=mode]", \ + "If a section is of type gecko code handler + cheat code, then make" \ + " the heap smaller and move the cheat codes before the FST section.\n" \ + " Parameter MODE is optional. If set, OFF disables and ON enables" \ + " this feature. For mode AUTO (default) this feature is enabled, if" \ + " the cheat codes are too large or for internal reasons." ) + +#:def_opt( "GCT_ADDR", "gct-addr|gctaddr", "CP", \ + "address", \ + "If {--gct-move} is active, a text section is created to manage the" \ + " code handling. This section is only needed at start and is placed by" \ + " default at address 0c802c0000 (good address for all 4 regions) as" \ + " part of BSS. This option changes the start address of the section." \ + " If address is @0@ or invalid, then unused BSS space is searched." ) + +#:def_opt( "GCT_SPACE", "gct-space|gctspace", "CP", \ + "size", \ + "Define the minimal space for the code part of {--gct-move}. This" \ + " value is ignored, if it is smaller than the size of the cheat codes." \ + " This option was only implemented to test the memory allocation by" \ + " {--gct-move} with large blocks." ) + +#:def_opt( "ALLOW_USER_GCH", "allow-user-gch|allowusergch", "CO", \ + "[=mode]", \ + " This option is only relevant, if main.dol is patched for wiimmfi.de." \ + " It is dedicated to custom distribution creators for test purposes." \ + " If enabled, a user can load an own instance of Gecko Code Handler" \ + " and cheat codes and can use them online. If disabled, pachcing for" \ + " online modus is disabled if an alternative Gecko Code Handler is" \ + " detected.\n" \ + " Parameter MODE is optional. If set, OFF disables and ON enables" \ + " this feature. For mode AUTO (default) it depends on other options." ) + +#:def_opt( "SECTIONS", "sections", "C", \ + "", \ + "Print in machine readable sections and parameter lines." ) + +#:def_opt( "VADDR", "vaddr|va", "CP", \ + "RANGE", \ + "Comma separated list of virtual addresses to select the hex-dump" \ + " ranges. Each element is either @ADDRESS@ (1 byte) or @START:END@ or" \ + " @START#SIZE@." ) + +#:def_opt( "FADDR", "faddr|fa", "CP", \ + "RANGE", \ + "Comma separated list of file addresses to select the hex-dump ranges." \ + " Each element is either @ADDRESS@ (1 byte) or @START:END@ or" \ + " @START#SIZE@." ) + +#:def_opt( "SNAME", "sname|sn", "CP", \ + "LIST", \ + "Comma separated list of section names to select the hex-dump ranges." ) + +#:def_opt( "INT1", "i1", "C", \ + "", \ + "Format: Print single bytes as integer number (default)." ) + +#:def_opt( "INT2", "i2", "C", \ + "", \ + "Format: Print each 2 bytes as integer number." ) + +#:def_opt( "INT3", "i3", "C", \ + "", \ + "Format: Print each 3 bytes as integer number." ) + +#:def_opt( "INT4", "i4", "C", \ + "", \ + "Format: Print each 4 bytes as integer number." ) + +#:def_opt( "INT5", "i5", "C", \ + "", \ + "Format: Print each 5 bytes as integer number." ) + +#:def_opt( "INT6", "i6", "C", \ + "", \ + "Format: Print each 6 bytes as integer number." ) + +#:def_opt( "INT7", "i7", "C", \ + "", \ + "Format: Print each 7 bytes as integer number." ) + +#:def_opt( "INT8", "i8", "C", \ + "", \ + "Format: Print each 8 bytes as integer number." ) + +#:def_opt( "FLOAT", "float", "C", \ + "", \ + "Format: Print each 4 bytes as floating point number." ) + +#:def_opt( "DOUBLE", "double", "C", \ + "", \ + "Format: Print each 8 bytes as double floating point number." ) + +#:def_opt( "LE", "le", "C", \ + "", \ + "Use little endian for binary data." ) + +#:def_opt( "BE", "be", "C", \ + "", \ + "Use big endian for binary data (default)." ) + +#:def_opt( "ZEROS", "zeros", "C", \ + "", \ + "Print integer numbers with leading zeros." ) + +#:def_opt( "HEX", "hex", "C", \ + "", \ + "Print integer numbers as hexadecimal numbers (default)." ) + +#:def_opt( "DEC", "dec", "C", \ + "", \ + "Print integer numbers as decimal numbers." ) + +#:def_opt( "C_SYNTAX", "c-syntax|csyntax", "C", \ + "", \ + "Print output in `C' syntax." ) + +#:def_opt( "ADDR", "address", "CP", \ + "addr", \ + "Use @ADDR@ instead of null as start address for the dump." ) + +#:def_opt( "ALIGN", "align", "C", \ + "", \ + "Try to align the dump, so that the addresses (printed or not) are" \ + " multiple of bytes-per-line." ) + +#:def_opt( "TRIGGER", "trigger", "CP", \ + "num", \ + "If the number @NUM@ is found and printed, close the current line and" \ + " start with a new one. If {--align} is set too, continue the next" \ + " line with the current column. The trigger works only for integer" \ + " formats, but not for floats." ) + +#:def_opt( "NO_ADDR", "no-address|noaddress", "C", \ + "", \ + "Suppress the address column of the dump." ) + +#:def_opt( "NO_NUMBERS", "no-numbers|nonumbers", "C", \ + "", \ + "Suppress the numbers column of the dump." ) + +#:def_opt( "NO_TEXT", "no-text|notext", "C", \ + "", \ + "Suppress the text column of the dump." ) + +#:def_opt( "FORMAT", "format", "C", \ + "", \ + "Add @'#FORMAT:'@ info lines." ) + +#:def_opt( "NO_NULL", "no-null|nonull", "C", \ + "", \ + "Ignore data lines with NULL bytes only and print a summary for each" \ + " NULL block. (@'#NULL: ...'@)" ) + +#:def_opt( "NARROW", "narrow", "C", \ + "", \ + "Suppress the spaces that are usually inserted for readability." ) + +#:def_opt( "SMALL", "small", "C", \ + "", \ + "Each call will halve the number of bytes per line. Usually, 16 bytes" \ + " per lines are reduced to 8, then to 4, 2 and 1. The effective number" \ + " of bytes per line is format dependent." ) + +#:def_opt( "WIDE", "wide", "C", \ + "", \ + "Each call will round down the current bytes per line value to the" \ + " next multiple of 16 and then increment it by 16. Usually, 16 bytes" \ + " per lines are enhanced to 32, then to 48, 64, 80 and so on. The" \ + " effective number of bytes per line is format dependent." ) + +#:def_opt( "BYTES", "bytes", "CP", \ + "num", \ + "Print up to @NUM@ bytes per line instead of 16. The maximum allowed" \ + " value is 600. The effective number of bytes per line is format" \ + " dependent (multiple of format size)." ) + +#:def_cmd_opt( "VERSION", "BRIEF", \ + "", \ + "Print only the version number, revision and system. If set twice," \ + " print only the version number." ) + +#:def_cmd_opt( "VERSION", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "VERSION", "LONG", \ + "", \ + "Print in long format. Ignored if option {--sections} is set." ) + +#:def_cmd_opt( "HELP", "WIDTH", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "NO_COLORS", \ + "", \ + "" ) + +#:def_cmd_opt( "COLORS", "LONG", \ + "", \ + "Print also a list of all colored text types. If set twice, include" \ + " alternative names. If set three times, include raw color names." ) + +#:def_cmd_opt( "COLORS", "BRIEF", \ + "", \ + "Ignore option --long and print a single status line about current" \ + " color settings." ) + +#:def_cmd_opt( "ERROR", "SECTIONS", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ERROR", "LONG", \ + "", \ + "Print message text instead of message name." ) + +#:def_cmd_opt( "ERROR", "BRIEF", \ + "", \ + "Print message number instead of message name." ) + +#:def_cmd_opt( "FILETYPE", "LONG", \ + "", \ + "Print an additional column with the decompressed file type and" \ + " version. If entered twice, a validation status column is added." ) + +#:def_cmd_opt( "FILETYPE", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILEATTRIB", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "RAWDUMP", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "RAWDUMP", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "DUMP", "LONG", \ + "", \ + "For DOL files: If set, a second table with the image map is printed." \ + " If set twice, a third table with delta values is printed too." ) + +#:def_cmd_opt( "HEXDUMP", "VADDR", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "FADDR", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "SNAME", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "LOGGING", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT1", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT2", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT3", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT4", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT5", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT6", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT7", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "INT8", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "FLOAT", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "DOUBLE", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "LE", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "BE", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "ZEROS", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "HEX", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "DEC", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "C_SYNTAX", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "ADDR", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "ALIGN", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "TRIGGER", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "NO_ADDR", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "NO_NUMBERS", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "NO_TEXT", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "FORMAT", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "NO_NULL", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "NARROW", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "SMALL", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "WIDE", \ + "", \ + "" ) + +#:def_cmd_opt( "HEXDUMP", "BYTES", \ + "", \ + "" ) + +#:def_cmd_opt( "HTTPS", "LONG", \ + "", \ + " Print output a C source" ) + +#:def_cmd_opt( "HTTPS", "VERBOSE", \ + "", \ + " Print hex dump if an entry differs." ) + +#:def_cmd_opt( "EXTRACT", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "EXTRACT", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "EXTRACT", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "LONG", \ + "", \ + "Add columns for music slot and short name." ) + +#:def_cmd_opt( "TRACKS", "DE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "ARENAS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "VS_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "BT_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "ALL_RANKS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "HTTPS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "DOMAIN", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "WIIMMFI", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "PB_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "WC24", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "VS", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "BT", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "CANNON", \ + "", \ + "" ) + +#:def_cmd_opt( "TRACKS", "MENO", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "NO_HEADER", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "BRIEF", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "LONG", \ + "", \ + "Add columns for music slot and short name." ) + +#:def_cmd_opt( "ARENAS", "DE", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "ARENAS", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "VS_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "BT_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "ALL_RANKS", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "HTTPS", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "DOMAIN", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "WIIMMFI", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "PB_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "WC24", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "VS", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "BT", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "CANNON", \ + "", \ + "" ) + +#:def_cmd_opt( "ARENAS", "MENO", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "ARENAS", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "VS_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "BT_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "ALL_RANKS", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "HTTPS", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "DOMAIN", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "WIIMMFI", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "PB_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "WC24", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "VS", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "BT", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "CANNON", \ + "", \ + "" ) + +#:def_cmd_opt( "FILES", "MENO", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "ARENAS", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "VS_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "BT_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "ALL_RANKS", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "HTTPS", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "DOMAIN", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "WIIMMFI", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "PB_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "WC24", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "VS", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "BT", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "CANNON", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "MENO", \ + "", \ + "" ) + +#:def_cmd_opt( "FIND", "LONG", \ + "", \ + "Add a user friendly track or arena name at the end of each line. If" \ + " set twice, add also the file names of the track and sound files" \ + " before the track name. If set three times, add the slot index and" \ + " the internal index (both zero based) before the file names." ) + +#:def_cmd_opt( "FIND", "DE", \ + "", \ + "" ) + +#:def_cmd_opt( "POINTS", "POINTS", \ + "", \ + "" ) + +#:def_cmd_opt( "POINTS", "BRIEF", \ + "", \ + "Print the table as comma separated lists in C notation." ) + +#:def_cmd_opt( "POINTS", "LONG", \ + "", \ + "Append a string in {--points} format that defines the table. If set" \ + " twice, append up to 3 strings in different modes." ) + +#:def_cmd_opt( "ANALYZE", "CLEAN_DOL", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "ARENAS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "VS_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "BT_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "ALL_RANKS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "HTTPS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "DOMAIN", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "WIIMMFI", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "PB_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "WC24", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "VS", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "BT", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "CANNON", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "MENO", \ + "", \ + "" ) + +#:def_cmd_opt( "ANALYZE", "LONG", \ + "", \ + "Print different lists if bugs found. If set twice, print the lists" \ + " always." ) + +#:def_cmd_opt( "PATCH", "DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DEST2", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ESC", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "OVERWRITE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "NUMBER", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "REMOVE_DEST", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "UPDATE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PRESERVE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "IGNORE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "CLEAN_DOL", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "TRACKS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ARENAS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "VS_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "BT_REGION", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ALL_RANKS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "HTTPS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "DOMAIN", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "WIIMMFI", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "PB_MODE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "WC24", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "VS", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "BT", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "CANNON", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "MENO", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ADD_CTCODE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "CT_DIR", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "MOVE_D8", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ADD_SECTION", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "GCT_MOVE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "GCT_ADDR", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "GCT_SPACE", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "ALLOW_USER_GCH", \ + "", \ + "" ) + +#:def_cmd_opt( "PATCH", "FORCE", \ + "", \ + "Accept overlapped sections.\n" \ + ">>> THE RESULT IS MOST LIKELY INVALID! <<<" ) + + +## +############################################################################### +############### END ############### +############################################################################### + diff --git a/project/src/ui/ui.h b/project/src/ui/ui.h new file mode 100644 index 0000000..8f43684 --- /dev/null +++ b/project/src/ui/ui.h @@ -0,0 +1,50 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#ifndef SZS_UI_H +#define SZS_UI_H + +#define OPT_OLD_NEW 1 // 0 | 1 -> enable options --OLD --NEW +#include "dclib-ui.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#endif // SZS_UI_H + diff --git a/project/src/wbmgt.c b/project/src/wbmgt.c new file mode 100644 index 0000000..3750bce --- /dev/null +++ b/project/src/wbmgt.c @@ -0,0 +1,984 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include + +#include "lib-xbmg.h" +#include "lib-szs.h" +#include "lib-mkw.h" +#include "dclib-utf8.h" +#include "dclib-regex.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wbmgt.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// functions not neded by wbmgt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if !SZS_WRAPPER + bool DefineIntVar ( VarMap_t * vm, ccp varname, int value ) { return false; } +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WBMGT_SHORT ": " WBMGT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wbmgt,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wbmgt,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WBMGT_SHORT "\n" + "name=" WBMGT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WBMGT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" old/new: %16x = %12d\n",opt_new,opt_new); + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + + printf(" inf-size: %16x = %12d\n",opt_bmg_inf_size,opt_bmg_inf_size); + printf(" bmg-mid: %16d = %12s\n",opt_bmg_mid,GetKeywordOffAutoOn(opt_bmg_mid)); + + char attrib[BMG_ATTRIB_BUF_SIZE]; + if (opt_bmg_force_attrib) + { + PrintAttribBMG(attrib,sizeof(attrib),bmg_force_attrib,BMG_ATTRIB_SIZE,0,true); + printf(" force-attrib: %s\n",attrib); + } + if (opt_bmg_def_attrib) + { + PrintAttribBMG(attrib,sizeof(attrib),bmg_def_attrib,BMG_ATTRIB_SIZE,0,true); + printf(" def-attrib: %s\n",attrib); + } + + printf(" no-attrib: %16x = %12d\n",opt_bmg_no_attrib,opt_bmg_no_attrib); + printf(" x-escapes: %16x = %12d\n",opt_bmg_x_escapes,opt_bmg_x_escapes); + printf(" old-escapes: %16x = %12d\n",opt_bmg_old_escapes,opt_bmg_old_escapes); + printf(" bmg-color: %16x = %12d\n",opt_bmg_colors,opt_bmg_colors); + printf(" bmg-inline: %16x = %12d\n",opt_bmg_inline_attrib,opt_bmg_inline_attrib); + + ListPatchingListBMG(stdout,2,"patch-bmg list"); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 0 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command points /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_points() +{ + if (n_param) + { + ERROR0(ERR_WARNING, + "%u parameter%s ignored.\n", + n_param, n_param == 1 ? "" : "s" ); + n_param = 0; + } + + const MkwPointInfo_t *mpi = GetMkwPointInfo(MkwPointsTab); + printf("\nVersus points [%s]:\n",mpi->info); + PrintMkwPoints( stdout, 2, MkwPointsTab, brief_count ? 2 : 1, 0, long_count ); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command regexp /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_regexp() +{ + if ( n_param < 2 ) + return ERROR0(ERR_SYNTAX,"At least 2 parameters needed: REGEX STRING...\n"); + + Regex_t re; + enumError err = ScanRegex(&re,true,first_param->arg); + if ( err || !re.valid ) + return ERROR0(err,"Invalid regex: %s\n",first_param->arg); + + struct { FastBuf_t b; char space[500]; } res; + InitializeFastBuf(&res,sizeof(res)); + + ParamList_t *param; + for ( param = first_param->next; param; param = param->next ) + { + int stat = ReplaceRegex( &re, &res.b, param->arg, -1 ); + printf("\n%s\n -> [%d] %s\n", + param->arg, stat, GetFastBufString(&res.b) ); + } + + putchar('\n'); + ResetFastBuf(&res.b); + ResetRegex(&re); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command extract /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_extract() +{ + if ( n_param != 2 ) + return ERROR0(ERR_SYNTAX,"Excat 2 parameters expected:" + " Section name and file\n"); + + ccp sname = first_param->arg; + const uint slen = strlen(sname); + if ( slen < 1 || slen > 4 ) + return ERROR0(ERR_SYNTAX, + "Section name must consist of 1 to 4 characters: %s",sname); + + ParamList_t *param = first_param->next; + NORMALIZE_FILENAME_PARAM(param); + + raw_data_t raw; + enumError err = LoadRawData(&raw,true,param->arg,0,opt_ignore>0,0); + if (err) + return err; + + if ( raw.fform != FF_BMG ) + return ERROR0(ERR_SYNTAX,"Not a binary BMG file: %s",raw.fname); + + + //--- search sections + + const bmg_sect_list_t *sl = ScanSectionsBMG(raw.data,raw.data_size); + #if 0 + { + const bmg_sect_info_t *si; + + si = SearchSectionBMG(sl,sname,false,false); + fprintf(stderr,"SEARCH »%s« : %s\n", + sname, si ? PrintID(si->sect->magic,4,0) : "-" ); + + si = SearchSectionBMG(sl,sname,true,false); + fprintf(stderr,"SEARCH abbrev »%s« : %s\n", + sname, si ? PrintID(si->sect->magic,4,0) : "-" ); + + si = SearchSectionBMG(sl,sname,false,true); + fprintf(stderr,"SEARCH icase »%s« : %s\n", + sname, si ? PrintID(si->sect->magic,4,0) : "-" ); + + si = SearchSectionBMG(sl,sname,true,true); + fprintf(stderr,"SEARCH abbrev icase »%s« : %s\n", + sname, si ? PrintID(si->sect->magic,4,0) : "-" ); + } + #endif + + err = ERR_NOT_EXISTS; + const bmg_sect_info_t *si = SearchSectionBMG(sl,sname,true,true); + + if (si) + { + const u8 *data = raw.data + si->offset; + uint size = si->real_size; + + if (!print_header) + { + const uint head_size = si->head_size + ? si->head_size : sizeof(bmg_section_t); + if ( size >= head_size ) + { + data += head_size; + size -= head_size; + } + } + + PRINT("FOUND: %.4s, off: %u 0x%x, size: %u 0x%x [h=%d]\n", + si->sect->magic, si->offset, si->offset, + size, size, print_header ); + + fwrite(data,1,size,stdout); + err = ERR_OK; + } + + FREE((void*)sl); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command sections /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_sections() +{ + raw_data_t raw; + InitializeRawData(&raw); + + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if (err) + return err; + + if ( raw.fform != FF_BMG ) + { + ERROR0(ERR_SYNTAX,"Not a binary BMG file: %s",raw.fname); + if ( max_err < ERR_SYNTAX ) + max_err = ERR_SYNTAX; + continue; + } + + const bmg_sect_list_t *sl = ScanSectionsBMG(raw.data,raw.data_size); + if (!sl) + { + ERROR0(ERR_INVALID_DATA,"Can't scan binary BMG file: %s",raw.fname); + if ( max_err < ERR_INVALID_DATA ) + max_err = ERR_INVALID_DATA; + continue; + } + + uint max_info_len = 4; + const bmg_sect_info_t *si; + for ( si = sl->info; si->sect; si++ ) + { + if (si->info) + { + const uint ilen = strlen(si->info); + if ( max_info_len < ilen ) + max_info_len = ilen; + } + } + + const uint seplen = 3*(56+max_info_len); + printf( "\n" + "%sFile: %s\n" + "%sSize: 0x%x = %u bytes, %u sections, encoding #%u (%s)\n" + "%s%.*s\n" + "%s offset size sect cut head n(elem) n(e)\n" + "%s hex hex size size magic size *size cut info\n" + "%s%.*s%s\n" + , + colout->heading, param->arg, + colout->heading, sl->source_size, sl->source_size, sl->n_sections, + sl->header->encoding, + GetEncodingNameBMG(sl->header->encoding,"?"), + colout->heading, seplen, ThinLine300_3, + colout->heading, + colout->heading, + colout->heading, seplen, ThinLine300_3, + colout->reset ); + + for ( si = sl->info; si->sect; si++ ) + { + printf("%8x %7x %7u", + si->offset, si->real_size, si->real_size ); + + if ( si->real_size < si->sect_size ) + printf(" %s%3u%s ", colout->warn, + si->sect_size - si->real_size, colout->reset ); + else + fputs(" - ",stdout); + + fputs(PrintID(si->sect->magic,4,0),stdout); + + if ( si->head_size ) + printf(" %4u", si->head_size ); + else + printf(" %s?%s", colout->warn, colout->reset ); + + if (si->elem_size) + { + printf(" %5u*%2u", si->n_elem_head, si->elem_size); + if ( si->n_elem_head > si->n_elem ) + printf(" %s%4u*%s",colout->warn,si->n_elem,colout->reset); + else + fputs(" - ",stdout); + } + else + fputs(" - - ",stdout); + + printf(" %s%s\n", si->info, si->is_empty ? ", empty" : "" ); + } + + printf("%s%.*s%s\n", colout->heading, seplen, ThinLine300_3, colout->reset ); + FREE((void*)sl); + } + + ResetRawData(&raw); + putchar('\n'); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_list() +{ + const uint max_width = GetTermWidth(80,40) - 9; + enumError err = SetupPatchingListBMG(); + if ( err > ERR_WARNING ) + return err; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + bmg_t bmg; + enumError err = LoadXBMG(&bmg,true,param->arg,true,opt_ignore>0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + err = UsePatchingListBMG(&bmg); + if ( err > ERR_WARNING ) + return err; + + printf("\nList [N=%u] %s:%s\n", + bmg.item_used, GetNameFF_BMG(&bmg), bmg.fname ); + + bmg_item_t * bi = bmg.item; + bmg_item_t * bi_end = bi + bmg.item_used; + for ( ; bi < bi_end; bi++ ) + { + if ( memcmp(bmg.attrib,bi->attrib,bmg.attrib_used) ) + { + char buf[BMG_ATTRIB_BUF_SIZE]; + PrintAttribBMG(buf,sizeof(buf),bi->attrib,bmg.attrib_used,0,true); + printf("%5x ~ %s\n",bi->mid,buf); + if ( bi->text == bmg_null_entry ) + continue; + } + + if ( bi->text == bmg_null_entry ) + printf("%5x /\n",bi->mid); + else if ( bi->text ) + { + PrintString16BMG(iobuf,sizeof(iobuf),bi->text,bi->len, + BMG_UTF8_MAX,0,opt_bmg_colors); + printf("%5x = %.*s\n", bi->mid, max_width, iobuf ); + } + } + ResetBMG(&bmg); + } + + ResetPatchingListBMG(); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command slots /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_slots() +{ + enumError err = SetupPatchingListBMG(); + if ( err > ERR_WARNING ) + return err; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + bmg_t bmg; + enumError err = LoadXBMG(&bmg,true,param->arg,true,opt_ignore>0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + err = UsePatchingListBMG(&bmg); + if ( err > ERR_WARNING ) + return err; + + bmg_create_t bc; + InitializeCreateBMG(&bc,&bmg); + bmg_item_t *bi; + for ( bi = GetFirstBI(&bc); bi; bi = GetNextBI(&bc) ) + ; + + const uint seplen = 3*60; + printf( "\n" + "%sFile: %s:%s\n" + "%s%.*s\n" + "%sslot/hex mid/hex delta attributes\n" + "%s%.*s%s\n" + , + colout->heading, GetNameFF_BMG(&bmg), param->arg, + colout->heading, seplen, ThinLine300_3, + colout->heading, + colout->heading, seplen, ThinLine300_3, + colout->reset ); + + int slot; + u32 *pmid = (u32*)bc.mid.buf; + bmg_inf_item_t *inf = (bmg_inf_item_t*)bc.inf.buf; + char buf[200]; + for ( slot = 0; slot < bc.n_msg; slot++ ) + { + const u32 mid = ntohl(*pmid++); + PrintAttribBMG(buf,sizeof(buf),inf->attrib,bmg.attrib_used,0,true); + printf("%7x %9x ", slot, mid ); + if ( mid == 0xffff ) + fputs(" -",stdout); + else + printf("%+7d",(int)mid-slot); + printf(" %s\n",buf); + + inf = (bmg_inf_item_t*)( (u8*)inf + bmg.inf_size ); + } + printf("%s%.*s%s\n", colout->heading, seplen, ThinLine300_3, colout->reset ); + + ResetCreateBMG(&bc); + ResetBMG(&bmg); + } + + putchar('\n'); + ResetPatchingListBMG(); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command diff /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError diff_files ( ccp fname1, ccp fname2 ) +{ + if ( verbose > 0 ) + printf("DIFF %s : %s\n", fname1, fname2 ); + + //--- setup bmg structures + + bmg_t bmg1; + enumError err = LoadXBMG(&bmg1,true,fname1,true,false); + if ( err > ERR_WARNING ) + return err; + err = UsePatchingListBMG(&bmg1); + if ( err > ERR_WARNING ) + return err; + + bmg_t bmg2; + err = LoadXBMG(&bmg2,true,fname2,true,false); + if ( err > ERR_WARNING ) + return err; + err = UsePatchingListBMG(&bmg2); + if ( err > ERR_WARNING ) + return err; + + //--- diff + + err = DiffBMG( &bmg1, &bmg2, verbose < 0, GetTermWidth(80,40)-1 ); + + if ( verbose >= -1 && err == ERR_DIFFER ) + printf("Content differ: %s : %s\n", bmg1.fname, bmg2.fname ); + else if ( verbose >= 0 && err == ERR_OK ) + printf("Content identical: %s : %s\n", bmg1.fname, bmg2.fname ); + + //--- reset bmg structures + + ResetBMG(&bmg1); + ResetBMG(&bmg2); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_diff_dest() +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,"%F",0,false); + const enumError err = diff_files(param->arg,dest); + if ( max_err < err ) + max_err = err; + if ( err > ERR_WARNING || err && verbose < -1 ) + break; + } + ResetPatchingListBMG(); + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_diff() +{ + if (opt_dest) + return cmd_diff_dest(); + + if ( n_param != 2 ) + return ERROR0(ERR_SYNTAX,"Exact 2 sources expected if --dest is not set.\n"); + + enumError err = SetupPatchingListBMG(); + if ( err > ERR_WARNING ) + return err; + + ASSERT(first_param); + ASSERT(first_param->next); + err = diff_files(first_param->arg,first_param->next->arg); + ResetPatchingListBMG(); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command patch/encode/decode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_patch ( int cmd_id, ccp cmd_name, ccp def_path ) +{ + CheckOptDest(def_path,false); + enumError err = SetupPatchingListBMG(); + if ( err > ERR_WARNING ) + return err; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + bmg_t bmg; + enumError err = LoadXBMG(&bmg,true,param->arg,cmd_id!=CMD_PATCH,opt_ignore>0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + const file_format_t dest_ff + = cmd_id == CMD_ENCODE || cmd_id == CMD_PATCH && !bmg.is_text_src + ? FF_BMG + : FF_BMG_TXT; + + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(dest_ff,0),false); + + if ( verbose >= 0 || testmode ) + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", cmd_name, + GetNameFF_BMG(&bmg), bmg.fname, + GetNameFF(dest_ff,0), dest ); + + err = UsePatchingListBMG(&bmg); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = dest_ff == FF_BMG + ? SaveRawXBMG(&bmg,dest,opt_preserve) + : SaveTextXBMG(&bmg,dest,opt_preserve); + if ( err > ERR_WARNING ) + return err; + } + ResetBMG(&bmg); + } + + ResetPatchingListBMG(); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + ccp filter_bmg = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wbmgt,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: break; // [[2do]] err += ScanOptConst(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + case GO_ALIGN: err += ScanOptAlign(optarg); break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + case GO_POINTS: err += ScanOptMkwPoints(optarg,false); break; + case GO_PATCH_BMG: err += ScanOptPatchMessage(optarg); break; + case GO_MACRO_BMG: err += ScanOptMacroBMG(optarg); break; + case GO_FILTER_BMG: filter_bmg = optarg; break; + + case GO_LONG: long_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + + case GO_BMG_ENCODING: err += ScanOptBmgEncoding(optarg); break; + case GO_BMG_INF_SIZE: err += ScanOptBmgInfSize(optarg,true); break; + case GO_BMG_MID: err += ScanOptBmgMid(optarg); break; + case GO_FORCE_ATTRIB: err += ScanOptForceAttrib(optarg); break; + case GO_DEF_ATTRIB: err += ScanOptDefAttrib(optarg); break; + case GO_NO_ATTRIB: opt_bmg_no_attrib = true; break; + case GO_X_ESCAPES: opt_bmg_x_escapes = true; break; + case GO_OLD_ESCAPES: opt_bmg_old_escapes = true; break; + case GO_SINGLE_LINE: opt_bmg_single_line++; break; + case GO_NO_BMG_COLORS: opt_bmg_colors = 0; break; + case GO_BMG_COLORS: opt_bmg_colors = 2; break; + case GO_NO_BMG_INLINE: opt_bmg_inline_attrib = false; break; + case GO_EXPORT: export_count++; break; + case GO_SECTIONS: print_sections++; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wbmgt,TRACE_FILE,11); + #endif + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + SetupBMG(filter_bmg); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wbmgt,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wbmgt); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wbmgt,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_POINTS: err = cmd_points(); break; + case CMD_REGEXP: err = cmd_regexp(); break; + + case CMD_EXTRACT: err = cmd_extract(); break; + case CMD_SECTIONS: err = cmd_sections(); break; + case CMD_LIST: err = cmd_list(); break; + case CMD_SLOTS: err = cmd_slots(); break; + case CMD_DIFF: err = cmd_diff(); break; + + case CMD_CAT: err = cmd_bmg_cat(false); break; + case CMD_MIX: err = cmd_bmg_cat(true); break; + case CMD_IDENTIFIER: err = cmd_bmg_identifier(); break; + case CMD_DECODE: err = cmd_patch(cmd_ct->id,"DECODE","\1P/\1N.txt"); break; + case CMD_ENCODE: err = cmd_patch(cmd_ct->id,"ENCODE","\1P/\1N\1?T"); break; + case CMD_PATCH: err = cmd_patch(cmd_ct->id,"PATCH", "\1P/\1F"); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wbmgt ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WBMGT_SHORT); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WBMGT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WBMGT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wctct.c b/project/src/wctct.c new file mode 100644 index 0000000..cf34003 --- /dev/null +++ b/project/src/wctct.c @@ -0,0 +1,992 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-ctcode.h" +#include "lib-szs.h" +#include "lib-xbmg.h" +#include "lib-bzip2.h" + +#include "ui.h" // [[dclib]] wrapper +#include "ui-wctct.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WCTCT_SHORT ": " WCTCT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// help /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wctct,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wctct,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WCTCT_SHORT "\n" + "name=" WCTCT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WCTCT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + + printf(" output-mode: %16x = %12d\n",output_mode.mode,output_mode.mode); + printf(" hex: %16x = %12d\n",output_mode.hex,output_mode.hex); + printf(" print-header:%16x = %12d\n",print_header,print_header); + printf(" brief: %16x = %12d\n",brief_count,brief_count); + printf(" export: %16x = %12d\n",export_count,export_count); + printf(" CT/LE modes: %s\n",GetCtLeInfo()); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 0 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 1 + + uint tr; + for ( tr = 0; tr < MKW_N_TRACKS + MKW_N_ARENAS; tr++ ) + { + const TrackInfo_t *info = GetTrackInfo(tr); + if (info) + printf(" %2u = %02x : 0x%02x\n",tr,tr,info->music_id); + //printf(" %2u = %02x : %s\n",tr,tr,PrintMusicID(tr,false)); + } + return ERR_OK; + + #elif 1 + + uint mi; + for ( mi = 0x75; mi <= 0xc9; mi += 2) + { + int ti = MusicID2TrackId(mi,-1,-1); + if ( ti > 0 ) + { + const TrackInfo_t *info = GetTrackInfo(ti); + printf("0x%02x = 0x%02x %2u %2u %s\n", + mi, info->music_id, + info->def_index, info->def_slot, info->sound_n_fname ); + } + } + return ERR_OK; + + #elif 1 + + uint ti; + for ( ti = 0; ti < MKW_N_TRACKS; ti++ ) + { + // find music slot + int mi, mslot = -1; + for ( mi = 0x75; mi <= 0xc9; mi += 2) + if ( MusicID2TrackId(mi,-1,-1) == ti ) + { + mslot = mi; + break; + } + + const TrackInfo_t *info = track_info + ti; + printf("\t{ %2u, \"%s\", \"T%02u\", 0x%02x },\n", + ti, info->name_en, info->def_slot, mslot ); + } + return ERR_OK; + + #elif 0 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetupVarsCTCODE(); + return ExportHelper("ctcode"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command dump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_dump() +{ + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + fprintf(stdlog,"\nDUMP %s:%s\n", GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + + ctcode_t ctcode; + err = ScanRawDataCTCODE(&ctcode,CTM_AUTO,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + DumpCTCODE(stdout,2,&ctcode); + ResetCTCODE(&ctcode); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// commands cat + bmg + tracks /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat ( uint cmd ) // cmd_bmg() cmd_tracks() +{ + stdlog = stderr; + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + ctcode_t ctcode; + err = ScanRawDataCTCODE(&ctcode,CTM_AUTO,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + switch (cmd) + { + case CMD_BMG: + err = SaveMessageCTCODE(&ctcode,"-",false); + break; + + case CMD_TRACKS: + err = SaveTrackListByOptCTCODE(&ctcode,"-",0); + break; + + default: + err = SaveTextCTCODE(&ctcode,"-",false,&output_mode); + break; + } + if ( err > ERR_WARNING ) + return err; + } + ResetCTCODE(&ctcode); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command decode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_decode() +{ + static ccp def_path = "\1P/\1F\1?T"; + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(FF_CT_TEXT,0),false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sDECODE %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(FF_CT_TEXT,0), dest ); + fflush(stdlog); + } + + ctcode_t ctcode; + err = ScanRawDataCTCODE(&ctcode,CTM_AUTO,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = SaveTextCTCODE(&ctcode,dest,opt_preserve,&output_mode); + if ( err > ERR_WARNING ) + return err; + } + ResetCTCODE(&ctcode); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command create /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_create() +{ + if ( n_param < 1 ) + { + ERROR0(ERR_SYNTAX,"Missing sub command for CREATE.\n"); + hint_exit(ERR_SYNTAX); + } + + + //----- find lang prefix + + static const KeywordTab_t reg_tab[] = + { + { CTL_EU, "EU", "PAL", 0 }, + { CTL_EU, "P", "RMCP", 0 }, + + { CTL_US, "US", "USA", 0 }, + { CTL_US, "E", "RMCE", 0 }, + + { CTL_JP, "JP", "JAP", 0 }, + { CTL_JP, "J", "RMCJ", 0 }, + + { CTL_KO, "KO", "KOR", 0 }, + { CTL_KO, "K", "RMCK", 0 }, + + {0,0,0,0}, + }; + + ParamList_t *param = first_param; + ccp cmd_name = param->arg; + param = param->next; + + char prefix[10]; + StringCopyS(prefix,sizeof(prefix),cmd_name); + char *found = strchr(prefix,'-'); + if (found) + *found = 0; + found = strchr(prefix,'.'); + if (found) + *found = 0; + + int cmd_stat; + const KeywordTab_t *cmd = ScanKeyword(&cmd_stat,prefix,reg_tab); + int len; + for ( len = 4; len >=2 && !cmd; len-- ) + { + prefix[len] = 0; + cmd = ScanKeyword(&cmd_stat,prefix,reg_tab); + } + + ctlang_save_t lang = CTL_ANY; + if (cmd) + { + lang = cmd->id; + cmd_name += strlen(prefix); + if ( *cmd_name == '-' || *cmd_name == '.' ) + cmd_name++; + } + noPRINT("PREFIX: %u |%s|%s|\n",lang,prefix,cmd_name); + + + //----- find sub command + + static const KeywordTab_t tab[] = + { + { CTS_CT0CODE, "BAD0CODE", "BOOT_CODE", 1 }, + { CTS_CT0DATA, "BAD0DATA", "BOOT_DATA", 1 }, + { CTS_CT1CODE, "BAD1CODE", "CODE", 1 }, + { CTS_CT1DATA, "BAD1DATA", "DATA", 1 }, + + { CTS_CUP1, "CUP1", 0, 0 }, + { CTS_CRS1, "CRS1", 0, 0 }, + { CTS_MOD1, "MOD1", 0, 1 }, + { CTS_MOD2, "MOD2", 0, 1 }, + { CTS_OVR1, "OVR1", 0, 1 }, + + { CTS_TEX0, "TEX0", 0, 1 }, + { CTS_BRRES, "BRRES", 0, 1 }, + { CTS_SZS, "SZS", 0, 1 }, + + { CTS_BMG, "BMG", 0, 0 }, + { CTS_TRACKS, "TRACKS", 0, 0 }, + { CTS_LIST, "LIST", 0, 0 }, + { CTS_REF, "REF", 0, 0 }, + { CTS_FULL, "FULL", 0, 0 }, + + { 0,0,0,0 } + }; + + cmd = ScanKeyword(&cmd_stat,cmd_name,tab); + if (!cmd) + { + PrintKeywordError(tab,cmd_name,cmd_stat,0,"sub command"); + hint_exit(ERR_SYNTAX); + } + + + //--- verify language + + if (!cmd->opt) + lang = CTL_ANY; + else if ( lang == CTL_ANY ) + return ERROR0(ERR_SYNTAX, + "Missiang language code (eg. 'EU' or 'RMCP') before keyword: %s\n", + cmd_name ); + + + //--- parameter setup + + ccp def_path = 0; + file_format_t dest_ff1 = FF_UNKNOWN; + file_format_t dest_ff2 = FF_UNKNOWN; + switch(cmd->id) + { + case CTS_CT0CODE: + dest_ff2 = FF_CT0_CODE; + def_path = "\1P/\1F\1?T"; + break; + + case CTS_CT0DATA: + dest_ff2 = FF_CT0_DATA; + def_path = "\1P/\1F\1?T"; + break; + + case CTS_CT1CODE: + dest_ff2 = FF_CT1_CODE; + def_path = "\1P/\1F\1?T"; + break; + + case CTS_CT1DATA: + dest_ff2 = FF_CT1_DATA; + def_path = "\1P/\1F\1?T"; + break; + + case CTS_CUP1: + dest_ff2 = FF_CUP1; + def_path = "\1P/\1N.bin"; + break; + + case CTS_CRS1: + dest_ff2 = FF_CRS1; + def_path = "\1P/\1N.bin"; + break; + + case CTS_MOD1: + dest_ff2 = FF_MOD1; + def_path = "\1P/\1N.bin"; + break; + + case CTS_MOD2: + dest_ff2 = FF_MOD2; + def_path = "\1P/\1N.bin"; + break; + + case CTS_OVR1: + dest_ff2 = FF_OVR1; + def_path = "\1P/\1N.bin"; + break; + + case CTS_TEX0: + dest_ff2 = FF_TEX_CT; + def_path = "\1P/\1F\1?T"; + break; + + case CTS_BRRES: + dest_ff2 = FF_BRRES; + def_path = "\1P/\1N.brres"; + break; + + case CTS_SZS: + dest_ff1 = FF_YAZ0; + dest_ff2 = FF_BRRES; + def_path = "\1P/\1N.szs"; + break; + + case CTS_BMG: + dest_ff2 = FF_BMG_TXT; + def_path = "\1P/\1F\1?T"; + break; + + case CTS_TRACKS: + dest_ff2 = FF_BMG_TXT; + def_path = "\1P/\1N.list"; + break; + + case CTS_LIST: + case CTS_REF: + case CTS_FULL: + dest_ff2 = FF_CT_TEXT; + def_path = "\1P/\1F\1?T"; + break; + } + + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + + //--- file loop + + for ( ; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,FF_CT_TEXT); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(dest_ff1,dest_ff2),false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sCREATE %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(dest_ff1,dest_ff2), dest ); + fflush(stdlog); + } + + ctcode_t ctcode; + err = ScanRawDataCTCODE(&ctcode,CTM_AUTO,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = SaveCTCODE(&ctcode,dest,opt_preserve,cmd->id,lang,&output_mode); + if ( err > ERR_WARNING ) + return err; + } + ResetCTCODE(&ctcode); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command patch /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_patch() +{ + static ccp def_path = "\1P/\1F"; + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(raw.fform_file,raw.fform),false); + const bool dest_is_source = !strcmp(dest,param->arg); + + if ( verbose >= 0 || testmode ) + { + ccp ff_name = GetNameFF(raw.fform_file,raw.fform); + if (dest_is_source) + fprintf(stdlog,"%s%sPATCH %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + ff_name, dest ); + else + fprintf(stdlog,"%s%sPATCH %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + ff_name, raw.fname, + ff_name, dest ); + fflush(stdlog); + } + + ctcode_t ctcode; + err = ScanRawDataCTCODE(&ctcode,CTM_AUTO,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + PrepareSaveCTCODE(&ctcode); + if (IsCompressedFF(raw.fform_file)) + { + szs_file_t szs; +// [[fname-]] + AssignSZS(&szs,true,raw.data,raw.data_size,false,raw.fform,0); + err = SaveSZS(&szs,dest,dest_is_source||opt_overwrite,true); + } + else + err = SaveFILE( dest, 0, dest_is_source||opt_overwrite, + raw.data, raw.data_size, 0 ); + if ( err > ERR_WARNING ) + return err; + } + ResetCTCODE(&ctcode); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + InitializeOutputMode(&output_mode); + + optind = 0; + int err = 0; + ccp filter_bmg = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wctct,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_OLD_SPINY: old_spiny = true; break; + case GO_CRS1: err += ScanOptCRS1(optarg); break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: err += ScanOptConst(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_LIST: output_mode.mode = 1; break; + case GO_REF: output_mode.mode = 2; break; + case GO_FULL: output_mode.mode = 3; break; + case GO_HEX: output_mode.hex = true; break; + case GO_LOAD_BMG: err += ScanOptLoadBMG(optarg); break; + case GO_PATCH_BMG: err += ScanOptPatchMessage(optarg); break; + case GO_MACRO_BMG: err += ScanOptMacroBMG(optarg); break; + case GO_FILTER_BMG: filter_bmg = optarg; break; + case GO_PATCH_NAMES: opt_patch_names = true; break; + + case GO_CT_DIR: AppendStringField(&ct_dir_list,optarg,false); break; + case GO_CT_LOG: err += ScanOptCtLog(optarg); break; + case GO_ALLOW_SLOTS: err += ScanOptAllowSlots(optarg); break; + case GO_IMAGES: opt_image_dir = optarg; break; + case GO_ORDER_BY: opt_order_by = optarg; break; + case GO_ORDER_ALL: opt_order_all = true; break; + case GO_DYNAMIC: opt_dynamic_ctcode = true; break; + case GO_WRITE_TRACKS: opt_write_tracks = optarg; break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; + case GO_NO_HEADER: print_header = output_mode.syntax = false; break; + case GO_BRIEF: brief_count++; output_mode.cross_ref = false; break; + case GO_RAW: raw_mode = true; break; + case GO_EXPORT: + export_count++; + print_header = output_mode.syntax = false; + break; + + case GO_BMG_ENCODING: err += ScanOptBmgEncoding(optarg); break; + case GO_BMG_INF_SIZE: err += ScanOptBmgInfSize(optarg,false); break; + case GO_BMG_MID: err += ScanOptBmgMid(optarg); break; + case GO_FORCE_ATTRIB: err += ScanOptForceAttrib(optarg); break; + case GO_DEF_ATTRIB: err += ScanOptDefAttrib(optarg); break; + case GO_NO_ATTRIB: opt_bmg_no_attrib = true; break; + case GO_X_ESCAPES: opt_bmg_x_escapes = true; break; + case GO_OLD_ESCAPES: opt_bmg_old_escapes = true; break; + case GO_SINGLE_LINE: opt_bmg_single_line++; break; + case GO_NO_BMG_COLORS: opt_bmg_colors = 0; break; + case GO_BMG_COLORS: opt_bmg_colors = 2; break; + case GO_NO_BMG_INLINE: opt_bmg_inline_attrib = false; break; + case GO_NO_PARAM: print_param = false; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_NO_CHECK: opt_no_check = true; break; + case GO_SECTIONS: print_sections++; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wctct,TRACE_FILE,11); + #endif + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + SetupBMG(filter_bmg); + UsePatchingListBMG(&opt_load_bmg); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wctct,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wctct); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + extern const data_tab_t ctcode_data_tab[]; + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wctct,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_EXPORT: err = cmd_export(); break; + case CMD_RAWDUMP: err = cmd_rawdump(ctcode_data_tab); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupVarsCTCODE()); break; + case CMD_FUNCTIONS: SetupVarsCTCODE(); err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupVarsCTCODE()); break; + case CMD_FLOAT: err = cmd_float(); break; + + case CMD_DUMP: err = cmd_dump(); break; + case CMD_CAT: + case CMD_BMG: + case CMD_TRACKS: err = cmd_cat(cmd_ct->id); break; + + case CMD_DECODE: err = cmd_decode(); break; + case CMD_CREATE: err = cmd_create(); break; + case CMD_PATCH: err = cmd_patch(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wctct ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WCTCT_SHORT); + ctcode_enabled = true; + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WCTCT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WCTCT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wimgt.c b/project/src/wimgt.c new file mode 100644 index 0000000..c4126e1 --- /dev/null +++ b/project/src/wimgt.c @@ -0,0 +1,785 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include + +#include "lib-image.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wimgt.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// functions not neded by wimgt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if !SZS_WRAPPER + bool DefineIntVar ( VarMap_t * vm, ccp varname, int value ) { return false; } +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WIMGT_SHORT ": " WIMGT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wimgt,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wimgt,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WIMGT_SHORT "\n" + "name=" WIMGT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WIMGT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +/////////////////////////////////////////////////////////////////////////////// + +#define SYNTAX_ERROR syntax_error(__FUNCTION__,__FILE__,__LINE__) + +static void syntax_error ( ccp func, ccp file, uint line ) +{ + if ( current_command + && current_command->id >= 0 + && current_command->id < InfoUI_wimgt.n_cmd ) + { + const InfoCommand_t *ic = InfoUI_wimgt.cmd_info + current_command->id; + if (strchr(ic->syntax,'\n')) + { + ccp src = ic->syntax; + char *dest = iobuf; + while (*src) + if ( (*dest++ = *src++) == '\n' ) + { + *dest++ = ' '; + *dest++ = ' '; + *dest++ = ' '; + } + *dest = 0; + PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax:\n %s\n",iobuf); + } + else + PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax: %s\n",ic->syntax); + } + else + PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax Error!\n"); + exit(ERR_SYNTAX); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + printf(" cmpr-default: valid=%d, RGB565: %04x %04x, RGB: %06x %06x\n", + opt_cmpr_valid, + be16(opt_cmpr_def), be16(opt_cmpr_def+2), + RGB565_to_RGB(be16(opt_cmpr_def)), + RGB565_to_RGB(be16(opt_cmpr_def+2)) ); + printf(" n-mipmaps: %16x = %12d\n",opt_n_images-1,opt_n_images-1); + printf(" max-mipmaps: %16x = %12d\n",opt_max_images-1,opt_max_images-1); + printf(" mipmap-size: %16x = %12d\n",opt_min_mipmap_size,opt_min_mipmap_size); + printf(" mipmaps: %16x = %12d\n",opt_mipmaps,opt_mipmaps); + printf(" fast-mipmaps:%16x = %12d\n",fast_resize_enabled,fast_resize_enabled); + +#if defined(TEST) || defined(DEBUG) || HAVE_WIIMM_EXT + MipmapOptions_t mmo; + SetupMipmapOptions(&mmo); + PrintMipmapOptions(stdout,2,&mmo); +#endif + + if (patch_image_list.used) + { + printf(" image patch list:\n"); + int i; + for ( i = 0; i < patch_image_list.used; i++ ) + { + const FormatFieldItem_t *ffi = patch_image_list.list + i; + printf("\t%04x=%s\n",ffi->patch_mode,ffi->key); + } + } + + DumpTransformList(stdout,1,false); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 1 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + Image_t img; + if (!LoadPNG(&img,true,true,param->arg,0)) + { + char path[PATH_MAX]; + char *end = StringCopyS(path,sizeof(path),param->arg); + if ( end > path + 4 && !strcasecmp(end-4,".png") ) + end -= 4; + *end = 0; + + StringCopyE(end,path+sizeof(path),"-std.png"); + SavePNG(&img,true,path,0,0,false,0); + + ConvertIMG(&img,false,0,IMG_X_RGB,PAL_INVALID); + StringCopyE(end,path+sizeof(path),"-rgb.png"); + SavePNG(&img,true,path,0,0,false,0); + + ConvertIMG(&img,false,0,IMG_X_GRAY,PAL_INVALID); + StringCopyE(end,path+sizeof(path),"-gray.png"); + SavePNG(&img,true,path,0,0,false,0); + + ConvertIMG(&img,false,0,IMG_X_PAL,PAL_INVALID); + StringCopyE(end,path+sizeof(path),"-pal.png"); + SavePNG(&img,true,path,0,0,false,0); + } + ResetIMG(&img); + } + return ERR_OK; + + #else + return ERR_OK; + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_list ( int long_level ) +{ + if ( long_level > 0 ) + { + RegisterOptionByIndex(&InfoUI_wimgt,OPT_LONG,long_level,false); + long_count += long_level; + } + + if (print_header) + PrintImageHead(0,long_count); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + char buf[CHECK_FILE_SIZE]; + enumError err = LoadFILE(param->arg,0,0,buf,sizeof(buf),1,0,false); + if ( err > ERR_WARNING ) + { + if (opt_ignore) + continue; + memset(buf,0,sizeof(buf)); + } + PrintImage(buf,CHECK_FILE_SIZE,param->arg,0,0,long_count,false); + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command decode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_decode() +{ + ccp def_path = "\1P/\1F.png"; + CheckOptDest(def_path,false); + + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + Image_t img; + enumError err = LoadIMG(&img, true, param->arg, + 0, opt_mipmaps >= 0, true, opt_ignore>0 ); + if ( err > ERR_WARNING ) + return err; + if (err) + continue; + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path,0,false); + + if ( verbose >= 0 || testmode ) + fprintf(stdlog,"%s%sDECODE %s:%s -> PNG:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + PrintFormat3(img.info_fform,img.info_iform,img.info_pform), + img.path, dest ); + + TransformIMG(&img,2); + if (opt_pre_convert) + { + enumError err = ExecTransformIMG(&img); + if ( max_err < err ) + max_err = err; + } + + if (!testmode) + { + err = SavePNG(&img,true,dest,0,0,false,0); + if ( max_err < err ) + max_err = err; + } + ResetIMG(&img); + } + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command encode/convert /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_convert ( int cmd_id, ccp cmd_name, ccp def_path ) +{ + CheckOptDest(def_path,false); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + Image_t img; + enumError err = LoadIMG( &img, true, param->arg, 0, + opt_mipmaps >= 0, false, opt_ignore ); + if ( err == ERR_NOT_EXISTS || err == ERR_WARNING ) + continue; + if ( err > ERR_WARNING ) + return err; + + const file_format_t src_f = img.info_fform; + const image_format_t src_i = img.info_iform; + const palette_format_t src_p = img.info_pform; + TransformIMG(&img,-1); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path,0,false); + const file_format_t fform + = GetImageFF( img.tform_valid ? img.tform_fform : FF_INVALID, + FF_INVALID, + dest, + img.info_fform, + cmd_id == CMD_CONVERT, + FF_TEX ); + + if ( fform == FF_PNG ) + Transform2XIMG(&img); + else + Transform2InternIMG(&img); + + const bool samefile = !strcmp(param->arg,dest); + if ( !all_count + && !img.conv_count + && !img.tform_exec + && !img.patch_done + && fform == img.info_fform + && samefile ) + { + continue; + } + + if ( verbose >= 0 || testmode ) + { + image_format_t iform = img.tform_valid && img.tform_iform != IMG_INVALID + ? img.tform_iform : img.iform; + palette_format_t pform = img.tform_valid && img.tform_pform != PAL_INVALID + ? img.tform_pform : img.pform; + + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", cmd_name, + PrintFormat3(src_f,src_i,src_p), + param->arg, + PrintFormat3(fform,iform,pform), + dest ); + } + + if (opt_pre_convert) + TransformIMG(&img,2); + + if (!testmode) + { + err = SaveIMG(&img,fform,0,dest,samefile); + if ( err > ERR_WARNING ) + return err; + } + ResetIMG(&img); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command copy /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_copy() +{ + RegisterOptionByIndex(&InfoUI_wimgt,OPT_OVERWRITE,1,false); + opt_overwrite = true; + + if (!opt_dest) + { + if (!first_param) + return ERROR0(ERR_MISSING_PARAM, "Missing destination parameter!\n" ); + + ParamList_t *param; + for ( param = first_param; param->next; param = param->next ) + ; + ASSERT(param); + ASSERT(!param->next); + SetDest(param->arg,false); + param->arg = 0; + } + + file_format_t fform_dest = FF_UNKNOWN; + ccp point = strrchr(opt_dest,'.'); + if (point) + fform_dest = IsImageFF(GetByNameFF(point+1),true); + PRINT("fform(dest) = %s\n",GetNameFF(0,fform_dest)); + + int done_count = 0; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + if (!param->arg) + continue; + NORMALIZE_FILENAME_PARAM(param); + done_count++; + + Image_t img; + enumError err = LoadIMG( &img, true, param->arg, 0, + opt_mipmaps >= 0, false, opt_ignore ); + if ( err == ERR_NOT_EXISTS || err == ERR_WARNING ) + continue; + if ( err > ERR_WARNING ) + return err; + + const file_format_t src_f = img.info_fform; + const image_format_t src_i = img.info_iform; + const palette_format_t src_p = img.info_pform; + TransformIMG(&img,-1); + + file_format_t fform = img.tform_valid && img.tform_fform != FF_INVALID + ? img.tform_fform + : fform_dest != FF_UNKNOWN + ? fform_dest + : src_f; + PRINT("FFORM = %s\n",GetNameFF(0,fform)); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,0,0,false); + + if ( fform == FF_PNG ) + Transform2XIMG(&img); + else + Transform2InternIMG(&img); + + const bool samefile = !strcmp(param->arg,dest); + if ( !all_count + && !img.conv_count + && !img.tform_exec + && !img.patch_done + && fform == img.info_fform + && samefile ) + { + continue; + } + + if ( verbose >= 0 || testmode ) + { + image_format_t iform = img.tform_valid && img.tform_iform != IMG_INVALID + ? img.tform_iform : img.iform; + palette_format_t pform = img.tform_valid && img.tform_pform != PAL_INVALID + ? img.tform_pform : img.pform; + + fprintf(stdlog,"%s%sCOPY %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + PrintFormat3(src_f,src_i,src_p), + param->arg, + PrintFormat3(fform,iform,pform), + dest ); + } + + if (opt_pre_convert) + TransformIMG(&img,2); + + if (!testmode) + { + err = SaveIMG(&img,fform,0,dest,samefile); + if ( err > ERR_WARNING ) + return err; + } + ResetIMG(&img); + } + + if (!done_count) + SYNTAX_ERROR; + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wimgt,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: break; // [[2do]] err += ScanOptConst(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + + case GO_LONG: long_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_SECTIONS: print_sections++; break; + + case GO_ALL: all_count++; break; + case GO_CMPR_DEFAULT: err += ScanOptCmprDefault(optarg); break; + case GO_N_MIPMAPS: err += ScanOptNMipmaps(optarg); break; + case GO_MAX_MIPMAPS: err += ScanOptMaxMipmaps(optarg); break; + case GO_MIPMAP_SIZE: err += ScanOptMipmapSize(optarg); break; + case GO_MIPMAPS: opt_mipmaps = +1; break; + case GO_NO_MIPMAPS: opt_mipmaps = -1; break; + case GO_FAST_MIPMAPS: fast_resize_enabled = true; break; + case GO_PRE_CONVERT: opt_pre_convert = true; break; + case GO_PATCH: err += ScanOptPatchImage(optarg); break; + case GO_TRANSFORM: err += ScanOptTransform(optarg); break; + case GO_STRIP: opt_strip = true; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wimgt,TRACE_FILE,11); + #endif + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wimgt,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wimgt); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wimgt,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + + case CMD_LIST: err = cmd_list(0); break; + case CMD_LIST_L: err = cmd_list(1); break; + case CMD_LIST_LL: err = cmd_list(2); break; + + case CMD_DECODE: err = cmd_decode(); break; + case CMD_ENCODE: err = cmd_convert(cmd_ct->id,"ENCODE","\1P/\1N"); break; + case CMD_CONVERT: err = cmd_convert(cmd_ct->id,"CONVERT",0); break; + case CMD_COPY: err = cmd_copy(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + ResetPatchListIMG(); + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wimgt ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WIMGT_SHORT); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WIMGT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WIMGT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wkclt.c b/project/src/wkclt.c new file mode 100644 index 0000000..891f7e1 --- /dev/null +++ b/project/src/wkclt.c @@ -0,0 +1,1356 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-kcl.h" +#include "lib-szs.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wkclt.c" +#include + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WKCLT_SHORT ": " WKCLT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +static const char autoname[] = "/course.kcl"; + +extern const char text_kcl_flag_template_cr[]; + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wkclt,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wkclt,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WKCLT_SHORT "\n" + "name=" WKCLT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WKCLT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +/////////////////////////////////////////////////////////////////////////////// + +#define SYNTAX_ERROR syntax_error(__FUNCTION__,__FILE__,__LINE__) + +static void syntax_error ( ccp func, ccp file, uint line ) +{ + if ( current_command + && current_command->id >= 0 + && current_command->id < InfoUI_wkclt.n_cmd ) + { + const InfoCommand_t *ic = InfoUI_wkclt.cmd_info + current_command->id; + if (strchr(ic->syntax,'\n')) + { + ccp src = ic->syntax; + char *dest = iobuf; + while (*src) + if ( (*dest++ = *src++) == '\n' ) + { + *dest++ = ' '; + *dest++ = ' '; + *dest++ = ' '; + } + *dest = 0; + PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax:\n %s\n",iobuf); + } + else + PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax: %s\n",ic->syntax); + } + else + PrintErrorFile(func,file,line,0,0,ERR_SYNTAX,"Syntax Error!\n"); + exit(ERR_SYNTAX); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + printf(" kcl modes: %16llx = \"%s\"\n",(u64)KCL_MODE,GetKclMode()); + printf(" tri-area: %16g\n",opt_tri_area); + printf(" tri-height: %16g\n",opt_tri_height); + + if (opt_xtridata) + printf(" xtridata: %16x = %12d [support %sabled]\n", + opt_xtridata, opt_xtridata, SUPPORT_KCL_XTRIDATA ? "en" : "dis" ); + + if ( kcl_script_list.used == 1 ) + printf(" kcl script: %s\n",kcl_script_list.field[0]); + else if ( kcl_script_list.used > 1 ) + { + printf(" %u kcl scripts:\n",kcl_script_list.used); + uint i; + for ( i = 0; i < kcl_script_list.used; i++ ) + printf("%6d.: %s\n",i+1,kcl_script_list.field[i]); + } + if (opt_slot) + printf(" slot: %16x = \"%s\"\n",opt_slot,PrintSlotMode(opt_slot)); + + DumpTransformationOpt(); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #else + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetupVarsKCL(); + return ExportHelper("kcl"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat() +{ + stdlog = stderr; + enable_kcl_drop_auto++; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + CheckMode_t mode = GetCheckMode(false,false,true,false); + if ( verbose >= 0 || testmode ) + { + mode = GetCheckMode(true,false,true,false); + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + kcl_t kcl; + err = ScanRawDataKCL(&kcl,true,&raw,mode,true); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = SaveTextKCL(&kcl,"-",false); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + ResetKCL(&kcl); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command encode/decode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_convert ( int cmd_id, ccp cmd_name, ccp def_path ) +{ + if ( cmd_id == CMD_DECODE ) + enable_kcl_drop_auto++; + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + const file_format_t dest_ff = cmd_id == CMD_ENCODE ? FF_KCL : FF_KCL_TXT; + + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(dest_ff,0),false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", cmd_name, + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(dest_ff,0), dest ); + fflush(stdlog); + } + + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = dest_ff; + err = ScanRawDataKCL(&kcl,false,&raw,global_check_mode,true); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = dest_ff == FF_KCL + ? SaveRawKCL(&kcl,dest,opt_preserve) + : SaveTextKCL(&kcl,dest,opt_preserve); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + ResetKCL(&kcl); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command copy /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_copy() +{ + if (!opt_dest) + { + if (!first_param) + return ERROR0(ERR_MISSING_PARAM, "Missing destination parameter!\n" ); + + ParamList_t *param; + for ( param = first_param; param->next; param = param->next ) + ; + ASSERT(param); + ASSERT(!param->next); + SetDest(param->arg,false); + param->arg = 0; + } + + enable_kcl_drop_auto = 0; + file_format_t fform_dest = FF_UNKNOWN; + ccp point = strrchr(opt_dest,'.'); + if (point) + { + if (!strcasecmp(point,".kcl")) + fform_dest = FF_KCL; + else if ( !strcasecmp(point,".obj") || !strcasecmp(point,".txt") ) + { + fform_dest = FF_WAV_OBJ; + enable_kcl_drop_auto++; + } + } + PRINT("fform(dest) = %s: %s\n",GetNameFF(0,fform_dest),opt_dest); + + raw_data_t raw; + InitializeRawData(&raw); + + int done_count = 0; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + if (!param->arg) + continue; + NORMALIZE_FILENAME_PARAM(param); + done_count++; + + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + const file_format_t dest_ff = fform_dest != FF_UNKNOWN + ? fform_dest + : raw.fform; + + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = dest_ff; + err = ScanRawDataKCL(&kcl,false,&raw,global_check_mode,true); + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,"\1P/\1N\1?T", + GetExtFF(dest_ff,0),false); + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sCOPY %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(raw.fform,0), kcl.fname, + GetNameFF(dest_ff,0), dest ); + fflush(stdlog); + } + + if (!testmode) + { + err = dest_ff == FF_KCL + ? SaveRawKCL(&kcl,dest,opt_preserve) + : SaveTextKCL(&kcl,dest,opt_preserve); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + + ResetKCL(&kcl); + } + ResetRawData(&raw); + + if (!done_count) + SYNTAX_ERROR; + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cff /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp cff_func_f ( u16 flag ) +{ + const uint bufsize = 20; + char *buf = GetCircBuf(bufsize); + DASSERT(buf); + snprintf(buf,bufsize,"f(0x%02x,0x%03x)", + flag % N_KCL_TYPE, flag / N_KCL_TYPE ); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp cff_func_a ( u16 flag ) +{ + const uint bufsize = 20; + char *buf = GetCircBuf(bufsize); + DASSERT(buf); + snprintf(buf,bufsize,"a(0x%02x,%u,%u,%u,%u)", + flag % N_KCL_TYPE, + flag >> 13 & 7, + flag >> 11 & 3, + flag >> 8 & 7, + flag >> 5 & 7 ); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp cff_func_i ( u16 flag ) +{ + const uint bufsize = 20; + char *buf = GetCircBuf(bufsize); + DASSERT(buf); + snprintf(buf,bufsize,"i(0x%02x,%u,%u)", + flag % N_KCL_TYPE, + flag >> 5 & 7, + flag >> 8 & 15 ); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cff() +{ + CheckOptDest(KCL_FLAG_FILE1,false); + + raw_data_t raw; + InitializeRawData(&raw); + uint *flag_count = AllocFlagCount(); + char dest_buf[PATH_MAX]; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + kcl_t kcl; + err = ScanRawDataKCL(&kcl,true,&raw,global_check_mode,true); + if ( err > ERR_WARNING ) + return err; + + LoadFlagFileKCL(&kcl); + + if ( kcl.fform == FF_KCL ) + { + CountFlagsKCL(&kcl,flag_count); + + uint i; + for ( i = 0; i < N_KCL_FLAG; i++ ) + if (flag_count[i]) + { + StringCopyS(dest_buf,sizeof(dest_buf),GetGroupNameKCL(i,false)); + char *p; + for ( p = dest_buf; *p; p++ ) + *p = toupper((int)*p); + GetFlagByNameKCL(&kcl,dest_buf,0); + } + } + + bool append = true; + ccp dest = kcl.flag_fname; + PRINT("DEST: %s\n",dest); + if (!dest) + { + SubstDest(dest_buf,sizeof(dest_buf),param->arg,opt_dest, + 0,KCL_FLAG_EXT,false); + dest = dest_buf; + append = false; + } + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%s%s %s for %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + !kcl.flag_missing.used ? "SKIP " + : append ? "APPEND" : "CREATE", + dest, GetNameFF(kcl.fform,0), kcl.fname ); + fflush(stdlog); + } + + if ( !testmode && kcl.flag_missing.used ) + { + File_t F; + enumError err; + if (append) + { + err = AppendFILE(&F,true,dest,false,opt_mkdir); + if (!err) + fputs("\r\n",F.f); + } + else + { + err = CreateFileOpt(&F,true,dest,false,0); + if (!err) + fputs(text_kcl_flag_template_cr,F.f); + } + + if (!err) + { + uint i, fw = 29; + for ( i = 0; i < kcl.flag_missing.used; i++ ) + { + FormatFieldItem_t *it = kcl.flag_missing.list + i; + const uint len = strlen(it->key); + if ( fw < len ) + fw = len; + } + fw = ( fw + 10 ) / 8 * 8 + 5; + + for ( i = 0; i < kcl.flag_missing.used; i++ ) + { + FormatFieldItem_t *it = kcl.flag_missing.list + i; + int tablen = (fw-strlen(it->key))/8; + if ( tablen < 1 ) + tablen = 1; + const bool is_auto = IsAutoFlagKCL(it->num); + const u16 num = is_auto ? 0 : it->num; // unsigned with reduced num of bits + if ( long_count > 1 ) + { + if (brief_count) + fprintf(F.f, + " %s%.*s= %s", + it->key, tablen, Tabs20, + cff_func_a(num) ); + else + fprintf(F.f, + " %s%.*s= %s # %s %s 0x%04x", + it->key, tablen, Tabs20, + cff_func_a(num), cff_func_f(num), cff_func_i(num), num ); + } + else if (long_count) + { + if (brief_count) + fprintf(F.f, + " %s%.*s= %s", + it->key, tablen, Tabs20, + cff_func_f(num) ); + else + fprintf(F.f, + " %s%.*s= %s # %s %s 0x%04x", + it->key, tablen, Tabs20, + cff_func_f(num), cff_func_a(num), cff_func_i(num), num ); + } + else if (brief_count) + fprintf(F.f, + " %s%.*s= 0x%04x", + it->key, tablen, Tabs20, num ); + else + fprintf(F.f, + " %s%.*s= 0x%04x # %s %s %s", + it->key, tablen, Tabs20, + num, cff_func_f(num), cff_func_a(num), cff_func_i(num) ); + + fputs(is_auto ? " # edit this line!\r\n" : "\r\n", F.f ); + } + fputs("\r\n",F.f); + } + + CloseFile(&F,false); + if ( err > ERR_WARNING ) + return err; + } + ResetKCL(&kcl); + } + + FREE(flag_count); + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command flags /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_flags() +{ + stdlog = stderr; + uint *count = AllocFlagCount(); + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + fprintf(stdout,"%sKCL Flags of %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + + kcl_t kcl; + err = ScanRawDataKCL(&kcl,true,&raw,global_check_mode,true); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + const uint n = kcl.tridata.used; + CountFlagsKCL(&kcl,count); + uint tcount[N_KCL_TYPE]; + memset(tcount,0,sizeof(tcount)); + + if ( print_header && !brief_count ) + printf("\n flag type variant triangle count\n" + "-----------------------------------------------\n"); + + uint i, j; + for ( i = 0; i < N_KCL_TYPE; i++ ) + { + int done = 0; + for ( j = i; j < N_KCL_FLAG; j += N_KCL_TYPE ) + if (count[j]) + { + tcount[i] += count[j]; + if (!brief_count) + { + done++; + printf(" 0x%04x = 0x%02x | 0x%03x << 5 :%7u = %6.2f%%\n", + j, j & 0x1f, j >> 5, + count[j], 100.0 * count[j] / n ); + } + } + if ( done && print_header ) + putchar('\n'); + } + + if (print_header) + printf( "\n" + " type triangle count type description\n" + "--------------------------------------------------\n"); + + for ( i = 0; i < N_KCL_TYPE; i++ ) + if (tcount[i]) + printf(" T 0x%02x :%7u = %6.2f%% %s\n", + i, tcount[i], 100.0 * tcount[i] / n, + kcl_type[i].info ); + putchar('\n'); + } + ResetKCL(&kcl); + fflush(stdout); + } + + ResetRawData(&raw); + FREE(count); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command dump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_dump ( bool brief_level, int cmd ) +{ + stdlog = stderr; + + if ( brief_level > 0 ) + { + RegisterOptionByIndex(&InfoUI_wkclt,OPT_BRIEF,brief_level,false); + brief_count += brief_level; + } + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + fprintf(stdout,"%s%s of %s:%s\r\n", + verbose > 0 ? "\r\n" : "", + cmd == CMD_LIST + ? "LIST" : cmd == CMD_TRIANGLES + ? "TRIANGLES" : "DUMP", + GetNameFF(raw.fform,0), raw.fname ); + + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fform_outfile = FF_KCL; + err = ScanRawDataKCL(&kcl,false,&raw,global_check_mode,true); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = cmd == CMD_LIST + ? DumpTrianglesKCL(&kcl,"-") + : cmd == CMD_TRIANGLES + ? ListTrianglesKCL(&kcl,"-",!brief_count,long_count) + : DumpKCL(&kcl,"-",!brief_count,long_count); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + ResetKCL(&kcl); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command traverse /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_traverse ( bool fall ) +{ + stdlog = stderr; + + uint width = 100; + u32 type_mask = M1(type_mask); + + char fall_hint[50] = {0}; + if (fall) + { + const Var_t *var = FindConst("WIDTH"); + if ( var && var->mode != VAR_UNSET ) + { + int temp = GetIntV(var); + if ( temp > 0 ) + width = temp; + } + + var = FindConst("MASK"); + if ( var && var->mode != VAR_UNSET ) + type_mask = GetIntV(var); + + snprintf(fall_hint,sizeof(fall_hint), + " [width=%u,mask=0x%x]", width, type_mask ); + } + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param = first_param; + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,first_param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"\nANALYZE OCTREE OF %s:%s%s\n\n", + GetNameFF(raw.fform,0), raw.fname, fall_hint ); + fflush(stdlog); + } + + kcl_t kcl; + err = ScanRawDataKCL(&kcl,true,&raw,0,true); + if ( err > ERR_WARNING || !param ) + return err; + + uint np = 1; + enumError max_err = ERR_OK; + for ( param = param->next; param; param = param->next, np++ ) + { + char name[50]; + snprintf(name,sizeof(name),"Point #%u",np); + + DEFINE_VAR(pt); + err = ScanVectorExpr(param->arg,name,&pt); + if ( max_err < err ) + max_err = err; + + if (fall) + { + int kcl_flag; + double height = FallKCL(&kcl,stdout,pt.d3,width, + long_count,type_mask,&kcl_flag); + if (brief_count>1) + printf("%11.3f\n",height); + else if (brief_count) + printf("%11.3f %11.3f %11.3f\n", + pt.d3.x, height, pt.d3.z ); + else if ( kcl_flag >= 0 ) + printf("%11.3f %11.3f %11.3f 0x%04x\n", + pt.d3.x, height, pt.d3.z, kcl_flag ); + else + printf("%11.3f %11.3f %11.3f -1\n", + pt.d3.x, height, pt.d3.z ); + } + else + err = TraverseOctreeKCL( &kcl, stdout, pt.d3, + brief_count<=0, long_count ); + fflush(stdout); + if ( max_err < err ) + max_err = err; + } + + ResetKCL(&kcl); + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command check /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_check() +{ + patch_action_log_disabled++; + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + const bool check_verbose = verbose > 0 || long_count > 0; + CheckMode_t mode = GetCheckMode(false,brief_count>0,verbose<0,check_verbose); + if ( verbose >= 0 || testmode ) + { + ColorSet_t col; + SetupColorSet(&col,stdlog); + mode = GetCheckMode(true,brief_count>0,false,check_verbose); + fprintf(stdlog,"%s%sCHECK %s:%s%s\n", + verbose > 0 ? "\n" : "", + col.heading, GetNameFF(raw.fform,0), raw.fname, col.reset ); + fflush(stdlog); + } + + kcl_t kcl; + err = ScanRawDataKCL(&kcl,true,&raw,0,true); + if ( err <= ERR_WARNING && ( !mode || CheckKCL(&kcl,mode) )) + cmd_err = ERR_DIFFER; + fflush(stdout); + ResetKCL(&kcl); + if ( err > ERR_WARNING ) + return err; + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command analyze /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void analyze_kcl ( kcl_t *kcl ) +{ + DASSERT(kcl); + + double v_min = INFINITY; + double v_max_neg = -INFINITY; + double v_min_pos = INFINITY; + double v_max = -INFINITY; + + float n_min = INFINITY; + float n_max_neg = -INFINITY; + float n_min_pos = INFINITY; + float n_max = -INFINITY; + + uint n = 0; + const uint n_tri = kcl->tridata.used; + + kcl_tridata_t *td_base = (kcl_tridata_t*)kcl->tridata.list; + kcl_tridata_t *td, *td_end = td_base + n_tri; + for ( td = td_base; td < td_end; td++ ) + { + if ( td->status & TD_INVALID_NORM ) + continue; + + n++; + + double *d = td->pt->v; + uint nd = 3; + for ( ; nd > 0; nd--, d++ ) + { + if ( v_min > *d ) v_min = *d; + if ( v_max < *d ) v_max = *d; + if ( *d > 0.0 && v_min_pos > *d ) v_min_pos = *d; + if ( *d < 0.0 && v_max_neg < *d ) v_max_neg = *d; + } + + float *f = td->normal->v; + uint nf = sizeof(td->normal)/sizeof(float); + for ( ; nf > 0; nf--, f++ ) + { + if ( n_min > *f ) n_min = *f; + if ( n_max < *f ) n_max = *f; + if ( *f > 0.0 && n_min_pos > *f ) n_min_pos = *f; + if ( *f < 0.0 && n_max_neg < *f ) n_max_neg = *f; + //printf("XX: %08x\n",*(u32*)f); + } + } + + + printf( + "\n" + "%5u / %-5u vertex normal\n" + "--------------------------------------------\n" + " min: %13.6e %13.6e\n" + " max negative: %13.6e %13.6e\n" + " min positive: %13.6e %13.6e\n" + " max: %13.6e %13.6e\n" + "\n", + n, n_tri, + v_min, n_min, + v_max_neg, n_max_neg, + v_min_pos, n_min_pos, + v_max, n_max ); + + // Value found in Nintendos tracks: + // -v_max_neg == v_min_pos == 1.001358e-05 == 0x1.5p-17 == 21.0>>21 +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_analyze() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + fprintf(stdout,"%sAnalyze %s:%s\r\n", + verbose > 0 ? "\r\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + + kcl_t kcl; + err = ScanRawDataKCL(&kcl,true,&raw,0,true); + if ( err > ERR_WARNING ) + return err; + + analyze_kcl(&kcl); + ResetKCL(&kcl); + } + + ResetRawData(&raw); + return cmd_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_blow() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,FF_KCL); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + kcl_t kcl; + err = ScanRawDataKCL(&kcl,true,&raw,0,true); + if ( err > ERR_WARNING ) + return err; + + uint blow = CalcBlowSizeKCL(&kcl); + fprintf(stdout,"%6u = blow size of %s:%s\r\n", + blow, GetNameFF(kcl.fform,0), kcl.fname ); + ResetKCL(&kcl); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wkclt,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: err += ScanOptConst(optarg); break; + case GO_SCALE: err += ScanOptScale(optarg); break; + case GO_SHIFT: err += ScanOptShift(optarg); break; + case GO_XSS: err += ScanOptXSS(0,optarg); break; + case GO_YSS: err += ScanOptXSS(1,optarg); break; + case GO_ZSS: err += ScanOptXSS(2,optarg); break; + case GO_ROT: err += ScanOptRotate(optarg); break; + case GO_XROT: err += ScanOptXRotate(0,optarg); break; + case GO_YROT: err += ScanOptXRotate(1,optarg); break; + case GO_ZROT: err += ScanOptXRotate(2,optarg); break; + case GO_TRANSLATE: err += ScanOptTranslate(optarg); break; + case GO_NULL: force_transform |= 1; break; + case GO_NEXT: err += NextTransformation(false); break; + case GO_ASCALE: err += ScanOptAScale(optarg); break; + case GO_AROT: err += ScanOptARotate(optarg); break; + case GO_TFORM_SCRIPT: err += ScanOptTformScript(optarg); break; + case GO_KCL: err += ScanOptKcl(optarg); break; + case GO_KCL_FLAG: err += ScanOptKclFlag(optarg); break; + case GO_KCL_SCRIPT: err += ScanOptKclScript(optarg); break; + case GO_TRI_AREA: err += ScanOptTriArea(optarg); break; + case GO_TRI_HEIGHT: err += ScanOptTriHeight(optarg); break; + case GO_FLAG_FILE: opt_flag_file = optarg; break; + case GO_XTRIDATA: opt_xtridata = optarg ? str2ul(optarg,0,10) : 1; break; + case GO_SLOT: err += ScanOptSlot(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + case GO_TINY: err += ScanOptTiny(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_NO_PARAM: print_param = false; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_NO_CHECK: opt_no_check = true; break; + case GO_SECTIONS: print_sections++; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wkclt,TRACE_FILE,11); + #endif + CloseTransformation(); + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + SetupKCL(); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wkclt,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wkclt); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wkclt,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_EXPORT: err = cmd_export(); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupVarsKCL()); break; + case CMD_FUNCTIONS: SetupVarsKCL(); err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupVarsKCL()); break; + case CMD_MATRIX: err = cmd_matrix(); break; + case CMD_FLOAT: err = cmd_float(); break; + + case CMD_CAT: err = cmd_cat(); break; + case CMD_DECODE: err = cmd_convert(cmd_ct->id,"DECODE","\1P/\1N.obj"); break; + case CMD_ENCODE: err = cmd_convert(cmd_ct->id,"ENCODE","\1P/\1N\1?T"); break; + case CMD_COPY: err = cmd_copy(); break; + case CMD_CFF: err = cmd_cff(); break; + case CMD_FLAGS: err = cmd_flags(); break; + case CMD_DUMP: err = cmd_dump(0,CMD_DUMP); break; + case CMD_DBRIEF: err = cmd_dump(1,CMD_DUMP); break; + case CMD_LIST: err = cmd_dump(0,CMD_LIST); break; + case CMD_TRIANGLES: err = cmd_dump(0,CMD_TRIANGLES); break; + case CMD_TRAVERSE: err = cmd_traverse(false); break; + case CMD_FALL: err = cmd_traverse(true); break; + case CMD_CHECK: err = cmd_check(); break; + case CMD_ANALYZE: err = cmd_analyze(); break; + case CMD_BLOW: err = cmd_blow(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wkclt ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WKCLT_SHORT); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WKCLT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WKCLT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wkmpt.c b/project/src/wkmpt.c new file mode 100644 index 0000000..baf73ec --- /dev/null +++ b/project/src/wkmpt.c @@ -0,0 +1,1835 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "dclib-xdump.h" +#include "lib-kmp.h" +#include "lib-kcl.h" +#include "lib-szs.h" +#include "db-object.h" +#include "lib-object.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wkmpt.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WKMPT_SHORT ": " WKMPT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +static const char autoname[] = "/course.kmp"; + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wkmpt,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wkmpt,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WKMPT_SHORT "\n" + "name=" WKMPT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WKMPT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + + printf(" kmp draw: %16llx = \"%s\"\n",(u64)KMP_DRAW,GetDrawMode()); + printf(" kmp modes: %16llx = \"%s\"\n",(u64)KMP_MODE,GetKmpMode()); + printf(" kmp tform: %16x = \"%s\"\n",KMP_TFORM,GetKmpTform()); + if (speed_mod_active) + printf(" kmp speed modifier: %04x ~ %5.3f\n", + speed_mod_val, speed_mod_factor ); + if (opt_slot) + printf(" slot: %16x = \"%s\"\n",opt_slot,PrintSlotMode(opt_slot)); + + printf(" battle mode: %16d = %s\n", + opt_battle_mode, GetKeywordOffAutoOn(opt_battle_mode) ); + printf(" export flags:%16d = %s\n", + opt_export_flags, GetKeywordOffAutoOn(opt_export_flags) ); + printf(" route-options: %14d = %s\n", + opt_route_options, GetKeywordOffAutoOn(opt_route_options) ); + printf(" wim0: %16d = %s\n", + opt_wim0, GetKeywordOffAutoOn(opt_wim0) ); + + if (opt_png) + { + printf(" png-pix-size:%16x = %12d\n",opt_png_pix_size,opt_png_pix_size); + printf(" png-a-alias: %16x = %12d\n",opt_png_aalise,opt_png_aalise); + printf(" png-x* %16d .. %d\n",opt_png_x1,opt_png_x2); + printf(" png-y* %16d .. %d\n",opt_png_y1,opt_png_y2); + printf(" png-type-mask: %#010x\n",opt_png_type_mask); + printf(" png-rm %11sabled\n", opt_png_rm ? "en" : "dis" ); + } + + DumpTransformationOpt(); + + if (const_map.used) + { + printf(" const list:\n"); + DumpVarMap(stdout,8,&const_map,false); + } + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 0 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command objects /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_objects() +{ + stdlog = stderr; + search_filter_t *filter = AllocSearchFilter(N_KMP_GOBJ); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + ccp arg = param->arg; + if (!*arg) + continue; + + char *end; + uint num = strtoul(arg,&end,16); + if ( num > 0 && num < N_KMP_GOBJ && !*end ) + filter[num].mode = 1; + + num = strtoul(arg,&end,10); + if ( num > 0 && num < N_KMP_GOBJ && !*end ) + filter[num].mode = 1; + + char needle[200]; + CreateNeedleWCMP(needle,sizeof(needle),arg); + + uint id; + const ObjectInfo_t *oi = ObjectInfo; + for ( id = 0; id < N_KMP_GOBJ; id++, oi++ ) + { + if ( filter[id].mode || !oi->name ) + continue; + + filter[id].mode = strcasestr(oi->name,arg) + || oi->fname && strcasestr(oi->fname,arg); + if (!filter[id].mode) + { + if (!filter[id].string) + { + ccp list[] = { oi->fname, oi->fname ? oi->fname : "", oi->info, 0 }; + char haystack[5000]; + uint len = CreateHaystackListWCMP(haystack,sizeof(haystack),list); + filter[id].string = MEMDUP(haystack,len+1); + } + filter[id].mode = FindWCMP(filter[id].string,needle,3,0,0); + } + } + } + + uint twidth = GetTermWidth(80,40)-1; + const bool print_sep = !brief_count || brief_count < 2 && long_count; + + uint id, found_count = 0; + const ObjectInfo_t *oi = ObjectInfo; + for ( id = 0; id < N_KMP_GOBJ; id++, oi++ ) + { + if ( !filter[id].mode || !oi->name ) + continue; + + if (!found_count++) + { + if (print_header) + { + if ( brief_count > 1 ) + { + twidth = OBJ_FW_NAME + 7; + printf("\n ID object name\n%.*s\n",twidth,Minus300); + } + else + printf("\n ID %-*s Characteristics\n%.*s\n", + OBJ_FW_NAME, "Object name", twidth, Minus300 ); + } + else if (print_sep) + printf("%.*s\n",twidth,Minus300); + } + + if ( brief_count > 1 ) + printf("%4x %s\n",id,oi->name); + else + { + printf("%4x %-*s %s\n",id,OBJ_FW_NAME,oi->name,oi->charact); + + if (!brief_count) + { + if (oi->info) + { + fputs(" Info:",stdout); + PutLines(stdout,10,twidth,8,0,oi->info,0); + } + + if ( oi->fname && !long_count ) + printf(" Files: %s\n",oi->fname); + } + + if (long_count) + { + UsedFileFILE_t used_file; + if (FindDbFileByObject(&used_file,true,id,1)) + { + ccp head = "Files:"; + int fi; + for ( fi = 0; fi < N_DB_FILE_FILE; fi++ ) + if (used_file.d[fi]) + { + printf("%9s %s\n",head,DbFileFILE[fi].file); + head = ""; + } + } + + uint p; + for ( p = 0; p < 8; p++ ) + if ( oi->param[p] ) + { + printf(" Set#%u:",p+1); + PutLines(stdout,10,twidth,9,0,oi->param[p],0); + } + } + + if (print_sep) + printf("%.*s\n",twidth,Minus300); + } + } + + if ( !print_sep && print_header ) + printf("%.*s\n\n",twidth,Minus300); + else if (print_header) + putchar('\n'); + + FreeSearchFilter(filter); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetupVarsKMP(); + return ExportHelper("kmp"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _xexport /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_xexport() +{ + //--- cond_ref_tab[] + + fputs("\nstatic const gobj_cond_ref_t cond_ref_tab[] =\n{",stdout); + + u16 next = 0; + const gobj_cond_ref_t *cr; + for ( cr = cond_ref_tab; cr->ref_id; cr++ ) + { + if ( next != cr->ref_id || cr->option & 1 ) + putchar('\n'); + next = cr->ref_id + 1; + + if ( strlen(cr->name) >= sizeof(cr->name) ) + printf("ERR: STRLEN(NAME,%#x): %zd >= %zd : %s\n", + cr->ref_id, strlen(cr->name), sizeof(cr->name), cr->name ); + if ( strlen(cr->alias) >= sizeof(cr->alias) ) + printf("ERR: STRLEN(ALIAS,%#x): %zd >= %zd : %s\n", + cr->ref_id, strlen(cr->alias), sizeof(cr->alias), cr->alias ); + if ( strlen(cr->info) >= sizeof(cr->info) ) + printf("ERR: STRLEN(INFO,%#x): %zd >= %zd : %s\n", + cr->ref_id, strlen(cr->info), sizeof(cr->info), cr->info ); + + if (!(cr->ref_id&1)) + { + const gobj_cond_ref_t *next = cr+1; + if ( next->ref_id != (cr->ref_id|1) ) + printf("ERR: REF-ID: %04x %04x\n",cr->ref_id,next->ref_id); + + if (!(cr->option&4)) + { + if ( cr->cond_mask && ( cr->cond_mask ^ next->cond_mask ) != 0xffff ) + printf("ERR: COND-MASK[%04x,%04x]: %04x %04x\n", + cr->ref_id, next->ref_id, cr->cond_mask, next->cond_mask ); + if ( cr->set_mask && ( cr->set_mask ^ next->set_mask ) != 0xff ) + printf("ERR: SET-MASK[%04x,%04x]: %02x %02x\n", + cr->ref_id, next->ref_id, cr->set_mask, next->set_mask ); + } + } + + printf(" { 0x%04x, 0x%04x, 0x%02x, %u }, // %-13s : %s\n", + cr->ref_id, cr->cond_mask, cr->set_mask, cr->op, + cr->name, cr->info ); + } + fputs("\n {0,0,0,0}\n};\n\n",stdout); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command startpos /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define POSITION_ORIGIN 0x9f42410 + +static enumError cmd_startpos() +{ + stdlog = stderr; + + enum { M_RESET, M_TABLE, M_RELATIVE, M_MIRROR, M_NARROW, + M_AUTO, M_HEX, M_Z, M_NUMBER }; + static const KeywordTab_t keytab[] = + { + { M_RESET, "CLEAR", "RESET", 0 }, + + { M_TABLE, "IMAGE", "I", 0 }, + { M_TABLE, "TABLE", "T", 1 }, + + { M_RELATIVE, "ABSOLUTE", "ABS", 0 }, + { M_RELATIVE, "RELATIVE", "REL", 1 }, + + { M_MIRROR, "LEFT", "L", 0 }, + { M_MIRROR, "RIGHT", "R", 1 }, + + { M_NARROW, "WIDE", "W", 0 }, + { M_NARROW, "NARROW", "N", 1 }, + + { M_AUTO, "AUTO", "A", 0x06 }, + { M_AUTO, "NO-AUTO", "NOAUTO", 0x00 }, + + { M_HEX, "DECIMAL", "DEC", 0 }, + { M_HEX, "HEXADECIMAL", "HEX", 1 }, + + { M_Z, "Z0", 0, 0 }, + { M_Z, "Z1", 0, 1 }, + { M_Z, "Z2", 0, 2 }, + { M_Z, "Z3", 0, 3 }, + + { M_NUMBER, "1", 0, 1 }, + { M_NUMBER, "2", 0, 2 }, + { M_NUMBER, "3", 0, 3 }, + { M_NUMBER, "4", 0, 4 }, + { M_NUMBER, "5", 0, 5 }, + { M_NUMBER, "6", 0, 6 }, + { M_NUMBER, "7", 0, 7 }, + { M_NUMBER, "8", 0, 8 }, + { M_NUMBER, "9", 0, 9 }, + { M_NUMBER, "10", 0, 10 }, + { M_NUMBER, "11", 0, 11 }, + { M_NUMBER, "12", 0, 12 }, + + {0,0,0,0} + }; + + enumError err = ERR_OK; + bool table = false; + bool relative = false; + bool mirror = false; + bool narrow = false; + bool hexmode = false; + uint z_mode = 1; + uint automode = 0x06; + + raw_data_t raw; + InitializeRawData(&raw); + kmp_t kmp; + InitializeKMP(&kmp); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + if (!param->arg) + continue; + if ( strchr(param->arg,'/') || strchr(param->arg,'\\') ) + { + ResetKMP(&kmp); + if ( !strcmp(param->arg,"/") || !strcmp(param->arg,"\\") ) + continue; + + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + err = ScanRawDataKMP(&kmp,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + continue; + } + + int cmd_stat; + const KeywordTab_t * job = ScanKeyword(&cmd_stat,param->arg,keytab); + if (!job) + { + PrintKeywordError(keytab,param->arg,cmd_stat,0,0); + err = ERR_SYNTAX; + continue; + } + + switch (job->id) + { + case M_RESET: + table = relative = mirror = narrow = hexmode = false; + z_mode = 1; + automode = 0x06; + ResetKMP(&kmp); + break; + + case M_TABLE: + table = job->opt != 0; + break; + + case M_RELATIVE: + relative = job->opt != 0; + break; + + case M_MIRROR: + mirror = job->opt != 0; + automode &= ~2; + break; + + case M_NARROW: + narrow = job->opt != 0; + automode &= ~4; + break; + + case M_AUTO: + automode = job->opt; + break; + + case M_HEX: + hexmode = job->opt != 0; + break; + + case M_Z: + z_mode = job->opt; + break; + + case M_NUMBER: + { + uint n = job->opt; + uint amode = automode; + if ( table && !relative ) + amode |= 1; + + const double3 *tab + = GetStartPosKMP(0,n,mirror,narrow,&kmp,amode); + if (!tab) + break; + + printf("\n%u player%s, %s (%s%u), %s (%s%u), %s%s:\n\n", + n, n == 1 ? "" : "s", + kmp_start_pos_mirror ? "pole is right" : "pole is left", + kmp_start_pos_auto & 2 ? "KMP=" : "", + kmp_start_pos_mirror, + kmp_start_pos_narrow ? "narrow mode" : "wide mode", + kmp_start_pos_auto & 4 ? "KMP=" : "", + kmp_start_pos_narrow, + kmp_start_pos_auto & 1 ? "absolute" : "relative", + hexmode ? ", protocol hex mode" : "" ); + + if (table) + { + if (hexmode) + { + const int origin + = kmp.dlist[KMP_KTPT].used ? POSITION_ORIGIN : 0; + + uint i; + for ( i = 0; i < n; i++ ) + printf("%4u: %8x %8x %8x\n", + i+1, + double2int(tab[i].x*16.0) + origin, + double2int(tab[i].y*16.0) + origin, + double2int(tab[i].z*16.0) + origin ); + } + else + { + uint i; + for ( i = 0; i < n; i++ ) + printf("%4u: %11.3f %11.3f %11.3f\n", + i+1, tab[i].x, tab[i].y, tab[i].z ); + } + } + else + { + const bool have_sep = n > 2 + && IsNormalD(tab[0].z) + && IsNormalD(tab[1].z); + const double sep = have_sep ? 1.5*( tab[1].z - tab[0].z ) : 1e9; + + uint i; + for ( i = 0; i < n; i++ ) + { + if ( have_sep + && i > 0 + && IsNormalD(tab[i-1].z) + && tab[i].z >= tab[i-1].z + sep ) + { + putchar('\n'); + } + + int x = (int)tab[i].x; + const int fw = x < -900 + ? 0 + : x > 900 + ? 1800/80 + : (900+x)/80; + const char sign = x < 0 ? '-' : x > 0 ? '+' : ' '; + if (hexmode) + printf("%3u:%*c%04x%*c", + i+1, fw+3, sign, abs(x)*16, 26-fw, ':' ); + else + printf("%3u:%*c%03u%*c", + i+1, fw+3, sign, abs(x), 26-fw, ':' ); + + if ( z_mode > 0 && IsNormalD(tab[i].z) ) + { + if (hexmode) + printf(" %5x",double2int(tab[i].z*16)); + else + printf(" %9.4f",tab[i].z); + + if ( z_mode > 1 && i > 0 && IsNormalD(tab[i-1].z) ) + { + double delta = tab[i].z - tab[i-1].z; + if (hexmode) + printf(" %5x",double2int(16*delta)); + else + printf(" %9.4f",delta); + + if ( z_mode > 2 && i > 1 && IsNormalD(tab[i-2].z) ) + { + delta -= tab[i-1].z - tab[i-2].z; + if (hexmode) + printf(" %5x",double2int(16*delta)&0xfffff); + else + printf(" %9.4f",delta); + } + } + } + putchar('\n'); + } + } + putchar('\n'); + } + break; + } + } + ResetKMP(&kmp); + ResetRawData(&raw); + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if (opt_ignore) + continue; + return err; + } + + if (!testmode) + { + err = SaveTextKMP(&kmp,"-",false); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + ResetKMP(&kmp); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command encode/decode /////////////// +/////////////////////////////////////////////////////////////////////////////// +// cmd_encode(), cmd_decode() + +static enumError cmd_convert ( int cmd_id, ccp cmd_name, ccp def_path ) +{ + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + const file_format_t dest_ff = cmd_id == CMD_ENCODE ? FF_KMP : FF_KMP_TXT; + + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(dest_ff,0),false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", cmd_name, + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(dest_ff,0), dest ); + fflush(stdlog); + } + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if (opt_ignore) + continue; + return err; + } + + if (!testmode) + { + err = dest_ff == FF_KMP + ? SaveRawKMP(&kmp,dest,opt_preserve) + : SaveTextKMP(&kmp,dest,opt_preserve); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + ResetKMP(&kmp); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command diff /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError diff_kmp ( ccp fname1, ccp fname2 ) +{ + if ( verbose > 0 ) + printf("DIFF/KMP %s : %s\n", fname1, fname2 ); + + kmp_t kmp1, kmp2; + InitializeKMP(&kmp1); + InitializeKMP(&kmp2); + + raw_data_t raw; + InitializeRawData(&raw); + + enumError err = LoadRawData(&raw,false,fname1,"course.kmp",false,0); + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + err = ScanRawDataKMP(&kmp1,false,&raw,0); + if (!err) + { + err = LoadRawData(&raw,false,fname2,"course.kmp",false,0); + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + err = ScanRawDataKMP(&kmp2,false,&raw,0); + if (!err) + { + const int diff_verbose = verbose + 2; + err = DiffKMP( &kmp1, &kmp2, diff_verbose < 0 ? 0 : diff_verbose ); + if ( verbose >= -2 && err == ERR_DIFFER ) + printf("KMP differ: %s : %s\n", kmp1.fname, kmp2.fname ); + else if ( verbose >= 0 && err == ERR_OK ) + printf("KMP identical: %s : %s\n", kmp1.fname, kmp2.fname ); + } + } + } + } + + ResetKMP(&kmp1); + ResetKMP(&kmp2); + ResetRawData(&raw); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError diff_kmp_dest() +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,"%F",0,false); + const enumError err = diff_kmp(param->arg,dest); + if ( max_err < err ) + max_err = err; + if ( err > ERR_WARNING || err && verbose < -1 ) + break; + } + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_diff() +{ + if ( verbose > 0 ) + fprintf(stdlog,"KMP diff mode: %s\n",GetKmpDiffMode()); + + SetPatchFileModeReadonly(); + + if (opt_dest) + return diff_kmp_dest(); + + if ( n_param != 2 ) + return ERROR0(ERR_SYNTAX,"Exact 2 sources expected if --dest is not set.\n"); + + ASSERT(first_param); + ASSERT(first_param->next); + return diff_kmp(first_param->arg,first_param->next->arg); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command draw /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_draw() +{ + if (opt_png) + { + KCL_MODE = KCL_MODE & ~KCLMD_M_PRESET | KCLMD_DRAW; + disable_transformation++; + } + + const file_format_t ff_dest = opt_png ? FF_PNG : FF_KCL_TXT; + ccp def_path = opt_png ? "\1P/\1N.kmp.png" : "\1P/\1N.kmp.obj"; + CheckOptDest(def_path,false); + enable_kcl_drop_auto++; + + noPRINT(">>> cmd_draw() png=%d, ff=%u,%s, def_path=%s\n", + opt_png, ff_dest, GetNameFF(ff_dest,0), def_path ); + + const bool detailed = ( KMP_DRAW & DMD_DETAILED ) != 0; + const bool warnings = ( KMP_DRAW & DMD_WARNINGS ) != 0; + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(ff_dest,0), false ); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sCREATE %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(ff_dest,0), dest ); + fflush(stdlog); + } + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if (opt_ignore) + continue; + return err; + } + + if (!testmode) + { + DrawKCL_t dk; + InitializeDrawKCL(&dk); + dk.kmp = &kmp; + dk.warnings = warnings; + dk.detailed = detailed; + + if ( KMP_DRAW & DMD_KCL ) + { + DefineAutoLoadReferenceKCL(kmp.fname,".kmp.txt"); + dk.kcl = dk.draw = CreateReferenceKCL(); + } + else + { + dk.draw = MALLOC(sizeof(*dk.draw)); + InitializeKCL(dk.draw); + + if ( KMP_DRAW & DMD_M_NEED_REF ) + { + DefineAutoLoadReferenceKCL(kmp.fname,".kmp.txt"); + dk.kcl = CreateReferenceKCL(); + } + else + dk.kcl = 0; + } + DASSERT(dk.draw); + dk.draw->no_limit = true; + + if ( KMP_DRAW & DMD_POTI ) + { + uint i; + for ( i = 0; i < MAX_ROUTE; i++ ) + dk.route[i] = DRTM_POTI; + } + + if ( KMP_DRAW & DMD_AREA ) + AddAreas2KCL(&dk); + + if ( KMP_DRAW & DMD_CKPT ) + AddCheckPoints2KCL( &dk, (KMP_DRAW&DMD_CJGPT) != 0 ); + + if ( KMP_DRAW & DMD_JGPT ) + AddArrow2KCL( &dk, KMP_JGPT, KCL_FLAG_JGPT, + KCL_FLAG_JGPT_AREA, 900.0, 1200.0 ); + + if ( KMP_DRAW & DMD_KTPT ) + { + double len = 5300; + if ( kmp.dlist[KMP_KTPT].used > 5 ) + { + // seems to be a battle arena (0=race,12=battle) + // => don't draw the starting area + len = 0.0; + } + else if (kmp.dlist[KMP_STGI].used) + { + const kmp_stgi_entry_t *stgi + = (kmp_stgi_entry_t*)kmp.dlist[KMP_STGI].list; + if ( stgi->narrow_start == 1 ) + len = 4800; + } + AddArrow2KCL( &dk, KMP_KTPT, KCL_FLAG_KTPT, + KCL_FLAG_KTPT_AREA, len, 2000.0 ); + } + + if ( KMP_DRAW & (DMD_ENPT|DMD_DISPATCH) ) + AddRoutes2KCL( &dk, KMP_ENPT, KCL_FLAG_ENPT ); + + if ( KMP_DRAW & DMD_ITPT ) + AddRoutes2KCL( &dk, KMP_ITPT, KCL_FLAG_ITPT ); + + if ( KMP_DRAW & DMD_CNPT ) + AddCannons2KCL(&dk); + + if ( KMP_DRAW & (DMD_ITEMBOX|DMD_SOLIDOBJ) ) + { + uint result = OBF_SOLID | OBF_NO_ROUTE; + uint mask = result | OBF_ROAD; + + switch ( KMP_DRAW & (DMD_ITEMBOX|DMD_SOLIDOBJ) ) + { + case DMD_ITEMBOX: + mask |= OBF_ITEMBOX; + result |= OBF_ITEMBOX; + break; + + case DMD_SOLIDOBJ: + mask |= OBF_ITEMBOX; + break; + + default: + break; + } + AddObjectsByFlag2KCL(&dk,mask,result); + } + + if ( KMP_DRAW & DMD_COIN ) + AddObjectsById2KCL(&dk,GOBJ_COIN); + + if ( KMP_DRAW & DMD_DECORATION ) + { + uint result = OBF_NO_ROUTE; + uint mask = result | OBF_SOLID | OBF_ROAD; + AddObjectsByFlag2KCL(&dk,mask,result); + } + + if ( KMP_DRAW & DMD_ROADOBJ ) + AddObjectsByFlag2KCL(&dk,OBF_ROAD,OBF_ROAD); + + if ( KMP_DRAW & (DMD_ITEMBOX|DMD_SOLIDOBJ|DMD_POTI) ) + { + const kmp_gobj_entry_t *op = (kmp_gobj_entry_t*)kmp.dlist[KMP_GOBJ].list; + const kmp_gobj_entry_t *op_end = op + kmp.dlist[KMP_GOBJ].used; + for ( ; op < op_end; op++ ) + { + if ( op->route_id < MAX_ROUTE && op->obj_id < N_KMP_GOBJ ) + { + const u32 flags = ObjectInfo[op->obj_id].flags; + if ( flags & OBF_SOLID && flags & (OBF_OPT_ROUTE|OBF_ALWAYS_ROUTE) ) + { + int drtm = 0; + Itembox_t ibox; + if (AnalyseItembox(&ibox,op)) + { + if ( KMP_DRAW & (DMD_ITEMBOX|DMD_POTI) ) + drtm = ibox.item_player + ? DRTM_ITEMBOX_PLAYER + : ibox.item_enemy + ? DRTM_ITEMBOX_ENEMY + : DRTM_ITEMBOX; + } + else if ( KMP_DRAW & (DMD_SOLIDOBJ|DMD_POTI) ) + drtm = DRTM_GOBJ; + + if ( drtm > 0 && dk.route[op->route_id] < drtm ) + dk.route[op->route_id] = drtm; + } + } + } + } + + // draw always late; drawing depends on 'dk.route' + AddPotiRoutes2KCL(&dk); + + DrawPosFiles(&dk); + + // zero ground is the latest element + if ( KMP_DRAW & DMD_BLACK ) + AddZeroGround2KCL(&dk,KCL_FLAG_ZERO_GROUND_BLACK); + else if ( KMP_DRAW & DMD_WHITE ) + AddZeroGround2KCL(&dk,KCL_FLAG_ZERO_GROUND_WHITE); + + if (opt_png) + SaveImageKCL(&kmp,dk.draw,dest,false); + else + SaveTextKCL(dk.draw,dest,false); + + ResetKCL(dk.draw); + FREE(dk.draw); + if ( dk.kcl && dk.kcl != dk.draw ) + { + ResetKCL((kcl_t*)dk.kcl); + FREE((kcl_t*)dk.kcl); + } + } + ResetKMP(&kmp); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command check /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_check() +{ + patch_action_log_disabled++; + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + const bool check_verbose = verbose > 0 || long_count > 0; + CheckMode_t mode = GetCheckMode(false,brief_count>0,verbose<0,check_verbose); + if ( verbose >= 0 || testmode ) + { + ColorSet_t col; + SetupColorSet(&col,stdlog); + mode = GetCheckMode(true,brief_count>0,false,check_verbose); + fprintf(stdlog,"%s%sCHECK %s:%s%s\n", + verbose > 0 ? "\n" : "", + col.heading, GetNameFF(raw.fform,0), raw.fname, col.reset ); + fflush(stdlog); + } + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,0); + if ( err <= ERR_WARNING && ( !mode || CheckKMP(&kmp,mode) )) + cmd_err = ERR_DIFFER; + fflush(stdout); + ResetKMP(&kmp); + if ( err > ERR_WARNING ) + return err; + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command stgi /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_routes() +{ + stdlog = stderr; + raw_data_t raw; + InitializeRawData(&raw); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,"/course.kmp",opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + if ( opt_ignore && raw.fform != FF_KMP && raw.fform != FF_KMP_TXT ) + continue; + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,CMOD_SILENT); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if ( max_err < err ) + max_err = err; + continue; + } + + + //--- print infos + + static struct { uint sect; int pmode; ccp info; } *ptr, tab[] = + { + { KMP_CKPH, -1, "CKPH (check point groups)" }, + { KMP_CKPT, 1, "CKPT (check points)" }, + { KMP_ENPH, -1, "ENPH (enemy route groups)" }, + { KMP_ENPT, 1, "ENPT (enemy route points)" }, + { KMP_ITPH, -1, "ITPH (item route groups)" }, + { KMP_ITPT, 1, "ITPT (item route points)" }, + {0,0} + }; + + for ( ptr = tab; ptr->info; ptr++ ) + { + kmp_linfo_t li; + if (AnalyseRouteKMP(&li,&kmp,ptr->sect)) + { + printf("\n%s with %u records\n",ptr->info,li.used); + uint i, j; + for ( i = 0; i < li.used; i++ ) + if (li.summary[i]) + { + printf("%5u:%3u%c", i, li.n_point[i], + li.gmode[i] & LINFOG_DISPATCH ? 'd' : ' ' ); + kmp_linfo_mode *md = li.list[i]; + for ( j = 0; j < li.used; j++, md++ ) + if (*md) + printf(" %3u:%s",j,PrintLinkInfoMode(*md,ptr->pmode,true)); + putchar('\n'); + } + } + } + + ResetKMP(&kmp); + } + putchar('\n'); + + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command gobj /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_gobj() +{ + patch_action_log_disabled++; + raw_data_t raw; + InitializeRawData(&raw); + + const ColorSet_t * col = GetFileColorSet(stdout); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if (print_header) + printf("\n%sFile %s%s:%s%s\n", + col->status, col->file, + GetNameFF(raw.fform,0), raw.fname, col->reset ); + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,CMOD_SILENT); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if (opt_ignore) + continue; + return err; + } + + kmp_ana_gobj_t ag; + AnalyseGobj(&ag,&kmp); + + static const char type_char[] = "0-.boa"; + uint i; + const kmp_gobj_info_t *info; + const kmp_gobj_entry_t *gobj; + for ( i = 0, info = ag.info, gobj = ag.gobj; i < ag.n_gobj; i++, info++, gobj++ ) + printf("%4d. %4x %4x : %4x %4x : %u %c %2x %3u : %3d\n", + i, + gobj->obj_id, info->obj_id, + gobj->ref_id, info->ref_id, + info->type, type_char[info->type], + info->status, info->ref_count, info->defobj ); + + if (print_header) + { + printf("%s%u object%s analyzed in %s: ", + col->status, + ag.n_gobj, ag.n_gobj == 1 ? "" : "s", + PrintTimerUSec6(0,0,ag.dur_usec,false) ); + + printf("o=%u=%u+%u, en=%d,%d, do=%u/%u=%u+%u+%u\n", + ag.n_object, ag.n_disabled, ag.n_enabled, + ag.n_engine, ag.n_random, + ag.n_defobj_used, ag.n_defobj, + ag.n_defobj_bits, ag.n_defobj_or, ag.n_defobj_and ); + } + + ResetAnaGobj(&ag); + ResetKMP(&kmp); + } + + putchar('\n'); + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command gamemodes /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_gamemodes() +{ + if ( brief_count > 1 ) + print_header = false; + + patch_action_log_disabled++; + raw_data_t raw; + InitializeRawData(&raw); + + const ColorSet_t * col = GetFileColorSet(stdout); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if (print_header) + printf("\n%sFile %s%s:%s%s\n", + col->status, col->file, + GetNameFF(raw.fform,0), raw.fname, col->reset ); + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,CMOD_SILENT); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if (opt_ignore) + continue; + return err; + } + + kmp_ana_pflag_t ap; + AnalysePFlagScenarios(&ap,&kmp,opt_gamemodes); + + uint ri; + const kmp_ana_pflag_res_t *res; + for ( ri = 0, res = ap.res_list; ri < ap.n_res; ri++, res++ ) + if (brief_count) + printf("%2u %s\n",res->version+1,res->name); + else if (long_count>2) + printf("%3d. %3u %-13s %u,%u,%02u,%02d,%u,%u,%u %s\n", + ri+1, res->version+1, + res->name, + res->lex_test.offline_online, + res->lex_test.n_offline, + res->lex_test.n_online, + res->lex_test.cond_bit, + res->lex_test.game_mode, + res->lex_test.random, + res->lex_test.engine, + res->status ); + else if (long_count) + printf("%3d. %3u %-13s %s\n",ri+1,res->version+1,res->name,res->status); + else + printf("%3d. %3u %s\n",ri+1,res->version+1,res->name); + + if (print_header) + { + printf("%s%u object%s analyzed in %s: ", + col->status, + ap.ag.n_gobj, ap.ag.n_gobj == 1 ? "" : "s", + PrintTimerUSec6(0,0,ap.dur_usec,false) ); + + if ( ap.n_version == ap.n_res ) + printf("%u different scenario%s total", + ap.n_res, ap.n_res == 1 ? "" : "s" ); + else + printf("%u scenario%s total, %u different", + ap.n_res, ap.n_res == 1 ? "" : "s", ap.n_version ); + + if ( ap.n_res_std && ap.n_res_ext ) + printf(" (%u standard code, %u extended code)", + ap.n_res_std, ap.n_res_ext ); + else if (ap.n_res_std) + fputs(" (standard code only)",stdout); + else if (ap.n_res_ext) + fputs(" (extended code only)",stdout); + printf(".%s\n",col->reset); + } + + ResetPFlagScenarios(&ap); + ResetKMP(&kmp); + } + + putchar('\n'); + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command wim0 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_wim0() +{ + patch_action_log_disabled++; + raw_data_t raw; + InitializeRawData(&raw); + + const ColorSet_t * col = GetFileColorSet(stdout); + + XDump_t xd; + InitializeXDump(&xd); + xd.print_format = false; + xd.print_summary = false; + if ( long_count > 0 ) + { + xd.min_addr_fw = 10; + } + else + { + xd.print_addr = false; + } + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + printf("\n%sFile %s%s:%s%s\n", + col->status, col->file, + GetNameFF(raw.fform,0), raw.fname, col->reset ); + + kmp_t kmp; + err = ScanRawDataKMP(&kmp,true,&raw,CMOD_SILENT); + if ( err > ERR_WARNING ) + { + ResetKMP(&kmp); + if (opt_ignore) + continue; + return err; + } + + if ( !kmp.wim0_data || !kmp.wim0_size ) + printf("%s> No WIM0 section found!%s\n",col->caption,col->reset); + else + { + const kmp_wim0_sect_t *sect = FindSectionWim0(0,&kmp,W0ID_VERSION); + printf("%s> WIM0 section (%u bytes total, %u payload) found, ", + col->caption, kmp.wim0_import_size, kmp.wim0_size ); + if (kmp.wim0_import_bz2) + fputs("bzip2, ",stdout); + + if (sect) + printf("version %s%s\n", (ccp)sect->data, col->reset ); + else + printf("no version info found.%s\n", col->reset ); + + if ( long_count > 0 ) + { + kmp_wim0_info_t info; + bool avail; + for ( avail = GetFirstSectionWim0(&info,&kmp); + avail; + avail = GetNextSectionWim0(&info,&kmp) ) + { + printf("%s index %d, offset 0x%x, %u bytes, section %s, type %s%s\n", + col->heading, + info.index, info.offset, info.size, + info.name, Wim0FormatName[info.format], + col->reset ); + if ( info.size > 0 ) + XDump( &xd, info.data, info.size, true ); + } + } + else + { + const uint fw = 103; + printf("%s%.*s%s\n" + "%sidx offset size section type data%s\n" + "%s%.*s%s\n", + col->heading, 3*fw, ThinLine300_3, col->reset, + col->heading, col->reset, + col->heading, 3*fw, ThinLine300_3, col->reset ); + + kmp_wim0_info_t info; + bool avail; + for ( avail = GetFirstSectionWim0(&info,&kmp); + avail; + avail = GetNextSectionWim0(&info,&kmp) ) + { + printf("%3d %05x %5u %-7s %-6s ", + info.index, info.offset, info.size, + info.name, Wim0FormatName[info.format] ); + + switch (info.format) + { + case W0F_TEXT: + printf("%.70s\n",(ccp)info.data); + break; + + default: + if ( info.size > 0 ) + XDump( &xd, info.data, info.size < 0x10 ? info.size : 0x10, true ); + else + putchar('\n'); + } + } + + printf( "%s%.*s%s\n", col->heading, 3*fw, ThinLine300_3, col->reset ); + } + } + ResetKMP(&kmp); + } + + putchar('\n'); + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wkmpt,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: err += ScanOptConst(optarg); break; + case GO_SCALE: err += ScanOptScale(optarg); break; + case GO_SHIFT: err += ScanOptShift(optarg); break; + case GO_XSS: err += ScanOptXSS(0,optarg); break; + case GO_YSS: err += ScanOptXSS(1,optarg); break; + case GO_ZSS: err += ScanOptXSS(2,optarg); break; + case GO_ROT: err += ScanOptRotate(optarg); break; + case GO_XROT: err += ScanOptXRotate(0,optarg); break; + case GO_YROT: err += ScanOptXRotate(1,optarg); break; + case GO_ZROT: err += ScanOptXRotate(2,optarg); break; + case GO_YPOS: err += ScanOptYPos(optarg); break; + case GO_TRANSLATE: err += ScanOptTranslate(optarg); break; + case GO_NULL: force_transform |= 1; break; + case GO_NEXT: err += NextTransformation(false); break; + case GO_ASCALE: err += ScanOptAScale(optarg); break; + case GO_AROT: err += ScanOptARotate(optarg); break; + case GO_TFORM_SCRIPT: err += ScanOptTformScript(optarg); break; + case GO_RM_GOBJ: err += ScanOptRmGobj(optarg); break; + case GO_BATTLE: err += ScanOptBattle(optarg); break; + case GO_EXPORT_FLAGS: err += ScanOptExportFlags(optarg); break; + case GO_ROUTE_OPTIONS: err += ScanOptRouteOptions(optarg); break; + case GO_WIM0: err += ScanOptWim0(optarg); break; + case GO_SLOT: err += ScanOptSlot(optarg); break; + case GO_DRAW: err += ScanOptDraw(optarg); break; + case GO_POS_FILE: err += ScanOptPosFile(optarg); break; + case GO_POS_MODE: err += ScanOptPosMode(optarg); break; + case GO_PNG: err += ScanOptPng(optarg); break; + case GO_LOAD_KCL: err += ScanOptLoadKcl(optarg); break; + case GO_KCL: err += ScanOptKcl(optarg); break; + case GO_KCL_FLAG: err += ScanOptKclFlag(optarg); break; + case GO_KCL_SCRIPT: err += ScanOptKclScript(optarg); break; + case GO_TRI_AREA: err += ScanOptTriArea(optarg); break; + case GO_TRI_HEIGHT: err += ScanOptTriHeight(optarg); break; + case GO_FLAG_FILE: opt_flag_file = optarg; break; + case GO_XTRIDATA: opt_xtridata = optarg ? str2ul(optarg,0,10) : 1; break; + case GO_KMP: err += ScanOptKmp(optarg); break; + case GO_SPEED_MOD: err += ScanOptSpeedMod(optarg); break; + case GO_KTPT2: err += ScanOptKtpt2(optarg); break; + case GO_TFORM_KMP: err += ScanOptTformKmp(optarg); break; + case GO_GAMEMODES: err += ScanOptGamemodes(optarg); break; + case GO_REPAIR_XPF: err += ScanOptRepairXPF(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + case GO_TINY: err += ScanOptTiny(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; +// case GO_FULL: full_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_EXPORT: export_count++; break; + case GO_EPSILON: err += ScanOptEpsilon(optarg); break; + case GO_DIFF: err += ScanOptKmpDiff(optarg); break; + case GO_NO_PARAM: print_param = false; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_GENERIC: opt_generic = true; break; + case GO_NO_CHECK: opt_no_check = true; break; + case GO_SECTIONS: print_sections++; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wkmpt,TRACE_FILE,11); + #endif + CloseTransformation(); + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + SetupKCL(); + SetupKMP(); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wkmpt,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wkmpt); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wkmpt,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_OBJECTS: err = cmd_objects(); break; + case CMD_EXPORT: err = cmd_export(); break; + case CMD_XEXPORT: err = cmd_xexport(); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupVarsKMP()); break; + case CMD_FUNCTIONS: SetupVarsKMP(); err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupVarsKMP()); break; + case CMD_MATRIX: err = cmd_matrix(); break; + case CMD_FLOAT: err = cmd_float(); break; + case CMD_STARTPOS: err = cmd_startpos(); break; + + case CMD_CAT: err = cmd_cat(); break; + case CMD_DECODE: err = cmd_convert(cmd_ct->id,"DECODE","\1P/\1N.txt"); break; + case CMD_ENCODE: err = cmd_convert(cmd_ct->id,"ENCODE","\1P/\1N\1?T"); break; + case CMD_DIFF: err = cmd_diff(); break; + case CMD_DRAW: err = cmd_draw(); break; + case CMD_CHECK: err = cmd_check(); break; + case CMD_STGI: err = cmd_stgi(); break; + case CMD_KTPT: err = cmd_ktpt(); break; + case CMD_ROUTES: err = cmd_routes(); break; + case CMD_GOBJ: err = cmd_gobj(); break; + case CMD_GAMEMODES: err = cmd_gamemodes(); break; + case CMD_WIM0: err = cmd_wim0(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wkmpt ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WKMPT_SHORT); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WKMPT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WKMPT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wlect.c b/project/src/wlect.c new file mode 100644 index 0000000..5794e6a --- /dev/null +++ b/project/src/wlect.c @@ -0,0 +1,1299 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-lecode.h" +#include "lib-szs.h" +#include "lib-xbmg.h" +//#include "lib-bzip2.h" + +#include "ui.h" // [[dclib]] wrapper +#include "ui-wlect.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WLECT_SHORT ": " WLECT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +static const char autoname[] = "/course.lex"; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// help /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wlect,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wlect,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WLECT_SHORT "\n" + "name=" WLECT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WLECT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + + printf(" print-header:%16x = %12d\n",print_header,print_header); + printf(" brief: %16x = %12d\n",brief_count,brief_count); + printf(" export: %16x = %12d\n",export_count,export_count); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 0 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 1 + + uint tr; + for ( tr = 0; tr < MKW_N_TRACKS + MKW_N_ARENAS; tr++ ) + { + const TrackInfo_t *info = GetTrackInfo(tr); + if (info) + printf(" %2u = %02x : 0x%02x\n",tr,tr,info->music_id); + //printf(" %2u = %02x : %s\n",tr,tr,PrintMusicID(tr,false)); + } + return ERR_OK; + + #elif 1 + + uint mi; + for ( mi = 0x75; mi <= 0xc9; mi += 2) + { + int ti = MusicID2TrackId(mi,-1,-1); + if ( ti > 0 ) + { + const TrackInfo_t *info = GetTrackInfo(ti); + printf("0x%02x = 0x%02x %2u %2u %s\n", + mi, info->music_id, + info->def_index, info->def_slot, info->sound_n_fname ); + } + } + return ERR_OK; + + #elif 1 + + uint ti; + for ( ti = 0; ti < MKW_N_TRACKS; ti++ ) + { + // find music slot + int mi, mslot = -1; + for ( mi = 0x75; mi <= 0xc9; mi += 2) + if ( MusicID2TrackId(mi,-1,-1) == ti ) + { + mslot = mi; + break; + } + + const TrackInfo_t *info = track_info + ti; + printf("\t{ %2u, \"%s\", \"T%02u\", 0x%02x },\n", + ti, info->name_en, info->def_slot, mslot ); + } + return ERR_OK; + + #elif 0 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetupVarsLECODE(); + return ExportHelper("lecode"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command dump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_dump ( int long_level ) +{ + if ( long_level > 0 ) + { + RegisterOptionByIndex(&InfoUI_wlect,OPT_LONG,long_level,false); + long_count += long_level; + } + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 ) + { + fprintf(stdlog,"\nDUMP %s:%s\n", GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + if ( raw.fform == FF_LE_BIN ) + { + le_analyse_t ana; + AnalyseLEBinary(&ana,raw.data,raw.data_size); + ApplyLEFile(&ana); + PatchLECODE(&ana); + DumpLEAnalyse(stdlog,2,&ana); + ResetLEAnalyse(&ana); + } + else if ( raw.fform == FF_LEX ) + { + putchar('\n'); + lex_t lex; + err = ScanRawLEX(&lex,true,raw.data,raw.data_size, + long_count > 2 ? -1 : + long_count > 1 ? 0x40 : + long_count > 0 ? 0x10 : 1 ); + ResetLEX(&lex); + } + else + { + ctcode_t ctcode; + err = ScanRawDataCTCODE(&ctcode,CTM_LECODE_DEF,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + DumpCTCODE(stdout,2,&ctcode); + ResetCTCODE(&ctcode); + } + fflush(stdout); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command bin-diff /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct lecode_bin_diff_t +{ + bool diff_size; + bool diff_header; + bool diff_timestamp; + bool diff_body; + bool diff_param; + bool diff_data; + bool diff_code; + + u8 stat_size; + u8 stat_header; + u8 stat_timestamp; + u8 stat_body; + u8 stat_param; + u8 stat_data; + u8 stat_code; + + enumError status; +} +lecode_bin_diff_t; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError LoadLECODE ( raw_data_t *raw, le_analyse_t *ana, ccp fname ) +{ + DASSERT(raw); + DASSERT(ana); + DASSERT(fname); + + InitializeRawData(raw); + memset(ana,0,sizeof(*ana)); + + enumError err = LoadRawData(raw,false,fname,0,false,0); + if (err) + goto abort; + + if ( verbose >= 0 || raw->fform != FF_LE_BIN ) + { + fprintf(stdlog,"File: %s:%s\n", GetNameFF(raw->fform,0), raw->fname ); + fflush(stdlog); + } + + if ( raw->fform != FF_LE_BIN ) + { + err = ERROR0(ERR_WRONG_FILE_TYPE,"Not a LECODE binary file: %s\n",raw->fname); + goto abort; + } + + err = AnalyseLEBinary(ana,raw->data,raw->data_size); + if (err) + { + err = ERROR0(ERR_INVALID_DATA,"Invalid LECODE data: %s\n",raw->fname); + goto abort; + } + + return ERR_OK; + + abort: + ResetLEAnalyse(ana); + ResetRawData(raw); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int BinDiffLECODE + ( lecode_bin_diff_t *bd, const le_analyse_t *a1, const le_analyse_t *a2 ) +{ + DASSERT(bd); + DASSERT(a1); + DASSERT(a2); + + + //--- clear "built on " + + static const char search[] = "built on "; + + const int bodysize1 = a1->size - a1->param_offset; + u8 *body1 = (u8*)a1->data + a1->param_offset; + + if ( bodysize1 > 0 ) + { + u8 *found = memmem(body1,bodysize1,search,sizeof(search)-1); + if (found) + { + PRINT0("FOUND: %zx %s\n",found-body1,found); + u8 *end = body1 + bodysize1; + while ( found && found < end && *found ) + *found++ = 0; + } + } + + const int bodysize2 = a2->size - a2->param_offset; + u8 *body2 = (u8*)a2->data + a2->param_offset; + + if ( bodysize2 > 0 ) + { + u8 *found = memmem(body2,bodysize2,search,sizeof(search)-1); + if (found) + { + PRINT0("FOUND: %zx %s\n",found-body2,found); + u8 *end = body2 + bodysize2; + while ( found && found < end && *found ) + *found++ = 0; + } + } + + + //--- compare ... + + const le_binary_head_t *h1 = a1->head; + const le_binary_head_t *h2 = a2->head; + + bd->stat_size = a1->size != a2->size; + bd->stat_header = memcmp(h1,h2,sizeof(*h1)) != 0; + + bd->stat_body + = bodysize1 < 0 + || bodysize2 < 0 + || bodysize1 != bodysize2 + || memcmp( body1, body2, bodysize1 ); + + ccp ts1 = h1->version == 4 ? a1->head_v4->timestamp : 0; + ccp ts2 = h2->version == 4 ? a2->head_v4->timestamp : 0; + bd->stat_timestamp = !ts1 || !ts2 || strcmp(ts1,ts2); + + bd->stat_param = memcmp(&a1->lpar,&a2->lpar,sizeof(a1->lpar)); + + bd->stat_data = a1->n_cup_track != a2->n_cup_track + || a1->n_cup_arena != a2->n_cup_arena + || a1->n_slot != a2->n_slot; + if (!bd->stat_data) + { + bd->stat_data + = memcmp( a1->cup_track, a2->cup_track, + a1->n_cup_track * sizeof(*a1->cup_track) ) + || memcmp( a1->cup_arena, a2->cup_arena, + a1->n_cup_arena * sizeof(*a1->cup_arena) ) + || memcmp( a1->property, a2->property, + a1->n_slot * sizeof(*a1->property) ) + || memcmp( a1->music, a2->music, + a1->n_slot * sizeof(*a1->music) ) + || memcmp( a1->flags, a2->flags, + a1->n_slot * sizeof(*a1->flags) ) + ; + } + + + //-- code is separated into 2 parts + + PRINT0("CODE1: %zx..%zx + %zx..%x\n", + body1 - a1->data, + a1->beg_of_data - a1->data, + a1->end_of_data - a1->data, + a1->size ); + PRINT0("CODE2: %zx..%zx + %zx..%x\n", + body2 - a2->data, + a2->beg_of_data - a2->data, + a2->end_of_data - a2->data, + a2->size ); + + bd->stat_code + = body1 - a1->data != body2 - a2->data + || a1->beg_of_data - a1->data != a2->beg_of_data - a2->data + || a1->end_of_data - a1->data != a2->end_of_data - a2->data + || a1->size != a2->size + ; + + if (!bd->stat_code) + { + int off1 = body1 - a1->data; + int off2 = a1->beg_of_data - a1->data; + if ( off1 < off2 && memcmp(a1->data+off1,a2->data+off1,off2-off1)) + bd->stat_code = true; + else + { + off1 = a1->end_of_data - a1->data; + off2 = a1->size; + if ( off1 < off2 && memcmp(a1->data+off1,a2->data+off1,off2-off1)) + bd->stat_code = true; + } + } + + + //--- final status + + int ec = 0; + if ( bd->diff_size && bd->stat_size ) { ec++; bd->stat_size = 2; } + if ( bd->diff_header && bd->stat_header ) { ec++; bd->stat_header = 2; } + if ( bd->diff_timestamp && bd->stat_timestamp ) { ec++; bd->stat_timestamp = 2; } + if ( bd->diff_body && bd->stat_body ) { ec++; bd->stat_body = 2; } + if ( bd->diff_param && bd->stat_param ) { ec++; bd->stat_param = 2; } + if ( bd->diff_data && bd->stat_data ) { ec++; bd->stat_data = 2; } + if ( bd->diff_code && bd->stat_code ) { ec++; bd->stat_code = 2; } + + return bd->status = ec ? ERR_DIFFER : ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_bin_diff() +{ + if ( n_param != 3 ) + return ERROR0(ERR_SYNTAX,"Exact 3 parameters expected for BIN-DIFF:" + " settings binary1 binary2\n"); + + + //--- scan settings + + lecode_bin_diff_t bd; + memset(&bd,0,sizeof(bd)); + + ParamList_t *param = first_param; + ccp arg = param->arg; + bool value = true; + + while (*arg) + { + char ch = tolower((int)*arg++); + switch (ch) + { + case '-': value = false; break; + case '+': value = true; break; + + case 's': bd.diff_size = value; break; + case 'h': bd.diff_header = value; break; + case 't': bd.diff_timestamp = value; break; + case 'b': bd.diff_body = value; break; + case 'p': bd.diff_param = value; break; + case 'd': bd.diff_data = value; break; + case 'c': bd.diff_code = value; break; + + case 'a': + bd.diff_size = value; + bd.diff_header = value; + bd.diff_timestamp = value; + bd.diff_body = value; + bd.diff_param = value; + bd.diff_data = value; + bd.diff_code = value; + break; + + default: + ERROR0(ERR_WARNING,"BIN-DIFF: Invalid setting: %c\n",ch); + break; + } + } + + + //--- open file 1 + + param = param->next; + NORMALIZE_FILENAME_PARAM(param); + + raw_data_t raw1; + le_analyse_t ana1; + + enumError err = LoadLECODE(&raw1,&ana1,param->arg); + if (err) + return err; + + + //--- open file 2 + + param = param->next; + NORMALIZE_FILENAME_PARAM(param); + + raw_data_t raw2; + le_analyse_t ana2; + + err = LoadLECODE(&raw2,&ana2,param->arg); + if (err) + return err; + + + //--- logging + + if ( verbose > 0 ) + { + fputs("\nCompare the follwong settings:\n",stdout); + if (bd.diff_size) fputs(" - File size (s)\n",stdout); + if (bd.diff_header) fputs(" - File header (h)\n",stdout); + if (bd.diff_timestamp) fputs(" - Timestamp (t)\n",stdout); + if (bd.diff_body) fputs(" - File body (b)\n",stdout); + if (bd.diff_param) fputs(" - Parameters (p)\n",stdout); + if (bd.diff_data) fputs(" - Cup and track data (d)\n",stdout); + if (bd.diff_code) fputs(" - Code (c)\n",stdout); + } + + + //-- diff + + err = BinDiffLECODE(&bd,&ana1,&ana2); + if ( verbose >= 0 ) + { + if (bd.stat_size>1) fputs("> File size differ\n",stdout); + if (bd.stat_header>1) fputs("> File header differ\n",stdout); + if (bd.stat_timestamp>1)fputs("> Timestamp differ\n",stdout); + if (bd.stat_body>1) fputs("> File body differ\n",stdout); + if (bd.stat_param>1) fputs("> Parameters differ\n",stdout); + if (bd.stat_data>1) fputs("> Cup and track data differ\n",stdout); + if (bd.stat_code>1) fputs("> Code differ\n",stdout); + + printf(">> Final status: %s (%d)\n", err == ERR_OK ? "same" : "DIFFER", err ); + } + + + //-- terminate + + ResetLEAnalyse(&ana1); + ResetLEAnalyse(&ana2); + ResetRawData(&raw1); + ResetRawData(&raw2); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command patch /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_patch() +{ + static ccp def_path = "\1P/\1F"; + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(raw.fform_file,raw.fform),false); + const bool dest_is_source = IsSameFilename(dest,param->arg); + + if ( verbose >= 0 || testmode ) + { + ccp ff_name = GetNameFF(raw.fform_file,raw.fform); + if (dest_is_source) + fprintf(stdlog,"%s%sPATCH %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + ff_name, dest ); + else + fprintf(stdlog,"%s%sPATCH %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + ff_name, raw.fname, + ff_name, dest ); + fflush(stdlog); + } + + if ( raw.fform != FF_LE_BIN ) + return ERROR0(ERR_INVALID_DATA,"Invalid file format: %s",raw.fname); + + le_analyse_t ana; + AnalyseLEBinary(&ana,raw.data,raw.data_size); + ApplyLEFile(&ana); + PatchLECODE(&ana); + err = SaveFILE( dest, 0, dest_is_source||opt_overwrite, + raw.data, raw.data_size, 0 ); + ResetLEAnalyse(&ana); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command create /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_create() +{ + stdlog = stderr; + + if ( n_param != 1 ) + { + fputs("\n" + "Use command CREATE without exact one keyword.\n" + "\n" + "Possible keywords:\n" + " LEX : Create a with all sections except »TEST«.\n" + " LEX+ : Create a complete LEX text file.\n" + " SET1 : Create a LEX text files with section »SET1« only.\n" + " CANNON : Create a LEX text files with section »CANN« only.\n" + " TEST : Create a LEX text files with section »TEST« only.\n" + " LPAR : Create a LPAR text file as template.\n" + #if HAVE_WIIMM_EXT + "\n" + "Hidden keyword:\n" + " DEVELOP : Like LEX+, but include all devoloper sections.\n" + #endif + "\n",stderr); + + hint_exit(ERR_SYNTAX); + } + + + //--- data + + enum { C_LEX, C_SET1, C_CANNON, C_HIDE_PT, C_TEST, C_LPAR }; + static const KeywordTab_t tab[] = + { + { C_LEX, "LEX", 0, 0 }, + { C_LEX, "LEX+", 0, 1 }, + { C_LEX, "DEVELOP", 0, 2 }, + { C_SET1, "SET1", 0, 0 }, + { C_CANNON, "CANNON", 0, 0 }, + { C_HIDE_PT, "HIPT", 0, 0 }, + { C_TEST, "TEST", 0, 0 }, + { C_LPAR, "LPAR", 0, 0 }, + { 0,0,0,0 } + }; + + + //--- scan keyword + + int cmd_stat; + const KeywordTab_t *cmd = ScanKeyword(&cmd_stat,first_param->arg,tab); + if (!cmd) + { + PrintKeywordError(tab,first_param->arg,cmd_stat,0,"sub command"); + hint_exit(ERR_SYNTAX); + } + + + //--- execute sub command + + enumError err = ERR_OK; + switch (cmd->id) + { + case C_LEX: + { + lex_t lex; + InitializeLEX(&lex); + lex.develop = cmd->opt > 1; + UpdateLEX(&lex,true,cmd->opt>0); + PatchLEX(&lex); + err = SaveTextLEX(&lex,"-",false); + ResetLEX(&lex); + } + break; + + case C_SET1: + { + lex_t lex; + InitializeLEX(&lex); + AppendSet1LEX(&lex,false); + err = SaveTextLEX(&lex,"-",false); + ResetLEX(&lex); + } + break; + + case C_CANNON: + { + lex_t lex; + InitializeLEX(&lex); + AppendCannLEX(&lex,false); + err = SaveTextLEX(&lex,"-",false); + ResetLEX(&lex); + } + break; + + case C_HIDE_PT: + { + lex_t lex; + InitializeLEX(&lex); + AppendHiptLEX(&lex,false); + err = SaveTextLEX(&lex,"-",false); + ResetLEX(&lex); + } + break; + + case C_TEST: + { + lex_t lex; + InitializeLEX(&lex); + lex_item_t * li = AppendTestLEX(&lex,false); + PatchTestLEX((lex_test_t*)li->elem.data,0); + err = SaveTextLEX(&lex,"-",false); + ResetLEX(&lex); + } + break; + + case C_LPAR: + { + le_lpar_t lpar; + InitializeLPAR(&lpar,true); + lpar.limit_mode = LPM_FORCE_AUTOMATIC; + err = SaveTextLPAR(&lpar,"-",false); + } + break; + } + + fflush(stdout); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat() +{ + stdlog = stderr; + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + if (IsLexFF(raw.fform)) + { + lex_t lex; + err = ScanRawDataLEX(&lex,true,&raw); + if ( err > ERR_WARNING ) + { + ResetLEX(&lex); + if (opt_ignore) + continue; + return err; + } + + if (!testmode) + { + err = SaveTextLEX(&lex,"-",false); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + ResetLEX(&lex); + } + } + else + { + ctcode_t ctcode; + err = ScanRawDataCTCODE(&ctcode,CTM_AUTO,&raw,global_check_mode); + if ( err > ERR_WARNING ) + { + ResetCTCODE(&ctcode); + if (opt_ignore) + continue; + return err; + } + + if (!testmode) + { + InitializeOutputMode(&output_mode); + err = SaveTextCTCODE(&ctcode,"-",false,&output_mode); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + ResetCTCODE(&ctcode); + } + } + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command lpar /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_lpar() +{ + stdlog = stderr; + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sLPAR %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + if ( raw.fform != FF_LE_BIN ) + return ERROR0(ERR_INVALID_DATA,"Invalid file format: %s",raw.fname); + + le_analyse_t ana; + AnalyseLEBinary(&ana,raw.data,raw.data_size); + ApplyLEFile(&ana); + PatchLECODE(&ana); + SaveTextLPAR(&ana.lpar,"-",false); + ResetLEAnalyse(&ana); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command encode/decode /////////////// +/////////////////////////////////////////////////////////////////////////////// +// cmd_encode(), cmd_decode() + +static enumError cmd_convert ( int cmd_id, ccp cmd_name, ccp def_path ) +{ + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,autoname,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + const file_format_t dest_ff = cmd_id == CMD_ENCODE ? FF_LEX : FF_LEX_TXT; + + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(dest_ff,0),false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", cmd_name, + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(dest_ff,0), dest ); + fflush(stdlog); + } + + lex_t lex; + err = ScanRawDataLEX(&lex,true,&raw); + if ( err > ERR_WARNING ) + { + ResetLEX(&lex); + if (opt_ignore) + continue; + return err; + } + + if (!testmode) + { + err = dest_ff == FF_LEX + ? SaveRawLEX(&lex,dest,opt_preserve) + : SaveTextLEX(&lex,dest,opt_preserve); + fflush(stdout); + if ( err > ERR_WARNING ) + return err; + } + ResetLEX(&lex); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wlect,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: err += ScanOptConst(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_LOAD_BMG: err += ScanOptLoadBMG(optarg); break; + case GO_PATCH_BMG: err += ScanOptPatchMessage(optarg); break; + case GO_MACRO_BMG: err += ScanOptMacroBMG(optarg); break; + case GO_PATCH_NAMES: opt_patch_names = true; break; + + case GO_ORDER_BY: opt_order_by = optarg; break; + case GO_ORDER_ALL: opt_order_all = true; break; + + case GO_LE_DEFINE: opt_le_define = optarg; break; + case GO_LPAR: opt_lpar = optarg; break; + case GO_ALIAS: err += ScanOptAlias(optarg); break; + case GO_ENGINE: err += ScanOptEngine(optarg); break; + case GO_200CC: err += ScanOpt200cc(optarg); break; + case GO_PERFMON: err += ScanOptPerfMon(optarg); break; + case GO_CUSTOM_TT: err += ScanOptCustomTT(optarg); break; + case GO_XPFLAGS: err += ScanOptXPFlags(optarg); break; + + case GO_TRACK_DIR: opt_track_dest = optarg; break; + case GO_COPY_TRACKS: err += ScanOptTrackSource(optarg,TFMD_COPY); break; + case GO_MOVE_TRACKS: err += ScanOptTrackSource(optarg,TFMD_MOVE); break; + case GO_MOVE1_TRACKS: err += ScanOptTrackSource(optarg,TFMD_MOVE1); break; + case GO_LINK_TRACKS: err += ScanOptTrackSource(optarg,TFMD_LINK); break; + + case GO_COMPLETE: opt_complete = true; break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_EXPORT: export_count++; print_header = false; break; + + case GO_LT_CLEAR: opt_lt_clear = true; break; + case GO_LT_ONLINE: err += ScanOptLtOnline(optarg); break; + case GO_LT_N_PLAYERS: err += ScanOptLtNPlayers(optarg); break; + case GO_LT_COND_BIT: err += ScanOptLtCondBit(optarg); break; + case GO_LT_GAME_MODE: err += ScanOptLtGameMode(optarg); break; + case GO_LT_ENGINE: err += ScanOptLtEngine(optarg); break; + case GO_LT_RANDOM: err += ScanOptLtRandom(optarg); break; + case GO_LEX_PURGE: opt_lex_purge = true; break; + + #if 0 // BMG disabled for wlect + case GO_RAW: raw_mode = true; break; + case GO_BMG_ENCODING: err += ScanOptBmgEncoding(optarg); break; + case GO_BMG_INF_SIZE: err += ScanOptBmgInfSize(optarg,false); break; + case GO_BMG_MID: err += ScanOptBmgMid(optarg); break; + case GO_FORCE_ATTRIB: err += ScanOptForceAttrib(optarg); break; + case GO_DEF_ATTRIB: err += ScanOptDefAttrib(optarg); break; + case GO_NO_ATTRIB: opt_bmg_no_attrib = true; break; + case GO_X_ESCAPES: opt_bmg_x_escapes = true; break; + case GO_OLD_ESCAPES: opt_bmg_old_escapes = true; break; + case GO_SINGLE_LINE: opt_bmg_single_line++; break; + case GO_NO_BMG_COLORS: opt_bmg_colors = 0; break; + case GO_BMG_COLORS: opt_bmg_colors = 2; break; + case GO_NO_BMG_INLINE: opt_bmg_inline_attrib = false; break; + #endif + + case GO_NO_PARAM: print_param = false; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_SECTIONS: print_sections++; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wlect,TRACE_FILE,11); + #endif + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + SetupBMG(0); + UsePatchingListBMG(&opt_load_bmg); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wlect,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wlect); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wlect,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_EXPORT: err = cmd_export(); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupVarsLECODE()); break; + case CMD_FUNCTIONS: SetupVarsLECODE(); err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupVarsLECODE()); break; + case CMD_FLOAT: err = cmd_float(); break; + + case CMD_DUMP: err = cmd_dump(0); break; + case CMD_DL: err = cmd_dump(1); break; + case CMD_DLL: err = cmd_dump(2); break; + case CMD_BIN_DIFF: err = cmd_bin_diff(); break; + case CMD_PATCH: err = cmd_patch(); break; + + case CMD_CREATE: err = cmd_create(); break; + case CMD_CAT: err = cmd_cat(); break; + case CMD_LPAR: err = cmd_lpar(); break; + case CMD_DECODE: err = cmd_convert(cmd_ct->id,"DECODE","\1P/\1N.txt"); break; + case CMD_ENCODE: err = cmd_convert(cmd_ct->id,"ENCODE","\1P/\1N\1?T"); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wlect ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WLECT_SHORT); + ctcode_enabled = true; + lecode_enabled = true; + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WLECT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WLECT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wmdlt.c b/project/src/wmdlt.c new file mode 100644 index 0000000..892c724 --- /dev/null +++ b/project/src/wmdlt.c @@ -0,0 +1,757 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-mdl.h" +#include "lib-szs.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wmdlt.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WMDLT_SHORT ": " WMDLT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wmdlt,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wmdlt,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WMDLT_SHORT "\n" + "name=" WMDLT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WMDLT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + + printf(" mdl modes: %16x = \"%s\"\n",MDL_MODE,GetMdlMode()); + printf(" patch files: %16x = \"%s\"\n",PATCH_FILE_MODE,GetFileClassInfo()); + DumpTransformationOpt(); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 0 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetupVarsMDL(); + return ExportHelper("mdl"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError iter_cat +( + mdl_t * mdl, // MDL data structure + void * param // a user defined parameter +) +{ + DASSERT(mdl); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(mdl->fform,0), mdl->fname ); + fflush(stdlog); + } + + if (!testmode) + { + const enumError err = SaveTextMDL(mdl,"-",false); + if ( err > ERR_WARNING ) + return err; + } + fflush(stdout); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + #if 1 + IterateRawDataMDL(&raw,global_check_mode,iter_cat,0); + #else + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + mdl_t mdl; + err = ScanRawDataMDL(&mdl,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = SaveTextMDL(&mdl,"-",false); + if ( err > ERR_WARNING ) + return err; + } + ResetMDL(&mdl); + #endif + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command encode/decode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_convert ( int cmd_id, ccp cmd_name, ccp def_path ) +{ + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + const file_format_t dest_ff = cmd_id == CMD_ENCODE ? FF_MDL : FF_MDL_TXT; + + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(dest_ff,0),false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", cmd_name, + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(dest_ff,0), dest ); + fflush(stdlog); + } + + mdl_t mdl; + err = ScanRawDataMDL(&mdl,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = dest_ff == FF_MDL + ? SaveRawMDL(&mdl,dest,opt_preserve) + : SaveTextMDL(&mdl,dest,opt_preserve); + if ( err > ERR_WARNING ) + return err; + } + ResetMDL(&mdl); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command strings /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError iter_strings +( + mdl_t * mdl, // MDL data structure + void * param // a user defined parameter +) +{ + DASSERT(mdl); + putchar('\n'); + PrintStringsMDL(stdout,0,mdl); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_strings() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + IterateRawDataMDL(&raw,global_check_mode,iter_strings,0); + } + + putchar('\n'); + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command geometry /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError iter_geometry +( + mdl_t * mdl, // MDL data structure + void * param // a user defined parameter +) +{ + DASSERT(mdl); + + if ( verbose >= 0 || testmode ) + { + printf("%sGeometry of %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(mdl->fform,0), mdl->fname ); + fflush(stdout); + } + + // ??? + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_geometry() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + IterateRawDataMDL(&raw,global_check_mode,iter_geometry,0); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _TEST /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError iter_xtest +( + mdl_t * mdl, // MDL data structure + void * param // a user defined parameter +) +{ + DASSERT(mdl); + + #if 1 // dump MDL section order ------------------------------------------- + + // old_mario_gc_*.szs 9,0,1,6,7,8, 2,3,4,5 + // all others: 11,0,1, 8,9,10,2,3,4,5 + static char order[] = {11,0,1,6,7,8,9,10,2,3,4,5,-1}; + static char ref_v8[20], ref_v11[20] = {-1}; + if ( ref_v11[0] == -1 ) + { + memset(ref_v11,0,sizeof(ref_v11)); + uint i; + for ( i = 0; order[i] >= 0; i++ ) + ref_v11[(int)order[i]] = i+2; + HexDump16(0,0,0,ref_v11,sizeof(ref_v11)); + + memcpy(ref_v8,ref_v11,sizeof(ref_v8)); + ref_v8[9] = 1; + } + ccp ref = mdl->version == 8 ? ref_v8 : ref_v11; + + printf("#ORDER: "); + char sep = ' '; + int sect = -1, last_ref = -1, fail = 0; + + SortMIL(&mdl->elem,true); + + const MemItem_t *mi = GetMemListElem(&mdl->elem,0,0); + const MemItem_t *mi_end = mi + mdl->elem.used; + for ( ; mi < mi_end; mi++ ) + { + if ( sect != mi->idx1 ) + { + sect = mi->idx1; + printf("%c%d",sep,sect); + sep = ','; + + if ( ref[sect] <= last_ref ) + fail++; + last_ref = ref[sect]; + } + } + printf(" %s: %s\n", fail ? "#FAIL! " : "", mdl->fname); + + #endif //------------------------------------------------------------------ + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_xtest() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + IterateRawDataMDL(&raw,global_check_mode,iter_xtest,0); + } + + putchar('\n'); + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wmdlt,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: err += ScanOptConst(optarg); break; + case GO_MDL: err += ScanOptMdl(optarg); break; + case GO_SCALE: err += ScanOptScale(optarg); break; + case GO_SHIFT: err += ScanOptShift(optarg); break; + case GO_XSS: err += ScanOptXSS(0,optarg); break; + case GO_YSS: err += ScanOptXSS(1,optarg); break; + case GO_ZSS: err += ScanOptXSS(2,optarg); break; + case GO_ROT: err += ScanOptRotate(optarg); break; + case GO_XROT: err += ScanOptXRotate(0,optarg); break; + case GO_YROT: err += ScanOptXRotate(1,optarg); break; + case GO_ZROT: err += ScanOptXRotate(2,optarg); break; + case GO_TRANSLATE: err += ScanOptTranslate(optarg); break; + case GO_NULL: force_transform |= 1; break; + case GO_NEXT: err += NextTransformation(false); break; + case GO_ASCALE: err += ScanOptAScale(optarg); break; + case GO_AROT: err += ScanOptARotate(optarg); break; + case GO_TFORM_SCRIPT: err += ScanOptTformScript(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + case GO_TINY: err += ScanOptTiny(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_NO_PARAM: print_param = false; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_NO_CHECK: opt_no_check = true; break; + case GO_SECTIONS: print_sections++; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wmdlt,TRACE_FILE,11); + #endif + CloseTransformation(); + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + SetupMDL(); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wmdlt,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wmdlt); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wmdlt,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_EXPORT: err = cmd_export(); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupVarsMDL()); break; + case CMD_FUNCTIONS: SetupVarsMDL(); err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupVarsMDL()); break; + case CMD_MATRIX: err = cmd_matrix(); break; + case CMD_FLOAT: err = cmd_float(); break; + + case CMD_CAT: err = cmd_cat(); break; + case CMD_DECODE: err = cmd_convert(cmd_ct->id,"DECODE","\1P/\1N.txt"); break; + case CMD_ENCODE: err = cmd_convert(cmd_ct->id,"ENCODE","\1P/\1N\1?T"); break; + case CMD_STRINGS: err = cmd_strings(); break; + case CMD_GEOMETRY: err = cmd_geometry(); break; + case CMD_XTEST: err = cmd_xtest(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wmdlt ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WMDLT_SHORT); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WMDLT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WMDLT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wpatt.c b/project/src/wpatt.c new file mode 100644 index 0000000..fd65bca --- /dev/null +++ b/project/src/wpatt.c @@ -0,0 +1,547 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-pat.h" +#include "lib-szs.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wpatt.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WPATT_SHORT ": " WPATT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wpatt,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wpatt,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WPATT_SHORT "\n" + "name=" WPATT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WPATT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + printf(" pat modes: %16x = \"%s\"\n",PAT_MODE,GetPatMode()); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 0 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetupVarsPAT(); + return ExportHelper("pat"); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError iter_cat +( + pat_t * pat, // PAT data structure + void * param // a user defined parameter +) +{ + DASSERT(pat); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(pat->fform,0), pat->fname ); + fflush(stdlog); + } + + if (!testmode) + { + const enumError err = SaveTextPAT(pat,"-",false); + if ( err > ERR_WARNING ) + return err; + } + fflush(stdout); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat() +{ + stdlog = stderr; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname ); + fflush(stdlog); + } + + pat_t pat; + err = ScanRawDataPAT(&pat,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = SaveTextPAT(&pat,"-",false); + if ( err > ERR_WARNING ) + return err; + } + ResetPAT(&pat); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command encode/decode /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_convert ( int cmd_id, ccp cmd_name, ccp def_path ) +{ + CheckOptDest(def_path,false); + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + char dest[PATH_MAX]; + const file_format_t dest_ff = cmd_id == CMD_ENCODE ? FF_PAT : FF_PAT_TXT; + + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(dest_ff,0),false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", cmd_name, + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(dest_ff,0), dest ); + fflush(stdlog); + } + + pat_t pat; + err = ScanRawDataPAT(&pat,true,&raw,global_check_mode); + if ( err > ERR_WARNING ) + return err; + + if (!testmode) + { + err = dest_ff == FF_PAT + ? SaveRawPAT(&pat,dest,opt_preserve) + : SaveTextPAT(&pat,dest,opt_preserve); + if ( err > ERR_WARNING ) + return err; + } + ResetPAT(&pat); + } + + ResetRawData(&raw); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wpatt,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: err += ScanOptConst(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + case GO_PAT: err += ScanOptPat(optarg); break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_NO_PARAM: print_param = false; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_NO_CHECK: opt_no_check = true; break; + case GO_SECTIONS: print_sections++; break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wpatt,TRACE_FILE,11); + #endif + //CloseTransformation(); + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + if (!err) + WarnDepractedOptions(&InfoUI_wpatt); + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + TRACE("CheckCommand(%d,) optind=%d\n",argc,optind); + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wpatt,cmd_ct); + if (err) + hint_exit(err); + } + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wpatt,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_EXPORT: err = cmd_export(); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupVarsPAT()); break; + case CMD_FUNCTIONS: SetupVarsPAT(); err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupVarsPAT()); break; + case CMD_FLOAT: err = cmd_float(); break; + + case CMD_CAT: err = cmd_cat(); break; + case CMD_DECODE: err = cmd_convert(cmd_ct->id,"DECODE","\1P/\1N.txt"); break; + case CMD_ENCODE: err = cmd_convert(cmd_ct->id,"ENCODE","\1P/\1N\1?T"); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wpatt ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WPATT_SHORT); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WPATT_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WPATT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wrapper-wbmgt.c b/project/src/wrapper-wbmgt.c new file mode 100644 index 0000000..9008610 --- /dev/null +++ b/project/src/wrapper-wbmgt.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wbmgt.c" diff --git a/project/src/wrapper-wctct.c b/project/src/wrapper-wctct.c new file mode 100644 index 0000000..01a33af --- /dev/null +++ b/project/src/wrapper-wctct.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wctct.c" diff --git a/project/src/wrapper-wimgt.c b/project/src/wrapper-wimgt.c new file mode 100644 index 0000000..25a6419 --- /dev/null +++ b/project/src/wrapper-wimgt.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wimgt.c" diff --git a/project/src/wrapper-wkclt.c b/project/src/wrapper-wkclt.c new file mode 100644 index 0000000..fe75acf --- /dev/null +++ b/project/src/wrapper-wkclt.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wkclt.c" diff --git a/project/src/wrapper-wkmpt.c b/project/src/wrapper-wkmpt.c new file mode 100644 index 0000000..ad8f8fb --- /dev/null +++ b/project/src/wrapper-wkmpt.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wkmpt.c" diff --git a/project/src/wrapper-wlect.c b/project/src/wrapper-wlect.c new file mode 100644 index 0000000..5453dcc --- /dev/null +++ b/project/src/wrapper-wlect.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wlect.c" diff --git a/project/src/wrapper-wmdlt.c b/project/src/wrapper-wmdlt.c new file mode 100644 index 0000000..873887c --- /dev/null +++ b/project/src/wrapper-wmdlt.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wmdlt.c" diff --git a/project/src/wrapper-wpatt.c b/project/src/wrapper-wpatt.c new file mode 100644 index 0000000..05e9da5 --- /dev/null +++ b/project/src/wrapper-wpatt.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wpatt.c" diff --git a/project/src/wrapper-wstrt.c b/project/src/wrapper-wstrt.c new file mode 100644 index 0000000..ed31b0d --- /dev/null +++ b/project/src/wrapper-wstrt.c @@ -0,0 +1,2 @@ +#define SZS_WRAPPER 1 +#include "wstrt.c" diff --git a/project/src/wstrt.c b/project/src/wstrt.c new file mode 100644 index 0000000..9ae3e33 --- /dev/null +++ b/project/src/wstrt.c @@ -0,0 +1,1485 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "lib-staticr.h" +#include "db-mkw.h" +#include "lib-mkw.h" +#include "dclib-utf8.h" +#include "dclib-xdump.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wstrt.c" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// functions not neded by wstrt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if !SZS_WRAPPER + bool DefineIntVar ( VarMap_t * vm, ccp varname, int value ) { return false; } +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WSTRT_SHORT ": " WSTRT_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wstrt,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wstrt,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WSTRT_SHORT "\n" + "name=" WSTRT_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WSTRT_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int ScanOptU64 ( u64 *num, ccp name, ccp arg ) +{ + DASSERT(num); + DASSERT(name); + + return ERR_OK != ScanSizeOptU64( + num, // u64 * num + arg, // ccp source + 1, // default_factor1 + 0, // int force_base + name, // ccp opt_name + 0, // u64 min + M1(*num), // u64 max + 1, // u32 multiple + 0, // u32 pow2 + true // bool print_err + ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_vsbt ( enum VsBtMode mode, ccp str, ccp std_str ) +{ + switch (mode) + { + case VSBT_OFF: + break; + + case VSBT_PATCH: + printf("%4s%-18c0x%02x 0x%02x = %c%c\n", + std_str, ':', str[0], str[1], str[0], str[1] ); + break; + + case VSBT_TEST: + printf("%4s%-23cTEST = %c1 %c2 %c3 %c4\n", + std_str, ':', + std_str[0], std_str[0], std_str[0], std_str[0] ); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + printf(" all-ranks: %16x = %12d\n",opt_all_ranks,opt_all_ranks); + + printf(" vs-region: %16x = %12d [%c%c]\n", + opt_str_vs_region, opt_str_vs_region, + opt_str_xvs_region ? 'x' : '-', + opt_str_tvs_region ? 't' : '-' ); + printf(" bt-region: %16x = %12d [%c%c]\n", + opt_str_bt_region, opt_str_bt_region, + opt_str_xbt_region ? 'x' : '-', + opt_str_tbt_region ? 't' : '-' ); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + if (opt_https) + { + printf(" https: %16x = '%s'\n", + opt_https, GetHttpsName(opt_https,"?") ); + printf(" domain:%14s%s\n","",mkw_domain); + } + + print_vsbt(patch_vs_mode,patch_vs_str,"vs"); + print_vsbt(patch_bt_mode,patch_bt_str,"bt"); + + if (opt_move_d8) + printf(" move-d8: true\n"); + + if (n_section_list) + { + printf(" create %u sections:\n",n_section_list); + uint s; + for ( s = 0; s < n_section_list; s++ ) + { + const dol_sect_select_t *sl = section_list + s; + printf("\t%s: %#10x %c : %s\n", + sl->name, sl->addr, sl->use_param ? 'P' : '-', sl->fname ); + } + } + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 0 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + //NORMALIZE_FILENAME_PARAM(param); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command dump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_dump ( bool use_c ) +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + if ( verbose >= 0 ) + printf("\n%sDUMP of %s:%s\n", use_c ? "C-" : "", + GetNameFF(str.fform,0), param->arg ); + DumpSTR(stdout,2,&str,use_c); + } + + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command hexdump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static XDump_t xparam; +static MemMap_t vaddr = {0}; +static MemMap_t faddr = {0}; +static uint dol_sections = 0; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_hexdump() +{ + //--- list addresses of option --vaddr and --faddr + + if ( logging > 1 ) + { + if (dol_sections) + fprintf(stdlog,"\nSections: %s\n", + DolSectionList2Text(dol_sections)); + + if (vaddr.used) + { + fputs("\nList of virtual addresses (option --vaddr):\n\n",stdlog); + PrintMemMap(&vaddr,stdlog,5,0); + } + + if (faddr.used) + { + fputs("\nList of file addresses (option --faddr):\n\n",stdlog); + PrintMemMap(&faddr,stdlog,5,0); + } + } + + SetupXDump(&xparam,XDUMPC_DUMP); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + + bool is_ok = err <= ERR_WARNING && err != ERR_NOT_EXISTS; + if ( is_ok && !str.is_dol ) + { + is_ok = false; + err = ERROR0(ERR_WRONG_FILE_TYPE, + "DOL expected, file ignored: %s\n",param->arg); + } + + if (is_ok) + { + const dol_header_t *dol = (dol_header_t*)str.data; + + MemMap_t mm; + InitializeMemMap(&mm); + if ( vaddr.used || faddr.used || dol_sections ) + { + CopyMemMap(&mm,&vaddr,true); + TranslateDolOffsets(dol,&mm,true,&faddr); + TranslateDolSections(dol,&mm,true,dol_sections); + } + else + TranslateAllDolOffsets(dol,&mm,true); + + if ( logging > 1 ) + { + fputs("\nList of virtual addresses to dump:\n\n",stdlog); + PrintMemMap(&mm,stdlog,5,0); + } + + if ( verbose >= 0 ) + printf("\nHEXDUMP of %s:%s\n", + GetNameFF(str.fform,0), param->arg ); + + uint mi; + for ( mi = 0; mi < mm.used; mi++ ) + { + const MemMapItem_t *src = mm.field[mi]; + uint addr = src->off; + uint size = src->size; + while ( size > 0 ) + { + u32 valid_size; + uint off = GetDolOffsetByAddr(dol,addr,size,&valid_size); + if ( !off || !valid_size ) + break; + xparam.start_addr = addr; + XDump(&xparam,str.data+off,valid_size,true); + addr += valid_size; + size -= valid_size; + } + } + ResetMemMap(&mm); + } + + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command https /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int compare_server_patch + ( const str_server_patch_t * a, const str_server_patch_t * b ) +{ + int stat = (int)a->mode - (int)b->mode; + if (!stat) + { + stat = (int)a->flags - (int)b->flags; + if (!stat) + { + stat = (int)a->serv_type - (int)b->serv_type; + if (!stat) + stat = (int)a->offset - (int)b->offset; + } + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_https() +{ + #if HAVE_ASSERT + { + const str_server_list_t *sl; + for ( sl = ServerList; sl->name; sl++ ) + ASSERT_MSG( sl->domain_len == strlen(sl->domain), + "ServerList[%i], len = %zu\n", + (int)(sl-ServerList), strlen(sl->domain) ); + } + #endif + + enumError max_err = ERR_OK; + + enum { LIST_SIZE = 1000 }; + str_server_patch_t patch_list[LIST_SIZE]; + memset(patch_list,0,sizeof(patch_list)); + uint n_list = 0; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + if ( max_err < err ) + max_err = err; + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + AnalyzeSTR(&str); + //printf("ST=%x,%x\n",str.str_status,str.dol_status); + if ( !( str.is_dol ? str.dol_status & DOL_S_ORIG : str.str_status & STR_S_ORIG )) + { + printf("\nIGNORE [NO ORIGINAL] %s:%s\n", GetNameFF(str.fform,0), param->arg ); + goto endloop; + } + + if ( verbose >= 0 ) + printf("SCAN %s:%s\n", GetNameFF(str.fform,0), param->arg ); + + const u8 *ptr = str.data; + const u8 *end = ptr + str.data_size; + while ( ptr < end ) + { + uint is_http; + const str_server_list_t *sl; + uint off; + + const u8 *ptr_h = memchr(ptr,'h',end-ptr); + const u8 *ptr_g = memchr(ptr,'g',end-ptr); + if (!ptr_h) + { + if (!ptr_g) + break; + ptr_h = ptr_g + 1; + } + else if (!ptr_g) + ptr_g = ptr_h + 1; + + if ( ptr_h < ptr_g ) + { + ptr = ptr_h; + if (!ptr) + break; + + if (!memcmp(ptr,"https://",8)) + { + off = 8; + is_http = 2; + } + else if (!memcmp(ptr,"http://",7)) + { + off = 7; + is_http = 1; + } + else if (!strcmp((ccp)ptr,"https")) + { + off = 5; + is_http = 2; + } + else + { + ptr++; + continue; + } +//printf(">H> %06x : %s\n",(int)(ptr-str.data),ptr); + sl = ServerList; + ptr += off; + if (*ptr) + { + for ( ; sl->name; sl++ ) + if ( ptr[sl->domain_len] == '/' + && !memcmp(ptr,sl->domain,sl->domain_len) ) + { + break; + } + if (!sl->name) + continue; + } + else if ( is_http != 2 ) + continue; + } + else + { + ptr = ptr_g; + if ( memcmp(ptr,"gs.nintendowifi.net",19) + || isalnum((int)ptr[19]) + || isalnum((int)ptr[-1]) ) + { + ptr++; + continue; + } +//printf(">G> %06x : %s\n",(int)(ptr-str.data),ptr); + + is_http = 0; + off = 19; + ptr += off; + sl = ServerList + SERV_GS; + } + + noPRINT(" -> %s\n",ptr); + if ( n_list >= LIST_SIZE ) + return ERROR0(ERR_FATAL,"Patch list full\n"); + + str_server_patch_t *pat = patch_list + n_list++; + pat->mode = str.mode; + pat->flags = str.is_dol; + pat->is_http = is_http; + pat->serv_type = sl->serv_type; + pat->offset = ptr - off - str.data; + pat->url_len = strlen((ccp)ptr) + off; + if (is_http) + ptr += sl->domain_len; + pat->param = STRDUP((ccp)ptr); + } + } + + endloop: + ResetSTR(&str); + } + + if ( n_list > 1 ) + qsort( patch_list, n_list, sizeof(*patch_list), + (qsort_func)compare_server_patch ); + + if (long_count>0) + { + printf("\nconst str_server_patch_t ServerPatch[%u+1] =\n{\n",n_list); + str_server_patch_t *pat = patch_list; + uint i, last_mode = pat->mode, last_flags = pat->flags; + for ( i = 0; i < n_list; i++, pat++ ) + { + if ( last_mode != pat->mode || last_flags != pat->flags ) + { + last_mode = pat->mode; + last_flags = pat->flags; + putchar('\n'); + } + + ccp name = ServerList[pat->serv_type].name; + printf(" { STR_M_%s,%u, %u,SERV_%s,%*s 0x%06x, %2u, \"%s\" },\n", + GetStrModeName(pat->mode), + pat->flags, + pat->is_http, + name, (int)(9-strlen(name)), "", + pat->offset, + pat->url_len, + pat->param ); + } + printf("\n {0,0,0,0,0,0,0}\n};\n\n"); + } + else + { + static ccp http_text[] = { " -- ", "HTTP ", "HTTPS" }; + printf("\n==> %u strings found:\n",n_list); + uint i; + const str_server_patch_t *pat = patch_list; + for ( i = 0; i < n_list; i++, pat++ ) + { + const int differ = memcmp(pat,ServerPatch+i,sizeof(*pat)); + printf(" %c %s %s %s %-10s %06x %2u %s\n", + differ ? '!' : ' ', + GetStrModeName(pat->mode), + pat->flags & STR_F_IS_DOL ? "DOL" : "REL", + http_text[pat->is_http], + ServerList[pat->serv_type].name, pat->offset, pat->url_len, + pat->param ); + if ( differ && verbose > 0 ) + { + HexDump(stdout,5,0,0,sizeof(*pat),pat,sizeof(*pat)); + HexDump(stdout,5,0,0,sizeof(*pat),ServerPatch+i,sizeof(*pat)); + } + } + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command extract /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_extract() +{ + if ( opt_dest && !*opt_dest ) + opt_dest = 0; + opt_mkdir = true; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + char dest[PATH_MAX]; + StringCat2S(dest,sizeof(dest),param->arg,".d"); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sEXTRACT %s:%s -> %s\n", + verbose > 1 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(str.fform,0), str.fname, dest ); + fflush(stdlog); + } + + if ( !opt_overwrite && !opt_update ) + { + struct stat st; + if (!stat(dest,&st)) + return ERROR0(ERR_ALREADY_EXISTS,"Destination already exists: %s",dest); + } + + ExtractSTR(&str,dest); + } + + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command tracks /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_tracks() +{ + const int mode = brief_count ? 2 : print_header ? 0 : 1; + if (!n_param) + { + DumpTrackList(stdout,0,mode); + return ERR_OK; + } + + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + printf("\n* Tracks of %s\n",param->arg); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + if (err) + { + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + continue; + } + + const u32 * offtab = GetTrackOffsetTab(&str); + if (offtab) + { + PatchSTR(&str); + + const u8 *d = str.data + *offtab; + u32 tab[MKW_N_TRACKS]; + uint i; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + tab[i] = be32( d + i*4 ) - MKW_TRACK_BEG; + DumpTrackList(stdout,tab,mode); + } + ResetSTR(&str); + } + putchar('\n'); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command arenas /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_arenas() +{ + const int mode = brief_count ? 2 : print_header ? 0 : 1; + if (!n_param) + { + DumpArenaList(stdout,0,mode); + return ERR_OK; + } + + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + printf("\n* Arenas of %s\n",param->arg); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + if (err) + { + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + continue; + } + + const u32 * offtab = GetArenaOffsetTab(&str); + if (offtab) + { + PatchSTR(&str); + + const u8 *d = str.data + *offtab; + u32 tab[MKW_N_ARENAS]; + uint i; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + tab[i] = be32( d + i*4 ) - MKW_ARENA_BEG; + DumpArenaList(stdout,tab,mode); + } + ResetSTR(&str); + } + putchar('\n'); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_files() +{ + if (!n_param) + { + DumpTrackFileList(stdout,0,0); + return ERR_OK; + } + + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + printf("\n* File list of %s\n",param->arg); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + if (err) + { + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + continue; + } + + const u32 * track_off_tab = GetTrackOffsetTab(&str); + const u32 * arena_off_tab = GetArenaOffsetTab(&str); + if ( track_off_tab && arena_off_tab ) + { + PatchSTR(&str); + + uint i; + u32 track_tab[MKW_N_TRACKS], arena_tab[MKW_N_ARENAS]; + const u8 *d = str.data + *track_off_tab; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + track_tab[i] = be32( d + i*4 ) - MKW_TRACK_BEG; + + d = str.data + *arena_off_tab; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + arena_tab[i] = be32( d + i*4 ) - MKW_ARENA_BEG; + DumpTrackFileList(stdout,track_tab,arena_tab); + } + ResetSTR(&str); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command find /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_found + ( uint idx1, uint idx2, uint n, const TrackInfo_t * info, char prefix ) +{ + DASSERT(info); + DASSERT(prefix); + if ( long_count > 2 ) + printf("%c%u.%u %2u %2u %s %s %s %s\n", prefix, + idx1/n+1, idx1%n+1, + idx1, idx2, + info->track_fname, info->sound_n_fname, info->sound_f_fname, + use_de ? info->name_de : info->name_en ); + else if ( long_count > 1 ) + printf("%c%u.%u %s %s %s %s\n", prefix, + idx1/n+1, idx1%n+1, + info->track_fname, info->sound_n_fname, info->sound_f_fname, + use_de ? info->name_de : info->name_en ); + else if ( long_count > 0 ) + printf("%c%u.%u %s\n", prefix, + info->def_slot/10, info->def_slot%10, + use_de ? info->name_de : info->name_en ); + else + printf("%c%u.%u\n", prefix, + info->def_slot/10, info->def_slot%10 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool find_track ( ccp name, bool allow_arenas ) +{ + int idx = ScanTrack(name); + if ( idx >= 0 ) + print_found(track_pos_r[idx],idx,4,track_info+idx,'T'); + else if (allow_arenas) + { + idx = ScanArena(name); + if ( idx >= 0 ) + print_found(arena_pos_r[idx],idx,5,arena_info+idx,'A'); + } + + if ( idx < 0 ) + { + printf("-\n"); + return false; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool find_arena ( ccp name, bool allow_tracks ) +{ + int idx = ScanArena(name); + if ( idx >= 0 ) + print_found(arena_pos_r[idx],idx,5,arena_info+idx,'A'); + else if (allow_tracks) + { + idx = ScanTrack(name); + if ( idx >= 0 ) + print_found(track_pos_r[idx],idx,4,track_info+idx,'T'); + } + + if ( idx < 0 ) + { + printf("-\n"); + return false; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_find() +{ + if (!n_param) + return ERROR0(ERR_SYNTAX,"Missing sub command for 'FIND'\n"); + + enum { SUBCMD_TRACKS, SUBCMD_ARENAS }; + + static const KeywordTab_t tab[] = + { + { SUBCMD_TRACKS, "TRACKS", 0, 0 }, + { SUBCMD_ARENAS, "ARENAS", 0, 0 }, + { SUBCMD_TRACKS, "+TRACKS", 0, 1 }, + { SUBCMD_ARENAS, "+ARENAS", 0, 1 }, + { 0,0,0,0 } + }; + + const KeywordTab_t * cmd = ScanKeyword(0,first_param->arg,tab); + if (!cmd) + return ERROR0(ERR_SYNTAX, + "Invalid sub command for 'FIND': %s\n",first_param->arg); + + enumError err = ERR_OK; + + DASSERT(first_param); + ParamList_t *param; + for ( param = first_param->next; param; param = param->next ) + { + char name[100]; + ccp arg = NormalizeTrackName(name,sizeof(name),param->arg); + if ( *arg || !*name ) + { + printf("-\n"); + err = ERR_NOT_EXISTS; + } + else + { + bool stat = cmd->id == SUBCMD_TRACKS + ? find_track( name, cmd->opt != 0 ) + : find_arena( name, cmd->opt != 0 ); + if (!stat) + err = ERR_NOT_EXISTS; + } + } + + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command points /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_points() +{ + if ( OptionUsed[OPT_POINTS] && n_param ) + { + ERROR0(ERR_WARNING, + "%u file parameter%s ignored because of --points\n", + n_param, n_param == 1 ? "" : "s" ); + n_param = 0; + } + + uint mode = brief_count ? 2 : 1; + u32 cheat_base = 0; + if ( opt_cheat_region != STR_M_UNKNOWN ) + { + const VersusPointsInfo_t *vpi = GetVersusPointsInfo(opt_cheat_region); + DASSERT(vpi); + mode = vpi->region_code; + cheat_base = vpi->cheat_base; + } + + if (!n_param) + { + const MkwPointInfo_t *mpi = GetMkwPointInfo(MkwPointsTab); + if ( mode < 'A' ) + printf("\nVersus points [%s]:\n",mpi->info); + PrintMkwPoints( stdout, 2, MkwPointsTab, mode, cheat_base, long_count ); + return ERR_OK; + } + + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + printf("\nVersus points of %s\n",param->arg); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + if (err) + { + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + continue; + } + + const uint offset = GetVersusPointsOffset(str.mode); + if (offset) + { + const MkwPointInfo_t *mpi = GetMkwPointInfo(str.data+offset); + printf("Type: %s\n",mpi->info); + PrintMkwPoints( stdout, 2, str.data+offset, mode, cheat_base, long_count ); + } + ResetSTR(&str); + } + putchar('\n'); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command analyze /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_analyze() +{ + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + printf("\n* Analyze %s\n",param->arg); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + if (err) + { + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + continue; + } + + PatchSTR(&str); + AnalyzeSTR(&str); + PrintStatusSTR(stdout,2,&str,long_count); + ResetSTR(&str); + } + putchar('\n'); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command patch /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_patch() +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + staticr_t str; + enumError err = LoadSTR(&str,true,param->arg,opt_ignore>0); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,0,0,false); + + if ( verbose >= 0 || testmode ) + { + if (strcmp(param->arg,dest)) + fprintf(stdlog,"%sPATCH %s -> %s\n", + testmode ? "WOULD " : "", param->arg, dest ); + else + fprintf(stdlog,"%sPATCH %s\n", + testmode ? "WOULD " : "", dest ); + } + + const uint patch_count = PatchSTR(&str); + + char path_buf[PATH_MAX]; + ccp dest_fname = param->arg; + if ( opt_dest && *opt_dest ) + { + if (IsDirectory(opt_dest,0)) + { + ccp slash = strrchr(param->arg,'/'); + dest_fname = PathCatPP(path_buf,sizeof(path_buf),opt_dest, + slash ? slash+1 : param->arg ); + } + else + dest_fname = opt_dest; + } + + bool create = false; + if (patch_count) + { + create = true; + if ( verbose >= 0 ) + printf("* %save patched file to: %s:%s\n", + testmode ? "Would s" : "S", + GetNameFF(str.fform,0), dest_fname ); + } + else if (strcmp(dest_fname,param->arg)) + { + create = true; + if ( verbose >= 0 ) + printf("* File not modified, %scopy to: %s:%s\n", + testmode ? "would " : "", + GetNameFF(str.fform,0), dest_fname ); + } + else + { + if ( verbose >= 0 ) + printf("* File not modified: %s:%s\n", + GetNameFF(str.fform,0), dest_fname); + } + + if (create) + { + File_t F; + err = CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + SetFileAttrib(&F.fatt,&str.fatt,0); + size_t wstat = fwrite(str.data,1,str.data_size,F.f); + if ( wstat != str.data_size ) + err = FILEERROR1(&F, + ERR_WRITE_FAILED,"Write failed: %s\n", + param->arg); + } + ResetFile(&F,opt_preserve); + } + } + + if ( max_err < err ) + max_err = err; + ResetSTR(&str); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wstrt,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + case GO_AT_DUMMY: break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: break; // [[2do]] err += ScanOptConst(optarg); break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + + case GO_CLEAN_DOL: opt_clean_dol = true; break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + case GO_REGION: err += ScanOptRegion(optarg); break; + case GO_VS_REGION: err += ScanOptVersusRegion(optarg); break; + case GO_BT_REGION: err += ScanOptBattleRegion(optarg); break; + case GO_ALL_RANKS: err += ScanOptAllRanks(optarg); break; + case GO_POINTS: err += ScanOptMkwPoints(optarg,false); break; + case GO_CHEAT: err += ScanOptCheatRegion(optarg); break; + case GO_ADD_LECODE: AppendStringField(&opt_wcode_list,"@LECODE",false); break; + case GO_ADD_CTCODE: opt_add_ctcode = true; break; + case GO_CT_DIR: AppendStringField(&ct_dir_list,optarg,false); break; + case GO_MOVE_D8: opt_move_d8 = true; break; + case GO_ADD_SECTION: AppendStringField(&opt_sect_list,optarg,false); break; + case GO_FULL_GCH: opt_full_gch = true; break; + + case GO_GCT_SEP: opt_gct_sep = true; break; + case GO_GCT_ASM_SEP: opt_gct_asm_sep = true; break; + case GO_GCT_LIST: opt_gct_list++; break; + case GO_GCT_MOVE: err += ScanOptGctMove(optarg); break; + case GO_GCT_ADDR: err += ScanOptGctAddr(optarg); break; + case GO_GCT_SPACE: err += ScanOptGctSpace(optarg); break; + case GO_ALLOW_USER_GCH: err += ScanOptAllowUserGch(optarg); break; + case GO_GCT_NO_SEP: opt_gct_scan_sep = false; break; + + case GO_CREATE_SECT: err += ScanOptCreateSect(optarg); break; + case GO_WPF: AppendStringField(&opt_wpf_list,optarg,false); break; + + case GO_HTTPS: err += ScanOptHttps(optarg); break; + case GO_DOMAIN: err += ScanOptDomain(0,optarg); break; + case GO_WIIMMFI: err += ScanOptDomain("domain","wiimmfi.de"); break; + case GO_TWIIMMFI: err += ScanOptDomain("domain","test.wiimmfi.de"); break; + case GO_WC24: opt_wc24 = true; break; + case GO_WCODE: err += ScanOptWCode(optarg); break; + case GO_ADD_WCODE: AppendStringField(&opt_wcode_list,optarg,false); break; + case GO_PB_MODE: err += ScanOptPBMode(optarg); break; + case GO_PATCHED_BY: err += ScanOptPatchedBy(optarg); break; + case GO_VS: err += ScanOptVS(0,0,optarg); break; + case GO_VS2: err += ScanOptVS(0,1,optarg); break; + case GO_BT: err += ScanOptVS(1,0,optarg); break; + case GO_BT2: err += ScanOptVS(1,1,optarg); break; + case GO_CANNON: err += ScanOptCannon(optarg); break; + case GO_MENO: opt_meno = true; break; + + case GO_LONG: long_count++; break; + case GO_BRIEF: brief_count++; break; + case GO_NO_HEADER: print_header = false; break; + case GO_SECTIONS: print_sections++; break; + + case GO_VADDR: InsertAddressMemMap(&vaddr,true,optarg,16); break; + case GO_FADDR: InsertAddressMemMap(&faddr,true,optarg,16); break; + case GO_SNAME: dol_sections |= ScanDolSectionList(optarg,0); break; + + case GO_INT1: + case GO_INT2: + case GO_INT3: + case GO_INT4: + case GO_INT5: + case GO_INT6: + case GO_INT7: + case GO_INT8: xparam.format = opt_stat - GO_INT1 + XDUMPF_INT_1; break; + case GO_FLOAT: xparam.format = XDUMPF_FLOAT; break; + case GO_DOUBLE: xparam.format = XDUMPF_DOUBLE; break; + + case GO_LE: xparam.endian = DC_LITTLE_ENDIAN; break; + case GO_BE: xparam.endian = DC_BIG_ENDIAN; break; + + case GO_ZEROS: xparam.mode_zero = true; break; + case GO_HEX: xparam.mode_dec = false; break; + case GO_DEC: xparam.mode_dec = true; break; + case GO_C_SYNTAX: xparam.mode_c = true; break; + + case GO_ADDR: err += ScanOptU64(&xparam.start_addr,"addr",optarg); break; + case GO_ALIGN: xparam.mode_align = true; break; + case GO_TRIGGER: err += ScanOptU64(&xparam.trigger,"trigger",optarg); + xparam.have_trigger = true; break; + case GO_NO_ADDR: xparam.print_addr = false; break; + case GO_NO_NUMBERS: xparam.print_number = false; break; + case GO_NO_TEXT: xparam.print_text = false; break; + case GO_FORMAT: xparam.print_format = true; break; + case GO_NO_NULL: xparam.mode_ignore = true; break; + + case GO_NARROW: xparam.extra_space = -1; break; + case GO_BYTES: xparam.min_width = str2ul(optarg,0,10); break; + + case GO_SMALL: + if ( !xparam.min_width ) + xparam.min_width = 8; + else if ( xparam.min_width > 1 ) + xparam.min_width /= 2; + break; + + case GO_WIDE: + if ( xparam.min_width < 32 ) + xparam.min_width = 32; + else + xparam.min_width = ( xparam.min_width / 16 + 1 ) * 16; + break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wstrt,TRACE_FILE,11); + #endif + NormalizeOptions( verbose > 3 && !is_env ? 2 : 0 ); + + if (!is_env) + { + if ( opt_wcode == WCODE_AUTO ) + opt_wcode = wifi_domain_is_wiimmfi || opt_wcode_list.used + ? WCODE_ON : WCODE_OFF; + if ( opt_wcode >= WCODE_ON && opt_gct_move < OFFON_FORCE ) + opt_gct_move = OFFON_FORCE; + } + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wstrt,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wstrt); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + extern const data_tab_t maindol_data_tab[]; + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wstrt,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); + break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_RAWDUMP: err = cmd_rawdump(maindol_data_tab); break; + + case CMD_DUMP: err = cmd_dump(false); break; + case CMD_CDUMP: err = cmd_dump(true); break; + case CMD_HEXDUMP: err = cmd_hexdump(); break; + case CMD_HTTPS: err = cmd_https(); break; + case CMD_EXTRACT: err = cmd_extract(); break; + + case CMD_TRACKS: err = cmd_tracks(); break; + case CMD_ARENAS: err = cmd_arenas(); break; + case CMD_FILES: err = cmd_files(); break; + case CMD_FIND: err = cmd_find(); break; + case CMD_POINTS: err = cmd_points(); break; + + case CMD_ANALYZE: err = cmd_analyze(); break; + case CMD_PATCH: err = cmd_patch(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if SZS_WRAPPER + int main_wstrt ( int argc, char ** argv ) +#else + int main ( int argc, char ** argv ) +#endif +{ + print_title_func = print_title; + SetupLib(argc,argv,WSTRT_SHORT); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WSTRT_SHORT ); + hint_exit(ERR_OK); + } + + InitializeXDump(&xparam); + xparam.f = stdlog; + xparam.indent = 2; + xparam.endian = DC_BIG_ENDIAN; + xparam.min_addr_fw = 8; + xparam.print_format = false; + xparam.print_summary= false; + + enumError err = CheckEnvOptions("WSTRT_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/src/wszst.c b/project/src/wszst.c new file mode 100644 index 0000000..21923e4 --- /dev/null +++ b/project/src/wszst.c @@ -0,0 +1,5896 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include +#include + +#include "dclib-utf8.h" +#include "lib-szs.h" +#include "lib-brres.h" +#include "lib-xbmg.h" +#include "lib-kcl.h" +#include "lib-kmp.h" +#include "lib-lecode.h" +#include "lib-mdl.h" +#include "lib-pat.h" +#include "lib-breff.h" +#include "lib-image.h" +#include "lib-common.h" +#include "lib-rkg.h" +#include "crypt.h" +#include "ui.h" // [[dclib]] wrapper +#include "ui-wszst.c" +#include "db-mkw.h" +#include "lib-object.h" +#include "lib-checksum.h" + +#if HAVE_WIIMM_EXT + #include "lib-vehicle.h" + #include "wcommand.h" +#endif + +#include "distrib.inc" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TITLE WSZST_SHORT ": " WSZST_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +#define TOOLSET_TITLE TOOLSET_SHORT ": " TOOLSET_LONG " v" VERSION " r" REVISION \ + " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// + +static void help_exit ( bool xmode ) +{ + fputs( TITLE "\n", stdout ); + + if (xmode) + { + int cmd; + for ( cmd = 0; cmd < CMD__N; cmd++ ) + PrintHelpCmd(&InfoUI_wszst,stdout,0,cmd,0,0,URI_HOME); + } + else + PrintHelpCmd(&InfoUI_wszst,stdout,0,0,"HELP",0,URI_HOME); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void list_compressions_exit() +{ + SetupColors(); + const int fw = GetTermWidth(80,40) - 1; + PrintColoredLines(stdout,colout,0,fw,0,0, + "\n{setup|%s}\n\n{caption|Compression modes:}\n" + "\n" + "There are several compression methods and most of them" + " support different compression levels." + " Each methods and level can be address by a numerical mode" + " and some by a name.\n" + "\n" + "In general, 2 compression formats are supported: YAZ0/YAZ1 and BZ (bzip2).\n" + "\n" + "The following list shows all modes." + " A mode can by selected by option {opt|--compr=param} or {opt|-C param}.\n" + "|[3,9,23]\n" + "{heading| numbers\tnames\tdescription}\n" + "{heading|%.*s\n" + "\t{hl| -1\tUNCOMPRESSED}\t|" + "Don't compress.\n" + "\t{hl| 0\tNOCHUNKS}\t|" + "{cmd|YAZ} only: Use only byte copies.\n" + "\n" + "\t{hl| 1\tFAST}\t|" + "Fastest available standard compression.\n" + "\t{hl|1-8\t}\t|" + "Standard levels between {par|FAST} and {par|BEST}.\n" + "\t{hl| 9\tBEST}\t|" + "Best and {par|default} standard compression.\n" + "\n" + "\t\t{hl|TRY2 - TRY5}\t|" + "Because of many repeated data, the best {cmd|BZ} compression mode varies." + " Therefor the levels {par|TRY2} to {par|TRY5} (or short {par|T2} to {par|T5})" + " are defined to find the best compression mode with testing the first" + " N levels of {par|9, 1, 8, 2, 5}. {par|TRY2} (levels 9 and 1)" + " is used for normalizing." + " For other than {cmd|BZ} compressions, {par|BEST} is used instead.\n" + "\n" + "\t{hl| 10\tULTRA}\t|" + "A special time-consuming compression method for {cmd|YAZ} only." + " It is dedicated to competitions with strict size limitations.\n" + "\n" + " {hl|100-150}\t\t|" + "This {cmd|YAZ} compression uses a back tracking" + " algorithm with recursion depth between {par|0 and 50} (last 2 digits)." + " Depths 0 and 1 are similar to mode {par|9}." + " Each new depth doubles the number of calculated pathes." + " Because of some optimizations, a depth of +5 results" + " in 10 to 15 times and not in 32 (2^5) times like expected." + " This algorithm needs 4 times as much memory as the uncompressed file." + " So it is dedicated to small files only.\n" + #if HAVE_WIIMM_EXT && 0 + "{heading|%.*s\n" + " {bad|500-530}\t\t|" + "This {bad|experimental} {cmd|YAZ} compression uses a brute force" + " algorithm with recursion depth from {par|0 to 30} (last 2 digits)." + " It is a very time-consuming compression method" + " and creates smaller files than {par|ULTRA}.\n" + " {bad|600-630}\t\t|" + "This {bad|experimental} {cmd|YAZ} compression" + " is an optimized version of {par|100-130}.\n" + #endif + "{heading|%.*s\n" + "\n" + ,TITLE + ,3*fw,ThinLine300_3 + #if HAVE_WIIMM_EXT && 0 + ,3*fw,ThinLine300_3 + #endif + ,3*fw,ThinLine300_3 + ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_version_section ( bool print_sect_header ) +{ + if (print_sect_header) + fputs("[version]\n",stdout); + + const u32 base = 0x04030201; + const u8 * e = (u8*)&base; + const u32 endian = be32(e); + + printf( "prog=" WSZST_SHORT "\n" + "name=" WSZST_LONG "\n" + "version=" VERSION "\n" + "beta=%d\n" + "revision=" REVISION "\n" + "system=" SYSTEM "\n" + "endian=%u%u%u%u %s\n" + "author=" AUTHOR "\n" + "date=" DATE "\n" + "url=" URI_HOME WSZST_SHORT "\n" + "\n" + , BETA_VERSION + , e[0], e[1], e[2], e[3] + , endian == 0x01020304 ? "little" + : endian == 0x04030201 ? "big" : "mixed" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void version_exit() +{ + if ( brief_count > 1 ) + fputs( VERSION "\n", stdout ); + else if (brief_count) + fputs( VERSION " r" REVISION " " SYSTEM "\n", stdout ); + else if (print_sections) + print_version_section(true); + else if (long_count) + print_version_section(false); + else + fputs( TITLE "\n", stdout ); + + exit(ERR_OK); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if (print_sections) + print_version_section(true); + else if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t * current_command = 0; + +static void hint_exit ( enumError stat ) +{ + if ( current_command ) + fprintf(stderr, + "-> Type '%s help %s' (pipe it to a pager like 'less') for more help.\n\n", + progname, CommandInfo[current_command->id].name1 ); + else + fprintf(stderr, + "-> Type '%s -h' or '%s help' (pipe it to a pager like 'less') for more help.\n\n", + progname, progname ); + exit(stat); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void set_all ( bool force_cut ) +{ + if ( all_count++ || force_cut ) + opt_cut = true; + opt_decode = opt_encode_all = true; + opt_mipmaps = 1; + opt_recurse = INT_MAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command test /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test_options() +{ + printf("\nOptions (compatibility: %s; format: hex=dec):\n",PrintOptCompatible()); + + printf(" test: %16x = %12d\n",testmode,testmode); + printf(" verbose: %16x = %12d\n",verbose,verbose); + printf(" force: %16x = %12d (kmp=%d)\n",force_count,force_count,force_kmp); + printf(" sort: %16x = %12d\n",opt_sort,opt_sort); + printf(" width: %16x = %12d\n",opt_width,opt_width); + printf(" escape-char: %16x = %12d\n",escape_char,escape_char); + printf(" align: %16x = %12d\n",opt_align,opt_align); + printf(" align-u8: %16x = %12d\n",opt_align_u8,opt_align_u8); + printf(" align-pack: %16x = %12d\n",opt_align_pack,opt_align_pack); + printf(" align-brres: %16x = %12d\n",opt_align_brres,opt_align_brres); + printf(" align-breff: %16x = %12d\n",opt_align_breff,opt_align_breff); + printf(" align-breft: %16x = %12d\n",opt_align_breft,opt_align_breft); + printf(" tiny: %16x = %12d\n",opt_tiny,opt_tiny); + printf(" compression: mode %2d, level %d\n",opt_compr_mode,opt_compr); + printf(" recurse: %16x = %12d\n",opt_recurse,opt_recurse); + printf(" cmpr-default: valid=%d, RGB565: %04x %04x, RGB: %06x %06x\n", + opt_cmpr_valid, + be16(opt_cmpr_def), be16(opt_cmpr_def+2), + RGB565_to_RGB(be16(opt_cmpr_def)), + RGB565_to_RGB(be16(opt_cmpr_def+2)) ); + printf(" n-mipmaps: %16x = %12d\n",opt_n_images-1,opt_n_images-1); + printf(" max-mipmaps: %16x = %12d\n",opt_max_images-1,opt_max_images-1); + printf(" mipmap-size: %16x = %12d\n",opt_min_mipmap_size,opt_min_mipmap_size); + printf(" mipmaps: %16x = %12d\n",opt_mipmaps,opt_mipmaps); + printf(" fast-mipmaps:%16x = %12d\n",fast_resize_enabled,fast_resize_enabled); + printf(" analyse-mode:%16x = %12d\n",opt_analyze_mode,opt_analyze_mode); + printf(" set-flags: %16x = %12d\n",set_flags,set_flags); + printf(" kcl modes: %16llx = \"%s\"\n",(u64)KCL_MODE,GetKclMode()); + + printf(" kmp modes: %16llx = \"%s\"\n",(u64)KMP_MODE,GetKmpMode()); + printf(" kmp tform: %16x = \"%s\"\n",KMP_TFORM,GetKmpTform()); + if (speed_mod_active) + printf(" kmp speed modifier: %04x ~ %5.3f\n", + speed_mod_val, speed_mod_factor ); + printf(" mdl modes: %16x = \"%s\"\n",MDL_MODE,GetMdlMode()); + printf(" pat modes: %16x = \"%s\"\n",PAT_MODE,GetPatMode()); + + if (have_patch_count) + printf(" patch count: %16x = %12d\n",have_patch_count,have_patch_count); + printf(" patch files: %16x = \"%s\"\n",PATCH_FILE_MODE,GetFileClassInfo()); + if (opt_slot) + printf(" slot: %16x = \"%s\"\n",opt_slot,PrintSlotMode(opt_slot)); + + DumpTransformationOpt(); + + if (opt_tracks) + DumpTrackList(0,0,0); + if (opt_arenas) + DumpArenaList(0,0,0); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_test() +{ + SetPatchFileModeReadonly(); + + #if 1 || !defined(TEST) // test options + + return cmd_test_options(); + + #elif 1 + + u8 * p1 = MALLOC(10); + u8 * p2 = CALLOC(10,20); + u8 * p3 = MALLOC(3000); + DUMP_TRACE_ALLOC(stderr); + p1[-1] = 'a'; + FREE(p1); + FREE(p2); + FREE(p3); + DUMP_TRACE_ALLOC(stderr); + return ERR_OK; + + #elif 1 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + SetupParam_t sp; + InitializeSetupParam(&sp); + enumError err = ScanSetupParam(&sp,true,param->arg,0,0,false); + printf("err=%d, ff=%s, pt-dir=%d : %s\n", + err, GetNameFF(sp.fform_file,sp.fform_arch), + sp.have_pt_dir, param->arg ); + ResetSetupParam(&sp); + } + return ERR_OK; + + #elif 1 + + const u64 base = 0x0807060504030201ull; + u64 val; + char buf[16]; + + const endian_func_t * endian = &be_func; + for(;;) + { + putchar('\n'); + + memset(buf,0,sizeof(buf)); + endian->wr16(buf,base); + HexDump16(stdout,0,0x16,buf,sizeof(buf)); + val = endian->rd16(buf); + ASSERT_MSG( val == (u16)base, "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr24(buf,base); + HexDump16(stdout,0,0x24,buf,sizeof(buf)); + val = endian->rd24(buf); + ASSERT_MSG( val == ( (u32)base & 0xffffff ), "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr32(buf,base); + HexDump16(stdout,0,0x32,buf,sizeof(buf)); + val = endian->rd32(buf); + ASSERT_MSG( val == (u32)base, "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr48(buf,base); + HexDump16(stdout,0,0x48,buf,sizeof(buf)); + val = endian->rd48(buf); + ASSERT_MSG( val == ( base & 0xffffffffffff ), "%llx %llx\n", val, base ); + + memset(buf,0,sizeof(buf)); + endian->wr64(buf,base); + HexDump16(stdout,0,0x64,buf,sizeof(buf)); + val = endian->rd64(buf); + ASSERT_MSG( val == base, "%llx %llx\n", val, base ); + + if ( endian == &le_func ) + break; + endian = &le_func; + } + putchar('\n'); + return ERR_OK; + + #elif 1 + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + ccp arg = param->arg; + char buf[1000]; + ccp subpath = SplitSubPath(buf,sizeof(buf),arg); + if (!subpath) + printf("NOT FOUND: |%s|\n",buf); + else if (!*subpath) + printf("REAL FILE: |%s|\n",buf); + else + printf("SUB PATH: |%s|%s|\n", buf, subpath ); + } + return ERR_OK; + + #endif +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command autoadd /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError exec_autoadd +( + UsedFileFILE_t *used, // vector with used files + ccp source // filename of source file +) +{ + DASSERT(used); + DASSERT(source); + PRINT("exec_autoadd() sizeof(used)==%zu\n",sizeof(*used)); + + char path_buf[PATH_MAX]; + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,source,true,opt_ignore>0,true); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sANALYZE %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF_SZS(&szs), szs.fname ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + CollectFilesSZS(&szs,true,0,-1,SORT_NONE); + + int idx; + for ( idx = 0; idx < N_DB_FILE_FILE; idx++ ) + { + if (used->d[idx]) + continue; + + const DbFileFILE_t *ptr = DbFileFILE + idx; + if (!DBF_ARCH_SUPPORT(ptr->flags)) + continue; + + szs_subfile_t *file, *file_end = szs.subfile.list + szs.subfile.used; + for ( file = szs.subfile.list; file < file_end; file++ ) + { + ccp fpath = file->path; + if( fpath[0] == '.' && fpath[1] == '/' ) + fpath += 2; + if (!strcmp(fpath,ptr->file)) + { + used->d[idx] = 2; + ccp path = PathCatPP(path_buf,sizeof(path_buf),opt_dest,ptr->file); + if ( verbose >= 0 || testmode ) + fprintf(stdlog," %sADD %7u, %s:%s\n", + testmode ? "WOULD " : "", + file->size, GetNameFF(0,ptr->fform), path ); + + File_t F; + CreateFileOpt(&F,true,path,testmode,0); + if (F.f) + { + if (opt_preserve) + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite( szs.data + file->offset, + 1, file->size, F.f ); + if ( wstat != file->size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %u bytes failed: %s\n", + file->size, path ); + } + ResetFile(&F,opt_preserve); + } + } + } + fflush(stdout); + } + + ResetSZS(&szs); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError find_autoadd +( + UsedFileFILE_t *used, // vector with used files + ccp source_dir, // filename of source directory + ccp name, // name of while without extension + ccp slot // NULL or slot prefix +) +{ + DASSERT(used); + DASSERT(source_dir); + DASSERT(name); + + struct stat st; + char pathbuf[PATH_MAX], fname[50]; + + int try; + for ( try = 0;; try++ ) + { + switch (try) + { + case 0: + snprintf(fname,sizeof(fname), + "%s", name ); + break; + + case 1: + snprintf(fname,sizeof(fname), + "%s_d", name ); + break; + + case 2: + if (!slot) + *fname = 0; + else + snprintf(fname,sizeof(fname), + "%s-%s", slot, name ); + + break; + + case 3: + if (!slot) + *fname = 0; + else + snprintf(fname,sizeof(fname), + "%s-%s_d", slot, name ); + break; + + default: + return ERR_OK; + } + + if (*fname) + { + ccp path = PathCatPPE(pathbuf,sizeof(pathbuf), source_dir, fname, ".szs" ); + PRINT("-> %s\n",path); + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + return exec_autoadd(used,path); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_autoadd() +{ + SetPatchFileModeReadonly(); + + char dest_path[PATH_MAX], path_buf[PATH_MAX]; + PathCatBufPP(dest_path,sizeof(dest_path),share_path,"auto-add/"); + CheckOptDest(dest_path,true); + + if ( !opt_mkdir && !ExistDirectory(opt_dest,0) ) + return ERROR0(ERR_CANT_CREATE_DIR, + "Directory does not exist: %s\n",opt_dest); + opt_mkdir = true; + + if (!n_param) + opt_overwrite = false; + + if ( !n_param || verbose >= 0 ) + fprintf(stdlog,"\nCURRENT AUTO-ADD PATH: %s\n",opt_dest); + + if ( !n_param || verbose >= 1 ) + { + SetupAutoAdd(); + ccp *aap; + for ( aap = auto_add_path; *aap; aap++ ) + fprintf(stdlog," SEARCH PATH[%td]: %s\n", + aap-auto_add_path,*aap); + } + + + //--- create file list + + UsedFileFILE_t used; + memset(&used,0,sizeof(used)); + uint n_invalid = 0; + + if ( !opt_overwrite || !n_param ) + { + if ( verbose >= 2 ) + fprintf(stdlog,"\nLIST OF FILES IN %s\n", opt_dest ); + + int count = 0; + const DbFileFILE_t *ptr; + for ( ptr = DbFileFILE; ptr->file; ptr++ ) + if (DBF_ARCH_SUPPORT(ptr->flags)) + { + ccp path = PathCatPP(path_buf,sizeof(path_buf),opt_dest,ptr->file); + struct stat st; + if ( !stat(path,&st) && S_ISREG(st.st_mode) ) + { + used.d[ptr-DbFileFILE] = 1; + if ( verbose >= 2 ) + { + count++; + fprintf(stdlog," %-5s %s\n", + GetNameFF(0,ptr->fform), ptr->file ); + } + + if ( verbose >= 1 ) + { + DASSERT( ptr->ref < N_DB_FILE_REF_FILE ); + const s16 ref = DbFileRefFILE[ptr->ref]; + noPRINT("%zd -> %d/%d : %d : %s\n", + ptr-DbFileFILE, ptr->ref ,ptr->group, ref, ptr->file ); + if ( ref >= 0 ) + { + DASSERT( ref < N_DB_FILE ); + const DbFile_t *db = DbFile + ref; + DASSERT( db->sha1 < N_DB_FILE_SHA1 ); + const u8 *sha1 = DbFileSHA1[db->sha1].sha1; + + sha1_size_t ss; + GetSSByFile(&ss,path,0); + if (memcmp(sha1,ss.hash,sizeof(ss.hash))) + { + n_invalid++; + used.d[ptr-DbFileFILE] = 2; + HexDump(stdout,0,0,4,20,sha1,20); + HexDump(stdout,0,0,4,20,ss.hash,20); + } + } + } + } + } + if (count) + fprintf(stdlog," => %u of %zu files found.\n",count,sizeof(used)); + } + + + //--- iterate parameters + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + if (!ExistDirectory(param->arg,0)) + { + enumError err = exec_autoadd(&used,param->arg); + if ( max_err < err ) + max_err = err; + } + else + { + const DbFileSZS_t *db; + for ( db = DbFileSZS; db->szs_name; db++ ) + if ( (db->type & (FLT_OLD|FLT_STD)) == FLT_STD ) + find_autoadd( &used, param->arg, db->szs_name, + db->idx < 0 ? 0 : db->id+1 ); + } + } + + + //--- print missing list + + if ( verbose >= 1 ) + { + fprintf(stdlog,"\nLIST OF MISSED FILES IN %s\n", opt_dest ); + + int count = 0; + const DbFileFILE_t *ptr; + for ( ptr = DbFileFILE; ptr->file; ptr++ ) + if ( !used.d[ptr-DbFileFILE] && DBF_ARCH_SUPPORT(ptr->flags) ) + { + count++; + fprintf(stdlog," %-5s %s\n", + GetNameFF(0,ptr->fform), ptr->file ); + } + if (count) + fprintf(stdlog," => %u of %zu files missed.\n",count,sizeof(used)); + } + + + //--- print invalid list + + if (n_invalid) + { + fprintf(stdlog,"\nLIST OF INVALID FILES IN %s\n", opt_dest ); + + const DbFileFILE_t *ptr; + for ( ptr = DbFileFILE; ptr->file; ptr++ ) + if ( used.d[ptr-DbFileFILE] == 2 ) + fprintf(stdlog," %-5s %s\n", + GetNameFF(0,ptr->fform), ptr->file ); + fprintf(stdlog," => %u of %zu files invalid.\n",n_invalid,sizeof(used)); + } + + if ( verbose >= 1 ) + fputs("\n",stdlog); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command files /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int files_func +( + ccp fname, // name of current file without '.szs' extension + ccp id, // unique ID of current file + FileList_t mode, // mode of current file + const TrackInfo_t *ti, // NULL or related track info + void *param // user defined parameter +) +{ + char name[BMG_MSG_BUF_SIZE]; + name[0] = 0; + const bmg_item_t *bi = 0; + + if ( ti && opt_load_bmg.item_used ) + { + bi = FindItemBMG(&opt_load_bmg,ti->track_id+MID_LE_TRACK_BEG); + if ( !bi || !bi->len ) + { + bi = FindItemBMG(&opt_load_bmg,ti->track_id+MID_CT_TRACK_BEG); + if ( !bi || !bi->len ) + { + bi = FindItemBMG(&opt_load_bmg,ti->bmg_mid); + if ( bi && !bi->len ) + bi = 0; + } + } + + if (bi) + { + PrintString16BMG(name,sizeof(name)-10,bi->text,bi->len,BMG_UTF8_MAX,0,0); + if (!strchr(name,'[')) + { + const uint pos = strlen(name); + snprintf(name+pos,sizeof(name)-pos," [%c%02u]", + mode & FLT_ARENA ? 'a' : 'r', ti->def_slot ); + } + } + } + + if (pipe_count) + { + if (verbose>0) + printf("%s|0x%02x|%s|%s\n",id,mode,fname,name); + else + printf("%s|%s|%s\n",id,fname,name); + } + else + { + if (verbose>0) + printf("%-5s 0x%02x ",id,mode); + else + printf("%-5s ",id); + + if (*name) + printf("%-21s %s\n",fname,name); + else + printf("%s\n",fname); + } + return 1; +} + +//----------------------------------------------------------------------------- + +static enumError cmd_tracks() +{ + SetPatchFileModeReadonly(); + + u32 mode = 0; + if (first_param) + { + ScanNumU32(first_param->arg,0,&mode,0,FLT_M_ALL); + mode &= FLT_M_ALL; + } + + if (!mode) + { + mode = FLT_TRACK | FLT_ARENA | FLT_STD; + + if ( all_count > 1 ) + mode = FLT_M_ALL; + else if ( all_count ) + mode |= FLT_D; + + if (long_count) + { + mode |= FLT_OTHER; + if ( long_count > 1 ) + mode |= FLT_OLD; + } + } + + IterateTrackFiles(mode,files_func,0); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command scancache /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_scancache() +{ + if (opt_cache) + AddParam(opt_cache); + if ( n_param != 1 ) + return ERROR0(ERR_SYNTAX,"Exact one parameter (directory) expected.\n"); + + if (!IsDirectory(first_param->arg,false)) + return ERROR0(ERR_SEMANTIC,"Directory expected: %s\n",first_param->arg); + + ScanSZSCache(first_param->arg,opt_purge); + return SaveSZSCache(); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command export /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_export() +{ + SetPatchFileModeReadonly(); + DefineDefaultParserFunc(); + return ExportHelper(""); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _CODE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_code() +{ + SetPatchFileModeReadonly(); + stdlog = stderr; + + if (!n_param) + AddParam("-"); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + if (verbose) + fprintf(stderr,"CODE %s\n",param->arg); + File_t F; + if (!OpenFILE(&F,true,param->arg,false,true)) + { + while ( !feof(F.f) && !ferror(F.f) ) + { + uint size = fread(iobuf,1,sizeof(iobuf),F.f); + u8 *src = (u8*)iobuf, *end = src + size; + while ( src < end ) + *src++ ^= 0xdc; + fwrite(iobuf,1,size,stdout); + } + } + ResetFile(&F,false); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _RECODE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef HAVE_WIIMM_EXT + + enumError cmd_recode() + { + ERROR0(ERR_NOT_IMPLEMENTED, + "Command _RECODE not implemented in this version!\n"); + exit(ERR_NOT_IMPLEMENTED); + } + +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command _SUBFILE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_subfile() +{ + if (!first_param) + return ERR_OK; + + SubDir_t dir; + InitializeSubDir(&dir); + + NORMALIZE_FILENAME_PARAM(first_param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,first_param->arg,false,opt_ignore>0,true); + if (!err) + { + DecompressSZS(&szs,true,0); + #ifdef TEST + { + printf("SZS: %p %zu / %p %zu\n",szs.cdata,szs.csize,szs.data,szs.size); + + DumpInfoContainer(stdlog,collog,2,"SZS: ",&szs.container,0x20); + szs_file_t szs2; + InitializeSubSZS(&szs2,&szs,4,12,FF_UNKNOWN,0,false); + printf("SZS2: %p %zu / %p %zu\n",szs2.cdata,szs2.csize,szs2.data,szs2.size); + DumpInfoContainer(stdlog,collog,2,"SZS1: ",&szs.container,0x20); + DumpInfoContainer(stdlog,collog,2,"SZS2: ",&szs2.container,0x20); + ResetSZS(&szs2); + DumpInfoContainer(stdlog,collog,2,"SZS: ",&szs.container,0x20); + } + #endif + DASSERT( !szs.file_size || szs.file_size >= szs.size ); + have_patch_count -= 1000000; + PRINT("EXTRACT/%s[%s]: %s\n",__FUNCTION__,GetNameFF_SZS(&szs),szs.fname); + err = ExtractFilesSZS(&szs,0,false,&dir,0); + have_patch_count += 1000000; + + if ( opt_dest && *opt_dest ) + { + DumpSubDir(stdout,2,&dir); + SaveSubFiles(&dir,opt_dest); + } + + SubFile_t *sf = FindSubFile(&dir,MemByString("3DModels(NW4R)/course")); + if (sf) + { + printf("** %s found [%p,0x%x] **\n",sf->fname,sf->data,sf->size); + #if 1 + //ccp err = RenameForSlot42MDL(sf->data,sf->size,ContainerSZS(&szs)); + ccp err = RenameForSlot42MDL(sf->data,sf->size,0); + if (err) + printf("\e[35;1m %s \e[0m\n",err); + printf(">> SAVE %p,0x%x\n",sf->data,sf->size); + SaveFile("_mdl.tmp",0,FM_OVERWRITE|FM_TOUCH,sf->data,sf->size,0); + #else + mdl_t mdl; + ScanMDL(&mdl,true,sf->data,sf->size,ContainerSZS(&szs),0); + SetupSlot42Materials(); + AppendMDL(&mdl,&slot42_mdl); + SaveRawMDL(&mdl,"_mdl.tmp",false); + ResetMDL(&mdl); + #endif + } + + szs_file_t szs2; + InitializeSZS(&szs2); + enumError err = CreateSZS(&szs2,0,0,&dir,0,0, + verbose > 0 ? UINT_MAX : 0, false ); + if (!err) + SaveSZS(&szs2,"_szs.tmp",true,false); + ResetSZS(&szs2); + } + ResetSubDir(&dir); + ResetSZS(&szs); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command BRSUB /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[check_brsub_t]] + +typedef struct check_brsub_t +{ + szs_file_t * szs; // pointer to valid SZS + int mode; // 0:warnings, 1:+hints, 2:+info + + bool header_printed; // true: header with filename printed + bool print_sep; // true: print separator + int sep_fw; // field width of separator + uint sep_count; // number of printed lines after last sep + + enumError max_err; // error code + ccp brres_path; // path of current BRRES + uint line_count; // number of printed status lines +} +check_brsub_t; + +/////////////////////////////////////////////////////////////////////////////// + +static int check_brsub +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + check_brsub_t *cb = (check_brsub_t*)it->param; + DASSERT(cb); + + if (term) + cb->brres_path = 0; + else if ( it->size >= 0x0c ) + { + u8 *data = it->szs->data + it->off; + const file_format_t fform = GetByMagicFF(data,it->size,it->size); + if ( fform == FF_BRRES ) + { + cb->brres_path = it->path; + cb->print_sep = print_header; + } + else if ( cb->brres_path && IsBRSUB(fform) ) + { + char stat_buf[50], vers_buf[50], ns_buf[50]; + const int version = it->endian->rd32(data+8); + const brsub_info_t *bi = GetInfoBRSUB(fform,version); + const brsub_info_t *bic = GetCorrectBRSUB(fform); + bool print_status = true; + enumError err = ERR_OK; + + if (!bi) + { + err = ERR_INVALID_DATA; + snprintf(stat_buf,sizeof(stat_buf), + "%sUnknown%s", colout->fatal, colout->reset ); + } + else + { + switch(bi->warn) + { + case BIMD_OK: + err = ERR_INVALID_DATA; + print_status = cb->mode > 1; + snprintf(stat_buf,sizeof(stat_buf), + "%sOk %s", colout->success, colout->reset ); + break; + + case BIMD_HINT: + err = ERR_INVALID_VERSION; + print_status = cb->mode > 0; + snprintf(stat_buf,sizeof(stat_buf), + "%sUnusual%s", colout->hint, colout->reset ); + break; + + case BIMD_FAIL: + err = ERR_INVALID_DATA; + snprintf(stat_buf,sizeof(stat_buf), + "%sFail %s", colout->warn, colout->reset ); + break; + + default: + //case BIMD_FATAL: + err = ERR_INVALID_DATA; + snprintf(stat_buf,sizeof(stat_buf), + "%sFreeze %s", colout->bad, colout->reset ); + break; + } + } + + if ( cb->max_err < err ) + cb->max_err = err; + + if (print_status) + { + if ( bic && version != bic->good_version ) + snprintf(vers_buf,sizeof(vers_buf), + "%s%3d!%s", colout->hint, version, colout->reset ); + else + snprintf(vers_buf,sizeof(vers_buf), "%3d ", version ); + + const int n_sect = GetGenericSectionNumBRSUB(data,it->size,it->endian); + if ( !bic || n_sect != bic->good_sect ) + snprintf(ns_buf,sizeof(ns_buf), + "%s%2d!%s", colout->fatal, n_sect, colout->reset ); + else + snprintf(ns_buf,sizeof(ns_buf), "%2d ", n_sect ); + + if (!cb->header_printed) + { + cb->header_printed = true; + cb->line_count = 999; + if (print_header) + printf( "\n%s%s:%s%s\n" + "%s%.*s\n" + "%s Status Type Ver N/s Filename of BRRES and sub file%s\n", + colout->caption, GetNameFF_SZS(cb->szs), + cb->szs->fname, colout->reset, + colout->heading, cb->sep_fw, ThinLine300_3, + colout->heading, colout->reset ); + else + printf("\n%s>%s:%s%s\n", + colout->caption, GetNameFF_SZS(cb->szs), + cb->szs->fname, colout->reset ); + } + + if ( cb->print_sep && cb->line_count > 2 ) + { + cb->print_sep = false; + cb->line_count = 0; + printf("%s%.*s%s\n", + colout->heading, cb->sep_fw, ThinLine300_3, colout->reset ); + } + + cb->sep_count++; + cb->line_count++; + printf(" %s %-3s %s %s %s / %s\n", + stat_buf, GetNameFF(0,bic?bic->fform1:fform), + vers_buf, ns_buf, + cb->brres_path, it->path ); + } + } + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError cmd_brsub() +{ + if (!n_param) + { + const int fw = 3*34; + if (print_header) + printf( "\n" + "%s%.*s\n" + "%s file format vers n(sect) warning%s\n", + colout->heading, fw, ThinLine300_3, + colout->heading, colout->reset ); + + file_format_t last_ff = 0; + const brsub_info_t *bi; + for ( bi = brsub_info; bi->fform1; bi++ ) + { + if ( print_header && last_ff != bi->fform1 ) + { + last_ff = bi->fform1; + if (print_header) + printf("%s%.*s%s\n", + colout->heading, fw, ThinLine300_3, colout->reset ); + } + + printf(" %-3s %-7s %3d %6u ", + GetNameFF(0,bi->fform1), + bi->fform2 ? GetNameFF(0,bi->fform2) : "-", + bi->version, bi->n_sect ); + switch(bi->warn) + { + case BIMD_OK: + fputs("-\n",stdout); + break; + + case BIMD_HINT: + printf("%sUNUSUAL%s\n",colout->hint,colout->reset); + break; + + case BIMD_FAIL: + printf("%sFAIL%s\n",colout->warn,colout->reset); + break; + + default: + //case BIMD_FATAL: + printf("%sFREEZE%s\n",colout->bad,colout->reset); + break; + } + } + + if ( print_header ) + printf("%s%.*s%s\n\n", + colout->heading, fw, ThinLine300_3, colout->reset ); + + return ERR_OK; + } + + //--- have param => check BRSUB versions + + szs_file_t szs; + InitializeSZS(&szs); + uint line_count = 0; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + ResetSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + if ( max_err < err ) + max_err = err; + + check_brsub_t cb; + memset(&cb,0,sizeof(cb)); + cb.szs = &szs; + cb.mode = long_count; + cb.sep_fw = 3*99; + IterateFilesSZS(&szs,check_brsub,&cb,false,-1,-1,SORT_NONE); + line_count += cb.line_count; + + if ( print_header && cb.header_printed ) + printf("%s%.*s%s\n", + colout->heading, cb.sep_fw, ThinLine300_3, colout->reset ); + + if ( max_err < cb.max_err ) + max_err = cb.max_err; + } + + if (line_count) + putchar('\n'); + ResetSZS(&szs); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int list_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if (term) + return 0; + + ccp col_reset = ""; + if (colorize_stdout) + { + if (it->is_dir) + { + fputs(colset->cmd,stdout); + col_reset = colset->reset; + } + else if ( it->has_subfiles ) + { + fputs(colset->heading,stdout); + col_reset = colset->reset; + } + } + + const int indent = 2 * it->recurse_level; + + if ( long_count > 1 ) + { + if (it->is_dir) + printf(" - - - - - %.*s%s%s\n", + indent, indent_msg, it->path, col_reset ); + else + { + char vers_buf[50]; + PrintVersionSZS(vers_buf,sizeof(vers_buf),it); + + const u8 * data = it->szs->data + it->off; + printf("%7x %7x %8u %-4s %s %.*s%s%s\n", + it->off, it->size, it->size, + PrintID(data, it->size < 4 ? it->size : 4, 0 ), + vers_buf, + indent,indent_msg, it->path, col_reset ); + } + } + else if ( long_count > 0 ) + { + if (it->is_dir) + printf(" - - %.*s%s%s\n", + indent,indent_msg, it->path, col_reset ); + else + printf("%8u %-4s %.*s%s%s\n", + it->size, + PrintID( it->szs->data+it->off, it->size < 4 ? it->size : 4, 0 ), + indent,indent_msg, it->path, col_reset ); + } + else + printf("%.*s%s%s\n",indent,indent_msg,it->path,col_reset); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_list ( int long_level ) +{ + SetPatchFileModeReadonly(); + + if ( opt_recurse < 0 ) + opt_recurse = 0; + + if ( long_level > 0 ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_LONG,long_level,false); + long_count += long_level; + } + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if (print_header) + { + printf("\n* Files of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + + static ccp f_or_d = "file or directory"; + if ( long_count > 2 ) + PrintFileHeadSZS(); + else if ( long_count > 1 ) + printf("\noff/hex siz/hex size/dec magic vers %s\n%.79s\n",f_or_d,Minus300); + else if ( long_count > 0 ) + printf("\nsize/dec magic %s\n%.79s\n",f_or_d,Minus300); + else + putchar('\n'); + } + else if ( n_param > 1 ) + printf("\n* Files of %s\n",param->arg); + + if ( long_count > 2 ) + IterateFilesSZS(&szs,PrintFileSZS,0,true,opt_recurse,opt_cut,opt_sort); + else + IterateFilesSZS(&szs,list_func,0,false,opt_recurse,opt_cut,opt_sort); + ResetSZS(&szs); + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command name-ref /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_name_ref() +{ + stdlog = stderr; + const SortMode_t std_sort = opt_sort == SORT_INAME ? SORT_INAME : SORT_BRRES; + + raw_data_t raw; + InitializeRawData(&raw); + + enumError cmd_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg, + "course_model.brres",opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + { + ResetRawData(&raw); + return err; + } + + if ( raw.fform != FF_BRRES && !IsBRSUB(raw.fform) ) + { + printf("No BRRES family -> ignore %s:%s\n", + GetNameFF(raw.fform,0), raw.fname ); + continue; + } + + szs_file_t szs; + AssignSZS(&szs,true,raw.data,raw.data_size,false,raw.fform,raw.fname); + raw.fname = NULL; + + name_ref_t nr; + err = CreateNameRef(&nr,true,&szs,brief_count); + if (err) + return err; + + if ( long_count > 0 ) + { + ListNameRef( stdout,0, &nr, std_sort ); + ListNameRef( stdout,0, &nr, SORT_OFFSET ); + if ( long_count > 1 ) + ListNameRef( stdout,0, &nr, SORT_NONE ); + } + else + ListNameRef( stdout,0, &nr, opt_sort ); + + ResetNameRef(&nr); + } + + ResetRawData(&raw); + return cmd_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command ilist /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int ilist_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + if (term) + return 0; + + if (!it->is_dir) + { + const u8 * data = it->szs->data+it->off; +// [[analyse-magic]] + PrintImage( data, it->size, it->path, + 0, 2 * it->recurse_level, long_count, + !it->has_subfiles + || GetByMagicFF(data,it->size,it->size) == FF_BREFF ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_ilist ( int long_level ) +{ + SetPatchFileModeReadonly(); + + if ( opt_recurse < 0 ) + opt_recurse = 0; + + if ( long_level > 0 ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_LONG,long_level,false); + long_count += long_level; + } + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if (print_header) + PrintImageHead(0,long_count); + else if ( n_param > 1 ) + printf("\n* Files of %s\n",param->arg); + + IterateFilesSZS(&szs,ilist_func,0,false,-1,0,opt_sort); + ResetSZS(&szs); + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command memory /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct dumpmem_t +{ + szs_file_t * szs; // the container + u8 * data_base; // pointer to base of complete file + uint last_off; // offset of last analyzed data + uint last_end; // end of last analyzed data + bool print_abs_off; // true: print absolute offset + +} dumpmem_t; + +/////////////////////////////////////////////////////////////////////////////// + +static void DumpUnused +( + dumpmem_t * dm, // dump info + uint offset, // offset of object to analyze and dump + file_format_t fform, // NULL for last entry or fileformat + uint recurse_level // recurse level +) +{ + DASSERT(dm); + + recurse_level *= 2; + if ( fform != FF_INVALID ) + { + if (dm->print_abs_off) + printf("%7x ",offset); + + ccp ff = GetNameFF(fform,0); + + if ( dm->last_end < offset ) + printf("%*s%-6.6s %6x ", + recurse_level, "", + ff,offset - dm->last_end ); + else if ( dm->last_end > offset ) + printf("%*s%-6.6s %6x-", + recurse_level, "", + ff, dm->last_end - offset ); + else + printf("%*s%-6.6s - ", + recurse_level, "", + ff ); + } + else if ( dm->last_end < offset ) + { + if (dm->print_abs_off) + printf("%7x ",offset); + + printf("%*s- %6x\n", + recurse_level, "", + offset - dm->last_end); + } + else if ( dm->last_end > offset ) + { + if (dm->print_abs_off) + printf("%7x ",offset); + + printf("%*s- %6x-\n", + recurse_level, "", + dm->last_end - offset); + } + + dm->last_off = dm->last_end = offset; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int memory_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + DASSERT(it); + DASSERT(it->szs); + DASSERT(it->param); + + dumpmem_t *dm = it->param; + + if (term) + { + const uint abs_off = it->szs->data - dm->data_base + it->szs->size; + noPRINT("TERM: %x %x %x\n",dm->last_off,dm->last_end,abs_off); + DumpUnused(dm,abs_off,FF_INVALID,it->recurse_level); + } + else if (!it->is_dir) + { + if (!it->client_int) + { + noPRINT("LAST: %x %x\n",dm->last_off, dm->last_end); + it->client_int++; + dm->last_end = dm->last_off; + } + + const u8 *data = it->szs->data + it->off; +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,it->size,it->size); + const uint abs_off = it->szs->data - dm->data_base + it->off; + DumpUnused(dm,abs_off,fform,it->recurse_level); + printf(" %6x .. %6x %6x %s\n", + it->off, it->off + it->size, it->size, it->path ); + + dm->last_off = abs_off; + dm->last_end = abs_off + it->size; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_memory() +{ + SetPatchFileModeReadonly(); + + if ( opt_recurse < 0 ) + opt_recurse = 0; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + dumpmem_t dm; + memset(&dm,0,sizeof(dm)); + dm.szs = &szs; + dm.data_base = szs.data; + dm.print_abs_off = long_count > 0; + + if (print_header) + { + printf("\n* memory dump of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + + fputs( dm.print_abs_off ? "\nabs off " : "\n", stdout); + printf( "type unused begin .. end size file name\n" + "%.80s\n", + Minus300 ); + } + + IterateFilesSZS(&szs,memory_func,&dm,false,opt_recurse,opt_cut,SORT_OFFSET); + ResetSZS(&szs); + } + + if (print_header) + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command dump /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_dump() +{ + SetPatchFileModeReadonly(); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( szs.fform_arch == FF_BRRES || IsBRSUB(szs.fform_arch) ) + { + printf("\n* Dump structure of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + DumpStructureBRRES(stdout,&szs); + } + else if (!opt_ignore) + ERROR0(ERR_WARNING,"No BRRES file -> ignored: %s\n",param->arg); + ResetSZS(&szs); + } + putchar('\n'); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command sha1 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_sha1() +{ + stdlog = stderr; + enumError max_err = ERR_OK; + SetPatchFileModeReadonly(); + SetupCoding64( opt_coding64, opt_db64 ? ENCODE_BASE64URL : ENCODE_BASE64 ); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( max_err < err ) + max_err = err; + if ( err > ERR_WARNING || err == ERR_NOT_EXISTS ) + { + if ( opt_db64 ) + { + //fputs("--------------------------------",stdout); + putchar('-'); + if (long_count) + fputs(" -1 -1 0.000 -",stdout); + if (brief_count) + putchar('\n'); + else + printf(" %s\n",param->arg); + } + + if ( err == ERR_NOT_EXISTS || opt_ignore ) + continue; + return err; + } + + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + char checksum[100]; + CreateSSChecksumBySZS(checksum,sizeof(checksum),&szs); + + if (opt_verify) + { + const uint clen = strlen(checksum); + ccp fname = strrchr(param->arg,'/'); + fname = fname ? fname+1 : param->arg; + bool found = false; + for(;;) + { + ccp pos = strstr(fname,checksum); + if (!pos) + break; + + if ( pos == param->arg || pos[-1] == '.' || pos[-1] == '-' ) + { + char ch = pos[clen]; + if ( !ch || ch == '.' || ch == '-' ) + { + found = true; + break; + } + } + + fname = pos+1; // search more + } + + if (!found) + { + if ( max_err < ERR_DIFFER ) + max_err = ERR_DIFFER; + if ( verbose >= 0 ) + printf("Checksum differ: %s\n",param->arg); + } + else if ( verbose > 0 ) + printf("Checksum ok: %s\n",param->arg); + } + else + { + if (!opt_db64) + { + if (long_count>1) + { + struct tm * tm = localtime(&szs.fatt.mtime); + char timbuf[40]; + strftime(timbuf,sizeof(timbuf),"%F %T ",tm); + fputs(timbuf,stdout); + } + if (long_count) + printf("%9zu,",szs.size); + } + + fputs(checksum,stdout); + + if ( opt_db64 && long_count ) + { + int ckpt0_count = -1; // number of LC in CKPT + int lap_count = 3; // STGI lap counter + float speed_factor = 1.0; // STGI speed factor + ccp slot_info = CreateSlotInfo(&szs); + + FindSpecialFilesSZS(&szs,false); + if (szs.course_kmp_data) + { + kmp_t kmp; + InitializeKMP(&kmp); + err = ScanKMP(&kmp,false,szs.course_kmp_data,szs.course_kmp_size,0); + if ( err <= ERR_WARNING ) + { + const uint n_ckpt = kmp.dlist[KMP_CKPT].used; + if (n_ckpt) + { + const kmp_ckpt_entry_t *ckpt + = (kmp_ckpt_entry_t*)kmp.dlist[KMP_CKPT].list; + uint i; + ckpt0_count = 0; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + if (!ckpt->mode) + ckpt0_count++; + } + + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.dlist[KMP_STGI].list; + if ( kmp.dlist[KMP_STGI].used > 0 ) + { + lap_count = stgi->lap_count; + + if (stgi->speed_mod) + speed_factor = SpeedMod2float(stgi->speed_mod); + } + } + ResetKMP(&kmp); + } + + printf(" %d %d %5.3f %s",ckpt0_count,lap_count,speed_factor,slot_info); + } + + if (brief_count) + putchar('\n'); + else + printf(" %s\n", param->arg ); + } + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command analyze /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_analyze() +{ + static ccp def_path = "\1P/\1F\1?T"; + CheckOptDest("-",false); + char dest[PATH_MAX]; + enumError max_err = ERR_OK; + if (!strcmp(opt_dest,"-")) + fputc('\n',stdout); + + szs_file_t szs; + InitializeSZS(&szs); + + File_t fo; + InitializeFile(&fo); + + PrintScript_t ps; + InitializePrintScript(&ps); + ps.fo = &fo; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + ResetSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( max_err < err ) + max_err = err; + + if (!fo.f) + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(script_fform,0),false); + + if ( verbose > 0 ) + { + fprintf(stdlog,"%sANALYZE %s:%s => %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(szs.fform_file,szs.fform_arch), szs.fname, + GetNameFF(script_fform,0), dest ); + fflush(stdlog); + } + + + //--- checksums + + sha1_size_t info; + SHA1(szs.data,szs.size,info.hash); + info.size = htonl(szs.size); + sha1_hex_t sha1, sha1_norm; + Sha1Bin2Hex(sha1,info.hash); + memcpy(sha1_norm,sha1,sizeof(sha1_norm)); + + char db64[CHECKSUM_DB_SIZE+1]; + CreateSSChecksumDB(db64,sizeof(db64),&info); + FindSpecialFilesSZS(&szs,false); + + + //--- scan LEX + + lex_info_t lexinfo; + InitializeLexInfo(&lexinfo); + + if (szs.course_lex_data) + { + lex_t lex; + InitializeLEX(&lex); + err = ScanLEX(&lex,false,szs.course_lex_data,szs.course_lex_size); + szs.have_lex = lex.have_lex; + SetupLexInfo(&lexinfo,&lex); + ResetLEX(&lex); + } + + + //--- scan KMP + + sha1_hex_t sha1_kmp = {0}, sha1_kmp_norm = {0}; + + char ct_attrib[300]; + char *ct_dest = ct_attrib; + char *ct_end = ct_attrib + sizeof(ct_attrib); + ct_attrib[1] = 0; + + int ckpt0_count = -1; // number of LC in CKPT + int lap_count = 3; // STGI lap counter + float speed_factor = 1.0; // STGI speed factor + + ccp slot_info = CreateSlotInfo(&szs); + if (szs.is_arena) + ct_dest = StringCopyE(ct_dest,ct_end,",arena"); + else + { + if (!strcmp(slot_info,"+6.1")) + ct_dest = StringCopyE(ct_dest,ct_end,",61"); + else if (!strcmp(slot_info,"+6.2")) + ct_dest = StringCopyE(ct_dest,ct_end,",62"); + else if (!strcmp(slot_info,"+3.1,+7.1")) + ct_dest = StringCopyE(ct_dest,ct_end,",31+71"); + } + + kmp_finish_t kf; + InitializeFinishLine(&kf); + + kmp_usedpos_t up; + InitializeUsedPos(&up); + + char gobj_info[20] = {0}; + if (szs.course_kmp_data) + { + SHA1(szs.course_kmp_data,szs.course_kmp_size,info.hash); + Sha1Bin2Hex(sha1_kmp,info.hash); + memcpy(sha1_kmp_norm,sha1_kmp,sizeof(sha1_kmp_norm)); + + kmp_t kmp; + InitializeKMP(&kmp); + kmp.lexinfo = &lexinfo; + err = ScanKMP(&kmp,false,szs.course_kmp_data,szs.course_kmp_size,0); + if ( err <= ERR_WARNING ) + { + if (kmp.stgi) + { + kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.stgi; + const u8 lap_count = stgi->lap_count; + const u16 speed_mod = stgi->speed_mod; + stgi->lap_count = 3; + stgi->speed_mod = 0; + + SHA1(szs.data,szs.size,info.hash); + Sha1Bin2Hex(sha1_norm,info.hash); + + SHA1(szs.course_kmp_data,szs.course_kmp_size,info.hash); + Sha1Bin2Hex(sha1_kmp_norm,info.hash); + + stgi->lap_count = lap_count; + stgi->speed_mod = speed_mod; + } + + CheckFinishLine(&kmp,&kf); + CheckUsedPos(&kmp,&up); + + const uint n_ckpt = kmp.dlist[KMP_CKPT].used; + if (n_ckpt) + { + const kmp_ckpt_entry_t *ckpt + = (kmp_ckpt_entry_t*)kmp.dlist[KMP_CKPT].list; + uint i; + ckpt0_count = 0; + for ( i = 0; i < n_ckpt; i++, ckpt++ ) + if (!ckpt->mode) + ckpt0_count++; + } + + const kmp_stgi_entry_t *stgi = (kmp_stgi_entry_t*)kmp.dlist[KMP_STGI].list; + if ( kmp.dlist[KMP_STGI].used > 0 ) + { + lap_count = stgi->lap_count; + if (!szs.is_arena) + { + if ( ckpt0_count > 1 ) + { + if ( lap_count == 1 ) + ct_dest = StringCopyE(ct_dest,ct_end,",1lap"); + ct_dest = snprintfE(ct_dest,ct_end,",%ulc",ckpt0_count); + } + else if ( lap_count != 0 && lap_count != 3 ) + ct_dest = snprintfE(ct_dest,ct_end,",%ulap%s", + lap_count, lap_count == 1 ? "" : "s" ); + } + + if (stgi->speed_mod) + { + speed_factor = SpeedMod2float(stgi->speed_mod); + char buf[20]; + snprintf(buf,sizeof(buf),",x%4.2f",speed_factor); + char *ptr = buf + strlen(buf) - 1; + while ( *ptr == '0' ) + *ptr-- = 0; + if ( *ptr == '.' ) + *ptr = 0; + if ( strcmp(buf,",x0") && strcmp(buf,",x1") ) + ct_dest = StringCopyE(ct_dest,ct_end,buf); + } + } + else if ( !szs.is_arena && ckpt0_count > 1 ) + ct_dest = snprintfE(ct_dest,ct_end,",%ulc",ckpt0_count); + + if ( kf.n_ktpt_m > 1 ) + ct_dest = StringCopyE(ct_dest,ct_end,",2ktpt"); + + kmp_ana_pflag_t ap; + AnalysePFlagScenarios(&ap,&kmp,GMD_M_DEFAULT); + snprintf(gobj_info,sizeof(gobj_info),"%d %d %d %d", + ap.ag.n_gobj, ap.n_version, ap.n_res_std, ap.n_res_ext ); + ResetPFlagScenarios(&ap); + } + + DetectSpecialKMP(&kmp,szs.kmp_special); + if ( szs.kmp_special[HAVEKMP_WOODBOX_HT] + || szs.kmp_special[HAVEKMP_MUSHROOM_CAR] + || szs.kmp_special[HAVEKMP_PENGUIN_POS] + || szs.kmp_special[HAVEKMP_EPROP_SPEED] + ) + { + ct_dest = StringCopyE(ct_dest,ct_end,",gobj"); + } + + if ( szs.kmp_special[HAVEKMP_X_PFLAGS] + || szs.kmp_special[HAVEKMP_X_COND] + || szs.kmp_special[HAVEKMP_X_DEFOBJ] + || szs.kmp_special[HAVEKMP_X_RANDOM] + ) + { + ct_dest = StringCopyE(ct_dest,ct_end,",xpf"); + } + + ResetKMP(&kmp); + } + + if ( szs.szs_special[HAVESZS_ITEM_SLOT_TABLE] ) + ct_dest = StringCopyE(ct_dest,ct_end,",itemslot"); + + if ( szs.szs_special[HAVESZS_OBJFLOW] ) + ct_dest = StringCopyE(ct_dest,ct_end,",objflow"); + + if ( szs.szs_special[HAVESZS_GHT_ITEM] + || szs.szs_special[HAVESZS_GHT_ITEM_OBJ] + || szs.szs_special[HAVESZS_GHT_KART] + || szs.szs_special[HAVESZS_GHT_KART_OBJ] + ) + { + ct_dest = StringCopyE(ct_dest,ct_end,",geohit"); + } + + if ( szs.szs_special[HAVESZS_MINIGAME] ) + ct_dest = StringCopyE(ct_dest,ct_end,",minigame"); + + + //--- scan KCL + + sha1_hex_t sha1_kcl = {0}; + if (szs.course_kcl_data) + { + SHA1(szs.course_kcl_data,szs.course_kcl_size,info.hash); + Sha1Bin2Hex(sha1_kcl,info.hash); + } + + + //--- add "lex" and terminate 'ct_dest' + + if (szs.course_lex_data) + ct_dest = StringCopyE(ct_dest,ct_end,",lex"); + *ct_dest = 0; + + + //--- course model (course_model.brres, course_d_model.brres) + + sha1_hex_t sha1_course = {0}; + if (szs.course_model_data) + { + SHA1(szs.course_model_data,szs.course_model_size,info.hash); + Sha1Bin2Hex(sha1_course,info.hash); + } + else if (szs.course_d_model_data) + { + SHA1(szs.course_d_model_data,szs.course_d_model_size,info.hash); + Sha1Bin2Hex(sha1_course,info.hash); + } + + + //--- vrcorn (vrcorn_model.brres) + + sha1_hex_t sha1_vrcorn = {0}; + if (szs.vrcorn_model_data) + { + SHA1(szs.vrcorn_model_data,szs.vrcorn_model_size,info.hash); + Sha1Bin2Hex(sha1_vrcorn,info.hash); + } + + + //--- minimap (map_model.brres) + + sha1_hex_t sha1_minimap = {0}; + if (szs.map_model_data) + { + SHA1(szs.map_model_data,szs.map_model_size,info.hash); + Sha1Bin2Hex(sha1_minimap,info.hash); + } + + + //--- status + + if ( err >= ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + if (!fo.f) + { + enumError err = CreateFile(&fo,false,dest,FM_STDIO|FM_OVERWRITE); + if (err) + { + max_err = err; + break; + } + PrintScriptHeader(&ps); + } + + + //--- name attributes ('dest' can be used now) + + PrintEscapedString(dest,sizeof(dest),param->arg,-1,CHMD_UTF8,'"',0); + + uint name_attrib_len = 0; + ccp name_attrib = strrchr(dest,'['); + if (name_attrib) + { + name_attrib++; + ccp end = strrchr(name_attrib,']'); + if (end) + name_attrib_len = end - name_attrib; + } + else + name_attrib = EmptyString; + + + //--- print result + + PrintScriptVars(&ps,1, + "file=\"%s\"\n" + "size=%zd\n" + "db64=\"%s\"\n" + "sha1=\"%s\"\n" + "sha1_norm=\"%s\"\n" + "sha1_kcl=\"%s\"\n" + "sha1_kmp=\"%s\"\n" + "sha1_kmp_norm=\"%s\"\n" + "sha1_course=\"%s\"\n" + "sha1_vrcorn=\"%s\"\n" + "sha1_minimap=\"%s\"\n" + "is_arena=%u\n" + "is_arena_name=\"%s\"\n" + "n_ckpt0=%d\n" + "lap_count=%d\n" + "speed_factor=%5.3f\n" + "slot_info=\"%s\"\n" + "used_x_pos=\"%u=%s %3.2f %3.2f %3.2f %3.2f\"\n" + "used_y_pos=\"%u=%s %3.2f %3.2f %3.2f %3.2f\"\n" + "used_z_pos=\"%u=%s %3.2f %3.2f %3.2f %3.2f\"\n" + "used_pos_suggest=\"%s\"\n" + "ktpt2=\"%s %4.2f %4.2f\"\n" + "gobj=\"%s\"\n" + "special_kmp=\"%s\"\n" + "special_files=\"%s\"\n" + "lex_sections=\"%s\"\n" + "ct_attributes=\"%s\"\n" + "name_attributes=\"%.*s\"\n" + ,dest + ,szs.size + ,db64 + ,sha1 + ,sha1_norm + ,sha1_kcl + ,sha1_kmp + ,sha1_kmp_norm + ,sha1_course + ,sha1_vrcorn + ,sha1_minimap + ,szs.is_arena + ,is_arena_name[szs.is_arena] + ,ckpt0_count + ,lap_count + ,speed_factor + ,slot_info + ,up.orig.rating[0],WarnLevelNameLo[up.orig.rating[0]] + ,up.orig.min.x,up.orig.max.x + ,fabsf(up.orig.max.x-up.orig.min.x) + ,(up.orig.min.x+up.orig.max.x)/2 + ,up.orig.rating[1],WarnLevelNameLo[up.orig.rating[1]] + ,up.orig.min.y,up.orig.max.y + ,fabsf(up.orig.max.y-up.orig.min.y) + ,(up.orig.min.y+up.orig.max.y)/2 + ,up.orig.rating[2],WarnLevelNameLo[up.orig.rating[2]] + ,up.orig.min.z,up.orig.max.z + ,fabsf(up.orig.max.z-up.orig.min.z) + ,(up.orig.min.z+up.orig.max.z)/2 + ,GetUsedPosObjSuggestion(up.suggest,true,"") + ,kf.warn ? "warn" : kf.hint ? "hint" : "ok" + ,kf.distance + ,kf.dir_delta + ,gobj_info + ,CreateSpecialInfoKMP(szs.kmp_special,true,"") + ,CreateSpecialFileInfo(&szs,true,"") + ,CreateSectionInfoLEX(szs.have_lex,true,"") + ,ct_attrib+1 + ,name_attrib_len,name_attrib + ); + + if ( long_count > 0 ) + { + const uint n = szs.special_file ? szs.special_file->used : 0; + PrintScriptVars(&ps,0,"subfile_n=%u\n",n); + + uint i; + ParamFieldItem_t *ptr = szs.special_file->field; + for (i = 0; i < n; i++, ptr++ ) + { + sha1_hex_t hex; + Sha1Bin2Hex(hex,(u8*)ptr->data); + PrintScriptVars(&ps,0,"subfile_%u=\"%u %s %s\"\n", + i, ptr->num, hex, ptr->key ); + } + } + + PutScriptVars(&ps,2,0); + + if (!script_array) + { + PrintScriptFooter(&ps); + ResetFile(&fo,0); + } + + ResetLexInfo(&lexinfo); + } + + PrintScriptFooter(&ps); + ResetPrintScript(&ps); + ResetFile(&fo,0); + ResetSZS(&szs); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command distribution /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanDistribFile + ( DistributionInfo_t *dinf, ccp fname, int ignore, bool assume_arena ) +{ + DASSERT(dinf); + if ( ignore && IsDirectory(fname,true) ) + return ERR_OK; + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,fname,true,ignore>0,true); + + if ( err > ERR_WARNING || err == ERR_NOT_EXISTS ) + { + ResetSZS(&szs); + return err == ERR_NOT_EXISTS || ignore ? ERR_OK : err; + } + + if ( szs.fform_arch != FF_U8 && szs.fform_arch != FF_WU8 ) + { + ResetSZS(&szs); + if ( ignore < 2 ) + return ERROR0(ERR_INVALID_DATA,"Not a SZS ot WBZ file: %s\n",fname); + return ERR_OK; + } + + if ( opt_norm || need_norm > 0 ) + NormalizeExSZS(&szs,false,false,false); + + if ( verbose > 0 ) + printf("Read %s\n",fname); + + + //--- cut filename + + char fname_buf[1000]; + + ccp slash = strrchr(fname,'/'); + if (slash) + fname = slash+1; + + StringCopyS(fname_buf,sizeof(fname_buf),fname); + fname = fname_buf; + + char *src, *dest = strrchr(fname_buf,'.'); + if ( dest && strlen(dest) <= 4 ) + *dest = 0; + + src = dest = fname_buf; + while (*src) + { + if (!memcmp(src,"ː",2)) + { + src += 2; + *dest++ = ':'; + } + else if (!memcmp(src,"%3a",3)) + { + src += 3; + *dest++ = ':'; + } + else + *dest++ = *src++; + } + *dest = 0; + + + //--- create checksum + + char checksum[100]; + CreateSSChecksumBySZS(checksum,sizeof(checksum),&szs); + ResetSZS(&szs); + + + //--- save result + + uint slot = FindSlotByTranslation(&dinf->translate,fname,checksum); + if (slot) + { + uint num; + char *next = ScanSlot(&num,fname,false); + if ( num == slot && (uchar)*next <= ' ' ) + fname = next; + else + { + num = strtoul(fname,&next,10); + if ( num == slot && (uchar)*next <= ' ' ) + fname = next; + } + + while ( *fname == ' ' ) + fname++; + } + + if (assume_arena) + slot += DISTRIBUTION_ARENA_DELTA; + + PRINT(">>> slot=%d[%d,%d] %s\n", + slot, IsValidDistribArena(slot), IsValidDistribTrack(slot), fname ); + + if (IsValidDistribTrack(slot)) + AppendParamField(dinf->track+slot,fname,false,slot,STRDUP(checksum)); + else if (IsValidDistribArena(slot)) + AppendParamField(dinf->arena+slot-DISTRIBUTION_ARENA_DELTA, + fname,false,slot,STRDUP(checksum)); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp GetDistribParamByNameS ( DistributionInfo_t *dinf, ccp name, ccp def ) +{ + DASSERT(dinf); + DASSERT(name); + ParamFieldItem_t *it = FindParamField(&dinf->param,name); + if ( it && it->data ) + { + it->num = 1; + return (ccp)it->data; + } + return def; +} + +/////////////////////////////////////////////////////////////////////////////// +// for name changes + +#if 0 + +static ccp GetDistribParamByNameS2 + ( DistributionInfo_t *dinf, ccp name1, ccp name2, ccp def ) +{ + DASSERT(dinf); + DASSERT(name); + ParamFieldItem_t *it = FindParamField(&dinf->param,name1); + if (!it) + it = FindParamField(&dinf->param,name2); + if ( it && it->data ) + { + it->num = 1; + return (ccp)it->data; + } + return def; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// + +static int GetDistribParamByNameIMM + ( DistributionInfo_t *dinf, ccp name, int def, int min, int max ) +{ + DASSERT(dinf); + DASSERT(name); + ParamFieldItem_t *it = FindParamField(&dinf->param,name); + if (!it) + return def; + it->num = 1; + + char *end; + int val = str2l((ccp)it->data,&end,10); + return *end || val < min || val > max ? def : val; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_distribution() +{ + SetPatchFileModeReadonly(); + SetupCoding64( opt_coding64, opt_db64 ? ENCODE_BASE64URL : ENCODE_BASE64 ); + + DistributionInfo_t dinf; + InitializeDistributionInfo(&dinf,true); + + char slotname[20]; + + + //--- define result file + + if ( !first_param || !first_param->arg || !*first_param->arg ) + return ERROR0(ERR_MISSING_PARAM, + "Definition of result file missed (first parameter)!\n" ); + + ParamList_t *param; + char result[PATH_MAX], path[PATH_MAX]; + + if (IsDirectory(first_param->arg,false)) + { + PathCatPP(result,sizeof(result),first_param->arg,DEFAULT_DISTIBUTION_FNAME); + param = first_param; + } + else + { + char *dest = StringCopyS(result,sizeof(result)-4,first_param->arg); + if ( dest < result+4 || strcasecmp(dest-4,".txt") ) + strcpy(dest,".txt"); + param = first_param->next; + } + + + //--- setup slot translation + + int i; + for ( i = 0; i < MKW_N_ARENAS; i++ ) + { + const TrackInfo_t *ti = arena_info+i; + DefineSlotTranslation(&dinf.translate,true,ti->def_slot,ti->track_fname); + DefineSlotTranslation(&dinf.translate,true,ti->def_slot,ti->name_en); + DefineSlotTranslation(&dinf.translate,true,ti->def_slot,ti->name_de); + } + + for ( i = 0; i < MKW_N_TRACKS; i++ ) + { + const TrackInfo_t *ti = track_info+i; + DefineSlotTranslation(&dinf.translate,false,ti->def_slot,ti->track_fname); + DefineSlotTranslation(&dinf.translate,false,ti->def_slot,ti->name_en); + DefineSlotTranslation(&dinf.translate,false,ti->def_slot,ti->name_de); + } + + bool result_found = false; + for ( i = 0; i < source_list.used; i++ ) + { + ccp arg = source_list.field[i]; + if ( !i && !strcmp(arg,"0") ) + ResetParamField(&dinf.translate); + else if ( !result_found && !strcmp(result,arg) ) + { + result_found = true; + ScanSlotTranslation(&dinf.translate,&dinf.param,arg,false); + } + else + ScanSlotTranslation(&dinf.translate,0,arg,false); + } + + if (!result_found) + ScanSlotTranslation(&dinf.translate,&dinf.param,result,true); + + AddParamDistributionInfo(&dinf,false); + + + //--- logging + + if ( logging > 0 ) + { + fputs("--------------- translation ---------------\n",stdlog); + + uint next_slot = 0; + while ( next_slot < INT_MAX ) + { + uint slot = next_slot; + next_slot = INT_MAX; + + uint i; + const ParamFieldItem_t *it = dinf.translate.field; + for ( i = 0; i < dinf.translate.used; i++, it++ ) + { + if ( it->num == slot ) + { + if ( slot < DISTRIBUTION_ARENA_DELTA ) + fprintf(stdlog,"%4u. %3u.%u %s\n", + i+1, it->num/10, it->num%10, it->key ); + else + { + snprintf( slotname, sizeof(slotname), "A%u.%u", + (it->num-DISTRIBUTION_ARENA_DELTA)/10, + it->num%10 ); + fprintf(stdlog,"%4u. %5s %s\n", i+1, slotname, it->key ); + } + } + else if ( it->num > slot && it->num < next_slot ) + next_slot = it->num; + } + } + + fputs("---------------- parameter ----------------\n",stdlog); + + uint i; + const ParamFieldItem_t *it = dinf.param.field; + for ( i = 0; i < dinf.param.used; i++, it++ ) + fprintf(stdlog,"%4u. %-20s = %s\n", + i+1, it->key, (ccp)it->data ); + + fputs("---------------------------------------------\n",stdlog); + } + + + //--- iterate files & directories + + if ( verbose == 0 ) + printf("Scan SZS files\n"); + + for ( ; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + bool assume_arena = false; + ccp ptr = param->arg; + for(;;) + { + ptr = strchr(ptr,'/'); + if (!ptr) + break; + ptr++; + if (!strncasecmp(ptr,"arena",5)) + { + assume_arena = true; + PRINT("ASSUME ARENA: %s\n",param->arg); + break; + } + } + + if (IsDirectory(param->arg,false)) + { + DIR * dir = opendir(param->arg); + if (dir) + { + for(;;) + { + struct dirent * dent = readdir(dir); + if (!dent) + break; + + if ( dent->d_name && *dent->d_name != '.' ) + { + PathCatPP(path,sizeof(path),param->arg,dent->d_name); + #if HAVE_WIIMM_EXT // [[2do]] by option + if ( !strstr(path,",clan]") && !strstr(path,",head=") ) + #endif + { + enumError err = ScanDistribFile(&dinf,path,2,assume_arena); + if (err) + return err; + } + } + } + closedir(dir); + } + } + else + { + enumError err = ScanDistribFile(&dinf,param->arg,opt_ignore,assume_arena); + if (err) + return err; + } + } + + + //--- print results + + if ( verbose >= 0 ) + printf("Create file %s\n",result); + + StringCat2S(path,sizeof(path),result,".bak"); + rename(result,path); + + FILE *f = fopen(result,"wb"); + if (!f) + ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",result); + + ccp uuid = GetDistribParamByNameS(&dinf,"UUID",""); + fprintf(f,text_distrib_head_cr + + ,GetDistribParamByNameS(&dinf,"COMMENT1","") + ,GetDistribParamByNameS(&dinf,"COMMENT2","") + ,GetDistribParamByNameS(&dinf,"COMMENT3","") + ,GetDistribParamByNameS(&dinf,"COMMENT4","") + ,GetDistribParamByNameS(&dinf,"COMMENT5","") + + ,GetDistribParamByNameS(&dinf,"USER-CT-WIIMM","") + ,GetDistribParamByNameS(&dinf,"USER-WIIMMFI","") + ,GetDistribParamByNameS(&dinf,"USER-CT-WIIKI","") + ,GetDistribParamByNameS(&dinf,"USER-MISC","") + ,GetDistribParamByNameS(&dinf,"MAIL","") + ,GetDistribParamByNameS(&dinf,"NOTE-FOR-WIMMM","") + + ,GetDistribParamByNameS(&dinf,"NAME","?") + ,GetDistribParamByNameS(&dinf,"VERSION","?") + ,GetDistribParamByNameS(&dinf,"AUTHORS","?") + ,GetDistribParamByNameS(&dinf,"RELEASE-DATE","") + ,GetDistribParamByNameS(&dinf,"KEYWORDS","") + ,GetDistribParamByNameS(&dinf,"PREDECESSOR","") + + ,GetDistribParamByNameS(&dinf,"WIIMMFI-REGION","") + ,GetDistribParamByNameS(&dinf,"INFO-TEXT","") + ,GetDistribParamByNameS(&dinf,"INFO-URL","") + + ,uuid + ,GetDistribParamByNameIMM(&dinf,"DISPLAY-MODE",3,0,3) + ,GetDistribParamByNameIMM(&dinf,"DATABASE-NAME",1,0,1) + ,GetDistribParamByNameIMM(&dinf,"VIEW-COMMENT",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-NEW",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-AGAIN",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-FILL",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-UPDATE",0,0,1) + ,GetDistribParamByNameIMM(&dinf,"ENABLE-BOOST",0,0,1) + + ,uuid + ,VERSION + ,REVISION_NUM + ,GetDistribParamByNameS(&dinf,"FIRST-CREATION","") + ,GetDistribParamByNameS(&dinf,"LAST-UPDATE","") + ); + + + + //--- mark some more params as 'used' + + GetDistribParamByNameS(&dinf,"WSZST-VERSION",""); + GetDistribParamByNameS(&dinf,"WSZST-REVISION",""); + + + //--- additional parameters + + ccp info = text_distrib_param_cr; + + const ParamFieldItem_t *it = dinf.param.field; + for ( i = 0; i < dinf.param.used; i++, it++ ) + if (!it->num) + { + int len = strlen(it->key); + ccp sep; + if ( len > 14 ) + sep = " "; + else if ( len > 6 ) + sep = "\t"; + else + sep = "\t\t"; + + fprintf(f,"%s@%s%s= %s\r\n", info, it->key, sep, (ccp)it->data ); + info = ""; + } + + + //--- battle arenas + + fputs(text_distrib_tracks_cr,f); + + int slot, slot10 = -1, count = 0; + for ( slot = 0; slot < MAX_DISTRIBUTION_ARENA; slot++ ) + { + ParamField_t *pf = dinf.arena + slot; + if ( pf->used ) + { + count++; + if ( slot10 != slot/10 ) + { + slot10 = slot/10; + fputs("\r\n",f); + } + + if (slot) + snprintf(slotname,sizeof(slotname),"A%u.%u",slot10,slot%10); + else + StringCopyS(slotname,sizeof(slotname),"arena"); + + uint i; + ParamFieldItem_t *it = pf->field; + for ( i = 0; i < pf->used; i++, it++ ) + fprintf(f,"%s %5s %s\r\n", (ccp)it->data, slotname, it->key ); + } + } + if (count) + fputs("\r\n",f); + + + //--- racing tracks + + + slot10 = -1; + count = 0; + + for ( slot = 0; slot < MAX_DISTRIBUTION_TRACK; slot++ ) + { + ParamField_t *pf = dinf.track + slot; + if ( pf->used ) + { + count++; + if ( slot10 != slot/10 ) + { + slot10 = slot/10; + fputs("\r\n",f); + } + + char slotname[10]; + if (slot) + snprintf(slotname,sizeof(slotname),"%3u.%u",slot10,slot%10); + else + StringCopyS(slotname,sizeof(slotname)," ---"); + + uint i; + ParamFieldItem_t *it = pf->field; + for ( i = 0; i < pf->used; i++, it++ ) + fprintf(f,"%s %s %s\r\n", (ccp)it->data, slotname, it->key ); + } + } + if (count) + fputs("\r\n",f); + fclose(f); + + ResetDistributionInfo(&dinf); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command diff /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError diff_files ( ccp fname1, ccp fname2 ) +{ + if ( verbose > 0 ) + printf("DIFF %s : %s\n", fname1, fname2 ); + + szs_file_t szs1, szs2; + InitializeSZS(&szs1); + InitializeSZS(&szs2); + + enumError err = LoadCreateSZS(&szs1,fname1,true,false,true); + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + err = LoadCreateSZS(&szs2,fname2,true,false,true); + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + err = DiffSZS( &szs1, &szs2, + opt_recurse < 0 ? 0 : opt_recurse, + opt_cut ? 1 : -1, + verbose < 0 ); + + if ( verbose >= -1 && err == ERR_DIFFER ) + printf("Content differ: %s : %s\n", fname1, fname2 ); + else if ( verbose >= 0 && err == ERR_OK ) + printf("Content identical: %s : %s\n", fname1, fname2 ); + + ResetSZS(&szs1); + ResetSZS(&szs2); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError diff_dest() +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,"%F",0,false); + const enumError err = diff_files(param->arg,dest); + if ( max_err < err ) + max_err = err; + if ( err > ERR_WARNING || err && verbose < -1 ) + break; + } + return max_err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_diff() +{ + SetPatchFileModeReadonly(); + + if (opt_dest) + return diff_dest(); + + if ( n_param != 2 ) + return ERROR0(ERR_SYNTAX,"Exact 2 sources expected if --dest is not set.\n"); + + ASSERT(first_param); + ASSERT(first_param->next); + return diff_files(first_param->arg,first_param->next->arg); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command check /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_check() +{ + SetPatchFileModeReadonly(); + + patch_action_log_disabled++; + if ( KCL_MODE & KCLMD_DROP_AUTO ) + SetKclMode(KCL_MODE|KCLMD_DROP_UNUSED); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + + const bool check_verbose = verbose > 0 || long_count > 0; + CheckMode_t mode = GetCheckMode(false,brief_count>0,verbose<0,check_verbose); + if ( verbose >= 0 || testmode ) + { + ColorSet_t col; + SetupColorSet(&col,stdlog); + mode = GetCheckMode(true,brief_count>0,false,check_verbose); + fprintf(stdlog,"\n%sCHECK %s:%s%s\n", + col.heading, GetNameFF_SZS(&szs), szs.fname, col.reset ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + switch (szs.fform_arch) + { + case FF_KCL: + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + { + kcl_t kcl; + err = ScanKCL(&kcl,true,szs.data,szs.size,true,mode); + ResetKCL(&kcl); + } + break; + + case FF_KMP: + case FF_KMP_TXT: + { + kmp_t kmp; + err = ScanKMP(&kmp,true,szs.data,szs.size,mode); + ResetKMP(&kmp); + } + break; + + case FF_U8: + case FF_WU8: + if (CheckSZS(&szs,mode,global_check_mode)) + err = ERR_DIFFER; + break; + + case FF_BRRES: + if (CheckBRRES(&szs,mode,0)) + err = ERR_DIFFER; + break; + + default: + ERROR0(ERR_INVALID_FFORM, + "CHECK doesn't support file format '%s': %s\n", + GetNameFF_SZS(&szs), param->arg ); + break; + } + fflush(stdout); + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command slot /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_slots() +{ + SetPatchFileModeReadonly(); + + KCL_MODE |= KCLMD_SILENT; + if ( KCL_MODE & KCLMD_DROP_AUTO ) + SetKclMode(KCL_MODE|KCLMD_DROP_UNUSED); + + const bool have_col = IsFileColorized(stdout); + ccp col_minus = GetTextMode(have_col,TTM_BOLD|TTM_RED); + ccp col_plus = GetTextMode(have_col,TTM_BOLD|TTM_GREEN); + ccp col_misc = GetTextMode(have_col,TTM_BOLD|TTM_CYAN); + ccp col_reset = GetTextMode(have_col,TTM_RESET); + MARK_USED(col_minus,col_plus,col_misc); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + if (err) + { + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + continue; + } + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + //PatchSZS(&szs); + + int slot[MKW_N_TRACKS]; + bool need_special; + const int stat = FindSlotsSZS(&szs,slot,&need_special); + + noPRINT("IS_ARENA=%d\n",szs.is_arena); + if ( szs.is_arena >= ARENA_FOUND ) + { + if (brief_count) + printf("%sARENA%s : %s\n",col_misc,col_reset,param->arg); + else + printf(" %s ARENA %s : %s\n",col_misc,col_reset,param->arg); + } + else + { + if (brief_count) + { + uint i, count = 0; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + if ( slot[i] && slot[i] != stat ) + { + ccp col = slot[i] < 0 ? col_minus : slot[i] > 0 ? col_plus : col_misc; + printf("%s%c%u%u", + col, + count ? ',' : stat < 0 ? '+' : '-', + i/4+1, i%4+1 ); + count++; + } + + while ( count++ < 3 ) + fputs(" ",stdout); + } + else + { + uint i, count = 0; + for ( i = 0; i < MKW_N_TRACKS; i++ ) + if ( slot[i] != stat ) + { + ccp col = slot[i] < 0 ? col_minus : slot[i] > 0 ? col_plus : col_misc; + printf(" %s%c%u.%u", + col, + slot[i] < 0 ? '-' : slot[i] > 0 ? '+' : '?' , + i/4+1, i%4+1 ); + count++; + } + else if ( !need_special && ( i == 13 || i == 20 || i == 21 )) + { + fputs(" ",stdout); + count++; + } + + while ( count++ < 3 ) + fputs(" ",stdout); + } + printf("%s : %s\n",col_reset,param->arg); + } + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command isarena /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_isarena() +{ + SetPatchFileModeReadonly(); + + if ( brief_count || verbose < 0 ) + print_header = false; + + if (print_header) + printf("\n%sstatus filename%s\n%s%.237s%s\n", + colout->heading, colout->reset, + colout->heading, ThinLine300_3, colout->reset ); + + int count = 0; + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,true); + + ccp status = 0, color = 0; + + if ( err == ERR_NOT_EXISTS ) + { + if (opt_ignore) + err = ERR_OK; + else + { + status = "!NO-FILE"; + color = colout->bad; + } + } + else if (err) + { + status = "!ERROR"; + color = colout->bad; + } + else + { + FindSpecialFilesSZS(&szs,false); + if (!szs.course_kmp_data) + { + status = "!NO-KMP"; + color = colout->bad; + } + else + { + kmp_t kmp; + InitializeKMP(&kmp); + err = ScanKMP(&kmp,false,szs.course_kmp_data,szs.course_kmp_size,0); + if (err) + { + status = "!KMP-ERR"; + color = colout->bad; + } + else + { + const IsArena_t is_arena = IsArenaKMP(&kmp); + switch (is_arena) + { + case ARENA_NONE: + err = ERR_DIFFER; + status = "-RACE"; + color = colout->b_orange; + break; + + case ARENA_MAYBE: + err = ERR_DIFFER; + status = "?MAYBE"; + color = colout->b_yellow; + break; + + case ARENA_FOUND: + status = "+ARENA"; + color = colout->b_green; + break; + + case ARENA_DISPATCH: + status = "+DISPATCH"; + color = colout->b_green; + break; + + case ARENA__N: + break; + } + + if (!status) + { + status = "!UNKNOWN"; + color = colout->bad; + } + } + ResetKMP(&kmp); + } + } + + if (status) + { + count++; + if ( verbose >= 0 ) + { + DASSERT(color); + if (brief_count) + printf("%s%s%s\n", color, status, colout->reset ); + else + printf("%s%-9s%s %s\n", color, status, colout->reset, param->arg ); + } + } + + ResetSZS(&szs); + if ( max_err < err ) + max_err = err; + } + + if (print_header) + putchar('\n'); + + return count ? max_err : ERR_NOTHING_TO_DO; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command patch /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool ScanByExtFF + ( file_format_t *ff_arch, file_format_t *ff_compr, ccp fname ) +{ + DASSERT(ff_arch); + DASSERT(ff_compr); + *ff_arch = FF_UNKNOWN; + *ff_compr = FF_UNKNOWN; + + ccp point = strrchr(fname,'.'); + if (!point) + return false; + + ++point; + if (!strcmp(point,"szs")) + { + *ff_arch = FF_U8; + *ff_compr = FF_YAZ0; + return true; + } + + if (!strcmp(point,"wbz")) + { + *ff_arch = FF_WU8; + *ff_compr = FF_BZ; + return true; + } + + if (!strcmp(point,"u8")) + { + *ff_arch = FF_U8; + return true; + } + + if (!strcmp(point,"wu8")) + { + *ff_arch = FF_WU8; + return true; + } + + if (!strcmp(point,"xyz")) + { + *ff_arch = FF_XYZ; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +// old [[container]] will be replaced by Container_t & ContainerData_t +#define PRINT_DATA PRINT("##### %s() #%u: cd=%p,%zu,%d / d=%p,%zu,%d / c=%p,%d #####\n", \ + __FUNCTION__, __LINE__, \ + szs.cdata, szs.csize, szs.cdata_alloced, \ + szs.data, szs.size, szs.data_alloced, \ + szs.old_container ? szs.old_container->data : 0, \ + szs.old_container ? szs.old_container->ref_count : -1 ) + +//----------------------------------------------------------------------------- + +static enumError cmd_patch() +{ + #if HAVE_PRINT + file_format_t dest_ff_arch; + file_format_t dest_ff_compr; + const bool dest_ff_valid = ScanByExtFF(&dest_ff_arch,&dest_ff_compr,opt_dest); + PRINT("fform(dest) = %s [valid=%d]\n", + GetNameFF(dest_ff_compr,dest_ff_arch), dest_ff_valid ); + #endif + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + if (!param->arg) + continue; + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,false,opt_ignore>0,true); + + //PRINT_DATA; + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + u8 * orig_data; + uint orig_size; + + const bool was_compressed = szs.cdata != 0; + if (was_compressed) + { + DecompressSZS(&szs,false,0); + DASSERT(szs.cdata); + DASSERT(szs.cdata_alloced); + orig_data = szs.cdata; + orig_size = szs.csize; + szs.cdata = 0; + szs.cdata_alloced = false; + } + else + { + DASSERT(szs.data); + orig_data = MEMDUP(szs.data,szs.size); + orig_size = szs.size; + } + + #ifdef TEST0 + const ccp src_format = GetNameFF_SZS(&szs); + file_format_t local_ff_compr = opt_fform; + file_format_t local_ff_arch = fform_compr; + #else + const ccp src_format = GetNameFF_SZS(&szs); + const bool convert_wu8 = opt_fform == FF_WU8 + || opt_fform == FF_UNKNOWN && szs.fform_arch == FF_WU8; + PRINT("CONVERT_WU8=%d, FF=%s\n", convert_wu8, src_format ); + #endif + + DecodeWU8(&szs); + + //szs.allow_ext_data = opt_norm; + szs.allow_ext_data = true; + + bool dirty = PatchSZS(&szs); + if ( ( opt_norm || need_norm || opt_auto_add || dirty ) + && NormalizeSZS(&szs) ) + { + dirty = true; + } + if ( OptionUsed[OPT_MINIMAP] && AutoAdjustMinimap(&szs) ) + dirty = true; + + if (convert_wu8) + { + err = EncodeWU8(&szs); + if (err) + goto abort; + } + + + //--- dest file + + char dest[PATH_MAX]; + if ( opt_fform == FF_U8 || opt_fform == FF_WU8 ) + { + SubstDest( dest, sizeof(dest), param->arg, + opt_dest && *opt_dest ? opt_dest : "\1P/\1N\1?T", "\1N\1?T", + GetExtFF(szs.fform_file,opt_fform), false ); + } + else if ( opt_dest && *opt_dest ) + { + SubstDest( dest, sizeof(dest), param->arg, opt_dest, 0, + GetExtFF(szs.fform_file,szs.fform_arch), false ); + } + else + StringCopyS(dest,sizeof(dest),param->arg); + + + //--- compression and SZS cache + + if (was_compressed) + { + CompressSZS(&szs,true); + + //PRINT_DATA; + if ( opt_norm && !dirty ) + dirty = orig_size != szs.csize + || memcmp(orig_data,szs.cdata,orig_size); + FREE(orig_data); + orig_data = szs.cdata; + orig_size = szs.csize; + } + else + { + //PRINT_DATA; + if (!dirty) + dirty = orig_size != szs.size + || memcmp(orig_data,szs.data,orig_size); + FREE(orig_data); + orig_data = szs.data; + orig_size = szs.size; + } + + const ccp dest_format = GetNameFF_SZScurrent(&szs); + const bool src_dest_diff + = strcmp(src_format,dest_format) || strcmp(dest,param->arg); + + if ( dirty || src_dest_diff ) + { + if ( verbose >= 0 || testmode ) + { + if (src_dest_diff) + fprintf(stdlog,"%s%s%s %s:%s -> %s:%s\n", + testmode ? "WOULD " : "", + opt_norm ? "NORMALIZE" : "PATCH", + szs.aiparam_removed ? " [-AIParam]" : "", + src_format, param->arg, dest_format, dest ); + else + fprintf(stdlog,"%s%s%s %s:%s\n", + testmode ? "WOULD " : "", + opt_norm ? "NORMALIZE" : "PATCH", + szs.aiparam_removed ? " [-AIParam]" : "", + dest_format, dest ); + fflush(stdlog); + } + + //PRINT_DATA; + File_t F; + err = CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite(orig_data,1,orig_size,F.f); + if ( wstat != orig_size ) + err = FILEERROR1(&F, + ERR_WRITE_FAILED, + "Writing %u bytes failed [%zu bytes written]: %s\n", + orig_size, wstat, dest ); + } + ResetFile(&F,opt_preserve); + if ( !err && opt_remove_src ) + RemoveSource(param->arg,dest,verbose>=0,testmode); + } + else if ( verbose > 0 ) + { + fprintf(stdlog,"ALREADY NORMALIZED: %s:%s\n", src_format, param->arg ); + fflush(stdlog); + } + + LinkCacheRAW(szs.cache_fname,dest,orig_data,orig_size); + + } + abort: + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command normalize /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_normalize() +{ + RegisterOptionByIndex(&InfoUI_wszst,OPT_NORM,1,false); + opt_norm = true; + return cmd_patch(); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command copy /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_copy() +{ + RegisterOptionByIndex(&InfoUI_wszst,OPT_OVERWRITE,1,false); + opt_overwrite = true; + + if (!opt_dest) + { + if (!first_param) + return ERROR0(ERR_MISSING_PARAM, "Missing destination parameter!\n" ); + + ParamList_t *param; + for ( param = first_param; param->next; param = param->next ) + ; + ASSERT(param); + ASSERT(!param->next); + SetDest(param->arg,false); + param->arg = 0; + } + + return cmd_patch(); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command duplicate /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_duplicate() +{ + RegisterOptionByIndex(&InfoUI_wszst,OPT_OVERWRITE,1,false); + opt_overwrite = true; + + + //--- check parameters + + if (!first_param) + return ERROR0(ERR_MISSING_PARAM, "Missing source file!\n" ); + + if (!opt_dest) + { + if (!first_param->next) + return ERROR0(ERR_MISSING_PARAM, "Missing destination parameter!\n" ); + + SetDest(first_param->next->arg,false); + n_param--; + } + + if ( n_param != 1 ) + return ERROR0(ERR_SEMANTIC,"Exact 1 source file expected!\n" ); + + NORMALIZE_FILENAME_PARAM(first_param); + ccp suffix, dest1 = opt_dest, dest2; + { + ccp slash = strrchr(dest1,'/'); + if (!slash) + slash = dest1; + + char *pos = strchr(slash,'@'); + if (pos) + { + *pos = 0; + dest2 = pos+1; + while ( *dest2 == ' ' ) + dest2++; + suffix = !*dest2 || *dest2 == '.' ? "" : " "; + while ( pos > dest1 && pos[-1] == ' ' ) + *--pos = 0; + } + else + { + pos = strrchr(slash,'.'); + if (pos) + { + *pos = 0; + dest2 = pos+1; + suffix = "."; + } + else + dest2 = suffix = EmptyString; + } + } + + + //--- open source + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,first_param->arg,false,opt_ignore>0,true); + if (err) + return err; + + const bool was_compressed = szs.cdata != 0; + if (was_compressed) + DecompressSZS(&szs,false,0); + + const ccp fform = GetNameFF_SZS(&szs); + if ( verbose >= 0 ) + { + fprintf(stdlog,"READ %s:%s\n",fform,first_param->arg); + fflush(stdlog); + } + + if ( szs.fform_current != FF_U8 && szs.fform_current != FF_WU8 ) + return ERROR0(ERR_INVALID_DATA,"A track file (SZS or WBZ) expected!\n"); + + force_lex_test = true; // force patch to insert LEX/TEST + PatchSZS(&szs); + + + //--- find KMP + + FindSpecialFilesSZS(&szs,false); + if (!szs.course_kmp_data) + return ERROR0(ERR_INVALID_DATA,"KMP not found → abort: %s\n",first_param->arg); + + kmp_t kmp; + err = ScanKMP(&kmp,true,szs.course_kmp_data,szs.course_kmp_size,0); + + + //--- find LEX/TEST + + if (!szs.course_lex_data) + return ERROR0(ERR_INTERNAL,0); + + lex_element_t *lex_test = FindLexElement( (lex_header_t*)szs.course_lex_data, + szs.course_lex_size, LEXS_TEST ); + if (!lex_test) + return ERROR0(ERR_INTERNAL,0); + + kmp_ana_pflag_t ap; + AnalysePFlagScenarios(&ap,&kmp,opt_gamemodes); + + char buf[10]; + const int fw = snprintf(buf,sizeof(buf),"%u",ap.n_res); + + uint ri; + const kmp_ana_pflag_res_t *res; + for ( ri = 0, res = ap.res_list; ri < ap.n_res; ri++, res++ ) + { + memcpy(lex_test->data,&res->lex_test,sizeof(res->lex_test)); + char fname[PATH_MAX]; + snprintf(fname,sizeof(fname),"%s {%02u,%s}%s%s", + dest1, res->version+1, res->name, suffix, dest2 ); + if ( verbose >= 0 || testmode > 0 ) + { + fprintf(stdlog,"%sCREATE %*u/%u %s:%s\n", + testmode > 0 ? "WOULD " : "", fw, ri+1, ap.n_res, fform, fname ); + fflush(stdlog); + } + + if (was_compressed) + { + ClearCompressedSZS(&szs); + CompressSZS(&szs,false); + } + + SaveSZS(&szs,fname,true,was_compressed); + } + + ResetKMP(&kmp); + ResetSZS(&szs); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command minimap /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_mmap ( ccp title, float3 *a, float3 *b ) +{ + DASSERT(title); + DASSERT(a); + DASSERT(b); + + printf("%-13s %9.1f %9.1f %9.1f %20.1f %9.1f %9.1f\n", + title, a->x, a->y, a->z, b->x, b->y, b->z ); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_matrix ( ccp title, float *a, float *b ) +{ + DASSERT(title); + DASSERT(a); + DASSERT(b); + + uint i; + for ( i = 0; i < 3; i++ ) + { + printf("%s-Matrix-%c: %9.1f %9.1f %9.1f %9.1f %9.1f %9.1f %9.1f %9.1f\n", + title, 'X'+i, + a[0], a[1], a[2], a[3], + b[0], b[1], b[2], b[3] ); + a += 4; + b += 4; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void print_data +( + mdl_minimap_t *mmap, // valid minimap record + mdl_sect1_t *root, // NULL or valid 'root' data + mdl_sect1_t *ld, // valid 'posLD' data + mdl_sect1_t *ru, // valid 'posRU' data + uint mode, // general print mode + uint modified // 0:none, 1:translate, 2:all, 3:all+minimum +) +{ + if ( modified != 1 ) + { + printf("Idx+Id+Flags: %9u %9u %#9x %21u %9u %#9x\n", + ld->index, ld->id, ld->flags, + ru->index, ru->id, ru->flags ); + print_mmap( "Scale:", &ld->scale, &ru->scale ); + print_mmap( "Rotation", &ld->rotate, &ru->rotate ); + } + + print_mmap( "Translation:", &ld->translate, &ru->translate ); + if ( mmap->rec_trans_valid && ( !modified || mode > 0 ) ) + print_mmap(" Recommend.:",&mmap->rec_trans_ld,&mmap->rec_trans_ru); + + if ( mode > 1 && !modified ) + { + print_mmap( "Minimum:", &ld->minimum, &ru->minimum ); + print_mmap( "Maximum:", &ld->maximum, &ru->maximum ); + } + + if ( mode > 0 ) + { + putchar('\n'); + print_matrix("Tra",ld->trans_matrix.v,ru->trans_matrix.v); + putchar('\n'); + print_matrix("Inv",ld->inv_matrix.v,ru->inv_matrix.v); + } + printf("%.95s\n",Minus300); + + if ( mode > 2 && !modified || modified > 2 ) + { + printf("Vertex-Min: %11.1f %9.1f %9.1f\n", + mmap->min.x, mmap->min.y, mmap->min.z ); + printf("Vertex-Max: %11.1f %9.1f %9.1f\n", + mmap->max.x, mmap->max.y, mmap->max.z ); + if (mmap->root) + { + printf("Root-Min: %11.1f %9.1f %9.1f\n", + root->minimum.x, root->minimum.y, root->minimum.z ); + printf("Root-Max: %11.1f %9.1f %9.1f\n", + root->maximum.x, root->maximum.y, root->maximum.z ); + } + printf("%.95s\n",Minus300); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void center_mmap ( uint idx, float3 *a, float3 *b ) +{ + DASSERT(idx<3); + DASSERT(a); + DASSERT(b); + + const float new_val = ( a->v[idx] - b->v[idx] ) / 2; + a->v[idx] = +new_val; + b->v[idx] = -new_val; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_minimap() +{ + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,false,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + if ( verbose >= 0 ) + fprintf(stdlog,"\nMinimap data of %s:%s\n", + GetNameFF_SZS(&szs), param->arg ); + + const bool was_compressed = szs.cdata != 0; + if (was_compressed) + DecompressSZS(&szs,true,0); + + bool dirty = false; + mdl_minimap_t mmap; + if (FindMinimapData(&mmap,&szs)) + { + DASSERT(mmap.posLD); + DASSERT(mmap.posRU); + PRINT("MMAP FOUND!\n"); + + mdl_sect1_t root, ld, ru; + ntoh_MDLs1(&root,mmap.root); + ntoh_MDLs1(&ld,mmap.posLD); + ntoh_MDLs1(&ru,mmap.posRU); + + if ( verbose >= 0 ) + { + printf("\n%53s%42s\n%.95s\n", + "______________left-down_______________", + "_______________right-up_______________", + Minus300); + print_data(&mmap,&root,&ld,&ru,long_count,0); + } + + if ( transform_active + || have_set_scale + || have_set_rot + || have_set_value + || OptionUsed[OPT_TOUCH] + || OptionUsed[OPT_AUTO] + || OptionUsed[OPT_XCENTER] + || OptionUsed[OPT_YCENTER] + || OptionUsed[OPT_ZCENTER] + || OptionUsed[OPT_CENTER] + || set_flags != M1(set_flags) + ) + { + uint modified = 1; + if ( OptionUsed[OPT_AUTO] && mmap.rec_trans_valid ) + { + modified = 2; + ClearMDLs1(&ld,false); + ld.translate = mmap.rec_trans_ld; + ClearMDLs1(&ru,false); + ru.translate = mmap.rec_trans_ru; + } + + if ( set_flags != M1(set_flags) ) + { + modified = 2; + ld.flags = ru.flags = set_flags; + } + + if (have_set_scale) + { + modified = 2; + ld.scale.x = ru.scale.x = set_scale.x; + ld.scale.y = ru.scale.y = set_scale.y; + ld.scale.z = ru.scale.z = set_scale.z; + } + + if (have_set_rot) + { + modified = 2; + ld.rotate.x = ru.rotate.x = set_rot.x; + ld.rotate.y = ru.rotate.y = set_rot.y; + ld.rotate.z = ru.rotate.z = set_rot.z; + } + + if ( have_set_value & 1 ) + { + ld.translate.x = set_value_min.x; + ru.translate.x = set_value_max.x; + } + if ( have_set_value & 2 ) + { + ld.translate.y = set_value_min.y; + ru.translate.y = set_value_max.y; + } + if ( have_set_value & 4 ) + { + ld.translate.z = set_value_max.z; + ru.translate.z = set_value_min.z; + } + + if ( OptionUsed[OPT_XCENTER] || OptionUsed[OPT_CENTER] ) + center_mmap(0,&ld.translate,&ru.translate); + if ( OptionUsed[OPT_YCENTER] || OptionUsed[OPT_CENTER] ) + center_mmap(1,&ld.translate,&ru.translate); + if ( OptionUsed[OPT_ZCENTER] || OptionUsed[OPT_CENTER] ) + center_mmap(2,&ld.translate,&ru.translate); + + TransformPosFloat3D(ld.translate.v,1,0); + TransformPosFloat3D(ru.translate.v,1,0); + CalcMatrixMDLs1(&ld); + CalcMatrixMDLs1(&ru); + + hton_MDLs1(&ld,&ld); + hton_MDLs1(&ru,&ru); + dirty = memcmp(mmap.posLD,&ld,sizeof(ld)) + || memcmp(mmap.posRU,&ru,sizeof(ru)); + +#if 1 + if (mmap.root) + { + root.minimum = mmap.min; + root.maximum = mmap.max; + + #if 0 // workaround fails + // workaround for a bug in KMP modifier + if ( root.minimum.x > root.minimum.z ) + root.minimum.x = root.minimum.z; + if ( root.maximum.x < root.maximum.z ) + root.maximum.x = root.maximum.z; + #endif + + hton_MDLs1(&root,&root); + if (memcmp(mmap.root,&root,sizeof(root))) + { + memcpy(mmap.root,&root,sizeof(root)); + modified = 3; + dirty = true; + } + ntoh_MDLs1(&root,&root); + } +#else + if ( mmap.root + && ( memcmp(&root.minimum,&mmap.min,sizeof(root.minimum)) + || memcmp(&root.maximum,&mmap.max,sizeof(root.maximum)) )) + { + root.minimum = mmap.min; + root.maximum = mmap.max; + + hton_MDLs1(mmap.root,&root); + modified = 3; + dirty = true; + } +#endif + + if (dirty) + { + memcpy(mmap.posLD,&ld,sizeof(ld)); + memcpy(mmap.posRU,&ru,sizeof(ru)); + ntoh_MDLs1(&ld,&ld); + ntoh_MDLs1(&ru,&ru); + if ( verbose >= 0 ) + print_data(&mmap,&root,&ld,&ru,long_count,modified); + } + } + } + + if (dirty) + { + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%sPATCH MINIMAP %s:%s\n", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), param->arg ); + fflush(stdlog); + } + + if (!testmode) + SaveSZS(&szs,szs.fname,true,was_compressed); + } + } + fflush(stdlog); + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command compress /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_compress() +{ + static const char dest_fname[] = "\1P/\1N\1?T"; + CheckOptDest(dest_fname,false); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,true,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + file_format_t ff_dest = szs.fform_arch; + if ( ff_dest == FF_U8 && opt_fform == FF_WU8 + || ff_dest == FF_WU8 && opt_fform == FF_U8 ) + { + ff_dest = opt_fform; + } + PRINT("FF = %s , %s => %s\n", + GetNameFF_SZS(&szs), + GetNameFF_SZScurrent(&szs), + GetNameFF(0,ff_dest) ); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,dest_fname, + GetExtFF(fform_compr,ff_dest),false); + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sCOMPRESS %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), param->arg, + GetNameFF(fform_compr,ff_dest), dest ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + PatchSZS(&szs); + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( ff_dest == FF_WU8 ) + err = EncodeWU8(&szs); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + szs.dest_fname = dest; + CompressSZS(&szs,true); + szs.dest_fname = 0; + + if ( szs.cache_used && (int)max_err < ERR_CACHE_USED ) + max_err = ERR_CACHE_USED; + + File_t F; + err = CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite(szs.cdata,1,szs.csize,F.f); + if ( wstat != szs.csize ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + szs.csize, dest); + } + ResetFile(&F,opt_preserve); + LinkCacheRAW(szs.cache_fname,dest,szs.cdata,szs.csize); + + if ( !err && opt_remove_src ) + RemoveSource(param->arg,dest,verbose>=0,testmode); + } + } + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command decompress /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_decompress() +{ + static const char dest_fname[] = "\1P/\1N\1?T"; + CheckOptDest(dest_fname,false); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,true,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + file_format_t ff_dest = szs.fform_arch; + if ( ff_dest == FF_U8 && opt_fform == FF_WU8 + || ff_dest == FF_WU8 && opt_fform == FF_U8 ) + { + ff_dest = opt_fform; + } + PRINT("FF = %s , %s => %s\n", + GetNameFF_SZS(&szs), + GetNameFF_SZScurrent(&szs), + GetNameFF(0,ff_dest) ); + + char dest[PATH_MAX]; + ccp ext = GetExtFF(ff_dest,0); + SubstDest(dest,sizeof(dest),param->arg,opt_dest,ext,ext,false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sDECOMPRESS %s:%s -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), param->arg, + GetNameFF(0,ff_dest), dest ); + fflush(stdlog); + } + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + PatchSZS(&szs); + if ( opt_norm || need_norm > 0 ) + NormalizeSZS(&szs); + + if ( ff_dest == FF_WU8 ) + err = EncodeWU8(&szs); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + File_t F; + err = CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + size_t wstat = fwrite(szs.data,1,szs.size,F.f); + if ( wstat != szs.size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + szs.size, dest); + } + ResetFile(&F,opt_preserve); + if ( !err && opt_remove_src ) + RemoveSource(param->arg,dest,verbose>=0,testmode); + } + } + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command create /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_create ( bool create ) +{ + static const char dest_fname[] = "\1P/\1N\1?T"; + CheckOptDest(dest_fname,false); + const bool save_auto_add = opt_auto_add; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + opt_auto_add = save_auto_add; // restore auto-add settings + + int src_len = strlen(param->arg); + while ( src_len > 0 && param->arg[src_len-1] == '/' ) + src_len--; + param->arg[src_len] = 0; + + SetupParam_t sp; + InitializeSetupParam(&sp); + ScanSetupParam(&sp,true,param->arg,SZS_SETUP_FILE,0,true); + + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,dest_fname, + GetExtFF(sp.fform_file,sp.fform_arch),false); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = CreateSZS(&szs,dest,param->arg,0,&sp,0, + verbose > 0 ? UINT_MAX : 0, false ); + + //--- create file + + if ( verbose >= 0 || testmode ) + { + if (create) + fprintf(stdlog,"%s%sCREATE %s/ -> %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + param->arg, + GetNameFF_SZS(&szs), + dest ); + else + fprintf(stdlog,"%s%sENCODE %s/\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + param->arg ); + fflush(stdlog); + } + + if ( create && err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + File_t F; + CreateFileOpt(&F,true,dest,testmode,0); + if (F.f) + { + SetFileAttrib(&F.fatt,&szs.fatt,0); + const u8 * data = szs.cdata ? szs.cdata : szs.data; + const size_t size = szs.cdata ? szs.csize : szs.size; + const size_t wstat = fwrite(data,1,size,F.f); + if ( wstat != size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + size, dest); + } + ResetFile(&F,opt_preserve); + LinkCacheSZS(&szs,dest); + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + ResetSetupParam(&sp); + } + opt_auto_add = save_auto_add; // restore auto-add settings + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command update /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct update_param_t +{ + ccp source; // source path + bool update_sub; // true: update sub archives + bool encode; // true: encode files if possible + int recurse_level; // current recurse level + int indent; // indention of log messages + +} update_param_t; + +//----------------------------------------------------------------------------- + +static enumError job_update +( + szs_file_t *szs, // valid szs file + int recurse_level // recursion level +); + +/////////////////////////////////////////////////////////////////////////////// + +static int update_func +( + struct szs_iterator_t *it, // iterator struct with all infos + bool term // true: termination hint +) +{ + if (term) + return 0; + + DASSERT(it); + DASSERT(it->param); + szs_file_t * szs = it->szs; + DASSERT(szs); + update_param_t * up = it->param; + DASSERT(up); + DASSERT(up->source); + + + //--- ignore directories + + if (it->is_dir) + return 0; + + + //--- setup path + + ccp local_path = it->path; + if ( *local_path == '.' && local_path[1] == '/' ) + local_path += 2; + + char path_buf[PATH_MAX]; + ccp path = PathCatPP(path_buf,sizeof(path_buf),up->source,local_path); + + u8 * data = szs->data + it->off; +// [[analyse-magic]] + file_format_t fform = GetByMagicFF(data,it->size,it->size); + + + //--- [2do] encode files first + + + //--- read updated file + + struct stat st; + if (!stat(path,&st)) + { + if (!S_ISREG(st.st_mode)) + { + if (!opt_ignore) + ERROR0(ERR_WARNING,"No regular file: %s\n",path); + } + else if ( st.st_size != it->size ) + { + if (!opt_ignore) + ERROR0(ERR_WARNING, + "File size mismatch: have %llu, need %u: %s\n", + (u64)st.st_size ,it->size, path ); + } + else + { + if ( verbose > 1 ) + fprintf(stdlog,"%*s- LOAD %s\n", up->indent,"", path ); + LoadFILE(path,0,0,data,it->size,0,0,false); + } + } + + + //--- update sub archives first + + if ( up->update_sub && IsArchiveFF(fform) ) + { + szs_file_t subszs; // [[2do]] [[subfile]] + InitializeSZS(&subszs); + subszs.data = data ; + subszs.size = it->size; + subszs.fname = path; + subszs.fform_file = fform; + subszs.fform_arch = fform; + job_update(&subszs,up->recurse_level+1); + subszs.fname = EmptyString; + ResetSZS(&subszs); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError job_update +( + szs_file_t *szs, // valid szs file + int recurse_level // recursion level +) +{ + DASSERT(szs); + MEM_CHECK; + PRINT("JOB UPDATE: %d/%d %s\n",recurse_level,opt_recurse,szs->fname); + + char source[PATH_MAX]; + StringCat2S(source,sizeof(source),szs->fname,".d"); + if ( !recurse_level && ( opt_source || !IsDirectory(source,false) ) ) + { + ccp my_source = opt_source; + if (!my_source) + { + switch (szs->fform_arch) + { + case FF_U8: + case FF_WU8: + case FF_RARC: + case FF_PACK: + case FF_RKC: + //case FF_BRRES: + my_source = "\1P/\1N.d/"; + break; + + default: + my_source = "\1P/\1F.d/"; + break; + } + } + SubstDest(source,sizeof(source),szs->fname,my_source,0,0,true); + } + + const int indent = 2 * recurse_level; + if ( verbose >= 0 && !recurse_level || verbose > 0 || testmode ) + fprintf(stdlog,"%s%*s- UPDATE %s:%s <- %s\n", + verbose > 1 ? "\n" : "", + indent,"", + GetNameFF_SZS(szs), szs->fname, source ); + + + update_param_t up; + memset(&up,0,sizeof(up)); + up.source = source; + up.encode = !opt_no_encode; + up.recurse_level = recurse_level; + up.update_sub = recurse_level < opt_recurse; + up.indent = indent + 2; + + IterateFilesSZS(szs,update_func,&up,false,0,-1,SORT_NONE); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_update() +{ + if ( opt_source && !*opt_source ) + opt_source = 0; + if ( opt_dest && !*opt_dest ) + opt_dest = 0; + opt_mkdir = true; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,false); + if (!err) + err = job_update(&szs,0); + if ( max_err < err ) + max_err = err; + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS ) + { + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,0,0,false); + + if ( verbose >= 0 || testmode ) + { + fprintf(stdlog,"%s%sCREATE %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF_SZS(&szs), + dest ); + fflush(stdlog); + } + + File_t F; + CreateFileOpt(&F,true,dest,testmode,param->arg); + if (F.f) + { + if (IsCompressedFF(szs.fform_file)) + CompressSZS(&szs,true); + SetFileAttrib(&F.fatt,&szs.fatt,0); + const u8 * data = szs.cdata ? szs.cdata : szs.data; + const size_t size = szs.cdata ? szs.csize : szs.size; + const size_t wstat = fwrite(data,1,size,F.f); + if ( wstat != size ) + err = FILEERROR1(&F,ERR_WRITE_FAILED, + "Writing %zu bytes failed: %s\n", + size, dest); + } + ResetFile(&F,opt_preserve); + } + + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command extract /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_extract ( enumCommands mode ) +{ + ccp basedir = GetOptBasedir(); + if ( mode == CMD_XDECODE ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_DECODE,1,false); + opt_decode = true; + } + else if ( mode == CMD_XEXPORT ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_DECODE,1,false); + opt_decode = true; + RegisterOptionByIndex(&InfoUI_wszst,OPT_EXPORT,1,false); + export_count++; + } + else if ( mode == CMD_XALL ) + { + RegisterOptionByIndex(&InfoUI_wszst,OPT_ALL,1,false); + opt_recurse = INT_MAX; + opt_decode = true; + opt_mipmaps = 1; + } + else if ( mode == CMD_XCOMMON ) + { + opt_recurse = INT_MAX; + opt_decode = false; + basedir = "common/"; + } + + if ( opt_dest && !*opt_dest ) + opt_dest = 0; + opt_mkdir = true; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadCreateSZS(&szs,param->arg,true,opt_ignore>0,false); + + if (!err) + { + DASSERT( !szs.file_size || szs.file_size >= szs.size ); + + if ( analyze_fname && IsBRSUB(szs.fform_arch) ) + AnalyzeBRSUB(&szs,szs.data,szs.size,param->arg); + + have_patch_count -= 1000000; + PRINT("EXTRACT/%s[%s]: %s\n",__FUNCTION__,GetNameFF_SZS(&szs),szs.fname); + err = ExtractFilesSZS(&szs,0,false,0,basedir); + have_patch_count += 1000000; + } + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// convert helper /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static bool ConvertHelper +( + const u8 * data, // valid data + uint data_size, // size of 'data' + ContainerData_t * cdata, // NULL or container data + ccp src_fname, // filename of source + ccp dest_fname, // filename of destination + bool binary_dest, // output: false:text, true:binary + enumError * err // not NULL: store error code here +) +{ + enumError temp_err; + if (!err) + err = &temp_err; + *err = ERR_OK; + +// [[analyse-magic]] + const file_format_t fform = GetByMagicFF(data,data_size,data_size); + switch(fform) + { + case FF_BMG: + case FF_BMG_TXT: + FreeContainerData(cdata); + { + bmg_t bmg; + *err = ScanBMG(&bmg,true,src_fname,data,data_size); + if (*err) + return true; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawXBMG(&bmg,dest_fname,true) + : SaveTextXBMG(&bmg,dest_fname,true); + ResetBMG(&bmg); + } + return true; + + case FF_GH_ITEM: + case FF_GH_IOBJ: + case FF_GH_KART: + case FF_GH_KOBJ: + case FF_GH_ITEM_TXT: + case FF_GH_IOBJ_TXT: + case FF_GH_KART_TXT: + case FF_GH_KOBJ_TXT: + FreeContainerData(cdata); + { + geohit_t geohit; + InitializeGEOHIT(&geohit,fform); + geohit.fname = src_fname; + *err = ScanGEOHIT(&geohit,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawGEOHIT(&geohit,dest_fname,true) + : SaveTextGEOHIT(&geohit,dest_fname,true,minimize_level); + geohit.fname = 0; + ResetGEOHIT(&geohit); + } + return true; + + case FF_KCL: + case FF_KCL_TXT: + case FF_WAV_OBJ: + case FF_SKP_OBJ: + FreeContainerData(cdata); + { + kcl_t kcl; + InitializeKCL(&kcl); + kcl.fname = src_fname; + *err = ScanKCL(&kcl,false,data,data_size,global_check_mode,true); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawKCL(&kcl,dest_fname,true) + : SaveTextKCL(&kcl,dest_fname,true); + kcl.fname = 0; + ResetKCL(&kcl); + } + return true; + + case FF_ITEMSLT: + case FF_ITEMSLT_TXT: + FreeContainerData(cdata); + { + itemslot_t itemslot; + InitializeITEMSLOT(&itemslot,fform); + itemslot.fname = src_fname; + *err = ScanITEMSLOT(&itemslot,false,data,data_size,src_fname); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawITEMSLOT(&itemslot,dest_fname,true) + : SaveTextITEMSLOT(&itemslot,dest_fname,true,minimize_level); + itemslot.fname = 0; + ResetITEMSLOT(&itemslot); + } + return true; + + case FF_KMG: + case FF_KMG_TXT: + FreeContainerData(cdata); + { + minigame_t minigame; + InitializeMINIGAME(&minigame,fform); + minigame.fname = src_fname; + *err = ScanMINIGAME(&minigame,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawMINIGAME(&minigame,dest_fname,true) + : SaveTextMINIGAME(&minigame,dest_fname,true,minimize_level); + minigame.fname = 0; + ResetMINIGAME(&minigame); + } + return true; + + case FF_KMP: + case FF_KMP_TXT: + FreeContainerData(cdata); + { + kmp_t kmp; + InitializeKMP(&kmp); + kmp.fname = src_fname; + *err = ScanKMP(&kmp,false,data,data_size,global_check_mode); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawKMP(&kmp,dest_fname,true) + : SaveTextKMP(&kmp,dest_fname,true); + kmp.fname = 0; + ResetKMP(&kmp); + } + return true; + + case FF_LEX: + case FF_LEX_TXT: + FreeContainerData(cdata); + { + lex_t lex; + InitializeLEX(&lex); + lex.fname = src_fname; + *err = ScanLEX(&lex,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawLEX(&lex,dest_fname,true) + : SaveTextLEX(&lex,dest_fname,true); + lex.fname = 0; + ResetLEX(&lex); + } + return true; + + case FF_MDL: + case FF_MDL_TXT: // [[2do]] add format to UI commands BINARY and TEXT + { + mdl_t mdl; + InitializeMDL(&mdl); + mdl.fname = src_fname; + #if USE_NEW_CONTAINER_MDL + *err = ScanMDL(&mdl,false,data,data_size,cdata,global_check_mode); + #else + *err = ScanMDL(&mdl,false,data,data_size,0,global_check_mode); + #endif + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawMDL(&mdl,dest_fname,true) + : SaveTextMDL(&mdl,dest_fname,true); + mdl.fname = 0; + ResetMDL(&mdl); + } + return true; + + case FF_OBJFLOW: + case FF_OBJFLOW_TXT: + FreeContainerData(cdata); + { + objflow_t objflow; + InitializeOBJFLOW(&objflow); + objflow.fname = src_fname; + *err = ScanOBJFLOW(&objflow,false,data,data_size); + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawOBJFLOW(&objflow,dest_fname,true) + : SaveTextOBJFLOW(&objflow,dest_fname,true,minimize_level); + objflow.fname = 0; + ResetOBJFLOW(&objflow); + } + return true; + + case FF_PAT: + case FF_PAT_TXT: + { + pat_t pat; + InitializePAT(&pat); + pat.fname = src_fname; + #if USE_NEW_CONTAINER_PAT + *err = ScanPAT(&pat,false,data,data_size,cdata,global_check_mode); + #else + *err = ScanPAT(&pat,false,data,data_size,0,global_check_mode); + #endif + if (*err) + return false; + *err = testmode + ? ERR_OK + : binary_dest + ? SaveRawPAT(&pat,dest_fname,true) + : SaveTextPAT(&pat,dest_fname,true); + pat.fname = 0; + ResetPAT(&pat); + } + return true; + + default: + break; + } + + *err = ERR_WRONG_FILE_TYPE; + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command binary/text /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_convert ( bool binary ) // cmd_binary() cmd_text() +{ + static ccp def_path = "\1P/\1N\1?T"; + CheckOptDest(def_path,false); + char dest[PATH_MAX]; + enumError max_err = ERR_OK; + + raw_data_t raw; + InitializeRawData(&raw); + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + { + ResetRawData(&raw); + return err; + } + + file_format_t ff_dest = binary + ? GetBinFF(raw.fform) : GetTextFF(raw.fform); + if (!ff_dest) + { + ERROR0(ERR_WARNING,"File format %s not supported: %s\n", + GetNameFF(raw.fform,0), param->arg ); + if ( max_err < ERR_WARNING ) + max_err = ERR_WARNING; + continue; + } + + SubstDest( dest, sizeof(dest), param->arg, opt_dest, + def_path, GetExtFF(0,ff_dest), false ); + + if ( verbose >= 0 ) + { + fprintf(stdlog,"%sCREATE/%s %s:%s => %s:%s\n", + verbose > 0 ? "\n" : "", + binary ? "BINARY" : "TEXT", + GetNameFF(raw.fform,0), param->arg, + GetNameFF(ff_dest,0), dest ); + fflush(stdlog); + } + + SetupContainerRawData(&raw); + ConvertHelper( raw.data, raw.data_size, + LinkContainerData(&raw.container), + param->arg, dest, binary, &err ); + if (err) + { + ERROR0(err,0); + if ( max_err < err ) + max_err = err; + } + } + + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command cat /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_cat() +{ + stdlog = stderr; + enumError max_err = ERR_OK; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_extract_t eszs; + enumError err = ExtractSZS(&eszs,true,param->arg,0,opt_ignore>0); + PRINT("stat=%u, data=%p, found=%d,%d, path=%s\n", + err, eszs.data, !eszs.subfile_found, eszs.szs_found, eszs.subpath ); + + if (eszs.data) + { + if ( verbose > 0 || testmode ) + { + fprintf(stdlog,"%s%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(eszs.fform_file,eszs.fform_arch), + eszs.fname ); + fflush(stdlog); + } + + if (!testmode) + { + if (opt_decode) + { + ConvertHelper( eszs.data, eszs.data_size, + LinkContainerESZS(&eszs), + eszs.fname, "-", false, &err ); + } + else + { + size_t wstat = fwrite(eszs.data,1,eszs.data_size,stdout); + if ( wstat != eszs.data_size ) + err = ERROR1(ERR_WRITE_FAILED,"Write to stdout failed\n"); + } + } + } + else if ( !err && !opt_ignore ) + err = PrintErrorExtractSZS(&eszs,param->arg); + fflush(stdout); + + if ( max_err < err ) + max_err = err; + ResetExtractSZS(&eszs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_FFORM ( file_format_t fform, ccp filename ) +{ + stdlog = stderr; + char pathbuf[PATH_MAX]; + enable_kcl_drop_auto++; + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),param->arg,filename); + + szs_extract_t eszs; + enumError err = ExtractSZS(&eszs,true,path,0,opt_ignore>0); + PRINT("stat=%u, data=%p, found=%d,%d, path=%s\n", + err, eszs.data, !eszs.subfile_found, eszs.szs_found, eszs.subpath ); + +// [[analyse-magic]] + if ( eszs.data && GetByMagicFF(eszs.data,eszs.data_size,eszs.data_size) == fform ) + { + if ( verbose > 0 || testmode ) + fprintf(stdlog,"%s%sCAT %s:%s\n", + verbose > 0 ? "\n" : "", + testmode ? "WOULD " : "", + GetNameFF(eszs.fform_file,eszs.fform_arch), + eszs.fname ); + + if (!testmode) + ConvertHelper( eszs.data, eszs.data_size, + LinkContainerESZS(&eszs), + eszs.fname, "-", false, &err ); + } + else if ( !err && !opt_ignore ) + err = PrintErrorExtractSZS(&eszs,param->arg); + + if ( max_err < err ) + max_err = err; + ResetExtractSZS(&eszs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_info() +{ + stdlog = stderr; + + char pathbuf[PATH_MAX]; + ccp search_tab[] = { "info.txt", "credits.txt", 0 }; + + szs_extract_t eszs; + InitializeExtractSZS(&eszs); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + ccp * search; + for ( search = search_tab; ; search++ ) + { + if (!*search) + { + if (!opt_ignore) + printf("\n* NO INFO FOUND: %s\n\n",param->arg); + max_err = ERR_WARNING; + break; + } + + ccp path = PathCatPP(pathbuf,sizeof(pathbuf),param->arg,*search); + ResetExtractSZS(&eszs); + enumError err = ExtractSZS(&eszs,false,path,0,true); + if ( !err && eszs.data ) + { + if (print_header) + printf("\n* %s\n\n",eszs.fname); + fwrite(eszs.data,1,eszs.data_size,stdout); + if ( eszs.data_size && eszs.data[eszs.data_size-1] != '\n' ) + putchar('\n'); + if (print_header) + putchar('\n'); + break; + } + } + } + ResetExtractSZS(&eszs); + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command ghost /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_ghost() +{ + static ccp def_path = "\1P/\1F\1?T"; + CheckOptDest(def_path,false); + char dest[PATH_MAX]; + + raw_data_t raw; + InitializeRawData(&raw); + + rkg_info_t ri; + InitializeRKGInfo(&ri); + enumError max_err = ERR_OK; + + File_t fo; + InitializeFile(&fo); + + PrintScript_t ps; + InitializePrintScript(&ps); + ps.fo = &fo; + + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + enumError err = LoadRawData(&raw,false,param->arg,0,opt_ignore>0,0); + if ( err == ERR_NOT_EXISTS || err > ERR_WARNING && opt_ignore ) + continue; + if ( err > ERR_WARNING ) + return err; + + if (!fo.f) + SubstDest(dest,sizeof(dest),param->arg,opt_dest,def_path, + GetExtFF(script_fform,0),false); + + if ( verbose >= 0 ) + { + fprintf(stdlog,"%sANALYZE %s:%s => %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF(raw.fform,0), raw.fname, + GetNameFF(script_fform,0), dest ); + fflush(stdlog); + } + + err = ScanRawDataGHOST(&ri,false,&raw); + if ( err >= ERR_WARNING ) + { + if ( max_err < err ) + max_err = err; + continue; + } + + if (!fo.f) + { + enumError err = CreateFile(&fo,false,dest,FM_STDIO|FM_OVERWRITE); + if (err) + { + max_err = err; + break; + } + PrintScriptHeader(&ps); + } + + PrintScriptVars(&ps,3, + "compressed=%d\n" + "size=%u\n" + "score=%u\n" + "time_button=%u\n" + "time_direction=%u\n" + "time_trick=%u\n" + ,ri.is_compressed + ,ri.data_size + ,ri.score + ,ri.time_but + ,ri.time_dir + ,ri.time_trick + ); + #if 0 + printf("score = %u, compr = %d, size = %u\n", + ri.score, ri.is_compressed, ri.data_size ); + printf("but = %u,%u, dir = %u,%u, trick=%u,%u\n", + ri.n_but, ri.time_but, + ri.n_dir, ri.time_dir, + ri.n_trick, ri.time_trick ); + #endif + + if (!script_array) + { + PrintScriptFooter(&ps); + ResetFile(&fo,0); + } + } + + PrintScriptFooter(&ps); + ResetPrintScript(&ps); + ResetFile(&fo,0); + ResetRKGInfo(&ri); + ResetRawData(&raw); + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command decompress /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError cmd_yazdump() +{ + CheckOptDest("-",false); + + enumError max_err = ERR_OK; + ParamList_t *param; + for ( param = first_param; param; param = param->next ) + { + NORMALIZE_FILENAME_PARAM(param); + + szs_file_t szs; + InitializeSZS(&szs); + enumError err = LoadSZS(&szs,param->arg,false,opt_ignore>0,false); + + if ( err <= ERR_WARNING && err != ERR_NOT_EXISTS + && ( szs.fform_file == FF_YAZ0 || szs.fform_file == FF_YAZ1 )) + { + char dest[PATH_MAX]; + SubstDest(dest,sizeof(dest),param->arg,opt_dest,".txt",".txt",false); + + File_t F; + CreateFileOpt(&F,true,dest,testmode,0); + if (F.f) + { + if ( verbose >= 0 || testmode ) + fprintf(F.f,"%sYAZ DUMP of %s:%s\n", + verbose > 0 ? "\n" : "", + GetNameFF_SZS(&szs), param->arg ); + err = DecompressSZS(&szs,true,F.f); + } + } + + if ( max_err < err ) + max_err = err; + ResetSZS(&szs); + } + + return max_err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command vehicle /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef HAVE_WIIMM_EXT + + enumError cmd_vehicle() + { + ERROR0(ERR_NOT_IMPLEMENTED, + "Command VEHICLE not implemented in this version!\n"); + exit(ERR_NOT_IMPLEMENTED); + } + +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check options /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckOptions ( int argc, char ** argv, bool is_env ) +{ + TRACE("CheckOptions(%d,%p,%d) optind=%d\n",argc,argv,is_env,optind); + + optind = 0; + int err = 0; + ccp filter_bmg = 0; + + for(;;) + { + const int opt_stat = getopt_long(argc,argv,OptionShort,OptionLong,0); + if ( opt_stat == -1 ) + break; + + RegisterOptionByName(&InfoUI_wszst,opt_stat,1,is_env); + + switch ((enumGetOpt)opt_stat) + { + case GO__ERR: err++; break; + + case GO_VERSION: version_exit(); + case GO_HELP: help_exit(false); + case GO_XHELP: help_exit(true); + case GO_ALLOW_ALL: allow_all = true; break; + case GO_COMPATIBLE: err += ScanOptCompatible(optarg); break; + case GO_WIDTH: err += ScanOptWidth(optarg); break; + case GO_QUIET: verbose = verbose > -1 ? -1 : verbose - 1; break; + case GO_VERBOSE: verbose = verbose < 0 ? 0 : verbose + 1; break; + case GO_LOGGING: logging++; break; + case GO_DE: use_de = true; break; + case GO_CT_CODE: ctcode_enabled = true; break; + case GO_LE_CODE: lecode_enabled = true; break; + case GO_COLORS: err += ScanOptColorize(0,optarg,0); break; + case GO_NO_COLORS: opt_colorize = COLMD_OFF; break; + + case GO_CHDIR: err += ScanOptChdir(optarg); break; + case GO_CONST: err += ScanOptConst(optarg); break; + case GO_TOUCH: break; + case GO_AUTO: break; + case GO_SET_FLAGS: err += ScanOptSetFlags(optarg); break; + case GO_SET_SCALE: err += ScanOptSetScale(optarg); break; + case GO_SET_ROT: err += ScanOptSetRot(optarg); break; + case GO_SET_X: err += ScanOptSet(0,optarg); break; + case GO_SET_Y: err += ScanOptSet(1,optarg); break; + case GO_SET_Z: err += ScanOptSet(2,optarg); break; + case GO_XCENTER: break; + case GO_YCENTER: break; + case GO_ZCENTER: break; + case GO_CENTER: break; + case GO_SCALE: err += ScanOptScale(optarg); break; + case GO_SHIFT: err += ScanOptShift(optarg); break; + case GO_XSS: err += ScanOptXSS(0,optarg); break; + case GO_YSS: err += ScanOptXSS(1,optarg); break; + case GO_ZSS: err += ScanOptXSS(2,optarg); break; + case GO_ROT: err += ScanOptRotate(optarg); break; + case GO_XROT: err += ScanOptXRotate(0,optarg); break; + case GO_YROT: err += ScanOptXRotate(1,optarg); break; + case GO_ZROT: err += ScanOptXRotate(2,optarg); break; + case GO_YPOS: err += ScanOptYPos(optarg); break; + case GO_TRANSLATE: err += ScanOptTranslate(optarg); break; + case GO_NULL: force_transform |= 1; break; + case GO_NEXT: err += NextTransformation(false); break; + case GO_ASCALE: err += ScanOptAScale(optarg); break; + case GO_AROT: err += ScanOptARotate(optarg); break; + case GO_TFORM_SCRIPT: err += ScanOptTformScript(optarg); break; + case GO_RM_GOBJ: err += ScanOptRmGobj(optarg); break; + case GO_BATTLE: err += ScanOptBattle(optarg); break; + case GO_EXPORT_FLAGS: err += ScanOptExportFlags(optarg); break; + case GO_ROUTE_OPTIONS: err += ScanOptRouteOptions(optarg); break; + case GO_WIM0: err += ScanOptWim0(optarg); break; + case GO_SLOT: err += ScanOptSlot(optarg); break; + case GO_LOAD_KCL: err += ScanOptLoadKcl(optarg); break; + case GO_KCL: err += ScanOptKcl(optarg); break; + case GO_KCL_FLAG: err += ScanOptKclFlag(optarg); break; + case GO_KCL_SCRIPT: err += ScanOptKclScript(optarg); break; + case GO_TRI_AREA: err += ScanOptTriArea(optarg); break; + case GO_TRI_HEIGHT: err += ScanOptTriHeight(optarg); break; + case GO_FLAG_FILE: opt_flag_file = optarg; break; + case GO_XTRIDATA: opt_xtridata = optarg ? str2ul(optarg,0,10) : 1; break; + case GO_KMP: err += ScanOptKmp(optarg); break; + case GO_SPEED_MOD: err += ScanOptSpeedMod(optarg); break; + case GO_KTPT2: err += ScanOptKtpt2(optarg); break; + case GO_TFORM_KMP: err += ScanOptTformKmp(optarg); break; + case GO_GAMEMODES: err += ScanOptGamemodes(optarg); break; + case GO_REPAIR_XPF: err += ScanOptRepairXPF(optarg); break; + + case GO_MDL: err += ScanOptMdl(optarg); break; + case GO_MINIMAP: break; + case GO_PAT: err += ScanOptPat(optarg); break; + case GO_PATCH_FILE: err += ScanOptPatchFile(optarg); break; + + case GO_KMG_LIMIT: err += ScanOptKmgLimit(optarg); break; + case GO_KMG_COPY: err += ScanOptKmgCopy(optarg); break; + + case GO_LT_CLEAR: opt_lt_clear = true; break; + case GO_LT_ONLINE: err += ScanOptLtOnline(optarg); break; + case GO_LT_N_PLAYERS: err += ScanOptLtNPlayers(optarg); break; + case GO_LT_COND_BIT: err += ScanOptLtCondBit(optarg); break; + case GO_LT_GAME_MODE: err += ScanOptLtGameMode(optarg); break; + case GO_LT_ENGINE: err += ScanOptLtEngine(optarg); break; + case GO_LT_RANDOM: err += ScanOptLtRandom(optarg); break; + case GO_LEX_PURGE: opt_lex_purge = true; break; + + case GO_UTF_8: use_utf8 = true; break; + case GO_NO_UTF_8: use_utf8 = false; break; + + case GO_TEST: testmode++; break; + case GO_FORCE: force_count++; break; + case GO_REPAIR_MAGICS: err += ScanOptRepairMagic(optarg); break; + case GO_TINY: err += ScanOptTiny(optarg); break; + + case GO_ANALYZE: analyze_fname = optarg; break; + case GO_ANALYZE_MODE: err += ScanOptAnalyzeMode(optarg); break; + + #if OPT_OLD_NEW + case GO_OLD: opt_new = opt_new>0 ? -1 : opt_new-1; break; + case GO_STD: opt_new = 0; break; + case GO_NEW: opt_new = opt_new<0 ? +1 : opt_new+1; break; + #endif + case GO_EXTRACT: opt_extract = optarg; break; + + case GO_ESC: err += ScanEscapeChar(optarg) < 0; break; + case GO_SOURCE: SetSource(optarg); break; + case GO_DEST: SetDest(optarg,false); break; + case GO_DEST2: SetDest(optarg,true); break; + case GO_OVERWRITE: opt_overwrite = true; break; + case GO_NUMBER: opt_number = true; break; + case GO_REMOVE_SRC: opt_remove_src = true; break; + case GO_REMOVE_DEST: opt_remove_dest = true; break; + case GO_UPDATE: opt_update = true; break; + case GO_PRESERVE: opt_preserve = true; break; + case GO_IGNORE: opt_ignore++; break; + case GO_IGNORE_SETUP: opt_ignore_setup = true; break; + case GO_PURGE: opt_purge = true; break; + + case GO_YAZ0: SetCompressionFF(0,FF_YAZ0); break; + case GO_YAZ1: SetCompressionFF(0,FF_YAZ1); break; + case GO_XYZ: SetCompressionFF(0,FF_XYZ); break; + case GO_BZ: SetCompressionFF(0,FF_BZ); break; + + case GO_U8: SetCompressionFF(FF_U8,0); break; + case GO_SZS: SetCompressionFF(FF_U8,FF_YAZ0); break; + case GO_WU8: SetCompressionFF(FF_WU8,0); break; + case GO_XWU8: SetCompressionFF(FF_WU8,FF_XYZ); break; + case GO_WBZ: SetCompressionFF(FF_WU8,FF_BZ); break; + //case GO_ARC: SetCompressionFF(FF_RARC,0); break; + case GO_BRRES: SetCompressionFF(FF_BRRES,0); break; + case GO_BREFF: SetCompressionFF(FF_BREFF,0); break; + case GO_BREFT: SetCompressionFF(FF_BREFT,0); break; + case GO_PACK: SetCompressionFF(FF_PACK,0); break; + + case GO_JSON: script_fform = FF_JSON; break; + case GO_SH: script_fform = FF_SH; break; + case GO_BASH: script_fform = FF_BASH; break; + case GO_PHP: script_fform = FF_PHP; break; + case GO_MAKEDOC: script_fform = FF_MAKEDOC; break; + case GO_VAR: script_varname = optarg; break; + case GO_ARRAY: script_array++; break; + case GO_AVAR: script_array++; script_varname = optarg; break; + + case GO_PT_DIR: err += ScanOptPtDir(optarg); break; + case GO_LINKS: opt_links = true; break; + case GO_RM_AIPARAM: opt_rm_aiparam = true; break; + case GO_ALIGN_U8: err += ScanOptAlignU8(optarg); break; + case GO_ALIGN_PACK: err += ScanOptAlignPACK(optarg); break; + case GO_ALIGN_BRRES: err += ScanOptAlignBRRES(optarg); break; + case GO_ALIGN_BREFF: err += ScanOptAlignBREFF(optarg); break; + case GO_ALIGN_BREFT: err += ScanOptAlignBREFT(optarg); break; + case GO_ALIGN: err += ScanOptAlign(optarg); break; + case GO_TRANSFORM: err += ScanOptTransform(optarg); break; + case GO_STRIP: opt_strip = true; break; + + case GO_NO_COMPRESS: opt_compr_mode = -1; break; + case GO_COMPRESS: err += ScanOptCompr(optarg); break; + case GO_FAST: opt_fast = true; err += ScanOptCompr("fast"); break; + case GO_NORM: opt_norm = true; break; + case GO_MAX_FILE_SIZE: err += ScanOptMaxFileSize(optarg); break; + case GO_TRACKS: err += ScanOptTracks(optarg); break; + case GO_ARENAS: err += ScanOptArenas(optarg); break; + case GO_LOAD_BMG: err += ScanOptLoadBMG(optarg); break; + case GO_PATCH_BMG: err += ScanOptPatchMessage(optarg); break; + case GO_MACRO_BMG: err += ScanOptMacroBMG(optarg); break; + case GO_FILTER_BMG: filter_bmg = optarg; break; + case GO_AUTOADD_PATH: DefineAutoAddPath(optarg); break; + + case GO_ENCODE_ALL: opt_encode_all = true; break; + case GO_ENCODE_IMG: opt_encode_img = true; break; + case GO_NO_ENCODE: opt_no_encode = true; break; + case GO_NO_RECURSE: opt_no_recurse = true; break; + case GO_AUTO_ADD: opt_auto_add = true; break; + case GO_NO_ECHO: opt_no_echo = true; break; + case GO_NO_CHECK: opt_no_check = true; break; + case GO_BASEDIR: opt_basedir = optarg; break; + case GO_RECURSE: err += ScanOptRecurse(optarg); break; + case GO_EXT: opt_ext++; break; + case GO_DECODE: opt_decode = true; break; + case GO_CMPR_DEFAULT: err += ScanOptCmprDefault(optarg); break; + case GO_N_MIPMAPS: err += ScanOptNMipmaps(optarg); break; + case GO_MAX_MIPMAPS: err += ScanOptMaxMipmaps(optarg); break; + case GO_MIPMAP_SIZE: err += ScanOptMipmapSize(optarg); break; + case GO_MIPMAPS: opt_mipmaps = +1; break; + case GO_NO_MIPMAPS: opt_mipmaps = -1; break; + case GO_FAST_MIPMAPS: fast_resize_enabled = true; break; + case GO_CUT: opt_cut = true; break; + case GO_RAW: opt_raw = true; break; + + case GO_ROUND: opt_round = true; break; + case GO_LONG: long_count++; break; +// case GO_FULL: full_count++; break; + case GO_EXPORT: export_count++; break; + case GO_SORT: err += ScanOptSort(optarg); break; + case GO_NO_HEADER: print_header = false; break; + case GO_BRIEF: brief_count++; break; + case GO_PIPE: pipe_count++; break; + case GO_DELTA: delta_count++; break; + case GO_DIFF: diff_count++; break; + case GO_NO_PARAM: print_param = false; break; + case GO_EPSILON: err += ScanOptEpsilon(optarg); break; + + case GO_BMG_ENCODING: err += ScanOptBmgEncoding(optarg); break; + case GO_BMG_INF_SIZE: err += ScanOptBmgInfSize(optarg,false); break; + case GO_BMG_MID: err += ScanOptBmgMid(optarg); break; + case GO_FORCE_ATTRIB: err += ScanOptForceAttrib(optarg); break; + case GO_DEF_ATTRIB: err += ScanOptDefAttrib(optarg); break; + case GO_NO_ATTRIB: opt_bmg_no_attrib = true; break; + case GO_X_ESCAPES: opt_bmg_x_escapes = true; break; + case GO_OLD_ESCAPES: opt_bmg_old_escapes = true; break; + case GO_SINGLE_LINE: opt_bmg_single_line++; break; + case GO_NO_BMG_COLORS: opt_bmg_colors = 0; break; + case GO_BMG_COLORS: opt_bmg_colors = 2; break; + case GO_NO_BMG_INLINE: opt_bmg_inline_attrib = false; break; + case GO_CACHE: opt_cache = optarg; break; + case GO_CNAME: opt_cname = optarg; break; + case GO_ID: opt_id = true; break; + case GO_BASE64: opt_base64 = true; err += ScanOptCoding64(optarg); break; + case GO_DB64: opt_db64 = true; err += ScanOptCoding64(optarg); break; + case GO_CODING: err += ScanOptCoding64(optarg); break; + case GO_VERIFY: opt_verify = true; break; + case GO_SECTIONS: print_sections++; break; + case GO_ALL: set_all(0); break; + + // no default case defined + // => compiler checks the existence of all enum values + } + } + + if ( opt_compr_mode == -9 ) + { +// [[?]] + if (opt_compr) + opt_colorize = 1; + list_compressions_exit(); + } + + #ifdef DEBUG + DumpUsedOptions(&InfoUI_wszst,TRACE_FILE,11); + #endif + CloseTransformation(); + NormalizeOptions( verbose > 3 && !is_env ? 3 : 0 ); + SetupBMG(filter_bmg); + SetupKCL(); + SetupKMP(); + SetupMDL(); + + // true until final version of 'post-patch-mkw.sh' + SetupSZSCache(opt_cache,true); + + if (!err) + { + enumError err = SetupPatchingListBMG(); + if ( err > ERR_WARNING ) + return err; + } + + return !err ? ERR_OK : max_error ? max_error : ERR_SYNTAX; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// check command /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError CheckCommand ( int argc, char ** argv ) +{ + const KeywordTab_t * cmd_ct = CheckCommandHelper(argc,argv,CommandTab); + if (!cmd_ct) + hint_exit(ERR_SYNTAX); + + TRACE("COMMAND FOUND: #%lld = %s\n",(u64)cmd_ct->id,cmd_ct->name1); + current_command = cmd_ct; + + if (!allow_all) + { + enumError err = VerifySpecificOptions(&InfoUI_wszst,cmd_ct); + if (err) + hint_exit(err); + } + WarnDepractedOptions(&InfoUI_wszst); + + if ( cmd_ct->id != CMD_ARGTEST ) + { + argc -= optind+1; + argv += optind+1; + + if ( cmd_ct->id == CMD_TEST ) + while ( argc-- > 0 ) + AddParam(*argv++); + else + while ( argc-- > 0 ) + AtFileHelper(*argv++,AddParam); + } + + enumError err = ERR_OK; + switch ((enumCommands)cmd_ct->id) + { + case CMD_VERSION: version_exit(); + case CMD_HELP: PrintHelp(&InfoUI_wszst,stdout,0,"HELP",0,URI_HOME, + first_param ? first_param->arg : 0 ); break; + case CMD_ARGTEST: err = cmd_argtest(argc,argv); break; + case CMD_TEST: err = cmd_test(); break; + case CMD_COLORS: err = Command_COLORS(brief_count?-brief_count:long_count,0,0); + break; + case CMD_ERROR: err = cmd_error(); break; + case CMD_FILETYPE: err = cmd_filetype(); break; + case CMD_FILEATTRIB: err = cmd_fileattrib(); break; + case CMD_BRSUB: err = cmd_brsub(); break; + + case CMD_SYMBOLS: err = DumpSymbols(SetupParserVars()); break; + case CMD_FUNCTIONS: SetupReferenceKCL(0); + err = ListParserFunctions(); break; + case CMD_CALCULATE: err = ParserCalc(SetupParserVars()); break; + case CMD_MATRIX: err = cmd_matrix(); break; + case CMD_FLOAT: err = cmd_float(); break; + case CMD_VR_CALC: err = cmd_vr_calc(); break; + case CMD_VR_RACE: err = cmd_vr_race(); break; + case CMD_AUTOADD: err = cmd_autoadd(); break; + case CMD_TRACKS: err = cmd_tracks(); break; + case CMD_SCANCACHE: err = cmd_scancache(); break; + case CMD_EXPORT: err = cmd_export(); break; + case CMD_CODE: err = cmd_code(); break; + case CMD_RECODE: err = cmd_recode(); break; + case CMD_SUBFILE: err = cmd_subfile(); break; + + case CMD_LIST: err = cmd_list(0); break; + case CMD_LIST_L: err = cmd_list(1); break; + case CMD_LIST_LL: err = cmd_list(2); break; + case CMD_LIST_LLL: err = cmd_list(3); break; + case CMD_LIST_A: set_all(1); err = cmd_list(1); break; + case CMD_LIST_LA: set_all(1); err = cmd_list(2); break; + + case CMD_NAME_REF: err = cmd_name_ref(); break; + + case CMD_ILIST: err = cmd_ilist(0); break; + case CMD_ILIST_L: err = cmd_ilist(1); break; + case CMD_ILIST_LL: err = cmd_ilist(2); break; + case CMD_ILIST_A: set_all(0); err = cmd_ilist(0); break; + case CMD_ILIST_LA: set_all(0); err = cmd_ilist(1); break; + + case CMD_DUMP: err = cmd_dump(); break; + case CMD_MEMORY: err = cmd_memory(); break; + case CMD_MEMORY_A: set_all(1); err = cmd_memory(); break; + case CMD_SHA1: err = cmd_sha1(); break; + case CMD_ANALYZE: err = cmd_analyze(); break; + case CMD_DISTRIBUTION: err = cmd_distribution(); break; + case CMD_DIFF: err = cmd_diff(); break; + + case CMD_CHECK: err = cmd_check(); break; + case CMD_SLOTS: err = cmd_slots(); break; + case CMD_STGI: err = cmd_stgi(); break; + case CMD_ISARENA: err = cmd_isarena(); break; + case CMD_PATCH: err = cmd_patch(); break; + case CMD_COPY: err = cmd_copy(); break; + case CMD_DUPLICATE: err = cmd_duplicate(); break; + case CMD_NORMALIZE: err = cmd_normalize(); break; + case CMD_MINIMAP: err = cmd_minimap(); break; + case CMD_COMPRESS: err = cmd_compress(); break; + case CMD_DECOMPRESS: err = cmd_decompress(); break; + case CMD_ENCODE: err = cmd_create(false); break; + case CMD_CREATE: err = cmd_create(true); break; + case CMD_UPDATE: err = cmd_update(); break; + case CMD_EXTRACT: + case CMD_XDECODE: + case CMD_XEXPORT: + case CMD_XALL: + case CMD_XCOMMON: err = cmd_extract(cmd_ct->id); break; + + case CMD_BINARY: err = cmd_convert(true); break; + case CMD_TEXT: err = cmd_convert(false); break; + case CMD_CAT: err = cmd_cat(); break; + case CMD_BMG: err = cmd_bmg_cat(true); break; + case CMD_KCL: err = cmd_FFORM(FF_KCL,"course.kcl"); break; + case CMD_KMP: err = cmd_FFORM(FF_KMP,"course.kmp"); break; + case CMD_LEX: err = cmd_FFORM(FF_LEX,"course.lex"); break; + case CMD_INFO: err = cmd_info(); break; + case CMD_GHOST: err = cmd_ghost(); break; + case CMD_YAZDUMP: err = cmd_yazdump(); break; + + case CMD_VEHICLE: err = cmd_vehicle(); break; + + // no default case defined + // => compiler checks the existence of all enum values + + case CMD__NONE: + case CMD__N: + help_exit(false); + } + + SaveSZSCache(); + return PrintErrorStat(err,verbose,cmd_ct->name1); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main_wszst() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int main_wszst ( int argc, char ** argv ) +{ + print_title_func = print_title; + SetupLib(argc,argv,WSZST_SHORT); + SetupExtendedSZS(); + + //----- process arguments + + if ( argc < 2 ) + { + printf("\n%s\n%s\nVisit %s%s for more info.\n\n", + text_logo, TITLE, URI_HOME, WSZST_SHORT ); + hint_exit(ERR_OK); + } + + enumError err = CheckEnvOptions("WSZST_OPT",CheckOptions); + if (err) + hint_exit(err); + + err = CheckOptions(argc,argv,false); + if (err) + hint_exit(err); + + err = CheckCommand(argc,argv); + CloseAnalyzeFile(); + DUMP_TRACE_ALLOC(TRACE_FILE); + + if (SIGINT_level) + err = ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + return err; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// tool wrapper /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef int (*main_func) ( int argc, char ** argv ); + +extern int main_wszst ( int argc, char ** argv ); +extern int main_wbmgt ( int argc, char ** argv ); +extern int main_wctct ( int argc, char ** argv ); +extern int main_wimgt ( int argc, char ** argv ); +extern int main_wkclt ( int argc, char ** argv ); +extern int main_wkmpt ( int argc, char ** argv ); +extern int main_wlect ( int argc, char ** argv ); +extern int main_wmdlt ( int argc, char ** argv ); +extern int main_wpatt ( int argc, char ** argv ); +extern int main_wstrt ( int argc, char ** argv ); +extern int main_wrapper ( int argc, char ** argv ); +extern int main_getopt ( int argc, char ** argv ); + +typedef struct wrapper_t +{ + uint hide; // 0:off, 1:hide, 2:is wrapper + main_func func; + ccp name; + ccp info; +} +wrapper_t; + +static const wrapper_t wrapper_tab[] = +{ + { 0, main_wszst, WSZST_SHORT, WSZST_LONG }, + { 0, main_wbmgt, WBMGT_SHORT, WBMGT_LONG }, + { 0, main_wctct, WCTCT_SHORT, WCTCT_LONG }, + { 0, main_wimgt, WIMGT_SHORT, WIMGT_LONG }, + { 0, main_wkclt, WKCLT_SHORT, WKCLT_LONG }, + { 0, main_wkmpt, WKMPT_SHORT, WKMPT_LONG }, + { 0, main_wlect, WLECT_SHORT, WLECT_LONG }, + { 0, main_wmdlt, WMDLT_SHORT, WMDLT_LONG }, + { 0, main_wpatt, WPATT_SHORT, WPATT_LONG }, + { 0, main_wstrt, WSTRT_SHORT, WSTRT_LONG }, + { 2, main_getopt, "getopt", 0 }, + { 2, main_wrapper, "wrapper", 0 }, + {0,0,0} +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const wrapper_t * FindWrapper ( int argc, char ** argv ) +{ + if ( argc > 0 && argv[0] ) + { + ccp name = strrchr(argv[0],'/'); + name = name ? name+1 : argv[0]; + if ( *name == 'w' || *name == 'W' ) + name++; + + const wrapper_t *w; + for ( w = wrapper_tab; w->func; w++ ) + if ( !strncasecmp(w->name+1,name,3) ) + return w; + } + + return wrapper_tab; +} + +/////////////////////////////////////////////////////////////////////////////// + +int main_wrapper ( int argc, char ** argv ) +{ + SetupLib(argc,argv,TOOLSET_SHORT); + + enum + { + C_CREATE = 0x001, + C_OVERWRITE = 0x002, + + C_HARDLINKS = 0x010, + C_SOFTLINKS = 0x020, + C_CYGWIN = 0x040, + C_SHELL = 0x080, + C_BASH = 0x100, + + C_QUIET = 0x200, + C_VERBOSE = 0x400, + + C_M_DEST = C_HARDLINKS|C_SOFTLINKS|C_CYGWIN|C_SHELL, + }; + + static const KeywordTab_t keytab[] = + { + { C_CREATE, "CREATE", 0, 3 }, + { C_CREATE|C_OVERWRITE, "OVERWRITE", 0, 4 }, + + { C_HARDLINKS, "HARDLINKS", 0, 4 }, + #ifdef __CYGWIN__ + { C_HARDLINKS, "BESTLINKS", 0, 4 }, + #endif + { C_SOFTLINKS, "SOFTLINKS", 0, 4 }, + #ifndef __CYGWIN__ + { C_SOFTLINKS, "BESTLINKS", 0, 4 }, + #endif + { C_CYGWIN, "CYGWIN", 0, 3 }, + { C_SHELL, "SHELL", 0, 2 }, + { C_SHELL|C_BASH, "BASH", 0, 4 }, + + { C_QUIET, "QUIET", 0, 1 }, + { C_VERBOSE, "VERBOSE", 0, 1 }, + + {0,0,0,0} + }; + + uint i, param = 0; + for ( i = 1; i < argc; i++ ) + { + const KeywordTab_t *key = ScanKeyword(0,argv[i],keytab); + if ( !key || strlen(argv[i]) < key->opt ) + goto abort; + param |= key->id; + } + + const uint quiet = param & C_QUIET; + const uint verbose = param & C_VERBOSE; + const uint overwrite= param & C_OVERWRITE; + const uint bash = param & C_BASH; + const uint desttype = param & C_M_DEST; + + char mypath[PATH_MAX], dest[PATH_MAX+20]; + GetProgramPath(mypath,sizeof(mypath),true,argv[0]); + if (verbose) + printf("My Path: %s\n",mypath); + + if ( param & C_CREATE + && Count1Bits(&desttype,sizeof(desttype)) == 1 + && *mypath ) + { + memcpy(dest,mypath,sizeof(dest)); + char *fname = strrchr(dest,'/'); + fname = fname ? fname+1 : dest; + if (!*fname) + goto abort; + + u16 cygwin_link[1000]; + uint cygwin_len = 0; + if ( desttype == C_CYGWIN ) + { + ccp src = fname; + while ( cygwin_len < sizeof(cygwin_link)/sizeof(*cygwin_link) ) + { + u32 code = ScanUTF8AnsiChar(&src); + if (!code) + break; + write_le16(cygwin_link+cygwin_len++,code); + } + } + + ccp srcname = mypath + (fname-dest); + char srcname_buf[PATH_MAX]; + if ( desttype == C_SHELL ) + { + ccp src; + for ( src = srcname; *src; src++ ) + { + const int ch = *src; + if ( !isalnum(ch) && ch != '_' && ( ch != '-' || src == srcname ) ) + { + uint len; + PrintEscapedString( srcname_buf+1, + sizeof(srcname_buf)-2, srcname, -1, CHMD__ALL, '"', &len ); + if (bash) + srcname = srcname_buf+1; + else + { + srcname_buf[0] = srcname_buf[len] = '"'; + srcname = srcname_buf; + } + break; + } + } + } + + FILE *f = 0; + const wrapper_t *w; + + for ( w = wrapper_tab; w->func; w++ ) + { + if (w->hide) + continue; + + strcpy(fname,w->name); + if (!strcmp(mypath,dest)) + continue; + + struct stat st; + if ( !overwrite && !stat(dest,&st) ) + continue; + unlink(dest); + + switch(desttype) + { + case C_HARDLINKS: + if (!quiet) + printf("CREATE HARDLINK %s\n",dest); + link(mypath,dest); + break; + + case C_SOFTLINKS: + if (!quiet) + printf("CREATE SOFTLINK %s\n",dest); + symlink(srcname,dest); + break; + + case C_CYGWIN: + if (!quiet) + printf("CREATE CYGWIN SOFTLINK %s\n",dest); + f = fopen(dest,"w"); + if (f) + { + fwrite("!\xff\xfe",1,12,f); + fwrite(cygwin_link,2,cygwin_len,f); + fwrite("\0\0",1,2,f); + fclose(f); + } + break; + + case C_SHELL: + if (!quiet) + printf("CREATE %sSH SCRIPT %s\n", bash ? "BA" : "", dest); + f = fopen(dest,"w"); + if (f) + { + if (bash) + fprintf(f,"#!/usr/bin/env bash\n" + "\"$(dirname \"$BASH_SOURCE\")/%s\" %s \"$@\"\n", + srcname, w->name ); + else + fprintf(f,"#!/bin/sh\n%s %s \"$@\"\n",srcname,w->name); + fclose(f); + chmod(dest,0755); + } + break; + //"$(dirname "$BASH_SOURCE")" + } + } + return ERR_OK; + } + + abort:; + fprintf(stdout, + "\n%s\n%s\n\n Usage: %s TOOLNAME ...\n\n" + "This is a wrapper for the following tools:\n\n", + text_logo, TOOLSET_TITLE, progname ); + + const wrapper_t *w, *active = FindWrapper(argc,argv); + DASSERT(active); + for ( w = wrapper_tab; w->func; w++ ) + if ( w == active || !w->hide ) + fprintf(stdout," %c %-5s : %s\n", + w == active ? '*' : ' ', w->name, w->info ); + + fprintf(stdout, + "\nWrapper commands:\n\n" + " WRAPPER HELP\n" + " WRAPPER CREATE [QUIET] HARDLINKS|SOFTLINKS|BESTLINKS|CYGWIN|SHELL|BASH\n" + " WRAPPER OVERWRITE [QUIET] HARDLINKS|SOFTLINKS|BESTLINKS|CYGWIN|SHELL|BASH\n" + "\n" + " H[ELP] : Print this help and exit.\n" + " CRE[ATE] : Create links or scripts, but don't overwrite.\n" + " OVER[WRITE] : Create links or scripts and remove existing files before.\n" + " HARD[LINKS] : Create hard links to the main program.\n" + " SOFT[LINKS] : Create soft links to the main program.\n" + " BEST[LINKS] : For Cygwin same as HARDLINKS, for all other same as SOFTLINKS.\n" + " CYG[WIN] : Create softlinks for Cygwin (plain files with special content).\n" + " SH[ELL] : Create simple `sh´ scripts assuming the main program is in PATH.\n" + " BASH : Create `bash´ scripts with run time path detection.\n" + " Q[UIET] : Option to suppress creation messages.\n" + "\n" + " See https://szs.wiimm.de/doc/wrapper for details.\n" + " \n"); + + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int main ( int argc, char ** argv ) +{ + //PRINT("ERR__N=%u\n",ERR__N); + if ( argc > 1 ) + { + const wrapper_t *w; + for ( w = wrapper_tab; w->func; w++ ) + if (!strcasecmp(w->name,argv[1])) + { + argv[1] = argv[0]; + return w->func(argc-1,argv+1); + } + } + + const wrapper_t *w = FindWrapper(argc,argv); + DASSERT(w); + return w->func(argc,argv); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + diff --git a/project/src/wtest.c b/project/src/wtest.c new file mode 100644 index 0000000..7d94158 --- /dev/null +++ b/project/src/wtest.c @@ -0,0 +1,3935 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +//#include +//#include + +#include "dclib-debug.h" +#include "version.h" +#include "lib-std.h" +#include "lib-image.h" +#include "lib-object.h" +#include "lib-kmp.h" +#include "lib-brres.h" +#include "lib-mdl.h" +#include "lib-bzip2.h" +#include "lib-mkw.h" +#include "lib-bmg.h" +#include "lib-lecode.h" +#include "lib-common.h" + +#define CMD1_FW 13 + +#if HAVE_PCRE || 1 + #include "dclib-regex.h" +#endif + +#ifdef HAVE_WIIMM_EXT + #include "wtest+.c" +#endif + +/////////////////////////////////////////////////////////////////////////////// + +#define NAME "wtest" +#undef TITLE +#define TITLE NAME " v" VERSION " r" REVISION " " SYSTEM " - " AUTHOR " - " DATE + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void print_title ( FILE * f ) +{ + static bool done = false; + if (!done) + { + done = true; + if ( verbose >= 1 && f == stdout ) + fprintf(f,"\n%s\n\n",TITLE); + else + fprintf(f,"***** %s *****\n",TITLE); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_float() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void test_float ( int argc, char ** argv ) +{ + uint i; + for ( i = 1; i < argc; i++ ) + { + char buf[5]; + memset(buf,0,sizeof(buf)); + strncpy(buf,argv[i],4); + float f = bef4(buf); + printf(" |%s| %11.8f %11.8e %11.8a |%.4s|\n",buf,f,f,f,(ccp)&f); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// create_float_tab() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError create_float_tab() +{ + union { float f; u32 i; } u; + uint i; + for ( i = 1; i <= 100; i += 5 ) + { + uint j; + for ( j = i; j 0 ) + n_pass = num < 1000000000 ? num : 1000000000; + } + } + + + //--- setup test vectors + + PATCH_FILE_MODE = 0; // disable logging + + double3 d = GetRandomD3(15000); + double dtab[] = { -10000, -5000, -10000/3.0, 0, 10000.0/3.0, + 5000, 10000, d.x, d.y, d.z }; + #undef N_NUM + #define N_NUM (sizeof(dtab)/sizeof(*dtab)) + #define N_VEC (N_NUM*N_NUM*N_NUM) + double3 vtab[N_VEC], *vptr; + + printf("n(numbers)=%zu, n(vectors)=%zu, n(pass)=%u\n",N_NUM,N_VEC,n_pass); + + uint x, y, z; + vptr = vtab; + for ( x = 0; x < N_NUM; x++ ) + { + printf(" num[%u] = %11.3f\n",x,dtab[x]); + for ( y = 0; y < N_NUM; y++ ) + { + for ( z = 0; z < N_NUM; z++ ) + { + DASSERT( vptr < vtab+N_VEC ); + vptr->x = dtab[x]; + vptr->y = dtab[y]; + vptr->z = dtab[z]; + vptr++; + } + } + } + fflush(stdout); + DASSERT( vptr == vtab+N_VEC ); + + //--- + + uint pass, err_count = 0, total_count = 0; + for ( pass = 0; pass < n_pass; pass++ ) + { + ResetTransformation(); + if (pass) + { + // random setup + double3 val, origin; + + val = GetRandomD3nn(10.0); + origin = GetRandomD3(1e5); + SetScaleMatrixD(&opt_transform,&val,&origin); + + val = GetRandomD3(1e5); + SetShiftMatrixD(&opt_transform,&val); + + val = GetRandomD3(1e5); + SetTranslateMatrixD(&opt_transform,&val); + + val = GetRandomD3(1000.0); + uint xyz; + for ( xyz = 0; xyz < 3; xyz++ ) + { + origin = GetRandomD3(1e4); + SetRotateMatrixD(&opt_transform,xyz,val.v[xyz],0.0,&origin); + } + } + CloseTransformation(); + global_check_mode = opt_no_check + ? 0 + : GetCheckMode(false,brief_count>0,!verbose,long_count>0); + + if (verbose) + { + char buf[60]; + uint len = snprintf(buf,sizeof(buf),"##### PASS %u/%u [err=%u] #####", + pass+1 ,n_pass, err_count ); + printf("\n%.*s\n%s\n%.*s\n",len,Hash200,buf,len,Hash200); + PrintTestMatrix(true); + } + else if ( pass % 1000 == 999 ) + printf("##### PASS %u/%u [err=%u] #####\n",pass+1,n_pass,err_count); + fflush(stdout); + + int i, fail = -1; + for ( i = 0; i < N_VEC; i++ ) + { + total_count++; + if (!TestMatrix(vtab+i,1,true)) + { + err_count++; + fail = i; + } + } + + if ( fail >= 0 ) + { + MatrixD_t *m = &opt_transform; + printf("\n\n" + " --scale %5.3f,%5.3f,%5.3f@%5.3f,%5.3f,%5.3f" + " --shift %5.3f,%5.3f,%5.3f" + " --xrot %5.3f@%5.3f,%5.3f,%5.3f" + " --yrot %5.3f@%5.3f,%5.3f,%5.3f" + " --zrot %5.3f@%5.3f,%5.3f,%5.3f" + " --translate %5.3f,%5.3f,%5.3f" + " %5.3f,%5.3f,%5.3f" + "\n\n", + m->scale.x, m->scale.y, m->scale.z, + m->scale_origin.x, m->scale_origin.y, m->scale_origin.z, + m->shift.x, m->shift.y, m->shift.z, + m->rotate_deg.v[0], m->rotate_origin[0].x, + m->rotate_origin[0].y, m->rotate_origin[0].z, + m->rotate_deg.v[1], m->rotate_origin[1].x, + m->rotate_origin[1].y, m->rotate_origin[1].z, + m->rotate_deg.v[2], m->rotate_origin[2].x, + m->rotate_origin[2].y, m->rotate_origin[2].z, + m->translate.x, m->translate.y, m->translate.z, + vtab[fail].x, vtab[fail].y, vtab[fail].z ); + } + } + + printf("\nerr=%u, total=%u\n",err_count,total_count); + if ( err_count && max_error < ERR_DIFFER ) + max_error = ERR_DIFFER; + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_match_pattern() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void test_match_pattern ( int argc, char ** argv ) +{ + int i, j, i_end = argc, j_start = 1; + for ( i = 1; i < argc; i++ ) + if (!strcmp(argv[i],"-")) + { + i_end = i; + j_start = i + 1; + break; + } + + for ( i = 1; i < i_end; i++ ) + for ( j = j_start; j < argc; j++ ) + printf("%u |%s|%s|\n",MatchPattern(argv[i],argv[j],'/'),argv[i],argv[j]); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_median_cut() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void call_mc ( const u32 * tab, uint ntab, uint max_pal ) +{ + u32 pal[0x200]; + u16 *res = (u16*)iobuf; + + ASSERT( ntab <= sizeof(iobuf)/sizeof(*tab) ); + ASSERT( max_pal <= sizeof(pal)/sizeof(*pal) ); + + u64 usec = GetTimerUSec(); + const uint npal = MedianCut(tab,res,ntab,0,1,pal,max_pal); + usec = GetTimerUSec() - usec; + + printf("\n*** %u palettes calculated in %llu usec ***",npal,usec); + + uint i; + for ( i = 0; i < npal; i++ ) + printf("%c%08x", i&7 ? ' ' : '\n', pal[i] ); + + for ( i = 0; i < ntab; i++ ) + printf("%s%02x", !(i&15) ? "\n" : i&3 ? " " : " ", res[i] ); + putchar('\n'); +} + +#define MC_CALL(t,n) call_mc(t,sizeof(t)/sizeof(u32),n); + +/////////////////////////////////////////////////////////////////////////////// + +static u32 mctab_4[] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }; + +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_median_cut ( int argc, char ** argv ) +{ + uint done_count = 0, wrong_arg_count = 0; + + int i; + for ( i = 1; i < argc; i++ ) + { + char *end; + uint num = strtoul(argv[i],&end,10); + if ( *end || end == argv[i] ) + num = 0; + + done_count++; + switch(num) + { + case 1: MC_CALL(mctab_4,256); break; + case 2: MC_CALL(mctab_4,2); break; + case 3: MC_CALL(mctab_4,3); break; + + default: + printf("Syntax error: %s\n",argv[i]); + wrong_arg_count++; + break; + } + } + + if ( !done_count || wrong_arg_count ) + fprintf(stderr, + "\n" + "usage: %s median-cut mode...\n" + " 1: 4 -> 256\n" + " 2: 4 -> 2\n" + " 3: 4 -> 3\n" + "\n", + NAME ); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_cmpr() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint calc_distance ( const u8 * v1, const u8 * v2 ) +{ + const int d0 = (int)*v1++ - (int)*v2++; + const int d1 = (int)*v1++ - (int)*v2++; + const int d2 = (int)*v1++ - (int)*v2++; + return abs(d0) + abs(d1) + abs(d2); +} + +//----------------------------------------------------------------------------- + +static uint calc_distance_square ( const u8 * v1, const u8 * v2 ) +{ + const int d0 = (int)*v1++ - (int)*v2++; + const int d1 = (int)*v1++ - (int)*v2++; + const int d2 = (int)*v1++ - (int)*v2++; + return d0*d0 + d1*d1 + d2*d2; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CMPR_distance +( + const u8 *data, // source data + cmpr_info_t *info, // info data structure + bool square_dist // true: use square distance +) +{ + DASSERT(info); + memset(info,0,sizeof(*info)); + + uint (*dist_func) ( const u8 * v1, const u8 * v2 ); + if ( square_dist) + { + dist_func = calc_distance_square; + info->name = "Dist^2"; + } + else + { + dist_func = calc_distance; + info->name = "Dist"; + } + + //--- analyze data (algorithm adapted from Chadderz CTools) + + uint opaque_count = 0; + int max_dist = -1; + + const u8 *data_end = data + CMPR_DATA_SIZE; + const u8 *pal0 = data; // palette entry #0 + const u8 *pal1 = data; // palette entry #1 + + const u8 *d1; + for ( d1 = data; d1 < data_end; d1 += 4 ) + { + if ( d1[3] & 0x80 ) + { + if (!opaque_count++) + pal0 = pal1 = data; + const u8 *d2; + for ( d2 = d1+4; d2 < data_end; d2 += 4 ) + { + if ( d2[3] & 0x80 ) + { + const int dist = dist_func(d1,d2); + if ( max_dist < dist ) + { + max_dist = dist; + pal0 = d1; + pal1 = d2; + } + } + } + } + } + + + //--- calculate and assign values + + info->opaque_count = opaque_count; + if (opaque_count) + { + memcpy(info->p[0],pal0,4); + memcpy(info->p[1],pal1,4); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static ccp print_col ( const u8 * val ) +{ + if ( val[3] < 0x80 ) + return "-transp-"; + + static char buf[10]; + snprintf(buf,sizeof(buf),"%02x-%02x-%02x",val[0],val[1],val[2]); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_cmpr ( int argc, char ** argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr, + "\n" + "usage: %s cmpr [color]...\n" + " color: 6 hexdigit color, each channel 00..ff\n" + " or the keyword 't' -> transparent pixel\n" + "\n", + NAME ); + return ERR_OK; + } + + + //--- scan params + + u8 vector[CMPR_MAX_COL][4]; + memset(vector,0,sizeof(vector)); + uint n_vector = 0; + + int i; + for ( i = 1; i < argc && n_vector < CMPR_MAX_COL; i++ ) + { + ccp arg = argv[i]; + if ( *arg == 't' || *arg == 'T' ) + n_vector++; + else + { + uint num = strtoul(arg,0,16); + if ( num < 0x1000000 && n_vector < CMPR_MAX_COL ) + { + u8 *v = vector[n_vector++]; + *v++ = num >> 16; + *v++ = num >> 8; + *v++ = num; + *v++ = 0xff; + } + } + } + + if ( n_vector > 0 ) + while ( n_vector < CMPR_MAX_COL ) + { + u8 *v = vector[n_vector]; + memcpy(v,v-4,4); + n_vector++; + } + + + //---- collect data + + const uint n_info = 3; + cmpr_info_t info_tab[n_info]; + + const uint N_LOOP = 100000; + for ( i = 0; i < n_info; i++ ) + { + cmpr_info_t *info = info_tab + i; + const u64 usec = GetTimerUSec(); + uint n = N_LOOP; + while ( n-- > 0 ) + switch (i) + { + case 0: CMPR_distance(vector[0],info,false); break; + case 1: CMPR_distance(vector[0],info,true); break; + case 2: CMPR_wiimm(vector[0],info); break; + default: ASSERT(0); + } + info->usec = GetTimerUSec() - usec; + CMPR_close_info(vector[0],info,info->cmpr,true); + } + + + //---- print out + + const uint seplen = 9 + 9 * n_info; + printf("%.*s\n ",seplen,Minus300); + + for ( i = 0; i < n_info; i++ ) + printf(" %8.8s",info_tab[i].name); + printf("\n%.*s\n",seplen,Minus300); + + uint v; + for ( v = 0; v < 2; v++ ) + { + printf("result-%u:",v); + for ( i = 0; i < n_info; i++ ) + { + u8 *c = info_tab[i].cmpr + 4*v; + printf(" %02x%02x%02x%02x",c[0],c[1],c[2],c[3]); + } + putchar('\n'); + } + printf("%.*s\n",seplen,Minus300); + + for ( v = 0; v < 4; v++ ) + { + printf(" P%u:",v); + for ( i = 0; i < n_info; i++ ) + printf(" %s",print_col(info_tab[i].p[v])); + putchar('\n'); + } + printf("%.*s\n",seplen,Minus300); + + for ( v = 0; v < n_vector; v++ ) + { + printf("%s:",print_col(vector[v])); + for ( i = 0; i < n_info; i++ ) + { + cmpr_info_t *info = info_tab + i; + printf("%6u,%u ",info->dist[v],info->index[v]); + } + putchar('\n'); + } + + printf("%.*s\nsum-dist:",seplen,Minus300); + for ( i = 0; i < n_info; i++ ) + printf("%8u ",info_tab[i].total_dist); + printf("\n nanosec:"); + for ( i = 0; i < n_info; i++ ) + printf("%8llu ",info_tab[i].usec * 1000 / N_LOOP ); + printf("\n%.*s\n",seplen,Minus300); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// create_images() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#undef SETUP_PART +#define SETUP_PART(a,b) x1=(a)*wd+2; x2=x1+wd-4; y1=(b)*ht+2; y2=y1+ht-4; + +static void CreateTestImage ( ccp dest_path ) +{ + //--- setup + + const uint N = 4; + const uint wd = 100; + const uint ht = 100; + int x1, x2, y1, y2, i, j, x, y; + + Color_t white, black, red, blue, col1[5], col2[5]; + //SETCOLOR(trans,0xff,0xff,0xff,0x00); + SETCOLOR(white,0xff,0xff,0xff,0xff); + SETCOLOR(black,0x00,0x00,0x00,0xff); + SETCOLOR(red, 0xff,0x00,0x00,0xff); + SETCOLOR(blue, 0x00,0x00,0xff,0xff); + SETCOLOR(col1[0],0x00,0x00,0x00,0xff); + SETCOLOR(col1[1],0xff,0x00,0x00,0xff); + SETCOLOR(col1[2],0x00,0xff,0x00,0xff); + SETCOLOR(col1[3],0x00,0x00,0xff,0xff); + SETCOLOR(col1[4],0xff,0xff,0xff,0xff); + SETCOLOR(col2[0],0x00,0x00,0x00,0x00); + SETCOLOR(col2[1],0xff,0x00,0x00,0x00); + SETCOLOR(col2[2],0x00,0xff,0x00,0x00); + SETCOLOR(col2[3],0x00,0x00,0xff,0x00); + SETCOLOR(col2[4],0xff,0xff,0xff,0x00); + + Image_t img; + InitializeIMG(&img); + CreateIMG(&img,true,N*wd,N*ht,white); + + + //--- stripes, wd=1 + + SETUP_PART(0,0); + x = x1+1; + for ( ; x < x2; x += 3 ) + DrawVLineIMG(&img, x, y1, y2, black ); + + SETUP_PART(1,0); + y = y1+1; + for ( ; y < y2; y += 3 ) + DrawHLineIMG(&img, x1, x2, y, black ); + + SETUP_PART(2,0); + x = x1+1; y = y1+1; + for ( ; x < x2; x += 3, y += 3 ) + { + DrawHLineIMG(&img, x1, x2, y, black ); + DrawVLineIMG(&img, x, y1, y2, black ); + } + + + //--- stripes, wd=1..3 + + SETUP_PART(0,1); + x = x1+3; + for ( i = 1; x+i < x2; i++ ) + { + for ( j = 0; j < 4 && x+i < x2; j++ ) + { + FillRectIMG(&img,x,y1,x+i,y2,black,false); + x += 2*i; + } + x += 3; + } + + SETUP_PART(1,1); + y = y1+3; + for ( i = 1; y+i < y2; i++ ) + { + for ( j = 0; j < 4 && y+i < y2; j++ ) + { + FillRectIMG(&img,x1,y,x2,y+i,black,false); + y += 2*i; + } + y += 3; + } + + SETUP_PART(2,1); + x = x1+3; y = y1+3; + for ( i = 1; x+i < x2 && y+i < y2; i++ ) + { + for ( j = 0; j < 4 && x+i < x2 && y+i < y2; j++ ) + { + FillRectIMG(&img,x,y1,x+i,y2,black,false); + FillRectIMG(&img,x1,y,x2,y+i,black,false); + x += 2*i; + y += 2*i; + } + x += 3; + y += 3; + } + + + //--- gradients + + SETUP_PART(0,2); + x = x1 + wd/10; + for ( i = 0; i < 4; i++ ) + { + FillVGradientIMG(&img,x,y1,x+wd/10,y2,col1[i],white); + x += wd/5; + } + + SETUP_PART(1,2); + y = y1 + wd/10; + for ( i = 0; i < 4; i++ ) + { + FillHGradientIMG(&img,x1,y,x2,y+wd/10,col1[i],white); + y += wd/5; + } + + SETUP_PART(2,2); + x = x1 + wd/10; + y = y1 + wd/10; + for ( i = 0; i < 4; i++ ) + { + FillVGradientIMG(&img,x,y1,x+wd/10,y2,col1[i],white); + FillHGradientIMG(&img,x1,y,x2,y+wd/10,col1[i],white); + x += wd/5; + y += wd/5; + } + + + //--- transparent gradients + + SETUP_PART(0,3); + x = x1 + wd/10; + for ( i = 0; i < 4; i++ ) + { + FillVGradientIMG(&img,x,y1,x+wd/10,y2,col1[i],col2[i]); + x += wd/5; + } + + SETUP_PART(1,3); + y = y1 + wd/10; + for ( i = 0; i < 4; i++ ) + { + FillHGradientIMG(&img,x1,y,x2,y+wd/10,col1[i],col2[i]); + y += wd/5; + } + + SETUP_PART(2,3); + x = x1 + wd/10; + y = y1 + wd/10; + for ( i = 0; i < 4; i++ ) + { + FillVGradientIMG(&img,x,y1,x+wd/10,y2,col1[i],col2[i]); + FillHGradientIMG(&img,x1,y,x2,y+wd/10,col1[i],col2[i]); + x += wd/5; + y += wd/5; + } + + + //--- rhombus + + SETUP_PART(3,0); + x = x1 + wd/2; + for ( i = 0; i < wd/2-1; i++ ) + { + DrawHLineIMG(&img, x-i, x+i+1, y1+i, black ); + DrawHLineIMG(&img, x-i, x+i+1, y2-i, black ); + } + + SETUP_PART(3,1); + x = x1 + wd/2; + for ( i = 0; i < wd/2-1; i++ ) + { + DrawHLineIMG(&img, x-i/2, x+i/2+1, y1+i, blue ); + DrawHLineIMG(&img, x-i/2, x+i/2+1, y2-i, blue ); + } + + SETUP_PART(3,2); + y = y1 + wd/2; + for ( i = 0; i < wd/2-1; i++ ) + { + DrawVLineIMG(&img, x1+i, y-i/2, y+i/2+1, red ); + DrawVLineIMG(&img, x2-i, y-i/2, y+i/2+1, red ); + } + + + //--- circle + + Image_t circ; + InitializeIMG(&circ); + CreateIMG(&circ,true,1000,1000,white); + x1 = circ.width/2; + y1 = circ.height/2; + + int r = circ.width/2 - 50; + x = r; + y = 0; + r *= r; + int r2 = r; + + while ( x >= 0 ) + { + DrawHLineIMG(&circ, x1-x, x1+x, y1-y, black ); + DrawHLineIMG(&circ, x1-x, x1+x, y1+y, black ); + if ( r > r2 ) + { + x--; + r -= 2*x + 1; + } + else + { + y++; + r += 2*y + 1; + } + } + + SmartResizeIMG(&circ,false,0,wd,ht); + SETUP_PART(3,3); + CopyRectIMG(&img,x1,y1, &circ,0,0, wd,ht, PIM_COPY ); + + + //--- terminate + + SavePNG(&img,true,dest_path,"test-image.png",0,true,0); + ResetIMG(&img); +} + +#undef SETUP_PART + +/////////////////////////////////////////////////////////////////////////////// + +static enumError save_image +( + Image_t * img, // pointer to valid img + ccp path1, // NULL or part #1 of path + ccp path2, // NULL or part #2 of path + Color_t line_col // color of lines +) +{ + DASSERT(img); + + DrawFrameIMG(img,0,0,img->width-1,img->height-1,line_col); + DrawHLineIMG(img,0,img->width,img->height/2,line_col); + DrawVLineIMG(img,img->width/2,0,img->height,line_col); + + return SavePNG(img,true,path1,path2,0,true,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int create_images ( int argc, char ** argv ) +{ + if ( argc != 2 ) + { + fprintf(stderr,"Usage: %s CREATE-IMAGES destdir\n",NAME); + return ERR_SYNTAX; + } + + Color_t base, col1, col2; + SETCOLOR(base,0xff,0xff,0xff,0x00); + + const uint len1 = 201; + const uint size1 = 20; + const uint next1 = 30; + const uint beg1 = ( len1 - 5 * next1 - size1 ) / 2; + + const uint len2 = 391; + const uint beg2 = 5; + const uint end2 = len2 - beg2; + + uint pos; + + + //--- bg-temp.png + + Image_t bg_img; + SETCOLOR(col1,0xff,0xff,0xff,0xff); + CreateIMG(&bg_img,true,len1+len2,len1+len2,col1); + + const uint bg_step = 16; + SETCOLOR(col1,0xc0,0xc0,0xc0,0xff); + int y, xstart = 0; + for ( y = -4; y < (int)bg_img.height; y += bg_step ) + { + xstart = bg_step - xstart; + int x; + for ( x = xstart-4; x < (int)bg_img.width; x += 2*bg_step ) + FillRectIMG(&bg_img,x,y,x+bg_step,y+bg_step,col1,true); + } + SavePNG(&bg_img,true,argv[1],"bg-temp.png",0,true,0); + + + //--- blue-temp.png + + Image_t blue_img; + CreateIMG(&blue_img,true,len1,len2,base); + + pos = beg1; + SETCOLOR(col1,0x00,0x00,0xff,0xff); + FillRectIMG(&blue_img,pos,beg2,pos+size1,end2,col1,true); + + pos += next1; + SETCOLOR(col1,0x00,0x00,0xff,0xff); + SETCOLOR(col2,0x00,0x00,0x00,0xff); + FillVGradientIMG(&blue_img,pos,beg2,pos+size1,end2,col1,col2); + + pos += next1; + SETCOLOR(col1,0x00,0x00,0xff,0xff); + SETCOLOR(col2,0x00,0x00,0xff,0x00); + FillVGradientIMG(&blue_img,pos,beg2,pos+size1,end2,col1,col2); + + pos += next1; + SETCOLOR(col1,0x00,0x00,0x00,0xff); + SETCOLOR(col2,0xff,0xff,0xff,0xff); + FillVGradientIMG(&blue_img,pos,beg2,pos+size1,end2,col1,col2); + + pos += next1; + SETCOLOR(col1,0x00,0x00,0x00,0xff); + SETCOLOR(col2,0x00,0x00,0x00,0x00); + FillVGradientIMG(&blue_img,pos,beg2,pos+size1,end2,col1,col2); + + SETCOLOR(col1,0x00,0x00,0x80,0xff); + save_image(&blue_img,argv[1],"blue-temp.png",col1); + + + //--- red-temp.png + + Image_t red_img; + CreateIMG(&red_img,true,len2,len1,base); + + pos = beg1; + SETCOLOR(col1,0xff,0x00,0x00,0xff); + FillRectIMG(&red_img,beg2,pos,end2,pos+size1,col1,true); + + pos += next1; + SETCOLOR(col1,0xff,0x00,0x00,0xff); + SETCOLOR(col2,0x00,0x00,0x00,0xff); + FillHGradientIMG(&red_img,beg2,pos,end2,pos+size1,col1,col2); + + pos += next1; + SETCOLOR(col1,0xff,0x00,0x00,0xff); + SETCOLOR(col2,0xff,0x00,0x00,0x00); + FillHGradientIMG(&red_img,beg2,pos,end2,pos+size1,col1,col2); + + pos += next1; + SETCOLOR(col1,0x00,0x00,0x00,0xff); + SETCOLOR(col2,0xff,0xff,0xff,0xff); + FillHGradientIMG(&red_img,beg2,pos,end2,pos+size1,col1,col2); + + pos += next1; + SETCOLOR(col1,0x00,0x00,0x00,0xff); + SETCOLOR(col2,0x00,0x00,0x00,0x00); + FillHGradientIMG(&red_img,beg2,pos,end2,pos+size1,col1,col2); + + SETCOLOR(col1,0x80,0x00,0x00,0xff); + save_image(&red_img,argv[1],"red-temp.png",col1); + + + //--- mix image + + Image_t mix_img; + InitializeIMG(&mix_img); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_COPY); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"mode-copy-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_FOREGROUND); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"mode-foreground-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_BACKGROUND); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"mode-background-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_MIX); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"mode-mix-temp.png",0,true,0); + + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_COPY|PIM_INS_LEFT|PIM_GROW); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"insert-left-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_COPY|PIM_INS_RIGHT|PIM_GROW); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"insert-right-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_COPY|PIM_INS_TOP|PIM_GROW); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"insert-top-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_COPY|PIM_INS_BOTTOM|PIM_GROW); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"insert-bottom-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_COPY|PIM_INS_TOP|PIM_INS_RIGHT|PIM_GROW); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"insert-top_right-temp.png",0,true,0); + + PatchIMG(&mix_img,&blue_img,&red_img,PIM_COPY|PIM_INS_BOTTOM|PIM_INS_LEFT|PIM_GROW); + PatchIMG(&mix_img,&mix_img,&bg_img,PIM_BACKGROUND); + SavePNG(&mix_img,true,argv[1],"insert-bottom_left-temp.png",0,true,0); + + + //--- test image + + CreateTestImage(argv[1]); + + + //--- term + + ResetIMG(&bg_img); + ResetIMG(&blue_img); + ResetIMG(&red_img); + ResetIMG(&mix_img); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_resize() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int test_resize ( int argc, char ** argv ) +{ + if ( argc != 3 ) + { + fprintf(stderr,"\n" + "Usage: %s RESIZE old-size new-size\n\n" + ,NAME); + return ERR_SYNTAX; + } + + uint src_size = strtoul(argv[1],0,10); + uint dest_size = strtoul(argv[2],0,10); + + ResizeHelper_t r; + InitializeResize(&r,src_size,dest_size); + printf("\nRESIZE: %u -> %u, step: %u + %u/%u, sum_factor: %u\n\n", + r.src_size, r.dest_size, + r.idx_inc, r.frac_inc, r.frac_next, + r.sum_factor ); + + bool stat; + for ( stat = FirstResize(&r); stat; stat = NextResize(&r) ) + { + printf(" %5u -> %5u", r.src_idx, r.dest_idx ); + ResizeElement_t *re; + for ( re = r.elem; re->n_elem; re++ ) + printf(" | %4u*%4u", re->n_elem, re->factor ); + putchar('\n'); + } + putchar('\n'); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// resize_image() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int resize_image ( int argc, char ** argv ) +{ + if ( argc < 4 ) + { + fprintf(stderr,"\n" + "Usage: %s RESIZE-IMAGE new_width['%%']" + " ('w'|'a'|new_height['%%']) img-file...\n\n" + ,NAME); + return ERR_SYNTAX; + } + + u32 wd_param = 0, ht_param = 0; + char *end = ScanNumU32(argv[1],0,&wd_param,0,1000); + const bool wd_percent = end && *end == '%'; + + const bool aspect = argv[2][0] == 'a' || argv[2][0] == 'A'; + end = ScanNumU32(argv[2],0,&ht_param,0,1000); + bool ht_percent = end && *end == '%'; + if ( argv[2][0] == 'w' || argv[2][0] == 'W' ) + { + ht_param = wd_param; + ht_percent = wd_percent; + } + + uint i; + for ( i = 3; i < argc; i++ ) + { + Image_t img1; + enumError err = LoadIMG(&img1,true,argv[i],0,false,true,false); + if (err) + return ERR_WARNING; + + const uint wd = wd_percent ? ( img1.width * wd_param + 50 ) / 100 : wd_param; + const uint ht = aspect + ? img1.height * wd / img1.width + : ht_percent + ? ( img1.height * ht_param + 50 ) / 100 + : ht_param; + + printf("NEW SIZE: %u*%u -> %u*%u: %s\n", + img1.width, img1.height, wd, ht, argv[i] ); + + Image_t img2; + u64 time = GetTimeUSec(false); + err = SmartResizeIMG(&img2,true,&img1,wd,ht); + time = GetTimeUSec(false) - time; + printf("S2: %5llu usec [smart resize, new image]\n",time); + if (err) + return ERR_WARNING; + + Image_t img3; + time = GetTimeUSec(false); + err = FastResizeIMG(&img3,true,&img1,wd,ht); + time = GetTimeUSec(false) - time; + printf("F3: %5llu usec [fast resize, new image]\n",time); + if (err) + return ERR_WARNING; + + time = GetTimeUSec(false); + err = SmartResizeIMG(&img1,false,0,wd,ht); + time = GetTimeUSec(false) - time; + printf("S1: %5llu usec [smart resize, inplace image]\n",time); + if (err) + return ERR_WARNING; + + char path[PATH_MAX]; + NewFileExtS(path,sizeof(path),argv[i],"1smart.png"); + err = SavePNG(&img1,false,path,0,true,true,0); + if (err) + return ERR_WARNING; + + NewFileExtS(path,sizeof(path),argv[i],"2smart.png"); + err = SavePNG(&img2,false,path,0,true,true,0); + if (err) + return ERR_WARNING; + + NewFileExtS(path,sizeof(path),argv[i],"3fast.png"); + err = SavePNG(&img3,false,path,0,true,true,0); + if (err) + return ERR_WARNING; + + ResetIMG(&img1); + ResetIMG(&img2); + ResetIMG(&img3); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_scan_text() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int test_scan_text ( int argc, char ** argv ) +{ + uint i; + for ( i = 1; i < argc; i++ ) + { + ccp src = argv[i]; + char *dest = iobuf; + char *end = iobuf + sizeof(iobuf) - 1; + while ( *src && dest < end ) + { + const char ch = *src++; + *dest++ = ch == '|' ? '\n' : ch; + } + *dest = 0; + + printf("* %s\n",argv[i]); + ScanInfo_t si; + InitializeSI(&si,iobuf,dest-iobuf,argv[i],0); + #if HAVE_PRINT + si.debug = 1; + #endif + for(;;) + { + NextCharSI(&si,true); + ScanFile_t *sf = si.cur_file; + DASSERT(sf); + //PRINT(" >%s\n",sf->ptr); + + ccp beg = sf->ptr; + while (NextCharSI(&si,false)) + sf->ptr++; + if ( beg == sf->ptr ) + break; + printf(" #%u \"%.*s\"\n", + sf->line, (int)( sf->ptr - beg ), argv[i] + ( beg - sf->data ) ); + } + ResetSI(&si); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_scan_string() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int test_scan_string ( int argc, char ** argv ) +{ + uint i; + for ( i = 1; i < argc; i++ ) + { + uint len1; + char buf[1000]; + ScanEscapedString(buf,sizeof(buf),argv[i],-1,true,0,&len1); + + uint len2; + char out[1000]; + PrintEscapedString(out,sizeof(out),buf,-1,CHMD__ALL,0,&len2); + + printf("\nsrc: %3u |%s|\ndest: %3u |%s|\n",len1,argv[i],len2,out); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_scan_file() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int test_scan_file ( int argc, char ** argv ) +{ + static VarMap_t vm = {0}; + if (!vm.used) + DefineParserVars(&vm); + + uint i; + for ( i = 1; i < argc; i++ ) + { + ccp fname = argv[i]; + File_t F; + enumError err = OpenFILE(&F,true,fname,false,false); + if (err) + return err; + + uint data_size = F.st.st_size; + PRINT("LOAD FILE: size = %#x = %u\n",data_size,data_size); + char * data = MALLOC(data_size+1); + data[data_size] = 0; // termination for text files + + if ( fread(data,1,data_size,F.f) != data_size ) + { + err = FILEERROR1(&F,ERR_READ_FAILED,"Read failed: %s\n",fname); + ResetFile(&F,false); + return err; + } + ResetFile(&F,false); + + printf("* %s\n",argv[i]); + + ScanInfo_t si; + InitializeSI(&si,data,data_size,fname,0); + si.predef = &vm; + #if HAVE_PRINT + si.debug = 1; + #endif + + #if 1 + while (NextLineSI(&si,false,true)) + if (NextCharSI(&si,false)) + { + ccp ptr = si.cur_file->ptr; + ccp eol = FindNextLineFeedSI(&si,true); + int len = eol - ptr; + ccp post = ""; + if ( len > 50 ) + { + len = 50; + post = "..."; + } + ERROR0(ERR_WARNING,"Line ignored: %.*s%s\n",len,ptr,post); + GotoEolSI(&si); + } + #else + uint n; + for ( n = 0; n < 3; n++ ) + { + NextCharSI(&si,true); + u32 num; + enumError err = ScanU32SI(&si,&num,1,0); + printf("\t\t%2u: err=%3u, line=%3u, num=%8u = %6x\n", + n, err, si.cur_file->line, num, num ); + } + #endif + ResetSI(&si); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_filename() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int test_filename ( int argc, char ** argv ) +{ + uint i; + for ( i = 1; i < argc; i++ ) + { + ParamList_t param; + memset(¶m,0,sizeof(param)); + param.arg = STRDUP(argv[i]); + printf("\nsrc: %s\n",param.arg); + + char dest[PATH_MAX]; + uint stat = NumberedFilename(dest,sizeof(dest),param.arg,0,0,true); + printf("num: %s [%u]\n",dest,stat); + + SubstDest( dest, sizeof(dest), param.arg, + opt_dest && *opt_dest ? opt_dest : "\1P/\1N\1?T", "\1N\1?T", + GetExtFF(FF_BZ,FF_WU8), false ); + printf("dest: %s\n",dest); + + NORMALIZE_FILENAME_PARAM(¶m); + printf("norm: %s\n",param.arg); + SubstDest( dest, sizeof(dest), param.arg, + opt_dest && *opt_dest ? opt_dest : "\1P/\1N\1?T", "\1N\1?T", + GetExtFF(FF_BZ,FF_WU8), false ); + printf("dest: %s\n",dest); + + stat = NumberedFilename(dest,sizeof(dest),dest,0,0,true); + printf("num: %s [%u]\n",dest,stat); + + FREE(param.arg); + } + putchar('\n'); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// draw_startpos() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError save_img ( Image_t *img, char type, uint n ) +{ + DASSERT(img); + + char fname[100]; + snprintf(fname,sizeof(fname),"./_start-pos.tmp/start-pos-%c-%02u.png",type,n); + enumError err = SavePNG(img,false,fname,0,0,true,0); + if (!err) + printf("Image %s created.\n",fname); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError draw_startpos_helper ( uint n ) +{ + double3 tab_w[12], tab_n[12]; + + GetStartPosKMP(tab_w,n,false,false,0,0); + GetStartPosKMP(tab_n,n,false,true,0,0); + + int i, z1; + const int xmid = 1200; + const int zmid = 300; + const int zmax = 5600; + const double scale = 1/20.0; + + Image_t imgw, imgn, imgx; + Color_t col1, col2; + col1.val = 0xff600000; + + enumError err1 = CreateIMG(&imgw,true,2*xmid*scale,(zmid+zmax)*scale,col1); + enumError err2 = CreateIMG(&imgn,true,2*xmid*scale,(zmid+zmax)*scale,col1); + enumError err3 = CreateIMG(&imgx,true,2*xmid*scale,(zmid+zmax)*scale,col1); + if (err1||err2||err3) + goto abort; + + //--- draw vertical `500´ lines + + col1.val = 0xff804000; + z1 = zmid * scale; + + for ( i = -500; i <= 500; i += 1000 ) + { + uint x = ( xmid + i ) * scale; + FillRectIMG( &imgw, x-1, z1, x, imgw.height, col1, true ); + } + + + //--- draw horizontal `500´ lines + + for ( i = zmid+500; i < zmid+zmax; i += 1000 ) + { + uint z = i * scale; + FillRectIMG( &imgw, 0, z-1, imgw.width, z, col1, true ); + } + + + //--- draw vertical `1000´ lines + + col1.val = 0xff808000; + z1 = zmid * scale; + + for ( i = -1000; i <= 1000; i += 1000 ) + { + uint x = ( xmid + i ) * scale; + FillRectIMG( &imgw, x-1, z1, x+1, imgw.height, col1, true ); + } + + + //--- draw horizontal `1000´ lines + + for ( i = zmid; i < zmid+zmax; i += 1000 ) + { + uint z = i * scale; + FillRectIMG( &imgw, 0, z-1, imgw.width, z+1, col1, true ); + } + + + //--- draw finisch line + + col1.val = 0xff0000ff; + FillRectIMG( &imgw, 0, + (zmid-25)*scale, + imgw.width, + (zmid+25)*scale, + col1, true ); + + + //--- copy the images + + CopyIMG(&imgn,false,&imgw,false); + CopyIMG(&imgx,false,&imgw,false); + + + //--- draw start positions + + col1.val = 0xff00ffff; + col2.val = 0xff0080ff; + + for ( i = 0; i < n; i++ ) + { + + //--- draw narrow + + const double3 *d = tab_n + i; + FillRectIMG( &imgn, + (xmid+d->x-100)*scale, + (zmid+d->z-180)*scale, + (xmid+d->x+100)*scale, + (zmid+d->z+180)*scale, + col1, true ); + + FillRectIMG( &imgx, + (xmid+d->x-100)*scale, + (zmid+d->z-180)*scale, + (xmid+d->x+100)*scale, + (zmid+d->z+180)*scale, + col2, true ); + + //--- draw wide + + d = tab_w + i; + FillRectIMG( &imgw, + (xmid+d->x-100)*scale, + (zmid+d->z-180)*scale, + (xmid+d->x+100)*scale, + (zmid+d->z+180)*scale, + col1, true ); + + FillRectIMG( &imgx, + (xmid+d->x-100)*scale, + (zmid+d->z-180)*scale, + (xmid+d->x+100)*scale, + (zmid+d->z+180)*scale, + col1, true ); + } + + + //--- draw defined start position + + col1.val = 0xff0000ff; + + FillRectIMG( &imgw, (xmid-80)*scale, + (zmid-80)*scale, + (xmid+80)*scale, + (zmid+80)*scale, + col1, true ); + + FillRectIMG( &imgn, (xmid-80)*scale, + (zmid-80)*scale, + (xmid+80)*scale, + (zmid+80)*scale, + col1, true ); + + FillRectIMG( &imgx, (xmid-80)*scale, + (zmid-80)*scale, + (xmid+80)*scale, + (zmid+80)*scale, + col1, true ); + + //--- save the images + + err1 = save_img(&imgw,'w',n); + err2 = save_img(&imgn,'n',n); + err3 = save_img(&imgx,'x',n); + + MARK_USED(col2); + + abort: + ResetIMG(&imgw); + ResetIMG(&imgn); + ResetIMG(&imgx); + return err1 || err2 || err3 ? ERR_ERROR : ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int draw_startpos ( int argc, char ** argv ) +{ + opt_mkdir = true; + + uint n; + for ( n = 1; n<= 12; n++ ) + draw_startpos_helper(n); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// draw_startpos() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static uint sort_mode = 0; + +//----------------------------------------------------------------------------- + +static int sort_path_cmp ( ccp *p1, ccp *p2 ) +{ + return PathCmp(*p1,*p2,sort_mode); +} + +//----------------------------------------------------------------------------- + +static int test_path_cmp ( int argc, char ** argv ) +{ + if ( argc < 3 ) + { + fprintf(stderr,"Usage: PATH-CMP mode string1 string2 [stringx]...\n"); + return ERR_SYNTAX; + } + + sort_mode = strtoul(argv[1],0,0) & 7; + argc -= 2; + argv += 2; + + printf("* Sort %u strings with mode %u\n",argc,sort_mode); + qsort( argv, argc, sizeof(*argv), (qsort_func)sort_path_cmp ); + + uint i; + for ( i = 0; i < argc; i++ ) + printf("%5u. %s\n",i,argv[i]); + return 0; +} +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ana_file_objflow() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp print16 ( u16 val ) +{ + if (!val) + return "-"; + + static char buf[10]; + snprintf(buf,sizeof(buf),"%x",val); + return buf; +} + +//----------------------------------------------------------------------------- + +static int ana_file_objflow ( int argc, char ** argv ) +{ + char buf[20]; + + uint i; + for ( i = 1; i < argc; i++ ) + { + u8 *raw_data; + uint data_size; + enumError err = OpenReadFILE(argv[i],0,false,&raw_data,&data_size,0,0); + if (err) + return err; + printf("* %s\n",argv[i]); + objflow_bin_t *odata = (objflow_bin_t*)(raw_data+2); + + #undef PLEN + #define PLEN ( sizeof(odata->param) / sizeof(*odata->param) ) + uint param_fw[PLEN]; + memset(param_fw,0,sizeof(param_fw)); + + uint max_name_size = 4, max_resources_size = 4; + const uint N = be16(raw_data); + objflow_bin_t *ptr, *end = odata + N; + for ( ptr = odata; ptr < end && ptr->id != 0xffff; ptr++ ) + { + uint len = strlen(ptr->name); + if ( max_name_size < len ) + max_name_size = len; + len = strlen(ptr->resources); + if ( max_resources_size < len ) + max_resources_size = len; + + ptr->id = be16(&ptr->id); + uint p; + for ( p = 0; p < PLEN; p++ ) + { + u32 val = be16(ptr->param+p); + ptr->param[p] = val; + if ( param_fw[p] < val ) + param_fw[p] = val; // store max value first + } + } + if ( max_name_size > sizeof(odata->name) - 1 ) + max_name_size = sizeof(odata->name) - 1; + if ( max_resources_size > sizeof(odata->resources) - 1 ) + max_resources_size = sizeof(odata->resources) - 1; + + uint linelen = printf(" Pos ID %-*s", max_name_size, "Name" ) + + max_resources_size + 3; + uint p; + for ( p = 0; p < PLEN; p++ ) + if (param_fw[p]) + { + uint fw = snprintf(buf,sizeof(buf),"%3x",param_fw[p]); + param_fw[p] = fw > 3 ? fw : 3; + linelen += printf("%*s%u",param_fw[p],"P",p); + } + printf(" Resources\n%.*s\n", linelen, Minus300); + + for ( ptr = odata; ptr < end && ptr->id != 0xffff; ptr++ ) + { + printf(" %3zu %03x %-*s", ptr-odata, ptr->id, max_name_size, ptr->name ); + for ( p = 0; p < PLEN; p++ ) + if (param_fw[p]) + printf(" %*s",param_fw[p],print16(ptr->param[p])); + printf(" %s\n",ptr->resources); + } + + #undef PLEN + + FREE(raw_data); + } + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_line() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError scan_double + ( double * pt, int n_dim, ScanInfo_t *si, double def_x, double def_y ) +{ + DASSERT(pt); + DASSERT(si); + DASSERT( n_dim == 2 || n_dim == 3 ); + + // set defaults + pt[0] = def_x; + pt[1] = def_y; + if ( n_dim == 3 ) + pt[2] = 0.0; + + char ch = NextCharSI(si,false); + if ( ch == ',' ) + { + si->cur_file->ptr++; + ch = NextCharSI(si,false); + } + + if ( ch == '.' ) + { + si->cur_file->ptr++; + return ERR_OK; + } + + uint dim; + for ( dim=0; dim < n_dim; dim++ ) + { + double d; + enumError err = ScanDValueSI(si,&d); + if (err) + return err; + + pt[dim] = d; + char ch = NextCharSI(si,false); + if ( !ch || ch == ',' ) + break; + } + + return ERR_OK; +} + +//----------------------------------------------------------------------------- + +static enumError scan_float + ( float * pt, int n_dim, ScanInfo_t *si, double def_x, double def_y ) +{ + DASSERT(pt); + DASSERT(si); + + DASSERT( n_dim <= 10 ); + double temp[10]; + enumError err = scan_double(temp,n_dim,si,def_x,def_y); + uint i; + for ( i = 0; i < n_dim; i++ ) + pt[i] = temp[i]; + + return err; +} + +//----------------------------------------------------------------------------- + +static void print_double_vec ( double *pt, int n_dim, ccp title ) +{ + if ( n_dim == 3 ) + printf("%s%9.3f %9.3f %9.3f\n",title,pt[0],pt[1],pt[2]); + else + printf("%s%9.3f %9.3f\n",title,pt[0],pt[1]); +} + +//----------------------------------------------------------------------------- + +static void print_float_vec ( float *pt, int n_dim, ccp title ) +{ + if ( n_dim == 3 ) + printf("%s%9.3f %9.3f %9.3f\n",title,pt[0],pt[1],pt[2]); + else + printf("%s%9.3f %9.3f\n",title,pt[0],pt[1]); +} + +//----------------------------------------------------------------------------- + +static int test_line ( int n_dim, int argc, char ** argv ) +{ + n_dim = n_dim == 3 ? 3 : 2; + if ( argc < 2 ) + { + fprintf(stderr, + "\n" + "Usage: %s 'LINE2' N [,P1] [,P2] [,Pa] [,Pb]\n" + "\n" + " 'Px' is a point with %u dimensions, missing dimensions are filled with 0.\n" + " A point means: default value (P1=0, P2=1000, P3=P4=500 500)\n" + "\n" + " => Create a line with N points from P1..P2.\n" + " Pa and Pb are helpers for Bézier curves.\n" + "\n" + " Linear: B(t) = (1-t)*P1 + t*P2\n" + " Quadratic: B(t) = (1-t)^2*P1 + 2*(1-t)*t*Pa + t^2*P2\n" + " Cubic: B(t) = (1-t)^3*P1 + 3*(1-t)^2*t*Pa\n" + " + 3*(1-t)*t^2*Pb + t^3*P2\n" + " where t=0.0 .. 1.0\n" + "\n" + ,NAME, n_dim ); + return ERR_SYNTAX; + } + + uint i; + for ( i = 1; i < argc; i++ ) + { + ScanInfo_t si; + InitializeSI(&si,argv[i],strlen(argv[i]),"arg",0); + + Line_t lin; + InitializeLine(&lin); + + long n; + enumError err = ScanUValueSI(&si,&n,false); + if (err) + return err; + lin.n = n; + + float p1[3] = { 0.0, 0.0, 0.0 }; + if (NextCharSI(&si,false)) + { + err = scan_float(p1,n_dim,&si,0.0,0.0); + if (err) + return err; + } + + float p2[3] = { 1000.0, 0.0, 0.0 }; + if (NextCharSI(&si,false)) + { + err = scan_float(p2,n_dim,&si,1000.0,0.0); + if (err) + return err; + } + + while ( lin.n_pt < 2 && NextCharSI(&si,false) ) + { + err = scan_double(lin.pt[lin.n_pt],n_dim,&si,500.0,500.0); + if (err) + return err; + lin.n_pt++; + } + + printf("\n-----------------------------------------------\n"); + print_float_vec(p1,n_dim," P1: "); + if ( lin.n_pt >= 1 ) + print_double_vec(lin.pt[0],n_dim," Pa: "); + if ( lin.n_pt >= 2 ) + print_double_vec(lin.pt[1],n_dim," Pb: "); + print_float_vec(p2,n_dim," P2: "); + printf("-----------------------------------------------\n"); + uint p; + for ( p = 0; p <= lin.n; p++ ) + { + float pt[3]; + CalcLine(&lin,pt,p1,p2,n_dim,p); + printf(" -> %2u: ",p); + print_float_vec(pt,n_dim,""); + } + printf("-----------------------------------------------\n"); + } + return ERR_OK; +} + +#if 0 + enumError err = n_dim == 3 + ? ScanLine3(&lin,&si,true) + : ScanLine2(&lin,&si,true); + if (err) + continue; +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int test ( int argc, char ** argv ) +{ + printf("sizeof: Color_t=%zu\n",sizeof(Color_t)); + Color_t c; + c.r = 0x01; + c.g = 0x02; + c.b = 0x03; + c.a = 0x04; + printf("col=%x\n",c.val); + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_memfile() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_memfile ( int argc, char ** argv ) +{ + MemFile_t mf; + InitializeMemFile(&mf,1000,0x100000); + mf.err_name = "TEST"; + DumpMemFile(stdout,2,&mf); + + WriteMemFile(&mf,"Hallo Dirk\n",11); + WriteMemFile(&mf,"Hallo Dirk\n",11); + WriteMemFileAt(&mf,4,"xxx",2); + WriteMemFile(&mf,"yyy",2); + DumpMemFile(stdout,2,&mf); + SaveMemFile(&mf,0,"memfile1.tmp",FM_OVERWRITE,false); + + WriteMemFileAt(&mf,20,"|Hallo Dirk\n",12); + DumpMemFile(stdout,2,&mf); + SaveMemFile(&mf,0,"memfile2.tmp",FM_OVERWRITE,false); + ResetMemFile(&mf); + + LoadMemFile(&mf,false,0,"memfile1.tmp",1,0,false); + SaveMemFile(&mf,0,"memfile3.tmp",FM_OVERWRITE,false); + + WriteMemFileAt(&mf,0x10080,"a",1); + WriteMemFileAt(&mf,0x20080,"",0); + SaveMemFile(&mf,0,"sparse.tmp",FM_OVERWRITE,true); + + for(;;) + { + char buf[5]; + uint size = ReadMemFile(&mf,buf,sizeof(buf)); + if (!size) + break; + fwrite(buf,1,size,stdout); + } + ResetMemFile(&mf); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_find_dol() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_find_dol ( int argc, char ** argv ) +{ + if ( argc < 3 ) + { + fprintf(stderr,"params: main.dol size...\n"); + return ERR_SYNTAX; + } + + dol_header_t dh; + enumError err = LoadFILE(argv[1],0,0,&dh,sizeof(dh),1,false,false); + if (err) + return err; + + dh.bss_size = htonl(0x2000000); + DumpDolHeader(stdout,3,&dh,0,2); + + uint i; + for ( i = 2; i < argc; i++ ) + { + const uint size = str2ul(argv[i],0,10); + u32 space, addr = FindFreeBssSpaceDOL(&dh,size,32,&space); + printf("%s %8x => %8x + %8x\n",argv[1],size,addr,space); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_subfile() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static int SubFileIt +( + SubFileIterator_t *sfi, // iterator data + uint mode, // 0:file, 1:open dir, 2:close dir + SubDir_t *dir, // current directory + SubFile_t *file // NULL or current file +) +{ + printf("%*s%u %s\n",sfi->depth,"",mode,sfi->path); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_subfile ( int argc, char ** argv ) +{ + SubDir_t dir; + InitializeSubDir(&dir); + + uint i; + for ( i = 1; i < argc; i++ ) + { + ccp arg = argv[i]; + bool new; + SubFile_t *sf = InsertSubFile(&dir,MemByString(arg),&new); + printf("%6s: %s\n",!sf?"FAIL":new?"INSERT":"FOUND",arg); + } + + putchar('\n'); + DumpSubDir(stdout,2,&dir); + + SubFileIterator_t it; + memset(&it,0,sizeof(it)); + it.func = SubFileIt; + IterateSubDir(&dir,&it); + + SaveSubFiles(&dir,"_subfile.tmp"); + + for ( i = 1; i < argc; i++ ) + { + ccp arg = argv[i]; + SubFile_t *sf = FindSubFile(&dir,MemByString(arg)); + if (sf) + printf("FOUND: %s -> %s\n",arg,sf->fname); + else + printf("NOT FOUND: %s\n",arg); + } + + ResetSubDir(&dir); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_str_list() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_str_list ( int argc, char ** argv ) +{ + if ( argc < 2 ) + { + printf( "STR-LIST | STR [option|string]...\n" + " -a : sort by ascii -> strcmp() [DEFAULT]\n" + " -i : sort 'ignore case'-> strcasecmp()\n" + " -n : sort numeric -> StrNumCmp()\n" + ); + return ERR_OK; + } + + StringField_t sf; + InitializeStringField(&sf); + + uint i; + for ( i = 1; i < argc; i++ ) + { + ccp arg = argv[i]; + if (!strcmp(arg,"-a")) + { + WriteStringField(stdout,"stdout",&sf,"\t",0); + fputs("-----\n",stdout); + sf.func_cmp = strcmp; + SortStringField(&sf); + } + else if (!strcmp(arg,"-i")) + { + WriteStringField(stdout,"stdout",&sf,"\t",0); + fputs("-----\n",stdout); + sf.func_cmp = strcasecmp; + SortStringField(&sf); + } + else if (!strcmp(arg,"-n")) + { + WriteStringField(stdout,"stdout",&sf,"\t",0); + fputs("-----\n",stdout); + sf.func_cmp = StrNumCmp; + SortStringField(&sf); + } + else + InsertStringField(&sf,arg,false); + } + + WriteStringField(stdout,"stdout",&sf,"\t",0); + ResetStringField(&sf); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_container() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void test_container_data ( int mode, ContainerData_t *cdata, uint modify ) +{ + Container_t c; + CatchContainerData(&c,true,cdata); + if ( mode >= 0 ) + DumpInfoContainer(stdout,colout,2,"FUNC: ",&c, mode > 0 ? 32 : 0 ); + + if ( modify == 1 ) + { + ModifyAllContainer(&c); + if ( c.cdata && c.cdata->data ) + StringCopyS((char*)c.cdata->data,c.cdata->size,"MODIFIED!"); + DumpInfoContainer(stdout,colout,2,"-MOD: ",&c, mode > 0 ? 32 : 0 ); + } + else if ( modify == 2 && c.cdata && c.cdata->size > 20 ) + { + char *data = (char*)c.cdata->data + 2; + if (ModifyContainer(&c,data,12,CPM_COPY)) + data = (char*)c.cdata->data; + StringCopyE(data,(ccp)c.cdata->data+c.cdata->size,"EXTRACT!"); + DumpInfoContainer(stdout,colout,2,"-EXT: ",&c, mode > 0 ? 32 : 0 ); + } + ResetContainer(&c); +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_container ( int argc, char ** argv ) +{ + //----------------------------------------------------------------------------- + // to test + //----------------------------------------------------------------------------- + // + Container_t * CreateContainer ( Container_t, protect, data,size, CopyMode_t ) + // - Container_t * InheritContainer ( Container_t cur, protect, Container_t parent, data,size ) + // + bool AssignContainer ( Container_t, protect, data,size, CopyMode_t ) + // + void ResetContainer ( Container_t ) + // / void DeleteContainer ( Container_t ) + // + void UnlinkContainerData ( Container_t ) + // + ContainerData_t * LinkContainerData ( Container_t ) + // + ContainerData_t * MoveContainerData ( Container_t ) + // + Container_t * CatchContainerData ( Container_t, int protect, ContainerData_t ) + //------------ + // - ContainerData_t DisjoinContainerData ( Container_t ) + // - void JoinContainerData ( Container_t, ContainerData_t ) + // + void FreeContainerData ( ContainerData_t ) + //------------ + // + static inline bool ModificationAllowed ( const Container_t ) + // + void ModifyAllContainer ( Container_t ) + // + void ModifyContainer ( Container_t, data, size, CopyMode_t ) + // + int SetProtectContainer ( Container_t, int new_protect ) + // + static inline int AddProtectContainer ( Container_t, int add_protect ) + // - static inline bool InContainerP ( const Container_t, cvp ptr ) + // + static inline bool InContainerS ( const Container_t, cvp ptr, uint size ) + // - static inline bool InContainerE ( const Container_t, cvp ptr, cvp end ) + // + uint DumpInfoContainer ( f,col,indent,prefix, Container_t, hexdump_len ) + // + uint DumpInfoContainerData ( f,col,indent,prefix, CData_t, hex_len, hex_indent ) + //----------------------------------------------------------------------------- + + if ( argc < 2 ) + { + printf( "Syntax: CONTAINTER [['-'|'+']command]...\n" + "\n" + "Commands:\n" + "\n" + " dump : do nothing, dump only.\n" + " dump2 : do nothing, dump only second container.\n" + " unlink2 : unlink second container.\n" + "\n" + " inherit : inherit base data.\n" + " assign : re-assign base data.\n" + " free : free linked data.\n" + " catch : catch by second container.\n" + " reset : reset second container.\n" + "\n" + " p0..p2 : set protection level\n" + " p- p+ : decrement or increment protection level\n" + "\n" + " null : call function & use NULL\n" + " link : call function & use LinkContainerData()\n" + " link+ : call function & use LinkContainerData() & modify\n" + " link++ : call function & use LinkContainerData() & extract\n" + " move : call function & use MoveContainerData()\n" + " move+ : call function & use MoveContainerData() & modify\n" + " move++ : call function & use MoveContainerData() & extract\n" + "\n" + "The base container is automatically recreated if empty.\n" + "\n" + "Command prefix:\n" + " - : silent = Don't dump.\n" + " + : verbose = include hex dump.\n" + "\n" + ); + return 0; + } + + char buf[1000]; + memset(buf,0,sizeof(buf)); + strcpy(buf,"0123456789 init"); + Container_t c, cc; + CreateContainer(&c,true,buf,sizeof(buf),CPM_LINK); + CreateContainer(&cc,true,0,0,0); + DumpInfoContainer(stdout,colout,2,"BASE: ",&c,32); + + uint i; + for ( i = 1; i < argc; i++ ) + { + ccp arg = argv[i]; + if ( !arg || !*arg ) + continue; + int mode = 0; + if ( *arg == '-' ) + mode--, arg++; + else if ( *arg == '+' ) + mode++, arg++; + + if ( mode >= 0 ) + printf("-------------------- %d. |%s| --------------------\n",i,arg); + + if (!strcasecmp(arg,"p0")) + SetProtectContainer(&c,0); + else if (!strcasecmp(arg,"p1")) + SetProtectContainer(&c,1); + else if (!strcasecmp(arg,"p2")) + SetProtectContainer(&c,2); + else if (!strcasecmp(arg,"p-")) + AddProtectContainer(&c,-1); + else if (!strcasecmp(arg,"p+")) + AddProtectContainer(&c,1); + + else if (!strcasecmp(arg,"null")) + test_container_data(mode,0,0); + + else if (!strcasecmp(arg,"link")) + test_container_data(mode,LinkContainerData(&c),0); + else if (!strcasecmp(arg,"link+")) + test_container_data(mode,LinkContainerData(&c),1); + else if (!strcasecmp(arg,"link++")) + test_container_data(mode,LinkContainerData(&c),2); + + else if (!strcasecmp(arg,"move")) + test_container_data(mode,MoveContainerData(&c),0); + else if (!strcasecmp(arg,"move+")) + test_container_data(mode,MoveContainerData(&c),1); + else if (!strcasecmp(arg,"move++")) + test_container_data(mode,MoveContainerData(&c),2); + + else if (!strcasecmp(arg,"unlink2")) + { + UnlinkContainerData(&cc); + } + else if (!strcasecmp(arg,"inherit")) + { + InheritContainer(&cc,2,&c,buf+5,3); + if ( mode >= 0 ) + DumpInfoContainer(stdout,colout,2,"C2: ",&cc, mode > 0 ? 32 : 0 ); + } + else if (!strcasecmp(arg,"assign")) + { + memset(buf,0,sizeof(buf)); + strcpy(buf,"0123456789 init"); + AssignContainer(&c,true,buf,sizeof(buf),CPM_LINK); + } + else if (!strcasecmp(arg,"free")) + { + FreeContainerData(LinkContainerData(&c)); + } + else if (!strcasecmp(arg,"catch")) + { + ResetContainer(&cc); + CatchContainerData(&cc,true,LinkContainerData(&c)); + if ( mode >= 0 ) + DumpInfoContainer(stdout,colout,2,"C2: ",&cc, mode > 0 ? 32 : 0 ); + } + else if (!strcasecmp(arg,"reset")) + { + ResetContainer(&cc); + if ( mode >= 0 ) + DumpInfoContainer(stdout,colout,2,"C2: ",&cc, mode > 0 ? 32 : 0 ); + } + else if (strcasecmp(arg,"dump")) + { + printf(" >>> IGNORED!\n"); + continue; + } + else if (strcasecmp(arg,"dump2")) + { + DumpInfoContainer(stdout,colout,2,"C2: ",&cc, mode > 0 ? 32 : 0 ); + continue; + } + + if ( mode >= 0 ) + DumpInfoContainer(stdout,colout,2,"BACK: ",&c, mode > 0 ? 32 : 0 ); + + if (!c.cdata) + { + memset(buf,0,sizeof(buf)); + strcpy(buf,"0123456789 init"); + AssignContainer(&c,true,buf,sizeof(buf),CPM_LINK); + } + } + + ResetContainer(&c); + ResetContainer(&cc); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_uuid() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_uuid ( int argc, char ** argv ) +{ + uint i; + char buf[100]; + + printf("\n--- test ReadFromUrandom()\n"); + for ( i = 0; i < 5; i++ ) + { + uint stat = ReadFromUrandom(buf,15); + HexDump16(stdout,4,0,buf,stat); + } + + printf("\n--- test CreateUUID() & MyRandom(1000000)\n"); + for ( i = 0; i < 5; i++ ) + { + uint stat = CreateTextUUID(buf,sizeof(buf)); + printf(" %u: %s %6u [avail=%d,use=%d]\n", + stat, buf, MyRandom(1000000), + urandom_available, use_urandom_for_myrandom ); + } + + printf("\n--- test ScanUUID()\n"); + for ( i = 1; i < argc; i++ ) + { + uuid_buf_t uuid; + ccp stat = ScanUUID(uuid,argv[i]); + PrintUUID(buf,sizeof(buf),uuid); + printf("%s %s<= %s\n", buf, stat==argv[i] ? "FAILED! " : "", argv[i] ); + } + + putchar('\n'); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_analyse_magic() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_analyse_magic ( int argc, char ** argv ) +{ + uint i; + u8 magic[8]; + + printf("\n--- test AnalyseMagic() + RepairMagic()\n"); + for ( i = 1; i < argc; i++ ) + { + ccp fname = argv[i]; + printf("\n%s\n", fname ); + + u8 *data; + uint data_size; + enumError err = OpenReadFILE(fname,0,false,&data,&data_size,0,0); + if ( err || data_size < sizeof(magic) ) + return err; + + memcpy(magic,data,sizeof(magic)); + + repair_ff_t rep; + AnalyseMagic(&rep,data,data_size,data_size,FF_UNKNOWN,fname,false); + printf("\t\e[35;1m=> Ana0: %s\e[0m\n",GetRepairMagicInfo(&rep)); + RepairMagic(&rep,data,data_size,data_size,FF_UNKNOWN,fname,false); + printf("\t\e[35;1m=> Rep0: %s\e[0m\n",GetRepairMagicInfo(&rep)); + + memcpy(data,magic,sizeof(magic)); + AnalyseMagic(&rep,data,data_size,data_size,FF_UNKNOWN,fname,true); + printf("\t\e[35;1m=> Ana1: %s\e[0m\n",GetRepairMagicInfo(&rep)); + RepairMagic(&rep,data,data_size,data_size,FF_UNKNOWN,fname,true); + printf("\t\e[35;1m=> Rep1: %s\e[0m\n",GetRepairMagicInfo(&rep)); + + if (rep.magic_len) + { + fputs("\t\e[35;1m=> magic0:",stdout); + uint i; + for ( i = 0; i < rep.magic_len; i++ ) + printf(" %02x",magic[i]); + fputs("\e[0m\n\t\e[35;1m=> magic1:",stdout); + for ( i = 0; i < rep.magic_len; i++ ) + printf(" %02x",data[i]); + fputs("\e[0m\n",stdout); + } + FREE(data); + } + + putchar('\n'); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_vr() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +// get VR diff by b-spline curve (by chadders, outdated (not exact)) +// https://www.desmos.com/calculator/gr17y7d944 + +static double GetVrDiffBSpline ( double d ) +{ + //------------------------------------------- + // x = loser - winner + // + // f(x): + // (0..1( : 1/6 * (3x^3 - 6x^2 + 4 ) + // (1..2( : 1/6 * (2-x)^3 + // else : 0 + // + //------------------------------------------- + + if ( d >= 0.0 && d < 1.0 ) + return ( 3*d*d*d - 6*d*d + 4 )/6.0; + + if ( d >= 1.0 && d < 2.0 ) + { + d = 2 - d; + return d*d*d/6.0; + } + + return 0; +} + + +double GetVrDiffD ( int diff ) +{ + // diff := Pwinner - Plooser + + + //-- cut differences + + if ( diff > 9998 ) + diff = 9998; + else if ( diff < -9998 ) + diff = -9998; + + + //-- calculate rating as sum of 10 knots + + double sum = 0; + int j; + for ( j = 0; j < 10; j++ ) + { + static int knot[] = {0,0,0,1,8,50,125,125,125,125}; + const double temp = fabs( (2-diff)/5000.0 - ( j - 4 ) ); + sum += GetVrDiffBSpline(temp) * knot[j]; + } + + return sum; +} + +static inline int GetVrDiffI ( int diff ) + { return floor(GetVrDiffD(diff)); } + +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_vr ( int argc, char ** argv ) +{ + const struct pair { int delta; int vr_add; } known[] = + { + {-20000, 112 }, + { -9998, 112 }, + { -9980, 112 }, + { -9890, 111 }, + { -9190, 105 }, + { -8000, 92 }, + { -6000, 67 }, + { -4000, 44 }, + { -2000, 26 }, + { -1000, 19 }, + { 0, 13 }, + { 2000, 6 }, + { 4000, 2 }, + { 6000, 1 }, + { 8000, 0 }, + { 9980, 0 }, + { 9998, 0 }, + { 20000, 0 }, + {-1,-1} + }; + + int i; + for ( i = 1; i < argc; i++ ) + { + bool verbose = !strcmp(argv[i],"test+"); + if ( verbose || !strcmp(argv[i],"test") ) + { + fputs("\n" + " delta : result : tab : good : status\n" + "-------------------------------------------\n",stdout); + int count = 0, total = 0; + const struct pair *ptr; + for ( ptr = known; ptr->vr_add != -1; ptr++ ) + { + total++; + double r = GetVrDiffD(ptr->delta); + int i = floor(r); + int j = GetVrDiffByTab(ptr->delta); + bool differ = i != ptr->vr_add || i != j; + count += differ; + if ( differ || verbose ) + printf("%8d : %8.4f : %4d : %4d %s\n", + ptr->delta, r, j, ptr->vr_add, + differ ? " : DIFFER" : "" ); + } + printf("-------------------------------------------\n" + " >>> %u of %u differ,%s\n", + count, total, + verbose ? "" : " [use test+ to show all]" ); + continue; + } + + if (!strcmp(argv[i],"table3")) + { + fputs("\n" + "\t// winner-loser add\n" + "\t// from, to, vr // N\n" + "\t//------------------------------\n",stdout); + + int diff = -10000, max_diff = 10000, n_record = 0; + + for ( ; diff < max_diff; diff++ ) + { + int start = diff, val = GetVrDiffI(diff); + while ( diff < max_diff && GetVrDiffI(diff) == val ) + diff++; + printf("\t{ %6d, %5d, %3d }, // %4u\n", + start, diff, val, diff-start+1 ); + n_record++; + } + printf("\t// %u records total\n",n_record); + continue; + } + + if ( !strcmp(argv[i],"table2") || !strcmp(argv[i],"table") ) + { + fputs("\n" + "\t// delta, +vr // N\n" + "\t//------------------------------\n",stdout); + + int diff = -10000, max_diff = 10000, n_record = 0; + + for ( ; diff < max_diff; diff++ ) + { + int start = diff, val = GetVrDiffI(diff); + while ( diff < max_diff && GetVrDiffI(diff) == val ) + diff++; + printf("\t{ %6d, %3d }, // %4u\n", + start, val, diff-start+1 ); + n_record++; + } + fputs("\t{ 10000, 0 } // upper border\n",stdout); + printf("\t// %u+1 records total\n",n_record); + continue; + } + + int d = str2l(argv[i],0,10); + double r = GetVrDiffD(d); + printf("%8d -> %10.4f\n",d,r); + } + putchar('\n'); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_vr2() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct vr1_diff_t +{ + s16 diff; + s16 add_vr; +} +vr1_diff_t; + +//----------------------------------------------------------------------------- + +static const vr1_diff_t vr1_diff_tab[] = +{ + // delta, +vr // N + //------------------------------ + { -10000, 112 }, // 68 + { -9932, 111 }, // 127 + { -9805, 110 }, // 122 + { -9683, 109 }, // 116 + { -9567, 108 }, // 113 + { -9454, 107 }, // 110 + { -9344, 106 }, // 106 + { -9238, 105 }, // 104 + { -9134, 104 }, // 101 + { -9033, 103 }, // 99 + { -8934, 102 }, // 97 + { -8837, 101 }, // 95 + { -8742, 100 }, // 94 + { -8648, 99 }, // 92 + { -8556, 98 }, // 91 + { -8465, 97 }, // 89 + { -8376, 96 }, // 89 + { -8287, 95 }, // 87 + { -8200, 94 }, // 87 + { -8113, 93 }, // 85 + { -8028, 92 }, // 85 + { -7943, 91 }, // 84 + { -7859, 90 }, // 84 + { -7775, 89 }, // 83 + { -7692, 88 }, // 82 + { -7610, 87 }, // 81 + { -7529, 86 }, // 82 + { -7447, 85 }, // 81 + { -7366, 84 }, // 80 + { -7286, 83 }, // 80 + { -7206, 82 }, // 80 + { -7126, 81 }, // 79 + { -7047, 80 }, // 80 + { -6967, 79 }, // 79 + { -6888, 78 }, // 79 + { -6809, 77 }, // 79 + { -6730, 76 }, // 78 + { -6652, 75 }, // 79 + { -6573, 74 }, // 79 + { -6494, 73 }, // 78 + { -6416, 72 }, // 79 + { -6337, 71 }, // 79 + { -6258, 70 }, // 79 + { -6179, 69 }, // 79 + { -6100, 68 }, // 79 + { -6021, 67 }, // 80 + { -5941, 66 }, // 79 + { -5862, 65 }, // 80 + { -5782, 64 }, // 81 + { -5701, 63 }, // 80 + { -5621, 62 }, // 82 + { -5539, 61 }, // 81 + { -5458, 60 }, // 82 + { -5376, 59 }, // 83 + { -5293, 58 }, // 83 + { -5210, 57 }, // 84 + { -5126, 56 }, // 85 + { -5041, 55 }, // 85 + { -4956, 54 }, // 87 + { -4869, 53 }, // 87 + { -4782, 52 }, // 88 + { -4694, 51 }, // 89 + { -4605, 50 }, // 90 + { -4515, 49 }, // 91 + { -4424, 48 }, // 91 + { -4333, 47 }, // 93 + { -4240, 46 }, // 94 + { -4146, 45 }, // 96 + { -4050, 44 }, // 96 + { -3954, 43 }, // 98 + { -3856, 42 }, // 98 + { -3758, 41 }, // 101 + { -3657, 40 }, // 101 + { -3556, 39 }, // 103 + { -3453, 38 }, // 105 + { -3348, 37 }, // 106 + { -3242, 36 }, // 107 + { -3135, 35 }, // 110 + { -3025, 34 }, // 111 + { -2914, 33 }, // 113 + { -2801, 32 }, // 115 + { -2686, 31 }, // 118 + { -2568, 30 }, // 119 + { -2449, 29 }, // 122 + { -2327, 28 }, // 125 + { -2202, 27 }, // 127 + { -2075, 26 }, // 130 + { -1945, 25 }, // 133 + { -1812, 24 }, // 137 + { -1675, 23 }, // 140 + { -1535, 22 }, // 144 + { -1391, 21 }, // 149 + { -1242, 20 }, // 153 + { -1089, 19 }, // 159 + { -930, 18 }, // 164 + { -766, 17 }, // 171 + { -595, 16 }, // 178 + { -417, 15 }, // 187 + { -230, 14 }, // 197 + { -33, 13 }, // 208 + { 175, 12 }, // 221 + { 396, 11 }, // 238 + { 634, 10 }, // 256 + { 890, 9 }, // 279 + { 1169, 8 }, // 308 + { 1477, 7 }, // 343 + { 1820, 6 }, // 393 + { 2213, 5 }, // 459 + { 2672, 4 }, // 562 + { 3234, 3 }, // 730 + { 3964, 2 }, // 1036 + { 5000, 1 }, // 1599 + { 6599, 0 }, // 3401 + { 10000, 0 } // upper border + // 113+1 records total +}; + +//----------------------------------------------------------------------------- + +int GetVr1DiffByTab ( int diff ) // diff = VR(winner) - VR(loser) +{ + int i = 0; + int j = sizeof(vr1_diff_tab) / sizeof(*vr1_diff_tab) - 2; + DASSERT( vr1_diff_tab[i].diff == -10000 ); + DASSERT( vr1_diff_tab[j+1].diff == 10000 ); + + #if HAVE_PRINT + { + static int done = 0; + PRINT_IF(!done++, + "vr1_diff_tab: %d,%d => %d,%d / %d,%d\n", + vr1_diff_tab[i].diff, vr1_diff_tab[i].add_vr, + vr1_diff_tab[j].diff, vr1_diff_tab[j].add_vr, + vr1_diff_tab[j+1].diff, vr1_diff_tab[j+1].add_vr ); + } + #endif + + if ( diff <= -10000 ) + return vr1_diff_tab[0].add_vr; + if ( diff >= 10000 ) + return vr1_diff_tab[j].add_vr; + + while ( i < j ) + { + const int k = (i+j)/2; + if ( diff < vr1_diff_tab[k].diff ) + j = k-1; + else if ( diff >= vr1_diff_tab[k+1].diff ) + i = k+1; + else + return vr1_diff_tab[k].add_vr; + } + + return vr1_diff_tab[i].add_vr; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const s16 vr2_diff_tab[112] = +{ + 6598, 4999, 3963, 3233, 2671, 2212, 1819, 1476, // 0 .. 7 + 1168, 889, 633, 395, 174, -34, -231, -418, // 8 .. 15 + -596, -767, -931, -1090, -1243, -1392, -1536, -1676, // 16 .. 23 + -1813, -1946, -2076, -2203, -2328, -2450, -2569, -2687, // 24 .. 31 + -2802, -2915, -3026, -3136, -3243, -3349, -3454, -3557, // 32 .. 39 + -3658, -3759, -3857, -3955, -4051, -4147, -4241, -4334, // 40 .. 47 + -4425, -4516, -4606, -4695, -4783, -4870, -4957, -5042, // 48 .. 55 + -5127, -5211, -5294, -5377, -5459, -5540, -5622, -5702, // 56 .. 63 + -5783, -5863, -5942, -6022, -6101, -6180, -6259, -6338, // 64 .. 71 + -6417, -6495, -6574, -6653, -6731, -6810, -6889, -6968, // 72 .. 79 + -7048, -7127, -7207, -7287, -7367, -7448, -7530, -7611, // 80 .. 87 + -7693, -7776, -7860, -7944, -8029, -8114, -8201, -8288, // 88 .. 95 + -8377, -8466, -8557, -8649, -8743, -8838, -8935, -9034, // 96 .. 103 + -9135, -9239, -9345, -9455, -9568, -9684, -9806, -9933, // 104 .. 111 +}; + +/////////////////////////////////////////////////////////////////////////////// + +int GetVr2DiffByTab ( int diff ) // diff = VR(winner) - VR(loser) +{ + if ( diff > 6598 ) + return 0; + if ( diff < -9933 ) + return 112; + + const s16 search = diff; + int i = 0, j = sizeof(vr2_diff_tab) / sizeof(*vr2_diff_tab) - 1; + + while ( i < j ) + { + const int k = (i+j)/2; + if ( search > vr2_diff_tab[k] ) + j = k-1; + else if ( search <= vr2_diff_tab[k+1] ) + i = k+1; + else + return k+1; + } + + return i+1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_vr2 ( int argc, char ** argv ) +{ + if ( argc > 1 && !strcmp(argv[1],"-c") ) + { + int i, j, vr = 0, n = sizeof(vr1_diff_tab) / sizeof(*vr1_diff_tab) - 1; + printf("\nstatic const int16_t vr2_diff_tab[%u] =\n{\n",n-1); + + for ( i = n-1; i > 0; i -= 8, vr += 8 ) + { + putchar('\t'); + for ( j = 0; j < 8; j++ ) + { + int idx = i - j; + if ( idx > 0 ) + printf("%6d,", vr1_diff_tab[idx].diff - 1 ); + } + printf(" // %3u .. %3u\n",vr,vr+7); + } + printf("};\n\n"); + return ERR_OK; + } + + const int from = -10000; + const int to = 10000; + printf(">>> Compare 3x VR from %d to %d <<<\n",from,to); + + int diff, err_count = 0; + for ( diff = from; diff <= to; diff++ ) + { + const int vr0 = GetVrDiffByTab(diff); + const int vr1 = GetVr1DiffByTab(diff); + const int vr2 = GetVr2DiffByTab(diff); + + if ( vr0 != vr1 || vr1 != vr2 ) + { + if ( ++err_count < 20 ) + printf("ERR: diff %6d : vr012 %3d %3d %3d\n",diff,vr0,vr1,vr2); + } + } + + if (err_count) + printf("!!! %u error%s total!\n",err_count,err_count==1?"":"s"); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_regex() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_regex ( int argc, char ** argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr,"\nUsage: wtest RE|REGEXP reg_expr string...\n\n"); + return ERR_SYNTAX; + } + + Regex_t re; + enumError err = ScanRegex(&re,true,argv[1]); + if ( err || !re.valid ) + return ERROR0(err,"Invalid regex: %s\n",argv[1]); + + struct { FastBuf_t b; char space[500]; } res; + InitializeFastBuf(&res,sizeof(res)); + + int i; + for ( i = 2; i < argc; i++ ) + { + ccp arg = argv[i]; + int stat = ReplaceRegex(&re,&res.b,arg,-1); + printf("\n%s\n -> [%d] %s\n",arg,stat,GetFastBufString(&res.b)); + } + + putchar('\n'); + ResetFastBuf(&res.b); + ResetRegex(&re); + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_ct_mode() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void test_ct_mode_helper ( ct_mode_t mode, bool show_id ) +{ + ct_bmg_t ctb; + SetupCtBMG(&ctb,mode,mode); + putchar('\n'); + if (show_id) + printf("%s\n",GetCtBMGIdentification(&ctb,true)); + DumpCtBMG(stdout,0,&ctb); +} + +static enumError test_ct_mode ( int argc, char ** argv ) +{ + bool show_id = false; + int i; + for ( i = 1; i < argc; i++ ) + { + ccp arg = argv[i]; + if (!strcmp(arg,"-l")) + show_id = true; + } + + test_ct_mode_helper(CTM_NINTENDO,show_id); + test_ct_mode_helper(CTM_CTCODE,show_id); + test_ct_mode_helper(CTM_LECODE1,show_id); + test_ct_mode_helper(CTM_LECODE2,show_id); + putchar('\n'); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_scan_hex() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_scan_hex ( int argc, char ** argv ) +{ + PRINT_SIZEOF1(VarMode_t); + PRINT_SIZEOF1(IntMode_t); + PRINT_SIZEOF1(Var_t); + PRINT_SIZEOF1(FastBuf_t); + + PRINT1("offsetof(Var_t,name) = %zu\n",offsetof(Var_t,name)); + PRINT1("offsetof(Var_t,mode) = %zu\n",offsetof(Var_t,mode)); + PRINT1("offsetof(Var_t,int_mode) = %zu\n",offsetof(Var_t,int_mode)); + PRINT1("offsetof(Var_t,i) = %zu\n",offsetof(Var_t,i)); + + FastBuf_t fb; + InitializeFastBuf(&fb,1000); + + int i; + for ( i = 1; i < argc; i++ ) + { + char name[20]; + snprintf(name,sizeof(name),"Param #%u",i); + + ScanInfo_t si; + InitializeSI(&si,argv[i],strlen(argv[i]),name,0); + + ClearFastBuf(&fb); + ScanHexlineSI(&si,&fb,true); + HexDump16(stdout,0,0,fb.buf,fb.ptr-fb.buf); + putchar('\n'); + CheckEolSI(&si); + ResetSI(&si); + } + + ResetFastBuf(&fb); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_shift_jis() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_shift_jis ( int argc, char ** argv ) +{ + printf("\nShift-JIS: %s\n\n",GetShiftJISStatistics()); + + { + static char info[] = "|\x82\x4a|\x82\x50|"; + HexDump16(stdout,0,0,info,sizeof(info)); + + cucp src0 = (cucp)info; + cucp src1 = (cucp)info; + cucp end1 = src1 + strlen(info); + while ( *src0 || *src1 ) + { + u16 code0 = ScanShiftJISChar(&src0); + u16 code1 = ScanShiftJISCharE(&src1,end1); + u16 ch0 = GetShiftJISChar(code0); + u16 ch1 = GetShiftJISChar(code1); + + printf("%4x %4x : %3zd %3zd : %4x %4x\n", + code0, code1, src0-(cucp)info, src1-(cucp)info, ch0, ch1 ); + } + } + + printf("\n------------------------------------------\n"); + + { + static u16 info2[] = { 0x7199, 0xff10, 0xff80, 0xff9f, 0xffff, 0 }; + char buf[100], *dest = buf; + + const u16 *p; + for ( p = info2; *p; p++ ) + { + u16 code = GetShiftJISChar(*p); + printf("%4x -> %4x\n",*p,code); + + if ( code >= 0x100 ) + { + write_be16(dest,code); + dest += 2; + } + else if ( code > 0 ) + *dest++ = code; + } + *dest++= 0; + HexDump16(stdout,0,0,buf,dest-buf); + + cucp ptr = (cucp)buf; + while (*ptr) + { + u16 code = ScanShiftJISChar(&ptr); + u16 ch = GetShiftJISChar(code); + + printf("%4x : %3zd : %4x\n", code, ptr-(cucp)buf, ch ); + } + } + + printf("\n------------------------------------------\n"); + + { + static char info[] = "|\xff\x80|"; + HexDump16(stdout,0,0,info,sizeof(info)); + + cucp src0 = (cucp)info; + cucp src1 = (cucp)info; + cucp end1 = src1 + strlen(info); + while ( *src0 || *src1 ) + { + u16 beg0 = be16(src0); + u16 beg1 = be16(src1); + u16 code0 = ScanShiftJISChar(&src0); + u16 code1 = ScanShiftJISCharE(&src1,end1); + u16 ch0 = GetShiftJISChar(code0); + u16 ch1 = GetShiftJISChar(code1); + + printf(" %4x %4x : %4x %4x : %3zd %3zd : %4x %4x\n", + beg0, beg1, code0, code1, + src0-(cucp)info, src1-(cucp)info, ch0, ch1 ); + } + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_item_slot() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_item_slot ( int argc, char ** argv ) +{ + int i; + for ( i = 1; i < argc; i++ ) + { + u8 *data; + uint size; + ccp fname; + enumError err = OpenReadFILE(argv[i],0,false,&data,&size,&fname,0); + if (err) + continue; + + printf("\n\e[36;1m----- %s [%u] -----\e[0m\n",fname,size); + u8 *ptr = data; + u8 *end = data+size; + uint n_tab = *ptr++; + printf("%u tables\n",n_tab); + uint t; + for ( t = 0; t < n_tab; t++ ) + { + uint tabsize = ptr[0] * ptr[1]; + u8 *nexttab = ptr + tabsize + 2; + if ( nexttab > end ) + break; + + printf("%2u/%u: %2u*%-2u %03zx..%03zx/%x\n", + t, n_tab, ptr[0], ptr[1], ptr - data, nexttab - data, size ); + ptr = nexttab; + } + FREE(data); + } + + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Since 2001 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// hour support + +#if 0 // [[?]] ??? +int hour2elapsed ( $hour = NULL, $ref_time = NULL ) +{ + if (!$ref_time) + $ref_time = time(); + $ref = self::time2hour($ref_time); + if ( !isset($hour) || $hour == $ref ) + return ( $ref_time - self::hour2time($ref) ) / self::DUR_HOUR; + + return $hour < $ref ? 1.0 : 0.0; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// day support + +#if 0 // [[?]] ??? +int day2elapsed ( $day = NULL, $ref_time = NULL ) +{ + if (!$ref_time) + $ref_time = time(); + $ref = self::time2day($ref_time); + if ( !isset($day) || $day == $ref ) + return ( $ref_time - self::day2time($ref) ) / self::DUR_DAY; + + return $day < $ref ? 1.0 : 0.0; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// week support + +#if 0 // [[?]] ??? +int week2elapsed ( $week = NULL, $ref_time = NULL ) +{ + if (!$ref_time) + $ref_time = time(); + $ref = self::time2week($ref_time); + if ( !isset($week) || $week == $ref ) + return ( $ref_time - self::week2time($ref) ) / self::DUR_WEEK; + + return $week < $ref ? 1.0 : 0.0; +} +#endif + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +#if 0 // [[?]] ??? +int GetWeekInfoByYear ( $year ) +{ + $r = new \stdClass; + $r->year = intval($year); + + $tim = gmmktime( 12,0,0, 1,4,$r->year ); + $r->first_week = self::time2week($tim); + + $tim = gmmktime( 12,0,0, 12,28,$r->year ); + $r->last_week = self::time2week($tim); + + return $r; +} +#endif + +/////////////////////////////////////////////////////////////////////////// + +#if 0 // [[?]] ??? +int GetWeekYearByWeek ( $week ) +{ + $l = explode(',',gmstrftime('%G,%V', + self::week2time($week) + self::DUR_DAY*3 )); + + $r = new \stdClass; + $r->year = intval($l[0]); // real year number + $r->week = intval($l[1]); // 1..12 + return $r; +} +#endif + +/////////////////////////////////////////////////////////////////////////// + +#if 0 // [[?]] ??? +int GetWeekYearByTime ( $time ) +{ + $l = explode(',',gmstrftime('%G,%V,%u',$time)); + + $r = new \stdClass; + $r->year = intval($l[0]); // real year number + $r->week = intval($l[1]); // 1..12 + $r->weekday = intval($l[2]); // 1=monday .. 7=sunday + return $r; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// month support + +#if 0 // [[?]] ??? +int month2elapsed ( $month = NULL, $ref_time = NULL ) +{ + if (!$ref_time) + $ref_time = time($ref_time); + $ref = self::time2month($ref_time); + if ( !isset($month) || $month == $ref ) + { + $beg = self::month2time($ref); + return ( $ref_time - $beg ) / ( self::month2time($ref+1) - $beg ); + } + + return $month < $ref ? 1.0 : 0.0; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// quarter support + +#if 0 // [[?]] ??? +int quarter2elapsed ( $quarter = NULL, $ref_time = NULL ) +{ + if (!$ref_time) + $ref_time = time(); + $ref = self::time2quarter($ref_time); + if ( !isset($quarter) || $quarter == $ref ) + { + $beg = self::quarter2time($ref); + return ( $ref_time - $beg ) / ( self::quarter2time($ref+1) - $beg ); + } + + return $quarter < $ref ? 1.0 : 0.0; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// year support + +#if 0 // [[?]] ??? +int year2elapsed ( $year = NULL, $ref_time = NULL ) +{ + if (!$ref_time) + $ref_time = time($ref_time); + $ref = self::time2year($ref_time); + if ( !isset($year) || $year == $ref ) + { + $beg = self::year2time($ref); + return ( $ref_time - $beg ) / ( self::year2time($ref+1) - $beg ); + } + + return $year < $ref ? 1.0 : 0.0; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// misc + +#if 0 // [[?]] ??? +int GetAverageDuration ( $period ) +{ + // Values for day and week are exact. + // Values for month, quarter and year are based on 365.2425 days/year. + + switch ($period) + { + case 'hour': return self::DUR_HOUR; break; + case 'day': return self::DUR_DAY; break; + case 'week': return self::DUR_WEEK; break; + case 'month': return self::DUR_MONTH; break; + case 'quarter': return self::DUR_QUARTER; break; + case 'year': return self::DUR_YEAR; break; + } + return false; +} +#endif + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_since2001() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_since2001 ( int argc, char ** argv ) +{ + uint i; + for ( i = 1; i < argc; i++ ) + { + s64 time0 = argv[i][0] == 'n' ? GetTimeSec(false) + : argv[i][0] == '+' ? 978307200 + : str2l(argv[i],0,10); + printf("\n\e[36m %s -> %lld -> %s \e[0m\n", + argv[i], time0, PrintTimeByFormat("%F %T",time0) ); + + int num; + s64 tim; + ccp t1, t2, format = "%c:%7d %-10s %-13s %-19s %9u\n"; + + //--- hour + + num = time2hour(time0); + tim = hour2time(num); + t1 = hour2text(num,0,0,0); + t2 = hour2text(num,99,0,0); + printf(format,'H',num,t1,t2, + PrintTimeByFormat("%F %T",tim), + hour2duration(num)); + + t1 = hour2text(num,10,0,0); + t2 = hour2text(num,11,0,0); + printf(format,'H',num,t1,t2, + PrintTimeByFormat("%F %T",tim), + hour2duration(num)); + + //--- day + + num = time2day(time0); + tim = day2time(num); + t1 = day2text(num,0,0,0); + t2 = day2text(num,99,0,0); + printf(format,'D',num,t1,t2, + PrintTimeByFormat("%F %T",tim), + day2duration(num)); + + //--- week + + num = time2week(time0); + tim = week2time(num); + t1 = week2text(num,0,0,0); + t2 = week2text(num,99,0,0); + printf(format,'W',num,t1,t2, + PrintTimeByFormat("%F %T",tim), + week2duration(num)); + + //--- month + + num = time2month(time0); + tim = month2time(num); + t1 = month2text(num,0,0,0); + t2 = month2text(num,99,0,0); + printf(format,'M',num,t1,t2, + PrintTimeByFormat("%F %T",tim), + month2duration(num)); + + //--- quarter + + num = time2quarter(time0); + tim = quarter2time(num); + t1 = quarter2text(num,0,0,0); + t2 = quarter2text(num,99,0,0); + printf(format,'Q',num,t1,t2, + PrintTimeByFormat("%F %T",tim), + quarter2duration(num)); + + //--- year + + num = time2year(time0); + tim = year2time(num); + t1 = year2text(num,0,0,0); + t2 = year2text(num,99,0,0); + printf(format,'Y',num,t1,t2, + PrintTimeByFormat("%F %T",tim), + year2duration(num)); + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_timezone() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_timezone ( int argc, char ** argv ) +{ + printf("\nEndless timezone test with interval of 30 minutes started (pid=%u)\n", + getpid() ); + printf("Typical usage: nohup ./%s TIMEZONE >>timezone-test.tmp 2>&1 &\n\n",progname); + + for(;;) + { + u_usec_t start = GetTimerUSec(); + ccp t1 = PrintTimevalByFormatUTC("%F %T.@@@@@@ %z",0); + ccp t2 = PrintTimevalByFormat("%F %T.@@@@@@ %z",0); + u_usec_t dur = GetTimerUSec() - start; + printf("%s : %s : in %llu usec\n",t1,t2,dur); + fflush(stdout); + sleep(1800); + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_condition() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_condition ( int argc, char ** argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr, + "\n" + "Usage: wtest 'CO' arg...\n" + " or: wtest 'CONDITION' arg...\n" + "ARG is one of: hex | 'r' | 's' | 'max' \n" + "\n" + ); + return 0; + } + + int setting = -1; + uint argi; + for ( argi = 1; argi < argc; argi++ ) + { + ccp arg = argv[argi]; + if ( *arg == 's' ) + { + setting = strtol(arg+1,0,10); + continue; + } + + if (!strcmp(arg,"max")) + { + uint max = 0, mask; + for ( mask = 0; mask <= 0xffff; mask++ ) + { + gobj_condition_set_t gcs; + const uint n = GetGobjConditions(&gcs,setting,mask); + if ( max < n ) + max = n; + } + printf("Max conditions for setting %2d: %u/%u\n", + setting, max, GOBJ_COND_SET_MAX ); + continue; + } + + const u16 mask = strcmp(arg,"r") ? str2ul(arg,0,16) : MyRandom(0x10000); + gobj_condition_set_t gcs; + const uint n = GetGobjConditions(&gcs,setting,mask); + printf("\n0x%04x = %u [setting %d -> %02x]\n", + mask, mask, setting, gcs.setting_mask ); + + uint i; + for ( i = 0; i < n; i++ ) + { + const gobj_condition_t *c = gcs.cond+i; + printf(" %04x.%02x %2d : %2d..%-2d %2d..%-2d : %s\n", + c->mask, + c->setting, + c->bitnum, + c->min_offline, + c->max_offline, + c->min_online, + c->max_online, + c->info ); + } + } + putchar('\n'); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_xbit() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int GetXPFBitNum ( int is_online, int n_offline, int n_online ) +{ + if ( is_online > 0 ) + { + int base = n_online < 7 ? 4 + : n_online < 10 ? 6 + : n_online < 13 ? 8 + : n_online < 19 ? 10 + : 12; + return base + ( n_offline > 1 ); + } + else + { + return n_offline < 2 ? 0 + : n_offline > 4 ? 3 + : n_offline - 1; + } +} + +//----------------------------------------------------------------------------- + +static enumError test_xbit ( int argc, char ** argv ) +{ + if ( argc < 2 ) + { + fprintf(stderr, + "\n" + "Usage: wtest 'XBIT' tupel..\n" + "TUPEL is: id_online,n_offline,n_online\n" + "Default is 0 for each param.\n" + "\n" + ); + return 0; + } + + uint argi; + for ( argi = 1; argi < argc; argi++ ) + { + int is_online = 0, n_offline = 0, n_online = 0; + + ccp arg = argv[argi]; + is_online = str2l(arg,0,10); + arg = strchr(arg,','); + if (arg) + { + n_offline = str2l(arg+1,0,10); + arg = strchr(arg+1,','); + if (arg) + n_online = str2l(arg+1,0,10); + } + + int bitnum = GetXPFBitNum(is_online,n_offline,n_online); + printf("%3d %3d %3d => %2d\n",is_online,n_offline,n_online,bitnum); + } + putchar('\n'); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// test_in_arch() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static enumError test_in_arch ( int argc, char ** argv ) +{ + int total = 0, differ = 0; + const DbFileFILE_t *ptr; + for ( ptr = DbFileFILE; ptr->file; ptr++ ) + { + total++; + bool old = OLD_DBF_IN_ARCHIVE(ptr->flags); + bool new = DBF_ARCH_REQUIRED(ptr->flags); + if ( old != new ) + { + differ++; + printf("old=%d, new=%d : %05x : %s\n", + old, new, ptr->flags, ptr->file ); + } + } + printf(" %u/%u differ\n",differ,total); + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// develop() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static void test_dropfastbuf ( FastBuf_t *fb, int index, int size ) +{ + AssignFastBuf(fb,"0123456789",-1); + DropFastBuf(fb,index,size); + printf("DROP(,%d,%d): %s: %s\n", + index, size, GetFastBufStatus(fb), GetFastBufString(fb) ); +} + +//----------------------------------------------------------------------------- + +const gobj_cond_ref_t * FindConditionRef2 ( u16 ref_id ) +{ + const gobj_cond_ref_t *rc = cond_ref_tab; + for ( ; rc->ref_id; rc++ ) + if ( rc->ref_id == ref_id ) + return rc; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static enumError develop ( int argc, char ** argv ) +{ + #if 1 + static const u16 tab[] = { 0x1000,0x103f, 0x1900,0x193f, 0 }; + const u16 *ptr; + + #if 1 + fputs("\nCompare results of FindConditionRef() and FindConditionRef2()\n",stdout); + for ( ptr = tab; *ptr; ptr += 2 ) + { + u16 ref_id; + for ( ref_id = ptr[0]; ref_id <= ptr[1]; ref_id++ ) + { + const gobj_cond_ref_t *r1 = FindConditionRef(ref_id); + const gobj_cond_ref_t *r2 = FindConditionRef2(ref_id); + if ( r1 != r2 ) + printf("DIFFER: %#06x : %#6x %#6x\n", + ref_id, r1 ? r1->ref_id : 0, r2 ? r2->ref_id : 0 ); + } + } + #endif + putchar('\n'); + + const int N1000 = 100; + for ( ptr = tab; *ptr; ptr += 2 ) + { + uint i; + u64 duration; + + duration = -GetTimerUSec(); + for ( i = 0; i < 1000*N1000; i++ ) + { + u16 ref_id; + for ( ref_id = ptr[0]; ref_id <= ptr[1]; ref_id++ ) + FindConditionRef(ref_id); + } + duration += GetTimerUSec(); + printf( " %s total, %s/1000, %u*1000 * FindConditionRef (%#x..%#x)\n", + PrintTimerUSec6(0,0,duration,true), + PrintTimerUSec6(0,0,duration/N1000/(ptr[1]-ptr[0]+1),true), + N1000, ptr[0], ptr[1] ); + + duration = -GetTimerUSec(); + for ( i = 0; i < 1000*N1000; i++ ) + { + u16 ref_id; + for ( ref_id = ptr[0]; ref_id <= ptr[1]; ref_id++ ) + FindConditionRef2(ref_id); + } + duration += GetTimerUSec(); + printf( " %s total, %s/1000, %u*1000 * FindConditionRef2(%#x..%#x)\n", + PrintTimerUSec6(0,0,duration,true), + PrintTimerUSec6(0,0,duration/N1000/(ptr[1]-ptr[0]+1),true), + N1000, ptr[0], ptr[1] ); + } + putchar('\n'); + + #elif 1 + static int ff_list[] = { FF_CHR, FF_CLR, FF_MDL, FF_PAT, FF_SCN, FF_SHP, + FF_SRT, FF_TEX, FF_TEX_CT, 0 }; + int *ff; + for ( ff = ff_list; *ff; ff++ ) + { + printf("--- %s ---\n",GetNameFF(0,*ff)); + int v; + for ( v = 0; v < 20; v++ ) + { + const brsub_info_t *bi = GetInfoBRSUB(*ff,v); + int ns = GetKnownSectionNumBRSUB(*ff,v); + if ( ( bi ? bi->n_sect : -1 ) != ns ) + printf("FAIL: %s v%u: %d != %d\n", + GetNameFF(0,*ff), v, + bi ? bi->n_sect : -1, ns ); + } + } + + #elif 1 + + kmp_special_t special; + memset(special,0,sizeof(special)); + u8 *ptr = (u8*)special; + ptr[0] = 1; + ptr[1] = 2; + printf("size=%zd : %zd : %d,%d: %d,%d\n", + sizeof(special), (u8*)(special+1) - (u8*)(special), + special[0], special[1], ptr[0], ptr[1] ); + + #elif 1 + + FastBuf_t fb1; + InitializeFastBuf(&fb1,sizeof(fb1)); + printf("fb1: %s\n",GetFastBufStatus(&fb1)); + + struct { FastBuf_t b; char space[2000]; } fb2; + InitializeFastBuf(&fb2,sizeof(fb2)); + printf("fb2: %s\n",GetFastBufStatus(&fb2.b)); + + char fb3x[2000]; + FastBuf_t *fb3 = InitializeFastBuf(&fb3x,sizeof(fb3x)); + printf("fb3: %s\n",GetFastBufStatus(fb3)); + + AppendFastBuf(&fb1,"aaa",-1); printf("fb1: %s: %s\n",GetFastBufStatus(&fb1),fb1.buf); + AppendFastBuf(&fb1,"BB",-1); printf("fb1: %s: %s\n",GetFastBufStatus(&fb1),fb1.buf); + InsertFastBuf(&fb1,1,"111",-1); printf("fb1: %s: %s\n",GetFastBufStatus(&fb1),fb1.buf); + InsertFastBuf(&fb1,-1,"222",-1); printf("fb1: %s: %s\n",GetFastBufStatus(&fb1),fb1.buf); + + test_dropfastbuf(&fb1,12,4); + test_dropfastbuf(&fb1, 8,4); + test_dropfastbuf(&fb1, 3,4); + test_dropfastbuf(&fb1,-9,4); + test_dropfastbuf(&fb1,-15,4); + test_dropfastbuf(&fb1,5,-3); + test_dropfastbuf(&fb1,5,-8); + + #elif 1 + if ( argc > 2 ) + { + double d1 = strtod(argv[1],0); + double d2 = strtod(argv[2],0); + + int i; + for ( i = -5; i< 60; i += 5 ) + { + bool sf = IsSameF(d1,d2,i); + bool sd = IsSameD(d1,d2,i); + printf(" %3i: %d %d %12.10f %12.10f\n", + i, sf, sd, d1, d2 ); + } + } + + #else + int i; + for ( i = 1; i < argc; i++ ) + { + char buf[1000]; + uint len = EncodeBase64(buf,sizeof(buf),argv[i],strlen(argv[i]), + TableEncode64x, true, "\n\t", 10 ); + printf("-----\n%s [%zu->%u]\n\t%s\n",argv[i],strlen(argv[i]),len,buf); + } + #endif + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// command definitions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + CMD_HELP, // help_exit() + CMD_TEST, // test() + CMD_DEVELOP, // develop(argc,argv) + CMD_WIIMM, // test_wiimm(argc,argv) + + CMD_FLOAT, // test_float(argc,argv) + CMD_FLOAT_TAB, // create_float_tab() + CMD_MATRIX, // test_matrix(argc,argv) + CMD_MATCH_PATTERN, // test_match_pattern(argc,argv) + CMD_MEDIAN_CUT, // test_median_cut(argc,argv) + CMD_CMPR, // test_cmpr(argc,argv) + CMD_CREATE_IMAGES, // create_images(argc,argv) + CMD_RESIZE, // test_resize(argc,argv) + CMD_RESIZE_IMAGE, // resize_image(argc,argv) + CMD_SCAN_TEXT, // test_scan_text(argc,argv) + CMD_SCAN_STRING, // test_scan_string(argc,argv) + CMD_SCAN_FILE, // test_scan_file(argc,argv) + CMD_FILENAME, // test_filename(argc,argv) + CMD_DRAW_STARTPOS, // draw_startpos(argc,argv) + CMD_PATH_CMP, // test_path_cmp(argc,argv) + CMD_LINE2, // test_line(2,argc,argv) + CMD_LINE3, // test_line(3,argc,argv) + CMD_OBJFLOW, // ana_file_objflow(argc,argv) + CMD_MEMFILE, // test_memfile(argc,argv) + CMD_FIND_DOL, // test_find_dol(argc,argv) + CMD_SUBFILE, // test_subfile(argc,argv) + CMD_STR_LIST, // test_str_list(argc,argv) + CMD_CONTAINER, // test_container(argc,argv) + CMD_UUID, // test_uuid(argc,argv) + CMD_ANA_MAGIC, // test_analyse_magic(argc,argv) + CMD_VR, // test_vr(argc,argv) + CMD_VR2, // test_vr2(argc,argv) + CMD_REGEX, // test_regex(argc,argv) + CMD_CT_MODE, // test_ct_mode(argc,argv) + CMD_SCAN_HEX, // test_scan_hex(argc,argv) + CMD_SHIFT_JIS, // test_shift_jis(argc,argv) + CMD_ITEMSLOT, // test_item_slot(argc,argv) + CMD_SINCE2001, // test_since2001(argc,argv) + CMD_TIMEZONE, // test_timezone(argc,argv) + CMD_CONDITION, // test_condition(argc,argv) + CMD_XBIT, // test_xbit(argc,argv) + CMD_IN_ARCH, // test_in_arch(argc,argv) + + CMD__N +}; + +/////////////////////////////////////////////////////////////////////////////// + +static const KeywordTab_t CommandTab[] = +{ + { CMD_HELP, "HELP", "?", 0 }, + { CMD_TEST, "TEST", "T", 0 }, + { CMD_DEVELOP, "DEVELOP", "D", 0 }, + + #ifdef HAVE_WIIMM_EXT + { CMD_WIIMM, "WIIMM", "W", 0 }, + #endif + + { CMD_FLOAT, "FLOAT", "F", 0 }, + { CMD_FLOAT_TAB, "FLOAT-TAB", "FT", 0 }, + { CMD_MATRIX, "MATRIX", 0, 0 }, + { CMD_MATCH_PATTERN, "MATCH", 0, 0 }, + { CMD_MEDIAN_CUT, "MEDIAN-CUT", "MC", 0 }, + { CMD_CMPR, "CMPR", 0, 0 }, + { CMD_CREATE_IMAGES, "CREATE-IMAGES","CI", 0 }, + { CMD_RESIZE, "RESIZE", "RES", 0 }, + { CMD_RESIZE_IMAGE, "RESIZE-IMAGE", "RI", 0 }, + { CMD_SCAN_TEXT, "SCAN-TEXT", "ST", 0 }, + { CMD_SCAN_STRING, "SCAN-STRING", "SS", 0 }, + { CMD_SCAN_FILE, "SCAN-FILE", "SF", 0 }, + { CMD_FILENAME, "FILENAME", "FNAME", 0 }, + { CMD_DRAW_STARTPOS, "DRAW-STARTPOS","DSP", 0 }, + { CMD_PATH_CMP, "PATH-CMP", "PC", 0 }, + { CMD_LINE2, "LINE2", "L2", 0 }, + { CMD_LINE3, "LINE3", "L3", 0 }, + { CMD_OBJFLOW, "ANA-OBJFLOW", "AOF", 0 }, + { CMD_MEMFILE, "MEMFILE", "MF", 0 }, + { CMD_FIND_DOL, "FIND-DOL", "FD", 0 }, + { CMD_SUBFILE, "SUBFILE", 0, 0 }, + { CMD_STR_LIST, "STR-LIST", "SL", 0 }, + { CMD_CONTAINER, "CONTAINER", 0, 0 }, + { CMD_UUID, "UUID", 0, 0 }, + { CMD_ANA_MAGIC, "ANA-MAGIC", "ANAMAGIC", 0 }, + { CMD_VR, "VR", 0, 0 }, + { CMD_VR2, "VR2", 0, 0 }, + { CMD_REGEX, "REGEXP", "RE", 0 }, + { CMD_CT_MODE, "CT-MODE", "CTM", 0 }, + { CMD_SCAN_HEX, "SCAN-HEX", "SH", 0 }, + { CMD_SHIFT_JIS, "SHIFT-JIS", "SJIS", 0 }, + { CMD_ITEMSLOT, "ITEM-SLOT", "IS", 0 }, + { CMD_SINCE2001, "SINCE2001", "S2", 0 }, + { CMD_TIMEZONE, "TIMEZONE", "TZ", 0 }, + { CMD_CONDITION, "CONDITION", "CO", 0 }, + { CMD_XBIT, "XBIT", 0, 0 }, + { CMD_IN_ARCH, "IN-ARCH", "INARCH", 0 }, + + { CMD__N,0,0,0 } +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// help_exit() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void help_exit() +{ + printf("\nCommands:\n\n"); + const KeywordTab_t * cmd; + for ( cmd = CommandTab; cmd->name1; cmd++ ) + if (cmd->name2) + printf(" %-*s | %s\n",CMD1_FW,cmd->name1,cmd->name2); + else + printf(" %s\n",cmd->name1); + putchar('\n'); + #ifdef HAVE_WIIMM_EXT + wiimm_help_exit(false); + #endif + exit(ERR_SYNTAX); +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// main() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +//#define XXX 0x123 +//#define MM1(a) #a +//#define MM2(a) MM1(a) + +void AddOne ( double m[3][4] ) +{ + m[0][0] += 1.0; + m[1][1] += 1.0; + m[2][2] += 1.0; +} + +int main ( int argc, char ** argv ) +{ + printf("***** %s *****\n",TITLE); + print_title_func = print_title; + SetupLib(argc,argv,NAME); + fflush(stdout); + + //printf(" |" MM1(XXX) "|" MM2(XXX) "\n"); + + if (0) + { + double34 x; + x.m[0][0] = 10.0; + x.m[1][1] = 20.0; + x.m[2][2] = 30.0; + printf(" %6.2f %6.2f %6.2f\n",x.m[0][0],x.m[1][1],x.m[2][2]); + AddOne(x.m); + printf(" %6.2f %6.2f %6.2f\n",x.m[0][0],x.m[1][1],x.m[2][2]); + } + + printf("term width = %d\n",GetTermWidth(80,0)); + + if ( argc < 2 ) + help_exit(); + + int cmd_stat; + const KeywordTab_t * cmd_ct = ScanKeyword(&cmd_stat,argv[1],CommandTab); + if (!cmd_ct) + { + PrintKeywordError(CommandTab,argv[1],cmd_stat,0,0); + help_exit(); + } + + argv[1] = argv[0]; + argv++; + argc--; + + switch(cmd_ct->id) + { + case CMD_TEST: test(argc,argv); break; + case CMD_DEVELOP: develop(argc,argv); break; + + #ifdef HAVE_WIIMM_EXT + case CMD_WIIMM: test_wiimm(argc,argv); break; + #endif + + case CMD_FLOAT: test_float(argc,argv); break; + case CMD_FLOAT_TAB: create_float_tab(); break; + case CMD_MATRIX: test_matrix(argc,argv); break; + case CMD_MATCH_PATTERN: test_match_pattern(argc,argv); break; + case CMD_MEDIAN_CUT: test_median_cut(argc,argv); break; + case CMD_CMPR: test_cmpr(argc,argv); break; + case CMD_CREATE_IMAGES: create_images(argc,argv); break; + case CMD_RESIZE: test_resize(argc,argv); break; + case CMD_RESIZE_IMAGE: resize_image(argc,argv); break; + case CMD_SCAN_TEXT: test_scan_text(argc,argv); break; + case CMD_SCAN_STRING: test_scan_string(argc,argv); break; + case CMD_SCAN_FILE: test_scan_file(argc,argv); break; + case CMD_FILENAME: test_filename(argc,argv); break; + case CMD_DRAW_STARTPOS: draw_startpos(argc,argv); break; + case CMD_PATH_CMP: test_path_cmp(argc,argv); break; + case CMD_LINE2: test_line(2,argc,argv); break; + case CMD_LINE3: test_line(3,argc,argv); break; + case CMD_OBJFLOW: ana_file_objflow(argc,argv); break; + case CMD_MEMFILE: test_memfile(argc,argv); break; + case CMD_FIND_DOL: test_find_dol(argc,argv); break; + case CMD_SUBFILE: test_subfile(argc,argv); break; + case CMD_STR_LIST: test_str_list(argc,argv); break; + case CMD_CONTAINER: test_container(argc,argv); break; + case CMD_UUID: test_uuid(argc,argv); break; + case CMD_ANA_MAGIC: test_analyse_magic(argc,argv); break; + case CMD_VR: test_vr(argc,argv); break; + case CMD_VR2: test_vr2(argc,argv); break; + case CMD_REGEX: test_regex(argc,argv); break; + case CMD_CT_MODE: test_ct_mode(argc,argv); break; + case CMD_SCAN_HEX: test_scan_hex(argc,argv); break; + case CMD_SHIFT_JIS: test_shift_jis(argc,argv); break; + case CMD_ITEMSLOT: test_item_slot(argc,argv); break; + case CMD_SINCE2001: test_since2001(argc,argv); break; + case CMD_TIMEZONE: test_timezone(argc,argv); break; + case CMD_CONDITION: test_condition(argc,argv); break; + case CMD_XBIT: test_xbit(argc,argv); break; + case CMD_IN_ARCH: test_in_arch(argc,argv); break; + //case CMD_HELP: + default: + help_exit(); + } + + if (SIGINT_level) + ERROR0(ERR_INTERRUPT,"Program interrupted by user."); + + return max_error; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/project/support/KCLFlags.xml b/project/support/KCLFlags.xml new file mode 100644 index 0000000..7e8cd24 --- /dev/null +++ b/project/support/KCLFlags.xml @@ -0,0 +1,39 @@ + + + + 00 Road + 01 Slippery Road + 02 Weak Off-road + 03 Off-road + 04 Heavy Off-road + 05 Slippery Road 2 + 06 Boost Pad + 07 Boost Ramp + 08 Jump Pad + 09 Item Road + 0a Solid Fall + 0b Moving Water + 0c Wall + 0d Invisible Wall + 0e Obstacle Wall + 0f Wall + 10 Fall Boundary + 11 Cannon Activator + 12 Unknown + 13 Half-pipe Ramp + 14 Wall + 15 Moving Road + 16 Sticky Road + 17 Road + 18 Sound Trigger + 19 Unknown + 1a Unknown + 1b Unknown + 1c Unknown + 1d Road + 1e Special Wall + 1f Wall + + + + \ No newline at end of file diff --git a/project/support/KMPItems.xml b/project/support/KMPItems.xml new file mode 100644 index 0000000..1bb7884 --- /dev/null +++ b/project/support/KMPItems.xml @@ -0,0 +1,319 @@ + + +airblock +Psea(B) +lensFX +venice_nami(B) +sound_river +sound_water_fall +pocha(BE) +sound_lake +sound_big_fall +sound_sea +sound_fountain +sound_volcano +sound_audience +sound_big_river +sound_sand_fall +sound_lift +pochaYogan(BE) +entry(E) +pochaMori(E) +eline_control +sound_Mii +begoman_manager +ice(B) +startline2D +itembox(B) +DummyPole +flag(?) +flagBlend(?) +gnd_sphere +gnd_trapezoid +gnd_wave1 +gnd_wave2 +gnd_wave3 +gnd_wave4 +sun(B) +woodbox(BE) +KmoonZ(B) +sunDS(B) +coin(BE) +ironbox +ItemDirect +s_itembox(B) +pile_coin(BE) +f_itembox(B) +MashBalloonGC(B) +WLwallGC(BK) +CarA1(B) +basabasa(B) +HeyhoShipGBA(B) +koopaBall(BE) +kart_truck(B) +car_body(B) +skyship(B) +w_woodbox(BE) +w_itembox(B) +w_itemboxline(3B2E) +VolcanoBall1(BE) +penguin_s(BE3D) +penguin_m(BED) +penguin_l(BE) +castleballoon1(B) +dossunc(BE) +dossunc_soko +boble(BE) +K_bomb_car(BE) +K_bomb_car_dummy(?) +car_body_dummy(?) +kart_truck_dummy(?) +hanachan(BE2D) +seagull(B) +moray(B) +crab(BE3D) +basabasa_dummy +CarA2(B) +CarA3(B) +Hwanwan(BE) +HeyhoBallGBA(BE) +Twanwan(B) +cruiserR(BE) +bird(B) +sin_itembox(B) +Twanwan_ue(?) +BossHanachan(BE2D) +Kdossunc +BossHanachanHead +K_bomb_car1(?) +dummy(?) +dokan_sfc(B) +castletree1(B) +castletree1c(B) +castletree2(B) +castleflower1(B) +mariotreeGC(B) +mariotreeGCc(B) +donkytree1GC(B) +donkytree2GC(B) +peachtreeGC(B) +peachtreeGCc(B) +npc_mii_a(?) +npc_mii_b(?) +npc_mii_c(?) +obakeblockSFCc(B) +WLarrowGC(B) +WLscreenGC(B) +WLdokanGC(BK) +MarioGo64c(B) +PeachHunsuiGC(BE) +kinokoT1(B) +kinokoT2(?) +pylon01(B) +PalmTree(B) +parasol(B) +cruiser(BE) +K_sticklift00(B) +heyho2(2B) +HeyhoTreeGBAc(B) +MFaceBill(?) +truckChimSmk(BE) +MiiObj01(B) +MiiObj02(B) +MiiObj03(B) +gardentreeDS(B) +gardentreeDSc(B) +FlagA1(B) +FlagA2(B) +FlagB1(B) +FlagB2(B) +FlagA3(B) +DKtreeA64(B) +DKtreeA64c(B) +DKtreeB64(B) +DKtreeB64c(B) +TownTreeDSc(B) +Piston(B) +oilSFC(B) +DKmarutaGCc(?) +DKropeGCc(?) +mii_balloon(B) +windmill(B) +dossun(BE) +TownTreeDS(B) +Ksticketc(B) +monte_a(B) +MiiStatueM1(B) +ShMiiObj01(B) +ShMiiObj02(B) +ShMiiObj03(B) +Hanabi(E) +miiposter(B) +dk_miiobj00(B) +light_house(B) +r_parasol(B) +obakeblock2SFCc(B) +obakeblock3SFCc(B) +koopaFigure(B2D) +pukupuku(B) +v_stand1(?) +v_stand2(?) +leaf_effect(E) +karehayama(BE) +EarthRing(B) +SpaceSun(B) +BlackHole(?) +StarRing(BE) +M_obj_kanban(?) +MiiStatueL1(B) +MiiStatueD1(B) +MiiSphinxY1(?) +MiiSphinxY2(B) +FlagA5(B) +CarB(B) +FlagA4(?) +Steam(E) +Alarm(E) +group_monte_a(B) +MiiStatueL2(B) +MiiStatueD2(B) +MiiStatueP1(B) +SentakuDS(B) +fks_screen_wii(B) +KoopaFigure64(BE) +b_teresa(B) +MiiStatueDK1(?) +MiiKanban(B) +BGteresaSFC(B) +kuribo(B2D) +choropu(BE) +cow(B5D) +pakkun_f(B2D) +WLfirebarGC(B) +wanwan(BE) +poihana(BE2D) +DKrockGC(BE) +sanbo(BED) +choropu2(BE) +TruckWagon(BE) +heyho(B) +Press(BE) +Press_soko +pile(?) +choropu_ground +WLfireringGC(B) +pakkun_dokan(B) +begoman_spike(BE) +FireSnake(BE) +koopaFirebar(2B) +Epropeller(BE) +dc_pillar_c +FireSnake_v(BE) +honeBall(?) +puchi_pakkun(BD) +sanbo_big(BED) +sanbo_big.2(BED) +kinoko_ud(B4K) +kinoko_bend(B4K) +VolcanoRock1(B2K) +bulldozer_left(BK) +bulldozer_right(BK) +kinoko_nm(B4K) +Crane(BK) +VolcanoPiece(B42K) +FlamePole(BEK) +TwistedWay(B) +TownBridgeDSc(B3K) +DKship64(3BE) +kinoko_kuki +DKturibashiGCc(B) +FlamePoleEff(?) +aurora(B) +venice_saku(BK) +casino_roulette(BK) +BossField01_OBJ1(?) +dc_pillar(BEK) +dc_sandcone(BEK) +venice_hasi(BEK) +venice_gondola(B?) +quicksand(2B2E) +bblock(B18K) +ami(B) +M_obj_jump(?) +starGate(?) +RM_ring1(B8K) +FlamePole_v(BE) +M_obj_s_jump(?) +InsekiA(B) +InsekiB(B) +FlamePole_v_big(B2E) +Mdush(BK) +HP_pipe(?) +DemoCol(BK) +M_obj_s_jump2(?) +M_obj_jump2(?) +DonkyCannonGC(BED) +BeltEasy(B) +BeltCrossing(B) +BeltCurveA(B) +BeltCurveB(?) +escalator(BK) +DonkyCannon_wii(BED) +escalator_group(2BK) +tree_cannon(BED) +group_enemy_b(B) +group_enemy_c(B) +taimatsu(E) +truckChimSmkW(BE) +Mstand(?) +dkmonitor(B) +group_enemy_a(B) +FlagB3(B) +spot(B) +group_enemy_d(?) +FlagB4(B) +group_enemy_e(B) +group_monte_L(B) +group_enemy_f(B) +FallBsA(E) +FallBsB(E) +FallBsC(?) +volsmk(E) +ridgemii00(B) +Flash_L(E) +Flash_B(E) +Flash_W(E) +Flash_M(E) +Flash_S(E) +MiiSignNoko(B) +UtsuboDokan(B) +Spot64(B) +DemoEf(E) +Fall_MH(E) +Fall_Y(E) +DemoJugemu(BE) +group_enemy_a_demo(B) +group_monte_a_demo(B) +volfall(?) +MiiStatueM2(B) +RhMiiKanban(B) +MiiStatueL3(B) +MiiSignWario(B) +MiiStatueBL1(B) +MiiStatueBD1(B) +Kamifubuki(E) +Crescent64(B) +MiiSighKino(B) +MiiObjD01(B) +MiiObjD02(B) +MiiObjD03(B) +mare_a(B) +mare_b(B) +EnvKareha(E) +EnvFire(E) +EnvSnow(E) +M_obj_start(?) +EnvKarehaUp(E) +M_obj_kanban_y(?) +DKfalls(E) + diff --git a/project/support/kcl-types.txt b/project/support/kcl-types.txt new file mode 100644 index 0000000..ae12d4e --- /dev/null +++ b/project/support/kcl-types.txt @@ -0,0 +1,1721 @@ + +Relation : FLAG == TYPE | VARIANT << 5 + == TYPE + VARIANT * 0x20 + +Percent : Usage within the named sub file + +---------------------------------------------------- +*** Road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x00 0x000 0x0000 0.46% T51 course.kcl +0x00 0x000 0x0000 3.26% T22 course.kcl +0x00 0x000 0x0000 3.61% T14 course.kcl +0x00 0x000 0x0000 4.63% T32 course.kcl +0x00 0x000 0x0000 7.72% T21 course.kcl +0x00 0x000 0x0000 9.98% T74 course.kcl +0x00 0x000 0x0000 11.50% T31 course.kcl +0x00 0x000 0x0000 16.67% T14 Crane.kcl +0x00 0x000 0x0000 17.29% T81 course.kcl +0x00 0x000 0x0000 20.94% T54 course.kcl +0x00 0x000 0x0000 23.60% T42 course.kcl +0x00 0x000 0x0000 33.33% T14 bulldozer_left.kcl +0x00 0x000 0x0000 33.33% T14 bulldozer_right.kcl +0x00 0x000 0x0000 44.11% T11 course.kcl +0x00 0x000 0x0000 100.00% T43 FlamePole.kcl + +0x00 0x001 0x0020 0.70% T43 course.kcl +0x00 0x001 0x0020 0.83% T24 course.kcl +0x00 0x001 0x0020 1.87% T33 course.kcl +0x00 0x001 0x0020 1.89% T32 course.kcl +0x00 0x001 0x0020 2.96% T73 course.kcl +0x00 0x001 0x0020 3.56% T82 course.kcl +0x00 0x001 0x0020 7.25% T83 course.kcl +0x00 0x001 0x0020 7.47% T14 course.kcl +0x00 0x001 0x0020 9.34% T74 course.kcl +0x00 0x001 0x0020 10.87% T13 course.kcl +0x00 0x001 0x0020 17.14% T34 VolcanoPiece16.kcl +0x00 0x001 0x0020 18.03% T52 course.kcl +0x00 0x001 0x0020 18.09% T34 VolcanoPiece18.kcl +0x00 0x001 0x0020 18.18% T34 VolcanoPiece14.kcl +0x00 0x001 0x0020 19.38% T34 course.kcl +0x00 0x001 0x0020 19.48% T34 VolcanoPiece9.kcl +0x00 0x001 0x0020 19.72% T34 VolcanoPiece3.kcl +0x00 0x001 0x0020 20.56% T34 VolcanoPiece2.kcl +0x00 0x001 0x0020 21.39% T34 VolcanoPiece11.kcl +0x00 0x001 0x0020 23.02% T34 VolcanoPiece0.kcl +0x00 0x001 0x0020 25.72% T12 course.kcl +0x00 0x001 0x0020 31.93% T34 VolcanoPiece13.kcl +0x00 0x001 0x0020 32.03% T34 VolcanoPiece1.kcl +0x00 0x001 0x0020 38.67% T34 VolcanoPiece4.kcl +0x00 0x001 0x0020 45.66% T64 course.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece0b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece11b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece13b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece14b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece16b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece18b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece1b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece2b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece3b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece4b.kcl +0x00 0x001 0x0020 100.00% T34 VolcanoPiece9b.kcl + +0x00 0x002 0x0040 0.15% T62 course.kcl +0x00 0x002 0x0040 0.60% T22 course.kcl +0x00 0x002 0x0040 0.67% T43 course.kcl +0x00 0x002 0x0040 1.89% T74 course.kcl +0x00 0x002 0x0040 2.25% T82 course.kcl +0x00 0x002 0x0040 2.36% T41 course.kcl +0x00 0x002 0x0040 5.73% T84 course.kcl +0x00 0x002 0x0040 8.88% T51 course.kcl +0x00 0x002 0x0040 11.03% T63 course.kcl +0x00 0x002 0x0040 17.82% T21 course.kcl +0x00 0x002 0x0040 24.00% T72 course.kcl +0x00 0x002 0x0040 27.27% T41 dc_pillar_base.kcl + +0x00 0x003 0x0060 0.75% T34 VolcanoPiece9.kcl +0x00 0x003 0x0060 0.75% T34 course.kcl +0x00 0x003 0x0060 0.81% T34 VolcanoPiece2.kcl +0x00 0x003 0x0060 0.89% T34 VolcanoPiece4.kcl +0x00 0x003 0x0060 0.92% T34 VolcanoPiece3.kcl +0x00 0x003 0x0060 0.94% T71 course.kcl +0x00 0x003 0x0060 1.01% T34 VolcanoPiece18.kcl +0x00 0x003 0x0060 1.16% T34 VolcanoPiece11.kcl +0x00 0x003 0x0060 1.31% T34 VolcanoPiece1.kcl +0x00 0x003 0x0060 1.59% T34 VolcanoPiece0.kcl +0x00 0x003 0x0060 1.68% T34 VolcanoPiece13.kcl +0x00 0x003 0x0060 2.11% T72 course.kcl +0x00 0x003 0x0060 2.29% T34 VolcanoPiece16.kcl +0x00 0x003 0x0060 3.78% T22 course.kcl +0x00 0x003 0x0060 13.04% T34 VolcanoRock1.kcl +0x00 0x003 0x0060 13.38% T81 course.kcl +0x00 0x003 0x0060 25.00% T64 WLdokanGC.kcl +0x00 0x003 0x0060 25.00% T64 WLwallGC.kcl +0x00 0x003 0x0060 27.00% T83 course.kcl + +0x00 0x004 0x0080 0.23% T33 course.kcl +0x00 0x004 0x0080 0.41% T23 course.kcl +0x00 0x004 0x0080 0.59% T22 course.kcl +0x00 0x004 0x0080 0.85% T73 course.kcl +0x00 0x004 0x0080 2.13% T13 course.kcl +0x00 0x004 0x0080 3.30% T63 course.kcl +0x00 0x004 0x0080 3.31% T52 course.kcl +0x00 0x004 0x0080 8.06% T74 course.kcl +0x00 0x004 0x0080 26.10% T53 course.kcl + +0x00 0x006 0x00c0 0.03% T64 course.kcl +0x00 0x006 0x00c0 0.17% T14 course.kcl +0x00 0x006 0x00c0 1.46% T42 course.kcl +0x00 0x006 0x00c0 2.34% T32 course.kcl + +0x00 0x007 0x00e0 0.17% T51 course.kcl +0x00 0x007 0x00e0 0.83% T32 course.kcl +0x00 0x007 0x00e0 3.67% T24 course.kcl +0x00 0x007 0x00e0 5.92% T22 course.kcl + +0x00 0x008 0x0100 0.48% T21 course.kcl +0x00 0x008 0x0100 0.84% T74 course.kcl +0x00 0x008 0x0100 1.24% T54 course.kcl +0x00 0x008 0x0100 2.90% T31 course.kcl +0x00 0x008 0x0100 3.82% T42 course.kcl +0x00 0x008 0x0100 5.10% T11 course.kcl +0x00 0x008 0x0100 13.32% T14 course.kcl + +0x00 0x009 0x0120 0.17% T14 course.kcl +0x00 0x009 0x0120 0.35% T24 course.kcl +0x00 0x009 0x0120 0.49% T64 course.kcl +0x00 0x009 0x0120 0.67% T13 course.kcl +0x00 0x009 0x0120 0.86% T83 course.kcl +0x00 0x009 0x0120 1.28% T12 course.kcl +0x00 0x009 0x0120 1.49% T32 course.kcl +0x00 0x009 0x0120 2.71% T52 course.kcl +0x00 0x009 0x0120 6.28% T33 course.kcl + +0x00 0x00a 0x0140 0.06% T82 course.kcl +0x00 0x00a 0x0140 0.12% T51 course.kcl +0x00 0x00a 0x0140 0.63% T21 course.kcl +0x00 0x00a 0x0140 1.40% T84 course.kcl +0x00 0x00a 0x0140 1.56% T43 course.kcl +0x00 0x00a 0x0140 9.26% T63 course.kcl +0x00 0x00a 0x0140 9.42% T41 course.kcl + +0x00 0x00b 0x0160 0.43% T54 course.kcl +0x00 0x00b 0x0160 0.81% T74 course.kcl +0x00 0x00b 0x0160 1.74% T84 course.kcl +0x00 0x00b 0x0160 2.33% T13 course.kcl +0x00 0x00b 0x0160 3.06% T34 course.kcl +0x00 0x00b 0x0160 3.60% T22 course.kcl +0x00 0x00b 0x0160 13.04% T34 VolcanoRock2.kcl +0x00 0x00b 0x0160 16.42% T34 VolcanoPiece5.kcl +0x00 0x00b 0x0160 22.71% T61 course.kcl +0x00 0x00b 0x0160 42.86% T34 VolcanoPiece7.kcl +0x00 0x00b 0x0160 100.00% T34 VolcanoPiece5b.kcl +0x00 0x00b 0x0160 100.00% T34 VolcanoPiece7b.kcl +0x00 0x00b 0x0160 100.00% T34 VolcanoPiece8b.kcl + +0x00 0x00c 0x0180 0.12% T32 course.kcl +0x00 0x00c 0x0180 0.33% T22 course.kcl +0x00 0x00c 0x0180 0.35% T33 course.kcl +0x00 0x00c 0x0180 0.86% T74 course.kcl +0x00 0x00c 0x0180 1.62% T52 course.kcl + +0x00 0x00e 0x01c0 0.39% T32 course.kcl +0x00 0x00e 0x01c0 2.76% T72 course.kcl + +0x00 0x00f 0x01e0 0.08% T42 course.kcl +0x00 0x00f 0x01e0 1.76% T22 course.kcl +0x00 0x00f 0x01e0 7.78% T72 course.kcl + +0x00 0x010 0x0200 0.06% T51 course.kcl +0x00 0x010 0x0200 1.82% T74 course.kcl +0x00 0x010 0x0200 1.96% T42 course.kcl +0x00 0x010 0x0200 2.20% T21 course.kcl +0x00 0x010 0x0200 3.35% T31 course.kcl +0x00 0x010 0x0200 3.45% T14 course.kcl + +0x00 0x011 0x0220 0.08% T73 course.kcl +0x00 0x011 0x0220 0.11% T34 course.kcl +0x00 0x011 0x0220 0.13% T43 course.kcl +0x00 0x011 0x0220 0.49% T13 course.kcl +0x00 0x011 0x0220 1.67% T74 course.kcl +0x00 0x011 0x0220 2.88% T24 course.kcl + +0x00 0x012 0x0240 0.98% T74 course.kcl +0x00 0x012 0x0240 1.41% T51 course.kcl +0x00 0x012 0x0240 1.76% T43 course.kcl +0x00 0x012 0x0240 4.06% T21 course.kcl + +0x00 0x013 0x0260 0.14% T22 course.kcl +0x00 0x013 0x0260 1.18% T34 course.kcl + +0x00 0x014 0x0280 2.09% T74 course.kcl +0x00 0x014 0x0280 4.23% T84 course.kcl + +0x00 0x016 0x02c0 0.61% T42 course.kcl + +0x00 0x017 0x02e0 0.85% T42 course.kcl +0x00 0x017 0x02e0 1.93% T24 course.kcl +0x00 0x017 0x02e0 3.12% T22 course.kcl + +0x00 0x018 0x0300 1.25% T42 course.kcl +0x00 0x018 0x0300 2.24% T32 course.kcl +0x00 0x018 0x0300 2.26% T22 course.kcl +0x00 0x018 0x0300 3.22% T14 course.kcl + +0x00 0x019 0x0320 0.10% T33 course.kcl +0x00 0x019 0x0320 0.35% T24 course.kcl +0x00 0x019 0x0320 0.72% T32 course.kcl + +0x00 0x01a 0x0340 0.07% T84 course.kcl +0x00 0x01a 0x0340 13.91% T43 course.kcl + +0x00 0x01b 0x0360 0.15% T84 course.kcl +0x00 0x01b 0x0360 3.79% T34 course.kcl + +0x00 0x01c 0x0380 0.21% T33 course.kcl + +0x00 0x01e 0x03c0 0.57% T32 course.kcl + +0x00 0x01f 0x03e0 0.04% T32 course.kcl +0x00 0x01f 0x03e0 0.05% T24 course.kcl +0x00 0x01f 0x03e0 0.10% T42 course.kcl + +0x00 0x021 0x0420 0.07% T24 course.kcl + +0x00 0x022 0x0440 0.84% T22 course.kcl +0x00 0x022 0x0440 2.41% T43 course.kcl + +0x00 0x023 0x0460 0.15% T22 course.kcl +0x00 0x023 0x0460 1.85% T34 course.kcl + +0x00 0x026 0x04c0 6.10% T43 course.kcl + +0x00 0x027 0x04e0 0.07% T24 course.kcl + +0x00 0x029 0x0520 0.61% T34 course.kcl + +0x00 0x02a 0x0540 0.17% T43 course.kcl + +0x00 0x045 0x08a0 47.14% T23 course.kcl + +0x00 0x04d 0x09a0 0.09% T23 course.kcl + +0x00 0x100 0x2000 0.05% T11 course.kcl +0x00 0x100 0x2000 0.06% T41 course.kcl +0x00 0x100 0x2000 0.23% T42 course.kcl +0x00 0x100 0x2000 100.00% T41 dc_pillar.kcl + +0x00 0x101 0x2020 1.49% T14 course.kcl +0x00 0x101 0x2020 1.51% T64 course.kcl +0x00 0x101 0x2020 2.14% T74 course.kcl + +0x00 0x102 0x2040 0.04% T84 course.kcl +0x00 0x102 0x2040 0.07% T63 course.kcl +0x00 0x102 0x2040 0.13% T82 course.kcl +0x00 0x102 0x2040 0.24% T74 course.kcl +0x00 0x102 0x2040 0.61% T34 course.kcl +0x00 0x102 0x2040 1.31% T34 VolcanoPiece1.kcl + +0x00 0x103 0x2060 0.09% T54 course.kcl +0x00 0x103 0x2060 1.90% T22 course.kcl + +0x00 0x104 0x2080 0.02% T12 course.kcl +0x00 0x104 0x2080 0.10% T32 course.kcl +0x00 0x104 0x2080 0.20% T83 course.kcl +0x00 0x104 0x2080 0.28% T52 course.kcl +0x00 0x104 0x2080 0.29% T53 course.kcl +0x00 0x104 0x2080 0.33% T24 course.kcl +0x00 0x104 0x2080 0.35% T22 course.kcl + +0x00 0x108 0x2100 0.27% T33 course.kcl + +0x00 0x10a 0x2140 0.11% T34 course.kcl + +0x00 0x10b 0x2160 0.13% T22 course.kcl +0x00 0x10b 0x2160 0.68% T54 course.kcl + +0x00 0x10c 0x2180 0.04% T32 course.kcl +0x00 0x10c 0x2180 0.28% T52 course.kcl + +0x00 0x118 0x2300 0.08% T43 course.kcl + +0x00 0x11f 0x23e0 0.10% T32 course.kcl + +0x00 0x123 0x2460 0.15% T22 course.kcl + +0x00 0x145 0x28a0 15.86% T23 course.kcl + +0x00 0x200 0x4000 11.11% T14 bulldozer_left.kcl +0x00 0x200 0x4000 11.11% T14 bulldozer_right.kcl + +0x00 0x201 0x4020 1.04% T83 course.kcl +0x00 0x201 0x4020 6.99% T64 course.kcl + +0x00 0x204 0x4080 0.25% T33 course.kcl + +0x00 0x206 0x40c0 0.06% T32 course.kcl +0x00 0x206 0x40c0 1.13% T44 course.kcl + +0x00 0x207 0x40e0 0.28% T24 course.kcl +0x00 0x207 0x40e0 0.30% T32 course.kcl +0x00 0x207 0x40e0 0.57% T22 course.kcl + +0x00 0x208 0x4100 0.04% T43 course.kcl +0x00 0x208 0x4100 3.26% T14 course.kcl + +0x00 0x20b 0x4160 2.31% T54 course.kcl + +0x00 0x20c 0x4180 1.21% T33 course.kcl + +0x00 0x20e 0x41c0 0.03% T32 course.kcl + +0x00 0x20f 0x41e0 0.02% T32 course.kcl +0x00 0x20f 0x41e0 0.61% T22 course.kcl + +0x00 0x210 0x4200 0.20% T43 course.kcl + +0x00 0x214 0x4280 0.25% T33 course.kcl + +0x00 0x217 0x42e0 0.66% T22 course.kcl +0x00 0x217 0x42e0 2.44% T32 course.kcl + +0x00 0x218 0x4300 0.54% T43 course.kcl +0x00 0x218 0x4300 0.98% T32 course.kcl + +0x00 0x21c 0x4380 0.08% T33 course.kcl + +0x00 0x21f 0x43e0 0.28% T32 course.kcl + +0x00 0x220 0x4400 4.22% T43 course.kcl + +0x00 0x245 0x48a0 1.95% T23 course.kcl + +---------------------------------------------------- +*** Slippery Road 1 *** +type variant flag percent track + subfile +---------------------------------------------------- +0x01 0x000 0x0001 43.78% T51 course.kcl +0x01 0x000 0x0001 43.89% T62 course.kcl + +0x01 0x001 0x0021 0.77% T82 course.kcl +0x01 0x001 0x0021 17.86% T73 course.kcl + +0x01 0x008 0x0101 1.31% T51 course.kcl +0x01 0x008 0x0101 4.37% T62 course.kcl + +0x01 0x009 0x0121 3.11% T73 course.kcl + +0x01 0x010 0x0201 0.20% T51 course.kcl + +0x01 0x011 0x0221 1.15% T73 course.kcl + +0x01 0x019 0x0321 0.60% T73 course.kcl + +0x01 0x045 0x08a1 14.92% T41 course.kcl +0x01 0x045 0x08a1 34.41% T71 course.kcl + +0x01 0x04d 0x09a1 1.85% T41 course.kcl +0x01 0x04d 0x09a1 1.92% T71 course.kcl + +0x01 0x145 0x28a1 0.91% T71 course.kcl + +---------------------------------------------------- +*** Weak Off-road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x02 0x001 0x0022 6.74% T34 VolcanoPiece9.kcl +0x02 0x001 0x0022 14.29% T34 VolcanoPiece13.kcl +0x02 0x001 0x0022 14.66% T34 course.kcl +0x02 0x001 0x0022 14.86% T34 VolcanoPiece16.kcl +0x02 0x001 0x0022 15.08% T34 VolcanoPiece0.kcl +0x02 0x001 0x0022 15.08% T34 VolcanoPiece18.kcl +0x02 0x001 0x0022 18.18% T34 VolcanoPiece14.kcl +0x02 0x001 0x0022 19.28% T34 VolcanoPiece1.kcl +0x02 0x001 0x0022 20.00% T34 VolcanoPiece4.kcl +0x02 0x001 0x0022 22.48% T34 VolcanoPiece3.kcl +0x02 0x001 0x0022 24.19% T34 VolcanoPiece2.kcl +0x02 0x001 0x0022 28.90% T34 VolcanoPiece11.kcl + +0x02 0x002 0x0042 0.22% T22 course.kcl +0x02 0x002 0x0042 33.70% T62 course.kcl + +0x02 0x003 0x0062 7.15% T42 course.kcl + +0x02 0x005 0x00a2 0.24% T22 course.kcl + +0x02 0x006 0x00c2 3.74% T32 course.kcl + +0x02 0x007 0x00e2 0.29% T34 course.kcl + +0x02 0x00f 0x01e2 1.91% T34 course.kcl +0x02 0x00f 0x01e2 28.57% T34 VolcanoPiece7.kcl +0x02 0x00f 0x01e2 48.39% T34 VolcanoPiece8.kcl +0x02 0x00f 0x01e2 53.73% T34 VolcanoPiece5.kcl + +0x02 0x011 0x0222 0.11% T34 course.kcl + +0x02 0x017 0x02e2 0.82% T34 course.kcl + +0x02 0x01e 0x03c2 2.47% T32 course.kcl + +0x02 0x01f 0x03e2 1.80% T34 course.kcl + +0x02 0x027 0x04e2 0.86% T34 course.kcl + +---------------------------------------------------- +*** Off-road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x03 0x000 0x0003 0.14% T13 course.kcl +0x03 0x000 0x0003 0.28% T21 course.kcl +0x03 0x000 0x0003 1.03% T72 course.kcl +0x03 0x000 0x0003 1.88% T54 course.kcl +0x03 0x000 0x0003 5.57% T11 course.kcl +0x03 0x000 0x0003 25.11% T81 course.kcl + +0x03 0x001 0x0023 0.17% T63 course.kcl +0x03 0x001 0x0023 0.64% T83 course.kcl +0x03 0x001 0x0023 4.45% T64 course.kcl + +0x03 0x002 0x0043 2.23% T64 course.kcl +0x03 0x002 0x0043 4.17% T14 course.kcl + +0x03 0x004 0x0083 0.94% T33 course.kcl +0x03 0x004 0x0083 1.42% T43 course.kcl +0x03 0x004 0x0083 1.84% T63 course.kcl +0x03 0x004 0x0083 1.95% T84 course.kcl +0x03 0x004 0x0083 2.76% T82 course.kcl +0x03 0x004 0x0083 3.39% T62 course.kcl +0x03 0x004 0x0083 3.78% T32 course.kcl +0x03 0x004 0x0083 6.77% T51 course.kcl +0x03 0x004 0x0083 10.06% T13 course.kcl +0x03 0x004 0x0083 11.90% T74 course.kcl +0x03 0x004 0x0083 12.25% T11 course.kcl +0x03 0x004 0x0083 12.70% T73 course.kcl +0x03 0x004 0x0083 13.68% T21 course.kcl +0x03 0x004 0x0083 14.77% T12 course.kcl +0x03 0x004 0x0083 20.59% T54 course.kcl + +0x03 0x007 0x00e3 0.07% T22 course.kcl + +0x03 0x008 0x0103 0.04% T54 course.kcl +0x03 0x008 0x0103 2.27% T13 course.kcl +0x03 0x008 0x0103 2.42% T11 course.kcl +0x03 0x008 0x0103 100.00% T41 dc_sandcone.kcl + +0x03 0x009 0x0123 0.11% T64 course.kcl +0x03 0x009 0x0123 0.16% T63 course.kcl +0x03 0x009 0x0123 2.46% T73 course.kcl + +0x03 0x00a 0x0143 0.17% T14 course.kcl + +0x03 0x00c 0x0183 0.25% T13 course.kcl +0x03 0x00c 0x0183 0.32% T51 course.kcl +0x03 0x00c 0x0183 0.34% T12 course.kcl +0x03 0x00c 0x0183 0.55% T21 course.kcl +0x03 0x00c 0x0183 0.77% T33 course.kcl +0x03 0x00c 0x0183 0.87% T32 course.kcl +0x03 0x00c 0x0183 0.91% T63 course.kcl +0x03 0x00c 0x0183 2.30% T11 course.kcl +0x03 0x00c 0x0183 2.34% T84 course.kcl +0x03 0x00c 0x0183 4.89% T54 course.kcl + +0x03 0x010 0x0203 0.08% T21 course.kcl + +0x03 0x014 0x0283 0.41% T43 course.kcl +0x03 0x014 0x0283 0.71% T82 course.kcl +0x03 0x014 0x0283 1.01% T13 course.kcl +0x03 0x014 0x0283 1.40% T51 course.kcl +0x03 0x014 0x0283 2.87% T74 course.kcl +0x03 0x014 0x0283 3.13% T73 course.kcl +0x03 0x014 0x0283 3.96% T21 course.kcl + +0x03 0x01c 0x0383 0.06% T84 course.kcl +0x03 0x01c 0x0383 2.50% T32 course.kcl + +0x03 0x046 0x08c3 1.49% T31 course.kcl + +0x03 0x04e 0x09c3 1.37% T31 course.kcl + +0x03 0x0c5 0x18a3 14.71% T41 course.kcl +0x03 0x0c5 0x18a3 21.98% T71 course.kcl + +0x03 0x0cd 0x19a3 2.62% T71 course.kcl +0x03 0x0cd 0x19a3 12.86% T41 course.kcl + +---------------------------------------------------- +*** Heavy Off-road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x04 0x002 0x0044 0.05% T63 course.kcl + +0x04 0x003 0x0064 0.74% T82 course.kcl + +0x04 0x004 0x0084 0.85% T22 course.kcl +0x04 0x004 0x0084 1.56% T62 course.kcl + +0x04 0x005 0x00a4 2.18% T23 course.kcl + +0x04 0x00a 0x0144 0.07% T63 course.kcl +0x04 0x00a 0x0144 1.54% T73 course.kcl + +0x04 0x00c 0x0184 0.12% T22 course.kcl + +0x04 0x01a 0x0344 0.70% T43 course.kcl + +0x04 0x024 0x0484 0.25% T22 course.kcl + +0x04 0x086 0x10c4 3.89% T41 course.kcl + +0x04 0x08e 0x11c4 1.04% T41 course.kcl + +---------------------------------------------------- +*** Slippery Road 2 *** +type variant flag percent track + subfile +---------------------------------------------------- +0x05 0x000 0x0005 4.93% T61 course.kcl + +0x05 0x008 0x0105 0.37% T61 course.kcl + +---------------------------------------------------- +*** Boost Pad *** +type variant flag percent track + subfile +---------------------------------------------------- +0x06 0x000 0x0006 0.02% T24 course.kcl +0x06 0x000 0x0006 0.02% T32 course.kcl +0x06 0x000 0x0006 0.03% T41 course.kcl +0x06 0x000 0x0006 0.08% T73 course.kcl +0x06 0x000 0x0006 0.14% T81 course.kcl +0x06 0x000 0x0006 0.16% T22 course.kcl +0x06 0x000 0x0006 0.16% T51 course.kcl +0x06 0x000 0x0006 0.17% T14 course.kcl +0x06 0x000 0x0006 0.22% T13 course.kcl +0x06 0x000 0x0006 0.26% T64 course.kcl +0x06 0x000 0x0006 0.28% T23 course.kcl +0x06 0x000 0x0006 0.29% T33 course.kcl +0x06 0x000 0x0006 0.29% T53 course.kcl +0x06 0x000 0x0006 0.35% T52 course.kcl +0x06 0x000 0x0006 0.69% T42 course.kcl +0x06 0x000 0x0006 1.33% T44 course.kcl +0x06 0x000 0x0006 1.72% T11 course.kcl +0x06 0x000 0x0006 8.33% T63 TownBridgeDS.kcl +0x06 0x000 0x0006 8.33% T63 TownBridgeDS2.kcl +0x06 0x000 0x0006 16.67% T63 TownBridgeDS3.kcl + +0x06 0x008 0x0106 0.03% T31 course.kcl +0x06 0x008 0x0106 0.05% T61 course.kcl +0x06 0x008 0x0106 0.07% T22 course.kcl +0x06 0x008 0x0106 0.09% T33 course.kcl +0x06 0x008 0x0106 0.14% T52 course.kcl + +0x06 0x010 0x0206 0.04% T32 course.kcl +0x06 0x010 0x0206 0.05% T24 course.kcl + +0x06 0x018 0x0306 0.41% T43 course.kcl + +---------------------------------------------------- +*** Boost Ramp *** +type variant flag percent track + subfile +---------------------------------------------------- +0x07 0x000 0x0007 0.02% T22 course.kcl +0x07 0x000 0x0007 0.02% T32 course.kcl +0x07 0x000 0x0007 0.05% T44 course.kcl +0x07 0x000 0x0007 0.08% T42 course.kcl +0x07 0x000 0x0007 0.09% T23 course.kcl +0x07 0x000 0x0007 0.10% T64 course.kcl +0x07 0x000 0x0007 8.33% T63 TownBridgeDS.kcl + +0x07 0x001 0x0027 0.02% T12 course.kcl +0x07 0x001 0x0027 0.02% T22 course.kcl +0x07 0x001 0x0027 0.02% T32 course.kcl +0x07 0x001 0x0027 0.04% T84 course.kcl +0x07 0x001 0x0027 0.06% T41 course.kcl +0x07 0x001 0x0027 0.07% T64 course.kcl +0x07 0x001 0x0027 0.08% T44 course.kcl +0x07 0x001 0x0027 0.11% T61 course.kcl +0x07 0x001 0x0027 0.11% T72 course.kcl +0x07 0x001 0x0027 0.14% T31 course.kcl +0x07 0x001 0x0027 0.21% T34 course.kcl +0x07 0x001 0x0027 0.23% T51 course.kcl +0x07 0x001 0x0027 0.33% T14 course.kcl +0x07 0x001 0x0027 0.73% T23 course.kcl +0x07 0x001 0x0027 8.33% T63 TownBridgeDS2.kcl +0x07 0x001 0x0027 22.22% T21 Mdush.kcl + +0x07 0x008 0x0107 0.02% T33 course.kcl + +0x07 0x009 0x0127 0.03% T31 course.kcl +0x07 0x009 0x0127 0.15% T33 course.kcl + +0x07 0x010 0x0207 0.02% T24 course.kcl +0x07 0x010 0x0207 0.02% T32 course.kcl + +0x07 0x011 0x0227 0.02% T24 course.kcl + +0x07 0x018 0x0307 0.08% T14 course.kcl + +0x07 0x020 0x0407 0.04% T43 course.kcl + +0x07 0x021 0x0427 0.52% T43 course.kcl + +---------------------------------------------------- +*** Jump Pad *** +type variant flag percent track + subfile +---------------------------------------------------- +0x08 0x000 0x0008 2.70% T72 course.kcl + +0x08 0x001 0x0028 0.14% T53 course.kcl + +0x08 0x002 0x0048 0.50% T62 course.kcl + +0x08 0x003 0x0068 0.07% T13 course.kcl + +0x08 0x004 0x0088 68.18% T13 kinoko_r.kcl + +0x08 0x006 0x00c8 0.42% T52 course.kcl + +0x08 0x00b 0x0168 0.11% T13 course.kcl + +0x08 0x00c 0x0188 68.18% T13 kinoko_d_r.kcl + +---------------------------------------------------- +*** Item Road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x09 0x000 0x0009 0.11% T34 course.kcl +0x09 0x000 0x0009 0.23% T71 course.kcl +0x09 0x000 0x0009 0.32% T82 course.kcl +0x09 0x000 0x0009 0.78% T32 course.kcl +0x09 0x000 0x0009 1.17% T74 course.kcl +0x09 0x000 0x0009 1.22% T54 course.kcl +0x09 0x000 0x0009 1.50% T43 course.kcl +0x09 0x000 0x0009 1.73% T14 course.kcl +0x09 0x000 0x0009 1.99% T83 course.kcl +0x09 0x000 0x0009 3.81% T31 course.kcl +0x09 0x000 0x0009 3.91% T51 course.kcl +0x09 0x000 0x0009 4.69% T21 course.kcl +0x09 0x000 0x0009 4.81% T63 course.kcl +0x09 0x000 0x0009 5.16% T22 course.kcl +0x09 0x000 0x0009 14.32% T81 course.kcl + +0x09 0x001 0x0029 0.25% T52 course.kcl +0x09 0x001 0x0029 2.43% T64 course.kcl + +0x09 0x002 0x0049 0.03% T51 course.kcl +0x09 0x002 0x0049 0.14% T71 course.kcl +0x09 0x002 0x0049 0.40% T24 course.kcl +0x09 0x002 0x0049 1.16% T82 course.kcl + +0x09 0x003 0x0069 0.05% T71 course.kcl +0x09 0x003 0x0069 0.07% T63 course.kcl +0x09 0x003 0x0069 0.10% T51 course.kcl +0x09 0x003 0x0069 0.10% T83 course.kcl +0x09 0x003 0x0069 0.84% T33 course.kcl +0x09 0x003 0x0069 1.08% T12 course.kcl +0x09 0x003 0x0069 4.17% T13 course.kcl +0x09 0x003 0x0069 4.40% T52 course.kcl + +0x09 0x005 0x00a9 0.27% T31 course.kcl +0x09 0x005 0x00a9 0.84% T33 course.kcl +0x09 0x005 0x00a9 2.19% T73 course.kcl +0x09 0x005 0x00a9 2.51% T84 course.kcl +0x09 0x005 0x00a9 4.27% T51 course.kcl +0x09 0x005 0x00a9 5.98% T42 course.kcl +0x09 0x005 0x00a9 6.37% T52 course.kcl +0x09 0x005 0x00a9 8.41% T74 course.kcl +0x09 0x005 0x00a9 15.70% T82 course.kcl + +0x09 0x008 0x0109 0.73% T74 course.kcl +0x09 0x008 0x0109 0.84% T31 course.kcl +0x09 0x008 0x0109 0.88% T32 course.kcl +0x09 0x008 0x0109 1.24% T14 course.kcl +0x09 0x008 0x0109 1.29% T22 course.kcl +0x09 0x008 0x0109 2.32% T43 course.kcl + +0x09 0x00b 0x0169 0.78% T33 course.kcl +0x09 0x00b 0x0169 2.56% T13 course.kcl + +0x09 0x00d 0x01a9 0.04% T31 course.kcl +0x09 0x00d 0x01a9 0.21% T33 course.kcl +0x09 0x00d 0x01a9 0.41% T84 course.kcl + +0x09 0x010 0x0209 0.08% T43 course.kcl +0x09 0x010 0x0209 1.78% T31 course.kcl + +0x09 0x018 0x0309 0.20% T32 course.kcl +0x09 0x018 0x0309 0.46% T43 course.kcl +0x09 0x018 0x0309 0.50% T34 course.kcl + +0x09 0x020 0x0409 0.39% T34 course.kcl + +---------------------------------------------------- +*** Solid Fall *** +type variant flag percent track + subfile +---------------------------------------------------- +0x0a 0x000 0x000a 1.00% T63 course.kcl +0x0a 0x000 0x000a 1.49% T51 course.kcl +0x0a 0x000 0x000a 6.02% T31 course.kcl + +0x0a 0x002 0x004a 0.03% T31 course.kcl +0x0a 0x002 0x004a 0.08% T43 course.kcl + +0x0a 0x003 0x006a 0.08% T61 course.kcl + +0x0a 0x004 0x008a 0.21% T14 course.kcl +0x0a 0x004 0x008a 3.93% T24 course.kcl + +0x0a 0x005 0x00aa 1.19% T82 course.kcl +0x0a 0x005 0x00aa 3.27% T21 course.kcl +0x0a 0x005 0x00aa 4.68% T54 course.kcl +0x0a 0x005 0x00aa 22.49% T12 course.kcl + +0x0a 0x006 0x00ca 0.05% T11 course.kcl +0x0a 0x006 0x00ca 0.09% T24 course.kcl +0x0a 0x006 0x00ca 0.24% T32 course.kcl +0x0a 0x006 0x00ca 0.74% T31 course.kcl + +0x0a 0x008 0x010a 0.46% T31 course.kcl + +0x0a 0x00c 0x018a 0.10% T43 course.kcl +0x0a 0x00c 0x018a 0.83% T14 course.kcl + +0x0a 0x00d 0x01aa 0.56% T33 course.kcl + +0x0a 0x00e 0x01ca 0.29% T31 course.kcl + +0x0a 0x012 0x024a 0.08% T43 course.kcl + +0x0a 0x016 0x02ca 0.96% T24 course.kcl + +0x0a 0x01c 0x038a 0.06% T43 course.kcl + +0x0a 0x0c1 0x182a 0.01% T31 course.kcl +0x0a 0x0c1 0x182a 0.10% T32 course.kcl +0x0a 0x0c1 0x182a 0.13% T41 course.kcl + +0x0a 0x0c7 0x18ea 11.28% T71 course.kcl + +---------------------------------------------------- +*** Moving Water *** +type variant flag percent track + subfile +---------------------------------------------------- +0x0b 0x000 0x000b 5.74% T32 course.kcl + +0x0b 0x001 0x002b 0.20% T32 course.kcl + +0x0b 0x002 0x004b 0.63% T32 course.kcl +0x0b 0x002 0x004b 0.63% T52 course.kcl + +0x0b 0x010 0x020b 2.26% T32 course.kcl + +0x0b 0x018 0x030b 1.08% T32 course.kcl + +0x0b 0x01a 0x034b 0.35% T32 course.kcl + +0x0b 0x01b 0x036b 0.56% T32 course.kcl + +---------------------------------------------------- +*** Wall *** +type variant flag percent track + subfile +---------------------------------------------------- +0x0c 0x000 0x000c 0.59% T22 course.kcl +0x0c 0x000 0x000c 1.27% T52 course.kcl +0x0c 0x000 0x000c 1.73% T32 course.kcl +0x0c 0x000 0x000c 2.27% T13 course.kcl +0x0c 0x000 0x000c 3.00% T34 VolcanoPiece9.kcl +0x0c 0x000 0x000c 3.56% T34 VolcanoPiece4.kcl +0x0c 0x000 0x000c 3.67% T34 VolcanoPiece3.kcl +0x0c 0x000 0x000c 3.92% T82 course.kcl +0x0c 0x000 0x000c 4.02% T34 VolcanoPiece18.kcl +0x0c 0x000 0x000c 4.14% T83 course.kcl +0x0c 0x000 0x000c 4.21% T24 course.kcl +0x0c 0x000 0x000c 4.27% T61 course.kcl +0x0c 0x000 0x000c 4.61% T42 course.kcl +0x0c 0x000 0x000c 4.62% T34 VolcanoPiece11.kcl +0x0c 0x000 0x000c 4.74% T74 course.kcl +0x0c 0x000 0x000c 5.33% T34 course.kcl +0x0c 0x000 0x000c 5.52% T21 course.kcl +0x0c 0x000 0x000c 6.00% T11 course.kcl +0x0c 0x000 0x000c 6.35% T34 VolcanoPiece0.kcl +0x0c 0x000 0x000c 6.72% T34 VolcanoPiece13.kcl +0x0c 0x000 0x000c 6.81% T71 course.kcl +0x0c 0x000 0x000c 8.46% T51 course.kcl +0x0c 0x000 0x000c 8.80% T43 course.kcl +0x0c 0x000 0x000c 9.14% T34 VolcanoPiece16.kcl +0x0c 0x000 0x000c 10.48% T34 VolcanoPiece2.kcl +0x0c 0x000 0x000c 11.44% T34 VolcanoPiece1.kcl +0x0c 0x000 0x000c 11.77% T54 course.kcl +0x0c 0x000 0x000c 14.46% T81 course.kcl +0x0c 0x000 0x000c 16.82% T84 course.kcl +0x0c 0x000 0x000c 17.39% T34 VolcanoRock1.kcl +0x0c 0x000 0x000c 24.42% T63 course.kcl +0x0c 0x000 0x000c 37.46% T72 course.kcl +0x0c 0x000 0x000c 75.00% T64 WLdokanGC.kcl +0x0c 0x000 0x000c 75.00% T64 WLwallGC.kcl + +0x0c 0x001 0x002c 0.07% T64 course.kcl +0x0c 0x001 0x002c 0.45% T33 course.kcl +0x0c 0x001 0x002c 0.81% T14 course.kcl +0x0c 0x001 0x002c 1.95% T74 course.kcl +0x0c 0x001 0x002c 2.01% T54 course.kcl +0x0c 0x001 0x002c 2.46% T34 course.kcl +0x0c 0x001 0x002c 4.13% T51 course.kcl +0x0c 0x001 0x002c 4.86% T42 course.kcl +0x0c 0x001 0x002c 5.21% T21 course.kcl +0x0c 0x001 0x002c 5.82% T24 course.kcl +0x0c 0x001 0x002c 6.17% T83 course.kcl +0x0c 0x001 0x002c 6.35% T32 course.kcl +0x0c 0x001 0x002c 11.41% T13 course.kcl +0x0c 0x001 0x002c 15.24% T73 course.kcl +0x0c 0x001 0x002c 28.10% T34 VolcanoPiece1.kcl +0x0c 0x001 0x002c 34.68% T34 VolcanoPiece11.kcl +0x0c 0x001 0x002c 36.89% T34 VolcanoPiece4.kcl +0x0c 0x001 0x002c 38.31% T34 VolcanoPiece2.kcl +0x0c 0x001 0x002c 38.66% T34 VolcanoPiece13.kcl +0x0c 0x001 0x002c 49.21% T34 VolcanoPiece0.kcl +0x0c 0x001 0x002c 49.71% T34 VolcanoPiece16.kcl +0x0c 0x001 0x002c 50.46% T34 VolcanoPiece3.kcl +0x0c 0x001 0x002c 57.79% T34 VolcanoPiece18.kcl +0x0c 0x001 0x002c 59.44% T34 VolcanoPiece14.kcl +0x0c 0x001 0x002c 70.04% T34 VolcanoPiece9.kcl + +0x0c 0x002 0x004c 0.60% T62 course.kcl +0x0c 0x002 0x004c 0.79% T61 course.kcl +0x0c 0x002 0x004c 0.84% T81 course.kcl +0x0c 0x002 0x004c 0.86% T63 course.kcl +0x0c 0x002 0x004c 1.04% T73 course.kcl +0x0c 0x002 0x004c 1.10% T51 course.kcl +0x0c 0x002 0x004c 1.19% T74 course.kcl +0x0c 0x002 0x004c 1.56% T12 course.kcl +0x0c 0x002 0x004c 1.73% T71 course.kcl +0x0c 0x002 0x004c 1.84% T72 course.kcl +0x0c 0x002 0x004c 1.91% T83 course.kcl +0x0c 0x002 0x004c 3.38% T52 course.kcl +0x0c 0x002 0x004c 4.03% T32 course.kcl +0x0c 0x002 0x004c 4.40% T21 course.kcl +0x0c 0x002 0x004c 5.04% T54 course.kcl +0x0c 0x002 0x004c 8.03% T82 course.kcl +0x0c 0x002 0x004c 10.07% T11 course.kcl +0x0c 0x002 0x004c 14.33% T64 course.kcl +0x0c 0x002 0x004c 14.41% T14 course.kcl +0x0c 0x002 0x004c 21.27% T42 course.kcl +0x0c 0x002 0x004c 38.89% T14 bulldozer_left.kcl +0x0c 0x002 0x004c 38.89% T14 bulldozer_right.kcl +0x0c 0x002 0x004c 70.00% T14 Crane.kcl + +0x0c 0x003 0x006c 0.06% T32 course.kcl +0x0c 0x003 0x006c 0.06% T33 course.kcl +0x0c 0x003 0x006c 0.47% T61 course.kcl +0x0c 0x003 0x006c 0.94% T51 course.kcl +0x0c 0x003 0x006c 1.20% T24 course.kcl +0x0c 0x003 0x006c 1.26% T41 course.kcl +0x0c 0x003 0x006c 2.09% T74 course.kcl +0x0c 0x003 0x006c 3.11% T21 course.kcl +0x0c 0x003 0x006c 3.56% T43 course.kcl +0x0c 0x003 0x006c 5.14% T23 course.kcl +0x0c 0x003 0x006c 5.38% T83 course.kcl +0x0c 0x003 0x006c 5.42% T63 course.kcl +0x0c 0x003 0x006c 6.24% T34 course.kcl +0x0c 0x003 0x006c 6.95% T71 course.kcl +0x0c 0x003 0x006c 7.69% T31 course.kcl +0x0c 0x003 0x006c 8.03% T73 course.kcl +0x0c 0x003 0x006c 12.25% T52 course.kcl +0x0c 0x003 0x006c 12.80% T13 course.kcl +0x0c 0x003 0x006c 13.76% T54 course.kcl +0x0c 0x003 0x006c 18.64% T12 course.kcl +0x0c 0x003 0x006c 29.17% T63 TownBridgeDS.kcl +0x0c 0x003 0x006c 29.17% T63 TownBridgeDS2.kcl +0x0c 0x003 0x006c 29.17% T63 TownBridgeDS3.kcl +0x0c 0x003 0x006c 31.81% T53 course.kcl +0x0c 0x003 0x006c 33.33% T21 Mdush.kcl + +0x0c 0x004 0x008c 0.46% T23 course.kcl +0x0c 0x004 0x008c 24.89% T61 course.kcl + +0x0c 0x005 0x00ac 0.73% T63 course.kcl +0x0c 0x005 0x00ac 0.76% T33 course.kcl +0x0c 0x005 0x00ac 1.16% T74 course.kcl +0x0c 0x005 0x00ac 1.27% T52 course.kcl +0x0c 0x005 0x00ac 1.86% T83 course.kcl +0x0c 0x005 0x00ac 7.41% T84 course.kcl +0x0c 0x005 0x00ac 23.06% T82 course.kcl + +0x0c 0x006 0x00cc 0.70% T63 course.kcl +0x0c 0x006 0x00cc 0.77% T73 course.kcl +0x0c 0x006 0x00cc 4.96% T52 course.kcl + +0x0c 0x007 0x00ec 0.51% T54 course.kcl +0x0c 0x007 0x00ec 2.20% T11 course.kcl + +0x0c 0x008 0x010c 0.19% T11 course.kcl +0x0c 0x008 0x010c 0.36% T32 course.kcl +0x0c 0x008 0x010c 0.59% T21 course.kcl +0x0c 0x008 0x010c 1.14% T74 course.kcl +0x0c 0x008 0x010c 1.47% T22 course.kcl +0x0c 0x008 0x010c 2.46% T34 course.kcl +0x0c 0x008 0x010c 7.21% T43 course.kcl +0x0c 0x008 0x010c 17.39% T34 VolcanoRock2.kcl +0x0c 0x008 0x010c 31.68% T84 course.kcl + +0x0c 0x009 0x012c 2.09% T24 course.kcl +0x0c 0x009 0x012c 2.54% T32 course.kcl +0x0c 0x009 0x012c 7.73% T73 course.kcl +0x0c 0x009 0x012c 12.80% T13 course.kcl +0x0c 0x009 0x012c 35.39% T61 course.kcl + +0x0c 0x00a 0x014c 0.06% T32 course.kcl +0x0c 0x00a 0x014c 0.26% T54 course.kcl +0x0c 0x00a 0x014c 0.32% T21 course.kcl +0x0c 0x00a 0x014c 0.73% T74 course.kcl +0x0c 0x00a 0x014c 10.37% T14 course.kcl + +0x0c 0x00b 0x016c 0.28% T24 course.kcl +0x0c 0x00b 0x016c 0.36% T32 course.kcl +0x0c 0x00b 0x016c 1.20% T33 course.kcl +0x0c 0x00b 0x016c 1.42% T61 course.kcl + +0x0c 0x00e 0x01cc 0.71% T24 course.kcl + +0x0c 0x010 0x020c 1.21% T43 course.kcl +0x0c 0x010 0x020c 1.68% T32 course.kcl +0x0c 0x010 0x020c 5.20% T84 course.kcl + +0x0c 0x011 0x022c 3.46% T34 course.kcl +0x0c 0x011 0x022c 6.59% T24 course.kcl + +0x0c 0x012 0x024c 0.48% T32 course.kcl + +0x0c 0x013 0x026c 4.79% T24 course.kcl + +0x0c 0x016 0x02cc 0.19% T24 course.kcl + +0x0c 0x018 0x030c 1.29% T32 course.kcl +0x0c 0x018 0x030c 5.61% T34 course.kcl +0x0c 0x018 0x030c 11.75% T43 course.kcl + +0x0c 0x019 0x032c 0.73% T32 course.kcl +0x0c 0x019 0x032c 2.40% T24 course.kcl + +0x0c 0x01a 0x034c 2.82% T32 course.kcl +0x0c 0x01a 0x034c 3.26% T14 course.kcl + +0x0c 0x01b 0x036c 0.28% T24 course.kcl +0x0c 0x01b 0x036c 2.15% T32 course.kcl + +0x0c 0x01e 0x03cc 0.66% T24 course.kcl + +0x0c 0x020 0x040c 0.54% T43 course.kcl +0x0c 0x020 0x040c 2.92% T34 course.kcl + +0x0c 0x023 0x046c 0.14% T43 course.kcl + +0x0c 0x400 0x800c 0.10% T64 course.kcl +0x0c 0x400 0x800c 0.22% T43 course.kcl +0x0c 0x400 0x800c 0.31% T42 course.kcl +0x0c 0x400 0x800c 0.36% T51 course.kcl +0x0c 0x400 0x800c 0.75% T54 course.kcl +0x0c 0x400 0x800c 0.82% T34 course.kcl +0x0c 0x400 0x800c 0.91% T83 course.kcl +0x0c 0x400 0x800c 1.31% T34 VolcanoPiece1.kcl +0x0c 0x400 0x800c 1.54% T82 course.kcl +0x0c 0x400 0x800c 1.68% T63 course.kcl +0x0c 0x400 0x800c 2.61% T61 course.kcl +0x0c 0x400 0x800c 3.14% T84 course.kcl +0x0c 0x400 0x800c 14.81% T72 course.kcl +0x0c 0x400 0x800c 69.57% T34 VolcanoRock1.kcl + +0x0c 0x401 0x802c 0.40% T24 course.kcl +0x0c 0x401 0x802c 0.51% T32 course.kcl +0x0c 0x401 0x802c 0.64% T14 course.kcl +0x0c 0x401 0x802c 0.85% T43 course.kcl +0x0c 0x401 0x802c 1.02% T41 course.kcl +0x0c 0x401 0x802c 1.79% T83 course.kcl +0x0c 0x401 0x802c 2.06% T71 course.kcl +0x0c 0x401 0x802c 2.75% T34 VolcanoPiece3.kcl +0x0c 0x401 0x802c 2.75% T42 course.kcl +0x0c 0x401 0x802c 2.98% T33 course.kcl +0x0c 0x401 0x802c 4.02% T34 VolcanoPiece18.kcl +0x0c 0x401 0x802c 4.20% T34 VolcanoPiece14.kcl +0x0c 0x401 0x802c 4.32% T73 course.kcl +0x0c 0x401 0x802c 4.49% T13 course.kcl +0x0c 0x401 0x802c 4.76% T34 VolcanoPiece0.kcl +0x0c 0x401 0x802c 5.23% T34 VolcanoPiece1.kcl +0x0c 0x401 0x802c 5.65% T34 VolcanoPiece2.kcl +0x0c 0x401 0x802c 6.72% T34 VolcanoPiece13.kcl +0x0c 0x401 0x802c 6.86% T34 VolcanoPiece16.kcl +0x0c 0x401 0x802c 8.66% T52 course.kcl +0x0c 0x401 0x802c 9.25% T34 VolcanoPiece11.kcl +0x0c 0x401 0x802c 10.76% T34 course.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece0c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece11c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece13c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece14c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece16c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece18c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece1c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece2c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece3c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece4c.kcl +0x0c 0x401 0x802c 100.00% T34 VolcanoPiece9c.kcl + +0x0c 0x402 0x804c 0.33% T32 course.kcl +0x0c 0x402 0x804c 0.68% T14 course.kcl +0x0c 0x402 0x804c 2.77% T42 course.kcl +0x0c 0x402 0x804c 13.33% T14 Crane.kcl + +0x0c 0x403 0x806c 0.17% T43 course.kcl +0x0c 0x403 0x806c 0.39% T83 course.kcl +0x0c 0x403 0x806c 0.42% T24 course.kcl +0x0c 0x403 0x806c 0.76% T13 course.kcl +0x0c 0x403 0x806c 1.38% T33 course.kcl +0x0c 0x403 0x806c 2.11% T52 course.kcl +0x0c 0x403 0x806c 2.34% T63 course.kcl +0x0c 0x403 0x806c 41.21% T53 course.kcl +0x0c 0x403 0x806c 41.67% T63 TownBridgeDS.kcl +0x0c 0x403 0x806c 41.67% T63 TownBridgeDS2.kcl +0x0c 0x403 0x806c 41.67% T63 TownBridgeDS3.kcl + +0x0c 0x404 0x808c 3.65% T23 course.kcl + +0x0c 0x408 0x810c 0.25% T43 course.kcl +0x0c 0x408 0x810c 1.60% T34 course.kcl +0x0c 0x408 0x810c 28.57% T34 VolcanoPiece7.kcl +0x0c 0x408 0x810c 29.85% T34 VolcanoPiece5.kcl +0x0c 0x408 0x810c 51.61% T34 VolcanoPiece8.kcl +0x0c 0x408 0x810c 69.57% T34 VolcanoRock2.kcl +0x0c 0x408 0x810c 100.00% T34 VolcanoPiece5c.kcl +0x0c 0x408 0x810c 100.00% T34 VolcanoPiece7c.kcl +0x0c 0x408 0x810c 100.00% T34 VolcanoPiece8c.kcl + +0x0c 0x409 0x812c 0.52% T13 course.kcl +0x0c 0x409 0x812c 0.88% T32 course.kcl +0x0c 0x409 0x812c 1.60% T41 course.kcl + +0x0c 0x40a 0x814c 0.60% T14 course.kcl +0x0c 0x40a 0x814c 5.30% T72 course.kcl + +0x0c 0x40b 0x816c 0.51% T33 course.kcl + +0x0c 0x411 0x822c 0.46% T24 course.kcl + +0x0c 0x413 0x826c 4.18% T24 course.kcl +0x0c 0x413 0x826c 6.74% T84 course.kcl + +0x0c 0x418 0x830c 0.25% T34 course.kcl +0x0c 0x418 0x830c 1.51% T43 course.kcl + +0x0c 0x419 0x832c 0.39% T32 course.kcl + +0x0c 0x41b 0x836c 0.42% T33 course.kcl + +0x0c 0x420 0x840c 0.18% T34 course.kcl +0x0c 0x420 0x840c 1.14% T43 course.kcl + +0x0c 0x423 0x846c 0.35% T43 course.kcl + +---------------------------------------------------- +*** Invisible Wall *** +type variant flag percent track + subfile +---------------------------------------------------- +0x0d 0x000 0x000d 0.60% T84 course.kcl +0x0d 0x000 0x000d 1.22% T43 course.kcl +0x0d 0x000 0x000d 1.32% T34 course.kcl +0x0d 0x000 0x000d 2.02% T33 course.kcl +0x0d 0x000 0x000d 2.33% T24 course.kcl +0x0d 0x000 0x000d 2.96% T32 course.kcl +0x0d 0x000 0x000d 3.45% T41 course.kcl +0x0d 0x000 0x000d 3.55% T14 course.kcl +0x0d 0x000 0x000d 3.60% T71 course.kcl +0x0d 0x000 0x000d 3.71% T11 course.kcl +0x0d 0x000 0x000d 3.84% T44 course.kcl +0x0d 0x000 0x000d 4.29% T13 course.kcl +0x0d 0x000 0x000d 5.21% T54 course.kcl +0x0d 0x000 0x000d 5.40% T63 course.kcl +0x0d 0x000 0x000d 5.70% T51 course.kcl +0x0d 0x000 0x000d 6.37% T22 course.kcl +0x0d 0x000 0x000d 7.90% T21 course.kcl +0x0d 0x000 0x000d 8.44% T74 course.kcl +0x0d 0x000 0x000d 9.25% T23 course.kcl +0x0d 0x000 0x000d 9.51% T31 course.kcl +0x0d 0x000 0x000d 10.53% T52 course.kcl +0x0d 0x000 0x000d 11.03% T73 course.kcl +0x0d 0x000 0x000d 11.40% T42 course.kcl +0x0d 0x000 0x000d 11.79% T12 course.kcl +0x0d 0x000 0x000d 14.46% T81 course.kcl +0x0d 0x000 0x000d 15.10% T64 course.kcl +0x0d 0x000 0x000d 15.14% T83 course.kcl +0x0d 0x000 0x000d 16.67% T14 bulldozer_left.kcl +0x0d 0x000 0x000d 16.67% T14 bulldozer_right.kcl +0x0d 0x000 0x000d 24.53% T82 course.kcl + +0x0d 0x008 0x010d 0.08% T31 course.kcl +0x0d 0x008 0x010d 0.32% T21 course.kcl +0x0d 0x008 0x010d 0.71% T24 course.kcl +0x0d 0x008 0x010d 0.75% T74 course.kcl +0x0d 0x008 0x010d 1.13% T32 course.kcl +0x0d 0x008 0x010d 2.10% T43 course.kcl +0x0d 0x008 0x010d 2.31% T33 course.kcl +0x0d 0x008 0x010d 2.51% T13 course.kcl +0x0d 0x008 0x010d 2.85% T22 course.kcl +0x0d 0x008 0x010d 3.30% T14 course.kcl + +0x0d 0x010 0x020d 0.33% T43 course.kcl +0x0d 0x010 0x020d 0.70% T22 course.kcl +0x0d 0x010 0x020d 2.06% T31 course.kcl +0x0d 0x010 0x020d 3.53% T24 course.kcl + +0x0d 0x018 0x030d 0.21% T43 course.kcl +0x0d 0x018 0x030d 0.63% T32 course.kcl +0x0d 0x018 0x030d 0.75% T34 course.kcl +0x0d 0x018 0x030d 1.01% T24 course.kcl + +0x0d 0x020 0x040d 0.61% T34 course.kcl +0x0d 0x020 0x040d 2.11% T43 course.kcl + +---------------------------------------------------- +*** Obstacle Wall *** +type variant flag percent track + subfile +---------------------------------------------------- +0x0e 0x000 0x000e 0.21% T54 course.kcl +0x0e 0x000 0x000e 0.26% T82 course.kcl +0x0e 0x000 0x000e 0.98% T83 course.kcl +0x0e 0x000 0x000e 1.96% T43 course.kcl +0x0e 0x000 0x000e 2.03% T31 course.kcl +0x0e 0x000 0x000e 2.79% T64 course.kcl +0x0e 0x000 0x000e 3.88% T51 course.kcl +0x0e 0x000 0x000e 5.71% T22 course.kcl +0x0e 0x000 0x000e 8.35% T21 course.kcl +0x0e 0x000 0x000e 8.80% T74 course.kcl +0x0e 0x000 0x000e 24.47% T63 course.kcl + +0x0e 0x001 0x002e 0.91% T21 course.kcl +0x0e 0x001 0x002e 1.30% T64 course.kcl +0x0e 0x001 0x002e 3.67% T23 course.kcl + +0x0e 0x002 0x004e 0.23% T71 course.kcl +0x0e 0x002 0x004e 0.36% T51 course.kcl +0x0e 0x002 0x004e 0.62% T73 course.kcl +0x0e 0x002 0x004e 1.73% T31 course.kcl +0x0e 0x002 0x004e 2.20% T21 course.kcl +0x0e 0x002 0x004e 5.00% T14 course.kcl +0x0e 0x002 0x004e 5.11% T82 course.kcl + +0x0e 0x003 0x006e 0.10% T33 course.kcl +0x0e 0x003 0x006e 0.20% T83 course.kcl +0x0e 0x003 0x006e 0.96% T22 course.kcl +0x0e 0x003 0x006e 2.19% T12 course.kcl +0x0e 0x003 0x006e 15.46% T52 course.kcl + +0x0e 0x005 0x00ae 0.11% T13 course.kcl +0x0e 0x005 0x00ae 2.17% T74 course.kcl +0x0e 0x005 0x00ae 3.40% T82 course.kcl + +0x0e 0x008 0x010e 0.69% T21 course.kcl +0x0e 0x008 0x010e 0.78% T74 course.kcl +0x0e 0x008 0x010e 0.86% T22 course.kcl +0x0e 0x008 0x010e 5.11% T43 course.kcl + +0x0e 0x00a 0x014e 8.98% T14 course.kcl + +0x0e 0x00b 0x016e 0.41% T33 course.kcl + +0x0e 0x010 0x020e 0.08% T22 course.kcl +0x0e 0x010 0x020e 0.19% T43 course.kcl + +0x0e 0x012 0x024e 2.03% T31 course.kcl + +0x0e 0x018 0x030e 0.56% T43 course.kcl + +0x0e 0x01a 0x034e 0.06% T32 course.kcl + +---------------------------------------------------- +*** Wall 3 *** +type variant flag percent track + subfile +---------------------------------------------------- +0x0f 0x000 0x000f 1.69% T32 course.kcl +0x0f 0x000 0x000f 16.59% T31 course.kcl +0x0f 0x000 0x000f 17.10% T41 course.kcl +0x0f 0x000 0x000f 22.64% T22 course.kcl +0x0f 0x000 0x000f 72.73% T41 dc_pillar_base.kcl + +0x0f 0x001 0x002f 0.59% T33 course.kcl + +0x0f 0x002 0x004f 2.33% T22 course.kcl +0x0f 0x002 0x004f 4.08% T31 course.kcl +0x0f 0x002 0x004f 5.44% T24 course.kcl + +0x0f 0x003 0x006f 5.63% T33 course.kcl + +0x0f 0x006 0x00cf 0.77% T73 course.kcl + +0x0f 0x008 0x010f 1.17% T32 course.kcl +0x0f 0x008 0x010f 4.28% T31 course.kcl +0x0f 0x008 0x010f 7.05% T41 course.kcl +0x0f 0x008 0x010f 8.42% T22 course.kcl + +0x0f 0x009 0x012f 10.71% T33 course.kcl + +0x0f 0x00a 0x014f 0.50% T22 course.kcl + +0x0f 0x00b 0x016f 34.65% T33 course.kcl + +0x0f 0x010 0x020f 0.59% T22 course.kcl +0x0f 0x010 0x020f 8.88% T31 course.kcl + +0x0f 0x012 0x024f 0.69% T22 course.kcl +0x0f 0x012 0x024f 9.60% T24 course.kcl + +0x0f 0x018 0x030f 0.40% T32 course.kcl + +0x0f 0x01a 0x034f 0.05% T24 course.kcl + +0x0f 0x400 0x800f 0.28% T22 course.kcl +0x0f 0x400 0x800f 1.60% T31 course.kcl + +0x0f 0x402 0x804f 1.08% T24 course.kcl +0x0f 0x402 0x804f 1.21% T22 course.kcl + +0x0f 0x403 0x806f 0.02% T22 course.kcl + +0x0f 0x404 0x808f 0.01% T22 course.kcl + +0x0f 0x407 0x80ef 0.02% T22 course.kcl + +0x0f 0x408 0x810f 0.07% T22 course.kcl +0x0f 0x408 0x810f 0.31% T31 course.kcl + +0x0f 0x40a 0x814f 0.04% T22 course.kcl + +0x0f 0x412 0x824f 1.01% T24 course.kcl + +0x0f 0x41a 0x834f 0.02% T24 course.kcl + +---------------------------------------------------- +*** Fall Boundary *** +type variant flag percent track + subfile +---------------------------------------------------- +0x10 0x000 0x0010 0.09% T22 course.kcl +0x10 0x000 0x0010 0.10% T64 course.kcl +0x10 0x000 0x0010 0.12% T24 course.kcl +0x10 0x000 0x0010 0.12% T73 course.kcl +0x10 0x000 0x0010 0.14% T52 course.kcl +0x10 0x000 0x0010 0.14% T53 course.kcl +0x10 0x000 0x0010 0.27% T32 course.kcl +0x10 0x000 0x0010 0.32% T23 course.kcl +0x10 0x000 0x0010 0.58% T33 course.kcl +0x10 0x000 0x0010 0.67% T42 course.kcl +0x10 0x000 0x0010 1.04% T31 course.kcl +0x10 0x000 0x0010 1.66% T83 course.kcl +0x10 0x000 0x0010 3.66% T13 course.kcl + +0x10 0x001 0x0030 0.03% T41 course.kcl +0x10 0x001 0x0030 0.09% T63 course.kcl +0x10 0x001 0x0030 0.16% T32 course.kcl +0x10 0x001 0x0030 0.19% T82 course.kcl +0x10 0x001 0x0030 0.25% T14 course.kcl +0x10 0x001 0x0030 0.40% T73 course.kcl +0x10 0x001 0x0030 0.45% T62 course.kcl +0x10 0x001 0x0030 0.98% T71 course.kcl + +0x10 0x002 0x0050 0.04% T34 course.kcl +0x10 0x002 0x0050 0.04% T43 course.kcl +0x10 0x002 0x0050 0.15% T84 course.kcl + +0x10 0x003 0x0070 1.48% T61 course.kcl + +0x10 0x005 0x00b0 7.06% T44 course.kcl + +0x10 0x007 0x00f0 0.50% T41 course.kcl +0x10 0x007 0x00f0 0.56% T54 course.kcl +0x10 0x007 0x00f0 0.61% T82 course.kcl +0x10 0x007 0x00f0 2.15% T71 course.kcl + +0x10 0x008 0x0110 0.06% T41 course.kcl +0x10 0x008 0x0110 0.23% T14 course.kcl + +0x10 0x009 0x0130 0.04% T14 course.kcl + +0x10 0x00a 0x0150 0.04% T43 course.kcl +0x10 0x00a 0x0150 0.11% T72 course.kcl + +0x10 0x00c 0x0190 0.39% T34 course.kcl + +0x10 0x010 0x0210 0.25% T24 course.kcl + +0x10 0x011 0x0230 0.64% T33 course.kcl + +0x10 0x012 0x0250 0.02% T43 course.kcl +0x10 0x012 0x0250 0.19% T84 course.kcl + +0x10 0x019 0x0330 0.14% T33 course.kcl + +0x10 0x01a 0x0350 0.06% T43 course.kcl + +0x10 0x022 0x0450 0.08% T43 course.kcl + +---------------------------------------------------- +*** Cannon Activator *** +type variant flag percent track + subfile +---------------------------------------------------- +0x11 0x000 0x0011 0.02% T33 course.kcl +0x11 0x000 0x0011 0.09% T23 course.kcl +0x11 0x000 0x0011 0.42% T83 course.kcl + +0x11 0x001 0x0031 0.04% T73 course.kcl + +0x11 0x002 0x0051 0.10% T44 course.kcl + +---------------------------------------------------- +*** Unknown *** +type variant flag percent track + subfile +---------------------------------------------------- +0x12 0x000 0x0012 0.09% T33 course.kcl +0x12 0x000 0x0012 0.10% T63 course.kcl +0x12 0x000 0x0012 0.43% T44 course.kcl +0x12 0x000 0x0012 0.48% T22 course.kcl +0x12 0x000 0x0012 0.48% T34 course.kcl + +0x12 0x008 0x0112 0.02% T33 course.kcl + +0x12 0x018 0x0312 0.39% T34 course.kcl + +---------------------------------------------------- +*** Half-pipe Ramp *** +type variant flag percent track + subfile +---------------------------------------------------- +0x13 0x000 0x0013 0.12% T24 course.kcl +0x13 0x000 0x0013 0.16% T32 course.kcl +0x13 0x000 0x0013 0.25% T33 course.kcl +0x13 0x000 0x0013 0.60% T41 course.kcl +0x13 0x000 0x0013 0.79% T64 course.kcl +0x13 0x000 0x0013 0.88% T83 course.kcl +0x13 0x000 0x0013 1.25% T44 course.kcl +0x13 0x000 0x0013 5.58% T23 course.kcl + +0x13 0x008 0x0113 0.71% T41 course.kcl + +0x13 0x021 0x0433 0.21% T43 course.kcl + +---------------------------------------------------- +*** Wall *** +type variant flag percent track + subfile +---------------------------------------------------- +0x14 0x007 0x00f4 0.48% T31 course.kcl + +0x14 0x407 0x80f4 0.10% T32 course.kcl +0x14 0x407 0x80f4 2.70% T24 course.kcl +0x14 0x407 0x80f4 11.39% T62 course.kcl + +0x14 0x40f 0x81f4 0.16% T32 course.kcl + +0x14 0x417 0x82f4 1.65% T84 course.kcl +0x14 0x417 0x82f4 5.36% T24 course.kcl + +0x14 0x41b 0x8374 0.04% T43 course.kcl + +0x14 0x41f 0x83f4 0.04% T32 course.kcl + +0x14 0x423 0x8474 0.19% T43 course.kcl + +---------------------------------------------------- +*** Moving Road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x15 0x000 0x0015 0.17% T14 course.kcl +0x15 0x000 0x0015 100.00% T22 escalator.kcl + +0x15 0x001 0x0035 0.04% T14 course.kcl + +0x15 0x002 0x0055 0.08% T14 course.kcl + +0x15 0x003 0x0075 0.08% T14 course.kcl + +0x15 0x00c 0x0195 0.25% T14 course.kcl + +0x15 0x00d 0x01b5 0.25% T14 course.kcl + +0x15 0x014 0x0295 0.54% T14 course.kcl + +0x15 0x015 0x02b5 0.54% T14 course.kcl + +---------------------------------------------------- +*** Sticky Road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x16 0x000 0x0016 0.08% T43 course.kcl +0x16 0x000 0x0016 0.23% T33 course.kcl +0x16 0x000 0x0016 4.74% T24 course.kcl +0x16 0x000 0x0016 44.44% T21 Mdush.kcl + +0x16 0x001 0x0036 0.12% T22 course.kcl +0x16 0x001 0x0036 0.51% T21 course.kcl +0x16 0x001 0x0036 0.81% T13 course.kcl +0x16 0x001 0x0036 0.83% T43 course.kcl + +0x16 0x003 0x0076 0.11% T52 course.kcl +0x16 0x003 0x0076 0.57% T64 course.kcl +0x16 0x003 0x0076 0.95% T33 course.kcl + +0x16 0x004 0x0096 0.09% T12 course.kcl +0x16 0x004 0x0096 0.99% T52 course.kcl +0x16 0x004 0x0096 1.88% T23 course.kcl +0x16 0x004 0x0096 7.75% T33 course.kcl +0x16 0x004 0x0096 12.50% T63 TownBridgeDS.kcl +0x16 0x004 0x0096 12.50% T63 TownBridgeDS2.kcl +0x16 0x004 0x0096 12.50% T63 TownBridgeDS3.kcl + +0x16 0x005 0x00b6 41.76% T44 course.kcl + +0x16 0x006 0x00d6 0.06% T42 course.kcl +0x16 0x006 0x00d6 1.25% T31 course.kcl + +0x16 0x007 0x00f6 0.08% T14 course.kcl + +0x16 0x008 0x0116 0.17% T33 course.kcl +0x16 0x008 0x0116 0.59% T24 course.kcl + +0x16 0x009 0x0136 0.18% T13 course.kcl +0x16 0x009 0x0136 1.80% T84 course.kcl +0x16 0x009 0x0136 2.40% T43 course.kcl + +0x16 0x00a 0x0156 0.90% T84 course.kcl + +0x16 0x00b 0x0176 0.53% T64 course.kcl +0x16 0x00b 0x0176 2.71% T33 course.kcl + +0x16 0x00c 0x0196 3.59% T33 course.kcl + +0x16 0x00d 0x01b6 0.60% T44 course.kcl + +0x16 0x00e 0x01d6 0.46% T31 course.kcl + +0x16 0x010 0x0216 8.45% T24 course.kcl + +0x16 0x011 0x0236 0.52% T43 course.kcl + +0x16 0x014 0x0296 0.59% T33 course.kcl + +0x16 0x018 0x0316 0.29% T43 course.kcl +0x16 0x018 0x0316 0.75% T24 course.kcl + +0x16 0x019 0x0336 0.31% T43 course.kcl + +0x16 0x020 0x0416 0.34% T24 course.kcl + +0x16 0x021 0x0436 0.04% T43 course.kcl + +---------------------------------------------------- +*** Road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x17 0x000 0x0017 0.39% T22 course.kcl + +0x17 0x001 0x0037 0.41% T84 course.kcl + +0x17 0x002 0x0057 16.36% T83 course.kcl + +0x17 0x003 0x0077 0.72% T11 course.kcl +0x17 0x003 0x0077 68.18% T13 kinoko_g.kcl + +0x17 0x005 0x00b7 0.34% T22 course.kcl + +0x17 0x007 0x00f7 12.73% T44 course.kcl + +0x17 0x009 0x0137 1.61% T84 course.kcl + +0x17 0x00a 0x0157 1.35% T83 course.kcl + +0x17 0x00b 0x0177 68.18% T13 kinoko_d_g.kcl + +0x17 0x011 0x0237 0.11% T84 course.kcl + +0x17 0x015 0x02b7 7.64% T32 course.kcl + +0x17 0x019 0x0337 0.15% T84 course.kcl + +0x17 0x0c4 0x1897 0.81% T33 course.kcl + +0x17 0x107 0x20f7 1.60% T44 course.kcl + +0x17 0x202 0x4057 1.14% T83 course.kcl + +0x17 0x215 0x42b7 8.54% T32 course.kcl + +---------------------------------------------------- +*** Sound Trigger *** +type variant flag percent track + subfile +---------------------------------------------------- +0x18 0x000 0x0018 0.05% T24 course.kcl +0x18 0x000 0x0018 0.06% T41 course.kcl +0x18 0x000 0x0018 0.06% T43 course.kcl +0x18 0x000 0x0018 0.06% T74 course.kcl +0x18 0x000 0x0018 0.07% T13 course.kcl +0x18 0x000 0x0018 0.07% T34 course.kcl +0x18 0x000 0x0018 0.07% T63 course.kcl +0x18 0x000 0x0018 0.07% T83 course.kcl +0x18 0x000 0x0018 0.07% T84 course.kcl +0x18 0x000 0x0018 0.08% T14 course.kcl +0x18 0x000 0x0018 0.08% T33 course.kcl +0x18 0x000 0x0018 0.08% T42 course.kcl +0x18 0x000 0x0018 0.10% T11 course.kcl +0x18 0x000 0x0018 0.11% T22 course.kcl +0x18 0x000 0x0018 0.11% T61 course.kcl +0x18 0x000 0x0018 0.13% T32 course.kcl +0x18 0x000 0x0018 0.17% T31 course.kcl +0x18 0x000 0x0018 0.17% T54 course.kcl +0x18 0x000 0x0018 0.18% T23 course.kcl +0x18 0x000 0x0018 0.23% T73 course.kcl +0x18 0x000 0x0018 0.24% T21 course.kcl +0x18 0x000 0x0018 0.33% T44 course.kcl + +0x18 0x001 0x0038 0.04% T33 course.kcl +0x18 0x001 0x0038 0.04% T43 course.kcl +0x18 0x001 0x0038 0.06% T41 course.kcl +0x18 0x001 0x0038 0.07% T13 course.kcl +0x18 0x001 0x0038 0.07% T22 course.kcl +0x18 0x001 0x0038 0.07% T34 course.kcl +0x18 0x001 0x0038 0.08% T14 course.kcl +0x18 0x001 0x0038 0.08% T32 course.kcl +0x18 0x001 0x0038 0.09% T24 course.kcl +0x18 0x001 0x0038 0.13% T74 course.kcl +0x18 0x001 0x0038 0.13% T83 course.kcl +0x18 0x001 0x0038 0.15% T42 course.kcl +0x18 0x001 0x0038 0.15% T84 course.kcl +0x18 0x001 0x0038 0.16% T21 course.kcl +0x18 0x001 0x0038 0.17% T54 course.kcl +0x18 0x001 0x0038 0.18% T23 course.kcl +0x18 0x001 0x0038 0.19% T11 course.kcl +0x18 0x001 0x0038 0.21% T61 course.kcl +0x18 0x001 0x0038 0.25% T44 course.kcl +0x18 0x001 0x0038 0.28% T63 course.kcl +0x18 0x001 0x0038 0.38% T73 course.kcl +0x18 0x001 0x0038 0.39% T31 course.kcl + +0x18 0x002 0x0058 0.04% T32 course.kcl +0x18 0x002 0x0058 0.04% T33 course.kcl +0x18 0x002 0x0058 0.04% T43 course.kcl +0x18 0x002 0x0058 0.04% T84 course.kcl +0x18 0x002 0x0058 0.06% T74 course.kcl +0x18 0x002 0x0058 0.07% T22 course.kcl +0x18 0x002 0x0058 0.07% T34 course.kcl +0x18 0x002 0x0058 0.07% T83 course.kcl +0x18 0x002 0x0058 0.08% T14 course.kcl +0x18 0x002 0x0058 0.08% T21 course.kcl +0x18 0x002 0x0058 0.09% T54 course.kcl +0x18 0x002 0x0058 0.11% T61 course.kcl +0x18 0x002 0x0058 0.12% T24 course.kcl +0x18 0x002 0x0058 0.13% T41 course.kcl +0x18 0x002 0x0058 0.14% T13 course.kcl +0x18 0x002 0x0058 0.15% T42 course.kcl +0x18 0x002 0x0058 0.15% T73 course.kcl +0x18 0x002 0x0058 0.18% T23 course.kcl +0x18 0x002 0x0058 0.19% T11 course.kcl +0x18 0x002 0x0058 0.21% T63 course.kcl +0x18 0x002 0x0058 0.22% T31 course.kcl +0x18 0x002 0x0058 0.25% T44 course.kcl + +0x18 0x003 0x0078 0.04% T22 course.kcl +0x18 0x003 0x0078 0.04% T33 course.kcl +0x18 0x003 0x0078 0.04% T43 course.kcl +0x18 0x003 0x0078 0.06% T41 course.kcl +0x18 0x003 0x0078 0.07% T13 course.kcl +0x18 0x003 0x0078 0.07% T24 course.kcl +0x18 0x003 0x0078 0.07% T34 course.kcl +0x18 0x003 0x0078 0.07% T63 course.kcl +0x18 0x003 0x0078 0.08% T14 course.kcl +0x18 0x003 0x0078 0.08% T42 course.kcl +0x18 0x003 0x0078 0.08% T73 course.kcl +0x18 0x003 0x0078 0.09% T23 course.kcl +0x18 0x003 0x0078 0.09% T54 course.kcl +0x18 0x003 0x0078 0.10% T11 course.kcl +0x18 0x003 0x0078 0.10% T32 course.kcl +0x18 0x003 0x0078 0.18% T44 course.kcl +0x18 0x003 0x0078 0.22% T84 course.kcl + +0x18 0x004 0x0098 0.04% T22 course.kcl +0x18 0x004 0x0098 0.04% T32 course.kcl +0x18 0x004 0x0098 0.04% T33 course.kcl +0x18 0x004 0x0098 0.06% T43 course.kcl +0x18 0x004 0x0098 0.07% T84 course.kcl +0x18 0x004 0x0098 0.08% T14 course.kcl +0x18 0x004 0x0098 0.09% T23 course.kcl +0x18 0x004 0x0098 0.14% T63 course.kcl +0x18 0x004 0x0098 0.15% T42 course.kcl +0x18 0x004 0x0098 0.18% T44 course.kcl + +0x18 0x005 0x00b8 0.04% T14 course.kcl +0x18 0x005 0x00b8 0.04% T22 course.kcl +0x18 0x005 0x00b8 0.04% T32 course.kcl +0x18 0x005 0x00b8 0.06% T43 course.kcl +0x18 0x005 0x00b8 0.08% T42 course.kcl +0x18 0x005 0x00b8 0.14% T63 course.kcl +0x18 0x005 0x00b8 0.15% T84 course.kcl +0x18 0x005 0x00b8 0.18% T44 course.kcl + +0x18 0x006 0x00d8 0.03% T32 course.kcl +0x18 0x006 0x00d8 0.04% T43 course.kcl +0x18 0x006 0x00d8 0.05% T44 course.kcl +0x18 0x006 0x00d8 0.07% T63 course.kcl +0x18 0x006 0x00d8 0.07% T84 course.kcl +0x18 0x006 0x00d8 0.08% T14 course.kcl + +0x18 0x007 0x00f8 0.02% T43 course.kcl +0x18 0x007 0x00f8 0.07% T63 course.kcl +0x18 0x007 0x00f8 0.12% T14 course.kcl + +---------------------------------------------------- +*** Unknown *** +type variant flag percent track + subfile +---------------------------------------------------- +0x19 0x400 0x8019 0.26% T24 course.kcl +0x19 0x400 0x8019 15.39% T44 course.kcl + +0x19 0x410 0x8219 0.14% T24 course.kcl + +---------------------------------------------------- +*** Unknown *** +type variant flag percent track + subfile +---------------------------------------------------- +0x1a 0x000 0x001a 0.04% T14 course.kcl +0x1a 0x000 0x001a 0.07% T13 course.kcl +0x1a 0x000 0x001a 0.08% T21 course.kcl + +0x1a 0x001 0x003a 0.02% T24 course.kcl +0x1a 0x001 0x003a 0.06% T41 course.kcl +0x1a 0x001 0x003a 0.18% T34 course.kcl +0x1a 0x001 0x003a 0.43% T54 course.kcl +0x1a 0x001 0x003a 0.63% T13 course.kcl +0x1a 0x001 0x003a 0.98% T71 course.kcl + +0x1a 0x002 0x005a 0.21% T32 course.kcl + +0x1a 0x004 0x009a 0.09% T32 course.kcl +0x1a 0x004 0x009a 0.48% T23 course.kcl +0x1a 0x004 0x009a 0.93% T44 course.kcl + +0x1a 0x009 0x013a 0.06% T33 course.kcl +0x1a 0x009 0x013a 0.06% T41 course.kcl +0x1a 0x009 0x013a 1.84% T11 course.kcl + +0x1a 0x00a 0x015a 0.07% T52 course.kcl +0x1a 0x00a 0x015a 0.35% T73 course.kcl + +0x1a 0x011 0x023a 0.02% T32 course.kcl + +0x1a 0x012 0x025a 0.36% T33 course.kcl + +0x1a 0x019 0x033a 0.02% T32 course.kcl + +0x1a 0x01c 0x039a 1.78% T43 course.kcl + +0x1a 0x022 0x045a 0.40% T42 course.kcl + +0x1a 0x02a 0x055a 2.36% T42 course.kcl + +---------------------------------------------------- +*** Unknown *** +type variant flag percent track + subfile +---------------------------------------------------- +0x1b 0x008 0x011b 0.08% T14 course.kcl + +---------------------------------------------------- +*** Unknown *** +type variant flag percent track + subfile +---------------------------------------------------- +0x1c 0x020 0x041c 0.50% T43 course.kcl + +---------------------------------------------------- +*** Road *** +type variant flag percent track + subfile +---------------------------------------------------- +0x1d 0x008 0x011d 0.02% T33 course.kcl + +0x1d 0x018 0x031d 0.02% T33 course.kcl + +---------------------------------------------------- +*** Special Walls *** +type variant flag percent track + subfile +---------------------------------------------------- +0x1e 0x000 0x001e 4.84% T41 course.kcl + +0x1e 0x003 0x007e 0.06% T32 course.kcl +0x1e 0x003 0x007e 0.07% T24 course.kcl + +0x1e 0x004 0x009e 10.10% T44 course.kcl + +0x1e 0x005 0x00be 4.33% T13 course.kcl +0x1e 0x005 0x00be 9.09% T13 kinoko_g.kcl +0x1e 0x005 0x00be 9.09% T13 kinoko_r.kcl + +0x1e 0x006 0x00de 1.12% T11 course.kcl + +0x1e 0x009 0x013e 0.39% T34 course.kcl + +0x1e 0x00d 0x01be 0.65% T13 course.kcl +0x1e 0x00d 0x01be 9.09% T13 kinoko_d_g.kcl +0x1e 0x00d 0x01be 9.09% T13 kinoko_d_r.kcl + +0x1e 0x016 0x02de 0.96% T32 course.kcl + +0x1e 0x403 0x807e 2.60% T22 course.kcl + +0x1e 0x405 0x80be 22.73% T13 kinoko_g.kcl +0x1e 0x405 0x80be 22.73% T13 kinoko_r.kcl + +0x1e 0x40b 0x817e 0.04% T32 course.kcl + +0x1e 0x40d 0x81be 22.73% T13 kinoko_d_g.kcl +0x1e 0x40d 0x81be 22.73% T13 kinoko_d_r.kcl + +0x1e 0x41b 0x837e 0.02% T32 course.kcl + +---------------------------------------------------- +*** Wall *** +type variant flag percent track + subfile +---------------------------------------------------- +0x1f 0x000 0x001f 0.04% T32 course.kcl +0x1f 0x000 0x001f 0.23% T44 course.kcl + +0x1f 0x018 0x031f 0.11% T32 course.kcl + diff --git a/project/system.c b/project/system.c new file mode 100644 index 0000000..01c5b8f --- /dev/null +++ b/project/system.c @@ -0,0 +1,45 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * http://szs.wiimm.de/ * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit http://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2012 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#include "dclib/dclib-system.h" + +int main ( int argc, char ** argv ) +{ + dclibPrintSystem(stdout); + return 0; +} + diff --git a/project/templates/DOCUMENTATION.txt b/project/templates/DOCUMENTATION.txt new file mode 100644 index 0000000..941dd38 --- /dev/null +++ b/project/templates/DOCUMENTATION.txt @@ -0,0 +1,35 @@ + +@@MODULE(head.txt)@@ + + +******************************************************************************* +******* Motivation ********* +******************************************************************************* + +The motives for writing a new tool are simple: + +I want to automate the creation of SZS files. I need this for Custom Mario +Kart Distributions. + +Visit @@URI-HOME@@ for more information. + + +******************************************************************************* +******* Features ********* +******************************************************************************* + +@@MODULE(features.txt)@@ + + +******************************************************************************* +******* HISTORY ******* +******************************************************************************* + +For the complete history see file 'HISTORY.txt'. The last changes: + +@@MODULE(last-history.txt)@@ + + +******************************************************************************* +******* END ******* +******************************************************************************* diff --git a/project/templates/HISTORY-v0.txt b/project/templates/HISTORY-v0.txt new file mode 100644 index 0000000..6bbc789 --- /dev/null +++ b/project/templates/HISTORY-v0.txt @@ -0,0 +1,784 @@ + +@@MODULE(head.txt)@@ + + +******************************************************************************* +******* HISTORY v0.* ******* +******************************************************************************* + +szs v0.01a.beta r2385 - 2011-03-31 + + - new Tool: wszst (Wiimms SZS Tool) + The following main commands are supported: + LIST, COMPRESS, DECOMPRESS, EXTRACT, CREATE. + + +szs v0.01a r2392 - 2011-04-01 + + - Bug fix for "wszst CREATE": Wrong calculation of parent directory link. + - New option: wszst CREATE --align: Overwrite the default aligment (32=0x20). + - wszst LIST: If --long is set three times debugging mode is enabled. + - wszst LIST: If listing a directory, then a virtual SZS file is created + (like CREATE do it) and listed. + - New command: wszst DIFF: Compare two sources (each SZS, U8 or directory) + on file level and report mismatches for each sub file. + + +szs v0.02a r2417 - 2011-04-04 + + - New option: --max-file-size: This options set the limit for input files. + The default value is 20 MiB. + - Option --dest: If the parameter is '-' the output is redirected to stdout. + - New tool: wbmgt: Wiimms BMG Tool + Commands: + - LIST : List a message file to proof the content. + - PATCH : Patch the entered text files. + - DECODE : Decode (and patch) the entered text files. + - ENCODE : Encode (and patch) the entered text files. + + +szs v0.03a r2437 - 2011-04-06 + + - wszst: Compression algorithm improved: The compression is now about 40%-50% + faster than before. + - wszst: New option --compr=level: define a compression level between 0-9. + Three clear text parameters accepted too: NONE=0, FAST=1 and BEST=9. + - wszst: New option --fast: This is a short cut for --compr=fast. Usually + "CREATE --fast" needs about 1/4 of time, but creates ~25% larger SZS files. + - New command: wszst CAT: Extract one or more sub files of SZS/U8 archives. + The output is written to stdout. Multiple outputs are concatenated. + - New command: wbmgt DIFF: Compare two BMG (raw or text) file on string level + and report mismatches for each string. + - wbmgt --patch: new mode: HIDE + - wbmgt is able to read BMG files directly from a SZS file. Just use a + combined path as 'my.szs/message/Common.bmg' + - new option: --ignore: Ignore non existing source files without warning. + + +szs v0.04a r2457 - 2011-04-09 + + - SZS compression improved: It uses now overlaps and save about 2% size. + With more complicated algorithm (that needs much more time) better + compression is possible -- but it's not worth it. + - wszst LIST --long: View the magic (first 4 bytes) of each file too. + - wszst can list, diff, (de-)compress and extract SZS.BRRES files. + - wbgmt: Bug fix: Wrong string end detection for the last message if + scanning Nintendos 0x1a (CTRL-Z, ASCII 'SUB') escape sequences. + - BGM/TEXT format: Allow \z{head,value} for Nintendos 0x1a escape sequence. + - Bug fix for wszst+wbmgt DIFF: Some files only in source #2 were not listed. + - New command: wszst NORMALIZE: resort and recompress an U8 archive. + - New option: wszst --norm: Force normalization while copying/converting. + - Many layout things. + + +szs v0.05a r2498 - 2011-04-15 + + - Code view & fine tuning (many little things...) + - wszst NORMALIZE: Bug fixed if source does not contain a "." directory. + - wszst CREATE+NORMALIZE: The files are ordered like Nintendo does it. + - New command: wszst SHA1: calculate and print the SHA1 checksum of + the decompressed data of all source files. + - wszst+wbmgt DIFF: If option --dest is set then all sources are compared + with the destination. + - New option: wszst --pdir (`point directory'): Decides if a base directory + named "." should be created. Modes are: REMOVE, AUTO, FORCE. + - New option: --preserve: If set, mtime and atime of the source(s) are used + to set the destination time stamps. + - wbmgt: If creating a text BMG file option --ho-header (or -H) suppresses + the output of the syntax information section. + - wbmgt: If creating a text BMG file the message IDs for tracks, battle + tracks and chat messages will be replaced by 'Txx', 'Uxx' and 'Mxx'. + This feature is disabled if option ---long is set. + + +szs v0.06a r2540 - 2011-04-27 + + - If extracting a SZS, U8 or BRRES archive a file named 'wszst-setup.txt' is + created. It contains some infos for a following creation. + - New option --ignore-setup: Ignore setup file 'wszst-setup.txt'. + - Option --pdir renamed to --pt-dir. --pdir is still allowed. + - wszst CREATE: Can now create a BRRES file as copy of a directory structure. + - wbmgt: If creating text BMG files, than unicode escapes "\z{801,hexnum}" + are written as \u{hexnum}. v0.05a can already scan such unicode escapes. + - wbmgt: new patch mode "print": create a new text using %s-escapes (like + print functions) to insert the old one. + - "wszst EXTRACT" can extract archives recursively and can decode BMG files + on the fly. This is controlled by the options --recurse, --decode and + --all. All needed information for recreation is stored in the setup file + 'wszst-setup.txt'. + - "wszst CREATE" creates a recursively directory structure created by EXTRACT + using 'wszst-setup.txt' as script. Files will be encoded again. + + +szs v0.07a r2584 - 2011-05-05 + + - wbmgt --patch mode PRINT: + - New escape %i inserts the message ID (MID, good for finding IDs) + - Inserting 'b-e' or 'e' between '%' and conversion character limits the + number of characters to copy. 'b' (begin) and 'e' (end) are numbers. + - Bug fix for wszst: The base directory was not always created if the U8 + archive has not point directory as base. + - Bug fix for wszst: If creating a BRRES file the programm runs sometimes + in a wrong assertion and terminates. + - Bug fix for wszst: Adjust all external offsets of BRRES sub files. + - wszst LIST: Can list files recursive if --recurse or --all is set. + - wszst: A string pool is added to extracted BRRES sub files. This string + pool is used for the creation of new BRRES files. + - "wszst CREATE file.brres" works now, but see also "Known bugs". + - New command: wszst DUMP: Dump the internal structure of BRRES files. The + main goal is to verify string relocation. + + - Known bugs: + + - If creating a BRRES the internal name of the sub file is not set to + the filename. This could be problematic if a sub file was renamed. + + +szs v0.08a r2617 - 2011-05-12 + + - Improved support for BRRES sub file versions (more string pointers). + - wszst EXTRACT --raw: Extract BRRES sub file in raw mode and do not adjust + string pointers and other offsets. + + - Known bugs: + + - If creating a BRRES the internal name of the sub file is not set to + the filename. This could be problematic if a sub file was renamed. + + +szs v0.09a r2636 - 2011-05-17 + + - New tool: wstrt: Wiimms StaticR Tool: manipulate StaticR files of MKW. + - It can analyze StaticR files and detects CTGP 4.4. patches and + modifications of track and arena orders. + - It can patch StaticR files and modifies the track and arena order. + So it is possible to move tracks with special features (like snow + or fog) to other slots. + - Commands TRACKS and ARENAS show details about tracks and arenas. + The order may be in StaticR related order. + - Command FILES shows the track and sound file names of all tracks and + arenas in machine readable format (not vertically formatted, one space + to separate columns). The order may be in 'StaticR' related order. + IMPORTANT: wstrt is EXPERIMENTAL! + - New option: --tracks=list: This option allow to define a new track order + for Mario Kart Wii. Tool wstrt uses this option for patching the track + order. Tools wszst and wbmgt use this to define the relation between Txx + and real message IDs. + - New option: --arenas=list: Does the same for arenas as --tracks for tracks. + + +szs v0.10a r2651 - 2011-05-21 + + - wstrt: Support for NTSC/JAP and KOREA version of StaticR.rel. + - wstrt: new command: FIND: Find tracks and arenas by name. + + +szs v0.11a r2670 - 2011-06-04 + + - New option: --single-line (-1): No continuation lines for BMG text output. + - Support for BREFF and BREFT files: Listing and extraction, but not creation: + - wszst LIST: List also BREFF and BREFT files including recursive lists. + - wszst EXTRACT: Extract also BREFF and BRRES files. Recursive extraction + of theses file types is implemented but disabled because of not + implemented creation support. + + +szs v0.12a r2693 - 2011-06-16 + + - Bug fix for Windows install script: Remove duplicate SZS entries from PATH. + - wszst CREATE: Create BREFF and BREFT files. + + +szs v0.13a r2840 - 2011-07-09 + + - Bug fix for tool wszst: Tool chrashed when creating BRRES files with empty + sub directories. + - wszst: Export of internal images (BREFT subfiles, TPL, TEX0) to PNG files + is done while extracting archives if decoding is enabled. Supported image + formats: I4, I8, IA4, IA8, RGB565, RGB5A3, RGBA32, C4, C8, C14X2 and CMPR. + - New tool: wimgt = Wiimms Image Tool : Convert and manage images files. + Supported file types: BREFT subfiles, TPL, TEX0 and PNG. + - wimgt LIST: List images files and print types and geometry data. + - wimgt DECODE: Export an internal image to a PNG file. + - wimgt ENCODE: Import PNG images and store them in internal formats. + - wimgt CONVERT: Convert formats (file and image format). + - All tools: new command FILETYPE (=FT): Scan the header of the entered files + and print file type and path for each file as list. + + +szs v0.14a r2862 - 2011-07-14 + + - New command: wszst UPDATE: Update sub files in an already existing archive. + - wbmgt --patch mode=file: A equal sign '=' is now used to separate mode and + filename. The old comma separator ',' is allowed but deprecated. + - New option for wimgt: --patch mode=file + This option allows to patch images by a list of other images. + + +szs v0.15a r2917 - 2011-08-07 + + - New tool: wkmpt: Wiimms KMP Tool + - wkmpt DECODE: Decode a binary KMP file and store the results + as user friendly, but machine readable text file. + - wkmpt ENCODE: Encode a text KMP file and store the results binary. + Indexing and grouping and some other stuff are done automatically. + - wszst EXTRACT: KMP files are decoded automatically. + - wszst CREATE: Encode KMP files automatically if changed. + - New image options for wszst: --transform= --encode-img + + +szs v0.16a r2931 - 2011-08-13 + + - Support of "Byte Order Marks" (BOM) when reading UTF-8 text files. + - New algorithm for CMPR image compression. + - Bug fix for KMP text files: Due a wrong info in the CT-Wiiki the 'prev' + and 'next' grouping values were swapped. + - Bug fix for KMP text files: Setting "[CKPT]/@AUTO-NEXT=1" failed. + + +szs v0.17a r2941 - 2011-08-16 + + - Scan KMP text: Correct line numbers in error messages. + - KMP: [CAME], [GOBJ] and [CHKT] entries can use names to point to the route + or respawn entries into the [POST] or [JGPT] sections. This makes list + modifications (inserting, removing, reordering) much easier. + - KMP: Parameter '@AUTO-ID=1' enables automatic setting of ID values. + - KMP text export: + - Improved and more detailed documentation. + - The sections are now sorted alphabetically. + - Links into other section uses generic names instead of index numbers. + + +szs v0.18a r2959 - 2011-08-26 + + - KMP: Improved text output with additinal infos. + - KMP: More intelligent group naming. + - KMP: Linear calculated points in CKPT, ENPT and ITPT. + - KMP: User may define local and global variables. + - KMP: Objects can be referenced my name (eg. o$itembox). + - KMP: Inter section links (AREA->CAME, CAME->POTI, GOBJ->POTI, CKPT->JGPT) + using names instead of indices. This allows easy reorganisation. + - KMP: AREA+CAME syntax (table layout) changed, old format stays compatible. + + +szs v0.18b r2966 - 2011-08-30 + + - Several small KMP decoding+encoding improvements. + + +szs v0.19a r3033 - 2011-09-15 + + - New option: --const NAME=VALUE,... + With this option the user can predefine variables in the command line. + - KMP scanner supports C like arithmetic expressions. + - KMP scanner supports nested @IF..@ELIF..@ELSE..@ENDIF structures. + - The KMP scanner accept copy&paste text of the SZS-Modifier, if parameter + @SZS-MODIFIER is set to 1. + - If decoding a KMP text file, names of entries of the source are used + instead of generic names while creating the destination output. + - Object infos (name, short description, parameters) are inluded into the + KMP object list. + - A decoded KMP file contains a template for parameters and conditional + encoding. Option --no-param (-P) disables the template. + - Several small KMP decoding+encoding improvements. + - New command: wkmpt SYMBOLS: Print a table with all predefined symbols. + - wszst CAT --decode: Decode files before output if possible. + - New command: wszst KMP: Open the sub file @course.kmp@, decode and print it. + - New command: wszst INFO: Print the content of the subfile 'info.txt' + or 'credits.txt' (first found file used, case ignored). + + +szs v0.20a r3069 - 2011-09-30 + + - Bug fix for KMP decoding: A wrong name was written for the opening camera. + - New option: --brief: Create KMP text files without most info lines. If set + twice, the output of unneeded sections is also suppressed. + - Tools exit with error status on encoding errors. + - The new KMP command '$BEZIER' works similar like '$LINE' and create + quadratic and cubic bezier curves for CKPT, ENPT, ITPT and POTI sections. + - New KMP commands: @ECHO and @WARN: Print a message or a warning. + - New option: --no-echo: Suppress messages made by the new '@ECHO' command. + - The KMP encoder and decoder support a free info string. It is stored at + and load from the end of the KMP file behind all sections. + - Mac: Support for PPC disabled, because the new LIBPNG does not support it. + - Mac: LIBPNG is now linked static. So there is no longer a need for Mac + users to install LIBPNG. + + +szs v0.21a.beta1 r3092 - 2011-10-09 + + - New wszst commands: XDECODE (=XD) as short cut for "EXTRACT --decode" and + XALL (=XA) as short cut for "EXTRACT --all". + - KMP encoder: Support of names for route points (section POTI). + - Mipmap support improved: + - If reading a TEX or BREFT, all mipmaps are scanned and internal stored. + - If reading a TPL file, all images are scanned internal stored. + - If reading a PNG, the tools search mipmap files. + - If storing a PNG, one PNG file is created for each image. + - If saving a format, all images are saved. The mipmaps of TEX and BREFT + are resized to have the correct size (half of the previos image). + - Option --no-mipmaps disables mipmap support. + - Option --n-mipmaps=NUM force the number of mipmaps. If needed, the + missing mipmaps are created from the existing images. + + +szs v0.21a r3142 - 2011-10-29 + + - Windows version only: Update to Cygwin DLL 1.7.9-1 + - Bug fix: If creating a TEX from multiple PNG files, sometimes not all + PNG sources were used. + - Bug fix: If extracting BRRES files, the informative file '.string-pool.txt' + was sometimes empty. + - The KMP/CAME text output contains now comments about camera, zoom and view + point durations. This helps to find out a good camera setup. + - KMP/CAME scanning: If zoom or view point speed is set to value -1, then the + value is calculated so that the time coincides with the camera running time. + - Internal object database updated. + + - The following points improve the diagnostic and analysis features: + - New wszst command: ILIST (or IL): List all images of an archive. To view + all sub archives recursivley, set option --all. Command ILL and ILLL are + short cuts for 'ILIST --long' and 'ILIST --long --long'. + - New option for tool wszst: --sort (-S): Define a sorting method for the + output of commands LIST and ILIST. + - New wszst command: MEMORY: Print a (recursive) memory map of all sources. + - New option --cut for "wszst LIST" and "wszst MEMORY": Cut different non + archive files into peaces (headers, groups, sections, ...) and list these + peaces as single sub files for a more detailed analysis. + Supported file types: BMG, BREFT-IMG, KMP, TEX, TPL and BRRES sub files. + + +szs v0.22a r3166 - 2011-11-06 + + - The KMP encoder is now a 2 pass compiler and the whole source is scanned + twice. In pass 1 only names are defined and warnings are suppressed. In + pass 2 all objects are defined. + - KMP logging: The job info line is now printed before warnings. + - Some new CAME+POTI variables are defined automatically as help for + calculated parameters, e.g. came.time or viewpt.len. + - If decoding or encoding a KMP, a plausibility check is done. This should + help to find KMP errors. Option --no-check disables this check. You can + find a detailed list of all checks on the tools website: + * https://szs.wiimm.de/cmd/wkmpt/check#desc + - New command: wkmpt CHECK: Run a KMP check and return 'DIFFER' as exit + status if any KMP error found. + - New command: wkmpt OBJECTS: Search objects in the internal object database + using the entered keywords and print infos about each found object. + Options --brief and --long control the verbosity of output. A online query + is also available: https://szs.wiimm.de/cgi/mkw/object + - Internal object database updated. + - KMP compiler/POTI: New command '$COPY': Copy some or all points from + previous defined routes. + - KMP compiler/POTI: New commands @shift-route, @hrotate-route, @scale-route: + These commands transform all points of a route. + + +szs v0.23a r3243 - 2011-11-25 + + - New command: wkmpt CAT: Print decoded KMP files to stdout. + - New option for wkmpt: --generic: Use generic names as references instead + of the names scanned from the source file. + - "wszst DIFF" supports now --recurse and --cut (and --all) to list + differences on sub file level. + + - Support of 3D vector variables and parser functions: + - New @DEF* and @GDEF* commands to define variables. The old @NUM, @GNUM, + @INT... are stil possible. + - Parser functions implemented, e.g. sin(x), hRot(pt,deg,[origin]). All + values of all KMP objects can be read by functions. + - New command: wkmpt FUNCTIONS: Print a return type and syntax info about + all parser functions. If --long is set, a little description is added. + The output can be filters by keywords. + - New command: wkmpt CALCULATE: Calculate each expression and print the + result to standard output. + + - Tool wstrt can now detect and patch region settings: + - wstrt ANALYZE: If a region patch is detected, a message is printed. + - wstrt PATCH --region=val: Patch a StaticR.rel to the entered region or, + if 'val' is 'RESET', restore it to the original Nintendo values. + + +szs v0.24a r3316 - 2011-12-30 + + - Feature to debug routes: The KMP compiler can automatically define + itemboxes for route points of the sections ENPT, ITPT and/or POTI. + - New commands: "wszst+wkmpt SYNTAX": Print out the generel syntax rules + for the text parser and compiler. + - Several bugs of parser functions fixed and some new functions added. + - Object DB updated. + - New feature: KMP transforming + The tools wszst & wkmpt support the new options --shift, --scale and + --hrot. With this options all position, size and rotation parameters + of a KMP can be transformed. The transformation is supported by nearly + all data commands like DECODE, ENCODE, CREATE, EXTRACT and NORMALIZE. + - wszst LIST/MEMORY/EXTRACT/UPDATE: Support of RARC and ARC files. + Creating a RARC file is not supported yet. + + +szs v0.25a r3321 - 2011-12-31 + + - The transformation options are now a little bit easier to handle: + - The execution order is now --scale before --shift before --hrot. + - New options for easier scale and shift setup: --xss, --yss and --zss. + => The user must select 2 points and make a note of the coordinates in + the old and in the new model. Then he must enter the coordinates as + parameters like --xss x1old,x1new,x2old,x2new and the scale and shift + values are calculated automatically. + + +szs v0.26a r3415 - 2012-02-04 + + - New general parser functions: dot(), cross(), unit(). + - New KMP specific parser functions: enpt$hDir(), enpt$next(), itpt$hDir(), + itpt$next(), stgi$byte(). + - An online function reference is also available: + -> https://szs.wiimm.de/cmd/functions#desc + - Bug fix: If scaling, the calculation of column 'scale' of sections ENPT + and ITPT were wrong. + - KMP/STGI table format changed to support 'pole position' and 'narrow'. + - Command FILETYPE: Accept --long to print an additional column with the + decompressed file type. + - wszst EXTRACT supports now --cut and stores the cutted peaces of files + into hidden directories. + - wszst EXTRACT supports extraction of BRRES sub files and all other file + formats, that supports the --cut option. + - New command for all tools: FILEATTRIB: Print out a table with all supported + file types and operations. + - First KCL support: + - New tool: "wkclt" with command DECODE (convert to obj like the + KCLexporter of `blank'). + - Tool wszst: If decoding is enabled, convert KCL to an obj file. + - Support for option --cut. + - wkmpt+wbmgt ENCODE: Wrong automatic file extensions fixed. + - New option for all tools: --chdir: Set a new working directory for all + following options, for all parameters and for the execution. + + +szs v0.27a r3437 - 2012-02-14 + + - KMP, sections JGPT+CNPT+MSPT: If parameter '@SNAP-TO-ENPT' is set to a + numeric value, then all points will be moved to the nearest point of the + ENPT section plus the entered value. The horizontal direction (Y-rotation) + is calculated to point to the next ENPT point, section linking is observed. + - KMP: Command '@ROUTE-ITEMBOX' is now avaialable for sections ENPT, ITPT, + JGPT, CNPT, MSPT and POTI. + - wszst EXTRACT: New option --ext or -e (for BRRES archives only): If a file + is extracted, add a handy file extension (like '.mdl') to the file name. + If set twice and a textual file magic is available, use the file magic in + lower case instead (like '.mdl0'). + - wszst CREATE: If creating a BRRES archive, the new file extensions (see + above) are detected and removed. + - wszst+wkmpt: More KMP checks. + See https://szs.wiimm.de/cmd/wkmpt/check#desc for a complete list. + + +szs v0.28a r3463 - 2012-02-20 + + - New command: wszst AUTOADD: Show the status of a auto-add archive and add + BRRES, BREFF and BREFT files to it. + Try "wszst AUTOADD ./path_to/Race/Course/" to add all supported files. + - New option: --auto-add (short: --aadd): Analyze the KMP (if exist) and add + missing BRRES files automatically, if the tool can find the files in any + sub directory named 'auto-add'. This sub directory is searched in all + directories of 'SEARCH_PATH'; try 'wszst AUTOADD' to view them. + - New command: wszst CHECK: Check track files: Try to read KMP (and check it) + and find missing BRRES, BREFF and BREFT files needed by used global objects. + - KMP/CKPT: The KMP compiler is now able to make check points visible by + using and scaling any global object. For details read the built in help. + If enabled, option --auto-add is implicitly set. + + +szs v0.29a r3515 - 2012-02-28 + + - KMP/GOBJ @SORT-OBJECTS=mode: The KMP compiler can sort objects in 2 ways. + - KMP/POTI: There are new transformation commands with changed syntax and + semantics. They are now identical to the global transformation options. + - KMP/POTI: Command @REVERSE-ORDER reverse the order of points of a route. + - Command 'wkmpt OBJECT --long' lists all needed files for each object. + - Minimum support (detection only) for file type BRASD. + - AUTO-ADD database: Object dependent KCL and BRASD files added. + Please redo: "wszst AUTOADD ./path_to/Race/Course/" + - New command: wszst TRACKS: Print a list of all known track SZS files of + Mario Kart internal directory './Race/Course'. These files are 32 standard + track files, 10 arena files, and 9 other files. + - KMP/CKPT: The KMP compiler is able to make check points visible by using + and scaling global objects. The interface has changes to allow different + viewing modes (forget the old settings). See built in help for details. + - New Command: wszst CHECK: Make a validity check for track files: Try to + read KMP (and check it) and find unknown, modified, needed and unneeded + files. This command uses a new internal object and file database. + - HINT: Unknown file: This will is not part of any Nintendo track. + - HINT: Additional file: Known file with unknown usage. + - HINT: Modified file: This file is different to Nintendo tracks. + + WARNING: Missing file: This file is needed/used (by an object). + + +szs v0.29b r3528 - 2012-03-02 + + - Bug fix for "wszst CHECK": There was a crash with core dump if analyzing + a SZS without valid KMP. + + +szs v0.30a r3548 - 2012-03-10 + + - There is a new BRRES subfile iterator. The old one is very strict and do + wrong things with BRRES strings and names while extracting some custom + tracks (Nintendo tracks work fine). The new one is more tolerant and runs + better on custom tracks. Now you can extract and repack every track. + Use option --OLD to activate the old iterator. + + +szs v0.31a r3579 - 2012-04-06 + + - Bug fix if BRRES sub file is cutted into sections: + Wrong size calculation of last section, if section type is unknown. + - BTI support: + - All tools and listing commands: Detect BTI files and print details. + - wimgt: Can now convert TPL, TEX, BTI, BREFT and PNG into all directions. + - wszst: Decode and encode BTI files on the fly. + + +szs v0.32a r3593 - 2012-04-14 + + - Bug fix for option --auto-add: Sometimes a segmentation fault appeared when + auto adding a missing subdirectory for a missing file. + - New command: wimgt COPY + Copy and convert source image to dest image. The default destination file + format is selected by scanning the file extension of the destination. + This is an alternative for the ENCODE and DECODE commands. + - KMP parser: @DEF.ENUM and @GDEF.ENUM define enumerated values. + - KMP parser: @DEF.SHIFT and @GDEF.SHIFT define shifted bit masks. + + +szs v0.33a r3627 - 2012-05-02 + + - Because of an update of the internal object & file database, redo the + command "wszst AUTOADD ./path_to/Race/Course/" to add more files to the + autoadd database -> see https://szs.wiimm.de/doc/autoadd for more info. + - wszst CHECK: Detect "optional files". + - KMP: New command '@HSNAP-TO-ENPT' is similar to '@SNAP-TO-ENPT', but + ignoring the vertical value of the enemy points. + - The text parser supports 5 kinds of nested loops (EXPERIMENTAL): + @LOOP ... @ENDLOOP : umlimted loop + @REPEAT ... @ENDREPEAT : loop with a predefined repeat count + @FOR ... @ENDFOR : classical 'for' with iterator variable + @FOREACH ... @ENDEACH : for each element in list + @WHILE ... @ENDWHILE : classical 'while' with condition + There are also conditional @BREAK and @CONTINUE. + - The KMP syntax and semantics documentation is now online available: + -> https://szs.wiimm.de/doc/kmp/syntax + The inline documentation in decoded KMP files is now much shorter. + - Command SYNTAX removed. Use page https://szs.wiimm.de/doc/syntax instead. + - The backtracking algorithm for analyzing of CKPH + ENPH + ITPH pathes has + been modified and optimized, because a test version of "Toad's Turnpike" + has 105 ITPH groups and needs much to much steps and time to calculate + about 10^28 pathes total. + + +szs v0.34a r3659 - 2012-05-12 + + - KMP text parser extensions: + - New commands: + @INCLUDE "filename": include text files. + @RETURN [val]: returns from the current input file. + @EXIT: exit the parser execution. + @ASSERT condition: exit execution, if 'condition' is false. + @MACRO .. @ENDMACRO: Define named macros. + @FUNCTION .. @ENDFUNCTION: Like @MACRO, but can also be used as + function in numerical expressions. + @CALL macroname(...): Call a previos defined macro with parameters. + '@:macroname(...)' is a short cut for '@CALL macroname(...):' + @DOIF (condition) anytext: Execute 'anytext', if 'condition' is true. + - New private variable name space: @PDEF + - P-Variables are only defined for the current source file or function. + - Loop variables (@FOR,@FOREACH) and macro parameters are defined in + this new private name space. + - New options for command FUNCTIONS (list parser functions): + - Option --long: The entered keys are compared against the description. + - Option --verbose: The description is printed too. + - Bug fix: Parser function minMax() returned wrong results. + + +szs v0.35a r3681 - 2012-05-20 + + - New options for "wstrt TRACKS+ARENAS": --brief --no-header + - KMP sections CKPH+ENPH+ITPH: Accept unsorted groups. + - New patching options: + - For wszst and wkclt: --kcl-flag=list: Exchange KCL flags in KCL files. + - For wszst and wkclt: --kcl-parse=script: Execute the script for each KCL + file and to calculate/modify the KCL flags. + - For wszst and wkmpt: --rm-gobj=list: Remove global objects in KMP files. + - New command: wkclt FLAGS: Print usage statistics about KCL flags. + - New parser functions: ptInCube(cube1,cube2,pt1,...) and a strict variant + ptInCubeS(cube1,cube2,pt1,...): Count the points in the cube. + - KCL exporter creates now 3dsMax compatible OBJ files with some issues. + + +szs v0.36a r3720 - 2012-05-28 + + - It the tools 'wkmpt' or 'wkclt' opens a SZS file, they use the internal + files 'course.kmp' or 'course.kcl' as source. + - New command: wszst KCL: Open the sub file @course.kcl@, decode and print it. + - KCL object exporter improved. + - New command: wkclt DUMP: Dumps the raw KCL records in human readable format. + - New command: wkclt OCTREE: Traverse and test the octree by using the entered + points. Logging of steps and triangles is controlled by --long and --brief. + - wkclt ENCODE: Import an OBJ file and create a new KCL from scratch. The + internal octree generator works fine and can be controlled by different + constants. See https://szs.wiimm.de/doc/kcl#encode for more details. + - If a KCL obj file with name NAME.EXT is scanned, a file name NAME.flag.txt + is scanned to find relations between group names and flags. + See https://szs.wiimm.de/doc/kcl#def-flag for more details. + - Read the new KCL export and import How-To: https://szs.wiimm.de/doc/kcl + + +szs v0.37a r3748 - 2012-06-02 + + - Read "Known Bugs" or https://szs.wiimm.de/doc/kcl about KCL issues. + - Command "wkclt dump" prints now more octree statistics. + As the result of the analysis of these statstics, the octree generation has + been optimized and some new parameters have been established. + - New command: wkclt TRIANGLES: It's like DUMP, but it print only 1 line + for each triangle data. The goal is to compare the data with other dumps. + - New option: --kcl-sort: Sort the triangles by length and positions to make + a dump more and easier compareable. + - New option: --kcl-drop: Remove all triangles, that are not referenced by + the octree. This helps to clean the KCL of custom tracks. + - The OBJ exporter creates Wavefront OBJ files compatible with "3ds Max" and + "Google SketchUp" (TIG's OBJ-importer plugin) and maybe with more 3D tools. + The exported data contains now also 'vn' vectors (normals). + - The OBJ importer read Wavefront OBJ files created by "3ds Max" and by + "Google SketchUp" (TIG's OBJ-exporter plugin) and maybe by other 3D tools. + + +szs v0.38a r3774 - 2012-06-05 + + - OBJ import and KCL creation tested with different tracks without issues. + - New option --kcl=list: 'list' is a comma separated list with keywords to + define options for the KCL processing. Allowed options are: RESET, FAST, + NORMALS, OUT-SWAP, IN-SWAP, AUTO, CLIP, DROP, SORT and DEFAULT. + See https://szs.wiimm.de/opt/kcl for details. + - The transforming options --shift, --scale, -xss, -yss, -zss and --hrot + are now available for KCL processing. + - Option --kcl-parse renamed to --kcl-script. + - Three new parser functions for the --kcl-script script: + tri$setPt() : Define new triangle points. + tri$shift() : Shift all points of a triangle. + tri$remove() : Remove a triangle from the list. + If one of these function is used, a new octree is calculated. + - New parser functions: sec(), mSec(), uSec() + - Several small bugs in the KCL creation removed. + + +szs v0.39a r3799 - 2012-06-09 + + - The KCL code was cleaned and the internal triangle data base has a new + optimized data structure. The new KCL tool will create the same KCL as + the previous r3774. Only the output of some dump commands and also + the created OBJ files have changed. + - Three new parser functions for the --kcl-script script: + tri$create() : Create a new triangle. + tri$scale() : Scale all points of a triangle. + tri$hRot() : Rotate all points of a triangle horizontally. + tri$unremove() : Clear 'REMOVED' marker + tri$isRemoved() : Get 'REMOVED' status. + Call "wkclt func tri$" for complete list of all 12 triangle functions. + - Command "wkclt OCTREE" renamed to "wkclt TRAVERSE". + - New command: wkclt COPY + Copy and convert source collision file (KCL or OBJ) to dest. The default + destination file format is selected by scanning the file extension of the + destination. This is an alternative for the ENCODE and DECODE commands + for single copy operations. + - Optimization: If input and output are binary KCL files and only KCL flags + were changed, then the original KCL data is copied 1:1 to the output file + and only the flag values are modifed. Use --kcl=new to build always a new + binary KCL and to disable this optimization. + + +szs v0.40a r3837 - 2012-06-17 + + - Windows version only: Update to Cygwin DLL 1.7.15-1 + - The KCL obj export will now create a material file with adequate colors. + To disable materials support, use "--kcl -mtl". + - KCL flag scanning: Some 3D tools use the generic suffix '_number' in goup + names. This may result in a conflict with the automatic detection of KCL + flags. Not only therefor, the interface has changed: + - If creating an OBJ file, '_Fxxxx' (literal '_F' and 4 hex digits) + instead of '_xxxx' is now appended to the group names. + - If scanning a groupname, '_Fxxxx' is always accepted as flag value. + - The old way to detect flag values is now disabled by default, but can be + enabled by different --kcl keywords: 'HEX4' enables '_ffff' (flag), + 'HEX23' enables '_tt_vvv' (type and variant) and 'HEX' enables both. + - The flag file may define HEX4 and/ot HEX23 to 0 (disable) or 1 (enable) + to override the defaults set by option --kcl. + - For all groups with unknown flag assignments, the unusal values 0xFFE0, + 0xFFC0, 0xFFA0 and so on (all type 0x00 (road), each 0x20 lower) are + used in this order. This makes the groups discriminable in the KCL. + - The default extension of the flag file is now '.flag' and not longer + '.flag.txt'. If scanning both files are searched. + - The new option --flag-file=path overrides the default search for a flag + definitions file while scanning OBJ files. + - New command "wkclt CFF" will create a flag file with all group names + as template. + - If scanning a OBJ file, faces (polygons) with up to 50 vertices in a plane + are supported. Polygons with N>3 are split into adequate triangles. + - The user interface of the parser function tri$create() has changed to + allow the definition of polygons with any number of verteces. + - Bug fix: The naming of some parser functions were wrong: ptInCube*() are + renamed to ptInCuboid*(). The obsolete old names are allowed for a while. + - New command: wkclt CHECK: Run a KCL check and return 'DIFFER' as exit + status if any KCL error found. + - The KCL creation is now included into the SZS creator: Touched OBJ files + will now converted to KCL automatically if creating a SZS file. + + +szs v0.41a r3866 - 2012-06-24 + + - Bug fix: The triangle split of a face with exact 4 verteces fails in 50% of + all cases (bad optimization). + - Bug fix: The parser expression "a?b:c" fails, if the operators not followed + by a blank. + - Bug fix for some parser functions. + - Using a KCL for defining KMP positions: + Now it is possible to set a point anywhere and let it fall down until a + collision. This is a very good function to place global objects and other + points of the KMP. This internal KCL-FALL is available for the user in + different stages: + - Command "wkclt FALL source point..." for testing. + - Parser functions kcl$fall() and kcl$fallFlag() for scripts. + - Automatic adjusting of KMP points and objects in the sections CNPT, + ENPT, GOBJ, ITPT, JGPT, KTPT, MSPT and POTI. + + +szs v0.42a r3927 - 2012-07-06 + + - Linux+Mac: The math library is now included explicitly to avoid a linking + failure on some systems. + - Bug fix: Assigning values to private macro variables has overwritten + existing variables in the standard or global namespace. + - New parser command: '@PARAM': Define a parameter name list for macro and + function calls. + - 'ROUTE-ITEMBOX' replaced by a more flexible 'ROUTE-OBJECT'. + - New command: wkmpt COBJECT: Create an OBJ file: Load the related KCL and + add new faces representing the KMP elements are added. Enemy, item, check + and respawn points and cannon destinations are supported at the moment. + Option --draw=list decides which elements are included into the OBJ file. + See https://szs.wiimm.de/cmd/draw for more details. + - New parser functions: mean(), dir(), xDir(), yDir(), zDir(), rot(), xRot() + yRot(), zRot(), axisRot(), tri$createCuboid(), tri$createJoist(), + tri$createPrism() and tri$createPyramid(). The tri$create*() functions are + internally used to add KMP objects to the OBJ file. + - New KCL creating parameter: KCL_BLOW: If calculating the overlay between + cubes and triangles, the cubes are blowed up by the some units, so that a + triangle may be visible in a neighbor cube. This avoids unexpected rarely + effects like bike hopping. The default value is 400. + See https://szs.wiimm.de/doc/kcl#KCL_BLOW for more info. + * If you have created a KCL with a previous version of my tools, use this + command to create a new octree: + wkclt encode course.kcl --kcl drop,new --overwrite + The file 'course.kcl' will be overwritten with a repaired version. + + +******************************************************************************* +******* END ******* +******************************************************************************* diff --git a/project/templates/HISTORY-v1.txt b/project/templates/HISTORY-v1.txt new file mode 100644 index 0000000..0b91a49 --- /dev/null +++ b/project/templates/HISTORY-v1.txt @@ -0,0 +1,1288 @@ + +@@MODULE(head.txt)@@ + + +******************************************************************************* +******* HISTORY v1.* ******* +******************************************************************************* + +szs v1.00a r3958 - 2012-07-14 + + - wkmpt COBJECT --draw=modelist: + - Mode 'JGPT': Draw arrows at respawn positions. + - Mode 'CJGPT' does what the old 'JGPT' did: Draw connections between + check points and respawn positions. + - Mode 'CNPT': Draw arrows instead of lines. + - New command "wszst MINIMAP": Print and optional patch the releavant minimap + data of a SZS, U8, BRRES or MDL file. Supported patching options: --touch, + --xcenter, --ycenter, --zcenter, --center, --scale, --shift, --xss, --yss + and --zss. + + +szs v1.01a r3987 - 2012-07-22 + + - Command "wszst MINIMAP" can now automatically adjust the minimap, if the + new option --auto is set. It analyses MDL section 2 (vertex list) to find + the minimum and maximum values. Then it makes the ranges 4% wider, store + the calculated values in 'posRU' and 'posLD' of the MDL and calculate the + transformation matrices. The command is very flexible and can modify the + map of SZS, U8, BRRES and MDL0 files. + See https://szs.wiimm.de/opt/auto for details. + + +szs v1.02a r3992 - 2012-08-01 + + - KCL: Detect invalid triangles (e.g. length=0) on OBJ import. + - Option --kcl: There are now 2 groups of removing commands: + - 'DROP*' drop unused, invalid or fixed triangles (bad triangles). + - 'RM*' remove triangles with special characteristics. At this moment + RM-FACEDOWN is the only command of the 'RM*' class: It will remove all + face down drivable triangles to avoid KCL glitches. + - File DB: Sub files 'kinoko_*.kcl' are now assigned to object group 'kinoko'. + - Update of the KMP Object database. + + +szs v1.03a r4002 - 2012-08-11 + + - New global option: --force: The tools analyse input files and reject files + with invalid data structures. If --force is set, little repairable issues + are ignored or repaired and a warning is printed. + >>> THE RESULT MAY BE INVALID OUTPUT FILES! + - Option --kcl: If no 'DROP*' mode is set, the tools drops automatically all + bad triangles. For an existing octree, it is only 'DROP-UNUSED'. + - Option --kcl: Correct handling, if 'DROP-UNUSED' and 'NEW' are set together. + - wszst MINIMAP --auto: The Y-translation is also calculated to make very + high faces visible. + + +szs v1.04a r4050 - 2012-09-08 + + - Bug fix for v1.03a: With some option combinations, the KCL generator failed + and created an invalid KCL (if modifying an existing KCL) or a valid KCL + with some unwanted triangles (if importing from OBJ). + - Option --kcl: Better handling of DROP modes. + - Option --kcl: Correct handling, if 'DROP-UNUSED' and 'NEW' are set together. + (Same message as before, but now I have fixed one forgotten special case). + - "wszst CHECK" will now compare the number of cannons in KCL and KMP. + - New option: "wszst NORMALIZE --minimap": If --minimap is set, the minimap + is automatically adjusted like "wszst MINIMAP --auto" does it. All other + options like --scale are ignored for this kind of minimap processing. + - New option: "wszst minimap --set-flags=flags": Set the MDL flags to an user + defined value (default 0x31f). + - The tools accept now 'Yaz1' files in the exact same manner as 'Yaz0' files + hoping that the data structure and the compression method are the same. + - New options: --yaz0 and --yaz1: Force the magic of new created YAZ files + to 'Yaz0' or 'Yaz1'. + - Object database updated. + + +szs v1.05a r4097 - 2012-09-29 + + - For text files, only the first 4 characters are used as magic ('#BMG' + instead of '#BMG-TXT'; same for '#KCL', '#KMP' and '#MDL'). On creation + a 8 character magic is still written for compatibility. + - Update of the KMP Object database. + - Files './AIParam/AIParam.baa' and './AIParam/AIParam.bas' are now known by + the internal file database. + - Some optimizations around the KCL octree creation. This includes a new + handling of automatic dropping of invalid KCL triangles. + - Some minor text and layout changes. + + +szs v1.06a r4110 - 2012-10-06 + + - Bug fix: "wkclt COPY" printed always a false syntax error after correct + finishing all copy jobs. + - Bug fixes for "wszst NORMALIZE": + - BRRES files will not touched now. + - Memory bug, if rebuilding the KCL. + - wszst CHECK will now make slot proposals with comments: + - Slots 3.1 and 7.1: Check usage of KMP objects 'sunDS' and 'pylon01'. + - Slot 4.2: Only the usage of at least one KMP object of 'kart_truck', + 'car_body' or 'K_bomb_car' is done. An additonally MDL analysis is + missed, so the status is only "may run on slot 4.2". + - Slot 6.1: Test the existence of file 'ice.brres' and of KCL flag 0x70. + - Slot 6.2: Check usage of KMP object 'HeyhoShipGBA'. + - New command: wszst SLOTS: Print the possible slots for a track file. + The analysis is the same as for "wszst CHECK" (see above). The command + prints either a positive or a negative slot list, whatever is shorter. + + +szs v1.07a r4137 - 2012-10-19 + + - "wszst NORMALIZE --kcl ...": In the old versions, the KCL was patched in + place and the total size of the KCL was not changed. If the new KCL was + larger, parameters were changes in up to 5 iteration steps until the new + KCL was less or equal the old size. Now a complete new KCL is created with + the optimal settings and the size may shrink or grow. + If 'INPLACE' is added to the '--kcl list', the old behavior becomes active. + - wzszt CHECK: The logging of sub checks (KCL+KMP) is now better integrated + into the main log and there is now only one summary line for all. + - KCL creation tuned. + - The auto-add library holds now also unique files of folder 'posteffect'. + Try "wszst AUTOADD ./path/to/Race/Course/" to add all supported files. + - New archive format: WU8 (Wiimms U8) + WU8 is like U8, but the sub files are coded as patch for Nintendos original + files. This removes any code of Nintendo and a compressed WU8 (default) is + in general smaller than the related SZS. For encoding end decoding an + auto-add archive is needed (see https://szs.wiimm.de/doc/autoadd). + - wszst NORMALIZE/PATCH: + - Accept options --dest and --DEST for an alternative destination path. + - Accept options --u8 and --wu8 to define the new (W)U8-archive format. + - Object DB updated. + + +szs v1.08a r4148 - 2012-10-27 + + - wszst CREATE/COMPRESS/DECOMPRESS: + - All 3 commands support now U8 and WU8 conversions and accept the + options --u8 and --wu8. + - Support of U8/WU8 conversions of non track files. + - All 3 commands support the new option --remove-src (or --rm-src) to + remove the source files on success and if different to dest files. + - Better error handling of non existing files if --ignore is set. + - New keyword for option --kcl: If creating an OBJ file and 'TRIANGLES' is + set, the triangles are not stored as vertices and faces. Instead each + triangle is stored in an independent line. The OBJ file itself becomes + incompatible (or invalid), because TRI lines are not supported by any + other tool, but it makes adding points or mixing OBJ files much easier. + + +szs v1.09a r4169 - 2012-11-11 + + - BZIP2 compression is now supported as alternative to Yaz0. It is dedicated + to the WU8 file format for transferring files. BZIP2 files are smaller than + Yaz0 files and the creation is faster. + - The new option --bz forces BZIP2 compression instead of Yaz0 compression. + - The new option --wbz is a short cut for --wu8 --bz. + => To create a WBZ (WU8+BZ) file: wszst compress --wbz TRACK.szs + - The new option --szs is a short cut for --u8 --yaz0. + => To create a SZS (U8+Yaz0) file: wszst compress --szs TRACK.wbz + + +szs v1.10a r4186 - 2012-11-17 + + - Bug fix: File type printing (e.g. "YAZ0.U8:filename") was reimplemented + because of the new bzip compression, but with bugs. + - Command "wszst CHECK" will now accept option --verbose to print some more + slot hints (negative info about slots 4.2, 6.1 and 6.2). + - New debug command for all tools: ARGTEST: Print all scanned arguments as + list. This helps to find out wrong parameters, especially for windows. + - Cygwin (Windows): Filename handling improved. If using double quotes around + a filename, backslashes work in a Windows usual way as directory separator. + - Cygwin (Windows): Windows 'SendTo' support added. It must be (un-)installed + with the batch files 'sendto-install.bat' and 'sendto-uninstall.bat'. + Available SendTo jobs: + - normalize-SZS.bat : Normalize a file and convert it to SZS. + - convert-to-WBZ.bat : Convert a SZS file to a WBZ file. + - convert-to-SZS.bat : Convert a WBZ file to a SZS file. + - create-autoadd-library.bat : Use it with the extracted Race/Course + folder to create the autoadd library. It is needed for WBZ conversions. + - New option: --number or --num: If a destination file already exists, append + a number directly before the file extension to make the filename unique. + If other numbered files already exist (ignoring case), use the maximum + existing index+1. Option --number is ignored if --overwrite is set. + + +szs v1.11a r4205 - 2013-01-01 + + - If creating the KCL file, the world will be enlarged by the KCL_BLOW value + to avoid problems at the world border. + - New parameter for KCL/octree fine tuning: KCL_MAX_SIZE: + Cubes, that are larger than KCL_MAX_SIZE, will always be splitted into 8 + sub cubes and will never have triangle lists. The maximum and default value + for KCL_MAX_SIZE is 1024*1024 (~1 million). + - New flag for --KCL=list: CENTER: If creating a new octree, adjust the cube + entrance values so that the real world is centered in the cube world. + - Some old and for a long time obsolate keywords removed. + + +szs v1.11b r4214 - 2013-01-20 + + - Bug fix: If creating a TEX0 file, the float number at offset 0x14 in the + TEX header was wrong (1 too large). The result were artifacts. + - Small updates of the object DB. + + +szs v1.12a r4236 - 2013-02-09 + + - Windows version only: Update to Cygwin v1.7.17 + - wstrt TRACKS+ARENAS: The index column shows now the internal zero based + track index of the track or arena instead of a simple track counter. + - wstrt FIND -lll: Additionally print the slot index and the internal index + (both zero based) before the file names. + - New command: wbmgt CAT: Print decoded BMG files to stdout. If a source is + a SZS file and no subfile is entered, subfile './message/Common.bmg' is + printed. + + +szs v1.12b r4246 - 2013-02-23 + + - Bug fix: The KCL cannon activator was internally handled as drivable type. + So it was wrongly a deletion candidate for --kcl=RM-FACEDOWN. + - Object DB updated. + + +szs v1.13a r4258 - 2013-03-01 + + - New option --rm-aiparam (short cut: --rmai): If set, the directory AIParam + and all files are removed from the archive. This option has only impact, + if creating a new archive or normalizing an existing archive. + - Command wszst NORMALIZE: Logging changed. + + +szs v1.14a r4274 - 2013-03-14 + + - wkmpt CHECK (and all commands calling this function internally): + 4 new tests for sections CKPH, ENPH and ITPH: + - The section must not be empty (WARNING). + - Only ITPH: The first 'next' link must be valid (WARNING). + - Each group must have at least one valid 'next' link (WARNING). + - Each group must have at least one valid 'prev' link (HINT). + See https://szs.wiimm.de/cmd/wkmpt/check#testlist for all tests. + - Option --kcl-script can now be used multiple times to execute multiple + kcl script in the entered order. + - Two new parser functions to identify the source within a script: isKCL() + and isKMP(). Both functions return 0 for false, 1 for true or 2 for true, + if the source is named 'course.*' (main KCL/KMP file). + + +szs v1.15a r4300 - 2013-03-28 + + - New docu file: INSTALL.txt + - Mac+Linux+Unix: Script 'install.sh' will call itself with 'sudo' if not + started by user 'root'. Read INSTALL.txt for details. + - Windows: Cygwin updated. + - Better detection of UTF-8 files with byte order mark (BOM). Tool wbmgt will + accept those files now. + - Bug fix: wbmgt CAT: The option --patch is now processed. + - Bug fix: wbmgt ENCODE: Wrong file naming fixed. + - Bug fix: Option --kcl: If no 'DROP*' mode is set, the tools drops all bad + triangles automatically. For an existing octree, it is only 'DROP-UNUSED'. + - Check KCL+KMP: The checks are now done after patching. + - New option --kmp=modes: It works like --kcl=modes to define KMP jobs and + modes. The first modes are: + - NEW : Scan KMP and create a new one. + - RM-SPCITEM : Clear 'special items for players' setting of the 6 item box + object types. + - FIX-CKPH : Fix the section CKPH. + - FIX-ENPH : Fix the section ENPH. + - FIX-ITPH : Fix the section ITPH. + - FIX-PH : Short cut for: FIX-CKPH,FIX-ENPH,FIX-ITPH. + - INPLACE : Force inplace replacing of KMP files (old mode). + - LOG : Debug: Enable KMP specific logging. + The fix modes are EXPERIMENTAL! + See https://szs.wiimm.de/opt/kmp for details. + + +szs v1.16a r4313 - 2013-04-08 + + - Bug fix for BTI detection (reduce false positives). + - wkmpt COBJECT --draw=ckpt: Draw lines to next point and to point linked + by the 'next' member. + - New command: wszst ENCODE: Works like wszst CREATE, but will not create a + first level archive. The intension is to create and update all subfiles. + 'wszst ENCODE' can be used as a prepararion before using 'wszst UPDATE'. + - New mode for --kmp: FIX-CKNEXT: Automatic calculation of the 'prev' and + 'next' links of KMP section CKPT. + See https://szs.wiimm.de/opt/kmp for details. + - Slot 4.2 detection: Improved 'moonview' detection, because MDL.s10 + 'polygon11' and 'polygon60' are searched and their size is checked. + - Command 'wszst SLOTS': Slots are assigned to output columns now. + - Windows 'SendTo' commands: Fix handling of filenames with spaces. + Reinstall the SendTo files with 'sendto-install.bat'. + - New Windows 'SendTo' commands: + - clean-KCL-of-SZS: Normalize a SZS, drop unneeded KCL triangles and + rebuild the KCL octree. + - adjust-minimap: Adjust the minimap of SZS, BRRES and MDL files. + - Object DB updated. + + +szs v1.17a r4358 - 2013-04-23 + + - Moonview slot proposal optimized. + - KMP check: Print a hint, if 200 or more global objects are defined. + - Option --fast overrides compression rates set by --norm or --compr=list. + - New --kcl mode: FIX-ALL: Short cut for DROP,RM,NEW,LOG. + - New option: --autoadd-path=dir: Define a path for the auto-add library + and override the default search pathes. + - New option: --mdl=list: Define a MDL patching method. VERTEX patching is + used by default. + - New option for wszst: --patch-file=list: Define, which kind of KMP, KCL and + BRRES/MDL files are objects for patching. The default is to patch only the + main KMP and KCL files and the MODEL and MAP files. --pfile is a short cut + for --patch-file. + - New name for option --hrot: --yrot + - New options: --xrot, --yrot, --zrot, --ypos: Options --xrot, --zrot and + --ypos are really new for X+Z rotation and --yrot is only a alternative + name for --hrot. See built in help for impact. + - New option: --translate: Works like --shift, but is done after the + rotation. This is similar to the MDL transformations, that works in the + order scale, x-rotate, y-rotate, z-rotate, translate. + - New command for testing: wszst MATRIX: Use the transformation options as + base and calculate and print the related vectors and the tranformation + matrices. Then execute the transformations for each vector (parameter). + - ATTENTION: Because of the new 3D rotation support, a new rotation + algorithm has been implemented. Implementation bugs are not impossible. + - ATTENTION: All new patching options are EXPERIMENTAL! + + +szs v1.18a r4404 - 2013-05-01 + + - There is a complete new transformation algorithm: All transforming options + will be used to create a transformation matrix. If using >1 transformation + steps (see below), the matrices are multiplied to get a final transformation + matrix. All transformations are now done using this final transformation + matrix. This is faster and the internal handling is now much easier. + - New option --next: This option allows and separates up to 10 transformation + steps. Logical, each point is transformed step by step in the definition + order. Internally, a final matrix is calculated and all transformations are + done in a single step by using this final matrix. There are some issues + transforming KMP object, see https://szs.wiimm.de/opt/next for details. + - Command MATRIX is now available for all tools with transformation support. + It was redesigned to control the impact of the new transformation steps. + See https://szs.wiimm.de/cmd/matrix + - New option --rot=deg@origin: Define the rotations for 3 axes by one option. + - New option --arot=deg[@pt1]@pt2: Do an (axis) rotation around the axis + specified by the 2 points as single transformation step. + This option is EXPERIMENTAL! See https://szs.wiimm.de/opt/arot + - New option --ascale=factor@dir: Do an (axis) scaling into the direction + specified by the vector 'dir' as single transformation step. + This option is EXPERIMENTAL! See https://szs.wiimm.de/opt/ascale + - KMP object database updated. + + +szs v1.19a r4461 - 2013-06-02 + + - wszst CHECK: Check existence of minimap bones 'posLD' and 'posRU'. + Read https://szs.wiimm.de/cmd/wszst/check#desc for a list of all tests. + - Check commands: KMP objects with presence flag == 0 will be handled as + non existent. This changes the missing files status and the slot proposals. + - KCL type names synchchronized to Wiki. + - Bug fix: If a function was searched and a const was defined with the same + name, a `segmentation fault´ (or similar) occured. + - New command: wkmpt STARTPOS: Print the start positions of players in + various formats. See https://szs.wiimm.de/cmd/wkmpt/startpos for details. + - Patching option --kmp accepts now the keywords LEFT, RIGHT, WIDE and NARROW + to modify the start options in section STGI. + - New global option --de: Use german track and arena names. + - Command FILETYPE --long: Column with version of file format added. + - wszst EXTRACT/CREATE: The version number of the archive file format is + stored in and read from the help file 'wszst-setup.txt' to support version + dependent file formats. + - BREFF and BREFT: + - Version numbers are printed in listings with version support. + - The file format version number is written to 'wszst-setup.txt' on + extracting and read from read from 'wszst-setup.txt' on creating. + - The default aligning for BREFT files depends now on the version number. + It is 0x20 for versions <10 (MKW=9) and 0x40 for versions >9 (NSMB=11). + At the moment the new alignment for NSMB is not well tested! + + +szs v1.19b r4482 - 2013-06-14 + + - Bug fix: If resizing an image (e.g. for mipmaps), an INTERNAL ERROR + appeared sometimes because of an unhandled combination of image type + and image size (odd height or width). [Thanx to HelloImYourMind] + + +szs v1.20a r4548 - 2013-07-21 + + - New Option for wszst and wimgt: --smart: + If resizing an image, use the new smart resize function. The new function + is much slower (factor 2-4) than the old function, but creates much better + resized images. This is only relevant for creating mipaps with odd width + or height. THIS NEW FUNCTION IS EXPERIMENTAL! + - Support of original SketchUp OBJ exporter, see next 2 points for details: + - More tolerant detection of Wavefront OBJ files. Export of the original + SketchUp exporter will be recognized as 'SKPOBJ' instead of 'WAVOBJ'. + - New --kcl flags: 'G' and 'USEMTL': If scanning an OBJ file, these flags + decides, which OBJ commands will define groups. If none is set (default) + 'USEMTL' is used for files of type 'SKPOBJ' and 'G' for all others. + - Command 'wkclt CFF' accepts now option --brief to suppress the comment + '# f(type,variant)' and option --long to force a '= f(type,variant)' + parameter. + - Suppress some patch logging messages for CHECK commands. + - CHECK KMP: If option --long is set, print some additional statistics. + - Some error messages are more exact now. + - Bug fix: If using options --test and --remove together, the destination + file was removed sometimes (but not created). + - New patching option: --tform-kmp=list: Fine control about the sections + and objects (position, rotation or scale vector), that are transformed. + See https://szs.wiimm.de/opt/tform-kmp for details. + - Windows only: Cygwin update to v1.7.20 2013-06-07. + + +szs v1.21a r4578 - 2013-09-07 + + - Improved built-in help system. Type e.g. "wszst help help" or visit + https://szs.wiimm.de/cmd/wszst/help for details. + - New keyword for option --kmp: FORCE: Ignore little repairable file structure + issues on reading KMP files. Works like --force, but only for KMP files. + - Bug fix: The check for the right position of all KMP sections failed. + - Optimized handling for SZS sub files with 'out of range offsets'. + - wszst: Option --auto-add can now be used together with the commands PATCH + and NORMALIZE. EXPERIMENTAL! + - Bug fix: Wrong calculation of parent directory index in U8 header fixed. + This bug existed from the beginning of the SZS tools and it seems, that it + had no impact for running a track or for other tools. However, the positive + impact is, that the creation of the files Track.szs, Race.szs and the other + language independent SZS files of MKWii directory './Scene/UI' works now. + + +szs v1.22a r4602 - 2013-09-14 + + - The Windows installer will now force type REG_EXPAND_SZ for path variables. + - New commmand: wbmgt MIX: Works like CAT, but combines all input files. + - Exported BMG text files will now show all characters of the Unicode Basic + Multilingual Plane (BMP; codes <=0xfffd) without escape sequences. + - Tool wbmgt: Option --single-line supports now 2 levels and option --brief + 3 levels. See https://szs.wiimm.de/opt/wbmgt/single-line and + https://szs.wiimm.de/opt/wbmgt/brief for details. + - New option: wszst --patch-bmg: Patch BMG sub files directly. There is no + more need to extract BMG files before patching. + - New option for tools wszst and wbmgt: --msg=list: If set, the BMG messages + are filtered by this list. Only enabled messages are exported to the ouput + BMG file (binary or text). + - If an archive (SZS,...) is found while reading a BMG file, the archive is + searched for BMG sub files and found sub files together are used as BMG + source. + - wstrt can now detect main.dol files and its type (PAL, USA, JAP or KOR). + - Option --kmp accepts now the keywords 1LAP, 2LAPS ... 9LAPS to modify the + first byte of KMP/STGI (number of laps). This first byte has only impact on + patched main.dol files. + + +szs v1.23a r4682 - 2013-10-05 + + - Exported BMG text files will now show all characters of the Unicode Basic + Multilingual Plane (BMP; codes <=0xfffd) without escape sequences. New is, + that surrogates and private ranges are always printed as escape sequences. + - New options for BMG text output: --color and --no-color: If enabled, colors + are printed as new escape sequence "\c{color}". 'color' is either a known + color name or a hex number with 4 digits. At the moment, --no-color is + the default to avoid problems with old versions and other externl tools. + In 2014 --color shall become the default. However, the BMG text scanner + accepts the new escape sequence from now and independent of these options. + - New modes for option --patch-bmg: 'ID', 'ID-ALL', 'CT-COPY' and 'CT-FILL'. + See https://szs.wiimm.de/opt/patch-bmg for details. + - New command: wstrt DUMP: Dump the data structure of DOL files as text. + - wstrt ANALYZE: Analyze DOL files and compare them section by section with + the orginal main.dol file of MKWii. + - New command: wstrt EXTRACT: Extract the sections of a DOL file into a + directory. StaticR.rel files are ignored. + - New global option: --ctcode: Enable the CT-CODE mode of the tools. + See https://szs.wiimm.de/doc/ctcode for details and about CT-CODE. + - wstrt PATCH --add-ctcode: Add CT-CODE to original main.dol files. + - New tool: wctct: Manage CT-CODE. See https://szs.wiimm.de/wctct and + https://szs.wiimm.de/doc/ctcode for details. + + +szs v1.24a r4703 - 2013-10-14 + + - Bug fix for wctct: Winning + losing ceremony after GP enabled. + - wctct: More possible names for music and property ids. + See https://szs.wiimm.de/doc/slot-id for details. + - Transforming complete tracks: VRCORN will now patched by default, if not + excluded by option --patch-file=list. + - Linux 'make': LIBPNG will be linked as static library, if available. + + +szs v1.25a r4715 - 2013-10-27 + + - New KMP command for CKPT, ENPT and ITPT: $SPLIT: Split the group into 2 or + more peaces. See https://szs.wiimm.de/doc/kmp/syntax#ckpt-grp for details. + - New KMP parameter for ENPT and ITPT: @AUTO-FILL: Add additional points if + the distance between points is smaller than the defined value. + See https://szs.wiimm.de/doc/kmp/syntax#enpt-par for details. + - KMP compiler: New snap variant to define x- and y-rotation. + See https://szs.wiimm.de/doc/kmp/snap for details. + - Command 'wkmpt COBJECT' accepts now option --kcl-script to filter the + exported OBJ file. + - Some small fixes for tool wctct. + + +szs v1.26a r4726 - 2013-11-10 + + - Bug fix: A patched file was not marked as dirty (and not written), if only + BMG was patched and only with modes 'ID' or 'ID-ALL'. + - New option --export (-X): Enable the export mode and create small and + machine readable text files for easy post processing. The option works + similar like -HBB for KMP and like -HBl11 for BMG text files. + - Windows version only: Update to Cygwin v1.7.25. I hope that it fixes the + bug that occurred during creation of MKW-Fun 2013-10 with Windows 8.1. + + +szs v1.27a r4742 - 2013-11-24 + + - Bug fix: Occasional a triangle was marked as invalid by mistake, if it was + inserted by the script function tri$create() (option --kcl-script). + - Minimap fixing: From now the minmum and maximum values of the MDL.s1 root + are set too. + - KMP check: A test added, which controls, that the check points are not to + near together and that they build convex quadrilaterals. + See https://szs.wiimm.de/cmd/wkmpt/check#desc for check details. + - KMP check: Some WARNINGS are HINTS now. + - New transformation option: --tform-script=script + The script is loaded and a macro is called for every vertex. So it is + possible to add user defined and also non linear transformations. + See https://szs.wiimm.de/opt/tform-script for details. + - New parser functions: status(), len2(), hlen2(), sideOfLine(), + ptInConvexPolygon(), ptsInConvexTri(), ptsInConvexQuad(). + See https://szs.wiimm.de/cmd/wszst/functions#desc for details. + + +szs v1.28a r4771 - 2013-12-09 + + - There was a disorder using the escape sequences '%E' and '%T' for file + extensions/types. '%E' included a leading point, '%T' only sometimes. + Henceforth both will include then leading '.' or are empty, if no file + extension is available. So the earlier '%N.%T' becomes '%N%T'. + - Command wkmpt COBJECT will now also draw the start position with area, the + respawn positions with area, the POTI routes and standard itemboxes. See + https://szs.wiimm.de/opt/draw for details. + - Command wkmpt COBJECT: Check points with quadrilateral warnings are drawn + in red. This option can be disabled by --draw=-warn. + - New KMP check: Test, if all enemy and item root points are inside of at + least one convex check point quadrilateral. + + +szs v1.29a r4788 - 2013-12-13 + + - Command 'wkmpt COBJECT' was renamed to the more intuitive name + 'wkmpt DRAW'. The old command name can still be used. + - Option --draw has now a short twin: -w + - Option --draw supports the new keyword SOLIDOBJECTS: Draw all solid objects + without any route as a blue octahedron. At the moment, the native object + size, scaling and rotation are ignored. + - Command 'wkmpt DRAW --draw ITEMBOXES' draws itemboxes like in-game as a + grey cube with a vertex down. The color for itemboxes with special items + for players or enemies is dark grey. + - New KCL script functions: tri$createOctahedron(), tri$createAntiPrism() + and tri$createAntiArrow(). + + +szs v1.30a r4810 - 2013-12-27 + + - Object export (wkclt DECODE & wkmpt DRAW): + - The default for normals changed. Normals will only be included if the + keyword NORMALS of option --kcl is set. The new default is better for + the SketchUp OBJ importer plugin in time and correctness. + - The ambient color of the material 'Ka' is not longer defined. + - Bug fix: If the object needed more than 65535 vertices, then a wrong OBJ + was written with indices modulo 65536. + - wkmpt DRAW: The following objects are drawn in correct size and with a + recognizable form and color: itembox, obakeblockSFC*, dokan*, karehayama + and all trees. + - KCL: Improved error system, if a KCL has more than 65535 normals. Before + aborting, the normals are rounded in multiple steps to find duplicates. + + +szs v1.31a r4828 - 2014-01-03 + + - BMG handling: \c{color} escape sequences in text output are now enabled + by default. To be compatible with old tool versions (v1.22a released at + 2013-09-14 and older versions), use option --no-color. + - wctct: Support of hex coded filenames for the 32 original tracks. + - Windows only: Cygwin update to v1.7.27 2013-12-09. + - New option: wstrt --all-ranks: Manipulate a query for Nintendos servers, so + that all players in a region are found independent of their ranking points. + + +szs v1.31b r4841 - 2014-01-17 + + - KCL bug fix: Creating and managing the flag file failed sometimes. + + +szs v1.32a r4958 - 2014-02-07 + + - wszst+wbmgt: Options --color and --no-color have new meanings (see next + point). Instead of the old option --no-color, there is a new option named + --no-bmg-color to suppress '\c{color}' escape sequences of BMG files. + - New global options for all tools: Option --color forces colorized text. It + is enabled by default for output to terminals. Option --no-color disables + colorized text at all. At the moment only the commands COLORS, CHECK and + SLOTS support colorized text. + - New command: COLORS: Test colorized text by printing it in different modes + (colors, bold, underline). Also test the options --color and --no-color. + - Bug fix: A patched BMG file was not marked as dirty (and not written), if + only some characters in the second half of the string are modified and the + length don't change (compared N bytes and not N words). + - Bug fix: wstrt patch --region=name: New assignment of region names to + region numbers: 0=Japan, 1=America, 2=Europe, 3=Australia, ... + - KMP/STGI: Support of the last 2 bytes as `speed modifier': + - On output, the factor is appended as comment. + - On input, a floating point number instead 2 single bytes is accepted + as speed mulitplier. The floating point number is rounded to the nearest + possible number (16 bit cut float with only 7 bits for the mantissa). + - New option for tools wszst and wkmpt: --speed-mod=factor: Define a speed + modifier and patch KMP files. + + +szs v1.33a r4983 - 2014-03-01 + + - Colorized patch log. + - New Command: wszst COPY source dest: COPY is an alternative command for + copying and pathing files. It uses the same internal procedure as command + PATCH, but has another command line syntax. + - New mode for option --kmp: FIX-CKJGPT: Fix invalid links to respawn points. + - wszst CHECK: Test geometry of images: + - Warnings about invalid images with zero width or height. + - Warnings about images with width or height not power of 2. + - Hints about images with width or height >1024. + See https://szs.wiimm.de/cmd/wszst/check#testlist for details. + - wszst CHECK: Count and warn about the number of vertices in MDL files + of 'course_model.brres', 'map_model.brres' and 'vrcorn_model.brres', + and about the total of all MDL files. + - New KMP checks for wszst+wkmpt CHECK: + - STGI: Print modified lap counter. + - STGI: Print invalid start position or mode. + - STGI: Print defined speed modifier. + See https://szs.wiimm.de/cmd/wkmpt/check#testlist for details. + - The tools 'wkclt' and 'wbmgt' accept the literal "0" as source filename for + an empty file (like "-" for stdin). + - Windows only: Cygwin update to v1.7.28 2014-02-09. + + +szs v1.34a r5070 - 2014-04-19 + + - New Option --slot=keyword: Patch a track file to run at the specified slot. + KMP and KCL may be modified. BRRES files may be added to or removed from + SZS. The main keywords are DAISY, DESERT, SHERBET, SHYGUY, STANDARD and + MOST. Slot numbers (except '42' or '4.2') are also accepted. + See https://szs.wiimm.de/opt/slot for details. + *** THIS NEW OPTION IS EXPERIMENTAL! *** + - New patch option: --https=mode: Patch the https strings of REL and DOL + files. 'mode' is one of the following keywords: + - RESTORE restores the original URLs and domains. + - HTTP replace HTTPS requests by HTTP and sub-domain 'naswii.' by 'nas.'. + - DOMAIN replace HTTPS requests by HTTP and domain 'nintendowifi.net' by + a domain specified by the option --domain. + - PORT1 sets the domain specified by option --domain in combination with + ports 90-93. + - PORT2 is like PORT1, but it uses 'mariokartwii.DOMAIN' to replace the + 'mariokartwii.race.gs.nintendowifi.net' domain. + - New patch option: --domain=dom: Define a domain for option --https. The + default domain is 'wiimmfi.de'. + + +szs v1.35a r5184 - 2014-05-16 + + - Two more modes for option --https: + - SAKE0: Same as DOMAIN, but don't replace URLs with SAKE domain. + - SAKE1: Same as SAKE0, but replace HTTPS of SAKE domains. + - New option: wstrt --wiimmfi: Patch the files for the new custom server. + It is a short cut for '{--https=domain --domain=wiimmfi.de}'. + + +szs v1.35b r5386 - 2014-07-02 + + - wstrt ANALYSE: Bug fix: HTTPS hint for original files removed. + - wstrt PATCH: Bug fix: Some forgotten 'https' and 1 'sake' are patched now. + + +szs v1.35c r5394 - 2014-07-03 + + - wstrt PATCH: Another 'https' bug fixed. + + +szs v1.36a r5680 - 2014-10-18 + + - New command: wstrt+wbmgt POINTS: Print a table with points assigned after + a versus or private room race. This command should help to find a good + parameter for option --points. + - New option --points=list: Define a new table with points assigned after a + versus or private room race. Predefined tables are: + NINTENDO: Original Nintendo table. + LINEAR: Last get 0 points, all other 1 point more than successor. + WIN15: Winner = 15 points, last=0, all other are evenly distributed. + WIN25: Winner = 25 points, last=0, all other are evenly distributed. + For LINEAR, WIN15 and WIN25 there are some options: + ONE: Last get 1 point instead of 0. + BONUS: Winner get a bonus of 1 point. + See https://szs.wiimm.de/opt/points for details. + - Windows only: Cygwin update to v1.7.32 2014-08-13. + + +szs v1.36b r5690 - 2014-10-21 + + - Bug fix: If transforming a track, the minimap became sometimes invisible. + The reason was, that the minimum and maximum parameters of the vertex list + were not calculated and not stored to reflect the new coordinates. + - New patching option: --null: Create a neutral transformation without + affecting the coordinates. The only influence is, that the dependent values + (like maximum) are calculated as if a transformation has taken place. + This option helps to fix the transforming bug mentioned above. + + +szs v1.37a r6010 - 2015-02-08 + + - New color management. + - Object database updated. + - New wstrt options --vs=ch & --bt=ch: Replace the second letter of + versus/battle identification ('vs','bt') to a new character. + - Windows only: Cygwin update to v1.7.34-6 2015-02-04. + + +szs v1.38a r6056 - 2015-02-27 + + - Tools WSZST contains now all other tools. It can be called in the usual way + or as tool wrappers. See https://szs.wiimm.de/doc/wrapper for details. + - wkmpt DRAW: If the drawing of itemboxes or other solid objects is enabled, + the routes of movable itemboxes or other solid objects are drawn too. + - Two new keywords for option --patch-bmg defined: + - CT-FORCE-COPY: Copy all standard track and arena name messages to + MID 4000..4029 and overwrite existing strings. + - RM-CUPS: Remove racing cup names (MID 23f0..23f7) from BMG. + - New keyword for option --msg=list: CTTRACKS enables the messages for the + CT-CODE track names (MID 4000..40ff). + - New option wctct --write-tracks=file : Create a machine readable file + reflecting the track distribution to the cups. --wtracks is a short cut. + - New option wctct --order-by=bmg : Load the BMG file and filter track + names. Then order the tracks in the cups by the alphabetic order of the + BMG messages. The tracks and its slots are notmodified. + - New option wctct --order-all : Option --order-by will order all tracks. + - New command wctct PATCH : Read each source file, find and scan the + CT-CODE part and patch it. + - New command wctct TRACKS: Create a track listing. + + +szs v1.39a r6123 - 2015-03-27 + + - BMG files: Support of new color names YOR0..YOR7: 8 continous colors from + (Y)ellow to (O)range to (R)ed. For compatibility reasons, hex-codes are + used instead of the new names on text output. + - wctct: Minor bugs fixed for handling BMG files. + - wctct CREATE: Support of output modes EU-BRRES, US-BRRES, JP-BRRES, EU-SZS, + US-SZS and JP-SZS to create strap files, either uncompressed (xx-BRRES) or + compressed (xx-SZS). + - New option: wctct CREATE --images=dir: Define a directory where the images + for BRRES or SZS files are searched. + - KMP/GOBJ/ELINE_CONTROL: Use group names instead of index numbers for links + into section ENPH. Reordering of groups is stable now. In section ENPT a + cross reference to ELINE_CONTROL objects is printed. + - New option for wstrt: --vs-region and --bt-region: Define different regions + for versus races and battles. + - New: wszst SHA1 --base64: print the checksum as BASE64 encoded string. + - New: wszst SHA1 --verify: Compare a checksum integrated in the filename. + + +szs v1.40a r6162 - 2015-04-13 + + - wstrt: The region is now patched at 4 places at StaticR.rel for versus + races and at 4 other places for battles. Versus and battle can be patched + to different regions. + - BMG: Each BMG string has an additional 32-bit value. In old versions, + a value different from 0x01000000 is printed as separate line using the + format 'NAME ~ VALUE'. With this version, also a combiantion of value and + string is possible. The new format is: NAME [value_vector] = STRING'. + At scanning, both formats are accepted. + - The new option --bmg-inline selects the new inline format for output. + It will be the default in the future. + - The new option --no-bmg-inline selects the old separate line format for + output. At the moment it is the default for compatibility reasons. + - Full support of PAT0 binary and text files including decoding and encoding: + - 2 models for text output are used. SIMPLE for all standard PAT0 files + and COMPLEX for PAT0 with extended features (e.g. multiple animations). + - The internal parser is used to scan text files (SIMPLE and COMPLEX). + - New tool 'wpatt': Wiimms PAT Tool: Decode and encode PAT0 files. + - Tool 'wszst' decodes on extract and encodes on archive creation if the + text file was touched. + - The new option --pat=list defines global options for PAT processing. + - Support of PACK archives (list, extract and create). + - New option --pack: Force a PACK archive on CREATE. + - New option --align-pack=size: Define the alignment for PACK sub-files. + - The tools of the Linux distributions are compiled now with static libraries + to improve the compatibility with various Linux systems. + + +szs v1.41a r6469 - 2015-09-04 + + - Bug fix: wstrt patch --region: Changing the region in a staticr.rel file + was internally not registered as modification with the result, that the + patched file was not rewritten. + - New option for wkmpt DRAW: --png geometry: A PNG with a top view of the + world is created instead of an OBJ file. Use rotation to view into other + directions. This is very slow. + - Internal sub-file database: Files "posteffect.bblm", "posteffect.bdof" and + "VolcanoPiece*.*" are now marked as 'optional'. The new classification + suppresses "Missing file:" warnings on checks. + + +szs v1.42a r6527 - 2015-10-03 + + - New commands: wszst STGI & wkmpt STGI: Print a single line statistic about + KMP/STGI settings (start pos, speed mod, laps) for each source file. + Both commands do the same and accept track and KMP (bin and text) files. + - New option: wstrt --add-section: Add a new section to a main.dol file. + It's also possible to add a gecko code interpreter together with cheat + codes, e.g. the Lap & Speed Modifier. + + +szs v1.43a r6536 - 2015-10-09 + + - Option --add-section supports now GCT files as input. In this case, + code-handler-only is added automatically. + + +szs v1.44a r6558 - 2015-10-17 + + - BMG: The `inline mode' for attributes is now enabled by default. It can be + disabled by option --no-bmg-inline. + - BMG message id 0 is allowed now. + - BMG: Support of 1A escapes with up to 254 bytes (before 12 bytes). + - BMG: Escape sequences \x{}, \u{} and \z{} can now handle comma separated + lists of parameters. See https://szs.wiimm.de/doc/bmg/esc for details. + - New option: --x-escapes: Use \x{} escapes instead of \z{} escapes. + - New option: --old-escapes: To be compatible with v1.43 and earlier, print + 1A escape sequences with total size of >12 bytes as single words. Also + don't use \x{} and \u{} with parameter lists. + - BMG supports now attribute vectors up to 20 bytes. MKWii uses only 4 bytes + for attributes. + - New option: --bmg-inf-size (also --inf-size for tool wbmgt): + Defines the size of section 'INF0'. For MKW it is usually 8. + - New option: --force-attrib=attrib: Override all BMG attributes with the + entered value. See https://szs.wiimm.de/doc/bmg/attrib for the syntax. + - New option: --def-attrib=attrib: Define alternative default attributes for + new BMG text files. See https://szs.wiimm.de/doc/bmg/attrib for the syntax. + - New option: --no-attrib: Suppress the output of string attributes at new + BMG text files. + + +szs v1.45a r6602 - 2015-10-31 + + - Option --patch-bmg=id: Better detection of the source file type (Race, + Event, MenuOther, ... ) especially in SZS files. + - The KMP compiler can now automatically create respawn points and/or + automatically connect check points to the nearest respawn point. Both + together make an initial respawn setup much easier. + Read https://szs.wiimm.de/info/respawn-setup.html for details. + - Tool wctct: It is now possible to define hidden tracks (don't appear in + any cup) by using control letter 'H' instead of 'T'. + - New option: wctct/wstrt --ct-dir: Define a search directory for CT-CODE + parts and use the internal copies only, if no valid file is found. Use + this option multiple times to define more than one search directory. + - wctct --ct-dir: Search files rmc?/mod1.bin, rmc?/mod2.bin, rmc?/ovr1.bin + and rmc?/bad1code.bin. + - wstrt --ct-dir: Search files rmc?/boot_code.bin and rmc?/boot_data.bin + if option --add-ctcode is used. + - Tools wctct and wstrt use now the most current CT-CODE version provided by + https://github.com/Wiimm/wii-ct-code (a fork of Chadderz code with support + of the SZS Tools). To use the old code of year 2013 download the zip file + https://szs.wiimm.de/download/ctcode/ctcode-2013-10.zip and use the base + directory as parameter for the new option --ct-dir (see above). + - wstrt --add-ctcode: The first free DOL sections are used now. Before only + sections T2 and D8 were added if not already in use. + - New option: wstrt --meno: Patch some 'MenuSingle' and 'MenuMulti' strings + of file StaticR.rel for online usage to 'MenO*' to distinguish between + offline and online. + + +szs v1.46a r6626 - 2015-11-08 + + - New option: --compatible=version: The option accpets a version number + (format '#.##' or 'v#.##') or a revision number (format 'r#') as parameter. + If set, the tools try to create BMG and KMP text files, that are compatible + to the entered version. This may override other legacy options. + - wkmpt DECODE: If creating a KMP text file, a new macro section is included. + The only yet provided macro is ITEMBOX: It help to setup a row of items + and supports automatic height correction. See the macro definition for more + details. The macro itself is very old and works also with old versions of + the tools, but they don't create it. + - wkmpt DRAW: Draw objects 'kinoko_*' and 'DKturibashiGC' as recognizable + polygons. These objects can also be used to stop fall-down on automatic + height evaluation, if the new parameter 'KCL-FALL-ADD-ROAD' is set to '1'. + - New: Post processing flags for KMP sections ENPT, ITPT and JGPT. To disable + the new flags column on text output, use option --compatible=v1.45. + - wkmpt DRAW: If drawing check point connection lines, red is used for the + left and green for the right side (like navigation lights). + - wkmpt DRAW: Option --draw supports now object filtering by presence flag. + The new keywords are: 1OFFLINE .. 3OFFLINE and 1ONLINE .. 3ONLINE. + + +szs v1.47a r6650 - 2015-11-27 + + - Tool wctct: Update of the internal CT-CODE (mod2). The new code improves + the modes "in order" and "random" for offline versus races. Here you can + find old CT-CODE version: https://szs.wiimm.de/download/ctcode/ + Visit https://github.com/Wiimm/wii-ct-code/ for more details. + + +szs v1.48a r6675 - 2015-12-11 + + - Tools wctct and wstrt support now Korean CT-CODE. Therefor external CT-CODE + files are needed. Use --ct-dir=path to declare at least one search path. + - Bug fix: wctct disables the hard coded region patch of the CT-CODE. Now + region patches in StaticR.rel are possible like before v1.45a. + - Bug fix: On CT-CODE text export wctct encloses the track names by quote + signs. This feature was accidentally lost with v1.38a. + - New option: wctct --allow-slots: Activate reserved slots for automatic + slot assigning. Example: --allow-slots ARENAS,SPECIAL + + +szs v1.49a r6974 - 2016-03-05 + + - The BMG is rewritten as independent source code lib. It can be used by + other tools (e.g. mkw-ana) now. + - BMG text output contains now more comments about message ID groups. + - Option --patch-bmg supports 2 new modes: + - UNICODE: Replace Unicode-escapes ('\u{xx}') with 16-bit codes by related + unicode character. + - RM-ESCAPE: Remove all 0x1a-escapes from all strings. + - KCL script 'lower-walls.txt' updated to support different gradients. + - New option: --mipmap-size=num: Define the minimal width and minimal height + of a generated mipmap. The default is 16. Tools before v1.49 used 1 as not + alterable value. --mm-size is a short cut. + - Option --kcl RM-FACEDOWN: Remove all road with face direction >30 degree + toward bottom. Before it was >0 degree. + - New keyword for option --kcl: RM-FACEUP: Remove all walls with a face + direction >80 degree towards the sky. + - New keyword for option --kcl: CONV-FACEUP: Convert all walls with a face + direction >80 degree towards the sky into a simple road. + - New keyword for option --kcl: SCRIPT: If set, KCL scripts are executed + before cleaning and transforming the KCL. + + +szs v1.50a r7053 - 2016-04-08 + + - Improved cheat code handling. + - Three new options: --gct-move & --gct-addr & --gct-space + With these 3 options it is possible to support a very large cheat code + table. Therefor a temporary section is created that runs at the very + beginning and takes space from the game heap to establish a code section + before the FST table. + - Tool wctct: Update of the internal CT-CODE (mod1). The new code allows to + select original tracks in regions. Here you can find old CT-CODE versions: + https://szs.wiimm.de/download/ctcode/ + Visit https://github.com/Wiimm/wii-ct-code/ for more details. + + +szs v1.51a r7107 - 2016-06-01 + + - wstrt is able to patch DOL files of other games and not only of MKWii. + - The CHECK for slot 4.2 will be more exact now because it looks for the + needed materials. The presence flags of objects 'car_body' and 'kart_truck' + is also tested. If option --OLD is set, the old polygon test will be used. + - Option --kmp=list supports now the keyword REVERSE as `backwards´ support: + Reverse KMP sections ENPH, ENPT, ITPH, ITPT, POTI. The implementation is + not finished yet and more section (CKPH, CKTP, KTPT, JGPT) will follow. + - Different small fixes. + + +szs v1.52a r7164 - 2016-06-29 + + - Bug fix for wkclt: Since v1.49a, the transformation was executed twice, + but only for commands DECODE and ENCODE. Command COPY works correct and + transforms only once. + - wstrt: Wiimmfi patching excludes now 3 patch positions for competitions. + It's now the job of the competition patcher to patch these 3 URLs. + - wkclt CFF: If creating a new flag file, the new function i(type,z,index) + is defined as support for fall boundaries (idea by Wexos). + - New tool: wmdlt: The tool wmdlt exists since v0.26a (2012), but was always + hidden, because it was useless. But now I started with MDL analysis and + plan to modifiy MDL data. + - wmdlt CAT/DECODE: Print a MDL file as text. It is incomplete, but can be + used for analysing. + - wmdlt STRINGS: Print all strings of a MDL. + - For slot 42 (moonview highway) the content of the 12 needed materials + is checked. + + +szs v1.53a r7308 - 2016-12-09 + + - Bug fix: Occasional a "Segmentation fault" occured, if reading an empty file. + - Option --draw supports the new keyword DECORATION: Draw unsolid objects + without any route as a magenta cuboid. + + +szs v1.54a r7393 - 2017-08-14 + + - Some KCL flags renamed. + - wkmpt draw --draw AREA: Areas of KMP are drawn as yellow arrows and as + transparent yellow cuboids. + - Support for RKC (competition) files added: + RKC files are handled like other archives. Tool wszst is able to detect, + list, extract or create a RKC file, also recursive. Direct creation and + optimation of the included SZS is not implemented yet. + - New keyword for option --kmp: MAX-LAPS: Keyword MAX-LAPS has only impact, + if one of 1LAP,...,9LAPS is set too. Then, the number of laps is only + decreased to the specified number, but never increased. + - New keyword for option --kmp: RM-EMPTY: After creating a new KMP, all empty + KMP sections are removed to make the KMP as small as possible. Use this + option only for compettitions! + - New command: YAZDUMP: Prints an info dump about the YAZ0/YAZ1 compression + details for analysis. + - New compression methods: 10 (=ULTRA) and 100-150: They need much more time + than BEST (=9) but create smaller SZS files than Nintendo did. It is + dedicated to competitions with strict size limitations. + - There is a new option: --tiny=level: It tries to minimize SZS and also the + included KCL and KMP by different technics (rounding values, sorting and + compression methods 100-150). It is not fully documentd yet. + See https://szs.wiimm.de/info/tiny.html for details. + - New keyword for option --kcl: WEAK-WALLS: Set the most significant bit + (bit 15, 0x8000) of KCL flag of all walls. It stops hard bumping at the + edges and has similar effects like lowering the walls. + - Windows only: Cygwin update to v2.8.1, 2017-07-03. + - Apple Mac is no longer supported because I no longer have access to a Mac. + + +szs v1.55a r7429 - 2017-11-11 + + - New command: wszst DISTRIBUTION: The aim is to create a text file that + describes a custom track distribution. The owner have to edit the created + file and send it to Wiimm. Wiimm add the information to the custom track + portal https://ct.wiimm.de/ with a complete track list. + + +szs v1.55b r7437 - 2017-11-19 + + - wszst DISTRIBUTION: New parameter: PREDECESSOR + - wszst AUTOADD: With option -v, the command will list invalid files too. + + +szs v1.56a r7461 - 2017-12-03 + + - wszst DISTRIBUTION: Track scanning improved. + - Option --cache is a global option now. It is used to speedup every YAZ0 + compression, but only for standard compression level 9 (BEST). A hash table + is used now for optimization. + - New option: --cname: Defines a cache file name for a single file. + - New Command: wszst SCANCACHE: Scan the cache directory for SZS files and + re-build the hash table. + + +szs v1.57a r7484 - 2018-04-11 + + - Windows version only: Update to Cygwin v2.10.0. + - New option for tools wkclt, wszst and wkmpt: --kcl-min=FLOAT: Define a + minimal `length` of KCL triangles. The length is the heights of the + reference side of the triangle. Smaller triangles are invalidated and + ignored or removed. The default value is 0.1 and good values are between + 0.01 and 2.0. Until v1.56a the minimal length was hard coded as 1e-6 + (1/million) to avoid mathematical overflows while calculating positions + by normals. The larger value reduces the number of KCL glitches + dramatically for some tracks with many unusual triangles. + - New option: --wc24: If set, options --wiimmfi and --domain will exclude + 2 strings of StaticR.rel from the URL patch to enable WC24 support. + + +szs v1.58a r7499 - 2018-04-20 + + - Option --kcl-min=EXPR: This option is deprecated, but still supported. It + is an alternative name for --tri-height=EXPR now. + - New option --tri-area=EXPR: Define the minimal area size of KCL triangles. + The intention is to ignore triangles that are generally to small. EXPR is + a floating point number or expression. Triangles are invalidated if their + area size is smaller than EXPR. The test is executed after reading files, + after transformations, and after calculating normals and lengths. Values + between 0.01 and 4.0 are recommended. The careful value 1.0 is used as + default. Value 0 disables this filter functionality. + - New option --tri-height=EXPR: Define the minimal height of KCL triangles. + The intention is to ignore deformed triangles (very slim, but long). EXPR + is a floating point number or expression. Triangles are invalidated if at + least 1 of the 3 heights is smaller than EXPR. The test is executed after + reading files, after transformations, and after calculating normals and + lengths. Values between 0.01 and 2.0 are recommended. The careful value 0.1 + is used as default. Value 0 disables this filter functionality. + - You can proof the impact of the options above by command wkclt TEST. + - Command wkclt DUMP has been expanded and shows metrics like side length, + area size and heights for each triangle. + - New command: wkclt LIST: Print a list of all triangles with metrics in + human and machine readable text format. It is like command DUMP, but + reduced to the list of triangles. + - New implementation for OBJ groups without assigned KCL flag. They will be + inserted into the OBJ export now. + + +szs v1.58b r7503 - 2018-05-18 + + - Default value for options --tri-area and --tri-height is now 1.0. + - Some more minor edits. + + +szs v1.59a r7533 - 2018-08-05 + + - Option --kcl-min is not longer supported. Use option --tri-height instead. + - The old file extension ".flag.txt" for KCL flag files is not longer + supported. + - wszst/wkmpt CHECK: Battle arenas are now detected in 2 levels: "some hints + found" and "is battle arena". Some hints and warnings are suppressed for + battle arenas. + - wszst SLOTS: For battle arenas, the keyword ARENA is printed instead of a + slot list. + - New option: --export-flags[=mode] (or short: --exflags): KMP compiler and + scanner support the export and import of KMP flags for sections ENPT, ITPT + and JGPT through a binary KMP file. Therefore, the last byte of the scale + or of the x-rotation is used. Values becomes inaccurate by 0.003% (not + relevant for scale or rotation and not noticeable while playing). + MODE is either OFF, AUTO (default) or ON. Without value, ON is used. ON and + OFF override the @EXPORT-FLAGS settings of a KMP text file. + - New option: --wim0[=mode]: KMP compiler and scanner support the export and + KMP parameters through a special KMP section named "@WIM0@". MODE is either + OFF, AUTO (default) or ON. Without value, ON is used. ON and OFF override + the @WIM0 setting of a KMP text file. This as an experimental feature at + the moment to proof the compatibility with other KMP tools. + - New command wkmpt WIM0: Analyze the WIM0 section of each KMP file (text, + binary or szs) and print an info about it. + - wkmpt DRAW: AREAs are drawn dependent of the mode either as a rectangle + cuboid or as a cylinder (more exact: as an anti prism with 20 sides). + - wkmpt: If creating a KMP by text file, the PREV and NEXT links of item + routes are checked. If missed, a link to self is added to avoid a freeze. + - Option --kmp FIX-CKPH,FIX-ENPH,FIX-ITPH: If a PREV or NEXT link is missed, + a link to itself is added. Before, a link to another group was inserted. + - Camera types 0 (Goal) and 3 (KartFollow) are excluded from transformation + and scaling now. + - KMP text files: STGI is printed with new column names according the new + knowledge of CT-Wiiki. + - Internal KMP GOBJ database updated by CT-Wiiki object table. + + +szs v1.60a r7578 - 2018-08-21 + + - Improved detection of battle arenas by KMP. The output of KMP text files + of racing tracks and battle arenas differ. + - New option:--battle[=mode]: The mode is either OFF, AUTO (default) or ON. + OFF and ON override the automatic detection of battle arenas. + - wkmpt DRAW: The connections between ENPT and ITPT routes are drawn with + half width and a brighter color now. Additional, there is a distinction + between bidirectional links, PREV only and NEXT only. + - wkmpt DRAW: Dispatch points of KMP sections ENPT and ITPT are detected and + printed as octahedron. The NEXT links point to the nearest end of the + destination routes. + - New keyword for option --draw: DISPATCH: If set, the settings of dispatch + points of areans are visualized by colors to support the analysis. + - New keyword for option --draw: COINS: If set, draw coins as yellow, orange + or red cubus. Color and size depend on coin place type. + - New keywords for option --draw: BLACK and WHITE: If set, a very dark grey + (BLACK) or a very bright grey (WHITE) ground is inserted at height 0.0. + The rectangle encloses all valid triangles. + - New command: wkmpt ROUTES: Print out the new internal cross references for + routes of KMP sections CKPH, CKPT, ENPH, ENPT, ITPH and ITPT. + - The settings of KMP sections CKPH, ENPH and ITPH are printed as "$SETTINGS" + line if not zero. They are also scanned and stored into binary KMP. Usually + only section ENPH of battle arenas use these settings. + - New feature for KMP sections ENPT and ITPT to support battle arenas: + @AUTO-CONNECT=MODE: Control the automatic calculation of PREV and NEXT + links. One MODE is DISPATCH: Connect routes and dispatch points + automatically controlled by different settings. THe default is to link all + routes, if no links is defined. + - Auto created group names of ENPT begin with 'DP' instead of 'G', if + DISPATCH mode is active. + - PREV links of KMP sections CKPH, ENPH and ITPH are printed as "$PREV" line, + if either @AUTO-CONNECT is changed or the PREV link lists is different from + automatic calculated list. + - New global option: --route-options: Disable or enable the new features + $SETTINGS, @AUTO-CONNECT, ... + - wkmpt CHECK and wszst CHECK improved: + - Info about arena classification: NONE, MAYBE, ARENA, ARREA+DISPATCH + - Battle arena: Hint about non empty CKPT and ITPT. + - Battle arena: Warnings about empty ENPT. + - Racing track: Warnings about empty CKPT, ENPT and ITPT. + - Check the number of KTPT elements and find missed or multiple defined + player indices for battle arenas. + - Check the number of MSPT elements and print warnings about too less or + hints about too much records. + - Check number and valid types of coin objects. + - Some camera warnings reduced to HINT. + - New global option: --battle=mode: MODE is either OFF, AUTO (default) or ON. + OFF and ON override the automatic battle detection. + - New commands to be used behind $GROUP: $PREV and $SETTINGS. + - New commands to fine control @AUTO-CONNECT: $DEF-CLASS, $CLASS, $AC-PREV, + $AC-NEXT and $ONEWAY. + - New keywords for option --kmp: DUMP-CLASS, DUMP-ONEWAY, DUMP-ALL: Dump + reference lists at the end of section ENPT and ITPT. + - Internal KMP GOBJ database updated by CT-Wiiki object table. + + +szs v1.61a r7601 - 2018-09-23 + + - Bug fix for the new route analysis (e.g. wkmpt DRAW): The positions were + wrong (e.g. NAN or too large) when analysing ITPT. + - New command: wszst ISARENA: Check the arena status of a SZS file, print a + status line and exit with an appropriate status code. + + +szs v1.62a r7620 - 2018-11-07 + + - Windows version only: Update to Cygwin 2.11.1. + - Cygwin bug fix: Color support failed, if tools started in a Windows console + window or as script outside a Cygwin window. + - The text parser (mainly used to scan KMP text files) supports the new data + type STRING. Different string functions and command @EXEC added. + - Battle arena detection improved. + - New command: wstrt HEXDUMP: Dump the content of a DOL file ordered by the + memory addres. Enter "wstrt help hexdump" to get a list of the many ouput + formatting options. + + +szs v1.62b r7624 - 2018-11-08 + + - Bug fix for new parser STRING support. + + +szs v1.62c r7630 - 2018-11-10 + + - wstrt --add-section FILE: If a single file is not found and the filename + (not path) contains at least 1 '@' character, then all '@' characters of + it are replaced by the region code (one of P,E,J,K) for a second try. + - New option for Command wstrt HEXDUMP: --sname=LIST: Select addresses to + dump by section names. + + +szs v1.62d r7634 - 2018-11-11 + + - DOL detection improved. + - Tool wstrt: After patching main.dol for Wiimmfi.de, the HTTP header + "Host: DOMAIN" is not longer sent twice. + - Tool wstrt: Patching and analysing of DOL files improved. + - Tool wstrt automatically patch main.dol to support the new login method + at Wiimmfi.de, when using option --wiimmfi. + + +szs v1.63a.beta1 r7657 - 2018-11-17 + + - Tool wstrt: The method to patch main.dol to support the new Wiimmfi login + changed: It don't use the Gecko Code Handler any more. + - The cheat code memory is allocated dynamically. It supports a total of more + than 90000 cheat code lines (>720kb). + - Behaviour of option --gct-move changed: It optionally accept one of the + keywords OFF, AUTO and ON. Without parameter, AUTO is used. + - A warning message is printed, if CT-CODE (option --add-ctcode) should be + added to a not original main.dol. + + +szs v1.63a r7664 - 2018-11-19 + + - Tool wstrt: All around Wiimmfi and CT-CODE patching, and around cheat code + management tuned and optimized. On the fly, some small bugs fixed. + Related options: --add-section --gct-move --gct-addr --gct-space + --add-ctcode --wiimmfi --domain --https --wc24 --force + + +szs v1.64a r7680 - 2018-11-23 + + - Tool wstrt: Wiimmfi patching optimized again, especially in combination + with CT-CODE. + - New option for tool wstrt: --clean-dol: Remove additional DOL sections, + reset entry point and VBI address, and try to restore other patches known + by SZS tools. This is done after loading a DOL and before analysing and + patching to allow re-patching. + - Error logging improved. + + +szs v1.65a r7690 - 2018-12-23 + + - Support of BMG files without section MID1. + - New Option: --bmg-mid=MODE: Control the creation of BMG section MID1. + MODE is one of OFF, AUTO (default) or ON. + - The impact of options --n-mipmaps, --max-mipmaps and --mipmap-size changed. + All 3 options together control the automatic creation of mipmaps. + Read https://szs.wiimm.de/info/mipmap.html for details. + - Option --smart is deprecated and ignored now. The new option --fast-mipmaps + will enable the old algorithm for images with odd width or with odd height. + Read https://szs.wiimm.de/doc/img/resize for details. + + +******************************************************************************* +******* END ******* +******************************************************************************* diff --git a/project/templates/HISTORY.txt b/project/templates/HISTORY.txt new file mode 100644 index 0000000..d546ea2 --- /dev/null +++ b/project/templates/HISTORY.txt @@ -0,0 +1,396 @@ + +@@MODULE(head.txt)@@ + + +******************************************************************************* +******* HISTORY v2.* ******* +******************************************************************************* + +szs v2.00a r7754 - 2019-02-22 + + - Windows version only: Update to Cygwin DLL 3.0.0 / 2019-02-16. + - A binary KCL with different section order is accepted now (like KMP before). + - The new option --repair-magics[=MODE] controls the reparing of missing or + wrong magics of files and sub-files by analysing file path and file + extension. MODE is one of OFF, ANALYSE or REPAIR. + - Command FLOAT accepts '+' and '-' to print the nearest successor and + predecessor of the previous value. + - New command: wszst VR-CALC: Calculate a VR increment for each given delta + value (=VR_winner-VR_loser). Print results as table. + - New command: wszst VR-RACE: Print a table with VR calculations for a race. + Each VR is the current rating of a player. + - wstrt analyse StaticR.rel: Detect modifications of cannon parameters. + - wstrt: New option: --cannon=IDX,P1,P2,P3,P4: Define parameters of cannon + with index IDX (0..2). + - New mode for option --patch-bmg: 'REGEX=list', where LIST is a semicolon + separated list of "/REGEX/REPLACE/" expressions (POSIX Extended Regex). + See https://szs.wiimm.de/opt/patch-bmg for details. + - New command: wbmgt REGEXP: Test implementation of regular expressions. + - New tool: wlect: Manage the LE-CODE (Leserattes CT-CODE) extension. + TOOL IS EXPERIMENTAL AND CAN'T BE USED WITHOUT ADDITIONAL FILES! + - Tool wctct accept LE-BIN files (LE-CODE binaries) as input. + - Global option --le-code: Enable LE-CODE (Leserattes CT-CODE) support for all + tools. It includes option --ct-code. + + +szs v2.01a r7798 - 2019-03-16 + + - Bug fix for BMG processing: Tools ran into endless loop, if an incorrect + 1A-escape tells a length of zero. + - BMG Filter option --msg renamed to the more meaningful name --filter-bmg. + The old name is still available. + - BMG Filter option --filter-bmg=modes supports more keywords now. + See https://szs.wiimm.de/opt/filter-bmg for details. + - Option --patch-bmg=mode[cond][=param]: The new optional condition COND is + either '?MID' or '!MID'. In case of '?MID', the patch is only applied if + the message id MID already exists. In case of '!MID', the patch is only + applied if the message id MID does not exists. + - New command: wszst NAME-REF: For each BRRES file or subfile, create a name + based cross reference and print the result as list in selectable order. + - Some minor bugs on arena name replication to CT/LE slots fixed. + - New option: --links: Support hardlinks if creating U8 and WU8 archives. + Keep hardlinks if normalizing U8 and WU8 archives. On extracting, search + hardlinks in every archive and try to create hardlinks at local file system. + - KMP object database updated by CT-Wiiki. + - LE-CODE support completed for the moment. + - Support for new file format LEX (LE-CODE extension). + Commands for tool wlect: CREATE, CAT, DECODE, ENCODE. + See http://wiki.tockdom.com/wiki/LEX for LEX details. + + +szs v2.01b r7802 - 2019-03-21 + + - Bug fix for v2.01a: The string support of the text parser had an memory + allocation bug. wctct uses the new string functions since v2.01 and + produces occasional (~25%) wrong CT-CODE files (strap files). + + +szs v2.02a r7827 - 2019-05-04 + + - New option for wlect: --move1-tracks: Files with more than one hard link + are copied and removed and not moved to create an unique version of the + file. + - New assigning operator for BMG text files: "MID1 : MID2": Copy attributes + and text of message MID2 to message MID1. Forward and chained references + are allowed. Multi message ids like 'T12' are supported on both sides. + - New escape sequence for BMG text files: \m{a,b,...}: Each parameter is + interpreted as message id. If a message with given ID exists (must be + defined before and must not have an assigned value), then insert the text + of the message. + - New option for wszst EXTRACT: --basedir=DIRECTORY: Extract only files from + archives, that are below the DIRECTORY. Leading points and slashes and + trailing slahes are removed from the parameter. --bdir is short cut. + - New command: wszst XCOMMON: Scan archives and extract files from archives, + that are below directory 'common'. Command XCOMMON is a short cut for + EXTRACT --basedir=common. + - New command: wszst ANALYZE: Analyse track files and print resulta in + machine readable format. + + +szs v2.03a r7854 - 2019-05-25 + - Command wszst CHECK: Hints about special files in subdirectory ./Common/. + - Command wszst/wkmpt CHECK: Hints about special GOBJ settings, that are + recognized by LE-CODE only. + - Bug fix for wszst ANALYZE --json: Object names are enclosed by quotes now. + - New option: --macro-bmg=file: Load a BMG file and add or overwrite the + messages to the macro library. The macro library is accessed by escape + sequence \m{MID} as fallback. + - New escape sequence for BMG text files: \M{a,b,...}: Each parameter is + interpreted as message id. If a message with given ID is defined by the + macro library, then insert the text of the message. + - New command: wkmpt KTPT: Analyse the start positions (KMP:KTPT) and lap + counters (KMP:CKPT) and print a simple information list. Use enemy points + (KMP:ENPT) to find recommendations. This command is dedicated to LE-CODE, + which supports an alternative position for the finish line at the minimap. + - New patch option for wszst and wkmpt: --ktpt2=vector + Insert or replace a second KTPT. Use VECTOR as new position. Then find + nearest lap counter (CKPT with mode 0) and adjust position and direction. + + +szs v2.04a r7864 - 2019-06-02 + + - New option for wszst and wimgt: --cmpr-default=rgb1[,rgb2] + Define 2 colors for the case, that all 16 pixels of a CMPR block are + transparent. The default is to calculate an average color of all + transparent pixels. Before v2.04 white was used. + - Command wszst DISTRIBUTION: Added battle arena support. + - Option --bmg-inline removed. It is deprecated since 2015. + + +szs v2.05a r7879 - 2019-06-09 + + - Warnings for not supported versions and for wrong number of sections of + BRRES sub files (CHR, CLR, MDL, PAT, SCN, SHP, SRT and TEX) implemented. + Some of these invalid sub files will freeze the Wii: + - New command: wszst BRSUB: If no file is defined, print a table with + known BRRES sub files consisting of file format, version, number of + sections and warn message. Otherwise analyse the version number and + number of sections of each BRRES sub file of each source and print + warnings about invalid sub files. This is like command CHECK, but only + for BRRES sub files. + - Command wszst CHECK will check BRRES files now. For SZS files, all BRRES + sub files are checked. At the moment only valid version numbers and + number of sections are tested. + - Command wszst LIST: If displaying version numbers, invalid version + numbers for BRRES sub files will be marked by an appended "!" and + highlighted by magenta background if colors are activated. + - Bug fix for wszst DISTRIBUTION: Scanning of slot numbers failed. + - Option --ktpt2 accepts keyword AUTO now and calculates the position + automatically. The result is the recommendation of command wkmpt KTPT. + + +szs v2.06a r7905 - 2019-07-01 + + - Bug fix for wszst CHECK: The summary will include BRRES error counters now. + A summary line is also printed, if a standalone BRRES file is checked. + - New command: wlect DL: Short cut for: wlect DUMP --long + - New command: wlect DLL: Short cut for: wlect DUMP --long --long + - Tool wlect: Option --perf-mon=FORCE enables performance counter for Dolphin. + - Command wlect DUMP --verbose: Slot usage map is colored now. + - Command wszst ANALYZE prints more details now. + - CT-CODE definition files: Added additional and optional field IDENTIFIER to + optimize track-to-cup references. + - Support for outdated LE-CODE phase 1 removed. + - Bug fix for option --ktpt2: KMP is marked as modified now, so that the + KMP of the source file is updated.. + - Support of files ObjFlow.bin and GeoHitTable*.bin (binary and text). + - New commands: wszst BINARY and wszst TEXT: Both commands work like the + ENCODE and DECODE commands of the other tools. The main idea is to support + simple text-to-binary and binary-to-text conversions of new file formats by + tool wszst instead of creating own tools for every new file format. + - Command wszst BINARY: Read each source file (binary or text, but not an + archive) and write it as binary file. + - Command wszst TEXT: Read each source file (binary or text, but not an + archive) and write it as text file. + - Currently supported file formats are BMG, GEOHIT, KCL, KMP, LEX, + OBJFLOW, PAT and the related text formats. + - New option: wszst --delta: Print only modified records on text output. + Supported file formats until now: GEOHIT, OBJFLOW + + +szs v2.07a r7960 - 2019-08-30 + + - New file formats to distinguish GeoHitTable*.bin (standard variant) from + GeoHitTable*Obj.bin ("Obj" variant). + - Command wszst TEXT for OBJFLOW and GEOHIT updated to support new file + formats and new features. + - If extracting an U8 or WU8 archive, then course.kcl.*, course.kmp.txt and + course.lex.txt are always added to the exclude list of control file + wszst-setup.txt. Files course.kcl, course.kmp and course.lex are added to + the encode list. Before the files were only added, if the binary file + exists and option --decode was set. + - Command wlect CREATE CANNON: Create a LEX text file with cannon section + only. + - Command wszst ANALYZE updated to support calculated attributes of tracks + for web site https://ct.wiimm.de/ + - BMG section INF1: Maximum attribute length increased from 16 to 32 bytes. + - BMG: The tools support only BMG encoding #2 (UTF-16/be) fully. Encoding #1 + (CP1252) can be read, but not be stored as binary BMG file. Error messages + are printed for unsupported encodings. + - BMG: Unknown parameters of sections INF1 and MID1 are scanned and written. + - New BMG option: --bmg-encoding=NAME: Force a BMG encoding. It is only + relevant if creating a BMG file. Accepted encoding names are: CP-1252, + UTF-16BE (or UTF-16), SHIFT-JIS, UTF-8 and AUTO for automatic detection + (default). Same keywords without minus signs are accepted too. + At the moment, only encodings CP-1252 and UTF-16BE are supported on reading + and only encoding UTF-16BE on writing a binary BMG. MKWii uses UTF-16BE. + - New command: wbmgt EXTRACT name source: Extract BMG section NAME of file + SOURCE and write the binary data to standard output. If NAME consists of + less than 4 characters, then the first section that starts with these + characters is dumped. + - New command: wbmgt SECTIONS source...: List all sections of each binary + BMG file. Print offset, size, magic and a short info. If known, print + number and size of elements too. + + +szs v2.08a r7979 - 2019-09-05 + + - BMG: Encoding of types CP1252, UTF-16be, Shift-JIS and UTF-8 are fully + supported now. + - New command: wbmgt SLOTS source...: Print a slot ordered list with slot + number, message id, delta and attributes. + - BMG supports predifined slots now. These slot assignments are detected + automatically while reading binary BMG files. Slot assignments are saved to + and scanned from BMG text files. + - BMG text files: Each message ID can be followed by '@SLOT' to force a slot + number for the message. + - BMG: Unknown sections are stored as hex dumps into BMG text files. The hex + dumps can be edited and are scanned on reading. Each section is started by + parameter "@SECTION NAME". + - BMG section INF1: Maximum attribute length increased from 32 to 40 bytes. + + +szs v2.09a r8000 - 2019-09-24 + + - BMG section "STR1" detected, but not supported. Metrics are displayed. + - Commands wszst BINARY/TEXT: Full support of files minigame.kmg and + ItemSlot.bin (binary and text). + - Command wszst ANALYZE returns 5 different SHA1 checksums now. + + +szs v2.10a r8023 - 2019-11-23 + + - Command wszst TRACKS: More options. + - Command wszst ANALYZE: More results. + - wszst ANALYSE + Check KMP: Analysis of KTPT, ENPT and ITPT to find values + outside range +-131071 where items can not be used online. + - Text export of ItemSlot.bin: A comment column with row summaries added. + - Cygwin (Windows) version: Updated color support again. + + +szs v2.11a r8074 - 2020-01-18 + + - wszst ANALYSE --sh --array: Output format fixed. + - New keywords for option --filter-bmg: ALLCODE, ALLCUPS, ALLTRACKS, + ALLARENAS and ALLREFS. + - The optional parameter of option --le-code (LE-CODE phase) is deprecated + and ignored. From now on, option --le-code always enables LE-CODE (phase 2) + and CT-CODE support. + - Deprecated and useless option --smart removed. + - KMP/GOBJ, ID: Support of objects flags in bits 10-15 (0xfc00) for LE-CODE. + - File course.lex: Support of new section TEST. It forces game modes like + "Online, 12 players, itemrain" to test them in standard offline versus. + - wlect CREATE: New keywords. Call the command without keyword to get a list. + - wszst+wlect: New options to create, modify or delete course.lex and/or LEX + section TEST: --lt-clear, --lt-online, --lt-n-players and --lt-game-mode. + So patching a track file to test special conditions becomes easy. + - General support of 'extended presence flags'. + - KMP/GOBJ: Detect definition-objects and references. + - New commands for KMP/GOBJ for easier handling: $DEFOBJ-RACE and + $DEFOBJ-BATTLE define a definition-objects. $REFERENCE defines a + reference (ref_id and mode=1) for the previous defined object. + - Hints and warnings about extended objects. + - New command: wkmpt GAMEMODES: Analyze the presence flags combinations with + and without extension support and print a list. + - New command: wszst DUPLICATE: It reads a single track file and duplicates + it several times. Therefor it analyzes the presence flags combinations with + and without extension support and creates one file for each relevant + scenario. A LEX section TEST will be inserted to force the execution mode + for offline tests. + - New option for commands wkmpt GAMEMODES and wszst DUPLICATE: + --gamemodes=list: Select game modes for the list. + - See http://wiki.tockdom.com/wiki/Extension_of_Presence_Flags for details + about the new Extension of Presence Flags. + - Scanning a KMP file now takes into account the new information about the + length of the file header. + + +szs v2.11b r8079 - 2020-01-20 + + - XPF, hard coded conditions: New conditions added. Gaps in list removed. + Order changed. These changes invalidate binary KMP files with hard coded + conditions created by previous v2.11a. + - Small text corrections in the new texts of the previous version. + + +szs v2.12a r8129 - 2020-02-07 + + - Parser: Bug fix for shift-right operator if shifted value is an integer. + - If using --force or --kmp=force, the KMP scanner ignores the size stored in + the file header and uses the file size instead. + - New command: wkmpt diff: Compare the KMP of two or more sources (KMP, + KMP-text or SZS). Each KMP will be converted into the internal format, + fixed and patched controlled by options. The known sections are compared, + some of them sorted before. + - New option to support wkmpt diff: --diff=list: List of KMP sections names + that should be compared. Additional keywords possible. + - New option to support wkmpt diff: --epsilon=float: When comparing floating + point numbers, 2 numbers are considered identical if their difference is + less than or equal to epsilon. + - New option: --repair-xpflags: Load a KMP file and use it to repair the + settings of the extended presence flags, if they are destroyed by another + KMP tool. --repxpf is a short cut for this option. + - Option --auto-add was broken for objects, if flags (bits 10-12) were set. + Bit 12 is used by extended presence flags (XPF). + - Support of random selections of KMP/GOBJ objects: + - 6 random scenarios supported. + - New keyword for option --gamemodes: RANDOM. + - Command wkmpt GAMEMODES can list and command wszst DUPLICATE can create + up to 1573 variants now. + - Added a LEX/TEST parameter to force a random mode for testing. + - New LEX section SET1 for various settings. The first parameter defines 3 + factors to increase the range for items outside -131071 and +131071. + - New LEX section HIPT: Define places, where the position tracker is hidden. + + +szs v2.12b r8135 - 2020-02-09 + + - Bug fixes for LEX parser: + - Section TEST: Parameter @ENGINE wasn't implemented. + - Symbols LEX$OO_AUTO and LEX$GM_AUTO were not defined. + - New option: --lex-purge: Delete LEX sections without any impact. Delete + sub-file course.lex if it no longer contains a section. + - Tool wstrt option --clean-dol: HTTPS modifications are now also reset. + + +szs v2.13a r8146 - 2020-02-15 + + - XPF: Added support for negation flag of references to definition objects. + - New keywords for option --kmp: + - RM-LECODE: Remove all objects from section GOBJ, that are only relevant + for LE-CODE. The result is a minimized section for standard code. + - PURGE-GOBJ: Remove all objects from section GOBJ that are neither a + valid object nor a definiton object for LE-CODE. + - wszst ANALYSE + Check KMP: Analysis of KTPT, ENPT and ITPT to find values + outside range +-131071 where items can not be used online. Additionally a + recommendation for using option --shift and/or LEX section SET1 is printed. + + +szs v2.14a r8160 - 2020-02-29 + + - KMP text processor: Settings of definition objects are printed in hex now. + - Bug fix for KMP text parser: It created incorrect conditions if definition + objects were defined in combination with $DISABLE or $ENABLE commands. + - Bug fix for wstrt option --add-section=GCT: Crashed for some invalid cheat + codes (Cygwin only). + - Recommendation for option --shift item pos factors (LEX:SET1) optimized. + - wimgt COPY: Option --overwrite is set implicitly now. + + +szs v2.15a r8176 - 2020-04-03 + + - KTPT is not longer used to find values outside range +-131071 where items + can not be used online. + - Text parser: Fixed different bugs for data type 'string'. + - Slot recommendations now ignore the objects deactivated by presence flags. + - Chat in private rooms: Each special function can be assigned to any chat + message, and also to more than 1 message. Before, the function werde hard + coded to some messages. Current modes: + - Host selects track. + - Karts or bikes only. + - Force engine class. + - Select number of races between 1 and 512. + - Support of new text file format LPAR to manage LE-CODE parameters by file + instead of options. This includes the support of the new parameters for + chat message modes: + - wlect CREATE lpar: Create a standard LPAR text file. + - wlect LPAR: Read LE-CODE binary and export the parameters as LPAR file. + - wlect DUMP: New section for "Special chat messages". + - Option --lpar: Read a LPAR file to patch the current settings. + + +szs v2.16a r8184 - 2020-04-09 + + - All commands, that create LPAR files, accept option --no-header (-H) and + option --brief (-B) now. + - More built-in explanations in new LPAR files. + + +szs v2.17a r8204 - 2020-05-12 + + - Command wszst DISTRIBUTION: New track attribute FILL to mark a track as + filler (some tracks are needed to fill the last cups). + - LPAR BLOCK-TRACK: Block previously used tracks for the next 0-50 races. + - LPAR: More chat messages and up to 2 modes for each chat message. + - Object files of CommonObj*.szs are included into the internal object + library and added to auto-add library. + - Objects 'VolcanoPiece', 'bblock' and 'RM_ring1': Setting #1 is evaluated to + find missed files. This has impact to command CHECK and option --auto-add. + + +@@MODULE(last-history.txt)@@ + + +******************************************************************************* +******* END ******* +******************************************************************************* diff --git a/project/templates/MILESTONES.txt b/project/templates/MILESTONES.txt new file mode 100644 index 0000000..273855f --- /dev/null +++ b/project/templates/MILESTONES.txt @@ -0,0 +1,153 @@ + +szs v0.01a.beta r2385 - 2011-03-31 + - First version of tool 'wszst' (Wiimms SZS Tool) with the commands LIST, + COMPRESS, DECOMPRESS, EXTRACT and CREATE. + +szs v0.02a r2417 - 2011-04-04 + - First version of tool 'wbgmt' (Wiimms BGM Tool) with the commands LIST, + PATCH, DECODE and ENOCDE. + +szs v0.04a r2457 - 2011-04-09 + - wbmgt: Support of Nintendos 0x1a escape in BGM/TEXT as "\z{head,value}". + - New command: wszst NORMALIZE: Resort and recompress an U8 archive like + Nintendo does it. Best custom compression at all. + +szs v0.09a r2636 - 2011-05-17 + - First version of tool 'wstrt' (Wiimms StaticR Tool). It allows to rearange + the slot order for tracks and arenas. + +szs v0.11a r2670 - 2011-06-04 + - Extraction of BREFF and BREFT files. + +szs v0.13a r2840 - 2011-07-09 + - First version of tool 'wimgt' (Wiimms Image Tool). It will convert BREFT + subfiles, TPL, TEX0 and PNG to each other format. Supported image formats + are I4, I8, IA4, IA8, RGB565, RGB5A3, RGBA32, C4, C8, C14X2 and CMPR. + +szs v0.15a r2917 - 2011-08-07 + - First version of tool 'wkmpt' (Wiimms KMP Tool). The decoder creates a + user friendly text file and the encoder a binary file. + +szs v0.19a r3033 - 2011-09-15 + - Implementation of a parser with arithmetic expressions and nested + @IF..@ELIF..@ELSE..@ENDIF. The parser is used to scan KMP text files. + - First support of an internal Object DB (copy of Wiki page 'Object'). + +szs v0.22a r3166 - 2011-11-06 + - The KMP encoder is now a 2 pass compiler and the whole source is scanned + twice. In pass 1 only names are defined and warnings are suppressed. In + pass 2 all objects are defined. + +szs v0.23a r3243 - 2011-11-25 + - Support of 3D vector variables and parser functions. + +szs v0.26a r3415 - 2012-02-04 + - First version of tool 'wkclt' (Wiimms KCL Tool). It can only create OBJ + files. + +szs v0.28a r3463 - 2012-02-20 + - Improved usage of Object DB and first implementation of the auto-add archive + to add missing objects automatically. + - The parser supports now much more functions. + +szs v0.29a r3515 - 2012-02-28 + - First implementation of 'wkmpt CHECK' to make KMP plausibility checks. + +szs v0.33a r3627 - 2012-05-02 + - The text parser supports now 5 kinds of loops. + +szs v0.34a r3659 - 2012-05-12 + - The text parser supports now user defined macros and functions and a new + private name space. + +szs v0.36a r3720 - 2012-05-28 + - The tools can now create a KCL file from scratch ony by importing an OBJ + file. The internal octree generator works fine and can be controlled by + different constants. + - KCL scripts are supported (option --ksl-script=file). + +szs v0.42a r3927 - 2012-07-06 + - KCL (and octree) creator are now very good. Last importand issues are + fixed. The octree cubes overlap now. + - KCL trasformation is enabled. + +szs v1.01a r3987 - 2012-07-22 + - Automated adjusting of minimaps is now possible. + +szs v1.02a r3992 - 2012-08-01 + - KCL: Detection of invalid triangles and face down roads implemented. Both + kinds can be removed controlled by option --kcl=modes. + +szs v1.05a r4097 - 2012-09-29 + - For text files, only the first 4 characters are used as magic ('#BMG' + instead of '#BMG-TXT'; same for '#KCL', '#KMP' and '#MDL'). On creation + a 8 character magic is still written for compatibility. + - Files './AIParam/AIParam.baa' and './AIParam/AIParam.bas' are now known by + the internal file database. + +szs v1.06a r4110 - 2012-10-06 + - wszst CHECK will now make slot proposals with comments. + - New command: wszst SLOTS: Print the possible slots for a track file. + +szs v1.07a r4137 - 2012-10-19 + - New archive format: WU8 (Wiimms U8). + +szs v1.09a r4169 - 2012-11-11 + - BZIP2 compression is now supported as alternative to Yaz0. It is dedicated + to the WU8 file format for transferring files. BZIP2 files are smaller than + Yaz0 files and the creation is faster. + - First WBZ support. + +szs v1.13a r4258 - 2013-03-01 + - New option --rm-aiparam (short cut: --rmai): If set, the directory AIParam + and all files are removed from the archive. + +szs v1.17a r4358 - 2013-04-23 + - 3D rotation for all 3 axis and transformation of whole tracks. + +szs v1.21a r4578 - 2013-09-07 + - A very old well known bug is now fixed: The old versions couldn't create + the language independent SZS files of MKWii directory './Scene/UI' like + Track.szs, Race.szs and others. + +szs v1.22a r4602 - 2013-09-14 + - BMG text patching of SZS files is now possible without extract, patch and + recreate using the command "wszst PATCH --patch-bmg ..." + +szs v1.23a r4682 - 2013-10-05 + - CT-CODE support to manage CT distributions with >32 tracks. + +szs v1.34a r5070 - 2014-04-19 + - Direct slot transforming of tracks with option --slot=NAME. + +szs v1.35a r5184 - 2014-05-16 + - Support of the new custom server 'wiimmfi.de'. + +szs v1.40a r6162 - 2015-04-13 + - Support for PACK archives & PAT0 files. + - New tool: wpatt + +szs v1.44a r6558 - 2015-10-17 + - Advanced BMG support with larger attributes. + +szs v1.45a r6602 - 2015-10-31 + - Support of external CT-CODE files. + - Update to current CT-CODE version. + +szs v1.57a r7484 - 2018-04-11 + - Removal of very small triangles to avoid KCL glitches. + +szs v1.60a r7578 - 2018-08-21 + - Advanced support of battle arenas. + - Support of dispatch points (enemy routes) + +szs v2.00a + - Support of LE-CODE (Leserattes CT-CODE). + - VR calculations. + +szs v2.01a + - First LEX support. + +szs v2.08a + - Support of all known BMG encodings (CP-1252, UTF-16, SHIFT-JIS and UTF-8). + diff --git a/project/templates/forums/mariokartwii.txt b/project/templates/forums/mariokartwii.txt new file mode 100644 index 0000000..6d0203b --- /dev/null +++ b/project/templates/forums/mariokartwii.txt @@ -0,0 +1,25 @@ +I have released [color=#000080]Wiimms SZS Tools v1.40a[/color]. +[color=#800000](A [url=https://szs.wiimm.de/doc/cli]command line[/url] tool set for Linux, Mac and Windows)[/color] + +[color=#800000][b]What's new:[/b][/color] +[list] +[color=#ffffff].[/color] +[/list] + +[color=#800000][b]Change Log:[/b][/color] +[code] +[/code] + + +[color=#800000][b]Links:[/b][/color] +[list] +[*] [url=https://szs.wiimm.de/features.html][color=#000080][b]Features[/b][/color][/url] +[*] [url=https://szs.wiimm.de/download.html][color=#000080][b]Download[/b][/color][/url] +[*] [url=https://szs.wiimm.de/doc/cli][color=#000080][b]Command Line Interface (using the tools)[/b][/color][/url] +[*] [url=https://szs.wiimm.de/doc/kcl][color=#000080][b]KCL Guide to export and import object files[/b][/color][/url] +[*] [url=http://wiki.tockdom.com/wiki/Wiimms_SZS_Tools][color=#000080][b]Custom Mario Kart Wiiki[/b][/color][/url] +[*] [url=http://wiki.tockdom.com/wiki/WU8][color=#000080][b]File Format WU8[/b][/color][/url] (track patch) +[*] [url=http://wiki.tockdom.com/wiki/WBZ][color=#000080][b]File Format WBZ[/b][/color][/url] (BZIP2 compression) +[*] [url=https://szs.wiimm.de/cgi/mkw/object][color=#000080][b]KMP Object Query[/b][/color][/url] +[*] [url=https://szs.wiimm.de/cgi/mkw/track-ref][color=#000080][b]MKW Track Cross Reference[/b][/color][/url] +[/list] diff --git a/project/templates/module/features-de.forum b/project/templates/module/features-de.forum new file mode 100644 index 0000000..bfc768a --- /dev/null +++ b/project/templates/module/features-de.forum @@ -0,0 +1,6 @@ +[list] +[*][url=@@URI-HOME@@features.html#1][b]SZS (Wiimms SZS Toolset) ist eine Sammlung von Kommandozeilen-Programmen.[/b][/url] +[*][url=@@URI-HOME@@features.html#3][b]wszst (Wiimms SZS tool) ist das Hauptprogramm zu Manipulation von SZS-Dateien.[/b][/url] +[*][url=@@URI-HOME@@features.html#4][b]wbmgt (Wiimms BMG tool) ist das Hauptprogramm zu Manipulation von BMG-Dateien.[/b][/url] +[*][url=@@URI-HOME@@features.html#5][b]Besondere Unterstützung von Script-Sprachen.[/b][/url] +[/list]Besuche [url=@@URI-HOME@@features.html][b]@@URI-HOME@@features.html[/b][/url] für mehr Details. diff --git a/project/templates/module/features-en.forum b/project/templates/module/features-en.forum new file mode 100644 index 0000000..84b11d7 --- /dev/null +++ b/project/templates/module/features-en.forum @@ -0,0 +1,6 @@ +[list] +[*][url=@@URI-HOME@@features.html#1][b]SZS (Wiimms SZS Toolset) is a set of command line tools.[/b][/url] +[*][url=@@URI-HOME@@features.html#3][b]wszst (Wiimms SZS tool) is the main SZS manipulation tool.[/b][/url] +[*][url=@@URI-HOME@@features.html#4][b]wbmgt (Wiimms BMG tool) is the main BMG manipulation tool.[/b][/url] +[*][url=@@URI-HOME@@features.html#5][b]Advanced shell support.[/b][/url] +[/list]Visit [url=@@URI-HOME@@features.html][b]@@URI-HOME@@features.html[/b][/url] for more details. diff --git a/project/templates/module/features.txt b/project/templates/module/features.txt new file mode 100644 index 0000000..5079f58 --- /dev/null +++ b/project/templates/module/features.txt @@ -0,0 +1,13 @@ +Features: + + - SZS (Wiimms SZS Toolset) is a set of command line tools. + - wszst (Wiimms SZS Tool) is the main SZS manipulation tool. + - wimgt (Wiimms IMG Tool) is a graphics conversion tool. + - wkclt (Wiimms KCL Tool) is a KCL creation and modification tool. + - wkmpt (Wiimms KMP Tool) is a KMP creation and modification tool. + - wbmgt (Wiimms BMG Tool) is a BMG manipulation tool. + - wstrt (Wiimms STATIC.REL Tool) is a staric.rel and main.dol manipulation tool. + - wctct (Wiimms CT-CODE Tool) manage CT-CODE extensions. + - Advanced shell support. + +Visit @@URI-HOME@@features.html for more details. diff --git a/project/templates/module/head.txt b/project/templates/module/head.txt new file mode 100644 index 0000000..0c0fe9c --- /dev/null +++ b/project/templates/module/head.txt @@ -0,0 +1,21 @@ +******************************************************************************* +******* Wiimms SZS Tools v@.@@@ - @@@@-@@-@@ ******* +******************************************************************************* + +Wiimms SZS Tools (SZS) is a command line tool set for linux and for other +unix like operating systems including Cygwin. The tools manage SZS files. + +The software is developed under the GPL 2.0 license. See file gpl-2.0.txt +or http://www.gnu.org/licenses/gpl-2.0.txt for details. + +The most current source is available under: + @@URI-SOURCE@@/ +~ +~The most current source is available under: +~ @@URI-VIEWVC@@/ +~The source of this revision (@@REV@@) is available under: +~ @@URI-VIEWVC@@/?pathrev=@@REV-NEXT@@ +~You can also checkout the SVN repository: +~ @@URI-REPOS@@/ + +See @@URI-HOME@@ for announcements and discussions. diff --git a/project/templates/module/last-history.txt b/project/templates/module/last-history.txt new file mode 100644 index 0000000..97a5e29 --- /dev/null +++ b/project/templates/module/last-history.txt @@ -0,0 +1,33 @@ +szs v@@VERSION@@ r@@REV-NEXT@@ - @@@@-@@-@@ + + - wszst/wkmpt CHECK: Hints about self linked item routes and item routes with + exact 1 point. A warning is printed when both events apply for same route. + - Bug fix: The number of tables is set to 6 if writing a short binary + item-slot file (without tables for battles). + - wszst TEXT/BINARY: New options to patch KMG files: --kmg-limit=seconds to + limit the run time of battles and --kmg-copy=id to copy one slot definition + to the 9 other slots. + - Leseratte and I analysed the MKW internal VR calculations and adapted the + results into the tools. So the calculations are 100% identical now. + - Command wszst VR-RACE supports now up to 30 players for the VR calculation. + - KMP/CAME type 6 and the related route is excluded from any transformation. + +~ - MDL support: Scanning a MDL file and transferring the data into an internal +~ format was already implemented, but only used for analysis purposes. Now it +~ is possible to create a binary MDL by reading the internal data. This is +~ the first step to add MDL materials to enable a track for slot 4.2. +~ - The tools verify the maximum number of CKPT, ENPT and ITPT (=255). + +Known bugs: + + - Minimaps with an additional bones layer above 'posLD' and 'posRU' (e.g. for + scaling) are handled incorrect on auto adjusting the minimap. + - The tools try always to find the optimal PNG format by analyzing the main + image. The bug is, that mipmaps (or following images in TPL) may need a + different output format (for example colors instead of grayscale). + SOLUTION: Force an output format with for example --transform RGBA32. +~ - Creating a BRRES from local files is buggy, because missing adjust of +~ several BRRES and string pool pointers. +~ - If creating a BRRES the internal name of the sub file is not set to +~ the filename. This could be problematic if a sub file was renamed. +~ diff --git a/project/text-files/common.inc/explain-objmgr-cr.inc b/project/text-files/common.inc/explain-objmgr-cr.inc new file mode 100644 index 0000000..b123052 --- /dev/null +++ b/project/text-files/common.inc/explain-objmgr-cr.inc @@ -0,0 +1,33 @@ + +#------------------------------------------------------------------------------ + +# Usually, only the data of objects defined by Nintendo can be replaced. +# It is not possible to remove or add an object, or to replace an object +# with one ID by another object with another ID. The following settings +# override this protection for experimental usage. + +# @NEW-OBJECT = MODE +# MODE is one of 4 settings: +# NEWOBJ$OFF: +# Disable experimental mode. This is the default. +# Ignore @OBJECT-SLOTS and @MAX-OBJECT-ID. +# NEWOBJ$REPLACE: +# Allow new objects, but keep the total number of slots +# at 0x13c. Ignore @OBJECT-SLOTS. +# NEWOBJ$SHRINK: +# Allow new objects and removal of old objects. The total number +# of slots may shrink, but not grow above 0x13c. +# NEWOBJ$GROW: +# Allow new objects and removal of old objects. The total number +# of slots may shrink or grow (limit 0x400). + +# @OBJECT-SLOTS = NUMBER +# Number of available objects slots. The default is 0x13c. Up to +# 0x400 slots are supported. This setting is ignored for some modes +# of @NEW-OBJECT. + +# @MAX-OBJECT-ID = NUMBER +# Define the maximim allowed object id. The default value is 0x2f3 and the +# maximal possible value is 0x3ff. +# Objects with id larger than this value are ignored. The value defines the +# size of the reference list of section 2 too. diff --git a/project/text-files/ctcode.inc/ctcode-head-cr.inc b/project/text-files/ctcode.inc/ctcode-head-cr.inc new file mode 100644 index 0000000..276b43a --- /dev/null +++ b/project/text-files/ctcode.inc/ctcode-head-cr.inc @@ -0,0 +1,27 @@ +#CT-CODE + +[INFO] +# This section will be ignored on scanning. + +# This is a textual representation of CT-CODE file used by CTGPR made by Chadderz +# and MrBean3500vr. "Wiimms SZS Tools" can convert binary (raw) and text CT-CODE +# files in both directions. The text parser supports variables, C like expressions +# and nested IF-THEN-ELSE and LOOP structures. +# +# Info about the general parser syntax and semantics: +# * https://szs.wiimm.de/doc/syntax +# +# Info about the CT-CODE: +# * https://szs.wiimm.de/doc/ctcode +# +# Reference list of CT-CODE parser functions: +# * https://szs.wiimm.de/doc/ctcode/func +# +# Hint: +# Set the options --no-header (-H) while decoding to suppress this info and some +# syntax descriptions below. + +#---------------------------------------------------------------------------------- +# Only the section [RACING-TRACK-LIST] is scanned, all other sections are ignored! +#---------------------------------------------------------------------------------- + diff --git a/project/text-files/ctcode.inc/ctcode-list-syntax-brief-cr.inc b/project/text-files/ctcode.inc/ctcode-list-syntax-brief-cr.inc new file mode 100644 index 0000000..16b0d1e --- /dev/null +++ b/project/text-files/ctcode.inc/ctcode-list-syntax-brief-cr.inc @@ -0,0 +1,20 @@ + +# Syntax: +# '%' name = value +# : Define an option. +# 'N' mode : Define mode for Nintendos original tracks: +# N$NONE | N$HIDE | N$SHOW | N$SWAP +# OR it with N$F_HEX and/or N$F_WII and/or N$F_ +# 'C' name : Enter a new cup and define its name. +# 'S' list : Predefine track slots for the next tracks. +# 'X' list : Mark slots as 'unusable'. +# 'T' music_id; property_id; file_name; [info;] [id] +# 'T' music_id; property_id; le_flags; file_name; [info;] [id] +# : Define a racing track, See https://szs.wiimm.de/doc/slot-id +# for 'music_id' and 'property_id'. +# 'H' music_id; property_id; file_name; [info;] [id] +# 'H' music_id; property_id; le_flags; file_name; [info;] [id] +# : Define a hidden track, that doesn't appear in any cup. +# 'A' music_id; property_id; file_name; [info;] [id] +# 'A' music_id; property_id; le_flags; file_name; [info;] [id] +# : Define a battle arena. diff --git a/project/text-files/ctcode.inc/ctcode-list-syntax-full-cr.inc b/project/text-files/ctcode.inc/ctcode-list-syntax-full-cr.inc new file mode 100644 index 0000000..e1e4fb2 --- /dev/null +++ b/project/text-files/ctcode.inc/ctcode-list-syntax-full-cr.inc @@ -0,0 +1,141 @@ + +#--------- +# Syntax: +#--------- +# +# 8 kinds of records are allowed: %, N, C, S, X, T, H and A +# +# * Option: '%' name = value +# +# Set option NAME to VAULE. The follwing options are supported: +# +# LE-FLAGS = 0 | 1 +# Enable or disable support of LE-FLAGS member. At section start +# it is disabled. If enabled, the Track definition expects an +# additional parameter between 'property_id' and 'file_name'. +# +# * Nintendo track mode: 'N' mode +# +# This command can only be used once and only before the first track +# definition. It defines, how to manage Nintendos original tracks. +# A numerical expression is expected. The 4 symbols are predefined: +# +# N$NONE : Nintendos tracks will NOT inserted automatically. +# This is the default. +# +# N$HIDE : Nintendos tracks will be defined, but the tracks are not +# shown in any Cup. If such track is selected by accident, +# it can be played. +# THIS MODE IS EXPERIMENTAL AND WILL FREEZE THE WII! +# +# N$SHOW : Define automatically 8 Cups with Nintendos tracks. +# +# N$SWAP : Define automatically 8 Cups with Nintendos tracks, but +# change the cup order, so that all 4 Wii Cups are in the +# first row and all 4 retro cups in the second row. +# +# For HIDE+SHOW+SWAP: +# The cup and track names are defined by internal names. If BMG is +# read, messages may override the internal names. +# The track order (except SWAP), file names, music and property ids +# are the same as in original MKWii. +# +# There are some flags, that are OR'ed to the base mode: +# |N$F_HEX : Replace original filenames by a 2 digit slot hex code. +# |N$F_WII : Prefix Wii track names by 'Wii ' +# |N$F_ : For BMG output, colorize the console prefix. Possible +# color names are: RED1, RED2, RED3, RED4, YELLOW, GREEN, +# BLUE1, BLUE2, WHITE and CLEAR (transparent). +# +# * Cup definition: 'C' name_of_cup +# +# Close the current cup and fill it with default tracks. Then define a +# cup name for the next created cup. This command is optional and only +# used, to store names in the CT-CODE file without any impact. +# +# * Slot definition: 'S' list +# +# Define the track slots for the next defined tracks. A list element is +# either an index 'NUMBER' or a range 'NUMBER1:NUMBER2'. The system will +# automatically skip invalid and already used slots. All numbers are +# numerical expressions handled by the parser. If a number or range is +# followed by an '!', the slots are used for the next tracks ignoring +# previous usage or reservation. +# +# Each slot definition overrides the previous one and an empty list will +# disable the slot list. Slot definiton are only needed to recreate a +# CT-CODE file with identical track layout. +# +# * Used definition: 'X' list +# +# Mark the slots as 'reserved'. A list element is either an index 'NUM' +# or a range 'NUMBER1:NUMBER2'. Only unused slots will be marked. All +# numbers are numerical expressions handled by the parser. If a number +# or range is followed by an '!', the slot is marked as used independent +# of its previous state. +# +# The line "X 0x20:0x2b 0x43 0xff" is executed internally at startup. +# +# * Track definition: +# 'T' music_id ';' property_id ';' file_name ';' info ';' id +# 'T' music_id ';' property_id ';' le_flags ';' file_name ';' info ';' id +# +# Define a new track. Use the next free slot defined by the previous +# slot definition, or the first not used slot. The elements are: +# +# * music_id +# The numeric ID of the sound file, a numerical expression is +# expected. The ID is normalized to avoid impossible values. +# Track numbers (0-31) are translated to the standard music ID. +# If a music ID is followed by an '!', the value is used without +# normalization and transformation. +# See https://szs.wiimm.de/doc/slot-id for infos about names. +# +# * property_id +# The numeric ID of a basic slot (0-31) for the properties. +# A numerical expression is expected. Only the lowsest 5 Bits are +# used to avoid impossible values. If a property ID is followed +# by an '!', the value is used as entered. +# See https://szs.wiimm.de/doc/slot-id for infos about names. +# +# * le_flags +# The flags are only allowed and mandatory, if option LE-FLAGS +# (see above) is enabled. It is a bit field; combine flags by OR. +# The flags are delivered to LE-CODE: +# LE$F_NEW : Track is marked as »new track«. +# LE$F_HEAD : Track is marked as »head of a group«. If such +# track is selected by user, a random selection +# of all group members is done. +# LE$F_GROUP : Track is marked as »additional group member« +# and is not added to a cup. +# +# * file_name +# The SZS file name without extension as string. If no file name +# is entered, the string 'slot_##' ('###' for LE-CODE) with a hex +# slot number is used. +# +# * info +# Optional information (no impact, maybe machine readable) as +# string. Maybe empty. It is only used for references lists like +# commands "wctct BMG" or "wctct TRACKS". +# +# * id +# Optional identifier. While creating references, it is assumed, +# that tracks with equal identifiers are equal. Empty strings +# and a single "-" means: no identifier available. +# +# * Hidden track definition: +# 'H' music_id ';' property_id ';' file_name ';' info ';' id +# 'H' music_id ';' property_id ';' le_flags ';' file_name ';' info ';' id +# +# Like 'T', but hidden: +# Define a new track, that doesn't appear in any cup. So it is hidden. +# A hidden track can be defined at any place in this section and doesn't +# touch any cup or the cup order. +# +# * Battle arena definition: +# 'A' music_id ';' property_id ';' file_name ';' info ';' id +# 'A' music_id ';' property_id ';' le_flags ';' file_name ';' info ';' id +# +# Like 'T', but for battle arenas: +# Overwrite a battle arena slot identified by 'property_id'. diff --git a/project/text-files/ctcode.inc/ctcode-setup-cr.inc b/project/text-files/ctcode.inc/ctcode-setup-cr.inc new file mode 100644 index 0000000..ff92238 --- /dev/null +++ b/project/text-files/ctcode.inc/ctcode-setup-cr.inc @@ -0,0 +1,33 @@ +[SETUP] + +#--- Identify the tool and version, that have written this file: + +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s + + +#--- mode + +CT-MODE = %3u # 1: CT-CODE, 2: LE-CODE + + +#--- Statistics + +USED-RACING-CUPS = %3u # number of active racing cups +MAX-RACING-CUPS = %3u # max possible number of racing cups +USED-BATTLE-CUPS = %3u # number of active battle cups +MAX-BATTLE-CUPS = %3u # max possible number of battle cups + +USED-RACING-TRACKS = %3u # number of active racing tracks (used by cup) +USED-BATTLE-TRACKS = %3u # number of active battle tracks (used by cup) +DEFINED-TRACKS = %3u # total number of defined tracks +MAX-TRACKS = %3u # total number of track slots + +DEFINED-RACING-CUPS = %3u # number of defined racing cups +MAX-RACING-CUPS = %3u # max possible number of racing cups +DEFINED-BATTLE-CUPS = %3u # number of defined battle cups +MAX-BATTLE-CUPS = %3u # max possible number of battle cups +UNUSED-CUPS = %3u # number of unused cups (space available) diff --git a/project/text-files/distrib.inc/distrib-head-cr.inc b/project/text-files/distrib.inc/distrib-head-cr.inc new file mode 100755 index 0000000..bdb3862 --- /dev/null +++ b/project/text-files/distrib.inc/distrib-head-cr.inc @@ -0,0 +1,144 @@ +#DISTRIB + +# IMPORTANT: +# Every parameter is copied to the next version of this file. For unknown +# parameters the new section "User defined parameters" is created. + +# Here you can find help: +# https://szs.wiimm.de/cmd/distribution#desc +# http://wiki.tockdom.com/wiki/Wiimm%%27s_Custom_Archive/Distribution + + + ############################################## + ### ### + ### Upload the final version of this ### + ### file to https://ct.wiimm.de/upload ### + ### ### + ############################################## + + +#------------------------------------------------------------------------------ +# Comments for personal use +#------------------------------------------------------------------------------ + +# These first comments are for personal use and will not imported to ct.wiimm.de +# and never visible for other persons. Use it as reminder for yourself. + +@COMMENT1 = %s +@COMMENT2 = %s +@COMMENT3 = %s +@COMMENT4 = %s +@COMMENT5 = %s + +#------------------------------------------------------------------------------ +# Identify yourself! (private section) +#------------------------------------------------------------------------------ + +# The next parameters are used to identify you and to contact you on problems. +# All data of this section will never be visible for other persons. It's no +# problem to leave some or all parameters empty. + +# A wish for an user name @ ct.wiimm.de, if it ever supports a login. +@USER-CT-WIIMM = %s + +# Your current user name @ Wiimmfi (same as @ wii-hombrew.com). +@USER-WIIMMFI = %s + +# Your current user name @ CT-Wiiki (wiki.tockdom.com). +@USER-CT-WIIKI = %s + +# Other user names, format: service=name +@USER-MISC = %s + +# A space separated list of mail addresses: +@MAIL = %s + +# A note addressed to Wiimm +@NOTE-FOR-WIMMM = %s + +#------------------------------------------------------------------------------ +# Distribution parameters +#------------------------------------------------------------------------------ + +# Edit the following distribution parameters +# All parameters will be visible at ct.wiimm.de + +# Identification of the distribution and its version. +# These first parameters are required! +@NAME = %s +@VERSION = %s +@AUTHORS = %s + +# Date of first release in format yyyy-mm-dd +@RELEASE-DATE = %s + +# Additional keywords for the search engine (optional). +# The 4 parameters above are automatically scanned for keywords. +@KEYWORDS = %s + +# ID or UUID of predecessor +@PREDECESSOR = %s + +# Additional infos (optional) +@WIIMMFI-REGION = %s +@INFO-TEXT = %s +@INFO-URL = %s + +#------------------------------------------------------------------------------ +# Settings for ct.wiimm.de => edit them +#------------------------------------------------------------------------------ + +# DISPLAY-MODE decides, how this distribution is displayed. +# 0: Hide the distribution completley (like not defined). +# 1: Hide the distribution, but allow access to overview page by UUID. +# 2: Like 1 + List the distribution name without a link at track info. +# 3: Distribution is fully visible and reachable by links. +# Once imported and mode>0, you can access the page with the following +# private URL: https://ct.wiimm.de/dis/%s +@DISPLAY-MODE = %d + +# DATABASE-NAME decides, which tracknames are used for the distribution list. +# 0: Use the names of this file. +# 1: Use the global names of the track archive. If a track is unknown by +# the database, then fall back to step 0 and use the name of this file. +@DATABASE-NAME = %d + +# VIEW-COMMENT decides, how the comments in square brackts [] of track names +# are printed at the distribution page. +# 0: Hide the name part beginning from the first "[". +# 1: Show complete name including the [...] part. +@VIEW-COMMENT = %d + +# The comments in square brackts [] of track names are read as comma separated +# list of attributes. The attributes 'new', 'again', 'fill' and 'update' (or shorter +# 'upd') are imported to ct.wiimm.de. +# Each of the next 4 parameters enables a common status column in the track +# list. Maximal one enabled status attribut is printed. 'new' has the highest +# priority and 'update' the lowest. The options are enabled by value '1'. +@ENABLE-NEW = %d +@ENABLE-AGAIN = %d +@ENABLE-FILL = %d +@ENABLE-UPDATE = %d + +# If enabled, boost tracks are highlighted. For tracks known by the database, +# the internal boost status is used. Unknown tracks are marked as boost, if +# either the attribute 'boost' exists, or the first 5 characters of the track +# name are 'boost'. The option is enabled by value '1'. +@ENABLE-BOOST = %d + +#------------------------------------------------------------------------------ +# Automatically created parameters => DO NOT EDIT! +#------------------------------------------------------------------------------ + +# The UUID (Universally Unique IDentifier) identifies the distribution. +# => Do not change it under any circumstances! +# => And don't copy it from another distribution! +@UUID = %s + +# wszst version that wrote this file +@WSZST-VERSION = %s +@WSZST-REVISION = %u + +# Timestamps for this file +@FIRST-CREATION = %s +@LAST-UPDATE = %s diff --git a/project/text-files/distrib.inc/distrib-param-cr.inc b/project/text-files/distrib.inc/distrib-param-cr.inc new file mode 100644 index 0000000..bac942e --- /dev/null +++ b/project/text-files/distrib.inc/distrib-param-cr.inc @@ -0,0 +1,6 @@ + +#------------------------------------------------------------------------------ +# User defined parameters +#------------------------------------------------------------------------------ +# Some unknown parameters were detected. They are listed here: + diff --git a/project/text-files/distrib.inc/distrib-tracks-cr.inc b/project/text-files/distrib.inc/distrib-tracks-cr.inc new file mode 100644 index 0000000..47c0e71 --- /dev/null +++ b/project/text-files/distrib.inc/distrib-tracks-cr.inc @@ -0,0 +1,9 @@ + +#FF# +#------------------------------------------------------------------------------ +# Track listing of format: SHA1 SLOT NAME +#------------------------------------------------------------------------------ + +# Please edit the slot column. Use »CUP.INDEX« (e.g. »1.2« or »20.4«) for +# racing tracks and »'A' CUP.INDEX« (e.g. »A1.1« or »A2.5«) for battle arenas. + diff --git a/project/text-files/geohit.inc/geohit-item-head-cr.inc b/project/text-files/geohit.inc/geohit-item-head-cr.inc new file mode 100644 index 0000000..3aa8911 --- /dev/null +++ b/project/text-files/geohit.inc/geohit-item-head-cr.inc @@ -0,0 +1 @@ +%s diff --git a/project/text-files/geohit.inc/geohit-item-info-cr.inc b/project/text-files/geohit.inc/geohit-item-info-cr.inc new file mode 100644 index 0000000..5c92c6b --- /dev/null +++ b/project/text-files/geohit.inc/geohit-item-info-cr.inc @@ -0,0 +1,36 @@ +%s <<< The first 8 characters are the magic for text files of GeoHit-Item. +# <<< Don't remove them! +# +#------------------------------------------------------------------------------ +# +# This is the textual representation of a GeoHitTableItem*.bin file. +# It is defined by Wiimm and scanned by a 2-pass parser. +# +# Hints for decoding to a text file: +# +# - If either option --no-header (-H) or option --brief (-B) is set, this +# general description is suppressed. +# +# - If either option --export (-X) is set once or option --brief (-B) is set +# twice, a very small text file without descriptions and other not needed +# parameters is created. +# +# - If option --long (-l) is set, then additional hidden parameters are +# included. They are automatically included, if they are differ from the +# the standard settings. +# +#------------------------------------------------------------------------------ +# +# Syntax for section [OBJECTS]: +# +# OBJ_ID P1 .. P15 +# +# OBJ_ID : Id of the object. Use prefix '0x' for hex numbers. +# P1..P15 : 15 hit parameters, optionally separated by commas. +# A point ('.') is interpreted as Null, and an equal sign ('=') +# leaves the value unchanged. Missed parameters are not modified. +# +# See http://wiki.tockdom.com/wiki/GeoHitTableItem.bin +# and http://wiki.tockdom.com/wiki/GeoHitTableItemObj.bin for more details. +# +#------------------------------------------------------------------------------ diff --git a/project/text-files/geohit.inc/geohit-item-objects-cr.inc b/project/text-files/geohit.inc/geohit-item-objects-cr.inc new file mode 100644 index 0000000..a5923f6 --- /dev/null +++ b/project/text-files/geohit.inc/geohit-item-objects-cr.inc @@ -0,0 +1,9 @@ +[OBJECTS] +@REVISION = %u + +#-----------------------------------------------------------------------#----------------- +# Green Shell Star Mega Shroom Golden Shroom +# : Red Shell : Blue Shell : Bob-omb : Bullet Bill +# : : Banana : : Thunder : : Blooper : : Thunder Cloud +# ID : : : Shroom : : : Fake Box : : POW : : : # Name +#-----------------------------------------------------------------------#----------------- diff --git a/project/text-files/geohit.inc/geohit-item-setup-cr.inc b/project/text-files/geohit.inc/geohit-item-setup-cr.inc new file mode 100644 index 0000000..c8d5b7c --- /dev/null +++ b/project/text-files/geohit.inc/geohit-item-setup-cr.inc @@ -0,0 +1,12 @@ + +# +############################################################################### + +[SETUP] + +# identification of creator tool +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s diff --git a/project/text-files/geohit.inc/geohit-kart-head-cr.inc b/project/text-files/geohit.inc/geohit-kart-head-cr.inc new file mode 100644 index 0000000..3aa8911 --- /dev/null +++ b/project/text-files/geohit.inc/geohit-kart-head-cr.inc @@ -0,0 +1 @@ +%s diff --git a/project/text-files/geohit.inc/geohit-kart-info-cr.inc b/project/text-files/geohit.inc/geohit-kart-info-cr.inc new file mode 100644 index 0000000..f07d2c1 --- /dev/null +++ b/project/text-files/geohit.inc/geohit-kart-info-cr.inc @@ -0,0 +1,36 @@ +%s <<< The first 8 characters are the magic for text files of GeoHit-Kart. +# <<< Don't remove them! +# +#------------------------------------------------------------------------------ +# +# This is the textual representation of a GeoHitTableKart*.bin file. +# It is defined by Wiimm and scanned by a 2-pass parser. +# +# Hints for decoding to a text file: +# +# - If either option --no-header (-H) or option --brief (-B) is set, this +# general description is suppressed. +# +# - If either option --export (-X) is set once or option --brief (-B) is set +# twice, a very small text file without descriptions and other not needed +# parameters is created. +# +# - If option --long (-l) is set, then additional hidden parameters are +# included. They are automatically included, if they are differ from the +# the standard settings. +# +#------------------------------------------------------------------------------ +# +# Syntax for section [OBJECTS]: +# +# OBJ_ID P1 P2 P3 P4 +# +# OBJ_ID : Id of the object. Use prefix '0x' for hex numbers. +# P1..P4 : 4 hit parameters, optionally separated by commas. +# A point ('.') is interpreted as Null, and an equal sign ('=') +# leaves the value unchanged. Missed parameters are not modified. +# +# See http://wiki.tockdom.com/wiki/GeoHitTableKart.bin +# and http://wiki.tockdom.com/wiki/GeoHitTableKartObj.bin for more details. +# +#------------------------------------------------------------------------------ diff --git a/project/text-files/geohit.inc/geohit-kart-objects-cr.inc b/project/text-files/geohit.inc/geohit-kart-objects-cr.inc new file mode 100644 index 0000000..0600d1f --- /dev/null +++ b/project/text-files/geohit.inc/geohit-kart-objects-cr.inc @@ -0,0 +1,9 @@ +[OBJECTS] +@REVISION = %u + +#--------------------------------#----------------- +# No power-up # Name +# : Star # +# : : Mega Mushroom +# ID : : : Bullet Bill +#--------------------------------#----------------- diff --git a/project/text-files/geohit.inc/geohit-kart-setup-cr.inc b/project/text-files/geohit.inc/geohit-kart-setup-cr.inc new file mode 100644 index 0000000..c8d5b7c --- /dev/null +++ b/project/text-files/geohit.inc/geohit-kart-setup-cr.inc @@ -0,0 +1,12 @@ + +# +############################################################################### + +[SETUP] + +# identification of creator tool +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s diff --git a/project/text-files/itemslot.inc/itemslot-head-cr.inc b/project/text-files/itemslot.inc/itemslot-head-cr.inc new file mode 100755 index 0000000..dbea88a --- /dev/null +++ b/project/text-files/itemslot.inc/itemslot-head-cr.inc @@ -0,0 +1 @@ +#ITEMSLT diff --git a/project/text-files/itemslot.inc/itemslot-info-cr.inc b/project/text-files/itemslot.inc/itemslot-info-cr.inc new file mode 100755 index 0000000..1d6c2a7 --- /dev/null +++ b/project/text-files/itemslot.inc/itemslot-info-cr.inc @@ -0,0 +1,24 @@ +#ITEMSLT <<< The first 8 characters are the magic for ItemSlot.bin text files. +# <<< Don't remove them! +# +#------------------------------------------------------------------------------ +# +# This is the textual representation of ItemSlot.bin and ItemSlotTable.slt +# files. It is defined by Wiimm and scanned by a 2-pass parser. +# +# Hints for decoding to a text file: +# +# - If either option --no-header (-H) or option --brief (-B) is set, this +# general description is suppressed. +# +# - If either option --export (-X) is set once or option --brief (-B) is set +# twice, a very small text file without descriptions and other not needed +# parameters is created. +# +# - If option --long (-l) is set, then additional hidden parameters are +# included. They are automatically included, if they are differ from the +# the standard settings. +# +# See http://wiki.tockdom.com/wiki/ItemSlot.bin for more details. +# +#------------------------------------------------------------------------------ diff --git a/project/text-files/itemslot.inc/itemslot-param-cr.inc b/project/text-files/itemslot.inc/itemslot-param-cr.inc new file mode 100755 index 0000000..6e16e0e --- /dev/null +++ b/project/text-files/itemslot.inc/itemslot-param-cr.inc @@ -0,0 +1,6 @@ +[PARAMETERS] +@REVISION = %u + +ADD-BATTLE = %d # used if creating a binary file +USE-SLT = %d # information only + diff --git a/project/text-files/itemslot.inc/itemslot-setup-cr.inc b/project/text-files/itemslot.inc/itemslot-setup-cr.inc new file mode 100755 index 0000000..c8d5b7c --- /dev/null +++ b/project/text-files/itemslot.inc/itemslot-setup-cr.inc @@ -0,0 +1,12 @@ + +# +############################################################################### + +[SETUP] + +# identification of creator tool +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s diff --git a/project/text-files/itemslot.inc/itemslot-table-cr.inc b/project/text-files/itemslot.inc/itemslot-table-cr.inc new file mode 100755 index 0000000..d7fe18b --- /dev/null +++ b/project/text-files/itemslot.inc/itemslot-table-cr.inc @@ -0,0 +1,3 @@ +[%s] # %s table at offset 0x%03x, %u rows, %u columns +@REVISION = %u + diff --git a/project/text-files/kcl.inc/kcl-dump-head-cr.inc b/project/text-files/kcl.inc/kcl-dump-head-cr.inc new file mode 100644 index 0000000..5a48c68 --- /dev/null +++ b/project/text-files/kcl.inc/kcl-dump-head-cr.inc @@ -0,0 +1,40 @@ + +S.verticies =%8u +S.normals =%8u +S.triangles =%8u, %u unused, %u fixed, %u invalid +S.octree_size =%8u +S.total_size =%8u + +H.unknown_0x10 = %11.3f +H.unknown_0x38 = %11.3f + +H.min_octree = %11.3f %11.3f %11.3f # minimum coord for octree entrance +S.min_point = %11.3f %11.3f %11.3f # minimum coord of all points +S.mean_point = %11.3f %11.3f %11.3f # arithmetic mean of all points +S.max_point = %11.3f %11.3f %11.3f # maximum coord of all points +S.max_octree = %11.3f %11.3f %11.3f # maximum coord for octree entrance + +H.mask = 0x%08x 0x%08x 0x%08x # masks (header) +H.shifts = %8u> %8u< %8u< # header shift values +S.null_bits = %8u %8u %8u # number of low 0 bits +S.bcube_bits = %8u %8u %8u # number of base cube bits +S.n_bcubes = %8u %8u %8u # number of base cubes + +S.min_distance = %14.6f # minimum distance between points of a triangle +S.max_distance = %11.3f # maximum distance between points of a triangle + +S.bcube_width = %8u # width of a single base cube = 2^rshift +S.max_cube_width = %8u # maximum width of a cube with triangles +S.min_cube_width = %8u # minimum width of a cube with triangles +~S.blow_size = %8u # suggestion: what KCL_BLOW value was used +S.n_base_cubes = %8u # number of base cubes +S.n_cube_nodes = %8u # number of cube nodes (each with 8 cube links) +S.total_cubes = %8u # total number of cubes +S.max_depth = %8u # maximum octree depth +S.ave_depth = %11.2f # average octree depth + +S.n_null_lists = %8u # number of triangle lists with 0 triangles +S.n_tri_lists = %8u # number of triangle lists with >0 triangles +S.n_null_links = %8u # number of triangle links to lists with 0 triangles +S.n_tri_links = %8u # number of triangle links to lists with >0 triangles + diff --git a/project/text-files/kcl.inc/kcl-dump-octlist-cr.inc b/project/text-files/kcl.inc/kcl-dump-octlist-cr.inc new file mode 100644 index 0000000..eab8bf7 --- /dev/null +++ b/project/text-files/kcl.inc/kcl-dump-octlist-cr.inc @@ -0,0 +1,2 @@ + +#L# diff --git a/project/text-files/kcl.inc/kcl-dump-octree-cr.inc b/project/text-files/kcl.inc/kcl-dump-octree-cr.inc new file mode 100644 index 0000000..daff0ce --- /dev/null +++ b/project/text-files/kcl.inc/kcl-dump-octree-cr.inc @@ -0,0 +1,6 @@ + +# +############################################################################### +# Octree dump: %u base cubes + %u normal cubes, %u triangle lists + +#O# diff --git a/project/text-files/kcl.inc/kcl-dump-tridata-cr.inc b/project/text-files/kcl.inc/kcl-dump-tridata-cr.inc new file mode 100644 index 0000000..d15fd47 --- /dev/null +++ b/project/text-files/kcl.inc/kcl-dump-tridata-cr.inc @@ -0,0 +1,15 @@ +# List of %u triangles + +#D# +#-----------------------------------------+---------------------------------- +# N0 _______ x,y,z of normal #0 _______ | flags (income -> current) +# N1 _______ x,y,z of normal #1 _______ | status +# N2 _______ x,y,z of normal #2 _______ | 'valid', 'used' +# N3 _______ x,y,z of normal #3 _______ | +# P1 _______ x,y,z of point #1 ________ | length +# P2 _______ x,y,z of point #2 ________ | difference: point2 - point1 +# P3 _______ x,y,z of point #3 ________ | difference: point3 - point1 +# LEN _______ 3x side length2 __________ | area of triangle +# HT _______ 3x height ________________ | min and max height +#-----------------------------------------+---------------------------------- + diff --git a/project/text-files/kcl.inc/kcl-flag-template-cr.inc b/project/text-files/kcl.inc/kcl-flag-template-cr.inc new file mode 100644 index 0000000..8394619 --- /dev/null +++ b/project/text-files/kcl.inc/kcl-flag-template-cr.inc @@ -0,0 +1,48 @@ + +############################################################################## +##### All behind a '#' on the same line is a comment and ignored. ##### +##### See https://szs.wiimm.de/doc/kcl/flagfile for more details. ##### +############################################################################## +# function f() + +# First, we define a flag function named f(). +# This allows us to define type and variant as 2 values. + +@function f # (type,variant) + @return $1 & 0x1f | $2 << 5 +@endfunction + +############################################################################## +# function a() + +# Here we define function a() to support the flag variant type A. +# For details see http://wiimm.de/r/ctw/KCL_flag + +@function a # (type,w,x,y,z) + @return $1 & 0x1f # type (5 bits) + > | ( $2 & 7 ) << 13 # W (3 bits) + > | ( $3 & 3 ) << 11 # X (2 bits) + > | ( $4 & 7 ) << 8 # Y (3 bits) + > | ( $5 & 7 ) << 5 # Z (3 bits) +@endfunction + +############################################################################## +# function i() + +# The function i() supports fall boundaries. + +@function i # (type,z,index) + @return $1 & 0x1f # type (5 bits) + > | ( $2 & 7 ) << 5 # Z (3 bits) + > | ( $3 & 0xff ) << 8 # index (8 bits) +@endfunction + +############################################################################## +# table + +#----------------------------------------------------------------------------- +# group_name = flag_or_expression +# = f(type,variant) +# = a(type,w,x,y,z) +# = i(type,z,index) +#----------------------------------------------------------------------------- diff --git a/project/text-files/kcl.inc/kcl-head-cr.inc b/project/text-files/kcl.inc/kcl-head-cr.inc new file mode 100644 index 0000000..1dfe947 --- /dev/null +++ b/project/text-files/kcl.inc/kcl-head-cr.inc @@ -0,0 +1,18 @@ +#KCL : Wavefront OBJ File + +## Created by 'Wiimms SZS Tools' - https://szs.wiimm.de/ +#@ TOOL = %s +#@ SYSTEM = %s +#@ VERSION = %s +#@ REVISION = %u +#@ DATE = %s +#@ MODE = %s + + +# Statistics: +# +# %8u vertices +# %8u normals +# %8u triangles +# %8u groups +# diff --git a/project/text-files/kmp.inc/kmp-area-cr.inc b/project/text-files/kmp.inc/kmp-area-cr.inc new file mode 100644 index 0000000..332e5b8 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-area-cr.inc @@ -0,0 +1,16 @@ + +#FF# +############################################################################### + +[AREA] +# Camera and other areas +# KMP/AREA syntax & semantics: https://szs.wiimm.de/doc/kmp/area + +@REVISION = %u +@AREA-HEAD-VALUE = %#x + +#AREA# +#------------------------------------------------------------------------------------ +# mode type _____position/rotation/scale______ _________settings_________ +# idx came prio x y z #1 #2 #3 #4 +#------------------------------------------------------------------------------------ diff --git a/project/text-files/kmp.inc/kmp-came-cr.inc b/project/text-files/kmp.inc/kmp-came-cr.inc new file mode 100644 index 0000000..a799131 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-came-cr.inc @@ -0,0 +1,19 @@ + +#FF# +############################################################################### + +[CAME] +# Cameras +# KMP/CAME syntax & semantics: https://szs.wiimm.de/doc/kmp/came + +@REVISION = %u +@OPENING-INDEX = %s +@SELECTION-INDEX = %s + +#CAME# +#------------------------------------------------------------------------------------------- +# idx type v(came) _________position (x,y,z)_________ zoom_beg # some +# next v(zoom) _________rotation (x,y,z)_________ zoom_end # calculated +# unknown v(v.pt) ______view point beg (x,y,z)______ - # values +# route unknown ______view point end (x,y,z)______ sec*60 # (info only) +#------------------------------------------------------------------------------------------- diff --git a/project/text-files/kmp.inc/kmp-ckph-cr.inc b/project/text-files/kmp.inc/kmp-ckph-cr.inc new file mode 100644 index 0000000..2877ac5 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-ckph-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[CKPH] +# Check points header: Divide check points into groups. +# KMP/CKPH syntax & semantics: https://szs.wiimm.de/doc/kmp/ckph diff --git a/project/text-files/kmp.inc/kmp-ckpt-cr.inc b/project/text-files/kmp.inc/kmp-ckpt-cr.inc new file mode 100644 index 0000000..575283a --- /dev/null +++ b/project/text-files/kmp.inc/kmp-ckpt-cr.inc @@ -0,0 +1,32 @@ + +#FF# +############################################################################### + +[CKPT] +# Check points +# KMP/CKPT syntax & semantics: https://szs.wiimm.de/doc/kmp/ckpt + +@REVISION = %u +@CKPT-HEAD-VALUE = %#x +@CKPH-HEAD-VALUE = %#x +@AUTO-NEXT = %u +@AUTO-GROUP-NAME = 0 +@AUTO-RESPAWN = 0 # 0:off, 1:on + +@AUTO-MODE = mode$automode +# allowed values: AM$OFF AM$NORM AM$RENUMBER AM$1LAP AM$SHORT AM$UNLIMIT + +@ROUTE-OBJECT-MODE = mode$showck +#--- default settings: +# @ROUTE-OBJECT1 = o$%s +# @ROUTE-OBJECT2 = o$%s +# @ROUTE-OBJECT1-SCALE = v( %4.2f, %4.2f, %4.2f ) +# @ROUTE-OBJECT2-SCALE = v( %4.2f, %4.2f, %4.2f ) +# @ROUTE-OBJECT1-BASE = %d +# @ROUTE-OBJECT2-BASE = %d + +#CKPT# +#----------------------------------------------------------------------#------------------- +# _____left_point______ _____right_point_____ respawn prev # width direc- +#idx x y x y mode next # of ckpt tion +#----------------------------------------------------------------------#------------------- diff --git a/project/text-files/kmp.inc/kmp-cnpt-cr.inc b/project/text-files/kmp.inc/kmp-cnpt-cr.inc new file mode 100644 index 0000000..e3a0648 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-cnpt-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[CNPT] +# Cannon target positions +# KMP/CNPT syntax & semantics: https://szs.wiimm.de/doc/kmp/cnpt diff --git a/project/text-files/kmp.inc/kmp-defaults-cr.inc b/project/text-files/kmp.inc/kmp-defaults-cr.inc new file mode 100644 index 0000000..abbdc82 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-defaults-cr.inc @@ -0,0 +1,8 @@ + +#------------------------------------------------------------------------------ + +# These are the default settings for route objects: +# @ROUTE-OBJECT = %s +# @ROUTE-OBJECT-SHIFT = v( %4.2f, %4.2f, %4.2f ) +# @ROUTE-OBJECT-SCALE = v( %4.2f, %4.2f, %4.2f ) + diff --git a/project/text-files/kmp.inc/kmp-enph-cr.inc b/project/text-files/kmp.inc/kmp-enph-cr.inc new file mode 100644 index 0000000..13225b9 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-enph-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[ENPH] +# Enemy route points header: Divide enemy points into groups. +# KMP/ENPH syntax & semantics: https://szs.wiimm.de/doc/kmp/enph diff --git a/project/text-files/kmp.inc/kmp-enpt-cr.inc b/project/text-files/kmp.inc/kmp-enpt-cr.inc new file mode 100644 index 0000000..101307a --- /dev/null +++ b/project/text-files/kmp.inc/kmp-enpt-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[ENPT] +# Enemy Points: Routes of the cpu racers +# KMP/ENPT syntax & semantics: https://szs.wiimm.de/doc/kmp/enpt diff --git a/project/text-files/kmp.inc/kmp-enpt-ropt-cr.inc b/project/text-files/kmp.inc/kmp-enpt-ropt-cr.inc new file mode 100644 index 0000000..367eee0 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-enpt-ropt-cr.inc @@ -0,0 +1,42 @@ + +# Experimental options, tools before v1.60a will throw error messages! +@SHOW-OPTIONS = %d # >0: force printing of all route options + +# AUTO-CONNECT is one of the following AC$* symbols: +# For racing tracks and for battle areenas with manual links setup: +# AC$OFF +# Do not any auto connection. Route connection are fully under control +# of the user. +# AC$PREV +# Setup PREV links by NEXT links of other routes. Existing PREV links +# are cleared before. This is the default and the behavior of all versions +# before v1.60a. +# +# Modes for automatic route connection in battle arenas: +# AC$DISPATCH +# Find dispatch points and create links automatically if not already +# set manually. This is the default for enemy routes, if dispatch +# points detected. +# +# Flags: The modes of above can be combined with the following options by +# the OR operator '|' (example: AC$PREV | ACF$FIX) +# ACF$FIX_PREV +# Add self-links, if a PREV list is empty. +# ACF$FIX_NEXT +# Add self-links, if a NEXT list is empty. +# ACF$FIX +# Abbreviation for: ACF$FIX_PREV | ACF$FIX_NEXT +# This is the default for item routes to avoid a freeze. +# +@AUTO-CONNECT = %s%s + +# Syntax of new command with impact to all following $GROUP commands: +# "$DEF-CLASS" [":"] class_name + +# Syntax of new route commands (placed behind $GROUP command): +# "$PREV" [":"] [group_name]... +# "$SETTINGS" [":"] setting1 setting2 [ "AUTO" | "ROUTE" | "DISPATCH" ] +# "$CLASS" [":"] class_name_prev [class_name_next] +# "$AC-PREV" [":"] class_name +# "$AC-NEXT" [":"] class_name +# "$ONEWAY" [":"] "NONE" | "PREV" | "NEXT" | "BOTH" diff --git a/project/text-files/kmp.inc/kmp-gobj-cr.inc b/project/text-files/kmp.inc/kmp-gobj-cr.inc new file mode 100644 index 0000000..86943aa --- /dev/null +++ b/project/text-files/kmp.inc/kmp-gobj-cr.inc @@ -0,0 +1,49 @@ + +#FF# +############################################################################### + +[GOBJ] +# Global objects. +# KMP/GOBJ syntax & semantics: https://szs.wiimm.de/doc/kmp/gobj + +@REVISION = %u +@GOBJ-HEAD-VALUE = %#x +@AUTO-ENEMY-ITEM = 0 # mode$test ? : 0 +@AUTO-OBJECT-NAME = 0 +@KCL-FALL-WIDTH = 0 # set to a value >0 (e.g. 200) to enable KCL falling +@KCL-FALL-PRE-ADD = 0 # add this height or vector before falling +@KCL-FALL-POST-ADD = 0 # add this height or vector after success +@KCL-FALL-TYPES = KCL$DRIVE # bit field of recognized KCL types + # Best values are: + # - KCL$BORDER: All KCL types except effects. + # - KCL$DRIVE: All driveable types like road, boost, ramp, water, ... +@SORT-OBJECTS = SORT$OFF # SORT$GROUPING SORT$ANGLE SORT$TINY + +#------------------------------------------------------------------------------ +# Definition objects and references + +# There is a new kind of presence flags in progress: +# http://wiki.tockdom.com/wiki/Extension_of_Presence_Flags +# Since v2.11a, the KMP compiler supports this new extension. +# Here you find some global settings for the new extension: + +# If >0: Reset not defined parameters of definitions-object to their default +# values. +@FIX-OBJDEF = 0 + +# If not DOB$OFF: Sort definitions-objects by object-id and move them to the +# beginning (DOB$BEGIN) or to the end (DOB$END) of all GOBJ elements. +@SORT-OBJDEF = DOB$OFF + +# If >0: Clear invalid reference-ids for modes 0 and 1. +# If >1: Clear invalid reference-ids independend of the mode. +@FIX-REFERENCE = 0 + +# If not DOB$OFF: Sort standard objects with a valid reference-id directly +# before (DOB$BEFORE) or behind (DOB$BEHIND) the related definition-object. +@SORT-REFERENCE = DOB$OFF + +# Object statistics: +##-------------------------------------------------------------------------------------------- +##count id/hex name characteristics +##-------------------------------------------------------------------------------------------- diff --git a/project/text-files/kmp.inc/kmp-gobj-tabhead-cr.inc b/project/text-files/kmp.inc/kmp-gobj-tabhead-cr.inc new file mode 100644 index 0000000..6aba1b7 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-gobj-tabhead-cr.inc @@ -0,0 +1,5 @@ +#GOBJ# +#------------------------------------------------------------------------------------ +# idx obj-id ______position/rotation/scale______ _________settings_________ route +# /ref-id x y z #1/#5 #2/#6 #3/#7 #4/#8 /p-flag +#------------------------------------------------------------------------------------ diff --git a/project/text-files/kmp.inc/kmp-head-cr.inc b/project/text-files/kmp.inc/kmp-head-cr.inc new file mode 100644 index 0000000..b4831b9 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-head-cr.inc @@ -0,0 +1,28 @@ +#KMP <<< The first 4 characters '#KMP' are the magic for a KMP text file. +# <<< Don't remove them! +# +############################################################################### +# +# This is a textual representation of a KMP file used in Nintendos "Mario Kart +# Wii". "Wiimms SZS Tools" can convert binary (raw) and text KMP files in both +# directions. The text parser supports variables, C like expressions and nested +# IF-THEN-ELSE and LOOP structures. +# +# Info about the general parser syntax and semantics: +# * https://szs.wiimm.de/doc/syntax +# +# Info about the KMP text syntax and semantics: +# * https://szs.wiimm.de/doc/kmp/syntax +# +# Reference list of KMP parser functions: +# * https://szs.wiimm.de/doc/kmp/func +# +# Info about the KMP file format: +# * https://szs.wiimm.de/r/wiki/KMP +# +# Hint: +# Set one of the options --no-header (-H) or --brief (-B) while decoding +# to suppress this general description. The --brief option will also disable +# info parts in the different sections. If --brief is set twice, the output +# of the unneded sections CKPH, ENPH and ITPH is completly suppressed. +# diff --git a/project/text-files/kmp.inc/kmp-itph-cr.inc b/project/text-files/kmp.inc/kmp-itph-cr.inc new file mode 100644 index 0000000..ee80374 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-itph-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[ITPH] +# Item route points header: Divide item points into groups. +# KMP/ITPH syntax & semantics: https://szs.wiimm.de/doc/kmp/itph diff --git a/project/text-files/kmp.inc/kmp-itpt-cr.inc b/project/text-files/kmp.inc/kmp-itpt-cr.inc new file mode 100644 index 0000000..20305d3 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-itpt-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[ITPT] +# Item Points: Routes of red shells and bullet bill +# KMP/ITPT syntax & semantics: https://szs.wiimm.de/doc/kmp/itpt diff --git a/project/text-files/kmp.inc/kmp-itpt-ropt-cr.inc b/project/text-files/kmp.inc/kmp-itpt-ropt-cr.inc new file mode 100644 index 0000000..87659bd --- /dev/null +++ b/project/text-files/kmp.inc/kmp-itpt-ropt-cr.inc @@ -0,0 +1,4 @@ + +# Route options. See section [ENPT] for details. +@SHOW-OPTIONS = %d # >0: force printing of all route options +@AUTO-CONNECT = %s%s diff --git a/project/text-files/kmp.inc/kmp-jgpt-auto-cr.inc b/project/text-files/kmp.inc/kmp-jgpt-auto-cr.inc new file mode 100644 index 0000000..6bf973e --- /dev/null +++ b/project/text-files/kmp.inc/kmp-jgpt-auto-cr.inc @@ -0,0 +1,26 @@ + +# Automatic definition of respawn points +@AUTO-DEFINE = 0 # 0 | SECT$ENPT | SECT$ITPT +@AUTO-DISTANCE = 1000.0 # minimal distance to other JGPT +@AUTO-ADJUST = vy(100) # add this value to each auto-JGPT +@AUTO-REMOVE = 0 # 0|1|2 + +# Column 'flag' is optional and only used internally. +# Syntax: FLAGS_STRING or '(' EXPRESSION ')' +# F (F$FALL): Point can be used for FALL-DOWN functionality. +# S (F$SNAP): Point can be used for SNAP functionality. +# Remove the letter or replace it by '-' to disable a feature. +# If the column is missed, all options are enabled by default. + +# If you enable EXPORT-FLAGS (set it to 1), the flags are exported using the +# lowest byte of ROTATION.x. The rotation value needs only 4 or 5 digits, so +# this has no disadvantage for the rotation. 5 of the 8 bits are used to +# verify changes by other tools on import. If at least 1 of these bits of all +# points is wrong, import is canceled. +@EXPORT-FLAGS = %d # 0=disabled, 1=enabled (overridden by option --export-flags) + +#JGPT# +#------------------------------------------------------------------------------------ +# _____________position_____________ _______rotation________ related flags +# idx x y z x y z id effect +#------------------------------------------------------------------------------------ diff --git a/project/text-files/kmp.inc/kmp-jgpt-cr.inc b/project/text-files/kmp.inc/kmp-jgpt-cr.inc new file mode 100644 index 0000000..266be1b --- /dev/null +++ b/project/text-files/kmp.inc/kmp-jgpt-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[JGPT] +# Respawn positions +# KMP/JGPT syntax & semantics: https://szs.wiimm.de/doc/kmp/jgpt diff --git a/project/text-files/kmp.inc/kmp-ktpt-cr.inc b/project/text-files/kmp.inc/kmp-ktpt-cr.inc new file mode 100644 index 0000000..b19a0b7 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-ktpt-cr.inc @@ -0,0 +1,23 @@ + +#FF# +############################################################################### + +[KTPT] +# Kart Points: Starting positions of the racers +# KMP/KTPT syntax & semantics: https://szs.wiimm.de/doc/kmp/ktpt + +@REVISION = %u +@KTPT-HEAD-VALUE = %#x +@KCL-FALL-WIDTH = 0 # set to a value >0 (e.g. 200) to enable KCL falling +@KCL-FALL-PRE-ADD = 0 # add this height or vector before falling +@KCL-FALL-POST-ADD = 0 # add this height or vector after success +@KCL-FALL-TYPES = KCL$DRIVE # bit field of recognized KCL types + # Best values are: + # - KCL$BORDER: All KCL types except effects. + # - KCL$DRIVE: All driveable types like road, boost, ramp, water, ... + +#KTPT# +#-------------------------------------------------------------------------------- +# _____________position______________ _______rotation_______ player +#idx x y z x y z index unknown +#-------------------------------------------------------------------------------- diff --git a/project/text-files/kmp.inc/kmp-macros-cr.inc b/project/text-files/kmp.inc/kmp-macros-cr.inc new file mode 100644 index 0000000..8104070 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-macros-cr.inc @@ -0,0 +1,60 @@ + +# +############################################################################### +# Macros + +@macro itembox + #------------------------------------------------------------------- + # This macro helps to place N itemboxes in a row between + # 2 points with optimal height. + #------------------------------------------------------------------- + # Syntax: + # @:ITEMBOX(fall,1,pos1) + # @:ITEMBOX(fall,n,pos1,pos2) + # @:ITEMBOX(fall,n,pos1,pos2,factor) + # @:ITEMBOX(fall,n,pos1,pos2,factor,second) + # @:ITEMBOX(fall,n,pos1,pos2,factor,second,itemtype) + #------------------------------------------------------------------- + # Parameters: + # FALL : If >0: Raise the box by FALL and let it fall to + # : the road to get the optimal height. + # N : If >0: Create N itemboxes between POS1 and POS2. + # POS1 : Position of first itembox. + # POS2 : Position of last itembox. + # FACTOR : If set and >0.01: Create a line from POS1 to POS2. + # : Enlarge the line by FACTOR in both directions. + # : Then use the new points as new POS1 and POS2. + # SECOND : If set and >0: Move each second itembox into a second + # : row. The distance between both rows is SECOND units. + # ITEMTYPE: Use this value for player and enemy items. + #------------------------------------------------------------------- + # Examples: + # @:itembox( 200, 1, point ) + # @:itembox( 0, 3, point1, point2, 0.9 ) + # @:itembox( 500, 8, point1, point2, 1, 250, ITEM$3GREEN ) + # @:itembox( 200, 4, v(2000,1000,3000), v(3000,1000,5000) ) + #------------------------------------------------------------------- + + @param fall n p1 p2 factor second itemtype + @pdef fall = fall > 0 ? fall : 0 + @if isScalar(factor) && factor > 0.01 + @pdef a = p1, b = p2, f = (1.0-factor)/2 + @pdef p1 = pos(f,a,b) + @pdef p2 = pos(f,b,a) + @endif + @pdef itemtype = int(itemtype) + @if n == 1 + @pdef p = fall ? kcl$fall(p1+vy(fall),200,KCL$DRIVE) : p1 + 0 o$itembox p 0 itemtype itemtype 0 -1 0 v3(0) 0 0 0 0 0x3f v3(1) + @else + @pdef second = isScalar(second) && second > 0 + > ? hrot(vz(second),hdir(p1,p2)+90) : v3(0) + @pdef n = n - 1.0 + @for i=0;n + @pdef p = pos(i/n,p1,p2) + @doif(i&1) @pdef p = p + second + @doif(fall) @pdef p = kcl$fall(p+vy(fall),200,KCL$DRIVE) + 0 o$itembox p 0 itemtype itemtype 0 -1 0 v3(0) 0 0 0 0 0x3f v3(1) + @endfor + @endif +@endmacro diff --git a/project/text-files/kmp.inc/kmp-mspt-cr.inc b/project/text-files/kmp.inc/kmp-mspt-cr.inc new file mode 100644 index 0000000..1cd13b2 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-mspt-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[MSPT] +# End battle positions +# KMP/MSPT syntax & semantics: https://szs.wiimm.de/doc/kmp/mspt diff --git a/project/text-files/kmp.inc/kmp-param1-cr.inc b/project/text-files/kmp.inc/kmp-param1-cr.inc new file mode 100644 index 0000000..9d44e42 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-param1-cr.inc @@ -0,0 +1,54 @@ + +#FF# +############################################################################### + +# This second part of section [SETUP] is an example to prepare a KMP source +# file for conditional encoding (compiling). The idea is, that the user +# defines variables with the "--const name=value" option while calling one of +# the tools 'wszst' or 'wkmpt' to create different KMP files. +# +# Set option --no-param (-P) to disable this parameter part. +# +# This example scans 7 parameters. All variables are not special for the KMP +# encoder and fully under user control: The user can remove or rename them or +# can add more parameters. But this values have predefined meanings: +# +# slot : Create a KMP for the entered slot. +# The default value 0 means: Design for allmost all slots. +# Use it with @if..@else..@endif to support special slots. +# +# d : Create a KMP file for a "_d" variant of a track. +# Use it with @if..@else..@endif to skip objects. +# +# arena : If >0, then create a battle arena instead of a racing track. +# +# test : If >0, then test mode is enabled. +# If <=0, test mode and also all parameters below are disabled. +# +# ocam : If >0 and also test>0, then enable test of opening camera # +# (endless looping until button 'A' is pressed). With the default +# value 0 all opening scenes are looped. If test mode is disabled, +# the cameras work as usual. +# +# showrt : If set and also test>0, points of different sections ar shown as +# item boxes and all standard item boxes are disabled. Set one or more +# of the following bits to show the points of the section: +# 1 : ENPT +# 2 : ITPT +# 4 : JGPT +# 8 : CNPT +# 16 : MSPT +# 32 : POTI +# -1 : enable all section (all bits set!) +# +# showck : If set and also test>0, show check points as trees (or other +# user defined objects). See section [CKPT] for details. +# +# laps : It controls the number of laps by changing the settinge of the +# check point modes (CKPT/@AUTO-MODE). Allowed values are: +# 0: Make the race a short as possible. +# 1: Finish the race after 1 lap +# 3: Finish the race after 3 laps (default). +# 4: Never finish the race (unlimited). +# Value 3 is used, if test mode is disabled. +# diff --git a/project/text-files/kmp.inc/kmp-param2-cr.inc b/project/text-files/kmp.inc/kmp-param2-cr.inc new file mode 100644 index 0000000..7ac8677 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-param2-cr.inc @@ -0,0 +1,28 @@ + +# First we define default values (if not set by --const) for all parameters: + + @def slot ?= 0 # Enable support for special slots + @def d ?= 0 # Enable support for _d files + @def arena ?= 0 # Enable battle arena support and allow different modes + @def test ?= 0 # Enable test mode + @def ocam ?= 0 # Test opening cam, only relevant if test>0 + @def showrt ?= 0 # Show item boxes for route points. + # Only relevant if test>0. Set bit combination: + # 1:ENPT, 2:ITPT, 4:JGPT, 8:CNPT, 0x10:MSPT, 0x20:POTI (-1:all) + @def showck ?= 0 # Show objects a check point positions if test>0. + # 0:off, 1:mandatory, 2:all, 3=left+right + @def laps ?= 3 # Number of laps (0|1|3|4), only relevant if test>0 + +# In a second step, global mode variables are defined and normalized. +# All conditions in this file should use these variables. + + @gdef mode$slot = slot > 0 ? slot : 0 + @gdef mode$d = d > 0 + @gdef mode$arena = arena > 0 ? arena : 0 + + @gdef mode$test = test > 0 ? test : 0 + @gdef mode$ocam = mode$test && ocam >= 1 ? ocam : 0 + @gdef mode$laps = mode$test && laps >= 0 ? laps : 3 + @gdef mode$showrt = mode$test ? showrt : 0 + @gdef mode$showck = mode$test ? showck : 0 + @gdef mode$automode = select(mode$laps,AM$SHORT,AM$1LAP,AM$OFF,AM$OFF,AM$UNLIMIT) diff --git a/project/text-files/kmp.inc/kmp-param3-cr.inc b/project/text-files/kmp.inc/kmp-param3-cr.inc new file mode 100644 index 0000000..653273a --- /dev/null +++ b/project/text-files/kmp.inc/kmp-param3-cr.inc @@ -0,0 +1,6 @@ + +# Now the tools 'wszst' and 'wkmpt' can be called with the --const (or short +# -c) option for a conditional encoding. Example: +# -c test=12,ocam=2,laps=0 +# Enable test mode 12, show opening camera 2 endless (until pressing A) and +# finish the race very fast to view the finish cameras very soon. diff --git a/project/text-files/kmp.inc/kmp-ph-cr.inc b/project/text-files/kmp.inc/kmp-ph-cr.inc new file mode 100644 index 0000000..c9dbeb3 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-ph-cr.inc @@ -0,0 +1,15 @@ + +####################################################################### +## This complete section is only informative and ignored on reading, ## +## because it will be created while scanning the [%s] section. ## +####################################################################### + +# values never used: + # @REVISION = %u + # @%s-HEAD-VALUE = %#x + +#%s# +#-----------------------------------------------------------------------------#------- +# ___range___ _________prev__________ _________next__________ # group +#idx beg end len #1 #2 #3 #4 #5 #6 #1 #2 #3 #4 #5 #6 unknown # name +#-----------------------------------------------------------------------------#------- diff --git a/project/text-files/kmp.inc/kmp-poti-cr.inc b/project/text-files/kmp.inc/kmp-poti-cr.inc new file mode 100644 index 0000000..e9cdc50 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-poti-cr.inc @@ -0,0 +1,42 @@ + +#FF# +############################################################################### + +[POTI] +# Routes +# KMP/POTI syntax & semantics: https://szs.wiimm.de/doc/kmp/poti + +@REVISION = %u +@AUTO-ROUTE-NAME = 0 + +# If a route is closed, 3 global variables are defined for the route. +# 'NAME' is a placeholder for the route name (before renaming happens): +# NAME.len = Total route distance over all points. +# NAME.time = Total route time in 1/60 seconds calculated by the sum of +# all distances and the associated speed values. +# NAME.time.set = Same value as 'NAME.time' (but see below). + +# These 15 commands that are computed after closing a route in this order: +# @KCL-FALL-WIDTH = CUBE_WIDTH +# @KCL-FALL-PRE-ADD = HEIGHT_OR_VECTOR +# @KCL-FALL-POST-ADD = HEIGHT_OR_VECTOR +# @KCL-FALL-TYPES = TYPEMASK +# @XSS = X1OLD,X1NEW, X2OLD,X2NEW +# @YSS = Y1OLD,Y1NEW, Y2OLD,Y2NEW +# @ZSS = Z1OLD,Z1NEW, Z2OLD,Z2NEW +# @SCALE = FACTOR [ORIGIN] +# @SHIFT = DELTA +# @HROTATE = DEGREE [ORIGIN] +# @REVERSE-ORDER = 0|1 +# @SET-ROUTE-TIME = SECONDS +# @ROUTE-OBJECT = 0=OFF | -1=USE_DEFAULT_SETTINGS | >0=OBJECT_ID +# # Example: mode$showrt & %u ? -1 : 0 +# @ROUTE-OBJECT-SHIFT = VECTOR +# @ROUTE-OBJECT-SCALE = VECTOR +# The commands are reset after computations. + +#POTI# +#------------------------------------------------------#-------------------------- +# _____________position_____________ value un- # seconds+distance horiz +#idx x y z 1/60s known # to next angle +#------------------------------------------------------#-------------------------- diff --git a/project/text-files/kmp.inc/kmp-pt-head-cr.inc b/project/text-files/kmp.inc/kmp-pt-head-cr.inc new file mode 100644 index 0000000..767cefd --- /dev/null +++ b/project/text-files/kmp.inc/kmp-pt-head-cr.inc @@ -0,0 +1,6 @@ + +#%s# +#----------------------------------------------------------------------#------------------- +# _____________position_____________ width properties # distance horiz +#idx x y z factor #1 #2 flag # to prev angle +#----------------------------------------------------------------------#------------------- diff --git a/project/text-files/kmp.inc/kmp-pt-info-cr.inc b/project/text-files/kmp.inc/kmp-pt-info-cr.inc new file mode 100644 index 0000000..8a06663 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-pt-info-cr.inc @@ -0,0 +1,32 @@ + +@REVISION = %u +@%s-HEAD-VALUE = %#x +@%s-HEAD-VALUE = %#x +@AUTO-FILL = 0.0 # >1 (e.g. 5000): create more points if distance is larger +@AUTO-GROUP-NAME = 0 +@ROTATE-GROUP-LINKS = 0 +@KCL-FALL-WIDTH = 0 # set to a value >0 (e.g. 200) to enable KCL falling +@KCL-FALL-PRE-ADD = 0 # add this height or vector before falling +@KCL-FALL-POST-ADD = 0 # add this height or vector after success +@KCL-FALL-TYPES = KCL$DRIVE # bit field of recognized KCL types + # Best values are: + # - KCL$BORDER: All KCL types except effects. + # - KCL$DRIVE: All driveable types like road, boost, ramp, water, ... +@ROUTE-OBJECT = mode$showrt & %u ? -1 : 0 # 0=off, -1=use default, >0=object id +@ROUTE-OBJECT-SHIFT = $NONE # v3(0) # add vector to position +@ROUTE-OBJECT-SCALE = $NONE # v3(1) # define vector scale + +# Column 'flag' is optional and ususally only used internally. +# Syntax: FLAGS_STRING or '(' EXPRESSION ')' +# F (F$FALL): Point can be used for FALL-DOWN functionality. +# S (F$SNAP): Point can be used for SNAP functionality (ignored by ITPT). +# J (F$JGPT): Point can be used for automatic JGPT creation. +# Remove the letter or replace it by '-' to disable a feature. +# If the column is missed, all options are enabled by default. + +# If you enable EXPORT-FLAGS (set it to 1), the flags are exported using the +# lowest byte of WIDTH FACTOR. The factor needs only 2 or 3 digits, so this +# has no disadvantage on the width factors. 5 of the 8 bits are used to verify +# changes by other tools on import. If at least 1 of these bit of all points +# is wrong, import is canceled. +@EXPORT-FLAGS = %d # 0=disabled, 1=enabled (overridden by option --export-flags) diff --git a/project/text-files/kmp.inc/kmp-pt-mask-cr.inc b/project/text-files/kmp.inc/kmp-pt-mask-cr.inc new file mode 100644 index 0000000..8e5da27 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-pt-mask-cr.inc @@ -0,0 +1,6 @@ + +# Both settings support a common definition of ENPT and ITPT. +# If writing a binary KMP file, the settings 1 and 2 are masked +# (AND operation) by the following values: +@mask-1 = 0x%04x +@mask-2 = 0x%04x diff --git a/project/text-files/kmp.inc/kmp-pt2-cr.inc b/project/text-files/kmp.inc/kmp-pt2-cr.inc new file mode 100644 index 0000000..e7ee696 --- /dev/null +++ b/project/text-files/kmp.inc/kmp-pt2-cr.inc @@ -0,0 +1,25 @@ + +@REVISION = %u +@%s-HEAD-VALUE = %#x +@AUTO-ID = %u + +# Activate automatic Y-correction (KCL fall) +@KCL-FALL-WIDTH = 0 # set to a value >0 (e.g. 200) to enable KCL falling +@KCL-FALL-PRE-ADD = 0 # add this height or vector before falling +@KCL-FALL-POST-ADD = 0 # add this height or vector after success +@KCL-FALL-TYPES = KCL$DRIVE # bit field of recognized KCL types + # Best values are: + # - KCL$BORDER: All KCL types except effects. + # - KCL$DRIVE: All driveable types like road, boost, ramp, water, ... + +# Snap to nearest point of the enemy route and add the vector. +# Activate it with something like: vy(200) => 200 units higher than the enemy point +@HSNAP-TO-ENPT = $NONE # horizontal snap without adjust, y-rotation +@SNAP-TO-ENPT = $NONE # 3D snap, y-rotation +@SNAP2-TO-ENPT = $NONE # 3D snap, x- and y-rotation + +# Show points in the track by using objects +# Values: 0=off, -1=use default (block of ghost value), >0=force object with 'id' +@ROUTE-OBJECT = mode$showrt & %u ? -1 : 0 +@ROUTE-OBJECT-SHIFT = $NONE # v3(0) # add vector to position +@ROUTE-OBJECT-SCALE = $NONE # v3(1) # define vector scale diff --git a/project/text-files/kmp.inc/kmp-pt2-head-cr.inc b/project/text-files/kmp.inc/kmp-pt2-head-cr.inc new file mode 100644 index 0000000..f7ba30d --- /dev/null +++ b/project/text-files/kmp.inc/kmp-pt2-head-cr.inc @@ -0,0 +1,6 @@ + +#%s# +#------------------------------------------------------------------------------- +# _____________position_____________ _______rotation________ related +# idx x y z x y z id effect +#------------------------------------------------------------------------------- diff --git a/project/text-files/kmp.inc/kmp-setup-cr.inc b/project/text-files/kmp.inc/kmp-setup-cr.inc new file mode 100644 index 0000000..37b170a --- /dev/null +++ b/project/text-files/kmp.inc/kmp-setup-cr.inc @@ -0,0 +1,43 @@ + +#FF# +############################################################################### + +[SETUP] +# This is the general setup and info section. Some parameters are preceeded by +# a hash ('#') to signal the informative only status. This section should +# always be the first one. + +# Identify the tool and version, that have written this file: +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s + +# If the following info string is not empty it will be +# inserted at the end of the KMP file behind all sections: +INFO = "%s" + +# KMP version (offset 0x0c): +KMP-VERSION = %d + +# If set to '1', objects with road features will be added to the internal KCL +# for the automatic height calculations. Like --kcl +add-road +KCL-FALL-ADD-ROAD = 0 + +# Define the battle mode for the next text output: +# 0 : Disable battle support +# 1 : Enable battle support +# 2 : Enable exclusive battle support => remove unneeded sections and parameters +# This setting is overiden by option --battle. +BATTLE-MODE = %d + +# Experimental feature in progress! +# If set to 1, an additional KMP section named »WIM0« is created. This new +# section should contain data of this text file like names or flags, that can +# be restored later. At the moment, the new section contains only dummy data +# for compatibility tests with other tools and the Wii. Option --wim0 can +# override this setting. +KMP-WIM0 = %d + +# Statistics: Entry counter for each section: diff --git a/project/text-files/kmp.inc/kmp-stgi-cr.inc b/project/text-files/kmp.inc/kmp-stgi-cr.inc new file mode 100644 index 0000000..8c8c57d --- /dev/null +++ b/project/text-files/kmp.inc/kmp-stgi-cr.inc @@ -0,0 +1,20 @@ + +#FF# +############################################################################### + +[STGI] +# Stage information +# KMP/STGI syntax & semantics: https://szs.wiimm.de/doc/kmp/stgi + +@REVISION = %u +@STGI-HEAD-VALUE = %#x + +#STGI# +#----------------------------------------------------------------------------- +# Write a floating point number (a '.' is mandatory) instead of the +# 2 last bytes, and it will be rounded and accepted as speed modifier. +#-----------------------------------------------------------------#----------- +# byte byte byte byte u32 byte byte byte byte # last 2 +# laps pole narrow flare flare flare pad or # bytes as +#idx 1..9 1=rt if 1 1=on color alpha 0 speed mod # speed mod +#-----------------------------------------------------------------#----------- diff --git a/project/text-files/lex.inc/lex-elem-cann-cr.inc b/project/text-files/lex.inc/lex-elem-cann-cr.inc new file mode 100644 index 0000000..50ae4ec --- /dev/null +++ b/project/text-files/lex.inc/lex-elem-cann-cr.inc @@ -0,0 +1,15 @@ + +#FF# +############################################################################### + +[CANN] +# Sort order: %u + +# Cannon characteristics: +# http://wiki.tockdom.com/wiki/Template:Cannon_characteristics + +#CANN# +#--------------------------------------------------- +# Deceleration End +# Idx Speed Height Factor Deceleration +#--------------------------------------------------- diff --git a/project/text-files/lex.inc/lex-elem-hipt-cr.inc b/project/text-files/lex.inc/lex-elem-hipt-cr.inc new file mode 100755 index 0000000..479099e --- /dev/null +++ b/project/text-files/lex.inc/lex-elem-hipt-cr.inc @@ -0,0 +1,35 @@ + +#FF# +############################################################################### + +[HIPT] +# Sort order: %u + +# Hide the position tracker based on online mode, the current lap index and +# the current checkpoint of the player. +# +# Each row is a rule. If a player enters a new check point, the first matching +# rule is used. Therefor the current mode (offline or online), the current lap +# index and the index of the current CKPT is compared with the row elements +# COND, LAP, FROM and TO. Lap is 0 until crossing the lap counter the first +# time. This includes the start position. +# +# Row members: +# COND is either 1 for offline only, 2 for online only, or 3 for both cases. +# LAP is compared with the current lap index. Negative values refer to the +# absolute number of laps: -1 is always the last lap and -2 is the penultimate +# lap. This means that the setting remains valid if the number of laps (STGI) +# is changed. The special value 99 means 'match all laps'. +# FROM and TO build an inclusive range to compare the current check point. +# SHOW is either 0 or 1 to hide or show the position tracker. If no rule +# matches, the position tracker is shown (like rule »3 99 0 255 1«). +# +# Notes: Place the most specific rules above and more general rules below on +# the list because of the first-hit search. Parameter @OFFLINE-ONLINE of +# section TEST can be used to force a condition for testing. + +#HIPT# +#-------------------------- +# __CKPT__ +# cond lap from to show +#-------------------------- diff --git a/project/text-files/lex.inc/lex-elem-set1-cr.inc b/project/text-files/lex.inc/lex-elem-set1-cr.inc new file mode 100644 index 0000000..541fc88 --- /dev/null +++ b/project/text-files/lex.inc/lex-elem-set1-cr.inc @@ -0,0 +1,20 @@ + +#FF# +############################################################################### + +[SET1] +# Sort order: %u + +# This section defines various settings. New settings are placed at the end +# end of the binary data. Unknown data is printed as hexdump, so no data is +# lost even with old tools. A value of 0 means: Not defined/initialized, +# replace the value by the default value (maybe 0 again). + +#------------------------- + +# Online, items are only transmitted correctly, if the positions of all 3 axis +# are between -131071 and +131071. The following vector defines 3 factors to +# increase the range at the expense of accuracy. Factors <1.0 are set to 1.0. + +@ITEM-POS-FACTOR = v( %5.3f, %5.3f, %5.3f ) + diff --git a/project/text-files/lex.inc/lex-elem-test-cr.inc b/project/text-files/lex.inc/lex-elem-test-cr.inc new file mode 100644 index 0000000..93bfb4c --- /dev/null +++ b/project/text-files/lex.inc/lex-elem-test-cr.inc @@ -0,0 +1,43 @@ + +#FF# +############################################################################### + +[TEST] +# Sort order: %u + +# This section defines various settings to test special situations. + +#----------------------------------------------------------------------- +# The next set defines parameters to test the extended conditions +# (extended presence flags). The settings allow to force special modes. +# Details: http://wiki.tockdom.com/wiki/Extended_presence_flags +#----------------------------------------------------------------------- + +# Force offline or online mode. Use one symbol of: LEX$OO_AUTO (default), +# LEX$OO_OFFLINE, LEX$OO_ONLINE. +@OFFLINE-ONLINE = LEX$OO_%s + +# If >0: Force number of local players at Wii. It is robust, so values >4 +# are accepted, but rounded down to 4 (offline) or 2 (online). +@N-OFFLINE = %u + +# If >0: Force number of total players for online modes. It is robust, so +# values >99 are rounded down to 99. +@N-ONLINE = %u + +# If between 0 and 15: Use this bit number to check 16-bit words of CONDITIONS +# and ignore previous settings. Good values are: LEX$CB_AUTO, LEX$CB_TIMETRIAL +# (same as LEX$CB_RESERVED1) and LEX$CB_RESERVED2. +@COND-BIT = %d + +# Force a game mode. Use one symbol of: LEX$GM_AUTO (default), LEX$GM_BALLOON, +# LEX$GM_COIN, LEX$GM_VERSUS, LEX$GM_ITEMRAIN or LEX$GM_STANDARD. +@GAME-MODE = LEX$GM_%s + +# Force an engine mode. Use one symbol of: LEX$EN_AUTO (default), +# LEX$EN_BATTLE, LEX$EN_50CC, LEX$EN_100CC, LEX$EN_150CC or LEX$EN_200CC. +# For mirror modes use LEX$EN_150M or LEX$EN_200M. +@ENGINE = LEX$EN_%s + +# If between 1 and 8: Force a random scenario. +@RANDOM = %u diff --git a/project/text-files/lex.inc/lex-head-cr.inc b/project/text-files/lex.inc/lex-head-cr.inc new file mode 100755 index 0000000..5c94c39 --- /dev/null +++ b/project/text-files/lex.inc/lex-head-cr.inc @@ -0,0 +1,12 @@ +#LEX <<< The first 4 characters '#LEX' are the magic for a LEX text file. +# <<< Don't remove them! +# +############################################################################### +# +# This is a textual representation of a LEX file. It is defined by Wiimm. +# +# Hint: +# Set one of the options --no-header (-H) or --brief (-B) while decoding +# to suppress this general description. The --brief option will also disable +# info parts in the different sections. +# diff --git a/project/text-files/lex.inc/lex-hexdump-cr.inc b/project/text-files/lex.inc/lex-hexdump-cr.inc new file mode 100755 index 0000000..156281f --- /dev/null +++ b/project/text-files/lex.inc/lex-hexdump-cr.inc @@ -0,0 +1,16 @@ + +#FF# +############################################################################### + +[HEXDUMP] +# Sort order: %u + +# Unknown sections are stored as hexdump to keep the data. The address field +# and the ASCII part are for info only and ignored on scanning. + +@MAGIC = 0x%08x # %s + +#HEX# +#----+------------------------------------------------------+----------------+ +#addr: data: bytes in hex | '(' expression ')' : ASCII of data : +#----+------------------------------------------------------+----------------+ diff --git a/project/text-files/lex.inc/lex-setup-cr.inc b/project/text-files/lex.inc/lex-setup-cr.inc new file mode 100755 index 0000000..d56bfdb --- /dev/null +++ b/project/text-files/lex.inc/lex-setup-cr.inc @@ -0,0 +1,17 @@ + +#FF# +############################################################################### + +[SETUP] +# This is the general setup and info section. Some parameters are preceeded by +# a hash ('#') to signal the informative only status. This section should +# always be the first one. + +# Identify the tool and version, that have written this file: +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s + +# Statistics: List of all included sections: diff --git a/project/text-files/logo.inc b/project/text-files/logo.inc new file mode 100644 index 0000000..23def7c --- /dev/null +++ b/project/text-files/logo.inc @@ -0,0 +1,14 @@ + *********************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * https://szs.wiimm.de/ * + * * + *********************************** diff --git a/project/text-files/lpar.inc/lpar-head-cr.inc b/project/text-files/lpar.inc/lpar-head-cr.inc new file mode 100644 index 0000000..0b7fa6e --- /dev/null +++ b/project/text-files/lpar.inc/lpar-head-cr.inc @@ -0,0 +1,13 @@ +#LE-LPAR <<< The first 8 characters are the magic for a LE-CODE parameter file. +# <<< Don't remove them! +# +############################################################################### +# +# This LE-CODE parameter file is a textual representation of the LPAR section +# of LE-CODE binary fils with editable parameters. It is defined by Wiimm. +# +# Hint: +# Set one of the options --no-header (-H) or --brief (-B) while decoding +# to suppress this general description. The --brief option will also disable +# info parts in the different sections. +# diff --git a/project/text-files/lpar.inc/lpar-sect-chat-cr.inc b/project/text-files/lpar.inc/lpar-sect-chat-cr.inc new file mode 100644 index 0000000..4685cea --- /dev/null +++ b/project/text-files/lpar.inc/lpar-sect-chat-cr.inc @@ -0,0 +1,56 @@ + +# LE-CODE supports a special feature for chat messages in private rooms. If a +# message is send by the host and at least one guest is present, then some +# messages change the behavior of the game. This section defines the special +# function. + +# The first parameter resets all message to CHAT$OFF (no function). +# The default is value 1. If set to 0, the current map is edited. +@reset = 1 + +# Set the value to 1 if you want to use the old behavior defined for MKW-Fun. +@legacy = 0 + +# And now define the functions for different chat messages. The syntax is: +# +# MESSAGE '=' EXPRESSION1 +# MESSAGE '=' EXPRESSION1 ',' EXPRESSION2 +# +# MESSAGE is the letter 'M' followed by a number between 1 and 96. Examples +# are M1, M02, M96. EXPRESSION is any expression supported by the parser. +# Up to 2 modes (separated by a comma) can be defined for each message. +# Usually the following constants or functions are used: +# +# CHAT$TRACK_BY_HOST : The host selects a tracks and wins the lottery. +# The track selection for guests is disabled. +# CHAT$ANY_TRACK : Cancel CHAT$TRACK_BY_HOST. +# +# CHAT$BLOCK_CLEAR : Clear the used-tracks list. +# CHAT$BLOCK_DISABLE : Disable the block-track functionality. +# CHAT$BLOCK_ENABLE : Enable the block-track functionality. This is the +# default, but have only impact, if BLOCK-TRACK > 0. +# +# CHAT$KARTS_ONLY : Each player must select a kart. +# CHAT$BIKES_ONLY : Each player must select a bike. +# CHAT$ANY_VEHICLE : Cancel vehicle requirement. +# +# CHAT$USE_ENGINE_1 : Select first engine class (100cc or 150cc if 200cc enabled) +# CHAT$USE_ENGINE_2 : Select second engine class (150cc or 200cc if 200cc enabled) +# CHAT$USE_ENGINE_3 : Select third engine class (mirror) +# CHAT$RESET_ENGINE : Cancel engine selection. +# +# chat$n_races(N) : Define the number of races in the next Grand Prix. +# N is a number between 1 and 512 (inclusive). +# +# chat$vehicle(...) : Define a chat modus for a vehicle group. 0 to N values +# are expected as function parameters. Each parameter is +# either from the group VEH$SMALL, VEH$MEDIUM, VEH$LARGE +# and VEH$ANY_SIZE for size selections and/or from the +# group VEH$KART, VEH$BIKE, VEH$ANY_TYPE for type +# selections. VEH$ANY resets the limitations. +~#VEH$OUT_BIKE, VEH$IN_BIKE +# +# CHAT$RESET : Reset all settings to their defaults. +# +# See http://wiki.tockdom.com/wiki/LE-CODE/Distribution_Tutorial#chatmsg +# for more details. diff --git a/project/text-files/lpar.inc/lpar-sect-chat-example-cr.inc b/project/text-files/lpar.inc/lpar-sect-chat-example-cr.inc new file mode 100644 index 0000000..a8af2f4 --- /dev/null +++ b/project/text-files/lpar.inc/lpar-sect-chat-example-cr.inc @@ -0,0 +1,4 @@ +# +# Examples: +# M01 = CHAT$TRACK_BY_HOST +# M96 = chat$n_races(15), CHAT$USE_ENGINE_2 diff --git a/project/text-files/lpar.inc/lpar-sect-param-cr.inc b/project/text-files/lpar.inc/lpar-sect-param-cr.inc new file mode 100644 index 0000000..f01d07c --- /dev/null +++ b/project/text-files/lpar.inc/lpar-sect-param-cr.inc @@ -0,0 +1,60 @@ + +# This section defines general LE-CODE parameters. The corresponding options +# have a higher priority. Remove or comment-out a parameter if its value +# should not be changed. All values will be limited to the allowed ranges. + +#------------------------------------------------------------------------------ + +# First we define a limitation mode. This mode defines the allowed values for +# the other parameters of this section: +# +# LE$PRODUCTIVE : Exclude test and experimental values. +# LE$TESTING : Allow test values, but exclude experimental values. +# LE$EXPERIMENTAL : Allow all values. +# LE$AUTOMATIC : Enable automatic detection by analyzing all values. +# +# Values set by an option are never limited. See parameter descriptions for +# limitation details. + +LIMIT-MODE = %s + +#------------------------------------------------------------------------------ + +# Define probabilities for engine classes. A list for »100cc,150cc,mirror« is +# expected. If 200cc is enabled, the values are for »150cc,200cc,mirror«. Any +# number is accepted. They are normalized to get a total of 100 percent. +# Use »0,1,0« to force 150cc. Use »0,0,0« to reset to Nintendo's VR based +# choice. Option --engine will override these settings. + +ENGINE = %u,%u,%u + +# Enable (1) or disable (0) 200cc support. Option --200cc will override this +# setting. LE-CODE hasn't implemented this feature yet! + +ENABLE-200CC = %u + +# Enable (1) or disable (0) the performance monitor at Wii and Wii U. Enabling +# is only possible if LIMIT-MODE is LE$TESTING or LE$EXPERIMENTAL. The special +# value 2 enables it for Dolphin too, but only on LE$EXPERIMENTAL. It doesn't +# work correctly with Dolphin. Option --perf-mon will override this setting. + +PERF-MONITOR = %u + +# Enable (1) or disable (0) Time Trial for custom tracks. Option --custom-tt +# will override this setting. + +CUSTOM-TT = %u + +# Enable (1) or disable (0) support for Extended Presence Flags. Disable it +# only for tests! If LIMIT-MODE==LE$PRODUCTIVE, XPFLAGS is always enabled. +# Option --xpflags will override this setting. + +XPFLAGS = %u + +# Define the number of races that a previously used track is blocked. LE-CODE +# will try to find another possible track among the selected tracks. Values +# between 0 (deactivated at all) and %u are allowed. Chat messages can clear +# the track list and enable or disable this feature. + +BLOCK-TRACK = %u + diff --git a/project/text-files/mdl.inc/mdl-file-head-cr.inc b/project/text-files/mdl.inc/mdl-file-head-cr.inc new file mode 100644 index 0000000..7725443 --- /dev/null +++ b/project/text-files/mdl.inc/mdl-file-head-cr.inc @@ -0,0 +1,26 @@ +#MDL <<< The first 4 characters '#MDL' are the magic for a MDL text file. +# <<< Don't remove them! +# +############################################################################### +# +# This is a textual representation of a MDL file used in Nintendos "Mario Kart +# Wii". "Wiimms SZS Tools" may convert binary (raw) and text MDL files in both +# directions in the near future. The text parser supports variables, C like +# expressions and nested IF-THEN-ELSE structures. +# +# Info about the general parser syntax and semantics: +# * https://szs.wiimm.de/doc/syntax +# +# Info about the MDL text syntax and semantics: +# * https://szs.wiimm.de/doc/mdl/syntax +# +# Reference list of MDL parser functions: +# * https://szs.wiimm.de/doc/mdl/func +# +# Info about the MDL file format: +# * https://szs.wiimm.de/r/wiki/MDL0 +# +# Hint: +# Set one of the options --no-header (-H) or --brief (-B) while decoding +# to suppress this large syntax info. The --brief option will also disable +# info parts in the different sections. diff --git a/project/text-files/mdl.inc/mdl-header-cr.inc b/project/text-files/mdl.inc/mdl-header-cr.inc new file mode 100644 index 0000000..315cd77 --- /dev/null +++ b/project/text-files/mdl.inc/mdl-header-cr.inc @@ -0,0 +1,8 @@ + +#FF# +############################################################################### + +[HEADER] +# MDL header +@REVISION = %u + diff --git a/project/text-files/mdl.inc/mdl-hexdump-cr.inc b/project/text-files/mdl.inc/mdl-hexdump-cr.inc new file mode 100755 index 0000000..caa6029 --- /dev/null +++ b/project/text-files/mdl.inc/mdl-hexdump-cr.inc @@ -0,0 +1,4 @@ +$HEX # switch to hexdump modus +#----+------------------------------------------------------+----------------+ +#addr: data: bytes in hex | '(' expression ')' : ASCII of data : +#----+------------------------------------------------------+----------------+ diff --git a/project/text-files/mdl.inc/mdl-s0-cr.inc b/project/text-files/mdl.inc/mdl-s0-cr.inc new file mode 100644 index 0000000..7bc1f1f --- /dev/null +++ b/project/text-files/mdl.inc/mdl-s0-cr.inc @@ -0,0 +1,19 @@ +# This section represents one draw list. +# +# A draw list is a sequence of u8, u16 (1 and 2 byte unsigned integers) and +# float (4 bytes) values divided into commands. For each command the first +# byte declares the length and the meaning of the command: +# +# #1 : 1 byte, end of command list +# #2 : 5 bytes, bone mapping +# #3 : 4+N*6 bytes, weighting +# #4 : 8 bytes, draw polygon +# #5 : 5 bytes, indexing +# +# And these are the codings of the commands: +# #1 +# #2, u16:weight_index, u16:parent:weight_index +# #3, u16:wight_id, u8:N, N*( u16:table_id, float:weight ) +# #4, u16:object_index, u16:material_index, u16:bone_index, u8:unknown +# #5, u16:weight_id, u16:weight_index + diff --git a/project/text-files/mdl.inc/mdl-s0-tabhead-cr.inc b/project/text-files/mdl.inc/mdl-s0-tabhead-cr.inc new file mode 100644 index 0000000..d9cc11f --- /dev/null +++ b/project/text-files/mdl.inc/mdl-s0-tabhead-cr.inc @@ -0,0 +1,2 @@ +# cmd command dependent parameters +#------------------------------------- diff --git a/project/text-files/mdl.inc/mdl-setup-cr.inc b/project/text-files/mdl.inc/mdl-setup-cr.inc new file mode 100644 index 0000000..da5d0ff --- /dev/null +++ b/project/text-files/mdl.inc/mdl-setup-cr.inc @@ -0,0 +1,21 @@ + +#FF# +############################################################################### + +[SETUP] +# This is the general setup and info section. Some parameters are preceeded by +# a hash ('#') to signal the informative only status. The scanning starts +# always with this section. + +# Identify the tool and version, that have written this file: +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s + +# The MDL name & version and the number of sections +@MDL-NAME = "%s" +@MDL-VERSION = %2u +@N-SECTIONS = %2u + diff --git a/project/text-files/minigame.inc/minigame-head-cr.inc b/project/text-files/minigame.inc/minigame-head-cr.inc new file mode 100755 index 0000000..2cf6953 --- /dev/null +++ b/project/text-files/minigame.inc/minigame-head-cr.inc @@ -0,0 +1 @@ +#KMG diff --git a/project/text-files/minigame.inc/minigame-info-cr.inc b/project/text-files/minigame.inc/minigame-info-cr.inc new file mode 100755 index 0000000..20aa095 --- /dev/null +++ b/project/text-files/minigame.inc/minigame-info-cr.inc @@ -0,0 +1,24 @@ +#KMG <<< The first 4 characters are the magic for minigame.kmg text files. +# <<< Don't remove them! +# +#------------------------------------------------------------------------------ +# +# This is the textual representation of an minigame.kmg file. +# It is defined by Wiimm and scanned by a 2-pass parser. +# +# Hints for decoding to a text file: +# +# - If either option --no-header (-H) or option --brief (-B) is set, this +# general description is suppressed. +# +# - If either option --export (-X) is set once or option --brief (-B) is set +# twice, a very small text file without descriptions and other not needed +# parameters is created. +# +# - If option --long (-l) is set, then additional hidden parameters are +# included. They are automatically included, if they are differ from the +# the standard settings. +# +# See http://wiki.tockdom.com/wiki/minigame.kmg for more details. +# +#------------------------------------------------------------------------------ diff --git a/project/text-files/minigame.inc/minigame-param-cr.inc b/project/text-files/minigame.inc/minigame-param-cr.inc new file mode 100755 index 0000000..286c2e0 --- /dev/null +++ b/project/text-files/minigame.inc/minigame-param-cr.inc @@ -0,0 +1,2 @@ +[PARAMETERS] +@REVISION = %u diff --git a/project/text-files/minigame.inc/minigame-setup-cr.inc b/project/text-files/minigame.inc/minigame-setup-cr.inc new file mode 100755 index 0000000..c8d5b7c --- /dev/null +++ b/project/text-files/minigame.inc/minigame-setup-cr.inc @@ -0,0 +1,12 @@ + +# +############################################################################### + +[SETUP] + +# identification of creator tool +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s diff --git a/project/text-files/minigame.inc/minigame-table-cr.inc b/project/text-files/minigame.inc/minigame-table-cr.inc new file mode 100755 index 0000000..80700c0 --- /dev/null +++ b/project/text-files/minigame.inc/minigame-table-cr.inc @@ -0,0 +1,3 @@ +[%s] # table at offset 0x%03x +@REVISION = %u + diff --git a/project/text-files/minigame.inc/minigame-table-head-cr.inc b/project/text-files/minigame.inc/minigame-table-head-cr.inc new file mode 100755 index 0000000..5737e65 --- /dev/null +++ b/project/text-files/minigame.inc/minigame-table-head-cr.inc @@ -0,0 +1,3 @@ +#--------------------------------------------------------------#-------------------------- +# slot N=2 3 4 5 6 7 8 9 10 11 12 # arena info +#--------------------------------------------------------------#-------------------------- diff --git a/project/text-files/obj-mtl-bz2.inc b/project/text-files/obj-mtl-bz2.inc new file mode 100644 index 0000000..6286940 --- /dev/null +++ b/project/text-files/obj-mtl-bz2.inc @@ -0,0 +1,1343 @@ + +newmtl road_1 + Kd 1.00 0.00 0.00 + Ks 1.00 0.00 0.00 + +newmtl road_2 + Kd 0.91 0.00 0.91 + Ks 0.91 0.00 0.91 + +newmtl road_3 + Kd 0.85 0.00 0.00 + Ks 0.85 0.00 0.00 + +newmtl road_4 + Kd 0.73 0.00 0.73 + Ks 0.73 0.00 0.73 + +newmtl road_5 + Kd 0.71 0.00 0.00 + Ks 0.71 0.00 0.00 + +newmtl road_6 + Kd 0.55 0.00 0.55 + Ks 0.55 0.00 0.55 + +newmtl road_7 + Kd 0.57 0.00 0.00 + Ks 0.57 0.00 0.00 + +newmtl road_8 + Kd 0.38 0.00 0.38 + Ks 0.38 0.00 0.38 + +newmtl road_9 + Kd 1.00 0.00 1.00 + Ks 1.00 0.00 1.00 + +newmtl road_10 + Kd 0.93 0.00 0.00 + Ks 0.93 0.00 0.00 + +newmtl road_11 + Kd 0.82 0.00 0.82 + Ks 0.82 0.00 0.82 + +newmtl road_12 + Kd 0.78 0.00 0.00 + Ks 0.78 0.00 0.00 + +newmtl road_13 + Kd 0.64 0.00 0.64 + Ks 0.64 0.00 0.64 + +newmtl road_14 + Kd 0.64 0.00 0.00 + Ks 0.64 0.00 0.00 + +newmtl road_15 + Kd 0.46 0.00 0.46 + Ks 0.46 0.00 0.46 + +newmtl road_16 + Kd 0.50 0.00 0.00 + Ks 0.50 0.00 0.00 + +newmtl offroad_1 + Kd 0.75 0.38 0.38 + Ks 0.75 0.38 0.38 + +newmtl offroad_2 + Kd 0.95 0.25 0.00 + Ks 0.95 0.25 0.00 + +newmtl offroad_3 + Kd 0.64 0.30 0.27 + Ks 0.64 0.30 0.27 + +newmtl offroad_4 + Kd 0.84 0.25 0.00 + Ks 0.84 0.25 0.00 + +newmtl offroad_5 + Kd 0.54 0.23 0.16 + Ks 0.54 0.23 0.16 + +newmtl offroad_6 + Kd 0.73 0.25 0.00 + Ks 0.73 0.25 0.00 + +newmtl offroad_7 + Kd 0.43 0.16 0.05 + Ks 0.43 0.16 0.05 + +newmtl offroad_8 + Kd 0.63 0.25 0.00 + Ks 0.63 0.25 0.00 + +newmtl offroad_9 + Kd 1.00 0.25 0.00 + Ks 1.00 0.25 0.00 + +newmtl offroad_10 + Kd 0.70 0.34 0.32 + Ks 0.70 0.34 0.32 + +newmtl offroad_11 + Kd 0.89 0.25 0.00 + Ks 0.89 0.25 0.00 + +newmtl offroad_12 + Kd 0.59 0.27 0.21 + Ks 0.59 0.27 0.21 + +newmtl offroad_13 + Kd 0.78 0.25 0.00 + Ks 0.78 0.25 0.00 + +newmtl offroad_14 + Kd 0.48 0.20 0.11 + Ks 0.48 0.20 0.11 + +newmtl offroad_15 + Kd 0.68 0.25 0.00 + Ks 0.68 0.25 0.00 + +newmtl offroad_16 + Kd 0.38 0.13 0.00 + Ks 0.38 0.13 0.00 + +newmtl fast_1 + Kd 1.00 0.63 0.19 + Ks 1.00 0.63 0.19 + +newmtl fast_2 + Kd 0.90 0.47 0.14 + Ks 0.90 0.47 0.14 + +newmtl fast_3 + Kd 0.95 0.55 0.16 + Ks 0.95 0.55 0.16 + +newmtl fast_4 + Kd 0.97 0.59 0.17 + Ks 0.97 0.59 0.17 + +newmtl fast_5 + Kd 0.88 0.44 0.13 + Ks 0.88 0.44 0.13 + +newmtl fast_6 + Kd 0.93 0.51 0.15 + Ks 0.93 0.51 0.15 + +newmtl halfpipe_1 + Kd 0.00 0.50 1.00 + Ks 0.00 0.50 1.00 + +newmtl halfpipe_2 + Kd 0.00 0.41 0.81 + Ks 0.00 0.41 0.81 + +newmtl halfpipe_3 + Kd 0.00 0.47 0.94 + Ks 0.00 0.47 0.94 + +newmtl halfpipe_4 + Kd 0.00 0.38 0.75 + Ks 0.00 0.38 0.75 + +newmtl halfpipe_5 + Kd 0.00 0.44 0.87 + Ks 0.00 0.44 0.87 + +newmtl moving_1 + Kd 1.00 0.00 0.38 + Ks 1.00 0.00 0.38 + +newmtl moving_2 + Kd 0.81 0.00 0.38 + Ks 0.81 0.00 0.38 + +newmtl moving_3 + Kd 0.94 0.00 0.38 + Ks 0.94 0.00 0.38 + +newmtl moving_4 + Kd 0.75 0.00 0.38 + Ks 0.75 0.00 0.38 + +newmtl moving_5 + Kd 0.87 0.00 0.38 + Ks 0.87 0.00 0.38 + +newmtl water_1 + Kd 0.00 0.00 1.00 + Ks 0.00 0.00 1.00 + +newmtl water_2 + Kd 0.00 0.00 0.64 + Ks 0.00 0.00 0.64 + +newmtl water_3 + Kd 0.00 0.00 0.85 + Ks 0.00 0.00 0.85 + +newmtl water_4 + Kd 0.00 0.00 0.50 + Ks 0.00 0.00 0.50 + +newmtl water_5 + Kd 0.00 0.00 0.71 + Ks 0.00 0.00 0.71 + +newmtl water_6 + Kd 0.00 0.00 0.93 + Ks 0.00 0.00 0.93 + +newmtl water_7 + Kd 0.00 0.00 0.57 + Ks 0.00 0.00 0.57 + +newmtl water_8 + Kd 0.00 0.00 0.78 + Ks 0.00 0.00 0.78 + +newmtl cannon_1 + Kd 1.00 0.50 1.00 + Ks 1.00 0.50 1.00 + +newmtl cannon_2 + Kd 0.62 0.31 0.62 + Ks 0.62 0.31 0.62 + +newmtl cannon_3 + Kd 0.87 0.44 0.87 + Ks 0.87 0.44 0.87 + +newmtl cannon_4 + Kd 0.50 0.25 0.50 + Ks 0.50 0.25 0.50 + +newmtl cannon_5 + Kd 0.75 0.38 0.75 + Ks 0.75 0.38 0.75 + +newmtl wall_1 + d 0.63 + Tr 0.37 + Kd 0.00 0.50 0.00 + Ks 0.00 0.50 0.00 + +newmtl wall_2 + d 0.63 + Tr 0.37 + Kd 0.00 0.57 0.29 + Ks 0.00 0.57 0.29 + +newmtl wall_3 + d 0.63 + Tr 0.37 + Kd 0.14 0.64 0.07 + Ks 0.14 0.64 0.07 + +newmtl wall_4 + d 0.63 + Tr 0.37 + Kd 0.00 0.71 0.36 + Ks 0.00 0.71 0.36 + +newmtl wall_5 + d 0.63 + Tr 0.37 + Kd 0.29 0.78 0.14 + Ks 0.29 0.78 0.14 + +newmtl wall_6 + d 0.63 + Tr 0.37 + Kd 0.00 0.85 0.43 + Ks 0.00 0.85 0.43 + +newmtl wall_7 + d 0.63 + Tr 0.37 + Kd 0.43 0.93 0.21 + Ks 0.43 0.93 0.21 + +newmtl wall_8 + d 0.63 + Tr 0.37 + Kd 0.00 1.00 0.50 + Ks 0.00 1.00 0.50 + +newmtl wall_9 + d 0.63 + Tr 0.37 + Kd 0.00 0.50 0.25 + Ks 0.00 0.50 0.25 + +newmtl wall_10 + d 0.63 + Tr 0.37 + Kd 0.07 0.57 0.04 + Ks 0.07 0.57 0.04 + +newmtl wall_11 + d 0.63 + Tr 0.37 + Kd 0.00 0.64 0.32 + Ks 0.00 0.64 0.32 + +newmtl wall_12 + d 0.63 + Tr 0.37 + Kd 0.21 0.71 0.11 + Ks 0.21 0.71 0.11 + +newmtl wall_13 + d 0.63 + Tr 0.37 + Kd 0.00 0.78 0.39 + Ks 0.00 0.78 0.39 + +newmtl wall_14 + d 0.63 + Tr 0.37 + Kd 0.36 0.85 0.18 + Ks 0.36 0.85 0.18 + +newmtl wall_15 + d 0.63 + Tr 0.37 + Kd 0.00 0.93 0.46 + Ks 0.00 0.93 0.46 + +newmtl wall_16 + d 0.63 + Tr 0.37 + Kd 0.50 1.00 0.25 + Ks 0.50 1.00 0.25 + +newmtl fall_1 + Kd 0.00 0.00 0.00 + Ks 0.00 0.00 0.00 + +newmtl fall_2 + Kd 0.19 0.06 0.06 + Ks 0.19 0.06 0.06 + +newmtl fall_3 + Kd 0.13 0.13 0.13 + Ks 0.13 0.13 0.13 + +newmtl fall_4 + Kd 0.31 0.19 0.19 + Ks 0.31 0.19 0.19 + +newmtl fall_5 + Kd 0.25 0.25 0.25 + Ks 0.25 0.25 0.25 + +newmtl fall_6 + Kd 0.06 0.06 0.06 + Ks 0.06 0.06 0.06 + +newmtl fall_7 + Kd 0.25 0.13 0.13 + Ks 0.25 0.13 0.13 + +newmtl fall_8 + Kd 0.19 0.19 0.19 + Ks 0.19 0.19 0.19 + +newmtl fall_9 + Kd 0.38 0.25 0.25 + Ks 0.38 0.25 0.25 + +newmtl fall_10 + Kd 0.13 0.00 0.00 + Ks 0.13 0.00 0.00 + +newmtl effect_1 + d 0.63 + Tr 0.37 + Kd 0.00 1.00 1.00 + Ks 0.00 1.00 1.00 + +newmtl effect_2 + d 0.63 + Tr 0.37 + Kd 0.08 0.67 0.67 + Ks 0.08 0.67 0.67 + +newmtl effect_3 + d 0.63 + Tr 0.37 + Kd 0.03 0.89 0.89 + Ks 0.03 0.89 0.89 + +newmtl effect_4 + d 0.63 + Tr 0.37 + Kd 0.11 0.56 0.56 + Ks 0.11 0.56 0.56 + +newmtl effect_5 + d 0.63 + Tr 0.37 + Kd 0.05 0.78 0.78 + Ks 0.05 0.78 0.78 + +newmtl effect_6 + d 0.63 + Tr 0.37 + Kd 0.01 0.94 0.94 + Ks 0.01 0.94 0.94 + +newmtl effect_7 + d 0.63 + Tr 0.37 + Kd 0.09 0.61 0.61 + Ks 0.09 0.61 0.61 + +newmtl effect_8 + d 0.63 + Tr 0.37 + Kd 0.04 0.83 0.83 + Ks 0.04 0.83 0.83 + +newmtl effect_9 + d 0.63 + Tr 0.37 + Kd 0.13 0.50 0.50 + Ks 0.13 0.50 0.50 + +newmtl effect_10 + d 0.63 + Tr 0.37 + Kd 0.07 0.72 0.72 + Ks 0.07 0.72 0.72 + +newmtl item_1 + Kd 0.19 1.00 0.50 + Ks 0.19 1.00 0.50 + +newmtl item_2 + Kd 0.12 0.81 0.41 + Ks 0.12 0.81 0.41 + +newmtl item_3 + Kd 0.16 0.94 0.47 + Ks 0.16 0.94 0.47 + +newmtl item_4 + Kd 0.09 0.75 0.38 + Ks 0.09 0.75 0.38 + +newmtl item_5 + Kd 0.14 0.87 0.44 + Ks 0.14 0.87 0.44 + +newmtl unknown_1 + d 0.63 + Tr 0.37 + Kd 1.00 1.00 1.00 + Ks 1.00 1.00 1.00 + +newmtl unknown_2 + d 0.63 + Tr 0.37 + Kd 0.82 0.82 0.94 + Ks 0.82 0.82 0.94 + +newmtl unknown_3 + d 0.63 + Tr 0.37 + Kd 0.87 0.87 0.87 + Ks 0.87 0.87 0.87 + +newmtl unknown_4 + d 0.63 + Tr 0.37 + Kd 0.69 0.69 0.81 + Ks 0.69 0.69 0.81 + +newmtl unknown_5 + d 0.63 + Tr 0.37 + Kd 0.75 0.75 0.75 + Ks 0.75 0.75 0.75 + +newmtl unknown_6 + d 0.63 + Tr 0.37 + Kd 0.94 0.94 0.94 + Ks 0.94 0.94 0.94 + +newmtl unknown_7 + d 0.63 + Tr 0.37 + Kd 0.75 0.75 0.87 + Ks 0.75 0.75 0.87 + +newmtl unknown_8 + d 0.63 + Tr 0.37 + Kd 0.81 0.81 0.81 + Ks 0.81 0.81 0.81 + +newmtl unknown_9 + d 0.63 + Tr 0.37 + Kd 0.63 0.63 0.75 + Ks 0.63 0.63 0.75 + +newmtl unknown_10 + d 0.63 + Tr 0.37 + Kd 0.88 0.88 1.00 + Ks 0.88 0.88 1.00 + +newmtl kmp_ckpt_warning + Kd 0.75 0.00 0.00 + Ks 0.75 0.00 0.00 + +newmtl kmp_ckpt_warning_t + d 0.69 + Tr 0.31 + Kd 0.75 0.00 0.00 + Ks 0.75 0.00 0.00 + +newmtl kmp_ckpt_warning_tt + d 0.44 + Tr 0.56 + Kd 0.75 0.00 0.00 + Ks 0.75 0.00 0.00 + +newmtl kmp_ckpt_lap + Kd 0.13 0.00 0.50 + Ks 0.13 0.00 0.50 + +newmtl kmp_ckpt_lap_t + d 0.69 + Tr 0.31 + Kd 0.13 0.00 0.50 + Ks 0.13 0.00 0.50 + +newmtl kmp_ckpt_lap_tt + d 0.44 + Tr 0.56 + Kd 0.13 0.00 0.50 + Ks 0.13 0.00 0.50 + +newmtl kmp_ckpt_mandatory + Kd 0.00 0.00 0.75 + Ks 0.00 0.00 0.75 + +newmtl kmp_ckpt_mandatory_t + d 0.69 + Tr 0.31 + Kd 0.00 0.00 0.75 + Ks 0.00 0.00 0.75 + +newmtl kmp_ckpt_mandatory_tt + d 0.44 + Tr 0.56 + Kd 0.00 0.00 0.75 + Ks 0.00 0.00 0.75 + +newmtl kmp_ckpt_mandatory5 + Kd 0.00 0.00 0.25 + Ks 0.00 0.00 0.25 + +newmtl kmp_ckpt_mandatory5_t + d 0.69 + Tr 0.31 + Kd 0.00 0.00 0.25 + Ks 0.00 0.00 0.25 + +newmtl kmp_ckpt_mandatory5_tt + d 0.44 + Tr 0.56 + Kd 0.00 0.00 0.25 + Ks 0.00 0.00 0.25 + +newmtl kmp_ckpt_etc + Kd 0.00 0.75 0.75 + Ks 0.00 0.75 0.75 + +newmtl kmp_ckpt_etc_t + d 0.69 + Tr 0.31 + Kd 0.00 0.75 0.75 + Ks 0.00 0.75 0.75 + +newmtl kmp_ckpt_etc_tt + d 0.44 + Tr 0.56 + Kd 0.00 0.75 0.75 + Ks 0.00 0.75 0.75 + +newmtl kmp_ckpt_tie_lt + Kd 0.50 0.00 0.00 + Ks 0.50 0.00 0.00 + +newmtl kmp_ckpt_tie_rt + Kd 0.00 0.50 0.00 + Ks 0.00 0.50 0.00 + +newmtl kmp_ckpt_tie_group_lt + Kd 0.25 0.00 0.00 + Ks 0.25 0.00 0.00 + +newmtl kmp_ckpt_tie_group_rt + Kd 0.00 0.25 0.00 + Ks 0.00 0.25 0.00 + +newmtl kmp_ckpt_tie_next_lt + Kd 0.31 0.00 0.19 + Ks 0.31 0.00 0.19 + +newmtl kmp_ckpt_tie_next_rt + Kd 0.00 0.31 0.31 + Ks 0.00 0.31 0.31 + +newmtl kmp_ckpt_jgpt + Kd 0.00 0.50 0.00 + Ks 0.00 0.50 0.00 + +newmtl kmp_jgpt + Kd 0.00 0.25 0.00 + Ks 0.00 0.25 0.00 + +newmtl kmp_jgpt_area + d 0.50 + Tr 0.50 + Kd 0.13 0.63 0.13 + Ks 0.13 0.63 0.13 + +newmtl kmp_ktpt + Kd 0.00 0.00 0.00 + Ks 0.00 0.00 0.00 + +newmtl kmp_ktpt_area + d 0.50 + Tr 0.50 + Kd 0.50 0.50 0.50 + Ks 0.50 0.50 0.50 + +newmtl kmp_enpt + d 0.75 + Tr 0.25 + Kd 0.00 0.00 0.50 + Ks 0.00 0.00 0.50 + +newmtl kmp_enpt_single + d 0.75 + Tr 0.25 + Kd 0.00 0.00 0.50 + Ks 0.00 0.00 0.50 + +newmtl kmp_enpt_tie_bi + d 0.75 + Tr 0.25 + Kd 0.00 0.25 0.63 + Ks 0.00 0.25 0.63 + +newmtl kmp_enpt_tie_next + d 0.75 + Tr 0.25 + Kd 0.00 0.38 0.69 + Ks 0.00 0.38 0.69 + +newmtl kmp_enpt_tie_prev + d 0.75 + Tr 0.25 + Kd 0.00 0.69 0.69 + Ks 0.00 0.69 0.69 + +newmtl kmp_enpt_dispatch + Kd 0.00 0.50 0.75 + Ks 0.00 0.50 0.75 + +newmtl kmp_itpt + d 0.75 + Tr 0.25 + Kd 0.25 0.00 0.00 + Ks 0.25 0.00 0.00 + +newmtl kmp_itpt_single + d 0.75 + Tr 0.25 + Kd 0.25 0.00 0.00 + Ks 0.25 0.00 0.00 + +newmtl kmp_itpt_tie_bi + d 0.75 + Tr 0.25 + Kd 0.38 0.00 0.25 + Ks 0.38 0.00 0.25 + +newmtl kmp_itpt_tie_next + d 0.75 + Tr 0.25 + Kd 0.50 0.00 0.31 + Ks 0.50 0.00 0.31 + +newmtl kmp_itpt_tie_prev + d 0.75 + Tr 0.25 + Kd 0.63 0.00 0.63 + Ks 0.63 0.00 0.63 + +newmtl kmp_itpt_dispatch + Kd 0.88 0.00 0.50 + Ks 0.88 0.00 0.50 + +newmtl kmp_cnpt_landing + Kd 1.00 0.50 1.00 + Ks 1.00 0.50 1.00 + +newmtl kmp_cnpt_way + Kd 1.00 0.63 1.00 + Ks 1.00 0.63 1.00 + +newmtl kmp_area_arrow + Kd 0.50 0.50 0.00 + Ks 0.50 0.50 0.00 + +newmtl kmp_area_border + d 0.25 + Tr 0.75 + Kd 0.50 0.50 0.00 + Ks 0.50 0.50 0.00 + +newmtl kmp_poti0 + d 0.75 + Tr 0.25 + Kd 0.00 0.50 0.00 + Ks 0.00 0.50 0.00 + +newmtl kmp_poti1 + d 0.75 + Tr 0.25 + Kd 0.00 0.57 0.29 + Ks 0.00 0.57 0.29 + +newmtl kmp_poti2 + d 0.75 + Tr 0.25 + Kd 0.14 0.64 0.07 + Ks 0.14 0.64 0.07 + +newmtl kmp_poti3 + d 0.75 + Tr 0.25 + Kd 0.14 0.64 0.07 + Ks 0.14 0.64 0.07 + +newmtl kmp_itembox + Kd 0.44 0.44 0.44 + Ks 0.44 0.44 0.44 + +newmtl kmp_itembox_enemy + Kd 0.25 0.25 0.25 + Ks 0.25 0.25 0.25 + +newmtl kmp_itembox_player + Kd 0.25 0.06 0.06 + Ks 0.25 0.06 0.06 + +newmtl kmp_itemroute + d 0.88 + Tr 0.12 + Kd 0.38 0.63 0.38 + Ks 0.38 0.63 0.38 + +newmtl kmp_itemroute_enemy + d 0.88 + Tr 0.12 + Kd 0.19 0.44 0.19 + Ks 0.19 0.44 0.19 + +newmtl kmp_itemroute_player + d 0.88 + Tr 0.12 + Kd 0.31 0.06 0.06 + Ks 0.31 0.06 0.06 + +newmtl kmp_kinoko_green + Kd 0.00 0.44 0.00 + Ks 0.00 0.44 0.00 + +newmtl kmp_kinoko_red + Kd 0.56 0.09 0.00 + Ks 0.56 0.09 0.00 + +newmtl kmp_bridge + Kd 0.44 0.25 0.38 + Ks 0.44 0.25 0.38 + +newmtl kmp_dokan + Kd 0.06 0.48 0.09 + Ks 0.06 0.48 0.09 + +newmtl kmp_karehayama + Kd 0.87 0.51 0.19 + Ks 0.87 0.51 0.19 + +newmtl kmp_obakeblock + Kd 0.44 0.37 0.21 + Ks 0.44 0.37 0.21 + +newmtl kmp_tree_crown + d 0.25 + Tr 0.75 + Kd 0.00 0.50 0.00 + Ks 0.00 0.50 0.00 + +newmtl kmp_tree_trunk + Kd 0.35 0.20 0.06 + Ks 0.35 0.20 0.06 + +newmtl kmp_woodbox + Kd 0.61 0.41 0.13 + Ks 0.61 0.41 0.13 + +newmtl kmp_coin_start + Kd 0.88 0.69 0.00 + Ks 0.88 0.69 0.00 + +newmtl kmp_coin_respawn + Kd 1.00 0.84 0.00 + Ks 1.00 0.84 0.00 + +newmtl kmp_coin_invalid + Kd 1.00 0.69 0.00 + Ks 1.00 0.69 0.00 + +newmtl kmp_solid_object + Kd 0.25 0.25 0.50 + Ks 0.25 0.25 0.50 + +newmtl kmp_solid_route + d 0.88 + Tr 0.12 + Kd 0.13 0.50 0.50 + Ks 0.13 0.50 0.50 + +newmtl kmp_deco_object + d 0.75 + Tr 0.25 + Kd 0.50 0.00 1.00 + Ks 0.50 0.00 1.00 + +newmtl kmp_zero_ground_black + Kd 0.06 0.06 0.06 + Ks 0.06 0.06 0.06 + +newmtl kmp_zero_ground_white + Kd 0.94 0.94 0.94 + Ks 0.94 0.94 0.94 + +newmtl red + Kd 1.00 0.13 0.13 + Ks 1.00 0.13 0.13 + +newmtl red_t + d 0.69 + Tr 0.31 + Kd 1.00 0.13 0.13 + Ks 1.00 0.13 0.13 + +newmtl red_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.13 0.13 + Ks 1.00 0.13 0.13 + +newmtl red_orange + Kd 1.00 0.38 0.06 + Ks 1.00 0.38 0.06 + +newmtl red_orange_t + d 0.69 + Tr 0.31 + Kd 1.00 0.38 0.06 + Ks 1.00 0.38 0.06 + +newmtl red_orange_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.38 0.06 + Ks 1.00 0.38 0.06 + +newmtl orange + Kd 1.00 0.63 0.00 + Ks 1.00 0.63 0.00 + +newmtl orange_t + d 0.69 + Tr 0.31 + Kd 1.00 0.63 0.00 + Ks 1.00 0.63 0.00 + +newmtl orange_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.63 0.00 + Ks 1.00 0.63 0.00 + +newmtl orange_yellow + Kd 0.97 0.78 0.00 + Ks 0.97 0.78 0.00 + +newmtl orange_yellow_t + d 0.69 + Tr 0.31 + Kd 0.97 0.78 0.00 + Ks 0.97 0.78 0.00 + +newmtl orange_yellow_tt + d 0.44 + Tr 0.56 + Kd 0.97 0.78 0.00 + Ks 0.97 0.78 0.00 + +newmtl yellow + Kd 0.94 0.94 0.00 + Ks 0.94 0.94 0.00 + +newmtl yellow_t + d 0.69 + Tr 0.31 + Kd 0.94 0.94 0.00 + Ks 0.94 0.94 0.00 + +newmtl yellow_tt + d 0.44 + Tr 0.56 + Kd 0.94 0.94 0.00 + Ks 0.94 0.94 0.00 + +newmtl yellow_green + Kd 0.63 0.94 0.00 + Ks 0.63 0.94 0.00 + +newmtl yellow_green_t + d 0.69 + Tr 0.31 + Kd 0.63 0.94 0.00 + Ks 0.63 0.94 0.00 + +newmtl yellow_green_tt + d 0.44 + Tr 0.56 + Kd 0.63 0.94 0.00 + Ks 0.63 0.94 0.00 + +newmtl green + Kd 0.00 1.00 0.00 + Ks 0.00 1.00 0.00 + +newmtl green_t + d 0.69 + Tr 0.31 + Kd 0.00 1.00 0.00 + Ks 0.00 1.00 0.00 + +newmtl green_tt + d 0.44 + Tr 0.56 + Kd 0.00 1.00 0.00 + Ks 0.00 1.00 0.00 + +newmtl green_cyan + Kd 0.00 1.00 0.63 + Ks 0.00 1.00 0.63 + +newmtl green_cyan_t + d 0.69 + Tr 0.31 + Kd 0.00 1.00 0.63 + Ks 0.00 1.00 0.63 + +newmtl green_cyan_tt + d 0.44 + Tr 0.56 + Kd 0.00 1.00 0.63 + Ks 0.00 1.00 0.63 + +newmtl cyan + Kd 0.00 1.00 1.00 + Ks 0.00 1.00 1.00 + +newmtl cyan_t + d 0.69 + Tr 0.31 + Kd 0.00 1.00 1.00 + Ks 0.00 1.00 1.00 + +newmtl cyan_tt + d 0.44 + Tr 0.56 + Kd 0.00 1.00 1.00 + Ks 0.00 1.00 1.00 + +newmtl cyan_blue + Kd 0.06 0.56 1.00 + Ks 0.06 0.56 1.00 + +newmtl cyan_blue_t + d 0.69 + Tr 0.31 + Kd 0.06 0.56 1.00 + Ks 0.06 0.56 1.00 + +newmtl cyan_blue_tt + d 0.44 + Tr 0.56 + Kd 0.06 0.56 1.00 + Ks 0.06 0.56 1.00 + +newmtl blue + Kd 0.13 0.13 1.00 + Ks 0.13 0.13 1.00 + +newmtl blue_t + d 0.69 + Tr 0.31 + Kd 0.13 0.13 1.00 + Ks 0.13 0.13 1.00 + +newmtl blue_tt + d 0.44 + Tr 0.56 + Kd 0.13 0.13 1.00 + Ks 0.13 0.13 1.00 + +newmtl blue_magenta + Kd 0.50 0.00 1.00 + Ks 0.50 0.00 1.00 + +newmtl blue_magenta_t + d 0.69 + Tr 0.31 + Kd 0.50 0.00 1.00 + Ks 0.50 0.00 1.00 + +newmtl blue_magenta_tt + d 0.44 + Tr 0.56 + Kd 0.50 0.00 1.00 + Ks 0.50 0.00 1.00 + +newmtl magenta + Kd 1.00 0.00 1.00 + Ks 1.00 0.00 1.00 + +newmtl magenta_t + d 0.69 + Tr 0.31 + Kd 1.00 0.00 1.00 + Ks 1.00 0.00 1.00 + +newmtl magenta_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.00 1.00 + Ks 1.00 0.00 1.00 + +newmtl magenta_red + Kd 1.00 0.00 0.50 + Ks 1.00 0.00 0.50 + +newmtl magenta_red_t + d 0.69 + Tr 0.31 + Kd 1.00 0.00 0.50 + Ks 1.00 0.00 0.50 + +newmtl magenta_red_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.00 0.50 + Ks 1.00 0.00 0.50 + +newmtl brown + Kd 0.50 0.31 0.06 + Ks 0.50 0.31 0.06 + +newmtl brown_t + d 0.69 + Tr 0.31 + Kd 0.50 0.31 0.06 + Ks 0.50 0.31 0.06 + +newmtl brown_tt + d 0.44 + Tr 0.56 + Kd 0.50 0.31 0.06 + Ks 0.50 0.31 0.06 + +newmtl white + Kd 1.00 1.00 1.00 + Ks 1.00 1.00 1.00 + +newmtl white_t + d 0.69 + Tr 0.31 + Kd 1.00 1.00 1.00 + Ks 1.00 1.00 1.00 + +newmtl white_tt + d 0.44 + Tr 0.56 + Kd 1.00 1.00 1.00 + Ks 1.00 1.00 1.00 + +newmtl grey + Kd 0.63 0.63 0.63 + Ks 0.63 0.63 0.63 + +newmtl grey_t + d 0.69 + Tr 0.31 + Kd 0.63 0.63 0.63 + Ks 0.63 0.63 0.63 + +newmtl grey_tt + d 0.44 + Tr 0.56 + Kd 0.63 0.63 0.63 + Ks 0.63 0.63 0.63 + +newmtl black + Kd 0.00 0.00 0.00 + Ks 0.00 0.00 0.00 + +newmtl black_t + d 0.69 + Tr 0.31 + Kd 0.00 0.00 0.00 + Ks 0.00 0.00 0.00 + +newmtl black_tt + d 0.44 + Tr 0.56 + Kd 0.00 0.00 0.00 + Ks 0.00 0.00 0.00 + +newmtl gold + Kd 1.00 0.84 0.00 + Ks 1.00 0.84 0.00 + +newmtl gold_t + d 0.69 + Tr 0.31 + Kd 1.00 0.84 0.00 + Ks 1.00 0.84 0.00 + +newmtl gold_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.84 0.00 + Ks 1.00 0.84 0.00 + +newmtl silver + Kd 0.75 0.75 0.75 + Ks 0.75 0.75 0.75 + +newmtl silver_t + d 0.69 + Tr 0.31 + Kd 0.75 0.75 0.75 + Ks 0.75 0.75 0.75 + +newmtl silver_tt + d 0.44 + Tr 0.56 + Kd 0.75 0.75 0.75 + Ks 0.75 0.75 0.75 + +newmtl bronze + Kd 0.85 0.69 0.50 + Ks 0.85 0.69 0.50 + +newmtl bronze_t + d 0.69 + Tr 0.31 + Kd 0.85 0.69 0.50 + Ks 0.85 0.69 0.50 + +newmtl bronze_tt + d 0.44 + Tr 0.56 + Kd 0.85 0.69 0.50 + Ks 0.85 0.69 0.50 + +newmtl test0 + Kd 0.00 0.82 1.00 + Ks 0.00 0.82 1.00 + +newmtl test0_t + d 0.69 + Tr 0.31 + Kd 0.00 0.82 1.00 + Ks 0.00 0.82 1.00 + +newmtl test0_tt + d 0.44 + Tr 0.56 + Kd 0.00 0.82 1.00 + Ks 0.00 0.82 1.00 + +newmtl test1 + Kd 0.00 1.00 0.82 + Ks 0.00 1.00 0.82 + +newmtl test1_t + d 0.69 + Tr 0.31 + Kd 0.00 1.00 0.82 + Ks 0.00 1.00 0.82 + +newmtl test1_tt + d 0.44 + Tr 0.56 + Kd 0.00 1.00 0.82 + Ks 0.00 1.00 0.82 + +newmtl test2 + Kd 0.38 1.00 0.00 + Ks 0.38 1.00 0.00 + +newmtl test2_t + d 0.69 + Tr 0.31 + Kd 0.38 1.00 0.00 + Ks 0.38 1.00 0.00 + +newmtl test2_tt + d 0.44 + Tr 0.56 + Kd 0.38 1.00 0.00 + Ks 0.38 1.00 0.00 + +newmtl test3 + Kd 0.94 0.94 0.00 + Ks 0.94 0.94 0.00 + +newmtl test3_t + d 0.69 + Tr 0.31 + Kd 0.94 0.94 0.00 + Ks 0.94 0.94 0.00 + +newmtl test3_tt + d 0.44 + Tr 0.56 + Kd 0.94 0.94 0.00 + Ks 0.94 0.94 0.00 + +newmtl test4 + Kd 1.00 0.63 0.00 + Ks 1.00 0.63 0.00 + +newmtl test4_t + d 0.69 + Tr 0.31 + Kd 1.00 0.63 0.00 + Ks 1.00 0.63 0.00 + +newmtl test4_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.63 0.00 + Ks 1.00 0.63 0.00 + +newmtl test5 + Kd 1.00 0.38 0.06 + Ks 1.00 0.38 0.06 + +newmtl test5_t + d 0.69 + Tr 0.31 + Kd 1.00 0.38 0.06 + Ks 1.00 0.38 0.06 + +newmtl test5_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.38 0.06 + Ks 1.00 0.38 0.06 + +newmtl test6 + Kd 1.00 0.13 0.13 + Ks 1.00 0.13 0.13 + +newmtl test6_t + d 0.69 + Tr 0.31 + Kd 1.00 0.13 0.13 + Ks 1.00 0.13 0.13 + +newmtl test6_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.13 0.13 + Ks 1.00 0.13 0.13 + +newmtl test7 + Kd 1.00 0.00 0.50 + Ks 1.00 0.00 0.50 + +newmtl test7_t + d 0.69 + Tr 0.31 + Kd 1.00 0.00 0.50 + Ks 1.00 0.00 0.50 + +newmtl test7_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.00 0.50 + Ks 1.00 0.00 0.50 + +newmtl test8 + Kd 1.00 0.00 1.00 + Ks 1.00 0.00 1.00 + +newmtl test8_t + d 0.69 + Tr 0.31 + Kd 1.00 0.00 1.00 + Ks 1.00 0.00 1.00 + +newmtl test8_tt + d 0.44 + Tr 0.56 + Kd 1.00 0.00 1.00 + Ks 1.00 0.00 1.00 + diff --git a/project/text-files/objflow.inc/objflow-head-cr.inc b/project/text-files/objflow.inc/objflow-head-cr.inc new file mode 100644 index 0000000..62cf5ef --- /dev/null +++ b/project/text-files/objflow.inc/objflow-head-cr.inc @@ -0,0 +1 @@ +#OBJFLOW diff --git a/project/text-files/objflow.inc/objflow-info-cr.inc b/project/text-files/objflow.inc/objflow-info-cr.inc new file mode 100644 index 0000000..f76dab2 --- /dev/null +++ b/project/text-files/objflow.inc/objflow-info-cr.inc @@ -0,0 +1,43 @@ +#OBJFLOW <<< The first 8 characters are the magic for OBJFLOW text files. +# <<< Don't remove them! +# +#------------------------------------------------------------------------------ +# +# This is the textual representation of an ObjFlow.bin file. +# It is defined by Wiimm and scanned by a 2-pass parser. +# +# Hints for decoding to a text file: +# +# - If either option --no-header (-H) or option --brief (-B) is set, this +# general description is suppressed. +# +# - If either option --export (-X) is set once or option --brief (-B) is set +# twice, a very small text file without descriptions and other not needed +# parameters is created. +# +# - If option --long (-l) is set, then additional hidden parameters are +# included. They are automatically included, if they are differ from the +# the standard settings. +# +#------------------------------------------------------------------------------ +# +# Syntax for section [OBJECTS]: +# +# Beside of emtpy lines and comments, there are 2 kinds of parameters. +# Both start with an keyword (case insensitive): +# +# 'OBJECT' ID, NAME, RESOURCES +# ID is the object ID to identify the object. Use prefix '0x' for hex +# numbers. NAME is the object name. If not set, the original name is +# used. RESOURCES is the resources string. If not set, the original +# string is used. Commas are mandatory to separate string parameters. +# +# 'PARAM' P0, P1, P2, P3, P4, P5, P6, P7, P8 +# Define up to 9 parameters of the previous defined 'OBJECT'. +# A point ('.') is interpreted as Null, and an equal sign ('=') +# leaves the value unchanged. Missed parameters are not modified. +# Commas are optional if expression is not ambiguous. +# +# See http://wiki.tockdom.com/wiki/ObjFlow.bin for more details. +# +#------------------------------------------------------------------------------ diff --git a/project/text-files/objflow.inc/objflow-objects-cr.inc b/project/text-files/objflow.inc/objflow-objects-cr.inc new file mode 100644 index 0000000..16b4787 --- /dev/null +++ b/project/text-files/objflow.inc/objflow-objects-cr.inc @@ -0,0 +1,7 @@ +[OBJECTS] +@REVISION = %u + +#----------------------------------------------------------------------- +# OBJECT ID, NAME, RESOURCES +# PARAM P0 P1 P2 P3 P4 P5 P6 P7 P8 +#----------------------------------------------------------------------- diff --git a/project/text-files/objflow.inc/objflow-setup-cr.inc b/project/text-files/objflow.inc/objflow-setup-cr.inc new file mode 100644 index 0000000..c8d5b7c --- /dev/null +++ b/project/text-files/objflow.inc/objflow-setup-cr.inc @@ -0,0 +1,12 @@ + +# +############################################################################### + +[SETUP] + +# identification of creator tool +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s diff --git a/project/text-files/pat.inc/pat-file-head-cr.inc b/project/text-files/pat.inc/pat-file-head-cr.inc new file mode 100644 index 0000000..249bc44 --- /dev/null +++ b/project/text-files/pat.inc/pat-file-head-cr.inc @@ -0,0 +1,16 @@ +#PAT <<< The first 4 characters '#PAT' are the magic for a PAT text file. +# <<< Don't remove them! +# +############################################################################### +# +# This is a textual representation of a PAT0 file used in Nintendos "Mario Kart +# Wii". "Wiimms SZS Tools" may convert binary (raw) and text PAT files in both +# directions in the near future. The text parser supports variables, C like +# expressions and nested IF-THEN-ELSE structures. +# +# Details about PAT text files are available in the Web: +# - Parser Syntax and Semantics: https://szs.wiimm.de/doc/syntax +# - PAT Syntax and Semantics: https://szs.wiimm.de/doc/pat/text +# - The BRRES/PAT0 file format: https://szs.wiimm.de/r/wiki/PAT0 +# +#------------------------------------------------------------------------------ diff --git a/project/text-files/pat.inc/pat-header-cr.inc b/project/text-files/pat.inc/pat-header-cr.inc new file mode 100644 index 0000000..b801384 --- /dev/null +++ b/project/text-files/pat.inc/pat-header-cr.inc @@ -0,0 +1,8 @@ + +#FF# +############################################################################### + +[HEADER] +# PAT header +@REVISION = %u + diff --git a/project/text-files/pat.inc/pat-s0_base-cr.inc b/project/text-files/pat.inc/pat-s0_base-cr.inc new file mode 100644 index 0000000..4c4379d --- /dev/null +++ b/project/text-files/pat.inc/pat-s0_base-cr.inc @@ -0,0 +1,8 @@ + +#FF# +############################################################################### + +[S0_BASE] +# PAT section 0, base data +@REVISION = %u + diff --git a/project/text-files/pat.inc/pat-s0_list-cr.inc b/project/text-files/pat.inc/pat-s0_list-cr.inc new file mode 100644 index 0000000..ef2c221 --- /dev/null +++ b/project/text-files/pat.inc/pat-s0_list-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[S0_LIST] +# PAT section 0, string reference lists +@REVISION = %u diff --git a/project/text-files/pat.inc/pat-setup-cr.inc b/project/text-files/pat.inc/pat-setup-cr.inc new file mode 100644 index 0000000..c8206c3 --- /dev/null +++ b/project/text-files/pat.inc/pat-setup-cr.inc @@ -0,0 +1,21 @@ + +#FF# +############################################################################### + +[SETUP] +# This is the general setup and info section. Some parameters are preceeded by +# a hash ('#') to signal the informative only status. The scanning starts +# always with this section. + +# Identify the tool and version, that have written this file: +TOOL = %s +SYSTEM = %s +VERSION = %s +REVISION = %u +DATE = %s + +# The PAT version, number of sections and output mode. Info only. +PAT-VERSION = %2u +N-SECTIONS = %2u +OUTPUT-MODE = %s + diff --git a/project/text-files/pat.inc/pat-simple-cr.inc b/project/text-files/pat.inc/pat-simple-cr.inc new file mode 100644 index 0000000..e8aee44 --- /dev/null +++ b/project/text-files/pat.inc/pat-simple-cr.inc @@ -0,0 +1,7 @@ + +#FF# +############################################################################### + +[SIMPLE] +# Simplified PAT definiton. +@REVISION = %u diff --git a/project/text-files/pat.inc/pat-strings-cr.inc b/project/text-files/pat.inc/pat-strings-cr.inc new file mode 100644 index 0000000..199d88c --- /dev/null +++ b/project/text-files/pat.inc/pat-strings-cr.inc @@ -0,0 +1,8 @@ + +#FF# +############################################################################### + +[STRINGS] +# Strings defined in section 1 with attributes defined in section 3 +@REVISION = %u + diff --git a/project/text-files/setup.inc/setup-create-cr.inc b/project/text-files/setup.inc/setup-create-cr.inc new file mode 100755 index 0000000..1442584 --- /dev/null +++ b/project/text-files/setup.inc/setup-create-cr.inc @@ -0,0 +1,24 @@ + +#------------------------------------------------------------------------------ + +[create] + +# Create (and overwrite without notice) the listed files before adding them to +# the new archive. The source directory is searched under the same filename +# with an appended ".d" or, if this fails, with an extension replaced by ".d". +# If a source directory does not exist, the job is ignored silently. +# Example: For "my.brres" search directories "my.brres.d" or "my.d" +# The source directory itself is set on the exclude list and will not stored +# into the new archive. Creating can be controlled by the options: +# --no-recurse : Do not create archives recursively. +# --no-encode : Do not encode files before creating an archive. +# --encode-img : Encode all image files (TEX,TPL,BREFT/sub), overrides --no-encode. +# --encode-all : Encode all files and ignore timestamps. +# --pt-dir : Set `point directory' mode. +# --u8 : Create an U8 archive. +# --brres : Create an BRRES archive. +# --breff : Create an BREFF archive. +# --breft : Create an BREFT archive. +# --no-compress : Create an uncompressed file (U8 instead of SZS). +# ... + diff --git a/project/text-files/setup.inc/setup-encode-cr.inc b/project/text-files/setup.inc/setup-encode-cr.inc new file mode 100755 index 0000000..9775867 --- /dev/null +++ b/project/text-files/setup.inc/setup-encode-cr.inc @@ -0,0 +1,26 @@ + +#------------------------------------------------------------------------------ + +[encode] + +# Encode (and overwrite without notice) the listed files before adding them +# to the new archive. The source is searched under the same filename with the +# correct source extension added or, if this failed, a replaced extension. +# Example: For "message.bmg" search files "message.bmg.txt" or "message.txt" +# The source file itself is set on the exclude list and will not stored into +# the new archive. +# +# The syntax of this file section is a little bit different as the other file +# sections. Each line begins with a comma separated list of keywords followed +# by an equal sign. +# Syntax: COMMA_SEPARATED_FORMAT_LIST = relative_filename +# Example: BREFT,IA8,3 = ./files/rk_steam +# The first keyword is the file format. The following keywords describe sub +# formats. An additonal integer is a file type specific information: +# -> For image files it is the number of images (main image + mipmaps). +# A value of 0 means: Use the tools default (see option --n-mipmaps). +# +# A file is only encoded if the destination does not exist or if the source +# is younger than the destination. Encoding can be controlled by options +# --no-encode, --encode-img and --encode-all (short: --noenc --eimg --eall). + diff --git a/project/text-files/setup.inc/setup-exclude-cr.inc b/project/text-files/setup.inc/setup-exclude-cr.inc new file mode 100755 index 0000000..3800343 --- /dev/null +++ b/project/text-files/setup.inc/setup-exclude-cr.inc @@ -0,0 +1,36 @@ + +#------------------------------------------------------------------------------ + +[exclude-pattern] + +# Files and directories in this section are ignored by the CREATE command. +# The include sections takes precedence over the exclude sections. + +# This section expects file patterns with wildcards. +# Here we exclude all original, backup and temp files: + +*.orig +*.bak +*.tmp +*.temp + +# Exclude all *.d directories. To enable it, remove "# " at the beginning. +# *.d + +# Exclude all PNG files. The intention is to allow the creation of new PNG +# images and mipmap files without any need to edit this setup file. All +# extracted PNG files are automatically listed in the [exclude] section. +# So the exclusion of all PNG is not really needed, but may help. The second +# (disabled) line is an alternative to exclude all mipmap PNG files. + +*.png +# *.mm#.png + +#------------------------------------------------------------------------------ + +[exclude] + +# Files and directories in this section are ignored by the CREATE command. +# This second exclude section accepts only complete file names and none of +# characters have any special meaning. + diff --git a/project/text-files/setup.inc/setup-header-cr.inc b/project/text-files/setup.inc/setup-header-cr.inc new file mode 100755 index 0000000..dac1238 --- /dev/null +++ b/project/text-files/setup.inc/setup-header-cr.inc @@ -0,0 +1,15 @@ + +############################################################################### +# +# wszst settings: +# --------------- +# +# This is the settings file for wszst (tool of "Wiimms SZS Tools"). It +# was created by the EXTRACT command and is needed by the CREATE command +# to rebuild the original source archive with or without modifications. +# You can edit it, if you know what you do. +# +############################################################################### + +# The first part contains some general settings: + diff --git a/project/text-files/setup.inc/setup-include-cr.inc b/project/text-files/setup.inc/setup-include-cr.inc new file mode 100755 index 0000000..8edec87 --- /dev/null +++ b/project/text-files/setup.inc/setup-include-cr.inc @@ -0,0 +1,26 @@ + +############################################################################### +# +# Some general rules for the next sections: +# All pathes are relative to the current base directory (directory that +# contains the file wszst-setup.txt). Prefix at least pathes that starts +# not with a letter, digit, point, star or underscore with "./". +# +#------------------------------------------------------------------------------ + +[include-pattern] + +# All files and directories start with a . are hidden (unix like) and excluded +# automatically. But filenames in one of the include sections are included +# independent of this point. Also the include sections takes precedence over +# the exclude sections. + +# This section expects file patterns with wildcards. + +#------------------------------------------------------------------------------ + +[include] + +# This second include section accpepts only complete file names and none of +# characters have any special meaning. + diff --git a/project/text-files/setup.inc/setup-order-cr.inc b/project/text-files/setup.inc/setup-order-cr.inc new file mode 100644 index 0000000..45c96fc --- /dev/null +++ b/project/text-files/setup.inc/setup-order-cr.inc @@ -0,0 +1,14 @@ + +#------------------------------------------------------------------------------ + +[order] + +# This list contains all extracted files in the order of the original archive. +# If creating archives (but not U8) this list will be used to sort the found +# files. For archives with directory support (all but not PACK) it is very +# important, that the directories are listed before the files that resides in +# the directories. +# +# The value before the equal sign is the file offset in the original archive +# and is ignored for sorting. + diff --git a/project/text-files/ui-head.inc b/project/text-files/ui-head.inc new file mode 100644 index 0000000..1ad49ab --- /dev/null +++ b/project/text-files/ui-head.inc @@ -0,0 +1,41 @@ + +/*************************************************************************** + * _______ _______ _______ * + * | ___ |____ | ___ | * + * | | |_| / /| | |_| * + * | |_____ / / | |_____ * + * |_____ | / / |_____ | * + * _ | | / / _ | | * + * | |___| |/ /____| |___| | * + * |_______|_______|_______| * + * * + * Wiimms SZS Tools * + * * + *************************************************************************** + * * + * This file is part of the SZS project. * + * Visit https://szs.wiimm.de/ for project details and sources. * + * * + * Copyright (c) 2011-2020 by Dirk Clemens * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + *************************************************************************** + * * + * >>> This file is automatically generated by './src/gen-ui.c'. <<< * + * >>> Do not edit this file! <<< * + * * + ***************************************************************************/ + -- cgit v1.3.1